pax_global_header00006660000000000000000000000064146427244300014520gustar00rootroot0000000000000052 comment=dad1033640f249fa4994f976cf6ee96826c15702 sngrep-1.8.2/000077500000000000000000000000001464272443000130265ustar00rootroot00000000000000sngrep-1.8.2/.gitignore000066400000000000000000000007211464272443000150160ustar00rootroot00000000000000# Ignore eclipse files .*project .settings .vscode .idea build # Build files src/sngrep tests/test-*.log tests/test-*.trs tests/test-??? *.o .deps .dirstamp *gmon.out src/config.h.in src/config.h src/stamp-h1 .autotools Makefile Makefile.in aclocal.m4 autom4te.cache config.log config.status config.sub config.guess configure missing install-sh depcomp test-driver # Ignore Doxygen generated files doc/html # Tags file tags TAGS # Vim swap files .*.swp .*.swo sngrep-1.8.2/.travis.yml000066400000000000000000000005431464272443000151410ustar00rootroot00000000000000language: c arch: - amd64 - ppc64le compiler: - clang - gcc notifications: recipients: - kaian@irontec.com install: - sudo apt-get -qq update || true - sudo apt-get install -y libncurses5-dev libpcap-dev libssl-dev script: - ./bootstrap.sh - ./configure - make branches: only: - master - travis - ppc64le sngrep-1.8.2/AUTHORS000066400000000000000000000000631464272443000140750ustar00rootroot00000000000000Ivan Alonso (aka Kaian) First version of all files sngrep-1.8.2/CMakeLists.txt000066400000000000000000000264761464272443000156050ustar00rootroot00000000000000# CMakeLists file derived from configure.ac cmake_minimum_required( VERSION 3.7...3.27 ) option( WITH_GNUTLS "Enable SSL Support (TLS SIP Transport)" no ) option( WITH_OPENSSL "Enable SSL Support (TLS SIP Transport)" no ) option( WITH_PCRE "Enable Perl compatible regular expressions" no ) option( WITH_PCRE2 "Enable Perl compatible regular expressions (v2)" no ) option( WITH_ZLIB "Enable zlib to support gzip compressed pcap files" no ) option( WITH_UNICODE "Enable Ncurses Unicode support" no ) option( USE_IPV6 "Enable IPv6 Support" no ) option( USE_EEP "Enable EEP/HEP Support" no ) option( DISABLE_LOGO "Disable Irontec Logo from Summary menu" no ) # Read parameters of AC_INIT() from file configure.ac file( STRINGS configure.ac AC_INIT REGEX "AC_INIT\\(.*\\)" ) if( AC_INIT MATCHES "^.*AC_INIT\\( *\\[([^]]*)\\], *\\[([^]]*)\\], *\\[([^]]*)\\], *\\[([^]]*)\\], *\\[([^]]*)\\] *\\).*$" ) set( AC_PACKAGE_NAME "${CMAKE_MATCH_1}" ) set( AC_PACKAGE_VERSION "${CMAKE_MATCH_2}" ) set( AC_PACKAGE_STRING "${CMAKE_MATCH_1} ${CMAKE_MATCH_2}" ) set( AC_PACKAGE_BUGREPORT "${CMAKE_MATCH_3}" ) set( AC_PACKAGE_TARNAME "${CMAKE_MATCH_4}" ) set( AC_PACKAGE_URL "${CMAKE_MATCH_5}" ) message( STATUS "${AC_PACKAGE_STRING}" ) else() message( FATAL_ERROR "Error parsing AC_INIT(...) from file configure.ac" ) endif() project( ${AC_PACKAGE_NAME} VERSION ${AC_PACKAGE_VERSION} LANGUAGES C ) add_executable( sngrep src/main.c ) set_target_properties( sngrep PROPERTIES C_STANDARD 11 C_STANDARD_REQUIRED YES C_EXTENSIONS YES ) target_compile_options( sngrep PRIVATE -Wall -pedantic ) # -Wextra -Wconversion -Werror # Define _GNU_SOURCE etc. if( CMAKE_SYSTEM_NAME STREQUAL "Linux" ) target_compile_definitions( sngrep PRIVATE _GNU_SOURCE=1 _XOPEN_SOURCE_EXTENDED=1 ) endif() # we might want to use this with zlib for compressed pcap support include( CheckFunctionExists ) check_function_exists( fopencookie HAVE_FOPENCOOKIE ) ####################################################################### # Check for other REQUIRED libraries find_package( PkgConfig REQUIRED ) message( STATUS "Checking for library 'libpthread'" ) find_library( LIBPTHREAD pthread ) # Note: The use of "REQUIRED" here would need CMake 3.18... if( LIBPTHREAD ) # ...therefore we check the result for ourself message( STATUS " Found ${LIBPTHREAD}" ) target_link_libraries( sngrep PUBLIC pthread ) else() message( STATUS " No library 'libpthread' found" ) message( FATAL_ERROR "You need to have libpthread installed to compile sngrep." ) endif() pkg_check_modules( LIBPCAP IMPORTED_TARGET libpcap ) if( LIBPCAP_FOUND ) target_link_libraries( sngrep PUBLIC PkgConfig::LIBPCAP ) else() message( STATUS "Checking for library 'libpcap'" ) find_library( LIBPCAP pcap ) if( LIBPCAP ) message( STATUS " Found ${LIBPCAP}" ) target_link_libraries( sngrep PUBLIC pcap ) else() message( STATUS " No library 'libpcap' found" ) message( FATAL_ERROR "You need to have libpcap installed to compile sngrep." ) endif() endif() #### #### Ncurses Wide character support #### if( WITH_UNICODE ) pkg_check_modules( NCURSES REQUIRED IMPORTED_TARGET ncursesw ) pkg_check_modules( PANEL REQUIRED IMPORTED_TARGET panelw ) pkg_check_modules( FORM REQUIRED IMPORTED_TARGET formw ) pkg_check_modules( MENU REQUIRED IMPORTED_TARGET menuw ) else() pkg_check_modules( NCURSES REQUIRED IMPORTED_TARGET ncurses ) pkg_check_modules( PANEL REQUIRED IMPORTED_TARGET panel ) pkg_check_modules( FORM REQUIRED IMPORTED_TARGET form ) pkg_check_modules( MENU REQUIRED IMPORTED_TARGET menu ) endif() target_link_libraries( sngrep PUBLIC PkgConfig::NCURSES PkgConfig::PANEL PkgConfig::FORM PkgConfig::MENU ) #### #### GnuTLS Support #### if( WITH_GNUTLS ) pkg_check_modules( GNUTLS REQUIRED IMPORTED_TARGET gnutls ) message( STATUS "Checking for library 'libgcrypt'" ) find_library( LIBGCRYPT gcrypt ) # Note: The use of "REQUIRED" here would need CMake 3.18... if( LIBGCRYPT ) # ...therefore we check the result for ourself message( STATUS " Found ${LIBGCRYPT}" ) else() message( STATUS " No library 'libgcrypt' found" ) message( FATAL_ERROR "You need to have libgcrypt installed to compile sngrep" ) endif() target_link_libraries( sngrep PUBLIC pthread PkgConfig::GNUTLS gcrypt ) endif() #### #### OpenSSL Support #### if( WITH_OPENSSL ) if( WITH_GNUTLS ) message( FATAL_ERROR "GnuTLS and OpenSSL can not be enabled at the same time" ) endif() pkg_check_modules( LIBSSL REQUIRED IMPORTED_TARGET libssl ) pkg_check_modules( LIBCRYPTO REQUIRED IMPORTED_TARGET libcrypto ) target_link_libraries( sngrep PUBLIC pthread PkgConfig::LIBSSL PkgConfig::LIBCRYPTO ) endif() #### #### PCRE Support #### if( WITH_PCRE ) pkg_check_modules( LIBPCRE REQUIRED IMPORTED_TARGET libpcre ) target_link_libraries( sngrep PUBLIC pthread PkgConfig::LIBPCRE ) endif() #### #### PCRE2 Support #### if( WITH_PCRE2 ) if( WITH_PCRE ) message( FATAL_ERROR "libpcre-2 and libpcre-3 can not be enabled at the same time" ) endif() target_compile_definitions( sngrep PRIVATE PCRE2_CODE_UNIT_WIDTH=8 ) # Required for including pcre2.h pkg_check_modules( LIBPCRE2 REQUIRED IMPORTED_TARGET libpcre2-8 ) target_link_libraries( sngrep PUBLIC pthread PkgConfig::LIBPCRE2 ) endif() #### #### zlib Support #### if ( WITH_ZLIB ) pkg_check_modules( ZLIB REQUIRED IMPORTED_TARGET zlib ) target_link_libraries( sngrep PUBLIC pthread PkgConfig::ZLIB ) endif() # Source inclusion target_sources( sngrep PRIVATE src/address.c src/packet.c src/sip.c src/sip_call.c src/sip_msg.c src/sip_attr.c src/option.c src/group.c src/filter.c src/keybinding.c src/media.c src/setting.c src/rtp.c src/util.c src/hash.c src/vector.c # src/curses/ui_panel.c src/curses/scrollbar.c src/curses/ui_manager.c src/curses/ui_call_list.c src/curses/ui_call_flow.c src/curses/ui_call_raw.c src/curses/ui_stats.c src/curses/ui_filter.c src/curses/ui_save.c src/curses/ui_msg_diff.c src/curses/ui_column_select.c src/curses/ui_settings.c ) target_include_directories( sngrep PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) # Conditional Source inclusion target_sources( sngrep PRIVATE src/capture.c ) if( WITH_GNUTLS ) target_sources( sngrep PRIVATE src/capture_gnutls.c ) endif() if( WITH_OPENSSL ) target_sources( sngrep PRIVATE src/capture_openssl.c ) endif() if( USE_EEP ) target_sources( sngrep PRIVATE src/capture_eep.c ) endif() ###################################################################### # Generate config.h configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h ) target_include_directories( sngrep PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ) ###################################################################### # Installing include( GNUInstallDirs ) install( TARGETS sngrep RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) install( FILES config/sngreprc DESTINATION /etc ) install( FILES doc/sngrep.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8 ) install( FILES README TODO COPYING ChangeLog DESTINATION "${CMAKE_INSTALL_DOCDIR}" ) ###################################################################### # Packaging set( CPACK_PACKAGE_NAME "${AC_PACKAGE_NAME}" ) set( CPACK_PACKAGE_VERSION "${AC_PACKAGE_VERSION}" ) set( CPACK_PACKAGE_CONTACT "Ivan Alonso " ) # ${AC_PACKAGE_BUGREPORT} only contains the E-Mail address, therefore we don't use it here set( CPACK_PACKAGE_HOMEPAGE_URL "${AC_PACKAGE_URL}" ) set( CPACK_PACKAGE_VENDOR "Irontec S.L." ) set( CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" ) set( CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README" ) set( CPACK_STRIP_FILES TRUE ) set( CPACK_PACKAGE_DESCRIPTION_SUMMARY "Ncurses SIP Messages flow viewer" ) set( CPACK_PACKAGE_DESCRIPTION "sngrep displays SIP Messages grouped by Call-Id into flow diagrams. It can be used as an offline PCAP viewer or online capture using libpcap functions. It supports SIP UDP, TCP and TLS transports (when each message is delivered in one packet). You can also create new PCAP files from captures or displayed dialogs." ) set( CPACK_DEBIAN_FILE_NAME DEB-DEFAULT ) #set( CPACK_DEBIAN_PACKAGE_RELEASE 1 ) set( CPACK_DEBIAN_PACKAGE_SECTION "comm" ) set( CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON ) set( CPACK_RPM_FILE_NAME RPM-DEFAULT ) #set( CPACK_RPM_PACKAGE_RELEASE 1 ) set( CPACK_RPM_PACKAGE_RELEASE_DIST ON ) set( CPACK_RPM_PACKAGE_GROUP "Applications/Engineering" ) set( CPACK_RPM_PACKAGE_LICENSE "GPLv3" ) set( CPACK_RPM_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}" ) set( CPACK_RPM_PACKAGE_AUTOREQ YES ) include( CPack ) ###################################################################### # Print Logo if( NOT DISABLE_LOGO ) message( STATUS "" ) message( STATUS " ██╗██████╗ ██████╗ ███╗ ██╗████████╗███████╗ ██████╗" ) message( STATUS " ██║██╔══██╗██╔═══██╗████╗ ██║╚══██╔══╝██╔════╝██╔════╝" ) message( STATUS " ██║██████╔╝██║ ██║██╔██╗ ██║ ██║ █████╗ ██║ " ) message( STATUS " ██║██╔══██╗██║ ██║██║╚██╗██║ ██║ ██╔══╝ ██║ " ) message( STATUS " ██║██║ ██║╚██████╔╝██║ ╚████║ ██║ ███████╗╚██████╗" ) message( STATUS " ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚══════╝ ╚═════╝" ) endif() message( STATUS "" ) message( STATUS "sngrep configure finished" ) message( STATUS "======================================================" ) message( STATUS "GnuTLS Support : ${WITH_GNUTLS}" ) message( STATUS "OpenSSL Support : ${WITH_OPENSSL}" ) message( STATUS "Unicode Support : ${WITH_UNICODE}" ) message( STATUS "Perl Expressions Support : ${WITH_PCRE}" ) message( STATUS "Perl Expressions Support (v2): ${WITH_PCRE2}" ) message( STATUS "IPv6 Support : ${USE_IPV6}" ) message( STATUS "EEP Support : ${USE_EEP}" ) message( STATUS "Zlib Support : ${WITH_ZLIB}" ) message( STATUS "======================================================" ) message( STATUS "" ) ###################################################################### # Tests enable_testing() # "ctest" will run all tests add_custom_target( tests ) # "make tests" will build all tests foreach( i 001 002 003 004 005 006 007 008 009 010 011 ) add_executable( test_${i} EXCLUDE_FROM_ALL tests/test_${i}.c ) if( i STREQUAL "007" ) target_sources( test_${i} PUBLIC src/vector.c src/util.c ) elseif( i STREQUAL "010" ) target_sources( test_${i} PUBLIC src/hash.c ) endif() target_include_directories( test_${i} PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ) add_test( NAME test_${i} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_${i} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests ) add_dependencies( tests test_${i} ) endforeach() sngrep-1.8.2/COPYING000066400000000000000000001045131464272443000140650ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . sngrep-1.8.2/ChangeLog000066400000000000000000000423701464272443000146060ustar00rootroot000000000000002024-07-08 Ivan Alonso * sngrep 1.8.2 released * capture: fix possible buffer overflow while processing RTP payload 2024-04-08 Ivan Alonso * sngrep 1.8.1 released * capture: fix possible buffer overflow while processing headers 2023-12-20 Ivan Alonso * sngrep 1.8.0 released * fix typo in message, thanks to lintian * fix compiler warnings about unused variables * fix debian pkg dependency to libpcre2-8-0 * Fixed a typo in comment line in filter.c * Redefine usage of POSIX signals * Support for building sngrep using CMake added 2023-03-31 Ivan Alonso * sngrep 1.7.0 released * save: add option --text to save captured data to plain text * capture: fix memory overflows while parsing IP headers * hep: fix hep listener enabled in offline mode * core: stop sngrep when parent process has ended * ssl: fix decrypt with AES256 GCM SHA384 cipher 2022-08-31 Ivan Alonso * sngrep 1.6.0 released * capture: added compatibility with openssl >= 3.5.0 * capture: fixed memory leak while rotating dialogs * capture: added save file rotation through SIPHUP signal * capture: added support for opening gzip compressed input files * hep: received HEP packets can now be saved to PCAP files * core: added support for PCRE2 library * cf: raw payload preview now properly displays characters near window borders * cli: properly display captured dialog count when no interface is used 2022-04-26 Ivan Alonso * sngrep 1.5.0 released * capture: add support for IP-IP encapsulation * capture: add support for IPv6 fragments reassembly * hep: add support for saving HEP received packets to PCAP * tls: check client TLS version in gnutls code * ui: fixed a crash when leaving ncurses screens 2021-11-19 Ivan Alonso * sngrep 1.4.10 released * build: fix compilation warnings on MacOS 10.15 * build: fix compilation errors when using -Werror=format-security * capture: properly set transport as TCP for assembled TCP messages * config: alias keyword now supports IP:PORT format * filter: increased size of src and dst fields for IPv6 addresses 2021-05-20 Ivan Alonso * sngrep 1.4.9 released * capture: add support for HEP/EEP protocol packet capture * capture: fixed a buffer overflow while validating TCP packets * core: fixed timestamp formats for OpenBSD * call flow: improved display for B-leg extended flows 2020-11-10 Ivan Alonso * sngrep 1.4.8 released * capture: add support for IPv6 SDP connection address * capture: fixed a crash while parsing INVITES without R-URI * capture: fixed a crash while parsing malformed Req/Resp lines * call flow: improved IPv6 RTP columns address display * core: code cleanup removing unused variables * core: fixed multiple snprintf memory overflows * core: avoid crash when linking against ncurses without tinfo 2020-05-21 Ivan Alonso * sngrep 1.4.7 released * capture: fixed a crash with invalid CSeq values * capture: allow configurable libpcap capture buffer * hep: support parsing of HEPv3 headers in any order * ssl: updated code to exclude depcreted OpenSSL functions * call raw: add support to IP alias display * call list: updated diverted Call state conditions * cli: match expression now applies to whole SIP payload * cli: add support to multiple comma separated capture devices * sip: add KDMQ custom method to SIP parser 2018-10-31 Ivan Alonso * sngrep 1.4.6 released * capture: fixed pcap reading from stdin * capture: make SIP regexp more tolerant to invalid uris * capture: SIP dialogs can now start with INFO, REFER and UPDATE * call list: Added methods and key bindings for soft clear of call list * call flow: improved performance while displaying a dialog with lots of messages * call flow: arrows are now sorted by time while displaying multiple dialogs * ui: fixed multiples buffer overflows crashes * ui: improve compatibility with newest ncurses version 2018-01-19 Ivan Alonso * sngrep 1.4.5 released * save: avoid crashing when multiple source inputs are used * call list: fix buffer overflow in display filter with wide terminals * capture: added new setting capture.tlsserver to only process TLS packets to tha address * capture: fixed compatibility with OpenSSL >= 1.1.0 * capture: only read stdin when input file is '-' 2017-09-17 Ivan Alonso * sngrep 1.4.4 released * Fixed multiples crashes with SIP TCP payloads * Improve configure detection for gnutls and ncurses * HEP/EEP: removed default password * HEP/EEP: hep capture mode is now displayed in call list window * -I flag now supports reading from standard input * Call List and Call Flow windows now resize properly 2017-05-10 Ivan Alonso * sngrep 1.4.3 released * Capture * Added support for capturing from multiples interfaces from command line * Fixed a bug where IP reassembly was only working whe packet only had two fragments * Fixed a bug where TCP packets where not assembled in the correct order * HEP/EEP * Added support for UUID chunks sent by asterisk HEP PJSIP module * SIP * Improved From/To header regexp to parse URIs without user part * SIP responses codes 600 will now set callstate to BUSY * Call Flow * Added keybinding to deselect previously selected arrow (defaults to Ctrl+W) * Fixed a bug while displaying arrows order of a dialog from multiple input files * Configuration * Configuration file can no be specified using environment variable SNGREPRC * Added `-F` command line option to start sngrep without reading any configuration file * Packages/Build * OpenSSL depedencies are now being searching using pkg-config if available 2016-12-19 Ivan Alonso * sngrep 1.4.2 released * Capture * Interface network mask and IP address are no longer a must to capture * TLS * gnutls: Add support for initial ClientHello in SSLv2 * gnutls: Initial support for GCM cipher modes * gnutls: Improve compatibility with old libgcrypt versions * Call List * Add setting for default sorting field and order * Call Flow * Added a keybinding to only display RTP streams * Added a new mode to only display _active_ RTP streams * Packages * Irontec Debian packages now compile using gnutls * Updated spec file for RPM packages 2016-10-28 Ivan Alonso * sngrep 1.4.1 released * Capture * Reworked how IP fragments are assembled * Increased default capture snapshot length * SIP * Added support for any kind of uri schemes * Added support for Warning header code * Added support for Reason header text * Added callstates BUSY and DIVERTED * Call List * Fixed a bug displaying sort by column ('<','>' keybinding) * Call Flow * Fixed a bug where arrows with same timestamp were not displayed * Settings * Added a setting for filter.payload * Other * Fixed text typos 2016-08-08 Ivan Alonso * sngrep 1.4.0 released * Capture * Added a setting and command line option ('-R') to rotate captured dialogs * Improved RTP stream detection * Call List * Added capture match and bpf filters labels * Added capture device label for online captures * Call Flow * Rework how RTP streams arrows select their source and destination columns * Settings * Added a setting to configure EEP/HEP capture id * Changed default savepath to current directory * Other * Fixed a crash with malformed From and To headers * Fixed a crash while comparing two big SIP messages * Fixed a crash while trying to decode TLS packets too early * Fixed incorrect state detection of Call status column * Increased the timeout to consider a test unsuccessful 2016-04-28 Ivan Alonso * sngrep 1.3.1 released * Fixed a crash where From or To headers where too long * Improved call list screen resize * Add support for IEEE 802.1Q Ethernet VLAN headers * Fixed a bug in displayed callstate after failed authentication * Associate RTP stream with the most recent dialog that uses stream rtp ports 2016-03-02 Ivan Alonso * sngrep 1.3.0 released * Interface * Recoded panel updates to only refresh when something changes * Allow saving displayed dialogs from Call Flow (default key 'S') * Allow clearing Call List from Flow or Raw (default keys Ctrl-L or F5) * Call List * Call List can now be sorted by displayed columns (default keys '<' and 'z') * Fixed a bug to handle filter.method setting on startup * Display Calls: instead of Dialogs: in header when only print calls is enabled * Call Flow * Improved Extended flow arrow display for dialogs with the same X-Call-Id * Merge RTP arrows that have the same port/address * Improved RTP arrow displayed in only one column * Display RTCP information in RTP arrows (still requires lots of work) * Arrow times can now be toggled (default key 'w') * Remove RTCP arrows from Call Flow * SIP * Stored Response texts when not matching their response code default text * Fixed a crash when parsing application type SDP * Capture * Improved TCP SIP detection and parsing * Refactored packet and configuration structures * Removed IP address lookup feature * Other * Adding new tests for Call List sorting * Keybinding fixes and user input improvements * Address/Port code refactoring 2015-12-10 Ivan Alonso * sngrep 1.2.0 released * Add support for EEP/HEP version 2 * Call List: autoscroll (default keybinding A) * Call List: Align SIP arrows timestamp * Call Flow: Disable RTP/RTCP arrows by default (m to display or set cf.media yes in sngreprc) * Resolved addresses from eep address settings * Fixed dump pcap while using -O option * Fixed a bug while parsing RTCP packet payload * Improved color sheme for b/w terminals 2015-10-28 Ivan Alonso * sngrep 1.1.0 released * Added GnuTLS support * Added initial RTCP support * Added a simple message statistics screen ('i' keybinding) * Added payload filtering in filter screen * Added alias directive to label addresses * Call Flow: Compressed view now uses alias to merge columns * Refactored Keybinding initializations * Improved keybinding dump information when using --dump-config * Make EEP support a configurable option * Make EEP and IPv6 configurable options enabled by default 2015-10-06 Ivan Alonso * sngrep 1.0.0 released * Implemented basic TCP reassembly * Implemented basic IP reassembly * Implememted EEP/HEP client/server support * Implemented command line option --dump-config * Disabled RTP packet payload storage by default * Removed --enable-openssl configure flag (use --with-openssl instead) * Removed configurable ignore directives in rc file * Removed configurable option cl.filter * Improved payload memory storage * Added a confirmation dialog to overwrite saved files * Added save button to Columns select UI * Added a keybinding to remove selected dialogs in Call List * Added a keybinding to move to the first and last item in Call List * Added attribute color support (can be disabled with cl.colorattr) * Added a compressed view in Call Flow (one message per row) * Fixed IPv6 support * Fixed multiple memory leaks 2015-09-01 Ivan Alonso * sngrep 0.4.2 released * Fixed a crash with RTP format detection * Fixed capture.rtp setting * Fixed a crash while parsing captured packets headers * Fixed a crash while creating new columns in call flow window * Restored TLS and WS transport payload display * Added Timestamp to the first RTP packet displayed in call flow window * Improved packet payload storage 2015-07-08 Ivan Alonso * sngrep 0.4.1 released * Added an option to capture RTP packets * Allow RTP packets to be saved with their calls * Improved Save panel default options * Added testing files * Improved SIP message parsed process * Improved SIP message payload memory usage * Fixed a bug with timestamp diff overflows * Fixed multiple memory leaks * Fixed compatibility with BSD systems 2015-06-24 Ivan Alonso * sngrep 0.4.0 released * Added Websocket (WS) transport support * Added an option (alias) to replace addresses on screen * Added a new screen to change/save settings * Added support for multiple SDP medias in flow screen * Added delta time between SIP messages in flow screen * Added RTP stream arrows in flow screen * RTP packets will be now saved with -O command line option * Merged pcap and txt save screens * Recoded screen update process * Replace all internal linked list with vector structures * Added index as first column in call list * Fixed a bug with address resloution 2015-05-17 Ivan Alonso * sngrep 0.3.2 released * Fixed sources compilation for BSD systems * Fixed a bug where Contact header was displayed in SIP From column * Fixed some keybindings in filter and save panels * Improved SIP packet detection from payload * Highlight local address columns in Call Flow 2015-04-14 Ivan Alonso * sngrep 0.3.1 released * Added command line option -N to not display ncurses interface * Added command line option -q to dont print any stdout output * Added optional IPv6 support * Added SIP compact headers support * Fixed autotools templates for OpenBSD 5 * Set dark background by default * Repladed SIP payload function with regexp * Implemented configurable keybindings via sngreprc * Added more default keybindings * Implement basic RTP detection * Improved parsing payload performance 2015-03-02 Ivan Alonso * sngrep 0.3.0 released * Added command line option -c to only display calls * Added command line option -l to change capture limit * Added command line match expression for packet payload * Converted all filters to regular expressions (POSIX or PCRE) * Added optional PCRE support (Perl Compatible Regular Expressions) * Added optional UTF-8 / Unicode compatible terminals support * Added current displayed and total dialogs counters in Call list * Added an option to save displayed dialogs (after filtering) * Added an option to save current columns layout * Added new attributes: - sipfromuser: User in From: Header - siptouser: User in To: header - convdur: Total conversation duration (from 200 to BYE) - totaldur: Total call duration (from first to last message) * Interface will now be displayed while pcaps are loading * Reworked Interface refresh process * Reworked Filtering process * Fixed general performance issues * Disable OpenSSL support by default * Removed command line option to read pcap without flags * Added long versions of command line flags 2015-02-09 Ivan Alonso * sngrep 0.2.2 released * Added a new panel to configure Call List columns during runtime (static configuration can still be done using sngreprc file) * Added a configuration option to change selected message highlight in Call Flow (bold is not properly displayed in some terminals) * Fixed message retransmission detection (-->>>) * Fixed some drawing issues on small screens * Fixed a crash when capturing while saving pcap dialog was being displayed * Fixed a bug that considered ACK as a response instead of a request * Fixed Call state for REJECTED calls * Fixed Colors for monochrome terminals * Changed default column width for SIP To/From to 30 characters * Other minor fixes 2015-01-22 Ivan Alonso * sngrep 0.2.1 released * Added SIP message color syntax options * Improved Call Flow refreshing logic * Fixed multiple locking problems with UI * Respect terminals colors (can be overridden with background dark option) * Improve offline pcap file reading to respect capture limit option * Swapped some keybindings * Multiple minor fixes and code refactoring 2014-12-04 Ivan Alonso * sngrep 0.2.0 released * Added Initial TLS Support (No compression, AES) * Added an option to resolve IP addresses * Added a new panel to compare two SIP messages * Fixed multiples memory leaks 2014-10-21 Ivan Alonso * sngrep 0.1.0 released * Remove ngrep compatibility. Force libpcap during compilation * Added TCP support * Allow saving selected dialogs into pcap * New UI design to maximize screen usage * Added a serach box in Call List window * Added Funtion Keys keybindings * Added command line arguments * Fixed some memory bugs 2013-04-22 Ivan Alonso * sngrep 0.0-alpha released. * Initial version, code is just a mere stub with global variables, without sanity checks... expect the worst. sngrep-1.8.2/Doxyfile000066400000000000000000002205121464272443000145360ustar00rootroot00000000000000# Doxyfile 1.7.6.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = sngrep # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "SIP Messages flow viewer" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = YES # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST = YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = src/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.h *.c *.php *.lua *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = *.lua=/usr/bin/lua2dox # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = YES # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = doc/footer.html # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # style sheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = YES # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the # mathjax.org site, so you can quickly see the result without installing # MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = YES # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = YES # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES sngrep-1.8.2/INSTALL000066400000000000000000000363321464272443000140660ustar00rootroot00000000000000Installation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the `make install' phase executed with root privileges. 5. Optionally, type `make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior `make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type `make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide `make distcheck', which can by used by developers to test that all other targets like `make install' and `make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. This is known as a "VPATH" build. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple `-arch' options to the compiler but only a single `-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the `lipo' tool if you have problems. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of `${prefix}', so that specifying just `--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to `configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the `make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, `make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of `${prefix}'. Any directories that were specified during `configure', but not in terms of `${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the `DESTDIR' variable. For example, `make install DESTDIR=/alternate/directory' will prepend `/alternate/directory' before all installation names. The approach of `DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of `${prefix}' at `configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of `make' will be. For these packages, running `./configure --enable-silent-rules' sets the default to minimal output, which can be overridden with `make V=1'; while running `./configure --disable-silent-rules' sets the default to verbose, which can be overridden with `make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put `/usr/ucb' early in your `PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in `/usr/bin'. So, if you need `/usr/ucb' in your `PATH', put it _after_ `/usr/bin'. On Haiku, software installed for all users goes in `/boot/common', not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf bug. Until the bug is fixed you can use this workaround: CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of all of the options to `configure', and exit. `--help=short' `--help=recursive' Print a summary of the options unique to this package's `configure', and exit. The `short' variant lists options used only in the top level, while the `recursive' variant lists options also present in any nested packages. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. `--no-create' `-n' Run the configure checks, but stop before creating any output files. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. sngrep-1.8.2/LICENSE000066400000000000000000001045131464272443000140370ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . sngrep-1.8.2/LICENSE.OpenSSL000066400000000000000000000051121464272443000153140ustar00rootroot00000000000000/* ==================================================================== * Copyright (c) 1998-2011 The OpenSSL Project. 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. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED 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 OpenSSL PROJECT OR * ITS 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. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ sngrep-1.8.2/Makefile.am000066400000000000000000000001151464272443000150570ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 SUBDIRS=src config doc tests EXTRA_DIST=bootstrap.sh sngrep-1.8.2/NEWS000066400000000000000000000000001464272443000135130ustar00rootroot00000000000000sngrep-1.8.2/README000066400000000000000000000127031464272443000137110ustar00rootroot00000000000000# sngrep [![Build Status](https://travis-ci.org/irontec/sngrep.svg)](https://travis-ci.org/irontec/sngrep) sngrep is a tool for displaying SIP calls message flows from terminal. It supports live capture to display realtime SIP packets and can also be used as PCAP viewer. [Some screenshots of sngrep](https://github.com/irontec/sngrep/wiki/Screenshots) ## Installing ### Binaries * [Debian / Ubuntu](https://github.com/irontec/sngrep/wiki/Installing-Binaries#debian--ubuntu) * [CentOS / RedHat / Fedora](https://github.com/irontec/sngrep/wiki/Installing-Binaries#centos--fedora--rhel) * [Alpine Linux](https://github.com/irontec/sngrep/wiki/Installing-Binaries#alpine-linux) * [Gentoo](https://github.com/irontec/sngrep/wiki/Installing-Binaries#gentoo) * [Arch](https://github.com/irontec/sngrep/wiki/Installing-Binaries#arch) * [OSX](https://github.com/irontec/sngrep/wiki/Installing-Binaries#osx) * [OpenWRT/LEDE](https://github.com/irontec/sngrep/wiki/Installing-Binaries#openwrtlede) ### Building from sources Prerequisites - libncurses5 - for UI, windows, panels. - libpcap - for capturing packets. - libssl - (optional) for TLS transport decrypt using OpenSSL and libcrypt - gnutls - (optional) for TLS transport decrypt using GnuTLS and libgcrypt - libncursesw5 - (optional) for UI, windows, panels (wide-character support) - libpcre - (optional) for Perl Compatible regular expressions - zlib - (optional) for gzip compressed pcap files On most systems the commands to build will be the standard autotools procedure: ./bootstrap.sh ./configure make make install (as root) You can pass following flags to ./configure to enable some features | configure flag | Feature | | ------------- | ------------- | | `--with-openssl` | Adds OpenSSL support to parse TLS captured messages (req. libssl) | | `--with-gnutls` | Adds GnuTLS support to parse TLS captured messages (req. gnutls) | | `--with-pcre`| Adds Perl Compatible regular expressions support in regexp fields | | `--with-zlib`| Enable zlib to support gzip compressed pcap files | | `--enable-unicode` | Adds Ncurses UTF-8/Unicode support (req. libncursesw5) | | `--enable-ipv6` | Enable IPv6 packet capture support. | | `--enable-eep` | Enable EEP packet send/receive support. | Instead of using autotools, sngrep could be build with CMake, e.g.: mkdir build && cd build cmake [] .. make make install (as root) You can pass following options to cmake to enable some features | CMake option | Feature | | ------------- | ------------- | | `-D WITH_OPENSSL=ON` | Adds OpenSSL support to parse TLS captured messages (req. libssl) | | `-D WITH_GNUTLS=ON` | Adds GnuTLS support to parse TLS captured messages (req. gnutls) | | `-D WITH_PCRE=ON`| Adds Perl Compatible regular expressions support in regexp fields | | `-D WITH_ZLIB=ON`| Enable zlib to support gzip compressed pcap files | | `-D WITH_UNICODE=ON` | Adds Ncurses UTF-8/Unicode support (req. libncursesw5) | | `-D USE_IPV6=ON` | Enable IPv6 packet capture support | | `-D USE_EEP=ON` | Enable EEP packet send/receive support | | `-D CPACK_GENERATOR=DEB` | `make package` builds a Debian package | | `-D CPACK_GENERATOR=RPM` | `make package` builds a RPM package | You can find [detailed instructions for some distributions](https://github.com/irontec/sngrep/wiki/Building) on wiki. ## Usage See `--help` for a list of available flags and their syntax For example, sngrep can be used to view SIP packets from a pcap file, also applying filters sngrep -I file.pcap host 192.168.1.1 and port 5060 or live capturing, saving packets to a new file sngrep -d eth0 -O save.pcap port 5060 and udp ## Configuration You can configure some options using [sngreprc](https://github.com/irontec/sngrep/wiki/Configuration) file ## Frequent Asked Questions Any feedback, request or question are welcomed at [#sngrep](https://kiwiirc.com/nextclient/irc.libera.chat/#sngrep) channel at irc.libera.chat See FAQ on [Github Wiki](https://github.com/irontec/sngrep/wiki#frequent-asked-questions) ## License sngrep - SIP Messages flow viewer Copyright (C) 2013-2018 Irontec S.L. 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. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. 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 . sngrep-1.8.2/README.md000077700000000000000000000000001464272443000151602READMEustar00rootroot00000000000000sngrep-1.8.2/TODO000066400000000000000000000106101464272443000135140ustar00rootroot00000000000000ToDo List ========= capture: * Improve Packet fragmentation Right now capture process only handle packet IP fragmentation and TCP segmentation when all parts come ordered. Also, packets being reassembled are stored in memory until fully assembled (which may never occur). * Improve TCP assembly We assume a packet is complete when PSH flag is set. In some cases this is not true and we have multiples SIP messages into what we consider an assembled packet. It's require to evaluate if a packet contains a SIP message every time we assemble a new segment. * Improve long run performance Right now, sngrep stores a lot of information in memory making it quite dangerous in long runs. We implemented a dialog limit to avoid being afraid of leaving an unattended sngrep, but it ended being also used by internal structures as a max size reference. While the limit is still a good idea, it will be nice to decouple it from the internals, allowing even unlimited captures if specified by the user at own risk. rtp: * Improve RTP stream creation We create new streams from SDP information every time we parse a SIP message with SDP content and the information doesn't match the last call stream. This should be improved to also consider the stream direction, so we compare with the last call stream with the same direction. This way, we would avoid drawing RTP arrows that have the same information * Improve RTCP parsing Right now only RTCP extended report is parsed and the information provided in flow panel doesn't seem to be useful. Determine what information will be interesting to display and parse it properly. * Remove RTCP stream arrows RTCP arrows in Call flow doesn't provide useful information. The RTCP information displayed in the preview panel could be displayed in the matching RTP arrow. pcap: * Sorting saved pakets Before creating a pcap, we sort packets by timestamp. When a lot of packets are handled (especially when RTP is captured) this can take A LOT of time. We should improve the sorting, allowing the save process to be canceled or allowing not to sort at all. * Allow saving HEP/EEP captured packets To create a full packet from HEP received packets its required to create the required Ethernet/TCP/UDP headers before dumping to pcap. Most of the information are part of HEP headers, other (like ethernet mac addresses) must be filled with dummy information. interface: * Change panels initialization Right now, all panels are initializated at the same, because each panel can only be invoked once (it is not possible to have two call details panel right now) * Add horizontal scrolling It should be nice to be able to scroll horizontaly (with unused right and left keys) in Call List and Call flow. * Interface resize When the terminal size changes, the ui is not properly redraw. It would be nice to handle KEY_RESIZE event and change all displayed panels. * Improve colors for white background terminals The best approach for colors should be use terminal defaults. Right now, white background terminals must set background dark option in order to see colors properly. This could be fixed implementing color themes. * Improve compatibility with IPv6 IPv6 packets are captured but IPv6 addresses can be 45 chars long, so current UI is not ready to display that kind of addresses * Improve Unicode support Even when compiling with libncruses wide-character support, we don't use the special fuctions that provide to write payload into the panels, making some characters to be displayed incorrectly. * Update keybinding display in help screen Most of the panels have a help window that display keybindings, but they are updated with the last keybiding mapping changes. * Create a loading dialog or include a Loading percentage in Call List Add the readed % of bytes from the pcap file next to the (Loading) label in Call List, or create a new Dialog when opening big pcaps that can be hidden to continue loading in background. sngrep-1.8.2/bootstrap.sh000077500000000000000000000016571464272443000154130ustar00rootroot00000000000000#!/bin/sh check_for_app() { $1 --version 2>&1 >/dev/null if [ $? != 0 ] then echo "Please install $1 and run bootstrap.sh again!" exit 1 fi } # On FreeBSD and OpenBSD, multiple autoconf/automake versions have different names. # On Linux, environment variables tell which one to use. case `uname -sr` in OpenBSD*) export AUTOCONF_VERSION=2.69 export AUTOMAKE_VERSION=1.15 ;; FreeBSD*) AUTOCONF_VERSION=2.69 AUTOMAKE_VERSION=1.15 export AUTOCONF_VERSION export AUTOMAKE_VERSION ;; *) ;; esac check_for_app autoconf check_for_app autoheader check_for_app automake check_for_app aclocal echo "Generating the configure script ..." aclocal autoconf autoheader automake --add-missing --copy 2>/dev/null exit 0 sngrep-1.8.2/compile000077500000000000000000000162451464272443000144140ustar00rootroot00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2012-10-14.11; # UTC # Copyright (C) 1999-2014 Free Software Foundation, Inc. # Written by Tom Tromey . # # 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, 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 . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: sngrep-1.8.2/config/000077500000000000000000000000001464272443000142735ustar00rootroot00000000000000sngrep-1.8.2/config/Makefile.am000066400000000000000000000000561464272443000163300ustar00rootroot00000000000000sysconfdir=@sysconfdir@ sysconf_DATA=sngreprc sngrep-1.8.2/config/sngreprc000066400000000000000000000064571464272443000160550ustar00rootroot00000000000000##----------------------------------------------------------------------------- ## sngreprc - sngrep configuration file ##----------------------------------------------------------------------------- ## This file stores sngrep configuration and it's totally optional ## ##----------------------------------------------------------------------------- ## Enable color on or off # set color off ## Use default foreground and background colors of your terminal # set background default ## Disable syntax highlighting # set syntax off ## Or enable branch/tag highlighting # set syntax.tag on # set syntax.branch on ##----------------------------------------------------------------------------- ## Uncomment to configure packet count capture limit (can't be disabled) # set capture.limit 50000 ## Default capture keyfile for TLS transport # set capture.keyfile /etc/ssl/key.pem ## Uncommnet to lookup hostnames from packets ips # set capture.lookup on ## Set default capture device # use special keyword 'any', a device name 'eth0' or a comma-separated list like 'eth1,eth3' # set capture.device any ## Set default dump file # set capture.outfile /tmp/last_capture.pcap ## Set size of pcap capture buffer in MB (default: 2) # set capture.buffer 2 ## Uncomment to enable parsing of captured HEP3 packets # set capture.eep on ##----------------------------------------------------------------------------- ## Default path in save dialog # set sngrep.savepath /tmp/sngrep-captures ##----------------------------------------------------------------------------- ## Change default scrolling in call list and call flow # set cl.scrollstep 20 # set cf.scrollstep 4 ## Disable exit prompt # set cl.noexitprompt off ## Or set its default button # set cl.defexitbutton 0/1 # Set default filter on startup # set filter.methods INVITE ##----------------------------------------------------------------------------- ## You can change the default number of columns in call list ## ## Set displayed columns in call list screen ## set cl.column{num} {field} ## ## You can optionally configure the column width using ## set cl.column{num}.width {num} ## ## Available columns fields are: ## - sipfrom ## - sipfromuser ## - sipto ## - siptouser ## - src ## - srchost ## - dst ## - dsthost ## - callid ## - xcallid ## - date ## - time ## - msgcnt ## - transport ## - state ## - convdur ## - totaldur ## ## Examples: # set cl.column0 sipfrom # set cl.column0.width 30 # set cl.column1 sipto # set cl.column2 msgcnt # set cl.column3 src # set cl.column4 dst # set cl.column4.width 22 # set cl.column5 starting # set cl.column5.width 15 # set cl.column6 state ##----------------------------------------------------------------------------- ## Default minimun size from Message payload in Call Flow panel # set cf.rawminwidth 40 ## Fixed raw preview size # set cf.rawfixedwidth 40 ## Set selected highlight mode in call flow (bold, reverse or reversebold) # set cf.highlight reverse ##----------------------------------------------------------------------------- ## Uncomment to display dialogs that does not start with a request method # set sip.noincomplete off ##----------------------------------------------------------------------------- ## Uncomment to define custom b_leg correlation header # set sip.xcid X-Call-ID|X-CID sngrep-1.8.2/configure.ac000066400000000000000000000255311464272443000153220ustar00rootroot00000000000000AC_PREREQ([2.59]) AC_INIT([sngrep], [1.8.2], [kaian@irontec.com], [sngrep], [http://www.irontec.com/]) AM_INIT_AUTOMAKE([1.9]) AC_CONFIG_HEADERS([src/config.h]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) m4_ifdef([AC_CONFIG_MACRO_DIRS], AC_CONFIG_MACRO_DIRS([m4]), m4_include([m4/sngrep.m4])) AC_COPYRIGHT("Irontec S.L.") # Define _GNU_SOURCE etc. m4_ifdef([AC_USE_SYSTEM_EXTENSIONS], [AC_USE_SYSTEM_EXTENSIONS]) # debug compilation AC_ARG_ENABLE(debug, AC_HELP_STRING(--enable-debug, [Debug compilation (Default = no)]), enable_debug=$enableval, enable_debug=no) if test "$enable_debug" = "yes" ; then CFLAGS="$CFLAGS -g -O0 -Wall -Werror -Wno-unused-but-set-variable" CXXFLAGS="$CXXFLAGS $CFLAGS" fi # Minimum checks for a C program :) AC_PROG_CC AC_PROG_CXX AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_EGREP AC_LANG(C) AM_PROG_CC_C_O # we might want to use this with zlib for compressed pcap support AC_CHECK_FUNCS([fopencookie]) ####################################################################### # Check for other REQUIRED libraries AC_CHECK_LIB([pthread], [pthread_create], [], [ AC_MSG_ERROR([ You need to have libpthread installed to compile sngrep.]) ]) AC_CHECK_LIB([pcap], [pcap_open_offline], [], [ AC_MSG_ERROR([ You need to have libpcap installed to compile sngrep.]) ]) AC_CHECK_HEADER([pcap.h], [], [ AC_MSG_ERROR([ You need to have libpcap development files installed to compile sngrep.]) ]) #### #### Ncurses Wide character support #### AC_ARG_ENABLE([unicode], AC_HELP_STRING([--enable-unicode], [Enable Ncurses Unicode support]), [AC_SUBST(UNICODE, $enableval)], [AC_SUBST(UNICODE, no)] ) AS_IF([test "x$enable_unicode" = "xyes"], [ # Ncurses with wide-character support AC_DEFINE([WITH_UNICODE], [], [Compile With Unicode compatibility]) SNGREP_CHECK_SCRIPT([ncursesw], [addnwstr], [WITH_UNICODE], "ncursesw6-config", SNGREP_CHECK_SCRIPT([ncursesw], [addnwstr], [WITH_UNICODE], "ncursesw5-config", SNGREP_CHECK_SCRIPT([ncurses], [addnwstr], [WITH_UNICODE], "ncurses5-config", SNGREP_CHECK_LIB([ncursesw], [addnwstr], [WITH_UNICODE], SNGREP_CHECK_LIB([ncursesw], [addnwstr], [WITH_UNICODE], SNGREP_CHECK_LIB([ncurses], [addnwstr], [WITH_UNICODE], )))))) AC_CHECK_LIB([panelw], [new_panel], [], [ AC_MSG_ERROR([ You need to have ncurses panelw library installed to compile sngrep.]) ]) AC_CHECK_LIB([formw], [new_form], [], [ AC_MSG_ERROR([ You need to have ncurses formsw library installed to compile sngrep.]) ]) AC_CHECK_LIB([menuw], [new_item], [], [ AC_MSG_ERROR([ You need to have ncurses menuw library installed to compile sngrep.]) ]) AC_SEARCH_LIBS([keyname], [tinfow], [], [ AC_MSG_ERROR([ You need to have ncurses tinfow library installed to compile sngrep.]) ]) ], [ # Ncurses without wide-character support AC_CHECK_HEADER([ncurses.h], [], [ AC_MSG_ERROR([ You need to have ncurses development files installed to compile sngrep.]) ]) AC_CHECK_LIB([ncurses], [initscr], [], [ AC_MSG_ERROR([ You need to have libncurses installed to compile sngrep.]) ]) AC_CHECK_LIB([panel], [new_panel], [], [ AC_MSG_ERROR([ You need to have ncurses panel library installed to compile sngrep.]) ]) AC_CHECK_LIB([form], [new_form], [], [ AC_MSG_ERROR([ You need to have ncurses forms library installed to compile sngrep.]) ]) AC_CHECK_LIB([menu], [new_item], [], [ AC_MSG_ERROR([ You need to have ncurses menu library installed to compile sngrep.]) ]) AC_SEARCH_LIBS([keyname], [tinfo], [], [ AC_MSG_ERROR([ You need to have ncurses tinfo library installed to compile sngrep.]) ]) ]) #### #### GnuTLS Support #### AC_ARG_WITH([gnutls], AS_HELP_STRING([--with-gnutls], [Enable SSL Support (TLS SIP Transport)]), [AC_SUBST(WITH_GNUTLS, $withval)], [AC_SUBST(WITH_GNUTLS, no)] ) AS_IF([test "x$WITH_GNUTLS" = "xyes"], [ m4_ifdef([PKG_CHECK_MODULES], [ PKG_CHECK_MODULES([LIBGNUTLS], [gnutls]) ], [ AC_CHECK_LIB([gnutls], [gnutls_init], [], [ AC_MSG_ERROR([ You need to have gnutls installed to compile sngrep]) ]) ]) AC_PATH_PROG([LIBGCRYPT_CONFIG],[libgcrypt-config],[no]) if test "x${LIBGCRYPT_CONFIG}" = "xno"; then AC_MSG_FAILURE([libgcrypt-config not found in PATH]) fi AC_CHECK_LIB( [gcrypt], [gcry_md_map_name], [LIBGCRYPT_CFLAGS="`${LIBGCRYPT_CONFIG} --cflags`" LIBGCRYPT_LIBS="`${LIBGCRYPT_CONFIG} --libs`" ], [AC_MSG_ERROR([ You need to have libgcrypt installed to compile sngrep])], [`${LIBGCRYPT_CONFIG} --libs --cflags`] ) AC_DEFINE([WITH_GNUTLS],[],[Compile With GnuTLS compatibility]) AC_SUBST(LIBGCRYPT_CFLAGS) AC_SUBST(LIBGCRYPT_LIBS) ], []) #### #### OpenSSL Support #### AC_ARG_WITH([openssl], AS_HELP_STRING([--with-openssl], [Enable SSL Support (TLS SIP Transport)]), [AC_SUBST(WITH_OPENSSL, $withval)], [AC_SUBST(WITH_OPENSSL, no)] ) AS_IF([test "x$WITH_OPENSSL" = "xyes"], [ AS_IF([test "x$WITH_GNUTLS" = "xyes"], [ AC_MSG_ERROR([ GnuTLS and OpenSSL can not be enabled at the same time ]) ], []) m4_ifdef([PKG_CHECK_MODULES], [ PKG_CHECK_MODULES([SSL], [libssl libcrypto]) ], [ AC_CHECK_LIB([ssl], [SSL_new], [], [ AC_MSG_ERROR([ You need to have libssl installed to compile sngrep]) ]) AC_CHECK_LIB([crypto], [EVP_get_cipherbyname], [], [ AC_MSG_ERROR([ You need to have libcrypto installed to compile sngrep]) ]) ]) AC_DEFINE([WITH_OPENSSL],[],[Compile With Openssl compatibility]) ], []) #### #### PCRE Support #### AC_ARG_WITH([pcre], AS_HELP_STRING([--with-pcre], [Enable Perl compatible regular expressions]), [AC_SUBST(WITH_PCRE, $withval)], [AC_SUBST(WITH_PCRE, no)] ) AS_IF([test "x$WITH_PCRE" = "xyes"], [ AC_CHECK_HEADER([pcre.h], [], [ AC_MSG_ERROR([ You need libpcre development files installed to compile with pcre support.]) ]) AC_CHECK_LIB([pcre], [pcre_exec], [], [ AC_MSG_ERROR([ You need libpcre library installed to compile with pcre support.]) ]) AC_DEFINE([WITH_PCRE],[],[Compile With Perl Compatible regular expressions support]) ], []) #### #### PCRE2 Support #### AC_ARG_WITH([pcre2], AS_HELP_STRING([--with-pcre2], [Enable Perl compatible regular expressions (v2)]), [AC_SUBST(WITH_PCRE2, $withval)], [AC_SUBST(WITH_PCRE2, no)] ) AS_IF([test "x$WITH_PCRE2" = "xyes"], [ AS_IF([test "x$WITH_PCRE" = "xyes"], [ AC_MSG_ERROR([libpcre-2 and libpcre-3 can not be enabled at the same time ]) ], []) AC_DEFINE([PCRE2_CODE_UNIT_WIDTH], [8], [Required for including pcre2.h]) AC_SUBST(PCRE2_CODE_UNIT_WIDTH, 8) m4_ifdef([PKG_CHECK_MODULES], [ PKG_CHECK_MODULES([PCRE2], [libpcre2-8]) ], [ AC_CHECK_HEADER([pcre2.h], [], [ AC_MSG_ERROR([ You need libpcre2 development files installed to compile with pcre support.]) ]) AC_CHECK_LIB([pcre2-8], [pcre2_compile_8], [], [ AC_MSG_ERROR([ You need libpcre2 library installed to compile with pcre support.]) ]) ]) AC_DEFINE([WITH_PCRE2],[],[Compile With Perl Compatible regular expressions support]) ], []) #### #### IPv6 Support #### AC_ARG_ENABLE([ipv6], AS_HELP_STRING([--enable-ipv6], [Enable IPv6 Support]), [AC_SUBST(USE_IPV6, $enableval)], [AC_SUBST(USE_IPV6, no)] ) AS_IF([test "x$USE_IPV6" = "xyes"], [ AC_CHECK_HEADERS([netinet/in.h netinet/ip6.h], [], [ AC_MSG_ERROR([ You dont seem to have ipv6 support (no ip6.h found).]) ], [ #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif ]) AC_DEFINE([USE_IPV6],[],[Compile With IPv6 support]) ], []) #### #### EEP Support #### AC_ARG_ENABLE([eep], AS_HELP_STRING([--enable-eep], [Enable EEP/HEP Support]), [AC_SUBST(USE_EEP, $enableval)], [AC_SUBST(USE_EEP, no)] ) AS_IF([test "x$USE_EEP" = "xyes"], [ AC_DEFINE([USE_EEP],[],[Compile With EEP support]) ], []) #### #### zlib Support #### AC_ARG_WITH([zlib], AS_HELP_STRING([--with-zlib], [Enable zlib to support gzip compressed pcap files]), [AC_SUBST(WITH_ZLIB, $withval)], [AC_SUBST(WITH_ZLIB, no)] ) AS_IF([test "x$WITH_ZLIB" = "xyes"], [ AC_CHECK_HEADER([zlib.h], [], [ AC_MSG_ERROR([ You need libz header files installed to compile with zlib support.]) ]) AC_CHECK_LIB([z], [gzdopen], [], [ AC_MSG_ERROR([ You need libz library installed to compile with zlib support.]) ]) AC_DEFINE([WITH_ZLIB],[],[Compile With zlib support]) ], []) # Conditional Source inclusion AM_CONDITIONAL([WITH_PCRE2], [test "x$WITH_PCRE2" = "xyes"]) AM_CONDITIONAL([WITH_GNUTLS], [test "x$WITH_GNUTLS" = "xyes"]) AM_CONDITIONAL([WITH_OPENSSL], [test "x$WITH_OPENSSL" = "xyes"]) AM_CONDITIONAL([USE_EEP], [test "x$USE_EEP" = "xyes"]) AM_CONDITIONAL([WITH_ZLIB], [test "x$WITH_ZLIB" = "xyes"]) ###################################################################### # Print Logo AC_ARG_ENABLE(logo, AC_HELP_STRING(--disable-logo, [Disable Irontec Logo from Summary menu]), [ enable_logo=$enableval], [ enable_logo=yes]) AS_IF([test "x$enable_logo" = "xyes"], [ echo '' echo ' ██╗██████╗ ██████╗ ███╗ ██╗████████╗███████╗ ██████╗' echo ' ██║██╔══██╗██╔═══██╗████╗ ██║╚══██╔══╝██╔════╝██╔════╝' echo ' ██║██████╔╝██║ ██║██╔██╗ ██║ ██║ █████╗ ██║ ' echo ' ██║██╔══██╗██║ ██║██║╚██╗██║ ██║ ██╔══╝ ██║ ' echo ' ██║██║ ██║╚██████╔╝██║ ╚████║ ██║ ███████╗╚██████╗' echo ' ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚══════╝ ╚═════╝' echo '' ]) AC_MSG_NOTICE AC_MSG_NOTICE( sngrep configure finished ) AC_MSG_NOTICE( ====================================================== ) AC_MSG_NOTICE( GnuTLS Support : ${WITH_GNUTLS} ) AC_MSG_NOTICE( OpenSSL Support : ${WITH_OPENSSL} ) AC_MSG_NOTICE( Unicode Support : ${UNICODE} ) AC_MSG_NOTICE( Perl Expressions Support : ${WITH_PCRE} ) AC_MSG_NOTICE( Perl Expressions Support (v2): ${WITH_PCRE2} ) AC_MSG_NOTICE( IPv6 Support : ${USE_IPV6} ) AC_MSG_NOTICE( EEP Support : ${USE_EEP} ) AC_MSG_NOTICE( Zlib Support : ${WITH_ZLIB} ) AC_MSG_NOTICE( ====================================================== ) AC_MSG_NOTICE AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([src/Makefile]) AC_CONFIG_FILES([config/Makefile]) AC_CONFIG_FILES([doc/Makefile]) AC_CONFIG_FILES([tests/Makefile]) AC_OUTPUT sngrep-1.8.2/doc/000077500000000000000000000000001464272443000135735ustar00rootroot00000000000000sngrep-1.8.2/doc/Makefile.am000066400000000000000000000000241464272443000156230ustar00rootroot00000000000000man_MANS = sngrep.8 sngrep-1.8.2/doc/footer.html000066400000000000000000000003431464272443000157570ustar00rootroot00000000000000
Generated on $datetime for $projectname  irontec
sngrep-1.8.2/doc/ironlogo.png000066400000000000000000000016101464272443000161270ustar00rootroot00000000000000PNG  IHDRasRGBbKGD pHYsUU? tIME &[DIDAT8˅[hSYŜͥFbEmU%bu{IVEt|V,$@/003_P6Ж!t`|PACVc&4;9'łu_- s#3ā&Ƈѯ/5q~﷥V|GD-Xx937$Z[׮8PZZ&tLWd0L 6G!]Sv佮bcp`__l@0r[63ͥG&9!T0䵆j:>J{sr棬lcO)LXY9F!$]Wa|(* ba(4 zSbIj7I(BU /]xzH断@UUpΝL|W\4m<;]HA[[pEK);~EJ^>7::v^UVU9i'OrOLMMs@ccƘ-?2MshSSS\7"]޽?ڶd2 5MCo_';vlWU5C+2Շ]|'!ٸq"Ԭk6 Ҥ,˯ciI$ñsfp`KwwB_^xr?9Bܹ\n,ˋ(^M,==w< >knΟXDon䳁ݻ߾ aI:k9IENDB`sngrep-1.8.2/doc/sngrep.8000066400000000000000000000125521464272443000151670ustar00rootroot00000000000000.\" Man page for the sngrep .\" .\" Copyright (c) 2013-2024 Ivan Alonso .\" Copyright (c) 2013-2024 Irontec S.L. .TH SNGREP 8 "Mar 2023" "sngrep 1.8.2" .SH NAME sngrep \- SIP Messages flow viewer .SH SYNOPSIS .B sngrep [-hVcivlkNqEr] [ -IO .I pcap_dump .B ] [ -d .I dev .B ] [ -l .I limit .B ] [ -k .I keyfile .B ] [-LH .I capture_url .B ] [ .I .B ] [ .I .B ] .SH DESCRIPTION sngrep is a terminal tool that groups SIP (Session Initiation Protocol) Messages by Call-Id, and displays them in arrow flows similar to the used in SIP RFCs. The aim of this tool is to make easier the process of learning or debugging SIP. It recognizes UDP, TCP and partially TLS SIP packets and understands bpf filter logic in the same way .B ngrep (8) and .B tcpdump (1) does. .SH OPTIONS .TP .I \-h Display help and usage information. .TP .I \-V Display version information. .TP .I \-c Only capture dialogs starting with an INVITE request. .TP .I \-r Store RTP packets payload in memory (enables saving RTP to pcap) .TP .I \-i Make match expression case insensitive. .TP .I \-v Invert match expression. .TP .I \-I pcap_dump Read packets from pcap file instead of network devices. This option can be used with bpf filters. .TP .I \-O pcap_dump Save all captured packets to a pcap file. This option can be used with bpf filters. When receiving a SIGUSR1 signal sngrep will reopen the pcap file in order to facilitate pcap file rotation. .TP .I -B buffer Change size of pcap capture buffer (default: 2MB) .TP .I \-d dev Use this capture device instead of default (\fIany\fP). Special keyword 'any', a device name like 'eth0' or a comma separated list like 'eth1,eth3'. This overrides the settings in the configuration file. .TP .I -k keyfile Use private keyfile to decrypt TLS packets. .TP .I -l limit Change default capture limit (20000 dialogs) Limit must be a numeric value above 1 and can not be disabled. This is both security measure to avoid unlimited memory usage and also used internally in sngrep to manage hash table sizes. .TP .I -R Remove oldest dialog when the capture limit has reached Although not recommended, this can be used to keep sngrep running during long times with some control over consumed memory. .TP .I -N Don't display sngrep interface, just capture .TP .I -q Don't print captured dialogs in no interface mode .TP .I -H Send captured packets to a HEP server (like Homer or another sngrep) Argument must be an IP address and port in the format: udp:A.B.C.D:PORT .TP .I -L Start a HEP server listening for packets Argument must be an IP address and port in the format: udp:A.B.C.D:PORT .TP .I -E Enable parsing of captured HEP3 packets. .TP .I match expression Match given expression in Messages' payload. If one request message matches the given expression, the following messages within the same dialog will be also captured. .TP .I bpf filter Selects a filter that specifies what packets will be parsed. If no \fIbpf filter\fP is given, all SIP packets seen on the selected interface or pcap file will be displayed. Otherwise, only packets for which \fIbpf filter\fP is `true' will be displayed. .SH Interface There are multiple windows to provide different information. Most of the program windows have a help dialog with a brief description and useful keybindings. .SH " Call List Window" .PP The first window that sngrep shows is Call List window and display the different SIP Call-Ids found in messages. The displayed columns depends on your terminal width and your custom configuration. You can move between dialogs with arrow keys and selected them using Spacebar. Selecting multiple dialogs will display all them in Call flow window and Call Raw window, and will allow to save only the selected message dialogs to a PCAP file. .SH " Call Flow Window" .PP This window will a flow diagram of the selected dialogs' messages. The selected message payload will be displayed in the right side of the window. You can move between messages using arrow keys and select them using Spacebar. Selecting multiple messages will display the Message Diff Window. .SH " Call Raw Window" .PP This window will display the selected dialog messages in plain text. It was designed to allow copying the messages payload easily. You can also save the displayed information to a text file from this screen. .SH " Column selection Window" .PP Columns displayed in Call List can be updated in this window. You can add or remove columns or change their order in the list. Additionally, you can save column state to be use in next sngrep execution. .SH " Message Diff Window" .PP This window will compare two messages. Right now the comparison is done searching each line in the other message, highlighting those not found exactly. You can reach this window by selecting two messages using Spacebar in Call Flow window .SH FILES Full paths below may vary between installations. .PP .I /etc/sngreprc .IP System\-wide configuration file. Some sngrep options can be overridden using this file. .PP .I ~/.sngreprc .IP User's configuration file. If this file is present, options will be override system\-wide configurations. .SH BUGS Please report bugs to the sngrep github project at http://github.com/irontec/sngrep Non-bug, non-feature-request general feedback should be sent to the author directly by email. .SH AUTHOR Written by Ivan Alonso [a.k.a. Kaian] . sngrep-1.8.2/m4/000077500000000000000000000000001464272443000133465ustar00rootroot00000000000000sngrep-1.8.2/m4/sngrep.m4000066400000000000000000000046041464272443000151120ustar00rootroot00000000000000# serial 100 # sngrep.m4: Custom autotools macros for sngrep # # @author Adam Duskett # @version 2017-05-25 # @license GNU General Public License 3.0 # # 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception, the you may copy, distribute and modify the # configure scripts that are the output of Autoconf when processing # the Macro. You need not follow the terms of the GNU General Public # License when using or distributing such scripts. # # SNGREP_CHECK_SCRIPT(LIBNAME, FUNCTION, DEFINE, CONFIG_SCRIPT, ELSE_PART) AC_DEFUN([SNGREP_CHECK_SCRIPT], [ if test ! -z "m4_toupper($SNGREP_[$1]_CONFIG_SCRIPT)"; then # to be used to set the path to *-config when cross-compiling sngrep_config_script_libs=$(m4_toupper($SNGREP_[$1]_CONFIG_SCRIPT) --libs 2> /dev/null) sngrep_config_script_cflags=$(m4_toupper($SNGREP_[$1]_CONFIG_SCRIPT) --cflags 2> /dev/null) else sngrep_config_script_libs=$([$4] --libs 2> /dev/null) sngrep_config_script_cflags=$([$4] --cflags 2> /dev/null) fi sngrep_script_success=no sngrep_save_LDFLAGS="$LDFLAGS" if test ! "x$sngrep_config_script_libs" = x; then LDFLAGS="$sngrep_config_script_libs $LDFLAGS" AC_CHECK_LIB([$1], [$2], [ AC_DEFINE([$3], 1, [The library is present.]) LIBS="$sngrep_config_script_libs $LIBS " CFLAGS="$sngrep_config_script_cflags $CFLAGS " sngrep_script_success=yes ], []) LDFLAGS="$sngrep_save_LDFLAGS" fi if test "x$sngrep_script_success" = xno; then [$5] fi ]) # SNGREP_CHECK_LIB(LIBNAME, FUNCTION, DEFINE, ELSE_PART) AC_DEFUN([SNGREP_CHECK_LIB], [ AC_CHECK_LIB([$1], [$2], [ AC_DEFINE([$3], 1, [The library is present.]) LIBS="-l[$1] $LIBS " ], [$4]) ]) sngrep-1.8.2/pkg/000077500000000000000000000000001464272443000136075ustar00rootroot00000000000000sngrep-1.8.2/pkg/apk/000077500000000000000000000000001464272443000143625ustar00rootroot00000000000000sngrep-1.8.2/pkg/apk/APKBUILD000066400000000000000000000017501464272443000155030ustar00rootroot00000000000000# Contributor: Francesco Colista # Maintainer: Francesco Colista pkgname=sngrep pkgver=1.8.2 pkgrel=0 pkgdesc="display SIP call message flows from a terminal" url="https://github.com/irontec/sngrep" arch="all !ppc64le" license="GPL-3.0-or-later" depends="sed" makedepends="autoconf automake ncurses-dev libpcap-dev pcre-dev libgcrypt-dev openssl-dev" subpackages="$pkgname-doc" source="$pkgname-$pkgver.tar.gz::https://github.com/irontec/sngrep/releases/download/v$pkgver/sngrep-$pkgver.tar.gz" prepare() { default_prepare ./bootstrap.sh } build() { ./configure \ --prefix=/usr \ --sysconfdir=/etc \ --with-openssl \ --with-pcre \ --disable-logo \ --enable-unicode \ --enable-ipv6 make } check() { make check } package() { make DESTDIR="$pkgdir/" install } sha512sums="d3aabe22a31ec5860ec80f94b6556d345d72574e552c4e92dfebdddeaaa5f69caf811fc2fa201ca7af24cabfcbdd530a4f50248ebf0381cef26390a78824d1af sngrep-1.4.10.tar.gz" sngrep-1.8.2/pkg/debian/000077500000000000000000000000001464272443000150315ustar00rootroot00000000000000sngrep-1.8.2/pkg/debian/changelog000066400000000000000000000110001464272443000166730ustar00rootroot00000000000000sngrep (1.8.2) experimental; urgency=low * sngrep 1.8.2 released -- Ivan Alonso Mon, 08 Jul 2024 09:27:47 +0200 sngrep (1.8.1) experimental; urgency=low * sngrep 1.8.1 released -- Ivan Alonso Mon, 08 Apr 2024 10:55:20 +0200 sngrep (1.8.0) experimental; urgency=low * sngrep 1.8.0 released -- Ivan Alonso Wed, 20 Dec 2023 10:34:52 +0100 sngrep (1.7.0) experimental; urgency=low * sngrep 1.7.0 released -- Ivan Alonso Fri, 31 Mar 2023 09:55:37 +0200 sngrep (1.6.0) experimental; urgency=low * sngrep 1.6.0 released -- Ivan Alonso Wed, 31 Aug 2022 11:56:30 +0200 sngrep (1.5.0) experimental; urgency=low * sngrep 1.5.0 released -- Ivan Alonso Tue, 26 Apr 2022 15:57:37 +0200 sngrep (1.4.10) experimental; urgency=low * sngrep 1.4.10 released -- Ivan Alonso Fri, 19 Nov 2021 11:00:24 +0100 sngrep (1.4.9) experimental; urgency=low * sngrep 1.4.9 released -- Ivan Alonso Thu, 20 May 2021 09:02:43 +0200 sngrep (1.4.8) experimental; urgency=low * sngrep 1.4.8 released -- Ivan Alonso Tue, 10 Oct 2020 10:46:10 +0200 sngrep (1.4.7) experimental; urgency=low * sngrep 1.4.7 released -- Ivan Alonso Thu, 21 May 2020 11:46:22 +0200 sngrep (1.4.6) experimental; urgency=low * sngrep 1.4.6 released -- Ivan Alonso Wed, 31 Oct 2018 17:19:27 +0100 sngrep (1.4.5) experimental; urgency=low * sngrep 1.4.5 released -- Ivan Alonso Fri, 22 Dec 2017 15:13:54 +0100 sngrep (1.4.4) experimental; urgency=low * sngrep 1.4.4 releaed -- Ivan Alonso Sun, 17 Sep 2017 10:51:57 +0200 sngrep (1.4.3) experimental; urgency=low * sngrep 1.4.3 releaed -- Ivan Alonso Wed, 10 May 2017 16:44:47 +0200 sngrep (1.4.2) experimental; urgency=low * sngrep 1.4.2 released -- Ivan Alonso Mon, 19 Dec 2016 14:00:26 +0100 sngrep (1.4.1) experimental; urgency=low * sngrep 1.4.1 released -- Ivan Alonso Fri, 28 Oct 2016 11:24:14 +0200 sngrep (1.4.0) experimental; urgency=low * sngrep 1.4.0 released -- Ivan Alonso Mon, 08 Aug 2016 10:57:07 +0200 sngrep (1.3.1) experimental; urgency=low * sngrep 1.3.1 released -- Ivan Alonso Thu, 28 Apr 2016 11:45:00 +0100 sngrep (1.3.0) experimental; urgency=low * sngrep 1.3.0 released -- Ivan Alonso Tue, 15 Mar 2016 13:14:00 +0100 sngrep (1.2.0) experimental; urgency=low * sngrep 1.2.0 released -- Ivan Alonso Thu, 10 Dec 2015 13:41:00 +0100 sngrep (1.1.0) experimental; urgency=low * sngrep 1.1.0 released -- Ivan Alonso Tue, 27 Oct 2015 16:01:00 +0100 sngrep (1.0.0) experimental; urgency=low * sngrep 1.3.0 released -- Ivan Alonso Tue, 06 Oct 2015 19:13:00 +0100 sngrep (0.4.2) experimental; urgency=low * sngrep 0.4.2 released -- Ivan Alonso Mon, 31 Aug 2015 11:21:00 +0100 sngrep (0.4.1) experimental; urgency=low * sngrep 0.4.1 released -- Ivan Alonso Fri, 10 Jul 2015 10:31:00 +0100 sngrep (0.4.0) experimental; urgency=low * sngrep 0.4.0 released -- Ivan Alonso Fri, 29 Jun 2015 15:18:00 +0100 sngrep (0.3.2) experimental; urgency=low * sngrep 0.3.2 released -- Ivan Alonso Fri, 29 May 2015 13:18:00 +0100 sngrep (0.3.1) experimental; urgency=low * sngrep 0.3.1 released -- Ivan Alonso Tue, 14 Apr 2015 19:22:00 +0100 sngrep (0.3.0) experimental; urgency=low * sngrep 0.3.0 released -- Ivan Alonso Wed, 04 Mar 2015 13:09:00 +0100 sngrep (0.2.2) experimental; urgency=low * sngrep 0.2.2 released -- Ivan Alonso Fri, 06 Feb 2015 17:54:24 +0100 sngrep (0.2.1) experimental; urgency=low * sngrep 0.2.1 released -- Ivan Alonso Thu, 22 Jan 2015 13:14:24 +0100 sngrep (0.2.0) experimental; urgency=low * sngrep 0.2.0 released -- Ivan Alonso Thu, 4 Dec 2014 20:39:04 +0100 sngrep (0.1.0) experimental; urgency=low * sngrep 0.1.0 released -- Ivan Alonso Sun, 21 Oct 2014 14:35:00 +0100 sngrep (0.0.1) experimental; urgency=low * sngrep 0.0.1 released -- Ivan Alonso Wed, 1 Oct 2014 20:39:04 +0100 sngrep-1.8.2/pkg/debian/compat000066400000000000000000000000021464272443000162270ustar00rootroot000000000000007 sngrep-1.8.2/pkg/debian/control000066400000000000000000000023501464272443000164340ustar00rootroot00000000000000Source: sngrep Section: comm Build-Depends: dh-autoreconf, debhelper, libpcap-dev, libncursesw5-dev, gnutls-dev, libgcrypt-dev, libpcre2-dev, libncurses5-dev, pkg-config Priority: optional Standards-Version: 3.9.6 Homepage: https://github.com/irontec/sngrep Maintainer: Ivan Alonso Package: sngrep Architecture: any Pre-Depends: ${misc:Pre-Depends} Depends: ${misc:Depends}, ${shlibs:Depends}, libpcap0.8, libncursesw5 | libncursesw6, libpcre2 | libpcre2-8-0 Description: Ncurses SIP Messages flow viewer sngrep displays SIP Messages grouped by Call-Id into flow diagrams. It can be used as an offline PCAP viewer or online capture using libpcap functions. . It supports SIP UDP, TCP and TLS transports (when each message is delivered in one packet). . You can also create new PCAP files from captures or displayed dialogs. . Package: sngrep-dbg Architecture: any Section: debug Priority: extra Depends: sngrep (= ${binary:Version}), ${misc:Depends} Description: Debugging symbols for sngrep SIP Messages flow viewer sngrep displays SIP Messages grouped by Call-Id into flow diagrams. It can be used as an offline PCAP viewer or online capture using libpcap functions. . This package contains the debugging sysmbols. . sngrep-1.8.2/pkg/debian/copyright000066400000000000000000000032341464272443000167660ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: sngrep Source: http://github.com/irontec/sngrep Files: * Copyright: 2013-2015 Ivan Alonso 2013-2015 Irontec SL. License: GPL-3+ with OpenSSL exception License: GPL-3+ with OpenSSL exception 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. . In addition, as a special exception, the author of this program gives permission to link the code of its release with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "OpenSSL". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your 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 . . On Debian systems, the full text of the GNU General Public License version 3 can be found in the file '/usr/share/common-licenses/GPL-3'. . sngrep-1.8.2/pkg/debian/rules000077500000000000000000000005401464272443000161100ustar00rootroot00000000000000#!/usr/bin/make -f override_dh_auto_configure: dh_auto_configure -- --with-gnutls --with-pcre --enable-unicode --enable-ipv6 --enable-eep override_dh_strip: dh_strip --dbg-package=sngrep-dbg override_dh_auto_install: dh_auto_install --destdir=debian/sngrep override_dh_installdocs: dh_installdocs --link-doc=sngrep %: dh $@ --with autoreconf sngrep-1.8.2/pkg/debian/source/000077500000000000000000000000001464272443000163315ustar00rootroot00000000000000sngrep-1.8.2/pkg/debian/source/format000066400000000000000000000000151464272443000175400ustar00rootroot000000000000003.0 (native) sngrep-1.8.2/pkg/debian/watch000066400000000000000000000004031464272443000160570ustar00rootroot00000000000000version=3 opts="filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/sngrep_$1\.tar\.gz/,pgpsigurlmangle=s/archive\/v(\d\S*)\.tar\.gz/releases\/download\/v$1\/v$1.tar.gz.asc/,uversionmangle=s/\-rc([\d]+)//" \ https://github.com/irontec/sngrep/tags .*/v?(\d\S*)\.tar\.gz sngrep-1.8.2/pkg/rpm/000077500000000000000000000000001464272443000144055ustar00rootroot00000000000000sngrep-1.8.2/pkg/rpm/SPECS/000077500000000000000000000000001464272443000152625ustar00rootroot00000000000000sngrep-1.8.2/pkg/rpm/SPECS/sngrep.spec000066400000000000000000000066221464272443000174420ustar00rootroot00000000000000%bcond_with openssl Summary: SIP Messages flow viewer Name: sngrep Version: 1.8.2 Release: 0%{?dist} License: GPLv3 Group: Applications/Engineering BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root Source: https://github.com/irontec/sngrep/releases/download/v%{version}/sngrep-%{version}.tar.gz URL: http://github.com/irontec/sngrep BuildRequires: ncurses-devel BuildRequires: make BuildRequires: libpcap-devel BuildRequires: pcre-devel BuildRequires: autoconf BuildRequires: automake BuildRequires: gcc %if %{with openssl} BuildRequires: openssl-devel %endif Requires: ncurses Requires: libpcap Requires: pcre %description sngrep displays SIP Messages grouped by Call-Id into flow diagrams. It can be used as an offline PCAP viewer or online capture using libpcap functions. It supports SIP UDP, TCP and TLS transports (when each message is delivered in one packet). You can also create new PCAP files from captures or displayed dialogs. %prep %setup -q %build ./bootstrap.sh %configure --with-pcre \ --enable-unicode \ --enable-ipv6 \ --enable-eep \ %{?_with_openssl} make %{?_smp_mflags} %install %{__make} install DESTDIR=%{buildroot} %files %doc README TODO COPYING ChangeLog %{_bindir}/* %{_mandir}/man8/* %config(noreplace) %{_sysconfdir}/* %clean %{__rm} -rf %{buildroot} %changelog * Mon Jul 08 2024 Ivan Alonso - 1.8.2 - Version 1.8.2 * Mon Apr 08 2024 Ivan Alonso - 1.8.1 - Version 1.8.1 * Wed Dec 20 2023 Ivan Alonso - 1.8.0 - Version 1.8.0 * Fri Mar 31 2023 Ivan Alonso - 1.7.0 - Version 1.7.0 * Wed Aug 31 2022 Ivan Alonso - 1.6.0 - Version 1.6.0 * Tue Apr 26 2022 Ivan Alonso - 1.5.0 - Version 1.5.0 * Fri Nov 19 2021 Ivan Alonso - 1.4.10 - Version 1.4.10 * Thu May 20 2021 Ivan Alonso - 1.4.9 - Version 1.4.9 * Tue Oct 10 2020 Ivan Alonso - 1.4.8 - Version 1.4.8 * Thu May 21 2020 Ivan Alonso - 1.4.7 - Version 1.4.7 * Wed Oct 31 2018 Ivan Alonso - 1.4.6 - Version 1.4.6 * Fri Dec 22 2017 Ivan Alonso - 1.4.5 - Version 1.4.5 * Sun Sep 17 2017 Ivan Alonso - 1.4.4 - Version 1.4.4 * Wed May 10 2017 Ivan Alonso - 1.4.3 - Version 1.4.3 * Fri Dec 19 2016 Ivan Alonso - 1.4.2 - Version 1.4.2 * Fri Oct 28 2016 Ivan Alonso - 1.4.1 - Version 1.4.1 * Tue Aug 23 2016 Ivan Alonso - 1.4.0 - Version 1.4.0 * Mon Mar 28 2016 Ivan Alonso - 1.3.1 - Version 1.3.1 * Tue Mar 15 2016 Ivan Alonso - 1.3.0 - Version 1.3.0 * Thu Dec 10 2015 Ivan Alonso - 1.2.0 - Version 1.2.0 * Wed Oct 28 2015 Ivan Alonso - 1.1.0 - Version 1.1.0 * Tue Oct 06 2015 Ivan Alonso - 1.0.0 - Version 1.0.0 * Mon Aug 31 2015 Ivan Alonso - 0.4.2 - Version 0.4.2 * Tue Jul 07 2015 Ivan Alonso - 0.4.1 - Version 0.4.1 * Mon Jun 29 2015 Ivan Alonso - 0.4.0 - Version 0.4.0 * Tue Apr 14 2015 Ivan Alonso - 0.3.1 - Version 0.3.1 * Wed Mar 04 2015 Ivan Alonso - 0.3.0 - First RPM version of sngrep sngrep-1.8.2/src/000077500000000000000000000000001464272443000136155ustar00rootroot00000000000000sngrep-1.8.2/src/Makefile.am000066400000000000000000000020561464272443000156540ustar00rootroot00000000000000AUTOMAKE_OPTIONS=subdir-objects bin_PROGRAMS=sngrep sngrep_SOURCES=capture.c sngrep_CFLAGS= sngrep_LDADD= if USE_EEP sngrep_SOURCES+=capture_eep.c endif if WITH_GNUTLS sngrep_SOURCES+=capture_gnutls.c sngrep_CFLAGS+=$(LIBGNUTLS_CFLAGS) $(LIBGCRYPT_CFLAGS) sngrep_LDADD+=$(LIBGNUTLS_LIBS) $(LIBGCRYPT_LIBS) endif if WITH_OPENSSL sngrep_SOURCES+=capture_openssl.c sngrep_CFLAGS+=$(SSL_CFLAGS) sngrep_LDADD+=$(SSL_LIBS) endif if WITH_PCRE2 sngrep_CFLAGS+=$(PCRE2_CFLAGS) sngrep_LDADD+=$(PCRE2_LIBS) endif if WITH_ZLIB sngrep_CFLAGS+=$(ZLIB_CFLAGS) sngrep_LDADD+=$(ZLIB_LIBS) endif sngrep_SOURCES+=address.c packet.c sip.c sip_call.c sip_msg.c sip_attr.c main.c sngrep_SOURCES+=option.c group.c filter.c keybinding.c media.c setting.c rtp.c sngrep_SOURCES+=util.c hash.c vector.c curses/ui_panel.c curses/scrollbar.c sngrep_SOURCES+=curses/ui_manager.c curses/ui_call_list.c curses/ui_call_flow.c curses/ui_call_raw.c sngrep_SOURCES+=curses/ui_stats.c curses/ui_filter.c curses/ui_save.c curses/ui_msg_diff.c sngrep_SOURCES+=curses/ui_column_select.c curses/ui_settings.c sngrep-1.8.2/src/address.c000066400000000000000000000064111464272443000154100ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file address.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in address.h * */ #include "config.h" #include "address.h" #include "util.h" #include #include #include #include #include bool addressport_equals(address_t addr1, address_t addr2) { return addr1.port == addr2.port && !strcmp(addr1.ip, addr2.ip); } bool address_equals(address_t addr1, address_t addr2) { return !strcmp(addr1.ip, addr2.ip); } bool address_is_local(address_t addr) { //! Local devices pointer static pcap_if_t *devices = 0; pcap_if_t *dev; pcap_addr_t *da; char errbuf[PCAP_ERRBUF_SIZE]; struct sockaddr_in *ipaddr; #ifdef USE_IPV6 struct sockaddr_in6 *ip6addr; #endif char ip[ADDRESSLEN]; // Get all network devices if (!devices) { // Get Local devices addresses pcap_findalldevs(&devices, errbuf); } for (dev = devices; dev; dev = dev->next) { for (da = dev->addresses; da ; da = da->next) { // Ingore empty addresses if (!da->addr) continue; // Initialize variables memset(ip, 0, sizeof(ip)); // Get address representation switch (da->addr->sa_family) { case AF_INET: ipaddr = (struct sockaddr_in *) da->addr; inet_ntop(AF_INET, &ipaddr->sin_addr, ip, sizeof(ip)); break; #ifdef USE_IPV6 case AF_INET6: ip6addr = (struct sockaddr_in6 *) da->addr; inet_ntop(AF_INET, &ip6addr->sin6_addr, ip, sizeof(ip)); break; #endif } // Check if this address matches if (!strcmp(addr.ip, ip)) { return true; } } } return false; } address_t address_from_str(const char *ipport) { address_t ret = {}; char scanipport[256]; char address[ADDRESSLEN + 1]; int port; if (!ipport || strlen(ipport) > ADDRESSLEN + 6) return ret; strncpy(scanipport, ipport, sizeof(scanipport)); if (sscanf(scanipport, "%" STRINGIFY(ADDRESSLEN) "[^:]:%d", address, &port) == 2) { strncpy(ret.ip, address, sizeof(ret.ip)); ret.port = port; } return ret; } sngrep-1.8.2/src/address.h000066400000000000000000000052261464272443000154200ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file address.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage network addresses * * Multiple structures contain source and destination address. * This file contains the unification of all sngrep address containers. * */ #ifndef __SNGREP_ADDRESS_H #define __SNGREP_ADDRESS_H #include #include #include //! Address string Length #ifdef USE_IPV6 #ifdef INET6_ADDRSTRLEN #define ADDRESSLEN INET6_ADDRSTRLEN #else #define ADDRESSLEN 46 #endif #else #define ADDRESSLEN INET_ADDRSTRLEN #endif //! Shorter declaration of address structure typedef struct address address_t; /** * @brief Network address */ struct address { //! IP address char ip[ADDRESSLEN]; //! Port uint16_t port; }; /** * @brief Check if two address are equal (including port) * * @param addr1 Address structure * @param addr2 Address structure * @return true if addresses contain the IP address, false otherwise */ bool addressport_equals(address_t addr1, address_t addr2); /** * @brief Check if two address are equal (ignoring port) * * @param addr1 Address structure * @param addr2 Address structure * @return true if addresses contain the same data, false otherwise */ bool address_equals(address_t addr1, address_t addr2); /** * @brief Check if a given IP address belongs to a local device * * @param address Address structure * @return true if address is local, false otherwise */ bool address_is_local(address_t addr); /** * @brief Convert string IP:PORT to address structure * * @param string in format IP:PORT * @return address structure */ address_t address_from_str(const char *ipport); #endif /* __SNGREP_ADDRESS_H */ sngrep-1.8.2/src/capture.c000066400000000000000000001224021464272443000154250ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file capture.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in pcap.h * * sngrep can parse a pcap file to display call flows. * This file include the functions that uses libpcap to do so. * */ #include "config.h" #include #include #include #include #include #include "capture.h" #ifdef USE_EEP #include "capture_eep.h" #endif #ifdef WITH_GNUTLS #include "capture_gnutls.h" #endif #ifdef WITH_OPENSSL #include "capture_openssl.h" #endif #ifdef WITH_ZLIB #include #endif #include "sip.h" #include "rtp.h" #include "setting.h" #include "util.h" #if __STDC_VERSION__ >= 201112L && __STDC_NO_ATOMICS__ != 1 // modern C with atomics #include typedef atomic_int signal_flag_type; #else // no atomics available typedef volatile sig_atomic_t signal_flag_type; #endif // Capture information capture_config_t capture_cfg = { 0 }; signal_flag_type sigusr1_received = 0; void sigusr1_handler(int signum) { sigusr1_received = 1; } #if defined(WITH_ZLIB) static ssize_t gzip_cookie_write(void *cookie, const char *buf, size_t size) { return gzwrite((gzFile)cookie, (voidpc)buf, size); } static ssize_t gzip_cookie_read(void *cookie, char *buf, size_t size) { return gzread((gzFile)cookie, (voidp)buf, size); } static int gzip_cookie_close(void *cookie) { return gzclose((gzFile)cookie); } #endif void capture_init(size_t limit, bool rtp_capture, bool rotate, size_t pcap_buffer_size) { capture_cfg.limit = limit; capture_cfg.pcap_buffer_size = pcap_buffer_size; capture_cfg.rtp_capture = rtp_capture; capture_cfg.rotate = rotate; capture_cfg.paused = 0; capture_cfg.sources = vector_create(1, 1); // set up SIGUSR1 signal handler for pcap dump file rotation // the handler will be served by any of the running threads // so we just set a flag and check it in dump_packet // so it is only acted upon before then next packed will be dumped if (signal(SIGUSR1, sigusr1_handler) == SIG_ERR) exit(EXIT_FAILURE); // Fixme if (setting_has_value(SETTING_CAPTURE_STORAGE, "none")) { capture_cfg.storage = CAPTURE_STORAGE_NONE; } else if (setting_has_value(SETTING_CAPTURE_STORAGE, "memory")) { capture_cfg.storage = CAPTURE_STORAGE_MEMORY; } else if (setting_has_value(SETTING_CAPTURE_STORAGE, "disk")) { capture_cfg.storage = CAPTURE_STORAGE_DISK; } #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) // Parse TLS Server setting capture_cfg.tlsserver = address_from_str(setting_get_value(SETTING_CAPTURE_TLSSERVER)); #endif // Initialize calls lock pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); #if defined(PTHREAD_MUTEX_RECURSIVE) || defined(__FreeBSD__) || defined(BSD) || defined (__OpenBSD__) || defined(__DragonFly__) pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); #else pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); #endif pthread_mutex_init(&capture_cfg.lock, &attr); } void capture_deinit() { // Close pcap handler capture_close(); // Deallocate vectors vector_set_destroyer(capture_cfg.sources, vector_generic_destroyer); vector_destroy(capture_cfg.sources); // Remove capture mutex pthread_mutex_destroy(&capture_cfg.lock); } int capture_online(const char *dev) { capture_info_t *capinfo; //! Error string char errbuf[PCAP_ERRBUF_SIZE]; // Create a new structure to handle this capture source if (!(capinfo = sng_malloc(sizeof(capture_info_t)))) { fprintf(stderr, "Can't allocate memory for capture data!\n"); return 1; } // Try to find capture device information if (pcap_lookupnet(dev, &capinfo->net, &capinfo->mask, errbuf) == -1) { capinfo->net = 0; capinfo->mask = 0; } // Open capture device capinfo->handle = pcap_create(dev, errbuf); if (capinfo->handle == NULL) { fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf); return 2; } if (pcap_set_snaplen(capinfo->handle, MAXIMUM_SNAPLEN) != 0) { fprintf(stderr, "Error setting snaplen on %s: %s\n", dev, pcap_geterr(capinfo->handle)); return 2; } if (pcap_set_promisc(capinfo->handle, 1) != 0) { fprintf(stderr, "Error setting promiscuous mode on %s: %s\n", dev, pcap_geterr(capinfo->handle)); return 2; } if (pcap_set_timeout(capinfo->handle, 1000) != 0) { fprintf(stderr, "Error setting capture timeout on %s: %s\n", dev, pcap_geterr(capinfo->handle)); return 2; } if (pcap_set_buffer_size(capinfo->handle, capture_cfg.pcap_buffer_size * 1024 * 1024) != 0) { fprintf(stderr, "Error setting capture buffer size on %s: %s\n", dev, pcap_geterr(capinfo->handle)); return 2; } if (pcap_activate(capinfo->handle) < 0) { fprintf(stderr, "Couldn't activate capture: %s\n", pcap_geterr(capinfo->handle)); return 2; } // Set capture thread function capinfo->capture_fn = capture_thread; // Store capture device capinfo->device = dev; capinfo->ispcap = true; // Get datalink to parse packets correctly capinfo->link = pcap_datalink(capinfo->handle); // Check linktypes sngrep knowns before start parsing packets if ((capinfo->link_hl = datalink_size(capinfo->link)) == -1) { fprintf(stderr, "Unable to handle linktype %d\n", capinfo->link); return 3; } // Create Vectors for IP and TCP reassembly capinfo->tcp_reasm = vector_create(0, 10); capinfo->ip_reasm = vector_create(0, 10); // Add this capture information as packet source capture_add_source(capinfo); return 0; } int capture_offline(const char *infile) { capture_info_t *capinfo; FILE *fstdin; // Error text (in case of file open error) char errbuf[PCAP_ERRBUF_SIZE]; // Create a new structure to handle this capture source if (!(capinfo = sng_malloc(sizeof(capture_info_t)))) { fprintf(stderr, "Can't allocate memory for capture data!\n"); return 1; } // Check if file is standard input if (strlen(infile) == 1 && *infile == '-') { infile = "/dev/stdin"; } // Set capture thread function capinfo->capture_fn = capture_thread; // Set capture input file capinfo->infile = infile; capinfo->ispcap = true; // Open PCAP file if ((capinfo->handle = pcap_open_offline(infile, errbuf)) == NULL) { #if defined(HAVE_FOPENCOOKIE) && defined(WITH_ZLIB) // we can't directly parse the file as pcap - could it be gzip compressed? gzFile zf = gzopen(infile, "rb"); if (!zf) goto openerror; static cookie_io_functions_t cookiefuncs = { gzip_cookie_read, NULL, NULL, gzip_cookie_close }; // reroute the file access functions // use the gzip read+close functions when accessing the file FILE *fp = fopencookie(zf, "r", cookiefuncs); if (!fp) { gzclose(zf); goto openerror; } if ((capinfo->handle = pcap_fopen_offline(fp, errbuf)) == NULL) { openerror: fprintf(stderr, "Couldn't open pcap file %s: %s\n", infile, errbuf); return 1; } } #else fprintf(stderr, "Couldn't open pcap file %s: %s\n", infile, errbuf); return 1; } #endif // Reopen tty for ncurses after pcap have used stdin if (!strncmp(infile, "/dev/stdin", 10)) { if (!(fstdin = freopen("/dev/tty", "r", stdin))) { fprintf(stderr, "Failed to reopen tty while using stdin for capture."); return 1; } } // Get datalink to parse packets correctly capinfo->link = pcap_datalink(capinfo->handle); // Check linktypes sngrep knowns before start parsing packets if ((capinfo->link_hl = datalink_size(capinfo->link)) == -1) { fprintf(stderr, "Unable to handle linktype %d\n", capinfo->link); return 3; } // Create Vectors for IP and TCP reassembly capinfo->tcp_reasm = vector_create(0, 10); capinfo->ip_reasm = vector_create(0, 10); // Add this capture information as packet source capture_add_source(capinfo); return 0; } void parse_packet(u_char *info, const struct pcap_pkthdr *header, const u_char *packet) { // Capture info capture_info_t *capinfo = (capture_info_t *) info; // UDP header data struct udphdr *udp; // UDP header size uint16_t udp_off; // TCP header data struct tcphdr *tcp; // TCP header size uint16_t tcp_off; // Packet data u_char data[MAX_CAPTURE_LEN]; // Packet payload data u_char *payload = NULL; // Whole packet size uint32_t size_capture = header->caplen; // Packet payload size uint32_t size_payload = size_capture - capinfo->link_hl; // Captured packet info packet_t *pkt; #ifdef USE_EEP // Captured HEP3 packet info packet_t *pkt_hep3; #endif // Ignore packets while capture is paused if (capture_paused()) return; // Check if we have reached capture limit if (capture_cfg.limit && sip_calls_count() >= capture_cfg.limit) { // If capture rotation is disabled, just skip this packet if (!capture_cfg.rotate) { return; } } // Check maximum capture length if (header->caplen > MAX_CAPTURE_LEN) return; // Copy packet payload memcpy(data, packet, header->caplen); // Check if we have a complete IP packet if (!(pkt = capture_packet_reasm_ip(capinfo, header, data, &size_payload, &size_capture))) return; // Only interested in UDP packets if (pkt->proto == IPPROTO_UDP) { // Get UDP header udp = (struct udphdr *)((u_char *)(data) + (size_capture - size_payload)); udp_off = sizeof(struct udphdr); // Set packet ports pkt->src.port = htons(udp->uh_sport); pkt->dst.port = htons(udp->uh_dport); // Remove UDP Header from payload size_payload -= udp_off; if ((int32_t)size_payload < 0) size_payload = 0; // Remove TCP Header from payload payload = (u_char *) (udp) + udp_off; #ifdef USE_EEP // check for HEP3 header and parse payload if(setting_enabled(SETTING_CAPTURE_EEP)) { pkt_hep3 = capture_eep_receive_v3(payload, size_payload); if (pkt_hep3) { packet_destroy(pkt); pkt = pkt_hep3; } else { // Complete packet with Transport information packet_set_type(pkt, PACKET_SIP_UDP); packet_set_payload(pkt, payload, size_payload); } } else { #endif // Complete packet with Transport information packet_set_type(pkt, PACKET_SIP_UDP); packet_set_payload(pkt, payload, size_payload); #ifdef USE_EEP } #endif } else if (pkt->proto == IPPROTO_TCP) { // Get TCP header tcp = (struct tcphdr *)((u_char *)(data) + (size_capture - size_payload)); tcp_off = (tcp->th_off * 4); // Set packet ports pkt->src.port = htons(tcp->th_sport); pkt->dst.port = htons(tcp->th_dport); // Get actual payload size size_payload -= tcp_off; if ((int32_t)size_payload < 0) size_payload = 0; // Get payload start payload = (u_char *)(tcp) + tcp_off; // Complete packet with Transport information packet_set_type(pkt, PACKET_SIP_TCP); packet_set_payload(pkt, payload, size_payload); // Create a structure for this captured packet if (!(pkt = capture_packet_reasm_tcp(capinfo, pkt, tcp, payload, size_payload))) return; #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) // Check if packet is TLS if (capture_cfg.keyfile) { tls_process_segment(pkt, tcp); } #endif // Check if packet is WS or WSS capture_ws_check_packet(pkt); } else { // Not handled protocol packet_destroy(pkt); return; } // Avoid parsing from multiples sources. // Avoid parsing while screen in being redrawn capture_lock(); // Check if we can handle this packet if (capture_packet_parse(pkt) == 0) { #ifdef USE_EEP // Send this packet through eep capture_eep_send(pkt); #endif // Store this packets in output file capture_dump_packet(pkt);; // If storage is disabled, delete frames payload if (capture_cfg.storage == 0) { packet_free_frames(pkt); } // Allow Interface refresh and user input actions capture_unlock(); return; } // Not an interesting packet ... packet_destroy(pkt); // Allow Interface refresh and user input actions capture_unlock(); } packet_t * capture_packet_reasm_ip(capture_info_t *capinfo, const struct pcap_pkthdr *header, u_char *packet, uint32_t *size, uint32_t *caplen) { // IP header data struct ip *ip4; #ifdef USE_IPV6 // IPv6 header data struct ip6_hdr *ip6; #endif // IP version uint32_t ip_ver; // IP protocol uint8_t ip_proto; // IP header size uint32_t ip_hl = 0; // Fragment offset uint16_t ip_off = 0; // IP content len uint16_t ip_len = 0; // Fragmentation flag uint16_t ip_frag = 0; // Fragmentation identifier uint32_t ip_id = 0; // Fragmentation offset uint16_t ip_frag_off = 0; //! Source Address address_t src = { }; //! Destination Address address_t dst = { }; //! Common interator for vectors vector_iter_t it; //! Packet containers packet_t *pkt; //! Storage for IP frame frame_t *frame; uint32_t len_data = 0; //! Link + Extra header size uint16_t link_hl = capinfo->link_hl; #ifdef USE_IPV6 struct ip6_frag *ip6f; #endif // Skip VLAN header if present if (capinfo->link == DLT_EN10MB) { struct ether_header *eth = (struct ether_header *) packet; if (ntohs(eth->ether_type) == ETHERTYPE_8021Q) { link_hl += 4; } } #ifdef SLL_HDR_LEN if (capinfo->link == DLT_LINUX_SLL) { struct sll_header *sll = (struct sll_header *) packet; if (ntohs(sll->sll_protocol) == ETHERTYPE_8021Q) { link_hl += 4; } } #endif // Skip NFLOG header if present if (capinfo->link == DLT_NFLOG) { // Parse NFLOG TLV headers while (link_hl + 8 <= *caplen) { nflog_tlv_t *tlv = (nflog_tlv_t *) (packet + link_hl); if (!tlv) break; if (tlv->tlv_type == NFULA_PAYLOAD) { link_hl += 4; break; } if (tlv->tlv_length >= 4) { link_hl += ((tlv->tlv_length + 3) & ~3); /* next TLV aligned to 4B */ } } } // Check capture length contains more than link layer if (link_hl >= *caplen) return NULL; while (*size >= sizeof(struct ip)) { // Get IP header ip4 = (struct ip *) (packet + link_hl); #ifdef USE_IPV6 // Get IPv6 header ip6 = (struct ip6_hdr *) (packet + link_hl); #endif // Get IP version ip_ver = ip4->ip_v; switch (ip_ver) { case 4: ip_hl = ip4->ip_hl * 4; ip_proto = ip4->ip_p; ip_off = ntohs(ip4->ip_off); ip_len = ntohs(ip4->ip_len); ip_frag = ip_off & (IP_MF | IP_OFFMASK); ip_frag_off = (ip_frag) ? (ip_off & IP_OFFMASK) * 8 : 0; ip_id = ntohs(ip4->ip_id); inet_ntop(AF_INET, &ip4->ip_src, src.ip, sizeof(src.ip)); inet_ntop(AF_INET, &ip4->ip_dst, dst.ip, sizeof(dst.ip)); break; #ifdef USE_IPV6 case 6: ip_hl = sizeof(struct ip6_hdr); ip_proto = ip6->ip6_nxt; ip_len = ntohs(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen) + ip_hl; if (ip_proto == IPPROTO_FRAGMENT) { ip_frag = 1; ip6f = (struct ip6_frag *) (packet + link_hl + ip_hl); ip_frag_off = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK); ip_id = ntohl(ip6f->ip6f_ident); } inet_ntop(AF_INET6, &ip6->ip6_src, src.ip, sizeof(src.ip)); inet_ntop(AF_INET6, &ip6->ip6_dst, dst.ip, sizeof(dst.ip)); break; #endif default: return NULL; } // Fixup VSS trailer in ethernet packets *caplen = link_hl + ip_len; // Remove IP Header length from payload *size = *caplen - link_hl - ip_hl; if (ip_proto == IPPROTO_IPIP) { // The payload is an incapsulated IP packet (IP-IP tunnel) // so we simply skip the "outer" IP header and repeat. // NOTE: this will break IP reassembly if the "outer" // packet is fragmented. link_hl += ip_hl; } else { break; } } // Check maximum capture len if (*caplen > MAX_CAPTURE_LEN) return NULL; // Check frame has at least IP header length if (ip_ver == 4 && header->caplen < link_hl + sizeof(struct ip)) return NULL; #ifdef USE_IPV6 if (ip_ver == 6 && header->caplen < link_hl + sizeof(struct ip6_hdr)) return NULL; #endif // If no fragmentation if (ip_frag == 0) { // Just create a new packet with given network data pkt = packet_create(ip_ver, ip_proto, src, dst, ip_id); packet_add_frame(pkt, header, packet); return pkt; } // Look for another packet with same id in IP reassembly vector it = vector_iterator(capinfo->ip_reasm); while ((pkt = vector_iterator_next(&it))) { if (addressport_equals(pkt->src, src) && addressport_equals(pkt->dst, dst) && pkt->ip_id == ip_id) { break; } } // If we already have this packet stored, append this frames to existing one if (pkt) { packet_add_frame(pkt, header, packet); } else { // Add To the possible reassembly list pkt = packet_create(ip_ver, ip_proto, src, dst, ip_id); packet_add_frame(pkt, header, packet); vector_append(capinfo->ip_reasm, pkt); } // Add this IP content length to the total captured of the packet pkt->ip_cap_len += ip_len - ip_hl; #ifdef USE_IPV6 if (ip_ver == 6 && ip_frag) { pkt->ip_cap_len -= sizeof(struct ip6_frag); } #endif // Calculate how much data we need to complete this packet // The total packet size can only be known using the last fragment of the packet // where 'No more fragments is enabled' and it's calculated based on the // last fragment offset if (ip_ver == 4 && (ip_off & IP_MF) == 0) { pkt->ip_exp_len = ip_frag_off + ip_len - ip_hl; } #ifdef USE_IPV6 if (ip_ver == 6 && ip_frag && (ip6f->ip6f_offlg & htons(0x01)) == 0) { pkt->ip_exp_len = ip_frag_off + ip_len - ip_hl - sizeof(struct ip6_frag); } #endif // If we have the whole packet (captured length is expected length) if (pkt->ip_cap_len == pkt->ip_exp_len) { // TODO Dont check the flag, check the holes // Calculate assembled IP payload data it = vector_iterator(pkt->frames); while ((frame = vector_iterator_next(&it))) { switch (ip_ver) { case 4: { struct ip *frame_ip = (struct ip *) (frame->data + link_hl); len_data += ntohs(frame_ip->ip_len) - frame_ip->ip_hl * 4; break; } #ifdef USE_IPV6 case 6: { struct ip6_hdr *frame_ip6 = (struct ip6_hdr *) (frame->data + link_hl); len_data += ntohs(frame_ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); break; } #endif default: break; } } // Check packet content length if (len_data > MAX_CAPTURE_LEN) return NULL; // Initialize memory for the assembly packet memset(packet, 0, link_hl + ip_hl + len_data); it = vector_iterator(pkt->frames); while ((frame = vector_iterator_next(&it))) { switch (ip_ver) { case 4: { // Get IP header struct ip *frame_ip = (struct ip *) (frame->data + link_hl); memcpy(packet + link_hl + ip_hl + (ntohs(frame_ip->ip_off) & IP_OFFMASK) * 8, frame->data + link_hl + frame_ip->ip_hl * 4, ntohs(frame_ip->ip_len) - frame_ip->ip_hl * 4); } break; #ifdef USE_IPV6 case 6: { struct ip6_hdr *frame_ip6 = (struct ip6_hdr*)(frame->data + link_hl); struct ip6_frag *frame_ip6f = (struct ip6_frag *)(frame->data + link_hl + ip_hl); uint16_t frame_ip_frag_off = ntohs(frame_ip6f->ip6f_offlg & IP6F_OFF_MASK); memcpy(packet + link_hl + ip_hl + sizeof(struct ip6_frag) + frame_ip_frag_off, frame->data + link_hl + ip_hl + sizeof (struct ip6_frag), ntohs(frame_ip6->ip6_ctlun.ip6_un1.ip6_un1_plen)); pkt->proto = frame_ip6f->ip6f_nxt; } break; #endif default: break; } } *caplen = link_hl + ip_hl + len_data; #ifdef USE_IPV6 if (ip_ver == 6) { *caplen += sizeof(struct ip6_frag); } #endif *size = len_data; // Return the assembled IP packet vector_remove(capinfo->ip_reasm, pkt); return pkt; } return NULL; } packet_t * capture_packet_reasm_tcp(capture_info_t *capinfo, packet_t *packet, struct tcphdr *tcp, u_char *payload, int size_payload) { vector_iter_t it = vector_iterator(capinfo->tcp_reasm); packet_t *pkt; u_char *new_payload; u_char full_payload[MAX_CAPTURE_LEN + 1]; //! Assembled if ((int32_t) size_payload <= 0) return packet; while ((pkt = vector_iterator_next(&it))) { if (addressport_equals(pkt->src, packet->src) && addressport_equals(pkt->dst, packet->dst)) { break; } } // If we already have this packet stored if (pkt) { frame_t *frame; // Append this frames to the original packet vector_iter_t frames = vector_iterator(packet->frames); while ((frame = vector_iterator_next(&frames))) packet_add_frame(pkt, frame->header, frame->data); // Destroy current packet as its frames belong to the stored packet packet_destroy(packet); } else { // First time this packet has been seen pkt = packet; // Add To the possible reassembly list vector_append(capinfo->tcp_reasm, packet); } // Store firt tcp sequence if (pkt->tcp_seq == 0) { pkt->tcp_seq = ntohl(tcp->th_seq); } // If the first frame of this packet if (vector_count(pkt->frames) == 1) { // Set initial payload packet_set_payload(pkt, payload, size_payload); } else { // Check payload length. Dont handle too big payload packets if (pkt->payload_len + size_payload > MAX_CAPTURE_LEN) { packet_destroy(pkt); vector_remove(capinfo->tcp_reasm, pkt); return NULL; } new_payload = sng_malloc(pkt->payload_len + size_payload); if (pkt->tcp_seq < ntohl(tcp->th_seq)) { // Append payload to the existing pkt->tcp_seq = ntohl(tcp->th_seq); memcpy(new_payload, pkt->payload, pkt->payload_len); memcpy(new_payload + pkt->payload_len, payload, size_payload); } else { // Prepend payload to the existing memcpy(new_payload, payload, size_payload); memcpy(new_payload + size_payload, pkt->payload, pkt->payload_len); } packet_set_payload(pkt, new_payload, pkt->payload_len + size_payload); sng_free(new_payload); } // Check if packet is too large after assembly if (pkt->payload_len > MAX_CAPTURE_LEN) { vector_remove(capinfo->tcp_reasm, pkt); return NULL; } // Store full payload content memset(full_payload, 0, MAX_CAPTURE_LEN); memcpy(full_payload, pkt->payload, pkt->payload_len); // This packet is ready to be parsed int original_size = pkt->payload_len; int valid = sip_validate_packet(pkt); if (valid == VALIDATE_COMPLETE_SIP) { // Full SIP packet! vector_remove(capinfo->tcp_reasm, pkt); return pkt; } else if (valid == VALIDATE_MULTIPLE_SIP) { vector_remove(capinfo->tcp_reasm, pkt); // We have a full SIP Packet, but do not remove everything from the reasm queue packet_t *cont = packet_clone(pkt); int pldiff = original_size - pkt->payload_len; if (pldiff > 0 && pldiff < MAX_CAPTURE_LEN) { packet_set_payload(cont, full_payload + pkt->payload_len, pldiff); vector_append(capinfo->tcp_reasm, cont); } // Return the full initial packet return pkt; } else if (valid == VALIDATE_NOT_SIP) { // Not a SIP packet, store until PSH flag if (tcp->th_flags & TH_PUSH) { vector_remove(capinfo->tcp_reasm, pkt); return pkt; } } // An incomplete SIP Packet return NULL; } int capture_ws_check_packet(packet_t *packet) { int ws_off = 0; u_char ws_opcode; u_char ws_mask; uint8_t ws_len; u_char ws_mask_key[4]; u_char *payload, *newpayload; uint32_t size_payload; int i; /** * WSocket header definition according to RFC 6455 * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-------+-+-------------+-------------------------------+ * |F|R|R|R| opcode|M| Payload len | Extended payload length | * |I|S|S|S| (4) |A| (7) | (16/64) | * |N|V|V|V| |S| | (if payload len==126/127) | * | |1|2|3| |K| | | * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + * | Extended payload length continued, if payload len == 127 | * + - - - - - - - - - - - - - - - +-------------------------------+ * | |Masking-key, if MASK set to 1 | * +-------------------------------+-------------------------------+ * | Masking-key (continued) | Payload Data | * +-------------------------------- - - - - - - - - - - - - - - - + * : Payload Data continued ... : * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * | Payload Data continued ... | * +---------------------------------------------------------------+ */ // Get payload from packet(s) size_payload = packet_payloadlen(packet); payload = packet_payload(packet); // Check we have enough payload (base) if (size_payload == 0 || size_payload <= 2) return 0; // Flags && Opcode ws_opcode = *payload & WH_OPCODE; ws_off++; // Only interested in Ws text packets if (ws_opcode != WS_OPCODE_TEXT) return 0; // Masked flag && Payload len ws_mask = (*(payload + ws_off) & WH_MASK) >> 4; ws_len = (*(payload + ws_off) & WH_LEN); ws_off++; // Skip Payload len switch (ws_len) { // Extended case 126: ws_off += 2; break; case 127: ws_off += 8; break; default: return 0; } // Check we have enough payload (base + extended payload headers) if ((int32_t) size_payload - ws_off <= 0) { return 0; } // Get Masking key if mask is enabled if (ws_mask) { // Check we have enough payload (base + extended payload headers + mask) if ((int32_t) size_payload - ws_off - 4 <= 0) { return 0; } memcpy(ws_mask_key, (payload + ws_off), 4); ws_off += 4; } // Skip Websocket headers size_payload -= ws_off; if ((int32_t) size_payload <= 0) return 0; newpayload = sng_malloc(size_payload); memcpy(newpayload, payload + ws_off, size_payload); // If mask is enabled, unmask the payload if (ws_mask) { for (i = 0; i < size_payload; i++) newpayload[i] = newpayload[i] ^ ws_mask_key[i % 4]; } // Set new packet payload into the packet packet_set_payload(packet, newpayload, size_payload); // Free the new payload sng_free(newpayload); if (packet->type == PACKET_SIP_TLS) { packet_set_type(packet, PACKET_SIP_WSS); } else { packet_set_type(packet, PACKET_SIP_WS); } return 1; } int capture_packet_parse(packet_t *packet) { // Media structure for RTP packets rtp_stream_t *stream; // We're only interested in packets with payload if (packet_payloadlen(packet)) { // Parse this header and payload if (sip_check_packet(packet)) { return 0; } // Check if this packet belongs to a RTP stream if ((stream = rtp_check_packet(packet))) { // We have an RTP packet! packet_set_type(packet, PACKET_RTP); // Store this pacekt if capture rtp is enabled if (capture_cfg.rtp_capture) { call_add_rtp_packet(stream_get_call(stream), packet); return 0; } } } return 1; } void capture_close() { capture_info_t *capinfo; // Nothing to close if (vector_count(capture_cfg.sources) == 0) return; // Close dump file if (capture_cfg.pd) { dump_close(capture_cfg.pd); } // Stop all captures vector_iter_t it = vector_iterator(capture_cfg.sources); while ((capinfo = vector_iterator_next(&it))) { //Close PCAP file if (capinfo->handle) { if (capinfo->running) { /* We must cancel the thread here instead of joining because, according to pcap_breakloop man page, * you can only break pcap_loop from within the same thread. * @see: https://www.tcpdump.org/manpages/pcap_breakloop.3pcap.html */ if (capinfo->ispcap) { pcap_breakloop(capinfo->handle); } pthread_cancel(capinfo->capture_t); pthread_join(capinfo->capture_t, NULL); } } } } int capture_launch_thread() { capture_info_t *capinfo = NULL; //! capture thread attributes pthread_attr_t attr; pthread_attr_init(&attr); // Start all captures threads vector_iter_t it = vector_iterator(capture_cfg.sources); while ((capinfo = vector_iterator_next(&it))) { // Mark capture as running capinfo->running = true; if (pthread_create(&capinfo->capture_t, &attr, (void *) capinfo->capture_fn, capinfo)) { return 1; } } pthread_attr_destroy(&attr); return 0; } void * capture_thread(void *info) { capture_info_t *capinfo = (capture_info_t *) info; // Parse available packets pcap_loop(capinfo->handle, -1, parse_packet, (u_char *) capinfo); capinfo->running = false; return NULL; } int capture_is_online() { capture_info_t *capinfo; vector_iter_t it = vector_iterator(capture_cfg.sources); while ((capinfo = vector_iterator_next(&it))) { if (capinfo->infile) return 0; } return 1; } int capture_is_running() { capture_info_t *capinfo; vector_iter_t it = vector_iterator(capture_cfg.sources); while ((capinfo = vector_iterator_next(&it))) { if (capinfo->running) return 1; } return 0; } int capture_set_bpf_filter(const char *filter) { vector_iter_t it = vector_iterator(capture_cfg.sources); capture_info_t *capinfo; // Apply the given filter to all sources while ((capinfo = vector_iterator_next(&it))) { //! Only try to validate bpf filter for pcap sources if (!capinfo->ispcap) continue; //! Check if filter compiles if (pcap_compile(capinfo->handle, &capture_cfg.fp, filter, 0, capinfo->mask) == -1) return 1; // Set capture filter if (pcap_setfilter(capinfo->handle, &capture_cfg.fp) == -1) return 1; } // Store valid capture filter capture_cfg.filter = filter; return 0; } const char * capture_get_bpf_filter() { return capture_cfg.filter; } void capture_set_paused(int pause) { capture_cfg.paused = pause; } bool capture_paused() { return capture_cfg.paused; } const char * capture_status_desc() { int online = 0, offline = 0, loading = 0; capture_info_t *capinfo; vector_iter_t it = vector_iterator(capture_cfg.sources); while ((capinfo = vector_iterator_next(&it))) { if (capinfo->infile) { offline++; if (capinfo->running) { loading++; } } else { online++; } } #ifdef USE_EEP // EEP Listen mode is always considered online if (capture_eep_listen_port()) { online++; } #endif if (capture_paused()) { if (online > 0 && offline == 0) { return "Online (Paused)"; } else if (online == 0 && offline > 0) { return "Offline (Paused)"; } else { return "Mixed (Paused)"; } } else if (loading > 0) { if (online > 0 && offline == 0) { return "Online (Loading)"; } else if (online == 0 && offline > 0) { return "Offline (Loading)"; } else { return "Mixed (Loading)"; } } else { if (online > 0 && offline == 0) { return "Online"; } else if (online == 0 && offline > 0) { return "Offline"; } else { return "Mixed"; } } } const char* capture_input_file() { capture_info_t *capinfo; if (vector_count(capture_cfg.sources) == 1) { capinfo = vector_first(capture_cfg.sources); if (capinfo->infile) { return sng_basename(capinfo->infile); } else { return NULL; } } else { return "Multiple files"; } } const char * capture_device() { capture_info_t *capinfo; if (vector_count(capture_cfg.sources) == 1) { capinfo = vector_first(capture_cfg.sources); return capinfo->device; } else { return "multi"; } return NULL; } const char* capture_keyfile() { return capture_cfg.keyfile; } void capture_set_keyfile(const char *keyfile) { capture_cfg.keyfile = keyfile; } address_t capture_tls_server() { return capture_cfg.tlsserver; } void capture_add_source(struct capture_info *capinfo) { vector_append(capture_cfg.sources, capinfo); } int capture_sources_count() { return vector_count(capture_cfg.sources); } char * capture_last_error() { capture_info_t *capinfo; if (vector_count(capture_cfg.sources) == 1) { capinfo = vector_first(capture_cfg.sources); return pcap_geterr(capinfo->handle); } return NULL; } void capture_lock() { // Avoid parsing more packet pthread_mutex_lock(&capture_cfg.lock); } void capture_unlock() { // Allow parsing more packets pthread_mutex_unlock(&capture_cfg.lock); } void capture_packet_time_sorter(vector_t *vector, void *item) { struct timeval curts, prevts; int count = vector_count(vector); int i; // TODO Implement multiframe packets curts = packet_time(item); for (i = count - 2 ; i >= 0; i--) { // Get previous packet prevts = packet_time(vector_item(vector, i)); // Check if the item is already in a sorted position if (timeval_is_older(curts, prevts)) { vector_insert(vector, item, i + 1); return; } } // Put this item at the begining of the vector vector_insert(vector, item, 0); } void capture_set_dumper(pcap_dumper_t *dumper, ino_t dump_inode) { capture_cfg.pd = dumper; capture_cfg.dump_inode = dump_inode; } void capture_dump_packet(packet_t *packet) { if (sigusr1_received && capture_cfg.pd) { // we got a SIGUSR1: reopen the dump file because it could have been renamed // we don't need to care about locking or other threads accessing in parallel // because dump_open ensures count(capture_cfg.sources) == 1 // check if the file has actually changed // only reopen if it has, otherwise we would overwrite the existing one struct stat sb; if (stat(capture_cfg.dumpfilename, &sb) == -1 || sb.st_ino != capture_cfg.dump_inode) { pcap_dump_close(capture_cfg.pd); capture_cfg.pd = dump_open(capture_cfg.dumpfilename, &capture_cfg.dump_inode); } sigusr1_received = 0; // error reopening capture file: we can't capture anymore if (!capture_cfg.pd) return; } dump_packet(capture_cfg.pd, packet); } int8_t datalink_size(int datalink) { // Datalink header size switch (datalink) { case DLT_EN10MB: return 14; case DLT_IEEE802: return 22; case DLT_LOOP: case DLT_NULL: return 4; case DLT_SLIP: case DLT_SLIP_BSDOS: return 16; case DLT_PPP: case DLT_PPP_BSDOS: case DLT_PPP_SERIAL: case DLT_PPP_ETHER: return 4; case DLT_RAW: return 0; case DLT_FDDI: return 21; case DLT_ENC: return 12; case DLT_NFLOG: return 4; #ifdef DLT_LINUX_SLL case DLT_LINUX_SLL: return 16; #endif #ifdef DLT_LINUX_SLL2 case DLT_LINUX_SLL2: return 20; #endif #ifdef DLT_IPNET case DLT_IPNET: return 24; #endif default: // Not handled datalink type return -1; } } bool is_gz_filename(const char *filename) { // does the filename end on ".gz"? char *dotpos = strrchr(filename, '.'); if (dotpos && (strcmp(dotpos, ".gz") == 0)) return true; else return false; } pcap_dumper_t * dump_open(const char *dumpfile, ino_t* dump_inode) { capture_info_t *capinfo; if (vector_count(capture_cfg.sources) == 1) { capture_cfg.dumpfilename = dumpfile; capinfo = vector_first(capture_cfg.sources); FILE *fp = fopen(dumpfile,"wb+"); if (!fp) return NULL; struct stat sb; if (fstat(fileno(fp), &sb) == -1) return NULL; if (dump_inode) { // read out the files inode, allows to later check if it has changed struct stat sb; if (fstat(fileno(fp), &sb) == -1) return NULL; *dump_inode = sb.st_ino; } if (is_gz_filename(dumpfile)) { #if defined(HAVE_FOPENCOOKIE) && defined(WITH_ZLIB) // create a gzip file stream out of the already opened file gzFile zf = gzdopen(fileno(fp), "w"); if (!zf) return NULL; static cookie_io_functions_t cookiefuncs = { NULL, gzip_cookie_write, NULL, gzip_cookie_close }; // reroute the file access functions // use the gzip write+close functions when accessing the file fp = fopencookie(zf, "w", cookiefuncs); if (!fp) return NULL; #else // no support for gzip compressed pcap files compiled in -> abort fclose(fp); return NULL; #endif } return pcap_dump_fopen(capinfo->handle, fp); } return NULL; } void dump_packet(pcap_dumper_t *pd, const packet_t *packet) { if (!pd || !packet) return; vector_iter_t it = vector_iterator(packet->frames); frame_t *frame; while ((frame = vector_iterator_next(&it))) { pcap_dump((u_char*) pd, frame->header, frame->data); } pcap_dump_flush(pd); } void dump_close(pcap_dumper_t *pd) { if (!pd) return; pcap_dump_close(pd); } sngrep-1.8.2/src/capture.h000066400000000000000000000302301464272443000154270ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file capture.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage pcap files * * sngrep can parse a pcap file to display call flows. * This file include the functions that uses libpcap to do so. * */ #ifndef __SNGREP_CAPTURE_H #define __SNGREP_CAPTURE_H #include "config.h" #include #include #include #include #include #include "address.h" #ifndef __FAVOR_BSD #define __FAVOR_BSD #endif #ifndef _BSD_SOURCE #define _BSD_SOURCE 1 #endif #if defined(BSD) || defined (__OpenBSD__) || defined(__FreeBSD__) #include #include #include #include #include #endif #ifdef USE_IPV6 #include #endif #ifdef SLL_HDR_LEN #include #endif #include #include #include #include #include #include #include "packet.h" #include "vector.h" //! Max allowed packet assembled size #define MAX_CAPTURE_LEN 20480 //! Max allowed packet length #define MAXIMUM_SNAPLEN 262144 //! Define VLAN 802.1Q Ethernet type #ifndef ETHERTYPE_8021Q #define ETHERTYPE_8021Q 0x8100 #endif //! NFLOG Support (for libpcap <1.6.0) #define DLT_NFLOG 239 #define NFULA_PAYLOAD 9 typedef struct nflog_tlv { u_int16_t tlv_length; u_int16_t tlv_type; } nflog_tlv_t; //! Define Websocket Transport codes #define WH_FIN 0x80 #define WH_RSV 0x70 #define WH_OPCODE 0x0F #define WH_MASK 0x80 #define WH_LEN 0x7F #define WS_OPCODE_TEXT 0x1 enum capture_storage { CAPTURE_STORAGE_NONE = 0, CAPTURE_STORAGE_MEMORY, CAPTURE_STORAGE_DISK }; //! Shorter declaration of capture_config structure typedef struct capture_config capture_config_t; //; Shorter declaration of capture_info structure typedef struct capture_info capture_info_t; /** * @brief Capture common configuration * * Store capture configuration and global data */ struct capture_config { //! Calls capture limit. 0 for disabling size_t limit; //! Set size of pcap buffer size_t pcap_buffer_size; //! Also capture RTP packets bool rtp_capture; //! Rotate capturad dialogs when limit have reached bool rotate; //! Capture sources are paused (all packets are skipped) int paused; //! Where should we store captured packets enum capture_storage storage; //! Key file for TLS decrypt const char *keyfile; //! TLS Server address address_t tlsserver; //! capture filter expression text const char *filter; //! The compiled filter expression struct bpf_program fp; //! libpcap dump file handler pcap_dumper_t *pd; //! libpcap dump file name const char *dumpfilename; //! inode of the dump file we have open ino_t dump_inode; //! Capture sources vector_t *sources; //! Capture Lock. Avoid parsing and handling data at the same time pthread_mutex_t lock; }; /** * @brief store all information related with packet capture * * Store capture required data from one packet source */ struct capture_info { //! Flag to determine if capture is running bool running; //! Flag to determine if this capture is libpcap bool ispcap; //! libpcap link type int link; //! libpcap link header size int8_t link_hl; //! libpcap capture handler pcap_t *handle; //! Netmask of our sniffing device bpf_u_int32 mask; //! The IP of our sniffing device bpf_u_int32 net; //! Input file in Offline capture const char *infile; //! Capture device in Online mode const char *device; //! Packets pending IP reassembly vector_t *ip_reasm; //! Packets pending TCP reassembly vector_t *tcp_reasm; //! Capture thread function void *(*capture_fn)(void *data); //! Capture thread for online capturing pthread_t capture_t; }; /** * @brief Initialize capture data * * @param limit Numbers of calls >0 * @param rtp_catpure Enable rtp capture * @param rotate Enable capture rotation * @param set size of pcap buffer */ void capture_init(size_t limit, bool rtp_capture, bool rotate, size_t pcap_buffer_size); /** * @brief Deinitialize capture data */ void capture_deinit(); /** * @brief Online capture function * * @param device Device to start capture from * * @return 0 on spawn success, 1 otherwise */ int capture_online(const char *dev); /** * @brief Read from pcap file and fill sngrep sctuctures * * This function will use libpcap files and previous structures to * parse the pcap file. * * @param infile File to read packets from * * @return 0 if load has been successfull, 1 otherwise */ int capture_offline(const char *infile); /** * @brief Read the next package and parse SIP messages * * This function is shared between online and offline capture * methods using pcap. This will get the payload from a package and * add it to the SIP storage layer. * */ void parse_packet(u_char *capinfo, const struct pcap_pkthdr *header, const u_char *packet); /** * @brief Reassembly capture IP fragments * * This function will try to assemble received PCAP data into a single IP packet. * It will return a packet structure if no fragmentation is found or a full packet * has been assembled. * * @note We assume packets higher than MAX_CAPTURE_LEN won't be SIP. This has been * done to avoid reassembling too big packets, that aren't likely to be interesting * for sngrep. * * TODO * Assembly only works when all of the IP fragments are received in the good order. * Properly check memory boundaries during packet reconstruction. * Implement a way to timeout pending IP fragments after some time. * TODO * * @param capinfo Packet capture session information * @para header Header received from libpcap callback * @para packet Packet contents received from libpcap callback * @param size Packet size (not including Layer and Network headers) * @param caplen Full packet size (current fragment -> whole assembled packet) * @return a Packet structure when packet is not fragmented or fully reassembled * @return NULL when packet has not been completely assembled */ packet_t * capture_packet_reasm_ip(capture_info_t *capinfo, const struct pcap_pkthdr *header, u_char *packet, uint32_t *size, uint32_t *caplen); /** * @brief Reassembly capture TCP segments * * This function will try to assemble TCP segments of an existing packet. * * @note We assume packets higher than MAX_CAPTURE_LEN won't be SIP. This has been * done to avoid reassembling too big packets, that aren't likely to be interesting * for sngrep. * * @param packet Capture packet structure * @param tcp TCP header extracted from capture packet data * @param payload Assembled TCP packet payload content * @param size_payload Payload length * @return a Packet structure when packet is not segmented or fully reassembled * @return NULL when packet has not been completely assembled */ packet_t * capture_packet_reasm_tcp(capture_info_t *capinfo, packet_t *packet, struct tcphdr *tcp, u_char *payload, int size_payload); /** * @brief Check if given payload belongs to a Websocket connection * * Parse the given payload and determine if given payload could belong * to a Websocket packet. This function will change the payload pointer * apnd size content to point to the SIP payload data. * * @return 0 if packet is websocket, 1 otherwise */ int capture_ws_check_packet(packet_t *packet); /** * @brief Check if the given packet structure is SIP/RTP/.. * * This function will call parse functions to determine if packet has relevant data * * @return 0 in case this packets has SIP/RTP data * @return 1 otherwise */ int capture_packet_parse(packet_t *pkt); /** * @brief Create a capture thread for online mode * * @return 0 on success, 1 otherwise */ int capture_launch_thread(); /** * @brief PCAP Capture Thread * * This function is used as worker thread for capturing filtered packets and * pass them to the UI layer. */ void * capture_thread(void *none); /** * @brief Check if capture is in Online mode * * @return 1 if capture is online, 0 if offline */ int capture_is_online(); /** * @brief Check if at least one capture handle is opened * * @return 1 if any capture source is running, 0 if all ended */ int capture_is_running(); /** * @brief Set a bpf filter in open capture * * @param filter String containing the BPF filter text * @return 0 if valid, 1 otherwise */ int capture_set_bpf_filter(const char *filter); /** * @brief Get the configured BPF filter * * @return String containing the BPF filter text or NULL */ const char * capture_get_bpf_filter(); /** * @brief Pause/Resume capture * * @param pause 1 to pause capture, 0 to resume */ void capture_set_paused(int pause); /** * @brief Check if capture is actually running * * @return 1 if capture is paused, 0 otherwise */ bool capture_paused(); /** * @brief Get capture status value */ int capture_status(); /** * @brief Return a string representing current capture status */ const char * capture_status_desc(); /** * @brief Get Input file from Offline mode * * @return Input file in Offline mode * @return NULL in Online mode */ const char* capture_input_file(); /** * @brief Get Device interface from Online mode * * @return Device name used to capture packets * @return NULL in Offline or Mixed mode */ const char * capture_device(); /** * @brief Get Key file from decrypting TLS packets * * @return given keyfile */ const char* capture_keyfile(); /** * @brief Set Keyfile to decrypt TLS packets * * @param keyfile Full path to keyfile */ void capture_set_keyfile(const char *keyfile); /** * @brief Get TLS Server address if configured * @return address scructure */ address_t capture_tls_server(); /** * @brief Add new source to capture list */ void capture_add_source(struct capture_info *capinfo); /** * @brief Return packet catprue sources count * @return capture sources count */ int capture_sources_count(); /** * @brief Return the last capture error */ char * capture_last_error(); /** * @brief Avoid parsing more packets */ void capture_lock(); /** * @brief Allow parsing more packets */ void capture_unlock(); /** * @brief Sorter by time for captured packets */ void capture_packet_time_sorter(vector_t *vector, void *item); /** * @brief Close pcap handler */ void capture_close(); /** * @brief Set general capture dumper */ void capture_set_dumper(pcap_dumper_t *dumper, ino_t dump_inode); /** * @brief Store a packet in dumper file */ void capture_dump_packet(packet_t *packet); /** * @brief Get datalink header size * */ int8_t datalink_size(int datalink); /** * @brief Open a new dumper file for capture handler */ pcap_dumper_t * dump_open(const char *dumpfile, ino_t* dump_inode); /** * @brief Store a packet in dump file * * File must be previously opened with dump_open */ void dump_packet(pcap_dumper_t *pd, const packet_t *packet); /** * @brief Close a dump file */ void dump_close(pcap_dumper_t *pd); /** * @brief Check if a given address belongs to a local device * * @param address IPv4 format for address * @return 1 if address is local, 0 otherwise */ int is_local_address(in_addr_t address); #endif sngrep-1.8.2/src/capture_eep.c000066400000000000000000000711011464272443000162550ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** Copyright (C) 2012 Homer Project (http://www.sipcapture.org) ** ** 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 . ** ****************************************************************************/ /** * @file capture.c * * @author Ivan Alonso [aka Kaian] * @author Alexandr Dubovikov * * @brief Functions to manage eep protocol * * This file contains declaration of structure and functions to send and * receive packet information through HEP-EEP (Extensible Encapsulation Protocol) * * Additional information about HEP-EEP protocol can be found in sipcature * repositories at https://github.com/sipcapture/HEP * * @note Most of this code has been taken from hep-c and sipgrep (originally * written by Alexandr Dubovikov). Modifications of sources to work with * sngrep packet structures has been made by Ivan Alonso (Kaian) * */ #include "config.h" #include #include #include #include #include #include #include "capture_eep.h" #include "util.h" #include "setting.h" capture_eep_config_t eep_cfg = { 0 }; void * accept_eep_client(void *info); int capture_eep_init() { struct addrinfo *ai, hints[1] = { { 0 } }; // Setting for EEP client if (setting_enabled(SETTING_EEP_SEND)) { // Fill configuration structure eep_cfg.capt_version = setting_get_intvalue(SETTING_EEP_SEND_VER); eep_cfg.capt_host = setting_get_value(SETTING_EEP_SEND_ADDR); eep_cfg.capt_port = setting_get_value(SETTING_EEP_SEND_PORT); eep_cfg.capt_password = setting_get_value(SETTING_EEP_SEND_PASS); eep_cfg.capt_id = setting_get_intvalue(SETTING_EEP_SEND_ID);; hints->ai_flags = AI_NUMERICSERV; hints->ai_family = AF_UNSPEC; hints->ai_socktype = SOCK_DGRAM; hints->ai_protocol = IPPROTO_UDP; if (getaddrinfo(eep_cfg.capt_host, eep_cfg.capt_port, hints, &ai)) { fprintf(stderr, "EEP client: failed getaddrinfo() for %s:%s\n", eep_cfg.capt_host, eep_cfg.capt_port); return 1; } eep_cfg.client_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (eep_cfg.client_sock < 0) { fprintf(stderr, "Sender socket creation failed: %s\n", strerror(errno)); return 1; } if (connect(eep_cfg.client_sock, ai->ai_addr, (socklen_t) (ai->ai_addrlen)) == -1) { if (errno != EINPROGRESS) { fprintf(stderr, "Sender socket creation failed: %s\n", strerror(errno)); return 1; } } } if (setting_enabled(SETTING_EEP_LISTEN)) { // Fill configuration structure eep_cfg.capt_srv_version = setting_get_intvalue(SETTING_EEP_LISTEN_VER); eep_cfg.capt_srv_host = setting_get_value(SETTING_EEP_LISTEN_ADDR); eep_cfg.capt_srv_port = setting_get_value(SETTING_EEP_LISTEN_PORT); eep_cfg.capt_srv_password = setting_get_value(SETTING_EEP_LISTEN_PASS); hints->ai_flags = AI_NUMERICSERV; hints->ai_family = AF_UNSPEC; hints->ai_socktype = SOCK_DGRAM; hints->ai_protocol = IPPROTO_UDP; if (getaddrinfo(eep_cfg.capt_srv_host, eep_cfg.capt_srv_port, hints, &ai)) { fprintf(stderr, "EEP server: failed getaddrinfo() for %s:%s\n", eep_cfg.capt_srv_host, eep_cfg.capt_srv_port); return 1; } // Create a socket for a new TCP IPv4 connection eep_cfg.server_sock = socket(AF_INET, SOCK_DGRAM, 0); if (eep_cfg.client_sock < 0) { fprintf(stderr, "Error creating server socket: %s\n", strerror(errno)); return 1; } // Bind that socket to the requested address and port if (bind(eep_cfg.server_sock, ai->ai_addr, ai->ai_addrlen) == -1) { fprintf(stderr, "Error binding address: %s\n", strerror(errno)); return 1; } capture_info_t *capinfo; // Create a new structure to handle this capture source if (!(capinfo = sng_malloc(sizeof(capture_info_t)))) { fprintf(stderr, "Can't allocate memory for capture data!\n"); return 1; } // Set capture thread function capinfo->capture_fn = accept_eep_client; capinfo->ispcap = false; // Open capture device capinfo->handle = pcap_open_dead(DLT_EN10MB, MAXIMUM_SNAPLEN); // Get datalink to parse packets correctly capinfo->link = pcap_datalink(capinfo->handle); // Check linktypes sngrep knowns before start parsing packets if ((capinfo->link_hl = datalink_size(capinfo->link)) == -1) { fprintf(stderr, "Unable to handle linktype %d\n", capinfo->link); return 3; } // Create Vectors for IP and TCP reassembly capinfo->tcp_reasm = vector_create(0, 10); capinfo->ip_reasm = vector_create(0, 10); // Add this capture information as packet source capture_add_source(capinfo); } // Settings for EEP server return 0; } void * accept_eep_client(void *info) { packet_t *pkt; capture_info_t *capinfo = (capture_info_t *) info; // Begin accepting connections while (eep_cfg.server_sock > 0) { if ((pkt = capture_eep_receive())) { // Avoid parsing from multiples sources. // Avoid parsing while screen in being redrawn capture_lock(); if (capture_packet_parse(pkt) != 0) { packet_destroy(pkt); } capture_unlock(); } } // Mark capture as not longer running capinfo->running = false; // Leave the thread gracefully pthread_exit(NULL); return 0; } void capture_eep_deinit() { if (eep_cfg.client_sock) close(eep_cfg.client_sock); if (eep_cfg.server_sock) { close(eep_cfg.server_sock); eep_cfg.server_sock = -1; } } const char * capture_eep_send_port() { return eep_cfg.capt_port; } const char * capture_eep_listen_port() { return eep_cfg.capt_srv_port; } int capture_eep_send(packet_t *pkt) { // Dont send RTP packets if (pkt->type == PACKET_RTP) return 1; // Check we have a connection established if (!eep_cfg.client_sock) return 1; switch (eep_cfg.capt_version) { case 2: return capture_eep_send_v2(pkt); case 3: return capture_eep_send_v3(pkt); } return 1; } struct pcap_pkthdr capture_eep_build_frame_data( const struct pcap_pkthdr header, const unsigned char *payload, const uint32_t payload_size, const address_t src, const address_t dst, unsigned char **frame_payload ) { //! Frame variables struct pcap_pkthdr frame_pcap_header; uint32_t frame_size = 0; // Build frame ethernet header struct ether_header ether_hdr = { .ether_dhost = { 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB }, .ether_shost = { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA }, .ether_type = htons(ETHERTYPE_IP), }; // Build frame IP header struct ip ip_hdr = { .ip_v = 4, .ip_p = IPPROTO_UDP, .ip_hl = sizeof(ip_hdr) / 4, .ip_len = htons(sizeof(ip_hdr) + sizeof(struct udphdr) + payload_size), .ip_ttl = 128, }; inet_pton(AF_INET, src.ip, &ip_hdr.ip_src); inet_pton(AF_INET, dst.ip, &ip_hdr.ip_dst); // Build frame UDP header struct udphdr udp_hdr = { .uh_sport = htons(src.port), .uh_dport = htons(dst.port), .uh_ulen = htons(sizeof(struct udphdr) + payload_size), }; // Allocate memory for payload contents *frame_payload = sng_malloc(sizeof(ether_hdr) + sizeof(ip_hdr) + sizeof(udp_hdr) + payload_size); // Append all headers to frame contents memcpy(*frame_payload + frame_size, (void*) ðer_hdr, sizeof(ether_hdr)); frame_size += sizeof(ether_hdr); memcpy(*frame_payload + frame_size, (void*) &ip_hdr, sizeof(ip_hdr)); frame_size += sizeof(ip_hdr); memcpy(*frame_payload + frame_size, (void*) &udp_hdr, sizeof(udp_hdr)); frame_size += sizeof(udp_hdr); memcpy(*frame_payload + frame_size, (void*) payload, payload_size); frame_size += payload_size; // Build a custom frame pcap header frame_pcap_header.caplen = frame_size; frame_pcap_header.len = frame_size; frame_pcap_header.ts = header.ts; return frame_pcap_header; } int capture_eep_send_v2(packet_t *pkt) { void* buffer; uint32_t buflen = 0, tlen = 0; struct hep_hdr hdr; struct hep_timehdr hep_time; struct hep_iphdr hep_ipheader; #ifdef USE_IPV6 struct hep_ip6hdr hep_ip6header; #endif unsigned char *data = packet_payload(pkt); uint32_t len = packet_payloadlen(pkt); frame_t *frame = vector_first(pkt->frames); /* Version && proto */ hdr.hp_v = 2; hdr.hp_f = pkt->ip_version == 4 ? AF_INET : AF_INET6; hdr.hp_p = pkt->proto; hdr.hp_sport = htons(pkt->src.port); hdr.hp_dport = htons(pkt->dst.port); /* Timestamp */ hep_time.tv_sec = frame->header->ts.tv_sec; hep_time.tv_usec = frame->header->ts.tv_usec; hep_time.captid = eep_cfg.capt_id; /* Calculate initial HEP packet size */ tlen = sizeof(struct hep_hdr) + sizeof(struct hep_timehdr); /* IPv4 */ if (pkt->ip_version == 4) { inet_pton(AF_INET, pkt->src.ip, &hep_ipheader.hp_src); inet_pton(AF_INET, pkt->dst.ip, &hep_ipheader.hp_dst); tlen += sizeof(struct hep_iphdr); hdr.hp_l += sizeof(struct hep_iphdr); } #ifdef USE_IPV6 /* IPv6 */ else if(pkt->ip_version == 6) { inet_pton(AF_INET6, pkt->src.ip, &hep_ip6header.hp6_src); inet_pton(AF_INET6, pkt->dst.ip, &hep_ip6header.hp6_dst); tlen += sizeof(struct hep_ip6hdr); hdr.hp_l += sizeof(struct hep_ip6hdr); } #endif // Add payload size to the final size of HEP packet tlen += len; hdr.hp_l = htons(tlen); // Allocate memory for HEPv2 packet if (!(buffer = sng_malloc(tlen))) return 1; // Copy basic headers buflen = 0; memcpy((void*) buffer + buflen, &hdr, sizeof(struct hep_hdr)); buflen += sizeof(struct hep_hdr); // Copy IP header if (pkt->ip_version == 4) { memcpy((void*) buffer + buflen, &hep_ipheader, sizeof(struct hep_iphdr)); buflen += sizeof(struct hep_iphdr); } #ifdef USE_IPV6 else if(pkt->ip_version == 6) { memcpy((void*) buffer + buflen, &hep_ip6header, sizeof(struct hep_ip6hdr)); buflen += sizeof(struct hep_ip6hdr); } #endif // Copy TImestamp header memcpy((void*) buffer + buflen, &hep_time, sizeof(struct hep_timehdr)); buflen += sizeof(struct hep_timehdr); // Now copy payload itself memcpy((void*) buffer + buflen, data, len); buflen += len; if (send(eep_cfg.client_sock, buffer, buflen, 0) == -1) { return 1; } /* FREE */ sng_free(buffer); return 1; } int capture_eep_send_v3(packet_t *pkt) { struct hep_generic *hg = NULL; void* buffer; uint32_t buflen = 0, iplen = 0, tlen = 0; hep_chunk_ip4_t src_ip4, dst_ip4; #ifdef USE_IPV6 hep_chunk_ip6_t src_ip6, dst_ip6; #endif hep_chunk_t payload_chunk; hep_chunk_t authkey_chunk; frame_t *frame = vector_first(pkt->frames); unsigned char *data = packet_payload(pkt); uint32_t len = packet_payloadlen(pkt); hg = sng_malloc(sizeof(struct hep_generic)); /* header set "HEP3" */ memcpy(hg->header.id, "\x48\x45\x50\x33", 4); /* IP proto */ hg->ip_family.chunk.vendor_id = htons(0x0000); hg->ip_family.chunk.type_id = htons(0x0001); hg->ip_family.data = pkt->ip_version == 4 ? AF_INET : AF_INET6; hg->ip_family.chunk.length = htons(sizeof(hg->ip_family)); /* Proto ID */ hg->ip_proto.chunk.vendor_id = htons(0x0000); hg->ip_proto.chunk.type_id = htons(0x0002); hg->ip_proto.data = pkt->proto; hg->ip_proto.chunk.length = htons(sizeof(hg->ip_proto)); /* IPv4 */ if (pkt->ip_version == 4) { /* SRC IP */ src_ip4.chunk.vendor_id = htons(0x0000); src_ip4.chunk.type_id = htons(0x0003); inet_pton(AF_INET, pkt->src.ip, &src_ip4.data); src_ip4.chunk.length = htons(sizeof(src_ip4)); /* DST IP */ dst_ip4.chunk.vendor_id = htons(0x0000); dst_ip4.chunk.type_id = htons(0x0004); inet_pton(AF_INET, pkt->dst.ip, &dst_ip4.data); dst_ip4.chunk.length = htons(sizeof(dst_ip4)); iplen = sizeof(dst_ip4) + sizeof(src_ip4); } #ifdef USE_IPV6 /* IPv6 */ else if(pkt->ip_version == 6) { /* SRC IPv6 */ src_ip6.chunk.vendor_id = htons(0x0000); src_ip6.chunk.type_id = htons(0x0005); inet_pton(AF_INET6, pkt->src.ip, &src_ip6.data); src_ip6.chunk.length = htons(sizeof(src_ip6)); /* DST IPv6 */ dst_ip6.chunk.vendor_id = htons(0x0000); dst_ip6.chunk.type_id = htons(0x0006); inet_pton(AF_INET6, pkt->dst.ip, &dst_ip6.data); dst_ip6.chunk.length = htons(sizeof(dst_ip6)); iplen = sizeof(dst_ip6) + sizeof(src_ip6); } #endif /* SRC PORT */ hg->src_port.chunk.vendor_id = htons(0x0000); hg->src_port.chunk.type_id = htons(0x0007); hg->src_port.data = htons(pkt->src.port); hg->src_port.chunk.length = htons(sizeof(hg->src_port)); /* DST PORT */ hg->dst_port.chunk.vendor_id = htons(0x0000); hg->dst_port.chunk.type_id = htons(0x0008); hg->dst_port.data = htons(pkt->dst.port); hg->dst_port.chunk.length = htons(sizeof(hg->dst_port)); /* TIMESTAMP SEC */ hg->time_sec.chunk.vendor_id = htons(0x0000); hg->time_sec.chunk.type_id = htons(0x0009); hg->time_sec.data = htonl(frame->header->ts.tv_sec); hg->time_sec.chunk.length = htons(sizeof(hg->time_sec)); /* TIMESTAMP USEC */ hg->time_usec.chunk.vendor_id = htons(0x0000); hg->time_usec.chunk.type_id = htons(0x000a); hg->time_usec.data = htonl(frame->header->ts.tv_usec); hg->time_usec.chunk.length = htons(sizeof(hg->time_usec)); /* Protocol TYPE */ hg->proto_t.chunk.vendor_id = htons(0x0000); hg->proto_t.chunk.type_id = htons(0x000b); hg->proto_t.data = 1; hg->proto_t.chunk.length = htons(sizeof(hg->proto_t)); /* Capture ID */ hg->capt_id.chunk.vendor_id = htons(0x0000); hg->capt_id.chunk.type_id = htons(0x000c); hg->capt_id.data = htons(eep_cfg.capt_id); hg->capt_id.chunk.length = htons(sizeof(hg->capt_id)); /* Payload */ payload_chunk.vendor_id = htons(0x0000); payload_chunk.type_id = htons(0x000f); payload_chunk.length = htons(sizeof(payload_chunk) + len); tlen = sizeof(struct hep_generic) + len + iplen + sizeof(hep_chunk_t); /* auth key */ if (eep_cfg.capt_password != NULL) { tlen += sizeof(hep_chunk_t); /* Auth key */ authkey_chunk.vendor_id = htons(0x0000); authkey_chunk.type_id = htons(0x000e); authkey_chunk.length = htons(sizeof(authkey_chunk) + strlen(eep_cfg.capt_password)); tlen += strlen(eep_cfg.capt_password); } /* total */ hg->header.length = htons(tlen); if (!(buffer = sng_malloc(tlen))) { sng_free(hg); return 1; } memcpy((void*) buffer, hg, sizeof(struct hep_generic)); buflen = sizeof(struct hep_generic); /* IPv4 */ if (pkt->ip_version == 4) { /* SRC IP */ memcpy((void*) buffer + buflen, &src_ip4, sizeof(struct hep_chunk_ip4)); buflen += sizeof(struct hep_chunk_ip4); memcpy((void*) buffer + buflen, &dst_ip4, sizeof(struct hep_chunk_ip4)); buflen += sizeof(struct hep_chunk_ip4); } #ifdef USE_IPV6 /* IPv6 */ else if(pkt->ip_version == 6) { /* SRC IPv6 */ memcpy((void*) buffer+buflen, &src_ip6, sizeof(struct hep_chunk_ip6)); buflen += sizeof(struct hep_chunk_ip6); memcpy((void*) buffer+buflen, &dst_ip6, sizeof(struct hep_chunk_ip6)); buflen += sizeof(struct hep_chunk_ip6); } #endif /* AUTH KEY CHUNK */ if (eep_cfg.capt_password != NULL) { memcpy((void*) buffer + buflen, &authkey_chunk, sizeof(struct hep_chunk)); buflen += sizeof(struct hep_chunk); /* Now copying payload self */ memcpy((void*) buffer + buflen, eep_cfg.capt_password, strlen(eep_cfg.capt_password)); buflen += strlen(eep_cfg.capt_password); } /* PAYLOAD CHUNK */ memcpy((void*) buffer + buflen, &payload_chunk, sizeof(struct hep_chunk)); buflen += sizeof(struct hep_chunk); /* Now copying payload itself */ memcpy((void*) buffer + buflen, data, len); buflen += len; if (send(eep_cfg.client_sock, buffer, buflen, 0) == -1) { return 1; } /* FREE */ sng_free(buffer); sng_free(hg); return 0; } packet_t * capture_eep_receive() { switch (eep_cfg.capt_srv_version) { case 2: return capture_eep_receive_v2(); case 3: return capture_eep_receive_v3(NULL, 0); } return NULL; } packet_t * capture_eep_receive_v2() { uint8_t family, proto; unsigned char *payload = 0; uint32_t pos; char buffer[MAX_CAPTURE_LEN] ; //! Source Address address_t src; //! Destination address address_t dst; //! Packet header struct pcap_pkthdr header; //! New created packet pointer packet_t *pkt; //! EEP client data struct sockaddr_storage eep_client; socklen_t eep_client_len=sizeof(eep_client); struct hep_hdr hdr; struct hep_timehdr hep_time; struct hep_iphdr hep_ipheader; //! Frame contents struct pcap_pkthdr frame_pcap_header; unsigned char *frame_payload; #ifdef USE_IPV6 struct hep_ip6hdr hep_ip6header; #endif // Initialize buffer memset(buffer, 0, MAX_CAPTURE_LEN); /* Receive EEP generic header */ if (recvfrom(eep_cfg.server_sock, buffer, MAX_CAPTURE_LEN, 0, (struct sockaddr*)&eep_client, &eep_client_len) == -1) return NULL; /* Copy initial bytes to HEPv2 header */ memcpy(&hdr, buffer, sizeof(struct hep_hdr)); // Check HEP version if (hdr.hp_v != 2) return NULL; /* IP proto */ family = hdr.hp_f; /* Proto ID */ proto = hdr.hp_p; pos = sizeof(struct hep_hdr); /* IPv4 */ if (family == AF_INET) { memcpy(&hep_ipheader, (void*) buffer + pos, sizeof(struct hep_iphdr)); inet_ntop(AF_INET, &hep_ipheader.hp_src, src.ip, sizeof(src.ip)); inet_ntop(AF_INET, &hep_ipheader.hp_dst, dst.ip, sizeof(dst.ip)); pos += sizeof(struct hep_iphdr); } #ifdef USE_IPV6 /* IPv6 */ else if(family == AF_INET6) { memcpy(&hep_ip6header, (void*) buffer + pos, sizeof(struct hep_ip6hdr)); inet_ntop(AF_INET6, &hep_ip6header.hp6_src, src.ip, sizeof(src.ip)); inet_ntop(AF_INET6, &hep_ip6header.hp6_dst, dst.ip, sizeof(dst.ip)); pos += sizeof(struct hep_ip6hdr); } #endif /* PORTS */ src.port = ntohs(hdr.hp_sport); dst.port = ntohs(hdr.hp_dport); /* TIMESTAMP*/ memcpy(&hep_time, (void*) buffer + pos, sizeof(struct hep_timehdr)); pos += sizeof(struct hep_timehdr); header.ts.tv_sec = hep_time.tv_sec; header.ts.tv_usec = hep_time.tv_usec; /* Protocol TYPE */ /* Capture ID */ // Calculate payload size (Total size - headers size) header.caplen = header.len = ntohs(hdr.hp_l) - pos; // Copy packet payload payload = sng_malloc(header.caplen + 1); memcpy(payload, (void*) buffer + pos, header.caplen); // Build a custom frame pcap header frame_pcap_header = capture_eep_build_frame_data(header, payload,header.caplen, src, dst, &frame_payload); // Create a new packet pkt = packet_create((family == AF_INET) ? 4 : 6, proto, src, dst, 0); packet_add_frame(pkt, &frame_pcap_header, frame_payload); packet_set_transport_data(pkt, src.port, dst.port); packet_set_type(pkt, PACKET_SIP_UDP); packet_set_payload(pkt, payload, header.caplen); // We don't longer require frame payload anymore, because adding the frame to packet clones its memory sng_free(frame_payload); // Store this packets in output file capture_dump_packet(pkt); /* FREE */ sng_free(payload); return pkt; } /** * @brief Received a HEP3 packet * * This function receives HEP protocol payload and converts it * to a Packet information. This code has been updated based on * Kamailio sipcapture module. * * @return packet pointer */ packet_t * capture_eep_receive_v3(const u_char *pkt, uint32_t size) { struct hep_generic hg; hep_chunk_ip4_t src_ip4, dst_ip4; #ifdef USE_IPV6 hep_chunk_ip6_t src_ip6, dst_ip6; #endif hep_chunk_t payload_chunk; hep_chunk_t authkey_chunk; char password[100]; int password_len; unsigned char *payload = 0; uint32_t total_len, pos; char buffer[MAX_CAPTURE_LEN] ; //! Source and Destination Address address_t src, dst; //! EEP client data struct sockaddr_storage eep_client; socklen_t eep_client_len=sizeof(eep_client); //! Packet header struct pcap_pkthdr header; //! New created packet pointer packet_t *pkt_new; //! Frame contents struct pcap_pkthdr frame_pcap_header; unsigned char *frame_payload; if(!pkt) { /* Receive EEP generic header */ if (recvfrom(eep_cfg.server_sock, buffer, MAX_CAPTURE_LEN, 0, (struct sockaddr*)&eep_client, &eep_client_len) == -1) return NULL; } else { memcpy(&buffer, pkt, size); } // Initialize structs memset(&hg, 0, sizeof(hep_generic_t)); memset(&password, 0, sizeof(password)); memset(&src, 0, sizeof(address_t)); memset(&dst, 0, sizeof(address_t)); memset(&header, 0, sizeof(struct pcap_pkthdr)); /* Copy initial bytes to EEP Generic header */ memcpy(&hg.header, buffer, sizeof(struct hep_generic)); /* header check */ if (memcmp(hg.header.id, "\x48\x45\x50\x33", 4) != 0) return NULL; total_len = ntohs(hg.header.length); pos = sizeof(hep_ctrl_t); while (pos < total_len) { hep_chunk_t *chunk = (struct hep_chunk*) (buffer + pos); int chunk_vendor = ntohs(chunk->vendor_id); int chunk_type = ntohs(chunk->type_id); int chunk_len = ntohs(chunk->length); /* Bad length, drop packet */ if (chunk_len == 0) { return NULL; } /* Skip not general chunks */ if (chunk_vendor != 0) { pos += chunk_len; continue; } switch (chunk_type) { case CAPTURE_EEP_CHUNK_INVALID: return NULL; case CAPTURE_EEP_CHUNK_FAMILY: memcpy(&hg.ip_family, (void*) buffer + pos, sizeof(hep_chunk_uint8_t)); break; case CAPTURE_EEP_CHUNK_PROTO: memcpy(&hg.ip_proto, (void*) buffer + pos, sizeof(hep_chunk_uint8_t)); break; case CAPTURE_EEP_CHUNK_SRC_IP4: memcpy(&src_ip4, (void*) buffer + pos, sizeof(struct hep_chunk_ip4)); inet_ntop(AF_INET, &src_ip4.data, src.ip, sizeof(src.ip)); break; case CAPTURE_EEP_CHUNK_DST_IP4: memcpy(&dst_ip4, (void*) buffer + pos, sizeof(struct hep_chunk_ip4)); inet_ntop(AF_INET, &dst_ip4.data, dst.ip, sizeof(src.ip)); break; #ifdef USE_IPV6 case CAPTURE_EEP_CHUNK_SRC_IP6: memcpy(&src_ip6, (void*) buffer + pos, sizeof(struct hep_chunk_ip6)); inet_ntop(AF_INET6, &src_ip6.data, src.ip, sizeof(src.ip)); break; case CAPTURE_EEP_CHUNK_DST_IP6: memcpy(&dst_ip6, (void*) buffer + pos, sizeof(struct hep_chunk_ip6)); inet_ntop(AF_INET6, &dst_ip6.data, dst.ip, sizeof(dst.ip)); break; #endif case CAPTURE_EEP_CHUNK_SRC_PORT: memcpy(&hg.src_port, (void*) buffer + pos, sizeof(hep_chunk_uint16_t)); src.port = ntohs(hg.src_port.data); break; case CAPTURE_EEP_CHUNK_DST_PORT: memcpy(&hg.dst_port, (void*) buffer + pos, sizeof(hep_chunk_uint16_t)); dst.port = ntohs(hg.dst_port.data); break; case CAPTURE_EEP_CHUNK_TS_SEC: memcpy(&hg.time_sec, (void*) buffer + pos, sizeof(hep_chunk_uint32_t)); header.ts.tv_sec = ntohl(hg.time_sec.data); break; case CAPTURE_EEP_CHUNK_TS_USEC: memcpy(&hg.time_usec, (void*) buffer + pos, sizeof(hep_chunk_uint32_t)); header.ts.tv_usec = ntohl(hg.time_usec.data); break; case CAPTURE_EEP_CHUNK_PROTO_TYPE: memcpy(&hg.proto_t, (void*) buffer + pos, sizeof(hep_chunk_uint8_t)); break; case CAPTURE_EEP_CHUNK_CAPT_ID: memcpy(&hg.capt_id, (void*) buffer + pos, sizeof(hep_chunk_uint32_t)); break; case CAPTURE_EEP_CHUNK_KEEP_TM: break; case CAPTURE_EEP_CHUNK_AUTH_KEY: memcpy(&authkey_chunk, (void*) buffer + pos, sizeof(authkey_chunk)); password_len = ntohs(authkey_chunk.length) - sizeof(authkey_chunk); memcpy(password, (void*) buffer + pos + sizeof(hep_chunk_t), password_len); break; case CAPTURE_EEP_CHUNK_PAYLOAD: memcpy(&payload_chunk, (void*) buffer + pos, sizeof(payload_chunk)); header.caplen = header.len = chunk_len - sizeof(hep_chunk_t); payload = sng_malloc(header.caplen); memcpy(payload, (void*) buffer + pos + sizeof(hep_chunk_t), header.caplen); break; case CAPTURE_EEP_CHUNK_CORRELATION_ID: break; default: break; } // Parse next chunk pos += chunk_len; } // Validate password if (eep_cfg.capt_srv_password != NULL) { // No password in packet if (strlen(password) == 0) return NULL; // Check password matches configured if (strncmp(password, eep_cfg.capt_srv_password, strlen(eep_cfg.capt_srv_password)) != 0) return NULL; } // Build a custom frame pcap header frame_pcap_header = capture_eep_build_frame_data(header, payload,header.caplen, src, dst, &frame_payload); // Create a new packet pkt_new = packet_create((hg.ip_family.data == AF_INET)?4:6, hg.ip_proto.data, src, dst, 0); packet_add_frame(pkt_new, &frame_pcap_header, frame_payload); packet_set_type(pkt_new, PACKET_SIP_UDP); packet_set_payload(pkt_new, payload, header.caplen); // We don't longer require frame payload anymore, because adding the frame to packet clones its memory sng_free(frame_payload); // Store this packets in output file capture_dump_packet(pkt_new); /* FREE */ sng_free(payload); return pkt_new; } int capture_eep_set_server_url(const char *url) { char urlstr[256]; char address[ADDRESSLEN + 1], port[6]; memset(address, 0, sizeof(address)); memset(port, 0, sizeof(port)); strncpy(urlstr, url, sizeof(urlstr)); if (sscanf(urlstr, "%*[^:]:%" STRINGIFY(ADDRESSLEN) "[^:]:%5s", address, port) == 2) { setting_set_value(SETTING_EEP_LISTEN, SETTING_ON); setting_set_value(SETTING_EEP_LISTEN_ADDR, address); setting_set_value(SETTING_EEP_LISTEN_PORT, port); return 0; } return 1; } int capture_eep_set_client_url(const char *url) { char urlstr[256]; char address[ADDRESSLEN + 1], port[6]; memset(address, 0, sizeof(address)); memset(port, 0, sizeof(port)); strncpy(urlstr, url, sizeof(urlstr)); if (sscanf(urlstr, "%*[^:]:%" STRINGIFY(ADDRESSLEN) "[^:]:%5s", address, port) == 2) { setting_set_value(SETTING_EEP_SEND, SETTING_ON); setting_set_value(SETTING_EEP_SEND_ADDR, address); setting_set_value(SETTING_EEP_SEND_PORT, port); return 0; } return 1; } sngrep-1.8.2/src/capture_eep.h000066400000000000000000000220071464272443000162630ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** Copyright (C) 2012 Homer Project (http://www.sipcapture.org) ** ** 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 . ** ****************************************************************************/ /** * @file capture.h * * @author Ivan Alonso [aka Kaian] * @author Alexandr Dubovikov * * @brief Functions to manage eep protocol * * This file contains declaration of structure and functions to send and * receive packet information through HEP-EEP (Extensible Encapsulation Protocol) * * Additional information about HEP-EEP protocol can be found in sipcature * repositories at https://github.com/sipcapture/HEP * * @note Most of this code has been taken from hep-c and sipgrep (originally * written by Alexandr Dubovikov). Modifications of sources to work with * sngrep packet structures has been made by Ivan Alonso (Kaian) * */ #ifndef __SNGREP_CAPTURE_EEP_H #define __SNGREP_CAPTURE_EEP_H #include #include "capture.h" //! HEP chunk types enum { CAPTURE_EEP_CHUNK_INVALID = 0, CAPTURE_EEP_CHUNK_FAMILY, CAPTURE_EEP_CHUNK_PROTO, CAPTURE_EEP_CHUNK_SRC_IP4, CAPTURE_EEP_CHUNK_DST_IP4, CAPTURE_EEP_CHUNK_SRC_IP6, CAPTURE_EEP_CHUNK_DST_IP6, CAPTURE_EEP_CHUNK_SRC_PORT, CAPTURE_EEP_CHUNK_DST_PORT, CAPTURE_EEP_CHUNK_TS_SEC, CAPTURE_EEP_CHUNK_TS_USEC, CAPTURE_EEP_CHUNK_PROTO_TYPE, CAPTURE_EEP_CHUNK_CAPT_ID, CAPTURE_EEP_CHUNK_KEEP_TM, CAPTURE_EEP_CHUNK_AUTH_KEY, CAPTURE_EEP_CHUNK_PAYLOAD, CAPTURE_EEP_CHUNK_CORRELATION_ID }; //! Shorter declaration of capture_eep_config structure typedef struct capture_eep_config capture_eep_config_t; /** * @brief EEP Client/Server configuration */ struct capture_eep_config { //! Client socket for sending EEP data int client_sock; //! Server socket for receiving EEP data int server_sock; //! Capture agent id int capt_id; //! Hep Version for sending data (2 or 3) int capt_version; //! IP address to sends EEP data const char *capt_host; //! Port to send EEP data const char *capt_port; //! Password for authenticate as client const char *capt_password; // HEp version for receiving data (2 or 3) int capt_srv_version; //! IP address to received EEP data const char *capt_srv_host; //! Local oort to receive EEP data const char *capt_srv_port; //! Server password to authenticate incoming connections const char *capt_srv_password; //! Server thread to parse incoming data pthread_t server_thread; }; /* HEPv3 types */ struct hep_chunk { u_int16_t vendor_id; u_int16_t type_id; u_int16_t length; }__attribute__((packed)); typedef struct hep_chunk hep_chunk_t; struct hep_chunk_uint8 { hep_chunk_t chunk; u_int8_t data; }__attribute__((packed)); typedef struct hep_chunk_uint8 hep_chunk_uint8_t; struct hep_chunk_uint16 { hep_chunk_t chunk; u_int16_t data; }__attribute__((packed)); typedef struct hep_chunk_uint16 hep_chunk_uint16_t; struct hep_chunk_uint32 { hep_chunk_t chunk; u_int32_t data; }__attribute__((packed)); typedef struct hep_chunk_uint32 hep_chunk_uint32_t; struct hep_chunk_str { hep_chunk_t chunk; char *data; }__attribute__((packed)); typedef struct hep_chunk_str hep_chunk_str_t; struct hep_chunk_ip4 { hep_chunk_t chunk; struct in_addr data; }__attribute__((packed)); typedef struct hep_chunk_ip4 hep_chunk_ip4_t; struct hep_chunk_ip6 { hep_chunk_t chunk; struct in6_addr data; }__attribute__((packed)); typedef struct hep_chunk_ip6 hep_chunk_ip6_t; struct hep_ctrl { char id[4]; u_int16_t length; }__attribute__((packed)); typedef struct hep_ctrl hep_ctrl_t; struct hep_chunk_payload { hep_chunk_t chunk; char *data; }__attribute__((packed)); typedef struct hep_chunk_payload hep_chunk_payload_t; /** * @brief Generic HEP header * * All EEP/HEP packets will contain at least this header. */ struct hep_generic { hep_ctrl_t header; hep_chunk_uint8_t ip_family; hep_chunk_uint8_t ip_proto; hep_chunk_uint16_t src_port; hep_chunk_uint16_t dst_port; hep_chunk_uint32_t time_sec; hep_chunk_uint32_t time_usec; hep_chunk_uint8_t proto_t; hep_chunk_uint32_t capt_id; }__attribute__((packed)); typedef struct hep_generic hep_generic_t; struct hep_hdr { u_int8_t hp_v; /* version */ u_int8_t hp_l; /* length */ u_int8_t hp_f; /* family */ u_int8_t hp_p; /* protocol */ u_int16_t hp_sport; /* source port */ u_int16_t hp_dport; /* destination port */ }; struct hep_timehdr { u_int32_t tv_sec; /* seconds */ u_int32_t tv_usec; /* useconds */ u_int16_t captid; /* Capture ID node */ }; struct hep_iphdr { struct in_addr hp_src; struct in_addr hp_dst; /* source and dest address */ }; #ifdef USE_IPV6 struct hep_ip6hdr { struct in6_addr hp6_src; /* source address */ struct in6_addr hp6_dst; /* destination address */ }; #endif /** * @brief Initialize EEP proccess * * This funtion will setup all required sockets both for * send and receiving information depending on sngrep configuration. * * It will also launch a thread to received EEP data if configured * to do so. * * @return 1 on any error occurs, 0 otherwise */ int capture_eep_init(); /** * @brief Unitialize EEP process * * Close used sockets for receive and send data and stop server * thread if server mode is enabled. */ void capture_eep_deinit(); /** * @brief Return the remote port where HEP packets are sent * * @return Remote port or NULL if HEP send mode is not running */ const char * capture_eep_send_port(); /** * @brief Return the local port where HEP packets are received * * @return Local listen port or NULL if HEP listen mode is not running */ const char * capture_eep_listen_port(); /** * @brief Wrapper for sending packet in configured EEP version * * @param pkt Packet Structure data * @return 1 on any error occurs, 0 otherwise */ int capture_eep_send(packet_t *pkt); /** * @brief Send a captured packet (EEP version 2) * * Send a packet encapsulated into EEP through the client socket. * This function will only handle SIP packets if EEP client mode * has been enabled. * * @param pkt Packet Structure data * @return 1 on any error occurs, 0 otherwise */ int capture_eep_send_v2(packet_t *pkt); /** * @brief Send a captured packet (EEP version 3) * * Send a packet encapsulated into EEP through the client socket. * This function will only handle SIP packets if EEP client mode * has been enabled. * * @param pkt Packet Structure data * @return 1 on any error occurs, 0 otherwise */ int capture_eep_send_v3(packet_t *pkt); /** * @brief Wrapper for receiving packet in configured EEP version * * @return NULL on any error, packet structure otherwise */ packet_t * capture_eep_receive(); /** * @brief Received a captured packet (EEP version 2) * * Wait for a packet to be received through the EEP server. This * function will parse received EEP data and create a new packet * structure. * * @return NULL on any error, packet structure otherwise */ packet_t * capture_eep_receive_v2(); /** * @brief Received a captured packet (EEP version 3) * * Wait for a packet to be received through the EEP server. This * function will parse received EEP data and create a new packet * structure. * * @param pkt packet structure data, NULL if socket should be used * @param size size of packet structure data * @return NULL on any error, packet structure otherwise */ packet_t * capture_eep_receive_v3(const u_char *pkt, uint32_t size); /** * @brief Set EEP server url * * Set EEP servermode settings using a url in the format: * - proto:address:port * For example: * - udp:10.10.0.100:9060 * - udp:0.0.0.0:9960 * * @param url URL to be parsed * @return 0 if url has been parsed, 1 otherwise */ int capture_eep_set_server_url(const char *url); /** * @brief Set EEP client url * * Set EEP clientmode settings using a url in the format: * - proto:address:port * For example: * - udp:10.10.0.100:9060 * - udp:0.0.0.0:9960 * * @param url URL to be parsed * @return 0 if url has been parsed, 1 otherwise */ int capture_eep_set_client_url(const char *url); #endif /* __SNGREP_CAPTURE_EEP_H */ sngrep-1.8.2/src/capture_gnutls.c000066400000000000000000000763201464272443000170300ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file capture_tls.c * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage SIP TLS transport for messages * * This file contains the functions and structures to manage the SIP messages * that use TLS as transport. * */ #include #include "capture.h" #include "capture_gnutls.h" #include "option.h" #include "util.h" #include "sip.h" struct SSLConnection *connections; struct CipherData ciphers[] = { /* { number, encoder, ivlen, bits, digest, diglen, mode }, */ { 0x002F, ENC_AES, 16, 128, DIG_SHA1, 20, MODE_CBC }, /* TLS_RSA_WITH_AES_128_CBC_SHA */ { 0x0035, ENC_AES256, 16, 256, DIG_SHA1, 20, MODE_CBC }, /* TLS_RSA_WITH_AES_256_CBC_SHA */ { 0x009d, ENC_AES256, 4, 256, DIG_SHA384, 48, MODE_GCM }, /* TLS_RSA_WITH_AES_256_GCM_SHA384 */ { 0, 0, 0, 0, 0, 0, 0 } }; #define TLS_DEBUG 0 void tls_debug_print_hex (char *desc, const void *ptr, int len) { #if TLS_DEBUG == 1 int i; uint8_t buff[17]; uint8_t *data = (uint8_t*)ptr; printf ("%s [%d]:\n", desc, len); if (len == 0) return; for (i = 0; i < len; i++) { if ((i % 16) == 0) { if (i != 0) printf (" |%s|\n", buff); printf ("|"); } printf (" %02x", data[i]); if ((data[i] < 0x20) || (data[i] > 0x7e)) buff[i % 16] = '.'; else buff[i % 16] = data[i]; buff[(i % 16) + 1] = '\0'; } while ((i % 16) != 0) { printf (" "); i++; } printf (" |%-16s|\n\n", buff); #endif } int P_hash(const char *digest, unsigned char *dest, int dlen, unsigned char *secret, int sslen, unsigned char *seed, int slen) { unsigned char hmac[48]; uint32_t hlen; gcry_md_hd_t md; uint32_t tmpslen; unsigned char tmpseed[slen]; unsigned char *out = dest; int pending = dlen; int algo = gcry_md_map_name(digest); int algolen = gcry_md_get_algo_dlen(algo); // Copy initial seed memcpy(tmpseed, seed, slen); tmpslen = slen; // Calculate enough data to fill destination while (pending > 0) { gcry_md_open(&md, algo, GCRY_MD_FLAG_HMAC); gcry_md_setkey(md, secret, sslen); gcry_md_write(md, tmpseed, tmpslen); memcpy(tmpseed, gcry_md_read(md, algo), algolen); tmpslen = algolen; gcry_md_close(md); gcry_md_open(&md, algo, GCRY_MD_FLAG_HMAC); gcry_md_setkey(md, secret, sslen); gcry_md_write(md, tmpseed, tmpslen); gcry_md_write(md, seed, slen); memcpy(hmac, gcry_md_read(md, algo), algolen); hlen = algolen; hlen = (hlen > pending) ? pending : hlen; memcpy(out, hmac, hlen); out += hlen; pending -= hlen; } return hlen; } int PRF(struct SSLConnection *conn, unsigned char *dest, int dlen, unsigned char *pre_master_secret, int plen, unsigned char *label, unsigned char *seed, int slen) { int i; if (conn->version < 3) { // Split the secret by half to generate MD5 and SHA secret parts int hplen = plen / 2 + plen % 2; unsigned char md5_secret[hplen]; unsigned char sha_secret[hplen]; memcpy(md5_secret, pre_master_secret, hplen); memcpy(sha_secret, pre_master_secret + plen / 2, plen / 2); // This vars will store the values of P_MD5 and P_SHA-1 unsigned char h_md5[dlen]; unsigned char h_sha[dlen]; // Concatenate given seed to the label to get the final seed int llen = strlen((const char*) label); unsigned char fseed[slen + llen]; memcpy(fseed, label, llen); memcpy(fseed + llen, seed, slen); // Get enough MD5 and SHA1 data to fill output len P_hash("MD5", h_md5, dlen, pre_master_secret, hplen, fseed, slen + llen); P_hash("SHA1", h_sha, dlen, pre_master_secret + hplen, hplen, fseed, slen + llen); // Final output will be MD5 and SHA1 X-ORed for (i = 0; i < dlen; i++) dest[i] = h_md5[i] ^ h_sha[i]; } else { // Concatenate given seed to the label to get the final seed int llen = strlen((const char*) label); unsigned char fseed[slen + llen]; memcpy(fseed, label, llen); memcpy(fseed + llen, seed, slen); // Get enough SHA data to fill output len switch (conn->cipher_data.digest) { case DIG_SHA1: P_hash("SHA256", dest, dlen, pre_master_secret, plen, fseed, slen + llen); break; case DIG_SHA256: P_hash("SHA256", dest, dlen, pre_master_secret, plen, fseed, slen + llen); break; case DIG_SHA384: P_hash("SHA384", dest, dlen, pre_master_secret, plen, fseed, slen + llen); break; default: break; } } tls_debug_print_hex("PRF out", dest, dlen); return dlen; } struct SSLConnection * tls_connection_create(struct in_addr caddr, uint16_t cport, struct in_addr saddr, uint16_t sport) { struct SSLConnection *conn = NULL; gnutls_datum_t keycontent = { NULL, 0 }; FILE *keyfp; gnutls_x509_privkey_t spkey; size_t br; int ret; // Allocate memory for this connection conn = sng_malloc(sizeof(struct SSLConnection)); memcpy(&conn->client_addr, &caddr, sizeof(struct in_addr)); memcpy(&conn->server_addr, &saddr, sizeof(struct in_addr)); memcpy(&conn->client_port, &cport, sizeof(uint16_t)); memcpy(&conn->server_port, &sport, sizeof(uint16_t)); gnutls_global_init(); if (gnutls_init(&conn->ssl, GNUTLS_SERVER) < GNUTLS_E_SUCCESS) return NULL; if (!(keyfp = fopen(capture_keyfile(), "rb"))) return NULL; fseek(keyfp, 0, SEEK_END); keycontent.size = ftell(keyfp); fseek(keyfp, 0, SEEK_SET); keycontent.data = sng_malloc(keycontent.size); br = fread(keycontent.data, 1, keycontent.size, keyfp); fclose(keyfp); gnutls_x509_privkey_init(&spkey); // Import PEM key data ret = gnutls_x509_privkey_import(spkey, &keycontent, GNUTLS_X509_FMT_PEM); if (ret != GNUTLS_E_SUCCESS) return NULL; sng_free(keycontent.data); // Check this is a valid RSA key if (gnutls_x509_privkey_get_pk_algorithm(spkey) != GNUTLS_PK_RSA) return NULL; // Store this key into the connection conn->server_private_key = spkey; // Add this connection to the list conn->next = connections; connections = conn; return conn; } void tls_connection_destroy(struct SSLConnection *conn) { struct SSLConnection *c; // Remove connection from connections list if (conn == connections) { connections = conn->next; } else { for (c = connections; c; c = c->next) { if (c->next == conn) { c->next = conn->next; break; } } } // Deallocate connection memory gnutls_deinit(conn->ssl); sng_free(conn->key_material.client_write_MAC_key); sng_free(conn->key_material.server_write_MAC_key); sng_free(conn->key_material.client_write_IV); sng_free(conn->key_material.server_write_IV); sng_free(conn->key_material.client_write_key); sng_free(conn->key_material.server_write_key); sng_free(conn); } /** * FIXME Replace this with a tls_load_key function and use it * in tls_connection_create. * * Most probably we only need one context and key for all connections */ int tls_check_keyfile(const char *keyfile) { gnutls_x509_privkey_t key; gnutls_datum_t keycontent = { NULL, 0 }; FILE *keyfp; size_t br; int ret; gnutls_global_init(); if (access(capture_keyfile(), R_OK) != 0) return 0; if (!(keyfp = fopen(capture_keyfile(), "rb"))) return 0; fseek(keyfp, 0, SEEK_END); keycontent.size = ftell(keyfp); fseek(keyfp, 0, SEEK_SET); keycontent.data = sng_malloc(keycontent.size); br = fread(keycontent.data, 1, keycontent.size, keyfp); fclose(keyfp); // Check we have read something from keyfile if (!keycontent.data) return 0; // Initialize keyfile structure ret = gnutls_x509_privkey_init(&key); if (ret < GNUTLS_E_SUCCESS) { fprintf (stderr, "Error initializing keyfile: %s\n", gnutls_strerror(ret)); return 0; } // Import RSA keyfile ret = gnutls_x509_privkey_import(key, &keycontent, GNUTLS_X509_FMT_PEM); sng_free(keycontent.data); if (ret < GNUTLS_E_SUCCESS) { fprintf (stderr, "Error loading keyfile: %s\n", gnutls_strerror(ret)); return 0; } return 1; } int tls_connection_dir(struct SSLConnection *conn, struct in_addr addr, uint16_t port) { if (conn->client_addr.s_addr == addr.s_addr && conn->client_port == port) return 0; if (conn->server_addr.s_addr == addr.s_addr && conn->server_port == port) return 1; return -1; } struct SSLConnection* tls_connection_find(struct in_addr src, uint16_t sport, struct in_addr dst, uint16_t dport) { struct SSLConnection *conn; for (conn = connections; conn; conn = conn->next) { if (tls_connection_dir(conn, src, sport) == 0 && tls_connection_dir(conn, dst, dport) == 1) { return conn; } if (tls_connection_dir(conn, src, sport) == 1 && tls_connection_dir(conn, dst, dport) == 0) { return conn; } } return NULL; } int tls_process_segment(packet_t *packet, struct tcphdr *tcp) { struct SSLConnection *conn; const u_char *payload = packet_payload(packet); uint32_t size_payload = packet_payloadlen(packet); uint8_t *out; uint32_t outl = packet->payload_len; out = sng_malloc(outl); struct in_addr ip_src, ip_dst; uint16_t sport = packet->src.port; uint16_t dport = packet->dst.port; address_t tlsserver = capture_tls_server(); // Convert addresses inet_pton(AF_INET, packet->src.ip, &ip_src); inet_pton(AF_INET, packet->dst.ip, &ip_dst); // Try to find a session for this ip if ((conn = tls_connection_find(ip_src, sport, ip_dst, dport))) { // Update last connection direction conn->direction = tls_connection_dir(conn, ip_src, sport); // Check current connection state switch (conn->state) { case TCP_STATE_SYN: // First SYN received, this package must be SYN/ACK if (tcp->th_flags & TH_SYN & ~TH_ACK) conn->state = TCP_STATE_SYN_ACK; break; case TCP_STATE_SYN_ACK: // We expect an ACK packet here if (tcp->th_flags & ~TH_SYN & TH_ACK) conn->state = TCP_STATE_ESTABLISHED; break; case TCP_STATE_ACK: case TCP_STATE_ESTABLISHED: // Check if we have a SSLv2 Handshake if(tls_record_handshake_is_ssl2(conn, payload, size_payload)) { if (tls_process_record_ssl2(conn, payload, size_payload, &out, &outl) != 0) outl = 0; } else { // Process data segment! if (tls_process_record(conn, payload, size_payload, &out, &outl) != 0) outl = 0; } // This seems a SIP TLS packet ;-) if ((int32_t) outl > 0) { packet_set_payload(packet, out, outl); packet_set_type(packet, PACKET_SIP_TLS); return 0; } break; case TCP_STATE_FIN: case TCP_STATE_CLOSED: // We can delete this connection tls_connection_destroy(conn); break; } } else { // Only create new connections whose destination is tlsserver if (tlsserver.port) { if (addressport_equals(tlsserver, packet->dst)) { // New connection, store it status and leave tls_connection_create(ip_src, sport, ip_dst, dport); } } else { // New connection, store it status and leave tls_connection_create(ip_src, sport, ip_dst, dport); } } sng_free(out); return 0; } int tls_record_handshake_is_ssl2(struct SSLConnection *conn, const uint8_t *payload, const int len) { // This magic belongs to wireshark people <3 if (len < 3) return 0; // v2 client hello should start this way if (payload[0] != 0x80) return 0; // v2 client hello msg type if (payload[2] != 0x01) return 0; // Seems SSLv2 return 1; } int tls_process_record_ssl2(struct SSLConnection *conn, const uint8_t *payload, const int len, uint8_t **out, uint32_t *outl) { int record_len_len; uint32_t record_len; uint8_t record_type; const opaque *fragment; int flen; // No record data here! if (len == 0) return 0; // Record header length record_len_len = (payload[0] & 0x80) ? 2 : 3; // Two bytes SSLv2 record length field if (record_len_len == 2) { record_len = (payload[0] & 0x7f) << 8; record_len += (payload[1]); record_type = payload[2]; fragment = payload + 3; flen = record_len - 1 /* record type */; } else { record_len = (payload[0] & 0x3f) << 8; record_len += payload[1]; record_len += payload[2]; record_type = payload[3]; fragment = payload + 4; flen = record_len - 1 /* record type */; } // We only handle Client Hello handshake SSLv2 records if (record_type == 0x01 && flen > sizeof(struct ClientHelloSSLv2)) { // Client Hello SSLv2 struct ClientHelloSSLv2 *clienthello = (struct ClientHelloSSLv2 *) fragment; // Check we have a TLS handshake if (clienthello->client_version.major != 0x03) { tls_connection_destroy(conn); return 1; } // Only TLS 1.0, 1.1 or 1.2 connections if (clienthello->client_version.minor != 0x01 && clienthello->client_version.minor != 0x02 && clienthello->client_version.minor != 0x03) { tls_connection_destroy(conn); return 1; } // Store TLS version conn->version = clienthello->client_version.minor; // Calculate where client random starts const opaque *random = fragment + sizeof(struct ClientHelloSSLv2) + UINT16_INT(clienthello->cipherlist_len) + UINT16_INT(clienthello->sessionid_len); // Get Client random memcpy(&conn->client_random, random, sizeof(struct Random)); } return 0; } int tls_process_record(struct SSLConnection *conn, const uint8_t *payload, const int len, uint8_t **out, uint32_t *outl) { struct TLSPlaintext *record; int record_len; const opaque *fragment; // No record data here! if (len == 0) return 1; // Get Record data record = (struct TLSPlaintext *) payload; record_len = sizeof(struct TLSPlaintext) + UINT16_INT(record->length); // Process record fragment if (UINT16_INT(record->length) > 0) { // TLSPlaintext fragment pointer fragment = (opaque *) payload + sizeof(struct TLSPlaintext); switch (record->type) { case handshake: // Hanshake Record, Try to get MasterSecret data if (tls_process_record_handshake(conn, fragment, UINT16_INT(record->length)) != 0) return 1; break; case change_cipher_spec: // From now on, this connection will be encrypted using MasterSecret if (conn->client_cipher_ctx && conn->server_cipher_ctx) conn->encrypted = 1; break; case application_data: if (conn->encrypted) { // Decrypt application data using MasterSecret tls_process_record_data(conn, fragment, UINT16_INT(record->length), out, outl); } break; default: return 1; } } // MultiRecord packet // FIXME We're only using the last application_data record!! FIXME if (len > record_len) { *outl = len; return tls_process_record(conn, payload + record_len, len - record_len, out, outl); } return 0; } int tls_process_record_handshake(struct SSLConnection *conn, const opaque *fragment, const int len) { struct Handshake *handshake; struct ClientHello *clienthello; struct ServerHello *serverhello; struct ClientKeyExchange *clientkeyex; const opaque *body; // Get Handshake data handshake = (struct Handshake *) fragment; if (UINT24_INT(handshake->length) > 0) { // Hanshake body pointer body = fragment + sizeof(struct Handshake); switch (handshake->type) { case hello_request: break; case client_hello: // Store client random clienthello = (struct ClientHello *) body; memcpy(&conn->client_random, &clienthello->random, sizeof(struct Random)); // Check we have a TLS handshake if (tls_valid_version(clienthello->client_version) != 0) { tls_connection_destroy(conn); return 1; } // Store TLS version conn->version = clienthello->client_version.minor; break; case server_hello: // Store server random serverhello = (struct ServerHello *) body; memcpy(&conn->server_random, &serverhello->random, sizeof(struct Random)); // Get the selected cipher memcpy(&conn->cipher_suite, body + sizeof(struct ServerHello) + serverhello->session_id_length, sizeof(uint16_t)); // Check if we have a handled cipher if (tls_connection_load_cipher(conn) != 0) { tls_connection_destroy(conn); return 1; } break; case certificate: case certificate_request: case server_hello_done: case certificate_verify: break; case client_key_exchange: // Decrypt PreMasterKey clientkeyex = (struct ClientKeyExchange *) body; gnutls_datum_t exkeys, pms; exkeys.size = UINT16_INT(clientkeyex->length); exkeys.data = (unsigned char *)&clientkeyex->exchange_keys; tls_debug_print_hex("exchange keys",exkeys.data, exkeys.size); tls_privkey_decrypt_data(conn->server_private_key, 0, &exkeys, &pms); if (!pms.data) break; memcpy(&conn->pre_master_secret, pms.data, pms.size); tls_debug_print_hex("pre_master_secret", pms.data, pms.size); tls_debug_print_hex("client_random", &conn->client_random, sizeof(struct Random)); tls_debug_print_hex("server_random", &conn->server_random, sizeof(struct Random)); // Get MasterSecret uint8_t *seed = sng_malloc(sizeof(struct Random) * 2); memcpy(seed, &conn->client_random, sizeof(struct Random)); memcpy(seed + sizeof(struct Random), &conn->server_random, sizeof(struct Random)); PRF(conn, (unsigned char *) &conn->master_secret, sizeof(struct MasterSecret), (unsigned char *) &conn->pre_master_secret, sizeof(struct PreMasterSecret), (unsigned char *) "master secret", seed, sizeof(struct Random) * 2); tls_debug_print_hex("master_secret", conn->master_secret.random, sizeof(struct MasterSecret)); memcpy(seed, &conn->server_random, sizeof(struct Random) * 2); memcpy(seed + sizeof(struct Random), &conn->client_random, sizeof(struct Random)); int key_material_len = 0; key_material_len += conn->cipher_data.diglen * 2; key_material_len += conn->cipher_data.ivblock * 2; key_material_len += conn->cipher_data.bits / 4; // Generate MACs, Write Keys and IVs uint8_t *key_material = sng_malloc(key_material_len); PRF(conn, (unsigned char *) key_material, key_material_len, (unsigned char *) &conn->master_secret, sizeof(struct MasterSecret), (unsigned char *) "key expansion", seed, sizeof(struct Random) * 2); // Get write mac keys if (conn->cipher_data.mode == MODE_GCM) { // AEAD ciphers conn->key_material.client_write_MAC_key = 0; conn->key_material.server_write_MAC_key = 0; } else { // Copy prf output to ssl connection key material int mk_len = conn->cipher_data.diglen; conn->key_material.client_write_MAC_key = sng_malloc(mk_len); memcpy(conn->key_material.client_write_MAC_key, key_material, mk_len); tls_debug_print_hex("client_write_MAC_key", key_material, mk_len); key_material += mk_len; conn->key_material.server_write_MAC_key = sng_malloc(mk_len); tls_debug_print_hex("server_write_MAC_key", key_material, mk_len); memcpy(conn->key_material.server_write_MAC_key, key_material, mk_len); key_material+=mk_len; } // Get write keys int wk_len = conn->cipher_data.bits / 8; conn->key_material.client_write_key = sng_malloc(wk_len); memcpy(conn->key_material.client_write_key, key_material, wk_len); tls_debug_print_hex("client_write_key", key_material, wk_len); key_material+=wk_len; conn->key_material.server_write_key = sng_malloc(wk_len); memcpy(conn->key_material.server_write_key, key_material, wk_len); tls_debug_print_hex("server_write_key", key_material, wk_len); key_material+=wk_len; // Get IV blocks conn->key_material.client_write_IV = sng_malloc(conn->cipher_data.ivblock); memcpy(conn->key_material.client_write_IV, key_material, conn->cipher_data.ivblock); tls_debug_print_hex("client_write_IV", key_material, conn->cipher_data.ivblock); key_material+=conn->cipher_data.ivblock; conn->key_material.server_write_IV = sng_malloc(conn->cipher_data.ivblock); memcpy(conn->key_material.server_write_IV, key_material, conn->cipher_data.ivblock); tls_debug_print_hex("server_write_IV", key_material, conn->cipher_data.ivblock); /* key_material+=conn->cipher_data.ivblock; */ // Free temporally allocated memory sng_free(seed); //sng_free(key_material); int mode = 0; if (conn->cipher_data.mode == MODE_CBC) { mode = GCRY_CIPHER_MODE_CBC; } else if (conn->cipher_data.mode == MODE_GCM) { mode = GCRY_CIPHER_MODE_CTR; } else { tls_connection_destroy(conn); return 1; } // Create Client decoder gcry_cipher_open(&conn->client_cipher_ctx, conn->ciph, mode, 0); gcry_cipher_setkey(conn->client_cipher_ctx, conn->key_material.client_write_key, gcry_cipher_get_algo_keylen(conn->ciph)); gcry_cipher_setiv(conn->client_cipher_ctx, conn->key_material.client_write_IV, gcry_cipher_get_algo_blklen(conn->ciph)); // Create Server decoder gcry_cipher_open(&conn->server_cipher_ctx, conn->ciph, mode, 0); gcry_cipher_setkey(conn->server_cipher_ctx, conn->key_material.server_write_key, gcry_cipher_get_algo_keylen(conn->ciph)); gcry_cipher_setiv(conn->server_cipher_ctx, conn->key_material.server_write_IV, gcry_cipher_get_algo_blklen(conn->ciph)); break; case finished: break; default: if (conn->encrypted) { // Encrypted Hanshake Message uint8_t *decoded = sng_malloc(len); uint32_t decodedlen = len; tls_process_record_data(conn, fragment, len, &decoded, &decodedlen); sng_free(decoded); } break; } } return 0; } int tls_process_record_data(struct SSLConnection *conn, const opaque *fragment, const int len, uint8_t **out, uint32_t *outl) { gcry_cipher_hd_t *evp; uint8_t pad; size_t flen = len; uint8_t nonce[16] = { 0 }; tls_debug_print_hex("Ciphertext", fragment, len); if (conn->direction == 0) { evp = &conn->client_cipher_ctx; } else { evp = &conn->server_cipher_ctx; } if (conn->cipher_data.mode == MODE_CBC) { // TLS 1.1 and later extract explicit IV if (conn->version >= 2 && len > 16) { gcry_cipher_setiv(*evp, fragment, 16); flen -= 16; fragment += 16; } } if (conn->cipher_data.mode == MODE_GCM) { if (conn->direction == 0) { memcpy(nonce, conn->key_material.client_write_IV, conn->cipher_data.ivblock); memcpy(nonce + conn->cipher_data.ivblock, fragment, 8); nonce[15] = 2; } else { memcpy(nonce, conn->key_material.server_write_IV, conn->cipher_data.ivblock); memcpy(nonce + conn->cipher_data.ivblock, fragment, 8); nonce[15] = 2; } gcry_cipher_setctr(*evp, nonce, sizeof(nonce)); flen -= 8; fragment += 8; } size_t dlen = len; uint8_t *decoded = sng_malloc(dlen); gcry_cipher_decrypt(*evp, decoded, dlen, (void *) fragment, flen); tls_debug_print_hex("Plaintext", decoded, flen); // Strip mac from the decoded data if (conn->cipher_data.mode == MODE_CBC) { // Get padding counter and remove from data pad = decoded[flen - 1]; dlen = flen - (pad + 1); int mac_len = conn->cipher_data.diglen; tls_debug_print_hex("Mac", decoded + (dlen - mac_len), mac_len); if ((int32_t)dlen > 0 && dlen <= *outl) { memcpy(*out, decoded, dlen); *outl = dlen - mac_len /* Trailing MAC */; } } // Strip auth tag from decoded data if (conn->cipher_data.mode == MODE_GCM) { if ((int32_t)flen > 16) { memcpy(*out, decoded, dlen); *outl = flen - 16; } } // Clenaup decoded memory sng_free(decoded); return *outl; } int tls_connection_load_cipher(struct SSLConnection *conn) { int i; int ciphnum = (conn->cipher_suite.cs1 << 8) | conn->cipher_suite.cs2; // Check if this is one of the supported ciphers for (i=0; ciphers[i].enc; i++) { if (ciphnum == ciphers[i].num) { conn->cipher_data = ciphers[i]; break; } } // Set proper cipher encoder switch (conn->cipher_data.enc) { case ENC_AES: conn->ciph = gcry_cipher_map_name("AES"); break; case ENC_AES256: conn->ciph = gcry_cipher_map_name("AES256"); break; default: return 1; } return 0; } int tls_valid_version(struct ProtocolVersion version) { switch(version.major) { case 0x03: switch(version.minor) { case 0x01: case 0x02: case 0x03: return 0; } } return 1; } int tls_privkey_decrypt_data(gnutls_x509_privkey_t key, unsigned int flags, const gnutls_datum_t * ciphertext, gnutls_datum_t * plaintext) { size_t decr_len = 0, i = 0; gcry_sexp_t s_data = NULL, s_plain = NULL; gcry_mpi_t encr_mpi = NULL, text = NULL; size_t tmp_size; gnutls_datum_t rsa_datum[6]; gcry_mpi_t rsa_params[6]; gcry_sexp_t rsa_priv_key = NULL; // Extract data from RSA key gnutls_x509_privkey_export_rsa_raw(key, &rsa_datum[0], &rsa_datum[1], &rsa_datum[2], &rsa_datum[3], &rsa_datum[4], &rsa_datum[5]); // Convert to RSA params for(i=0; i<6; i++) { gcry_mpi_scan(&rsa_params[i], GCRYMPI_FMT_USG, rsa_datum[i].data, rsa_datum[i].size, &tmp_size); } if (gcry_mpi_cmp(rsa_params[3], rsa_params[4]) > 0) gcry_mpi_swap(rsa_params[3], rsa_params[4]); // Convert to sexp gcry_mpi_invm(rsa_params[5], rsa_params[3], rsa_params[4]); gcry_sexp_build(&rsa_priv_key, NULL, "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", rsa_params[0], rsa_params[1], rsa_params[2], rsa_params[3], rsa_params[4], rsa_params[5]); // Free not longer required data for (i=0; i< 6; i++) { gcry_mpi_release(rsa_params[i]); gnutls_free(rsa_datum[i].data); } gcry_mpi_scan(&encr_mpi, GCRYMPI_FMT_USG, ciphertext->data, ciphertext->size, NULL); gcry_sexp_build(&s_data, NULL, "(enc-val(rsa(a%m)))", encr_mpi); gcry_pk_decrypt(&s_plain, s_data, rsa_priv_key); text = gcry_sexp_nth_mpi(s_plain, 0, 0); gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &decr_len, text); gcry_mpi_print(GCRYMPI_FMT_USG, ciphertext->data, ciphertext->size, &decr_len, text); int pad = 0; for (i = 1; i < decr_len; i++) { if (ciphertext->data[i] == 0) { pad = i+1; break; } } plaintext->size = decr_len - pad; plaintext->data = gnutls_malloc(plaintext->size); memmove(plaintext->data, ciphertext->data + pad, plaintext->size); gcry_sexp_release(s_data); gcry_sexp_release(s_plain); gcry_mpi_release(encr_mpi); gcry_mpi_release(text); return (int) decr_len; } sngrep-1.8.2/src/capture_gnutls.h000066400000000000000000000347711464272443000170410ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ** In addition, as a special exception, the copyright holders give ** permission to link the code of portions of this program with the ** OpenSSL library under certain conditions as described in each ** individual source file, and distribute linked combinations ** including the two. ** You must obey the GNU General Public License in all respects ** for all of the code used other than OpenSSL. If you modify ** file(s) with this exception, you may extend this exception to your ** version of the file(s), but you are not obligated to do so. If you ** do not wish to do so, delete this exception statement from your ** version. If you delete this exception statement from all source ** files in the program, then also delete it here. ** ****************************************************************************/ /** * @file capture_tls.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage SIP TLS messages * * This file contains the functions and structures to manage the SIP messages * that use TLS as transport. * */ #ifndef __SNGREP_CAPTURE_TLS_ #define __SNGREP_CAPTURE_TLS_ #include "config.h" #include #include #include #include "capture.h" //! Cast two bytes into decimal (Big Endian) #define UINT16_INT(i) ((i.x[0] << 8) | i.x[1]) //! Cast three bytes into decimal (Big Endian) #define UINT24_INT(i) ((i.x[0] << 16) | (i.x[1] << 8) | i.x[2]) //! Three bytes unsigned integer typedef struct uint16 { unsigned char x[2]; } uint16; //! Three bytes unsigned integer typedef struct uint24 { unsigned char x[3]; } uint24; //! One byte generic type typedef unsigned char opaque; //! SSLConnections states enum SSLConnectionState { //! Initial SYN packet has been received from client TCP_STATE_SYN = 0, //! SYN/ACK packet has been sent from the server TCP_STATE_SYN_ACK, //! Client ACK'ed the connection TCP_STATE_ACK, //! Connection is up, now SSL handshake should start! TCP_STATE_ESTABLISHED, //! Connection about to end TCP_STATE_FIN, //! Connection closed TCP_STATE_CLOSED }; //! SSL Encoders algo enum SSLCipherEncoders { ENC_AES = 1, ENC_AES256 = 2 }; //! SSL Digests algo enum SSLCIpherDigest { DIG_SHA1 = 1, DIG_SHA256 = 2, DIG_SHA384 = 3 }; //! SSL Decode mode enum SSLCipherMode { MODE_CBC, MODE_GCM }; //! ContentType values as defined in RFC5246 enum ContentType { change_cipher_spec = 20, alert = 21, handshake = 22, application_data = 23 }; //! HanshakeType values as defined in RFC5246 enum HandshakeType { hello_request = GNUTLS_HANDSHAKE_HELLO_REQUEST, client_hello = GNUTLS_HANDSHAKE_CLIENT_HELLO, server_hello = GNUTLS_HANDSHAKE_SERVER_HELLO, certificate = GNUTLS_HANDSHAKE_CERTIFICATE_PKT, certificate_request = GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, server_hello_done = GNUTLS_HANDSHAKE_SERVER_HELLO_DONE, certificate_verify = GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY, client_key_exchange = GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE, finished = GNUTLS_HANDSHAKE_FINISHED }; //! ProtocolVersion header as defined in RFC5246 struct ProtocolVersion { uint8_t major; uint8_t minor; }; //! TLSPlaintext record structure struct TLSPlaintext { uint8_t type; struct ProtocolVersion version; uint16 length; }; //! Hanshake record structure struct Handshake { uint8_t type; uint24 length; }; //! Handshake random structure struct Random { uint8_t gmt_unix_time[4]; uint8_t random_bytes[28]; }; struct CipherSuite { uint8_t cs1; uint8_t cs2; }; struct CipherData { int num; int enc; int ivblock; int bits; int digest; int diglen; int mode; }; struct ClientHelloSSLv2 { struct ProtocolVersion client_version; uint16 cipherlist_len; uint16 sessionid_len; uint16 random_len; // CipherSuite cipher_suite; // struct Random random; }; //! ClientHello type in Handshake records struct ClientHello { struct ProtocolVersion client_version; struct Random random; // uint8_t session_id_length; // CipherSuite cipher_suite; // Extension extensions; }; //! ServerHello type in Handshake records struct ServerHello { struct ProtocolVersion server_version; struct Random random; uint8_t session_id_length; // SessionID session_id; // CipherSuite cipher_suite; // CompressionMethod compression_method; }; struct MasterSecret { uint8_t random[48]; }; struct PreMasterSecret { struct ProtocolVersion client_version; uint8_t random[46]; }; struct EncryptedPreMasterSecret { uint8_t pre_master_secret[128]; }; //! ClientKeyExchange type in Handshake records struct ClientKeyExchange { uint16 length; struct EncryptedPreMasterSecret exchange_keys; }; /** * Structure to store all information from a TLS * connection. This is also used as linked list * node. */ struct SSLConnection { //! Connection status enum SSLConnectionState state; //! Current packet direction int direction; //! Data is encrypted flag int encrypted; //! TLS version int version; //! Client IP address struct in_addr client_addr; //! Server IP address struct in_addr server_addr; //! Client port uint16_t client_port; //! Server port uint16_t server_port; gnutls_session_t ssl; int ciph; gnutls_x509_privkey_t server_private_key; struct Random client_random; struct Random server_random; struct CipherSuite cipher_suite; struct CipherData cipher_data; struct PreMasterSecret pre_master_secret; struct MasterSecret master_secret; struct tls_data { uint8_t *client_write_MAC_key; uint8_t *server_write_MAC_key; uint8_t *client_write_key; uint8_t *server_write_key; uint8_t *client_write_IV; uint8_t *server_write_IV; } key_material; gcry_cipher_hd_t client_cipher_ctx; gcry_cipher_hd_t server_cipher_ctx; struct SSLConnection *next; }; /** * @brief P_hash expansion function as defined in RFC5246 * * This function will expand Secret and Seed into output using digest * hash function. The amount of data generated will be determined by output * length (dlen). * * @param digest Digest name to get the hash function * @param dest Destination of hash function result. Memory must be already allocated * @param dlen Destination length in bytes * @param secret Input for the hash function * @param sslen Secret length in bytes * @param seed Input for the hash function * @param slen Seed length in bytes * @return Output bytes */ int P_hash(const char *digest, unsigned char *dest, int dlen, unsigned char *secret, int sslen, unsigned char *seed, int slen); /** * @brief Pseudorandom Function as defined in RFC2246 * * This function will generate MasterSecret and KeyMaterial data from PreMasterSecret and Seed * * @param dest Destination of PRF function result. Memory must be already allocated * @param dlen Destination length in bytes * @param pre_master_secret PreMasterSecret decrypted from ClientKeyExchange Handhsake record * @param pslen PreMasterSecret length in bytes * @param label Fixed ASCII string * @param seed Concatenation of Random data from Hello Handshake records * @param slen Seed length in bytes * @return destination length in bytes */ int PRF(struct SSLConnection *conn, unsigned char *dest, int dlen, unsigned char *pre_master_secret, int plen, unsigned char *label, unsigned char *seed, int slen); /** * @brief Create a new SSLConnection * * This will allocate enough memory to store all connection data * from a detected SSL connection. This will also add this structure to * the connections linked list. * * @param caddr Client address * @param cport Client port * @param saddr Server address * @param sport Server port * @return a pointer to a new allocated SSLConnection structure */ struct SSLConnection * tls_connection_create(struct in_addr caddr, uint16_t cport, struct in_addr saddr, uint16_t sport); /** * @brief Destroys an existing SSLConnection * * This will free all allocated memory of SSLConnection also removing * the connection from connections list. * * @param conn Existing connection pointer */ void tls_connection_destroy(struct SSLConnection *conn); /** * @brief Check if given keyfile is valid * * This can be used to check if a file contains valid RSA data * * @param keyfile Absolute path the keyfile * @return 1 if file contains RSA private info, 0 otherwise */ int tls_check_keyfile(const char *keyfile); /** * @brief Determines packet direction * * Determine if the given address is from client or server. * * @param conn Existing connection pointer * @param addr Client or server address * @param port Client or server port * @return 0 if address belongs to client, 1 to server or -1 otherwise */ int tls_connection_dir(struct SSLConnection *conn, struct in_addr addr, uint16_t port); /** * @brief Find a connection * * Try to find connection data for a given address and port. * This address:port convination can be the client or server one. * * @param addr Client or server address * @param port Client or server port * @return an existing Connection pointer or NULL if not found */ struct SSLConnection* tls_connection_find(struct in_addr src, uint16_t sport, struct in_addr dst, uint16_t dport); /** * @brief Process a TCP segment to check TLS data * * Check if a TCP segment contains TLS data. In case a TLS record is found * process it and return decrypted data if case of application_data record. * * @param tcp Pointer to tcp header of the packet * @param out Pointer to the output char array. Memory must be already allocated * @param out Number of bytes returned by this function * @return 0 in all cases */ int tls_process_segment(packet_t *packet, struct tcphdr *tcp); /** * @brief Process TLS record data * * Process a TLS record * - If the record type is Handshake process it in tls_process_record_handshake * - If the record type is Application Data process it in tls_process_record_data * * @param conn Existing connection pointer * @param payload Packet peyload * @param len Payload length * @param out pointer to store decryted data * @param outl decrypted data length * @return Decrypted data length */ int tls_process_record(struct SSLConnection *conn, const uint8_t *payload, const int len, uint8_t **out, uint32_t *outl); /** * @brief Check if this Record looks like SSLv2 * * Some devices send the initial ClientHello in a SSLv2 record for compatibility * with only SSLv2 protocol. * * We will only parse SSLv2 Client hello fragments that have TLSv1/SSLv3 content * so this does not make us SSLv2 compatible ;-p * @param conn Existing connection pointer * @param payload Packet peyload * @param len Payload length * @return 1 if payload seems a SSLv2 record, 0 otherwise */ int tls_record_handshake_is_ssl2(struct SSLConnection *conn, const uint8_t *payload, const int len); /** * @brief Process TLS Handshake SSLv2 record types * * Process all types of Handshake records to store and compute all required * data to decrypt application data packets * * @param conn Existing connection pointer * @param fragment Handshake record data * @param len Decimal length of the fragment * @return 0 on valid record processed, 1 otherwise */ int tls_process_record_ssl2(struct SSLConnection *conn, const uint8_t *payload, const int len, uint8_t **out, uint32_t *outl); /** * @brief Process TLS Handshake record types * * Process all types of Handshake records to store and compute all required * data to decrypt application data packets * * @param conn Existing connection pointer * @param fragment Handshake record data * @param len Decimal length of the fragment * @return 0 on valid record processed, 1 otherwise */ int tls_process_record_handshake(struct SSLConnection *conn, const opaque *fragment, const int len); /** * @brief Process TLS ApplicationData record types * * Process application data record, trying to decrypt it with connection * information * * @param conn Existing connection pointer * @param fragment Application record data * @param len record length in bytes * @param out pointer to store decryted data * @param outl decrypted data length * @return decoded data length */ int tls_process_record_data(struct SSLConnection *conn, const opaque *fragment, const int len, uint8_t **out, uint32_t *outl); /** * @brief Get the cipher data from the given connection * * Load cipher pointer depending on the selected cipher in * Handshake messages. * * This function can be used to test is a cipher decrypting is supported * @param conn Existing connection pointer * @return 0 on valid cipher, 1 otherwise */ int tls_connection_load_cipher(struct SSLConnection *conn); /** * @brief Determine if the given version is valid for us * * We only handle some SSL/TLS versions. This function will filter out * records from unsupported versions. * * @return 0 if the version is supported, 1 otherwise */ int tls_valid_version(struct ProtocolVersion version); /** * @brief Decrypt data using private RSA key * * This function code has been taken from wireshark. * Because wireshark simply rocks. * * @param key Imported RSA key data * @param flags decrpyt flag (no used) * @param ciphertext Encrypted data * @param plaintext Decrypted data * @return number of bytes of decrypted data */ int tls_privkey_decrypt_data(gnutls_x509_privkey_t key, unsigned int flags, const gnutls_datum_t * ciphertext, gnutls_datum_t * plaintext); #endif sngrep-1.8.2/src/capture_openssl.c000066400000000000000000000712701464272443000171760ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file capture_tls.c * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage SIP TLS transport for messages * * This file contains the functions and structures to manage the SIP messages * that use TLS as transport. * */ #include #include "capture.h" #include "capture_openssl.h" #include "option.h" #include "util.h" #include "sip.h" struct SSLConnection *connections; struct CipherData ciphers[] = { /* { number, encoder, ivlen, bits, digest, diglen, mode }, */ { 0x002F, ENC_AES, 16, 128, DIG_SHA1, 20, MODE_CBC }, /* TLS_RSA_WITH_AES_128_CBC_SHA */ { 0x0035, ENC_AES256, 16, 256, DIG_SHA1, 20, MODE_CBC }, /* TLS_RSA_WITH_AES_256_CBC_SHA */ { 0x009d, ENC_AES256, 4, 256, DIG_SHA384, 48, MODE_GCM }, /* TLS_RSA_WITH_AES_256_GCM_SHA384 */ { 0, 0, 0, 0, 0, 0, 0 } }; #define TLS_DEBUG 0 void tls_debug_print_hex (char *desc, const void *ptr, int len) { #if TLS_DEBUG == 1 int i; uint8_t buff[17]; uint8_t *data = (uint8_t*)ptr; printf ("%s [%d]:\n", desc, len); if (len == 0) return; for (i = 0; i < len; i++) { if ((i % 16) == 0) { if (i != 0) printf (" |%s|\n", buff); printf ("|"); } printf (" %02x", data[i]); if ((data[i] < 0x20) || (data[i] > 0x7e)) buff[i % 16] = '.'; else buff[i % 16] = data[i]; buff[(i % 16) + 1] = '\0'; } while ((i % 16) != 0) { printf (" "); i++; } printf (" |%-16s|\n\n", buff); #endif } int P_hash(const char *digest, unsigned char *dest, int dlen, unsigned char *secret, int sslen, unsigned char *seed, int slen) { unsigned char hmac[48]; uint32_t hlen; const EVP_MD *md = EVP_get_digestbyname(digest); uint32_t tmpslen; unsigned char tmpseed[slen]; unsigned char *out = dest; int pending = dlen; // Copy initial seed memcpy(tmpseed, seed, slen); tmpslen = slen; // Calculate enough data to fill destination while (pending > 0) { #if MODSSL_USE_OPENSSL_PRE_1_1_API HMAC_CTX hm; HMAC_Init(&hm, secret, sslen, md); HMAC_Update(&hm, tmpseed, tmpslen); HMAC_Final(&hm, tmpseed, &tmpslen); HMAC_Init(&hm, secret, sslen, md); HMAC_Update(&hm, tmpseed, tmpslen); HMAC_Update(&hm, seed, slen); HMAC_Final(&hm, hmac, &hlen); HMAC_cleanup(&hm); #else HMAC_CTX *hm = HMAC_CTX_new(); HMAC_Init_ex(hm, secret, sslen, md, NULL); HMAC_Update(hm, tmpseed, tmpslen); HMAC_Final(hm, tmpseed, &tmpslen); HMAC_Init_ex(hm, secret, sslen, md, NULL); HMAC_Update(hm, tmpseed, tmpslen); HMAC_Update(hm, seed, slen); HMAC_Final(hm, hmac, &hlen); HMAC_CTX_free(hm); #endif hlen = (hlen > pending) ? pending : hlen; memcpy(out, hmac, hlen); out += hlen; pending -= hlen; } return hlen; } int PRF(struct SSLConnection *conn, unsigned char *dest, int dlen, unsigned char *pre_master_secret, int plen, unsigned char *label, unsigned char *seed, int slen) { int i; if (conn->version < 3) { // Split the secret by half to generate MD5 and SHA secret parts int hplen = plen / 2 + plen % 2; unsigned char md5_secret[hplen]; unsigned char sha_secret[hplen]; memcpy(md5_secret, pre_master_secret, hplen); memcpy(sha_secret, pre_master_secret + plen / 2, plen / 2); // This vars will store the values of P_MD5 and P_SHA-1 unsigned char h_md5[dlen]; unsigned char h_sha[dlen]; // Concatenate given seed to the label to get the final seed int llen = strlen((const char*) label); unsigned char fseed[slen + llen]; memcpy(fseed, label, llen); memcpy(fseed + llen, seed, slen); // Get enough MD5 and SHA1 data to fill output len P_hash("MD5", h_md5, dlen, pre_master_secret, hplen, fseed, slen + llen); P_hash("SHA1", h_sha, dlen, pre_master_secret + hplen, hplen, fseed, slen + llen); // Final output will be MD5 and SHA1 X-ORed for (i = 0; i < dlen; i++) dest[i] = h_md5[i] ^ h_sha[i]; } else { // Concatenate given seed to the label to get the final seed int llen = strlen((const char*) label); unsigned char fseed[slen + llen]; memcpy(fseed, label, llen); memcpy(fseed + llen, seed, slen); // Get enough SHA data to fill output len switch (conn->cipher_data.digest) { case DIG_SHA1: P_hash("SHA256", dest, dlen, pre_master_secret, plen, fseed, slen + llen); break; case DIG_SHA256: P_hash("SHA256", dest, dlen, pre_master_secret, plen, fseed, slen + llen); break; case DIG_SHA384: P_hash("SHA384", dest, dlen, pre_master_secret, plen, fseed, slen + llen); break; default: break; } } tls_debug_print_hex("PRF out", dest, dlen); return dlen; } struct SSLConnection * tls_connection_create(struct in_addr caddr, uint16_t cport, struct in_addr saddr, uint16_t sport) { struct SSLConnection *conn = NULL; conn = sng_malloc(sizeof(struct SSLConnection)); memcpy(&conn->client_addr, &caddr, sizeof(struct in_addr)); memcpy(&conn->server_addr, &saddr, sizeof(struct in_addr)); memcpy(&conn->client_port, &cport, sizeof(uint16_t)); memcpy(&conn->server_port, &sport, sizeof(uint16_t)); #if MODSSL_USE_OPENSSL_PRE_1_1_API SSL_library_init(); ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); if (!(conn->ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) #else if (!(conn->ssl_ctx = SSL_CTX_new(TLS_server_method()))) #endif return NULL; SSL_CTX_use_PrivateKey_file(conn->ssl_ctx, capture_keyfile(), SSL_FILETYPE_PEM); if (!(conn->ssl = SSL_new(conn->ssl_ctx))) return NULL; conn->server_private_key = SSL_get_privatekey(conn->ssl); conn->client_cipher_ctx = EVP_CIPHER_CTX_new(); conn->server_cipher_ctx = EVP_CIPHER_CTX_new(); // Add this connection to the list conn->next = connections; connections = conn; return conn; } void tls_connection_destroy(struct SSLConnection *conn) { struct SSLConnection *c; // Remove connection from connections list if (conn == connections) { connections = conn->next; } else { for (c = connections; c; c = c->next) { if (c->next == conn) { c->next = conn->next; break; } } } // Deallocate connection memory EVP_CIPHER_CTX_free(conn->client_cipher_ctx); EVP_CIPHER_CTX_free(conn->server_cipher_ctx); SSL_CTX_free(conn->ssl_ctx); SSL_free(conn->ssl); sng_free(conn); } /** * FIXME Replace this with a tls_load_key function and use it * in tls_connection_create. * * Most probably we only need one context and key for all connections */ int tls_check_keyfile(const char *keyfile) { SSL *ssl; SSL_CTX *ssl_ctx; char errbuf[1024]; #if MODSSL_USE_OPENSSL_PRE_1_1_API SSL_library_init(); SSL_load_error_strings(); ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); #endif if (access(capture_keyfile(), R_OK) != 0) return 0; #if MODSSL_USE_OPENSSL_PRE_1_1_API if (!(ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) #else if (!(ssl_ctx = SSL_CTX_new(TLS_server_method()))) #endif return 0; if (!(SSL_CTX_use_PrivateKey_file(ssl_ctx, capture_keyfile(), SSL_FILETYPE_PEM))) { unsigned long n = ERR_get_error(); fprintf(stderr, "%s\n", ERR_error_string(n, errbuf)); return 0; } if (!(ssl = SSL_new(ssl_ctx))) return 0; if (!SSL_get_privatekey(ssl)) return 0; return 1; } int tls_connection_dir(struct SSLConnection *conn, struct in_addr addr, uint16_t port) { if (conn->client_addr.s_addr == addr.s_addr && conn->client_port == port) return 0; if (conn->server_addr.s_addr == addr.s_addr && conn->server_port == port) return 1; return -1; } struct SSLConnection* tls_connection_find(struct in_addr src, uint16_t sport, struct in_addr dst, uint16_t dport) { struct SSLConnection *conn; for (conn = connections; conn; conn = conn->next) { if (tls_connection_dir(conn, src, sport) == 0 && tls_connection_dir(conn, dst, dport) == 1) { return conn; } if (tls_connection_dir(conn, src, sport) == 1 && tls_connection_dir(conn, dst, dport) == 0) { return conn; } } return NULL; } int tls_process_segment(packet_t *packet, struct tcphdr *tcp) { struct SSLConnection *conn; const u_char *payload = packet_payload(packet); uint32_t size_payload = packet_payloadlen(packet); uint8_t *out; uint32_t outl = packet->payload_len; out = sng_malloc(outl); struct in_addr ip_src, ip_dst; uint16_t sport = packet->src.port; uint16_t dport = packet->dst.port; address_t tlsserver = capture_tls_server(); // Convert addresses inet_pton(AF_INET, packet->src.ip, &ip_src); inet_pton(AF_INET, packet->dst.ip, &ip_dst); // Try to find a session for this ip if ((conn = tls_connection_find(ip_src, sport, ip_dst, dport))) { // Update last connection direction conn->direction = tls_connection_dir(conn, ip_src, sport); // Check current connection state switch (conn->state) { case TCP_STATE_SYN: // First SYN received, this package must be SYN/ACK if (tcp->th_flags & TH_SYN & ~TH_ACK) conn->state = TCP_STATE_SYN_ACK; break; case TCP_STATE_SYN_ACK: // We expect an ACK packet here if (tcp->th_flags & ~TH_SYN & TH_ACK) conn->state = TCP_STATE_ESTABLISHED; break; case TCP_STATE_ACK: case TCP_STATE_ESTABLISHED: // Check if we have a SSLv2 Handshake if(tls_record_handshake_is_ssl2(conn, payload, size_payload)) { if (tls_process_record_ssl2(conn, payload, size_payload, &out, &outl) != 0) outl = 0; } else { // Process data segment! if (tls_process_record(conn, payload, size_payload, &out, &outl) != 0) outl = 0; } // This seems a SIP TLS packet ;-) if ((int32_t) outl > 0) { packet_set_payload(packet, out, outl); packet_set_type(packet, PACKET_SIP_TLS); return 0; } break; case TCP_STATE_FIN: case TCP_STATE_CLOSED: // We can delete this connection tls_connection_destroy(conn); break; } } else { if (tcp->th_flags & TH_SYN & ~TH_ACK) { // Only create new connections whose destination is tlsserver if (tlsserver.port) { if (addressport_equals(tlsserver, packet->dst)) { // New connection, store it status and leave tls_connection_create(ip_src, sport, ip_dst, dport); } } else { // New connection, store it status and leave tls_connection_create(ip_src, sport, ip_dst, dport); } } } sng_free(out); return 0; } int tls_record_handshake_is_ssl2(struct SSLConnection *conn, const uint8_t *payload, const int len) { // This magic belongs to wireshark people <3 if (len < 3) return 0; // v2 client hello should start this way if (payload[0] != 0x80) return 0; // v2 client hello msg type if (payload[2] != 0x01) return 0; // Seems SSLv2 return 1; } int tls_process_record_ssl2(struct SSLConnection *conn, const uint8_t *payload, const int len, uint8_t **out, uint32_t *outl) { int record_len_len; uint32_t record_len; uint8_t record_type; const opaque *fragment; int flen; // No record data here! if (len == 0) return 1; // Record header length record_len_len = (payload[0] & 0x80) ? 2 : 3; // Two bytes SSLv2 record length field if (record_len_len == 2) { record_len = (payload[0] & 0x7f) << 8; record_len += (payload[1]); record_type = payload[2]; fragment = payload + 3; flen = record_len - 1 /* record type */; } else { record_len = (payload[0] & 0x3f) << 8; record_len += payload[1]; record_len += payload[2]; record_type = payload[3]; fragment = payload + 4; flen = record_len - 1 /* record type */; } // We only handle Client Hello handshake SSLv2 records if (record_type == 0x01 && flen > sizeof(struct ClientHelloSSLv2)) { // Client Hello SSLv2 struct ClientHelloSSLv2 *clienthello = (struct ClientHelloSSLv2 *) fragment; // Check we have a TLS handshake if (clienthello->client_version.major != 0x03) { tls_connection_destroy(conn); return 1; } // Only TLS 1.0, 1.1 or 1.2 connections if (clienthello->client_version.minor != 0x01 && clienthello->client_version.minor != 0x02 && clienthello->client_version.minor != 0x03) { tls_connection_destroy(conn); return 1; } // Store TLS version conn->version = clienthello->client_version.minor; // Calculate where client random starts const opaque *random = fragment + sizeof(struct ClientHelloSSLv2) + UINT16_INT(clienthello->cipherlist_len) + UINT16_INT(clienthello->sessionid_len); // Get Client random memcpy(&conn->client_random, random, sizeof(struct Random)); } return 0; } int tls_process_record(struct SSLConnection *conn, const uint8_t *payload, const int len, uint8_t **out, uint32_t *outl) { struct TLSPlaintext *record; int record_len; const opaque *fragment; // No record data here! if (len == 0) return 1; // Get Record data record = (struct TLSPlaintext *) payload; record_len = sizeof(struct TLSPlaintext) + UINT16_INT(record->length); // Process record fragment if (UINT16_INT(record->length) > 0) { // TLSPlaintext fragment pointer fragment = (opaque *) payload + sizeof(struct TLSPlaintext); switch (record->type) { case handshake: // Hanshake Record, Try to get MasterSecret data if (tls_process_record_handshake(conn, fragment, UINT16_INT(record->length)) != 0) return 1; break; case change_cipher_spec: // From now on, this connection will be encrypted using MasterSecret #if MODSSL_USE_OPENSSL_PRE_1_1_API if (conn->client_cipher_ctx->cipher && conn->server_cipher_ctx->cipher) conn->encrypted = 1; #else if (EVP_CIPHER_CTX_cipher(conn->client_cipher_ctx) && EVP_CIPHER_CTX_cipher(conn->server_cipher_ctx)) conn->encrypted = 1; #endif break; case application_data: if (conn->encrypted) { // Decrypt application data using MasterSecret tls_process_record_data(conn, fragment, UINT16_INT(record->length), out, outl); } break; default: return 1; } } // MultiRecord packet if (len > record_len) { *outl = len; return tls_process_record(conn, payload + record_len, len - record_len, out, outl); } return 0; } int tls_process_record_handshake(struct SSLConnection *conn, const opaque *fragment, const int len) { struct Handshake *handshake; struct ClientHello *clienthello; struct ServerHello *serverhello; struct ClientKeyExchange *clientkeyex; const opaque *body; // Get Handshake data handshake = (struct Handshake *) fragment; if (UINT24_INT(handshake->length) > 0) { // Hanshake body pointer body = fragment + sizeof(struct Handshake); switch (handshake->type) { case hello_request: break; case client_hello: // Store client random clienthello = (struct ClientHello *) body; memcpy(&conn->client_random, &clienthello->random, sizeof(struct Random)); // Check we have a TLS handshake if (!(clienthello->client_version.major == 0x03)) { tls_connection_destroy(conn); return 1; } // Only TLS 1.0, 1.1 or 1.2 connections if (clienthello->client_version.minor != 0x01 && clienthello->client_version.minor != 0x02 && clienthello->client_version.minor != 0x03) { tls_connection_destroy(conn); return 1; } // Store TLS version conn->version = clienthello->client_version.minor; break; case server_hello: // Store server random serverhello = (struct ServerHello *) body; memcpy(&conn->server_random, &serverhello->random, sizeof(struct Random)); // Get the selected cipher memcpy(&conn->cipher_suite, body + sizeof(struct ServerHello) + serverhello->session_id_length, sizeof(uint16_t)); // Check if we have a handled cipher if (tls_connection_load_cipher(conn) != 0) { tls_connection_destroy(conn); return 1; } break; case certificate: case certificate_request: case server_hello_done: case certificate_verify: break; case client_key_exchange: // Decrypt PreMasterKey clientkeyex = (struct ClientKeyExchange *) body; #if MODSSL_USE_OPENSSL_PRE_1_1_API RSA_private_decrypt(UINT16_INT(clientkeyex->length), (const unsigned char *) &clientkeyex->exchange_keys, (unsigned char *) &conn->pre_master_secret, conn->server_private_key->pkey.rsa, RSA_PKCS1_PADDING); #else RSA_private_decrypt(UINT16_INT(clientkeyex->length), (const unsigned char *) &clientkeyex->exchange_keys, (unsigned char *) &conn->pre_master_secret, EVP_PKEY_get0_RSA(conn->server_private_key), RSA_PKCS1_PADDING); #endif tls_debug_print_hex("client_random", &conn->client_random, 32); tls_debug_print_hex("server_random", &conn->server_random, 32); uint8_t *seed = sng_malloc(sizeof(struct Random) * 2); memcpy(seed, &conn->client_random, sizeof(struct Random)); memcpy(seed + sizeof(struct Random), &conn->server_random, sizeof(struct Random)); // Get MasterSecret PRF(conn, (unsigned char *) &conn->master_secret, sizeof(struct MasterSecret), (unsigned char *) &conn->pre_master_secret, sizeof(struct PreMasterSecret), (unsigned char *) "master secret", seed, sizeof(struct Random) * 2); tls_debug_print_hex("master_secret", conn->master_secret.random, 48); memcpy(seed, &conn->server_random, sizeof(struct Random) * 2); memcpy(seed + sizeof(struct Random), &conn->client_random, sizeof(struct Random)); int key_material_len = 0; key_material_len += conn->cipher_data.diglen * 2; key_material_len += conn->cipher_data.ivblock * 2; key_material_len += conn->cipher_data.bits / 4; uint8_t *key_material = sng_malloc(key_material_len); // Generate MACs, Write Keys and IVs PRF(conn, (unsigned char *) key_material, key_material_len, (unsigned char *) &conn->master_secret, sizeof(struct MasterSecret), (unsigned char *) "key expansion", seed, sizeof(struct Random) * 2); // Get write mac keys if (conn->cipher_data.mode == MODE_GCM) { // AEAD ciphers conn->key_material.client_write_MAC_key = 0; conn->key_material.server_write_MAC_key = 0; } else { // Copy prf output to ssl connection key material int mk_len = conn->cipher_data.diglen; conn->key_material.client_write_MAC_key = sng_malloc(mk_len); tls_debug_print_hex("client_write_MAC_key", key_material, mk_len); memcpy(conn->key_material.client_write_MAC_key, key_material, mk_len); key_material += mk_len; conn->key_material.server_write_MAC_key = sng_malloc(mk_len); tls_debug_print_hex("server_write_MAC_key", key_material, mk_len); memcpy(conn->key_material.server_write_MAC_key, key_material, mk_len); key_material += mk_len; } // Get write keys int wk_len = conn->cipher_data.bits / 8; conn->key_material.client_write_key = sng_malloc(wk_len); memcpy(conn->key_material.client_write_key, key_material, wk_len); tls_debug_print_hex("client_write_key", key_material, wk_len); key_material+=wk_len; conn->key_material.server_write_key = sng_malloc(wk_len); memcpy(conn->key_material.server_write_key, key_material, wk_len); tls_debug_print_hex("server_write_key", key_material, wk_len); key_material+=wk_len; // Get IV blocks conn->key_material.client_write_IV = sng_malloc(conn->cipher_data.ivblock); memcpy(conn->key_material.client_write_IV, key_material, conn->cipher_data.ivblock); tls_debug_print_hex("client_write_IV", key_material, conn->cipher_data.ivblock); key_material+=conn->cipher_data.ivblock; conn->key_material.server_write_IV = sng_malloc(conn->cipher_data.ivblock); memcpy(conn->key_material.server_write_IV, key_material, conn->cipher_data.ivblock); tls_debug_print_hex("server_write_IV", key_material, conn->cipher_data.ivblock); // Done with the seed sng_free(seed); // Create Client decoder #if MODSSL_USE_OPENSSL_PRE_1_1_API EVP_CIPHER_CTX_init(conn->client_cipher_ctx); #else EVP_CIPHER_CTX_reset(conn->client_cipher_ctx); #endif EVP_CipherInit(conn->client_cipher_ctx, conn->ciph, conn->key_material.client_write_key, conn->key_material.client_write_IV, 0); #if MODSSL_USE_OPENSSL_PRE_1_1_API EVP_CIPHER_CTX_init(conn->server_cipher_ctx); #else EVP_CIPHER_CTX_reset(conn->server_cipher_ctx); #endif EVP_CipherInit(conn->server_cipher_ctx, conn->ciph, conn->key_material.server_write_key, conn->key_material.server_write_IV, 0); break; #ifndef OLD_OPENSSL_VERSION case new_session_ticket: #endif case finished: break; default: if (conn->encrypted) { // Encrypted Hanshake Message uint8_t *decoded = sng_malloc(len); uint32_t decodedlen = len; tls_process_record_data(conn, fragment, len, &decoded, &decodedlen); sng_free(decoded); } break; } } return 0; } int tls_process_record_data(struct SSLConnection *conn, const opaque *fragment, const int len, uint8_t **out, uint32_t *outl) { EVP_CIPHER_CTX *evp; uint8_t pad; size_t flen = len; uint8_t nonce[16] = { 0 }; tls_debug_print_hex("Ciphertext", fragment, len); if (conn->direction == 0) { evp = conn->client_cipher_ctx; } else { evp = conn->server_cipher_ctx; } if (conn->cipher_data.mode == MODE_CBC) { // TLS 1.1 and later extract explicit IV if (conn->version >= 2 && len > 16) { if (conn->direction == 0) { EVP_CipherInit(evp, conn->ciph, conn->key_material.client_write_key, fragment, 0); } else { EVP_CipherInit(evp, conn->ciph, conn->key_material.server_write_key, fragment, 0); } flen -= 16; fragment += 16; } } if (conn->cipher_data.mode == MODE_GCM) { if (conn->direction == 0) { memcpy(nonce, conn->key_material.client_write_IV, conn->cipher_data.ivblock); memcpy(nonce + conn->cipher_data.ivblock, fragment, 8); nonce[15] = 2; EVP_CipherInit(evp, conn->ciph,conn->key_material.client_write_key,nonce, 0); } else { memcpy(nonce, conn->key_material.server_write_IV, conn->cipher_data.ivblock); memcpy(nonce + conn->cipher_data.ivblock, fragment, 8); nonce[15] = 2; EVP_CipherInit(evp, conn->ciph,conn->key_material.server_write_key,nonce, 0); } flen -= 8; fragment += 8; } size_t dlen = len; uint8_t *decoded = sng_malloc(dlen); EVP_Cipher(evp, decoded, (unsigned char *) fragment, flen); tls_debug_print_hex("Plaintext", decoded, flen); if (conn->cipher_data.mode == MODE_CBC) { // Get padding counter and remove from data pad = decoded[flen - 1]; dlen = flen - (pad + 1); tls_debug_print_hex("Mac", decoded + (dlen - 20), 20); if ((int32_t) dlen > 0 && dlen <= *outl) { memcpy(*out, decoded, dlen); *outl = dlen - 20 /* Trailing MAC */; } } // Strip auth tag from decoded data if (conn->cipher_data.mode == MODE_GCM) { if ((int32_t)flen > 16) { memcpy(*out, decoded, dlen); *outl = flen - 16; } } // Cleanup decoded memory sng_free(decoded); return *outl; } int tls_connection_load_cipher(struct SSLConnection *conn) { int i; int ciphnum = (conn->cipher_suite.cs1 << 8) | conn->cipher_suite.cs2; // Check if this is one of the supported ciphers for (i=0; ciphers[i].enc; i++) { if (ciphnum == ciphers[i].num) { conn->cipher_data = ciphers[i]; break; } } // Set proper cipher encoder switch (conn->cipher_data.enc) { case ENC_AES: conn->ciph = EVP_get_cipherbyname("AES128"); break; case ENC_AES256: if (conn->cipher_data.mode == MODE_CBC) { conn->ciph = EVP_get_cipherbyname("AES256"); } else { conn->ciph = EVP_get_cipherbyname("AES-256-CTR"); } break; default: return 1; } return 0; } sngrep-1.8.2/src/capture_openssl.h000066400000000000000000000346441464272443000172070ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ** In addition, as a special exception, the copyright holders give ** permission to link the code of portions of this program with the ** OpenSSL library under certain conditions as described in each ** individual source file, and distribute linked combinations ** including the two. ** You must obey the GNU General Public License in all respects ** for all of the code used other than OpenSSL. If you modify ** file(s) with this exception, you may extend this exception to your ** version of the file(s), but you are not obligated to do so. If you ** do not wish to do so, delete this exception statement from your ** version. If you delete this exception statement from all source ** files in the program, then also delete it here. ** ****************************************************************************/ /** * @file capture_tls.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage SIP TLS messages * * This file contains the functions and structures to manage the SIP messages * that use TLS as transport. * */ #ifndef __SNGREP_CAPTURE_TLS_ #define __SNGREP_CAPTURE_TLS_ #include "config.h" #include #include #include #include #include #include #include "capture.h" //! Cast two bytes into decimal (Big Endian) #define UINT16_INT(i) ((i.x[0] << 8) | i.x[1]) //! Cast three bytes into decimal (Big Endian) #define UINT24_INT(i) ((i.x[0] << 16) | (i.x[1] << 8) | i.x[2]) //The symbol SSL3_MT_NEWSESSION_TICKET appears to have been introduced at around //openssl 0.9.8f, and the use of if breaks builds with older openssls #if OPENSSL_VERSION_NUMBER < 0x00908070L #define OLD_OPENSSL_VERSION 1 #endif /* LibreSSL declares OPENSSL_VERSION_NUMBER == 2.0 but does not include most * changes from OpenSSL >= 1.1 (new functions, macros, deprecations, ...) until * version 3.5.0 */ #if defined(LIBRESSL_VERSION_NUMBER) #define MODSSL_USE_OPENSSL_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x30500000L) #else #define MODSSL_USE_OPENSSL_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L) #endif //! Three bytes unsigned integer typedef struct uint16 { unsigned char x[2]; } uint16; //! Three bytes unsigned integer typedef struct uint24 { unsigned char x[3]; } uint24; //! One byte generic type typedef unsigned char opaque; //! SSLConnections states enum SSLConnectionState { //! Initial SYN packet has been received from client TCP_STATE_SYN = 0, //! SYN/ACK packet has been sent from the server TCP_STATE_SYN_ACK, //! Client ACK'ed the connection TCP_STATE_ACK, //! Connection is up, now SSL handshake should start! TCP_STATE_ESTABLISHED, //! Connection about to end TCP_STATE_FIN, //! Connection closed TCP_STATE_CLOSED }; //! SSL Encoders algo enum SSLCipherEncoders { ENC_AES = 1, ENC_AES256 = 2 }; //! SSL Digests algo enum SSLCIpherDigest { DIG_SHA1 = 1, DIG_SHA256 = 2, DIG_SHA384 = 3 }; //! SSL Decode mode enum SSLCipherMode { MODE_CBC, MODE_GCM }; //! ContentType values as defined in RFC5246 enum ContentType { change_cipher_spec = SSL3_RT_CHANGE_CIPHER_SPEC, alert = SSL3_RT_ALERT, handshake = SSL3_RT_HANDSHAKE, application_data = SSL3_RT_APPLICATION_DATA }; //! HanshakeType values as defined in RFC5246 enum HandshakeType { hello_request = SSL3_MT_HELLO_REQUEST, client_hello = SSL3_MT_CLIENT_HELLO, server_hello = SSL3_MT_SERVER_HELLO, certificate = SSL3_MT_CERTIFICATE, certificate_request = SSL3_MT_CERTIFICATE_REQUEST, server_hello_done = SSL3_MT_SERVER_DONE, certificate_verify = SSL3_MT_CERTIFICATE_VERIFY, client_key_exchange = SSL3_MT_CLIENT_KEY_EXCHANGE, #ifndef OLD_OPENSSL_VERSION new_session_ticket = SSL3_MT_NEWSESSION_TICKET, #endif finished = SSL3_MT_FINISHED }; //! ProtocolVersion header as defined in RFC5246 struct ProtocolVersion { uint8_t major; uint8_t minor; }; //! TLSPlaintext record structure struct TLSPlaintext { uint8_t type; struct ProtocolVersion version; uint16 length; }; //! Hanshake record structure struct Handshake { uint8_t type; uint24 length; }; //! Handshake random structure struct Random { uint8_t gmt_unix_time[4]; uint8_t random_bytes[28]; }; struct CipherSuite { uint8_t cs1; uint8_t cs2; }; struct CipherData { int num; int enc; int ivblock; int bits; int digest; int diglen; int mode; }; struct ClientHelloSSLv2 { struct ProtocolVersion client_version; uint16 cipherlist_len; uint16 sessionid_len; uint16 random_len; // CipherSuite cipher_suite; // struct Random random; }; //! ClientHello type in Handshake records struct ClientHello { struct ProtocolVersion client_version; struct Random random; // uint8_t session_id_length; // CipherSuite cipher_suite; // Extension extensions; }; //! ServerHello type in Handshake records struct ServerHello { struct ProtocolVersion server_version; struct Random random; uint8_t session_id_length; // SessionID session_id; // CipherSuite cipher_suite; // CompressionMethod compression_method; }; struct MasterSecret { uint8_t random[48]; }; struct PreMasterSecret { struct ProtocolVersion client_version; uint8_t random[46]; }; struct EncryptedPreMasterSecret { uint8_t pre_master_secret[128]; }; //! ClientKeyExchange type in Handshake records struct ClientKeyExchange { uint16 length; struct EncryptedPreMasterSecret exchange_keys; }; /** * Structure to store all information from a TLS * connection. This is also used as linked list * node. */ struct SSLConnection { //! Connection status enum SSLConnectionState state; //! Current packet direction int direction; //! Data is encrypted flag int encrypted; //! TLS version int version; //! Client IP address struct in_addr client_addr; //! Server IP address struct in_addr server_addr; //! Client port uint16_t client_port; //! Server port uint16_t server_port; SSL *ssl; SSL_CTX *ssl_ctx; EVP_PKEY *server_private_key; const EVP_CIPHER *ciph; struct Random client_random; struct Random server_random; struct CipherSuite cipher_suite; struct CipherData cipher_data; struct PreMasterSecret pre_master_secret; struct MasterSecret master_secret; struct tls_data { uint8_t *client_write_MAC_key; uint8_t *server_write_MAC_key; uint8_t *client_write_key; uint8_t *server_write_key; uint8_t *client_write_IV; uint8_t *server_write_IV; } key_material; EVP_CIPHER_CTX *client_cipher_ctx; EVP_CIPHER_CTX *server_cipher_ctx; struct SSLConnection *next; }; /** * @brief P_hash expansion function as defined in RFC5246 * * This function will expand Secret and Seed into output using digest * hash function. The amount of data generated will be determined by output * length (dlen). * * @param digest Digest name to get the hash function * @param dest Destination of hash function result. Memory must be already allocated * @param dlen Destination length in bytes * @param secret Input for the hash function * @param sslen Secret length in bytes * @param seed Input for the hash function * @param slen Seed length in bytes * @return Output bytes */ int P_hash(const char *digest, unsigned char *dest, int dlen, unsigned char *secret, int sslen, unsigned char *seed, int slen); /** * @brief Pseudorandom Function as defined in RFC2246 * * This function will generate MasterSecret and KeyMaterial data from PreMasterSecret and Seed * * @param dest Destination of PRF function result. Memory must be already allocated * @param dlen Destination length in bytes * @param pre_master_secret PreMasterSecret decrypted from ClientKeyExchange Handhsake record * @param pslen PreMasterSecret length in bytes * @param label Fixed ASCII string * @param seed Concatenation of Random data from Hello Handshake records * @param slen Seed length in bytes * @return destination length in bytes */ int PRF(struct SSLConnection *conn, unsigned char *dest, int dlen, unsigned char *pre_master_secret, int plen, unsigned char *label, unsigned char *seed, int slen); /** * @brief Create a new SSLConnection * * This will allocate enough memory to store all connection data * from a detected SSL connection. This will also add this structure to * the connections linked list. * * @param caddr Client address * @param cport Client port * @param saddr Server address * @param sport Server port * @return a pointer to a new allocated SSLConnection structure */ struct SSLConnection * tls_connection_create(struct in_addr caddr, uint16_t cport, struct in_addr saddr, uint16_t sport); /** * @brief Destroys an existing SSLConnection * * This will free all allocated memory of SSLConnection also removing * the connection from connections list. * * @param conn Existing connection pointer */ void tls_connection_destroy(struct SSLConnection *conn); /** * @brief Check if given keyfile is valid * * This can be used to check if a file contains valid RSA data * * @param keyfile Absolute path the keyfile * @return 1 if file contains RSA private info, 0 otherwise */ int tls_check_keyfile(const char *keyfile); /** * @brief Determines packet direction * * Determine if the given address is from client or server. * * @param conn Existing connection pointer * @param addr Client or server address * @param port Client or server port * @return 0 if address belongs to client, 1 to server or -1 otherwise */ int tls_connection_dir(struct SSLConnection *conn, struct in_addr addr, uint16_t port); /** * @brief Find a connection * * Try to find connection data for a given address and port. * This address:port convination can be the client or server one. * * @param addr Client or server address * @param port Client or server port * @return an existing Connection pointer or NULL if not found */ struct SSLConnection* tls_connection_find(struct in_addr src, uint16_t sport, struct in_addr dst, uint16_t dport); /** * @brief Process a TCP segment to check TLS data * * Check if a TCP segment contains TLS data. In case a TLS record is found * process it and return decrypted data if case of application_data record. * * @param tcp Pointer to tcp header of the packet * @param out Pointer to the output char array. Memory must be already allocated * @param out Number of bytes returned by this function * @return 0 in all cases */ int tls_process_segment(packet_t *packet, struct tcphdr *tcp); /** * @brief Process TLS record data * * Process a TLS record * - If the record type is Handshake process it in tls_process_record_handshake * - If the record type is Application Data process it in tls_process_record_data * * @param conn Existing connection pointer * @param payload Packet peyload * @param len Payload length * @param out pointer to store decryted data * @param outl decrypted data length * @return Decrypted data length */ int tls_process_record(struct SSLConnection *conn, const uint8_t *payload, const int len, uint8_t **out, uint32_t *outl); /** * @brief Check if this Record looks like SSLv2 * * Some devices send the initial ClientHello in a SSLv2 record for compatibility * with only SSLv2 protocol. * * We will only parse SSLv2 Client hello fragments that have TLSv1/SSLv3 content * so this does not make us SSLv2 compatible ;-p * @param conn Existing connection pointer * @param payload Packet peyload * @param len Payload length * @return 1 if payload seems a SSLv2 record, 0 otherwise */ int tls_record_handshake_is_ssl2(struct SSLConnection *conn, const uint8_t *payload, const int len); /** * @brief Process TLS Handshake SSLv2 record types * * Process all types of Handshake records to store and compute all required * data to decrypt application data packets * * @param conn Existing connection pointer * @param fragment Handshake record data * @param len Decimal length of the fragment * @return 0 on valid record processed, 1 otherwise */ int tls_process_record_ssl2(struct SSLConnection *conn, const uint8_t *payload, const int len, uint8_t **out, uint32_t *outl); /** * @brief Process TLS Handshake record types * * Process all types of Handshake records to store and compute all required * data to decrypt application data packets * * @param conn Existing connection pointer * @param fragment Handshake record data * @param len Decimal length of the fragment * @return 0 on valid record processed, 1 otherwise */ int tls_process_record_handshake(struct SSLConnection *conn, const opaque *fragment, const int len); /** * @brief Process TLS ApplicationData record types * * Process application data record, trying to decrypt it with connection * information * * @param conn Existing connection pointer * @param fragment Application record data * @param len record length in bytes * @param out pointer to store decryted data * @param outl decrypted data length * @return decoded data length */ int tls_process_record_data(struct SSLConnection *conn, const opaque *fragment, const int len, uint8_t **out, uint32_t *outl); /** * @brief Get the cipher data from the given connection * * Load cipher pointer depending on the selected cipher in * Handshake messages. * * This function can be used to test is a cipher decrypting is supported * @param conn Existing connection pointer * @return 0 on valid cipher, 1 otherwise */ int tls_connection_load_cipher(struct SSLConnection *conn); #endif sngrep-1.8.2/src/config.h.cmake000066400000000000000000000043451464272443000163200ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2023 Ivan Alonso (Kaian) ** Copyright (C) 2013-2023 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file config.h * @author Ivan Alonso [aka Kaian] * * @brief Generated from config.h.cmake by CMake * */ #ifndef __SNGREP_CONFIG_H #define __SNGREP_CONFIG_H /* Parameters of AC_INIT() */ #define PACKAGE "@AC_PACKAGE_NAME@" #define PACKAGE_BUGREPORT "@AC_PACKAGE_BUGREPORT@" #define PACKAGE_NAME "@AC_PACKAGE_NAME@" #define PACKAGE_STRING "@AC_PACKAGE_STRING@" #define PACKAGE_URL "@AC_PACKAGE_URL@" #define PACKAGE_VERSION "@AC_PACKAGE_VERSION@" #define VERSION "@AC_PACKAGE_VERSION@" /* Define if you have the `fopencookie' function */ #cmakedefine HAVE_FOPENCOOKIE /* Compile With Unicode compatibility */ #cmakedefine WITH_UNICODE /* Compile With GnuTLS compatibility */ #cmakedefine WITH_GNUTLS /* Compile With Openssl compatibility */ #cmakedefine WITH_OPENSSL /* Compile With Perl Compatible regular expressions support */ #cmakedefine WITH_PCRE /* Compile With Perl Compatible regular expressions support */ #cmakedefine WITH_PCRE2 /* Compile With zlib support */ #cmakedefine WITH_ZLIB /* Compile With IPv6 support */ #cmakedefine USE_IPV6 /* Compile With EEP support */ #cmakedefine USE_EEP /* CMAKE_CURRENT_BINARY_DIR is needed in tests/test_input.c */ #define CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@" #endif /* __SNGREP_CONFIG_H */ sngrep-1.8.2/src/curses/000077500000000000000000000000001464272443000151215ustar00rootroot00000000000000sngrep-1.8.2/src/curses/scrollbar.c000066400000000000000000000042101464272443000172450ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file scrollbar.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in scrollbar.h */ #include "config.h" #include "scrollbar.h" scrollbar_t ui_set_scrollbar(WINDOW *win, int alignment, int dock) { scrollbar_t sb; sb.win = win; sb.alignment = alignment; sb.dock = dock; return sb; } void ui_scrollbar_draw(scrollbar_t sb) { int height, width, cline, scrollen, scrollypos, scrollxpos; // Get window available space getmaxyx(sb.win, height, width); // If no even a screen has been filled, don't draw it if (sb.max < height) return; // Display the scrollbar left or right scrollxpos = (sb.dock == SB_LEFT) ? 0 : width - 1; // Initialize scrollbar line mvwvline(sb.win, 0, scrollxpos, ACS_VLINE, height); // How long the scroll will be if (!(scrollen = (height * 1.0f / sb.max * height) + 0.5)) scrollen = 1; // Where will the scroll start scrollypos = height * (sb.pos * 1.0f / sb.max); // Draw the N blocks of the scrollbar for (cline = 0; cline < scrollen; cline++) { mvwaddch(sb.win, cline + scrollypos, scrollxpos, ACS_CKBOARD); } } sngrep-1.8.2/src/curses/scrollbar.h000066400000000000000000000040641464272443000172610ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file scrollbar.h * @author Ivan Alonso [aka Kaian] * * @brief Scrollbar function and structures * */ #ifndef __SNGREP_SCROLLBAR_H #define __SNGREP_SCROLLBAR_H #ifdef WITH_UNICODE #define _X_OPEN_SOURCE_EXTENDED #include #endif #include //! Shorter declaration of scrollbar typedef struct scrollbar scrollbar_t; //! Available scrollbar alignments enum sb_alignment { SB_HORIZONTAL, SB_VERTICAL }; //! Available scrollbar positions enum sb_dock { SB_TOP, SB_BOTTOM, SB_LEFT, SB_RIGHT }; /** * @brief Window scrollbar * * This struct contains the required information to draw * a scrollbar into a ncurses window */ struct scrollbar { //! Ncurses window associated to this scrollbar WINDOW *win; //! Alignment enum sb_alignment alignment; //! Position enum sb_dock dock; //! Current scrollbar position int pos; //! Max scrollbar positions int max; }; scrollbar_t ui_set_scrollbar(WINDOW *win, int alignment, int dock); void ui_scrollbar_draw(scrollbar_t sb); #endif /* __SNGREP_SCROLLBAR_H */ sngrep-1.8.2/src/curses/theme.h000066400000000000000000000032721464272443000164000ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file theme.h * @author Ivan Alonso [aka Kaian] * * @brief Theme and Color definitions * */ #ifndef __SNGREP_THEME_H #define __SNGREP_THEME_H /** * @brief Enum for available color pairs */ enum sngrep_colors_pairs { CP_DEFAULT = 0, CP_CYAN_ON_DEF, CP_YELLOW_ON_DEF, CP_MAGENTA_ON_DEF, CP_GREEN_ON_DEF, CP_RED_ON_DEF, CP_BLUE_ON_DEF, CP_WHITE_ON_DEF, CP_DEF_ON_CYAN, CP_DEF_ON_BLUE, CP_WHITE_ON_BLUE, CP_BLACK_ON_CYAN, CP_WHITE_ON_CYAN, CP_YELLOW_ON_CYAN, CP_BLUE_ON_CYAN, CP_BLUE_ON_WHITE, CP_CYAN_ON_BLACK, CP_CYAN_ON_WHITE, }; // Used to configure color pairs only with fg color #define COLOR_DEFAULT -1 #endif /* __SNGREP_THEME_H */ sngrep-1.8.2/src/curses/ui_call_flow.c000066400000000000000000001505311464272443000177310ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_call_flow.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in ui_call_flow.h */ #include "config.h" #include #include #include "capture.h" #include "ui_manager.h" #include "ui_call_flow.h" #include "ui_call_raw.h" #include "ui_msg_diff.h" #include "ui_save.h" #include "util.h" #include "vector.h" #include "option.h" #define METHOD_MAXLEN 80 /*** * * Some basic ascii art of this panel. * * +--------------------------------------------------------+ * | Title | * | addr1 addr2 addr3 addr4 | Selected Raw Message | * | ----- ----- ----- ----- | preview | * | Tmst| | | | | | * | Tmst|----->| | | | | * | Tmst| |----->| | | | * | Tmst| |<-----| | | | * | Tmst| | |----->| | | * | Tmst|<-----| | | | | * | Tmst| |----->| | | | * | Tmst| |<-----| | | | * | Tmst| |------------>| | | * | Tmst| |<------------| | | * | | | | | | | * | | | | | | | * | | | | | | | * | Useful hotkeys | * +--------------------------------------------------------+ * */ /** * Ui Structure definition for Call Flow panel */ ui_t ui_call_flow = { .type = PANEL_CALL_FLOW, .panel = NULL, .create = call_flow_create, .destroy = call_flow_destroy, .redraw = call_flow_redraw, .draw = call_flow_draw, .handle_key = call_flow_handle_key, .help = call_flow_help }; void call_flow_create(ui_t *ui) { // Create a new panel to fill all the screen ui_panel_create(ui, LINES, COLS); // Initialize Call List specific data call_flow_info_t *info = malloc(sizeof(call_flow_info_t)); memset(info, 0, sizeof(call_flow_info_t)); // Display timestamp next to each arrow info->arrowtime = true; // Calculate available printable area for messages info->flow_win = subwin(ui->win, ui->height - 6, ui->width - 2, 4, 0); info->scroll = ui_set_scrollbar(info->flow_win, SB_VERTICAL, SB_LEFT); // Create vectors for columns and flow arrows info->columns = vector_create(2, 1); info->arrows = vector_create(20, 5); vector_set_sorter(info->arrows, call_flow_arrow_sorter); // Store it into panel userptr set_panel_userptr(ui->panel, (void*) info); } void call_flow_destroy(ui_t *ui) { call_flow_info_t *info; // Free the panel information if ((info = call_flow_info(ui))) { // Delete panel columns vector_destroy_items(info->columns); // Delete panel arrows vector_destroy_items(info->arrows); // Delete panel windows delwin(info->flow_win); delwin(info->raw_win); // Delete displayed call group call_group_destroy(info->group); // Free panel info free(info); } ui_panel_destroy(ui); } call_flow_info_t * call_flow_info(ui_t *ui) { return (call_flow_info_t*) panel_userptr(ui->panel); } bool call_flow_redraw(ui_t *ui) { int maxx, maxy; // Get panel information call_flow_info_t *info = call_flow_info(ui); // Get current screen dimensions getmaxyx(stdscr, maxy, maxx); // Change the main window size wresize(ui->win, maxy, maxx); // Store new size ui->width = maxx; ui->height = maxy; // Calculate available printable area wresize(info->flow_win, maxy - 6, maxx); // Force flow redraw call_flow_draw(ui); // Check if any of the group has changed // return call_group_has_changed(info->group); return 0; } int call_flow_draw(ui_t *ui) { char title[256]; // Get panel information call_flow_info_t *info = call_flow_info(ui); // Get window of main panel werase(ui->win); // Set title if (info->group->callid) { sprintf(title, "Extended Call flow for %s", info->group->callid); } else if (call_group_count(info->group) == 1) { sip_call_t *call = call_group_get_next(info->group, NULL); sprintf(title, "Call flow for %s", call->callid); } else { sprintf(title, "Call flow for %d dialogs", call_group_count(info->group)); } // Print color mode in title if (setting_has_value(SETTING_COLORMODE, "request")) strcat(title, " (Color by Request/Response)"); if (setting_has_value(SETTING_COLORMODE, "callid")) strcat(title, " (Color by Call-Id)"); if (setting_has_value(SETTING_COLORMODE, "cseq")) strcat(title, " (Color by CSeq)"); // Draw panel title ui_set_title(ui, title); // Show some keybinding call_flow_draw_footer(ui); // Redraw columns call_flow_draw_columns(ui); // Redraw arrows call_flow_draw_arrows(ui); // Redraw preview call_flow_draw_preview(ui); // Draw the scrollbar vector_iter_t it = vector_iterator(info->darrows); call_flow_arrow_t *arrow = NULL; info->scroll.max = info->scroll.pos = 0; while ((arrow = vector_iterator_next(&it))) { // Store current position arrow if (vector_iterator_current(&it) == info->first_arrow) { info->scroll.pos = info->scroll.max; } info->scroll.max += call_flow_arrow_height(ui, arrow); } ui_scrollbar_draw(info->scroll); // Redraw flow win wnoutrefresh(info->flow_win); return 0; } void call_flow_draw_footer(ui_t *ui) { const char *keybindings[] = { key_action_key_str(ACTION_PREV_SCREEN), "Calls List", key_action_key_str(ACTION_CONFIRM), "Raw", key_action_key_str(ACTION_SELECT), "Compare", key_action_key_str(ACTION_SHOW_HELP), "Help", key_action_key_str(ACTION_SDP_INFO), "SDP", key_action_key_str(ACTION_TOGGLE_MEDIA), "RTP", key_action_key_str(ACTION_SHOW_FLOW_EX), "Extended", key_action_key_str(ACTION_COMPRESS), "Compressed", key_action_key_str(ACTION_SHOW_RAW), "Raw", key_action_key_str(ACTION_CYCLE_COLOR), "Colour by", key_action_key_str(ACTION_INCREASE_RAW), "Increase Raw" }; ui_draw_bindings(ui, keybindings, 22); } int call_flow_draw_columns(ui_t *ui) { call_flow_info_t *info; call_flow_column_t *column; sip_call_t *call = NULL; rtp_stream_t *stream; sip_msg_t *msg = NULL; vector_iter_t streams; vector_iter_t columns; char coltext[MAX_SETTING_LEN]; address_t addr; // Get panel information info = call_flow_info(ui); // In extended call flow, columns can have multiple call-ids if (info->group->callid) { info->maxcallids = call_group_count(info->group); } else { info->maxcallids = 2; } // Load columns while((msg = call_group_get_next_msg(info->group, msg))) { call_flow_column_add(ui, msg->call->callid, msg->packet->src); call_flow_column_add(ui, msg->call->callid, msg->packet->dst); } // Add RTP columns FIXME Really if (!setting_disabled(SETTING_CF_MEDIA)) { while ((call = call_group_get_next(info->group, call)) ) { streams = vector_iterator(call->streams); while ((stream = vector_iterator_next(&streams))) { if (stream->type == PACKET_RTP && stream_get_count(stream)) { addr = stream->src; addr.port = 0; call_flow_column_add(ui, NULL, addr); addr = stream->dst; addr.port = 0; call_flow_column_add(ui, NULL, addr); } } } } // Draw columns columns = vector_iterator(info->columns); while ((column = vector_iterator_next(&columns))) { mvwvline(info->flow_win, 0, 20 + 30 * column->colpos, ACS_VLINE, ui->height - 6); mvwhline(ui->win, 3, 10 + 30 * column->colpos, ACS_HLINE, 20); mvwaddch(ui->win, 3, 20 + 30 * column->colpos, ACS_TTEE); // Set bold to this address if it's local if (setting_enabled(SETTING_CF_LOCALHIGHLIGHT)) { if (address_is_local(column->addr)) wattron(ui->win, A_BOLD); } if (setting_enabled(SETTING_CF_SPLITCALLID) || !column->addr.port) { snprintf(coltext, MAX_SETTING_LEN, "%s", column->alias); } else if (setting_enabled(SETTING_DISPLAY_ALIAS)) { if (strlen(column->addr.ip) > 15) { snprintf(coltext, MAX_SETTING_LEN, "..%.*s:%u", MAX_SETTING_LEN - 9, column->alias + strlen(column->alias) - 13, column->addr.port); } else { snprintf(coltext, MAX_SETTING_LEN, "%.*s:%u", MAX_SETTING_LEN - 7, column->alias, column->addr.port); } } else { if (strlen(column->addr.ip) > 15) { snprintf(coltext, MAX_SETTING_LEN, "..%.*s:%u", MAX_SETTING_LEN - 9, column->addr.ip + strlen(column->addr.ip) - 13, column->addr.port); } else { snprintf(coltext, MAX_SETTING_LEN, "%.*s:%u", MAX_SETTING_LEN - 7, column->addr.ip, column->addr.port); } } mvwprintw(ui->win, 2, 10 + 30 * column->colpos + (22 - strlen(coltext)) / 2, "%s", coltext); wattroff(ui->win, A_BOLD); } return 0; } void call_flow_draw_arrows(ui_t *ui) { call_flow_info_t *info; call_flow_arrow_t *arrow = NULL; int cline = 0; // Get panel information info = call_flow_info(ui); // Create pending SIP arrows sip_msg_t *msg = NULL; while ((msg = call_group_get_next_msg(info->group, msg))) { if (!call_flow_arrow_find(ui, msg)) { arrow = call_flow_arrow_create(ui, msg, CF_ARROW_SIP); vector_append(info->arrows, arrow); } } // Create pending RTP arrows rtp_stream_t *stream = NULL; while ((stream = call_group_get_next_stream(info->group, stream))) { if (!call_flow_arrow_find(ui, stream)) { arrow = call_flow_arrow_create(ui, stream, CF_ARROW_RTP); vector_append(info->arrows, arrow); } } // Copy displayed arrows // vector_destroy(info->darrows); //info->darrows = vector_copy_if(info->arrows, call_flow_arrow_filter); info->darrows = info->arrows; // If no active call, use the fist one (if exists) if (info->cur_arrow == -1 && vector_count(info->darrows)) { info->cur_arrow = info->first_arrow = 0; } // Draw arrows vector_iter_t it = vector_iterator(info->darrows); vector_iterator_set_current(&it, info->first_arrow - 1); vector_iterator_set_filter(&it, call_flow_arrow_filter); while ((arrow = vector_iterator_next(&it))) { // Stop if we have reached the bottom of the screen if (cline >= getmaxy(info->flow_win)) break; // Draw arrow cline += call_flow_draw_arrow(ui, arrow, cline); } } int call_flow_draw_arrow(ui_t *ui, call_flow_arrow_t *arrow, int line) { if (arrow->type == CF_ARROW_SIP) { return call_flow_draw_message(ui, arrow, line); } else { return call_flow_draw_rtp_stream(ui, arrow, line); } } void call_flow_draw_preview(ui_t *ui) { call_flow_arrow_t *arrow = NULL; call_flow_info_t *info; // Check if not displaying raw has been requested if (setting_disabled(SETTING_CF_FORCERAW)) return; // Get panel information info = call_flow_info(ui); // Draw current arrow preview if ((arrow = vector_item(info->darrows, info->cur_arrow))) { if (arrow->type == CF_ARROW_SIP) { call_flow_draw_raw(ui, arrow->item); } else { call_flow_draw_raw_rtcp(ui, arrow->item); } } } int call_flow_draw_message(ui_t *ui, call_flow_arrow_t *arrow, int cline) { call_flow_info_t *info; WINDOW *flow_win; sdp_media_t *media; const char *callid; char msg_method[SIP_ATTR_MAXLEN]; char msg_time[80]; address_t src; address_t dst; char method[METHOD_MAXLEN + 1]; char delta[15] = {}; int flowh; char mediastr[40]; sip_msg_t *msg = arrow->item; vector_iter_t medias; int color = 0; int msglen; int aline = cline + 1; // Initialize method memset(method, 0, sizeof(method)); // Get panel information info = call_flow_info(ui); // Get the messages window flow_win = info->flow_win; flowh = getmaxx(flow_win); // Store arrow start line arrow->line = cline; // Calculate how many lines this message requires arrow->height = call_flow_arrow_height(ui, arrow); // Check this message fits on the panel if (cline > flowh + arrow->height) return 0; // For extended, use xcallid nstead callid = msg->call->callid; src = msg->packet->src; dst = msg->packet->dst; media = vector_first(msg->medias); msg_get_attribute(msg, SIP_ATTR_METHOD, msg_method); timeval_to_time(msg_get_time(msg), msg_time); // Get Message method (include extra info) snprintf(method, METHOD_MAXLEN, "%.*s", METHOD_MAXLEN-1, msg_method); // If message has sdp information if (msg_has_sdp(msg) && setting_has_value(SETTING_CF_SDP_INFO, "off")) { // Show sdp tag in title snprintf(method, METHOD_MAXLEN, "%.*s (SDP)", METHOD_MAXLEN-7, msg_method ); } // If message has sdp information if (setting_has_value(SETTING_CF_SDP_INFO, "compressed")) { // Show sdp tag in title if (msg_has_sdp(msg)) { snprintf(method, METHOD_MAXLEN, "%.*s (SDP)", 12, msg_method); } else { snprintf(method, METHOD_MAXLEN, "%.*s", 17, msg_method); } } if (msg_has_sdp(msg) && setting_has_value(SETTING_CF_SDP_INFO, "first")) { snprintf(method, METHOD_MAXLEN, "%.3s (%s:%u)", msg_method, media->address.ip, media->address.port); } if (msg_has_sdp(msg) && setting_has_value(SETTING_CF_SDP_INFO, "full")) { snprintf(method, METHOD_MAXLEN, "%.3s (%s)", msg_method, media->address.ip); } // Draw message type or status and line msglen = (strlen(method) > 24) ? 24 : strlen(method); // Get origin and destination column arrow->scolumn = call_flow_column_get(ui, callid, src); arrow->dcolumn = call_flow_column_get(ui, callid, dst); // Determine start and end position of the arrow line int startpos, endpos; if (arrow->scolumn == arrow->dcolumn) { arrow->dir = CF_ARROW_SPIRAL; startpos = 19 + 30 * arrow->dcolumn->colpos; endpos = 20 + 30 * arrow->scolumn->colpos; } else if (arrow->scolumn->colpos < arrow->dcolumn->colpos) { arrow->dir = CF_ARROW_RIGHT; startpos = 20 + 30 * arrow->scolumn->colpos; endpos = 20 + 30 * arrow->dcolumn->colpos; } else { arrow->dir = CF_ARROW_LEFT; startpos = 20 + 30 * arrow->dcolumn->colpos; endpos = 20 + 30 * arrow->scolumn->colpos; } int distance = abs(endpos - startpos) - 3; // Highlight current message if (arrow == vector_item(info->darrows, info->cur_arrow)) { if (setting_has_value(SETTING_CF_HIGHTLIGHT, "reverse")) { wattron(flow_win, A_REVERSE); } if (setting_has_value(SETTING_CF_HIGHTLIGHT, "bold")) { wattron(flow_win, A_BOLD); } if (setting_has_value(SETTING_CF_HIGHTLIGHT, "reversebold")) { wattron(flow_win, A_REVERSE); wattron(flow_win, A_BOLD); } } // Color the message { if (setting_has_value(SETTING_COLORMODE, "request")) { // Color by request / response color = (msg_is_request(msg)) ? CP_RED_ON_DEF : CP_GREEN_ON_DEF; } else if (setting_has_value(SETTING_COLORMODE, "callid")) { // Color by call-id color = call_group_color(info->group, msg->call); } else if (setting_has_value(SETTING_COLORMODE, "cseq")) { // Color by CSeq within the same call color = msg->cseq % 7 + 1; } // Print arrow in the same line than message if (setting_has_value(SETTING_CF_SDP_INFO, "compressed")) { aline = cline; } // Turn on the message color wattron(flow_win, COLOR_PAIR(color)); // Clear the line mvwprintw(flow_win, cline, startpos + 2, "%*s", distance, ""); // Draw method if (arrow->dir == CF_ARROW_SPIRAL) { mvwprintw(flow_win, cline, startpos + 5, "%.26s", method); } else { mvwprintw(flow_win, cline, startpos + distance / 2 - msglen / 2 + 2, "%.26s", method); } // Draw media information if (msg_has_sdp(msg) && setting_has_value(SETTING_CF_SDP_INFO, "full")) { medias = vector_iterator(msg->medias); while ((media = vector_iterator_next(&medias))) { sprintf(mediastr, "%s %d (%s)", media->type, media->address.port, media_get_prefered_format(media)); if (arrow->dir == CF_ARROW_SPIRAL) { mvwprintw(flow_win, cline + 1, startpos + 5, "%s", mediastr); } else { mvwprintw(flow_win, cline + 1, startpos + distance / 2 - strlen(mediastr) / 2 + 2, "%s", mediastr); } cline++; aline++; } } if (arrow->dir != CF_ARROW_SPIRAL) { if (arrow == call_flow_arrow_selected(ui)) { mvwhline(flow_win, aline, startpos + 2, '=', distance); } else { mvwhline(flow_win, aline, startpos + 2, ACS_HLINE, distance); } } // Write the arrow at the end of the message (two arrows if this is a retrans) if (arrow->dir == CF_ARROW_SPIRAL) { mvwaddch(flow_win, aline, startpos + 2, '<'); if (msg->retrans) { mvwaddch(flow_win, aline, startpos + 3, '<'); mvwaddch(flow_win, aline, startpos + 4, '<'); } // If multiple lines are available, print a spiral icon if (aline != cline) { mvwaddch(flow_win, aline, startpos + 3, ACS_LRCORNER); mvwaddch(flow_win, aline - 1, startpos + 3, ACS_URCORNER); mvwaddch(flow_win, aline - 1, startpos + 2, ACS_HLINE); } } else if (arrow->dir == CF_ARROW_RIGHT) { mvwaddch(flow_win, aline, endpos - 2, '>'); if (msg->retrans) { mvwaddch(flow_win, aline, endpos - 3, '>'); mvwaddch(flow_win, aline, endpos - 4, '>'); } } else { mvwaddch(flow_win, aline, startpos + 2, '<'); if (msg->retrans) { mvwaddch(flow_win, aline, startpos + 3, '<'); mvwaddch(flow_win, aline, startpos + 4, '<'); } } if (setting_has_value(SETTING_CF_SDP_INFO, "compressed")) mvwprintw(flow_win, cline, startpos + distance / 2 - msglen / 2 + 2, " %.26s ", method); // Turn off colors wattroff(flow_win, COLOR_PAIR(CP_RED_ON_DEF)); wattroff(flow_win, COLOR_PAIR(CP_GREEN_ON_DEF)); wattroff(flow_win, COLOR_PAIR(CP_CYAN_ON_DEF)); wattroff(flow_win, COLOR_PAIR(CP_YELLOW_ON_DEF)); wattroff(flow_win, A_BOLD | A_REVERSE); // Print timestamp if (info->arrowtime) { if (arrow == call_flow_arrow_selected(ui)) wattron(flow_win, COLOR_PAIR(CP_CYAN_ON_DEF)); if (arrow == vector_item(info->darrows, info->cur_arrow)) { wattron(flow_win, A_BOLD); mvwprintw(flow_win, cline, 2, "%s", msg_time); wattroff(flow_win, A_BOLD); } else { mvwprintw(flow_win, cline, 2, "%s", msg_time); } // Print delta from selected message if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed")) { if (info->selected == -1) { if (setting_enabled(SETTING_CF_DELTA)) { struct timeval selts, curts; selts = msg_get_time(call_group_get_prev_msg(info->group, msg)); curts = msg_get_time(msg); timeval_to_delta(selts, curts, delta); } } else if (arrow == vector_item(info->darrows, info->cur_arrow)) { struct timeval selts, curts; selts = msg_get_time(call_flow_arrow_message(call_flow_arrow_selected(ui))); curts = msg_get_time(msg); timeval_to_delta(selts, curts, delta); } if (strlen(delta)) { wattron(flow_win, COLOR_PAIR(CP_CYAN_ON_DEF)); mvwprintw(flow_win, cline - 1 , 2, "%15s", delta); } wattroff(flow_win, COLOR_PAIR(CP_CYAN_ON_DEF)); } } wattroff(flow_win, COLOR_PAIR(CP_CYAN_ON_DEF)); return arrow->height; } int call_flow_draw_rtp_stream(ui_t *ui, call_flow_arrow_t *arrow, int cline) { call_flow_info_t *info; WINDOW *win; char text[50], time[20]; int height; rtp_stream_t *stream = arrow->item; sip_msg_t *msg; sip_call_t *call; call_flow_arrow_t *msgarrow; address_t addr; // Get panel information info = call_flow_info(ui); // Get the messages window win = info->flow_win; height = getmaxy(win); // Store arrow start line arrow->line = cline; // Calculate how many lines this message requires arrow->height = call_flow_arrow_height(ui, arrow); // Check this media fits on the panel if (cline > height + arrow->height) return 0; // Get arrow text sprintf(text, "RTP (%s) %d", stream_get_format(stream), stream_get_count(stream)); // Get message data call = stream->media->msg->call; /** * This logic will try to use the same columns for the stream representation * that are used in the SIP messages that configured the streams in their SDP * if they share the same IP addresses. */ // Message with Stream destination configured in SDP content msg = stream->media->msg; // If message and stream share the same IP address if (address_equals(msg->packet->src, stream->dst)) { // Reuse the msg arrow columns as destination column if ((msgarrow = call_flow_arrow_find(ui, msg))) { // Get origin and destination column arrow->dcolumn = call_flow_column_get(ui, msg->call->callid, msg->packet->src); } } // fallback: Just use any column that have the destination IP printed if (!arrow->dcolumn) { // FIXME figure a better way to find ignoring port :( addr = stream->dst; addr.port = 0; arrow->dcolumn = call_flow_column_get(ui, 0, addr); } /** * For source address of the stream, first try to find a message that have * the stream source configured in their SDP as destination and then apply * the same previous logic address: if IP address matches, reuse message * column, othwerise any column with the source IP will be used. */ // Message with Stream source configured in SDP content msg = call_msg_with_media(call, stream->src); // Try to find a message with configured SDP matching the source of this stream if (msg && address_equals(msg->packet->src, stream->src)) { // Reuse the msg arrow columns as destination column if ((msgarrow = call_flow_arrow_find(ui, msg))) { // Get origin and destination column arrow->scolumn = call_flow_column_get(ui, msg->call->callid, msg->packet->src); } } // Prefer message that configured this stream rather than any column if (!arrow->scolumn) { msg = stream->media->msg; // If message and stream share the same IP address if (address_equals(msg->packet->dst, stream->src)) { // Reuse the msg arrow columns as destination column if ((msgarrow = call_flow_arrow_find(ui, msg))) { arrow->scolumn = call_flow_column_get(ui, msg->call->callid, msg->packet->dst); } } } // fallback: Just use any column that have the soruce IP printed if (!arrow->scolumn) { // FIXME figure a better way to find ignoring port :( addr = stream->src; addr.port = 0; arrow->scolumn = call_flow_column_get(ui, 0, addr); } // Determine start and end position of the arrow line int startpos, endpos; if (arrow->scolumn->colpos < arrow->dcolumn->colpos) { arrow->dir= CF_ARROW_RIGHT; startpos = 20 + 30 * arrow->scolumn->colpos; endpos = 20 + 30 * arrow->dcolumn->colpos; } else { arrow->dir = CF_ARROW_LEFT; startpos = 20 + 30 * arrow->dcolumn->colpos; endpos = 20 + 30 * arrow->scolumn->colpos; } int distance = 0; if (startpos != endpos) { // In compressed mode, we display the src and dst port inside the arrow // so fixup the stard and end position if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed")) { startpos += 5; endpos -= 5; } distance = abs(endpos - startpos) - 4 + 1; } else { // Fix port positions startpos -= 2; endpos += 2; distance = 1; // Fix arrow direction based on ports if (stream->src.port < stream->dst.port) { arrow->dir = CF_ARROW_RIGHT; } else { arrow->dir = CF_ARROW_LEFT; } } // Highlight current message if (arrow == vector_item(info->darrows, info->cur_arrow)) { if (setting_has_value(SETTING_CF_HIGHTLIGHT, "reverse")) { wattron(win, A_REVERSE); } if (setting_has_value(SETTING_CF_HIGHTLIGHT, "bold")) { wattron(win, A_BOLD); } if (setting_has_value(SETTING_CF_HIGHTLIGHT, "reversebold")) { wattron(win, A_REVERSE); wattron(win, A_BOLD); } } // Check if displayed stream is active int active = stream_is_active(stream); // Clear the line mvwprintw(win, cline, startpos + 2, "%*s", distance, ""); // Draw RTP arrow text mvwprintw(win, cline, startpos + (distance) / 2 - strlen(text) / 2 + 2, "%s", text); if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed")) cline++; // Draw line between columns if (active) mvwhline(win, cline, startpos + 2, '-', distance); else mvwhline(win, cline, startpos + 2, ACS_HLINE, distance); // Write the arrow at the end of the message (two arrows if this is a retrans) if (arrow->dir == CF_ARROW_RIGHT) { if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed")) { mvwprintw(win, cline, startpos - 4, "%d", stream->src.port); mvwprintw(win, cline, endpos, "%d", stream->dst.port); } mvwaddch(win, cline, endpos - 2, '>'); if (active) { arrow->rtp_count = stream_get_count(stream); arrow->rtp_ind_pos = (arrow->rtp_ind_pos + 1) % distance; mvwaddch(win, cline, startpos + arrow->rtp_ind_pos + 2, '>'); } } else { if (!setting_has_value(SETTING_CF_SDP_INFO, "compressed")) { mvwprintw(win, cline, endpos, "%d", stream->src.port); mvwprintw(win, cline, startpos - 4, "%d", stream->dst.port); } mvwaddch(win, cline, startpos + 2, '<'); if (active) { arrow->rtp_count = stream_get_count(stream); arrow->rtp_ind_pos = (arrow->rtp_ind_pos + 1) % distance; mvwaddch(win, cline, endpos - arrow->rtp_ind_pos - 2, '<'); } } if (setting_has_value(SETTING_CF_SDP_INFO, "compressed")) mvwprintw(win, cline, startpos + (distance) / 2 - strlen(text) / 2 + 2, " %s ", text); wattroff(win, A_BOLD | A_REVERSE); // Print timestamp if (info->arrowtime) { timeval_to_time(stream->time, time); if (arrow == vector_item(info->darrows, info->cur_arrow)) { wattron(win, A_BOLD); mvwprintw(win, cline, 2, "%s", time); wattroff(win, A_BOLD); } else { mvwprintw(win, cline, 2, "%s", time); } } return arrow->height; } call_flow_arrow_t * call_flow_arrow_create(ui_t *ui, void *item, int type) { call_flow_arrow_t *arrow; if ((arrow = call_flow_arrow_find(ui, item))) return arrow; // Create a new arrow of the given type arrow = malloc(sizeof(call_flow_arrow_t)); memset(arrow, 0, sizeof(call_flow_arrow_t)); arrow->type = type; arrow->item = item; return arrow; } int call_flow_arrow_height(ui_t *ui, const call_flow_arrow_t *arrow) { if (arrow->type == CF_ARROW_SIP) { if (setting_enabled(SETTING_CF_ONLYMEDIA)) return 0; if (setting_has_value(SETTING_CF_SDP_INFO, "compressed")) return 1; if (!msg_has_sdp(arrow->item)) return 2; if (setting_has_value(SETTING_CF_SDP_INFO, "off")) return 2; if (setting_has_value(SETTING_CF_SDP_INFO, "first")) return 2; if (setting_has_value(SETTING_CF_SDP_INFO, "full")) return msg_media_count(arrow->item) + 2; } else if (arrow->type == CF_ARROW_RTP || arrow->type == CF_ARROW_RTCP) { if (setting_has_value(SETTING_CF_SDP_INFO, "compressed")) return 1; if (setting_disabled(SETTING_CF_MEDIA)) return 0; return 2; } return 0; } call_flow_arrow_t * call_flow_arrow_find(ui_t *ui, const void *data) { call_flow_info_t *info; call_flow_arrow_t *arrow; vector_iter_t arrows; if (!data) return NULL; if (!(info = call_flow_info(ui))) return NULL; arrows = vector_iterator(info->arrows); while ((arrow = vector_iterator_next(&arrows))) if (arrow->item == data) return arrow; return arrow; } sip_msg_t * call_flow_arrow_message(const call_flow_arrow_t *arrow) { if (!arrow) return NULL; if (arrow->type == CF_ARROW_SIP) { return arrow->item; } if (arrow->type == CF_ARROW_RTP) { rtp_stream_t *stream = arrow->item; return stream->media->msg; } return NULL; } int call_flow_draw_raw(ui_t *ui, sip_msg_t *msg) { call_flow_info_t *info; WINDOW *raw_win; vector_iter_t arrows; call_flow_arrow_t *arrow; int raw_width, raw_height; int min_raw_width, fixed_raw_width; // Get panel information if (!(info = call_flow_info(ui))) return 1; // Get min raw width min_raw_width = setting_get_intvalue(SETTING_CF_RAWMINWIDTH); fixed_raw_width = setting_get_intvalue(SETTING_CF_RAWFIXEDWIDTH); // Calculate the raw data width (width - used columns for flow - vertical lines) raw_width = ui->width - (30 * vector_count(info->columns)) - 2; // If last column has spirals, add an extra column with arrows = vector_iterator(info->arrows); while ((arrow = vector_iterator_next(&arrows))) { if (arrow->dir == CF_ARROW_SPIRAL && arrow->scolumn == vector_last(info->columns)) { raw_width -= 15; break; } } // We can define a mininum size for rawminwidth if (raw_width < min_raw_width) { raw_width = min_raw_width; } // We can configure an exact raw size if (fixed_raw_width > 0) { raw_width = fixed_raw_width; } // Height of raw window is always available size minus 6 lines for header/footer raw_height = ui->height - 3; // If we already have a raw window raw_win = info->raw_win; if (raw_win) { // Check it has the correct size if (getmaxx(raw_win) != raw_width) { // We need a new raw window delwin(raw_win); info->raw_win = raw_win = newwin(raw_height, raw_width, 0, 0); } else { // We have a valid raw win, clear its content werase(raw_win); } } else { // Create the raw window of required size info->raw_win = raw_win = newwin(raw_height, raw_width, 0, 0); } // Draw raw box li wattron(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); mvwvline(ui->win, 1, ui->width - raw_width - 2, ACS_VLINE, ui->height - 2); wattroff(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); // Print msg payload draw_message(info->raw_win, msg); // Copy the raw_win contents into the panel copywin(raw_win, ui->win, 0, 0, 1, ui->width - raw_width - 1, raw_height, ui->width - 2, 0); return 0; } int call_flow_draw_raw_rtcp(ui_t *ui, rtp_stream_t *stream) { /** * TODO This is too experimental to even display it */ return 0; call_flow_info_t *info; WINDOW *raw_win; int raw_width, raw_height; int min_raw_width, fixed_raw_width; // Get panel information if (!(info = call_flow_info(ui))) return 1; // Get min raw width min_raw_width = setting_get_intvalue(SETTING_CF_RAWMINWIDTH); fixed_raw_width = setting_get_intvalue(SETTING_CF_RAWFIXEDWIDTH); // Calculate the raw data width (width - used columns for flow - vertical lines) raw_width = ui->width - (30 * vector_count(info->columns)) - 2; // We can define a mininum size for rawminwidth if (raw_width < min_raw_width) { raw_width = min_raw_width; } // We can configure an exact raw size if (fixed_raw_width > 0) { raw_width = fixed_raw_width; } // Height of raw window is always available size minus 6 lines for header/footer raw_height = ui->height - 3; // If we already have a raw window raw_win = info->raw_win; if (raw_win) { // Check it has the correct size if (getmaxx(raw_win) != raw_width) { // We need a new raw window delwin(raw_win); info->raw_win = raw_win = newwin(raw_height, raw_width, 0, 0); } else { // We have a valid raw win, clear its content werase(raw_win); } } else { // Create the raw window of required size info->raw_win = raw_win = newwin(raw_height, raw_width, 0, 0); } // Draw raw box lines wattron(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); mvwvline(ui->win, 1, ui->width - raw_width - 2, ACS_VLINE, ui->height - 2); wattroff(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); mvwprintw(raw_win, 0, 0, "============ RTCP Information ============"); mvwprintw(raw_win, 2, 0, "Sender's packet count: %d", stream->rtcpinfo.spc); mvwprintw(raw_win, 3, 0, "Fraction Lost: %d / 256", stream->rtcpinfo.flost); mvwprintw(raw_win, 4, 0, "Fraction discarded: %d / 256", stream->rtcpinfo.fdiscard); mvwprintw(raw_win, 6, 0, "MOS - Listening Quality: %.1f", (float) stream->rtcpinfo.mosl / 10); mvwprintw(raw_win, 7, 0, "MOS - Conversational Quality: %.1f", (float) stream->rtcpinfo.mosc / 10); // Copy the raw_win contents into the panel copywin(raw_win, ui->win, 0, 0, 1, ui->width - raw_width - 1, raw_height, ui->width - 2, 0); return 0; } int call_flow_handle_key(ui_t *ui, int key) { int raw_width; call_flow_info_t *info = call_flow_info(ui); ui_t *next_ui; sip_call_t *call = NULL, *xcall = NULL; int rnpag_steps = setting_get_intvalue(SETTING_CF_SCROLLSTEP); int action = -1; // Sanity check, this should not happen if (!info) return KEY_NOT_HANDLED; // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { // Check if we handle this action switch(action) { case ACTION_DOWN: call_flow_move(ui, info->cur_arrow + 1); break; case ACTION_UP: call_flow_move(ui, info->cur_arrow - 1); break; case ACTION_HNPAGE: rnpag_steps = rnpag_steps / 2; /* no break */ case ACTION_NPAGE: call_flow_move(ui, info->cur_arrow + rnpag_steps); break; case ACTION_HPPAGE: rnpag_steps = rnpag_steps / 2; /* no break */ case ACTION_PPAGE: // Prev page => N key up strokes call_flow_move(ui, info->cur_arrow - rnpag_steps); break; case ACTION_BEGIN: call_flow_move(ui, 0); break; case ACTION_END: call_flow_move(ui, vector_count(info->darrows)); break; case ACTION_SHOW_FLOW_EX: werase(ui->win); if (call_group_count(info->group) == 1) { call = vector_first(info->group->calls); if (call->xcallid != NULL && strlen(call->xcallid)) { if ((xcall = sip_find_by_callid(call->xcallid))) { call_group_del(info->group, call); call_group_add(info->group, xcall); call_group_add_calls(info->group, xcall->xcalls); info->group->callid = xcall->callid; } } else { call_group_add_calls(info->group, call->xcalls); info->group->callid = call->callid; } } else { call = vector_first(info->group->calls); vector_clear(info->group->calls); call_group_add(info->group, call); info->group->callid = 0; } call_flow_set_group(info->group); break; case ACTION_SHOW_RAW: // KEY_R, display current call in raw mode ui_create_panel(PANEL_CALL_RAW); call_raw_set_group(info->group); break; case ACTION_DECREASE_RAW: raw_width = getmaxx(info->raw_win); if (raw_width - 2 > 1) { setting_set_intvalue(SETTING_CF_RAWFIXEDWIDTH, raw_width - 2); } break; case ACTION_INCREASE_RAW: raw_width = getmaxx(info->raw_win); if (raw_width + 2 < COLS - 1) { setting_set_intvalue(SETTING_CF_RAWFIXEDWIDTH, raw_width + 2); } break; case ACTION_RESET_RAW: setting_set_intvalue(SETTING_CF_RAWFIXEDWIDTH, -1); break; case ACTION_ONLY_SDP: // Toggle SDP mode info->group->sdp_only = !(info->group->sdp_only); // Disable sdp_only if there are not messages with sdp if (call_group_msg_count(info->group) == 0) info->group->sdp_only = 0; // Reset screen call_flow_set_group(info->group); break; case ACTION_SDP_INFO: setting_toggle(SETTING_CF_SDP_INFO); break; case ACTION_ONLY_MEDIA: setting_toggle(SETTING_CF_ONLYMEDIA); call_flow_set_group(info->group); break; case ACTION_TOGGLE_MEDIA: setting_toggle(SETTING_CF_MEDIA); // Force reload arrows call_flow_set_group(info->group); break; case ACTION_TOGGLE_RAW: setting_toggle(SETTING_CF_FORCERAW); break; case ACTION_COMPRESS: setting_toggle(SETTING_CF_SPLITCALLID); // Force columns reload call_flow_set_group(info->group); break; case ACTION_SAVE: if (capture_sources_count() > 1) { dialog_run("Saving is not possible when multiple input sources are specified."); break; } next_ui = ui_create_panel(PANEL_SAVE); save_set_group(next_ui, info->group); save_set_msg(next_ui, call_flow_arrow_message(vector_item(info->darrows, info->cur_arrow))); break; case ACTION_TOGGLE_TIME: info->arrowtime = (info->arrowtime) ? false : true; break; case ACTION_SELECT: if (info->selected == -1) { info->selected = info->cur_arrow; } else { if (info->selected == info->cur_arrow) { info->selected = -1; } else { // Show diff panel next_ui = ui_create_panel(PANEL_MSG_DIFF); msg_diff_set_msgs(next_ui, call_flow_arrow_message(vector_item(info->darrows, info->selected)), call_flow_arrow_message(vector_item(info->darrows, info->cur_arrow))); } } break; case ACTION_CLEAR: info->selected = -1; break; case ACTION_CONFIRM: // KEY_ENTER, display current message in raw mode ui_create_panel(PANEL_CALL_RAW); call_raw_set_group(info->group); call_raw_set_msg(call_flow_arrow_message(vector_item(info->darrows, info->cur_arrow))); break; case ACTION_CLEAR_CALLS: case ACTION_CLEAR_CALLS_SOFT: // Propagate the key to the previous panel return KEY_PROPAGATED; default: // Parse next action continue; } // We've handled this key, stop checking actions break; } // Return if this panel has handled or not the key return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED; } int call_flow_help(ui_t *ui) { WINDOW *help_win; int height, width; // Create a new panel and show centered height = 28; width = 65; help_win = newwin(height, width, (LINES - height) / 2, (COLS - width) / 2); // Set the window title mvwprintw(help_win, 1, 18, "Call Flow Help"); // Write border and boxes around the window wattron(help_win, COLOR_PAIR(CP_BLUE_ON_DEF)); box(help_win, 0, 0); mvwhline(help_win, 2, 1, ACS_HLINE, 63); mvwhline(help_win, 7, 1, ACS_HLINE, 63); mvwhline(help_win, height - 3, 1, ACS_HLINE, 63); mvwaddch(help_win, 2, 0, ACS_LTEE); mvwaddch(help_win, 7, 0, ACS_LTEE); mvwaddch(help_win, height - 3, 0, ACS_LTEE); mvwaddch(help_win, 2, 64, ACS_RTEE); mvwaddch(help_win, 7, 64, ACS_RTEE); mvwaddch(help_win, height - 3, 64, ACS_RTEE); // Set the window footer (nice blue?) mvwprintw(help_win, height - 2, 20, "Press any key to continue"); // Some brief explanation abotu what window shows wattron(help_win, COLOR_PAIR(CP_CYAN_ON_DEF)); mvwprintw(help_win, 3, 2, "This window shows the messages from a call and its relative"); mvwprintw(help_win, 4, 2, "ordered by sent or received time."); mvwprintw(help_win, 5, 2, "This panel is mosly used when capturing at proxy systems that"); mvwprintw(help_win, 6, 2, "manages incoming and outgoing request between calls."); wattroff(help_win, COLOR_PAIR(CP_CYAN_ON_DEF)); // A list of available keys in this window mvwprintw(help_win, 8, 2, "Available keys:"); mvwprintw(help_win, 9, 2, "Esc/Q Go back to Call list window"); mvwprintw(help_win, 10, 2, "F5/Ctrl-L Leave screen and clear call list"); mvwprintw(help_win, 11, 2, "Enter Show current message Raw"); mvwprintw(help_win, 12, 2, "F1/h Show this screen"); mvwprintw(help_win, 13, 2, "F2/d Toggle SDP Address:Port info"); mvwprintw(help_win, 14, 2, "F3/m Toggle RTP arrows display"); mvwprintw(help_win, 15, 2, "F4/X Show call-flow with X-CID/X-Call-ID dialog"); mvwprintw(help_win, 16, 2, "F5/s Toggle compressed view (One address <=> one column"); mvwprintw(help_win, 17, 2, "F6/R Show original call messages in raw mode"); mvwprintw(help_win, 18, 2, "F7/c Cycle between available color modes"); mvwprintw(help_win, 19, 2, "F8/C Turn on/off message syntax highlighting"); mvwprintw(help_win, 20, 2, "F9/l Turn on/off resolved addresses"); mvwprintw(help_win, 21, 2, "9/0 Increase/Decrease raw preview size"); mvwprintw(help_win, 22, 2, "t Toggle raw preview display"); mvwprintw(help_win, 23, 2, "T Restore raw preview size"); mvwprintw(help_win, 24, 2, "D Only show SDP messages"); // Press any key to close wgetch(help_win); return 0; } int call_flow_set_group(sip_call_group_t *group) { ui_t *ui; call_flow_info_t *info; if (!(ui = ui_find_by_type(PANEL_CALL_FLOW))) return -1; if (!(info = call_flow_info(ui))) return -1; vector_clear(info->columns); vector_clear(info->arrows); info->group = group; info->cur_arrow = info->selected = -1; return 0; } void call_flow_column_add(ui_t *ui, const char *callid, address_t addr) { call_flow_info_t *info; call_flow_column_t *column; vector_iter_t columns; if (!(info = call_flow_info(ui))) return; if (call_flow_column_get(ui, callid, addr)) return; // Try to fill the second Call-Id of the column columns = vector_iterator(info->columns); while ((column = vector_iterator_next(&columns))) { if (addressport_equals(column->addr, addr)) { if (column->colpos != 0 && vector_count(column->callids) < info->maxcallids) { vector_append(column->callids, (void*)callid); return; } } } // Create a new column column = malloc(sizeof(call_flow_column_t)); memset(column, 0, sizeof(call_flow_column_t)); column->callids = vector_create(1, 1); vector_append(column->callids, (void*)callid); column->addr = addr; if (setting_enabled(SETTING_ALIAS_PORT)) { strcpy(column->alias, get_alias_value_vs_port(addr.ip, addr.port)); } else { strcpy(column->alias, get_alias_value(addr.ip)); } column->colpos = vector_count(info->columns); vector_append(info->columns, column); } call_flow_column_t * call_flow_column_get(ui_t *ui, const char *callid, address_t addr) { call_flow_info_t *info; call_flow_column_t *column; vector_iter_t columns; int match_port; const char *alias; if (!(info = call_flow_info(ui))) return NULL; // Look for address or address:port ? match_port = addr.port != 0; // Get alias value for given address if (setting_enabled(SETTING_ALIAS_PORT) && match_port) { alias = get_alias_value_vs_port(addr.ip, addr.port); } else { alias = get_alias_value(addr.ip); } columns = vector_iterator(info->columns); while ((column = vector_iterator_next(&columns))) { // In compressed mode, we search using alias instead of address if (setting_enabled(SETTING_CF_SPLITCALLID)) { if (!strcmp(column->alias, alias)) { return column; } } else { // Check if this column matches requested address if (match_port) { if (addressport_equals(column->addr, addr)) { if (vector_index(column->callids, (void*)callid) >= 0) { return column; } } } else { // Dont check port if (address_equals(column->addr, addr)) { return column; } } } } return NULL; } void call_flow_move(ui_t *ui, int arrowindex) { call_flow_info_t *info; call_flow_arrow_t *arrow; int flowh; int curh = 0; // Get panel info if (!(info = call_flow_info(ui))) return; // Already in this position? if (info->cur_arrow == arrowindex) return; // Get flow subwindow height (for scrolling) flowh = getmaxy(info->flow_win); // Moving down or up? bool move_down = (info->cur_arrow < arrowindex); vector_iter_t it = vector_iterator(info->darrows); vector_iterator_set_current(&it, info->cur_arrow); vector_iterator_set_filter(&it, call_flow_arrow_filter); if (move_down) { while ((arrow = vector_iterator_next(&it))) { // Get next selected arrow info->cur_arrow = vector_iterator_current(&it); // We have reached our destination if (info->cur_arrow >= arrowindex) { break; } } } else { while ((arrow = vector_iterator_prev(&it))) { // Get previous selected arrow info->cur_arrow = vector_iterator_current(&it); // We have reached our destination if (info->cur_arrow <= arrowindex) { break; } } } // Update the first displayed arrow if (info->cur_arrow <= info->first_arrow) { info->first_arrow = info->cur_arrow; } else { // Draw the scrollbar vector_iterator_set_current(&it, info->first_arrow - 1); while ((arrow = vector_iterator_next(&it))) { // Increase current arrow height position curh += call_flow_arrow_height(ui, arrow); // If we have reached current arrow if (vector_iterator_current(&it) == info->cur_arrow) { if (curh > flowh) { // Go to the next first arrow and check if current arrow // is still out of bottom bounds info->first_arrow++; vector_iterator_set_current(&it, info->first_arrow - 1); curh = 0; } else { break; } } } } } call_flow_arrow_t * call_flow_arrow_selected(ui_t *ui) { // Get panel info call_flow_info_t *info = call_flow_info(ui); // No selected call if (info->selected == -1) return NULL; return vector_item(info->darrows, info->selected); } struct timeval call_flow_arrow_time(call_flow_arrow_t *arrow) { struct timeval ts = { 0 }; sip_msg_t *msg; rtp_stream_t *stream; if (!arrow) return ts; if (arrow->type == CF_ARROW_SIP) { msg = (sip_msg_t *) arrow->item; ts = packet_time(msg->packet); } else if (arrow->type == CF_ARROW_RTP) { stream = (rtp_stream_t *) arrow->item; ts = stream->time; } return ts; } void call_flow_arrow_sorter(vector_t *vector, void *item) { struct timeval curts, prevts; int count = vector_count(vector); int i; // First item is alway sorted if (vector_count(vector) == 1) return; curts = call_flow_arrow_time(item); for (i = count - 2 ; i >= 0; i--) { // Get previous arrow prevts = call_flow_arrow_time(vector_item(vector, i)); // Check if the item is already in a sorted position if (timeval_is_older(curts, prevts)) { vector_insert(vector, item, i + 1); return; } } // Put this item at the begining of the vector vector_insert(vector, item, 0); } int call_flow_arrow_filter(void *item) { call_flow_arrow_t *arrow = (call_flow_arrow_t *) item; // SIP arrows are never filtered if (arrow->type == CF_ARROW_SIP && setting_disabled(SETTING_CF_ONLYMEDIA)) return 1; // RTP arrows are only displayed when requested if (arrow->type == CF_ARROW_RTP) { // Display all streams if (setting_enabled(SETTING_CF_MEDIA)) return 1; // Otherwise only show active streams if (setting_has_value(SETTING_CF_MEDIA, SETTING_ACTIVE)) return stream_is_active(arrow->item); } // Rest of the arrows are never displayed return 0; } sngrep-1.8.2/src/curses/ui_call_flow.h000066400000000000000000000340661464272443000177420ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_call_flow.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage Call Flow screen * * This file contains the functions and structures to manage the call flow * screen. Call flow screen display a ladder of arrows corresponding to the * SIP messages and RTP streams of the selected dialogs. * * Some basic ascii art of this panel. * * +--------------------------------------------------------+ * | Title | * | addr1 addr2 addr3 addr4 | Selected Raw Message | * | ----- ----- ----- ----- | preview | * | Tmst| | | | | | * | Tmst|----->| | | | | * | Tmst| |----->| | | | * | Tmst| |<-----| | | | * | Tmst| | |----->| | | * | Tmst|<-----| | | | | * | Tmst| |----->| | | | * | Tmst| |<-----| | | | * | Tmst| |------------>| | | * | Tmst| |<------------| | | * | | | | | | | * | | | | | | | * | | | | | | | * | Useful hotkeys | * +--------------------------------------------------------+ * */ #ifndef __SNGREP_UI_CALL_FLOW_H #define __SNGREP_UI_CALL_FLOW_H #include #include "ui_manager.h" #include "group.h" #include "scrollbar.h" //! Sorter declaration of struct call_flow_info typedef struct call_flow_info call_flow_info_t; //! Sorter declaration of struct call_flow_column typedef struct call_flow_column call_flow_column_t; //! Sorter declaration of struct call_flow_arrow typedef struct call_flow_arrow call_flow_arrow_t; /** * @brief Call flow arrow types */ enum call_flow_arrow_type { CF_ARROW_SIP, CF_ARROW_RTP, CF_ARROW_RTCP, }; /** * @brief Call flow arrow directions */ enum call_flow_arrow_dir { CF_ARROW_RIGHT = 0, CF_ARROW_LEFT, CF_ARROW_SPIRAL }; /** * @brief Call Flow arrow information */ struct call_flow_arrow { //! Type of arrow @see call_flow_arrow_type int type; //! Item owner of this arrow void *item; //! Stream packet count for this arrow int rtp_count; //! Stream arrow position int rtp_ind_pos; //! Number of screen lines this arrow uses int height; //! Line of flow window this line starts int line; //! Arrow direction enum call_flow_arrow_dir dir; //! Source column for this arrow call_flow_column_t *scolumn; //! Destination column for this arrow call_flow_column_t *dcolumn; }; /** * @brief Structure to hold one column information * * One column has one address:port for packets source or destination matching * and a couple of Call-Ids. Limiting the number of Call-Ids each column have * we avoid having one column with lots of begining or ending arrows of * different dialogs. * */ struct call_flow_column { //! Address header for this column address_t addr; //! Alias for the given address char alias[MAX_SETTING_LEN]; //! Call Ids vector_t *callids; //! Column position (starting with zero) // FIXME array position? int colpos; }; /** * @brief Call flow Extended status information * * This data stores the actual status of the panel. It's stored in the * PANEL user pointer. */ struct call_flow_info { //! Window to display SIP payload WINDOW *raw_win; //! Window to diplay arrows WINDOW *flow_win; //! Group of calls displayed on the panel sip_call_group_t *group; //! List of arrows (call_flow_arrow_t *) vector_t *arrows; //! List of displayed arrows vector_t *darrows; //! First displayed arrow in the list int first_arrow; //! Current arrow index where the cursor is int cur_arrow; //! Selected arrow to compare int selected; //! Current line for scrolling scrollbar_t scroll; //! List of columns in the panel vector_t *columns; //! Max callids per column int maxcallids; //! Print timestamp next to the arrow bool arrowtime; }; /** * @brief Create Call Flow extended panel * * This function will allocate the ncurses pointer and draw the static * stuff of the screen (which usually won't be redrawn) * It will also create an information structure of the panel status and * store it in the panel's userpointer * * @param ui UI structure pointer */ void call_flow_create(ui_t *ui); /** * @brief Destroy panel * * This function will hide the panel and free all allocated memory. * * @param ui UI structure pointer */ void call_flow_destroy(ui_t *ui); /** * @brief Get custom information of given panel * * Return ncurses users pointer of the given panel into panel's * information structure pointer. * * @param ui UI structure pointer * @return a pointer to info structure of given panel */ call_flow_info_t * call_flow_info(ui_t *ui); /** * @brief Determine if the screen requires redrawn * * This will query the interface if it requires to be redraw again. * * @param ui UI structure pointer * @return true if the panel requires redraw, false otherwise */ bool call_flow_redraw(ui_t *ui); /** * @brief Draw the Call flow extended panel * * This function will drawn the panel into the screen based on its stored * status * * @param ui UI structure pointer * @return 0 if the panel has been drawn, -1 otherwise */ int call_flow_draw(ui_t *ui); /** * @brief Draw the footer of the panel with keybindings info * * @param ui UI structure pointer */ void call_flow_draw_footer(ui_t *ui); /** * @brief Draw the visible columns in panel window * * @param ui UI structure pointer */ int call_flow_draw_columns(ui_t *ui); /** * @brief Draw arrows in the visible part of the panel * * @param ui UI structure pointer */ void call_flow_draw_arrows(ui_t *ui); /** * @brief Draw panel preview of current arrow * * If user request to not draw preview panel, this function does nothing. * * @param ui UI structure pointer */ void call_flow_draw_preview(ui_t *ui); /** * @brief Draw a single arrow in arrow flow * * This function draws an arrow of any type in the given line of the flow. * * @param ui UI Structure pointer * @param arrow Arrow structure pointer of any type * @param line Line of the flow window to draw this arrow * @return the number of screen lines this arrow uses on screen */ int call_flow_draw_arrow(ui_t *ui, call_flow_arrow_t *arrow, int line); /** * @brief Draw the message arrow in the given line * * Draw the given message arrow in the given line. * This function will calculate origin and destination coordinates * base on message information. Each message use multiple lines * depending on the display mode of call flow * * @param ui UI structure pointer * @param arrow Call flow arrow with message to be drawn * @param cline Window line to draw the message * @return the number of screen lines this arrow uses on screen */ int call_flow_draw_message(ui_t *ui, call_flow_arrow_t *arrow, int cline); /** * @brief Draw the stream data in the given line * * Draw the given arrow of type stream in the given line. * * @param ui UI structure pointer * @param arrow Call flow arrow of stream to be drawn * @param cline Window line to draw the message * @return the number of screen lines this arrow uses on screen */ int call_flow_draw_rtp_stream(ui_t *ui, call_flow_arrow_t *arrow, int cline); /** * @brief Create a new arrow of given type * * Allocate memory for a new arrow of the given type and associate the * item pointer. If the arrow already exists in the ui arrows vector * this function will return that arrow instead of creating a new one. * * This function WON'T add the arrow to any ui vector. * * @param ui UI structure pointer * @param item Item pointer to associate to the arrow * @param type Type of arrow as defined in enum @call_flow_arrow_type * @return an arrow pointer or NULL in case of error */ call_flow_arrow_t * call_flow_arrow_create(ui_t *ui, void *item, int type); /** * @brief Get how many lines of screen an arrow will use * * Depending on the arrow tipe and panel display mode lines can * take more than two lines. This function will calculate how many * lines the arrow will use. * * @param ui UI structure pointer * @param arrow Arrow structure to calculate height * @return height the arrow will have */ int call_flow_arrow_height(ui_t *ui, const call_flow_arrow_t *arrow); /** * @brief Return the arrow of a SIP msg or RTP stream * * This function will try to find an existing arrow with a * message or stream equals to the giving pointer. * * @param ui UI structure pointer * @param data Data to search in the arrow structure * @return a pointer to the found arrow or NULL */ call_flow_arrow_t * call_flow_arrow_find(ui_t *ui, const void *data); /** * @brief Return the SIP message associated with the arrow * * Return the SIP message. If the arrow is of type SIP msg, it will * return the message itself. If the arrow is of type RTP stream, * it will return the SIP msg that setups the stream. * * @param arrow Call Flow Arrow pointer * @return associated SIP message with the arrow */ sip_msg_t * call_flow_arrow_message(const call_flow_arrow_t *arrow); /** * @brief Draw raw panel with message payload * * Draw the given message payload into the raw window. * * @param ui UI structure pointer * @param msg Message data to draw * @return 0 in all cases */ int call_flow_draw_raw(ui_t *ui, sip_msg_t *msg); /** * @brief Draw raw panel with RTCP data * * Draw the given stream data into the raw window. * * @param ui UI structure pointer * @param rtcp stream containing the RTCP conection data * @return 0 in all cases */ int call_flow_draw_raw_rtcp(ui_t *ui, rtp_stream_t *rtcp); /** * @brief Handle Call flow extended key strokes * * This function will manage the custom keybindings of the panel. * This function return one of the values defined in @key_handler_ret * * @param ui UI structure pointer * @param key Pressed keycode * @return enum @key_handler_ret */ int call_flow_handle_key(ui_t *ui, int key); /** * @brief Request the panel to show its help * * This function will request to panel to show its help (if any) by * invoking its help function. * * @param ui UI structure pointer * @return 0 if the screen has help, -1 otherwise */ int call_flow_help(ui_t *ui); /** * @brief Set the group call of the panel * * This function will access the panel information and will set the * group call pointer to the processed calls. * * @param group Call group pointer to be set in the internal info struct */ int call_flow_set_group(sip_call_group_t *group); /** * @brief Add a new column (if required) * * Check if the given callid and address has already a column. * If not, create a new call for that callid/address * Each column has one address and two callids (unless split mode * is disabled) * * @param ui UI structure pointer * @param callid Call-Id header of SIP payload * @param addr Address:port string */ void call_flow_column_add(ui_t *ui, const char *callid, address_t addr); /** * @brief Get a flow column data * * @param ui UI structure pointer * @param callid Call-Id header of SIP payload * @param addr Address:port string * @return column structure pointer or NULL if not found */ call_flow_column_t * call_flow_column_get(ui_t *ui, const char *callid, address_t address); /** * @brief Move selected cursor to given arrow * * This function will move the cursor to given arrow, taking into account * selected line and scrolling position. * * @param ui UI structure pointer * @param arrowindex Position to move the cursor */ void call_flow_move(ui_t *ui, int arrowindex); /* * @brief Return selected flow arrow * * User can select an arrow to compare times or payload with another * arrow. Don't confuse this with the current arrow (where the cursor is) * * @param ui UI Structure pointer * @return user selected arrow */ call_flow_arrow_t * call_flow_arrow_selected(ui_t *ui); /** * @brief Return timestamp for given arrow * * This function is a wrapper to return arrow timestamp no matter what * type of arrow is passed. Arrows of different types store their times * in different locations. * * If pointer is invalid of arrow type doesn't match anything known, the * timestamp returned structure will be zero'd * * @param arrow Arrow structure pointer * @return timestamp for given arrow */ struct timeval call_flow_arrow_time(call_flow_arrow_t *arrow); /** * @brief Sort arrows by timestamp * * This function acts as sorter for arrows vector. Each time a new arrow * is appended, it's sorted based on its timestamp. * * @param vector Arrows vector pointer * @param item Call Flow arrow structure pointer */ void call_flow_arrow_sorter(vector_t *vector, void *item); /** * @brief Filter displayed arrows based on configuration */ int call_flow_arrow_filter(void *item); #endif /* __SNGREP_UI_CALL_FLOW_H */ sngrep-1.8.2/src/curses/ui_call_list.c000066400000000000000000001034421464272443000177340ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_call_list.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in ui_call_list.h * */ #include #include #include #include #include #include #include "option.h" #include "filter.h" #include "capture.h" #ifdef USE_EEP #include "capture_eep.h" #endif #include "ui_manager.h" #include "ui_call_list.h" #include "ui_call_flow.h" #include "ui_call_raw.h" #include "ui_filter.h" #include "ui_save.h" #include "sip.h" /** * Ui Structure definition for Call List panel */ ui_t ui_call_list = { .type = PANEL_CALL_LIST, .create = call_list_create, .destroy = call_list_destroy, .redraw = call_list_redraw, .draw = call_list_draw, .resize = call_list_resize, .handle_key = call_list_handle_key, .help = call_list_help, }; void call_list_create(ui_t *ui) { int i, attrid, collen; call_list_info_t *info; char option[80]; const char *field, *title; // Create a new panel that fill all the screen ui_panel_create(ui, LINES, COLS); // Initialize Call List specific data info = malloc(sizeof(call_list_info_t)); memset(info, 0, sizeof(call_list_info_t)); set_panel_userptr(ui->panel, (void*) info); // Add configured columns for (i = 0; i < SIP_ATTR_COUNT; i++) { // Get column attribute name from options sprintf(option, "cl.column%d", i); if ((field = get_option_value(option))) { if ((attrid = sip_attr_from_name(field)) == -1) continue; // Get column width from options sprintf(option, "cl.column%d.width", i); if ((collen = get_option_int_value(option)) == -1) collen = sip_attr_get_width(attrid); // Get column title title = sip_attr_get_title(attrid); // Add column to the list call_list_add_column(ui, attrid, field, title, collen); } } // Initialize the fields info->fields[FLD_LIST_FILTER] = new_field(1, ui->width - 19, 3, 18, 0, 0); info->fields[FLD_LIST_COUNT] = NULL; // Create the form and post it info->form = new_form(info->fields); set_form_sub(info->form, ui->win); // Form starts inactive call_list_form_activate(ui, 0); info->menu_active = 0; // Calculate available printable area info->list_win = subwin(ui->win, ui->height - 6, ui->width, 5, 0); info->scroll = ui_set_scrollbar(info->list_win, SB_VERTICAL, SB_LEFT); // Group of selected calls info->group = call_group_create(); // Get current call list info->cur_call = -1; // Set autoscroll default status info->autoscroll = setting_enabled(SETTING_CL_AUTOSCROLL); // Apply initial configured filters filter_method_from_setting(setting_get_value(SETTING_FILTER_METHODS)); filter_payload_from_setting(setting_get_value(SETTING_FILTER_PAYLOAD)); } void call_list_destroy(ui_t *ui) { call_list_info_t *info; // Free its status data if ((info = call_list_info(ui))) { // Deallocate forms data if (info->form) { unpost_form(info->form); free_form(info->form); free_field(info->fields[FLD_LIST_FILTER]); } // Deallocate group data call_group_destroy(info->group); vector_destroy(info->dcalls); // Deallocate panel windows delwin(info->list_win); sng_free(info); } ui_panel_destroy(ui); } call_list_info_t * call_list_info(ui_t *ui) { return (call_list_info_t*) panel_userptr(ui->panel); } bool call_list_redraw(ui_t *ui) { return sip_calls_has_changed(); } int call_list_resize(ui_t *ui) { int maxx, maxy; // Get panel info call_list_info_t *info = call_list_info(ui); // Get current screen dimensions getmaxyx(stdscr, maxy, maxx); // Change the main window size wresize(ui->win, maxy, maxx); // Store new size ui->width = maxx; ui->height = maxy; // Calculate available printable area wresize(info->list_win, maxy - 6, maxx); //-4 // Force list redraw call_list_clear(ui); return 0; } void call_list_draw_header(ui_t *ui) { const char *infile, *coldesc; int colpos, collen, i; char sortind; const char *countlb; const char *device, *filterexpr, *filterbpf; // Get panel info call_list_info_t *info = call_list_info(ui); // Draw panel title ui_set_title(ui, "sngrep - SIP messages flow viewer"); // Draw a Panel header lines ui_clear_line(ui, 1); // Print Open filename in Offline mode if (!capture_is_online() && (infile = capture_input_file())) mvwprintw(ui->win, 1, 77, "Filename: %s", infile); mvwprintw(ui->win, 1, 2, "Current Mode: "); if (capture_is_online()) { wattron(ui->win, COLOR_PAIR(CP_GREEN_ON_DEF)); } else { wattron(ui->win, COLOR_PAIR(CP_RED_ON_DEF)); } wprintw(ui->win, "%s ", capture_status_desc()); // Get online mode capture device if ((device = capture_device())) wprintw(ui->win, "[%s]", device); #ifdef USE_EEP const char *eep_port; if ((eep_port = capture_eep_send_port())) { wprintw(ui->win, "[H:%s]", eep_port); } if ((eep_port = capture_eep_listen_port())) { wprintw(ui->win, "[L:%s]", eep_port); } #endif wattroff(ui->win, COLOR_PAIR(CP_GREEN_ON_DEF)); wattroff(ui->win, COLOR_PAIR(CP_RED_ON_DEF)); // Label for Display filter mvwprintw(ui->win, 3, 2, "Display Filter: "); mvwprintw(ui->win, 2, 2, "Match Expression: "); wattron(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF)); if ((filterexpr = sip_get_match_expression())) wprintw(ui->win, "%s", filterexpr); wattroff(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF)); mvwprintw(ui->win, 2, 45, "BPF Filter: "); wattron(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF)); if ((filterbpf = capture_get_bpf_filter())) wprintw(ui->win, "%s", filterbpf); wattroff(ui->win, COLOR_PAIR(CP_YELLOW_ON_DEF)); // Reverse colors on monochrome terminals if (!has_colors()) wattron(ui->win, A_REVERSE); // Get configured sorting options sip_sort_t sort = sip_sort_options(); // Draw columns titles wattron(ui->win, A_BOLD | COLOR_PAIR(CP_DEF_ON_CYAN)); ui_clear_line(ui, 4); // Draw separator line if (info->menu_active) { colpos = 18; mvwprintw(ui->win, 4, 0, "Sort by"); wattron(ui->win, A_BOLD | COLOR_PAIR(CP_CYAN_ON_BLACK)); mvwprintw(ui->win, 4, 11, "%*s", 1, ""); wattron(ui->win, A_BOLD | COLOR_PAIR(CP_DEF_ON_CYAN)); } else { colpos = 6; } for (i = 0; i < info->columncnt; i++) { // Get current column width collen = info->columns[i].width; // Get current column title coldesc = sip_attr_get_title(info->columns[i].id); // Check if the column will fit in the remaining space of the screen if (colpos + strlen(coldesc) >= ui->width) break; // Print sort column indicator if (info->columns[i].id == sort.by) { wattron(ui->win, A_BOLD | COLOR_PAIR(CP_YELLOW_ON_CYAN)); sortind = (sort.asc) ? '^' : 'v'; mvwprintw(ui->win, 4, colpos, "%c%.*s", sortind, collen, coldesc); wattron(ui->win, A_BOLD | COLOR_PAIR(CP_DEF_ON_CYAN)); } else { mvwprintw(ui->win, 4, colpos, "%.*s", collen, coldesc); } colpos += collen + 1; } // Print Autoscroll indicator if (info->autoscroll) mvwprintw(ui->win, 4, 0, "A"); wattroff(ui->win, A_BOLD | A_REVERSE | COLOR_PAIR(CP_DEF_ON_CYAN)); // Print Dialogs or Calls in label depending on calls filter if (setting_enabled(SETTING_SIP_CALLS)) { countlb = "Calls"; } else { countlb = "Dialogs"; } // Print calls count (also filtered) sip_stats_t stats = sip_calls_stats(); mvwprintw(ui->win, 1, 45, "%*s", 30, ""); if (stats.total != stats.displayed) { mvwprintw(ui->win, 1, 45, "%s: %d (%d displayed)", countlb, stats.total, stats.displayed); } else { mvwprintw(ui->win, 1, 45, "%s: %d", countlb, stats.total); } } void call_list_draw_footer(ui_t *ui) { const char *keybindings[] = { key_action_key_str(ACTION_PREV_SCREEN), "Quit", key_action_key_str(ACTION_SHOW_FLOW), "Show", key_action_key_str(ACTION_SELECT), "Select", key_action_key_str(ACTION_SHOW_HELP), "Help", key_action_key_str(ACTION_SAVE), "Save", key_action_key_str(ACTION_DISP_FILTER), "Search", key_action_key_str(ACTION_SHOW_FLOW_EX), "Extended", key_action_key_str(ACTION_CLEAR_CALLS), "Clear", key_action_key_str(ACTION_SHOW_FILTERS), "Filter", key_action_key_str(ACTION_SHOW_SETTINGS), "Settings", key_action_key_str(ACTION_CLEAR_CALLS_SOFT), "Clear with Filter", key_action_key_str(ACTION_SHOW_COLUMNS), "Columns" }; ui_draw_bindings(ui, keybindings, 23); } void call_list_draw_list(ui_t *ui) { WINDOW *list_win; int listh, listw, cline = 0; struct sip_call *call = NULL; int i, collen; char coltext[SIP_ATTR_MAXLEN]; int colid; int colpos; int color; // Get panel info call_list_info_t *info = call_list_info(ui); // Get window of call list panel list_win = info->list_win; getmaxyx(list_win, listh, listw); // Store selected call if (info->cur_call >= 0) call = vector_item(info->dcalls, info->cur_call); // Get the list of calls that are goint to be displayed vector_destroy(info->dcalls); info->dcalls = vector_copy_if(sip_calls_vector(), filter_check_call); // If no active call, use the fist one (if exists) if (info->cur_call == -1 && vector_count(info->dcalls)) { info->cur_call = info->scroll.pos = 0; } // If autoscroll is enabled, select the last dialog if (info->autoscroll) { sip_sort_t sort = sip_sort_options(); if (sort.asc) { call_list_move(ui, vector_count(info->dcalls) - 1); } else { call_list_move(ui, 0); } } else if (call) { call_list_move(ui, vector_index(info->dcalls, call)); } // Clear call list before redrawing werase(list_win); // Set the iterator position to the first call vector_iter_t it = vector_iterator(info->dcalls); vector_iterator_set_current(&it, info->scroll.pos - 1); // Fill the call list while ((call = vector_iterator_next(&it))) { // Stop if we have reached the bottom of the list if (cline == listh) break; // We only print calls with messages (In fact, all call should have msgs) if (!call_msg_count(call)) continue; // Show bold selected rows if (call_group_exists(info->group, call)) wattron(list_win, A_BOLD | COLOR_PAIR(CP_DEFAULT)); // Highlight active call if (info->cur_call == vector_iterator_current(&it)) { wattron(list_win, COLOR_PAIR(CP_WHITE_ON_BLUE)); // Reverse colors on monochrome terminals if (!has_colors()) wattron(list_win, A_REVERSE); } // Set current line background mvwprintw(list_win, cline, 0, "%*s", listw, ""); // Set current line selection box mvwprintw(list_win, cline, 2, call_group_exists(info->group, call) ? "[*]" : "[ ]"); // Print requested columns colpos = 6; for (i = 0; i < info->columncnt; i++) { // Get current column id colid = info->columns[i].id; // Get current column width collen = info->columns[i].width; // Check if next column fits on window width if (colpos + collen >= listw) break; // Initialize column text memset(coltext, 0, sizeof(coltext)); // Get call attribute for current column if (!call_get_attribute(call, colid, coltext)) { colpos += collen + 1; continue; } // Enable attribute color (if not current one) color = 0; if (info->cur_call != vector_iterator_current(&it)) { if ((color = sip_attr_get_color(colid, coltext)) > 0) { wattron(list_win, color); } } // Add the column text to the existing columns mvwprintw(list_win, cline, colpos, "%.*s", collen, coltext); colpos += collen + 1; // Disable attribute color if (color > 0) wattroff(list_win, color); } cline++; wattroff(list_win, COLOR_PAIR(CP_DEFAULT)); wattroff(list_win, COLOR_PAIR(CP_DEF_ON_BLUE)); wattroff(list_win, A_BOLD | A_REVERSE); } // Draw scrollbar to the right info->scroll.max = vector_count(info->dcalls); ui_scrollbar_draw(info->scroll); // Refresh the list if (!info->menu_active) wnoutrefresh(info->list_win); } int call_list_draw(ui_t *ui) { int cury, curx; // Store cursor position getyx(ui->win, cury, curx); // Draw the header call_list_draw_header(ui); // Draw the footer call_list_draw_footer(ui); // Draw the list content call_list_draw_list(ui); // Restore cursor position wmove(ui->win, cury, curx); return 0; } void call_list_form_activate(ui_t *ui, int active) { call_list_info_t *info = call_list_info(ui); // Store form state info->form_active = active; if (active) { set_current_field(info->form, info->fields[FLD_LIST_FILTER]); // Show cursor curs_set(1); // Change current field background set_field_back(info->fields[FLD_LIST_FILTER], A_REVERSE); } else { set_current_field(info->form, NULL); // Hide cursor curs_set(0); // Change current field background set_field_back(info->fields[FLD_LIST_FILTER], A_NORMAL); } post_form(info->form); form_driver(info->form, REQ_END_LINE); } const char * call_list_line_text(ui_t *ui, sip_call_t *call, char *text) { int i, collen; char call_attr[SIP_ATTR_MAXLEN]; char coltext[SIP_ATTR_MAXLEN]; int colid; // Get panel info call_list_info_t *info = call_list_info(ui); // Print requested columns for (i = 0; i < info->columncnt; i++) { // Get current column id colid = info->columns[i].id; // Get current column width collen = info->columns[i].width; // Check if next column fits on window width if (strlen(text) + collen >= ui->width) collen = ui->width - strlen(text); // If no space left on the screen stop processing columns if (collen <= 0) break; // Initialize column text memset(coltext, 0, sizeof(coltext)); memset(call_attr, 0, sizeof(call_attr)); // Get call attribute for current column if (call_get_attribute(call, colid, call_attr)) { sprintf(coltext, "%.*s", collen, call_attr); } // Add the column text to the existing columns sprintf(text + strlen(text), "%-*s ", collen, coltext); } return text; } int call_list_handle_key(ui_t *ui, int key) { int rnpag_steps = setting_get_intvalue(SETTING_CL_SCROLLSTEP); call_list_info_t *info; ui_t *next_ui; sip_call_group_t *group; int action = -1; sip_call_t *call, *xcall; sip_sort_t sort; // Sanity check, this should not happen if (!(info = call_list_info(ui))) return -1; // Handle form key if (info->form_active) return call_list_handle_form_key(ui, key); if (info->menu_active) return call_list_handle_menu_key(ui, key); // Get window of call list panel WINDOW *list_win = info->list_win; // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { // Check if we handle this action switch (action) { case ACTION_DOWN: call_list_move(ui, info->cur_call + 1); break; case ACTION_UP: call_list_move(ui, info->cur_call - 1); break; case ACTION_HNPAGE: rnpag_steps = rnpag_steps / 2; /* no break */ case ACTION_NPAGE: // Next page => N key down strokes call_list_move(ui, info->cur_call + rnpag_steps); break; case ACTION_HPPAGE: rnpag_steps = rnpag_steps / 2; /* no break */ case ACTION_PPAGE: // Prev page => N key up strokes call_list_move(ui, info->cur_call - rnpag_steps); break; case ACTION_BEGIN: // Move to first list entry call_list_move(ui, 0); break; case ACTION_END: call_list_move(ui, vector_count(info->dcalls)); break; case ACTION_DISP_FILTER: // Activate Form call_list_form_activate(ui, 1); break; case ACTION_SHOW_FLOW: case ACTION_SHOW_FLOW_EX: case ACTION_SHOW_RAW: // Check we have calls in the list if (info->cur_call == -1) break; // Create a new group of calls group = call_group_clone(info->group); // If not selected call, show current call flow if (call_group_count(info->group) == 0) call_group_add(group, vector_item(info->dcalls, info->cur_call)); // Add xcall to the group if (action == ACTION_SHOW_FLOW_EX) { call = vector_item(info->dcalls, info->cur_call); if (call->xcallid != NULL && strlen(call->xcallid)) { if ((xcall = sip_find_by_callid(call->xcallid))) { call_group_del(group, call); call_group_add(group, xcall); call_group_add_calls(group, xcall->xcalls); group->callid = xcall->callid; } } else { call_group_add_calls(group, call->xcalls); group->callid = call->callid; } } if (action == ACTION_SHOW_RAW) { // Create a Call Flow panel ui_create_panel(PANEL_CALL_RAW); call_raw_set_group(group); } else { // Display current call flow (normal or extended) ui_create_panel(PANEL_CALL_FLOW); call_flow_set_group(group); } break; case ACTION_SHOW_FILTERS: ui_create_panel(PANEL_FILTER); break; case ACTION_SHOW_COLUMNS: ui_create_panel(PANEL_COLUMN_SELECT); break; case ACTION_SHOW_STATS: ui_create_panel(PANEL_STATS); break; case ACTION_SAVE: if (capture_sources_count() > 1) { dialog_run("Saving is not possible when multiple input sources are specified."); break; } next_ui = ui_create_panel(PANEL_SAVE); save_set_group(next_ui, info->group); break; case ACTION_CLEAR: // Clear group calls vector_clear(info->group->calls); break; case ACTION_CLEAR_CALLS: // Remove all stored calls sip_calls_clear(); // Clear List call_list_clear(ui); break; case ACTION_CLEAR_CALLS_SOFT: // Remove stored calls, keeping the currently displayed calls sip_calls_clear_soft(); // Clear List call_list_clear(ui); break; case ACTION_AUTOSCROLL: info->autoscroll = (info->autoscroll) ? 0 : 1; break; case ACTION_SHOW_SETTINGS: ui_create_panel(PANEL_SETTINGS); break; case ACTION_SELECT: call = vector_item(info->dcalls, info->cur_call); if (call_group_exists(info->group, call)) { call_group_del(info->group, call); } else { call_group_add(info->group, call); } break; case ACTION_SORT_SWAP: // Change sort order sort = sip_sort_options(); sort.asc = (sort.asc) ? false : true; sip_set_sort_options(sort); break; case ACTION_SORT_NEXT: case ACTION_SORT_PREV: call_list_select_sort_attribute(ui); break; case ACTION_PREV_SCREEN: // Handle quit from this screen unless requested if (setting_enabled(SETTING_EXITPROMPT)) { if (dialog_confirm("Confirm exit", "Are you sure you want to quit?", "Yes,No") == 0) { ui_destroy(ui); } } else { ui_destroy(ui); } return KEY_HANDLED; break; default: // Parse next action continue; } // This panel has handled the key successfully break; } // Disable autoscroll on some key pressed switch(action) { case ACTION_DOWN: case ACTION_UP: case ACTION_HNPAGE: case ACTION_HPPAGE: case ACTION_NPAGE: case ACTION_PPAGE: case ACTION_BEGIN: case ACTION_END: case ACTION_DISP_FILTER: info->autoscroll = 0; break; } // Return if this panel has handled or not the key return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED; } int call_list_handle_form_key(ui_t *ui, int key) { char *dfilter; int action = -1; // Get panel information call_list_info_t *info = call_list_info(ui); // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { // Check if we handle this action switch (action) { case ACTION_PRINTABLE: // If this is a normal character on input field, print it form_driver(info->form, key); break; case ACTION_PREV_SCREEN: case ACTION_NEXT_FIELD: case ACTION_CONFIRM: case ACTION_SELECT: case ACTION_UP: case ACTION_DOWN: // Activate list call_list_form_activate(ui, 0); break; case ACTION_RIGHT: form_driver(info->form, REQ_RIGHT_CHAR); break; case ACTION_LEFT: form_driver(info->form, REQ_LEFT_CHAR); break; case ACTION_BEGIN: form_driver(info->form, REQ_BEG_LINE); break; case ACTION_END: form_driver(info->form, REQ_END_LINE); break; case ACTION_CLEAR: form_driver(info->form, REQ_BEG_LINE); form_driver(info->form, REQ_CLR_EOL); break; case ACTION_DELETE: form_driver(info->form, REQ_DEL_CHAR); break; case ACTION_BACKSPACE: form_driver(info->form, REQ_DEL_PREV); break; default: // Parse next action continue; } // We've handled this key, stop checking actions break; } // Filter has changed, re-apply filter to displayed calls if (action == ACTION_PRINTABLE || action == ACTION_BACKSPACE || action == ACTION_DELETE || action == ACTION_CLEAR) { // Updated displayed results call_list_clear(ui); // Reset filters on each key stroke filter_reset_calls(); } // Validate all input data form_driver(info->form, REQ_VALIDATION); // Store dfilter input int field_len = strlen(field_buffer(info->fields[FLD_LIST_FILTER], 0)); dfilter = malloc(field_len + 1); memset(dfilter, 0, field_len + 1); strncpy(dfilter, field_buffer(info->fields[FLD_LIST_FILTER], 0), field_len); // Trim any trailing spaces strtrim(dfilter); // Set display filter filter_set(FILTER_CALL_LIST, strlen(dfilter) ? dfilter : NULL); free(dfilter); // Return if this panel has handled or not the key return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED; } int call_list_handle_menu_key(ui_t *ui, int key) { MENU *menu; int i; int action = -1; sip_sort_t sort; enum sip_attr_id id; // Get panel information call_list_info_t *info = call_list_info(ui); menu = info->menu; // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { // Check if we handle this action switch (action) { case ACTION_DOWN: menu_driver(menu, REQ_DOWN_ITEM); break; case ACTION_UP: menu_driver(menu, REQ_UP_ITEM); break; case ACTION_NPAGE: menu_driver(menu, REQ_SCR_DPAGE); break; case ACTION_PPAGE: menu_driver(menu, REQ_SCR_UPAGE); break; case ACTION_CONFIRM: case ACTION_SELECT: // Change sort attribute sort = sip_sort_options(); id = sip_attr_from_name(item_name(current_item(info->menu))); if (sort.by == id) { sort.asc = (sort.asc) ? false : true; } else { sort.by = id; } sip_set_sort_options(sort); /* no break */ case ACTION_PREV_SCREEN: // Desactive sorting menu info->menu_active = 0; // Remove menu unpost_menu(info->menu); free_menu(info->menu); // Remove items for (i = 0; i < SIP_ATTR_COUNT; i++) { if (!info->items[i]) break; free_item(info->items[i]); } // Restore list position mvderwin(info->list_win, 5, 0); // Restore list window size wresize(info->list_win, ui->height - 6, ui->width); break; default: // Parse next action continue; } // We've handled this key, stop checking actions break; } // Return if this panel has handled or not the key return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED; } int call_list_help(ui_t *ui) { WINDOW *help_win; int height, width; // Create a new panel and show centered height = 28; width = 65; help_win = newwin(height, width, (LINES - height) / 2, (COLS - width) / 2); // Set the window title mvwprintw(help_win, 1, 25, "Call List Help"); // Write border and boxes around the window wattron(help_win, COLOR_PAIR(CP_BLUE_ON_DEF)); box(help_win, 0, 0); mvwhline(help_win, 2, 1, ACS_HLINE, width - 2); mvwhline(help_win, 7, 1, ACS_HLINE, width - 2); mvwhline(help_win, height - 3, 1, ACS_HLINE, width - 2); mvwaddch(help_win, 2, 0, ACS_LTEE); mvwaddch(help_win, 7, 0, ACS_LTEE); mvwaddch(help_win, height - 3, 0, ACS_LTEE); mvwaddch(help_win, 2, 64, ACS_RTEE); mvwaddch(help_win, 7, 64, ACS_RTEE); mvwaddch(help_win, height - 3, 64, ACS_RTEE); // Set the window footer (nice blue?) mvwprintw(help_win, height - 2, 20, "Press any key to continue"); // Some brief explanation abotu what window shows wattron(help_win, COLOR_PAIR(CP_CYAN_ON_DEF)); mvwprintw(help_win, 3, 2, "This windows show the list of parsed calls from a pcap file "); mvwprintw(help_win, 4, 2, "(Offline) or a live capture with libpcap functions (Online)."); mvwprintw(help_win, 5, 2, "You can configure the columns shown in this screen and some"); mvwprintw(help_win, 6, 2, "static filters using sngreprc resource file."); wattroff(help_win, COLOR_PAIR(CP_CYAN_ON_DEF)); // A list of available keys in this window mvwprintw(help_win, 8, 2, "Available keys:"); mvwprintw(help_win, 10, 2, "Esc/Q Exit sngrep."); mvwprintw(help_win, 11, 2, "Enter Show selected calls message flow"); mvwprintw(help_win, 12, 2, "Space Select call"); mvwprintw(help_win, 13, 2, "F1/h Show this screen"); mvwprintw(help_win, 14, 2, "F2/S Save captured packages to a file"); mvwprintw(help_win, 15, 2, "F3// Display filtering (match string case insensitive)"); mvwprintw(help_win, 16, 2, "F4/X Show selected call-flow (Extended) if available"); mvwprintw(help_win, 17, 2, "F5/Ctrl-L Clear call list (can not be undone!)"); mvwprintw(help_win, 18, 2, "F6/R Show selected call messages in raw mode"); mvwprintw(help_win, 19, 2, "F7/F Show filter options"); mvwprintw(help_win, 20, 2, "F8/o Show Settings"); mvwprintw(help_win, 21, 2, "F10/t Select displayed columns"); mvwprintw(help_win, 22, 2, "i/I Set display filter to invite"); mvwprintw(help_win, 23, 2, "p Stop/Resume packet capture"); // Press any key to close wgetch(help_win); delwin(help_win); return 0; } int call_list_add_column(ui_t *ui, enum sip_attr_id id, const char* attr, const char *title, int width) { call_list_info_t *info; if (!(info = call_list_info(ui))) return 1; info->columns[info->columncnt].id = id; info->columns[info->columncnt].attr = attr; info->columns[info->columncnt].title = title; info->columns[info->columncnt].width = width; info->columncnt++; return 0; } void call_list_clear(ui_t *ui) { call_list_info_t *info; // Get panel info if (!(info = call_list_info(ui))) return; // Initialize structures info->scroll.pos = info->cur_call = -1; vector_clear(info->group->calls); // Clear Displayed lines werase(info->list_win); } void call_list_move(ui_t *ui, int line) { call_list_info_t *info; // Get panel info if (!(info = call_list_info(ui))) return; // Already in this position? if (info->cur_call == line) return; // Moving down or up? bool move_down = (info->cur_call < line); vector_iter_t it = vector_iterator(info->dcalls); vector_iterator_set_current(&it, info->cur_call); if (move_down) { while (info->cur_call < line) { // Check if there is a call below us if (!vector_iterator_next(&it)) break; // Increase current call position info->cur_call++; // If we are out of the bottom of the displayed list // refresh it starting in the next call if (info->cur_call - info->scroll.pos == getmaxy(info->list_win)) { info->scroll.pos++; } } } else { while (info->cur_call > line) { // Check if there is a call above us if (!vector_iterator_prev(&it)) break; // If we are out of the top of the displayed list // refresh it starting in the previous (in fact current) call if (info->cur_call == info->scroll.pos) { info->scroll.pos--; } // Move current call position info->cur_call--; } } } void call_list_select_sort_attribute(ui_t *ui) { call_list_info_t *info; int i; // Get panel info if (!(info = call_list_info(ui))) return; // Activete sorting menu info->menu_active = 1; wresize(info->list_win, ui->height - 6, ui->width - 12); mvderwin(info->list_win, 5, 12); // Create menu entries for (i = 0; i < info->columncnt; i++) { info->items[i] = new_item(sip_attr_get_name(info->columns[i].id), 0); } info->items[info->columncnt] = NULL; // Create the columns menu and post it info->menu = new_menu(info->items); // Set main window and sub window set_menu_win(info->menu, ui->win); set_menu_sub(info->menu, derwin(ui->win, 20, 15, 5, 0)); werase(menu_win(info->menu)); set_menu_format(info->menu, ui->height, 1); set_menu_mark(info->menu, ""); set_menu_fore(info->menu, COLOR_PAIR(CP_DEF_ON_BLUE)); menu_opts_off(info->menu, O_ONEVALUE); post_menu(info->menu); } sngrep-1.8.2/src/curses/ui_call_list.h000066400000000000000000000204101464272443000177320ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_call_list.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage Call List screen * * This file contains the functions and structures to manage the call list * screen. * */ #ifndef __UI_CALL_LIST_H #define __UI_CALL_LIST_H #include "ui_manager.h" #include #include "scrollbar.h" /** * @brief Enum of available fields * */ enum call_list_field_list { FLD_LIST_FILTER = 0, //! Never remove this field id FLD_LIST_COUNT }; //! Sorter declaration of call_list_column struct typedef struct call_list_column call_list_column_t; //! Sorter declaration of call_list_info struct typedef struct call_list_info call_list_info_t; /** * @brief Call List column information * * It will be nice make which columns will appear in this list and * in which order a configurable option. * This structure is one step towards configurable stuff */ struct call_list_column { enum sip_attr_id id; const char *attr; const char *title; int width; }; /** * @brief Call List panel status information * * This data stores the actual status of the panel. It's stored in the * panel pointer. */ struct call_list_info { //! Displayed calls vector vector_t *dcalls; //! Selected call in the list int cur_call; //! Selected calls with space sip_call_group_t *group; //! Displayed column list, make it configurable in the future call_list_column_t columns[SIP_ATTR_COUNT]; //! Displayed column count. int columncnt; //! List subwindow WINDOW *list_win; //! Form that contains the display filter FORM *form; //! An array of window form fields FIELD *fields[FLD_LIST_COUNT + 1]; //! We're entering keys on form int form_active; // Columns sort menu MENU *menu; // Columns sort menu items ITEM *items[SIP_ATTR_COUNT + 1]; //! We're selecting sorting field int menu_active; //! Move to last list entry if autoscroll is enabled int autoscroll; //! List scrollbar scrollbar_t scroll; }; /** * @brief Create Call List panel * * This function will allocate the ncurses pointer and draw the static * stuff of the screen (which usually won't be redrawn) * It will also create an information structure of the panel status and * store it in the panel's userpointer * * @param ui UI structure pointer * @return the allocated ncurses panel */ void call_list_create(ui_t *ui); /** * @brief Destroy panel * * This function will hide the panel and free all allocated memory. * * @param ui UI structure pointer */ void call_list_destroy(ui_t *ui); /** * @brief Get custom information of given panel * * Return ncurses users pointer of the given panel into panel's * information structure pointer. * * @param ui UI structure pointer * @return a pointer to info structure of given panel */ call_list_info_t * call_list_info(ui_t *ui); /** * @brief Determine if the screen requires redrawn * * This will query the interface if it requires to be redraw again. * * @param ui UI structure pointer * @return true if the panel requires redraw, false otherwise */ bool call_list_redraw(ui_t *ui); /** * @brief Resize the windows of Call List * * This function will be invoked when the ui size has changed * * @param ui UI structure pointer * @return 0 if the panel has been resized, -1 otherwise */ int call_list_resize(ui_t *ui); /** * @brief Draw panel header * * This funtion will draw Call list header * * @param ui UI structure pointer */ void call_list_draw_header(ui_t *ui); /** * @brief Draw panel footer * * This funtion will draw Call list footer that contains * keybinginds * * @param ui UI structure pointer */ void call_list_draw_footer(ui_t *ui); /** * @brief Draw panel list contents * * This funtion will draw Call list dialogs list * * @param ui UI structure pointer */ void call_list_draw_list(ui_t *ui); /** * @brief Draw the Call list panel * * This function will drawn the panel into the screen based on its stored * status * * @param ui UI structure pointer * @return 0 if the panel has been drawn, -1 otherwise */ int call_list_draw(ui_t *ui); /** * @brief Enable/Disable Panel form focus * * Enable or disable form fields focus so the next input will be * handled by call_list_handle_key or call_list_handle_form_key * This will also set properties in fields to show them as focused * and show/hide the cursor * * @param ui UI structure pointer * @param active Enable/Disable flag */ void call_list_form_activate(ui_t *ui, int active); /** * @brief Get List line from the given call * * Get the list line of the given call to display in the list * This line is built using the configured columns and sizes * * @param ui UI structure pointer * @param call Call to get data from * @param text Text pointer to store the generated line * @return A pointer to text */ const char* call_list_line_text(ui_t *ui, sip_call_t *call, char *text); /** * @brief Handle Call list key strokes * * This function will manage the custom keybindings of the panel. * This function return one of the values defined in @key_handler_ret * * @param ui UI structure pointer * @param key Pressed keycode * @return enum @key_handler_ret */ int call_list_handle_key(ui_t *ui, int key); /** * @brief Handle Forms entries key strokes * * This function will manage the custom keybindings of the panel form. * * @param ui UI structure pointer * @param key Pressed keycode * @return enum @key_handler_ret */ int call_list_handle_form_key(ui_t *ui, int key); /** * @brief Handle Sort menu key strokes * * This function will manage the custom keybidnings for sort menu * * @param ui UI structure pointer * @param key Pressed keycode * @return enum @key_handler_ret */ int call_list_handle_menu_key(ui_t *ui, int key); /** * @brief Request the panel to show its help * * This function will request to panel to show its help (if any) by * invoking its help function. * * @param ui UI structure pointer * @return 0 if the screen has help, -1 otherwise */ int call_list_help(ui_t *ui); /** * @brief Add a column the Call List * * This function will add a new column to the Call List panel * @todo Columns are not configurable yet. * * @param ui UI structure pointer * @param id SIP call attribute id * @param attr SIP call attribute name * @param title SIP call attribute description * @param width Column Width * @return 0 if column has been successufly added to the list, -1 otherwise */ int call_list_add_column(ui_t *ui, enum sip_attr_id id, const char* attr, const char *title, int width); /** * @brief Remove all calls from the list and calls storage * * This funtion will clear all call lines in the list * @param ui UI structure pointer */ void call_list_clear(ui_t *ui); /** * @brief Move selected cursor to given line * * This function will move the cursor to given line, taking into account * selected line and scrolling position. * * @param ui UI structure pointer * @param line Position to move the cursor */ void call_list_move(ui_t *ui, int line); /** * @brief Select column to sort by * * This function will display a lateral menu to select the column to sort * the list for. The menu will be filled with current displayed columns. * * @param ui UI structure pointer */ void call_list_select_sort_attribute(ui_t *ui); #endif sngrep-1.8.2/src/curses/ui_call_raw.c000066400000000000000000000231711464272443000175520ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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. ** == 1) ** 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 . ** ****************************************************************************/ /** * @file ui_call_raw.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in ui_call_raw.h * * @todo Code help screen. Please. * @todo Replace the panel refresh. Wclear sucks on high latency conections. * */ #include #include #include "ui_manager.h" #include "ui_call_raw.h" #include "ui_save.h" #include "capture.h" /** * Ui Structure definition for Call Raw panel */ ui_t ui_call_raw = { .type = PANEL_CALL_RAW, .panel = NULL, .create = call_raw_create, .destroy = call_raw_destroy, .redraw = call_raw_redraw, .draw = call_raw_draw, .handle_key = call_raw_handle_key }; void call_raw_create(ui_t *ui) { // Create a new panel to fill all the screen ui_panel_create(ui, LINES, COLS); // Initialize Call List specific data call_raw_info_t *info = sng_malloc(sizeof(call_raw_info_t)); // Store it into panel userptr set_panel_userptr(ui->panel, (void*) info); // Create a initial pad of 1000 lines info->pad = newpad(500, COLS); info->padline = 0; info->scroll = 0; } void call_raw_destroy(ui_t *ui) { call_raw_info_t *info; if ((info = call_raw_info(ui))) { // Delete panel windows delwin(info->pad); sng_free(info); } ui_panel_destroy(ui); } call_raw_info_t * call_raw_info(ui_t *ui) { return (call_raw_info_t*) panel_userptr(ui->panel); } bool call_raw_redraw(ui_t *ui) { // Get panel information call_raw_info_t *info = call_raw_info(ui); // Check if any of the group has changed return call_group_has_changed(info->group); } int call_raw_draw(ui_t *ui) { call_raw_info_t *info; sip_msg_t *msg = NULL; // Get panel information if(!(info = call_raw_info(ui))) return -1; if (info->group) { // Print the call group messages into the pad while ((msg = call_group_get_next_msg(info->group, info->last))) call_raw_print_msg(ui, msg); } else { call_raw_set_msg(info->msg); } // Copy the visible part of the pad into the panel window copywin(info->pad, ui->win, info->scroll, 0, 0, 0, ui->height - 1, ui->width - 1, 0); touchwin(ui->win); return 0; } int call_raw_print_msg(ui_t *ui, sip_msg_t *msg) { call_raw_info_t *info; int payload_lines, i, column, height, width; // Message ngrep style Header char header[256]; char payload[MAX_SIP_PAYLOAD]; int color = 0; // Get panel information if (!(info = call_raw_info(ui))) return -1; // Get the pad window WINDOW *pad = info->pad; // Get current pad dimensions getmaxyx(pad, height, width); // Get message payload strcpy(payload, msg_get_payload(msg)); // Check how many lines we well need to draw this message payload_lines = 0; column = 0; for (i = 0; i < strlen(payload); i++) { if (column == width || payload[i] == '\n') { payload_lines++; column = 0; continue; } column++; } // Check if we have enough space in our huge pad to store this message if (info->padline + payload_lines > height) { // Create a new pad with more lines! pad = newpad(height + 500, COLS); // And copy all previous information overwrite(info->pad, pad); // Delete previous pad delwin(info->pad); // And store the new pad info->pad = pad; } // Color the message { if (setting_has_value(SETTING_COLORMODE, "request")) { // Determine arrow color if (msg_is_request(msg)) { color = CP_RED_ON_DEF; } else { color = CP_GREEN_ON_DEF; } } else if (info->group && setting_has_value(SETTING_COLORMODE, "callid")) { // Color by call-id color = call_group_color(info->group, msg->call); } else if (setting_has_value(SETTING_COLORMODE, "cseq")) { // Color by CSeq within the same call color = msg->cseq % 7 + 1; } // Turn on the message color wattron(pad, COLOR_PAIR(color)); // Print msg header wattron(pad, A_BOLD); mvwprintw(pad, info->padline++, 0, "%s", sip_get_msg_header(msg, header)); wattroff(pad, A_BOLD); // Print msg payload info->padline += draw_message_pos(pad, msg, info->padline); // Extra line between messages info->padline++; // Set this as the last printed message info->last = msg; return 0; } int call_raw_handle_key(ui_t *ui, int key) { call_raw_info_t *info; ui_t *next_ui; int rnpag_steps = setting_get_intvalue(SETTING_CR_SCROLLSTEP); int action = -1; // Sanity check, this should not happen if (!(info = call_raw_info(ui))) return -1; // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { // Check if we handle this action switch (action) { case ACTION_DOWN: info->scroll++; break; case ACTION_UP: info->scroll--; break; case ACTION_HNPAGE: rnpag_steps = rnpag_steps / 2; /* no break */ case ACTION_NPAGE: // Next page => N key down strokes info->scroll += rnpag_steps; break; case ACTION_HPPAGE: rnpag_steps = rnpag_steps / 2; /* no break */ case ACTION_PPAGE: // Prev page => N key up strokes info->scroll -= rnpag_steps; break; case ACTION_SAVE: if (capture_sources_count() > 1) { dialog_run("Saving is not possible when multiple input sources are specified."); break; } if (info->group) { // KEY_S, Display save panel next_ui = ui_create_panel(PANEL_SAVE); save_set_group(next_ui, info->group); } break; case ACTION_TOGGLE_SYNTAX: case ACTION_CYCLE_COLOR: // Handle colors using default handler ui_default_handle_key(ui, key); // Create a new pad (forces messages draw) delwin(info->pad); info->pad = newpad(500, COLS); info->last = NULL; // Force refresh panel if (info->group) { call_raw_set_group(info->group); } else { call_raw_set_msg(info->msg); } break; case ACTION_CLEAR_CALLS: case ACTION_CLEAR_CALLS_SOFT: // Propagate the key to the previous panel return KEY_PROPAGATED; case ACTION_SHOW_ALIAS: setting_toggle(SETTING_DISPLAY_ALIAS); // Create a new pad (forces messages draw) delwin(info->pad); info->pad = newpad(500, COLS); info->last = NULL; // Force refresh panel if (info->group) { call_raw_set_group(info->group); } else { call_raw_set_msg(info->msg); } break; default: // Parse next action continue; } // This panel has handled the key successfully break; } if (info->scroll < 0 || info->padline < LINES) { info->scroll = 0; // Disable scrolling if there's nothing to scroll } else { if (info->scroll + LINES / 2 > info->padline) info->scroll = info->padline - LINES / 2; } // Return if this panel has handled or not the key return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED; } int call_raw_set_group(sip_call_group_t *group) { ui_t *ui; call_raw_info_t *info; if (!group) return -1; if (!(ui = ui_find_by_type(PANEL_CALL_RAW))) return -1; if (!(info = call_raw_info(ui))) return -1; // Set call raw call group info->group = group; info->msg = NULL; // Initialize internal pad info->padline = 0; wclear(info->pad); return 0; } int call_raw_set_msg(sip_msg_t *msg) { ui_t *ui; call_raw_info_t *info; if (!msg) return -1; if (!(ui = ui_find_by_type(PANEL_CALL_RAW))) return -1; if (!(info = call_raw_info(ui))) return -1; // Set call raw message info->group = NULL; info->msg = msg; // Initialize internal pad info->padline = 0; wclear(info->pad); // Print the message in the pad call_raw_print_msg(ui, msg); return 0; } sngrep-1.8.2/src/curses/ui_call_raw.h000066400000000000000000000106201464272443000175520ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_call_raw.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage Raw output screen of Sip messages * * This file contains the functions and structures to manage the raw message * output screen. * */ #ifndef __UI_CALL_RAW_H #define __UI_CALL_RAW_H #include "config.h" #include "ui_manager.h" //! Sorter declaration of struct call_raw_info typedef struct call_raw_info call_raw_info_t; /** * @brief Call raw status information * * This data stores the actual status of the panel. It's stored in the * PANEL user pointer. */ struct call_raw_info { //! Group of calls displayed on the panel (Call raw display) sip_call_group_t *group; //! Message to display on the panel (Single message raw display) sip_msg_t *msg; //! Last printed message on panel (Call raw display) sip_msg_t *last; //! Window pad to copy on displayed screen WINDOW *pad; //! Already used lines of the window pad int padline; //! Scroll position of the window pad int scroll; }; /** * @brief Create Call Raw panel * * This function will allocate the ncurses pointer and draw the static * stuff of the screen (which usually won't be redrawn) * It will also create an information structure of the panel status and * store it in the panel's userpointer */ void call_raw_create(ui_t *ui); /** * @brief Destroy panel * * This function will hide the panel and free all allocated memory. * * @param panel Ncurses panel pointer */ void call_raw_destroy(ui_t *ui); /** * @brief Get custom information of given panel * * Return ncurses users pointer of the given panel into panel's * information structure pointer. * * @param panel Ncurses panel pointer * @return a pointer to info structure of given panel */ call_raw_info_t * call_raw_info(ui_t *ui); /** * @brief Determine if the screen requires redrawn * * This will query the interface if it requires to be redraw again. * * @param ui UI structure pointer * @return true if the panel requires redraw, false otherwise */ bool call_raw_redraw(ui_t *ui); /** * @brief Draw the Call Raw panel * * This function will drawn the panel into the screen based on its stored * status * * @param panel Ncurses panel pointer * @return 0 if the panel has been drawn, -1 otherwise */ int call_raw_draw(ui_t *ui); /** * @brief Draw a message in call Raw * * Draw a new message in the Raw pad. * * @param panel Ncurses panel pointer * @param msg New message to be printed * @return 0 in call cases */ int call_raw_print_msg(ui_t *ui, sip_msg_t *msg); /** * @brief Handle Call Raw key strokes * * This function will manage the custom keybindings of the panel. * This function return one of the values defined in @key_handler_ret * * @param panel Ncurses panel pointer * @param key Pressed keycode * @return enum @key_handler_ret */ int call_raw_handle_key(ui_t *ui, int key); /** * @brief Set the active call group of the panel * * This function will access the panel information and will set the * call group pointer to the processed calls. * * @param group Call Group pointer to be set in the internal info struct */ int call_raw_set_group(sip_call_group_t *group); /** * @brief Set the active msg of the panel * * This function will access the panel information and will set the * msg pointer to the processed message. * * @param msg Message pointer to be set in the internal info struct */ int call_raw_set_msg(sip_msg_t *msg); #endif sngrep-1.8.2/src/curses/ui_column_select.c000066400000000000000000000400171464272443000206200ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_column_select.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in ui_column_select.h * */ #include #include #include #include #include #include "ui_manager.h" #include "ui_call_list.h" #include "ui_column_select.h" /** * Ui Structure definition for Message Diff panel */ ui_t ui_column_select = { .type = PANEL_COLUMN_SELECT, .panel = NULL, .create = column_select_create, .handle_key = column_select_handle_key, .destroy = column_select_destroy }; void column_select_create(ui_t *ui) { int attr_id, column; MENU *menu; column_select_info_t *info; // Cerate a new indow for the panel and form ui_panel_create(ui, 20, 60); // Initialize Filter panel specific data info = sng_malloc(sizeof(column_select_info_t)); // Store it into panel userptr set_panel_userptr(ui->panel, (void*) info); // Initialize the fields info->fields[FLD_COLUMNS_ACCEPT] = new_field(1, 10, ui->height - 2, 13, 0, 0); info->fields[FLD_COLUMNS_SAVE] = new_field(1, 10, ui->height - 2, 25, 0, 0); info->fields[FLD_COLUMNS_CANCEL] = new_field(1, 10, ui->height - 2, 37, 0, 0); info->fields[FLD_COLUMNS_COUNT] = NULL; // Field Labels set_field_buffer(info->fields[FLD_COLUMNS_ACCEPT], 0, "[ Accept ]"); set_field_buffer(info->fields[FLD_COLUMNS_SAVE], 0, "[ Save ]"); set_field_buffer(info->fields[FLD_COLUMNS_CANCEL], 0, "[ Cancel ]"); // Create the form and post it info->form = new_form(info->fields); set_form_sub(info->form, ui->win); post_form(info->form); // Create a subwin for the menu area info->menu_win = derwin(ui->win, 10, ui->width - 2, 7, 0); // Initialize one field for each attribute for (attr_id = 0; attr_id < SIP_ATTR_COUNT; attr_id++) { // Create a new field for this column info->items[attr_id] = new_item("[ ]", sip_attr_get_description(attr_id)); set_item_userptr(info->items[attr_id], (void*) sip_attr_get_name(attr_id)); } info->items[SIP_ATTR_COUNT] = NULL; // Create the columns menu and post it info->menu = menu = new_menu(info->items); // Set current enabled fields // FIXME Stealing Call list columns :/ call_list_info_t *list_info = call_list_info(ui_find_by_type(PANEL_CALL_LIST)); // Enable current enabled fields and move them to the top for (column = 0; column < list_info->columncnt; column++) { const char *attr = list_info->columns[column].attr; for (attr_id = 0; attr_id < item_count(menu); attr_id++) { if (!strcmp(item_userptr(info->items[attr_id]), attr)) { column_select_toggle_item(ui, info->items[attr_id]); column_select_move_item(ui, info->items[attr_id], column); break; } } } // Set main window and sub window set_menu_win(menu, ui->win); set_menu_sub(menu, derwin(ui->win, 10, ui->width - 5, 7, 2)); set_menu_format(menu, 10, 1); set_menu_mark(menu, ""); set_menu_fore(menu, COLOR_PAIR(CP_DEF_ON_BLUE)); menu_opts_off(menu, O_ONEVALUE); post_menu(menu); // Draw a scrollbar to the right info->scroll = ui_set_scrollbar(info->menu_win, SB_VERTICAL, SB_RIGHT); info->scroll.max = item_count(menu) - 1; ui_scrollbar_draw(info->scroll); // Set the window title and boxes mvwprintw(ui->win, 1, ui->width / 2 - 14, "Call List columns selection"); wattron(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); title_foot_box(ui->panel); mvwhline(ui->win, 6, 1, ACS_HLINE, ui->width - 1); mvwaddch(ui->win, 6, 0, ACS_LTEE); mvwaddch(ui->win, 6, ui->width - 1, ACS_RTEE); wattroff(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); // Some brief explanation abotu what window shows wattron(ui->win, COLOR_PAIR(CP_CYAN_ON_DEF)); mvwprintw(ui->win, 3, 2, "This windows show the list of columns displayed on Call"); mvwprintw(ui->win, 4, 2, "List. You can enable/disable using Space Bar and reorder"); mvwprintw(ui->win, 5, 2, "them using + and - keys."); wattroff(ui->win, COLOR_PAIR(CP_CYAN_ON_DEF)); info->form_active = 0; } void column_select_destroy(ui_t *ui) { int i; column_select_info_t *info = column_select_info(ui); // Remove menu and items unpost_menu(info->menu); free_menu(info->menu); for (i = 0; i < SIP_ATTR_COUNT; i++) free_item(info->items[i]); // Remove form and fields unpost_form(info->form); free_form(info->form); for (i = 0; i < FLD_COLUMNS_COUNT; i++) free_field(info->fields[i]); sng_free(info); // Remove panel window and custom info ui_panel_destroy(ui); } column_select_info_t * column_select_info(ui_t *ui) { return (column_select_info_t*) panel_userptr(ui->panel); } int column_select_handle_key(ui_t *ui, int key) { // Get panel information column_select_info_t *info = column_select_info(ui); if (info->form_active) { return column_select_handle_key_form(ui, key); } else { return column_select_handle_key_menu(ui, key); } return 0; } int column_select_handle_key_menu(ui_t *ui, int key) { MENU *menu; ITEM *current; int current_idx; int action = -1; // Get panel information column_select_info_t *info = column_select_info(ui); menu = info->menu; current = current_item(menu); current_idx = item_index(current); // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { // Check if we handle this action switch (action) { case ACTION_DOWN: menu_driver(menu, REQ_DOWN_ITEM); break; case ACTION_UP: menu_driver(menu, REQ_UP_ITEM); break; case ACTION_NPAGE: menu_driver(menu, REQ_SCR_DPAGE); break; case ACTION_PPAGE: menu_driver(menu, REQ_SCR_UPAGE); break; case ACTION_SELECT: column_select_toggle_item(ui, current); column_select_update_menu(ui); break; case ACTION_COLUMN_MOVE_DOWN: column_select_move_item(ui, current, current_idx + 1); column_select_update_menu(ui); break; case ACTION_COLUMN_MOVE_UP: column_select_move_item(ui, current, current_idx - 1); column_select_update_menu(ui); break; case ACTION_NEXT_FIELD: info->form_active = 1; set_menu_fore(menu, COLOR_PAIR(CP_DEFAULT)); set_field_back(info->fields[FLD_COLUMNS_ACCEPT], A_REVERSE); form_driver(info->form, REQ_VALIDATION); break; case ACTION_CONFIRM: column_select_update_columns(ui); ui_destroy(ui); return KEY_HANDLED; default: // Parse next action continue; } // This panel has handled the key successfully break; } // Draw a scrollbar to the right info->scroll.pos = top_row(menu); ui_scrollbar_draw(info->scroll); wnoutrefresh(info->menu_win); // Return if this panel has handled or not the key return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED; } int column_select_handle_key_form(ui_t *ui, int key) { int field_idx, new_field_idx; char field_value[48]; int action = -1; // Get panel information column_select_info_t *info = column_select_info(ui); // Get current field id field_idx = field_index(current_field(info->form)); // Get current field value. memset(field_value, 0, sizeof(field_value)); strcpy(field_value, field_buffer(current_field(info->form), 0)); strtrim(field_value); // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { // Check if we handle this action switch (action) { case ACTION_RIGHT: case ACTION_NEXT_FIELD: form_driver(info->form, REQ_NEXT_FIELD); break; case ACTION_LEFT: case ACTION_PREV_FIELD: form_driver(info->form, REQ_PREV_FIELD); break; case ACTION_SELECT: case ACTION_CONFIRM: switch(field_idx) { case FLD_COLUMNS_ACCEPT: column_select_update_columns(ui); ui_destroy(ui); return KEY_HANDLED; case FLD_COLUMNS_CANCEL: ui_destroy(ui); return KEY_HANDLED; case FLD_COLUMNS_SAVE: column_select_update_columns(ui); column_select_save_columns(ui); ui_destroy(ui); return KEY_HANDLED; } break; default: // Parse next action continue; } // This panel has handled the key successfully break; } // Validate all input data form_driver(info->form, REQ_VALIDATION); // Change background and cursor of "button fields" set_field_back(info->fields[FLD_COLUMNS_ACCEPT], A_NORMAL); set_field_back(info->fields[FLD_COLUMNS_SAVE], A_NORMAL); set_field_back(info->fields[FLD_COLUMNS_CANCEL], A_NORMAL); // Get current selected field new_field_idx = field_index(current_field(info->form)); // Swap between menu and form if (field_idx == FLD_COLUMNS_CANCEL && new_field_idx == FLD_COLUMNS_ACCEPT) { set_menu_fore(info->menu, COLOR_PAIR(CP_DEF_ON_BLUE)); info->form_active = 0; } else { // Change current field background set_field_back(info->fields[new_field_idx], A_REVERSE); } // Return if this panel has handled or not the key return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED; } void column_select_update_columns(ui_t *ui) { int column, attr_id; // Get panel information column_select_info_t *info = column_select_info(ui); // Set enabled fields ui_t *ui_list = ui_find_by_type(PANEL_CALL_LIST); call_list_info_t *list_info = call_list_info(ui_list); // Reset column count list_info->columncnt = 0; // Add all selected columns for (column = 0; column < item_count(info->menu); column++) { // If column is active if (!strncmp(item_name(info->items[column]), "[ ]", 3)) continue; // Get column attribute attr_id = sip_attr_from_name(item_userptr(info->items[column])); // Add a new column to the list call_list_add_column(ui_list, attr_id, sip_attr_get_name(attr_id), sip_attr_get_title(attr_id), sip_attr_get_width(attr_id)); } } void column_select_save_columns(ui_t *ui) { int column; FILE *fi, *fo; char columnopt[128]; char line[1024]; char *rcfile; char *userconf = NULL; char *tmpfile = NULL; // Use current $SNGREPRC or $HOME/.sngreprc file if ((rcfile = getenv("SNGREPRC"))) { if ((userconf = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) { if ((tmpfile = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) { sprintf(userconf, "%s", rcfile); sprintf(tmpfile, "%s.old", rcfile); } else { sng_free(userconf); return; } } else { return; } } else if ((rcfile = getenv("HOME"))) { if ((userconf = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) { if ((tmpfile = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) { sprintf(userconf, "%s/.sngreprc", rcfile); sprintf(tmpfile, "%s/.sngreprc.old", rcfile); } else { sng_free(userconf); return; } } else { return; } } else { return; } // Remove old config file unlink(tmpfile); // Move user conf file to temporal file rename(userconf, tmpfile); // Create a new user conf file if (!(fo = fopen(userconf, "w"))) { dialog_run("Unable to open %s: %s", userconf, strerror(errno)); sng_free(userconf); sng_free(tmpfile); return; } // Read all lines of old sngreprc file if ((fi = fopen(tmpfile, "r"))) { // Read all configuration file while (fgets(line, 1024, fi) != NULL) { // Ignore lines starting with set (but keep settings) if (strncmp(line, "set ", 4) || strncmp(line, "set cl.column", 13)) { // Put everyting in new user conf file fputs(line, fo); } } fclose(fi); } // Get panel information column_select_info_t *info = column_select_info(ui); // Add all selected columns for (column = 0; column < item_count(info->menu); column++) { // If column is active if (!strncmp(item_name(info->items[column]), "[ ]", 3)) continue; // Add the columns settings sprintf(columnopt, "set cl.column%d %s\n", column, (const char*) item_userptr(info->items[column])); fputs(columnopt, fo); } fclose(fo); // Show a information dialog dialog_run("Column layout successfully saved to %s", userconf); sng_free(userconf); sng_free(tmpfile); } void column_select_move_item(ui_t *ui, ITEM *item, int pos) { // Get panel information column_select_info_t *info = column_select_info(ui); // Check we have a valid position if (pos == item_count(info->menu) || pos < 0) return; // Swap position with destination int item_pos = item_index(item); info->items[item_pos] = info->items[pos]; info->items[pos] = item; set_menu_items(info->menu, info->items); } void column_select_toggle_item(ui_t *ui, ITEM *item) { // Get panel information column_select_info_t *info = column_select_info(ui); int pos = item_index(item); // Change item name if (!strncmp(item_name(item), "[ ]", 3)) { info->items[pos] = new_item("[*]", item_description(item)); } else { info->items[pos] = new_item("[ ]", item_description(item)); } // Restore menu item set_item_userptr(info->items[pos], item_userptr(item)); set_menu_items(info->menu, info->items); // Destroy old item free_item(item); } void column_select_update_menu(ui_t *ui) { // Get panel information column_select_info_t *info = column_select_info(ui); ITEM *current = current_item(info->menu); int top_idx = top_row(info->menu); // Remove the menu from the subwindow unpost_menu(info->menu); // Set menu items set_menu_items(info->menu, info->items); // Put the menu agin into its subwindow post_menu(info->menu); // Move until the current position is set set_top_row(info->menu, top_idx); set_current_item(info->menu, current); // Force menu redraw menu_driver(info->menu, REQ_UP_ITEM); menu_driver(info->menu, REQ_DOWN_ITEM); } sngrep-1.8.2/src/curses/ui_column_select.h000066400000000000000000000122271464272443000206270ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_column_select.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage columns select panel */ #ifndef __UI_COLUMN_SELECT_H #define __UI_COLUMN_SELECT_H #include "config.h" #include #include #include "ui_manager.h" #include "scrollbar.h" #include "sip_attr.h" /** * @brief Enum of available fields */ enum column_select_field_list { FLD_COLUMNS_ACCEPT = 0, FLD_COLUMNS_SAVE, FLD_COLUMNS_CANCEL, //! Never remove this field id FLD_COLUMNS_COUNT }; //! Sorter declaration of struct columns_select_info typedef struct column_select_info column_select_info_t; /** * @brief Column selector panel private information * * This structure contains the durable data of column selection panel. */ struct column_select_info { // Section of panel where menu is being displayed WINDOW *menu_win; // Columns menu MENU *menu; // Columns Items ITEM *items[SIP_ATTR_COUNT + 1]; //! Form that contains the save fields FORM *form; //! An array of window form fields FIELD *fields[FLD_COLUMNS_COUNT + 1]; //! Flag to handle key inputs int form_active; //! Scrollbar for the menu window scrollbar_t scroll; }; /** * @brief Creates a new column selection panel * * This function allocates all required memory for * displaying the column selection panel. It also draws all the * static information of the panel that will never be * redrawn. * * @param ui UI structure pointer */ void column_select_create(ui_t *ui); /** * @brief Destroy column selection panel * * This function do the final cleanups for this panel * * @param ui UI structure pointer */ void column_select_destroy(ui_t *ui); /** * @brief Get custom information of given panel * * Return ncurses users pointer of the given panel into panel's * information structure pointer. * * @param ui UI structure pointer * @return a pointer to info structure of given panel */ column_select_info_t * column_select_info(ui_t *ui); /** * @brief Manage pressed keys for column selection panel * * This function is called by UI manager every time a * key is pressed. This allow the filter panel to manage * its own keys. * * @param ui UI structure pointer * @param key key code * @return enum @key_handler_ret */ int column_select_handle_key(ui_t *ui, int key); /** * @brief Manage pressed keys for column selection panel * * This function will handle keys when menu is active. * You can switch between menu and rest of the components * using TAB * * @param ui UI structure pointer * @param key key code * @return enum @key_handler_ret */ int column_select_handle_key_menu(ui_t *ui, int key); /** * @brief Manage pressed keys for column selection panel * * This function will handle keys when form is active. * You can switch between menu and rest of the components * using TAB * * @param ui UI structure pointer * @param key key code * @return enum @key_handler_ret */ int column_select_handle_key_form(ui_t *ui, int key); /** * @brief Update Call List columns * * This function will update the columns of Call List * * @param ui UI structure pointer */ void column_select_update_columns(ui_t *ui); /** * @brief Save selected columns to user config file * * Remove previously configurated columns from user's * $SNGREPRC or $HOME/.sngreprc and add new ones * * @param ui UI structure pointer */ void column_select_save_columns(ui_t *ui); /** * @brief Move a item to a new position * * This function can be used to reorder the column list * * @param ui UI structure pointer * @param item Menu item to be moved * @param post New position in the menu */ void column_select_move_item(ui_t *ui, ITEM *item, int pos); /** * @brief Select/Deselect a menu item * * This function can be used to toggle selection status of * the menu item * * @param ui UI structure pointer * @param item Menu item to be (de)selected */ void column_select_toggle_item(ui_t *ui, ITEM *item); /** * @brief Update menu after a change * * After moving an item or updating its selectioactivn status * menu must be redrawn. * * @param ui UI structure pointer */ void column_select_update_menu(ui_t *ui); #endif /* __UI_COLUMN_SELECT_H */ sngrep-1.8.2/src/curses/ui_filter.c000066400000000000000000000430511464272443000172520ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_filter.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in ui_filter.h * */ #include "config.h" #include #include #include #include "ui_manager.h" #include "ui_filter.h" #include "ui_call_list.h" #include "sip.h" #include "filter.h" #include "setting.h" /** * Ui Structure definition for Filter panel */ ui_t ui_filter = { .type = PANEL_FILTER, .panel = NULL, .create = filter_create, .handle_key = filter_handle_key, .destroy = filter_destroy }; void filter_create(ui_t *ui) { filter_info_t *info; const char *method, *payload; // Cerate a new indow for the panel and form ui_panel_create(ui, 18, 50); // Initialize Filter panel specific data info = sng_malloc(sizeof(filter_info_t)); // Store it into panel userptr set_panel_userptr(ui->panel, (void*) info); // Initialize the fields info->fields[FLD_FILTER_SIPFROM] = new_field(1, 28, 3, 18, 0, 0); info->fields[FLD_FILTER_SIPTO] = new_field(1, 28, 4, 18, 0, 0); info->fields[FLD_FILTER_SRC] = new_field(1, 18, 5, 18, 0, 0); info->fields[FLD_FILTER_DST] = new_field(1, 18, 6, 18, 0, 0); info->fields[FLD_FILTER_PAYLOAD] = new_field(1, 28, 7, 18, 0, 0); info->fields[FLD_FILTER_REGISTER] = new_field(1, 1, 9, 15, 0, 0); info->fields[FLD_FILTER_INVITE] = new_field(1, 1, 10, 15, 0, 0); info->fields[FLD_FILTER_SUBSCRIBE] = new_field(1, 1, 11, 15, 0, 0); info->fields[FLD_FILTER_NOTIFY] = new_field(1, 1, 12, 15, 0, 0); info->fields[FLD_FILTER_INFO] = new_field(1, 1, 13, 15, 0, 0); info->fields[FLD_FILTER_KDMQ] = new_field(1, 1, 14, 15, 0, 0); info->fields[FLD_FILTER_OPTIONS] = new_field(1, 1, 9, 37, 0, 0); info->fields[FLD_FILTER_PUBLISH] = new_field(1, 1, 10, 37, 0, 0); info->fields[FLD_FILTER_MESSAGE] = new_field(1, 1, 11, 37, 0, 0); info->fields[FLD_FILTER_REFER] = new_field(1, 1, 12, 37, 0, 0); info->fields[FLD_FILTER_UPDATE] = new_field(1, 1, 13, 37, 0, 0); info->fields[FLD_FILTER_FILTER] = new_field(1, 10, ui->height - 2, 11, 0, 0); info->fields[FLD_FILTER_CANCEL] = new_field(1, 10, ui->height - 2, 30, 0, 0); info->fields[FLD_FILTER_COUNT] = NULL; // Set fields options field_opts_off(info->fields[FLD_FILTER_SIPFROM], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_SIPTO], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_SRC], O_AUTOSKIP | O_STATIC); field_opts_off(info->fields[FLD_FILTER_DST], O_AUTOSKIP | O_STATIC); field_opts_off(info->fields[FLD_FILTER_PAYLOAD], O_AUTOSKIP | O_STATIC); field_opts_off(info->fields[FLD_FILTER_REGISTER], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_INVITE], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_SUBSCRIBE], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_NOTIFY], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_INFO], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_KDMQ], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_OPTIONS], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_PUBLISH], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_MESSAGE], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_REFER], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_UPDATE], O_AUTOSKIP); field_opts_off(info->fields[FLD_FILTER_FILTER], O_EDIT); field_opts_off(info->fields[FLD_FILTER_CANCEL], O_EDIT); // Set max field size set_max_field(info->fields[FLD_FILTER_SRC], ADDRESSLEN); set_max_field(info->fields[FLD_FILTER_DST], ADDRESSLEN); // Change background of input fields set_field_back(info->fields[FLD_FILTER_SIPFROM], A_UNDERLINE); set_field_back(info->fields[FLD_FILTER_SIPTO], A_UNDERLINE); set_field_back(info->fields[FLD_FILTER_SRC], A_UNDERLINE); set_field_back(info->fields[FLD_FILTER_DST], A_UNDERLINE); set_field_back(info->fields[FLD_FILTER_PAYLOAD], A_UNDERLINE); // Create the form and post it info->form = new_form(info->fields); set_form_sub(info->form, ui->win); post_form(info->form); // Fields labels mvwprintw(ui->win, 3, 3, "SIP From:"); mvwprintw(ui->win, 4, 3, "SIP To:"); mvwprintw(ui->win, 5, 3, "Source:"); mvwprintw(ui->win, 6, 3, "Destination:"); mvwprintw(ui->win, 7, 3, "Payload:"); mvwprintw(ui->win, 9, 3, "REGISTER [ ]"); mvwprintw(ui->win, 10, 3, "INVITE [ ]"); mvwprintw(ui->win, 11, 3, "SUBSCRIBE [ ]"); mvwprintw(ui->win, 12, 3, "NOTIFY [ ]"); mvwprintw(ui->win, 13, 3, "INFO [ ]"); mvwprintw(ui->win, 14, 3, "KDMQ [ ]"); mvwprintw(ui->win, 9, 25, "OPTIONS [ ]"); mvwprintw(ui->win, 10, 25, "PUBLISH [ ]"); mvwprintw(ui->win, 11, 25, "MESSAGE [ ]"); mvwprintw(ui->win, 12, 25, "REFER [ ]"); mvwprintw(ui->win, 13, 25, "UPDATE [ ]"); // Get Method filter if (!(method = filter_get(FILTER_METHOD))) method = setting_get_value(SETTING_FILTER_METHODS); // Get Payload filter if (!(payload = filter_get(FILTER_PAYLOAD))) payload = setting_get_value(SETTING_FILTER_PAYLOAD); // Set Default field values set_field_buffer(info->fields[FLD_FILTER_SIPFROM], 0, filter_get(FILTER_SIPFROM)); set_field_buffer(info->fields[FLD_FILTER_SIPTO], 0, filter_get(FILTER_SIPTO)); set_field_buffer(info->fields[FLD_FILTER_SRC], 0, filter_get(FILTER_SOURCE)); set_field_buffer(info->fields[FLD_FILTER_DST], 0, filter_get(FILTER_DESTINATION)); set_field_buffer(info->fields[FLD_FILTER_PAYLOAD], 0, filter_get(FILTER_PAYLOAD)); set_field_buffer(info->fields[FLD_FILTER_REGISTER], 0, strcasestr(method, sip_method_str(SIP_METHOD_REGISTER)) ? "*" : ""); set_field_buffer(info->fields[FLD_FILTER_INVITE], 0, strcasestr(method, sip_method_str(SIP_METHOD_INVITE)) ? "*" : ""); set_field_buffer(info->fields[FLD_FILTER_SUBSCRIBE], 0, strcasestr(method,sip_method_str(SIP_METHOD_SUBSCRIBE)) ? "*" : ""); set_field_buffer(info->fields[FLD_FILTER_NOTIFY], 0, strcasestr(method, sip_method_str(SIP_METHOD_NOTIFY)) ? "*" : ""); set_field_buffer(info->fields[FLD_FILTER_INFO], 0, strcasestr(method, sip_method_str(SIP_METHOD_INFO)) ? "*" : ""); set_field_buffer(info->fields[FLD_FILTER_KDMQ], 0, strcasestr(method, sip_method_str(SIP_METHOD_KDMQ)) ? "*" : ""); set_field_buffer(info->fields[FLD_FILTER_OPTIONS], 0, strcasestr(method, sip_method_str(SIP_METHOD_OPTIONS)) ? "*" : ""); set_field_buffer(info->fields[FLD_FILTER_PUBLISH], 0, strcasestr(method, sip_method_str(SIP_METHOD_PUBLISH)) ? "*" : ""); set_field_buffer(info->fields[FLD_FILTER_MESSAGE], 0, strcasestr(method, sip_method_str(SIP_METHOD_MESSAGE)) ? "*" : ""); set_field_buffer(info->fields[FLD_FILTER_REFER], 0, strcasestr(method, sip_method_str(SIP_METHOD_REFER)) ? "*" : ""); set_field_buffer(info->fields[FLD_FILTER_UPDATE], 0, strcasestr(method, sip_method_str(SIP_METHOD_UPDATE)) ? "*" : ""); set_field_buffer(info->fields[FLD_FILTER_FILTER], 0, "[ Filter ]"); set_field_buffer(info->fields[FLD_FILTER_CANCEL], 0, "[ Cancel ]"); // Set the window title and boxes mvwprintw(ui->win, 1, 18, "Filter options"); wattron(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); title_foot_box(ui->panel); mvwhline(ui->win, 8, 1, ACS_HLINE, 49); mvwaddch(ui->win, 8, 0, ACS_LTEE); mvwaddch(ui->win, 8, 49, ACS_RTEE); wattroff(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); // Set default cursor position set_current_field(info->form, info->fields[FLD_FILTER_SIPFROM]); wmove(ui->win, 3, 18); curs_set(1); } void filter_destroy(ui_t *ui) { curs_set(0); ui_panel_destroy(ui); } filter_info_t * filter_info(ui_t *ui) { return (filter_info_t*) panel_userptr(ui->panel); } int filter_handle_key(ui_t *ui, int key) { int field_idx; char field_value[MAX_SETTING_LEN]; int action = -1; // Get panel information filter_info_t *info = filter_info(ui); // Get current field id field_idx = field_index(current_field(info->form)); // Get current field value. // We trim spaces with sscanf because and empty field is stored as // space characters memset(field_value, 0, sizeof(field_value)); strcpy(field_value, field_buffer(current_field(info->form), 0)); strtrim(field_value); // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { // Check if we handle this action switch (action) { case ACTION_PRINTABLE: // If this is a normal character on input field, print it if (field_idx == FLD_FILTER_SIPFROM || field_idx == FLD_FILTER_SIPTO || field_idx == FLD_FILTER_SRC || field_idx == FLD_FILTER_DST || field_idx == FLD_FILTER_PAYLOAD) { form_driver(info->form, key); break; } continue; case ACTION_NEXT_FIELD: form_driver(info->form, REQ_NEXT_FIELD); form_driver(info->form, REQ_END_LINE); break; case ACTION_PREV_FIELD: form_driver(info->form, REQ_PREV_FIELD); form_driver(info->form, REQ_END_LINE); break; case ACTION_RIGHT: form_driver(info->form, REQ_RIGHT_CHAR); break; case ACTION_LEFT: form_driver(info->form, REQ_LEFT_CHAR); break; case ACTION_BEGIN: form_driver(info->form, REQ_BEG_LINE); break; case ACTION_END: form_driver(info->form, REQ_END_LINE); break; case ACTION_CLEAR: form_driver(info->form, REQ_CLR_FIELD); break; case KEY_DC: form_driver(info->form, REQ_DEL_CHAR); break; case ACTION_DELETE: form_driver(info->form, REQ_DEL_CHAR); break; case ACTION_BACKSPACE: if (strlen(field_value) > 0) form_driver(info->form, REQ_DEL_PREV); break; case ACTION_SELECT: switch (field_idx) { case FLD_FILTER_REGISTER: case FLD_FILTER_INVITE: case FLD_FILTER_SUBSCRIBE: case FLD_FILTER_NOTIFY: case FLD_FILTER_INFO: case FLD_FILTER_KDMQ: case FLD_FILTER_OPTIONS: case FLD_FILTER_PUBLISH: case FLD_FILTER_MESSAGE: case FLD_FILTER_REFER: case FLD_FILTER_UPDATE: if (field_value[0] == '*') { form_driver(info->form, REQ_DEL_CHAR); } else { form_driver(info->form, '*'); } break; case FLD_FILTER_CANCEL: ui_destroy(ui); return KEY_HANDLED; case FLD_FILTER_FILTER: filter_save_options(ui); ui_destroy(ui); return KEY_HANDLED; } break; case ACTION_CONFIRM: if (field_idx != FLD_FILTER_CANCEL) filter_save_options(ui); ui_destroy(ui); return KEY_HANDLED; default: // Parse next action continue; } // This panel has handled the key successfully break; } // Validate all input data form_driver(info->form, REQ_VALIDATION); // Change background and cursor of "button fields" set_field_back(info->fields[FLD_FILTER_FILTER], A_NORMAL); set_field_back(info->fields[FLD_FILTER_CANCEL], A_NORMAL); curs_set(1); // Change current field background field_idx = field_index(current_field(info->form)); if (field_idx == FLD_FILTER_FILTER || field_idx == FLD_FILTER_CANCEL) { set_field_back(info->fields[field_idx], A_REVERSE); curs_set(0); } // Return if this panel has handled or not the key return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED; } void filter_save_options(ui_t *ui) { char field_value[MAX_SETTING_LEN]; char *expr; int field_id; char method_expr[256]; // Initialize variables memset(method_expr, 0, sizeof(method_expr)); // Get panel information filter_info_t *info = filter_info(ui); for (field_id = 0; field_id < FLD_FILTER_COUNT; field_id++) { // Get current field value. // We trim spaces with sscanf because and empty field is stored as // space characters memset(field_value, 0, sizeof(field_value)); strcpy(field_value, field_buffer(info->fields[field_id], 0)); strtrim(field_value); // Set filter expression expr = strlen(field_value) ? field_value : NULL; switch (field_id) { case FLD_FILTER_SIPFROM: filter_set(FILTER_SIPFROM, expr); break; case FLD_FILTER_SIPTO: filter_set(FILTER_SIPTO, expr); break; case FLD_FILTER_SRC: filter_set(FILTER_SOURCE, expr); break; case FLD_FILTER_DST: filter_set(FILTER_DESTINATION, expr); break; case FLD_FILTER_PAYLOAD: filter_set(FILTER_PAYLOAD, expr); break; case FLD_FILTER_REGISTER: case FLD_FILTER_INVITE: case FLD_FILTER_SUBSCRIBE: case FLD_FILTER_NOTIFY: case FLD_FILTER_OPTIONS: case FLD_FILTER_PUBLISH: case FLD_FILTER_MESSAGE: case FLD_FILTER_INFO: case FLD_FILTER_REFER: case FLD_FILTER_UPDATE: case FLD_FILTER_KDMQ: if (!strcmp(field_value, "*")) { if (strlen(method_expr)) { sprintf(method_expr + strlen(method_expr), ",%s", filter_field_method(field_id)); } else { strcpy(method_expr, filter_field_method(field_id)); } } break; default: break; } } // Set Method filter filter_method_from_setting(method_expr); // Force filter evaluation filter_reset_calls(); // TODO FIXME Refresh call list FIXME call_list_clear(ui_find_by_type(PANEL_CALL_LIST)); } const char* filter_field_method(int field_id) { int method = 0; switch(field_id) { case FLD_FILTER_REGISTER: method = SIP_METHOD_REGISTER; break; case FLD_FILTER_INVITE: method = SIP_METHOD_INVITE; break; case FLD_FILTER_SUBSCRIBE: method = SIP_METHOD_SUBSCRIBE; break; case FLD_FILTER_NOTIFY: method = SIP_METHOD_NOTIFY; break; case FLD_FILTER_OPTIONS: method = SIP_METHOD_OPTIONS; break; case FLD_FILTER_PUBLISH: method = SIP_METHOD_PUBLISH; break; case FLD_FILTER_MESSAGE: method = SIP_METHOD_MESSAGE; break; case FLD_FILTER_INFO: method = SIP_METHOD_INFO; break; case FLD_FILTER_REFER: method = SIP_METHOD_REFER; break; case FLD_FILTER_UPDATE: method = SIP_METHOD_UPDATE; break; case FLD_FILTER_KDMQ: method = SIP_METHOD_KDMQ; break; } return sip_method_str(method); } void filter_method_from_setting(const char *value) { char methods[250], method_expr[256]; int methods_len = strlen(value); char *comma; // If there's a method filter if (methods_len) { // Copy value into temporal array strncpy(methods, value, sizeof(methods)); // Replace all commas with pippes while ((comma = strchr(methods, ','))) *comma = '|'; // Create a regular expression memset(method_expr, 0, sizeof(method_expr)); snprintf(method_expr, sizeof(method_expr), "(%s)", methods); filter_set(FILTER_METHOD, method_expr); } else { filter_set(FILTER_METHOD, " "); } } void filter_payload_from_setting(const char *value) { if (value) filter_set(FILTER_PAYLOAD, value); } sngrep-1.8.2/src/curses/ui_filter.h000066400000000000000000000077641464272443000172720ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_filter.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage ui window for filtering options * * This file contains the functions and structures to manage the filter * dialog, that can be used to filter the lines in call list window. */ #ifndef __UI_FILTER_H #define __UI_FILTER_H #include "config.h" #include #include "ui_manager.h" /** * @brief Enum of available dialog fields * * Dialog form has a field array. Following enum represents the * order this fields are stored in panel info structure. * */ enum filter_field_list { FLD_FILTER_SIPFROM = 0, FLD_FILTER_SIPTO, FLD_FILTER_SRC, FLD_FILTER_DST, FLD_FILTER_PAYLOAD, FLD_FILTER_REGISTER, FLD_FILTER_INVITE, FLD_FILTER_SUBSCRIBE, FLD_FILTER_NOTIFY, FLD_FILTER_INFO, FLD_FILTER_KDMQ, FLD_FILTER_OPTIONS, FLD_FILTER_PUBLISH, FLD_FILTER_MESSAGE, FLD_FILTER_REFER, FLD_FILTER_UPDATE, FLD_FILTER_FILTER, FLD_FILTER_CANCEL, //! Never remove this field id @see filter_info FLD_FILTER_COUNT }; //! Sorter declaration of struct filter_info typedef struct filter_info filter_info_t; /** * @brief Filter panel private information * * This structure contains the durable data of filter panel. */ struct filter_info { //! Form that contains the filter fields FORM *form; //! An array of fields FIELD *fields[FLD_FILTER_COUNT + 1]; }; /** * @brief Creates a new filter panel * * This function allocates all required memory for * displaying the filter panel. It also draws all the * static information of the panel that will never be * redrawn. * * @param ui UI structure pointer */ void filter_create(ui_t *ui); /** * @brief Destroy filter panel * * This function do the final cleanups for this panel * @param ui UI structure pointer */ void filter_destroy(ui_t *ui); /** * @brief Get custom information of given panel * * Return ncurses users pointer of the given panel into panel's * information structure pointer. * * @param ui UI structure pointer * @return a pointer to info structure of given panel */ filter_info_t * filter_info(ui_t *ui); /** * @brief Manage pressed keys for filter panel * * This function is called by UI manager every time a * key is pressed. This allow the filter panel to manage * its own keys. * * @param ui UI structure pointer * @param key key code * @return enum @key_handler_ret */ int filter_handle_key(ui_t *ui, int key); /** * @brief Save form data to options * * This function will update the options values * of filter fields with its new value. * * @param ui UI structure pointer */ void filter_save_options(ui_t *ui); /** * @brief Return String value for a filter field * * @return method name */ const char* filter_field_method(int field_id); /** * @brief Set Method filtering from filter.methods setting format */ void filter_method_from_setting(const char *value); /** * @brief Set Payload filter from filter.payload setting */ void filter_payload_from_setting(const char *value); #endif sngrep-1.8.2/src/curses/ui_manager.c000066400000000000000000000506561464272443000174100ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_manager.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in ui_manager.h * */ #include "config.h" #include #include #include #include #include #include #include #include "setting.h" #include "ui_manager.h" #include "capture.h" #include "ui_call_list.h" #include "ui_call_flow.h" #include "ui_call_raw.h" #include "ui_filter.h" #include "ui_msg_diff.h" #include "ui_column_select.h" #include "ui_save.h" #include "ui_settings.h" /** * @brief Available panel windows list * * This list contains the available list of windows * and pointer to their main functions. */ static ui_t *panel_pool[] = { &ui_call_list, &ui_call_flow, &ui_call_raw, &ui_filter, &ui_save, &ui_msg_diff, &ui_column_select, &ui_settings, &ui_stats }; int ncurses_init() { int bg, fg; const char *term; // Set Locale setlocale(LC_CTYPE, ""); // Initialize curses if (!initscr()) { fprintf(stderr, "Unable to initialize ncurses mode.\n"); return -1; } // Check if user wants a black background if (setting_has_value(SETTING_BACKGROUND, "dark")) { assume_default_colors(COLOR_WHITE, COLOR_BLACK); } else { use_default_colors(); } // Enable Colors start_color(); cbreak(); // Dont write user input on screen noecho(); // Hide the cursor curs_set(0); // Only delay ESC Sequences 25 ms (we dont want Escape sequences) set_escdelay(25); // Redefine some keys term = getenv("TERM"); if (term && (!strcmp(term, "xterm") || !strcmp(term, "xterm-color") || !strcmp(term, "vt220"))) { define_key("\033[H", KEY_HOME); define_key("\033[F", KEY_END); define_key("\033OP", KEY_F(1)); define_key("\033OQ", KEY_F(2)); define_key("\033OR", KEY_F(3)); define_key("\033OS", KEY_F(4)); define_key("\033[11~", KEY_F(1)); define_key("\033[12~", KEY_F(2)); define_key("\033[13~", KEY_F(3)); define_key("\033[14~", KEY_F(4)); define_key("\033[17;2~", KEY_F(18)); } if (setting_has_value(SETTING_BACKGROUND, "dark")) { fg = COLOR_WHITE; bg = COLOR_BLACK; } else { fg = COLOR_DEFAULT; bg = COLOR_DEFAULT; } // Initialize colorpairs init_pair(CP_CYAN_ON_DEF, COLOR_CYAN, bg); init_pair(CP_YELLOW_ON_DEF, COLOR_YELLOW, bg); init_pair(CP_MAGENTA_ON_DEF, COLOR_MAGENTA, bg); init_pair(CP_GREEN_ON_DEF, COLOR_GREEN, bg); init_pair(CP_RED_ON_DEF, COLOR_RED, bg); init_pair(CP_BLUE_ON_DEF, COLOR_BLUE, bg); init_pair(CP_WHITE_ON_DEF, COLOR_WHITE, bg); init_pair(CP_DEF_ON_CYAN, fg, COLOR_CYAN); init_pair(CP_DEF_ON_BLUE, fg, COLOR_BLUE); init_pair(CP_WHITE_ON_BLUE, COLOR_WHITE, COLOR_BLUE); init_pair(CP_BLACK_ON_CYAN, COLOR_BLACK, COLOR_CYAN); init_pair(CP_WHITE_ON_CYAN, COLOR_WHITE, COLOR_CYAN); init_pair(CP_YELLOW_ON_CYAN, COLOR_YELLOW, COLOR_CYAN); init_pair(CP_BLUE_ON_CYAN, COLOR_BLUE, COLOR_CYAN); init_pair(CP_BLUE_ON_WHITE, COLOR_BLUE, COLOR_WHITE); init_pair(CP_CYAN_ON_WHITE, COLOR_CYAN, COLOR_WHITE); init_pair(CP_CYAN_ON_BLACK, COLOR_CYAN, COLOR_BLACK); return 0; } void ncurses_deinit() { // Clear screen before leaving refresh(); // End ncurses mode endwin(); } ui_t * ui_create_panel(enum panel_types type) { // Find the panel of given type and create it return ui_create(ui_find_by_type(type)); } ui_t * ui_find_by_panel(PANEL *panel) { int i; // Return ui pointer if found for (i = 0; i < PANEL_COUNT; i++) { if (panel_pool[i]->panel == panel) return panel_pool[i]; } return NULL; } ui_t * ui_find_by_type(enum panel_types type) { int i; // Return ui pointer if found for (i = 0; i < PANEL_COUNT; i++) { if (panel_pool[i]->type == type) return panel_pool[i]; } return NULL; } int ui_wait_for_input() { ui_t *ui; WINDOW *win; PANEL *panel; // While there are still panels while ((panel = panel_below(NULL))) { if (was_sigterm_received()) return 0; // Get panel interface structure ui = ui_find_by_panel(panel); // Set character input timeout 200 ms halfdelay(REFRESHTHSECS); // Avoid parsing any packet while UI is being drawn capture_lock(); // Query the interface if it needs to be redrawn if (ui_draw_redraw(ui)) { // Redraw this panel if (ui_draw_panel(ui) != 0) { capture_unlock(); return -1; } } capture_unlock(); // Update panel stack update_panels(); doupdate(); // Get topmost panel panel = panel_below(NULL); // Enable key input on current panel win = panel_window(panel); keypad(win, TRUE); // Get pressed key int c = wgetch(win); // Timeout, no key pressed if (c == ERR) continue; capture_lock(); // Handle received key int hld = KEY_NOT_HANDLED; while (hld != KEY_HANDLED) { // Check if current panel has custom bindings for that key hld = ui_handle_key(ui, c); if (hld == KEY_HANDLED) { // Panel handled this key continue; } else if (hld == KEY_PROPAGATED) { // Destroy current panel ui_destroy(ui); // Try to handle this key with the previous panel ui = ui_find_by_panel(panel_below(NULL)); } else { // Key not handled by UI nor propagated. Use default handler hld = ui_default_handle_key(ui, c); } } capture_unlock(); } return 0; } int ui_default_handle_key(ui_t *ui, int key) { int action = -1; // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { // Check if we handle this action switch (action) { case ACTION_RESIZE_SCREEN: ui_resize_panels(); break; case ACTION_TOGGLE_SYNTAX: setting_toggle(SETTING_SYNTAX); break; case ACTION_TOGGLE_HINT: setting_toggle(SETTING_ALTKEY_HINT); break; case ACTION_CYCLE_COLOR: setting_toggle(SETTING_COLORMODE); break; case ACTION_SHOW_ALIAS: setting_toggle(SETTING_DISPLAY_ALIAS); break; case ACTION_SHOW_SETTINGS: ui_create_panel(PANEL_SETTINGS); break; case ACTION_TOGGLE_PAUSE: // Pause/Resume capture capture_set_paused(!capture_paused()); break; case ACTION_SHOW_HELP: ui_help(ui); break; case ACTION_PREV_SCREEN: ui_destroy(ui); break; default: // Parse next action continue; } // Default handler has handled the key break; } // Consider the key handled at this point return KEY_HANDLED; } void ui_resize_panels() { PANEL *panel = NULL; // While there are still panels while ((panel = panel_below(panel))) { // Invoke resize for this panel ui_resize_panel(ui_find_by_panel(panel)); } } void title_foot_box(PANEL *panel) { int height, width; WINDOW *win = panel_window(panel); // Sanity check if (!win) return; // Get window size getmaxyx(win, height, width); box(win, 0, 0); mvwaddch(win, 2, 0, ACS_LTEE); mvwhline(win, 2, 1, ACS_HLINE, width - 2); mvwaddch(win, 2, width - 1, ACS_RTEE); mvwaddch(win, height - 3, 0, ACS_LTEE); mvwhline(win, height - 3, 1, ACS_HLINE, width - 2); mvwaddch(win, height - 3, width - 1, ACS_RTEE); } int draw_message(WINDOW *win, sip_msg_t *msg) { return draw_message_pos(win, msg, 0); } int draw_message_pos(WINDOW *win, sip_msg_t *msg, int starting) { int height, width, line, column, i; const char *cur_line, *payload, *method = NULL; int syntax = setting_enabled(SETTING_SYNTAX); const char *nonascii = setting_get_value(SETTING_CR_NON_ASCII); // Default text format int attrs = A_NORMAL | COLOR_PAIR(CP_DEFAULT); if (syntax) wattrset(win, attrs); // Get window of main panel getmaxyx(win, height, width); // Get message method (if request) if (msg_is_request(msg)) { method = sip_method_str(msg->reqresp); } // Get packet payload cur_line = payload = (const char *) msg_get_payload(msg); // Print msg payload line = starting; column = 0; for (i = 0; i < strlen(payload); i++) { // If syntax highlighting is enabled if (syntax) { // First line highlight if (line == starting) { // Request syntax if (i == 0 && strncmp(cur_line, "SIP/2.0", 7)) attrs = A_BOLD | COLOR_PAIR(CP_YELLOW_ON_DEF); // Response syntax if (i == 8 && !strncmp(cur_line, "SIP/2.0", 7)) attrs = A_BOLD | COLOR_PAIR(CP_RED_ON_DEF); // SIP URI syntax if (method && i == strlen(method) + 1) { attrs = A_BOLD | COLOR_PAIR(CP_CYAN_ON_DEF); } } else { // Header syntax if (strchr(cur_line, ':') && payload + i < strchr(cur_line, ':')) attrs = A_NORMAL | COLOR_PAIR(CP_GREEN_ON_DEF); // Call-ID Header syntax if (!strncasecmp(cur_line, "Call-ID:", 8) && column > 8) attrs = A_BOLD | COLOR_PAIR(CP_MAGENTA_ON_DEF); // CSeq Heaedr syntax if (!strncasecmp(cur_line, "CSeq:", 5) && column > 5 && !isdigit(payload[i])) attrs = A_NORMAL | COLOR_PAIR(CP_YELLOW_ON_DEF); // tag and branch syntax if (i > 0 && payload[i - 1] == ';') { // Highlight branch if requested if (setting_enabled(SETTING_SYNTAX_BRANCH)) { if (!strncasecmp(payload + i, "branch", 6)) { attrs = A_BOLD | COLOR_PAIR(CP_CYAN_ON_DEF); } } // Highlight tag if requested if (setting_enabled(SETTING_SYNTAX_TAG)) { if (!strncasecmp(payload + i, "tag", 3)) { if (!strncasecmp(cur_line, "From:", 5)) { attrs = A_BOLD | COLOR_PAIR(CP_DEFAULT); } else { attrs = A_BOLD | COLOR_PAIR(CP_GREEN_ON_DEF); } } } } // SDP syntax if (strcspn(cur_line, "=") == 1) attrs = A_NORMAL | COLOR_PAIR(CP_DEFAULT); } // Remove previous syntax if (strcspn(payload + i, " \n;<>") == 0) { wattroff(win, attrs); attrs = A_NORMAL | COLOR_PAIR(CP_DEFAULT); } // Syntax hightlight text! wattron(win, attrs); } // Dont print this characters if (payload[i] == '\r') continue; // Store where the line begins if (payload[i] == '\n') cur_line =payload + i + 1; // Move to the next line if line is filled or a we reach a line break if (column > width - 1 || payload[i] == '\n') { line++; column = 0; } // No need to print new line characters if (payload[i] == '\n') continue; // Put next character in position if (isascii(payload[i])) { mvwaddch(win, line, column++, payload[i]); } else { mvwaddch(win, line, column++, *nonascii); } // Stop if we've reached the bottom of the window if (line == height) break; } // Disable syntax when leaving if (syntax) wattroff(win, attrs); // Redraw raw win wnoutrefresh(win); return line - starting; } int dialog_run(const char *fmt, ...) { char textva[2048]; va_list ap; int height, width; WINDOW *win; char *word; int col = 2; int line = 2; // Get the message from the format string va_start(ap, fmt); vsprintf(textva, fmt, ap); va_end(ap); // Determine dialog dimensions height = 6 + (strlen(textva) / 50); width = strlen(textva); // Check we don't have a too big or small window if (width > DIALOG_MAX_WIDTH) width = DIALOG_MAX_WIDTH; if (width < DIALOG_MIN_WIDTH) width = DIALOG_MIN_WIDTH; // Create the window win = newwin(height, width, (LINES - height) / 2, (COLS - width) / 2); box(win, 0, 0); // Write the message into the screen for (word = strtok(textva, " "); word; word = strtok(NULL, " ")) { if (col + strlen(word) > width - 2) { line++; col = 2; } mvwprintw(win, line, col, "%s", word); col += strlen(word) + 1; } // Write Accept button wattron(win, A_REVERSE); mvwprintw(win, height - 2, width/2 - 5, "[ Accept ]"); curs_set(0); // Disable input timeout nocbreak(); cbreak(); // Wait for input wgetch(win); delwin(win); return 1; } WINDOW * dialog_progress_run(const char *fmt, ...) { char textva[2048]; va_list ap; int height, width; WINDOW *win; char *word; int col = 2; int line = 2; // Get the message from the format string va_start(ap, fmt); vsprintf(textva, fmt, ap); va_end(ap); // Determine dialog dimensions height = 6 + (strlen(textva) / 50); width = strlen(textva); // Check we don't want a too big or small window if (width > DIALOG_MAX_WIDTH) width = DIALOG_MAX_WIDTH; if (width < DIALOG_MIN_WIDTH) width = DIALOG_MIN_WIDTH; // Create the window win = newwin(height, width, (LINES - height) / 2, (COLS - width) / 2); box(win, 0, 0); // Write the message into the screen for (word = strtok(textva, " "); word; word = strtok(NULL, " ")) { if (col + strlen(word) > width - 2) { line++; col = 2; } mvwprintw(win, line, col, "%s", word); col += strlen(word) + 1; } curs_set(0); wrefresh(win); // Disable input timeout nocbreak(); cbreak(); return win; } void dialog_progress_set_value(WINDOW *win, int perc) { int width; width = getmaxx(win); mvwhline(win, 4, 4, '-', width - 10); mvwaddch(win, 4, 3, '['); mvwaddch(win, 4, width - 7, ']'); mvwprintw(win, 4, width - 5, "%d%%", perc); if (perc > 0 && perc <= 100) mvwhline(win, 4, 4, ACS_CKBOARD, (width - 10) * ((float)perc/100)); wrefresh(win); } void dialog_progress_destroy(WINDOW *win) { delwin(win); } int dialog_confirm(const char *title, const char *text, const char *options) { WINDOW *dialog_win; int key, i, curs, newl, height, width; char *str, *tofree, *option, *word; int selected = 0; int optioncnt = 1; int col = 2; int line = 3; char opts[4][10]; int action = -1; // Initialize memset(opts, 0, 4 * 10); // Check how many options exists for (i=0; options[i]; i++) { if (options[i] == ',') optioncnt++; } // We only support 4 options if (optioncnt > 4) return -1; // Calculate proper width taking into acount longest data width = strlen(options) + 6 * optioncnt; if (strlen(title) + 4 > width) width = strlen(title) + 4; if (strlen(text) > width && strlen(text) < 50) width = strlen(text); // Check we don't want a too big or small window if (width > DIALOG_MAX_WIDTH) width = DIALOG_MAX_WIDTH; if (width < DIALOG_MIN_WIDTH) width = DIALOG_MIN_WIDTH; // Determine dialog dimensions height = 7; // Minimum for header and button lines height += (strlen(text) / width); // Space for the text. // Add one extra line for each newline in the text for (i=0; text[i]; i++) { if (text[i] == '\n') height++; } // Parse each line of payload looking for sdp information tofree = str = strdup((char*)options); i = 0; while ((option = strsep(&str, ",")) != NULL) { strcpy(opts[i++], option); } sng_free(tofree); // Create a new panel and show centered dialog_win = newwin(height, width, (LINES - height) / 2, (COLS - width) / 2); keypad(dialog_win, TRUE); curs = curs_set(0); // Set the window title mvwprintw(dialog_win, 1, (width - strlen(title)) / 2, "%s", title); // Write border and boxes around the window wattron(dialog_win, COLOR_PAIR(CP_BLUE_ON_DEF)); box(dialog_win, 0, 0); mvwhline(dialog_win, 2, 1, ACS_HLINE, width); mvwaddch(dialog_win, 2, 0, ACS_LTEE); mvwaddch(dialog_win, 2, width - 1, ACS_RTEE); mvwhline(dialog_win, height - 3, 1, ACS_HLINE, width); mvwaddch(dialog_win, height - 3, 0, ACS_LTEE); mvwaddch(dialog_win, height - 3, width - 1, ACS_RTEE); // Exit confirmation message message wattron(dialog_win, COLOR_PAIR(CP_CYAN_ON_DEF)); // Write the message into the screen tofree = str = strdup((char*)text); newl = 0; while ((word = strsep(&str, " ")) != NULL) { if (word[strlen(word)-1] == '\n') { word[strlen(word)-1] = '\0'; newl = 1; } if (col + strlen(word) > width - 2) { line++; col = 2; } mvwprintw(dialog_win, line, col, "%s", word); col += strlen(word) + 1; if (newl) { line++; col = 2; newl = 0; } } sng_free(tofree); wattroff(dialog_win, COLOR_PAIR(CP_CYAN_ON_DEF)); for (;;) { // A list of available keys in this window for (i = 0; i < optioncnt; i++ ) { if (i == selected) wattron(dialog_win, A_REVERSE); mvwprintw(dialog_win, height - 2, 10 + 10 * i, "[ %s ]", opts[i]); wattroff(dialog_win, A_REVERSE); } // Get pressed key key = wgetch(dialog_win); // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { // Check if we handle this action switch (action) { case ACTION_RIGHT: selected++; break; case ACTION_LEFT: case ACTION_NEXT_FIELD: selected--; break; case ACTION_SELECT: case ACTION_CONFIRM: goto done; case ACTION_PREV_SCREEN: selected = -1; goto done; default: // Parse next action continue; } // key handled successfully break; } // Cycle through ooptions if (selected > optioncnt - 1) selected = 0; if (selected < 0) selected = optioncnt - 1; } done: delwin(dialog_win); curs_set(curs); return selected; } sngrep-1.8.2/src/curses/ui_manager.h000066400000000000000000000140011464272443000173750ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_manager.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage interface panels * * All sngrep panel pointers are encapsulated into a ui structure that is * used to invoke custom functions for creating, destroying, drawing, etc * the screens. * * This sctructure also manages concurrents updates and access to ncurses * panel pointers. * */ #ifndef __SNGREP_UI_MANAGER_H #define __SNGREP_UI_MANAGER_H #include "config.h" #include "theme.h" #include "ui_panel.h" #include "sip.h" #include "group.h" #include "keybinding.h" #include "setting.h" //! Refresh UI every 200 ms #define REFRESHTHSECS 2 //! Default dialog dimensions #define DIALOG_MAX_WIDTH 100 #define DIALOG_MIN_WIDTH 40 /** * Define existing panels */ extern ui_t ui_call_list; extern ui_t ui_call_flow; extern ui_t ui_call_raw; extern ui_t ui_filter; extern ui_t ui_save; extern ui_t ui_msg_diff; extern ui_t ui_column_select; extern ui_t ui_settings; extern ui_t ui_stats; /** * @brief Initialize ncurses mode * * This functions will initialize ncurses mode * * @returns 0 on ncurses initialization success, 1 otherwise */ int ncurses_init(); /** * @brief Stops ncurses mode * * This functions will deinitialize ncurse mode * * @returns 0 on ncurses initialization success, 1 otherwise */ void ncurses_deinit(); /** * @brief Create a panel of a given type * * Create a ncurses panel of the given type. * This function is a small wrapper for panel create function * * @param type Panel Type * @return the ui structure with the panel pointer created* */ ui_t * ui_create_panel(enum panel_types type); /** * @brief Find a ui from its pannel pointer */ ui_t * ui_find_by_panel(PANEL *panel); /** * @brief Find a ui form its panel id */ ui_t * ui_find_by_type(enum panel_types type); /** * @brief Wait for user input in topmost panel * * This function manages all user input in all panel types and * redraws the panel using its own draw function * */ int ui_wait_for_input(); /** * @brief Default handler for keys * * If ui doesn't handle the given key (ui_handle_key returns the key value) * then the default handler will be invoked * * @param ui Current displayed UI structure * @param key key pressed by user */ int ui_default_handle_key(ui_t *ui, int key); /** * @brief Call Resize function in all panels in the stack * * This function acts as handler of screen resize function invoking all * resize functions of panels that implement it. */ void ui_resize_panels(); /** * @brief Draw a box around passed windows * * Draw a box around passed windows with two bars * (top and bottom) of one line each. * * @param win Window to draw borders on */ void title_foot_box(PANEL *panel); /** * @brief Draw a message payload in a window * * Generic drawing function for payload. This function will start * writting at 0,0 and return the number of lines written. * * @param win Ncurses window to draw payload * @param msg Msg to be drawn */ int draw_message(WINDOW *win, sip_msg_t *msg); /** * @brief Draw a message payload in a window starting at a given line * * Generic drawing function for payload. This function will start * writting at line starting and first column and return the number * of lines written. * * @param win Ncurses window to draw payload * @param msg Msg to be drawn * @param starting Number of win line to start writting payload */ int draw_message_pos(WINDOW *win, sip_msg_t *msg, int starting); /** * @brief Draw a centered dialog with a message * * Create a centered dialog with a message. * @param msg Message to be drawn */ int dialog_run(const char *fmt, ...); /** * @brief Create a new progress bar dialog * * Create a new progress bar dialog with the given text. The returned * pointer should be used as parameter for @dialog_progress_set_value * in order to move the progress bar percentage. * * @param fmt, vaarg Text to be displayed above the progress bar * @return a pointer to the created window. */ WINDOW * dialog_progress_run(const char *fmt, ...); /** * @brief Set current percentage of dialog progress bar * * @param win Window pointer created with @dialog_progress_run * @param perc 0-100 percentage of progress bar */ void dialog_progress_set_value(WINDOW *win, int perc); /** * @brief Destroy a dialog created by @dialog_progress_run * * This function will deallocate all memory and close the * given window pointer. * * @param win Window pointer created with @dialog_progress_run */ void dialog_progress_destroy(WINDOW *win); /** * @brief Create a new confirmation dialog with multiple buttons * * This function can be used to create dialogs with multiple buttons to * request user confirmation. By default, the first given option will * be selected. * * @param title Title displayed in the top of the dialog * @param text Text displayed inside the dialog * @param options Comma separated labels for the different buttons * @return the index of the button pressed */ int dialog_confirm(const char *title, const char *text, const char *options); #endif // __SNGREP_UI_MANAGER_H sngrep-1.8.2/src/curses/ui_msg_diff.c000066400000000000000000000146011464272443000175420ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_msg_diff.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in ui_msg_diff.h * */ #include #include #include "ui_msg_diff.h" #include "option.h" /*** * * Some basic ascii art of this panel. * * +--------------------------------------------------------+ * | Title | * | First message header | Second message header | * | | | * | First message payload | | * | | | * | | Second message payload | * | | | * | | | * | | | * | | | * | | | * | Usefull hotkeys | * +--------------------------------------------------------+ * */ /** * Ui Structure definition for Message Diff panel */ ui_t ui_msg_diff = { .type = PANEL_MSG_DIFF, .panel = NULL, .create = msg_diff_create, .handle_key = NULL, .destroy = msg_diff_destroy, .draw = msg_diff_draw, .help = NULL }; void msg_diff_create(ui_t *ui) { int hwidth; msg_diff_info_t *info; // Create a new panel to fill all the screen ui_panel_create(ui, LINES, COLS); // Initialize panel specific data info = sng_malloc(sizeof(msg_diff_info_t)); // Store it into panel userptr set_panel_userptr(ui->panel, (void*) info); // Calculate subwindows width hwidth = ui->width / 2 - 1; // Create 2 subwindows, one for each message info->one_win = subwin(ui->win, ui->height - 2, hwidth, 1, 0); info->two_win = subwin(ui->win, ui->height - 2, hwidth, 1, hwidth + 1); // Header - Footer - Address // Draw a vertical line to separe both subwindows mvwvline(ui->win, 0, hwidth, ACS_VLINE, ui->height); // Draw title ui_set_title(ui, "sngrep - SIP messages flow viewer"); // Draw keybindings msg_diff_draw_footer(ui); } void msg_diff_destroy(ui_t *ui) { sng_free(msg_diff_info(ui)); ui_panel_destroy(ui); } msg_diff_info_t * msg_diff_info(ui_t *ui) { return (msg_diff_info_t*) panel_userptr(ui->panel); } int msg_diff_line_highlight(const char* payload1, const char* payload2, char *highlight) { char search[MAX_SIP_PAYLOAD]; int len, i; // Initialize search terms memset(search, 0, sizeof(search)); len = 0; for (i = 0; i < strlen(payload1); i++) { // Store this char in the search term search[len++] = payload1[i]; // If we have a full line in search array if (payload1[i] == '\n') { // Check if this line is in the other payload if (strstr(payload2, search) == NULL) { // Highlight this line as different from the other payload memset(highlight + i - len + 1, '1', len); } // Reset search terms memset(search, 0, sizeof(search)); len = 0; } } return 0; } void msg_diff_draw_footer(ui_t *ui) { const char *keybindings[] = { key_action_key_str(ACTION_PREV_SCREEN), "Calls Flow", key_action_key_str(ACTION_SHOW_HELP), "Help" }; ui_draw_bindings(ui, keybindings, 4); } int msg_diff_draw(ui_t *ui) { // Get panel information msg_diff_info_t *info = msg_diff_info(ui); char highlight[MAX_SIP_PAYLOAD]; // Draw first message memset(highlight, 0, sizeof(highlight)); msg_diff_line_highlight(msg_get_payload(info->one), msg_get_payload(info->two), highlight); msg_diff_draw_message(info->one_win, info->one, highlight); // Draw second message memset(highlight, 0, sizeof(highlight)); msg_diff_line_highlight(msg_get_payload(info->two), msg_get_payload(info->one), highlight); msg_diff_draw_message(info->two_win, info->two, highlight); // Redraw footer msg_diff_draw_footer(ui); return 0; } int msg_diff_draw_message(WINDOW *win, sip_msg_t *msg, char *highlight) { int height, width, line, column, i; char header[MAX_SIP_PAYLOAD]; const char * payload = msg_get_payload(msg); // Clear the window werase(win); // Get window of main panel getmaxyx(win, height, width); wattron(win, A_BOLD); mvwprintw(win, 0, 0, "%s", sip_get_msg_header(msg, header)); wattroff(win, A_BOLD); // Print msg payload line = 2; column = 0; for (i = 0; i < strlen(payload); i++) { if (payload[i] == '\r') continue; if (column == width || payload[i] == '\n') { line++; column = 0; continue; } if (line == height) break; if (highlight[i] == '1') { wattron(win, COLOR_PAIR(CP_YELLOW_ON_DEF)); } else { wattroff(win, COLOR_PAIR(CP_YELLOW_ON_DEF)); } // Put next character in position mvwaddch(win, line, column++, payload[i]); } // Redraw raw win wnoutrefresh(win); return 0; } int msg_diff_set_msgs(ui_t *ui, sip_msg_t *one, sip_msg_t *two) { msg_diff_info_t *info; // Get panel information info = msg_diff_info(ui); info->one = one; info->two = two; return 0; } sngrep-1.8.2/src/curses/ui_msg_diff.h000066400000000000000000000072451464272443000175550ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_msg_diff.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage diff display * */ #ifndef __UI_MSG_DIFF_H #define __UI_MSG_DIFF_H #include "config.h" #include "ui_manager.h" //! Sorter declaration of struct msg_diff_info typedef struct msg_diff_info msg_diff_info_t; /** * @brief Call raw status information * * This data stores the actual status of the panel. It's stored in the * PANEL user pointer. */ struct msg_diff_info { //! First message to compare sip_msg_t *one; //! Second message to compare sip_msg_t *two; //! Left displayed subwindow WINDOW *one_win; //! Right displayed subwindow WINDOW *two_win; }; /** * @brief Create Message diff panel * * This function will allocate the ncurses pointer and draw the static * stuff of the screen (which usually won't be redrawn) * It will also create an information structure of the panel status and * store it in the panel's userpointer * * @param ui UI structure pointer * @return the allocated ncurses panel */ void msg_diff_create(ui_t *ui); /** * @brief Deallocate panel memory * * This function will be called from ui manager logic to free * message diff panel memory * * @param ui UI structure pointer */ void msg_diff_destroy(ui_t *ui); /** * @brief Get panel information structure * * All required information of the panel is stored in the info pointer * of the panel. * This function will return the pointer to the info structure of the * panel. * * @param ui UI structure pointer * @return a pointer to the info structure or NULL if no structure exists */ msg_diff_info_t * msg_diff_info(ui_t *ui); /** * @brief Redraw panel data * * This function will be called from ui manager logic when the panels * needs to be redrawn. * * @param ui UI structure pointer * @return 0 in all cases */ int msg_diff_draw(ui_t *ui); /** * @brief Draw panel footer * * Usually panel footer contains useful keybidings. This function * will draw that footer * * @param ui UI structure pointer */ void msg_diff_draw_footer(ui_t *ui); /** * @brief Draw a message into a raw subwindow * * This function will be called for each message that wants to be draw * in the panel. * */ int msg_diff_draw_message(WINDOW *win, sip_msg_t *msg, char *highlight); /** * @brief Set the panel working messages * * This function will access the panel information and will set the * msg pointers to the processed messages. * * @param ui UI structure pointer * @param one Message pointer to be set in the internal info struct * @param two Message pointer to be set in the internal info struct * @return 0 in all cases */ int msg_diff_set_msgs(ui_t *ui, sip_msg_t *one, sip_msg_t *two); #endif sngrep-1.8.2/src/curses/ui_panel.c000066400000000000000000000130311464272443000170570ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_panel.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in ui_panel.h */ #include "config.h" #include "ui_panel.h" #include #include "theme.h" ui_t * ui_create(ui_t *ui) { // If ui has no panel if (!ui->panel) { // Create the new panel for this ui if (ui->create) { ui->create(ui); } } // Force screen draw for the first time ui->changed = true; // And return it return ui; } void ui_destroy(ui_t *ui) { // If there is no ui panel, we're done if (!ui || !ui->panel) return; // Hide this panel before destroying hide_panel(ui->panel); // If panel has a destructor function use it if (ui->destroy) { ui->destroy(ui); } // Initialize panel pointer ui->panel = NULL; } PANEL * ui_get_panel(ui_t *ui) { // Return panel pointer of ui struct return (ui) ? ui->panel : NULL; } bool ui_draw_redraw(ui_t *ui) { // Sanity check, this should not happen if (!ui || !ui->panel) return false; // If ui has changed, force redraw. Don't even ask. if (ui->changed) { ui->changed = false; return true; } // Query the panel if its needs to be redrawn if (ui->redraw) { return ui->redraw(ui); } return true; } int ui_draw_panel(ui_t *ui) { //! Sanity check, this should not happen if (!ui || !ui->panel) return -1; // Request the panel to draw on the scren if (ui->draw) { return ui->draw(ui); } else { touchwin(ui->win); } return 0; } int ui_resize_panel(ui_t *ui) { //! Sanity check, this should not happen if (!ui) return -1; // Notify the panel screen size has changed if (ui->resize) { return ui->resize(ui); } return 0; } void ui_help(ui_t *ui) { // Disable input timeout nocbreak(); cbreak(); // If current ui has help function if (ui->help) { ui->help(ui); } } int ui_handle_key(ui_t *ui, int key) { int hld = KEY_NOT_HANDLED; // Request the panel to handle the key if (ui->handle_key) { hld = ui->handle_key(ui, key); } // Force redraw when the user presses keys ui->changed = true; return hld; } void ui_panel_create(ui_t *ui, int height, int width) { ui->width = width; ui->height = height; ui->x = ui->y = 0; // If panel doesn't fill the screen center it if (ui->height != LINES) ui->x = (LINES - height) / 2; if (ui->width != COLS) ui->y = (COLS - width) / 2; ui->win = newwin(height, width, ui->x, ui->y); ui->panel = new_panel(ui->win); } void ui_panel_destroy(ui_t *ui) { // Deallocate panel pointer del_panel(ui->panel); // Deallocate panel window delwin(ui->win); } void ui_set_title(ui_t *ui, const char *title) { // FIXME Reverse colors on monochrome terminals if (!has_colors()) { wattron(ui->win, A_REVERSE); } // Center the title on the window wattron(ui->win, A_BOLD | COLOR_PAIR(CP_DEF_ON_CYAN)); ui_clear_line(ui, 0); mvwprintw(ui->win, 0, (ui->width - strlen(title)) / 2, "%s", title); wattroff(ui->win, A_BOLD | A_REVERSE | COLOR_PAIR(CP_DEF_ON_CYAN)); } void ui_clear_line(ui_t *ui, int line) { // We could do this with wcleartoel but we want to // preserve previous window attributes. That way we // can set the background of the line. mvwprintw(ui->win, line, 0, "%*s", ui->width, ""); } void ui_draw_bindings(ui_t *ui, const char *keybindings[], int count) { int key, xpos = 0; // Reverse colors on monochrome terminals if (!has_colors()) { wattron(ui->win, A_REVERSE); } // Write a line all the footer width wattron(ui->win, COLOR_PAIR(CP_DEF_ON_CYAN)); ui_clear_line(ui, ui->height - 1); // Draw keys and their actions for (key = 0; key < count; key += 2) { wattron(ui->win, A_BOLD | COLOR_PAIR(CP_WHITE_ON_CYAN)); mvwprintw(ui->win, ui->height - 1, xpos, "%-*s", (int) strlen(keybindings[key]) + 1, keybindings[key]); xpos += strlen(keybindings[key]) + 1; wattroff(ui->win, A_BOLD | COLOR_PAIR(CP_WHITE_ON_CYAN)); wattron(ui->win, COLOR_PAIR(CP_BLACK_ON_CYAN)); mvwprintw(ui->win, ui->height - 1, xpos, "%-*s", (int) strlen(keybindings[key + 1]) + 1, keybindings[key + 1]); wattroff(ui->win, COLOR_PAIR(CP_BLACK_ON_CYAN)); xpos += strlen(keybindings[key + 1]) + 3; } // Disable reverse mode in all cases wattroff(ui->win, A_REVERSE | A_BOLD); } sngrep-1.8.2/src/curses/ui_panel.h000066400000000000000000000154321464272443000170730ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file u_panel.h * @author Ivan Alonso [aka Kaian] * * @brief Common process for all Interface panels * * This file contains common functions shared by all panels. */ #ifndef __SNGREP_UI_PANEL_H #define __SNGREP_UI_PANEL_H #ifdef WITH_UNICODE #define _X_OPEN_SOURCE_EXTENDED #include #endif #include #include #include #include //! Possible key handler results enum key_handler_ret { //! Panel has handled the key, dont'use default key handler KEY_HANDLED = 0, //! Panel has not handled the key, try defualt key handler KEY_NOT_HANDLED = -1, //! Panel destroys and requests previous panel to handle key KEY_PROPAGATED = -2 }; /** * @brief Enum for available panel types * * Mostly used for managing keybindings and offloop ui refresh */ enum panel_types { //! Call List ui screen PANEL_CALL_LIST = 0, //! Call-Flow ui screen PANEL_CALL_FLOW, //! Raw SIP messages ui screen PANEL_CALL_RAW, //! Filters panel PANEL_FILTER, //! Save to pcap panel PANEL_SAVE, //! Message comprare PANEL_MSG_DIFF, //! Column selector panel PANEL_COLUMN_SELECT, //! Settings panel PANEL_SETTINGS, //! Stats panel PANEL_STATS, //! Panel Counter PANEL_COUNT, }; //! Shorter declaration of ui structure typedef struct ui ui_t; /** * @brief Panel information structure * * This struct contains the panel related data, including * a pointer to the function that manages its drawing */ struct ui { //! Curses panel pointer PANEL *panel; //! Window for the curses panel WINDOW *win; //! Height of the curses window int height; //! Width of the curses window int width; //! Vertical starting position of the window int x; //! Horizontal starting position of the window int y; //! Panel Type @see panel_types enum enum panel_types type; //! Flag this panel as redraw required bool changed; //! Constructor for this panel void (*create)(ui_t *); //! Destroy current panel void (*destroy)(ui_t *); //! Query the panel if redraw is required bool (*redraw)(ui_t *); //! Request the panel to redraw its data int (*draw)(ui_t *); //! Notifies the panel the screen has changed int (*resize)(ui_t *); //! Handle a custom keybind on this panel int (*handle_key)(ui_t *, int key); //! Show help window for this panel (if any) int (*help)(ui_t *); }; /** * @brief Create a panel structure * * Create a ncurses panel associated to the given ui * This function is a small wrapper for panel create function * * @param ui UI structure * @return the ui structure with the panel pointer created */ ui_t * ui_create(ui_t *ui); /** * @brief Destroy a panel structure * * Removes the panel associatet to the given ui and free * its memory. Most part of this task is done in the custom * destroy function of the panel. * * @param ui UI structure */ void ui_destroy(ui_t *ui); /** * @brief Get panel pointer from an ui element * * Basic getter to get the Ncurses PANEL pointer * from ui structure. Use this instead of accessing * directly to the pointer. * * @param ui UI structure * @return ncurses panel pointer of given UI */ PANEL * ui_get_panel(ui_t *ui); /** * @brief Redrawn current ui * * This function acts as wrapper to custom ui draw functions * with some checks * * @param ui UI structure * @return 0 if ui has been drawn, -1 otherwise */ int ui_resize_panel(ui_t *ui); /** * @brief Check if the panel requires redraw * * This function acts as wrapper to custom ui redraw function * with some checks * * @param ui UI structure * @return true if the panel must be drawn, false otherwise */ bool ui_draw_redraw(ui_t *ui); /** * @brief Notifies current ui the screen size has changed * * This function acts as wrapper to custom ui resize functions * with some checks * * @param ui UI structure * @return 0 if ui has been resize, -1 otherwise */ int ui_draw_panel(ui_t *ui); /** * @brief Show help screen from current UI (if any) * * This function will display the help screen for given * ui if exits. * All help screens exits after any character input * * @param ui UI structure */ void ui_help(ui_t *ui); /** * @brief Handle key inputs on given UI * * This function will pass the input key sequence * to the given UI. * * @param ui UI structure * @param key keycode sequence of the pressed keys and mods * @return enum @key_handler_ret* */ int ui_handle_key(ui_t *ui, int key); /** * @brief Create a ncurses panel for the given ui * * Create a panel and associated window and store their * porinters in ui structure. * If height and widht doesn't match the screen dimensions * the panel will be centered on the screen. * * @param ui UI structure * @param height panel window height * @param width panel windo width */ void ui_panel_create(ui_t *ui, int height, int width); /** * @brief Deallocate ncurses panel and window * * @param ui UI structure */ void ui_panel_destroy(ui_t *ui); /** * @brief Draw title at the top of the panel UI * * This function will draw a line with the title on the first * row of the UI panel's window * * @param ui UI structure * @param title String containing the title */ void ui_set_title(ui_t *ui, const char *title); /** * @brief Clear a given window line * * This function can be used to clear a given line on the * screen. * * @param ui UI structure * @param line Number of line to be cleared */ void ui_clear_line(ui_t *ui, int line); /** * @brief Draw keybinding info at the bottom of the panel * * This function will draw a line with the available keybindings * in the last line of the given panel * */ void ui_draw_bindings(ui_t *ui, const char *keybindings[], int count); #endif /* __SNGREP_UI_PANEL_H */ sngrep-1.8.2/src/curses/ui_save.c000066400000000000000000000463311464272443000167270ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_save_pcap.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in ui_save_pcap.c */ #include #include #include #include #include #include #include #include "ui_save.h" #include "setting.h" #include "capture.h" #include "filter.h" /** * Ui Structure definition for Save panel */ ui_t ui_save = { .type = PANEL_SAVE, .panel = NULL, .create = save_create, .draw = save_draw, .handle_key = save_handle_key, .destroy = save_destroy }; void save_create(ui_t *ui) { save_info_t *info; char savepath[MAX_SETTING_LEN]; // Pause the capture while saving capture_set_paused(1); // Cerate a new indow for the panel and form ui_panel_create(ui, 15, 68); // Initialize save panel specific data info = sng_malloc(sizeof(save_info_t)); // Store it into panel userptr set_panel_userptr(ui->panel, (void*) info); // Initialize the fields int total, displayed; info->fields[FLD_SAVE_PATH] = new_field(1, 52, 3, 13, 0, 0); info->fields[FLD_SAVE_FILE] = new_field(1, 47, 4, 13, 0, 0); info->fields[FLD_SAVE_ALL] = new_field(1, 1, 7, 4, 0, 0); info->fields[FLD_SAVE_SELECTED] = new_field(1, 1, 8, 4, 0, 0); info->fields[FLD_SAVE_DISPLAYED] = new_field(1, 1, 9, 4, 0, 0); info->fields[FLD_SAVE_MESSAGE] = new_field(1, 1, 10, 4, 0, 0); info->fields[FLD_SAVE_PCAP] = new_field(1, 1, 7, 36, 0, 0); info->fields[FLD_SAVE_PCAP_RTP] = new_field(1, 1, 8, 36, 0, 0); info->fields[FLD_SAVE_TXT] = new_field(1, 1, 9, 36, 0, 0); info->fields[FLD_SAVE_SAVE] = new_field(1, 10, ui->height - 2, 20, 0, 0); info->fields[FLD_SAVE_CANCEL] = new_field(1, 10, ui->height - 2, 40, 0, 0); info->fields[FLD_SAVE_COUNT] = NULL; // Set fields options field_opts_off(info->fields[FLD_SAVE_PATH], O_STATIC); field_opts_off(info->fields[FLD_SAVE_PATH], O_AUTOSKIP); field_opts_off(info->fields[FLD_SAVE_FILE], O_STATIC); field_opts_off(info->fields[FLD_SAVE_FILE], O_AUTOSKIP); field_opts_off(info->fields[FLD_SAVE_ALL], O_AUTOSKIP); field_opts_off(info->fields[FLD_SAVE_SELECTED], O_AUTOSKIP); field_opts_off(info->fields[FLD_SAVE_DISPLAYED], O_AUTOSKIP); field_opts_off(info->fields[FLD_SAVE_MESSAGE], O_VISIBLE); // Limit max save path and file length set_max_field(info->fields[FLD_SAVE_PATH], MAX_SETTING_LEN); set_max_field(info->fields[FLD_SAVE_FILE], MAX_SETTING_LEN); // Change background of input fields set_field_back(info->fields[FLD_SAVE_PATH], A_UNDERLINE); set_field_back(info->fields[FLD_SAVE_FILE], A_UNDERLINE); // Disable Save RTP if RTP packets are not being captured if (!setting_enabled(SETTING_CAPTURE_RTP)) field_opts_off(info->fields[FLD_SAVE_PCAP_RTP], O_ACTIVE); // Create the form and post it info->form = new_form(info->fields); set_form_sub(info->form, ui->win); post_form(info->form); form_opts_off(info->form, O_BS_OVERLOAD); // Set Default field values sprintf(savepath, "%s", setting_get_value(SETTING_SAVEPATH)); set_field_buffer(info->fields[FLD_SAVE_PATH], 0, savepath); set_field_buffer(info->fields[FLD_SAVE_SAVE], 0, "[ Save ]"); set_field_buffer(info->fields[FLD_SAVE_CANCEL], 0, "[ Cancel ]"); // Set window boxes wattron(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); // Window border title_foot_box(ui->panel); // Header and footer lines mvwhline(ui->win, ui->height - 3, 1, ACS_HLINE, ui->width - 1); mvwaddch(ui->win, ui->height - 3, 0, ACS_LTEE); mvwaddch(ui->win, ui->height - 3, ui->width - 1, ACS_RTEE); // Save mode box mvwaddch(ui->win, 6, 2, ACS_ULCORNER); mvwhline(ui->win, 6, 3, ACS_HLINE, 30); mvwaddch(ui->win, 6, 32, ACS_URCORNER); mvwvline(ui->win, 7, 2, ACS_VLINE, 4); mvwvline(ui->win, 7, 32, ACS_VLINE, 4); mvwaddch(ui->win, 11, 2, ACS_LLCORNER); mvwhline(ui->win, 11, 3, ACS_HLINE, 30); mvwaddch(ui->win, 11, 32, ACS_LRCORNER); // Save mode box mvwaddch(ui->win, 6, 34, ACS_ULCORNER); mvwhline(ui->win, 6, 35, ACS_HLINE, 30); mvwaddch(ui->win, 6, 64, ACS_URCORNER); mvwvline(ui->win, 7, 34, ACS_VLINE, 3); mvwvline(ui->win, 7, 64, ACS_VLINE, 3); mvwaddch(ui->win, 10, 34, ACS_LLCORNER); mvwhline(ui->win, 10, 35, ACS_HLINE, 30); mvwaddch(ui->win, 10, 64, ACS_LRCORNER); wattroff(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); // Set screen labels mvwprintw(ui->win, 1, 27, "Save capture"); mvwprintw(ui->win, 3, 3, "Path:"); mvwprintw(ui->win, 4, 3, "Filename:"); wattron(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); mvwprintw(ui->win, 6, 4, " Dialogs "); mvwprintw(ui->win, 6, 36, " Format "); wattroff(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); // Set default cursor position set_current_field(info->form, info->fields[FLD_SAVE_FILE]); form_driver(info->form, REQ_END_LINE); curs_set(1); // Get filter stats sip_stats_t stats = sip_calls_stats(); // Set default save modes info->savemode = (stats.displayed == stats.total) ? SAVE_ALL : SAVE_DISPLAYED; info->saveformat = (setting_enabled(SETTING_CAPTURE_RTP))? SAVE_PCAP_RTP : SAVE_PCAP; } void save_destroy(ui_t *ui) { save_info_t *info; int i; // Get panel information if ((info = save_info(ui))) { // Remove panel form and fields unpost_form(info->form); free_form(info->form); for (i = 0; i < FLD_SAVE_COUNT; i++) free_field(info->fields[i]); // Remove panel window and custom info sng_free(info); } // Delete panel ui_panel_destroy(ui); // Resume capture capture_set_paused(0); // Disable cursor position curs_set(0); } save_info_t * save_info(ui_t *ui) { return (save_info_t*) panel_userptr(ui->panel); } int save_draw(ui_t *ui) { char field_value[MAX_SETTING_LEN]; // Get panel information save_info_t *info = save_info(ui); // Get filter stats sip_stats_t stats = sip_calls_stats(); mvwprintw(ui->win, 7, 3, "( ) all dialogs "); mvwprintw(ui->win, 8, 3, "( ) selected dialogs [%d]", call_group_count(info->group)); mvwprintw(ui->win, 9, 3, "( ) filtered dialogs [%d]", stats.displayed); // Print 'current SIP message' field label if required if (info->msg != NULL) mvwprintw(ui->win, 10, 3, "( ) current SIP message"); mvwprintw(ui->win, 7, 35, "( ) .pcap (SIP)"); mvwprintw(ui->win, 8, 35, "( ) .pcap (SIP + RTP)"); mvwprintw(ui->win, 9, 35, "( ) .txt"); // Get filename field value. memset(field_value, 0, sizeof(field_value)); strcpy(field_value, field_buffer(info->fields[FLD_SAVE_FILE], 0)); strtrim(field_value); mvwprintw(ui->win, 4, 60, " "); if (strstr(field_value, ".pcap")) { info->saveformat = (setting_enabled(SETTING_CAPTURE_RTP))? SAVE_PCAP_RTP : SAVE_PCAP; } else if (strstr(field_value, ".txt")) { info->saveformat = SAVE_TXT; } else { if (info->saveformat == SAVE_PCAP || info->saveformat == SAVE_PCAP_RTP) mvwprintw(ui->win, 4, 60, ".pcap"); else mvwprintw(ui->win, 4, 60, ".txt "); } set_field_buffer(info->fields[FLD_SAVE_ALL], 0, (info->savemode == SAVE_ALL) ? "*" : " "); set_field_buffer(info->fields[FLD_SAVE_SELECTED], 0, (info->savemode == SAVE_SELECTED) ? "*" : " "); set_field_buffer(info->fields[FLD_SAVE_DISPLAYED], 0, (info->savemode == SAVE_DISPLAYED) ? "*" : " "); set_field_buffer(info->fields[FLD_SAVE_MESSAGE], 0, (info->savemode == SAVE_MESSAGE) ? "*" : " "); set_field_buffer(info->fields[FLD_SAVE_PCAP], 0, (info->saveformat == SAVE_PCAP) ? "*" : " "); set_field_buffer(info->fields[FLD_SAVE_PCAP_RTP], 0, (info->saveformat == SAVE_PCAP_RTP) ? "*" : " "); set_field_buffer(info->fields[FLD_SAVE_TXT], 0, (info->saveformat == SAVE_TXT) ? "*" : " "); // Show disabled options with makers if (!setting_enabled(SETTING_CAPTURE_RTP)) set_field_buffer(info->fields[FLD_SAVE_PCAP_RTP], 0, "-"); set_current_field(info->form, current_field(info->form)); form_driver(info->form, REQ_VALIDATION); return 0; } int save_handle_key(ui_t *ui, int key) { int field_idx; int action = -1; // Get panel information save_info_t *info = save_info(ui); // Get current field id field_idx = field_index(current_field(info->form)); // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { // Check if we handle this action switch (action) { case ACTION_PRINTABLE: if (field_idx == FLD_SAVE_PATH || field_idx == FLD_SAVE_FILE) { form_driver(info->form, key); break; } continue; case ACTION_NEXT_FIELD: form_driver(info->form, REQ_NEXT_FIELD); form_driver(info->form, REQ_END_LINE); break; case ACTION_PREV_FIELD: form_driver(info->form, REQ_PREV_FIELD); form_driver(info->form, REQ_END_LINE); break; case ACTION_RIGHT: form_driver(info->form, REQ_RIGHT_CHAR); break; case ACTION_LEFT: form_driver(info->form, REQ_LEFT_CHAR); break; case ACTION_BEGIN: form_driver(info->form, REQ_BEG_LINE); break; case ACTION_END: form_driver(info->form, REQ_END_LINE); break; case ACTION_DELETE: form_driver(info->form, REQ_DEL_CHAR); break; case ACTION_BACKSPACE: form_driver(info->form, REQ_DEL_PREV); break; case ACTION_CLEAR: form_driver(info->form, REQ_CLR_FIELD); break; case ACTION_SELECT: switch (field_idx) { case FLD_SAVE_ALL: info->savemode = SAVE_ALL; break; case FLD_SAVE_SELECTED: info->savemode = SAVE_SELECTED; break; case FLD_SAVE_DISPLAYED: info->savemode = SAVE_DISPLAYED; break; case FLD_SAVE_MESSAGE: info->savemode = SAVE_MESSAGE; break; case FLD_SAVE_PCAP: info->saveformat = SAVE_PCAP; break; case FLD_SAVE_PCAP_RTP: info->saveformat = SAVE_PCAP_RTP; break; case FLD_SAVE_TXT: info->saveformat = SAVE_TXT; break; case FLD_SAVE_FILE: form_driver(info->form, key); break; default: break; } break; case ACTION_CONFIRM: if (field_idx != FLD_SAVE_CANCEL) { save_to_file(ui); } ui_destroy(ui); return KEY_HANDLED; default: // Parse next action continue; } // This panel has handled the key successfully break; } // Validate all input data form_driver(info->form, REQ_VALIDATION); // Change background and cursor of "button fields" set_field_back(info->fields[FLD_SAVE_SAVE], A_NORMAL); set_field_back(info->fields[FLD_SAVE_CANCEL], A_NORMAL); curs_set(1); // Change current field background field_idx = field_index(current_field(info->form)); if (field_idx == FLD_SAVE_SAVE || field_idx == FLD_SAVE_CANCEL) { set_field_back(info->fields[field_idx], A_REVERSE); curs_set(0); } // Return if this panel has handled or not the key return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED; } void save_set_group(ui_t *ui, sip_call_group_t *group) { // Get panel information save_info_t *info = save_info(ui); info->group = group; if (call_group_count(group)) { info->savemode = SAVE_SELECTED; } } void save_set_msg(ui_t *ui, sip_msg_t *msg) { // Get panel information save_info_t *info = save_info(ui); info->msg = msg; // make 'current SIP message' field visible field_opts_on(info->fields[FLD_SAVE_MESSAGE], O_VISIBLE); } int save_to_file(ui_t *ui) { char savepath[MAX_SETTING_LEN]; char savefile[MAX_SETTING_LEN]; char fullfile[MAX_SETTING_LEN*2]; sip_call_t *call = NULL; sip_msg_t *msg = NULL; pcap_dumper_t *pd = NULL; FILE *f = NULL; int cur = 0, total = 0; WINDOW *progress; vector_iter_t calls, msgs, rtps, packets; packet_t *packet; vector_t *sorted; // Get panel information save_info_t *info = save_info(ui); // Get current path field value. memset(savepath, 0, sizeof(savepath)); strcpy(savepath, field_buffer(info->fields[FLD_SAVE_PATH], 0)); strtrim(savepath); if (strlen(savepath)) strcat(savepath, "/"); // Get current file field value. memset(savefile, 0, sizeof(savefile)); strcpy(savefile, field_buffer(info->fields[FLD_SAVE_FILE], 0)); strtrim(savefile); if (!strlen(savefile)) { dialog_run("Please enter a valid filename"); return 1; } if (info->saveformat == SAVE_PCAP || info->saveformat == SAVE_PCAP_RTP) { if (!strstr(savefile, ".pcap")) strcat(savefile, ".pcap"); } else { if (!strstr(savefile, ".txt")) strcat(savefile, ".txt"); } // Absolute filename sprintf(fullfile, "%s%s", savepath, savefile); if (access(fullfile, R_OK) == 0) { if (dialog_confirm("Overwrite confirmation", "Selected file already exists.\n Do you want to overwrite it?", "Yes,No") != 0) return 1; } // Don't allow to save no packets! if (info->savemode == SAVE_SELECTED && call_group_msg_count(info->group) == 0) { dialog_run("Unable to save: No selected dialogs."); return 1; } if (info->saveformat == SAVE_PCAP || info->saveformat == SAVE_PCAP_RTP) { // Open dump file pd = dump_open(fullfile, NULL); if (access(fullfile, W_OK) != 0) { dialog_run("%s", capture_last_error()); return 1; } } else { // Open a text file if (!(f = fopen(fullfile, "w"))) { dialog_run("Error: %s", strerror(errno)); return 0; } } // Get calls iterator switch (info->savemode) { case SAVE_ALL: // Get calls iterator calls = sip_calls_iterator(); break; case SAVE_SELECTED: // Save selected packets to file calls = vector_iterator(info->group->calls); break; case SAVE_DISPLAYED: // Set filtering for this iterator calls = sip_calls_iterator(); vector_iterator_set_filter(&calls, filter_check_call); break; default: break; } if (info->savemode == SAVE_MESSAGE) { if (info->saveformat == SAVE_TXT) { // Save selected message to file save_msg_txt(f, info->msg); } else { // Save selected message packet to pcap dump_packet(pd, info->msg->packet); } } else if (info->saveformat == SAVE_TXT) { // Save selected packets to file while ((call = vector_iterator_next(&calls))) { msgs = vector_iterator(call->msgs); // Save SIP message content while ((msg = vector_iterator_next(&msgs))) { save_msg_txt(f, msg); } } } else { // Store all messages in a time sorted vector sorted = vector_create(100, 50); vector_set_sorter(sorted, capture_packet_time_sorter); // Count packages for progress bar while ((call = vector_iterator_next(&calls))) { total += vector_count(call->msgs); if (info->saveformat == SAVE_PCAP_RTP) total += vector_count(call->rtp_packets); } vector_iterator_reset(&calls); progress = dialog_progress_run("Saving packets..."); dialog_progress_set_value(progress, 0); // Save selected packets to file while ((call = vector_iterator_next(&calls))) { msgs = vector_iterator(call->msgs); // Save SIP message content while ((msg = vector_iterator_next(&msgs))) { // Update progress bar dialog dialog_progress_set_value(progress, (++cur * 100) / total); vector_append(sorted, msg->packet); } // Save RTP packets if (info->saveformat == SAVE_PCAP_RTP) { rtps = vector_iterator(call->rtp_packets); while ((packet = vector_iterator_next(&rtps))) { // Update progress bar dialog dialog_progress_set_value(progress, (++cur * 100) / total); vector_append(sorted, packet); } } } // Save sorted packets packets = vector_iterator(sorted); while ((packet = vector_iterator_next(&packets))) { dump_packet(pd, packet); } dialog_progress_destroy(progress); } // Close saved file if (info->saveformat == SAVE_PCAP || info->saveformat == SAVE_PCAP_RTP) { dump_close(pd); } else { fclose(f); } // Show success popup if (info->savemode == SAVE_MESSAGE) { dialog_run("Successfully saved selected SIP message to %s", savefile); } else { dialog_run("Successfully saved %d dialogs to %s", vector_iterator_count(&calls), savefile); } return 0; } void save_msg_txt(FILE *f, sip_msg_t *msg) { char date[20], time[20], src[80], dst[80]; fprintf(f, "%s %s %s -> %s\n%s\n\n", msg_get_attribute(msg, SIP_ATTR_DATE, date), msg_get_attribute(msg, SIP_ATTR_TIME, time), msg_get_attribute(msg, SIP_ATTR_SRC, src), msg_get_attribute(msg, SIP_ATTR_DST, dst), msg_get_payload(msg)); } sngrep-1.8.2/src/curses/ui_save.h000066400000000000000000000123431464272443000167300ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_save_pcap.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage ui window for saving captured packages * * This file contains the functions and structures to manage the save * dialog, that can be used to copy the temporal sngrep file to another location * */ #ifndef __UI_SAVE_PCAP_H #define __UI_SAVE_PCAP_H #include "config.h" #include #include "group.h" #include "ui_manager.h" /** * @brief Enum of available dialog fields * * Dialog form has a field array. Following enum represents the * order this fields are stored in panel info structure. */ enum save_field_list { FLD_SAVE_PATH = 0, FLD_SAVE_FILE, FLD_SAVE_ALL, FLD_SAVE_SELECTED, FLD_SAVE_DISPLAYED, FLD_SAVE_MESSAGE, FLD_SAVE_PCAP, FLD_SAVE_PCAP_RTP, FLD_SAVE_TXT, FLD_SAVE_SAVE, FLD_SAVE_CANCEL, FLD_SAVE_COUNT }; /** * @brief Dialogs to be saved */ enum save_mode { SAVE_ALL = 0, SAVE_SELECTED, SAVE_DISPLAYED, SAVE_MESSAGE }; /** * @brief Save file formats */ enum save_format { SAVE_PCAP = 0, SAVE_PCAP_RTP, SAVE_TXT }; //! Sorter declaration of struct save_info typedef struct save_info save_info_t; /** * @brief Save panel private information * * This structure contains the durable data of save panel. */ struct save_info { //! Form that contains the save fields FORM *form; //! An array of fields FIELD *fields[FLD_SAVE_COUNT + 1]; //! Save mode @see save_modes enum save_mode savemode; //! Save format @see save_formats enum save_format saveformat; //! Call group to be saved sip_call_group_t *group; //! Message to be saved sip_msg_t *msg; }; /** * @brief Creates a new save panel * * This function allocates all required memory for * displaying the save panel. It also draws all the * static information of the panel that will never be * redrawn. * * @param ui UI structure pointer */ void save_create(ui_t *ui); /** * @brief Destroy save panel * * This function do the final cleanups for this panel * * @param ui UI structure pointer */ void save_destroy(ui_t *ui); /** * @brief Get custom information of given panel * * Return ncurses users pointer of the given panel into panel's * information structure pointer. * * @param ui UI structure pointer * @return a pointer to info structure of given panel */ save_info_t * save_info(ui_t *ui); /** * @brief Draw the Save panel * * This function will drawn the panel into the screen based on its stored * status * * @param ui UI structure pointer * @return 0 if the panel has been drawn, -1 otherwise */ int save_draw(ui_t *ui); /** * @brief Manage pressed keys for save panel * * This function is called by UI manager every time a * key is pressed. This allow the save panel to manage * its own keys. * * @param ui UI structure pointer * @param key key code * @return enum @key_handler_ret */ int save_handle_key(ui_t *ui, int key); /** * @brief Set the group call of the panel * * This function will access the panel information and will set the * group call pointer to the selected calls * * @param ui UI structure pointer * @param group Call group pointer to be set in the internal info struct */ void save_set_group(ui_t *ui, sip_call_group_t *group); /** * @brief Set the SIP message to be saved * * This function will access the panel information and will set the * pointer to the selected SIP message * * @param ui UI structure pointer * @param msg SIP message pointer to be set in the internal info struct */ void save_set_msg(ui_t *ui, sip_msg_t *msg); /** * @brief Print an error message in Save panel * * General function to print any save error message * * @param ui UI structure pointer * @param message Message to be printed in the panel */ void save_error_message(ui_t *ui, const char *message); /** * @brief Save form data to options * * Save capture packets to a file based on selected modes on screen * It will display an error or success dialog before exit * * @param ui UI structure pointer * @returns 1 in case of error, 0 otherwise. */ int save_to_file(ui_t *ui); /** * @brief Save one SIP message into open file * * @param f File opened with fopen * @param msg a SIP Message */ void save_msg_txt(FILE *f, sip_msg_t *msg); #endif sngrep-1.8.2/src/curses/ui_settings.c000066400000000000000000000517631464272443000176360ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_settings.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in ui_settings.h */ #include #include #include #include "ui_manager.h" #include "ui_settings.h" #include "setting.h" /** * Ui Structure definition for Settings panel */ ui_t ui_settings = { .type = PANEL_SETTINGS, .panel = NULL, .create = settings_create, .draw = settings_draw, .handle_key = settings_handle_key, .destroy = settings_destroy }; settings_category_t categories[] = { { CAT_SETTINGS_INTERFACE, "Interface" }, { CAT_SETTINGS_CAPTURE, "Capture" }, { CAT_SETTINGS_CALL_FLOW, "Call Flow" }, #ifdef USE_EEP { CAT_SETTINGS_EEP_HOMER, "EEP/HEP Homer" }, #endif { 0 , NULL }, }; settings_entry_t entries[] = { { CAT_SETTINGS_INTERFACE, FLD_SETTINGS_BACKGROUND, SETTING_BACKGROUND, "Background * .............................." }, { CAT_SETTINGS_INTERFACE, FLD_SETTINGS_SYNTAX, SETTING_SYNTAX, "SIP message syntax ........................" }, { CAT_SETTINGS_INTERFACE, FLD_SETTINGS_SYNTAX_TAG, SETTING_SYNTAX_TAG, "SIP tag syntax ............................" }, { CAT_SETTINGS_INTERFACE, FLD_SETTINGS_SYNTAX_BRANCH, SETTING_SYNTAX_BRANCH, "SIP branch syntax ........................." }, { CAT_SETTINGS_INTERFACE, FLD_SETTINGS_ALTKEY_HINT, SETTING_ALTKEY_HINT, "Alternative keybinding hints .............." }, { CAT_SETTINGS_INTERFACE, FLD_SETTINGS_COLORMODE, SETTING_COLORMODE, "Default message color mode ................" }, { CAT_SETTINGS_INTERFACE, FLD_SETTINGS_EXITPROMPT, SETTING_EXITPROMPT, "Always prompt on quit ....................." }, { CAT_SETTINGS_INTERFACE, FLD_SETTINGS_DISPLAY_ALIAS, SETTING_DISPLAY_ALIAS, "Replace addresses with alias .............." }, { CAT_SETTINGS_CAPTURE, FLD_SETTINGS_CAPTURE_LIMIT, SETTING_CAPTURE_LIMIT, "Max dialogs * ............................." }, { CAT_SETTINGS_CAPTURE, FLD_SETTINGS_CAPTURE_DEVICE, SETTING_CAPTURE_DEVICE, "Capture device * .........................." }, { CAT_SETTINGS_CAPTURE, FLD_SETTINGS_SIP_NOINCOMPLETE, SETTING_SIP_NOINCOMPLETE, "Capture full transactions ................." }, { CAT_SETTINGS_CAPTURE, FLD_SETTINGS_SAVEPATH, SETTING_SAVEPATH, "Default Save path ........................." }, { CAT_SETTINGS_CALL_FLOW, FLD_SETTINGS_CF_FORCERAW, SETTING_CF_FORCERAW, "Show message preview panel ................" }, { CAT_SETTINGS_CALL_FLOW, FLD_SETTINGS_CF_HIGHTLIGHT, SETTING_CF_HIGHTLIGHT, "Selected message highlight ................" }, { CAT_SETTINGS_CALL_FLOW, FLD_SETTINGS_CF_LOCALHIGHLIGHT, SETTING_CF_LOCALHIGHLIGHT, "Highlight local addresses ................." }, { CAT_SETTINGS_CALL_FLOW, FLD_SETTINGS_CF_SPLITCACALLID, SETTING_CF_SPLITCALLID, "Merge columns with same address ..........." }, { CAT_SETTINGS_CALL_FLOW, FLD_SETTINGS_CF_SDPONLY, SETTING_CF_SDP_INFO, "Show SDP information in messages .........." }, { CAT_SETTINGS_CALL_FLOW, FLD_SETTINGS_CF_DELTA, SETTING_CF_DELTA, "Show delta time between messages .........." }, { CAT_SETTINGS_CALL_FLOW, FLD_SETTINGS_CF_MEDIA, SETTING_CF_MEDIA, "Show RTP media streams ...................." }, { CAT_SETTINGS_CALL_FLOW, FLD_SETTINGS_CF_SCROLLSTEP, SETTING_CF_SCROLLSTEP, "Steps for PgUp/PgDown ....................." }, #ifdef USE_EEP { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_SEND, SETTING_EEP_SEND, "Send all captured SIP packets ............." }, { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_SEND_VER, SETTING_EEP_SEND_VER, "Send EEP version .........................." }, { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_SEND_ADDR, SETTING_EEP_SEND_ADDR, "Send EEP packet address ..................." }, { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_SEND_PORT, SETTING_EEP_SEND_PORT, "Send EEP packet port ......................" }, { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_SEND_PASS, SETTING_EEP_SEND_PASS, "EEP send password ........................." }, { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_SEND_ID, SETTING_EEP_SEND_ID, "EEP send capture id ......................." }, { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_LISTEN, SETTING_EEP_LISTEN, "Listen for eep packets ...................." }, { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_LISTEN_VER, SETTING_EEP_LISTEN_VER, "Listen EEP version ......................." }, { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_LISTEN_ADDR, SETTING_EEP_LISTEN_ADDR, "Listen EEP packet address ................." }, { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_LISTEN_PORT, SETTING_EEP_LISTEN_PORT, "Listen EEP packet port ...................." }, { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_LISTEN_PASS, SETTING_EEP_LISTEN_PASS, "EEP server password ......................." }, { CAT_SETTINGS_EEP_HOMER, FLD_SETTINGS_EEP_LISTEN_UUID, SETTING_EEP_LISTEN_UUID, "EEP server expects UUID (Asterisk) ........" }, #endif { 0 , 0, 0, NULL }, }; void settings_create(ui_t *ui) { int i, j, line; settings_info_t *info; FIELD *entry = NULL, *label; int field = 0; // Cerate a new window for the panel and form ui_panel_create(ui, 24, 70); // Initialize Filter panel specific data info = sng_malloc(sizeof(settings_info_t)); // Store it into panel userptr set_panel_userptr(ui->panel, (void*) info); // Create a scrollable subwindow for settings info->form_win = derwin(ui->win, ui->height - 11, ui->width - 2, 8, 1); // Configure panel buttons info->buttons[BTN_SETTINGS_ACCEPT] = new_field(1, 10, ui->height - 2, 12, 0, 0); info->buttons[BTN_SETTINGS_SAVE] = new_field(1, 10, ui->height - 2, 29, 0, 0); info->buttons[BTN_SETTINGS_CANCEL] = new_field(1, 10, ui->height - 2, 46, 0, 0); info->buttons[BTN_SETTINGS_COUNT] = NULL; field_opts_off(info->buttons[BTN_SETTINGS_ACCEPT], O_EDIT); field_opts_off(info->buttons[BTN_SETTINGS_SAVE], O_EDIT); field_opts_off(info->buttons[BTN_SETTINGS_CANCEL], O_EDIT); set_field_buffer(info->buttons[BTN_SETTINGS_ACCEPT], 0, "[ Accept ]"); set_field_buffer(info->buttons[BTN_SETTINGS_SAVE], 0, "[ Save ]"); set_field_buffer(info->buttons[BTN_SETTINGS_CANCEL], 0, "[ Cancel ]"); info->buttons_form = new_form(info->buttons); set_form_sub(info->buttons_form, ui->win); post_form(info->buttons_form); // Initialize rest of settings fields for (i = 0; categories[i].cat_id; i++) { // Each category section begins with fields in the first line line = 0; for (j = 0; entries[j].cat_id; j++) { // Ignore entries of other categories if (entries[j].cat_id != categories[i].cat_id) continue; // Create the label label = new_field(1, 45, line, 3, 0, 0); set_field_buffer(label, 0, entries[j].label); field_opts_off(label, O_ACTIVE); // Change field properties according to field type switch(setting_format(entries[j].setting_id)) { case SETTING_FMT_NUMBER: entry = new_field(1, 18, line, 48, 0, 0); set_field_back(entry, A_UNDERLINE); set_field_type(entry, TYPE_REGEXP, "[0-9]+"); break; case SETTING_FMT_STRING: entry = new_field(1, 18, line, 48, 0, 0); field_opts_off(entry, O_STATIC); set_field_back(entry, A_UNDERLINE); break; case SETTING_FMT_ENUM: entry = new_field(1, 12, line, 48, 0, 0); field_opts_off(entry, O_EDIT); set_field_type(entry, TYPE_ENUM, setting_valid_values(entries[j].setting_id), 0, 0); break; } field_opts_off(entry, O_AUTOSKIP); set_field_buffer(entry, 0, setting_get_value(entries[j].setting_id)); set_field_userptr(entry, (void *) &entries[j]); if (line == 0) { // Set last field as page breaker set_new_page(entry, TRUE); } // Store field info->fields[field++] = entry; info->fields[field++] = label; line++; } } // Create the form and post it info->form = new_form(info->fields); set_form_sub(info->form, info->form_win); post_form(info->form); // Set the window title and boxes mvwprintw(ui->win, 1, ui->width / 2 - 5, "Settings"); wattron(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); title_foot_box(ui->panel); mvwhline(ui->win, 6, 1, ACS_HLINE, ui->width - 1); mvwaddch(ui->win, 6, 0, ACS_LTEE); mvwaddch(ui->win, 6, ui->width - 1, ACS_RTEE); wattroff(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); wattron(ui->win, COLOR_PAIR(CP_CYAN_ON_DEF)); mvwprintw(ui->win, 3, 1, " Use arrow keys, PgUp, PgDown and Tab to move around settings."); mvwprintw(ui->win, 4, 1, " Settings with (*) requires restart."); wattroff(ui->win, COLOR_PAIR(CP_CYAN_ON_DEF)); // Set default field info->active_form = info->form; set_current_field(info->form, *info->fields); info->active_category = form_page(info->form) + 1; } void settings_destroy(ui_t *ui) { curs_set(0); ui_panel_destroy(ui); } settings_info_t * settings_info(ui_t *ui) { return (settings_info_t*) panel_userptr(ui->panel); } int settings_draw(ui_t *ui) { int i; int cury, curx; // Get panel information settings_info_t *info = settings_info(ui); // Store cursor position getyx(ui->win, cury, curx); // Print category headers int colpos = 2; for (i = 0; categories[i].cat_id; i++) { if (categories[i].cat_id == info->active_category) { mvwprintw(ui->win, 6, colpos, "%c %s %c", '[', categories[i].title, ']'); } else { wattron(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); mvwprintw(ui->win, 6, colpos, "%c %s %c", '[', categories[i].title, ']'); wattroff(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); } colpos += strlen(categories[i].title) + 5; } // Reset all field background for (i = 0; i < FLD_SETTINGS_COUNT; i++) { set_field_fore(info->fields[i + 1], A_NORMAL); if (!strncmp(field_buffer(info->fields[i], 0), "on", 2)) set_field_fore(info->fields[i], COLOR_PAIR(CP_GREEN_ON_DEF)); if (!strncmp(field_buffer(info->fields[i], 0), "off", 3)) set_field_fore(info->fields[i], COLOR_PAIR(CP_RED_ON_DEF)); } for (i=0; i < BTN_SETTINGS_COUNT; i++) { set_field_back(info->buttons[i], A_NORMAL); } // Highlight current field if (info->active_form == info->buttons_form) { set_field_back(current_field(info->buttons_form), A_REVERSE); } else { set_field_fore(info->fields[field_index(current_field(info->form)) + 1], A_BOLD); } touchwin(ui->win); // Restore cursor position wmove(ui->win, cury, curx); return 0; } int settings_handle_key(ui_t *ui, int key) { int action = -1; int field_idx; settings_entry_t *entry; enum setting_fmt sett_fmt = -1; // Get panel information settings_info_t *info = settings_info(ui); // Get current field id field_idx = field_index(current_field(info->active_form)); // Get current setting id; if ((entry = ui_settings_is_entry(current_field(info->active_form)))) { sett_fmt = setting_format(entry->setting_id); } // Check actions for this key while ((action = key_find_action(key, action)) != ERR) { if (info->active_form == info->form) { // Check if we handle this action switch (action) { case ACTION_PRINTABLE: if (sett_fmt == SETTING_FMT_NUMBER || sett_fmt == SETTING_FMT_STRING) { form_driver(info->form, key); break; } continue; case ACTION_UP: case ACTION_HPPAGE: form_driver(info->form, REQ_PREV_FIELD); form_driver(info->form, REQ_END_LINE); break; case ACTION_DOWN: case ACTION_HNPAGE: form_driver(info->form, REQ_NEXT_FIELD); form_driver(info->form, REQ_END_LINE); break; case ACTION_SELECT: case ACTION_RIGHT: form_driver(info->form, REQ_NEXT_CHOICE); form_driver(info->form, REQ_RIGHT_CHAR); break; case ACTION_LEFT: form_driver(info->form, REQ_PREV_CHOICE); form_driver(info->form, REQ_LEFT_CHAR); break; case ACTION_NPAGE: form_driver(info->form, REQ_NEXT_PAGE); form_driver(info->form, REQ_END_LINE); info->active_category = form_page(info->form) + 1; break; case ACTION_PPAGE: form_driver(info->form, REQ_PREV_PAGE); form_driver(info->form, REQ_END_LINE); info->active_category = form_page(info->form) + 1; break; case ACTION_BEGIN: form_driver(info->form, REQ_BEG_LINE); break; case ACTION_END: form_driver(info->form, REQ_END_LINE); break; case ACTION_NEXT_FIELD: info->active_form = info->buttons_form; set_current_field(info->active_form, info->buttons[BTN_SETTINGS_ACCEPT]); break; case ACTION_CLEAR: if (sett_fmt == SETTING_FMT_NUMBER || sett_fmt == SETTING_FMT_STRING) { form_driver(info->form, REQ_BEG_LINE); form_driver(info->form, REQ_CLR_EOL); } break; case ACTION_DELETE: if (sett_fmt == SETTING_FMT_NUMBER || sett_fmt == SETTING_FMT_STRING) { form_driver(info->form, REQ_DEL_CHAR); } break; case ACTION_BACKSPACE: if (sett_fmt == SETTING_FMT_NUMBER || sett_fmt == SETTING_FMT_STRING) { form_driver(info->form, REQ_DEL_PREV); } break; case ACTION_CONFIRM: ui_settings_update_settings(ui); ui_destroy(ui); return KEY_HANDLED; default: // Parse next action continue; } } else { // Check if we handle this action switch (action) { case ACTION_RIGHT: case ACTION_DOWN: case ACTION_NEXT_FIELD: if (field_idx == BTN_SETTINGS_CANCEL) { info->active_form = info->form; } else { form_driver(info->buttons_form, REQ_NEXT_FIELD); } break; case ACTION_LEFT: case ACTION_UP: case ACTION_PREV_FIELD: if (field_idx == BTN_SETTINGS_ACCEPT) { info->active_form = info->form; } else { form_driver(info->buttons_form, REQ_PREV_FIELD); } break; case ACTION_SELECT: case ACTION_CONFIRM: if (field_idx == BTN_SETTINGS_SAVE) ui_settings_save(ui); ui_settings_update_settings(ui); ui_destroy(ui); return KEY_HANDLED; default: continue; } } // This panel has handled the key successfully break; } // Validate all input data form_driver(info->active_form, REQ_VALIDATION); // Get current setting id if ((entry = ui_settings_is_entry(current_field(info->active_form)))) { // Enable cursor on string and number fields curs_set(setting_format(entry->setting_id) != SETTING_FMT_ENUM); } else { curs_set(0); } // Return if this panel has handled or not the key return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED; } settings_entry_t * ui_settings_is_entry(FIELD *field) { return (settings_entry_t *) field_userptr(field); } int ui_settings_update_settings(ui_t *ui) { int i; char field_value[180]; settings_entry_t *entry; // Get panel information settings_info_t *info = settings_info(ui); for (i=0; i < FLD_SETTINGS_COUNT; i++) { if ((entry = ui_settings_is_entry(info->fields[i]))) { // Get field value. memset(field_value, 0, sizeof(field_value)); strcpy(field_value, field_buffer(info->fields[i], 0)); strtrim(field_value); // Change setting value setting_set_value(entry->setting_id, field_value); } } return 0; } void ui_settings_save(ui_t *ui) { int i; FILE *fi, *fo; char line[1024]; char *rcfile; char *userconf = NULL; char *tmpfile = NULL; char field_value[180]; settings_entry_t *entry; // Get panel information settings_info_t *info = settings_info(ui); // Use current $SNGREPRC or $HOME/.sngreprc file if ((rcfile = getenv("SNGREPRC"))) { if ((userconf = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) { if ((tmpfile = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) { sprintf(userconf, "%s", rcfile); sprintf(tmpfile, "%s.old", rcfile); } else { sng_free(userconf); return; } } else { return; } } else if ((rcfile = getenv("HOME"))) { if ((userconf = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) { if ((tmpfile = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) { sprintf(userconf, "%s/.sngreprc", rcfile); sprintf(tmpfile, "%s/.sngreprc.old", rcfile); } else { sng_free(userconf); return; } } else { return; } } else { dialog_run("Unable to save configuration. User has no $SNGREPRC or $HOME dir."); return; } // Remove old config file unlink(tmpfile); // Move user conf file to temporal file rename(userconf, tmpfile); // Create a new user conf file if (!(fo = fopen(userconf, "w"))) { sng_free(userconf); sng_free(tmpfile); return; } // Read all lines of old sngreprc file if ((fi = fopen(tmpfile, "r"))) { // Read all configuration file while (fgets(line, 1024, fi) != NULL) { // Ignore lines starting with set (but keep set column ones) if (strncmp(line, "set ", 4) || !strncmp(line, "set cl.column", 13)) { // Put everyting in new user conf file fputs(line, fo); } } fclose(fi); } for (i=0; i < FLD_SETTINGS_COUNT; i++) { if ((entry = ui_settings_is_entry(info->fields[i]))) { // Get field value. memset(field_value, 0, sizeof(field_value)); strcpy(field_value, field_buffer(info->fields[i], 0)); strtrim(field_value); // Change setting value fprintf(fo, "set %s %s\n", setting_name(entry->setting_id), field_value); } } fclose(fo); dialog_run("Settings successfully saved to %s", userconf); sng_free(userconf); sng_free(tmpfile); } sngrep-1.8.2/src/curses/ui_settings.h000066400000000000000000000165121464272443000176340ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_settings.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to change sngrep configurable settings * * This file contains the functions to display the interface panel that handles * the changes of settings in realtime, also allowing to save them. * */ #ifndef __SNGREP_UI_SETTINGS_H #define __SNGREP_UI_SETTINGS_H //! Sorter declaration of struct option_info typedef struct settings_info settings_info_t; //! Sorter declaration of struct settings_category typedef struct settings_category settings_category_t; //! Sorter declaration of struct settings_entry typedef struct settings_entry settings_entry_t; enum settings_category_list { CAT_SETTINGS_INTERFACE = 1, CAT_SETTINGS_CAPTURE, CAT_SETTINGS_CALL_FLOW, CAT_SETTINGS_EEP_HOMER, CAT_SETTINGS_COUNT, }; /** * @brief Enum of available dialog fields * * Dialog form has a field array. Following enum represents the * order this fields are stored in panel info structure. * */ enum settings_field_list { FLD_SETTINGS_BACKGROUND = 0, FLD_SETTINGS_BACKGROUND_LB, FLD_SETTINGS_SYNTAX, FLD_SETTINGS_SYNTAX_LB, FLD_SETTINGS_SYNTAX_TAG, FLD_SETTINGS_SYNTAX_TAG_LB, FLD_SETTINGS_SYNTAX_BRANCH, FLD_SETTINGS_SYNTAX_BRANCH_LB, FLD_SETTINGS_ALTKEY_HINT, FLD_SETTINGS_ALTKEY_HINT_LB, FLD_SETTINGS_COLORMODE, FLD_SETTINGS_COLORMODE_LB, FLD_SETTINGS_EXITPROMPT, FLD_SETTINGS_EXITPROMPT_LB, FLD_SETTINGS_DISPLAY_ALIAS, FLD_SETTINGS_DISPLAY_ALIAS_LB, FLD_SETTINGS_CAPTURE_LIMIT, FLD_SETTINGS_CAPTURE_LIMIT_LB, FLD_SETTINGS_CAPTURE_DEVICE, FLD_SETTINGS_CAPTURE_DEVICE_LB, FLD_SETTINGS_SIP_NOINCOMPLETE, FLD_SETTINGS_SIP_NOINCOMPLETE_LB, FLD_SETTINGS_SAVEPATH, FLD_SETTINGS_SAVEPATH_LB, FLD_SETTINGS_CF_FORCERAW, FLD_SETTINGS_CF_FORCERAW_LB, FLD_SETTINGS_CF_SPLITCACALLID, FLD_SETTINGS_CF_SPLITCACALLID_LB, FLD_SETTINGS_CF_SDPONLY, FLD_SETTINGS_CF_SDPONLY_LB, FLD_SETTINGS_CF_SCROLLSTEP, FLD_SETTINGS_CF_SCROLLSTEP_LB, FLD_SETTINGS_CF_HIGHTLIGHT, FLD_SETTINGS_CF_HIGHTLIGHT_LB, FLD_SETTINGS_CF_LOCALHIGHLIGHT, FLD_SETTINGS_CF_LOCALHIGHLIGHT_LB, FLD_SETTINGS_CF_DELTA, FLD_SETTINGS_CF_DELTA_LB, FLD_SETTINGS_CF_MEDIA, FLD_SETTINGS_CF_MEDIA_LB, #ifdef USE_EEP FLD_SETTINGS_EEP_SEND, FLD_SETTINGS_EEP_SEND_LB, FLD_SETTINGS_EEP_SEND_VER, FLD_SETTINGS_EEP_SEND_VER_LB, FLD_SETTINGS_EEP_SEND_ADDR, FLD_SETTINGS_EEP_SEND_ADDR_LB, FLD_SETTINGS_EEP_SEND_PORT, FLD_SETTINGS_EEP_SEND_PORT_LB, FLD_SETTINGS_EEP_SEND_PASS, FLD_SETTINGS_EEP_SEND_PASS_LB, FLD_SETTINGS_EEP_SEND_ID, FLD_SETTINGS_EEP_SEND_ID_LB, FLD_SETTINGS_EEP_LISTEN, FLD_SETTINGS_EEP_LISTEN_LB, FLD_SETTINGS_EEP_LISTEN_VER, FLD_SETTINGS_EEP_LISTEN_VER_LB, FLD_SETTINGS_EEP_LISTEN_ADDR, FLD_SETTINGS_EEP_LISTEN_ADDR_LB, FLD_SETTINGS_EEP_LISTEN_PORT, FLD_SETTINGS_EEP_LISTEN_PORT_LB, FLD_SETTINGS_EEP_LISTEN_PASS, FLD_SETTINGS_EEP_LISTEN_PASS_LB, FLD_SETTINGS_EEP_LISTEN_UUID, FLD_SETTINGS_EEP_LISTEN_UUID_LB, #endif FLD_SETTINGS_COUNT, }; enum settings_button_list { BTN_SETTINGS_ACCEPT = 0, BTN_SETTINGS_SAVE, BTN_SETTINGS_CANCEL, BTN_SETTINGS_COUNT, }; #define SETTINGS_ENTRY_COUNT (FLD_SETTINGS_COUNT - 3) struct settings_category { // Category id enum settings_category_list cat_id; // Category label const char *title; }; struct settings_entry { enum settings_category_list cat_id; //! Field id in settings_info array enum settings_field_list field_id; //! Setting id of current entry enum setting_id setting_id; //! Entry text const char *label; }; /** * @brief settings panel private information * * This structure contains the durable data of settings panel. */ struct settings_info { // Window containing form data (and buttons) WINDOW *form_win; //! Form that contains the filter fields FORM *form; //! An array of fields FIELD *fields[FLD_SETTINGS_COUNT + 1]; //! Form that contains the buttons FORM *buttons_form; //! Array of panel buttons FIELD *buttons[BTN_SETTINGS_COUNT + 1]; //! Active form FORM *active_form; //! Active category enum settings_category_list active_category; }; /** * @brief Creates a new settings panel * * This function allocates all required memory for * displaying the save panel. It also draws all the * static information of the panel that will never be * redrawn. * * @param ui UI structure pointer */ void settings_create(ui_t *ui); /** * @brief Destroy settings panel * * This function do the final cleanups for this panel * * @param ui UI structure pointer */ void settings_destroy(ui_t *ui); /** * @brief Get custom information of given panel * * Return ncurses users pointer of the given panel into panel's * information structure pointer. * * @param ui UI structure pointer * @return a pointer to info structure of given panel */ settings_info_t * settings_info(ui_t *ui); /** * @brief Draw the settings panel * * This function will drawn the panel into the screen with * current status settings * * @param ui UI structure pointer * @return 0 if the panel has been drawn, -1 otherwise */ int settings_draw(ui_t *ui); /** * @brief Manage pressed keys for settings panel * * This function is called by UI manager every time a * key is pressed. This allow the filter panel to manage * its own keys. * * @param ui UI structure pointer * @param key key code * @return enum @key_handler_ret */ int settings_handle_key(ui_t *ui, int key); /** * @brief Return entry information of the field * * If field is storing a setting value, return the entry * structure associated to the setting * * @param field Ncurses field pointer of screen * @return Setting information structure */ settings_entry_t * ui_settings_is_entry(FIELD *field); /** * @brief Update settings with panel values * * Update all settings with the selected on screen. * Note that some settings require application restart to * take effect. * * @param ui UI structure pointer * @return 0 in all cases */ int ui_settings_update_settings(ui_t *ui); /** * @brief Update user resource file with panel values * * Save all settings into user configuration file located * in it's home directory. * * @param ui UI structure pointer */ void ui_settings_save(ui_t *ui); #endif /* __SNGREP_UI_SETTINGS_H */ sngrep-1.8.2/src/curses/ui_stats.c000066400000000000000000000235021464272443000171220ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_stats.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in ui_stats.h */ /* * +---------------------------------------------------------+ * | Stats Information | * +---------------------------------------------------------+ * | Dialogs: 725 COMPLETED: 7 (22.1%) | * | Calls: 10 CANCELLED: 2 (12.2%) | * | Messages: 200 IN CALL: 10 (60.5%) | * | REJECTED: 0 (0.0%) | * | BUSY: 0 (0.0%) | * | DIVERTED: 0 (0.0%) | * | CALL SETUP: 0 (0.0%) | * +---------------------------------------------------------+ * | INVITE: 10 (0.5%) 1XX: 123 (1.5%) | * | REGISTER: 200 (5.1%) 2XX: 231 (3.1%) | * | SUBSCRIBE: 20 (1.0%) 3XX: 0 (0.0%) | * | UPDATE: 30 (1.3%) 4XX: 12 (1.5%) | * | NOTIFY: 650 (22.7%) 5XX: 0 (0.0%) | * | OPTIONS: 750 (27.4%) 6XX: 3 (0.5%) | * | PUBLISH: 0 (0.0%) 7XX: 0 (0.0%) | * | MESSAGE: 0 (0.0%) 8XX: 0 (0.0%) | * | INFO: 0 (0.0%) | * | BYE: 10 (0.5%) | * | CANCEL: 0 (0.0%) | * +---------------------------------------------------------+ * | Press any key to continue | * +---------------------------------------------------------+ * */ #include "config.h" #include "vector.h" #include "sip.h" #include "ui_manager.h" #include "ui_stats.h" /** * Ui Structure definition for Stats panel */ ui_t ui_stats = { .type = PANEL_STATS, .panel = NULL, .create = stats_create, .destroy = ui_panel_destroy, .handle_key = NULL }; void stats_create(ui_t *ui) { vector_iter_t calls; vector_iter_t msgs; sip_call_t *call; sip_msg_t *msg; // Counters! struct { int dtotal, dcalls, completed, cancelled, incall, rejected, setup, busy, diverted; int mtotal, invite, regist, subscribe, update, notify, options; int publish, message, info, ack, bye, cancel; int r100, r200, r300, r400, r500, r600, r700, r800; } stats; memset(&stats, 0, sizeof(stats)); // Calculate window dimensions ui_panel_create(ui, 25, 60); // Set the window title and boxes mvwprintw(ui->win, 1, ui->width / 2 - 9, "Stats Information"); wattron(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); title_foot_box(ui->panel); mvwhline(ui->win, 10, 1, ACS_HLINE, ui->width - 1); mvwaddch(ui->win, 10, 0, ACS_LTEE); mvwaddch(ui->win, 10, ui->width - 1, ACS_RTEE); mvwprintw(ui->win, ui->height - 2, ui->width / 2 - 9, "Press ESC to leave"); wattroff(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF)); // Parse the data calls = sip_calls_iterator(); stats.dtotal = vector_iterator_count(&calls); // Ignore this screen when no dialog exists if (!stats.dtotal) { mvwprintw(ui->win, 3, 3, "No information to display"); return; } while ((call = vector_iterator_next(&calls))) { // If this dialog is a call if (call->state) { // Increase call counter stats.dcalls++; // Increase call status counter switch (call->state) { case SIP_CALLSTATE_CALLSETUP: stats.setup++; break; case SIP_CALLSTATE_INCALL: stats.incall++; break; case SIP_CALLSTATE_CANCELLED: stats.cancelled++; break; case SIP_CALLSTATE_REJECTED: stats.rejected++; break; case SIP_CALLSTATE_BUSY: stats.busy++; break; case SIP_CALLSTATE_DIVERTED: stats.diverted++; break; case SIP_CALLSTATE_COMPLETED: stats.completed++; break; } } // For each message in call msgs = vector_iterator(call->msgs); while ((msg = vector_iterator_next(&msgs))) { // Increase message counter stats.mtotal++; // Check message type switch (msg->reqresp) { case SIP_METHOD_REGISTER: stats.regist++; break; case SIP_METHOD_INVITE: stats.invite++; break; case SIP_METHOD_SUBSCRIBE: stats.subscribe++; break; case SIP_METHOD_NOTIFY: stats.notify++; break; case SIP_METHOD_OPTIONS: stats.options++; break; case SIP_METHOD_PUBLISH: stats.publish++; break; case SIP_METHOD_MESSAGE: stats.message++; break; case SIP_METHOD_CANCEL: stats.cancel++; break; case SIP_METHOD_BYE: stats.bye++; break; case SIP_METHOD_ACK: stats.ack++; break; // case SIP_METHOD_PRACK: case SIP_METHOD_INFO: stats.info++; break; // case SIP_METHOD_REFER: case SIP_METHOD_UPDATE: stats.update++; break; default: if (msg->reqresp >= 800) stats.r800++; else if (msg->reqresp >= 700) stats.r700++; else if (msg->reqresp >= 600) stats.r600++; else if (msg->reqresp >= 500) stats.r500++; else if (msg->reqresp >= 400) stats.r400++; else if (msg->reqresp >= 300) stats.r300++; else if (msg->reqresp >= 200) stats.r200++; else if (msg->reqresp >= 100) stats.r100++; } } } // Print parses data mvwprintw(ui->win, 3, 3, "Dialogs: %d", stats.dtotal); mvwprintw(ui->win, 4, 3, "Calls: %d (%.1f%%)", stats.dcalls, (float) stats.dcalls * 100 / stats.dtotal); mvwprintw(ui->win, 5, 3, "Messages: %d", stats.mtotal); // Print status of calls if any if (stats.dcalls) { mvwprintw(ui->win, 3, 33, "COMPLETED: %d (%.1f%%)", stats.completed, (float) stats.completed * 100 / stats.dcalls); mvwprintw(ui->win, 4, 33, "CANCELLED: %d (%.1f%%)", stats.cancelled, (float) stats.cancelled * 100 / stats.dcalls); mvwprintw(ui->win, 5, 33, "IN CALL: %d (%.1f%%)", stats.incall, (float) stats.incall * 100 / stats.dcalls); mvwprintw(ui->win, 6, 33, "REJECTED: %d (%.1f%%)", stats.rejected, (float) stats.rejected * 100 / stats.dcalls); mvwprintw(ui->win, 7, 33, "BUSY: %d (%.1f%%)", stats.busy, (float) stats.busy * 100 / stats.dcalls); mvwprintw(ui->win, 8, 33, "DIVERTED: %d (%.1f%%)", stats.diverted, (float) stats.diverted * 100 / stats.dcalls); mvwprintw(ui->win, 9, 33, "CALL SETUP: %d (%.1f%%)", stats.setup, (float) stats.setup * 100 / stats.dcalls); } mvwprintw(ui->win, 11, 3, "INVITE: %d (%.1f%%)", stats.invite, (float) stats.invite * 100 / stats.mtotal); mvwprintw(ui->win, 12, 3, "REGISTER: %d (%.1f%%)", stats.regist, (float) stats.regist * 100 / stats.mtotal); mvwprintw(ui->win, 13, 3, "SUBSCRIBE: %d (%.1f%%)", stats.subscribe, (float) stats.subscribe * 100 / stats.mtotal); mvwprintw(ui->win, 14, 3, "UPDATE: %d (%.1f%%)", stats.update, (float) stats.update * 100 / stats.mtotal); mvwprintw(ui->win, 15, 3, "NOTIFY: %d (%.1f%%)", stats.notify, (float) stats.notify * 100 / stats.mtotal); mvwprintw(ui->win, 16, 3, "OPTIONS: %d (%.1f%%)", stats.options, (float) stats.options * 100 / stats.mtotal); mvwprintw(ui->win, 17, 3, "PUBLISH: %d (%.1f%%)", stats.publish, (float) stats.publish * 100 / stats.mtotal); mvwprintw(ui->win, 18, 3, "MESSAGE: %d (%.1f%%)", stats.message, (float) stats.message * 100 / stats.mtotal); mvwprintw(ui->win, 19, 3, "INFO: %d (%.1f%%)", stats.info, (float) stats.info * 100 / stats.mtotal); mvwprintw(ui->win, 20, 3, "BYE: %d (%.1f%%)", stats.bye, (float) stats.bye * 100 / stats.mtotal); mvwprintw(ui->win, 21, 3, "CANCEL: %d (%.1f%%)", stats.cancel, (float) stats.cancel * 100 / stats.mtotal); mvwprintw(ui->win, 11, 33, "1XX: %d (%.1f%%)", stats.r100, (float) stats.r100 * 100 / stats.mtotal); mvwprintw(ui->win, 12, 33, "2XX: %d (%.1f%%)", stats.r200, (float) stats.r200 * 100 / stats.mtotal); mvwprintw(ui->win, 13, 33, "3XX: %d (%.1f%%)", stats.r300, (float) stats.r300 * 100 / stats.mtotal); mvwprintw(ui->win, 14, 33, "4XX: %d (%.1f%%)", stats.r400, (float) stats.r400 * 100 / stats.mtotal); mvwprintw(ui->win, 15, 33, "5XX: %d (%.1f%%)", stats.r500, (float) stats.r500 * 100 / stats.mtotal); mvwprintw(ui->win, 16, 33, "6XX: %d (%.1f%%)", stats.r600, (float) stats.r600 * 100 / stats.mtotal); mvwprintw(ui->win, 17, 33, "7XX: %d (%.1f%%)", stats.r700, (float) stats.r700 * 100 / stats.mtotal); mvwprintw(ui->win, 18, 33, "8XX: %d (%.1f%%)", stats.r800, (float) stats.r800 * 100 / stats.mtotal); } sngrep-1.8.2/src/curses/ui_stats.h000066400000000000000000000027361464272443000171350ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file ui_stats.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage ui window for capture stats display */ #ifndef __SNGREP_UI_STATS_H #define __SNGREP_UI_STATS_H /** * @brief Creates a new stats panel * * This function allocates all required memory for * displaying the stats panel. It also draws all the * static information of the panel that will never be * redrawn. * * @param ui UI structure pointer */ void stats_create(ui_t *ui); #endif /* __SNGREP_UI_STATS_H */ sngrep-1.8.2/src/filter.c000066400000000000000000000155061464272443000152550ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file filter.c * @author Ivan Alonso [aka Kaian] * * @brief Source code of functions defined in filter.h * */ #include #include #include "sip.h" #include "curses/ui_call_list.h" #include "filter.h" //! Storage of filter information filter_t filters[FILTER_COUNT] = { }; int filter_set(int type, const char *expr) { #ifdef WITH_PCRE pcre *regex = NULL; // If we have an expression, check if compiles before changing the filter if (expr) { const char *re_err = NULL; int32_t err_offset; int32_t pcre_options = PCRE_UNGREEDY | PCRE_CASELESS; // Check if we have a valid expression if (!(regex = pcre_compile(expr, pcre_options, &re_err, &err_offset, 0))) return 1; } // Remove previous value if (filters[type].expr) { sng_free(filters[type].expr); pcre_free(filters[type].regex); } // Set new expresion values filters[type].expr = (expr) ? strdup(expr) : NULL; filters[type].regex = regex; #elif defined(WITH_PCRE2) pcre2_code *regex = NULL; // If we have an expression, check if compiles before changing the filter if (expr) { int re_err = 0; PCRE2_SIZE err_offset = 0; uint32_t pcre_options = PCRE2_UNGREEDY | PCRE2_CASELESS; // Check if we have a valid expression if (!(regex = pcre2_compile((PCRE2_SPTR) expr, PCRE2_ZERO_TERMINATED, pcre_options, &re_err, &err_offset, NULL))) return 1; } // Remove previous value if (filters[type].expr) { sng_free(filters[type].expr); pcre2_code_free(filters[type].regex); } // Set new expresion values filters[type].expr = (expr) ? strdup(expr) : NULL; filters[type].regex = regex; #else regex_t regex; // If we have an expression, check if compiles before changing the filter if (expr) { // Check if we have a valid expression if (regcomp(®ex, expr, REG_EXTENDED | REG_ICASE) != 0) return 1; } // Remove previous value if (filters[type].expr) { sng_free(filters[type].expr); regfree(&filters[type].regex); } // Set new expresion values filters[type].expr = (expr) ? strdup(expr) : NULL; memcpy(&filters[type].regex, ®ex, sizeof(regex)); #endif return 0; } const char * filter_get(int type) { return filters[type].expr; } int filter_check_call(void *item) { int i; char data[MAX_SIP_PAYLOAD]; sip_call_t *call = (sip_call_t*) item; sip_msg_t *msg; vector_iter_t it; // Dont filter calls without messages if (call_msg_count(call) == 0) return 0; // Filter for this call has already be processed if (call->filtered != -1) return (call->filtered == 0); // By default, call matches all filters call->filtered = 0; // Check all filter types for (i=0; i < FILTER_COUNT; i++) { // If filter is not enabled, go to the next if (!filters[i].expr) continue; // Initialize memset(data, 0, sizeof(data)); // Get filtered field switch(i) { case FILTER_SIPFROM: call_get_attribute(call, SIP_ATTR_SIPFROM, data); break; case FILTER_SIPTO: call_get_attribute(call, SIP_ATTR_SIPTO, data); break; case FILTER_SOURCE: call_get_attribute(call, SIP_ATTR_SRC, data); break; case FILTER_DESTINATION: call_get_attribute(call, SIP_ATTR_DST, data); break; case FILTER_METHOD: call_get_attribute(call, SIP_ATTR_METHOD, data); break; case FILTER_PAYLOAD: break; case FILTER_CALL_LIST: // FIXME Maybe call should know how to calculate this line call_list_line_text(ui_find_by_type(PANEL_CALL_LIST), call, data); break; default: // Unknown filter id return 0; } // For payload filtering, check all messages payload if (i == FILTER_PAYLOAD) { // Assume this call doesn't match the filter call->filtered = 1; // Create an iterator for the call messages it = vector_iterator(call->msgs); while ((msg = vector_iterator_next(&it))) { // Copy message payload strcpy(data, msg_get_payload(msg)); // Check if this payload matches the filter if (filter_check_expr(filters[i], data) == 0) { call->filtered = 0; break; } } if (call->filtered == 1) break; } else { // Check the filter against given data if (filter_check_expr(filters[i], data) != 0) { // The data didn't matched the filter call->filtered = 1; break; } } } // Return the final filter status return (call->filtered == 0); } int filter_check_expr(filter_t filter, const char *data) { #ifdef WITH_PCRE return pcre_exec(filter.regex, 0, data, strlen(data), 0, 0, 0, 0); #elif defined(WITH_PCRE2) pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(filter.regex, NULL); int ret = pcre2_match(filter.regex, (PCRE2_SPTR) data, (PCRE2_SIZE) strlen(data), 0, 0, match_data, NULL); pcre2_match_data_free(match_data); return (ret == PCRE2_ERROR_NOMATCH) ? 1 : 0; #else // Call doesn't match this filter return regexec(&filter.regex, data, 0, NULL, 0); #endif } void filter_reset_calls() { sip_call_t *call; vector_iter_t calls = sip_calls_iterator(); // Force filter evaluation while ((call = vector_iterator_next(&calls))) call->filtered = -1; } sngrep-1.8.2/src/filter.h000066400000000000000000000071751464272443000152650ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file option.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage filtering options * * There are two types of filters: capture and display. * * Capture filters are handled in capture functions and they limit the * number of calls are created in sip storage. * * Display filters are handled in this file and they limit the number of * calls that are displayed to the user. Multiple display filters can be * set at the same time. In order to be valid, a call MUST match all the * enabled filters to be shown. * */ #ifndef __SNGREP_FILTER_H_ #define __SNGREP_FILTER_H_ #include "config.h" #ifdef WITH_PCRE #include #elif defined(WITH_PCRE2) #include #else #include #endif #include "sip.h" //! Shorter declaration of sip_call_group structure typedef struct filter filter_t; /** * @brief Available filter types */ enum filter_type { //! SIP From header in packet payload FILTER_SIPFROM = 0, //! SIP To header in packet payload FILTER_SIPTO, //! Packet source address FILTER_SOURCE, //! Packet destination address FILTER_DESTINATION, //! SIP Method in packet payload FILTER_METHOD, //! SIP Payload in any call packet FILTER_PAYLOAD, //! Displayed line in call list FILTER_CALL_LIST, //! Number of available filter types FILTER_COUNT, }; /** * @brief Filter information */ struct filter { //! The filter text char *expr; #ifdef WITH_PCRE //! The filter compiled expression pcre *regex; #elif defined(WITH_PCRE2) //! The filter compiled expression pcre2_code *regex; #else //! The filter compiled expression regex_t regex; #endif }; /** * @brief Set a given filter expression * * This function is used to set the filter expression * on a given filter. If given expression is NULL * the filter will be removed. * * @param type Type of the filter * @param expr Regexpression to match * @return 0 if the filter is valid, 1 otherwise */ int filter_set(int type, const char *expr); /** * @brief Get filter text expression * * @param type filter type * @return filter text expressions */ const char * filter_get(int type); /** * @brief Check if a call if filtered * * @param call Call to be checked * @return 1 if call is filtered */ int filter_check_call(void *item); /** * @brief Check if data matches the filter regexp * * @return 0 if the given data matches the filter */ int filter_check_expr(filter_t filter, const char *data); /** * @brief Reset filtered flag in all calls * * This function can be used to force reevaluation * of filters in all calls. */ void filter_reset_calls(); #endif /* __SNGREP_FILTER_H_ */ sngrep-1.8.2/src/group.c000066400000000000000000000215731464272443000151250ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file group.c * @author Ivan Alonso [aka Kaian] * * @brief Source code of functions defined in group.h * */ #include #include #include "group.h" sip_call_group_t * call_group_create() { sip_call_group_t *group; if (!(group = sng_malloc(sizeof(sip_call_group_t)))) { return NULL; } group->calls = vector_create(5, 2); return group; } void call_group_destroy(sip_call_group_t *group) { // Remove all calls of the group sip_call_t *call; while ((call = vector_first(group->calls))) { call_group_del(group, call); } vector_destroy(group->calls); sng_free(group); } bool call_group_has_changed(sip_call_group_t *group) { bool changed = false; // Check if any of the group has changed // We check all the calls even after we found a changed one to reset all // the changed pointers sip_call_t *call = NULL; while ((call = call_group_get_next(group, call))) { if (call_has_changed(call)) { call->changed = false; changed = true; // If this group is based on a Call-Id, check there are no new call related if (group->callid && !strcmp(group->callid, call->callid)) { call_group_add_calls(group, call->xcalls); } } } // Return if any of the calls have changed return changed; } sip_call_group_t * call_group_clone(sip_call_group_t *original) { sip_call_group_t *clone; if (!original) return NULL; if (!(clone = sng_malloc(sizeof(sip_call_group_t)))) { return NULL; } clone->calls = vector_clone(original->calls); return clone; } void call_group_add(sip_call_group_t *group, sip_call_t *call) { if (!call) return; if (!call_group_exists(group, call)) { call->locked = true; vector_append(group->calls, call); } } void call_group_add_calls(sip_call_group_t *group, vector_t *calls) { sip_call_t *call; vector_iter_t it = vector_iterator(calls); // Get the call with the next chronological message while ((call = vector_iterator_next(&it))) { call->locked = true; if (!call_group_exists(group, call)) { vector_append(group->calls, call); } } } void call_group_del(sip_call_group_t *group, sip_call_t *call) { if (!call) return; call->locked = false; vector_remove(group->calls, call); } int call_group_exists(sip_call_group_t *group, sip_call_t *call) { return (vector_index(group->calls, call) >= 0) ? 1 : 0; } int call_group_color(sip_call_group_t *group, sip_call_t *call) { return (vector_index(group->calls, call) % 7) + 1; } sip_call_t * call_group_get_next(sip_call_group_t *group, sip_call_t *call) { sip_msg_t *next, *first; sip_call_t *c; int i; if (!group) return NULL; // Get call of the first message in group if (!call) { if ((next = call_group_get_next_msg(group, NULL))) { return next->call; } return NULL; } // Initialize candidate next = NULL; // Get the call with the next chronological message for (i = 0; i < vector_count(group->calls); i++) { if ((c = vector_item(group->calls, i)) == call) continue; // Get first message first = vector_first(c->msgs); // Is first message of this call older? if (msg_is_older(first, vector_first(call->msgs)) && (!next || !msg_is_older(first, next))) { next = first; break; } } return (next) ? next->call : NULL; } int call_group_count(sip_call_group_t *group) { return vector_count(group->calls); } int call_group_msg_count(sip_call_group_t *group) { sip_call_t *call; vector_iter_t msgs; int msgcnt = 0, i; for (i = 0; i < vector_count(group->calls); i++) { call = vector_item(group->calls, i); msgs = vector_iterator(call->msgs); if (group->sdp_only) { vector_iterator_set_filter(&msgs, msg_has_sdp); } msgcnt += vector_iterator_count(&msgs); } return msgcnt; } int call_group_msg_number(sip_call_group_t *group, sip_msg_t *msg) { int number = 0; sip_msg_t *cur = NULL; while ((cur = call_group_get_next_msg(group, cur))) { if (group->sdp_only && !msg_has_sdp(msg)) continue; if (cur == msg) return number; number++; } return 0; } sip_msg_t * call_group_get_next_msg(sip_call_group_t *group, sip_msg_t *msg) { sip_msg_t *next; if (call_group_count(group) == 1) { sip_call_t *call = vector_first(group->calls); vector_iter_t it = vector_iterator(call->msgs); vector_iterator_set_current(&it, vector_index(call->msgs, msg)); next = vector_iterator_next(&it); } else { vector_t *messages = vector_create(1,10); vector_set_sorter(messages, call_group_msg_sorter); vector_iter_t callsit = vector_iterator(group->calls); sip_call_t *call; while ((call = vector_iterator_next(&callsit))) { vector_append_vector(messages, call->msgs); } if (msg == NULL) { next = vector_first(messages); } else { next = vector_item(messages, vector_index(messages, msg) + 1); } vector_destroy(messages); } next = sip_parse_msg(next); if (next && group->sdp_only && !msg_has_sdp(next)) { return call_group_get_next_msg(group, next); } return next; } sip_msg_t * call_group_get_prev_msg(sip_call_group_t *group, sip_msg_t *msg) { sip_msg_t *prev; if (call_group_count(group) == 1) { sip_call_t *call = vector_first(group->calls); vector_iter_t it = vector_iterator(call->msgs); vector_iterator_set_current(&it, vector_index(call->msgs, msg)); prev = vector_iterator_prev(&it); } else { vector_t *messages = vector_create(1, 10); vector_set_sorter(messages, call_group_msg_sorter); vector_iter_t callsit = vector_iterator(group->calls); sip_call_t *call; while ((call = vector_iterator_next(&callsit))) { vector_append_vector(messages, call->msgs); } if (msg == NULL) { prev = vector_last(messages); } else { prev = vector_item(messages, vector_index(messages, msg) - 1); } vector_destroy(messages); } prev = sip_parse_msg(prev); if (prev && group->sdp_only && !msg_has_sdp(prev)) { return call_group_get_prev_msg(group, prev); } return prev; } rtp_stream_t * call_group_get_next_stream(sip_call_group_t *group, rtp_stream_t *stream) { rtp_stream_t *next = NULL; rtp_stream_t *cand; sip_call_t *call; vector_iter_t streams; int i; for (i = 0; i < vector_count(group->calls); i++) { call = vector_item(group->calls, i); streams = vector_iterator(call->streams); while ( (cand = vector_iterator_next(&streams))) { if (!stream_get_count(cand)) continue; if (cand->type != PACKET_RTP) continue; // candidate must be between msg and next if (stream_is_older(cand, stream) && (!next || stream_is_older(next, cand))) { next = cand; } } } return next; } void call_group_msg_sorter(vector_t *vector, void *item) { struct timeval curts, prevts; int count = vector_count(vector); int i; // Current and last packet times curts = msg_get_time(item); for (i = count - 2 ; i >= 0; i--) { // Get previous packet prevts = msg_get_time(vector_item(vector, i)); // Check if the item is already in a sorted position if (timeval_is_older(curts, prevts)) { vector_insert(vector, item, i + 1); return; } } // Put this item at the begining of the vector vector_insert(vector, item, 0); } sngrep-1.8.2/src/group.h000066400000000000000000000147471464272443000151370ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file option.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage call groups * * Call groups are used to pass a set of calls between different panels of * sngrep. * */ #ifndef __SNGREP_GROUP_H_ #define __SNGREP_GROUP_H_ #include "config.h" #include "vector.h" #include "sip.h" //! Shorter declaration of sip_call_group structure typedef struct sip_call_group sip_call_group_t; /** * @brief Contains a list of calls * * This structure is used for displaying more than one dialog in the * same call flow. Instead of displaying a call flow, we will display * a calls group flow. */ struct sip_call_group { //! For extended display, main call-id char *callid; //! Calls array in the group vector_t *calls; //! Color of the last printed call in mode Color-by-Call int color; //! Only consider SDP messages from Calls int sdp_only; }; /** * @brief Create a new groupt to hold Calls in it * * Allocate memory to create a new calls group * * @return Pointer to a new group */ sip_call_group_t * call_group_create(); /** * @brief Deallocate memory of an existing group * * @param Pointer to an existing group */ void call_group_destroy(sip_call_group_t *group); /** * @brief Check if any of the calls of the group has changed * * This checks all the group flags to check if any of the call has * changed. * * @param ggroup Call group structure pointer * @return true if any of the calls of the group has changed, false otherwise */ bool call_group_has_changed(sip_call_group_t *group); /** * @brief Clone an existing call group * * Create a new call group with the same calls of the * original one. Beware: The call pointers are shared between * original and clone groups. * */ sip_call_group_t * call_group_clone(sip_call_group_t *original); /** * @brief Add a Call to the group * * @param group Pointer to an existing group * @param call Pointer to an existing call */ void call_group_add(sip_call_group_t *group, sip_call_t *call); /** * @brief Add several Calls to the group * * @param group Pointer to an existing group * @param calls Pointer to a vector with calls */ void call_group_add_calls(sip_call_group_t *group, vector_t *calls); /** * @brief Remove a call from the group * * @param group Pointer to an existing group * @param call Pointer to an existing call */ void call_group_del(sip_call_group_t *group, sip_call_t *call); /** * @brief Check if a call is in the group * * @param group Pointer to an existing group * @param call Pointer to an existing call * @return 1 if the call is in the group, 0 otherwise */ int call_group_exists(sip_call_group_t *group, sip_call_t *call); /** * @brief Return the color pair number of a call * * When color by callid mode is enabled, this function will * return the color pair number of the call depending its * position inside the call * * @param group Pointer to an existing group * @param call Pointer to an existing call * @return Color pair number */ int call_group_color(sip_call_group_t *group, sip_call_t *call); /** * @brief Return the next call in the group * * Return then next call after the given call parameter. * If NULL is used as parameter, return the first call. * It will return NULL if last call is given as parameter. * * @param group Pointer to an existing group * @param call Pointer to an existing call or NULL * @return Next call of the group, or NULL */ sip_call_t * call_group_get_next(sip_call_group_t *group, sip_call_t *call); /** * @brief Return number of calls in a group * * @param group Pointer to an existing group * @return How many calls the group has */ int call_group_count(sip_call_group_t *group); /** * @brief Return message count in the group * * Return the sum of messages of all calls in the group * * @param group Pointer to an existing group * @return How many messages have the calls in the group */ int call_group_msg_count(sip_call_group_t *group); /** * @brief Return Message position in the group * * Return how many messages are before the given message * sorting all messages in all group calls by timestamp * * @param group Pointer to an existing group * @param msg A sip message from a call in the group * @return The position of given message in the group */ int call_group_msg_number(sip_call_group_t *group, sip_msg_t *msg); /** * @brief Finds the next msg in a call group. * * If the passed msg is NULL it returns the first message * of the group. * * @param callgroup SIP call group structure * @param msg Actual SIP msg from any call of the group (can be NULL) * @return Next chronological message in the group or NULL */ sip_msg_t * call_group_get_next_msg(sip_call_group_t *group, sip_msg_t *msg); /** * @brief Find the previous message in a call group * * @param callgroup SIP call group structure * @param msg Actual SIP msg from any call of the group * @return Previous chronological message in the group or NULL */ sip_msg_t * call_group_get_prev_msg(sip_call_group_t *group, sip_msg_t *msg); /** * @brief Find the next stream in a call group * * @param callgroup SIP call group structure * @param msg Actual stream structure from any call of the group * @return next chronological stream in the group or NULL */ rtp_stream_t * call_group_get_next_stream(sip_call_group_t *group, rtp_stream_t *stream); /** * @brief Sort messages in a group by message time * @param vector sorted vector * @param item item to add to the vector */ void call_group_msg_sorter(vector_t *vector, void *item); #endif /* __SNGREP_GROUP_H_ */ sngrep-1.8.2/src/hash.c000066400000000000000000000070321464272443000147060ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file hash.c * @author Ivan Alonso [aka Kaian] * * @brief Source code of functions defined in hash.h * */ #include "hash.h" #include #include htable_t * htable_create(size_t size) { htable_t *h; // Allocate memory for this table data if (!(h = malloc(sizeof(htable_t)))) return NULL; h->size = size; // Allocate memory for this table buckets if (!(h->buckets = malloc(sizeof(hentry_t) * size))) { free(h); return NULL; } // Initialize allocated memory memset(h->buckets, 0, sizeof(hentry_t) * size); // Return allocated table return h; } void htable_destroy(htable_t *table) { free(table->buckets); free(table); } int htable_insert(htable_t *table, const char *key, void *data) { // Get hash position for given entry size_t pos = htable_hash(table, key); // Create a new entry for given key hentry_t *entry; if (!(entry = malloc(sizeof(hentry_t)))) return -1; entry->key = key; entry->data = data; entry->next = 0; // Check if the hash position is in use hentry_t *exists = table->buckets[pos]; if (!exists) { table->buckets[pos] = entry; } else { while (exists->next) { exists = exists->next; } exists->next = entry; } return 0; } void htable_remove(htable_t *table, const char *key) { // Get hash position for given entry size_t pos = htable_hash(table, key); // Check if the hash position is in use hentry_t *entry, *prev = NULL; for (entry = table->buckets[pos]; entry; prev = entry, entry = entry->next) { if (!strcmp(entry->key, key)) { if (prev) { prev->next = entry->next; } else { table->buckets[pos] = entry->next; } // Remove item memory free(entry); return; } } } void * htable_find(htable_t *table, const char *key) { // Get hash position for given entry size_t pos = htable_hash(table, key); // Check if the hash position is in use hentry_t *entry; for (entry = table->buckets[pos]; entry; entry = entry->next) { if (!strcmp(entry->key, key)) { //! Found return entry->data; } } // Not found return NULL; } size_t htable_hash(htable_t *table, const char *key) { // dbj2 - http://www.cse.yorku.ca/~oz/hash.html size_t hash = 5381; while (*key++) { hash = ((hash << 5) + hash) ^ *key; } return hash & (table->size - 1); } sngrep-1.8.2/src/hash.h000066400000000000000000000037131464272443000147150ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file hash.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage hash tables */ #ifndef __SNGREP_HASH_H_ #define __SNGREP_HASH_H_ #include "config.h" #include //! Shorter declaration of hash structures typedef struct htable htable_t; typedef struct hentry hentry_t; /** * Structure to hold a Hash table entry */ struct hentry { //! Key of the hash entry const char *key; //! Pointer to has entry data void *data; //! Next entry sharing the same hash value hentry_t *next; }; struct htable { //! Fixed hash table limit size_t size; // Hash table entries hentry_t **buckets; }; htable_t * htable_create(size_t size); void htable_destroy(htable_t *table); int htable_insert(htable_t *table, const char *key, void *data); void htable_remove(htable_t *table, const char *key); void * htable_find(htable_t *table, const char *key); size_t htable_hash(htable_t *table, const char *key); #endif /* __SNGREP_HASH_H_ */ sngrep-1.8.2/src/keybinding.c000066400000000000000000000216211464272443000161060ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file group.c * @author Ivan Alonso [aka Kaian] * * @brief Source code of functions defined in keybinding.h * */ #include "config.h" #include #include #include #include "setting.h" #include "keybinding.h" #include "curses/ui_manager.h" //! sngrep keybindings key_binding_t bindings[ACTION_SENTINEL] = { { ACTION_PRINTABLE, "", { }, 0 }, { ACTION_UP, "up", { KEY_UP, 'k' }, 2 }, { ACTION_DOWN, "down", { KEY_DOWN, 'j' }, 2 }, { ACTION_LEFT, "left", { KEY_LEFT, 'h' }, 2 }, { ACTION_RIGHT, "right", { KEY_RIGHT, 'l'}, 2 }, { ACTION_DELETE, "delete", { KEY_DC }, 1 }, { ACTION_BACKSPACE, "backspace", { KEY_BACKSPACE, KEY_BACKSPACE2, KEY_BACKSPACE3 }, 3 }, { ACTION_NPAGE, "npage", { KEY_NPAGE, KEY_CTRL('F') }, 2 }, { ACTION_PPAGE, "ppage", { KEY_PPAGE, KEY_CTRL('B') }, 2 }, { ACTION_HNPAGE, "hnpage", { KEY_CTRL('D') }, 1 }, { ACTION_HPPAGE, "hppage", { KEY_CTRL('U') }, 2 }, { ACTION_BEGIN, "begin", { KEY_HOME, KEY_CTRL('A') }, 2 }, { ACTION_END, "end", { KEY_END, KEY_CTRL('E') }, 2 }, { ACTION_PREV_FIELD, "pfield", { KEY_UP }, 1 }, { ACTION_NEXT_FIELD, "nfield", { KEY_DOWN, KEY_TAB }, 2 }, { ACTION_RESIZE_SCREEN, "", { KEY_RESIZE }, 1 }, { ACTION_CLEAR, "clear", { KEY_CTRL('U'), KEY_CTRL('W')}, 2 }, { ACTION_CLEAR_CALLS, "clearcalls", { KEY_F(5), KEY_CTRL('L')}, 2 }, { ACTION_CLEAR_CALLS_SOFT, "clearcallssoft", {KEY_F(9)}, 2 }, { ACTION_TOGGLE_SYNTAX, "togglesyntax", { KEY_F(8), 'C' }, 2 }, { ACTION_CYCLE_COLOR, "colormode", { 'c' }, 1 }, { ACTION_COMPRESS, "compress", { 's' }, 1 }, { ACTION_SHOW_ALIAS, "togglealias", { 'a' }, 1 }, { ACTION_TOGGLE_PAUSE, "pause", { 'p' }, 1 }, { ACTION_PREV_SCREEN, "prevscreen", { KEY_ESC, 'q', 'Q' }, 3 }, { ACTION_SHOW_HELP, "help", { KEY_F(1), 'h', 'H', '?' }, 4 }, { ACTION_SHOW_RAW, "raw", { KEY_F(6), 'R', 'r' }, 3 }, { ACTION_SHOW_FLOW, "flow", { KEY_INTRO }, 1 }, { ACTION_SHOW_FLOW_EX, "flowex", { KEY_F(4), 'x' }, 2 }, { ACTION_SHOW_FILTERS, "filters", { KEY_F(7), 'f', 'F' }, 3 }, { ACTION_SHOW_COLUMNS, "columns", { KEY_F(10), 't', 'T' }, 3 }, { ACTION_SHOW_SETTINGS, "settings", { KEY_F(8), 'o', 'O' }, 3 }, { ACTION_SHOW_STATS, "stats", { 'i' }, 1 }, { ACTION_COLUMN_MOVE_UP, "columnup", { '-' }, 1 }, { ACTION_COLUMN_MOVE_DOWN, "columndown", { '+' }, 1 }, { ACTION_SDP_INFO, "sdpinfo", { KEY_F(2), 'd' }, 2 }, { ACTION_DISP_FILTER, "search", { KEY_F(3), '/', KEY_TAB }, 3 }, { ACTION_SAVE, "save", { KEY_F(2), 's', 'S'}, 3 }, { ACTION_SELECT, "select", { KEY_SPACE }, 1 }, { ACTION_CONFIRM, "confirm", { KEY_INTRO }, 1 }, { ACTION_TOGGLE_MEDIA, "togglemedia", { KEY_F(3), 'm' }, 2 }, { ACTION_ONLY_MEDIA, "onlymedia", { 'M' }, 1 }, { ACTION_TOGGLE_RAW, "rawpreview", { 't' }, 1 }, { ACTION_INCREASE_RAW, "morerawpreview", { '9' }, 1 }, { ACTION_DECREASE_RAW, "lessrawpreview", { '0' }, 1 }, { ACTION_RESET_RAW, "resetrawpreview", { 'T' }, 1 }, { ACTION_ONLY_SDP, "onlysdp", { 'D' }, 1 }, { ACTION_AUTOSCROLL, "autoscroll", { 'A' }, 1 }, { ACTION_TOGGLE_HINT, "hintalt", { 'K' }, 1 }, { ACTION_SORT_PREV, "sortprev", { '<' }, 1 }, { ACTION_SORT_NEXT, "sortnext", { '>' }, 1 }, { ACTION_SORT_SWAP, "sortswap", { 'z' }, 1 }, { ACTION_TOGGLE_TIME, "toggletime", { 'w' }, 1 }, }; void key_bindings_dump() { int i, j; for (i = 1; i < ACTION_SENTINEL; i++) { for (j = 0; j < bindings[i].bindcnt; j++) { printf("ActionID: %d\t ActionName: %-21s Key: %d (%s)\n", bindings[i].id, bindings[i].name, bindings[i].keys[j], key_to_str(bindings[i].keys[j])); } } } key_binding_t * key_binding_data(int action) { int i; for (i = 1; i < ACTION_SENTINEL; i++) { if (bindings[i].id == action) return &bindings[i]; } return NULL; } void key_bind_action(int action, int key) { key_binding_t *bind; if (!(bind = key_binding_data(action))) return; if (bind->bindcnt == MAX_BINDINGS) return; bind->keys[bind->bindcnt++] = key; } void key_unbind_action(int action, int key) { key_binding_t tmp, *bind; int i; // Action is not valid if (!(bind = key_binding_data(action))) return; // Copy binding to temporal struct memcpy(&tmp, bind, sizeof(key_binding_t)); // Reset bindings for this action memset(&bind->keys, 0, sizeof(int) * MAX_BINDINGS); // Add all bindings but the unbinded for (i=0; i < tmp.bindcnt; i++) { if (tmp.keys[i] != key) { key_bind_action(action, tmp.keys[i]); } } } int key_find_action(int key, int start) { int i, j; for (i = start + 1; i < ACTION_SENTINEL; i++) { if (i == ACTION_PRINTABLE && key_is_printable(key)) return ACTION_PRINTABLE; for (j = 0; j < bindings[i].bindcnt; j++) if (bindings[i].keys[j] == key) return bindings[i].id; } return -1; } int key_action_id(const char *action) { int i; for (i = 1; i < ACTION_SENTINEL; i++) { if (!strcasecmp(action, bindings[i].name)) return bindings[i].id; } return -1; } int key_is_printable(int key) { return key == ' ' || (key > 33 && key < 126) || (key > 160 && key < 255); } const char * key_to_str(int key) { //! Check function keys and Special keys switch(key) { case KEY_F(1): return "F1"; case KEY_F(2): return "F2"; case KEY_F(3): return "F3"; case KEY_F(4): return "F4"; case KEY_F(5): return "F5"; case KEY_F(6): return "F6"; case KEY_F(7): return "F7"; case KEY_F(8): return "F8"; case KEY_F(9): return "F9"; case KEY_F(10): return "F10"; case KEY_ESC: return "Esc"; case KEY_INTRO: return "Enter"; case ' ': return "Space"; default: if (key_is_printable(key)) return keyname(key); } return ""; } int key_from_str(const char *key) { if (!key) return 0; // Single character string if (strlen(key) == 1) return *key; // Function keys if (*key == 'F') return KEY_F(atoi(key+1)); // Control Secuences if (*key == '^') return KEY_CTRL(toupper(*(key+1))); if (!strncasecmp(key, "Ctrl-", 5)) return KEY_CTRL(toupper(*(key+5))); // Special Name characters if (!strcasecmp(key, "Esc")) return KEY_ESC; if (!strcasecmp(key, "Space")) return ' '; if (!strcasecmp(key, "Enter")) return KEY_INTRO; return 0; } const char * key_action_key_str(int action) { key_binding_t *bind; if (!(bind = key_binding_data(action))) return NULL; if (setting_enabled(SETTING_ALTKEY_HINT) && bind->bindcnt > 1) { // First alt keybinding return key_to_str(bind->keys[1]); } else { // Default keybinding return key_to_str(bind->keys[0]); } } int key_action_key(int action) { key_binding_t *bind; if (!(bind = key_binding_data(action))) return -1; if (setting_enabled(SETTING_ALTKEY_HINT) && bind->bindcnt > 1) { // First alt keybinding return bind->keys[1]; } else { // Default keybinding return bind->keys[0]; } return -1; } sngrep-1.8.2/src/keybinding.h000066400000000000000000000134521464272443000161160ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file option.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage keybindings * * sngrep keybindings are associated with actions. Each action can store multiple * keybindings. * Keybindings configured by user using *key* directive of sngreprc file, * in the format: * * key ui_action keycode * * keycode must be a letter (lowercase or uppercase) or a ^ sign with an uppercase * letter when Ctrl modifier is used. * */ #ifndef __SNGREP_KEYBINDING_H_ #define __SNGREP_KEYBINDING_H_ //! Number of keybindings per action #define MAX_BINDINGS 5 //! Some undefined key codes #define KEY_CTRL(n) ((n)-64) #define KEY_ESC 27 #define KEY_INTRO 10 #define KEY_TAB 9 #define KEY_BACKSPACE2 8 #define KEY_BACKSPACE3 127 #define KEY_SPACE ' ' /** * @brief Available Key actions */ enum key_actions { ACTION_PRINTABLE = 0, ACTION_UP, ACTION_DOWN, ACTION_LEFT, ACTION_RIGHT, ACTION_DELETE, ACTION_BACKSPACE, ACTION_NPAGE, ACTION_PPAGE, ACTION_HNPAGE, ACTION_HPPAGE, ACTION_BEGIN, ACTION_END, ACTION_PREV_FIELD, ACTION_NEXT_FIELD, ACTION_RESIZE_SCREEN, ACTION_CLEAR, ACTION_CLEAR_CALLS, ACTION_CLEAR_CALLS_SOFT, ACTION_TOGGLE_SYNTAX, ACTION_CYCLE_COLOR, ACTION_COMPRESS, ACTION_SHOW_HOSTNAMES, ACTION_SHOW_ALIAS, ACTION_TOGGLE_PAUSE, ACTION_PREV_SCREEN, ACTION_SHOW_HELP, ACTION_SHOW_RAW, ACTION_SHOW_FLOW, ACTION_SHOW_FLOW_EX, ACTION_SHOW_FILTERS, ACTION_SHOW_COLUMNS, ACTION_SHOW_SETTINGS, ACTION_SHOW_STATS, ACTION_COLUMN_MOVE_UP, ACTION_COLUMN_MOVE_DOWN, ACTION_SDP_INFO, ACTION_DISP_FILTER, ACTION_SAVE, ACTION_SELECT, ACTION_CONFIRM, ACTION_TOGGLE_MEDIA, ACTION_ONLY_MEDIA, ACTION_TOGGLE_RAW, ACTION_INCREASE_RAW, ACTION_DECREASE_RAW, ACTION_RESET_RAW, ACTION_ONLY_SDP, ACTION_TOGGLE_HINT, ACTION_AUTOSCROLL, ACTION_SORT_PREV, ACTION_SORT_NEXT, ACTION_SORT_SWAP, ACTION_TOGGLE_TIME, ACTION_SENTINEL }; //! Shorter declaration of key_binding structure typedef struct key_binding key_binding_t; /** * @brief Struct to hold a keybinding data */ struct key_binding { //! Keybinding action id int id; //! Keybinding action name const char *name; //! keybindings for this action int keys[MAX_BINDINGS]; //! How many keys are binded to this action int bindcnt; }; /** * @brief Print configured keybindigs */ void key_bindings_dump(); /** * @brief Return Keybinding data for a given action * @return key_binding_t structure pointer or NULL if not found */ key_binding_t * key_binding_data(int action); /** * @brief Bind a key to an action * * @param action One action defined in @key_actions * @param key Keycode returned by getch */ void key_bind_action(int action, int key); /** * @brief Unbind a key to an action * * @param action One action defined in @key_actions * @param key Keycode returned by getch */ void key_unbind_action(int action, int key); /** * @brief Find the next action for a given key * * Set start parameter to -1 for start searching the * first action. * * @param action One action defined in @key_actions * @param key Keycode returned by getch */ int key_find_action(int key, int start); /** * @brief Return the action id associate to an action str * * This function is used to translate keybindings configuration * found in sngreprc file to internal Action IDs * * @param action Configuration string for an action * @return action id from @key_actions or -1 if none found */ int key_action_id(const char *action); /** * @brief Check if key is a printable ascii character * * @return 1 if key is alphanumeric or space */ int key_is_printable(int key); /** * @brief Return a Human readable representation of a key * * @return Character string representing the key */ const char * key_to_str(int key); /** * @brief Parse Human key declaration to curses key * * This function is used to translate keybindings configuration * keys found in sngreprc file into internal ncurses keycodes * * @return ncurses keycode for the given key string */ int key_from_str(const char *key); /** * @brief Return Human readable key for an action * * This function is used to display keybindings in the bottom bar * of panels. Depending on sngrep configuration it will display the * first associated keybding with the action or the second one * (aka alternative). * * @param action One action defined in @key_actions * @return Main/Alt keybinding for the given action */ const char * key_action_key_str(int action); /** * @brief Return key value for a given action * * @param action One action defined in @key_actions * @return Main/Alt keybinding for the given action */ int key_action_key(int action); #endif /* __SNGREP_KEYBINDING_H_ */ sngrep-1.8.2/src/main.c000066400000000000000000000411621464272443000147110ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file main.c * @author Ivan Alonso [aka Kaian] * * @brief Source of initial functions used by sngrep */ #include "config.h" #include #include #include #include #include #include "option.h" #include "vector.h" #include "capture.h" #include "capture_eep.h" #include "curses/ui_save.h" #ifdef WITH_GNUTLS #include "capture_gnutls.h" #endif #ifdef WITH_OPENSSL #include "capture_openssl.h" #endif #include "curses/ui_manager.h" /** * @brief Usage function * * Print all available options (which are none actually) */ void usage() { printf("Usage: %s [-hVcivNqrD] [-IO pcap_dump] [-d dev] [-l limit] [-B buffer]" #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) " [-k keyfile]" #endif #ifdef USE_EEP " [-LHE capture_url]" #endif " [] []\n\n" " -h --help\t\t This usage\n" " -V --version\t Version information\n" " -d --device\t\t Use this capture device instead of default\n" " -I --input\t\t Read captured data from pcap file\n" " -O --output\t\t Write captured data to pcap file\n" " -B --buffer\t\t Set pcap buffer size in MB (default: 2)\n" " -c --calls\t\t Only display dialogs starting with INVITE\n" " -r --rtp\t\t Capture RTP packets payload\n" " -l --limit\t\t Set capture limit to N dialogs\n" " -i --icase\t\t Make case insensitive\n" " -v --invert\t\t Invert \n" " -N --no-interface\t Don't display sngrep interface, just capture\n" " -q --quiet\t\t Don't print captured dialogs in no interface mode\n" " -D --dump-config\t Print active configuration settings and exit\n" " -f --config\t\t Read configuration from file\n" " -F --no-config\t Do not read configuration from default config file\n" " -T --text\t Save pcap to text file\n" " -R --rotate\t\t Rotate calls when capture limit have been reached\n" #ifdef USE_EEP " -H --eep-send\t Homer sipcapture url (udp:X.X.X.X:XXXX)\n" " -L --eep-listen\t Listen for encapsulated packets (udp:X.X.X.X:XXXX)\n" " -E --eep-parse\t Enable EEP parsing in captured packets\n" #endif #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) " -k --keyfile\t RSA private keyfile to decrypt captured packets\n" #endif "\n",PACKAGE); } void version() { printf("%s - %s\n" "Copyright (C) 2013-2018 Irontec S.L.\n" "License GPLv3+: GNU GPL version 3 or later .\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" #ifdef WITH_GNUTLS " * Compiled with GnuTLS support.\n" #endif #ifdef WITH_OPENSSL " * Compiled with OpenSSL support.\n" #endif #ifdef WITH_UNICODE " * Compiled with Wide-character support.\n" #endif #ifdef WITH_PCRE " * Compiled with Perl Compatible regular expressions support.\n" #endif #ifdef USE_IPV6 " * Compiled with IPv6 support.\n" #endif #ifdef USE_EEP " * Compiled with EEP/HEP support.\n" #endif "\nWritten by Ivan Alonso [aka Kaian]\n", PACKAGE, VERSION); } /** * @brief Main function logic * * Parse command line options and start running threads */ int main(int argc, char* argv[]) { int opt, idx, limit, only_calls, no_incomplete, pcap_buffer_size, i; const char *device, *outfile, *text_outfile = NULL; char bpf[512]; #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) const char *keyfile; #endif const char *match_expr; int match_insensitive = 0, match_invert = 0; int no_interface = 0, quiet = 0, rtp_capture = 0, rotate = 0, no_config = 0; vector_t *infiles = vector_create(0, 1); vector_t *indevices = vector_create(0, 1); char *token; // Program options static struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V' }, { "device", required_argument, 0, 'd' }, { "input", required_argument, 0, 'I' }, { "output", required_argument, 0, 'O' }, { "buffer", required_argument, 0, 'B' }, #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) { "keyfile", required_argument, 0, 'k' }, #endif { "calls", no_argument, 0, 'c' }, { "rtp", no_argument, 0, 'r' }, { "limit", required_argument, 0, 'l' }, { "icase", no_argument, 0, 'i' }, { "invert", no_argument, 0, 'v' }, { "no-interface", no_argument, 0, 'N' }, { "dump-config", no_argument, 0, 'D' }, { "rotate", no_argument, 0, 'R' }, { "config", required_argument, 0, 'f' }, { "no-config", no_argument, 0, 'F' }, { "text", required_argument, 0, 'T' }, #ifdef USE_EEP { "eep-listen", required_argument, 0, 'L' }, { "eep-send", required_argument, 0, 'H' }, { "eep-parse", required_argument, 0, 'E' }, #endif { "quiet", no_argument, 0, 'q' }, }; // Parse command line arguments that have high priority opterr = 0; char *options = "hVd:I:O:B:pqtW:k:crl:ivNqDL:H:ERf:FT"; while ((opt = getopt_long(argc, argv, options, long_options, &idx)) != -1) { switch (opt) { case 'h': usage(); return 0; case 'V': version(); return 0; case 'F': no_config = 1; break; default: break; } } // Initialize configuration options init_options(no_config); // Get initial values for configurable arguments device = setting_get_value(SETTING_CAPTURE_DEVICE); outfile = setting_get_value(SETTING_CAPTURE_OUTFILE); pcap_buffer_size = setting_get_intvalue(SETTING_CAPTURE_BUFFER); #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) keyfile = setting_get_value(SETTING_CAPTURE_KEYFILE); #endif limit = setting_get_intvalue(SETTING_CAPTURE_LIMIT); only_calls = setting_enabled(SETTING_SIP_CALLS); no_incomplete = setting_enabled(SETTING_SIP_NOINCOMPLETE); rtp_capture = setting_enabled(SETTING_CAPTURE_RTP); rotate = setting_enabled(SETTING_CAPTURE_ROTATE); // Parse the rest of command line arguments opterr = 0; optind = 1; /* reset getopt index */ while ((opt = getopt_long(argc, argv, options, long_options, &idx)) != -1) { switch (opt) { case 'h': /* handled before with higher priority options */ break; case 'V': /* handled before with higher priority options */ break; case 'd': token = strtok(optarg, ","); while (token) { vector_append(indevices, token); token = strtok(NULL, ","); } break; case 'I': vector_append(infiles, optarg); break; case 'O': outfile = optarg; break; case 'T': text_outfile = optarg; no_interface = 1; setting_set_value(SETTING_CAPTURE_STORAGE, "none"); break; case 'B': if(!(pcap_buffer_size = atoi(optarg))) { fprintf(stderr, "Invalid buffer size.\n"); return 0; } if(!(pcap_buffer_size > 0 && pcap_buffer_size <= 2048)) { fprintf(stderr, "Buffer size not in range (0 < b <= 2048).\n"); return 0; } break; case 'l': if(!(limit = atoi(optarg))) { fprintf(stderr, "Invalid limit value.\n"); return 0; } break; case 'k': #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) keyfile = optarg; break; #else fprintf(stderr, "sngrep is not compiled with SSL support."); exit(1); #endif case 'c': only_calls = 1; setting_set_value(SETTING_SIP_CALLS, SETTING_ON); break; case 'r': rtp_capture = 1; setting_set_value(SETTING_CAPTURE_RTP, SETTING_ON); break; case 'i': match_insensitive++; break; case 'v': match_invert++; break; case 'N': no_interface = 1; setting_set_value(SETTING_CAPTURE_STORAGE, "none"); break; case 'q': quiet = 1; break; case 'D': key_bindings_dump(); settings_dump(); return 0; case 'f': read_options(optarg); break; case 'F': /* handled before with higher priority options */ break; case 'R': rotate = 1; setting_set_value(SETTING_CAPTURE_ROTATE, SETTING_ON); break; // Dark options for dummy ones case 'p': case 't': case 'W': break; case 'L': #ifdef USE_EEP capture_eep_set_server_url(optarg); break; #else fprintf(stderr, "sngrep is not compiled with HEP/EEP support."); exit(1); #endif case 'H': #ifdef USE_EEP capture_eep_set_client_url(optarg); break; #else fprintf(stderr, "sngrep is not compiled with HEP/EEP support."); exit(1); #endif case 'E': #ifdef USE_EEP setting_set_value(SETTING_CAPTURE_EEP, SETTING_ON); break; #else fprintf(stderr, "sngrep is not compiled with HEP/EEP support."); exit(1); #endif case '?': if (strchr(options, optopt)) { fprintf(stderr, "-%c option requires an argument.\n", optopt); } else if (isprint(optopt)) { fprintf(stderr, "Unknown option -%c.\n", optopt); } else { fprintf(stderr, "Unknown option character '\\x%x'.\n", optopt); } return 1; default: break; } } setup_sigterm_handler(); #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) // Set capture decrypt key file capture_set_keyfile(keyfile); // Check if we have a keyfile and is valid if (keyfile && !tls_check_keyfile(keyfile)) { fprintf(stderr, "%s does not contain a valid RSA private key.\n", keyfile); return 1; } #endif // Check if given argument is a file if (argc == 2 && (access(argv[1], F_OK) == 0)) { // Old legacy option to open pcaps without other arguments printf("%s seems to be a file: You forgot -I flag?\n", argv[1]); return 0; } // Initialize SIP Messages Storage sip_init(limit, only_calls, no_incomplete); // Set capture options capture_init(limit, rtp_capture, rotate, pcap_buffer_size); #ifdef USE_EEP // Disable HEP listen when input files are specified in command line, otherwise online and offline packets // will be mixed, and it will be confusing if (vector_count(infiles) != 0) { setting_set_value(SETTING_EEP_LISTEN, SETTING_OFF); } // Initialize EEP if enabled capture_eep_init(); #endif // If no device or files has been specified in command line, use default if (capture_sources_count() == 0 && vector_count(indevices) == 0 && vector_count(infiles) == 0) { token = strdup(device); token = strtok(token, ","); while (token) { vector_append(indevices, token); token = strtok(NULL, ","); } sng_free(token); } // If we have an input file, load it for (i = 0; i < vector_count(infiles); i++) { // Try to load file if (capture_offline(vector_item(infiles, i)) != 0) return 1; } // If we have an input device, load it for (i = 0; i < vector_count(indevices); i++) { // Check if all capture data is valid if (capture_online(vector_item(indevices, i)) != 0) return 1; } if (outfile) { ino_t dump_inode; pcap_dumper_t *dumper = dump_open(outfile, &dump_inode); capture_set_dumper(dumper, dump_inode); } // Remove Input files vector vector_destroy(infiles); // More arguments pending! if (argv[optind]) { // Assume first pending argument is match expression match_expr = argv[optind++]; // Try to build the bpf filter string with the rest memset(bpf, 0, sizeof(bpf)); for (i = optind; i < argc; i++) sprintf(bpf + strlen(bpf), "%s ", argv[i]); // Check if this BPF filter is valid if (capture_set_bpf_filter(bpf) != 0) { // BPF Filter invalid, check incluiding match_expr match_expr = 0; // There is no need to parse all payload at this point // Build the bpf filter string memset(bpf, 0, sizeof(bpf)); for (i = optind - 1; i < argc; i++) sprintf(bpf + strlen(bpf), "%s ", argv[i]); // Check bpf filter is valid again if (capture_set_bpf_filter(bpf) != 0) { fprintf(stderr, "Couldn't install filter %s: %s\n", bpf, capture_last_error()); return 1; } } // Set the capture filter if (match_expr) if (sip_set_match_expression(match_expr, match_insensitive, match_invert)) { fprintf(stderr, "Unable to parse expression %s\n", match_expr); return 1; } } // Start a capture thread if (capture_launch_thread() != 0) { ncurses_deinit(); fprintf(stderr, "Failed to launch capture thread.\n"); return 1; } if (!no_interface) { // Initialize interface ncurses_init(); // This is a blocking call. // Create the first panel and wait for user input ui_create_panel(PANEL_CALL_LIST); ui_wait_for_input(); } else { setbuf(stdout, NULL); while(capture_is_running() && !was_sigterm_received()) { if (!quiet) printf("\rDialog count: %d", sip_calls_count_unrotated()); usleep(500 * 1000); } if (!quiet) printf("\rDialog count: %d\n", sip_calls_count_unrotated()); } if (text_outfile) { vector_iter_t calls; calls = sip_calls_iterator(); sip_call_t *call = NULL; sip_msg_t *msg = NULL; vector_iter_t msgs; FILE *f = NULL; if (!(f = fopen(text_outfile, "w"))) { fprintf(stderr, "Couldn't open sip output file"); return 0; } while ((call = vector_iterator_next(&calls))) { msgs = vector_iterator(call->msgs); // Save SIP message content while ((msg = vector_iterator_next(&msgs))) { save_msg_txt(f, msg); } } fclose(f); } // Capture deinit capture_deinit(); // Deinitialize interface ncurses_deinit(); // Deinitialize configuration options deinit_options(); // Deallocate sip stored messages sip_deinit(); // Leaving! return 0; } sngrep-1.8.2/src/media.c000066400000000000000000000057641464272443000150540ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file media.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in media.h */ #include "config.h" #include #include #include "media.h" #include "rtp.h" #include "util.h" sdp_media_t * media_create(struct sip_msg *msg) { sdp_media_t *media;; // Allocate memory for this media structure if (!(media = sng_malloc(sizeof(sdp_media_t)))) return NULL; // Initialize all fields media->msg = msg; media->formats = vector_create(0, 1); vector_set_destroyer(media->formats, vector_generic_destroyer); return media; } void media_destroyer(void *item) { sdp_media_t *media = (sdp_media_t *) item; if (!item) return; vector_destroy(media->formats); sng_free(media); } void media_set_type(sdp_media_t *media, const char *type) { strncpy(media->type, type, MEDIATYPELEN - 1); } void media_set_address(sdp_media_t *media, address_t addr) { media->address = addr; } void media_set_prefered_format(sdp_media_t *media, uint32_t code) { media->fmtcode = code; } void media_add_format(sdp_media_t *media, uint32_t code, const char *format) { sdp_media_fmt_t *fmt; if (!(fmt = sng_malloc(sizeof(sdp_media_fmt_t)))) return; fmt->id = code; strcpy(fmt->format, format); vector_append(media->formats, fmt); } const char * media_get_format(sdp_media_t *media, uint32_t code) { sdp_media_fmt_t *format; vector_iter_t iter; iter = vector_iterator(media->formats); while ((format = vector_iterator_next(&iter))) { if (format->id == code) return format->format; } return "Unassigned"; } const char * media_get_prefered_format(sdp_media_t *media) { const char *format; // Check if format is standard if ((format = rtp_get_standard_format(media->fmtcode))) { return format; } // Try to get format form SDP payload return media_get_format(media, media->fmtcode); } int media_get_format_code(sdp_media_t *media) { return media->fmtcode; } sngrep-1.8.2/src/media.h000066400000000000000000000051601464272443000150470ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file media.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage call media * */ #ifndef __SNGREP_MEDIA_H_ #define __SNGREP_MEDIA_H_ #include "config.h" #include #include "capture.h" #define MEDIATYPELEN 15 //! Shorter declaration of sdp_media structure typedef struct sdp_media sdp_media_t; //! Shorter declaration of sdp_media_fmt structure typedef struct sdp_media_fmt sdp_media_fmt_t; struct sip_msg; struct sip_call; struct sdp_media_fmt { uint32_t id; char format[50]; }; struct sdp_media { //! SDP Addresses information address_t address; char type[MEDIATYPELEN]; uint32_t fmtcode; //! List of described formats in this media vector_t *formats; //! Message with this SDP content struct sip_msg *msg; }; /** * @brief Allocate memory for a new media structure * * Create a structure for a new message sdp connection data. * * @param msg SIP Message pointer owner of this media * @return new allocated structure */ sdp_media_t * media_create(struct sip_msg *msg); /** * @brief Vector destroyer for media items */ void media_destroyer(void *item); void media_set_type(sdp_media_t *media, const char *type); void media_set_address(sdp_media_t *media, address_t addr); void media_set_prefered_format(sdp_media_t *media, uint32_t code); void media_add_format(sdp_media_t *media, uint32_t code, const char *format); const char * media_get_type(sdp_media_t *media); const char * media_get_format(sdp_media_t *media, uint32_t code); const char * media_get_prefered_format(sdp_media_t *media); int media_get_format_code(sdp_media_t *media); #endif /* __SNGREP_MEDIA_H_ */ sngrep-1.8.2/src/option.c000066400000000000000000000140511464272443000152720ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file option.c * @author Ivan Alonso [aka Kaian] * * @brief Source code of functions defined in option.h * */ #include "config.h" #include #include #include #include #include #include "keybinding.h" #include "option.h" #include "setting.h" #include "util.h" /** * @brief Configuration options array * * Contains all availabe options that can be optionured * @todo make this dynamic */ option_opt_t options[1024]; int optscnt = 0; int init_options(int no_config) { // Custom user conf file char *userconf = NULL; char *rcfile; char pwd[MAX_SETTING_LEN]; // Defualt savepath is current directory if (getcwd(pwd, MAX_SETTING_LEN)) { setting_set_value(SETTING_SAVEPATH, pwd); } // Initialize settings setting_set_value(SETTING_FILTER_METHODS, "REGISTER,INVITE,SUBSCRIBE,NOTIFY,OPTIONS,PUBLISH,MESSAGE,INFO,REFER,UPDATE,KDMQ"); // Add Call list column options set_option_value("cl.column0", "index"); set_option_value("cl.column1", "method"); set_option_value("cl.column2", "sipfrom"); set_option_value("cl.column3", "sipto"); set_option_value("cl.column4", "msgcnt"); set_option_value("cl.column5", "src"); set_option_value("cl.column6", "dst"); set_option_value("cl.column7", "state"); // Done if config file should not be read if(no_config) { return 0; } // Read options from configuration files read_options("/etc/sngreprc"); read_options("/usr/local/etc/sngreprc"); // Get user configuration if ((rcfile = getenv("SNGREPRC"))) { read_options(rcfile); } else if ((rcfile = getenv("HOME"))) { if ((userconf = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) { sprintf(userconf, "%s/.sngreprc", rcfile); read_options(userconf); sng_free(userconf); } } return 0; } void deinit_options() { int i; // Deallocate options memory for (i = 0; i < optscnt; i++) { sng_free(options[i].opt); sng_free(options[i].value); } } int read_options(const char *fname) { FILE *fh; char line[1024], type[20], option[50], value[500]; int id; if (!(fh = fopen(fname, "rt"))) return -1; while (fgets(line, 1024, fh) != NULL) { // Check if this line is a commentary or empty line if (!strlen(line) || *line == '#') continue; // Get configuration option from setting line if (sscanf(line, "%19s %49s %499[^\t\n]", type, option, value) == 3) { if (!strcasecmp(type, "set")) { if ((id = setting_id(option)) >= 0) { setting_set_value(id, value); } else { set_option_value(option, value); } } else if (!strcasecmp(type, "alias")) { set_alias_value(option, value); } else if (!strcasecmp(type, "bind")) { key_bind_action(key_action_id(option), key_from_str(value)); } else if (!strcasecmp(type, "unbind")) { key_unbind_action(key_action_id(option), key_from_str(value)); } } } fclose(fh); return 0; } const char* get_option_value(const char *opt) { int i; for (i = 0; i < optscnt; i++) { if (!strcasecmp(opt, options[i].opt)) { return options[i].value; } } return NULL; } int get_option_int_value(const char *opt) { const char *value; if ((value = get_option_value(opt))) { return atoi(value); } return -1; } void set_option_value(const char *opt, const char *value) { if (!opt || !value) return; int i; if (!get_option_value(opt)) { options[optscnt].type = COLUMN; options[optscnt].opt = strdup(opt); options[optscnt].value = strdup(value); optscnt++; } else { for (i = 0; i < optscnt; i++) { if (!strcasecmp(opt, options[i].opt)) { sng_free(options[i].value); options[i].value = strdup(value); } } } } void set_alias_value(const char *address, const char *alias) { options[optscnt].type = ALIAS; options[optscnt].opt = strdup(address); options[optscnt].value = strdup(alias); optscnt++; } const char * get_alias_value_vs_port(const char *address, uint16_t port) { if (!address) return NULL; int i; char *addr_port = sng_malloc(ADDRESSLEN + 10); sprintf(addr_port, "%s:%d", address, port); for (i = 0; i < optscnt; i++) { if (options[i].type != ALIAS) continue; if (!strcmp(options[i].opt, addr_port) || !strcmp(options[i].opt, address)) { sng_free(addr_port); return options[i].value; } } return address; } const char * get_alias_value(const char *address) { int i; if (!address) return NULL; for (i = 0; i < optscnt; i++) { if (options[i].type != ALIAS) continue; if (!strcmp(options[i].opt, address)) return options[i].value; } return address; } sngrep-1.8.2/src/option.h000066400000000000000000000107621464272443000153040ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file option.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage application settings * * This file contains the functions to manage application settings and * optionuration resource files. Configuration will be parsed in this order, * from less to more priority, so the later will overwrite the previous. * * - Initialization * - \@sysdir\@/sngreprc * - $HOME/.sngreprc * - $SNGREPRC * * This is a basic approach to configuration, but at least a minimun is required * for those who can not see all the list columns or want to disable colours in * every sngrep execution. * */ #ifndef __SNGREP_OPTION_H #define __SNGREP_OPTION_H #include //! Shorter declaration of config_option struct typedef struct config_option option_opt_t; //! Option types enum option_type { COLUMN = 0, ALIAS }; /** * @brief Configurable option structure * * sngrep is optionured by a group of attributes that can be * modified using resource files. */ struct config_option { //! Setting type enum option_type type; //! Name of attribute char *opt; //! Value of attribute char *value; }; /** * @brief Initialize all program options * * This function will give all available settings an initial value. * This values can be overriden using resources files, either from system dir * or user home dir. * * @param no_config Do not load config file if set to 1 * @return 0 in all cases */ int init_options(int no_config); /** * @brief Deallocate options memory * * Deallocate memory used for program configurations */ void deinit_options(); /** * @brief Read optionuration directives from file * * This function will parse passed filenames searching for configuration * directives of sngrep. See documentation for a list of available * directives and attributes * * @param fname Full path configuration file name * @return 0 in case of parse success, -1 otherwise */ int read_options(const char *fname); /** * @brief Get settings option value (string) * * Used in all the program to access the optionurable options of sngrep * Use this function instead of accessing optionuration array. * * @param opt Name of optionurable option * @return configuration option value or NULL if not found */ const char* get_option_value(const char *opt); /** * @brief Get settings option value (int) * * Basically the same as get_option_value converting the result to * integer. * Use this function instead of accessing configuration array. * * @todo -1 is an error! * * @param opt Name of optionurable option * @return option numeric value or -1 in case of error */ int get_option_int_value(const char *opt); /** * @brief Sets a settings option value * * Basic setter for 'set' directive attributes * * @param opt Name of configuration option * @param value Value of configuration option */ void set_option_value(const char *opt, const char *value); /** * @brief Sets an alias for a given address * * @param address IP Address * @param string representing the alias */ void set_alias_value(const char *address, const char *alias); /** * @brief Get alias for a given address (string) * * @param address IP Address * @return configured alias or address if alias not found */ const char * get_alias_value(const char *address); /** * @brief Get alias for a given address and port (string) * * @param address IP Address * @param port port * @return configured alias or address if alias not found */ const char * get_alias_value_vs_port(const char *address, uint16_t port); #endif sngrep-1.8.2/src/packet.c000066400000000000000000000110741464272443000152330ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file packet.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in packet.h * */ #include "config.h" #include #include #include "packet.h" packet_t * packet_create(uint8_t ip_ver, uint8_t proto, address_t src, address_t dst, uint32_t id) { // Create a new packet packet_t *packet; packet = malloc(sizeof(packet_t)); memset(packet, 0, sizeof(packet_t)); packet->ip_version = ip_ver; packet->proto = proto; packet->frames = vector_create(1, 1); packet->ip_id = id; packet->src = src; packet->dst = dst; return packet; } packet_t* packet_clone(packet_t *packet) { packet_t *clone; frame_t *frame; // Create a new packet with the original information clone = packet_create(packet->ip_version, packet->proto, packet->src, packet->dst, packet->ip_id); clone->tcp_seq = packet->tcp_seq; clone->type = packet->type; // Append this frames to the original packet vector_iter_t frames = vector_iterator(packet->frames); while ((frame = vector_iterator_next(&frames))) packet_add_frame(clone, frame->header, frame->data); return clone; } void packet_destroy(packet_t *packet) { frame_t *frame; // Check we have a valid packet pointer if (!packet) return; // Destroy frames vector_iter_t it = vector_iterator(packet->frames); while ((frame = vector_iterator_next(&it))) { free(frame->header); free(frame->data); } // TODO Free remaining packet data vector_set_destroyer(packet->frames, vector_generic_destroyer); vector_destroy(packet->frames); free(packet->payload); free(packet); } void packet_destroyer(void *packet) { packet_destroy((packet_t*) packet); } void packet_free_frames(packet_t *pkt) { frame_t *frame; vector_iter_t it = vector_iterator(pkt->frames); while ((frame = vector_iterator_next(&it))) { free(frame->data); frame->data = NULL; } } packet_t * packet_set_transport_data(packet_t *pkt, uint16_t sport, uint16_t dport) { pkt->src.port = sport; pkt->dst.port = dport; return pkt; } frame_t * packet_add_frame(packet_t *pkt, const struct pcap_pkthdr *header, const u_char *packet) { frame_t *frame = malloc(sizeof(frame_t)); frame->header = malloc(sizeof(struct pcap_pkthdr)); memcpy(frame->header, header, sizeof(struct pcap_pkthdr)); frame->data = malloc(header->caplen); memcpy(frame->data, packet, header->caplen); vector_append(pkt->frames, frame); return frame; } void packet_set_type(packet_t *packet, enum packet_type type) { packet->type = type; } void packet_set_payload(packet_t *packet, u_char *payload, uint32_t payload_len) { // Free previous payload if (packet->payload) free(packet->payload); packet->payload_len = 0; // Set new payload if (payload) { packet->payload = malloc(payload_len + 1); memset(packet->payload, 0, payload_len + 1); memcpy(packet->payload, payload, payload_len); packet->payload[payload_len] = '\0'; packet->payload_len = payload_len; } } uint32_t packet_payloadlen(packet_t *packet) { return packet->payload_len; } u_char * packet_payload(packet_t *packet) { return packet->payload; } struct timeval packet_time(packet_t *packet) { frame_t *first; struct timeval ts = { 0 }; // Return first frame timestamp if (packet && (first = vector_first(packet->frames))) { ts.tv_sec = first->header->ts.tv_sec; ts.tv_usec = first->header->ts.tv_usec; } // Return packe timestamp return ts; } sngrep-1.8.2/src/packet.h000066400000000000000000000110011464272443000152260ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file packet.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage captured packet * * Capture packet contains the information about a one or more packets captured * from network interface or readed from a .PCAP file. * * The binary content of the packet can be stored in one or more frames (if * packet has been reassembled). * */ #ifndef __SNGREP_CAPTURE_PACKET_H #define __SNGREP_CAPTURE_PACKET_H #include #include #include #include "address.h" #include "vector.h" //! Stored packet types enum packet_type { PACKET_SIP_UDP = 0, PACKET_SIP_TCP, PACKET_SIP_TLS, PACKET_SIP_WS, PACKET_SIP_WSS, PACKET_RTP, PACKET_RTCP, }; //! Shorter declaration of packet structure typedef struct packet packet_t; //! Shorter declaration of frame structure typedef struct frame frame_t; /** * @brief Packet capture data. * * One packet can contain more than one frame after assembly. We assume than * one SIP message has one packet (maybe in multiple frames) and that one * packet can only contain one SIP message. */ struct packet { //! IP protocol uint8_t ip_version; //! Transport protocol uint8_t proto; //! Packet type as defined in capture_packet_type enum packet_type type; //! Source address_t src; //! Destination address_t dst; //! Packet IP id #ifdef USE_IPV6 uint32_t ip_id; #else uint16_t ip_id; #endif //! Packet IP fragmentation captured data uint32_t ip_cap_len; //! Packet IP fragmentation expected data uint32_t ip_exp_len; //! Last TCP sequence frame uint32_t tcp_seq; //! PCAP Packet payload when it can not be get from data u_char *payload; //! Payload length uint32_t payload_len; //! Packet frame list (frame_t) vector_t *frames; }; /** * @brief Capture frame. * * One packet can contain multiple frames. This structure is designed to store * the required information to save a packet into a PCAP file. */ struct frame { //! PCAP Frame Header data struct pcap_pkthdr *header; //! PCAP Frame content u_char *data; }; /** * @brief Allocate memory to store new packet data */ packet_t * packet_create(uint8_t ip_ver, uint8_t proto, address_t src, address_t dst, uint32_t id); /** * @brief Deep clone one packet */ packet_t* packet_clone(packet_t *packet); /** * @brief Set Transport layer information */ packet_t * packet_set_transport_data(packet_t *pkt, uint16_t sport, uint16_t dport); /** * @brief Add a new frame to the given packet */ frame_t * packet_add_frame(packet_t *pkt, const struct pcap_pkthdr *header, const u_char *packet); /** * @brief Deallocate a packet structure memory */ void packet_destroy(packet_t *packet); /** * @brief Destroyer function for packet vectors */ void packet_destroyer(void *packet); /** * @brief Free packet frames data. * * This can be used to avoid storing packet payload in memory or disk */ void packet_free_frames(packet_t *pkt); /** * @brief Set packet type */ void packet_set_type(packet_t *packet, enum packet_type type); /** * @brief Set packet payload when it can not be get from packet */ void packet_set_payload(packet_t *packet, u_char *payload, uint32_t payload_len); /** * @brief Getter for capture payload size */ uint32_t packet_payloadlen(packet_t *packet); /** * @brief Getter for capture payload pointer */ u_char * packet_payload(packet_t *packet); /** * @brief Get The timestamp for a packet. */ struct timeval packet_time(packet_t *packet); #endif /* __SNGREP_CAPTURE_PACKET_H */ sngrep-1.8.2/src/rtp.c000066400000000000000000000373311464272443000145750ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file rtp.c * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage RTP captured packets * * This file contains the functions and structures to manage the RTP streams * */ #include "config.h" #include #include #include "rtp.h" #include "sip.h" #include "vector.h" /** * @brief Known RTP encodings */ rtp_encoding_t encodings[] = { { 0, "PCMU/8000", "g711u" }, { 3, "GSM/8000", "gsm" }, { 4, "G723/8000", "g723" }, { 5, "DVI4/8000", "dvi" }, { 6, "DVI4/16000", "dvi" }, { 7, "LPC/8000", "lpc" }, { 8, "PCMA/8000", "g711a" }, { 9, "G722/8000", "g722" }, { 10, "L16/44100", "l16" }, { 11, "L16/44100", "l16" }, { 12, "QCELP/8000", "qcelp" }, { 13, "CN/8000", "cn" }, { 14, "MPA/90000", "mpa" }, { 15, "G728/8000", "g728" }, { 16, "DVI4/11025", "dvi" }, { 17, "DVI4/22050", "dvi" }, { 18, "G729/8000", "g729" }, { 25, "CelB/90000", "celb" }, { 26, "JPEG/90000", "jpeg" }, { 28, "nv/90000", "nv" }, { 31, "H261/90000", "h261" }, { 32, "MPV/90000", "mpv" }, { 33, "MP2T/90000", "mp2t" }, { 34, "H263/90000", "h263" }, { 0, NULL, NULL } }; rtp_stream_t * stream_create(sdp_media_t *media, address_t dst, int type) { rtp_stream_t *stream; // Allocate memory for this stream structure if (!(stream = sng_malloc(sizeof(rtp_stream_t)))) return NULL; // Initialize all fields stream->type = type; stream->media = media; stream->dst = dst; return stream; } rtp_stream_t * stream_complete(rtp_stream_t *stream, address_t src) { stream->src = src; return stream; } void stream_set_format(rtp_stream_t *stream, uint32_t format) { stream->rtpinfo.fmtcode = format; } void stream_add_packet(rtp_stream_t *stream, packet_t *packet) { if (stream->pktcnt == 0) stream->time = packet_time(packet); stream->lasttm = (int) time(NULL); stream->pktcnt++; } uint32_t stream_get_count(rtp_stream_t *stream) { return stream->pktcnt; } struct sip_call * stream_get_call(rtp_stream_t *stream) { if (stream && stream->media && stream->media->msg) return stream->media->msg->call; return NULL; } const char * stream_get_format(rtp_stream_t *stream) { const char *fmt; // Get format for media payload if (!stream || !stream->media) return NULL; // Try to get standard format form code if ((fmt = rtp_get_standard_format(stream->rtpinfo.fmtcode))) return fmt; // Try to get format form SDP payload if ((fmt = media_get_format(stream->media, stream->rtpinfo.fmtcode))) return fmt; // Not found format for this code return NULL; } const char * rtp_get_standard_format(uint32_t code) { int i; // Format from RTP codec id for (i = 0; encodings[i].format; i++) { if (encodings[i].id == code) return encodings[i].format; } return NULL; } rtp_stream_t * rtp_check_packet(packet_t *packet) { address_t src, dst; rtp_stream_t *stream; rtp_stream_t *reverse; u_char format = 0; u_char *payload; uint32_t size, bsize; uint16_t len; struct rtcp_hdr_generic hdr; struct rtcp_hdr_sr hdr_sr; struct rtcp_hdr_xr hdr_xr; struct rtcp_blk_xr blk_xr; struct rtcp_blk_xr_voip blk_xr_voip; // Get packet data payload = packet_payload(packet); size = packet_payloadlen(packet); // Get Addresses from packet src = packet->src; dst = packet->dst; if (data_is_rtp(payload, size) == 0) { // Get RTP payload type format = RTP_PAYLOAD_TYPE(*(payload + 1)); // Find the matching stream stream = rtp_find_stream_format(src, dst, format); // Check if a valid stream has been found if (!stream) return NULL; // We have found a stream, but with different format if (stream_is_complete(stream) && stream->rtpinfo.fmtcode != format) { // Create a new stream for this new format stream = stream_create(stream->media, dst, PACKET_RTP); stream_complete(stream, src); stream_set_format(stream, format); call_add_stream(msg_get_call(stream->media->msg), stream); } // First packet for this stream, set source data if (!(stream_is_complete(stream))) { stream_complete(stream, src); stream_set_format(stream, format); /** * TODO This is a mess. Rework required * * This logic tries to handle a common problem when SDP address and RTP address * doesn't match. In some cases one endpoint waits until RTP data is sent to its * configured port in SDP and replies its RTP to the source ignoring what the other * endpoint has configured in its SDP. * * For such cases, we create streams 'on the fly', when a stream is completed (the * first time its source address is filled), a new stream is created with the * opposite src and dst. * * BUT, there are some cases when this 'reverse' stream should not be created: * - When there already exists a stream with that setup * - When there exists an incomplete stream with that destination (and still no source) * - ... * */ // Check if an stream in the opposite direction exists if (!(reverse = rtp_find_call_stream(stream->media->msg->call, stream->dst, stream->src))) { reverse = stream_create(stream->media, stream->src, PACKET_RTP); stream_complete(reverse, stream->dst); stream_set_format(reverse, format); call_add_stream(msg_get_call(stream->media->msg), reverse); } else { // If the reverse stream has other source configured if (reverse->src.port && !addressport_equals(stream->src, reverse->src)) { if (!(reverse = rtp_find_call_exact_stream(stream->media->msg->call, stream->dst, stream->src))) { // Create a new reverse stream reverse = stream_create(stream->media, stream->src, PACKET_RTP); stream_complete(reverse, stream->dst); stream_set_format(reverse, format); call_add_stream(msg_get_call(stream->media->msg), reverse); } } } } // Add packet to stream stream_add_packet(stream, packet); } else if (data_is_rtcp(payload, size) == 0) { // Find the matching stream if ((stream = rtp_find_rtcp_stream(src, dst))) { // Parse all packet payload headers while ((int32_t) size > 0) { // Check we have at least rtcp generic info if (size < sizeof(struct rtcp_hdr_generic)) break; memcpy(&hdr, payload, sizeof(hdr)); // Check RTP version if (RTP_VERSION(hdr.version) != RTP_VERSION_RFC1889) break; // Header length if ((len = ntohs(hdr.len) * 4 + 4) > size) break; // Check RTCP packet header typ switch (hdr.type) { case RTCP_HDR_SR: // Ensure there is enough payload to fill the header if (size < sizeof(struct rtcp_hdr_sr)) break; // Get Sender Report header memcpy(&hdr_sr, payload, sizeof(hdr_sr)); stream->rtcpinfo.spc = ntohl(hdr_sr.spc); break; case RTCP_HDR_RR: case RTCP_HDR_SDES: case RTCP_HDR_BYE: case RTCP_HDR_APP: case RTCP_RTPFB: case RTCP_PSFB: break; case RTCP_XR: // Ensure there is enough payload to fill the header if (size < sizeof(struct rtcp_hdr_xr)) break; // Get Sender Report Extended header memcpy(&hdr_xr, payload, sizeof(hdr_xr)); bsize = sizeof(hdr_xr); // Read all report blocks while (bsize < ntohs(hdr_xr.len) * 4 + 4) { // Read block header memcpy(&blk_xr, payload + bsize, sizeof(blk_xr)); // Check block type switch (blk_xr.type) { case RTCP_XR_VOIP_METRCS: memcpy(&blk_xr_voip, payload + sizeof(hdr_xr), sizeof(blk_xr_voip)); stream->rtcpinfo.fdiscard = blk_xr_voip.drate; stream->rtcpinfo.flost = blk_xr_voip.lrate; stream->rtcpinfo.mosl = blk_xr_voip.moslq; stream->rtcpinfo.mosc = blk_xr_voip.moscq; break; default: break; } bsize += ntohs(blk_xr.len) * 4 + 4; } break; case RTCP_AVB: case RTCP_RSI: case RTCP_TOKEN: default: // Not handled headers. Skip the rest of this packet size = 0; break; } payload += len; size -= len; } // Add packet to stream stream_complete(stream, src); stream_add_packet(stream, packet); } } else { return NULL; } return stream; } rtp_stream_t * rtp_find_stream_format(address_t src, address_t dst, uint32_t format) { // Structure for RTP packet streams rtp_stream_t *stream; // Check if this is a RTP packet from active calls sip_call_t *call; // Iterator for active calls vector_iter_t calls; // Iterator for call streams vector_iter_t streams; // Candiate stream rtp_stream_t *candidate = NULL; // Get active calls (during conversation) calls = sip_calls_iterator(); vector_iterator_set_current(&calls, vector_iterator_count(&calls)); while ((call = vector_iterator_prev(&calls))) { streams = vector_iterator(call->streams); vector_iterator_set_last(&streams); while ((stream = vector_iterator_prev(&streams))) { // Only look RTP packets if (stream->type != PACKET_RTP) continue; // Stream complete, check source, dst if (stream_is_complete(stream)) { if (addressport_equals(stream->src, src) && addressport_equals(stream->dst, dst)) { // Exact searched stream format if (stream->rtpinfo.fmtcode == format) { return stream; } else { // Matching addresses but different format candidate = stream; } } } else { // Incomplete stream, if dst match is enough if (addressport_equals(stream->dst, dst)) { return stream; } } } } return candidate; } rtp_stream_t * rtp_find_rtcp_stream(address_t src, address_t dst) { // Structure for RTP packet streams rtp_stream_t *stream; // Check if this is a RTP packet from active calls sip_call_t *call; // Iterator for active calls vector_iter_t calls; // Get active calls (during conversation) calls = sip_calls_iterator(); vector_iterator_set_current(&calls, vector_iterator_count(&calls)); while ((call = vector_iterator_prev(&calls))) { // Check if this call has an RTP stream for current packet data if ((stream = rtp_find_call_stream(call, src, dst))) { if (stream->type != PACKET_RTCP) continue; return stream; } } return NULL; } rtp_stream_t * rtp_find_call_stream(struct sip_call *call, address_t src, address_t dst) { rtp_stream_t *stream; vector_iter_t it; // Create an iterator for call streams it = vector_iterator(call->streams); // Look for an incomplete stream with this destination vector_iterator_set_last(&it); while ((stream = vector_iterator_prev(&it))) { if (addressport_equals(dst, stream->dst)) { if (!src.port) { return stream; } else { if (!stream->pktcnt) { return stream; } } } } // Try to look for a complete stream with this destination if (src.port) { return rtp_find_call_exact_stream(call, src, dst); } // Nothing found return NULL; } rtp_stream_t * rtp_find_call_exact_stream(struct sip_call *call, address_t src, address_t dst) { rtp_stream_t *stream; vector_iter_t it; // Create an iterator for call streams it = vector_iterator(call->streams); vector_iterator_set_last(&it); while ((stream = vector_iterator_prev(&it))) { if (addressport_equals(src, stream->src) && addressport_equals(dst, stream->dst)) { return stream; } } // Nothing found return NULL; } int stream_is_older(rtp_stream_t *one, rtp_stream_t *two) { // Yes, you are older than nothing if (!two) return 1; // No, you are not older than yourself if (one == two) return 0; // Otherwise return timeval_is_older(one->time, two->time); } int stream_is_complete(rtp_stream_t *stream) { return (stream->pktcnt != 0); } int stream_is_active(rtp_stream_t *stream) { return ((int) time(NULL) - stream->lasttm <= STREAM_INACTIVE_SECS); } int data_is_rtp(u_char *data, uint32_t len) { u_char pt = RTP_PAYLOAD_TYPE(*(data + 1)); if ((len >= RTP_HDR_LENGTH) && (RTP_VERSION(*data) == RTP_VERSION_RFC1889) && (data[0] > 127 && data[0] < 192) && (pt <= 64 || pt >= 96)) { return 0; } // Not a RTP packet return 1; } int data_is_rtcp(u_char *data, uint32_t len) { struct rtcp_hdr_generic *hdr = (struct rtcp_hdr_generic*) data; if ((len >= RTCP_HDR_LENGTH) && (RTP_VERSION(*data) == RTP_VERSION_RFC1889) && (data[0] > 127 && data[0] < 192) && (hdr->type >= 192 && hdr->type <= 223)) { return 0; } // Not a RTCP packet return 1; } sngrep-1.8.2/src/rtp.h000066400000000000000000000212551464272443000146000ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file rtp.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage rtp captured packets * * @note RTP_VERSION and RTP_PAYLOAD_TYPE macros has been taken from wireshark * source code: packet-rtp.c */ #ifndef __SNGREP_RTP_H #define __SNGREP_RTP_H #include "config.h" #include "capture.h" #include "media.h" // Version is the first 2 bits of the first octet #define RTP_VERSION(octet) ((octet) >> 6) // Payload type is the last 7 bits #define RTP_PAYLOAD_TYPE(octet) ((octet) & 0x7F) // Handled RTP versions #define RTP_VERSION_RFC1889 2 // RTP header length #define RTP_HDR_LENGTH 12 // RTCP common header length #define RTCP_HDR_LENGTH 4 // If stream does not receive a packet in this seconds, we consider it inactive #define STREAM_INACTIVE_SECS 3 // RTCP header types //! http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml enum rtcp_header_types { RTCP_HDR_SR = 200, RTCP_HDR_RR, RTCP_HDR_SDES, RTCP_HDR_BYE, RTCP_HDR_APP, RTCP_RTPFB, RTCP_PSFB, RTCP_XR, RTCP_AVB, RTCP_RSI, RTCP_TOKEN, }; //! http://www.iana.org/assignments/rtcp-xr-block-types/rtcp-xr-block-types.xhtml enum rtcp_xr_block_types { RTCP_XR_LOSS_RLE = 1, RTCP_XR_DUP_RLE, RTCP_XR_PKT_RXTIMES, RTCP_XR_REF_TIME, RTCP_XR_DLRR, RTCP_XR_STATS_SUMRY, RTCP_XR_VOIP_METRCS, RTCP_XR_BT_XNQ, RTCP_XR_TI_VOIP, RTCP_XR_PR_LOSS_RLE, RTCP_XR_MC_ACQ, RTCP_XR_IDMS }; //! Shorter declaration of rtp_encoding structure typedef struct rtp_encoding rtp_encoding_t; //! Shorter declaration of rtp_stream structure typedef struct rtp_stream rtp_stream_t; struct rtp_encoding { uint32_t id; const char *name; const char *format; }; struct rtp_stream { //! Determine stream type uint32_t type; //! Source address address_t src; //! Destination address address_t dst; //! SDP media that setup this stream sdp_media_t *media; //! Packet count for this stream uint32_t pktcnt; //! Time of first received packet of stream struct timeval time; //! Unix timestamp of last received packet int lasttm; // Stream information (depending on type) union { struct { //! Format of first received packet of stre uint32_t fmtcode; } rtpinfo; struct { //! Sender packet count uint32_t spc; //! Fraction lost x/256 uint8_t flost; //! uint8_t discarded x/256 uint8_t fdiscard; //! MOS - listening Quality uint8_t mosl; //! MOS - Conversational Quality uint8_t mosc; } rtcpinfo; }; }; struct rtcp_hdr_generic { //! version (V): 2 bits uint8_t version; //! packet type (PT): 8 bits uint8_t type; //! length: 16 bits uint16_t len; }; struct rtcp_hdr_sr { //! version (V): 2 bits uint8_t version:2; //! padding (P): 1 bit uint8_t padding:1; //! reception report count (RC): 5 bits uint8_t rcount:5; //! packet type (PT): 8 bits uint8_t type; //! length: 16 bits uint16_t len; //! SSRC: 32 bits uint32_t ssrc; //! NTP timestamp: 64 bits uint64_t ntpts; //! RTP timestamp: 32 bits uint32_t rtpts; //! sender's packet count: 32 bits uint32_t spc; //! sender's octet count: 32 bits uint32_t soc; }; struct rtcp_blk_sr { //! SSRC_n (source identifier): 32 bits uint32_t ssrc; //! fraction lost: 8 bits uint8_t flost; //! cumulative number of packets lost: 24 bits struct { uint8_t pl1; uint8_t pl2; uint8_t pl3; } plost; //! extended highest sequence number received: 32 bits uint32_t hseq; //! interarrival jitter: 32 bits uint32_t ijitter; }; struct rtcp_hdr_xr { //! version (V): 2 bits uint8_t version:2; //! padding (P): 1 bit uint8_t padding:1; //! reserved: 5 bits uint8_t reserved:5; //! packet type (PT): 8 bits uint8_t type; //! length: 16 bits uint16_t len; //! SSRC: 32 bits uint32_t ssrc; }; struct rtcp_blk_xr { //! block type (BT): 8 bits uint8_t type; //! type-specific: 8 bits uint8_t specific; //! length: 16 bits uint16_t len; }; struct rtcp_blk_xr_voip { //! block type (BT): 8 bits uint8_t type; //! type-specific: 8 bits uint8_t reserved; //! length: 16 bits uint16_t len; //! SSRC: 32 bits uint32_t ssrc; //! loss rate: 8 bits uint8_t lrate; //! discard rate: 8 bits uint8_t drate; //! burst density: 8 bits uint8_t bdens; //! gap density: 8 bits uint8_t gdens; //! burst duration: 16 bits uint16_t bdur; //! gap duration: 16 bits uint16_t gdur; //! round trip delay: 16 bits uint16_t rtd; //! end system delay: 16 bits uint16_t esd; //! signal level: 8 bits uint8_t slevel; //! noise level: 8 bits uint8_t nlevel; //! residual echo return loss (RERL): 8 bits uint8_t rerl; //! Gmin: 8 bits uint8_t gmin; //! R factor: 8 bits uint8_t rfactor; //! ext. R factor: 8 bits uint8_t xrfactor; //! MOS-LQ: 8 bits uint8_t moslq; //! MOS-CQ: 8 bits uint8_t moscq; //! receiver configuration byte (RX config): 8 bits uint8_t rxc; //! packet loss concealment (PLC): 2 bits uint8_t plc:2; //! jitter buffer adaptive (JBA): 2 bits uint8_t jba:2; //! jitter buffer rate (JB rate): 4 bits uint8_t jbrate:4; //! reserved: 8 bits uint8_t reserved2; //! jitter buffer nominal delay (JB nominal): 16 bits uint16_t jbndelay; //! jitter buffer maximum delay (JB maximum): 16 bits uint16_t jbmdelay; //! jitter buffer absolute maximum delay (JB abs max): 16 bits uint16_t jbadelay; }; rtp_stream_t * stream_create(sdp_media_t *media, address_t dst, int type); rtp_stream_t * stream_complete(rtp_stream_t *stream, address_t src); void stream_set_format(rtp_stream_t *stream, uint32_t format); void stream_add_packet(rtp_stream_t *stream, packet_t *packet); uint32_t stream_get_count(rtp_stream_t *stream); struct sip_call * stream_get_call(rtp_stream_t *stream); const char * stream_get_format(rtp_stream_t *stream); const char * rtp_get_standard_format(uint32_t code); rtp_stream_t * rtp_check_packet(packet_t *packet); rtp_stream_t * rtp_find_stream_format(address_t src, address_t dst, uint32_t format); rtp_stream_t * rtp_find_rtcp_stream(address_t src, address_t dst); rtp_stream_t * rtp_find_call_stream(struct sip_call *call, address_t src, address_t dst); rtp_stream_t * rtp_find_call_exact_stream(struct sip_call *call, address_t src, address_t dst); /** * @brief Check if a message is older than other * * @param one rtp stream pointer * @param two rtp stream pointer * @return 1 if one is older than two * @return 0 if equal or two is older than one */ int stream_is_older(rtp_stream_t *one, rtp_stream_t *two); int stream_is_complete(rtp_stream_t *stream); /** * @brief Determine if a stream is still active * * This simply checks the timestamp of the last received packet of the stream * marking as inactive if it was before STREAM_INACTIVE_SECS ago * * @return 1 if stream is active * @return 0 if stream is inactive */ int stream_is_active(rtp_stream_t *stream); /** * @brief Check if the data is a RTP packet * RFC 5761 Section 4. Distinguishable RTP and RTCP Packets * RFC 5764 Section 5.1.2. Reception (packet demultiplexing) */ int data_is_rtp(u_char *data, uint32_t len); /** * @brief Check if the data is a RTCP packet * RFC 5761 Section 4. Distinguishable RTP and RTCP Packets * RFC 5764 Section 5.1.2. Reception (packet demultiplexing) */ int data_is_rtcp(u_char *data, uint32_t len); #endif /* __SNGREP_RTP_H */ sngrep-1.8.2/src/setting.c000066400000000000000000000247611464272443000154500ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file setting.c * @author Ivan Alonso [aka Kaian] * * @brief Source code of functions defined in setting.h * */ #include "config.h" #include #include #include #include "setting.h" #include "util.h" //! Available configurable settings setting_t settings[SETTING_COUNT] = { { SETTING_BACKGROUND, "background", SETTING_FMT_ENUM, "dark", SETTING_ENUM_BACKGROUND }, { SETTING_COLORMODE, "colormode", SETTING_FMT_ENUM, "request", SETTING_ENUM_COLORMODE }, { SETTING_SYNTAX, "syntax", SETTING_FMT_ENUM, SETTING_ON, SETTING_ENUM_ONOFF }, { SETTING_SYNTAX_TAG, "syntax.tag", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_SYNTAX_BRANCH, "syntax.branch", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_ALTKEY_HINT, "hintkeyalt", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_EXITPROMPT, "exitprompt", SETTING_FMT_ENUM, SETTING_ON, SETTING_ENUM_ONOFF }, { SETTING_CAPTURE_LIMIT, "capture.limit", SETTING_FMT_NUMBER, "20000", NULL }, { SETTING_CAPTURE_DEVICE, "capture.device", SETTING_FMT_STRING, "any", NULL }, { SETTING_CAPTURE_OUTFILE, "capture.outfile", SETTING_FMT_STRING, "", NULL }, { SETTING_CAPTURE_BUFFER, "capture.buffer", SETTING_FMT_NUMBER, "2", NULL }, #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) { SETTING_CAPTURE_KEYFILE, "capture.keyfile", SETTING_FMT_STRING, "", NULL }, { SETTING_CAPTURE_TLSSERVER, "capture.tlsserver", SETTING_FMT_STRING, "", NULL }, #endif #ifdef USE_EEP { SETTING_CAPTURE_EEP, "capture.eep", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, #endif { SETTING_CAPTURE_RTP, "capture.rtp", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_CAPTURE_STORAGE, "capture.storage", SETTING_FMT_ENUM, "memory", SETTING_ENUM_STORAGE }, { SETTING_CAPTURE_ROTATE, "capture.rotate", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_SIP_NOINCOMPLETE, "sip.noincomplete", SETTING_FMT_ENUM, SETTING_ON, SETTING_ENUM_ONOFF }, { SETTING_SIP_HEADER_X_CID, "sip.xcid", SETTING_FMT_STRING, "X-Call-ID|X-CID", NULL }, { SETTING_SIP_CALLS, "sip.calls", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_SAVEPATH, "savepath", SETTING_FMT_STRING, "", NULL }, { SETTING_DISPLAY_ALIAS, "displayalias", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_ALIAS_PORT, "aliasport", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_CL_SCROLLSTEP, "cl.scrollstep", SETTING_FMT_NUMBER, "4", NULL }, { SETTING_CL_COLORATTR, "cl.colorattr", SETTING_FMT_ENUM, SETTING_ON, SETTING_ENUM_ONOFF }, { SETTING_CL_AUTOSCROLL, "cl.autoscroll", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_CL_SORTFIELD, "cl.sortfield", SETTING_FMT_STRING, "index", NULL }, { SETTING_CL_SORTORDER, "cl.sortorder", SETTING_FMT_STRING, "asc", NULL }, { SETTING_CF_FORCERAW, "cf.forceraw", SETTING_FMT_ENUM, SETTING_ON, SETTING_ENUM_ONOFF }, { SETTING_CF_RAWMINWIDTH, "cf.rawminwidth", SETTING_FMT_NUMBER, "40", NULL }, { SETTING_CF_RAWFIXEDWIDTH, "cf.rawfixedwidth", SETTING_FMT_NUMBER, "", NULL }, { SETTING_CF_SPLITCALLID, "cf.splitcallid", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_CF_HIGHTLIGHT, "cf.highlight", SETTING_FMT_ENUM, "bold", SETTING_ENUM_HIGHLIGHT }, { SETTING_CF_SCROLLSTEP, "cf.scrollstep", SETTING_FMT_NUMBER, "4", NULL }, { SETTING_CF_LOCALHIGHLIGHT, "cf.localhighlight", SETTING_FMT_ENUM, SETTING_ON, SETTING_ENUM_ONOFF }, { SETTING_CF_SDP_INFO, "cf.sdpinfo", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_SDP_INFO }, { SETTING_CF_MEDIA, "cf.media", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_MEDIA }, { SETTING_CF_ONLYMEDIA, "cf.onlymedia", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_CF_DELTA, "cf.deltatime", SETTING_FMT_ENUM, SETTING_ON, SETTING_ENUM_ONOFF }, { SETTING_CR_SCROLLSTEP, "cr.scrollstep", SETTING_FMT_NUMBER, "10", NULL }, { SETTING_CR_NON_ASCII, "cr.nonascii", SETTING_FMT_STRING, ".", NULL }, { SETTING_FILTER_PAYLOAD, "filter.payload", SETTING_FMT_STRING, "", NULL }, { SETTING_FILTER_METHODS, "filter.methods", SETTING_FMT_STRING, "", NULL }, #ifdef USE_EEP { SETTING_EEP_SEND, "eep.send", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_EEP_SEND_VER, "eep.send.version", SETTING_FMT_ENUM, "3", SETTING_ENUM_HEPVERSION }, { SETTING_EEP_SEND_ADDR, "eep.send.address", SETTING_FMT_STRING, "127.0.0.1", NULL }, { SETTING_EEP_SEND_PORT, "eep.send.port", SETTING_FMT_NUMBER, "9060", NULL }, { SETTING_EEP_SEND_PASS, "eep.send.pass", SETTING_FMT_STRING, "", NULL }, { SETTING_EEP_SEND_ID, "eep.send.id", SETTING_FMT_NUMBER, "2002", NULL }, { SETTING_EEP_LISTEN, "eep.listen", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, { SETTING_EEP_LISTEN_VER, "eep.listen.version", SETTING_FMT_ENUM, "3", SETTING_ENUM_HEPVERSION }, { SETTING_EEP_LISTEN_ADDR, "eep.listen.address", SETTING_FMT_STRING, "0.0.0.0", NULL }, { SETTING_EEP_LISTEN_PORT, "eep.listen.port", SETTING_FMT_NUMBER, "9060", NULL }, { SETTING_EEP_LISTEN_PASS, "eep.listen.pass", SETTING_FMT_STRING, "", NULL }, { SETTING_EEP_LISTEN_UUID, "eep.listen.uuid", SETTING_FMT_ENUM, SETTING_OFF, SETTING_ENUM_ONOFF }, #endif }; setting_t * setting_by_id(int id) { int i; for (i = 0; i < SETTING_COUNT; i++) { if (id == settings[i].id) return &settings[i]; } return NULL; } setting_t * setting_by_name(const char *name) { int i; for (i = 0; i < SETTING_COUNT; i++) { if (!strcmp(name, settings[i].name)) return &settings[i]; } return NULL; } int setting_id(const char *name) { const setting_t *sett = setting_by_name(name); return (sett) ? sett->id : -1; } const char * setting_name(int id) { const setting_t *sett = setting_by_id(id); return (sett) ? sett->name : NULL; } int setting_format(int id) { const setting_t *sett = setting_by_id(id); return (sett) ? sett->fmt : -1; } const char ** setting_valid_values(int id) { const setting_t *sett = setting_by_id(id); return (sett) ? sett->valuelist : NULL; } const char * setting_get_value(int id) { const setting_t *sett = setting_by_id(id); return (sett && strlen(sett->value)) ? sett->value : NULL; } int setting_get_intvalue(int id) { const setting_t *sett = setting_by_id(id); return (sett && strlen(sett->value)) ? atoi(sett->value) : -1; } void setting_set_value(int id, const char *value) { setting_t *sett = setting_by_id(id); if (sett) { memset(sett->value, 0, sizeof(sett->value)); if (value) { if (strlen(value) < MAX_SETTING_LEN) { strcpy(sett->value, value); } else { fprintf(stderr, "Setting value %s for %s is too long\n", sett->value, sett->name); exit(1); } } } } void setting_set_intvalue(int id, int value) { char strvalue[80]; sprintf(strvalue, "%d", value); setting_set_value(id, strvalue); } int setting_enabled(int id) { return setting_has_value(id, "on") || setting_has_value(id, "yes"); } int setting_disabled(int id) { return setting_has_value(id, "off") || setting_has_value(id, "no"); } int setting_has_value(int id, const char *value) { setting_t *sett = setting_by_id(id); if (sett) { return !strcmp(sett->value, value); } return 0; } void setting_toggle(int id) { setting_t *sett = setting_by_id(id); if (sett) { if (sett->fmt == SETTING_FMT_STRING) return; if (sett->fmt == SETTING_FMT_NUMBER) return; if (sett->fmt == SETTING_FMT_ENUM) { setting_set_value(id, setting_enum_next(id, sett->value)); } } } const char * setting_enum_next(int id, const char *value) { int i; const char *vnext; setting_t *sett; if (!(sett = setting_by_id(id))) return NULL; if (sett->fmt != SETTING_FMT_ENUM) return NULL; if (!sett->valuelist) return NULL; // If setting has no value, set the first one if (!value) return *sett->valuelist; // Iterate through valid values for (i = 0; sett->valuelist[i]; i++) { vnext = sett->valuelist[i + 1]; // If current value matches if (!strcmp(sett->valuelist[i], value)) { return (vnext) ? vnext : setting_enum_next(id, NULL); } } return NULL; } void settings_dump() { int i; for (i = 1; i < SETTING_COUNT; i++) { printf("SettingId: %d\t SettingName: %-20s Value: %s\n", i, setting_name(i), setting_get_value(i)); } } sngrep-1.8.2/src/setting.h000066400000000000000000000136701464272443000154520ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file setting.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage application settings * * This file contains the functions to manage application settings and * optionuration resource files. Configuration will be parsed in this order, * from less to more priority, so the later will overwrite the previous. * * - Initialization * - \@sysdir\@/sngreprc * - $HOME/.sngreprc * - $SNGREPRC * * This is a basic approach to configuration, but at least a minimun is required * for those who can not see all the list columns or want to disable colours in * every sngrep execution. */ #ifndef __SNGREP_SETTING_H #define __SNGREP_SETTING_H //! Max setting value #define MAX_SETTING_LEN 1024 //! Max extra length needed for "/.sngreprc.old" #define RCFILE_EXTRA_LEN 16 //! Shorter declarartion of setting_option struct typedef struct setting_option setting_t; //! Generic setting formats #define SETTING_ENUM_ONOFF (const char *[]){ "on", "off", NULL } #define SETTING_ENUM_YESNO (const char *[]){ "yes", "no", NULL } #define SETTING_ENUM_BACKGROUND (const char *[]){ "dark" , "default", NULL } #define SETTING_ENUM_COLORMODE (const char *[]){ "request", "cseq", "callid", NULL } #define SETTING_ENUM_HIGHLIGHT (const char *[]){ "bold", "reverse", "reversebold", NULL } #define SETTING_ENUM_SDP_INFO (const char *[]){ "off", "first", "full", "compressed", NULL} #define SETTING_ENUM_STORAGE (const char *[]){ "none", "memory", NULL } #define SETTING_ENUM_HEPVERSION (const char *[]){ "2", "3", NULL } #define SETTING_ENUM_MEDIA (const char *[]){ "off", "on", "active", NULL } //! Other useful defines #define SETTING_ON "on" #define SETTING_OFF "off" #define SETTING_YES "yes" #define SETTING_NO "no" #define SETTING_ACTIVE "active" //! Available setting Options enum setting_id { SETTING_BACKGROUND = 0, SETTING_COLORMODE, SETTING_SYNTAX, SETTING_SYNTAX_TAG, SETTING_SYNTAX_BRANCH, SETTING_ALTKEY_HINT, SETTING_EXITPROMPT, SETTING_CAPTURE_LIMIT, SETTING_CAPTURE_DEVICE, SETTING_CAPTURE_OUTFILE, SETTING_CAPTURE_BUFFER, #if defined(WITH_GNUTLS) || defined(WITH_OPENSSL) SETTING_CAPTURE_KEYFILE, SETTING_CAPTURE_TLSSERVER, #endif #ifdef USE_EEP SETTING_CAPTURE_EEP, #endif SETTING_CAPTURE_RTP, SETTING_CAPTURE_STORAGE, SETTING_CAPTURE_ROTATE, SETTING_SIP_NOINCOMPLETE, SETTING_SIP_HEADER_X_CID, SETTING_SIP_CALLS, SETTING_SAVEPATH, SETTING_DISPLAY_ALIAS, SETTING_ALIAS_PORT, SETTING_CL_SCROLLSTEP, SETTING_CL_COLORATTR, SETTING_CL_AUTOSCROLL, SETTING_CL_SORTFIELD, SETTING_CL_SORTORDER, SETTING_CF_FORCERAW, SETTING_CF_RAWMINWIDTH, SETTING_CF_RAWFIXEDWIDTH, SETTING_CF_SPLITCALLID, SETTING_CF_HIGHTLIGHT, SETTING_CF_SCROLLSTEP, SETTING_CF_LOCALHIGHLIGHT, SETTING_CF_SDP_INFO, SETTING_CF_MEDIA, SETTING_CF_ONLYMEDIA, SETTING_CF_DELTA, SETTING_CR_SCROLLSTEP, SETTING_CR_NON_ASCII, SETTING_FILTER_PAYLOAD, SETTING_FILTER_METHODS, #ifdef USE_EEP SETTING_EEP_SEND, SETTING_EEP_SEND_VER, SETTING_EEP_SEND_ADDR, SETTING_EEP_SEND_PORT, SETTING_EEP_SEND_PASS, SETTING_EEP_SEND_ID, SETTING_EEP_LISTEN, SETTING_EEP_LISTEN_VER, SETTING_EEP_LISTEN_ADDR, SETTING_EEP_LISTEN_PORT, SETTING_EEP_LISTEN_PASS, SETTING_EEP_LISTEN_UUID, #endif SETTING_COUNT }; //! Available setting formats enum setting_fmt { SETTING_FMT_STRING = 0, SETTING_FMT_NUMBER, SETTING_FMT_ENUM, }; /** * @brief Configurable Setting structure */ struct setting_option { //! Setting id enum setting_id id; //! Setting name const char *name; //! Setting format enum setting_fmt fmt; //! Value of the setting char value[MAX_SETTING_LEN]; //! Compa separated valid values const char **valuelist; }; setting_t * setting_by_id(int id); setting_t * setting_by_name(const char *name); /** * @brief Return the setting id of a given string * * @param name String representing configurable setting * @return setting id or -1 if setting is not found */ int setting_id(const char *name); /** * @brief Return string representing given setting id * * @param id Setting id from settings enum * @return string representation of setting or NULL */ const char * setting_name(int id); int setting_format(int id); const char ** setting_valid_values(int id); const char* setting_get_value(int id); int setting_get_intvalue(int id); void setting_set_value(int id, const char *value); void setting_set_intvalue(int id, int value); int setting_enabled(int id); int setting_disabled(int id); int setting_has_value(int id, const char *value); void setting_toggle(int id); const char * setting_enum_next(int id, const char *value); /** * @brief Dump configuration settings * * This function will print to stdout configuration settings * after reading system/local/user resource files (in that order). * */ void settings_dump(); #endif /* __SNGREP_SETTING_H */ sngrep-1.8.2/src/sip.c000066400000000000000000000765751464272443000146000ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file sip.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in sip.h */ #include "config.h" #include #include #include #include #include #include #include "sip.h" #include "option.h" #include "setting.h" #include "filter.h" /** * @brief Linked list of parsed calls * * All parsed calls will be added to this list, only accesible from * this awesome structure, so, keep it thread-safe. */ sip_call_list_t calls = { 0 }; /* @brief list of methods and responses */ sip_code_t sip_codes[] = { { SIP_METHOD_REGISTER, "REGISTER" }, { SIP_METHOD_INVITE, "INVITE" }, { SIP_METHOD_SUBSCRIBE, "SUBSCRIBE" }, { SIP_METHOD_NOTIFY, "NOTIFY" }, { SIP_METHOD_OPTIONS, "OPTIONS" }, { SIP_METHOD_PUBLISH, "PUBLISH" }, { SIP_METHOD_KDMQ, "KDMQ" }, { SIP_METHOD_MESSAGE, "MESSAGE" }, { SIP_METHOD_CANCEL, "CANCEL" }, { SIP_METHOD_BYE, "BYE" }, { SIP_METHOD_ACK, "ACK" }, { SIP_METHOD_PRACK, "PRACK" }, { SIP_METHOD_INFO, "INFO" }, { SIP_METHOD_REFER, "REFER" }, { SIP_METHOD_UPDATE, "UPDATE" }, { 100, "100 Trying" }, { 180, "180 Ringing" }, { 181, "181 Call is Being Forwarded" }, { 182, "182 Queued" }, { 183, "183 Session Progress" }, { 199, "199 Early Dialog Terminated" }, { 200, "200 OK" }, { 202, "202 Accepted" }, { 204, "204 No Notification" }, { 300, "300 Multiple Choices" }, { 301, "301 Moved Permanently" }, { 302, "302 Moved Temporarily" }, { 305, "305 Use Proxy" }, { 380, "380 Alternative Service" }, { 400, "400 Bad Request" }, { 401, "401 Unauthorized" }, { 402, "402 Payment Required" }, { 403, "403 Forbidden" }, { 404, "404 Not Found" }, { 405, "405 Method Not Allowed" }, { 406, "406 Not Acceptable" }, { 407, "407 Proxy Authentication Required" }, { 408, "408 Request Timeout" }, { 409, "409 Conflict" }, { 410, "410 Gone" }, { 411, "411 Length Required" }, { 412, "412 Conditional Request Failed" }, { 413, "413 Request Entity Too Large" }, { 414, "414 Request-URI Too Long" }, { 415, "415 Unsupported Media Type" }, { 416, "416 Unsupported URI Scheme" }, { 417, "417 Unknown Resource-Priority" }, { 420, "420 Bad Extension" }, { 421, "421 Extension Required" }, { 422, "422 Session Interval Too Small" }, { 423, "423 Interval Too Brief" }, { 424, "424 Bad Location Information" }, { 428, "428 Use Identity Header" }, { 429, "429 Provide Referrer Identity" }, { 430, "430 Flow Failed" }, { 433, "433 Anonymity Disallowed" }, { 436, "436 Bad Identity-Info" }, { 437, "437 Unsupported Certificate" }, { 438, "438 Invalid Identity Header" }, { 439, "439 First Hop Lacks Outbound Support" }, { 470, "470 Consent Needed" }, { 480, "480 Temporarily Unavailable" }, { 481, "481 Call/Transaction Does Not Exist" }, { 482, "482 Loop Detected." }, { 483, "483 Too Many Hops" }, { 484, "484 Address Incomplete" }, { 485, "485 Ambiguous" }, { 486, "486 Busy Here" }, { 487, "487 Request Terminated" }, { 488, "488 Not Acceptable Here" }, { 489, "489 Bad Event" }, { 491, "491 Request Pending" }, { 493, "493 Undecipherable" }, { 494, "494 Security Agreement Required" }, { 500, "500 Server Internal Error" }, { 501, "501 Not Implemented" }, { 502, "502 Bad Gateway" }, { 503, "503 Service Unavailable" }, { 504, "504 Server Time-out" }, { 505, "505 Version Not Supported" }, { 513, "513 Message Too Large" }, { 580, "580 Precondition Failure" }, { 600, "600 Busy Everywhere" }, { 603, "603 Decline" }, { 604, "604 Does Not Exist Anywhere" }, { 606, "606 Not Acceptable" }, { -1 , NULL }, }; void sip_init(int limit, int only_calls, int no_incomplete) { int match_flags, reg_rule_len, reg_rule_err; char reg_rule[SIP_ATTR_MAXLEN]; const char *setting = NULL; // Store capture limit calls.limit = limit; calls.only_calls = only_calls; calls.ignore_incomplete = no_incomplete; calls.last_index = 0; calls.call_count_unrotated = 0; // Create a vector to store calls calls.list = vector_create(200, 50); vector_set_destroyer(calls.list, call_destroyer); vector_set_sorter(calls.list, sip_list_sorter); calls.active = vector_create(10, 10); // Create hash table for callid search calls.callids = htable_create(calls.limit); // Set default sorting field if (sip_attr_from_name(setting_get_value(SETTING_CL_SORTFIELD)) >= 0) { calls.sort.by = sip_attr_from_name(setting_get_value(SETTING_CL_SORTFIELD)); calls.sort.asc = (!strcmp(setting_get_value(SETTING_CL_SORTORDER), "asc")); } else { // Fallback to default sorting field calls.sort.by = SIP_ATTR_CALLINDEX; calls.sort.asc = true; } // Initialize payload parsing regexp match_flags = REG_EXTENDED | REG_ICASE | REG_NEWLINE; regcomp(&calls.reg_method, "^([a-zA-Z]+) [a-zA-Z]+:.* SIP/2.0[ ]*\r", match_flags & ~REG_NEWLINE); regcomp(&calls.reg_callid, "^(Call-ID|i):[ ]*([^ ]+)[ ]*\r$", match_flags); setting = setting_get_value(SETTING_SIP_HEADER_X_CID); reg_rule_len = strlen(setting) + 22; if (reg_rule_len >= SIP_ATTR_MAXLEN) { setting = "X-Call-ID|X-CID"; reg_rule_len = strlen(setting) + 22; fprintf(stderr, "%s setting too long, using default.\n", setting_name(SETTING_SIP_HEADER_X_CID)); } snprintf(reg_rule, reg_rule_len, "^(%s):[ ]*([^ ]+)[ ]*\r$", setting); reg_rule_err = regcomp(&calls.reg_xcallid, reg_rule, match_flags); if(reg_rule_err != 0) { regerror(reg_rule_err, &calls.reg_xcallid, reg_rule, SIP_ATTR_MAXLEN); regfree(&calls.reg_xcallid); fprintf(stderr, "%s setting produces regex compilation error: %s" "using default value instead\n", setting_name(SETTING_SIP_HEADER_X_CID), reg_rule); regcomp(&calls.reg_xcallid, "^(X-Call-ID|X-CID):[ ]*([^ ]+)[ ]*\r$", match_flags); } regcomp(&calls.reg_response, "^SIP/2.0[ ]*(([0-9]{3}) [^\r]*)[ ]*\r", match_flags & ~REG_NEWLINE); regcomp(&calls.reg_cseq, "^CSeq:[ ]*([0-9]{1,10}) .+\r$", match_flags); regcomp(&calls.reg_from, "^(From|f):[ ]*[^:]*:(([^@>]+)@?[^\r>;]+)", match_flags); regcomp(&calls.reg_to, "^(To|t):[ ]*[^:]*:(([^@>]+)@?[^\r>;]+)", match_flags); regcomp(&calls.reg_valid, "^([A-Z]+ [a-zA-Z]+:|SIP/2.0 [0-9]{3})", match_flags & ~REG_NEWLINE); regcomp(&calls.reg_cl, "^(Content-Length|l):[ ]*([0-9]+)[ ]*\r$", match_flags); regcomp(&calls.reg_body, "\r\n\r\n(.*)", match_flags & ~REG_NEWLINE); regcomp(&calls.reg_reason, "Reason:[ ]*[^\r]*;text=\"([^\r]+)\"", match_flags); regcomp(&calls.reg_warning, "Warning:[ ]*([0-9]*)", match_flags); } void sip_deinit() { // Remove all calls sip_calls_clear(); // Remove Call-id hash table htable_destroy(calls.callids); // Remove calls vector vector_destroy(calls.list); vector_destroy(calls.active); // Deallocate regular expressions regfree(&calls.reg_method); regfree(&calls.reg_callid); regfree(&calls.reg_xcallid); regfree(&calls.reg_response); regfree(&calls.reg_cseq); regfree(&calls.reg_from); regfree(&calls.reg_to); regfree(&calls.reg_valid); regfree(&calls.reg_cl); regfree(&calls.reg_body); regfree(&calls.reg_reason); regfree(&calls.reg_warning); } char * sip_get_callid(const char* payload, char *callid) { regmatch_t pmatch[3]; // Try to get Call-ID from payload if (regexec(&calls.reg_callid, payload, 3, pmatch, 0) == 0) { int input_len = pmatch[2].rm_eo - pmatch[2].rm_so; // Ensure the copy length does not exceed MAX_CALLID_SIZE - 1 if (input_len > MAX_CALLID_SIZE - 1) { input_len = MAX_CALLID_SIZE - 1; } // Copy the matching part of payload strncpy(callid, payload + pmatch[2].rm_so, input_len); callid[input_len] = '\0'; } return callid; } char * sip_get_xcallid(const char *payload, char *xcallid) { regmatch_t pmatch[3]; // Try to get X-Call-ID from payload if (regexec(&calls.reg_xcallid, (const char *)payload, 3, pmatch, 0) == 0) { int input_len = pmatch[2].rm_eo - pmatch[2].rm_so; // Ensure the copy length does not exceed MAX_XCALLID_SIZE - 1 if (input_len > MAX_XCALLID_SIZE - 1) { input_len = MAX_XCALLID_SIZE - 1; } strncpy(xcallid, (const char *)payload + pmatch[2].rm_so, input_len); xcallid[input_len] = '\0'; } return xcallid; } int sip_validate_packet(packet_t *packet) { uint32_t plen = packet_payloadlen(packet); u_char payload[MAX_SIP_PAYLOAD]; regmatch_t pmatch[4]; char cl_header[MAX_CONTENT_LENGTH_SIZE]; int content_len; int bodylen; // Max SIP payload allowed if (plen == 0 || plen > MAX_SIP_PAYLOAD) return VALIDATE_NOT_SIP; // Get payload from packet(s) memset(payload, 0, MAX_SIP_PAYLOAD); memcpy(payload, packet_payload(packet), plen); // Initialize variables memset(cl_header, 0, sizeof(cl_header)); // Check if the first line follows SIP request or response format if (regexec(&calls.reg_valid, (const char *) payload, 2, pmatch, 0) != 0) { // Not a SIP message AT ALL return VALIDATE_NOT_SIP; } // Check if we have Content Length header if (regexec(&calls.reg_cl, (const char *) payload, 4, pmatch, 0) != 0) { // Not a SIP message or not complete return VALIDATE_PARTIAL_SIP; } // Ensure the copy length does not exceed MAX_CONTENT_LENGTH_SIZE - 1 int cl_match_len = pmatch[2].rm_eo - pmatch[2].rm_so; if (cl_match_len > MAX_CONTENT_LENGTH_SIZE - 1) { cl_match_len = MAX_CONTENT_LENGTH_SIZE - 1; } strncpy(cl_header, (const char *)payload + pmatch[2].rm_so, cl_match_len); cl_header[cl_match_len] = '\0'; // Ensuring null termination content_len = atoi(cl_header); // Check if we have Body separator field if (regexec(&calls.reg_body, (const char *) payload, 2, pmatch, 0) != 0) { // Not a SIP message or not complete return VALIDATE_PARTIAL_SIP; } // Get the SIP message body length bodylen = (int) pmatch[1].rm_eo - pmatch[1].rm_so; // The SDP body of the SIP message ends in another packet if (content_len > bodylen) { return VALIDATE_PARTIAL_SIP; } if (content_len < bodylen) { // Check body ends with '\r\n' if (payload[pmatch[1].rm_so + content_len - 1] != '\n') return VALIDATE_NOT_SIP; if (payload[pmatch[1].rm_so + content_len - 2] != '\r') return VALIDATE_NOT_SIP; // We got more than one SIP message in the same packet packet_set_payload(packet, payload, pmatch[1].rm_so + content_len); return VALIDATE_MULTIPLE_SIP; } // We got all the SDP body of the SIP message return VALIDATE_COMPLETE_SIP; } sip_msg_t * sip_check_packet(packet_t *packet) { sip_msg_t *msg; sip_call_t *call; char callid[MAX_CALLID_SIZE], xcallid[MAX_XCALLID_SIZE]; u_char payload[MAX_SIP_PAYLOAD]; bool newcall = false; // Max SIP payload allowed if (packet->payload_len > MAX_SIP_PAYLOAD) return NULL; // Initialize local variables memset(callid, 0, sizeof(callid)); memset(xcallid, 0, sizeof(xcallid)); // Get payload from packet(s) memset(payload, 0, MAX_SIP_PAYLOAD); memcpy(payload, packet_payload(packet), packet_payloadlen(packet)); // Get the Call-ID of this message if (!sip_get_callid((const char*) payload, callid)) return NULL; // Create a new message from this data if (!(msg = msg_create())) return NULL; // Get Method and request for the following checks // There is no need to parse all payload at this point // If no response or request code is found, this is not a SIP message if (!sip_get_msg_reqresp(msg, payload)) { // Deallocate message memory msg_destroy(msg); return NULL; } // Find the call for this msg if (!(call = sip_find_by_callid(callid))) { // Check if payload matches expression if (!sip_check_match_expression((const char*) payload)) goto skip_message; // User requested only INVITE starting dialogs if (calls.only_calls && msg->reqresp != SIP_METHOD_INVITE) goto skip_message; // Only create a new call if the first msg // is a request message in the following gorup if (calls.ignore_incomplete && msg->reqresp > SIP_METHOD_MESSAGE) goto skip_message; // Get the Call-ID of this message sip_get_xcallid((const char*) payload, xcallid); // Rotate call list if limit has been reached if (calls.limit == sip_calls_count()) sip_calls_rotate(); // Create the call if not found if (!(call = call_create(callid, xcallid))) goto skip_message; // Add this Call-Id to hash table htable_insert(calls.callids, call->callid, call); // Set call index call->index = ++calls.last_index; // Mark this as a new call newcall = true; } // At this point we know we're handling an interesting SIP Packet msg->packet = packet; // Always parse first call message if (call_msg_count(call) == 0) { // Parse SIP payload sip_parse_msg_payload(msg, payload); // If this call has X-Call-Id, append it to the parent call if (strlen(call->xcallid)) { call_add_xcall(sip_find_by_callid(call->xcallid), call); } } // Add the message to the call call_add_message(call, msg); // check if message is a retransmission call_msg_retrans_check(msg); if (call_is_invite(call)) { // Parse media data sip_parse_msg_media(msg, payload); // Update Call State call_update_state(call, msg); // Parse extra fields sip_parse_extra_headers(msg, payload); // Check if this call should be in active call list if (call_is_active(call)) { if (sip_call_is_active(call)) { vector_append(calls.active, call); } } else { if (sip_call_is_active(call)) { vector_remove(calls.active, call); } } } if (newcall) { // Append this call to the call list vector_append(calls.list, call); ++calls.call_count_unrotated; } // Mark the list as changed calls.changed = true; // Return the loaded message return msg; skip_message: // Deallocate message memory msg_destroy(msg); return NULL; } bool sip_calls_has_changed() { bool changed = calls.changed; calls.changed = false; return changed; } int sip_calls_count() { return vector_count(calls.list); } int sip_calls_count_unrotated() { return calls.call_count_unrotated; } vector_iter_t sip_calls_iterator() { return vector_iterator(calls.list); } vector_iter_t sip_active_calls_iterator() { return vector_iterator(calls.active); } bool sip_call_is_active(sip_call_t *call) { return vector_index(calls.active, call) != -1; } vector_t * sip_calls_vector() { return calls.list; } vector_t * sip_active_calls_vector() { return calls.active; } sip_stats_t sip_calls_stats() { sip_stats_t stats; vector_iter_t it = vector_iterator(calls.list); // Total number of calls without filtering stats.total = vector_iterator_count(&it); // Total number of calls after filtering vector_iterator_set_filter(&it, filter_check_call); stats.displayed = vector_iterator_count(&it); return stats; } sip_call_t * sip_find_by_index(int index) { return vector_item(calls.list, index); } sip_call_t * sip_find_by_callid(const char *callid) { return htable_find(calls.callids, callid); } int sip_get_msg_reqresp(sip_msg_t *msg, const u_char *payload) { regmatch_t pmatch[3]; char resp_str[SIP_ATTR_MAXLEN]; char reqresp[SIP_ATTR_MAXLEN]; char cseq[11]; const char *resp_def; // Initialize variables memset(pmatch, 0, sizeof(pmatch)); memset(resp_str, 0, sizeof(resp_str)); memset(reqresp, 0, sizeof(reqresp)); // If not already parsed if (!msg->reqresp) { // Method & CSeq if (regexec(&calls.reg_method, (const char *)payload, 2, pmatch, 0) == 0) { if ((int)(pmatch[1].rm_eo - pmatch[1].rm_so) >= SIP_ATTR_MAXLEN) { strncpy(reqresp, "", 12); } else { sprintf(reqresp, "%.*s", (int) (pmatch[1].rm_eo - pmatch[1].rm_so), payload + pmatch[1].rm_so); } } // CSeq if (regexec(&calls.reg_cseq, (char*)payload, 2, pmatch, 0) == 0) { sprintf(cseq, "%.*s", (int)(pmatch[1].rm_eo - pmatch[1].rm_so), payload + pmatch[1].rm_so); msg->cseq = atoi(cseq); } // Response code if (regexec(&calls.reg_response, (const char *)payload, 3, pmatch, 0) == 0) { if ((int)(pmatch[1].rm_eo - pmatch[1].rm_so) >= SIP_ATTR_MAXLEN) { strncpy(resp_str, "", 12); } else { sprintf(resp_str, "%.*s", (int) (pmatch[1].rm_eo - pmatch[1].rm_so), payload + pmatch[1].rm_so); } if ((int)(pmatch[2].rm_eo - pmatch[2].rm_so) >= SIP_ATTR_MAXLEN) { strncpy(resp_str, "", 12); } else { sprintf(reqresp, "%.*s", (int) (pmatch[2].rm_eo - pmatch[2].rm_so), payload + pmatch[2].rm_so); } } // Get Request/Response Code msg->reqresp = sip_method_from_str(reqresp); // For response codes, check if the text matches the default if (!msg_is_request(msg)) { resp_def = sip_method_str(msg->reqresp); if (!resp_def || strcmp(resp_def, resp_str)) { msg->resp_str = strdup(resp_str); } } } return msg->reqresp; } const char * sip_get_msg_reqresp_str(sip_msg_t *msg) { // Check if code has non-standard text if (msg->resp_str) { return msg->resp_str; } else { return sip_method_str(msg->reqresp); } } sip_msg_t * sip_parse_msg(sip_msg_t *msg) { if (msg && !msg->cseq) { sip_parse_msg_payload(msg, (u_char*) msg_get_payload(msg)); } return msg; } int sip_parse_msg_payload(sip_msg_t *msg, const u_char *payload) { regmatch_t pmatch[4]; // From if (regexec(&calls.reg_from, (const char *)payload, 4, pmatch, 0) == 0) { msg->sip_from = sng_malloc((int)pmatch[2].rm_eo - pmatch[2].rm_so + 1); strncpy(msg->sip_from, (const char *)payload + pmatch[2].rm_so, (int)pmatch[2].rm_eo - pmatch[2].rm_so); } else { // Malformed From Header msg->sip_from = sng_malloc(12); strncpy(msg->sip_from, "", 12); } // To if (regexec(&calls.reg_to, (const char *)payload, 4, pmatch, 0) == 0) { msg->sip_to = sng_malloc((int)pmatch[2].rm_eo - pmatch[2].rm_so + 1); strncpy(msg->sip_to, (const char *)payload + pmatch[2].rm_so, (int)pmatch[2].rm_eo - pmatch[2].rm_so); } else { // Malformed To Header msg->sip_to = sng_malloc(12); strncpy(msg->sip_to, "", 12); } return 0; } void sip_parse_msg_media(sip_msg_t *msg, const u_char *payload) { #define ADD_STREAM(stream) \ if (stream) { \ if (!rtp_find_call_stream(call, src, stream->dst)) { \ call_add_stream(call, stream); \ } else { \ sng_free(stream); \ stream = NULL; \ } \ } address_t dst, src = { }; rtp_stream_t *rtp_stream = NULL, *rtcp_stream = NULL, *msg_rtp_stream = NULL; char media_type[MEDIATYPELEN + 1] = { }; char media_format[30] = { }; char address[ADDRESSLEN + 1] = { }; uint32_t media_fmt_pref; uint32_t media_fmt_code; sdp_media_t *media = NULL; char *payload2, *tofree, *line; sip_call_t *call = msg_get_call(msg); // If message is retrans, there's no need to parse the payload again if (msg->retrans) { // Use the media vector from the original message msg->medias = msg->retrans->medias; return; } // Parse each line of payload looking for sdp information tofree = payload2 = strdup((char*)payload); while ((line = strsep(&payload2, "\r\n")) != NULL) { // Check if we have a media string if (!strncmp(line, "m=", 2)) { if (sscanf(line, "m=%" STRINGIFY(MEDIATYPELEN) "s %hu RTP/%*s %u", media_type, &dst.port, &media_fmt_pref) == 3 || sscanf(line, "m=%" STRINGIFY(MEDIATYPELEN) "s %hu UDP/%*s %u", media_type, &dst.port, &media_fmt_pref) == 3) { // Add streams from previous 'm=' line to the call ADD_STREAM(msg_rtp_stream); ADD_STREAM(rtp_stream); ADD_STREAM(rtcp_stream); // Create a new media structure for this message if ((media = media_create(msg))) { media_set_type(media, media_type); media_set_address(media, dst); media_set_prefered_format(media, media_fmt_pref); msg_add_media(msg, media); /** * From SDP we can only guess destination address port. RTP Capture proccess * will determine when the stream has been completed, getting source address * and port of the stream. */ // Create a new stream with this destination address:port // Create RTP stream with source of message as destination address msg_rtp_stream = stream_create(media, dst, PACKET_RTP); msg_rtp_stream->dst = msg->packet->src; msg_rtp_stream->dst.port = dst.port; // Create RTP stream rtp_stream = stream_create(media, dst, PACKET_RTP); // Create RTCP stream rtcp_stream = stream_create(media, dst, PACKET_RTCP); rtcp_stream->dst.port++; } } } // Check if we have a connection string if (!strncmp(line, "c=", 2)) { if (sscanf(line, "c=IN IP%*c %" STRINGIFY(ADDRESSLEN) "s", address)) { strncpy(dst.ip, address, ADDRESSLEN - 1); if (media) { media_set_address(media, dst); strcpy(rtp_stream->dst.ip, dst.ip); strcpy(rtcp_stream->dst.ip, dst.ip); } } } // Check if we have attribute format string if (!strncmp(line, "a=rtpmap:", 9)) { if (media && sscanf(line, "a=rtpmap:%u %29[^ ]", &media_fmt_code, media_format)) { media_add_format(media, media_fmt_code, media_format); } } // Check if we have attribute format RTCP port if (!strncmp(line, "a=rtcp:", 7) && rtcp_stream) { sscanf(line, "a=rtcp:%hu", &rtcp_stream->dst.port); } } // Add streams from last 'm=' line to the call ADD_STREAM(msg_rtp_stream); ADD_STREAM(rtp_stream); ADD_STREAM(rtcp_stream); sng_free(tofree); #undef ADD_STREAM } void sip_parse_extra_headers(sip_msg_t *msg, const u_char *payload) { regmatch_t pmatch[4]; char warning[MAX_WARNING_SIZE]; // Reason text if (regexec(&calls.reg_reason, (const char *)payload, 2, pmatch, 0) == 0) { msg->call->reasontxt = sng_malloc((int)pmatch[1].rm_eo - pmatch[1].rm_so + 1); strncpy(msg->call->reasontxt, (const char *)payload + pmatch[1].rm_so, (int)pmatch[1].rm_eo - pmatch[1].rm_so); } // Warning code if (regexec(&calls.reg_warning, (const char *)payload, 2, pmatch, 0) == 0) { // Ensure the copy length does not exceed MAX_WARNING_SIZE - 1 int warning_match_len = pmatch[1].rm_eo - pmatch[1].rm_so; if (warning_match_len > MAX_WARNING_SIZE - 1) { warning_match_len = MAX_WARNING_SIZE - 1; } strncpy(warning, (const char *)payload + pmatch[1].rm_so, warning_match_len); warning[warning_match_len] = '\0'; // Ensuring null termination msg->call->warning = atoi(warning); } } void sip_calls_clear() { // Create again the callid hash table htable_destroy(calls.callids); calls.callids = htable_create(calls.limit); // Remove all items from vector vector_clear(calls.list); vector_clear(calls.active); } void sip_calls_clear_soft() { // Create again the callid hash table htable_destroy(calls.callids); calls.callids = htable_create(calls.limit); // Repopulate list applying current filter calls.list = vector_copy_if(sip_calls_vector(), filter_check_call); calls.active = vector_copy_if(sip_active_calls_vector(), filter_check_call); // Repopulate callids based on filtered list sip_call_t *call; vector_iter_t it = vector_iterator(calls.list); while ((call = vector_iterator_next(&it))) { htable_insert(calls.callids, call->callid, call); } } void sip_calls_rotate() { sip_call_t *call; vector_iter_t it = vector_iterator(calls.list); while ((call = vector_iterator_next(&it))) { if (!call->locked) { // Remove from callids hash htable_remove(calls.callids, call->callid); // Remove first call from active and call lists vector_remove(calls.active, call); vector_remove(calls.list, call); return; } } } int sip_set_match_expression(const char *expr, int insensitive, int invert) { // Store expression text calls.match_expr = expr; // Set invert flag calls.match_invert = invert; #ifdef WITH_PCRE const char *re_err = NULL; int32_t err_offset; int32_t pflags = PCRE_UNGREEDY | PCRE_DOTALL; if (insensitive) pflags |= PCRE_CASELESS; // Check if we have a valid expression calls.match_regex = pcre_compile(expr, pflags, &re_err, &err_offset, 0); return calls.match_regex == NULL; #elif defined(WITH_PCRE2) int re_err = 0; PCRE2_SIZE err_offset = 0; uint32_t pflags = PCRE2_UNGREEDY | PCRE2_CASELESS; if (insensitive) pflags |= PCRE2_CASELESS; // Check if we have a valid expression calls.match_regex = pcre2_compile((PCRE2_SPTR) expr, PCRE2_ZERO_TERMINATED, pflags, &re_err, &err_offset, NULL); return calls.match_regex == NULL; #else int cflags = REG_EXTENDED; // Case insensitive requested if (insensitive) cflags |= REG_ICASE; // Check the expresion is a compilable regexp return regcomp(&calls.match_regex, expr, cflags) != 0; #endif } const char * sip_get_match_expression() { return calls.match_expr; } int sip_check_match_expression(const char *payload) { // Everything matches when there is no match if (!calls.match_expr) return 1; #ifdef WITH_PCRE switch (pcre_exec(calls.match_regex, 0, payload, strlen(payload), 0, 0, 0, 0)) { case PCRE_ERROR_NOMATCH: return 1 == calls.match_invert; } return 0 == calls.match_invert; #elif defined(WITH_PCRE2) pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(calls.match_regex, NULL); int ret = pcre2_match(calls.match_regex, (PCRE2_SPTR) payload, (PCRE2_SIZE) strlen(payload), 0, 0, match_data, NULL); pcre2_match_data_free(match_data); if (ret == PCRE2_ERROR_NOMATCH) { return 1 == calls.match_invert; } return 0 == calls.match_invert; #else // Check if payload matches the given expresion return (regexec(&calls.match_regex, payload, 0, NULL, 0) == calls.match_invert); #endif } const char * sip_method_str(int method) { int i; // Standard method for (i = 0; sip_codes[i].id > 0; i++) { if (method == sip_codes[i].id) return sip_codes[i].text; } return NULL; } int sip_method_from_str(const char *method) { int i; // Standard method for (i = 0; sip_codes[i].id > 0; i++) { if (!strcmp(method, sip_codes[i].text)) return sip_codes[i].id; } return atoi(method); } const char * sip_transport_str(int transport) { switch(transport) { case PACKET_SIP_UDP: return "UDP"; case PACKET_SIP_TCP: return "TCP"; case PACKET_SIP_TLS: return "TLS"; case PACKET_SIP_WS: return "WS"; case PACKET_SIP_WSS: return "WSS"; } return ""; } char * sip_get_msg_header(sip_msg_t *msg, char *out) { char from_addr[80], to_addr[80], time[80], date[80]; // Source and Destination address msg_get_attribute(msg, SIP_ATTR_DATE, date); msg_get_attribute(msg, SIP_ATTR_TIME, time); msg_get_attribute(msg, SIP_ATTR_SRC, from_addr); msg_get_attribute(msg, SIP_ATTR_DST, to_addr); // Get msg header if (setting_enabled(SETTING_DISPLAY_ALIAS)) { sprintf(out, "%s %s %s -> %s", date, time, get_alias_value(from_addr), get_alias_value(to_addr)); } else { sprintf(out, "%s %s %s -> %s", date, time, from_addr, to_addr); } return out; } void sip_set_sort_options(sip_sort_t sort) { calls.sort = sort; sip_sort_list(); } sip_sort_t sip_sort_options() { return calls.sort; } void sip_sort_list() { // Cloning the vector automatically sorts it vector_t *clone = vector_clone(calls.list); // FIXME FIXME FIXME // There should be a way to destroy the vector without calling the // vector destroyer for each item... vector_set_destroyer(calls.list, NULL); vector_destroy(calls.list); // The new sorted list calls.list = clone; } void sip_list_sorter(vector_t *vector, void *item) { sip_call_t *prev, *cur = (sip_call_t *)item; int count = vector_count(vector); int i; // First item is alway sorted if (vector_count(vector) == 1) return; for (i = count - 2 ; i >= 0; i--) { // Get previous item prev = vector_item(vector, i); // Check if the item is already in a sorted position int cmp = call_attr_compare(cur, prev, calls.sort.by); if ((calls.sort.asc && cmp > 0) || (!calls.sort.asc && cmp < 0)) { vector_insert(vector, item, i + 1); return; } } // Put this item at the begining of the vector vector_insert(vector, item, 0); } sngrep-1.8.2/src/sip.h000066400000000000000000000300551464272443000145640ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file sip.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage SIP calls and messages * * This file contains the functions and structures to manage the SIP calls and * messages. */ #ifndef __SNGREP_SIP_H #define __SNGREP_SIP_H #include "config.h" #include #include #ifdef WITH_PCRE #include #elif defined(WITH_PCRE2) #include #endif #include "sip_call.h" #include "vector.h" #include "hash.h" #define MAX_SIP_PAYLOAD 10240 #define MAX_CALLID_SIZE 1024 #define MAX_XCALLID_SIZE 1024 #define MAX_CONTENT_LENGTH_SIZE 10 #define MAX_WARNING_SIZE 10 //! Shorter declaration of sip_call_list structure typedef struct sip_call_list sip_call_list_t; //! Shorter declaration of sip codes structure typedef struct sip_code sip_code_t; //! Shorter declaration of sip stats typedef struct sip_stats sip_stats_t; //! Shorter declaration of sip sort typedef struct sip_sort sip_sort_t; //! SIP Methods enum sip_methods { SIP_METHOD_REGISTER = 1, SIP_METHOD_INVITE, SIP_METHOD_SUBSCRIBE, SIP_METHOD_NOTIFY, SIP_METHOD_OPTIONS, SIP_METHOD_PUBLISH, SIP_METHOD_INFO, SIP_METHOD_REFER, SIP_METHOD_UPDATE, SIP_METHOD_KDMQ, SIP_METHOD_MESSAGE, SIP_METHOD_CANCEL, SIP_METHOD_BYE, SIP_METHOD_ACK, SIP_METHOD_PRACK, }; //! Return values for sip_validate_packet enum validate_result { VALIDATE_NOT_SIP = -1, VALIDATE_PARTIAL_SIP = 0, VALIDATE_COMPLETE_SIP = 1, VALIDATE_MULTIPLE_SIP = 2 }; /** * @brief Different Request/Response codes in SIP Protocol */ struct sip_code { int id; const char *text; }; /** * @brief Structure to store dialog stats */ struct sip_stats { //! Total number of captured dialogs int total; //! Total number of displayed dialogs after filtering int displayed; }; /** * @brief Sorting information for the sip list */ struct sip_sort { //! Sort call list by this attribute enum sip_attr_id by; //! Sory by attribute ascending bool asc; }; /** * @brief call structures head list * * This structure acts as header of calls list */ struct sip_call_list { //! List of all captured calls vector_t *list; //! List of active captured calls vector_t *active; //! Changed flag. For interface optimal updates bool changed; //! Sort call list following this options sip_sort_t sort; //! Last created id int last_index; //! Call-Ids hash table htable_t *callids; //! Full count of all captured calls, regardless of rotation int call_count_unrotated; // Max call limit int limit; //! Only store dialogs starting with INVITE int only_calls; //! Only store dialogs starting with some Methods int ignore_incomplete; //! match expression text const char *match_expr; #ifdef WITH_PCRE //! Compiled match expression pcre *match_regex; #elif defined(WITH_PCRE2) //! Compiled match expression pcre2_code *match_regex; #else //! Compiled match expression regex_t match_regex; #endif //! Invert match expression result int match_invert; //! Regexp for payload matching regex_t reg_method; regex_t reg_callid; regex_t reg_xcallid; regex_t reg_response; regex_t reg_cseq; regex_t reg_from; regex_t reg_to; regex_t reg_valid; regex_t reg_cl; regex_t reg_body; regex_t reg_reason; regex_t reg_warning; }; /** * @brief Initialize SIP Storage structures * * @param limit Max number of Stored calls * @param only_calls only parse dialogs starting with INVITE * @param no_incomplete only parse dialog starting with some methods */ void sip_init(int limit, int only_calls, int no_incomplete); /** * @brief Deallocate all memory used for SIP calls */ void sip_deinit(); /** * @brief Parses Call-ID header of a SIP message payload * * Mainly used to check if a payload contains a callid. * * @param payload SIP message payload * @param callid Character array to store callid * @return callid parsed from Call-ID header */ char * sip_get_callid(const char* payload, char *callid); /** * @brief Parses X-Call-ID header of a SIP message payload * * Mainly used to check if a payload contains a xcallid. * * @param payload SIP message payload * @paramx callid Character array to store callid * @return xcallid parsed from Call-ID header */ char * sip_get_xcallid(const char* payload, char *xcallid); /** * @brief Validate the packet payload is a SIP message * * This function will validate the payload of a packet to determine if it * contains a full SIP packet. In order to be valid, the SIP packet must * have a initial line with Request or Respones, a Content-Length header * field and a body matching the length of that header. * * This function will only be used for TCP captured packets, when the * Content-Length header field is a MUST. * * @param packet TCP assembled packet structure * @return -1 if the packet first line doesn't match a SIP message * @return 0 if the packet contains SIP but is not yet complete * @return 1 if the packet is a complete SIP message */ int sip_validate_packet(packet_t *packet); /** * @brief Loads a new message from raw header/payload * * Use this function to convert raw data into call and message * structures. This is mainly used to load data from a file or * * @param packet Packet structure pointer * @return a SIP msg structure pointer */ sip_msg_t * sip_check_packet(packet_t *packet); /** * @brief Return if the call list has changed * * Check if the call list has changed since the last time * this function was invoked. We consider list has changed when a new * call has been added or removed. * * @return true if list has changed, false otherwise */ bool sip_calls_has_changed(); /** * @brief Getter for calls linked list size * * @return how many calls are linked in the list */ int sip_calls_count(); /** * @brief Getter for full count of calls since program start * * @return full number of calls since program start, regardless of rotation */ int sip_calls_count_unrotated(); /** * @brief Return an iterator of call list */ vector_iter_t sip_calls_iterator(); /** * @brief Return an iterator of call list * * We consider 'active' calls those that are willing to have * an rtp stream that will receive new packets. * */ vector_iter_t sip_active_calls_iterator(); /** * @brief Return if a call is in active's call vector * * @param call Call to be searched * @return TRUE if call is active, FALSE otherwise */ bool sip_call_is_active(sip_call_t *call); /** * @brief Return the call list */ vector_t * sip_calls_vector(); /** * @brief Return the active call list */ vector_t * sip_active_calls_vector(); /** * @brief Return stats from call list * * @param total Total calls processed * @param displayed number of calls matching filters */ sip_stats_t sip_calls_stats(); /** * @brief Find a call structure in calls linked list given a call index * * @param index Position of the call in the calls vector * @return pointer to the sip_call structure found or NULL */ sip_call_t * sip_find_by_index(int index); /** * @brief Find a call structure in calls linked list given an callid * * @param callid Call-ID Header value * @return pointer to the sip_call structure found or NULL */ sip_call_t * sip_find_by_callid(const char *callid); /** * @brief Parse extra fields only for dialogs strarting with invite * * @note This function assumes the msg is already part of a call * * @param msg SIP message structure * @param payload SIP message payload */ void sip_parse_extra_headers(sip_msg_t *msg, const u_char *payload); /** * @brief Remove al calls * * This funtion will clear the call list invoking the destroy * function for each one. */ void sip_calls_clear(); /** * @brief Remove al calls * * This funtion will clear the call list of calls other than ones * fitting the current filter */ void sip_calls_clear_soft(); /** * @brief Remove first call in the call list * * This function removes the first call in the calls vector avoiding * reaching the capture limit. */ void sip_calls_rotate(); /** * @brief Get message Request/Response code * * Parse Payload to get Message Request/Response code. * * @param msg SIP Message to be parsed * @return numeric representation of Request/ResponseCode */ int sip_get_msg_reqresp(sip_msg_t *msg, const u_char *payload); /** * @brief Get full Response code (including text) * * */ const char * sip_get_msg_reqresp_str(sip_msg_t *msg); /** * @brief Parse SIP Message payload if not parsed * * This function can be used for delayed parsing. This way * the message will only use the minimun required memory * to store basic information. * * @param msg SIP message structure * @return parsed message */ sip_msg_t * sip_parse_msg(sip_msg_t *msg); /** * @brief Parse SIP Message payload to fill sip_msg structe * * Parse the payload content to set message attributes. * * @param msg SIP message structure * @param payload SIP message payload * @return 0 in all cases */ int sip_parse_msg_payload(sip_msg_t *msg, const u_char *payload); /** * @brief Parse SIP Message payload for SDP media streams * * Parse the payload content to get SDP information * * @param msg SIP message structure * @return 0 in all cases */ void sip_parse_msg_media(sip_msg_t *msg, const u_char *payload); /** * @brief Set Capture Matching expression * * @param expr String containing matching expression * @param insensitive 1 for case insensitive matching * @param invert 1 for reverse matching * @return 0 if expresion is valid, 1 otherwise */ int sip_set_match_expression(const char *expr, int insensitive, int invert); /** * @brief Get Capture Matching expression * * @return String containing matching expression */ const char * sip_get_match_expression(); /** * @brief Checks if a given payload matches expression * * @param payload Packet payload * @return 1 if matches, 0 otherwise */ int sip_check_match_expression(const char *payload); /** * @brief Get String value for a Method * * @param method One of the methods defined in @sip_codes * @return a string representing the method text */ const char * sip_method_str(int method); /* * @brief Get String value of Transport */ const char * sip_transport_str(int transport); /** * @brief Converts Request Name or Response code to number * * If the argument is a method, the corresponding value of @sip_methods * will be returned. If a Resposne code, the numeric value of the code * will be returned. * * @param a string representing the Request/Resposne code text * @return numeric representation of Request/Response code */ int sip_method_from_str(const char *method); /** * @brief Get summary of message header data * * For raw prints, it's handy to have the ngrep header style message * data. * * @param msg SIP message * @param out pointer to allocated memory to contain the header output * @returns pointer to out */ char * sip_get_msg_header(sip_msg_t *msg, char *out); void sip_set_sort_options(sip_sort_t sort); sip_sort_t sip_sort_options(); void sip_sort_list(); void sip_list_sorter(vector_t *vector, void *item); #endif sngrep-1.8.2/src/sip_attr.c000066400000000000000000000130251464272443000156070ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file sip_attr.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in sip_attr.h * */ #include "config.h" #include #include #include #include "option.h" #include "sip_attr.h" #include "util.h" #include "curses/ui_manager.h" static sip_attr_hdr_t attrs[SIP_ATTR_COUNT] = { { SIP_ATTR_CALLINDEX, "index", "Idx", "Call Index", 4 }, { SIP_ATTR_SIPFROM, "sipfrom", NULL, "SIP From", 25 }, { SIP_ATTR_SIPFROMUSER, "sipfromuser", NULL, "SIP From User", 20 }, { SIP_ATTR_SIPTO, "sipto", NULL, "SIP To", 25 }, { SIP_ATTR_SIPTOUSER, "siptouser", NULL, "SIP To User", 20 }, { SIP_ATTR_SRC, "src", NULL, "Source", 22 }, { SIP_ATTR_DST, "dst", NULL, "Destination", 22 }, { SIP_ATTR_CALLID, "callid", NULL, "Call-ID", 50 }, { SIP_ATTR_XCALLID, "xcallid", NULL, "X-Call-ID", 50 }, { SIP_ATTR_DATE, "date", NULL, "Date", 10 }, { SIP_ATTR_TIME, "time", NULL, "Time", 8 }, { SIP_ATTR_METHOD, "method", NULL, "Method", 10, sip_attr_color_method }, { SIP_ATTR_TRANSPORT, "transport", "Trans", "Transport", 3 }, { SIP_ATTR_MSGCNT, "msgcnt", "Msgs", "Message Count", 5 }, { SIP_ATTR_CALLSTATE, "state", NULL, "Call State", 10, sip_attr_color_state }, { SIP_ATTR_CONVDUR, "convdur", "ConvDur", "Conversation Duration", 7 }, { SIP_ATTR_TOTALDUR, "totaldur", "TotalDur", "Total Duration", 8 }, { SIP_ATTR_REASON_TXT, "reason", "Reason Text", "Reason Text", 25 }, { SIP_ATTR_WARNING, "warning", "Warning", "Warning code", 4 } }; sip_attr_hdr_t * sip_attr_get_header(enum sip_attr_id id) { return &attrs[id]; } const char * sip_attr_get_description(enum sip_attr_id id) { sip_attr_hdr_t *header; if ((header = sip_attr_get_header(id))) { return header->desc; } return NULL; } const char * sip_attr_get_title(enum sip_attr_id id) { sip_attr_hdr_t *header; if ((header = sip_attr_get_header(id))) { if (header->title) return header->title; return header->desc; } return NULL; } const char * sip_attr_get_name(enum sip_attr_id id) { sip_attr_hdr_t *header; if ((header = sip_attr_get_header(id))) { return header->name; } return NULL; } int sip_attr_get_width(enum sip_attr_id id) { sip_attr_hdr_t *header; if ((header = sip_attr_get_header(id))) { return header->dwidth; } return 0; } int sip_attr_from_name(const char *name) { int i; for (i = 0; i < SIP_ATTR_COUNT; i++) { if (!strcasecmp(name, attrs[i].name)) { return attrs[i].id; } } return -1; } int sip_attr_get_color(int id, const char *value) { sip_attr_hdr_t *header; if (!setting_enabled(SETTING_CL_COLORATTR)) return 0; if ((header = sip_attr_get_header(id))) { if (header->color) { return header->color(value); } } return 0; } int sip_attr_color_method(const char *value) { switch (sip_method_from_str(value)) { case SIP_METHOD_INVITE: return COLOR_PAIR(CP_RED_ON_DEF) | A_BOLD; case SIP_METHOD_NOTIFY: return COLOR_PAIR(CP_YELLOW_ON_DEF); case SIP_METHOD_OPTIONS: return COLOR_PAIR(CP_YELLOW_ON_DEF); case SIP_METHOD_REGISTER: return COLOR_PAIR(CP_MAGENTA_ON_DEF); case SIP_METHOD_SUBSCRIBE: return COLOR_PAIR(CP_BLUE_ON_DEF); case SIP_METHOD_KDMQ: return COLOR_PAIR(CP_CYAN_ON_DEF) | A_BOLD; default: return 0; } } int sip_attr_color_state(const char *value) { if (!strcmp(value, call_state_to_str(SIP_CALLSTATE_CALLSETUP))) return COLOR_PAIR(CP_YELLOW_ON_DEF); if (!strcmp(value, call_state_to_str(SIP_CALLSTATE_INCALL))) return COLOR_PAIR(CP_BLUE_ON_DEF); if (!strcmp(value, call_state_to_str(SIP_CALLSTATE_COMPLETED))) return COLOR_PAIR(CP_GREEN_ON_DEF); if (!strcmp(value, call_state_to_str(SIP_CALLSTATE_CANCELLED))) return COLOR_PAIR(CP_RED_ON_DEF); if (!strcmp(value, call_state_to_str(SIP_CALLSTATE_REJECTED))) return COLOR_PAIR(CP_RED_ON_DEF); if (!strcmp(value, call_state_to_str(SIP_CALLSTATE_BUSY))) return COLOR_PAIR(CP_MAGENTA_ON_DEF); if (!strcmp(value, call_state_to_str(SIP_CALLSTATE_DIVERTED))) return COLOR_PAIR(CP_CYAN_ON_DEF); return 0; } sngrep-1.8.2/src/sip_attr.h000066400000000000000000000126461464272443000156240ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file sip_attr.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage SIP calls and messages attributes */ #ifndef __SNGREP_SIP_ATTR_H #define __SNGREP_SIP_ATTR_H #include "config.h" #include "vector.h" //! Max attribute length #define SIP_ATTR_MAXLEN 255 //! Shorter declaration of sip_attr_hdr structure typedef struct sip_attr_hdr sip_attr_hdr_t; //! Shorter declaration of sip_attr structure typedef struct sip_attr sip_attr_t; /** * @brief Available SIP Attributes * * This enum contains the list of available attributes * a call or message can have. */ enum sip_attr_id { //! Call index in the Call List SIP_ATTR_CALLINDEX = 0, //! SIP Message From: header SIP_ATTR_SIPFROM, //! SIP Message User of From: header SIP_ATTR_SIPFROMUSER, //! SIP Message To: header SIP_ATTR_SIPTO, //! SIP Message User of To: header SIP_ATTR_SIPTOUSER, //! Package IP source address and port SIP_ATTR_SRC, //! Package IP destination address and port SIP_ATTR_DST, //! SIP Message Call-ID header SIP_ATTR_CALLID, //! SIP Message X-Call-ID or X-CID header SIP_ATTR_XCALLID, //! SIP Message Date SIP_ATTR_DATE, //! SIP Message Time SIP_ATTR_TIME, //! SIP Message Method or Response code SIP_ATTR_METHOD, //! SIP Message transport SIP_ATTR_TRANSPORT, //! SIP Call message counter SIP_ATTR_MSGCNT, //! SIP Call state SIP_ATTR_CALLSTATE, //! Conversation duration SIP_ATTR_CONVDUR, //! Total call duration SIP_ATTR_TOTALDUR, //! Text from SIP Reason header SIP_ATTR_REASON_TXT, //! Warning Header SIP_ATTR_WARNING, //! SIP Attribute count SIP_ATTR_COUNT }; /** * @brief Attribute header data * * This sctructure contains the information about the * attribute, description, id, type and so. It's the * static information of the attributed shared by all * attributes pointer to its type. * */ struct sip_attr_hdr { //! Attribute id enum sip_attr_id id; //! Attribute name char *name; //! Attribute column title char *title; //! Attribute description char *desc; //! Attribute default display width int dwidth; //! This function determines the color of this attribute in CallList int (*color)(const char *value); }; /** * @brief Attribute storage struct */ struct sip_attr { //! Attribute id enum sip_attr_id id; //! Attribute value char *value; }; /** * @brief Get the header information of an Attribute * * Retrieve header data from attribute list * * @param id Attribute id * @return Attribute header data structure pointer */ sip_attr_hdr_t * sip_attr_get_header(enum sip_attr_id id); /** * @brief Get Attribute description * * Retrieve description of given attribute from its * header structure. * * @param id Attribute id * @return Attribute description from its header */ const char * sip_attr_get_description(enum sip_attr_id id); /** * @brief Get Attribute title * * Retrieve title of given attribute from its * header structure. * * @param id Attribute id * @return Attribute title from its header */ const char * sip_attr_get_title(enum sip_attr_id id); /** * @brief Get Attribute name * * Retrieve name of given attribute from its * header structure. * * @param id Attribute id * @return Attribute name from its header */ const char * sip_attr_get_name(enum sip_attr_id id); /** * @brief Get Attribute prefered display width * * @param id Attribute id * @return prefered attribute width */ int sip_attr_get_width(enum sip_attr_id id); /** * @brief Get Attribute id from its name * * Retrieve attribute id of the given attribute name. * * @param name Attribut name * @return Attribute id or -1 if not found */ int sip_attr_from_name(const char *name); /** * @brief Determine the color of the attribute in Call List * * Return the color pair to display an attribute in * call list or -1 if default color must be used. */ int sip_attr_get_color(int id, const char *value); /** * @brief Determine the color of the attribute in Call List * * This function can be used to show the Method attribute * with different colours in Call List. */ int sip_attr_color_method(const char *value); /** * @brief Determine the color of the attribute in Call List * * This function can be used to show the state attribute * with different colours in Call List. */ int sip_attr_color_state(const char *value); #endif /* __SNGREP_SIP_ATTR_H */ sngrep-1.8.2/src/sip_call.c000066400000000000000000000253131464272443000155530ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file sip_call.c * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage SIP call data * * This file contains the functions and structure to manage SIP call data * */ #include "sip_call.h" #include "sip.h" #include "setting.h" sip_call_t * call_create(char *callid, char *xcallid) { sip_call_t *call; // Initialize a new call structure if (!(call = sng_malloc(sizeof(sip_call_t)))) return NULL; // Create a vector to store call messages call->msgs = vector_create(2, 2); vector_set_destroyer(call->msgs, msg_destroyer); // Create an empty vector to store rtp packets if (setting_enabled(SETTING_CAPTURE_RTP)) { call->rtp_packets = vector_create(0, 40); vector_set_destroyer(call->rtp_packets, packet_destroyer); } // Create an empty vector to strore stream data call->streams = vector_create(0, 2); vector_set_destroyer(call->streams, vector_generic_destroyer); // Create an empty vector to store x-calls call->xcalls = vector_create(0, 1); // Initialize call filter status call->filtered = -1; // Set message callid call->callid = strdup(callid); call->xcallid = strdup(xcallid); return call; } void call_destroy(sip_call_t *call) { // Remove all call messages vector_destroy(call->msgs); // Remove all call streams vector_destroy(call->streams); // Remove all call rtp packets vector_destroy(call->rtp_packets); // Remove all xcalls vector_destroy(call->xcalls); // Deallocate call memory sng_free(call->callid); sng_free(call->xcallid); sng_free(call->reasontxt); sng_free(call); } void call_destroyer(void *call) { call_destroy((sip_call_t*)call); } bool call_has_changed(sip_call_t *call) { return call->changed; } void call_add_message(sip_call_t *call, sip_msg_t *msg) { // Set the message owner msg->call = call; // Put this msg at the end of the msg list msg->index = vector_append(call->msgs, msg); // Flag this call as changed call->changed = true; } void call_add_stream(sip_call_t *call, rtp_stream_t *stream) { // Store stream vector_append(call->streams, stream); // Flag this call as changed call->changed = true; } void call_add_rtp_packet(sip_call_t *call, packet_t *packet) { // Store packet vector_append(call->rtp_packets, packet); // Flag this call as changed call->changed = true; } int call_msg_count(sip_call_t *call) { return vector_count(call->msgs); } int call_is_active(sip_call_t *call) { return (call->state == SIP_CALLSTATE_CALLSETUP || call->state == SIP_CALLSTATE_INCALL); } int call_is_invite(sip_call_t *call) { sip_msg_t *first; if ((first = vector_first(call->msgs))) return (first->reqresp == SIP_METHOD_INVITE); return 0; } void call_msg_retrans_check(sip_msg_t *msg) { sip_msg_t *prev = NULL; vector_iter_t it; // Get previous message in call with same origin and destination it = vector_iterator(msg->call->msgs); vector_iterator_set_current(&it, vector_index(msg->call->msgs, msg)); while ((prev = vector_iterator_prev(&it))) { if (addressport_equals(prev->packet->src, msg->packet->src) && addressport_equals(prev->packet->dst, msg->packet->dst)) break; } // Store the flag that determines if message is retrans if (prev && !strcasecmp(msg_get_payload(msg), msg_get_payload(prev))) { msg->retrans = prev; } } sip_msg_t * call_msg_with_media(sip_call_t *call, address_t dst) { sip_msg_t *msg; sdp_media_t *media; vector_iter_t itmsg; vector_iter_t itmedia; // Get message with media address configured in given dst itmsg = vector_iterator(call->msgs); while ((msg = vector_iterator_next(&itmsg))) { itmedia = vector_iterator(msg->medias); while ((media = vector_iterator_next(&itmedia))) { if (addressport_equals(dst, media->address)) { return msg; } } } return NULL; } void call_update_state(sip_call_t *call, sip_msg_t *msg) { int reqresp; if (!call_is_invite(call)) return; // Get current message Method / Response Code reqresp = msg->reqresp; // If this message is actually a call, get its current state if (call->state) { if (call->state == SIP_CALLSTATE_CALLSETUP) { if (reqresp == SIP_METHOD_ACK && call->invitecseq == msg->cseq) { // Alice and Bob are talking call->state = SIP_CALLSTATE_INCALL; call->cstart_msg = msg; } else if (reqresp == SIP_METHOD_CANCEL) { // Alice is not in the mood call->state = SIP_CALLSTATE_CANCELLED; } else if ((reqresp == 480) || (reqresp == 486) || (reqresp == 600 )) { // Bob is busy call->state = SIP_CALLSTATE_BUSY; } else if (reqresp > 400 && call->invitecseq == msg->cseq) { // Bob is not in the mood call->state = SIP_CALLSTATE_REJECTED; } else if (reqresp == 181 || reqresp == 302 || reqresp == 301) { // Bob has diversion call->state = SIP_CALLSTATE_DIVERTED; } } else if (call->state == SIP_CALLSTATE_INCALL) { if (reqresp == SIP_METHOD_BYE) { // Thanks for all the fish! call->state = SIP_CALLSTATE_COMPLETED; call->cend_msg = msg; } } else if (reqresp == SIP_METHOD_INVITE && call->state != SIP_CALLSTATE_INCALL) { // Call is being setup (after proper authentication) call->invitecseq = msg->cseq; call->state = SIP_CALLSTATE_CALLSETUP; } } else { // This is actually a call if (reqresp == SIP_METHOD_INVITE) { call->invitecseq = msg->cseq; call->state = SIP_CALLSTATE_CALLSETUP; } } } const char * call_get_attribute(sip_call_t *call, enum sip_attr_id id, char *value) { sip_msg_t *first, *last; if (!call) return NULL; switch (id) { case SIP_ATTR_CALLINDEX: sprintf(value, "%d", call->index); break; case SIP_ATTR_CALLID: sprintf(value, "%s", call->callid); break; case SIP_ATTR_XCALLID: sprintf(value, "%s", call->xcallid); break; case SIP_ATTR_MSGCNT: sprintf(value, "%d", vector_count(call->msgs)); break; case SIP_ATTR_CALLSTATE: sprintf(value, "%s", call_state_to_str(call->state)); break; case SIP_ATTR_TRANSPORT: first = vector_first(call->msgs); sprintf(value, "%s", sip_transport_str(first->packet->type)); break; case SIP_ATTR_CONVDUR: timeval_to_duration(msg_get_time(call->cstart_msg), msg_get_time(call->cend_msg), value); break; case SIP_ATTR_TOTALDUR: first = vector_first(call->msgs); last = vector_last(call->msgs); timeval_to_duration(msg_get_time(first), msg_get_time(last), value); break; case SIP_ATTR_REASON_TXT: if (call->reasontxt) sprintf(value, "%s", call->reasontxt); break; case SIP_ATTR_WARNING: if (call->warning) sprintf(value, "%d", call->warning); break; default: return msg_get_attribute(vector_first(call->msgs), id, value); break; } return strlen(value) ? value : NULL; } const char * call_state_to_str(int state) { switch (state) { case SIP_CALLSTATE_CALLSETUP: return "CALL SETUP"; case SIP_CALLSTATE_INCALL: return "IN CALL"; case SIP_CALLSTATE_CANCELLED: return "CANCELLED"; case SIP_CALLSTATE_REJECTED: return "REJECTED"; case SIP_CALLSTATE_BUSY: return "BUSY"; case SIP_CALLSTATE_DIVERTED: return "DIVERTED"; case SIP_CALLSTATE_COMPLETED: return "COMPLETED"; } return ""; } int call_attr_compare(sip_call_t *one, sip_call_t *two, enum sip_attr_id id) { char onevalue[256], twovalue[256]; int oneintvalue, twointvalue; int comparetype; /* TODO 0 = string compare, 1 = int comprare */ switch (id) { case SIP_ATTR_CALLINDEX: oneintvalue = one->index; twointvalue = two->index; comparetype = 1; break; case SIP_ATTR_MSGCNT: oneintvalue = call_msg_count(one); twointvalue = call_msg_count(two); comparetype = 1; break; default: // Get attribute values memset(onevalue, 0, sizeof(onevalue)); memset(twovalue, 0, sizeof(twovalue)); call_get_attribute(one, id, onevalue); call_get_attribute(two, id, twovalue); comparetype = 0; break; } switch (comparetype) { case 0: if (strlen(twovalue) == 0 && strlen(onevalue) == 0) return 0; if (strlen(twovalue) == 0) return 1; if (strlen(onevalue) == 0) return -1; return strcmp(onevalue, twovalue); case 1: if (oneintvalue == twointvalue) return 0; if (oneintvalue > twointvalue) return 1; if (oneintvalue < twointvalue) return -1; /* no break */ default: return 0; } } void call_add_xcall(sip_call_t *call, sip_call_t *xcall) { if (!call || !xcall) return; // Mark this call as changed call->changed = true; // Add the xcall to the list vector_append(call->xcalls, xcall); } sngrep-1.8.2/src/sip_call.h000066400000000000000000000163141464272443000155610ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file sip_call.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage sip calls * */ #ifndef __SNGREP_SIP_CALL_H #define __SNGREP_SIP_CALL_H #include "config.h" #include #include #include "vector.h" #include "rtp.h" #include "sip_msg.h" #include "sip_attr.h" //! Shorter declaration of sip_call structure typedef struct sip_call sip_call_t; //! SIP Call State enum call_state { SIP_CALLSTATE_CALLSETUP = 1, SIP_CALLSTATE_INCALL, SIP_CALLSTATE_CANCELLED, SIP_CALLSTATE_REJECTED, SIP_CALLSTATE_DIVERTED, SIP_CALLSTATE_BUSY, SIP_CALLSTATE_COMPLETED }; /** * @brief Contains all information of a call and its messages * * This structure acts as header of messages list of the same * callid (considered a dialog). It contains some replicated * data from its messages to speed up searches. */ struct sip_call { // Call index in the call list int index; // Call identifier char *callid; //! Related Call identifier char *xcallid; //! Flag this call as filtered so won't be displayed signed char filtered; //! Call State. For dialogs starting with an INVITE method int state; //! Changed flag. For interface optimal updates bool changed; //! Locked flag. Calls locked are never deleted bool locked; //! Last reason text value for this call char *reasontxt; //! Last warning text value for this call int warning; //! List of calls with with this call as X-Call-Id vector_t *xcalls; //! Cseq from invite startint the call uint32_t invitecseq; //! List of messages of this call (sip_msg_t*) vector_t *msgs; //! Message when conversation started and ended sip_msg_t *cstart_msg, *cend_msg; //! RTP streams for this call (rtp_stream_t *) vector_t *streams; //! RTP packets for this call (capture_packet_t *) vector_t *rtp_packets; }; /** * @brief Create a new call with the given callid (Minimum required data) * * Allocated required memory for a new SIP Call. The call acts as * header structure to all the messages with the same callid. * * @param callid Call-ID Header value * @param xcallid X-Call-ID Header value * @return pointer to the sip_call created */ sip_call_t * call_create(char *callid, char *xcallid); /** * @brief Free all related memory from a call and remove from call list * * Deallocate memory of an existing SIP Call. * This will also remove all messages, calling sip_msg_destroy for each * one. * * @param call Call to be destroyed */ void call_destroy(sip_call_t *call); /** * @brief Wrapper around Message destroyer to clear call vectors */ void call_destroyer(void *call); /** * @brief Return if the call has changed * * Check if the call has changed since the last time * this function was * invoked. We consider list has changed when a new message or stream * has been added to the call. * * @return true if call has changed, false otherwise */ bool call_has_changed(sip_call_t *call); /** * @brief Append message to the call's message list * * Creates a relation between this call and the message, appending it * to the end of the message list and setting the message owner. * * @param call pointer to the call owner of the message * @param msg SIP message structure */ void call_add_message(sip_call_t *call, sip_msg_t *msg); /** * @brief Append a new RTP stream to the call * * Add a new stream to be monitored * * @param call pointer to the call owner of the stream * @param stream RTP stream data */ void call_add_stream(sip_call_t *call, rtp_stream_t *stream); /** * @brief Append a new RTP packet to the call * * @param call pointer to the call owner of the stream * @param packet new RTP packet from call rtp streams */ void call_add_rtp_packet(sip_call_t *call, packet_t *packet); /** * @brief Getter for call messages linked list size * * Return the number of messages stored in this call. All messages * share the same Call-ID * * @param call SIP call structure * @return how many messages are in the call */ int call_msg_count(sip_call_t *call); /** * @brief Determine if a dilog is a call in progress * * @param call SIP call structure * @return 1 if the passed call state is active, 0 otherwise */ int call_is_active(sip_call_t *call); /** * @brief Determine if this call starts with an Invite request * * @param call SIP call structure * @return 1 if first call message has method INVITE, 0 otherwise */ int call_is_invite(sip_call_t *call); /** * @brief Check if a message is a retransmission * * This function will compare its payload with the previous message * in the dialog, to check if it has the same content. * * @param msg SIP message that will be checked */ void call_msg_retrans_check(sip_msg_t *msg); /** * @brief Find a message in the call with SDP with the given address * * @param call SIP call structure * @param dst address:port structure to be searched * @return the message found or NULL */ sip_msg_t * call_msg_with_media(sip_call_t *call, address_t dst); /** * @brief Update Call State attribute with its last parsed message * * @param call Call structure to be updated * @param msg Last received message of this call */ void call_update_state(sip_call_t *call, sip_msg_t *msg); /** * @brief Return a call attribute value * * This function will be used to avoid accessing call structure * fields directly. * * @param call SIP call structure * @param id Attribute id * @return Attribute value or NULL if not found */ const char * call_get_attribute(struct sip_call *call, enum sip_attr_id id, char *value); /** * @brief Return the string represtation of a call state * */ const char * call_state_to_str(int state); /** * @brief Compare two calls based on a given attribute * * @return 0 if call attributes are equal * @return 1 if first call is greater * @return -1 if first call is lesser */ int call_attr_compare(sip_call_t *one, sip_call_t *two, enum sip_attr_id id); /** * @brief Relate this two calls * * Add a call to the internal xcalls vector of another call. * This calls are related by the SIP header X-Call-Id or X-CID * * @param call SIP call structure * @param xcall SIP call structure */ void call_add_xcall(sip_call_t *call, sip_call_t *xcall); #endif /* __SNGREP_SIP_CALL_H */ sngrep-1.8.2/src/sip_msg.c000066400000000000000000000115721464272443000154300ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file sip_msg.c * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage SIP call data * * This file contains the functions and structure to manage SIP message data * */ #include "sip_msg.h" #include "media.h" #include "sip.h" sip_msg_t * msg_create() { sip_msg_t *msg; if (!(msg = sng_malloc(sizeof(sip_msg_t)))) return NULL; return msg; } void msg_destroy(sip_msg_t *msg) { // Free message SDP media if (!msg->retrans) vector_destroy(msg->medias); // Free message packets packet_destroy(msg->packet); // Free all memory sng_free(msg->resp_str); sng_free(msg->sip_from); sng_free(msg->sip_to); sng_free(msg); } void msg_destroyer(void *msg) { msg_destroy((sip_msg_t *)msg); } struct sip_call * msg_get_call(const sip_msg_t *msg) { return msg->call; } int msg_media_count(sip_msg_t *msg) { return vector_count(msg->medias); } int msg_has_sdp(void *item) { return vector_count(((sip_msg_t *)item)->medias) ? 1 : 0; } int msg_is_request(sip_msg_t *msg) { return msg->reqresp < 100; } void msg_add_media(sip_msg_t *msg, sdp_media_t *media) { if (!msg->medias) { // Create a vector to store sdp msg->medias = vector_create(2, 2); vector_set_destroyer(msg->medias, media_destroyer); } vector_append(msg->medias, media); } const char * msg_get_payload(sip_msg_t *msg) { return (const char *) packet_payload(msg->packet); } struct timeval msg_get_time(sip_msg_t *msg) { struct timeval t = { }; frame_t *frame; if (msg && (frame = vector_first(msg->packet->frames))) { t.tv_sec = frame->header->ts.tv_sec; t.tv_usec = frame->header->ts.tv_usec; } return t; } const char * msg_get_attribute(sip_msg_t *msg, int id, char *value) { char *ar; switch (id) { case SIP_ATTR_SRC: if (msg->packet->ip_version == 6) { sprintf(value, "[%s]:%u", msg->packet->src.ip, msg->packet->src.port); } else { sprintf(value, "%s:%u", msg->packet->src.ip, msg->packet->src.port); } break; case SIP_ATTR_DST: if (msg->packet->ip_version == 6) { sprintf(value, "[%s]:%u", msg->packet->dst.ip, msg->packet->dst.port); } else { sprintf(value, "%s:%u", msg->packet->dst.ip, msg->packet->dst.port); } break; case SIP_ATTR_METHOD: sprintf(value, "%.*s", SIP_ATTR_MAXLEN, sip_get_msg_reqresp_str(msg)); break; case SIP_ATTR_SIPFROM: sprintf(value, "%.*s", SIP_ATTR_MAXLEN, msg->sip_from); break; case SIP_ATTR_SIPTO: sprintf(value, "%.*s", SIP_ATTR_MAXLEN, msg->sip_to); break; case SIP_ATTR_SIPFROMUSER: if (msg->sip_from && (ar = strchr(msg->sip_from, '@'))) { strncpy(value, msg->sip_from, ar - msg->sip_from); value[ar - msg->sip_from] = '\0'; } break; case SIP_ATTR_SIPTOUSER: if (msg->sip_to && (ar = strchr(msg->sip_to, '@'))) { strncpy(value, msg->sip_to, ar - msg->sip_to); value[ar - msg->sip_to] = '\0'; } break; case SIP_ATTR_DATE: timeval_to_date(msg_get_time(msg), value); break; case SIP_ATTR_TIME: timeval_to_time(msg_get_time(msg), value); break; default: fprintf(stderr, "Unhandled attribute %s (%d)\n", sip_attr_get_name(id), id); abort(); break; } return strlen(value) ? value : NULL; } int msg_is_older(sip_msg_t *one, sip_msg_t *two) { // Yes, you are older than nothing if (!two) return 1; // No, you are not older than yourself if (one == two) return 0; // Otherwise return timeval_is_older(msg_get_time(one), msg_get_time(two)); } sngrep-1.8.2/src/sip_msg.h000066400000000000000000000117411464272443000154330ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file sip_msg.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage sip messages * */ #ifndef __SNGREP_SIP_MSG_H #define __SNGREP_SIP_MSG_H #include "config.h" #include #include "vector.h" #include "media.h" #include "sip_attr.h" #include "util.h" //! Shorter declaration of sip_msg structure typedef struct sip_msg sip_msg_t; /** * @brief Information of a single message withing a dialog. * * Most of the data is just stored to be displayed in the UI so * the formats may be no the best, but the simplest for this * purpose. It also works as a linked lists of messages in a * call. */ struct sip_call; struct sip_msg { //! Request Method or Response Code @see sip_methods int reqresp; //! Response text if it doesn't matches an standard char *resp_str; //! Message Cseq uint32_t cseq; //! SIP From Header char *sip_from; //! SIP To Header char *sip_to; //! SDP payload information (sdp_media_t *) vector_t *medias; //! Captured packet for this message packet_t *packet; //! Index of this message in call uint32_t index; //! Message owner struct sip_call *call; //! Message is a retransmission from other message sip_msg_t *retrans; }; /** * @brief Create a new message from the readed header and payload * * Allocate required memory for a new SIP message. This function * will only store the given information, but wont parse it until * needed. * * @param payload Raw payload content * @return a new allocated message */ sip_msg_t * msg_create(); /** * @brief Destroy a SIP message and free its memory * * Deallocate memory of an existing SIP Message. * This function will remove the message from the call and the * passed pointer will be NULL. * * @param nsg SIP message to be deleted */ void msg_destroy(sip_msg_t *msg); /** * @brief Wrapper around Message destroyer to clear msg vectors */ void msg_destroyer(void *msg); /** * @brief Return the call owner of this message */ struct sip_call * msg_get_call(const sip_msg_t *msg); /** * @brief Getter for media of given messages * * Return the number of media structures of given msg * stored in this call. * * @param msg SIP message structure * @return how many media structures are in the msg */ int msg_media_count(sip_msg_t *msg); /** * @brief Check if given message has spd content */ int msg_has_sdp(void *item); /** * @brief Add a media structure to a msg * * @param cmsg SIP Message to be updated * @param media Media structure to be added */ void msg_add_media(sip_msg_t *msg, sdp_media_t *media); /** * @brief Check if a message is a Request or response * * @param msg SIP message that will be checked * @return 1 if the message is a request, 0 if a response */ int msg_is_request(sip_msg_t *msg); /** * @brief Add a new media for given message * * A SIP message can have multiple media description in * the SIP payload content * * @param msg SIP message that will store this packet * @param media parsed media structure from payload */ void msg_add_media(sip_msg_t *msg, sdp_media_t *media); /** * @brief Get SIP Message payload */ const char * msg_get_payload(sip_msg_t *msg); /** * @brief Get Time of message from packet header * * @param msg SIP message * @return timeval structure with message first packet time */ struct timeval msg_get_time(sip_msg_t *msg); /** * @brief Return a message attribute value * * This function will be used to avoid accessing call structure * fields directly. * * @param msg SIP message structure * @param id Attribute id * @param out Buffer to store attribute value * @return Attribute value or NULL if not found */ const char * msg_get_attribute(struct sip_msg *msg, int id, char *value); /** * @brief Check if a message is older than other * * @param one SIP message pointer * @param two SIP message pointer * @return 1 if one is older than two * @return 0 if equal or two is older than one */ int msg_is_older(sip_msg_t *one, sip_msg_t *two); #endif /* __SNGREP_SIP_MSG_H */ sngrep-1.8.2/src/util.c000066400000000000000000000114061464272443000147400ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file util.c * @author Ivan Alonso [aka Kaian] * * @brief Source of functions defined in util.h * */ #include "config.h" #include #include #include #include #include #include #include "util.h" #if __STDC_VERSION__ >= 201112L && __STDC_NO_ATOMICS__ != 1 // modern C with atomics #include typedef atomic_int signal_flag_type; #else // no atomics available typedef volatile sig_atomic_t signal_flag_type; #endif static signal_flag_type sigterm_received = 0; static void sigterm_handler(int signum) { sigterm_received = 1; } void setup_sigterm_handler(void) { // set up SIGTERM handler (also used for SIGINT and SIGQUIT) // the handler will be served by any of the running threads // so we just set a flag and check it in one of the two available // main loops of the program (main for --no-interface and // ui_wait_for_input() for curses) if (signal(SIGTERM, sigterm_handler) == SIG_ERR) exit(EXIT_FAILURE); if (signal(SIGINT, sigterm_handler) == SIG_ERR) exit(EXIT_FAILURE); if (signal(SIGQUIT, sigterm_handler) == SIG_ERR) exit(EXIT_FAILURE); // Handle SIGHUP signal, received when our controlling terminal is closed. // This prevents running on dead ssh connections. if (signal(SIGHUP, sigterm_handler) == SIG_ERR) exit(EXIT_FAILURE); } bool was_sigterm_received(void) { return (sigterm_received == 1); } void * sng_malloc(size_t size) { void *data; // Check memory allocation size if (size <= 0 || size > MALLOC_MAX_SIZE) return NULL; // Allocate memory if (!(data = malloc(size))) return NULL; // Initialize allocated memory memset(data, 0, size); return data; } void sng_free(void *ptr) { if (ptr) free(ptr); } char * sng_basename(const char *name) { const char *base = name; while (*name) { if (*name++ == '/') { base = name; } } return (char *) base; } int timeval_is_older(struct timeval t1, struct timeval t2) { long long int t1sec, t2sec; t1sec = t1.tv_sec; t1sec = t1sec * 1000000; t2sec = t2.tv_sec; t2sec = t2sec * 1000000; return ((t2sec + t2.tv_usec) - (t1sec + t1.tv_usec) <= 0); } const char * timeval_to_date(struct timeval time, char *out) { time_t t = (time_t) time.tv_sec; struct tm *timestamp = localtime(&t); strftime(out, 11, "%Y/%m/%d", timestamp); return out; } const char * timeval_to_time(struct timeval time, char *out) { time_t t = (time_t) time.tv_sec; struct tm *timestamp = localtime(&t); strftime(out, 19, "%H:%M:%S", timestamp); sprintf(out + 8, ".%06d", (int) time.tv_usec); return out; } const char * timeval_to_duration(struct timeval start, struct timeval end, char *out) { int seconds; char duration[20]; if (!out || !start.tv_sec || !end.tv_sec) return NULL; // Differnce in secons seconds = end.tv_sec - start.tv_sec; // Set Human readable format sprintf(duration, "%d:%02d", seconds / 60, seconds % 60); sprintf(out, "%7s", duration); return out; } const char * timeval_to_delta(struct timeval start, struct timeval end, char *out) { long diff; int nsec, nusec; int sign; if (!out || !start.tv_sec || !end.tv_sec) return NULL; diff = end.tv_sec * 1000000 + end.tv_usec; diff -= start.tv_sec * 1000000 + start.tv_usec; nsec = diff / 1000000; nusec = labs(diff - (nsec * 1000000)); sign = (diff >= 0) ? '+' : '-'; sprintf(out, "%c%d.%06d", sign, abs(nsec), nusec); return out; } char * strtrim(char *str) { int i; if (!str || !strlen(str)) return str; for (i = strlen(str) - 1; i >= 0 && isspace(str[i]); i--) str[i] = 0; return str; } sngrep-1.8.2/src/util.h000066400000000000000000000055101464272443000147440ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file util.h * @author Ivan Alonso [aka Kaian] * * @brief Functions for general use in the program * */ #ifndef __SNGREP_UTIL_H #define __SNGREP_UTIL_H // Capture headers has some fixes for pcap timevals in BSD systems #include "capture.h" // Max Memmory allocation #define MALLOC_MAX_SIZE 102400 // Stringify numbers for concatenation #define STRINGIFY_ARG(x) #x #define STRINGIFY(x) STRINGIFY_ARG(x) /** * @brief Wrapper for memory allocation */ void * sng_malloc(size_t size); /** * @brief Wrapper for memmory deallocation */ void sng_free(void *ptr); /* * @brief Generic implementation of basename */ char * sng_basename(const char *name); /** * @brief Compare two timeval structures * * @param t1 First timeval structure * @param t2 Second timval structure * @return 1 if t1 > t2, 0 if t1 <= t2 */ int timeval_is_older(struct timeval t1, struct timeval t2); /** * @brief Convert timeval to yyyy/mm/dd format */ const char * timeval_to_date(struct timeval time, char *out); /** * @brief Convert timeval to HH:MM:SS.mmmmmm format */ const char * timeval_to_time(struct timeval time, char *out); /** * @brief Calculate the time difference between two timeval * * @return Human readable time difference in mm:ss format */ const char * timeval_to_duration(struct timeval start, struct timeval end, char *out); /** * @brief Convert timeval diference to +mm:ss.mmmmmm */ const char * timeval_to_delta(struct timeval start, struct timeval end, char *out); /** * @brief Return a given string without trailing spaces */ char * strtrim(char *str); /** * @brief Set up handler for SIGTERM, SIGINT and SIGQUIT */ void setup_sigterm_handler(void); /** * @brief Check if SIGTERM, SIGINT or SIGQUIT were received * * @return true if any of the exit signals were received */ bool was_sigterm_received(void); #endif /* __SNGREP_UTIL_H */ sngrep-1.8.2/src/vector.c000066400000000000000000000212361464272443000152670ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file vector.c * @author Ivan Alonso [aka Kaian] * * @brief Source code of functions defined in vector.h * */ #include "vector.h" #include #include #include #include "util.h" vector_t * vector_create(int limit, int step) { vector_t *v; // Allocate memory for this vector data if (!(v = malloc(sizeof(vector_t)))) return NULL; v->count = 0; v->limit = limit; v->step = step; v->list = NULL; v->sorter = NULL; v->destroyer = NULL; return v; } void vector_destroy(vector_t *vector) { // Nothing to free. Done. if (!vector) return; // Remove all items if a destroyer is set vector_clear(vector); // Deallocate vector list sng_free(vector->list); // Deallocate vector itself sng_free(vector); } void vector_destroy_items(vector_t *vector) { int i; // Nothing to free. Done if (!vector) return; // If vector contains items if (vector->count) { for (i = 0; i < vector->count; i++) { free(vector->list[i]); } free(vector->list); } free(vector); } vector_t * vector_clone(vector_t *original) { vector_t *clone; vector_iter_t it; void *item; // Check we have a valid vector pointer if (!original) return NULL; // Create a new vector structure clone = vector_create(original->limit, original->step); vector_set_destroyer(clone, original->destroyer); vector_set_sorter(clone, original->sorter); // Fill the clone vector with the same elements it = vector_iterator(original); while ((item = vector_iterator_next(&it))) vector_append(clone, item); // Return the cloned vector return clone; } vector_t * vector_copy_if(vector_t *original, int (*filter)(void *item)) { vector_t *clone; vector_iter_t it; void *item; // Check we have a valid vector pointer if (!original) return NULL; // Create a new vector structure clone = vector_create(0, 1); // Fill the clone vector with the same elements applying filter it = vector_iterator(original); vector_iterator_set_filter(&it, filter); while ((item = vector_iterator_next(&it))) { vector_append(clone, item); } // Return the cloned vector return clone; } void vector_clear(vector_t *vector) { // Remove all items in the vector while (vector_first(vector)) vector_remove(vector, vector_first(vector)); } int vector_append(vector_t *vector, void *item) { // Sanity check if (!item) return vector->count; // Check if the vector has been initializated if (!vector->list) { vector->list = malloc(sizeof(void *) * vector->limit); memset(vector->list, 0, sizeof(void *) * vector->limit); } // Check if we need to increase vector size if (vector->count == vector->limit) { // Increase vector size vector->limit += vector->step; // Add more memory to the list vector->list = realloc(vector->list, sizeof(void *) * vector->limit); // Initialize new allocated memory memset(vector->list + vector->limit - vector->step, 0, vector->step); } // Add item to the end of the list vector->list[vector->count++] = item; // Check if vector has a sorter if (vector->sorter) { vector->sorter(vector, item); } return vector->count - 1; } int vector_append_vector(vector_t *dst, vector_t *src) { if (!dst || !src) return 1; vector_iter_t it = vector_iterator(src); void *item; while ((item = vector_iterator_next(&it))) vector_append(dst, item); return 0; } int vector_insert(vector_t *vector, void *item, int pos) { if (!item) return vector->count; if (pos < 0 || pos > vector->count - 2) return vector->count; // If position is already filled with that item, we're done if (vector->list[pos] == item) return vector->count; // If possition is occupied, move the other position if (vector->list[pos]) { memmove(vector->list + pos + 1, vector->list + pos, sizeof(void *) * (vector->count - pos - 1)); } // Set the position vector->list[pos] = item; return vector->count; } void vector_remove(vector_t *vector, void *item) { // Get item position int idx = vector_index(vector, item); // Not found in the vector if (idx == -1) return; // Decrease item counter vector->count--; // Move the rest of the elements one position up memmove(vector->list + idx, vector->list + idx + 1, sizeof(void *) * (vector->count - idx)); // Reset vector last position vector->list[vector->count] = NULL; // Destroy the item if vector has a destroyer if (vector->destroyer) { vector->destroyer(item); } } void vector_set_destroyer(vector_t *vector, void (*destroyer) (void *item)) { vector->destroyer = destroyer; } void vector_set_sorter(vector_t *vector, void (*sorter) (vector_t *vector, void *item)) { vector->sorter = sorter; } void vector_generic_destroyer(void *item) { sng_free(item); } void * vector_item(vector_t *vector, int index) { if (!vector || index >= vector->count || index < 0) return NULL; return vector->list[index]; } void vector_set_item(vector_t *vector, int index, void *item) { if (!vector || index >= vector->count || index < 0) return; vector->list[index] = item; } void * vector_first(vector_t *vector) { return vector_item(vector, 0); } void * vector_last(vector_t *vector) { return vector_item(vector, vector_count(vector) - 1); } int vector_index(vector_t *vector, void *item) { // FIXME Bad perfomance int i; for (i = 0; i < vector->count; i++) { if (vector->list[i] == item) return i; } return -1; } int vector_count(vector_t *vector) { return (vector) ? vector->count : 0; } vector_iter_t vector_iterator(vector_t *vector) { vector_iter_t it; memset(&it, 0, sizeof(vector_iter_t)); it.current = -1; it.vector = vector; return it; } vector_t * vector_iterator_vector(vector_iter_t *it) { return it->vector; } int vector_iterator_count(vector_iter_t *it) { int count = 0; int pos = it->current; vector_iterator_reset(it); if (!it->filter) { count = vector_count(it->vector); } else { while (vector_iterator_next(it)) { count++; } } vector_iterator_set_current(it, pos); return count; } void * vector_iterator_next(vector_iter_t *it) { void *item; if (!it || it->current >= vector_count(it->vector)) return NULL; while ((item = vector_item(it->vector, ++it->current))) { if (it->filter) { if (it->filter(item)) { return item; } } else { return item; } } return NULL; } void * vector_iterator_prev(vector_iter_t *it) { void *item; if (it->current == -1) return NULL; while ((item = vector_item(it->vector, --it->current))) { if (it->filter) { if (it->filter(item)) { return item; } } else { return item; } } return NULL; } void vector_iterator_set_filter(vector_iter_t *it, int (*filter)(void *item)) { it->filter = filter; } void vector_iterator_set_current(vector_iter_t *it, int current) { it->current = current; } void vector_iterator_set_last(vector_iter_t *it) { it->current = vector_count(it->vector); } int vector_iterator_current(vector_iter_t *it) { return it->current; } void vector_iterator_reset(vector_iter_t *it) { vector_iterator_set_current(it, -1); } sngrep-1.8.2/src/vector.h000066400000000000000000000145351464272443000153000ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file vector.h * @author Ivan Alonso [aka Kaian] * * @brief Functions to manage lists of pointers * * */ #ifndef __SNGREP_VECTOR_H_ #define __SNGREP_VECTOR_H_ #include "config.h" #include //! Shorter declaration of vector structure typedef struct vector vector_t; //! Shorter declaration of iterator structure typedef struct vector_iter vector_iter_t; /** * @brief Structure to hold a list of pointers */ struct vector { //! Number of elements in list uint32_t count; //! Total space in list (available + elements) uint32_t limit; //! Number of new spaces to be reallocated uint8_t step; //! Elements of the vector void **list; //! Function to destroy one item void (*destroyer) (void *item); //! Function to sort each appended/inserted item void (*sorter) (vector_t *vector, void *item); }; struct vector_iter { //! Last requested position int current; //! Last vector position int current_vector; //! Vector that's being iterated vector_t *vector; //! Filter iterator results using this func int (*filter) (void *item); }; /** * @brief Create a new vector * * Create a new vector with initial size and * step increase settings. */ vector_t * vector_create(int limit, int step); /** * @brief Free vector memory */ void vector_destroy(vector_t *vector); /** * @brief Free all vector items and memory */ void vector_destroy_items(vector_t *vector); /** * @brief Clone a vector container * * The cloned vector will have its own storage of pointers * **but the items in its list will be shared with the * original vector** * * Use with caution, specially with vector that have sorter * or destroyer functions. */ vector_t * vector_clone(vector_t *original); /** * @brief Copy filtered elements to a new vector * */ vector_t * vector_copy_if(vector_t *original, int (*filter)(void *item)); /** * @brief Remove all items of vector * */ void vector_clear(vector_t *vector); /** * @brief Append an item to vector * * Item will be added at the end of the * items list. * * @return index of the appended item */ int vector_append(vector_t *vector, void *item); /** * @brief Append a vector to another vector * @param dst Vector that will append the new items * @param src Vector that contain the items to append * @return 0 in case of success, 1 otherwise */ int vector_append_vector(vector_t *dst, vector_t *src); /** * @brief Insert an item in a given vector position * * @return count of elements in vector */ int vector_insert(vector_t *vector, void *item, int pos); /** * @brief Remove itemn from vector */ void vector_remove(vector_t *vector, void *item); /** * @brief Set the vector destroyer * * A destroyer is a function that will be invoked * for each item when the vector is destroyed or an * item is removed. */ void vector_set_destroyer(vector_t *vector, void (*destroyer) (void *item)); /** * @brief Set the vector sorter * * The sorter function will be invoked every time a new * item is appended into the vector. * */ void vector_set_sorter(vector_t *vector, void (*sorter) (vector_t *vector, void *item)); /** * @brief A generic item destroyer * * Generic memory deallocator for those items that only * require a simple 'free' */ void vector_generic_destroyer(void *item); /** * @brief Get an item from vector * * Return the item at given index, or NULL * if index is out of the vector bounds * */ void * vector_item(vector_t *vector, int index); /** * @brief Set an item in a given index * * This funtion will set an item in a given index. * The index MUST be in the already allocated memory * of the vector. This can be used to replace a vector * item. If position is already in use, destroyer won't * be call for existing item. */ void vector_set_item(vector_t *vector, int index, void *item); /** * @brief Return first item of the vector */ void * vector_first(vector_t *vector); /** * @brief Return last item of the vector */ void * vector_last(vector_t *vector); /** * @brief Get the index of an item * * Return the index of item in vector or -1 if * the item is not found */ int vector_index(vector_t *vector, void *item); /** * @brief Return the number of items of vector */ int vector_count(vector_t *vector); /** * @brief Return a new iterator for given vector */ vector_iter_t vector_iterator(vector_t *vector); /** * @brief Return the vector of this iterator */ vector_t * vector_iterator_vector(vector_iter_t *it); /** * @brief Return the number of items of iterator */ int vector_iterator_count(vector_iter_t *it); /** * @brief Return next element of iterator */ void * vector_iterator_next(vector_iter_t *it); /** * @brief Return prev element of iterator */ void * vector_iterator_prev(vector_iter_t *it); /** * @brief Set iterator filter funcion * * Filter iterator results using given function */ void vector_iterator_set_filter(vector_iter_t *it, int (*filter) (void *item)); /** * @brief Set current iterator position */ void vector_iterator_set_current(vector_iter_t *it, int current); /** * @brief Set iterator position to the last element */ void vector_iterator_set_last(vector_iter_t *it); /** * @brief Return current iterator position */ int vector_iterator_current(vector_iter_t *it); /** * @brief Reset iterator position to initial */ void vector_iterator_reset(vector_iter_t *it); #endif /* __SNGREP_VECTOR_H_ */ sngrep-1.8.2/tests/000077500000000000000000000000001464272443000141705ustar00rootroot00000000000000sngrep-1.8.2/tests/Makefile.am000066400000000000000000000010571464272443000162270ustar00rootroot00000000000000AUTOMAKE_OPTIONS=subdir-objects check_PROGRAMS=test-001 test-002 test-003 test-004 test-005 check_PROGRAMS+=test-006 test-007 test-008 test-009 test-010 check_PROGRAMS+=test-011 test_001_SOURCES=test_001.c test_002_SOURCES=test_002.c test_003_SOURCES=test_003.c test_004_SOURCES=test_004.c test_005_SOURCES=test_005.c test_006_SOURCES=test_006.c test_007_SOURCES=test_007.c ../src/vector.c ../src/util.c test_008_SOURCES=test_008.c test_009_SOURCES=test_009.c test_010_SOURCES=test_010.c ../src/hash.c test_011_SOURCES=test_011.c TESTS = $(check_PROGRAMS) sngrep-1.8.2/tests/README000066400000000000000000000010661464272443000150530ustar00rootroot00000000000000Basic testing programs for sngrep. This set of test will do some basic inputs to check sngrep screen navigation doesn't crash. This checks are ultra-super-basic. - test_001 : UI testing - test_002 : Call List testing - test_003 : Call Flow testing - test_004 : Call Raw testing - test_005 : Column selection testing - test_006 : Message diff testing - test_007: Test vector container structures - test_011: Test mix of normal packets with IPIP tunneled packets Sample capture files has been taken from wireshark Wiki: - https://wiki.wireshark.org/SampleCaptures sngrep-1.8.2/tests/aaa.pcap000066400000000000000000003307451464272443000155730ustar00rootroot00000000000000ò$B \\nENiL:[ EFEDEJFPEEEPENEBEJEOCACACACACABM %B\\nENiL:[ EFEDEJFPEEEPENEBEJEOCACACACACABM &B#\\nENiL:[ EFEDEJFPEEEPENEBEJEOCACACACACABM /B **nn/B <<n0T4V0T4Vnd7 ! ;/B LL0T4VnE>iM 5*#Csip cybercitydk0B LL0T4VnE>iM 5*#Csip cybercitydk2B LL0T4VnE>iM 5*#Csip cybercitydk2BA n0T4VE@@5 n5 G[ΫҀ100127in-addrarpa  ' localhostDB 0T4VnEi!#nREGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp151248737-46ea715e192.168.1.2;rport From: ;tag=903df0a To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=1200;q=0.500 Expires: 1200 CSeq: 68 REGISTER Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 DBn0T4VE@7+!#SIP/2.0 401 Unauthorized Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 68 REGISTER From: ;tag=903df0a To: ;tag=00-04092-1701af62-120c67172 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp151248737-46ea715e192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701af566be182070084c6f740706bb",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 EB.VV0T4VnEHiM 54V8_sip_udpsip cybercitydk!FBVV0T4VnEHiM 54V8_sip_udpsip cybercitydk!HBVV0T4VnEHiM 54V8_sip_udpsip cybercitydk!JBVV0T4VnEHiM 54V8_sip_udpsip cybercitydk!NB"VV0T4VnEHiM 54V8_sip_udpsip cybercitydk!UB9\\nENiL:[ EFEDEJFPEEEPENEBEJEOCACACACACABM UBw \\nENiL:[ EFEDEJFPEEEPENEBEJEOCACACACACABM VBRR0T4VnEDiM 50zO100127in-addrarpa VBiin0T4VE[@@>5 G\˪Ӏ100127in-addrarpa  ' localhostVB$0T4VnEi!#REGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp149505178-438c528b192.168.1.2;rport From: ;tag=8e948b0 To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=1200;q=0.500 Expires: 1200 CSeq: 69 REGISTER Content-Length: 0 Authorization: Digest username="voi18063",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701af566be182070084c6f740706bb",opaque="1701a1351f70795",nc="00000001",response="bd79fecae600a2eb79d37ec73214830b" Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 VB1\\n0T4VEN@7!#:YSIP/2.0 100 Trying Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 69 REGISTER From: ;tag=8e948b0 To: Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp149505178-438c528b192.168.1.2 Content-Length: 0 VByn0T4VEv@7!#bxSIP/2.0 403 Wrong password Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 69 REGISTER From: ;tag=8e948b0 To: ;tag=00-04085-1701af98-51a65b340 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp149505178-438c528b192.168.1.2 Content-Length: 0 VB+\\nENiL:[ EFEDEJFPEEEPENEBEJEOCACACACACABM [BynEiL[h EEDADADCDEDGDFCACACACACACACACACA EFEDEJFPEEEPENEBEJEOCACACACACABNSMB%!!V2\MAILSLOT\BROWSE D002465UkBJ >>0T4VnE0i@&^ p@;WkB >>0T4VnE0i@&^ -~p@ nB>>0T4VnE0i@&^ -~p@ nBݴ>>0T4VnE0i@&^ p@;WtB4>>0T4VnE0i@&^ -~p@ tB>>0T4VnE0i@&^ p@;WB] KK0T4VnE=iM 5)J^ftpecitelecomBj KK0T4VnE=iM 5)J^ftpecitelecomBH n0T4VE@@5 ԁftpecitelecom dns-!CnsbaraknetilC-OԖ0Bg >>0T4VnE0i@9 p@n+BG >>n0T4VE0x@9q7 pc6B 660T4VnE(i@9 PB$B'lln0T4VE^y@9p Pc6220 ProFTPD Server In ECI Telecom (ftp.ecitele.com) B)FF0T4VnE8i@9 ;PAUSER anonymous B<<n0T4VE(y@9q- ; Pc6=B3)n0T4VEty@9p ; Pc6^s331 Anonymous login ok, send your complete email address as your password. B2BB0T4VnE4i@9  PAPASS d0xa! Bg<<n0T4VE(y@9q+ ,Pc6BxUUn0T4VEGy@9q  ,Pc6];230-Welcome To ECI FTP Server B<<n0T4VE+y@9q& ,Pc6Ү Bå660T4VnE(i@9 ,PAzB[<<n0T4VE+y@9q% ,Pc6ҫ BQQn0T4VECy@9q  ,Pc6 /pub -> Public Folder. B٩660T4VnE(i@9 ,PAbzBܪYYn0T4VEKy@9q ǯ,Pc6H ECI users can do everything B RRn0T4VEDy @9q  ꯝ,Pc6' Others can only read B*660T4VnE(i@9 ,PA#zBWWn0T4VEIy @9q ,Pc6 /incoming -> Incoming Folder. Bgqqn0T4VEcy @9p ',Pc6O Anyone can access, write & retrieve a specific file B660T4VnE(i@9 ,bP@zBϲWWn0T4VEIy @9q b,Pc6 /outgoing -> outgoing Folder. Bqqn0T4VEcy @9p ,Pc6 Anyone can access, write & retrieve a specific file B660T4VnE(i@9 ,P@kzB<<n0T4VE+y@9q ,Pc6і Bqqn0T4VEcy@9p ,Pc6= Files larger then 250MB will be deleted after 5 days !!! B660T4VnE(i@9 ,P@-zBffn0T4VEXy@9p ,Pc6 Other Files will be deleted after 2 weeks !!! B<<n0T4VE+y@9q ,,Pc6( B;660T4VnE(i@9 ,/P?zB<<n0T4VE+y@9q /,Pc6% B*IIn0T4VE;y@9q 2,Pc6 Enjoy your stay. B]660T4VnE(i@9 ,EP?zB__n0T4VEQy@9p E,Pc60230 Guest access granted for anonymous. B>>0T4VnE0i@9~ ,nP?LxTYPE I BIIn0T4VE;y@9q n4Pc6k#200 Type set to I B<<0T4VnE.i@9 4P?mPASV B@jjn0T4VE\y@9p :Pc6Y227 Entering Passive Mode (147,234,1,253,230,119). BII0T4VnE;i@9q :P?tRETR SiteStat.xml B>>0T4VnE0i@9{ wꘊp@FB>ccn0T4VEUy@9p MPc6c550 SiteStat.xml: No such file or directory BNT>>n0T4VE0y@9qw !ꘋpc6BT660T4VnE(i@9 wꘋ!PB$PBU660T4VnE(i@9 wꘋ!PB$OB Z<<0T4VnE.i@9z MP?GvQUIT BK<<n0T4VE(y@9q%w !ꘌPc6=B۸DDn0T4VE6y@9q ⯝SPc6 v221 Goodbye. Bù660T4VnE(i@9 SP?9RBD<<n0T4VE(y@9q SPc6TBu660T4VnE(i@9~ TP?9QB<<n0T4VE(y@9q TPc6SBX\\nENiL:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B \\nENiL:[ EFEDEJFPEEEPENEBEJEOCACACACACABM Bc\\nENiL:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B <<n0T4V0T4Vd7 ! ;͇B **0T4Vnn0T4VBkVV0T4VnEHiM 54lM_sip_udpsip cybercitydk!B̟VV0T4VnEHiM 54lM_sip_udpsip cybercitydk!BVV0T4VnEHiM 54lM_sip_udpsip cybercitydk!B^VV0T4VnEHiM 54lM_sip_udpsip cybercitydk!BVV0T4VnEHiM 54lM_sip_udpsip cybercitydk!B%RR0T4VnEDiM 50EA100127in-addrarpa Biin0T4VE[@@>5 GAր100127in-addrarpa  ' localhostB30T4VnEip!#,REGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp140520199-489d520f192.168.1.2;rport From: ;tag=8602b14 To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=1200;q=0.500 Expires: 1200 CSeq: 70 REGISTER Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 B n0T4VE@7+!#SIP/2.0 401 Unauthorized Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 70 REGISTER From: ;tag=8602b14 To: ;tag=00-04089-1701b050-61ac4c943 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp140520199-489d520f192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b042073e40dc3ac7adb052ad41d",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 B(VV0T4VnEHiM 54SJ_sip_udpsip cybercitydk!BhVV0T4VnEHiM 54SJ_sip_udpsip cybercitydk!B[VV0T4VnEHiM 54SJ_sip_udpsip cybercitydk!B3\\nENiL{:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B\\nENiLz:[ EFEDEJFPEEEPENEBEJEOCACACACACABM BVV0T4VnEHiM} 54SJ_sip_udpsip cybercitydk!B~y \\nENiLx:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B(VV0T4VnEHiM{ 54SJ_sip_udpsip cybercitydk!BRR0T4VnEDiM~ 50B@100127in-addrarpa B iin0T4VE[@@>5 Gƾ@׀100127in-addrarpa  ' localhostBLL0T4VnE>iM 5*.sip cybercitydkB$\\n0T4VEN@@K5 :ׁsip cybercitydk '!#B(0T4VnEi!#5REGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp138780672-45022a1c192.168.1.2;rport From: ;tag=845a00d To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=1200;q=0.500 Expires: 1200 CSeq: 71 REGISTER Content-Length: 0 Authorization: Digest username="voi18063",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701b042073e40dc3ac7adb052ad41d",opaque="1701a1351f70795",nc="00000001",response="cc2d4573ee9399f34c1c7f3bf5144103" Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 B@z n0T4VE@7&!#SIP/2.0 401 nonce has changed Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 71 REGISTER From: ;tag=845a00d To: ;tag=00-04089-1701b067-320ad2da3 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp138780672-45022a1c192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b061158742446d3de8b80d645f3",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 B<<n0T4V0T4Vd7 ! ;B**0T4Vnn0T4VB \\nENiLs:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B8,\\nENiLr:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B_\\nENiLq:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B\\nENiLh:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B` \\nENiLg:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B .\\nENiLf:[ EFEDEJFPEEEPENEBEJEOCACACACACABM CB%\\nENjL0:[ EFEDEJFPEEEPENEBEJEOCACACACACABM DB+\\nENjL/:[ EFEDEJFPEEEPENEBEJEOCACACACACABM DBi\\nENjL.:[ EFEDEJFPEEEPENEBEJEOCACACACACABM FB **nnFBk <<n0T4V0T4Vnd7 ! ;FB VV0T4VnEHj M1 54@\_sip_udpsip cybercitydk!GBW VV0T4VnEHj!M0 54@\_sip_udpsip cybercitydk!IB VV0T4VnEHj"M/ 54@\_sip_udpsip cybercitydk!KB VV0T4VnEHj#M. 54@\_sip_udpsip cybercitydk!OB" VV0T4VnEHj$M- 54@\_sip_udpsip cybercitydk!WBJ* RR0T4VnEDj%M0 50\9100127in-addrarpa WB60 iin0T4VE[@@>5 G>؀100127in-addrarpa  ' localhostWB6 0T4VnEj&!#ۛrREGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp123759063-464bc1bb192.168.1.2;rport From: ;tag=76069e4 To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=1200;q=0.500 Expires: 1200 CSeq: 72 REGISTER Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 WB{Y n0T4VE@7+!#SIP/2.0 401 Unauthorized Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 72 REGISTER From: ;tag=76069e4 To: ;tag=00-04071-1701b172-22e1cc470 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp123759063-464bc1bb192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b16a2a09bc3847cfd2015612fe2",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 WBCVV0T4VnEHj'M* 54<}_sip_udpsip cybercitydk!XB@VV0T4VnEHj(M) 54<}_sip_udpsip cybercitydk!ZB<VV0T4VnEHj)M( 54<}_sip_udpsip cybercitydk!\B <<n0T4V0T4Vd7 ! ;\B"! **0T4Vnn0T4V\B>VV0T4VnEHj*M' 54<}_sip_udpsip cybercitydk!`BVV0T4VnEHj+M& 54<}_sip_udpsip cybercitydk!hBc RR0T4VnEDj,M) 5014100127in-addrarpa hB@&iin0T4VE[@@>5 Gۀ100127in-addrarpa  ' localhosthB>,0T4VnEj-<!#VJREGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp122028667-481b9fc8192.168.1.2;rport From: ;tag=7460288 To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=1200;q=0.500 Expires: 1200 CSeq: 73 REGISTER Content-Length: 0 Authorization: Digest username="voi18062",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701b16a2a09bc3847cfd2015612fe2",opaque="1701a1351f70795",nc="00000001",response="29cd776112a709cb9cbe978baff5e478" Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 iB 7n0T4VE@7&!#|SIP/2.0 401 nonce has changed Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 73 REGISTER From: ;tag=7460288 To: ;tag=00-04086-1701b193-645204ed6 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp122028667-481b9fc8192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b191514c6b695b4a2dd659cc0ff",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 sB} \\nENj.L:[ EFEDEJFPEEEPENEBEJEOCACACACACABM tBg\\nENj/L:[ EFEDEJFPEEEPENEBEJEOCACACACACABM uB\\nENj0L:[ EFEDEJFPEEEPENEBEJEOCACACACACABM BӴ VV0T4VnEHj5M 548|_sip_udpsip cybercitydk!B> VV0T4VnEHj6M 548|_sip_udpsip cybercitydk!B)\\nENj7L:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B1 \\nENj8L:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B\\nENj9L:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B VV0T4VnEHj:M 548|_sip_udpsip cybercitydk!B VV0T4VnEHj=M 548|_sip_udpsip cybercitydk!B VV0T4VnEHj>M 548|_sip_udpsip cybercitydk!B RR0T4VnEDj?M 50 1100127in-addrarpa Bu iin0T4VE[@@>5 G݀100127in-addrarpa  ' localhostBI 0T4VnEj@!#ɥREGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp114639000-477e7591192.168.1.2;rport From: ;tag=6d540a5 To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=1200;q=0.500 Expires: 1200 CSeq: 74 REGISTER Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Bźn0T4VE@7+!#81SIP/2.0 401 Unauthorized Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 74 REGISTER From: ;tag=6d540a5 To: ;tag=00-04089-1701b236-2b7211607 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp114639000-477e7591192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b22972b90f440c3e4eb250842bb",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 BiVV0T4VnEHjAM 545_sip_udpsip cybercitydk!BWVV0T4VnEHjBM 545_sip_udpsip cybercitydk!BRbVV0T4VnEHjCM 545_sip_udpsip cybercitydk!B <<n0T4V0T4Vd7 ! ;ͷB **0T4Vnn0T4VBvmVV0T4VnEHjDM  545_sip_udpsip cybercitydk!BVV0T4VnEHjEM  545_sip_udpsip cybercitydk!BRR0T4VnEDjFM 50-j100127in-addrarpa BÑiin0T4VE[@@>5 Gjހ100127in-addrarpa  ' localhostB0T4VnEjG"!#WkREGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp112903503-43a64480192.168.1.2;rport From: ;tag=6bac55c To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=1200;q=0.500 Expires: 1200 CSeq: 75 REGISTER Content-Length: 0 Authorization: Digest username="voi18062",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701b22972b90f440c3e4eb250842bb",opaque="1701a1351f70795",nc="00000001",response="79a0543188495d288c9ebbe0c881abdc" Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 B7\\n0T4VEN@7!#:; SIP/2.0 100 Trying Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 75 REGISTER From: ;tag=6bac55c To: Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp112903503-43a64480192.168.1.2 Content-Length: 0 B5n0T4VE@7;!#(SIP/2.0 200 OK Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;q=0.500;expires=1200 CSeq: 75 REGISTER From: ;tag=6bac55c P-Associated-URI: To: ;tag=00-04081-1701b256-5586b7324 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp112903503-43a64480192.168.1.2 Content-Length: 0 BTmVV0T4VnEHjHM  541,_sip_udpsip cybercitydk!BiVV0T4VnEHjIM 541,_sip_udpsip cybercitydk!BtVV0T4VnEHjJM 541,_sip_udpsip cybercitydk!BZVV0T4VnEHjKM 541,_sip_udpsip cybercitydk!BVV0T4VnEHjLM 541,_sip_udpsip cybercitydk!BNK\\nENjML:[  EFEDEJFPEEEPENEBEJEOCACACACACABM Bر\\nENjNK:[  EFEDEJFPEEEPENEBEJEOCACACACACABM B \\nENjOK:[  EFEDEJFPEEEPENEBEJEOCACACACACABM BRR0T4VnEDjQM 50I3100127in-addrarpa BXiin0T4VE[@@>5 G3100127in-addrarpa  ' localhostB//0T4VnE!jR!# K BVV0T4VnEHjSL 54vN_sip_udpsip cybercitydk!BVV0T4VnEHjTL 54vN_sip_udpsip cybercitydk!B-VV0T4VnEHjUL 54vN_sip_udpsip cybercitydk!BVV0T4VnEHjVL 54vN_sip_udpsip cybercitydk!B'VV0T4VnEHjWL 54vN_sip_udpsip cybercitydk!Bu9`rE\X) K) EMEBECDBDBDBCACACACACACACACACACA FHEPFCELEHFCEPFFFACACACACACACABNSMB%!!V2\MAILSLOT\BROWSE LAB111h"UBlRR0T4VnEDjXL 50E|100127in-addrarpa BHiin0T4VE[@@>5 G|€100127in-addrarpa  ' localhostB#//0T4VnE!jY!# K B<<n0T4V0T4Vd7 ! ;B**0T4Vnn0T4VB%VV0T4VnEHjZL 54II_sip_udpsip cybercitydk!B VV0T4VnEHj[L 54II_sip_udpsip cybercitydk!B.\\nENj`K:[ EFEDEJFPEEEPENEBEJEOCACACACACABM BVV0T4VnEHjaL 54II_sip_udpsip cybercitydk!B \\nENjbK:[ EFEDEJFPEEEPENEBEJEOCACACACACABM BY\\nENjcK:[ EFEDEJFPEEEPENEBEJEOCACACACACABM BVV0T4VnEHjdL 54II_sip_udpsip cybercitydk!BJ7VV0T4VnEHjeL 54II_sip_udpsip cybercitydk!B?RR0T4VnEDjfL 500B100127in-addrarpa BeEiin0T4VE[@@>5 GÀ100127in-addrarpa  ' localhostBK//0T4VnE!jg!# K BVV0T4VnEHjhL 54_sip_udpvoipbrujulanet!B>VV0T4VnEHjiL 54_sip_udpvoipbrujulanet!BxVV0T4VnEHjjL 54_sip_udpvoipbrujulanet!BVV0T4VnEHjkL 54_sip_udpvoipbrujulanet!BXVV0T4VnEHjlL 54_sip_udpvoipbrujulanet!!BtRR0T4VnEDjmL 503>100127in-addrarpa !BJiin0T4VE[@@>5 Gŀ100127in-addrarpa  ' localhost!BA``0T4VnERjnDxQ>INVITE sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 INVITE User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Expires: 120 Accept: application/sdp Content-Type: application/sdp Content-Length: 272 Contact: Max-Forwards: 70 Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO v=0 o=SIPPS 105015165 105015162 IN IP4 192.168.1.2 s=SIP call c=IN IP4 192.168.1.2 t=0 0 m=audio 30000 RTP/AVP 0 8 97 2 3 a=rtpmap:0 pcmu/8000 a=rtpmap:8 pcma/8000 a=rtpmap:97 iLBC/8000 a=rtpmap:2 G726-32/8000 a=rtpmap:3 GSM/8000 a=fmtp:97 mode=20 a=sendrecv !BVV0T4VnEHjoL 54,B_sip_udpsip cybercitydk!!B" ``0T4VnERjpDxQ>INVITE sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 INVITE User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Expires: 120 Accept: application/sdp Content-Type: application/sdp Content-Length: 272 Contact: Max-Forwards: 70 Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO v=0 o=SIPPS 105015165 105015162 IN IP4 192.168.1.2 s=SIP call c=IN IP4 192.168.1.2 t=0 0 m=audio 30000 RTP/AVP 0 8 97 2 3 a=rtpmap:0 pcmu/8000 a=rtpmap:8 pcma/8000 a=rtpmap:97 iLBC/8000 a=rtpmap:2 G726-32/8000 a=rtpmap:3 GSM/8000 a=fmtp:97 mode=20 a=sendrecv "BVV0T4VnEHjqL 54,B_sip_udpsip cybercitydk!"B ``0T4VnERjrDxQ>INVITE sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 INVITE User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Expires: 120 Accept: application/sdp Content-Type: application/sdp Content-Length: 272 Contact: Max-Forwards: 70 Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO v=0 o=SIPPS 105015165 105015162 IN IP4 192.168.1.2 s=SIP call c=IN IP4 192.168.1.2 t=0 0 m=audio 30000 RTP/AVP 0 8 97 2 3 a=rtpmap:0 pcmu/8000 a=rtpmap:8 pcma/8000 a=rtpmap:97 iLBC/8000 a=rtpmap:2 G726-32/8000 a=rtpmap:3 GSM/8000 a=fmtp:97 mode=20 a=sendrecv #B.}}n0T4VEo@/G>DxQ[(SIP/2.0 100 trying -- your call is important to us Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport=5060;received=80.230.219.70 From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 INVITE Server: Sip EXpress router (0.8.12 (i386/linux)) Content-Length: 0 Warning: 392 200.68.120.81:5060 "Noisy feedback tells: pid=32642 req_src_ip=80.230.219.70 req_src_port=5060 in_uri=sip:97239287044@voip.brujula.net out_uri=sip:97239287044@voip.brujula.net via_cnt==1" $BVV0T4VnEHjsL 54,B_sip_udpsip cybercitydk!&Bo<<n0T4V0T4Vd7 ! ;&B**0T4Vnn0T4V&BVV0T4VnEHjtL 54,B_sip_udpsip cybercitydk!*BVV0T4VnEHjuL 54,B_sip_udpsip cybercitydk!1Bv\\nENjvK:[ EFEDEJFPEEEPENEBEJEOCACACACACABM 2B"\\nENjwK:[ EFEDEJFPEEEPENEBEJEOCACACACACABM 2B(RR0T4VnEDjxL 50 :100127in-addrarpa 2Biin0T4VE[@@>5 GȀ100127in-addrarpa  ' localhost2Bt//0T4VnE!jy!# K 2B{VV0T4VnEHjzL 54?_sip_udpvoipbrujulanet!2Bj \\nENj{K:[ EFEDEJFPEEEPENEBEJEOCACACACACABM 3BsVV0T4VnEHj|L 54?_sip_udpvoipbrujulanet!5Bv~VV0T4VnEHj}L 54?_sip_udpvoipbrujulanet!7BVV0T4VnEHj~L 54?_sip_udpvoipbrujulanet!;B2VV0T4VnEHjL 54?_sip_udpvoipbrujulanet!CBRR0T4VnEDjL 50 6100127in-addrarpa CB~iin0T4VE[@@>5 Gʀ100127in-addrarpa  ' localhostCB-0T4VnEj̘DxQCANCEL sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 CB`VV0T4VnEHjL 548p_sip_udpsip cybercitydk!CBr 0T4VnEj̖DxQCANCEL sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 DBVV0T4VnEHjL 548p_sip_udpsip cybercitydk!DBܞ 0T4VnEj̔DxQCANCEL sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 EBn0T4VE@/G+DxQnSIP/2.0 408 Request Timeout Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport=5060;received=80.230.219.70 From: "arik" ;tag=6433ef9 To: ;tag=a6a1c5f60faecf035a1ae5b6e96e979a-6167 Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 INVITE Server: Sip EXpress router (0.8.12 (i386/linux)) Content-Length: 0 Warning: 392 200.68.120.81:5060 "Noisy feedback tells: pid=32753 req_src_ip=80.230.219.70 req_src_port=5060 in_uri=sip:97239287044@voip.brujula.net out_uri=sip:97239287044@voip.brujula.net via_cnt==0" EB0T4VnEwj̯DxQcЬACK sip:97239287044@voip.brujula.net SIP/2.0 From: "arik" ;tag=6433ef9 Call-ID: 105090259-446faf7a@192.168.1.2 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport To: ;tag=a6a1c5f60faecf035a1ae5b6e96e979a-6167 CSeq: 1 ACK Content-Length: 0 FBVV0T4VnEHjL 548p_sip_udpsip cybercitydk!FB 0T4VnEj̑DxQCANCEL sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 HB VV0T4VnEHjL 548p_sip_udpsip cybercitydk!JB= 0T4VnEj̏DxQCANCEL sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 LBVV0T4VnEHjL 548p_sip_udpsip cybercitydk!NB& 0T4VnEj̍DxQCANCEL sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 RB@= 0T4VnEǰDxQCANCEL sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 TB7RR0T4VnEDjL 50/H100127in-addrarpa TBiin0T4VE[@@>5 GH΀100127in-addrarpa  ' localhostTB//0T4VnE!j}!# K VB}z 0T4VnEj̉DxQCANCEL sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 ZB 0T4VnEj̈DxQCANCEL sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 ^B 0T4VnEj̇DxQCANCEL sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 `B:VV0T4VnEHjL 543}_sip_udpsip cybercitydk!`B/ \\nENjK:[ EFEDEJFPEEEPENEBEJEOCACACACACABM aB'VV0T4VnEHjL 543}_sip_udpsip cybercitydk!aB\\\nENjK:[ EFEDEJFPEEEPENEBEJEOCACACACACABM bB\\nENjK:[ EFEDEJFPEEEPENEBEJEOCACACACACABM bB$ 0T4VnEj́DxQCANCEL sip:97239287044@voip.brujula.net SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport From: "arik" ;tag=6433ef9 To: Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 cB2VV0T4VnEHjL 543}_sip_udpsip cybercitydk!dB1Bn0T4VE}@/G0DxQiSIP/2.0 408 Request Timeout Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp104984053-44ce4a41192.168.1.2;rport=5060;received=80.230.219.70 From: "arik" ;tag=6433ef9 To: ;tag=a6a1c5f60faecf035a1ae5b6e96e979a-e525 Call-ID: 105090259-446faf7a@192.168.1.2 CSeq: 1 CANCEL Server: Sip EXpress router (0.8.12 (i386/linux)) Content-Length: 0 Warning: 392 200.68.120.81:5060 "Noisy feedback tells: pid=32753 req_src_ip=80.230.219.70 req_src_port=5060 in_uri=sip:97239287044@voip.brujula.net out_uri=sip:97239287044@voip.brujula.net via_cnt==0" eB>VV0T4VnEHjL 543}_sip_udpsip cybercitydk!iB0<<n0T4V0T4Vd7 ! ;iB1**0T4Vnn0T4ViB VV0T4VnEHjL 543}_sip_udpsip cybercitydk!qB RR0T4VnEDjL 50 U100127in-addrarpa qB iin0T4VE[@@>5 GU100127in-addrarpa  ' localhostqB //0T4VnE!jo!# K }B)a VV0T4VnEHjL 54)_sip_udpsip cybercitydk!~BN VV0T4VnEHjL 54)_sip_udpsip cybercitydk!BY VV0T4VnEHjL 54)_sip_udpsip cybercitydk!Be VV0T4VnEHjL 54)_sip_udpsip cybercitydk!B{ VV0T4VnEHjL 54)_sip_udpsip cybercitydk!B RR0T4VnEDjL 50E 100127in-addrarpa BA iin0T4VE[@@>5 G'100127in-addrarpa  ' localhostBՎ //0T4VnE!jd!# K B \\nENjK:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B`\\nENjK:[ EFEDEJFPEEEPENEBEJEOCACACACACABM BB\\nENjK:[ EFEDEJFPEEEPENEBEJEOCACACACACABM B VV0T4VnEHjL 54 G_sip_udpsip cybercitydk!B VV0T4VnEHjL 54 G_sip_udpsip cybercitydk!B VV0T4VnEHjL 54 G_sip_udpsip cybercitydk!B VV0T4VnEHjL 54 G_sip_udpsip cybercitydk!B VV0T4VnEHjL 54 G_sip_udpsip cybercitydk!B RR0T4VnEDjL 50(100127in-addrarpa B iin0T4VE[@@>5 Gހ(100127in-addrarpa  ' localhostBd //0T4VnE!jZ!# K B <<n0T4V0T4Vd7 ! ;ͰBC **0T4Vnn0T4VBY VV0T4VnEHjL 54p_sip_udpsip cybercitydk!BF VV0T4VnEHjL 54p_sip_udpsip cybercitydk!BQ VV0T4VnEHjL 54p_sip_udpsip cybercitydk!B/] VV0T4VnEHjL 54p_sip_udpsip cybercitydk!B` \\nENjK:[~ EFEDEJFPEEEPENEBEJEOCACACACACABM B \\nENjK:[~ EFEDEJFPEEEPENEBEJEOCACACACACABM B{s VV0T4VnEHjL 54p_sip_udpsip cybercitydk!B1\\nENjK:[~ EFEDEJFPEEEPENEBEJEOCACACACACABM Bz RR0T4VnEDjL 50+100127in-addrarpa B€ iin0T4VE[@@>5 G{+100127in-addrarpa  ' localhostBX //0T4VnE!jP!# K B VV0T4VnEHjL 54~_sip_udpsip cybercitydk!B2 VV0T4VnEHjL 54~_sip_udpsip cybercitydk!Bp VV0T4VnEHjL 54~_sip_udpsip cybercitydk!B VV0T4VnEHjL 54~_sip_udpsip cybercitydk!B VV0T4VnEHjL 54~_sip_udpsip cybercitydk!B RR0T4VnEDjL 50t100127in-addrarpa Bh iin0T4VE[@@>5 GWv100127in-addrarpa  ' localhostB_ cc0T4VnEUj!#AINVITE sip:0097239287044@sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp85213694-430aa1de192.168.1.2;rport From: "arik" ;tag=51449dc To: Call-ID: 85216695-42dcdb1d@192.168.1.2 CSeq: 1 INVITE User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Expires: 120 Accept: application/sdp Content-Type: application/sdp Content-Length: 270 Contact: Max-Forwards: 70 Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO v=0 o=SIPPS 85214742 85214739 IN IP4 192.168.1.2 s=SIP call c=IN IP4 192.168.1.2 t=0 0 m=audio 30000 RTP/AVP 0 8 97 2 3 a=rtpmap:0 pcmu/8000 a=rtpmap:8 pcma/8000 a=rtpmap:97 iLBC/8000 a=rtpmap:2 G726-32/8000 a=rtpmap:3 GSM/8000 a=fmtp:97 mode=20 a=sendrecv B& VV0T4VnEHjL 54I_sip_udpsip cybercitydk!B&ucc0T4VnEUj!#AINVITE sip:0097239287044@sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp85213694-430aa1de192.168.1.2;rport From: "arik" ;tag=51449dc To: Call-ID: 85216695-42dcdb1d@192.168.1.2 CSeq: 1 INVITE User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Expires: 120 Accept: application/sdp Content-Type: application/sdp Content-Length: 270 Contact: Max-Forwards: 70 Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO v=0 o=SIPPS 85214742 85214739 IN IP4 192.168.1.2 s=SIP call c=IN IP4 192.168.1.2 t=0 0 m=audio 30000 RTP/AVP 0 8 97 2 3 a=rtpmap:0 pcmu/8000 a=rtpmap:8 pcma/8000 a=rtpmap:97 iLBC/8000 a=rtpmap:2 G726-32/8000 a=rtpmap:3 GSM/8000 a=fmtp:97 mode=20 a=sendrecv B VV0T4VnEHjL 54I_sip_udpsip cybercitydk!BX|cc0T4VnEUj!#AINVITE sip:0097239287044@sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp85213694-430aa1de192.168.1.2;rport From: "arik" ;tag=51449dc To: Call-ID: 85216695-42dcdb1d@192.168.1.2 CSeq: 1 INVITE User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Expires: 120 Accept: application/sdp Content-Type: application/sdp Content-Length: 270 Contact: Max-Forwards: 70 Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO v=0 o=SIPPS 85214742 85214739 IN IP4 192.168.1.2 s=SIP call c=IN IP4 192.168.1.2 t=0 0 m=audio 30000 RTP/AVP 0 8 97 2 3 a=rtpmap:0 pcmu/8000 a=rtpmap:8 pcma/8000 a=rtpmap:97 iLBC/8000 a=rtpmap:2 G726-32/8000 a=rtpmap:3 GSM/8000 a=fmtp:97 mode=20 a=sendrecv B{{n0T4VEm@7!#Ye SIP/2.0 407 authentication required Allow: UPDATE,REFER Call-ID: 85216695-42dcdb1d@192.168.1.2 Contact: CSeq: 1 INVITE From: "arik" ;tag=51449dc Proxy-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b4767d49c41117c7b73a255a353",opaque="1701a1351f70795",stale=false,algorithm=MD5 Server: Cirpack/v4.38e (gw_sip) To: ;tag=00-04073-1701b482-069239f90 Via: SIP/2.0/UDP 192.168.1.2:5060;received=80.230.219.70;rport=5060;branch=z9hG4bKnp85213694-430aa1de192.168.1.2 Content-Length: 0 Bv~~0T4VnEpj!#\kACK sip:0097239287044@sip.cybercity.dk SIP/2.0 From: "arik" ;tag=51449dc Call-ID: 85216695-42dcdb1d@192.168.1.2 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp85213694-430aa1de192.168.1.2;rport To: ;tag=00-04073-1701b482-069239f90 CSeq: 1 ACK Content-Length: 0 B# VV0T4VnEHjL 54I_sip_udpsip cybercitydk!Bt <<n0T4V0T4Vd7 ! ;B **0T4Vnn0T4VB\. VV0T4VnEHjL 54I_sip_udpsip cybercitydk!BE VV0T4VnEHjL 54I_sip_udpsip cybercitydk!BL RR0T4VnEDjL 50h100127in-addrarpa BQ iin0T4VE[@@>5 Gph100127in-addrarpa  ' localhostBwV //0T4VnE!j?!# K B VV0T4VnEHjL 54{_sip_udpsip cybercitydk!B VV0T4VnEHjL 54{_sip_udpsip cybercitydk!B VV0T4VnEHjL 54{_sip_udpsip cybercitydk!B VV0T4VnEHjL 54{_sip_udpsip cybercitydk!B7Z\\nENjK{:[y" EFEDEJFPEEEPENEBEJEOCACACACACABM B, \\nENjKv:[y" EFEDEJFPEEEPENEBEJEOCACACACACABM B \\nENjKu:[y" EFEDEJFPEEEPENEBEJEOCACACACACABM BVVV0T4VnEHjLx 54{_sip_udpsip cybercitydk!B RR0T4VnEDjL{ 50l100127in-addrarpa Biin0T4VE[@@>5 GOn100127in-addrarpa  ' localhostB^^0T4VnEPj!#<$INVITE sip:0097239287044@sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp83260863-46304c10192.168.1.2;rport From: "arik" ;tag=51449dc To: Call-ID: 85216695-42dcdb1d@192.168.1.2 CSeq: 2 INVITE Proxy-Authorization: Digest username="voi18062",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701b4767d49c41117c7b73a255a353",opaque="1701a1351f70795",nc="00000001",response="8258d3744c08b75f7af46cd0f1762510" Content-Type: application/sdp Content-Length: 270 Date: Mon, 04 Jul 2005 09:43:55 GMT Contact: Expires: 120 Accept: application/sdp Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO v=0 o=SIPPS 85214742 85214739 IN IP4 192.168.1.2 s=SIP call c=IN IP4 192.168.1.2 t=0 0 m=audio 30000 RTP/AVP 0 8 97 2 3 a=rtpmap:0 pcmu/8000 a=rtpmap:8 pcma/8000 a=rtpmap:97 iLBC/8000 a=rtpmap:2 G726-32/8000 a=rtpmap:3 GSM/8000 a=fmtp:97 mode=20 a=sendrecv BVV0T4VnEHjLu 546_sip_udpsip cybercitydk!Bn0T4VE@7T!#'QSIP/2.0 403 Wrong password or domain Allow: UPDATE,REFER Call-ID: 85216695-42dcdb1d@192.168.1.2 Contact: CSeq: 2 INVITE From: "arik" ;tag=51449dc Server: Cirpack/v4.38e (gw_sip) To: ;tag=00-04071-1701b4ad-52a186e31 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp83260863-46304c10192.168.1.2 Content-Length: 0 Byy0T4VnEkj!#WuACK sip:0097239287044@sip.cybercity.dk SIP/2.0 From: "arik" ;tag=51449dc Call-ID: 85216695-42dcdb1d@192.168.1.2 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp83260863-46304c10192.168.1.2;rport To: ;tag=00-04071-1701b4ad-52a186e31 CSeq: 2 ACK Content-Length: 0 BVV0T4VnEHjLs 546_sip_udpsip cybercitydk!BVV0T4VnEHjLr 546_sip_udpsip cybercitydk!BD'VV0T4VnEHjLq 546_sip_udpsip cybercitydk!B<VV0T4VnEHjLp 546_sip_udpsip cybercitydk! BERR0T4VnEDjLs 50*100127in-addrarpa  BJiin0T4VE[@@>5 Gj*100127in-addrarpa  ' localhost BTO//0T4VnE!j)!# K BVV0T4VnEHjLm 54)_sip_udpsip cybercitydk!BVV0T4VnEHjLl 54)_sip_udpsip cybercitydk!BVV0T4VnEHjLk 54)_sip_udpsip cybercitydk!B VV0T4VnEHjLj 54)_sip_udpsip cybercitydk!B\\nENjKe:[v% EFEDEJFPEEEPENEBEJEOCACACACACABM  B\\\nENjKd:[v% EFEDEJFPEEEPENEBEJEOCACACACACABM !B\\nENjKc:[v% EFEDEJFPEEEPENEBEJEOCACACACACABM #B&$VV0T4VnEHjLf 54)_sip_udpsip cybercitydk!+B+RR0T4VnEDjLi 50' 100127in-addrarpa +B0iin0T4VE[@@>5 G 100127in-addrarpa  ' localhost+Bg5//0T4VnE!j!# K +B6 nEjJ[-' EEDADADCDEDGDFCACACACACACACACACA EFEDEJFPEEEPENEBEJEOCACACACACABNSMB%!!V2\MAILSLOT\BROWSE D002465U0B<<n0T4V0T4Vd7 ! ;0B**0T4Vnn0T4V7BMmVV0T4VnEHjLb 54_sip_udpsip cybercitydk!8BYVV0T4VnEHjLa 54_sip_udpsip cybercitydk!:B)eVV0T4VnEHjL` 54_sip_udpsip cybercitydk!<BkpVV0T4VnEHjL_ 54_sip_udpsip cybercitydk!@BVV0T4VnEHjL^ 54_sip_udpsip cybercitydk!HBRR0T4VnEDjLa 50t100127in-addrarpa HBwiin0T4VE[@@>5 Gt100127in-addrarpa  ' localhostHBH//0T4VnE!j!# K NBp \\nENjKW:[q* EFEDEJFPEEEPENEBEJEOCACACACACABM OBȞ \\nENjKV:[q* EFEDEJFPEEEPENEBEJEOCACACACACABM PB\\nENjKU:[q* EFEDEJFPEEEPENEBEJEOCACACACACABM TB/VV0T4VnEHjLX 54W _sip_udpsip cybercitydk!UBVV0T4VnEHjLW 54W _sip_udpsip cybercitydk!WBDVV0T4VnEHjLV 54W _sip_udpsip cybercitydk!YBgVV0T4VnEHjLU 54W _sip_udpsip cybercitydk!]BP VV0T4VnEHjLT 54W _sip_udpsip cybercitydk!eBZ RR0T4VnEDjLW 50z100127in-addrarpa eB0 iin0T4VE[@@>5 G\100127in-addrarpa  ' localhosteB //0T4VnE!j !# K qBe VV0T4VnEHkLM 54 ._sip_udpsip cybercitydk!rBR VV0T4VnEHkLL 54 ._sip_udpsip cybercitydk!tBt] VV0T4VnEHkLK 54 ._sip_udpsip cybercitydk!vBki VV0T4VnEHk LF 54 ._sip_udpsip cybercitydk!zB~ VV0T4VnEHk L1 54 ._sip_udpsip cybercitydk!~B$X\\nENk:K:[l/ EFEDEJFPEEEPENEBEJEOCACACACACABM ~BT\\nENk;K:[l/ EFEDEJFPEEEPENEBEJEOCACACACACABM B \\nENk<K:[l/ EFEDEJFPEEEPENEBEJEOCACACACACABM BV RR0T4VnEDk=L 50O100127in-addrarpa B iin0T4VE[@@>5 GxO100127in-addrarpa  ' localhostB //0T4VnE!k>!# K B <<n0T4V0T4Vd7 ! ;͇B **0T4Vnn0T4VBf VV0T4VnEHkJL 54I_sip_udpsip cybercitydk!B VV0T4VnEHkKL 54I_sip_udpsip cybercitydk!BF VV0T4VnEHkLL 54I_sip_udpsip cybercitydk!B VV0T4VnEHkML 54I_sip_udpsip cybercitydk!B VV0T4VnEHkNL 54I_sip_udpsip cybercitydk!B RR0T4VnEDkOL 50100127in-addrarpa Bh iin0T4VE[@@>5 Giu100127in-addrarpa  ' localhostB //0T4VnE!kP!# K B-VV0T4VnEHkQL 54(_sip_udpsip cybercitydk!BVV0T4VnEHkRK 54(_sip_udpsip cybercitydk!B%VV0T4VnEHkSK 54(_sip_udpsip cybercitydk!B0VV0T4VnEHkTK 54(_sip_udpsip cybercitydk!B;\\nENkUJ:[i2 EFEDEJFPEEEPENEBEJEOCACACACACABM B\\nENkVJ:[i2 EFEDEJFPEEEPENEBEJEOCACACACACABM Bz\\nENkWJ:[i2 EFEDEJFPEEEPENEBEJEOCACACACACABM B)GVV0T4VnEHkXK 54(_sip_udpsip cybercitydk!BNRR0T4VnEDkYK 50@100127in-addrarpa B]Tiin0T4VE[@@>5 G#q100127in-addrarpa  ' localhostBZ0T4VnEkZ!#REGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp62913665-430aa2da192.168.1.2;rport From: ;tag=3bffccc To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=0;q=0.750 Expires: 0 CSeq: 76 REGISTER Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 B^n0T4VE@7,!#SIP/2.0 401 Unauthorized Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 76 REGISTER From: ;tag=3bffccc To: ;tag=00-04090-1701b651-15c238ce6 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp62913665-430aa2da192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b63914805cd2139536c00323d58",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 BvVV0T4VnEHk[K 544_sip_udpsip cybercitydk!BVV0T4VnEHk\K 544_sip_udpsip cybercitydk!B VV0T4VnEHk]K 544_sip_udpsip cybercitydk!BVV0T4VnEHk^K 544_sip_udpsip cybercitydk!BVV0T4VnEHk_K 544_sip_udpsip cybercitydk!B[RR0T4VnEDk`K 50j100127in-addrarpa BB iin0T4VE[@@>5 Gmj100127in-addrarpa  ' localhostBQ0T4VnEka!#]REGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp61178202-452852a6192.168.1.2;rport From: ;tag=3a58141 To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=0;q=0.500 Expires: 0 CSeq: 77 REGISTER Content-Length: 0 Authorization: Digest username="voi18062",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701b63914805cd2139536c00323d58",opaque="1701a1351f70795",nc="00000001",response="d973fd6f5ae1ac5accaff93cd603c1b5" Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 B VV0T4VnEHkbK 54w_sip_udpsip cybercitydk!B;tag=3a58141 To: ;tag=00-04089-1701b683-18ec171b5 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp61178202-452852a6192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b6784feeadb2410d11d8053d2a5",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 B VV0T4VnEHkcK 54w_sip_udpsip cybercitydk!B VV0T4VnEHkdK 54w_sip_udpsip cybercitydk!B<<n0T4V0T4Vd7 ! ;B**0T4Vnn0T4VBe VV0T4VnEHkeK 54w_sip_udpsip cybercitydk!B¿ VV0T4VnEHkfK 54w_sip_udpsip cybercitydk!BC RR0T4VnEDkgK 50b100127in-addrarpa B iin0T4VE[@@>5 Gib100127in-addrarpa  ' localhostB 0T4VnEkh!#5REGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp61001873-43beb0a5192.168.1.2;rport From: ;tag=3a2d0dc To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=1200;q=0.500 Expires: 1200 CSeq: 78 REGISTER Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Bp n0T4VE@7,!#SIP/2.0 401 Unauthorized Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 78 REGISTER From: ;tag=3a2d0dc To: ;tag=00-04077-1701b6ab-13935c834 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp61001873-43beb0a5192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b69b47b51c6d66c9e4503c679c2",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 BVV0T4VnEHkiK 54_sip_udpsip cybercitydk!BVV0T4VnEHkjK 54_sip_udpsip cybercitydk!BVV0T4VnEHkkK 54_sip_udpsip cybercitydk!B\\nENklJ:[d7 EFEDEJFPEEEPENEBEJEOCACACACACABM B_ \\nENkmJ:[d7 EFEDEJFPEEEPENEBEJEOCACACACACABM B] \\nENknJ:[d7 EFEDEJFPEEEPENEBEJEOCACACACACABM B3VV0T4VnEHkoK 54_sip_udpsip cybercitydk!BGVV0T4VnEHktK 54_sip_udpsip cybercitydk!B`RR0T4VnEDkuK 50@100127in-addrarpa B6iin0T4VE[@@>5 Gf@100127in-addrarpa  ' localhostBN0T4VnEkv!#8REGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp57726197-4841c7cd192.168.1.2;rport From: ;tag=370d8e5 To: Call-ID: 578222729-4665d775@578222732-4665d772 Contact: ;expires=1200;q=0.500 Expires: 1200 CSeq: 79 REGISTER Content-Length: 0 Authorization: Digest username="voi18062",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701b69b47b51c6d66c9e4503c679c2",opaque="1701a1351f70795",nc="00000001",response="e733a545cf353a92820b48c90153e59d" Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 B& n0T4VE@7'!#SIP/2.0 401 nonce has changed Call-ID: 578222729-4665d775@578222732-4665d772 CSeq: 79 REGISTER From: ;tag=370d8e5 To: ;tag=00-04083-1701b6dd-051f5dc31 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp57726197-4841c7cd192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b6cc1cbc90d328fe1f1a31a2c4e",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 B?\\nENkwJ:[a: EFEDEJFPEEEPENEBEJEOCACACACACABM Bn\\nENkxJ:[a: EFEDEJFPEEEPENEBEJEOCACACACACABM B\\nENkyJ:[a: EFEDEJFPEEEPENEBEJEOCACACACACABM <B \\nENkzJ:[^= EFEDEJFPEEEPENEBEJEOCACACACACABM =B^ \\nENk{J:[^= EFEDEJFPEEEPENEBEJEOCACACACACABM >BT\\nENk|J:[^= EFEDEJFPEEEPENEBEJEOCACACACACABM lB\\\nENkJ:[YB EFEDEJFPEEEPENEBEJEOCACACACACABM lB7 \\nENkJ:[YB EFEDEJFPEEEPENEBEJEOCACACACACABM mBj\\nENkJ:[YB EFEDEJFPEEEPENEBEJEOCACACACACABM BPW `rE\X)_M) EMEBECDBDBDBCACACACACACACACACAAA FHEPFCELEHFCEPFFFACACACACACACABNSMB%V\MAILSLOT\BROWSE B}W \\`rEN\Yf):h#O FHEPFCELEHFCEPFFFACACACACACACABL B\\`rEN\Ye):h#O FHEPFCELEHFCEPFFFACACACACACACABL Bl\\`rEN\Yd):h#O FHEPFCELEHFCEPFFFACACACACACACABL B}`rE\X)_P) EMEBECDBDBDBCACACACACACACACACAAA FHEPFCELEHFCEPFFFACACACACACACABNSMB%V\MAILSLOT\BROWSE B\\`rEN\Yb):h R FHEPFCELEHFCEPFFFACACACACACACABL Bug \\`rEN\Ya):h R FHEPFCELEHFCEPFFFACACACACACACABL BΚ\\`rEN\Y`):h R FHEPFCELEHFCEPFFFACACACACACACABL B`rE\X)_S) EMEBECDBDBDBCACACACACACACACACAAA FHEPFCELEHFCEPFFFACACACACACACABNSMB%V\MAILSLOT\BROWSE Bw\\`rEN\Y^):hU FHEPFCELEHFCEPFFFACACACACACACABL B \\`rEN\Y]):hU FHEPFCELEHFCEPFFFACACACACACACABL Bt \\`rEN\Y\):hU FHEPFCELEHFCEPFFFACACACACACACABL B\\`rEN\Y[):eW FHEPFCELEHFCEPFFFACACACACACACABO BS\\`rEN\YZ):eW FHEPFCELEHFCEPFFFACACACACACACABO B'\\`rEN\YY):eW FHEPFCELEHFCEPFFFACACACACACACABO Bҭ\\nENkJ:[VE EFEDEJFPEEEPENEBEJEOCACACACACABM B \\nENkJ:[VE EFEDEJFPEEEPENEBEJEOCACACACACABM BQ \\nENkJ:[VE EFEDEJFPEEEPENEBEJEOCACACACACABM B1**nnB3<<n0T4V0T4Vnd7 ! ;ͽB3VV0T4VnEHkJDC4m 8ncSc5=n d002465Q d002465.<MSFT 5.07 ,./!+B:NNn0T4VE@@YCD,m 8ncSc563 d002465B `rE\X)чX) EMEBECDBDBDBCACACACACACACACACACA FHEPFCELEHFCEPFFFACACACACACACABNSMB%!!V2\MAILSLOT\BROWSE LAB111UB\\nENkJ:[OL EFEDEJFPEEEPENEBEJEOCACACACACABM B4\\nENkJ:[OL EFEDEJFPEEEPENEBEJEOCACACACACABM B| \\nENkJ:[OL EFEDEJFPEEEPENEBEJEOCACACACACABM BEK LL0T4VnE>kK 5*3regsippstarcomBDP LL0T4VnE>kK 5*3regsippstarcomB[ LL0T4VnE>kK 5*3regsippstarcomBXnn0T4VE@@5 E쁀regsippstarcom XRb'XnshspeednetXns3A>>];YRBZh<<n0T4V0T4Vd7 ! ;Bh**0T4Vnn0T4VBLL0T4VnE>kK 5*x\sip cybercitydkBLL0T4VnE>kK 5*sip cybercitydkBLL0T4VnE>kK 5*x\sip cybercitydkBLL0T4VnE>kK 5*sip cybercitydkBDLL0T4VnE>kK 5*x\sip cybercitydkBLL0T4VnE>kK 5*sip cybercitydkBbLL0T4VnE>kK 5*x\sip cybercitydkB9n0T4VEr@@'5 ^ʣsip cybercitydk ,!#,ns1,ns2B_An0T4VEr@@'5 ^P\sip cybercitydk ,!#,ns2,ns1BjnVV0T4VnEHkK 54S_sip_udpsip cybercitydk!BVu VV0T4VnEHkK 54S_sip_udpsip cybercitydk!B;3 \\nENkJ:[LO EFEDEJFPEEEPENEBEJEOCACACACACABM B"J \\nENkJ:[LO EFEDEJFPEEEPENEBEJEOCACACACACABM B}\\nENkJ:[LO EFEDEJFPEEEPENEBEJEOCACACACACABM BK VV0T4VnEHkK 54S_sip_udpsip cybercitydk!B& VV0T4VnEHkK 54S_sip_udpsip cybercitydk!BRN VV0T4VnEHkK 54S_sip_udpsip cybercitydk! Bo9 RR0T4VnEDkK 50Y100127in-addrarpa  B*? iin0T4VE[@@>5 G<;100127in-addrarpa  ' localhost BE 0T4VnEk!#,REGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp28874686-455dbbd9192.168.1.2;rport From: ;tag=1b89bcd To: Call-ID: 29858147-465b0752@29858051-465b07b2 Contact: pel ;expires=1200;q=0.500 Expires: 1200 CSeq: 1 REGISTER Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 B'\  n0T4VE@7/!#2SIP/2.0 401 Unauthorized Call-ID: 29858147-465b0752@29858051-465b07b2 CSeq: 1 REGISTER From: ;tag=1b89bcd To: ;tag=00-04072-1701b941-779945843 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp28874686-455dbbd9192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b9333e87132e7f747c507263d93",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 BVV0T4VnEHkK 547_sip_udpsip cybercitydk!BVV0T4VnEHkK 547_sip_udpsip cybercitydk!BVV0T4VnEHkK 547_sip_udpsip cybercitydk!BNVV0T4VnEHkK 547_sip_udpsip cybercitydk!BVV0T4VnEHkK 547_sip_udpsip cybercitydk!BRR0T4VnEDkK 50100127in-addrarpa Biin0T4VE[@@>5 G7100127in-addrarpa  ' localhostBd0T4VnEk!#RREGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp27111175-4330c9d6192.168.1.2;rport From: ;tag=19db316 To: Call-ID: 29858147-465b0752@29858051-465b07b2 Contact: pel ;expires=1200;q=0.500 Expires: 1200 CSeq: 2 REGISTER Content-Length: 0 Authorization: Digest username="voi18062",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701b9333e87132e7f747c507263d93",opaque="1701a1351f70795",nc="00000001",response="0df96eb528bb630a16fd0252618cf3cb" Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 BVV0T4VnEHkK 541_sip_udpsip cybercitydk!B+"XXn0T4VEJ@7!#6SIP/2.0 100 Trying Call-ID: 29858147-465b0752@29858051-465b07b2 CSeq: 2 REGISTER From: ;tag=19db316 To: Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp27111175-4330c9d6192.168.1.2 Content-Length: 0 B Vn0T4VE@7;q=0.500;expires=1200 CSeq: 2 REGISTER From: ;tag=19db316 P-Associated-URI: To: ;tag=00-04095-1701b95f-5bde7e420 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp27111175-4330c9d6192.168.1.2 Content-Length: 0 BC VV0T4VnEHkK 541_sip_udpsip cybercitydk!!BVV0T4VnEHkK 541_sip_udpsip cybercitydk!#B VV0T4VnEHkK 541_sip_udpsip cybercitydk!'BHVV0T4VnEHkK 541_sip_udpsip cybercitydk!,BG \\nENkJ:[IR EFEDEJFPEEEPENEBEJEOCACACACACABM -BI\\nENkJ:[IR EFEDEJFPEEEPENEBEJEOCACACACACABM .Be}\\nENkJ:[IR EFEDEJFPEEEPENEBEJEOCACACACACABM /BRR0T4VnEDkK 50z100127in-addrarpa /B#iin0T4VE[@@>5 G2z100127in-addrarpa  ' localhost/B)0T4VnEk!#ڃREGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp26936945-479e83f1192.168.1.2;rport From: ;tag=19b0614 To: Call-ID: 29858147-465b0752@29858051-465b07b2 Contact: pel ;expires=1200;q=0.500 Expires: 1200 CSeq: 3 REGISTER Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 /BVV0T4VnEHkK 54_sip_udpsip cybercitydk!/B>  n0T4VE@7/!#ÉSIP/2.0 401 Unauthorized Call-ID: 29858147-465b0752@29858051-465b07b2 CSeq: 3 REGISTER From: ;tag=19b0614 To: ;tag=00-04095-1701b970-463ba6b36 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp26936945-479e83f1192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b96c474e0c4b5dcd407f27bdde2",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 0BVV0T4VnEHkK 54_sip_udpsip cybercitydk!2BsVV0T4VnEHkK 54_sip_udpsip cybercitydk!4Bw<<n0T4V0T4Vd7 ! ;4B**0T4Vnn0T4V4BVV0T4VnEHkK 54_sip_udpsip cybercitydk!8B1VV0T4VnEHkK 54_sip_udpsip cybercitydk!@BXRR0T4VnEDkK 50ɲ[100127in-addrarpa @B iin0T4VE[@@>5 G.[100127in-addrarpa  ' localhost@Bcc0T4VnEUk!#AZINVITE sip:0097239287044@sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp24466402-45dc61d5192.168.1.2;rport From: "arik" ;tag=175a1dd To: Call-ID: 24487391-449bf2a0@192.168.1.2 CSeq: 1 INVITE User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Expires: 120 Accept: application/sdp Content-Type: application/sdp Content-Length: 270 Contact: Max-Forwards: 70 Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO v=0 o=SIPPS 24466422 24466418 IN IP4 192.168.1.2 s=SIP call c=IN IP4 192.168.1.2 t=0 0 m=audio 30000 RTP/AVP 0 8 97 2 3 a=rtpmap:0 pcmu/8000 a=rtpmap:8 pcma/8000 a=rtpmap:97 iLBC/8000 a=rtpmap:2 G726-32/8000 a=rtpmap:3 GSM/8000 a=fmtp:97 mode=20 a=sendrecv @B= VV0T4VnEHkK 54?_sip_udpsip cybercitydk!@Bk {{n0T4VEm@7!#YSIP/2.0 407 authentication required Allow: UPDATE,REFER Call-ID: 24487391-449bf2a0@192.168.1.2 Contact: CSeq: 1 INVITE From: "arik" ;tag=175a1dd Proxy-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b97b4eb6c66d64d0b20f5a0bef8",opaque="1701a1351f70795",stale=false,algorithm=MD5 Server: Cirpack/v4.38e (gw_sip) To: ;tag=00-04095-1701b9a0-13c92a672 Via: SIP/2.0/UDP 192.168.1.2:5060;received=80.230.219.70;rport=5060;branch=z9hG4bKnp24466402-45dc61d5192.168.1.2 Content-Length: 0 @B { ~~0T4VnEpk!#\ACK sip:0097239287044@sip.cybercity.dk SIP/2.0 From: "arik" ;tag=175a1dd Call-ID: 24487391-449bf2a0@192.168.1.2 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp24466402-45dc61d5192.168.1.2;rport To: ;tag=00-04095-1701b9a0-13c92a672 CSeq: 1 ACK Content-Length: 0 AB VV0T4VnEHkK 54?_sip_udpsip cybercitydk!CB VV0T4VnEHkK 54?_sip_udpsip cybercitydk!EBu VV0T4VnEHkK 54?_sip_udpsip cybercitydk!IBW VV0T4VnEHkK 54?_sip_udpsip cybercitydk!QBi RR0T4VnEDkK 50U100127in-addrarpa QBQ iin0T4VE[@@>5 G8+100127in-addrarpa  ' localhostQB 0T4VnEk!#REGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp23652235-46b50e7c192.168.1.2;rport From: ;tag=168e795 To: Call-ID: 29858147-465b0752@29858051-465b07b2 Contact: pel ;expires=1200;q=0.500 Expires: 1200 CSeq: 4 REGISTER Content-Length: 0 Authorization: Digest username="voi18062",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701b96c474e0c4b5dcd407f27bdde2",opaque="1701a1351f70795",nc="00000001",response="9e65b34f9156b9f55d83ac1bcd84ba57" Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 QB{ VV0T4VnEHkK 54_sip_udpsip cybercitydk!QBG n0T4VE@7*!#SIP/2.0 401 nonce has changed Call-ID: 29858147-465b0752@29858051-465b07b2 CSeq: 4 REGISTER From: ;tag=168e795 To: ;tag=00-04085-1701b9c9-48e7b0863 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp23652235-46b50e7c192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701b9c069173a215e356e3017b179d",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 RBo VV0T4VnEHkK 54_sip_udpsip cybercitydk!TB, VV0T4VnEHkK 54_sip_udpsip cybercitydk!VBl VV0T4VnEHkK 54_sip_udpsip cybercitydk!ZB VV0T4VnEHkK 54_sip_udpsip cybercitydk![Bl2\\nENkJ:[DW EFEDEJFPEEEPENEBEJEOCACACACACABM \B` \\nENkJ:[DW EFEDEJFPEEEPENEBEJEOCACACACACABM ]B.\\nENkJ:[DW EFEDEJFPEEEPENEBEJEOCACACACACABM bB RR0T4VnEDkK 50q100127in-addrarpa bB iin0T4VE[@@>5 GT'100127in-addrarpa  ' localhostbB //0T4VnE!k<!# K bB VV0T4VnEHkK 54>_sip_udpsip cybercitydk!cB4 VV0T4VnEHkK 54>_sip_udpsip cybercitydk!eB VV0T4VnEHkK~ 54>_sip_udpsip cybercitydk!gB VV0T4VnEHkK} 54>_sip_udpsip cybercitydk!kB" VV0T4VnEHkK| 54>_sip_udpsip cybercitydk!sBO RR0T4VnEDkK 50|100127in-addrarpa sB/$ iin0T4VE[@@>5 GA|100127in-addrarpa  ' localhostsB* ^^0T4VnEPk!#<~INVITE sip:0097239287044@sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp20238275-478927ba192.168.1.2;rport From: "arik" ;tag=175a1dd To: Call-ID: 24487391-449bf2a0@192.168.1.2 CSeq: 2 INVITE Proxy-Authorization: Digest username="voi18062",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701b97b4eb6c66d64d0b20f5a0bef8",opaque="1701a1351f70795",nc="00000001",response="0b7990bb21d5572d6571e97b98c6d70f" Content-Type: application/sdp Content-Length: 270 Date: Mon, 04 Jul 2005 09:54:25 GMT Contact: Expires: 120 Accept: application/sdp Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO v=0 o=SIPPS 24466422 24466418 IN IP4 192.168.1.2 s=SIP call c=IN IP4 192.168.1.2 t=0 0 m=audio 30000 RTP/AVP 0 8 97 2 3 a=rtpmap:0 pcmu/8000 a=rtpmap:8 pcma/8000 a=rtpmap:97 iLBC/8000 a=rtpmap:2 G726-32/8000 a=rtpmap:3 GSM/8000 a=fmtp:97 mode=20 a=sendrecv sB2 VV0T4VnEHkKy 549_sip_udpsip cybercitydk!tBҐn0T4VE@7!#SIP/2.0 100 Trying Allow: UPDATE,REFER Call-ID: 24487391-449bf2a0@192.168.1.2 Contact: CSeq: 2 INVITE From: "arik" ;tag=175a1dd Server: Cirpack/v4.38e (gw_sip) To: Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp20238275-478927ba192.168.1.2 Content-Length: 0 tBn0T4VE@7T!#TSIP/2.0 403 Wrong password or domain Allow: UPDATE,REFER Call-ID: 24487391-449bf2a0@192.168.1.2 Contact: CSeq: 2 INVITE From: "arik" ;tag=175a1dd Server: Cirpack/v4.38e (gw_sip) To: ;tag=00-04083-1701ba17-57d493ef5 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp20238275-478927ba192.168.1.2 Content-Length: 0 tByy0T4VnEkk!#W6ACK sip:0097239287044@sip.cybercity.dk SIP/2.0 From: "arik" ;tag=175a1dd Call-ID: 24487391-449bf2a0@192.168.1.2 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp20238275-478927ba192.168.1.2;rport To: ;tag=00-04083-1701ba17-57d493ef5 CSeq: 2 ACK Content-Length: 0 tB VV0T4VnEHkKw 549_sip_udpsip cybercitydk!vB, VV0T4VnEHkKv 549_sip_udpsip cybercitydk!xB> <<n0T4V0T4Vd7 ! ;xBp **0T4Vnn0T4VxB&7 VV0T4VnEHkKu 549_sip_udpsip cybercitydk!|B O VV0T4VnEHkKt 549_sip_udpsip cybercitydk!BT RR0T4VnEDkKw 50X100127in-addrarpa BZ iin0T4VE[@@>5 G=X100127in-addrarpa  ' localhostB_ //0T4VnE!k-!# K BS\\nENkJm:[AZ EFEDEJFPEEEPENEBEJEOCACACACACABM B\\nENkJl:[AZ EFEDEJFPEEEPENEBEJEOCACACACACABM B:\\nENkJk:[AZ EFEDEJFPEEEPENEBEJEOCACACACACABM BrVV0T4VnEHkKn 54Q_sip_udpsip cybercitydk!BnVV0T4VnEHkKm 54Q_sip_udpsip cybercitydk!B/zVV0T4VnEHkKl 54Q_sip_udpsip cybercitydk!BsVV0T4VnEHkKk 54Q_sip_udpsip cybercitydk!BVV0T4VnEHkKj 54Q_sip_udpsip cybercitydk!BRR0T4VnEDkKm 50100127in-addrarpa Bֿiin0T4VE[@@>5 Gg:100127in-addrarpa  ' localhostB XX0T4VnEJk!#6%INVITE sip:35104724@sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp11888298-448e3777192.168.1.2;rport From: "arik" ;tag=b56e6e To: Call-ID: 11894297-4432a9f8@192.168.1.2 CSeq: 1 INVITE User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Expires: 120 Accept: application/sdp Content-Type: application/sdp Content-Length: 270 Contact: Max-Forwards: 70 Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO v=0 o=SIPPS 11888330 11888327 IN IP4 192.168.1.2 s=SIP call c=IN IP4 192.168.1.2 t=0 0 m=audio 30000 RTP/AVP 0 8 97 2 3 a=rtpmap:0 pcmu/8000 a=rtpmap:8 pcma/8000 a=rtpmap:97 iLBC/8000 a=rtpmap:2 G726-32/8000 a=rtpmap:3 GSM/8000 a=fmtp:97 mode=20 a=sendrecv B- uun0T4VEg@7!#SSIP/2.0 407 authentication required Allow: UPDATE,REFER Call-ID: 11894297-4432a9f8@192.168.1.2 Contact: CSeq: 1 INVITE From: "arik" ;tag=b56e6e Proxy-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701ba6557c1e1b4223e887e293cfa8",opaque="1701a1351f70795",stale=false,algorithm=MD5 Server: Cirpack/v4.38e (gw_sip) To: ;tag=00-04079-1701ba6f-3e08e2f66 Via: SIP/2.0/UDP 192.168.1.2:5060;received=80.230.219.70;rport=5060;branch=z9hG4bKnp11888298-448e3777192.168.1.2 Content-Length: 0 B@ ss0T4VnEek!#QfACK sip:35104724@sip.cybercity.dk SIP/2.0 From: "arik" ;tag=b56e6e Call-ID: 11894297-4432a9f8@192.168.1.2 Via: SIP/2.0/UDP 192.168.1.2:5060;branch=z9hG4bKnp11888298-448e3777192.168.1.2;rport To: ;tag=00-04079-1701ba6f-3e08e2f66 CSeq: 1 ACK Content-Length: 0 B# VV0T4VnEHkKf 54?_sip_udpsip cybercitydk!B: VV0T4VnEHkKe 54?_sip_udpsip cybercitydk!B VV0T4VnEHkKd 54?_sip_udpsip cybercitydk!B\\nENkJ_:[<_ EFEDEJFPEEEPENEBEJEOCACACACACABM Bg<<n0T4V0T4Vd7 ! ;ͻB**0T4Vnn0T4VB; \\nENkJ^:[<_ EFEDEJFPEEEPENEBEJEOCACACACACABM B VV0T4VnEHkKa 54?_sip_udpsip cybercitydk!B\\nENkJ\:[<_ EFEDEJFPEEEPENEBEJEOCACACACACABM B; VV0T4VnEHkK_ 54?_sip_udpsip cybercitydk!B RR0T4VnEDkK^ 50100127in-addrarpa B& iin0T4VE[@@>5 G~6100127in-addrarpa  ' localhostB- SS0T4VnEEk!#1INVITE sip:35104724@sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp10144774-4725f980192.168.1.2;rport From: "arik" ;tag=b56e6e To: Call-ID: 11894297-4432a9f8@192.168.1.2 CSeq: 2 INVITE Proxy-Authorization: Digest username="voi18062",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701ba6557c1e1b4223e887e293cfa8",opaque="1701a1351f70795",nc="00000001",response="83e1608f6d9ad38597ac6bbe4d3aafae" Content-Type: application/sdp Content-Length: 270 Date: Mon, 04 Jul 2005 09:56:06 GMT Contact: Expires: 120 Accept: application/sdp Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 Allow: INVITE, ACK, CANCEL, BYE, REFER, OPTIONS, NOTIFY, INFO v=0 o=SIPPS 11888330 11888327 IN IP4 192.168.1.2 s=SIP call c=IN IP4 192.168.1.2 t=0 0 m=audio 30000 RTP/AVP 0 8 97 2 3 a=rtpmap:0 pcmu/8000 a=rtpmap:8 pcma/8000 a=rtpmap:97 iLBC/8000 a=rtpmap:2 G726-32/8000 a=rtpmap:3 GSM/8000 a=fmtp:97 mode=20 a=sendrecv B>VV0T4VnEHkKX 54U_sip_udpsip cybercitydk!Bn0T4VE@7!# CSeq: 2 INVITE From: "arik" ;tag=b56e6e Server: Cirpack/v4.38e (gw_sip) To: Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp10144774-4725f980192.168.1.2 Content-Length: 0 Bln0T4VE@7t!#SIP/2.0 183 In band info available Allow: UPDATE,REFER Call-ID: 11894297-4432a9f8@192.168.1.2 Contact: Content-Type: application/sdp CSeq: 2 INVITE From: "arik" ;tag=b56e6e Server: Cirpack/v4.38e (gw_sip) To: ;tag=00-04075-1701baa2-2dfdf7c21 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp10144774-4725f980192.168.1.2 Content-Length: 199 v=0 o=cp10 112047106116 112047106116 IN IP4 212.242.33.75 s=SIP Call c=IN IP4 212.242.33.36 t=0 0 m=audio 40392 RTP/AVP 8 0 b=AS:64 a=rtpmap:8 PCMA/8000/1 a=rtpmap:0 PCMU/8000/1 a=ptime:20 BOn0T4VE@7U!#ĻSIP/2.0 480 Error Allow: UPDATE,REFER Call-ID: 11894297-4432a9f8@192.168.1.2 Contact: CSeq: 2 INVITE From: "arik" ;tag=b56e6e Reason: q.850;cause=21 Server: Cirpack/v4.38e (gw_sip) To: ;tag=00-04075-1701baa2-2dfdf7c21 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp10144774-4725f980192.168.1.2 Content-Length: 0 B>cnn0T4VnE`k!#L.ACK sip:35104724@sip.cybercity.dk SIP/2.0 From: "arik" ;tag=b56e6e Call-ID: 11894297-4432a9f8@192.168.1.2 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp10144774-4725f980192.168.1.2;rport To: ;tag=00-04075-1701baa2-2dfdf7c21 CSeq: 2 ACK Content-Length: 0 BvVV0T4VnEHkKV 54U_sip_udpsip cybercitydk!BP0T4VnEkh!$u0ހo7qj   i`bj`ia}tR[YG\VRUDKBuYsxZ|nhjB6b0T4VnEkg!$u0ox7qnhji`njlndljlnm`f~ESt_B[WZOMug}fetuCxKGUCgfmcldfdxdoollbmcjnewr@s}~bgbjjeCqZY~`fzrrIez`x]DBBp0T4VnEkf!$u0܌o7qq~pvKtvvOPAXfgegoxcffjofOzeeabh`Oafdruxmljglibxf`gcatvcbaxoxr`incPYpemdagejjjoF]E^DIQQAtg藖씓ꔟB%0T4VnEke!$u0oo7qxzadp`gx앑꒒ZOXCTABB`0T4VnEld!$u0oX7qD}pVxxdda|OTY_䔓B|KWu]EBgdxgazhrQpMVAcfxfljjo`pxB]0T4VnElc!$u0wo7qeef`cmbjhohjmanccimidhjibhoffmamhnjlzqFqefhirvrjjdS_O[KIuTB0T4VnElb!$u0o7qKI|cibohommmcedrsxofjhhjchzsMtgz~Y]EZIthmfgKpGTYMO~ZGpdgfldszadrxuMVB0T4VnEla!$u0o 87q甐암uM쓗QP㗑ꓖ씗锕ꖑ앗B<0T4VnEl`!$u0o 7q閝薑閐ꑝT蔔铓픗ꖐB[0T4VnEl!$u1p,7qB^$  7q11894297-4432a9f8@192.168.1.2SIPPS7qsession shutdownBVVV0T4VnEHlKK 54U_sip_udpsip cybercitydk!BáVV0T4VnEHlKJ 54U_sip_udpsip cybercitydk!BVV0T4VnEHlKI 54U_sip_udpsip cybercitydk!BxRR0T4VnEDl:K 50 100127in-addrarpa BSiin0T4VE[@@>5 G, 100127in-addrarpa  ' localhostB=0T4VnEl;!#حREGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp8886016-44b14fe3192.168.1.2;rport From: ;tag=87971a To: Call-ID: 29858147-465b0752@29858051-465b07b2 Contact: pel ;expires=1200;q=0.500 Expires: 1200 CSeq: 5 REGISTER Content-Length: 0 Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 B .  n0T4VE@71!#1SIP/2.0 401 Unauthorized Call-ID: 29858147-465b0752@29858051-465b07b2 CSeq: 5 REGISTER From: ;tag=87971a To: ;tag=00-04074-1701bac9-1daa0b4c5 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp8886016-44b14fe3192.168.1.2 WWW-Authenticate: Digest realm="sip.cybercity.dk",nonce="1701babd2ad677745e6d6bf15442a8e",opaque="1701a1351f70795",stale=false,algorithm=MD5 Content-Length: 0 B VV0T4VnEHl<K 54 _sip_udpsip cybercitydk!B VV0T4VnEHl=K 54 _sip_udpsip cybercitydk!B VV0T4VnEHl>K 54 _sip_udpsip cybercitydk!Bb VV0T4VnEHlGK  54 _sip_udpsip cybercitydk!BB VV0T4VnEHlHK  54 _sip_udpsip cybercitydk!B: RR0T4VnEDlIK  50 100127in-addrarpa B& iin0T4VE[@@>5 G' 100127in-addrarpa  ' localhostBt 0T4VnElJ"!#OREGISTER sip:sip.cybercity.dk SIP/2.0 Via: SIP/2.0/UDP 192.168.1.2;branch=z9hG4bKnp6658824-465059f1192.168.1.2;rport From: ;tag=659abf To: Call-ID: 29858147-465b0752@29858051-465b07b2 Contact: pel ;expires=1200;q=0.500 Expires: 1200 CSeq: 6 REGISTER Content-Length: 0 Authorization: Digest username="voi18062",realm="sip.cybercity.dk",uri="sip:192.168.1.2",nonce="1701babd2ad677745e6d6bf15442a8e",opaque="1701a1351f70795",nc="00000001",response="92f9402a5ee6660935cb1254a04a523a" Max-Forwards: 70 User-Agent: Nero SIPPS IP Phone Version 2.0.51.16 B=I VVn0T4VEH@7!#4!SIP/2.0 100 Trying Call-ID: 29858147-465b0752@29858051-465b07b2 CSeq: 6 REGISTER From: ;tag=659abf To: Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp6658824-465059f1192.168.1.2 Content-Length: 0 Bt n0T4VE@7>!#ەSIP/2.0 200 OK Call-ID: 29858147-465b0752@29858051-465b07b2 Contact: pel;q=0.500;expires=1200 CSeq: 6 REGISTER From: ;tag=659abf P-Associated-URI: To: ;tag=00-04087-1701bae7-76fb74995 Via: SIP/2.0/UDP 192.168.1.2;received=80.230.219.70;rport=5060;branch=z9hG4bKnp6658824-465059f1192.168.1.2 Content-Length: 0 B؇\\nENlKJ:[9b EFEDEJFPEEEPENEBEJEOCACACACACABM BVV0T4VnEHlLK 54̮] _sip_udpsip cybercitydk!B \\nENlMJ:[9b EFEDEJFPEEEPENEBEJEOCACACACACABM B#VV0T4VnEHlNK 54̮] _sip_udpsip cybercitydk!B\\nENlOI:[9b EFEDEJFPEEEPENEBEJEOCACACACACABM BXVV0T4VnEHlPK 54̮] _sip_udpsip cybercitydk!B <<n0T4V0T4Vd7 ! ;B **0T4Vnn0T4VBͲVV0T4VnEHlQK 54̮] _sip_udpsip cybercitydk!BVV0T4VnEHlRJ 54̮] _sip_udpsip cybercitydk!B nElSIcZd EEDADADCDEDGDFCACACACACACACACACA EFEDEJFPEEEPENEBEJEOCACACACACABNSMB%!!V2\MAILSLOT\BROWSE D002465UBRR0T4VnEDlTK 50100127in-addrarpa Byiin0T4VE[@@>5 Gx"100127in-addrarpa  ' localhostBX//0T4VnE!lU!# K B,VV0T4VnEHlVJ 54D0_sip_udpsip cybercitydk! BKVV0T4VnEHlWJ 54D0_sip_udpsip cybercitydk! B#VV0T4VnEHlXJ 54D0_sip_udpsip cybercitydk! B.VV0T4VnEHlYJ 54D0_sip_udpsip cybercitydk!BVEVV0T4VnEHlZJ 54D0_sip_udpsip cybercitydk!BOMRR0T4VnEDl[J 50<2100127in-addrarpa BNSiin0T4VE[@@>5 G<2100127in-addrarpa  ' localhostB/X//0T4VnE!l\!# K B:\\nENl]I:[4g EFEDEJFPEEEPENEBEJEOCACACACACABM BK\\nENl^I:[4g EFEDEJFPEEEPENEBEJEOCACACACACABM B \\nENl_I:[4g EFEDEJFPEEEPENEBEJEOCACACACACABM %BVV0T4VnEHl`J 54Y3_sip_udpsip cybercitydk!&BVV0T4VnEHlaJ 54Y3_sip_udpsip cybercitydk!(BQVV0T4VnEHlbJ 54Y3_sip_udpsip cybercitydk!*BtVV0T4VnEHlcJ 54Y3_sip_udpsip cybercitydk!,B|LL0T4VnE>ldJ 5*hM5sip cybercitydk-B:tLL0T4VnE>leJ 5*hM5sip cybercitydk.B"VV0T4VnEHlfJ 54Y3_sip_udpsip cybercitydk!/BLL0T4VnE>lgJ 5*hM5sip cybercitydk0Bn0T4VEr@@'5 ^`;M5sip cybercitydk ,!#,ns1,ns25B*<<n0T4V0T4Vd7 ! ;5B[**0T4Vnn0T4V6BCRR0T4VnEDlhJ 50yc6100127in-addrarpa 6BIiin0T4VE[@@>5 Gc6100127in-addrarpa  ' localhost6BN//0T4VnE!li!# K BBVV0T4VnEHlnJ 547~6_sip_udpsip cybercitydk!CBVV0T4VnEHloJ 547~6_sip_udpsip cybercitydk!sngrep-1.8.2/tests/ipip.pcap000066400000000000000000000061051464272443000160000ustar00rootroot00000000000000òӠaPV1PV& E 8@@{ g E}`'E|  |udINVITE sip:1bdaa608131517540001@172.28.1.3;transport=tcp SIP/2.0 Via: SIP/2.0/TCP 10.15.197.103:5090;branch=z9hG4bK8JQkQhxdSmM To: From: ;tag=jr57na6shh Call-Id: 1RLuVzzBClYCf2 CSeq: 6 INVITE Max-Forwards: 68 Contact: Content-Type: application/sdp Content-Length: 649 v=0 o=- 8566248303013635582 8175253998610074627 IN IP4 10.15.194.45 s=- c=IN IP4 10.15.194.45 t=0 0 m=audio 50232 RTP/AVP 0 111 109 18 8 96 101 103 9 a=rtpmap:0 PCMU/8000 a=rtpmap:111 OPUS/48000/2 a=fmtp:111 useinbandfec=1;usedtx=1 a=rtpmap:109 OPUS/16000 a=fmtp:109 useinbandfec=1;usedtx=1 a=rtpmap:18 G729/8000 a=fmtp:18 annexb=no a=rtpmap:8 PCMA/8000 a=rtpmap:96 iLBC/8000 a=fmtp:96 mode=20 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-15 a=rtpmap:103 telephone-event/16000 a=fmtp:103 0-15 a=rtpmap:9 G722/8000 a=sendrecv a=rtcp-fb:111 ccm tmmbr a=rtcp-fb:109 ccm tmmbr a=ssrc:2348060575 cname:G1C/FVTf7W2cspeyYobiWw== Ӡa|GPV& |ZE/  E>  gE'E|}$_ d|uSIP/2.0 183 Ringing Via: SIP/2.0/TCP 10.15.197.103:5090;branch=z9hG4bK8JQkQhxdSmM From: ;tag=jr57na6shh To: ;tag=to-tag Call-ID: 1RLuVzzBClYCf2 CSeq: 6 INVITE Content-Length: 263 Content-Type: application/sdp Contact: v=0 o=- 912505582455771910 149809613282366629 IN IP4 172.28.1.3 s=- c=IN IP4 172.28.1.3 t=0 0 m=audio 50234 RTP/AVP 0 101 a=rtpmap:0 PCMU/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-15 a=sendrecv a=ssrc:556397832 cname:JiIoj2YQRxWgqi5zCWrfjw== Ԡa4/PV& |ZE4  E>  gE'E}$| d2|SIP/2.0 200 OK Via: SIP/2.0/TCP 10.15.197.103:5090;branch=z9hG4bK8JQkQhxdSmM From: ;tag=jr57na6shh To: ;tag=to-tag Call-ID: 1RLuVzzBClYCf2 CSeq: 6 INVITE Content-Length: 263 Content-Type: application/sdp Contact: v=0 o=- 912505582455771910 149809613282366629 IN IP4 172.28.1.3 s=- c=IN IP4 172.28.1.3 t=0 0 m=audio 50234 RTP/AVP 0 101 a=rtpmap:0 PCMU/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-15 a=sendrecv a=ssrc:556397832 cname:JiIoj2YQRxWgqi5zCWrfjw== aPV1PV& E ;@@~K g E}'EQ$d }$d2BYE sip:1bdaa608131517540001@172.28.1.3;transport=tcp SIP/2.0 Via: SIP/2.0/TCP 10.15.197.103:5090;branch=z9hG4bK8d6XSN3vE2i To: ;tag=to-tag From: ;tag=jr57na6shh Call-Id: 1RLuVzzBClYCf2 CSeq: 16 BYE Contact: Content-Length: 0 sngrep-1.8.2/tests/ipv6frag.pcap000077500000000000000000000751411464272443000165740ustar00rootroot00000000000000òq\9bPVSUT`,@b\7 '!b\7 '5dw:{INVITE sip:08019200028@[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060 SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-0 From: sipp ;tag=397430SIPpTag0071846 To: mcr Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 1 INVITE Contact: sip:sipp@[fd17:625c:f037:2:a00:27ff:feb9:1521]:15060 Max-Forwards: 70 Subject: Performance Test Supported: 100rel,precondition Route: ,, Record-Route: P-Served-User: "uas" ;sescase=orig P-Asserted-Identity: "uas" Allow: INVITE,PRACK,CANCEL,BYE,UPDATE Content-Type: application/sdp Content-Length: 655 v=0 o=user1 53655765 2353687637 IN IP6 [fd17:625c:f037:2:a00:27ff:feb9:1521] s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:1521 t=0 0 m=audio 15062 RTP/AVP 100 101 0 120 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:101 AMR-WB/16000 a=fmtp:101 mode-set="0,1,2";octet-align=1 a=rtpmap:110 AMR/8000 a=fmtp:110 mode-set="0,1,2";octet-align=0 a=rtpmap:111 AMR/800\9b(;;PVS1,`,@b\7 '!b\7 '5dw0 a=fmtp:111 mode-set="0,1,2";octet-align=1 a=rtpmap:0 PCMU/8000 a=rtpmap:120 telephone-event/8000 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local none a=des:qos mandatory remote none a=curr:qos local none a=curr:qos remote none \9bPV4vou`2@b\7 '5b\7 '!:LSIP/2.0 100 Trying CSeq: 1 INVITE Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-0;received=fd17:625c:f037:2:a00:27ff:feb9:1521 Content-Length: 0 \9bPV4v: `&},@b\7 '5b\7 'B"TaW INVITE sip:08019200028@[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060 SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-f3b6705d5de367dfb415ff898550f9c2,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-0;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 1 INVITE Contact: Max-Forwards: 69 Subject: Performance Test Supported: 100rel,precondition Route: , Record-Route: , P-Served-User: "uas" ;sescase=orig P-Asserted-Identity: "uas" Allow: INVITE,PRACK,CANCEL,BYE,UPDATE X-Orig-Call-Record: TRUE Content-Type: application/sdp Content-Length: 629 v=0 o=user1 53655765 2353687637 IN IP6 fd17:625c:f037:2:a00:27ff:feb9:1521 s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4334 t=0 0 m=audio 30002 RTP/AVP 100 101 120 121 a=rtpmap:100 AMR-WB/1600\9bPV4v 0`&},@b\7 '5b\7 'B"T0 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:101 AMR-WB/16000 a=fmtp:101 mode-set="0,1,2";octet-align=1 a=rtpmap:110 AMR/8000 a=fmtp:110 mode-set="0,1,2";octet-align=0 a=rtpmap:111 AMR/8000 a=fmtp:111 mode-set="0,1,2";octet-align=1 a=rtpmap:120 telephone-event/8000 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local none a=des:qos mandatory remote none a=curr:qos local none a=curr:qos remote none \9bPV="`:@b\7 'B"b\7 '5a'SIP/2.0 183 Session Progress Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-f3b6705d5de367dfb415ff898550f9c2,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-0;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 1 INVITE Allow: INVITE,PRACK,CANCEL,BYE,UPDATE Record-Route: , Require: 100rel,precondition RSeq: 1 P-Early-Media: inactive Contact: Content-Type: application/sdp Content-Length: 274 v=0 o=user1 53655765 2353687637 IN IP6 [fd17:625c:f037:2:a00:27ff:feb9:4222] s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4222 t=0 0 m=audio 25062 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 \9b%PV4vF1`2M@b\7 '5b\7 '!:MOgSIP/2.0 183 Session Progress Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-0;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 1 INVITE Allow: INVITE,PRACK,CANCEL,BYE,UPDATE Record-Route: , Require: 100rel,precondition RSeq: 1 P-Early-Media: inactive Contact: Content-Type: application/sdp Content-Length: 272 v=0 o=user1 53655765 2353687637 IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4222 s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4334 t=0 0 m=audio 30004 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 \9brPVS'`@b\7 '!b\7 '5:PPRACK sip:[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060;transport=UDP SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-3 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 PRACK Max-Forwards: 70 Route: , Subject: Performance Test RAck: 1 2 INVITE Content-Length: 0 \9br..PV4vee`&}@b\7 '5b\7 'B"a{PRACK sip:[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060;transport=UDP SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-5952bebef7026469e082e7a4c1d98825,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-3;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 PRACK Max-Forwards: 69 Route: Subject: Performance Test RAck: 1 2 INVITE Content-Length: 0 \9bҮVVPV:2`:@b\7 'B"b\7 '5aSIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-5952bebef7026469e082e7a4c1d98825,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-3;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 PRACK \9bPV4ven`2@b\7 '5b\7 '!:LSIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-3;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 PRACK Content-Length: 0 \9bȱ~~PVS`F@b\7 '!b\7 '5:F$UPDATE sip:[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060;transport=UDP SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-5 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 3 UPDATE Max-Forwards: 70 Route: , Subject: Performance Test Content-Type: application/sdp Content-Length: 398 v=0 o=user1 53655765 2353687639 IN IP6 [fd17:625c:f037:2:a00:27ff:feb9:1521] s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:1521 t=0 0 m=audio 15062 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local sendrecv a=des:qos mandatory remote sendrecv a=curr:qos local sendrecv a=curr:qos remote none \9bݴPV4v:0`&}@b\7 '5b\7 'B"a|UPDATE sip:[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060;transport=UDP SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-b8fad594c0b08ad5a6af031f6e3515c5,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-5;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 3 UPDATE Max-Forwards: 69 Route: Subject: Performance Test Content-Type: application/sdp Content-Length: 396 v=0 o=user1 53655765 2353687639 IN IP6 fd17:625c:f037:2:a00:27ff:feb9:1521 s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4334 t=0 0 m=audio 30002 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local sendrecv a=des:qos mandatory remote sendrecv a=curr:qos local sendrecv a=curr:qos remote none \9bFPVes`:@b\7 'B"b\7 '5a SIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-b8fad594c0b08ad5a6af031f6e3515c5,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-5;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 3 UPDATE Content-Type: application/sdp Content-Length: 402 v=0 o=user1 53655765 2353687638 IN IP6 [fd17:625c:f037:2:a00:27ff:feb9:4222] s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4222 t=0 0 m=audio 25062 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local sendrecv a=des:qos mandatory remote sendrecv a=curr:qos local sendrecv a=curr:qos remote sendrecv \9bPV4v-1`2u@b\7 '5b\7 '!:uNSIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-5;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 3 UPDATE Content-Type: application/sdp Content-Length: 400 v=0 o=user1 53655765 2353687638 IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4222 s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4334 t=0 0 m=audio 30004 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local sendrecv a=des:qos mandatory remote sendrecv a=curr:qos local sendrecv a=curr:qos remote sendrecv \9bpc YYPV00`:!@b\7 'B"b\7 '5a!SIP/2.0 183 Session Progress Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-f3b6705d5de367dfb415ff898550f9c2,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-0;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 1 INVITE Allow: INVITE,PRACK,CANCEL,BYE,UPDATE Record-Route: , Require: 100rel,precondtion RSeq: 2 Contact: Content-Type: application/sdp Content-Length: 398 v=0 o=user1 53655765 2353687638 IN IP6 [fd17:625c:f037:2:a00:27ff:feb9:4222] s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4222 t=0 0 m=audio 25062 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local sendrecv a=des:qos mandatory remote sendrecv a=curr:qos local sendrecv a=curr:qos remote none \9bt PV4v52`2@b\7 '5b\7 '!:OSIP/2.0 183 Session Progress Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-0;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 1 INVITE Allow: INVITE,PRACK,CANCEL,BYE,UPDATE Record-Route: , Require: 100rel,precondtion RSeq: 2 Contact: Content-Type: application/sdp Content-Length: 396 v=0 o=user1 53655765 2353687638 IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4222 s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4334 t=0 0 m=audio 30004 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local sendrecv a=des:qos mandatory remote sendrecv a=curr:qos local sendrecv a=curr:qos remote none \9bv PVSƆ`@b\7 '!b\7 '5:]PRACK sip:[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060;transport=UDP SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-8 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 PRACK Contact: sip:sipp@[fd17:625c:f037:2:a00:27ff:feb9:1521]:15060 Max-Forwards: 70 Route: , Subject: Performance Test RAck: 2 1 INVITE Content-Length: 0 \9b;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 PRACK Contact: Max-Forwards: 69 Route: Subject: Performance Test RAck: 2 1 INVITE Content-Length: 0 \9b{ VVPV82`:@b\7 'B"b\7 '5aSIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-848e3adf6ca17e995a1b555c0f927d63,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-8;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 PRACK \9b~ PV4vae`2@b\7 '5b\7 '!:LSIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-8;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 PRACK Content-Length: 0 \9b@ PVS02`^@b\7 '!b\7 '5:^UPDATE sip:[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060;transport=UDP SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-10 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 3 UPDATE Max-Forwards: 70 Route: , Require: precondition Subject: Performance Test Content-Type: application/sdp Content-Length: 398 v=0 o=user1 53655765 2353687637 IN IP6 [fd17:625c:f037:2:a00:27ff:feb9:1521] s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:1521 t=0 0 m=audio 15062 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local sendrecv a=des:qos mandatory remote sendrecv a=curr:qos local sendrecv a=curr:qos remote none \9b PV4vnf`&}@b\7 '5b\7 'B"a|UPDATE sip:[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060;transport=UDP SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-db2b774d20cc03da0a52fd8ce24968f1,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-10;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 3 UPDATE Max-Forwards: 69 Route: Require: precondition Subject: Performance Test Content-Type: application/sdp Content-Length: 396 v=0 o=user1 53655765 2353687637 IN IP6 fd17:625c:f037:2:a00:27ff:feb9:1521 s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4334 t=0 0 m=audio 30002 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local sendrecv a=des:qos mandatory remote sendrecv a=curr:qos local sendrecv a=curr:qos remote none \9bG ??PVAa`:@b\7 'B"b\7 '5aySIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-db2b774d20cc03da0a52fd8ce24968f1,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-10;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 3 UPDATE Content-Type: application/sdp Content-Type: application/sdp Content-Length: 402 v=0 o=user1 53655765 2353687639 IN IP6 [fd17:625c:f037:2:a00:27ff:feb9:4222] s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4222 t=0 0 m=audio 25062 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local sendrecv a=des:qos mandatory remote sendrecv a=curr:qos local sendrecv a=curr:qos remote sendrecv \9bƊ PV4v00`2v@b\7 '5b\7 '!:vNSIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-10;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 3 UPDATE Content-Type: application/sdp Content-Length: 400 v=0 o=user1 53655765 2353687639 IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4222 s=- c=IN IP6 fd17:625c:f037:2:a00:27ff:feb9:4334 t=0 0 m=audio 30004 RTP/AVP 100 121 a=rtpmap:100 AMR-WB/16000 a=fmtp:100 mode-set="0,1,2";octet-align=0 a=rtpmap:121 telephone-event/16000 a=des:qos mandatory local sendrecv a=des:qos mandatory remote sendrecv a=curr:qos local sendrecv a=curr:qos remote sendrecv \9b PV00`:V@b\7 'B"b\7 '5aVpSIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-f3b6705d5de367dfb415ff898550f9c2,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-0;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 1 INVITE Allow: INVITE,PRACK,CANCEL,BYE,UPDATE Record-Route: , Contact: Content-Length: 0 \9bPVeb`:V@b\7 'B"b\7 '5aVpSIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-f3b6705d5de367dfb415ff898550f9c2,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-0;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0271847;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 1 INVITE Allow: INVITE,PRACK,CANCEL,BYE,UPDATE Record-Route: , Contact: Content-Length: 0 \9b PV4vd1`2@b\7 '5b\7 '!:MSIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-0;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 1 INVITE Allow: INVITE,PRACK,CANCEL,BYE,UPDATE Record-Route: , Contact: Content-Length: 0 \9bL PVS21`@b\7 '!b\7 '5:WACK sip:[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060;transport=UDP SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-13 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 1 ACK Contact: sip:sipp@[fd17:625c:f037:2:a00:27ff:feb9:1521]:15060 Max-Forwards: 70 Route: , Subject: Performance Test Content-Length: 0 \9bm ZZPV4v`&}"@b\7 '5b\7 'B"a"{=ACK sip:[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060;transport=UDP SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-806649e0adce581a35218e4b1baa416d,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-13;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 1 ACK Contact: Max-Forwards: 69 Route: Subject: Performance Test Content-Length: 0 >]9bu PVSfd`@b\7 '!b\7 '5:_BYE sip:[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060;transport=UDP SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-15 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 BYE Contact: sip:sipp@[fd17:625c:f037:2:a00:27ff:feb9:1521]:15060 Max-Forwards: 70 Route: , Subject: Performance Test Content-Length: 0 >]9b ZZPV4vip`&}"@b\7 '5b\7 'B"a"{=BYE sip:[fd17:625c:f037:2:a00:27ff:feb9:4222]:25060;transport=UDP SIP/2.0 Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-875833642fa62c3f863167cfcbb74dff,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-15;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 BYE Contact: Max-Forwards: 69 Route: Subject: Performance Test Content-Length: 0 >]9b PVf:`:z@b\7 'B"b\7 '5azN,SIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:3519]:5062;branch=z9hG4bK-333138-875833642fa62c3f863167cfcbb74dff,SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-15;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 BYE Contact: Content-Length: 0 >]9b DDPV4va0`2 @b\7 '5b\7 '!: M&SIP/2.0 200 OK Via: SIP/2.0/UDP [fd17:625c:f037:2:a00:27ff:feb9:1521]:15060;branch=z9hG4bK-397430-71846-15;received=fd17:625c:f037:2:a00:27ff:feb9:1521 From: "sipp" ;tag=397430SIPpTag0071846 To: "mcr" ;tag=1632476SIPpTag0171847 Call-ID: 71846-1647924829-397430@fd17:625c:f037:2:a00:27ff:feb9:1521 CSeq: 2 BYE Contact: Content-Length: 0 sngrep-1.8.2/tests/test_001.c000066400000000000000000000022561464272443000157000ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_001.c * @author Ivan Alonso [aka Kaian] * * Basic read from file test */ const char keys[] = { 27, 10, 0 }; #include "test_input.c" sngrep-1.8.2/tests/test_002.c000066400000000000000000000041571464272443000157030ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_002.c * @author Ivan Alonso [aka Kaian] * * Basic Call list testing */ const char keys[] = { /* Swap some options */ 'c', 'l', 'p', 'p', /* Select some dialogs */ 107, 32, 107, 107, 32, 107, 107, 107, 32, 107, 107, /* Enter Call Flow */ 10, 27, /* Enter Call Raw */ 'R', 27, /* Enter Filter screen */ 'F', 27, /* Enter Column screen */ 't', 27, /* Unselect some dialogs */ 32, 107, 32, /* Move beyond list limits */ 107, 107, 107, 107, 107, 106, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 4, 4, 2, 2, 4, 2, 4, 2, /* Enter help screen */ 'h', 100, /* Enter save screen */ 's', 20, 30, 40, 50, 27, /* Enter display filter */ '/', 20, 30, 40, 40, 10, '/', 27, /* Enter Call Flow once again */ 10, 27, /* Exit */ 27, 10, 0 }; #include "test_input.c" sngrep-1.8.2/tests/test_003.c000066400000000000000000000045401464272443000157000ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_003.c * @author Ivan Alonso [aka Kaian] * * Basic Call Flow testing */ const char keys[] = { /* Select some dialogs */ 32, 107, 32, 107, /* Enter Call Flow */ 10, 27, /* Move arrow messages */ 107, 107, 107, 107, 107, 106, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 4, 4, 2, 2, 4, 2, 4, 2, /* Reenter Call Flow */ 10, /* Swap colors */ 'c', 'c', 'c', 'c', 'c', 'c', /* Enter Extended Call Flow */ 'x', 'x', 'x', 'x', /* Compress column display */ 's', /* Toggle media display */ 'd', 'd', 'd', 'd', 'd', /* Toggle Only SDP */ 'D', /* Enter Call Raw (all dialogs) */ 'R', 27, /* Enter Call Raw (single message) */ 10, 27, /* Enter help screen */ 'h', 100, /* Toggle raw screen */ 't', 't', 't', 't', /* Change Raw size */ '0', '0', '0', '0', '0', '0', '0', '0', '9', '9', '9', '9', '9', '9', '9', '9', /* Reset Raw size */ 'T', 'T', /* Leave Call Flow */ 27, /* Exit */ 27, 10, 0 }; #include "test_input.c" sngrep-1.8.2/tests/test_004.c000066400000000000000000000043031464272443000156760ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_004.c * @author Ivan Alonso [aka Kaian] * * Basic Call Raw testing */ const char keys[] = { /* Select some dialogs */ 32, 107, 107, 107, 32, 107, /* Show Raw panel */ 'r', /* Cicle options */ 'c', 'c', 'c', 'c', 'c', 'c', 'a', 'a', 'a', 'a', 'a', 'a', 'l', 'l', 'l', 'C', 'C', 'C', /* Move through Raw panel */ 107, 107, 107, 107, 107, 106, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 4, 4, 2, 2, 4, 2, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* Enter save screen */ 's', 20, 30, 40, 50, 60, 27, /* Leave call Raw */ 27, /* Exit */ 27, 10, 0 }; #include "test_input.c" sngrep-1.8.2/tests/test_005.c000066400000000000000000000040331464272443000156770ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_005.c * @author Ivan Alonso [aka Kaian] * * Basic Column selection testing */ const char keys[] = { /* Show Raw panel */ 't', /* Cycle through fields */ 9, 9, 9, 9, 9, 9, 9, 107, 107, 9, 106, /* Move through Column list */ 107, 107, 107, 107, 107, 106, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, /* Move items up and down */ '-', '-', '-', '-', 107, 107, '+', '+', '+', '+', 107, 107, '-', '-', '-', '-', /* Apply new settings */ 10, /* Exit */ 27, 10, 0 }; #include "test_input.c" sngrep-1.8.2/tests/test_006.c000066400000000000000000000026411464272443000157030ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_006.c * @author Ivan Alonso [aka Kaian] * * Basic Message diff testing */ const char keys[] = { /* Select some dialog */ 107, 107, 107, 32, 10, /* Move Select some messages */ 107, 107, 32, 32, 107, 32, 107, 107, 107, 106, 32, /* Leave Diff screen */ 27, 27, /* Exit */ 27, 10, 0 }; #include "test_input.c" sngrep-1.8.2/tests/test_007.c000066400000000000000000000055111464272443000157030ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_vector.c * @author Ivan Alonso [aka Kaian] * * Basic testing of vector structures */ #include "config.h" #include #include #include "../src/vector.h" #include "../src/util.h" int main () { vector_t *vector; // Basic Vector append/remove test vector = vector_create(10, 10); assert(vector); assert(vector_count(vector) == 0); vector_append(vector, 0); assert(vector_count(vector) == 0); vector_append(vector, sng_malloc(1024)); assert(vector_count(vector) == 1); assert(vector_first(vector) == vector_item(vector, 0)); vector_remove(vector, vector_first(vector)); assert(vector_count(vector) == 0); assert(vector_first(vector) == vector_item(vector, 0)); // Vector overflow test vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); // Next append requires memory reallocation vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); vector_append(vector, sng_malloc(32)); // Expected vector size assert(vector_count(vector) == 16); // Expected empty position assert(vector_item(vector, vector_count(vector)) == 0); // Remove position (use generic destroyer) vector_set_destroyer(vector, vector_generic_destroyer); vector_remove(vector, vector_item(vector, 12)); assert(vector_count(vector) == 15); return 0; } sngrep-1.8.2/tests/test_008.c000066400000000000000000000031001464272443000156740ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_008.c * @author Ivan Alonso [aka Kaian] * * Test for sorting columns based on standard attributes */ const char keys[] = { /* show sort menu */ 60, /* move arround menu */ 107, 107, 106, 106, 106, 107, /* select sort field */ 10, /* move arround call list */ 107, 107, 106, 106, 106, 107, /* change sort order */ 106, 106, 60, 10, /* change sort order */ 122, 122, /* Leave */ 27, 10, 0 }; #include "test_input.c" sngrep-1.8.2/tests/test_009.c000066400000000000000000000031021464272443000156770ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_009.c * @author Ivan Alonso [aka Kaian] * * Test for adding a new attribute column and sorting using it. */ const char keys[] = { /* show sort menu */ 60, /* select sort field */ 106, 106, 10, /* Enter Column screen */ 116, 27, /* add a new column */ 106, 106, 106, 32, /* select new sort attribute */ 60, 106, 106, 106, 106, 106, 106, 10, /* swap order */ 122, 122, /* Leave */ 27, 10, 0 }; #include "test_input.c" sngrep-1.8.2/tests/test_010.c000066400000000000000000000064501464272443000157000ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_vector.c * @author Ivan Alonso [aka Kaian] * * Basic testing of vector structures */ #include "config.h" #include #include #include "../src/hash.h" int main () { htable_t *table; table = htable_create(10); assert(table); // Check a key is not found assert(htable_find(table, "notfoud") == NULL); // Check a key can be added htable_insert(table, "key", "data"); const char *data = htable_find(table, "key"); // And found assert(strcmp(data, "data") == 0); // Try filling all the buckets htable_insert(table, "key1", "data1"); htable_insert(table, "key2", "data2"); htable_insert(table, "key3", "data3"); htable_insert(table, "key4", "data4"); htable_insert(table, "key5", "data5"); htable_insert(table, "key6", "data6"); htable_insert(table, "key7", "data7"); htable_insert(table, "key8", "data8"); htable_insert(table, "key9", "data9"); htable_insert(table, "key10", "data10"); htable_insert(table, "key11", "data11"); htable_insert(table, "key12", "data12"); htable_insert(table, "key13", "data13"); htable_insert(table, "key14", "data14"); htable_insert(table, "key15", "data15"); // Find one entry const char *data7 = htable_find(table, "key7"); assert(strcmp(data7, "data7") == 0); // Remove one entry htable_remove(table, "key7"); assert(htable_find(table, "key7") == NULL); // Find another entries const char *data5 = htable_find(table, "key5"); assert(strcmp(data5, "data5") == 0); const char *data10 = htable_find(table, "key10"); assert(strcmp(data10, "data10") == 0); // Remove all entries htable_remove(table, "key1"); htable_remove(table, "key2"); htable_remove(table, "key3"); htable_remove(table, "key4"); htable_remove(table, "key5"); htable_remove(table, "key6"); htable_remove(table, "key7"); htable_remove(table, "key8"); htable_remove(table, "key9"); htable_remove(table, "key10"); htable_remove(table, "key11"); htable_remove(table, "key12"); htable_remove(table, "key13"); htable_remove(table, "key14"); htable_remove(table, "key15"); // Search a not found entry assert(htable_find(table, "key7") == NULL); // Destroy the table htable_destroy(table); return 0; } sngrep-1.8.2/tests/test_011.c000066400000000000000000000025161464272443000157000ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_011.c * @author Evgeny Khramtsov * * IP-IP tunnel test from ipip.pcap */ const char keys[] = { /* Enter Call Flow */ 10, /* Leave Call Flow */ 27, /* Exit */ 27, 10, 0 }; #define TEST_PCAP_INPUT "ipip.pcap" #include "test_input.c" sngrep-1.8.2/tests/test_input.c000066400000000000000000000046321464272443000165370ustar00rootroot00000000000000/************************************************************************** ** ** sngrep - SIP Messages flow viewer ** ** Copyright (C) 2013-2018 Ivan Alonso (Kaian) ** Copyright (C) 2013-2018 Irontec SL. All rights reserved. ** ** 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 . ** ****************************************************************************/ /** * @file test_input.c * @author Ivan Alonso [aka Kaian] * * Basic input injector for sngrep testing */ #include "config.h" #include #include #include #include #ifndef TEST_MAX_DURATION #define TEST_MAX_DURATION 60 #endif #ifndef TEST_INITIAL_WAIT #define TEST_INITIAL_WAIT 1000 * 1200 #endif #ifndef TEST_KEY_DELAY #define TEST_KEY_DELAY 3000 #endif #ifndef TEST_PCAP_INPUT #define TEST_PCAP_INPUT "aaa.pcap" #endif /* keys array needs to be of type "char" */ const char keys[]; int main() { int ppipe[2]; int unused, ret = 0; int child; unused = pipe(ppipe); // Max test duration alarm(TEST_MAX_DURATION); if ((child = fork()) < 0) { fprintf(stderr, "Fatal: unable to fork test.\n"); return 127; } else if (child == 0) { // Child process, run sngrep with test pcap dup2(ppipe[0], STDIN_FILENO); #ifdef CMAKE_CURRENT_BINARY_DIR char *argv[] = { CMAKE_CURRENT_BINARY_DIR "/sngrep", "-I", TEST_PCAP_INPUT, 0 }; #else char *argv[] = { "../src/sngrep", "-I", TEST_PCAP_INPUT, 0 }; #endif execv(argv[0], argv); } else { // Parent process, send keys to child stdin usleep(TEST_INITIAL_WAIT); int i; for (i = 0; keys[i]; i++) { unused = write(ppipe[1], &keys[i], sizeof(char)); usleep(TEST_KEY_DELAY); } unused = wait(&ret); } return ret; }