pax_global_header00006660000000000000000000000064131343752240014516gustar00rootroot0000000000000052 comment=647393a5e4b49551f34e213c91d80b675c60a7f6 belle-sip-1.6.3/000077500000000000000000000000001313437522400134015ustar00rootroot00000000000000belle-sip-1.6.3/.clang-format000066400000000000000000000036721313437522400157640ustar00rootroot00000000000000--- # BasedOnStyle: LLVM AccessModifierOffset: -2 AlignAfterOpenBracket: true AlignEscapedNewlinesLeft: false AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: false AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackArguments: true BinPackParameters: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Attach BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false ColumnLimit: 120 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] IndentCaseLabels: false IndentFunctionDeclarationAfterType: false IndentWidth: 4 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: true Language: Cpp MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 4 UseTab: Always ... belle-sip-1.6.3/.cproject000066400000000000000000000456521313437522400152270ustar00rootroot00000000000000 make install true true true make CFLAGS="-g -Wall " -j3 all true true true make CFLAGS="-g -Wall " -j3 test true true true belle-sip-1.6.3/.git-pre-commit000077500000000000000000000043501313437522400162440ustar00rootroot00000000000000#!/bin/bash # This hook purpose is to keep coding style consistent between all developers # It is automatically installed in .git/hooks folder by cmake on first run. # From https://github.com/tatsuhiro-t/nghttp2/blob/master/pre-commit function invalid-format-detected { cat git-clang-format.diff echo "*****************" echo "$0: Invalid coding style detected (see git-clang-format.diff for issues). Please correct it using one of the following:" echo "1) Apply patch located at git-clang-format.diff using:" echo " cd $(git rev-parse --show-toplevel) && $1" echo "2) Use clang-format to correctly format source code using:" echo " $2" echo "3) Reformat these lines manually." echo "*** Aborting commit.***" exit 1 } function git-clang-format-diffing { format_diff=$(which git-clang-format) format_diff_options="--style=file" #only diffing commited files, ignored staged one $format_diff $format_diff_options --diff $(git --no-pager diff --cached --name-status | grep -v '^D' | cut -f2) > git-clang-format.diff if ! grep -q -E '(no modified files to format|clang-format did not modify any files)' git-clang-format.diff; then invalid-format-detected "git apply git-clang-format.diff" "clang-format $format_diff_options -i " fi } function clang-format-diff-diffing { format_diff=$(find /usr/bin/ -name 'clang-format-diff*' -type f | tail -n1) format_diff_options="-style file" git diff-index --cached --diff-filter=ACMR -p HEAD -- | $format_diff $format_diff_options -p1 > git-clang-format.diff if [ -s git-clang-format.diff ]; then invalid-format-detected "patch -p0 < git-clang-format.diff" "${format_diff/-diff/} $format_diff_options -i " fi } set -e if which git-clang-format &>/dev/null; then git-clang-format-diffing $@ elif [ ! -z "$(find /usr/bin/ /usr/local/bin/ /opt/bin/ -name 'clang-format-diff*' -type f 2>/dev/null)" ]; then # Warning! We need at least version 1.6... clang-format-diff-diffing $@ else echo "$0: Please install clang-format (coding style checker) - could not find git-clang-format nor clang-format-diff in PATH. Skipping code verification..." exit 0 fi belle-sip-1.6.3/.gitignore000066400000000000000000000012621313437522400153720ustar00rootroot00000000000000INSTALL aclocal.m4 *~ configure Makefile.in Makefile build-aux m4 autom4te.cache *.o *.lo *.la config.log config.status .deps .libs libtool compile depcomp missing install-sh config.guess config.h.in config.sub ltmain.sh stamp-h1 .anjuta belle-sip.anjuta .kdev4/ belle-sip.kdev4 config.h src/specs.c belle-sip.pc belle-sip.spec html/ *.orig tester/belle_sip_object_describe tester/belle_sip_parse tester/belle_sip_tester tester/belle_http_get tester/belle_sip_resolve tester/CUnitAutomated-Results.xml tester/download.tar.gz tester/tmp_resolv.conf tester/belle_sip_tester_crt tester/CUnitAutomated-Results.xml.tmp-Results.xml belle-sip-tests.log git-clang-format.diff .bc_tester_utils.tmp belle-sip-1.6.3/.project000066400000000000000000000053541313437522400150570ustar00rootroot00000000000000 belle-sip org.eclipse.cdt.managedbuilder.core.genmakebuilder clean,full,incremental, ?name? org.eclipse.cdt.make.core.append_environment true org.eclipse.cdt.make.core.autoBuildTarget all org.eclipse.cdt.make.core.buildArguments CFLAGS="-g -Wall " V=1 org.eclipse.cdt.make.core.buildCommand make org.eclipse.cdt.make.core.cleanBuildTarget clean org.eclipse.cdt.make.core.contents org.eclipse.cdt.make.core.activeConfigSettings org.eclipse.cdt.make.core.enableAutoBuild false org.eclipse.cdt.make.core.enableCleanBuild true org.eclipse.cdt.make.core.enableFullBuild true org.eclipse.cdt.make.core.fullBuildTarget all org.eclipse.cdt.make.core.stopOnError true org.eclipse.cdt.make.core.useDefaultBuildCmd false org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder full,incremental, org.eclipse.cdt.core.cnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 1329213830469 22 org.eclipse.ui.ide.multiFilter 1.0-name-matches-false-false-*.lo 1329213830473 22 org.eclipse.ui.ide.multiFilter 1.0-name-matches-false-false-*.o belle-sip-1.6.3/AUTHORS000066400000000000000000000001101313437522400144410ustar00rootroot00000000000000Belledonne Communications SARL belle-sip-1.6.3/CMakeLists.txt000066400000000000000000000204131313437522400161410ustar00rootroot00000000000000 # CMakeLists.txt # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ cmake_minimum_required(VERSION 3.0) project(belle-sip VERSION 1.6.3 LANGUAGES C CXX) set(PACKAGE "${PROJECT_NAME}") set(PACKAGE_NAME "${PROJECT_NAME}") set(PACKAGE_VERSION "${PROJECT_VERSION}") set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") set(PACKAGE_BUGREPORT "jehan.monnier@linphone.org") set(PACKAGE_TARNAME "${PROJECT_NAME}") set(PACKAGE_URL "") set(VERSION "${PACKAGE_VERSION}") option(ENABLE_SHARED "Build shared library." YES) option(ENABLE_STATIC "Build static library." YES) option(ENABLE_RTP_MAP_ALWAYS_IN_SDP "Always include rtpmap in SDP." OFF) option(ENABLE_STRICT "Build with strict compile options." YES) option(ENABLE_TUNNEL "Enable tunnel support" OFF) option(ENABLE_TESTS "Enable compilation of tests" ON) include(CheckLibraryExists) include(CheckSymbolExists) include(CheckCSourceCompiles) include(CMakePushCheckState) include(GNUInstallDirs) if(NOT CMAKE_INSTALL_RPATH AND CMAKE_INSTALL_PREFIX) set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}) message(STATUS "Setting install rpath to ${CMAKE_INSTALL_RPATH}") endif() set(MSVC_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/MSVC") if(MSVC) list(APPEND CMAKE_REQUIRED_INCLUDES ${MSVC_INCLUDE_DIR}) endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") check_library_exists("dl" "dlopen" "" HAVE_LIBDL) check_library_exists("rt" "clock_gettime" "" HAVE_LIBRT) cmake_push_check_state(RESET) check_symbol_exists("res_ndestroy" "resolv.h" HAVE_RES_NDESTROY) set(CMAKE_REQUIRED_LIBRARIES resolv) check_c_source_compiles("#include int main(int argc, char *argv[]) { res_getservers(NULL,NULL,0); return 0; }" HAVE_RES_GETSERVERS) if(HAVE_RES_NDESTROY AND HAVE_RES_GETSERVERS) set(HAVE_RESINIT 1) endif() cmake_pop_check_state() find_package(Threads) find_package(Zlib) find_package(Antlr3 REQUIRED) cmake_push_check_state(RESET) set(CMAKE_REQUIRED_INCLUDES ${ANTLR3C_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${ANTLR3C_LIBRARIES}) check_symbol_exists("antlr3StringStreamNew" "antlr3.h" HAVE_ANTLR_STRING_STREAM_NEW) cmake_pop_check_state() if(ENABLE_RTP_MAP_ALWAYS_IN_SDP) set(BELLE_SDP_FORCE_RTP_MAP 1) endif() if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) set(BcToolbox_FIND_COMPONENTS tester) include("${EP_bctoolbox_CONFIG_DIR}/BcToolboxConfig.cmake") else() find_package(BcToolbox 0.5.0 REQUIRED OPTIONAL_COMPONENTS tester) endif() if(ENABLE_TUNNEL) if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) include("${EP_tunnel_CONFIG_DIR}/TunnelConfig.cmake") else() find_package(Tunnel) endif() if(TUNNEL_FOUND) set(HAVE_TUNNEL 1) else() message(WARNING "Could not find the tunnel library!") set(ENABLE_TUNNEL OFF CACHE BOOL "Enable tunnel support" FORCE) endif() endif() if(ZLIB_FOUND) set(HAVE_ZLIB 1) endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) set(prefix ${CMAKE_INSTALL_PREFIX}) set(exec_prefix ${prefix}/bin) set(libdir ${prefix}/lib) set(includedir ${prefix}/include) get_filename_component(antlr3c_library_path "${ANTLR3C_LIBRARIES}" PATH) set(LIBS_PRIVATE "-L${antlr3c_library_path} -lantlr3c") get_filename_component(bctoolbox_library_path "${BCTOOLBOX_CORE_LIBRARIES}" PATH) set(LIBS_PRIVATE "${LIBS_PRIVATE} -L${bctoolbox_library_path} -lbctoolbox") if(ZLIB_FOUND) set(REQUIRES_PRIVATE "${REQUIRES_PRIVATE} z") endif() if(HAVE_LIBDL) set(LIBS_PRIVATE "${LIBS_PRIVATE} -ldl") endif() if(HAVE_LIBRT) set(LIBS_PRIVATE "${LIBS_PRIVATE} -lrt") endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/belle-sip.pc.in ${CMAKE_CURRENT_BINARY_DIR}/belle-sip.pc) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/belle-sip.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") include_directories( include src ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src ${BCTOOLBOX_CORE_INCLUDE_DIRS} ) if(TUNNEL_FOUND) include_directories(${TUNNEL_INCLUDE_DIRS}) endif() if(ZLIB_FOUND) include_directories(${ZLIB_INCLUDE_DIRS}) endif() if(MSVC) include_directories(${MSVC_INCLUDE_DIR}) endif() add_definitions("-DHAVE_CONFIG_H") set(STRICT_OPTIONS_CPP ) set(STRICT_OPTIONS_C ) set(STRICT_OPTIONS_OBJC "-fmodules") if(MSVC) list(APPEND STRICT_OPTIONS_CPP "/wd4068") # Disable "unknown pragma" warnings from antlr list(APPEND STRICT_OPTIONS_CPP "/wd4129") # Disable "unrecognized character escape sequence" warnings from antlr if(ENABLE_STRICT) list(APPEND STRICT_OPTIONS_CPP "/WX") endif() else() list(APPEND STRICT_OPTIONS_CPP "-Wall" "-Wuninitialized" "-Wno-error=deprecated-declarations") if(CMAKE_C_COMPILER_ID STREQUAL "Clang") list(APPEND STRICT_OPTIONS_CPP "-Wno-error=unknown-warning-option" "-Qunused-arguments" "-Wno-tautological-compare" "-Wno-builtin-requires-header" "-Wno-unused-function" "-Wno-gnu-designator" "-Wno-array-bounds") elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU") list(APPEND STRICT_OPTIONS_CPP "-Wno-error=pragmas") endif() if(APPLE) list(APPEND STRICT_OPTIONS_CPP "-Wno-error=unknown-warning-option" "-Qunused-arguments" "-Wno-tautological-compare" "-Wno-unused-function" "-Wno-array-bounds") endif() if(ENABLE_STRICT) list(APPEND STRICT_OPTIONS_CPP "-Werror" "-Wextra" "-Wno-unused-parameter" "-Wno-error=unknown-pragmas" "-Wuninitialized" "-fno-strict-aliasing") list(APPEND STRICT_OPTIONS_C "-Wdeclaration-after-statement" "-Wstrict-prototypes -Wno-missing-field-initializers" "-Wno-error=unused-result") endif() # this warning is generated by antlr so ignore it for now list(APPEND STRICT_OPTIONS_C "-Wno-strict-prototypes" "-Wno-error=sign-compare") endif() if(STRICT_OPTIONS_CPP) list(REMOVE_DUPLICATES STRICT_OPTIONS_CPP) endif() if(STRICT_OPTIONS_C) list(REMOVE_DUPLICATES STRICT_OPTIONS_C) endif() set(LINK_FLAGS ) if(APPLE) list(APPEND LINK_FLAGS "-framework Foundation") if(IOS) list(APPEND LINK_FLAGS "-framework CoreFoundation" "-framework CFNetwork" "-framework UIKit") endif() endif() string(REPLACE ";" " " LINK_FLAGS_STR "${LINK_FLAGS}") set(BELLESIP_CPPFLAGS ${BCTOOLBOX_CPPFLAGS}) if(ENABLE_STATIC) list(APPEND BELLESIP_CPPFLAGS "-DBELLESIP_STATIC") endif() if(BELLESIP_CPPFLAGS) list(REMOVE_DUPLICATES BELLESIP_CPPFLAGS) add_definitions(${BELLESIP_CPPFLAGS}) endif() add_definitions("-DBELLESIP_EXPORTS") if(CMAKE_SYSTEM_NAME STREQUAL "WindowsPhone" OR CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") add_definitions( -DHAVE_COMPILER_TLS -DUSE_FIXED_NAMESERVERS -DUSE_GETADDRINFO_FALLBACK ) endif() if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) set(EXPORT_TARGETS_NAME "LinphoneBuilder") else() set(EXPORT_TARGETS_NAME "BelleSIP") endif() add_subdirectory(include) add_subdirectory(src) if(ENABLE_TESTS AND BCTOOLBOX_TESTER_FOUND) enable_testing() add_subdirectory(tester) endif() include(CMakePackageConfigHelpers) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/BelleSIPConfigVersion.cmake" VERSION ${PACKAGE_VERSION} COMPATIBILITY AnyNewerVersion ) export(EXPORT ${EXPORT_TARGETS_NAME}Targets FILE "${CMAKE_CURRENT_BINARY_DIR}/BelleSIPTargets.cmake" ) configure_file(cmake/BelleSIPConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/BelleSIPConfig.cmake" @ONLY ) set(CONFIG_PACKAGE_LOCATION "${CMAKE_INSTALL_DATADIR}/BelleSIP/cmake") install(EXPORT ${EXPORT_TARGETS_NAME}Targets FILE BelleSIPTargets.cmake DESTINATION ${CONFIG_PACKAGE_LOCATION} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/BelleSIPConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/BelleSIPConfigVersion.cmake" DESTINATION ${CONFIG_PACKAGE_LOCATION} ) add_subdirectory(build) belle-sip-1.6.3/COPYING000066400000000000000000000431271313437522400144430ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS 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 convey 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 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision 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, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This 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 Library General Public License instead of this License. belle-sip-1.6.3/ChangeLog000066400000000000000000000000001313437522400151410ustar00rootroot00000000000000belle-sip-1.6.3/Makefile.am000066400000000000000000000011421313437522400154330ustar00rootroot00000000000000#ACLOCAL_AMFLAGS = -I m4 SUBDIRS=include src tester all-local: belle-sip.spec belle-sip.spec: belle-sip.spec.in test: cd tester $(MAKE) -C tester test rpm: $(MAKE) dist TAR_OPTIONS=--wildcards rpmbuild -ta --clean --rmsource --rmspec $(PACKAGE)-$(VERSION).tar.gz pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = belle-sip.pc EXTRA_DIST= \ belle-sip.spec \ belle-sip.spec.in \ autogen.sh \ CMakeLists.txt \ cmake/BelleSIPConfig.cmake.in \ cmake/FindAntlr3.cmake \ cmake/FindZlib.cmake \ config.h.cmake \ include/CMakeLists.txt \ src/CMakeLists.txt \ tester/CMakeLists.txt \ README.md belle-sip-1.6.3/NEWS000066400000000000000000000034011313437522400140760ustar00rootroot00000000000000belle_sip-1.6.2 -- July 21th, 2017 * Build fix for Windows belle_sip-1.6.2 -- July 20th, 2017 * Bug fixes belle_sip-1.6.1 -- March 2nd, 2017 * Fix bug around nsswitch.conf parsing on GNU/Linux belle-sip-1.6.0 -- February 23th, 2017 belle-sip-1.5.0 -- August 8th, 2016 * move general purpose and encryption related functions to bctoolbox, which becomes a mandatory dependency. * mbedTLS support through bctoolbox * SUBSCRIBE/NOTIFY dialog improvements: can be created upon reception of NOTIFY (as requested by RFC), automatic deletion when dialog expires. * fix: retransmit 200Ok of a reINVITE (was formely only done for initial INVITE) * add support for zlib in body handling belle-sip-1.4.2 -- November 2nd, 2015 * Support of Accept, Content-Disposition and Supported headers * Bug fixes belle-sip-1.4.1 -- May 6th, 2015 * Bug fixes belle-sip-1.4.0 -- March 11th, 2015 * support of display names of type (token LWS)* instead of just token * support for absolute URIs belle-sip-1.3.3 -- September 18th, 2014 * DNS SRV weight selection * support of multipart messages * support of Wake Locks on Android * support of backslash escaped display name belle-sip-1.3.0 -- February 20th, 2014 -- initial release of belle-sip * RFC3261 compliant implementation of SIP parser, writer, transactions and dialog layers. * http client api * support of client TLS certificate * fully asynchronous transport layer (UDP, TCP, TLS) * fully asynchronous DNS resolution with SRV * full dual-stack IPv6 support * SIP transaction state machines with lastest corrections (RFC6026) * automatic management of request refreshes with network disconnection resiliency thanks to the "refresher" object * supported platforms: Linux, Mac OSX, windows XP+, iOS, Android, Blackberry 10. belle-sip-1.6.3/README.centos000077500000000000000000000015161313437522400155610ustar00rootroot00000000000000Belle-sip rpm can be built applying the following procedure: centos6.4 *********** *antlr.jar* Belle sip needs antlr.jar to generate parser code. A prebuilt jar is available from the antlr git and should be manually install using the following command: git clone -b linphone git://git.linphone.org/antlr3 sudo mkdir /usr/local/share/java sudo cp antlr3/antlr-3.4-complete.jar /usr/local/share/java/antlr.jar *libantlr3c* Belle sip uses libantlr3c version 3.2 or above. This dependency cannot be easely met on centos 6.4. To manually build libantlr3c on a 64 bits system, checkout source code using: cd antlr3/runtime/C ./autogen.sh ./configure --disable-shared --enable-64bit --with-pic make sudo make install Now, back to belle-sip ./autogen.sh ./configure make rpm belle-sip-1.6.3/README.md000066400000000000000000000045551313437522400146710ustar00rootroot00000000000000Belle-sip ========= Overview -------- Belle-sip is a SIP (RFC3261) implementation written in C, with an object oriented API. Please check "NEWS" file for an overview of current features. Copyright 2012-2014, Belledonne Communications SARL , all rights reserved. Belle-sip is distributed to everyone under the GNU GPLv2 (see COPYING file for details). Incorporating belle-sip within a closed source project is not possible under the GPL. Commercial licensing can be purchased for that purpose from [Belledonne Communications](http://www.belledonne-communications.com). Dependencies ------------ ### Build-time dependencies These are required to generate a C sourcefile from SIP grammar using [antlr3](http://www.antlr3.org/) generator: * [Java SE](http://www.oracle.com/technetwork/java/javase/downloads/index.html) or OpenJDK * [antlr 3.4](https://github.com/antlr/website-antlr3/blob/gh-pages/download/antlr-3.4-complete.jar) ### Runtime dependencies - *libantlr3c* version 3.2 or 3.4 - *bctoolbox* (git://git.linphone.org/bctoolbox.git or ) ### Under Debian/Ubuntu apt-get install libantlr3c-dev antlr3 ### Under MacOS X using HomeBrew brew install libantlr3.4c homebrew/versions/antlr3 Building bctoolbox with CMake ----------------------------- cmake . -DCMAKE_INSTALL_PREFIX= -DCMAKE_PREFIX_PATH= make make install Build options ------------- * `CMAKE_INSTALL_PREFIX=` : install prefix. * `CMAKE_PREFIX_PATH=` : column-separated list of prefixes where to find dependencies. * `ENABLE_TESTS=NO` : disable non-regression tests. * `ENABLE_STRICT=NO` : build without strict build options like `-Wall -Werror` * `ENABLE_SHARED=NO` : do not build the shared library * `ENABLE_STATIC=NO` : do not build the static library Note for packagers ------------------ Our CMake scripts may automatically add some paths into research paths of generated binaries. To ensure that the installed binaries are striped of any rpath, use `-DCMAKE_SKIP_INSTALL_RPATH=ON` while you invoke cmake. Rpm packaging belle-sip can be generated with cmake3 using the following command: mkdir WORK cd WORK cmake3 ../ make package_source rpmbuild -ta --clean --rmsource --rmspec belle-sip--.tar.gz belle-sip-1.6.3/autogen.sh000077500000000000000000000023701313437522400154040ustar00rootroot00000000000000#!/bin/sh srcdir=`dirname $0` test -z "$srcdir" && srcdir=. THEDIR=`pwd` cd $srcdir #AM_VERSION="1.10" if ! type aclocal-$AM_VERSION 1>/dev/null 2>&1; then # automake-1.10 (recommended) is not available on Fedora 8 AUTOMAKE=automake ACLOCAL=aclocal else ACLOCAL=aclocal-${AM_VERSION} AUTOMAKE=automake-${AM_VERSION} fi libtoolize="libtoolize" for lt in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do if test -x /usr/bin/$lt ; then libtoolize=$lt ; break fi if test -x /usr/local/bin/$lt ; then libtoolize=$lt ; break fi if test -x /opt/local/bin/$lt ; then libtoolize=$lt ; break fi done if test -d /usr/local/share/aclocal ; then ACLOCAL_ARGS="$ACLOCAL_ARGS -I /usr/local/share/aclocal" fi if test -d /share/aclocal ; then ACLOCAL_ARGS="$ACLOCAL_ARGS -I /share/aclocal" fi set -x rm -rf config.cache autom4te.cache $libtoolize --copy --force $ACLOCAL -I m4 $ACLOCAL_ARGS autoheader $AUTOMAKE --force-missing --add-missing --copy autoconf #install git pre-commit hooks if possible if [ -d .git/hooks ] && [ ! -f .git/hooks/pre-commit ]; then cp .git-pre-commit .git/hooks/pre-commit chmod +x .git/hooks/pre-commit fi cd $THEDIR belle-sip-1.6.3/belle-sip.pc.in000066400000000000000000000005371313437522400162130ustar00rootroot00000000000000# This is a comment prefix=@prefix@ exec_prefix=@exec_prefix@ includedir=@includedir@ Name: belle-sip Description: A c based implementation of the SIP (RFC 3261) telephony protocol Requires: bctoolbox Requires.private: @REQUIRES_PRIVATE@ @TLS_PC@ Version: @VERSION@ Libs: -L@libdir@ -lbellesip Libs.private: @LIBS_PRIVATE@ Cflags: -I@includedir@ belle-sip-1.6.3/belle-sip.spec.in000077500000000000000000000044401313437522400165430ustar00rootroot00000000000000# -*- rpm-spec -*- ## rpmbuild options # These 2 lines are here because we can build the RPM for flexisip, in which # case we prefix the entire installation so that we don't break compatibility # with the user's libs. # To compile with bc prefix, use rpmbuild -ba --with bc [SPEC] %define pkg_name %{?_with_bc:bc-belle-sip}%{!?_with_bc:belle-sip} %{?_with_bc: %define _prefix /opt/belledonne-communications} %define srtp %{?_without_srtp:0}%{?!_without_srtp:1} # re-define some directories for older RPMBuild versions which don't. This messes up the doc/ dir # taken from https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros %define _datarootdir %{_prefix}/share %define _datadir %{_datarootdir} %define _docdir %{_datadir}/doc Name: %{pkg_name} Version: @VERSION@ Release: %(version=`git describe --tags --abbrev=40 | sed -rn 's/^.*-([0-9]+)-g[a-z0-9]{40}$/\1/p'` && if test -z "$version" ; then echo 0 ; else echo $version ; fi)%{?dist} Summary: Linphone sip stack Group: Applications/Communications License: GPL URL: http://www.belle-sip.org BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Source0: %{name}-%{version}.tar.gz %description Belle-sip is an object oriented SIP stack, written in C, used by Linphone. BuildRequires: antlr3-tool antlr3-C-devel %package devel Summary: Development libraries for belle-sip Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel Libraries and headers required to develop software with belle-sip %prep %setup -q %build %configure \ --disable-tests --enable-static --docdir=%{_docdir} %__make %{?_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT %clean rm -rf $RPM_BUILD_ROOT %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %defattr(-,root,root) %doc AUTHORS ChangeLog COPYING NEWS README.md %{_libdir}/*.so.* %files devel %defattr(-,root,root) %{_includedir}/belle-sip %{_libdir}/libbellesip.a %{_libdir}/libbellesip.la %{_libdir}/libbellesip.so %{_libdir}/pkgconfig/belle-sip.pc %changelog * Mon Aug 19 2013 jehan.monnier - Initial RPM release. belle-sip-1.6.3/build/000077500000000000000000000000001313437522400145005ustar00rootroot00000000000000belle-sip-1.6.3/build/CMakeLists.txt000066400000000000000000000036401313437522400172430ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2017 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ if (NOT CPACK_PACKAGE_NAME) set(CPACK_PACKAGE_NAME "belle-sip") ENDIF() set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/../COPYING") set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${PROJECT_VERSION}) set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_IGNORE_FILES "${CMAKE_BINARY_DIR}" "^${PROJECT_SOURCE_DIR}/.git*" ) bc_project_build_version(${PROJECT_VERSION} PROJECT_VERSION_BUILD) set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${PROJECT_VERSION_BUILD}") message("-- Package file name is ${CPACK_PACKAGE_FILE_NAME}" ) set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/rpm/belle-sip.spec.cmake ${CMAKE_CURRENT_SOURCE_DIR}/../belle-sip.spec) include(CPack) belle-sip-1.6.3/build/android/000077500000000000000000000000001313437522400161205ustar00rootroot00000000000000belle-sip-1.6.3/build/android/Android.mk000066400000000000000000000051631313437522400200360ustar00rootroot00000000000000## ## Android.mk -Android build script- ## ## ## Copyright (C) 2013 Belledonne Communications, Grenoble, France ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ## LOCAL_PATH:= $(call my-dir)/../../src include $(CLEAR_VARS) LOCAL_MODULE := libbellesip LOCAL_CFLAGS += \ -DHAVE_CONFIG_H -DHAVE_ZLIB=1 \ -DBELLESIP_VERSION=\"$(BELLESIP_VERSION)\" LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/../../externals/antlr3/runtime/C/include \ $(LOCAL_PATH)/../include \ $(LOCAL_PATH)/../build/android LOCAL_SRC_FILES := \ auth_event.c \ auth_helper.c \ belle_sdp_impl.c \ http-listener.c \ http-provider.c \ http-message.c \ generic-uri.c \ belle_sip_headers_impl.c \ belle_sip_loop.c \ grammars/belle_sip_messageLexer.c \ grammars/belle_sip_messageParser.c \ grammars/belle_sdpLexer.c \ grammars/belle_sdpParser.c \ belle_sip_object.c \ belle_sip_dict.c \ belle_sip_parameters.c \ belle_sip_resolver.c \ belle_sip_uri_impl.c \ belle_sip_utils.c \ channel.c \ clock_gettime.c \ dialog.c \ dns.c \ ict.c \ ist.c \ listeningpoint.c \ md5.c \ message.c \ nict.c \ nist.c \ port.c \ provider.c \ refresher.c \ siplistener.c \ sipstack.c \ transaction.c \ bodyhandler.c \ transports/stream_channel.c \ transports/stream_listeningpoint.c \ transports/tls_channel.c \ transports/tls_listeningpoint.c \ transports/udp_channel.c \ transports/udp_listeningpoint.c \ wakelock.c ifeq ($(BUILD_TUNNEL), 1) LOCAL_CFLAGS += -DHAVE_TUNNEL LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../tunnel/include LOCAL_SRC_FILES += \ transports/tunnel_listeningpoint.c \ transports/tunnel_channel.c \ transports/tunnel_wrapper.cc endif ifeq ($(BUILD_RTP_MAP),1) LOCAL_CFLAGS += -DBELLE_SDP_FORCE_RTP_MAP endif ifeq ($(BUILD_DONT_CHECK_HEADERS_IN_MESSAGE),1) LOCAL_CFLAGS += -DBELLE_SIP_DONT_CHECK_HEADERS_IN_MESSAGE endif LOCAL_STATIC_LIBRARIES := \ antlr3 LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../bctoolbox/include LOCAL_STATIC_LIBRARIES += bctoolbox include $(BUILD_STATIC_LIBRARY) belle-sip-1.6.3/build/android/config.h000066400000000000000000000051331313437522400175400ustar00rootroot00000000000000/* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ /* Define to 1 if you have the header file. */ #define HAVE_ANTLR3_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_BCUNIT_BCUNIT_H */ /* defined when CU_curses_run_tests is available */ /* #undef HAVE_CU_CURSES */ /* defined when CU_get_suite is available */ /* #undef HAVE_CU_GET_SUITE */ /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 1 /* Defined when gnutls api is available */ /* #undef HAVE_GNUTLS */ /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 /* Define to 1 if you have the `dl' library (-ldl). */ #define HAVE_LIBDL 1 /* Define to 1 if you have the `rt' library (-lrt). */ /* #undef HAVE_CLOCK_GETTIME */ #define HAVE_ZLIB 1 /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" /* Define to 1 if your C compiler doesn't accept -c and -o together. */ /* #undef NO_MINUS_C_MINUS_O */ /* Name of package */ #define PACKAGE "belle-sip" /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "jehan.monnier@linphone.org" /* Define to the full name of this package. */ #define PACKAGE_NAME "belle-sip" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "belle-sip 0.0.1" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "belle-sip" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ //#define PACKAGE_VERSION "0.0.1" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Version number of package */ #define VERSION "0.0.1" /* Defined when antlr 3.4 api is detected */ #define HAVE_ANTLR_STRING_STREAM_NEW 1 #define ENABLE_SERVER_SOCKETS 1 belle-sip-1.6.3/build/rpm/000077500000000000000000000000001313437522400152765ustar00rootroot00000000000000belle-sip-1.6.3/build/rpm/belle-sip.spec.cmake000077500000000000000000000051761313437522400211210ustar00rootroot00000000000000# -*- rpm-spec -*- ## rpmbuild options # These 2 lines are here because we can build the RPM for flexisip, in which # case we prefix the entire installation so that we don't break compatibility # with the user's libs. # To compile with bc prefix, use rpmbuild -ba --with bc [SPEC] %define pkg_name %{?_with_bc:bc-belle-sip}%{!?_with_bc:belle-sip} %{?_with_bc: %define _prefix /opt/belledonne-communications} %define srtp %{?_without_srtp:0}%{?!_without_srtp:1} # re-define some directories for older RPMBuild versions which don't. This messes up the doc/ dir # taken from https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros %define _datarootdir %{_prefix}/share %define _datadir %{_datarootdir} %define _docdir %{_datadir}/doc %define build_number @PROJECT_VERSION_BUILD@ Name: %{pkg_name} Version: @PROJECT_VERSION@ Release: %build_number%{?dist} Summary: Linphone's sip stack Group: Applications/Communications License: GPL URL: http://www.belle-sip.org Source0: %{name}-%{version}-%{build_number}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot %description Belle-sip is an object oriented SIP stack, written in C, used by Linphone. BuildRequires: antlr3-tool antlr3-C-devel %package devel Summary: Development libraries for belle-sip Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel Libraries and headers required to develop software with belle-sip %if 0%{?rhel} && 0%{?rhel} <= 7 %global cmake_name cmake3 %define ctest_name ctest3 %else %global cmake_name cmake %define ctest_name ctest %endif %prep %setup -n %{name}-%{version}-%build_number %build %{expand:%%%cmake_name} . -DCMAKE_INSTALL_LIBDIR:PATH=%{_libdir} -DCMAKE_PREFIX_PATH:PATH=%{_prefix} make %{?_smp_mflags} %install make install DESTDIR=%{buildroot} %check #%{ctest_name} -V %{?_smp_mflags} %clean rm -rf $RPM_BUILD_ROOT %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %defattr(-,root,root) %doc AUTHORS ChangeLog COPYING NEWS README.md %{_libdir}/*.so.* %files devel %defattr(-,root,root) %{_includedir}/belle-sip %{_libdir}/libbellesip.a %{_libdir}/libbellesip.so %{_libdir}/pkgconfig/belle-sip.pc %{_datadir}/BelleSIP/cmake/BelleSIPConfig.cmake %{_datadir}/BelleSIP/cmake/BelleSIPConfigVersion.cmake %{_datadir}/BelleSIP/cmake/BelleSIPTargets-noconfig.cmake %{_datadir}/BelleSIP/cmake/BelleSIPTargets.cmake %{_bindir}/* %changelog * Mon Aug 19 2013 jehan.monnier - Initial RPM release. belle-sip-1.6.3/build/wp8/000077500000000000000000000000001313437522400152165ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/000077500000000000000000000000001313437522400211125ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-native/000077500000000000000000000000001313437522400255565ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-native/belle-sip-tester-native.cpp000066400000000000000000000056471313437522400327420ustar00rootroot00000000000000#include #include "belle-sip-tester-native.h" #include "belle-sip/belle-sip.h" #include "bcunit/Util.h" using namespace belle_sip_tester_native; using namespace Platform; #define MAX_TRACE_SIZE 512 #define MAX_SUITE_NAME_SIZE 128 static OutputTraceListener^ sTraceListener; static void nativeOutputTraceHandler(int lev, const char *fmt, va_list args) { if (sTraceListener) { wchar_t wstr[MAX_TRACE_SIZE]; std::string str; str.resize(MAX_TRACE_SIZE); vsnprintf((char *)str.c_str(), MAX_TRACE_SIZE, fmt, args); mbstowcs(wstr, str.c_str(), sizeof(wstr)); String^ msg = ref new String(wstr); sTraceListener->outputTrace(msg); } } static void belleSipNativeOutputTraceHandler(belle_sip_log_level lev, const char *fmt, va_list args) { nativeOutputTraceHandler((int)lev, fmt, args); } BelleSipTesterNative::BelleSipTesterNative() { belle_sip_tester_init(); } BelleSipTesterNative::~BelleSipTesterNative() { belle_sip_tester_uninit(); } void BelleSipTesterNative::setOutputTraceListener(OutputTraceListener^ traceListener) { sTraceListener = traceListener; } void BelleSipTesterNative::run(Platform::String^ suiteName, Platform::String^ caseName, Platform::Boolean verbose) { std::wstring all(L"ALL"); std::wstring wssuitename = suiteName->Data(); std::wstring wscasename = caseName->Data(); char csuitename[MAX_SUITE_NAME_SIZE] = { 0 }; char ccasename[MAX_SUITE_NAME_SIZE] = { 0 }; wcstombs(csuitename, wssuitename.c_str(), sizeof(csuitename)); wcstombs(ccasename, wscasename.c_str(), sizeof(ccasename)); if (verbose) { belle_sip_set_log_level(BELLE_SIP_LOG_DEBUG); } else { belle_sip_set_log_level(BELLE_SIP_LOG_ERROR); } belle_sip_set_log_handler(belleSipNativeOutputTraceHandler); CU_set_trace_handler(nativeOutputTraceHandler); belle_sip_tester_set_root_ca_path("Assets/rootca.pem"); belle_sip_tester_run_tests(wssuitename == all ? 0 : csuitename, wscasename == all ? 0 : ccasename); } unsigned int BelleSipTesterNative::nbTestSuites() { return belle_sip_tester_nb_test_suites(); } unsigned int BelleSipTesterNative::nbTests(Platform::String^ suiteName) { std::wstring suitename = suiteName->Data(); char cname[MAX_SUITE_NAME_SIZE] = { 0 }; wcstombs(cname, suitename.c_str(), sizeof(cname)); return belle_sip_tester_nb_tests(cname); } Platform::String^ BelleSipTesterNative::testSuiteName(int index) { const char *cname = belle_sip_tester_test_suite_name(index); wchar_t wcname[MAX_SUITE_NAME_SIZE]; mbstowcs(wcname, cname, sizeof(wcname)); return ref new String(wcname); } Platform::String^ BelleSipTesterNative::testName(Platform::String^ suiteName, int testIndex) { std::wstring suitename = suiteName->Data(); char csuitename[MAX_SUITE_NAME_SIZE] = { 0 }; wcstombs(csuitename, suitename.c_str(), sizeof(csuitename)); const char *cname = belle_sip_tester_test_name(csuitename, testIndex); wchar_t wcname[MAX_SUITE_NAME_SIZE]; mbstowcs(wcname, cname, sizeof(wcname)); return ref new String(wcname); } belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-native/belle-sip-tester-native.h000066400000000000000000000013321313437522400323720ustar00rootroot00000000000000#pragma once #include "belle-sip/belle-sip.h" #include "belle_sip_tester.h" namespace belle_sip_tester_native { public interface class OutputTraceListener { public: void outputTrace(Platform::String^ msg); }; public ref class BelleSipTesterNative sealed { public: BelleSipTesterNative(); virtual ~BelleSipTesterNative(); void setOutputTraceListener(OutputTraceListener^ traceListener); unsigned int nbTestSuites(); unsigned int nbTests(Platform::String^ suiteName); Platform::String^ testSuiteName(int index); Platform::String^ testName(Platform::String^ suiteName, int testIndex); void run(Platform::String^ suiteName, Platform::String^ caseName, Platform::Boolean verbose); }; }belle-sip-tester-native.vcxproj000066400000000000000000000153521313437522400335660ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-native Debug Win32 Debug ARM Release Win32 Release ARM {3b4e9c2c-d5dc-4cf9-a7f4-5cb4ab55af79} belle_sip_tester_native en-US 11.0 true DynamicLibrary true v110_wp80 DynamicLibrary false true v110_wp80 $(SolutionDir)$(Platform)\$(Configuration)\ $(SolutionDir)$(Platform)\$(Configuration)\ false Level4 $(ProjectDir);$(GeneratedFilesDir);$(IntDir);$(ProjectDir)..\..\..\..\include;$(ProjectDir)..\..\..\..\src;$(ProjectDir)..\..\..\..\tester;$(ProjectDir)..\..\..\..\..\bcunit\build\wp8\bcunit\$(Platform)\$(Configuration);%(AdditionalIncludeDirectories) WIN32;_WINDOWS;_WINRT_DLL;_CRT_SECURE_NO_WARNINGS;HAVE_CU_GET_SUITE;PACKAGE_VERSION=$(BELLESIP_PACKAGE_VERSION);%(PreprocessorDefinitions) Default NotUsing $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) false Console false ole32.lib;%(IgnoreSpecificDefaultLibraries) true WindowsPhoneCore.lib;RuntimeObject.lib;PhoneAppModelHost.lib;ws2_32.lib;%(AdditionalDependencies) $(SolutionDir)$(Platform)\$(Configuration) _DEBUG;%(PreprocessorDefinitions) NDEBUG;%(PreprocessorDefinitions) MaxSpeed true true true true false true {902daf1d-ebf1-4d03-b598-143500a50ab4} {4c225a82-800b-427b-ba7b-61686a9b347f} belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8.sln000066400000000000000000000151151313437522400255270ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Express 2012 for Windows Phone Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "belle-sip-tester-wp8", "belle-sip-tester-wp8\belle-sip-tester-wp8.csproj", "{2CEBCF29-6B23-46C2-B579-588321228F3E}" ProjectSection(ProjectDependencies) = postProject {3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79} = {3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "belle-sip-tester-native", "belle-sip-tester-native\belle-sip-tester-native.vcxproj", "{3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79}" ProjectSection(ProjectDependencies) = postProject {4C225A82-800B-427B-BA7B-61686A9B347F} = {4C225A82-800B-427B-BA7B-61686A9B347F} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bcunit", "..\..\..\..\bcunit\build\wp8\bcunit\bcunit.vcxproj", "{902DAF1D-EBF1-4D03-B598-143500A50AB4}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libantlr3c", "..\..\..\..\antlr3\runtime\C\build\wp8\libantlr3c\libantlr3c.vcxproj", "{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "belle-sip", "..\belle-sip\belle-sip.vcxproj", "{4C225A82-800B-427B-BA7B-61686A9B347F}" ProjectSection(ProjectDependencies) = postProject {8FA74260-151B-429B-83EF-3CF3EAC8CFD9} = {8FA74260-151B-429B-83EF-3CF3EAC8CFD9} {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09} = {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "polarssl", "..\..\..\..\polarssl\build\wp8\polarssl\polarssl.vcxproj", "{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tunnel", "..\..\..\..\tunnel\build\wp8\tunnel\tunnel.vcxproj", "{59500DD1-B192-4DDF-A402-8A8E3739E032}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM Debug|x86 = Debug|x86 Release|ARM = Release|ARM Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {2CEBCF29-6B23-46C2-B579-588321228F3E}.Debug|ARM.ActiveCfg = Debug|ARM {2CEBCF29-6B23-46C2-B579-588321228F3E}.Debug|ARM.Build.0 = Debug|ARM {2CEBCF29-6B23-46C2-B579-588321228F3E}.Debug|ARM.Deploy.0 = Debug|ARM {2CEBCF29-6B23-46C2-B579-588321228F3E}.Debug|x86.ActiveCfg = Debug|x86 {2CEBCF29-6B23-46C2-B579-588321228F3E}.Debug|x86.Build.0 = Debug|x86 {2CEBCF29-6B23-46C2-B579-588321228F3E}.Debug|x86.Deploy.0 = Debug|x86 {2CEBCF29-6B23-46C2-B579-588321228F3E}.Release|ARM.ActiveCfg = Release|ARM {2CEBCF29-6B23-46C2-B579-588321228F3E}.Release|ARM.Build.0 = Release|ARM {2CEBCF29-6B23-46C2-B579-588321228F3E}.Release|ARM.Deploy.0 = Release|ARM {2CEBCF29-6B23-46C2-B579-588321228F3E}.Release|x86.ActiveCfg = Release|x86 {2CEBCF29-6B23-46C2-B579-588321228F3E}.Release|x86.Build.0 = Release|x86 {2CEBCF29-6B23-46C2-B579-588321228F3E}.Release|x86.Deploy.0 = Release|x86 {3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79}.Debug|ARM.ActiveCfg = Debug|ARM {3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79}.Debug|ARM.Build.0 = Debug|ARM {3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79}.Debug|x86.ActiveCfg = Debug|Win32 {3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79}.Debug|x86.Build.0 = Debug|Win32 {3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79}.Release|ARM.ActiveCfg = Release|ARM {3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79}.Release|ARM.Build.0 = Release|ARM {3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79}.Release|x86.ActiveCfg = Release|Win32 {3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79}.Release|x86.Build.0 = Release|Win32 {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|ARM.ActiveCfg = Debug|ARM {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|ARM.Build.0 = Debug|ARM {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|x86.ActiveCfg = Debug|Win32 {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Debug|x86.Build.0 = Debug|Win32 {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|ARM.ActiveCfg = Release|ARM {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|ARM.Build.0 = Release|ARM {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|x86.ActiveCfg = Release|Win32 {902DAF1D-EBF1-4D03-B598-143500A50AB4}.Release|x86.Build.0 = Release|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|ARM.ActiveCfg = Debug|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|ARM.Build.0 = Debug|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|x86.ActiveCfg = Debug|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|x86.Build.0 = Debug|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|ARM.ActiveCfg = Release|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|ARM.Build.0 = Release|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|x86.ActiveCfg = Release|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|x86.Build.0 = Release|Win32 {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|ARM.ActiveCfg = Debug|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|ARM.Build.0 = Debug|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|x86.ActiveCfg = Debug|Win32 {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|x86.Build.0 = Debug|Win32 {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|ARM.ActiveCfg = Release|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|ARM.Build.0 = Release|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|x86.ActiveCfg = Release|Win32 {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|x86.Build.0 = Release|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|ARM.ActiveCfg = Debug|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|ARM.Build.0 = Debug|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|x86.ActiveCfg = Debug|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|x86.Build.0 = Debug|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|ARM.ActiveCfg = Release|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|ARM.Build.0 = Release|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|x86.ActiveCfg = Release|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|x86.Build.0 = Release|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|ARM.ActiveCfg = Debug|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|ARM.Build.0 = Debug|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|x86.ActiveCfg = Debug|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|x86.Build.0 = Debug|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|ARM.ActiveCfg = Release|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|ARM.Build.0 = Release|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|x86.ActiveCfg = Release|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/000077500000000000000000000000001313437522400250065ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/App.xaml000066400000000000000000000016451313437522400264170ustar00rootroot00000000000000 belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/App.xaml.cs000066400000000000000000000222371313437522400270230ustar00rootroot00000000000000using System; using System.Diagnostics; using System.Resources; using System.Windows; using System.Windows.Markup; using System.Windows.Navigation; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; using belle_sip_tester_wp8.Resources; using belle_sip_tester_native; namespace belle_sip_tester_wp8 { public partial class App : Application { /// /// Provides easy access to the root frame of the Phone Application. /// /// The root frame of the Phone Application. public static PhoneApplicationFrame RootFrame { get; private set; } /// /// Constructor for the Application object. /// public App() { // Global handler for uncaught exceptions. UnhandledException += Application_UnhandledException; // Standard XAML initialization InitializeComponent(); // Phone-specific initialization InitializePhoneApplication(); // Language display initialization InitializeLanguage(); // Show graphics profiling information while debugging. if (Debugger.IsAttached) { // Display the current frame rate counters. Application.Current.Host.Settings.EnableFrameRateCounter = true; // Show the areas of the app that are being redrawn in each frame. //Application.Current.Host.Settings.EnableRedrawRegions = true; // Enable non-production analysis visualization mode, // which shows areas of a page that are handed off to GPU with a colored overlay. //Application.Current.Host.Settings.EnableCacheVisualization = true; // Prevent the screen from turning off while under the debugger by disabling // the application's idle detection. // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run // and consume battery power when the user is not using the phone. PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled; } tester = new BelleSipTesterNative(); suite = null; } // Code to execute when the application is launching (eg, from Start) // This code will not execute when the application is reactivated private void Application_Launching(object sender, LaunchingEventArgs e) { } // Code to execute when the application is activated (brought to foreground) // This code will not execute when the application is first launched private void Application_Activated(object sender, ActivatedEventArgs e) { } // Code to execute when the application is deactivated (sent to background) // This code will not execute when the application is closing private void Application_Deactivated(object sender, DeactivatedEventArgs e) { } // Code to execute when the application is closing (eg, user hit Back) // This code will not execute when the application is deactivated private void Application_Closing(object sender, ClosingEventArgs e) { } // Code to execute if a navigation fails private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e) { if (Debugger.IsAttached) { // A navigation has failed; break into the debugger Debugger.Break(); } } // Code to execute on Unhandled Exceptions private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) { if (Debugger.IsAttached) { // An unhandled exception has occurred; break into the debugger Debugger.Break(); } } #region Phone application initialization // Avoid double-initialization private bool phoneApplicationInitialized = false; // Do not add any additional code to this method private void InitializePhoneApplication() { if (phoneApplicationInitialized) return; // Create the frame but don't set it as RootVisual yet; this allows the splash // screen to remain active until the application is ready to render. RootFrame = new PhoneApplicationFrame(); RootFrame.Navigated += CompleteInitializePhoneApplication; // Handle navigation failures RootFrame.NavigationFailed += RootFrame_NavigationFailed; // Handle reset requests for clearing the backstack RootFrame.Navigated += CheckForResetNavigation; // Ensure we don't initialize again phoneApplicationInitialized = true; } // Do not add any additional code to this method private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e) { // Set the root visual to allow the application to render if (RootVisual != RootFrame) RootVisual = RootFrame; // Remove this handler since it is no longer needed RootFrame.Navigated -= CompleteInitializePhoneApplication; } private void CheckForResetNavigation(object sender, NavigationEventArgs e) { // If the app has received a 'reset' navigation, then we need to check // on the next navigation to see if the page stack should be reset if (e.NavigationMode == NavigationMode.Reset) RootFrame.Navigated += ClearBackStackAfterReset; } private void ClearBackStackAfterReset(object sender, NavigationEventArgs e) { // Unregister the event so it doesn't get called again RootFrame.Navigated -= ClearBackStackAfterReset; // Only clear the stack for 'new' (forward) and 'refresh' navigations if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh) return; // For UI consistency, clear the entire page stack while (RootFrame.RemoveBackEntry() != null) { ; // do nothing } } #endregion // Initialize the app's font and flow direction as defined in its localized resource strings. // // To ensure that the font of your application is aligned with its supported languages and that the // FlowDirection for each of those languages follows its traditional direction, ResourceLanguage // and ResourceFlowDirection should be initialized in each resx file to match these values with that // file's culture. For example: // // AppResources.es-ES.resx // ResourceLanguage's value should be "es-ES" // ResourceFlowDirection's value should be "LeftToRight" // // AppResources.ar-SA.resx // ResourceLanguage's value should be "ar-SA" // ResourceFlowDirection's value should be "RightToLeft" // // For more info on localizing Windows Phone apps see http://go.microsoft.com/fwlink/?LinkId=262072. // private void InitializeLanguage() { try { // Set the font to match the display language defined by the // ResourceLanguage resource string for each supported language. // // Fall back to the font of the neutral language if the Display // language of the phone is not supported. // // If a compiler error is hit then ResourceLanguage is missing from // the resource file. RootFrame.Language = XmlLanguage.GetLanguage(AppResources.ResourceLanguage); // Set the FlowDirection of all elements under the root frame based // on the ResourceFlowDirection resource string for each // supported language. // // If a compiler error is hit then ResourceFlowDirection is missing from // the resource file. FlowDirection flow = (FlowDirection)Enum.Parse(typeof(FlowDirection), AppResources.ResourceFlowDirection); RootFrame.FlowDirection = flow; } catch { // If an exception is caught here it is most likely due to either // ResourceLangauge not being correctly set to a supported language // code or ResourceFlowDirection is set to a value other than LeftToRight // or RightToLeft. if (Debugger.IsAttached) { Debugger.Break(); } throw; } } public bool suiteRunning() { return (suite != null) && (suite.running); } public BelleSipTesterNative tester { get; set; } public UnitTestSuite suite { get; set; } } }belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Assets/000077500000000000000000000000001313437522400262505ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Assets/AlignmentGrid.png000066400000000000000000000215221313437522400315040ustar00rootroot00000000000000PNG  IHDRQ5 pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F}IDATxف 0D( :݋;4!LZa $]@ @ @  @@ @ @  @@ @ @ 5tzFkz{m_("@ @ @  @@/$nJx@ @ @}{=V[m=zM[}Ƕ/  @  @@ @ `-@ / @  7ѧ3|_^[m֣N۴wl⍼@ @@ @ @ @f[I @@~}{=V[m=zM[}Ƕ/  @  @@ @ `-@ / @  7ѧ3|_^[m֣N۴wl⍼@ @@ @ @ @f[I @@~}{=V[m=zM[}Ƕ/  @  @@ @ `-@ / @  7ѧ3|_^[m֣N۴wl⍼@ @@ @ @ @f[I @@~}{=V[m=zM[}Ƕ/  @  @@ @ `-@ / @  @]OwgjGoiV[y" @  @@ @ J @@ ͮѧ3|_^[m֣N۴wl⍼@ @@ @ @ @f[I @@~}{=V[m=zM[}Ƕ/  @  @@ @ `-@ / @  7ѧ3|_^[m֣N۴wl⍼@ @@ @ @ @f[I @@~}{=V[m=zM[}Ƕ/  @  @@ @ `-@ / @  7ѧ3|_^[m֣N۴wl⍼@ @@ @ @ @f[I @@~}{=V[m=zM[}Ƕ/  @  @@ @ `-@ / @  7ѧ3|_^[m֣N۴wl⍼@ @@ @ @ @f[I @@~}{=V[m=zM[}Ƕ/  @  @@ @ `-@ / @  @]OwgjGoiV[y" @  @@ @ ̶( @  5tzFkz{m_("@ @ @  @@l+[^@ @ @o\OwgjGoiV[y" @  @@ @ ̶( @  @W`g&IENDB`belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Assets/ApplicationIcon.png000066400000000000000000000065001313437522400320330ustar00rootroot00000000000000PNG  IHDRddpTsRGBgAMA a pHYs.#.#x?v IDATx^]{tU<&-oRzDWQz_+]+9+kȞqQW, >"/[E^-"-}ϤM$s23$ffIw߽ߝ{3Id,?cTaE?L{$qC.#$ %NHy@`p=$N< 08'D`x"0 N 'zmōL}8C$2\Vw9ؖ0q^C㽲Z}YA qKtVN_ԙnc?Xo,5Bjw:[Vmb ;5ێLںT1WD0̯Hb*`QqѨWQ Lbխ#р5BtۏA:"1.:t@~d ⴣEKTEC"IVbŢ^y#"U^:m&`Vn&",$nb)BVN:vwgyK9ޙ8,*=КGX,r窤]ɃsV`*y/Y:ugZ-@ XŐ5uϮz;]?ޗP6@8׳mCeCYM}ǰF#T%Vckڋ_]򥔟-:;7yfIUHߜzdn"7': cħN1"~Ak2:Bc[L}PWot) I@Ԅ;ߙQ} c,۞w}dRqgeS$s`~="'N6t;Vcr5&H_7g*۶F+[* wU;6Q@39_=d9rKr}\Z7rb! #,p߻k?g v&njӆ H >b? Ių27 Ǻ Pu >EC9҂/>KB)?"59\uM@O͟ lP -+$ix&cĶic'SBi2/ɽJV%@!Bg*aC9#E [CW<\ف33s $X,¼IFb1D\( SF w@"zE#0]&C{X [X3&;ܸS y?Ɠm8M_rE޹=q6dF!g1$\W}58\!9uPG4CdD$᩼E_tzsa_uܒXȗn_Q!sg*EàHI&U.y =* E{1y^K~Xfl)!6cYmZoqup82 5H3-B2'H7PS7WfLM3wfƕ`okŻ-n\4y펛^?{[u.^)5Og_S8 \1 U{k\Sgz[^2~sL)ϫφs'ߣĹ߄d x#B:"aH8I. 8nZ);q ,sЁt^]$-l|-;v˷U,>[iݶ`zP|Y7H/gMM^2ோvcfۘoH!#}g5Wh?{`]~ݲ#mہ@2NһOi5R>c;.a;pۋ/ b@灠7?[qr/<%ć+o v 0%cp?.L/M[+/T ۇζ;N륑ʝ#>5 8h!% ptq`] l4"bm~Udx]00}r}z9v +6=Fitghߥ!/*; p9;W/|i_7c* o2_S'X$d4ŜP2sh`"0 'C!qB8!{a=7 Sqb@$a&[3UIENDB`belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Assets/Tiles/000077500000000000000000000000001313437522400273305ustar00rootroot00000000000000FlipCycleTileLarge.png000066400000000000000000000233121313437522400334230ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Assets/TilesPNG  IHDRP pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATx}dY93}_Y{XII@$q'iy!BTR S5  yITB PBb묽ٝ3s~e3޽w>)w9s33d)%`'aYff@aYYf@@aYff@aaYf@@aYYf@aaYff@aYYf@@aYff@aaYf@@aYff@aaYf@@aYYf@aaYff@aYv`LEX齇>*ecgf`Cݽۚ"7K)ר<0 e曺foC꺾MV2RJy"3RJfޜlҶ~8">/gw <3+<3"M/O,z腥)?0 Ћ.ᑑYJΝ;w0[ekj.ܴS/do߾߈^\){`yzfa=*TDܾu]DH/v?WUWX<1!,VOen;3_mdNo}a@vYm7Eą?~|5,:=;;_[Cq*;;;'6oivv۩0@,c5ok4R~?Zp;<::lzzzՂFc||~agϞ_JxD|flllO l/\.'"c ז}Ze'''p~լrW\x߳a' fcſAgi:Hyj }`fi=+̣ סݰų>=0 ]5K{\x=t~66jlbffa`,w ҮpmUeнV%5Fr+ɓl, 0Y[3{fllszzzoDk ϑ믿~!{ fZغfic̼կ~F<󃯃~<"62u1`Gk*)-}c~rץ]ѾT;Η?[`)`f]ߴdRP~#`'33 d|Y:VGK)~都^rEa`RFzi{m5nkX-p7_',@b/AvnڲeJffM,6'v=44>, l~,oG7_.'M b 0 9!fJnڋR֟::7777 h//מ:uj՚ɓv}}Rsfdd% l8VJQ0h7͇?VJ̇뺾n7::Yƾb쫙h/^|YRJj}޽{lw Yfd;3qxEJ)?S=|Uw]D|tf^7vơ32;WXa>W 8ʃ+<MUU:t].v?K,g?mטlTU"UFclAeYA`ffo_kK)_Uϟ̾},'|rWDu}sUUߔ[A50Ο?={N!bDuf]]?eP[Sۂ0  TfU]Df"[ť ο }JÎtR `-l3Xk׹p%]HUUm6Y`+|D|V%vFV׵w/fݭ< |D2,/RJo@v=}/ڧ0 `w>8}}L%×GY`;rH95,0XXn׾;[x=Gc`f =*R>\v23 >afԩSGj̜h6nw""F"bhu=1UUR&"b"3!{aGٙ8nef+"f#b뙈hELDjuݙh5Vә)J)3H';Zo'0k av𙈸IBwfY@f%"<"P1W"%0 F,7*Z,0 '9~| |H] dkn/XT5pff_l&0 lznf^l+ph7^賨z0 '0 lyn@?ܣx+Pj7G#;{u aB{Y- | yDM:ܓgO%A50snz^ f#k7`=;9}[v֠/>s* ¬پ o,,m@?vVfۅ ~U`RFTf1u:R~gbbfUa̼X)dyԄ,+K)wDFSl ڌƝW^yT)vE0c]}Эykf@U)GGGrU@f  ̮lzzz7G{"Uc2z߾}O j|,ɓRMn:4dقWD{j۽Ɠ'O6UXvpV ޿翫lޚ#*v+Lg'#%)0{+Rʻ3{T8y)}rbb 0@IDqD);(~#3/ 6=0g_t:)< vU,0dvv?V . {.\x_ ϙxG1">\o^%g}X:NUU}<3UVJf%S f  C Nf^Jlx[k+p K9uR>lOn0 l?~ u]^LEJ);vGim02mt:oyH T]l6qyEgfXf/^|y)K&G.^ҵYaXѿ9s+jg'Μ9ѿQ @6͑#GZFmhU)]O6J, ƞ٥Ƨ2T ٩n֡?a ,aCCC . {i/A@z611D)>`=2wo'Ty< |k&>UUݢlӧO^Y0@avCnxx7#Ha+amF }^'SGK)_t:oS @6dvc)WhƧJ)ONN:.C|E$ִܹsݻ3uf)ޅ ޹o߾'׸0k avevfS-*ڿmo aXsu:7?dk>f ;up)壍Fc9"lqo4v9u԰ˎ VuU!:>>%S f  vFqwfV.@;vzom`SF~AH8YJYvFmasPD|~\-+l:<<["`xvllk, R~9"P5)A63t:AuR^X5\W+>^J522Y`qrcǎ]7F=aunǏ?hA ,Vh/"^blg"72UU0f  ̮ܷ ZU{"գv65N`i3lttocǎ}[<ӆpg)*i#zff}SSSW]u[J)wZV65KAA50۷rM]w1.l77y N,7 ,qthllWT>dm]n'Ue{20@a/^g_ m@ϴ'@g0uh/`n zl01 flz8ngggg'"ND=٣VjUY`Wi6n1(Y]?tn|Gd2󿍎ell~y!~r_)Vndq#NMMM\uU n7;,Rʏu]u:yO Kk"nfy0 d `{03o{Y`'7 XPM+ l̖R.?] ;_3F2N~WDRJǬY@^gf|_SJy~ 3OZ3fvХƾ""&&&v@=q7v""{fN[ҚYmfcx>*i_{Pofv5.5㏫x̙wͽx#d8a]arpcfc>lBDrȑ׺;J)^כwaVEu]f ^l RJ]׷d旟}lhh۝g-ݻLxgDnaFz% _ z^ZJyd̟k6/"}O?sE fR3dfn08޵[ǎr?#azO3 c{)e2">%3??Vuefc/{,";3^=_SJ<̼:3{E={BDo7f;30;ٳy6v^sv_o^-.<|>"^OWUuN `USSSzڊR^]rv8f0`f0 , 0 ,,  0 , 0 0 ,  0 ,, 0 0 , 0 ,,  0 , 0 0 ,  0 , 0 0 ,  0 ,, 0 0 , 0 ,,  0 ,pYKЭ|GIENDB`FlipCycleTileMedium.png000066400000000000000000000215561313437522400336210ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Assets/TilesPNG  IHDRPP& pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxu}n.sI8`95p b-ڑqm?f( BE`HBb CTю0byu{{c&}w~U39J(@ @P @(@ (P @(@ (P @@ (P @@ (P @@ (P @@ (P @ Ul_b\ݟNnҥ_2us f9i сRc@D z>_\af?###fٽ:P:P$/VՌ.-fVk{U5 @hAt;jq?'tҭ"rsTV"(^DLStkd'gys]8UDg---d-ER;+S"@>_afH?^.ą[UuQnJ===ND6NuU]vu Eu۷oQt7PճDK,믿ID j2Jw*jnD[D2^3{^ܴ۫-_4( Ι.T*]fxdfTuLPFD1;؍^<q}ella >ǒT댢vifWU/~o4?HRsnppe,㙪s3>?MIN88b;~33B*xx]g%4ׂhB7ݨ\efed᣾W57Ǯ@@A :P`gG"T*]Xodï؛gs.ׁ-\+.>66-GEX]_C-d2 ޡvӁ63{1'"<(6z3[\A(R٪f}hoooX,FQt^`@H?Ne PTk%9$1bf/ٓQ3429Zu3Q;kfsps"։履l}4-"e-l>E#Bl6w,"f EU$mKU/TU.pxxxe{{X{1qh2BP/허 g2?kfo/~sL8 _-GvjB3 uι DATrOЁ6M*"Eѵι/Wȫf\U]f;OD^wy AcgMԽi?%@ ^/qTaZU h2p>YQ?6W {}_kVxf:^U_mgfORcf{2϶Oڬ=K(HE{!@1WFFFR*X 1иc""gTwιt߬:P>$zz$@1+ιT!Ꮶe͚5s.˨j6 LEYAgE$+":2fU5cfG~ Dd5k ~gfD$/"9U͙Yrι>/"?w(ʋH.\TʛY{r{ɗ?4Mv:th *mbN?4ͪ<h .|YU~.ulvy;e`o~\W1ߣ;#<hvQ(FQ/;#<(sW(<>#NjusLU?E5"2BE0i[7nx… _"1и<; Bg*O0{Y6~龡9 =ccc-jo' ֛&UP9T43̍7x[OOP849*qQ$<=I27qETS$@ʢE9+"q\ߵgϞ94#Dbq]2EUVUs94" ,P(ZV=gQ%SP,3'ѳ3ץbxf[[TR~A&BT:휨AM2N$`lh](;=r\m `dGB4P|G|L) Ü~^H@xEd{9AZ#ȫ@O<ċsݪnISQp>p K.\mf[)4kfJMtI~NϢEιx?ofTsyFT:ƍAtuہN7:{^G١i}g,] ۗ,YA\%"o08_0MιM ~" FGG_JU[U@E1-ιͪ+fSM4:彿9w%ńFA<*-I "_TSaWh#vǙY'0k#*Jg2),hcqf5S쨃0 26AJ:AE:LEq4[ (J2cGy}FxFw0ʃY;PFw(϶O&:@1<>#|7Fw{{{CAt]%"wRA7ACJ6MkE" "ST3T'(#|Dv)Q):PFw(϶O&*@QO<>#<;(O(;mg'@Qm`mthh#\)"YɈH96i3dU5cfG[7MQT5gf=o"2""#D$/"bWthGOP6Je[[[3b1J2QeU9wk >EѮ rb1JrbkooϩH __T?&rğEQ:*|bSdhRWsPIvQFxF9dfi3'9%E\yoVhS:lp` s{XXsϨ~ÌιՌt³v« B' @QcmmmQ!EAp)Uh.T! _szf6 "+|)"U-kӁ63;lXR2=c^ Ð3(jx*4%@Q YU}h\.J+W^$"*.@U␙VX㝙D/DxV=KD=;엱6H>e+Qv?E_O6Ĉ9wɸoO "Ǩ:3GU=~_ـ@t]g0)}13={\efq?4<܍j|q '({&@'d3Tk+yUDvWxUtU}~gf?a~wW,8 ï&j((@t\J߭OٞJ'x7YxbETrRx≊NpRiStt(j)~0c]*0|x0ifU?QOFQVDe'@ T(3Nqێ;n _fx؍g Mcq c[Ddk->{7("f|ap};EM3x?sΝZW>6AUp?0(*aÆ,Ul"rlSDdŊC"r\J~(hvtt jfDQ1:lS<96eЁҁ"1VZ3G'<;Uo'P$ܦ):'vy]5cÆ xjM03'yL|A~;_gU3,TzJDNwUur#<({ X,_|R"wRIU#irN@[q-%|wWXcccOxgdd0F_Zqn@)xerc8EJvD K*0O?@<3"F'CQp=Hn_~3$L[ATpޢu-f}+ðԵv8` w>Wh+0_3ެf;k6lQ|ZeXg3h" #\Ëp;נϴ!XWZ0D$<2hQ|(V/^PDW[A 46gէX, djT!u>0CI-)ؓv%)NSߟ?4Ldi/77^[Q|m_d^"֑q1,wEM\%'1jQ} ҥK?A \?M O;n<mmmϛ7+Q@uMoo\8dĈe/0|Ѥ3?!7#X ey-[h&9)(=)&=X΄e=\n6ͮ'C [&ў p+Ȝ<c$e1KVFNAF ~a7o"2Dׂې"p4;ҼM _TR->%toh-ʍ-_A|`^|g"k` ^|nOuYFVS%üV.̈ >­g am?2A|Q_Րߨ_U'>x2 ܪ{CXyuj#6˲EO[`wX~͊P%(PeN+]i:ڃFa-yLM|rqR{ SӣlTbiȚ;n񏨻?ǪUz?DҪtE}G껂SkjXp[}qMcMm.`TÉRRRmj= $'HRN@'N;m [kI7cV;LauƁzWŋ?CFTM&:ɑjքg~`i":[;,Zkg? $f/|~"DENM~`$5:23A%%›]7yR~IK+<>0__1C^X"G%K)c5!#X-a&[۬s]&pl>x%k-' {³YfiNC+_,X&PΒC 2cHӧOo^br:|ҙV RxiWebuwwA}!DZ',/Ctw]Ɵ<~&&{}>Fɼ|VoP_2_V'|Aa! 28UnYv3AN7+[_}Ap7޾}(~ 9Ncu<644t`ڵV9"V𿼖ag+1WYw')k0]lŧff+<-׺hn/kk_z'[W^]pB[yĕ%+jʘ-a467 0(([M|nGb|>a066jCyBe$pAGnv %Tϕ3KBo TDzP.,[KkEVrR!KjUNȁH"9on0(1jazOj ]paJNP]A=c8P'9<e"S>pD > ݎxӗ26FTbgt+vt淦Boymu/m˴E%!qxkϯCM>ۣIXiq i-m0d1 /yGVL\LpeX_8-C&C{{D"=zZЕ- Tu8sC@ 6*qh^^2bj V+5,D߯ψ +,{>9&$,</8Gv̹0Px .y 4<9^S >*/ Q!?+caldzns{a{A @41cqS\2Ap-L p_W3na, !lIENDB`IconicTileMediumLarge.png000066400000000000000000000115111313437522400341140ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Assets/TilesPNG  IHDR`8 pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FtIDATxqJF{(ff "0`,,#G`dAe%4 Ω\G{#dp @0a @0a @0a kvؠv[S{ if6xX/^rdo)i { {#<=aW#0%tM@Fr]\^6Ht<k!g:k a("_G'o0>N L#ڵaE魊E'mB!|ET |Á}V,6Ggs[cw%뎯E\:ǿjz0>FB8MJ"c.ʷk#p\] /V>I'Ë} qè5Z{zȼy6wB"Oa|0okS/Jh[;ئz: s獍K7Q )(Oa6rrE`*"/?h1 C0av4ƘJŧޞc/"cc̶RHURJR0H)=I!R )))bRB x qJ%J~ + RJ)R*!BjE RJ)VPQDҎA6:vmWrعKKlDUD.7]U 1~4+y5Ƭ* C/tu+ 0 EzUȿrxFam|?Hy3,L@Qd "c61{cX7 cs/"ZB{@vm꒺W]Ӷ5sCW8uY(Jg|,ƘeK1V?C'VETZI$}~|+z_fss髫Zw]e(uC뎟[]A'ĥdѫl%c1R*vQHDuNG k\Dg<cA=֒$E4Ȭ;D(;̑(^Dveޓaxq30d}Ž ZሽJ( (^\($"1>|9G CJNHQ'h=2'::GnM_l^0E1o.we:1o>)[;|3hu c݈ 4˨ΛJhQ/ǤK|] /nƠM m1H# жH%UIJѯQO'7޶A{9lſ9oo꓅?H}7 \|k9\\U\'SrJu, ia溭MD־..Mfh79ηmF,iT2G~扊"n4 fyywa$.5Zkc e3|`'v_f`݄1hN lLˊg]I:2у?﬇lDg&[Q;;)SlST\.FęȚȴ)kxM{/kNaLj)8Ͳh~3А @0a @0a @0a@ohWIENDB`belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Assets/Tiles/IconicTileSmall.png000066400000000000000000000072141313437522400330550ustar00rootroot00000000000000PNG  IHDRGnu pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxu0PK  t*XSAB pqk]C,4<tlˌf4bf #p8G#8G#pF̼`Բm̋|QSCj ̉ݡ6}E/g&VWuqsliske5L ϸ]dݦY˄}/pWj~d- ݠ܍qL :ttrܳtG%n[5=K1[rpT ;_D!)of_s?dvbaDDEs`9̿DO8ΗpOWg"$"LKICH7d9[֭yetF y057F+"R㶋Vו;9P'jt'$P-6,0Q%ni[ jbr.*O%HZ HY@Lʥ@ݩɽDZdV^O,5g@l /// Provides access to string resources. /// public class LocalizedStrings { private static AppResources _localizedResources = new AppResources(); public AppResources LocalizedResources { get { return _localizedResources; } } } }belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/MainPage.xaml000066400000000000000000000074611313437522400273620ustar00rootroot00000000000000 belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/MainPage.xaml.cs000066400000000000000000000054061313437522400277630ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; using belle_sip_tester_wp8.Resources; namespace belle_sip_tester_wp8 { public partial class MainPage : PhoneApplicationPage { // Constructor public MainPage() { InitializeComponent(); var tester = (Application.Current as App).tester; List source = new List(); source.Add(new UnitTestSuiteName("ALL")); for (int i = 0; i < tester.nbTestSuites(); i++) { source.Add(new UnitTestSuiteName(tester.testSuiteName(i))); } Tests.ItemsSource = source; // Sample code to localize the ApplicationBar //BuildLocalizedApplicationBar(); } private void Tests_Tap(object sender, System.Windows.Input.GestureEventArgs e) { UnitTestSuiteName test = (sender as LongListSelector).SelectedItem as UnitTestSuiteName; if (test == null) return; if (test.Name == "ALL") { NavigationService.Navigate(new Uri("/TestResultPage.xaml?SuiteName=" + test.Name + "&Verbose=" + Verbose.IsChecked.GetValueOrDefault(), UriKind.Relative)); } else { NavigationService.Navigate(new Uri("/TestCasePage.xaml?SuiteName=" + test.Name + "&Verbose=" + Verbose.IsChecked.GetValueOrDefault(), UriKind.Relative)); } } // Sample code for building a localized ApplicationBar //private void BuildLocalizedApplicationBar() //{ // // Set the page's ApplicationBar to a new instance of ApplicationBar. // ApplicationBar = new ApplicationBar(); // // Create a new button and set the text value to the localized string from AppResources. // ApplicationBarIconButton appBarButton = new ApplicationBarIconButton(new Uri("/Assets/AppBar/appbar.add.rest.png", UriKind.Relative)); // appBarButton.Text = AppResources.AppBarButtonText; // ApplicationBar.Buttons.Add(appBarButton); // // Create a new menu item with the localized string from AppResources. // ApplicationBarMenuItem appBarMenuItem = new ApplicationBarMenuItem(AppResources.AppBarMenuItemText); // ApplicationBar.MenuItems.Add(appBarMenuItem); //} } public class UnitTestSuiteName { public string Name { get; set; } public UnitTestSuiteName(string name) { this.Name = name; } } }belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Properties/000077500000000000000000000000001313437522400271425ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Properties/AppManifest.xml000066400000000000000000000003111313437522400320660ustar00rootroot00000000000000 belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Properties/AssemblyInfo.cs000066400000000000000000000027721313437522400320740ustar00rootroot00000000000000using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Resources; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("BelleSip Tester")] [assembly: AssemblyDescription("BelleSip tester for Windows Phone 8")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Belledonne Communications")] [assembly: AssemblyProduct("BelleSip Tester")] [assembly: AssemblyCopyright("Copyright © Belledonne Communications 2013")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("9455c69c-2077-4be0-b75c-13dc43bafef6")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: NeutralResourcesLanguageAttribute("en-US")] belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Properties/WMAppManifest.xml000066400000000000000000000036611313437522400323450ustar00rootroot00000000000000 Assets\ApplicationIcon.png Assets\Tiles\FlipCycleTileSmall.png 0 Assets\Tiles\FlipCycleTileMedium.png belle_sip_tester_wp8 belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Resources/000077500000000000000000000000001313437522400267605ustar00rootroot00000000000000AppResources.Designer.cs000066400000000000000000000104461313437522400334070ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Resources//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.17626 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace belle_sip_tester_wp8.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class AppResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal AppResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("belle_sip_tester_wp8.Resources.AppResources", typeof(AppResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to LeftToRight. /// public static string ResourceFlowDirection { get { return ResourceManager.GetString("ResourceFlowDirection", resourceCulture); } } /// /// Looks up a localized string similar to us-EN. /// public static string ResourceLanguage { get { return ResourceManager.GetString("ResourceLanguage", resourceCulture); } } /// /// Looks up a localized string similar to MY APPLICATION. /// public static string ApplicationTitle { get { return ResourceManager.GetString("ApplicationTitle", resourceCulture); } } /// /// Looks up a localized string similar to button. /// public static string AppBarButtonText { get { return ResourceManager.GetString("AppBarButtonText", resourceCulture); } } /// /// Looks up a localized string similar to menu item. /// public static string AppBarMenuItemText { get { return ResourceManager.GetString("AppBarMenuItemText", resourceCulture); } } } } belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/Resources/AppResources.resx000066400000000000000000000144551313437522400323070ustar00rootroot00000000000000 text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 LeftToRight Controls the FlowDirection for all elements in the RootFrame. Set to the traditional direction of this resource file's language en-US Controls the Language and ensures that the font for all elements in the RootFrame aligns with the app's language. Set to the language code of this resource file's language. MY APPLICATION add Menu Item belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/TestCasePage.xaml000066400000000000000000000040661313437522400302070ustar00rootroot00000000000000 belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/TestCasePage.xaml.cs000066400000000000000000000036671313437522400306210ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; namespace belle_sip_tester_wp8 { public partial class TestCasePage : PhoneApplicationPage { public TestCasePage() { InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); suiteName = NavigationContext.QueryString["SuiteName"]; verbose = Convert.ToBoolean(NavigationContext.QueryString["Verbose"]); var tester = (Application.Current as App).tester; List source = new List(); source.Add(new UnitTestCaseName("ALL")); for (int i = 0; i < tester.nbTests(suiteName); i++) { source.Add(new UnitTestCaseName(tester.testName(suiteName, i))); } Tests.ItemsSource = source; } private void Tests_Tap(object sender, System.Windows.Input.GestureEventArgs e) { UnitTestCaseName test = (sender as LongListSelector).SelectedItem as UnitTestCaseName; if (test == null) return; test.Name = test.Name.Replace("+", "%2B").Replace(" ", "%20"); if (!(Application.Current as App).suiteRunning()) { NavigationService.Navigate(new Uri("/TestResultPage.xaml?SuiteName=" + suiteName + "&CaseName=" + test.Name + "&Verbose=" + verbose, UriKind.Relative)); } } private string suiteName; private bool verbose; } public class UnitTestCaseName { public string Name { get; set; } public UnitTestCaseName(string name) { this.Name = name; } } }belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/TestResultPage.xaml000066400000000000000000000036661313437522400306170ustar00rootroot00000000000000 belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/TestResultPage.xaml.cs000066400000000000000000000057231313437522400312170ustar00rootroot00000000000000using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; using Microsoft.Phone.Controls; using Microsoft.Phone.Shell; using belle_sip_tester_native; using belle_sip_tester_wp8; namespace belle_sip_tester_wp8 { public delegate void OutputDisplayDelegate(String msg); public partial class TestResultPage : PhoneApplicationPage { public TestResultPage() { InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); string suiteName = NavigationContext.QueryString["SuiteName"]; string caseName; if (NavigationContext.QueryString.ContainsKey("CaseName")) { caseName = NavigationContext.QueryString["CaseName"]; } else { caseName = "ALL"; } bool verbose = Convert.ToBoolean(NavigationContext.QueryString["Verbose"]); var app = (Application.Current as App); app.suite = new UnitTestSuite(suiteName, caseName, verbose, new OutputDisplayDelegate(OutputDisplay)); app.suite.run(); } public void OutputDisplay(String msg) { this.Dispatcher.BeginInvoke(() => { TestResults.Text += msg; }); } } public class UnitTestSuite : OutputTraceListener { public UnitTestSuite(string SuiteName, string CaseName, bool Verbose, OutputDisplayDelegate OutputDisplay) { this.SuiteName = SuiteName; this.CaseName = CaseName; this.Verbose = Verbose; this.Running = false; this.OutputDisplay = OutputDisplay; } async public void run() { Running = true; var tup = new Tuple(SuiteName, CaseName, Verbose); var t = Task.Factory.StartNew((object parameters) => { var tester = (Application.Current as App).tester; tester.setOutputTraceListener(this); var p = parameters as Tuple; tester.run(p.Item1, p.Item2, p.Item3); }, tup); await t; Running = false; } public void outputTrace(String msg) { if (OutputDisplay != null) { OutputDisplay(msg); } System.Diagnostics.Debug.WriteLine(msg); } public bool running { get { return Running; } protected set { Running = value; } } private string SuiteName; private string CaseName; private bool Verbose; private bool Running; private OutputDisplayDelegate OutputDisplay; } }belle-sip-1.6.3/build/wp8/belle-sip-tester-wp8/belle-sip-tester-wp8/belle-sip-tester-wp8.csproj000066400000000000000000000175241313437522400321350ustar00rootroot00000000000000 Debug AnyCPU 10.0.20506 2.0 {2CEBCF29-6B23-46C2-B579-588321228F3E} {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties belle_sip_tester_wp8 belle_sip_tester_wp8 WindowsPhone v8.0 $(TargetFrameworkVersion) true true true belle_sip_tester_wp8_$(Configuration)_$(Platform).xap Properties\AppManifest.xml belle_sip_tester_wp8.App true 11.0 true true full false Bin\Debug\ DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE true true prompt 4 pdbonly true Bin\Release\ TRACE;SILVERLIGHT;WINDOWS_PHONE true true prompt 4 true full false Bin\x86\Debug\ DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE true true prompt 4 pdbonly true Bin\x86\Release\ TRACE;SILVERLIGHT;WINDOWS_PHONE true true prompt 4 true full false Bin\ARM\Debug\ DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE true true prompt 4 pdbonly true Bin\ARM\Release\ TRACE;SILVERLIGHT;WINDOWS_PHONE true true prompt 4 App.xaml MainPage.xaml True True AppResources.resx TestCasePage.xaml TestResultPage.xaml Designer MSBuild:Compile Designer MSBuild:Compile Designer MSBuild:Compile Designer MSBuild:Compile PreserveNewest Designer PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PublicResXFileCodeGenerator AppResources.Designer.cs {3B4E9C2C-D5DC-4CF9-A7F4-5CB4AB55AF79} belle-sip-tester-native belle-sip-1.6.3/build/wp8/belle-sip/000077500000000000000000000000001313437522400170725ustar00rootroot00000000000000belle-sip-1.6.3/build/wp8/belle-sip/belle-sip.props000066400000000000000000000007761313437522400220450ustar00rootroot00000000000000 "1.3.2" $(BELLESIP_PACKAGE_VERSION) belle-sip-1.6.3/build/wp8/belle-sip/belle-sip.sln000066400000000000000000000071041313437522400214660ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Express 2012 for Windows Phone Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "belle-sip", "belle-sip.vcxproj", "{4C225A82-800B-427B-BA7B-61686A9B347F}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libantlr3c", "..\..\..\..\antlr3\runtime\C\build\wp8\libantlr3c\libantlr3c.vcxproj", "{8FA74260-151B-429B-83EF-3CF3EAC8CFD9}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tunnel", "..\..\..\..\tunnel\build\wp8\tunnel\tunnel.vcxproj", "{59500DD1-B192-4DDF-A402-8A8E3739E032}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "polarssl", "..\..\..\..\polarssl\build\wp8\polarssl\polarssl.vcxproj", "{E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM Debug|Win32 = Debug|Win32 Release|ARM = Release|ARM Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|ARM.ActiveCfg = Debug|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|ARM.Build.0 = Debug|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|Win32.ActiveCfg = Debug|Win32 {4C225A82-800B-427B-BA7B-61686A9B347F}.Debug|Win32.Build.0 = Debug|Win32 {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|ARM.ActiveCfg = Release|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|ARM.Build.0 = Release|ARM {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|Win32.ActiveCfg = Release|Win32 {4C225A82-800B-427B-BA7B-61686A9B347F}.Release|Win32.Build.0 = Release|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|ARM.ActiveCfg = Debug|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|ARM.Build.0 = Debug|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|Win32.ActiveCfg = Debug|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Debug|Win32.Build.0 = Debug|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|ARM.ActiveCfg = Release|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|ARM.Build.0 = Release|ARM {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|Win32.ActiveCfg = Release|Win32 {8FA74260-151B-429B-83EF-3CF3EAC8CFD9}.Release|Win32.Build.0 = Release|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|ARM.ActiveCfg = Debug|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|ARM.Build.0 = Debug|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|Win32.ActiveCfg = Debug|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Debug|Win32.Build.0 = Debug|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|ARM.ActiveCfg = Release|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|ARM.Build.0 = Release|ARM {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|Win32.ActiveCfg = Release|Win32 {59500DD1-B192-4DDF-A402-8A8E3739E032}.Release|Win32.Build.0 = Release|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|ARM.ActiveCfg = Debug|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|ARM.Build.0 = Debug|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|Win32.ActiveCfg = Debug|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Debug|Win32.Build.0 = Debug|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|ARM.ActiveCfg = Release|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|ARM.Build.0 = Release|ARM {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|Win32.ActiveCfg = Release|Win32 {E9F8C5D1-13A2-46B6-A9BC-878030D4BE09}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal belle-sip-1.6.3/build/wp8/belle-sip/belle-sip.vcxproj000066400000000000000000000304731313437522400223720ustar00rootroot00000000000000 Debug Win32 Debug ARM Release Win32 Release ARM {4C225A82-800B-427B-BA7B-61686A9B347F} belle_sip en-US 11.0 DynamicLibrary true v110_wp80 false DynamicLibrary false true v110_wp80 false $(SolutionDir)$(Platform)\$(Configuration)\ $(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\ false Level4 WIN32;_WINDOWS;_USRDLL;BELLESIP_EXPORTS;BELLESIP_INTERNAL_EXPORTS;HAVE_POLARSSL;HAVE_TUNNEL;HAVE_ANTLR_STRING_STREAM_NEW;HAVE_COMPILER_TLS;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;USE_FIXED_NAMESERVERS;USE_GETADDRINFO_FALLBACK;ENABLE_SERVER_SOCKETS;HAVE_CONFIG_H;%(PreprocessorDefinitions) $(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\..\antlr3\runtime\C\include;$(ProjectDir)..\..\..\src;$(ProjectDir)..\..\..\..\polarssl\include;$(ProjectDir)..\..\..\..\tunnel\include;$(ProjectDir);%(AdditionalIncludeDirectories) Default NotUsing false $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) Console false false ws2_32.lib;libantlr3c.lib;polarssl.lib;tunnel.lib;%(AdditionalDependencies) $(SolutionDir)$(Platform)\$(Configuration);%(AdditionalLibraryDirectories) $(TargetDir)$(TargetName).lib java -jar $(ProjectDir)..\..\..\..\antlr3\antlr-3.4-complete.jar -make -fo $(ProjectDir)..\..\..\src\grammars\ %(FullPath) version.bat Sets the package version _DEBUG;%(PreprocessorDefinitions) true NDEBUG;%(PreprocessorDefinitions) MaxSpeed true true true false Document $(ProjectDir)..\..\..\src\grammars\belle_sdpLexer.c;$(ProjectDir)..\..\..\src\grammars\belle_sdpLexer.h;$(ProjectDir)..\..\..\src\grammars\belle_sdpParser.c;$(ProjectDir)..\..\..\src\grammars\belle_sdpParser.h Document $(ProjectDir)..\..\..\src\grammars\belle_sip_messageLexer.c;$(ProjectDir)..\..\..\src\grammars\belle_sip_messageLexer.h;$(ProjectDir)..\..\..\src\grammars\belle_sip_messageParser.c;$(ProjectDir)..\..\..\src\grammars\belle_sip_messageParser.h true true false {8fa74260-151b-429b-83ef-3cf3eac8cfd9} {e9f8c5d1-13a2-46b6-a9bc-878030d4be09} {59500dd1-b192-4ddf-a402-8a8e3739e032} belle-sip-1.6.3/build/wp8/belle-sip/belle-sip_no_tunnel.vcxproj000066400000000000000000000275311313437522400244540ustar00rootroot00000000000000 Debug Win32 Debug ARM Release Win32 Release ARM {4C225A82-800B-427B-BA7B-61686A9B347F} belle_sip en-US 11.0 DynamicLibrary true v110_wp80 false DynamicLibrary false true v110_wp80 false $(SolutionDir)$(Platform)\$(Configuration)\ $(SolutionDir)$(Platform)\$(Configuration)\$(TargetName)\ false Level4 WIN32;_WINDOWS;_USRDLL;BELLESIP_EXPORTS;BELLESIP_INTERNAL_EXPORTS;HAVE_POLARSSL;HAVE_ANTLR_STRING_STREAM_NEW;HAVE_COMPILER_TLS;_CRT_SECURE_NO_WARNINGS;_UNICODE;UNICODE;USE_FIXED_NAMESERVERS;USE_GETADDRINFO_FALLBACK;ENABLE_SERVER_SOCKETS;HAVE_CONFIG_H;%(PreprocessorDefinitions) $(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\..\antlr3\runtime\C\include;$(ProjectDir)..\..\..\src;$(ProjectDir)..\..\..\..\polarssl\include;$(ProjectDir)..\..\..\..\tunnel\include;$(ProjectDir);%(AdditionalIncludeDirectories) Default NotUsing false $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories) Console false false ws2_32.lib;libantlr3c.lib;polarssl.lib;%(AdditionalDependencies) $(SolutionDir)$(Platform)\$(Configuration);%(AdditionalLibraryDirectories) $(TargetDir)$(TargetName).lib java -jar $(ProjectDir)..\..\..\..\antlr3\antlr-3.4-complete.jar -make -fo $(ProjectDir)..\..\..\src\grammars\ %(FullPath) version.bat Sets the package version _DEBUG;%(PreprocessorDefinitions) true NDEBUG;%(PreprocessorDefinitions) MaxSpeed true true true false Document $(ProjectDir)..\..\..\src\grammars\belle_sdpLexer.c;$(ProjectDir)..\..\..\src\grammars\belle_sdpLexer.h;$(ProjectDir)..\..\..\src\grammars\belle_sdpParser.c;$(ProjectDir)..\..\..\src\grammars\belle_sdpParser.h Document $(ProjectDir)..\..\..\src\grammars\belle_sip_messageLexer.c;$(ProjectDir)..\..\..\src\grammars\belle_sip_messageLexer.h;$(ProjectDir)..\..\..\src\grammars\belle_sip_messageParser.c;$(ProjectDir)..\..\..\src\grammars\belle_sip_messageParser.h true true false {8fa74260-151b-429b-83ef-3cf3eac8cfd9} {e9f8c5d1-13a2-46b6-a9bc-878030d4be09} belle-sip-1.6.3/build/wp8/belle-sip/version.bat000066400000000000000000000003071313437522400212470ustar00rootroot00000000000000@ECHO off FOR /F "delims=" %%a IN ('findstr /B AC_INIT ..\..\..\configure.ac') DO ( FOR /F "tokens=1,2,3 delims=[,]" %%1 IN ("%%a") DO ( ECHO #define PACKAGE_VERSION "%%3" > config.h ) ) belle-sip-1.6.3/cmake/000077500000000000000000000000001313437522400144615ustar00rootroot00000000000000belle-sip-1.6.3/cmake/BelleSIPConfig.cmake.in000066400000000000000000000050541313437522400206210ustar00rootroot00000000000000############################################################################ # BelleSIPConfig.cmake # Copyright (C) 2015 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # Config file for the belle-sip package. # It defines the following variables: # # BELLESIP_FOUND - system has belle-sip # BELLESIP_INCLUDE_DIRS - the belle-sip include directory # BELLESIP_LIBRARIES - The libraries needed to use belle-sip # BELLESIP_CPPFLAGS - The compilation flags needed to use belle-sip # BELLESIP_LDFLAGS - The linking flags needed to use belle-sip if(NOT LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) include("${CMAKE_CURRENT_LIST_DIR}/BelleSIPTargets.cmake") endif() if(@ENABLE_SHARED@) set(BELLESIP_TARGETNAME bellesip) set(BELLESIP_LIBRARIES ${BELLESIP_TARGETNAME}) else() set(BELLESIP_TARGETNAME bellesip-static) if(TARGET ${BELLESIP_TARGETNAME}) if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) set(BELLESIP_LIBRARIES ${BELLESIP_TARGETNAME}) else() get_target_property(BELLESIP_LIBRARIES ${BELLESIP_TARGETNAME} LOCATION) endif() get_target_property(BELLESIP_LINK_LIBRARIES ${BELLESIP_TARGETNAME} INTERFACE_LINK_LIBRARIES) if(BELLESIP_LINK_LIBRARIES) list(APPEND BELLESIP_LIBRARIES ${BELLESIP_LINK_LIBRARIES}) endif() endif() endif() get_target_property(BELLESIP_INCLUDE_DIRS ${BELLESIP_TARGETNAME} INTERFACE_INCLUDE_DIRECTORIES) if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) list(INSERT BELLESIP_INCLUDE_DIRS 0 "${EP_bellesip_INCLUDE_DIR}") else() list(INSERT BELLESIP_INCLUDE_DIRS 0 "@CMAKE_INSTALL_FULL_INCLUDEDIR@") endif() list(REMOVE_DUPLICATES BELLESIP_INCLUDE_DIRS) set(BELLESIP_CPPFLAGS @BELLESIP_CPPFLAGS@) set(BELLESIP_LDFLAGS "@LINK_FLAGS_STR@") set(BELLESIP_FOUND 1) belle-sip-1.6.3/cmake/FindAntlr3.cmake000066400000000000000000000044751313437522400174410ustar00rootroot00000000000000############################################################################ # FindAntlr3.txt # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the antlr3c include file and library and antlr.jar # # ANTLR3_FOUND - system has antlr3c # ANTLR3C_INCLUDE_DIR - the antlr3c include directory # ANTLR3C_LIBRARIES - The libraries needed to use antlr3c # ANTLR3_COMMAND - The command to run the antlr jar find_package(Java COMPONENTS Runtime REQUIRED) set(_ANTLR3C_ROOT_PATHS ${CMAKE_INSTALL_PREFIX} ) set(_ANTLR3_JAR_ROOT_PATHS ${CMAKE_INSTALL_PREFIX} /usr/local /usr /opt/local ) find_path(ANTLR3C_INCLUDE_DIRS NAMES antlr3.h HINTS _ANTLR3C_ROOT_PATHS PATH_SUFFIXES include ) if(ANTLR3C_INCLUDE_DIRS) set(HAVE_ANTLR3_H 1) endif() find_library(ANTLR3C_LIBRARIES NAMES antlr3c HINTS _ANTLR3C_ROOT_PATHS PATH_SUFFIXES bin lib ) find_file(ANTLR3_COMMAND NAMES antlr3 HINTS ${_ANTLR3_JAR_ROOT_PATHS} PATH_SUFFIXES bin ) if(NOT ANTLR3_COMMAND) # antlr3 command not found, search for the jar file find_file(ANTLR3_JAR_PATH NAMES antlr3.jar antlr.jar HINTS ${_ANTLR3_JAR_ROOT_PATHS} PATH_SUFFIXES share/java ) if(ANTLR3_JAR_PATH) set(ANTLR3_COMMAND ${Java_JAVA_EXECUTABLE} -Xmx384m -jar ${ANTLR3_JAR_PATH}) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Antlr3 DEFAULT_MSG ANTLR3C_INCLUDE_DIRS ANTLR3C_LIBRARIES ANTLR3_COMMAND ) mark_as_advanced(ANTLR3C_INCLUDE_DIRS ANTLR3C_LIBRARIES ANTLR3_COMMAND) belle-sip-1.6.3/cmake/FindZlib.cmake000066400000000000000000000037071313437522400171730ustar00rootroot00000000000000############################################################################ # FindZlib.txt # Copyright (C) 2015 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the zlib include file and library # # ZLIB_FOUND - system has zlib # ZLIB_INCLUDE_DIRS - the zlib include directory # ZLIB_LIBRARIES - The libraries needed to use zlib if(APPLE AND NOT IOS AND NOT CMAKE_FIND_ROOT_PATH) set(CMAKE_FIND_ROOT_PATH ${CMAKE_OSX_SYSROOT}) #On MACOSX zlib is from xcode SDK endif() if(ZLIB_HINTS) set(ZLIB_LIBRARIES_HINTS "${ZLIB_HINTS}/lib") endif() find_path(ZLIB_INCLUDE_DIRS NAMES zlib.h HINTS "${ZLIB_HINTS}" PATH_SUFFIXES include ) if(ZLIB_INCLUDE_DIRS) set(HAVE_ZLIB_H 1) endif() if(ENABLE_STATIC) find_library(ZLIB_LIBRARIES NAMES zstatic zlibstatic zlibstaticd z HINTS "${ZLIB_LIBRARIES_HINTS}" ) else() find_library(ZLIB_LIBRARIES NAMES z zlib zlibd HINTS "${ZLIB_LIBRARIES_HINTS}" ) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Zlib DEFAULT_MSG ZLIB_INCLUDE_DIRS ZLIB_LIBRARIES HAVE_ZLIB_H ) mark_as_advanced(ZLIB_INCLUDE_DIRS ZLIB_LIBRARIES HAVE_ZLIB_H) belle-sip-1.6.3/config.h.cmake000066400000000000000000000031631313437522400161010ustar00rootroot00000000000000/*************************************************************************** * config.h.cmake * Copyright (C) 2014 Belledonne Communications, Grenoble France * **************************************************************************** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ****************************************************************************/ #cmakedefine PACKAGE "@PACKAGE@" #cmakedefine PACKAGE_NAME "@PACKAGE_NAME@" #cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@" #cmakedefine PACKAGE_STRING "@PACKAGE_STRING@" #cmakedefine PACKAGE_TARNAME "@PACKAGE_TARNAME@" #cmakedefine PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" #cmakedefine PACKAGE_URL "@PACKAGE_URL@" #cmakedefine VERSION "@VERSION@" #cmakedefine HAVE_LIBDL #cmakedefine HAVE_CLOCK_GETTIME #cmakedefine HAVE_RESINIT #cmakedefine HAVE_ANTLR3_H #cmakedefine HAVE_ANTLR_STRING_STREAM_NEW #cmakedefine HAVE_TUNNEL #cmakedefine HAVE_ZLIB #cmakedefine BELLE_SDP_FORCE_RTP_MAP #cmakedefine ENABLE_SERVER_SOCKETS belle-sip-1.6.3/configure.ac000077500000000000000000000241061313437522400156750ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.63]) AC_INIT([belle-sip],[1.6.1],[jehan.monnier@linphone.org]) BELLESIP_SO_CURRENT=0 dnl increment this number when you add/change/remove an interface BELLESIP_SO_REVISION=0 dnl increment this number when you change source code, without changing interfaces; set to 0 when incrementing CURRENT BELLESIP_SO_AGE=0 dnl increment this number when you add an interface, set to 0 if you remove an interface BELLESIP_SO_VERSION=$BELLESIP_SO_CURRENT:$BELLESIP_SO_REVISION:$BELLESIP_SO_AGE AC_SUBST(BELLESIP_SO_CURRENT, $BELLESIP_SO_CURRENT) AC_SUBST(BELLESIP_SO_VERSION) AC_CONFIG_SRCDIR([src/belle_sip_utils.c]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) AC_CANONICAL_SYSTEM dnl initialize pkg-config so that we can use it within if else fi statements. PKG_PROG_PKG_CONFIG() AM_INIT_AUTOMAKE([subdir-objects foreign]) AM_SILENT_RULES(yes) # Checks for programs. dnl do not put anythingelse before AC_PROG_CC unless checking if macro still work for clang dnl because of tunnel library wrapper, C++ is required. AC_PROG_CXX(["xcrun clang++" g++]) AC_PROG_CC(["xcrun clang" gcc]) AC_PROG_OBJC(["xcrun clang" gcc]) AM_PROG_CC_C_O case $INSTALL in *ginstall*) INSTALL="$INSTALL -C" ;; esac AC_PROG_CC_C99 LT_INIT(win32-dll) case "$target" in *-apple-darwin.ios|i386-apple*|armv6-apple*|armv7-apple*|armv7s-apple*|arm64-apple*|aarch64-apple*) LIBS="$LIBS -framework Foundation -framework CoreFoundation -framework CFNetwork -framework UIKit" build_apple=yes ;; #macosx 64 bits x86_64-apple-darwin*) LIBS="$LIBS -framework Foundation" OBJCFLAGS="$OBJCFLAGS -fmodules" build_apple=yes ;; esac AM_CONDITIONAL([BUILD_APPLE], [test "x$build_apple" = "xyes"]) dnl Workaround for mingw, whose compiler does not check in /usr/include ... case "$target_os" in *mingw*) if test "$cross_compiling" != "yes"; then if test "$prefix" = "/usr" ; then CPPFLAGS="$CPPFLAGS -I/usr/include" LDFLAGS="$LDFLAGS -L/usr/lib" fi fi ;; esac if test -f /etc/debian_version ; then use_deb=true; else use_rpm=true; fi AC_ARG_ENABLE(debug, [ --enable-debug Turn on debug mode (default=no)], [case "${enableval}" in yes) debug=true ;; no) debug=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-debug) ;; esac],[debug=false]) CFLAGS="$CFLAGS -fms-extensions" if test "$debug" = "no" ; then CFLAGS="$CFLAGS -g -O2" else CFLAGS="$CFLAGS -g" fi AC_ARG_ENABLE(strict, [ --enable-strict Turn on strict mode compilation, no warnings allowed (default=yes)], [case "${enableval}" in yes) strict=true ;; no) strict=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-strict) ;; esac],[strict=true]) STRICT_OPTIONS="-Wall" STRICT_OPTIONS_CC="-Wdeclaration-after-statement -Wstrict-prototypes" STRICT_OPTIONS_CXX="" case "$CC" in *clang*) STRICT_OPTIONS="$STRICT_OPTIONS -Wno-error=unknown-warning-option -Qunused-arguments -Wno-tautological-compare -Wno-builtin-requires-header -Wno-unused-function -Wno-gnu-designator " #disabled due to wrong optimization false positive with small string #(cf. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35903) STRICT_OPTIONS="$STRICT_OPTIONS -Wno-array-bounds " ;; gcc*) STRICT_OPTIONS="$STRICT_OPTIONS -Wno-error=pragmas" ;; esac # because Darwin's gcc is actually clang, we need to check it... case "$target_os" in *darwin*) STRICT_OPTIONS="$STRICT_OPTIONS -Wno-error=unknown-warning-option -Qunused-arguments -Wno-tautological-compare -Wno-unused-function " #disabled due to wrong optimization false positive with small string #(cf. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35903) STRICT_OPTIONS="$STRICT_OPTIONS -Wno-array-bounds " ;; esac if test "$strict" = "true"; then STRICT_OPTIONS="$STRICT_OPTIONS -Werror -Wextra -Wno-unused-parameter -Wno-error=unknown-pragmas -Wuninitialized -Wno-error=strict-prototypes -Wno-missing-field-initializers -Wno-error=deprecated-declarations" fi dnl because of antlr3 we must accept a few warnings... dnl more portable for the moment LESS_STRICT_OPTIONS="-Wno-error=sign-compare" AC_SUBST(STRICT_OPTIONS) AC_SUBST(STRICT_OPTIONS_CC) AC_SUBST(STRICT_OPTIONS_CXX) AC_SUBST(LESS_STRICT_OPTIONS) # Checks for libraries. # Checks for header files. AC_ARG_WITH( antlr, [ --with-antlr Set prefix where libantlr3c can be found (ex:/usr or /usr/local)[default=PREFIX] ], [ antlr_prefix=${withval}],[ antlr_prefix=${prefix} ]) found_antlr3=no if test "$antlr_prefix" != "NONE" && test "$antlr_prefix" != "/usr" ; then ANTLR_CFLAGS="-I${antlr_prefix}/include" ANTLR_LIBS="-L${antlr_prefix}/lib" fi ANTLR_LIBS="$ANTLR_LIBS -lantlr3c" dnl check antlr headers CPPFLAGS_save=$CPPFLAGS CPPFLAGS="$ANTLR_CFLAGS $CPPFLAGS" AC_CHECK_HEADERS([antlr3.h], [found_antlr3=yes]) AC_CHECK_DECL([antlr3StringStreamNew], [AC_DEFINE(HAVE_ANTLR_STRING_STREAM_NEW,1,[Defined when antlr 3.4 api is detected])], [foo=bar], [#include ]) libresolv_have_res_get_servers=no resolv_h_hav_res_ndestroy=no LIBS_save=$LIBS LIBS="$LIBS -lresolv" AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[res_getservers(NULL,NULL,0)]])] ,[libresolv_have_res_get_servers=yes AC_MSG_NOTICE([res_getservers usable])] ,[LIBS=$LIBS_save AC_MSG_WARN([res_getservers not usable])]) AC_CHECK_DECL([res_ndestroy], [resolv_h_hav_res_ndestroy=yes], [foo=bar], [#include ]) if test "${libresolv_have_res_get_servers}${resolv_h_hav_res_ndestroy}" == "yesyes" ; then AC_DEFINE(HAVE_RESINIT,1,[Defined when res_ninit api is available]) fi CPPFLAGS=$CPPFLAGS_save if test "$found_antlr3" != "yes" ; then AC_MSG_ERROR([Could not find antlr3 development files. Please install antlr3 version > 3.2 (libantlr3c-dev on debian/ubuntu systems)]) ANTLR_CFLAGS= ANTLR_LIBS= fi AC_PATH_PROG([ANTLR],[antlr3],[no],[$antlr_prefix/bin /usr/bin]) if test $ANTLR = "no" ; then antlr_java_prefixes="$antlr_prefix/share/java /usr/local/share/java /usr/share/java /opt/local/share/java" for antlr_java_prefix in $antlr_java_prefixes do antlr_jar=$antlr_java_prefix/antlr.jar if test -f $antlr_jar ; then break else antlr_jar=no fi antlr_jar=$antlr_java_prefix/antlr3.jar if test -f $antlr_jar ; then break else antlr_jar=no fi done if test $antlr_jar = "no" ; then AC_MSG_ERROR([Could not find antlr.jar. Please install antlr3 ]) fi AC_PATH_PROG([JAVA],[java],[no]) if test $JAVA = "no" ; then AC_MSG_ERROR([Could not find java prog. Please install java ]) else ANTLR="$JAVA -Xmx512m -jar $antlr_jar" fi fi AC_SUBST(ANTLR_CFLAGS) AC_SUBST(ANTLR_LIBS) AC_ARG_ENABLE( tls, [ --enable-tls Enable TLS support (default=yes)], [case "${enableval}" in yes) use_tls=true ;; no) use_tls=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-tls) ;; esac],[use_tls=true]) PKG_CHECK_MODULES(BCTOOLBOX, bctoolbox, [found_bctoolbox=yes],[found_bctoolbox=no]) if test "x$found_bctoolbox" = "xyes" ; then TLS_CFLAGS=$BCTOOLBOX_CFLAGS TLS_LIBS=$BCTOOLBOX_LIBS else AC_MSG_ERROR(["Could not find bctoolbox (required dependency)"]) fi AC_SUBST(TLS_CFLAGS) AC_SUBST(TLS_LIBS) AC_SUBST(TLS_PC) AC_ARG_ENABLE(tunnel, [AS_HELP_STRING([--enable-tunnel], [Enable tunnel support (default=no)])]) if test "$enable_tunnel" = "yes" ; then PKG_CHECK_MODULES(TUNNEL, tunnel, [found_tunnel=yes], [found_tunnel=no]) if test "$found_tunnel" = "yes" ; then AC_DEFINE(HAVE_TUNNEL, 1, [Defined when tunnel is enabled and available]) fi fi AM_CONDITIONAL(BUILD_TUNNEL, test "$found_tunnel" = "yes") PKG_CHECK_MODULES(BCTOOLBOXTESTER, bctoolbox-tester, [found_pkg_config_bctoolboxtester=yes],[found_pkg_config_bctoolboxtester=no]) case "$target_os" in *mingw*) LIBBELLESIP_CFLAGS="-DBELLESIP_EXPORTS" CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0501" LIBS="$LIBS -lws2_32 -liphlpapi" LDFLAGS="$LDFLAGS -Wl,--export-all-symbols" ;; esac AC_SUBST(LIBBELLESIP_CFLAGS) if test "$found_pkg_config_bctoolboxtester" = "no" ; then AC_MSG_WARN([Could not find bctoolbox tester wrapper, tests are not compiled.]) fi AC_ARG_ENABLE(tests, [AS_HELP_STRING([--disable-tests], [Disable compilation of tests])], [case "${enableval}" in yes) tests_enabled=true ;; no) tests_enabled=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --disable-tests) ;; esac], [tests_enabled=yes] ) AM_CONDITIONAL(ENABLE_TESTS, test x$tests_enabled = xyes && test x$found_pkg_config_bctoolboxtester = xyes) dnl check zlib AC_ARG_ENABLE(zlib, [AS_HELP_STRING([--disable-zlib], [Disable ZLib support])], [case "${enableval}" in yes) build_zlib=true ;; no) build_zlib=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --disable-zlib) ;; esac], [build_zlib=auto] ) if test "$build_zlib" != "false" ; then PKG_CHECK_MODULES(ZLIB, [zlib], [found_zlib=yes], [found_zlib=no]) if test "x$found_zlib" = "xno" ; then AC_CHECK_LIB(z, inflate, [AC_CHECK_HEADER([zlib.h], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include #if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1230) // compile error #endif ]],[])], [found_zlib=yes])])]) if test "x$found_zlib" = "xno" ; then AC_MSG_NOTICE([zlib library and headers not found]) else AC_DEFINE( HAVE_ZLIB, 1, [ZLIB support] ) ZLIB_LIBS='-lz' AC_SUBST(ZLIB_LIBS) fi else AC_MSG_NOTICE([ZLIB found]) AC_DEFINE( HAVE_ZLIB, 1, [ZLIB support] ) fi fi LIBS_PRIVATE="$LIBS_PRIVATE $ANTLR_LIBS $POLARSSL_LIBS" AC_SUBST(LIBS_PRIVATE) REQUIRES_PRIVATE="" AC_SUBST(REQUIRES_PRIVATE) # Checks for typedefs, structures, and compiler characteristics. # Eliminate -lstdc++ addition to postdeps for cross compiles. postdeps_CXX=`echo " $postdeps_CXX " | sed 's, -lstdc++ ,,g'` # Checks for library functions. AC_CHECK_LIB(rt, clock_gettime) AC_CHECK_LIB(dl, dlopen) AC_CHECK_LIB(pthread, pthread_getspecific,, [AC_MSG_ERROR([pthread library not found])]) AC_CONFIG_FILES( [ Makefile include/Makefile include/belle-sip/Makefile src/Makefile src/grammars/Makefile tester/Makefile belle-sip.pc belle-sip.spec ]) AC_OUTPUT belle-sip-1.6.3/include/000077500000000000000000000000001313437522400150245ustar00rootroot00000000000000belle-sip-1.6.3/include/CMakeLists.txt000066400000000000000000000033661313437522400175740ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2015 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ set(HEADER_FILES auth-helper.h belle-sdp.h belle-sip.h bodyhandler.h defs.h dialog.h dict.h generic-uri.h headers.h http-listener.h http-message.h http-provider.h list.h listener.h listeningpoint.h mainloop.h message.h object.h parameters.h provider.h refresher.h resolver.h sipstack.h sip-uri.h transaction.h types.h utils.h wakelock.h ) set(BELLE_SIP_HEADER_FILES ) foreach(HEADER_FILE ${HEADER_FILES}) list(APPEND BELLE_SIP_HEADER_FILES "${CMAKE_CURRENT_LIST_DIR}/belle-sip/${HEADER_FILE}") endforeach() set(BELLE_SIP_HEADER_FILES ${BELLE_SIP_HEADER_FILES} PARENT_SCOPE) install(FILES ${BELLE_SIP_HEADER_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/belle-sip PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ ) belle-sip-1.6.3/include/MSVC/000077500000000000000000000000001313437522400155745ustar00rootroot00000000000000belle-sip-1.6.3/include/MSVC/inttypes.h000066400000000000000000000175041313437522400176330ustar00rootroot00000000000000// ISO C9x compliant inttypes.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006 Alexander Chemeris // // 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. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_INTTYPES_H_ // [ #define _MSC_INTTYPES_H_ #if _MSC_VER > 1000 #pragma once #endif #include "stdint.h" // 7.8 Format conversion of integer types typedef struct { intmax_t quot; intmax_t rem; } imaxdiv_t; // 7.8.1 Macros for format specifiers #if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 // The fprintf macros for signed integers are: #define PRId8 "d" #define PRIi8 "i" #define PRIdLEAST8 "d" #define PRIiLEAST8 "i" #define PRIdFAST8 "d" #define PRIiFAST8 "i" #define PRId16 "hd" #define PRIi16 "hi" #define PRIdLEAST16 "hd" #define PRIiLEAST16 "hi" #define PRIdFAST16 "hd" #define PRIiFAST16 "hi" #define PRId32 "I32d" #define PRIi32 "I32i" #define PRIdLEAST32 "I32d" #define PRIiLEAST32 "I32i" #define PRIdFAST32 "I32d" #define PRIiFAST32 "I32i" #define PRId64 "I64d" #define PRIi64 "I64i" #define PRIdLEAST64 "I64d" #define PRIiLEAST64 "I64i" #define PRIdFAST64 "I64d" #define PRIiFAST64 "I64i" #define PRIdMAX "I64d" #define PRIiMAX "I64i" #define PRIdPTR "Id" #define PRIiPTR "Ii" // The fprintf macros for unsigned integers are: #define PRIo8 "o" #define PRIu8 "u" #define PRIx8 "x" #define PRIX8 "X" #define PRIoLEAST8 "o" #define PRIuLEAST8 "u" #define PRIxLEAST8 "x" #define PRIXLEAST8 "X" #define PRIoFAST8 "o" #define PRIuFAST8 "u" #define PRIxFAST8 "x" #define PRIXFAST8 "X" #define PRIo16 "ho" #define PRIu16 "hu" #define PRIx16 "hx" #define PRIX16 "hX" #define PRIoLEAST16 "ho" #define PRIuLEAST16 "hu" #define PRIxLEAST16 "hx" #define PRIXLEAST16 "hX" #define PRIoFAST16 "ho" #define PRIuFAST16 "hu" #define PRIxFAST16 "hx" #define PRIXFAST16 "hX" #define PRIo32 "I32o" #define PRIu32 "I32u" #define PRIx32 "I32x" #define PRIX32 "I32X" #define PRIoLEAST32 "I32o" #define PRIuLEAST32 "I32u" #define PRIxLEAST32 "I32x" #define PRIXLEAST32 "I32X" #define PRIoFAST32 "I32o" #define PRIuFAST32 "I32u" #define PRIxFAST32 "I32x" #define PRIXFAST32 "I32X" #define PRIo64 "I64o" #define PRIu64 "I64u" #define PRIx64 "I64x" #define PRIX64 "I64X" #define PRIoLEAST64 "I64o" #define PRIuLEAST64 "I64u" #define PRIxLEAST64 "I64x" #define PRIXLEAST64 "I64X" #define PRIoFAST64 "I64o" #define PRIuFAST64 "I64u" #define PRIxFAST64 "I64x" #define PRIXFAST64 "I64X" #define PRIoMAX "I64o" #define PRIuMAX "I64u" #define PRIxMAX "I64x" #define PRIXMAX "I64X" #define PRIoPTR "Io" #define PRIuPTR "Iu" #define PRIxPTR "Ix" #define PRIXPTR "IX" // The fscanf macros for signed integers are: #define SCNd8 "d" #define SCNi8 "i" #define SCNdLEAST8 "d" #define SCNiLEAST8 "i" #define SCNdFAST8 "d" #define SCNiFAST8 "i" #define SCNd16 "hd" #define SCNi16 "hi" #define SCNdLEAST16 "hd" #define SCNiLEAST16 "hi" #define SCNdFAST16 "hd" #define SCNiFAST16 "hi" #define SCNd32 "ld" #define SCNi32 "li" #define SCNdLEAST32 "ld" #define SCNiLEAST32 "li" #define SCNdFAST32 "ld" #define SCNiFAST32 "li" #define SCNd64 "I64d" #define SCNi64 "I64i" #define SCNdLEAST64 "I64d" #define SCNiLEAST64 "I64i" #define SCNdFAST64 "I64d" #define SCNiFAST64 "I64i" #define SCNdMAX "I64d" #define SCNiMAX "I64i" #ifdef _WIN64 // [ # define SCNdPTR "I64d" # define SCNiPTR "I64i" #else // _WIN64 ][ # define SCNdPTR "ld" # define SCNiPTR "li" #endif // _WIN64 ] // The fscanf macros for unsigned integers are: #define SCNo8 "o" #define SCNu8 "u" #define SCNx8 "x" #define SCNX8 "X" #define SCNoLEAST8 "o" #define SCNuLEAST8 "u" #define SCNxLEAST8 "x" #define SCNXLEAST8 "X" #define SCNoFAST8 "o" #define SCNuFAST8 "u" #define SCNxFAST8 "x" #define SCNXFAST8 "X" #define SCNo16 "ho" #define SCNu16 "hu" #define SCNx16 "hx" #define SCNX16 "hX" #define SCNoLEAST16 "ho" #define SCNuLEAST16 "hu" #define SCNxLEAST16 "hx" #define SCNXLEAST16 "hX" #define SCNoFAST16 "ho" #define SCNuFAST16 "hu" #define SCNxFAST16 "hx" #define SCNXFAST16 "hX" #define SCNo32 "lo" #define SCNu32 "lu" #define SCNx32 "lx" #define SCNX32 "lX" #define SCNoLEAST32 "lo" #define SCNuLEAST32 "lu" #define SCNxLEAST32 "lx" #define SCNXLEAST32 "lX" #define SCNoFAST32 "lo" #define SCNuFAST32 "lu" #define SCNxFAST32 "lx" #define SCNXFAST32 "lX" #define SCNo64 "I64o" #define SCNu64 "I64u" #define SCNx64 "I64x" #define SCNX64 "I64X" #define SCNoLEAST64 "I64o" #define SCNuLEAST64 "I64u" #define SCNxLEAST64 "I64x" #define SCNXLEAST64 "I64X" #define SCNoFAST64 "I64o" #define SCNuFAST64 "I64u" #define SCNxFAST64 "I64x" #define SCNXFAST64 "I64X" #define SCNoMAX "I64o" #define SCNuMAX "I64u" #define SCNxMAX "I64x" #define SCNXMAX "I64X" #ifdef _WIN64 // [ # define SCNoPTR "I64o" # define SCNuPTR "I64u" # define SCNxPTR "I64x" # define SCNXPTR "I64X" #else // _WIN64 ][ # define SCNoPTR "lo" # define SCNuPTR "lu" # define SCNxPTR "lx" # define SCNXPTR "lX" #endif // _WIN64 ] #endif // __STDC_FORMAT_MACROS ] // 7.8.2 Functions for greatest-width integer types // 7.8.2.1 The imaxabs function #define imaxabs _abs64 // 7.8.2.2 The imaxdiv function // This is modified version of div() function from Microsoft's div.c found // in %MSVC.NET%\crt\src\div.c #ifdef STATIC_IMAXDIV // [ static #else // STATIC_IMAXDIV ][ _inline #endif // STATIC_IMAXDIV ] imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) { imaxdiv_t result; result.quot = numer / denom; result.rem = numer % denom; if (numer < 0 && result.rem > 0) { // did division wrong; must fix up ++result.quot; result.rem -= denom; } return result; } // 7.8.2.3 The strtoimax and strtoumax functions #define strtoimax _strtoi64 #define strtoumax _strtoui64 // 7.8.2.4 The wcstoimax and wcstoumax functions #define wcstoimax _wcstoi64 #define wcstoumax _wcstoui64 #endif // _MSC_INTTYPES_H_ ] belle-sip-1.6.3/include/MSVC/stdint.h000066400000000000000000000170601313437522400172560ustar00rootroot00000000000000// ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2008 Alexander Chemeris // // 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. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ #if _MSC_VER > 1000 #pragma once #endif #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we should wrap include with 'extern "C++" {}' // or compiler give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed #ifdef __cplusplus extern "C" { #endif # include #ifdef __cplusplus } #endif // Define _W64 macros to mark types changing their size, like intptr_t. #ifndef _W64 # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 # define _W64 __w64 # else # define _W64 # endif #endif // 7.18.1 Integer types // 7.18.1.1 Exact-width integer types // Visual Studio 6 and Embedded Visual C++ 4 doesn't // realize that, e.g. char has the same size as __int8 // so we give up on __intX for them. #if (_MSC_VER < 1300) typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; // 7.18.1.2 Minimum-width integer types typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; typedef int64_t int_least64_t; typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; typedef int16_t int_fast16_t; typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; // 7.18.1.4 Integer types capable of holding object pointers #ifdef _WIN64 // [ typedef signed __int64 intptr_t; typedef unsigned __int64 uintptr_t; #else // _WIN64 ][ typedef _W64 signed int intptr_t; typedef _W64 unsigned int uintptr_t; #endif // _WIN64 ] // 7.18.1.5 Greatest-width integer types typedef int64_t intmax_t; typedef uint64_t uintmax_t; // 7.18.2 Limits of specified-width integer types #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 // 7.18.2.1 Limits of exact-width integer types #define INT8_MIN ((int8_t)_I8_MIN) #define INT8_MAX _I8_MAX #define INT16_MIN ((int16_t)_I16_MIN) #define INT16_MAX _I16_MAX #define INT32_MIN ((int32_t)_I32_MIN) #define INT32_MAX _I32_MAX #define INT64_MIN ((int64_t)_I64_MIN) #define INT64_MAX _I64_MAX #define UINT8_MAX _UI8_MAX #define UINT16_MAX _UI16_MAX #define UINT32_MAX _UI32_MAX #define UINT64_MAX _UI64_MAX // 7.18.2.2 Limits of minimum-width integer types #define INT_LEAST8_MIN INT8_MIN #define INT_LEAST8_MAX INT8_MAX #define INT_LEAST16_MIN INT16_MIN #define INT_LEAST16_MAX INT16_MAX #define INT_LEAST32_MIN INT32_MIN #define INT_LEAST32_MAX INT32_MAX #define INT_LEAST64_MIN INT64_MIN #define INT_LEAST64_MAX INT64_MAX #define UINT_LEAST8_MAX UINT8_MAX #define UINT_LEAST16_MAX UINT16_MAX #define UINT_LEAST32_MAX UINT32_MAX #define UINT_LEAST64_MAX UINT64_MAX // 7.18.2.3 Limits of fastest minimum-width integer types #define INT_FAST8_MIN INT8_MIN #define INT_FAST8_MAX INT8_MAX #define INT_FAST16_MIN INT16_MIN #define INT_FAST16_MAX INT16_MAX #define INT_FAST32_MIN INT32_MIN #define INT_FAST32_MAX INT32_MAX #define INT_FAST64_MIN INT64_MIN #define INT_FAST64_MAX INT64_MAX #define UINT_FAST8_MAX UINT8_MAX #define UINT_FAST16_MAX UINT16_MAX #define UINT_FAST32_MAX UINT32_MAX #define UINT_FAST64_MAX UINT64_MAX // 7.18.2.4 Limits of integer types capable of holding object pointers #ifdef _WIN64 // [ # define INTPTR_MIN INT64_MIN # define INTPTR_MAX INT64_MAX # define UINTPTR_MAX UINT64_MAX #else // _WIN64 ][ # define INTPTR_MIN INT32_MIN # define INTPTR_MAX INT32_MAX # define UINTPTR_MAX UINT32_MAX #endif // _WIN64 ] // 7.18.2.5 Limits of greatest-width integer types #define INTMAX_MIN INT64_MIN #define INTMAX_MAX INT64_MAX #define UINTMAX_MAX UINT64_MAX // 7.18.3 Limits of other integer types #ifdef _WIN64 // [ # define PTRDIFF_MIN _I64_MIN # define PTRDIFF_MAX _I64_MAX #else // _WIN64 ][ # define PTRDIFF_MIN _I32_MIN # define PTRDIFF_MAX _I32_MAX #endif // _WIN64 ] #define SIG_ATOMIC_MIN INT_MIN #define SIG_ATOMIC_MAX INT_MAX #ifndef SIZE_MAX // [ # ifdef _WIN64 // [ # define SIZE_MAX _UI64_MAX # else // _WIN64 ][ # define SIZE_MAX _UI32_MAX # endif // _WIN64 ] #endif // SIZE_MAX ] // WCHAR_MIN and WCHAR_MAX are also defined in #ifndef WCHAR_MIN // [ # define WCHAR_MIN 0 #endif // WCHAR_MIN ] #ifndef WCHAR_MAX // [ # define WCHAR_MAX _UI16_MAX #endif // WCHAR_MAX ] #define WINT_MIN 0 #define WINT_MAX _UI16_MAX #endif // __STDC_LIMIT_MACROS ] // 7.18.4 Limits of other integer types #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants #define INTMAX_C INT64_C #define UINTMAX_C UINT64_C #endif // __STDC_CONSTANT_MACROS ] #endif // _MSC_STDINT_H_ ] belle-sip-1.6.3/include/Makefile.am000066400000000000000000000000221313437522400170520ustar00rootroot00000000000000SUBDIRS=belle-sip belle-sip-1.6.3/include/belle-sip/000077500000000000000000000000001313437522400167005ustar00rootroot00000000000000belle-sip-1.6.3/include/belle-sip/Makefile.am000066400000000000000000000010551313437522400207350ustar00rootroot00000000000000bellesipdir=$(includedir)/belle-sip bellesip_HEADERS=\ object.h \ sip-uri.h \ list.h \ headers.h \ parameters.h \ mainloop.h \ transaction.h \ message.h \ listeningpoint.h \ sipstack.h \ provider.h \ listener.h \ dialog.h \ utils.h \ auth-helper.h \ belle-sdp.h \ belle-sip.h \ refresher.h \ defs.h \ resolver.h \ dict.h \ http-provider.h \ http-message.h \ http-listener.h \ generic-uri.h \ types.h \ bodyhandler.h EXTRA_DIST=$(bellesip_HEADERS) belle-sip-1.6.3/include/belle-sip/auth-helper.h000066400000000000000000000313461313437522400212760ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef AUTHENTICATION_HELPER_H_ #define AUTHENTICATION_HELPER_H_ #include "belle-sip/defs.h" #include "belle-sip/belle-sip.h" BELLE_SIP_BEGIN_DECLS /** * Create an authorization header from an www_authenticate header, all common parameters are copyed. * copy params: scheme, realm, nonce, algorithm, opaque * @param authentication source to be used as input * @return belle_sip_header_authorization_t* */ BELLESIP_EXPORT belle_sip_header_authorization_t* belle_sip_auth_helper_create_authorization(const belle_sip_header_www_authenticate_t* authentication); /** * Create an www_authenticate header from an authorization header, all common parameters are copyed. * copy params: scheme, realm, nonce, algorithm, opaque * @param authorization source to be used as input * @return belle_sip_header_www_authenticate_t* */ BELLESIP_EXPORT belle_sip_header_www_authenticate_t* belle_sip_auth_helper_create_www_authenticate(const belle_sip_header_authorization_t* authorization); /** * Create an http authorization header from an www_authenticate header, all common parameters are copyed. * copy params: scheme, realm, nonce, algorithm, opaque * @param authentication source to be used as input * @return belle_http_header_authorization_t* */ BELLESIP_EXPORT belle_http_header_authorization_t* belle_http_auth_helper_create_authorization(const belle_sip_header_www_authenticate_t* authentication); /** * Create an proxy_authorization header from an www_authenticate header, all common parameters are copyed. * copy params: scheme, realm, nonce, algorithm, opaque * @param authentication source to be used as input * @return belle_sip_header_authorization_t* */ BELLESIP_EXPORT belle_sip_header_proxy_authorization_t* belle_sip_auth_helper_create_proxy_authorization(const belle_sip_header_proxy_authenticate_t* proxy_authentication); /** * compute and set response value according to parameters * HA1=MD5(username:realm:passwd) * fills cnonce if needed (qop=auth); * fills qop * * @return 0 if succeed */ BELLESIP_EXPORT int belle_sip_auth_helper_fill_authorization(belle_sip_header_authorization_t* authorization ,const char* method ,const char* ha1); /** * compute and set response value according to parameters * @return 0 if succeed */ BELLESIP_EXPORT int belle_sip_auth_helper_fill_proxy_authorization(belle_sip_header_proxy_authorization_t* proxy_authorization ,const char* method ,const char* ha1); /* * compute HA1 (NULL terminated) * HA1=MD5(userid:realm:passwd) * return 0 in case of success * */ BELLESIP_EXPORT int belle_sip_auth_helper_compute_ha1(const char* userid,const char* realm,const char* password, char ha1[33]); /* * compute HA2 (NULL terminated) * HA2=MD5(method:uri) * return 0 in case of success * */ BELLESIP_EXPORT int belle_sip_auth_helper_compute_ha2(const char* method,const char* uri, char ha2[33]); /* * compute response(NULL terminated) * res=MD5(ha1:nonce:ha2) * return 0 in case of success * */ BELLESIP_EXPORT int belle_sip_auth_helper_compute_response(const char* ha1,const char* nonce, const char* ha2, char response[33]); /* * compute response(NULL terminated) * res=MD5(HA1:nonce:nonce_count:cnonce:qop:HA2) * return 0 in case of success * */ BELLESIP_EXPORT int belle_sip_auth_helper_compute_response_qop_auth( const char* ha1 , const char* nonce , unsigned int nonce_count , const char* cnonce , const char* qop , const char* ha2 , char response[33]); /*TLS client certificate auth*/ /** * Set TLS certificate verification callback * * @param callback function pointer for callback, or NULL to unset * * Callback signature is: * int (*verify_cb_error_cb_t)(unsigned char* der, int length, int depth, int* flags); * der - raw certificate data, in DER format * length - length of certificate DER data * depth - position of certificate in cert chain, ending at 0 = root or top * flags - verification state for CURRENT certificate only */ BELLESIP_EXPORT int belle_sip_tls_set_verify_error_cb(void *callback); /** * Format of certificate buffer **/ typedef enum belle_sip_certificate_raw_format { BELLE_SIP_CERTIFICATE_RAW_FORMAT_PEM, /** PEM format*/ BELLE_SIP_CERTIFICATE_RAW_FORMAT_DER /** ASN.1 raw format*/ }belle_sip_certificate_raw_format_t; /** * Parse a buffer containing either a certificate chain order in PEM format or a single DER cert * @param buff raw buffer * @param size buffer size * @param format either PEM or DER * @return belle_sip_certificates_chain_t or NULL if cannot be decoded */ BELLESIP_EXPORT belle_sip_certificates_chain_t* belle_sip_certificates_chain_parse(const char* buff, size_t size,belle_sip_certificate_raw_format_t format); /** * Parse a buffer containing either a private or public rsa key in PEM format * @param buff raw buffer * @param size buffer size * @param passwd password (optionnal) * @return list of belle_sip_signing_key_t or NULL if cannot be decoded */ BELLESIP_EXPORT belle_sip_signing_key_t* belle_sip_signing_key_parse(const char* buff, size_t size,const char* passwd); /** * Parse a pather containing either a certificate chain order in PEM format or a single DER cert * @param path file * @param format either PEM or DER * @return belle_sip_certificates_chain_t or NUL if cannot be decoded */ BELLESIP_EXPORT belle_sip_certificates_chain_t* belle_sip_certificates_chain_parse_file(const char* path, belle_sip_certificate_raw_format_t format); /** * Parse a directory for *.pem file containing a certificate and private key in PEM format or a single DER cert with subject CNAME as given * * @param[in] path directory to parse * @param[in] subject subject CNAME to look for * @param[out] certificate result certificate, NULL if not found. Is allocated by this function, caller must do a belle_sip_object_unref on it after use * @param[out] pkey result private key, NULL if not found. Is allocated by this function, caller must do a belle_sip_object_unref on it after use * @param[in] format either PEM or DER * @return 0 if we found a certificate and key matching given subject common name */ BELLESIP_EXPORT int belle_sip_get_certificate_and_pkey_in_dir(const char *path, const char *subject, belle_sip_certificates_chain_t **certificate, belle_sip_signing_key_t **pkey, belle_sip_certificate_raw_format_t format); /** * Generate a self signed certificate and key and save them in a file if a path is given, file will be .pem * * @param[in] path If not NULL a file will be written in the given directory. filename is .pem * @param[in] subject used in the CN= field of issuer and subject name * @param[out] certificate the generated certificate. Must be destroyed using belle_sip_certificates_chain_destroy * @param[out] key the generated key. Must be destroyed using belle_sip_signing_key_destroy * @return 0 on success */ BELLESIP_EXPORT int belle_sip_generate_self_signed_certificate(const char* path, const char *subject, belle_sip_certificates_chain_t **certificate, belle_sip_signing_key_t **pkey); /** * Convert a certificate into a its PEM format string * * @param[in] cert The certificate to be converted into PEM format string * @return the PEM representation of certificate. Buffer is allocated by this function and must be freed by caller */ BELLESIP_EXPORT char *belle_sip_certificates_chain_get_pem(belle_sip_certificates_chain_t *cert); /** * Convert a key into a its PEM format string * * @param[in] key The key to be converted into PEM format string * @return the PEM representation of key. Buffer is allocated by this function and must be freed by caller */ BELLESIP_EXPORT char *belle_sip_signing_key_get_pem(belle_sip_signing_key_t *key); /** * Generate a certificate fingerprint as described in RFC4572 * Note: only SHA1 signing algo is supported for now * * @param[in] certificate The certificate used to generate the fingerprint * @return The generated fingerprint formatted according to RFC4572 section 5. Is a null terminated string, must be freed by caller */ BELLESIP_EXPORT char *belle_sip_certificates_chain_get_fingerprint(belle_sip_certificates_chain_t *certificate); /** * Parse a pather containing either a private or public rsa key * @param path file * @param passwd password (optionnal) * @return list of belle_sip_signing_key_t or NUL iff cannot be decoded */ BELLESIP_EXPORT belle_sip_signing_key_t* belle_sip_signing_key_parse_file(const char* path, const char* passwd); #define BELLE_TLS_VERIFY_NONE (0) #define BELLE_TLS_VERIFY_CN_MISMATCH (1) #define BELLE_TLS_VERIFY_ANY_REASON (0xff) /* Set of functions deprecated on 2016/02/02 use the belle_tls_crypto_config_XXX ones */ BELLESIP_DEPRECATED BELLESIP_EXPORT belle_tls_verify_policy_t *belle_tls_verify_policy_new(void); BELLESIP_DEPRECATED BELLESIP_EXPORT int belle_tls_verify_policy_set_root_ca(belle_tls_verify_policy_t *obj, const char *path); BELLESIP_DEPRECATED BELLESIP_EXPORT void belle_tls_verify_policy_set_exceptions(belle_tls_verify_policy_t *obj, int flags); BELLESIP_DEPRECATED BELLESIP_EXPORT unsigned int belle_tls_verify_policy_get_exceptions(const belle_tls_verify_policy_t *obj); /** * Create a new crypto configuration object * The crypto configuration may be passed to a http provider or a listening point using the appropriate methods * It can be used to provide : * - a path to the trusted root certificates * - a way to override certificate verification exceptions * - a ssl configuration structure provided directly to the underlying crypto library (mbedtls 2 or above), * @return an empty belle_tls_crypto_config object, trusted certificate path is initialised to the default system path without any warranty */ BELLESIP_EXPORT belle_tls_crypto_config_t *belle_tls_crypto_config_new(void); /** * Set the path to the trusted certificate chain * @param[in/out] obj The crypto configuration object to set * @param[in] path The path to the trusted certificate chain file(NULL terminated string) * * @return 0 on success */ BELLESIP_EXPORT int belle_tls_crypto_config_set_root_ca(belle_tls_crypto_config_t *obj, const char *path); /** * Set the content of the trusted certificate chain * @param[in/out] obj The crypto configuration object to set * @param[in] data The content to the trusted certificate chain data(NULL terminated string) * * @return 0 on success */ BELLESIP_EXPORT int belle_tls_crypto_config_set_root_ca_data(belle_tls_crypto_config_t *obj, const char *data); /** * Set the exception flags to manage exception overriding during peer certificate verification * @param[in/out] obj The crypto configuration object to set * @param[in] flags Flags value to set: * BELLE_TLS_VERIFY_NONE to raise and error on any exception * BELLE_TLS_VERIFY_CN_MISMATCH to ignore Common Name mismatch * BELLE_TLS_VERIFY_ANY_REASON to ignore any exception * * @return 0 on success */ BELLESIP_EXPORT void belle_tls_crypto_config_set_verify_exceptions(belle_tls_crypto_config_t *obj, int flags); /** * Get the exception flags used to manage exception overriding during peer certificate verification * @param[in]i obj The crypto configuration object to set * @return Possible flags value : * BELLE_TLS_VERIFY_NONE to raise and error on any exception * BELLE_TLS_VERIFY_CN_MISMATCH to ignore Common Name mismatch * BELLE_TLS_VERIFY_ANY_REASON to ignore any exception * */ BELLESIP_EXPORT unsigned int belle_tls_crypto_config_get_verify_exceptions(const belle_tls_crypto_config_t *obj); /** * Set the pointer to an externally provided ssl configuration for the crypto library * @param[in/out] obj The crypto configuration object to set * @param[in] ssl_config A pointer to an opaque structure which will be provided directly to the crypto library used in bctoolbox. Use with extra care. * This ssl_config structure is responsability of the caller and will not be freed at the connection's end. */ BELLESIP_EXPORT void belle_tls_crypto_config_set_ssl_config(belle_tls_crypto_config_t *obj, void *ssl_config); BELLE_SIP_END_DECLS #endif /* AUTHENTICATION_HELPER_H_ */ belle-sip-1.6.3/include/belle-sip/belle-sdp.h000066400000000000000000001013461313437522400207250ustar00rootroot00000000000000/* belle-sdp - SIP (RFC4566) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SDP_H_ #define BELLE_SDP_H_ #include "belle-sip/defs.h" #include "belle-sip/list.h" #define BELLE_SDP_CAST(obj,t) BELLE_SIP_CAST(obj,t) BELLE_SIP_BEGIN_DECLS /*************************************************************************************** * Attribute * **************************************************************************************/ typedef struct _belle_sdp_attribute belle_sdp_attribute_t; BELLESIP_EXPORT belle_sdp_attribute_t* belle_sdp_attribute_new(void); BELLESIP_EXPORT belle_sdp_attribute_t* belle_sdp_attribute_parse (const char* attribute); BELLESIP_EXPORT belle_sdp_attribute_t* belle_sdp_attribute_create (const char* name,const char* value); BELLESIP_EXPORT const char* belle_sdp_attribute_get_name(const belle_sdp_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_attribute_set_name(belle_sdp_attribute_t* attribute, const char* name); BELLESIP_EXPORT const char* belle_sdp_attribute_get_value(belle_sdp_attribute_t* attribute); BELLESIP_EXPORT unsigned int belle_sdp_attribute_has_value(belle_sdp_attribute_t* attribute); #define BELLE_SDP_ATTRIBUTE(t) BELLE_SDP_CAST(t,belle_sdp_attribute_t) #define belle_sdp_attribute_init(obj) /*nothing*/ /*************************************************************************************** * RAW Attribute * **************************************************************************************/ typedef struct _belle_sdp_raw_attribute belle_sdp_raw_attribute_t; BELLESIP_EXPORT belle_sdp_raw_attribute_t* belle_sdp_raw_attribute_new(void); BELLESIP_EXPORT belle_sdp_raw_attribute_t* belle_sdp_raw_attribute_parse(const char* attribute); BELLESIP_EXPORT belle_sdp_raw_attribute_t* belle_sdp_raw_attribute_create(const char* name, const char* value); BELLESIP_EXPORT void belle_sdp_raw_attribute_set_value(belle_sdp_raw_attribute_t* attribute, const char* value); #define BELLE_SDP_RAW_ATTRIBUTE(t) BELLE_SDP_CAST(t,belle_sdp_raw_attribute_t) /*************************************************************************************** * RTCP-FB Attribute * **************************************************************************************/ typedef enum _belle_sdp_rtcp_fb_val_type { BELLE_SDP_RTCP_FB_ACK, BELLE_SDP_RTCP_FB_NACK, BELLE_SDP_RTCP_FB_TRR_INT, BELLE_SDP_RTCP_FB_CCM } belle_sdp_rtcp_fb_val_type_t; typedef enum _belle_sdp_rtcp_fb_val_param { BELLE_SDP_RTCP_FB_NONE, BELLE_SDP_RTCP_FB_PLI, BELLE_SDP_RTCP_FB_SLI, BELLE_SDP_RTCP_FB_RPSI, BELLE_SDP_RTCP_FB_APP, BELLE_SDP_RTCP_FB_FIR, BELLE_SDP_RTCP_FB_TMMBR } belle_sdp_rtcp_fb_val_param_t; typedef struct _belle_sdp_rtcp_fb_attribute belle_sdp_rtcp_fb_attribute_t; BELLESIP_EXPORT belle_sdp_rtcp_fb_attribute_t* belle_sdp_rtcp_fb_attribute_new(void); BELLESIP_EXPORT belle_sdp_rtcp_fb_attribute_t* belle_sdp_rtcp_fb_attribute_parse(const char* attribute); BELLESIP_EXPORT belle_sdp_rtcp_fb_attribute_t* belle_sdp_rtcp_fb_attribute_create(void); BELLESIP_EXPORT int8_t belle_sdp_rtcp_fb_attribute_get_id(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_id(belle_sdp_rtcp_fb_attribute_t* attribute, int8_t id); BELLESIP_EXPORT belle_sdp_rtcp_fb_val_type_t belle_sdp_rtcp_fb_attribute_get_type(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_type(belle_sdp_rtcp_fb_attribute_t* attribute, belle_sdp_rtcp_fb_val_type_t type); BELLESIP_EXPORT belle_sdp_rtcp_fb_val_param_t belle_sdp_rtcp_fb_attribute_get_param(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_param(belle_sdp_rtcp_fb_attribute_t* attribute, belle_sdp_rtcp_fb_val_param_t param); BELLESIP_EXPORT uint16_t belle_sdp_rtcp_fb_attribute_get_trr_int(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_trr_int(belle_sdp_rtcp_fb_attribute_t* attribute, uint16_t milliseconds); BELLESIP_EXPORT uint32_t belle_sdp_rtcp_fb_attribute_get_smaxpr(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_smaxpr(belle_sdp_rtcp_fb_attribute_t* attribute, uint32_t smaxpr); #define BELLE_SDP_RTCP_FB_ATTRIBUTE(t) BELLE_SDP_CAST(t,belle_sdp_rtcp_fb_attribute_t) /*************************************************************************************** * RTCP-XR Attribute * **************************************************************************************/ typedef struct _belle_sdp_rtcp_xr_attribute belle_sdp_rtcp_xr_attribute_t; BELLESIP_EXPORT belle_sdp_rtcp_xr_attribute_t* belle_sdp_rtcp_xr_attribute_new(void); BELLESIP_EXPORT belle_sdp_rtcp_xr_attribute_t* belle_sdp_rtcp_xr_attribute_parse(const char* attribute); BELLESIP_EXPORT belle_sdp_rtcp_xr_attribute_t* belle_sdp_rtcp_xr_attribute_create(void); BELLESIP_EXPORT const char* belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(const belle_sdp_rtcp_xr_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode(belle_sdp_rtcp_xr_attribute_t* attribute, const char *mode); BELLESIP_EXPORT int belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_max_size(const belle_sdp_rtcp_xr_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_max_size(belle_sdp_rtcp_xr_attribute_t* attribute, int max_size); BELLESIP_EXPORT unsigned int belle_sdp_rtcp_xr_attribute_has_stat_summary(const belle_sdp_rtcp_xr_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_xr_attribute_set_stat_summary(belle_sdp_rtcp_xr_attribute_t* attribute, unsigned int enable); BELLESIP_EXPORT const belle_sip_list_t* belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(const belle_sdp_rtcp_xr_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(belle_sdp_rtcp_xr_attribute_t* attribute, const char* flag); BELLESIP_EXPORT unsigned int belle_sdp_rtcp_xr_attribute_has_voip_metrics(const belle_sdp_rtcp_xr_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_xr_attribute_set_voip_metrics(belle_sdp_rtcp_xr_attribute_t* attribute, unsigned int enable); #define BELLE_SDP_RTCP_XR_ATTRIBUTE(t) BELLE_SDP_CAST(t,belle_sdp_rtcp_xr_attribute_t) /*************************************************************************************** * Bandwidth * **************************************************************************************/ typedef struct _belle_sdp_bandwidth belle_sdp_bandwidth_t; BELLESIP_EXPORT belle_sdp_bandwidth_t* belle_sdp_bandwidth_new(void); BELLESIP_EXPORT belle_sdp_bandwidth_t* belle_sdp_bandwidth_parse (const char* bandwidth); BELLESIP_EXPORT int belle_sdp_bandwidth_get_value(const belle_sdp_bandwidth_t* attribute); BELLESIP_EXPORT const char* belle_sdp_bandwidth_get_type(const belle_sdp_bandwidth_t* attribute); BELLESIP_EXPORT void belle_sdp_bandwidth_set_value(belle_sdp_bandwidth_t* attribute, int value); BELLESIP_EXPORT void belle_sdp_bandwidth_set_type(belle_sdp_bandwidth_t* attribute, const char* type); #define BELLE_SDP_BANDWIDTH(t) BELLE_SDP_CAST(t,belle_sdp_bandwidth_t) /*************************************************************************************** * Connection * **************************************************************************************/ typedef struct _belle_sdp_connection belle_sdp_connection_t; BELLESIP_EXPORT belle_sdp_connection_t* belle_sdp_connection_new(void); BELLESIP_EXPORT belle_sdp_connection_t* belle_sdp_connection_create(const char* net_type, const char* addr_type, const char* addr); BELLESIP_EXPORT belle_sdp_connection_t* belle_sdp_connection_parse (const char* connection); BELLESIP_EXPORT const char* belle_sdp_connection_get_address(const belle_sdp_connection_t* connection); BELLESIP_EXPORT const char* belle_sdp_connection_get_address_type(const belle_sdp_connection_t* connection); BELLESIP_EXPORT const char* belle_sdp_connection_get_network_type(const belle_sdp_connection_t* connection); BELLESIP_EXPORT int belle_sdp_connection_get_ttl(const belle_sdp_connection_t* connection); BELLESIP_EXPORT int belle_sdp_connection_get_range(const belle_sdp_connection_t* connection); BELLESIP_EXPORT void belle_sdp_connection_set_address(belle_sdp_connection_t* connection, const char* addr); BELLESIP_EXPORT void belle_sdp_connection_set_address_type(belle_sdp_connection_t* connection, const char* type); BELLESIP_EXPORT void belle_sdp_connection_set_network_type(belle_sdp_connection_t* connection, const char* type); BELLESIP_EXPORT void belle_sdp_connection_set_ttl(belle_sdp_connection_t* connection,int ttl); BELLESIP_EXPORT void belle_sdp_connection_set_range(belle_sdp_connection_t* connection,int range); #define BELLE_SDP_CONNECTION(t) BELLE_SDP_CAST(t,belle_sdp_connection_t) /*************************************************************************************** * Email * **************************************************************************************/ typedef struct _belle_sdp_email belle_sdp_email_t; BELLESIP_EXPORT belle_sdp_email_t* belle_sdp_email_new(void); BELLESIP_EXPORT belle_sdp_email_t* belle_sdp_email_parse (const char* email); BELLESIP_EXPORT const char* belle_sdp_email_get_value(const belle_sdp_email_t* email); BELLESIP_EXPORT void belle_sdp_email_set_value(belle_sdp_email_t* email, const char* value); #define BELLE_SDP_EMAIL(t) BELLE_SDP_CAST(t,belle_sdp_email_t) /*************************************************************************************** * Info * **************************************************************************************/ typedef struct _belle_sdp_info belle_sdp_info_t; BELLESIP_EXPORT belle_sdp_info_t* belle_sdp_info_new(void); BELLESIP_EXPORT belle_sdp_info_t* belle_sdp_info_parse (const char* info); BELLESIP_EXPORT const char* belle_sdp_info_get_value(const belle_sdp_info_t* info); BELLESIP_EXPORT void belle_sdp_info_set_value(belle_sdp_info_t* info, const char* value); #define BELLE_SDP_INFO(t) BELLE_SDP_CAST(t,belle_sdp_info_t) /*************************************************************************************** * Key * **************************************************************************************/ //typedef struct _belle_sdp_key belle_sdp_key_t; //belle_sdp_key_t* belle_sdp_key_new(void); //belle_sdp_key_t* belle_sdp_key_parse (const char* key); //const char* belle_sdp_key_get_key(const belle_sdp_key_t* key); //const char* belle_sdp_key_get_method(const belle_sdp_key_t* key); //unsigned int belle_sdp_key_as_key(const belle_sdp_key_t* key); //void belle_sdp_key_set_key(belle_sdp_key_t* key, const char* keyvalue); //void belle_sdp_key_set_method(belle_sdp_key_t* key, const char* method); //#define BELLE_SDP_KEY(t) BELLE_SDP_CAST(t,belle_sdp_key_t); /*************************************************************************************** * Media * **************************************************************************************/ typedef struct _belle_sdp_media belle_sdp_media_t; BELLESIP_EXPORT belle_sdp_media_t* belle_sdp_media_new(void); BELLESIP_EXPORT belle_sdp_media_t* belle_sdp_media_parse (const char* media); BELLESIP_EXPORT belle_sdp_media_t* belle_sdp_media_create(const char* media_type ,int media_port ,int port_count ,const char* protocol ,belle_sip_list_t* static_media_formats); BELLESIP_EXPORT belle_sip_list_t* belle_sdp_media_get_media_formats(const belle_sdp_media_t* media); BELLESIP_EXPORT const char* belle_sdp_media_get_raw_fmt(const belle_sdp_media_t* media); BELLESIP_EXPORT int belle_sdp_media_get_media_port(const belle_sdp_media_t* media); BELLESIP_EXPORT const char* belle_sdp_media_get_media_type(const belle_sdp_media_t* media); BELLESIP_EXPORT int belle_sdp_media_get_port_count(const belle_sdp_media_t* media); BELLESIP_EXPORT const char* belle_sdp_media_get_protocol(const belle_sdp_media_t* media); BELLESIP_EXPORT void belle_sdp_media_set_media_formats(belle_sdp_media_t* media, belle_sip_list_t* mediaFormats); BELLESIP_EXPORT void belle_sdp_media_set_raw_fmt(belle_sdp_media_t* media, const char* fmt); BELLESIP_EXPORT void belle_sdp_media_set_media_port(belle_sdp_media_t* media, int port); BELLESIP_EXPORT void belle_sdp_media_set_media_type(belle_sdp_media_t* media, const char* mediaType); BELLESIP_EXPORT void belle_sdp_media_set_port_count(belle_sdp_media_t* media, int port_count); BELLESIP_EXPORT void belle_sdp_media_set_protocol(belle_sdp_media_t* media, const char* protocole); #define BELLE_SDP_MEDIA(t) BELLE_SDP_CAST(t,belle_sdp_media_t) /*************************************************************************************** * mime_parameter * **************************************************************************************/ typedef struct _belle_sdp_mime_parameter belle_sdp_mime_parameter_t; BELLESIP_EXPORT belle_sdp_mime_parameter_t* belle_sdp_mime_parameter_new(void); BELLESIP_EXPORT belle_sdp_mime_parameter_t* belle_sdp_mime_parameter_create(const char* type, int media_format, int rate,int channel_count); BELLESIP_EXPORT int belle_sdp_mime_parameter_get_rate(const belle_sdp_mime_parameter_t* mime_parameter); BELLESIP_EXPORT void belle_sdp_mime_parameter_set_rate(belle_sdp_mime_parameter_t* mime_parameter,int rate); BELLESIP_EXPORT int belle_sdp_mime_parameter_get_channel_count(const belle_sdp_mime_parameter_t* mime_parameter); BELLESIP_EXPORT void belle_sdp_mime_parameter_set_channel_count(belle_sdp_mime_parameter_t* mime_parameter,int count); BELLESIP_EXPORT int belle_sdp_mime_parameter_get_ptime(const belle_sdp_mime_parameter_t* mime_parameter); BELLESIP_EXPORT void belle_sdp_mime_parameter_set_ptime(belle_sdp_mime_parameter_t* mime_parameter,int ptime); BELLESIP_EXPORT int belle_sdp_mime_parameter_get_max_ptime(const belle_sdp_mime_parameter_t* mime_parameter); BELLESIP_EXPORT void belle_sdp_mime_parameter_set_max_ptime(belle_sdp_mime_parameter_t* mime_parameter,int max_ptime); BELLESIP_EXPORT const char* belle_sdp_mime_parameter_get_type(const belle_sdp_mime_parameter_t* mime_parameter); BELLESIP_EXPORT void belle_sdp_mime_parameter_set_type(belle_sdp_mime_parameter_t* mime_parameter,const char* type); BELLESIP_EXPORT int belle_sdp_mime_parameter_get_media_format(const belle_sdp_mime_parameter_t* mime_parameter); BELLESIP_EXPORT void belle_sdp_mime_parameter_set_media_format(belle_sdp_mime_parameter_t* mime_parameter,int format); BELLESIP_EXPORT const char* belle_sdp_mime_parameter_get_parameters(const belle_sdp_mime_parameter_t* mime_parameter); BELLESIP_EXPORT void belle_sdp_mime_parameter_set_parameters(belle_sdp_mime_parameter_t* mime_parameter,const char* parameters); #define BELLE_SDP_MIME_PARAMETER(t) BELLE_SDP_CAST(t,belle_sdp_mime_parameter_t) /*************************************************************************************** * Media Description * **************************************************************************************/ typedef struct _belle_sdp_media_description belle_sdp_media_description_t; BELLESIP_EXPORT belle_sdp_media_description_t* belle_sdp_media_description_new(void); BELLESIP_EXPORT belle_sdp_media_description_t* belle_sdp_media_description_parse (const char* media_description); BELLESIP_EXPORT belle_sdp_media_description_t* belle_sdp_media_description_create(const char* media_type ,int media_port ,int port_count ,const char* protocol ,belle_sip_list_t* static_media_formats); BELLESIP_EXPORT void belle_sdp_media_description_add_dynamic_payloads(belle_sdp_media_description_t* media_description, belle_sip_list_t* payloadNames, belle_sip_list_t* payloadValues); BELLESIP_EXPORT const char* belle_sdp_media_description_get_attribute_value(const belle_sdp_media_description_t* media_description, const char* name); BELLESIP_EXPORT belle_sdp_attribute_t* belle_sdp_media_description_get_attribute(const belle_sdp_media_description_t* media_description, const char* name); BELLESIP_EXPORT belle_sip_list_t* belle_sdp_media_description_get_attributes(const belle_sdp_media_description_t* media_description); BELLESIP_EXPORT int belle_sdp_media_description_get_bandwidth(const belle_sdp_media_description_t* media_description, const char* name); BELLESIP_EXPORT belle_sip_list_t* belle_sdp_media_description_get_bandwidths(const belle_sdp_media_description_t* media_description); BELLESIP_EXPORT belle_sdp_connection_t* belle_sdp_media_description_get_connection(const belle_sdp_media_description_t* media_description); BELLESIP_EXPORT belle_sdp_info_t* belle_sdp_media_description_get_info(const belle_sdp_media_description_t* media_description); /*belle_sdp_key_t* belle_sdp_media_description_get_key(const belle_sdp_media_description_t* media_description);*/ BELLESIP_EXPORT belle_sdp_media_t* belle_sdp_media_description_get_media(const belle_sdp_media_description_t* media_description); BELLESIP_EXPORT belle_sip_list_t* belle_sdp_media_description_build_mime_parameters(const belle_sdp_media_description_t* media_description); /*belle_sip_list_t* belle_sdp_media_description_get_mime_types(const belle_sdp_media_description_t* media_description);*/ BELLESIP_EXPORT void belle_sdp_media_description_remove_attribute(belle_sdp_media_description_t* media_description,const char* attribute); BELLESIP_EXPORT void belle_sdp_media_description_remove_bandwidth(belle_sdp_media_description_t* media_description,const char* bandwidth); BELLESIP_EXPORT void belle_sdp_media_description_set_attribute_value(belle_sdp_media_description_t* media_description, const char* name, const char* value); BELLESIP_EXPORT void belle_sdp_media_description_add_attribute(belle_sdp_media_description_t* media_description, const belle_sdp_attribute_t* attr); BELLESIP_EXPORT void belle_sdp_media_description_set_attributes(belle_sdp_media_description_t* media_description, belle_sip_list_t* Attributes); BELLESIP_EXPORT void belle_sdp_media_description_set_bandwidth(belle_sdp_media_description_t* media_description, const char* name, int value); BELLESIP_EXPORT void belle_sdp_media_description_add_bandwidth(belle_sdp_media_description_t* media_description, const belle_sdp_bandwidth_t* bandwidth); BELLESIP_EXPORT void belle_sdp_media_description_set_bandwidths(belle_sdp_media_description_t* media_description, belle_sip_list_t* bandwidths); BELLESIP_EXPORT void belle_sdp_media_description_set_connection(belle_sdp_media_description_t* media_description, belle_sdp_connection_t* conn); BELLESIP_EXPORT void belle_sdp_media_description_set_info(belle_sdp_media_description_t* media_description,belle_sdp_info_t* i); /*void belle_sdp_media_description_set_key(belle_sdp_media_description_t* media_description,belle_sdp_key_t* key);*/ BELLESIP_EXPORT void belle_sdp_media_description_set_media(belle_sdp_media_description_t* media_description, belle_sdp_media_t* media); BELLESIP_EXPORT void belle_sdp_media_description_append_values_from_mime_parameter(belle_sdp_media_description_t* media_description, const belle_sdp_mime_parameter_t* mime_parameter); #define BELLE_SDP_MEDIA_DESCRIPTION(t) BELLE_SDP_CAST(t,belle_sdp_media_description_t) /*************************************************************************************** * Origin * **************************************************************************************/ typedef struct _belle_sdp_origin belle_sdp_origin_t; BELLESIP_EXPORT belle_sdp_origin_t* belle_sdp_origin_new(void); BELLESIP_EXPORT belle_sdp_origin_t* belle_sdp_origin_parse (const char* origin); BELLESIP_EXPORT belle_sdp_origin_t* belle_sdp_origin_create(const char* user_name , unsigned int session_id , unsigned int session_version , const char* network_type , const char* addr_type , const char* address); BELLESIP_EXPORT const char* belle_sdp_origin_get_address(const belle_sdp_origin_t* origin); BELLESIP_EXPORT const char* belle_sdp_origin_get_address_type(const belle_sdp_origin_t* origin); BELLESIP_EXPORT const char* belle_sdp_origin_get_network_type(const belle_sdp_origin_t* origin); BELLESIP_EXPORT unsigned int belle_sdp_origin_get_session_id(const belle_sdp_origin_t* origin); BELLESIP_EXPORT unsigned int belle_sdp_origin_get_session_version(const belle_sdp_origin_t* origin); BELLESIP_EXPORT const char* belle_sdp_origin_get_username(const belle_sdp_origin_t* origin); BELLESIP_EXPORT void belle_sdp_origin_set_address(belle_sdp_origin_t* origin, const char* address); BELLESIP_EXPORT void belle_sdp_origin_set_address_type(belle_sdp_origin_t* origin, const char* address); BELLESIP_EXPORT void belle_sdp_origin_set_network_type(belle_sdp_origin_t* origin, const char* network_type); BELLESIP_EXPORT void belle_sdp_origin_set_session_id(belle_sdp_origin_t* origin, unsigned int session_id); BELLESIP_EXPORT void belle_sdp_origin_set_session_version(belle_sdp_origin_t* origin, unsigned int version); BELLESIP_EXPORT void belle_sdp_origin_set_username(belle_sdp_origin_t* origin, const char* username); #define BELLE_SDP_ORIGIN(t) BELLE_SDP_CAST(t,belle_sdp_origin_t) /*************************************************************************************** * Phone * **************************************************************************************/ typedef struct _belle_sdp_phone belle_sdp_phone_t; BELLESIP_EXPORT belle_sdp_phone_t* belle_sdp_phone_new(void); BELLESIP_EXPORT belle_sdp_phone_t* belle_sdp_phone_parse (const char* phone); BELLESIP_EXPORT const char* belle_sdp_phone_get_value(const belle_sdp_phone_t* phone); BELLESIP_EXPORT void belle_sdp_phone_set_value(belle_sdp_phone_t* phone, const char* value); #define BELLE_SDP_PHONE(t) BELLE_SDP_CAST(t,belle_sdp_phone_t) /*************************************************************************************** * Repeat time * **************************************************************************************/ typedef struct _belle_sdp_repeate_time belle_sdp_repeate_time_t; BELLESIP_EXPORT belle_sdp_repeate_time_t* belle_sdp_repeate_time_new(void); BELLESIP_EXPORT belle_sdp_repeate_time_t* belle_sdp_repeate_time_parse (const char* repeate_time); BELLESIP_EXPORT const char* belle_sdp_repeate_time_get_value(const belle_sdp_repeate_time_t* repeate_time); BELLESIP_EXPORT void belle_sdp_repeate_time_set_value(belle_sdp_repeate_time_t* repeate_time, const char* value); #define BELLE_SDP_REPEATE_TIME(t) BELLE_SDP_CAST(t,belle_sdp_repeate_time_t) /*************************************************************************************** * Session Name * **************************************************************************************/ typedef struct _belle_sdp_session_name belle_sdp_session_name_t; BELLESIP_EXPORT belle_sdp_session_name_t* belle_sdp_session_name_new(void); BELLESIP_EXPORT belle_sdp_session_name_t* belle_sdp_session_name_create (const char* name); BELLESIP_EXPORT const char* belle_sdp_session_name_get_value(const belle_sdp_session_name_t* session_name); BELLESIP_EXPORT void belle_sdp_session_name_set_value(belle_sdp_session_name_t* session_name, const char* value); #define BELLE_SDP_SESSION_NAME(t) BELLE_SDP_CAST(t,belle_sdp_session_name_t) /*************************************************************************************** * Time * **************************************************************************************/ typedef struct _belle_sdp_time belle_sdp_time_t; BELLESIP_EXPORT belle_sdp_time_t* belle_sdp_time_new(void); BELLESIP_EXPORT belle_sdp_time_t* belle_sdp_time_parse (const char* time); BELLESIP_EXPORT int belle_sdp_time_get_start(const belle_sdp_time_t* time); BELLESIP_EXPORT int belle_sdp_time_get_stop(const belle_sdp_time_t* time); BELLESIP_EXPORT void belle_sdp_time_set_start(belle_sdp_time_t* time, int value); BELLESIP_EXPORT void belle_sdp_time_set_stop(belle_sdp_time_t* time, int value); #define BELLE_SDP_TIME(t) BELLE_SDP_CAST(t,belle_sdp_time_t) /*************************************************************************************** * Time description * **************************************************************************************/ typedef struct _belle_sdp_time_description belle_sdp_time_description_t; BELLESIP_EXPORT belle_sdp_time_description_t* belle_sdp_time_description_new(void); BELLESIP_EXPORT belle_sdp_time_description_t* belle_sdp_time_description_parse (const char* time_description); BELLESIP_EXPORT belle_sdp_time_description_t* belle_sdp_time_description_create (int start,int stop); BELLESIP_EXPORT belle_sip_list_t* belle_sdp_time_description_get_repeate_times(const belle_sdp_time_description_t* time_description); BELLESIP_EXPORT belle_sdp_time_t* belle_sdp_time_description_get_time(const belle_sdp_time_description_t* time_description); BELLESIP_EXPORT void belle_sdp_time_description_set_repeate_times(belle_sdp_time_description_t* time_description, belle_sip_list_t* times); BELLESIP_EXPORT void belle_sdp_time_description_set_time(belle_sdp_time_description_t* time_description, belle_sdp_time_t* times); #define BELLE_SDP_TIME_DESCRIPTION(t) BELLE_SDP_CAST(t,belle_sdp_time_description_t) /*************************************************************************************** * URI * **************************************************************************************/ typedef struct _belle_sdp_uri belle_sdp_uri_t; BELLESIP_EXPORT belle_sdp_uri_t* belle_sdp_uri_new(void); BELLESIP_EXPORT belle_sdp_uri_t* belle_sdp_uri_parse (const char* uri); BELLESIP_EXPORT const char* belle_sdp_uri_get_value(const belle_sdp_uri_t* uri); BELLESIP_EXPORT void belle_sdp_uri_set_value(belle_sdp_uri_t* uri, const char* value); #define BELLE_SDP_URI(t) BELLE_SDP_CAST(t,belle_sdp_uri_t) /*************************************************************************************** * Version * **************************************************************************************/ typedef struct _belle_sdp_version belle_sdp_version_t; belle_sdp_version_t* belle_sdp_version_new(void); BELLESIP_EXPORT belle_sdp_version_t* belle_sdp_version_create(int version); BELLESIP_EXPORT int belle_sdp_version_get_version(const belle_sdp_version_t* version); BELLESIP_EXPORT void belle_sdp_version_set_version(belle_sdp_version_t* version, int value); #define BELLE_SDP_VERSION(t) BELLE_SDP_CAST(t,belle_sdp_version_t) /*************************************************************************************** * Session Description * **************************************************************************************/ typedef struct _belle_sdp_session_description belle_sdp_session_description_t; BELLESIP_EXPORT belle_sdp_session_description_t* belle_sdp_session_description_new(void); BELLESIP_EXPORT belle_sdp_session_description_t* belle_sdp_session_description_parse (const char* session_description); BELLESIP_EXPORT belle_sip_list_t * belle_sdp_session_description_get_attributes(const belle_sdp_session_description_t *session_description); BELLESIP_EXPORT const char* belle_sdp_session_description_get_attribute_value(const belle_sdp_session_description_t* session_description, const char* name); BELLESIP_EXPORT const belle_sdp_attribute_t* belle_sdp_session_description_get_attribute(const belle_sdp_session_description_t* session_description, const char* name); BELLESIP_EXPORT int belle_sdp_session_description_get_bandwidth(const belle_sdp_session_description_t* session_description, const char* name); BELLESIP_EXPORT belle_sip_list_t* belle_sdp_session_description_get_bandwidths(const belle_sdp_session_description_t* session_description); BELLESIP_EXPORT belle_sdp_connection_t* belle_sdp_session_description_get_connection(const belle_sdp_session_description_t* session_description); BELLESIP_EXPORT belle_sip_list_t* belle_sdp_session_description_get_emails(const belle_sdp_session_description_t* session_description); BELLESIP_EXPORT belle_sdp_info_t* belle_sdp_session_description_get_info(const belle_sdp_session_description_t* session_description); /*belle_sdp_key_t* belle_sdp_session_description_get_key(const belle_sdp_session_description_t* session_description);*/ BELLESIP_EXPORT belle_sip_list_t* belle_sdp_session_description_get_media_descriptions(const belle_sdp_session_description_t* session_description); BELLESIP_EXPORT belle_sdp_origin_t* belle_sdp_session_description_get_origin(const belle_sdp_session_description_t* session_description); BELLESIP_EXPORT belle_sip_list_t* belle_sdp_session_description_get_phones(const belle_sdp_session_description_t* session_description); BELLESIP_EXPORT belle_sdp_session_name_t* belle_sdp_session_description_get_session_name(const belle_sdp_session_description_t* session_description); BELLESIP_EXPORT belle_sip_list_t* belle_sdp_session_description_get_time_descriptions(const belle_sdp_session_description_t* session_description); BELLESIP_EXPORT belle_sdp_uri_t* belle_sdp_session_description_get_uri(const belle_sdp_session_description_t* session_description); BELLESIP_EXPORT belle_sdp_version_t* belle_sdp_session_description_get_version(const belle_sdp_session_description_t* session_description); BELLESIP_EXPORT belle_sdp_uri_t* belle_sdp_session_description_get_zone_adjustments(const belle_sdp_session_description_t* session_description); BELLESIP_EXPORT void belle_sdp_session_description_remove_attribute(belle_sdp_session_description_t* session_description, const char* name); BELLESIP_EXPORT void belle_sdp_session_description_remove_bandwidth(belle_sdp_session_description_t* session_description, const char* name); BELLESIP_EXPORT void belle_sdp_session_description_set_attribute_value(belle_sdp_session_description_t* session_description, const char* name, const char* value); BELLESIP_EXPORT void belle_sdp_session_description_add_attribute(belle_sdp_session_description_t* session_description, const belle_sdp_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_session_description_set_attributes(belle_sdp_session_description_t* session_description, belle_sip_list_t* Attributes); BELLESIP_EXPORT void belle_sdp_session_description_set_bandwidth(belle_sdp_session_description_t* session_description, const char* name, int value); BELLESIP_EXPORT void belle_sdp_session_description_set_bandwidths(belle_sdp_session_description_t* session_description, belle_sip_list_t* bandwidths); BELLESIP_EXPORT void belle_sdp_session_description_add_bandwidth(belle_sdp_session_description_t* session_description, const belle_sdp_bandwidth_t* bandwidth); BELLESIP_EXPORT void belle_sdp_session_description_set_connection(belle_sdp_session_description_t* session_description, belle_sdp_connection_t* conn); BELLESIP_EXPORT void belle_sdp_session_description_set_emails(belle_sdp_session_description_t* session_description, belle_sip_list_t* emails); BELLESIP_EXPORT void belle_sdp_session_description_set_info(belle_sdp_session_description_t* session_description, belle_sdp_info_t* i); /*void belle_sdp_session_description_set_key(belle_sdp_session_description_t* session_description, belle_sdp_key_t* key);*/ BELLESIP_EXPORT void belle_sdp_session_description_set_media_descriptions(belle_sdp_session_description_t* session_description, belle_sip_list_t* mediaDescriptions); BELLESIP_EXPORT void belle_sdp_session_description_add_media_description(belle_sdp_session_description_t* session_description, belle_sdp_media_description_t* media_description); BELLESIP_EXPORT void belle_sdp_session_description_set_origin(belle_sdp_session_description_t* session_description, belle_sdp_origin_t* origin); BELLESIP_EXPORT void belle_sdp_session_description_set_phones(belle_sdp_session_description_t* session_description, belle_sip_list_t* phones); BELLESIP_EXPORT void belle_sdp_session_description_set_session_name(belle_sdp_session_description_t* session_description, belle_sdp_session_name_t* sessionName); BELLESIP_EXPORT void belle_sdp_session_description_set_time_descriptions(belle_sdp_session_description_t* session_description, belle_sip_list_t* times); BELLESIP_EXPORT void belle_sdp_session_description_set_time_description(belle_sdp_session_description_t* session_description, belle_sdp_time_description_t* time_desc); BELLESIP_EXPORT void belle_sdp_session_description_set_uri(belle_sdp_session_description_t* session_description, belle_sdp_uri_t* uri); BELLESIP_EXPORT void belle_sdp_session_description_set_version(belle_sdp_session_description_t* session_description, belle_sdp_version_t* v); BELLESIP_EXPORT void belle_sdp_session_description_set_zone_adjustments(belle_sdp_session_description_t* session_description, belle_sdp_uri_t* zoneAdjustments); #define BELLE_SDP_SESSION_DESCRIPTION(t) BELLE_SDP_CAST(t,belle_sdp_session_description_t) BELLE_SIP_END_DECLS #endif /* BELLE_SDP_H_ */ belle-sip-1.6.3/include/belle-sip/belle-sip.h000066400000000000000000000033531313437522400207310ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SIP_H #define BELLE_SIP_H #include "belle-sip/types.h" #include "belle-sip/utils.h" #include "belle-sip/list.h" #include "belle-sip/listener.h" #include "belle-sip/mainloop.h" #include "belle-sip/sip-uri.h" #include "belle-sip/headers.h" #include "belle-sip/parameters.h" #include "belle-sip/message.h" #include "belle-sip/refresher.h" #include "belle-sip/transaction.h" #include "belle-sip/dialog.h" #include "belle-sip/sipstack.h" #include "belle-sip/resolver.h" #include "belle-sip/listeningpoint.h" #include "belle-sip/provider.h" #include "belle-sip/auth-helper.h" #include "belle-sip/generic-uri.h" #include "belle-sip/http-listener.h" #include "belle-sip/http-provider.h" #include "belle-sip/http-listener.h" #include "belle-sip/http-message.h" #include "belle-sip/belle-sdp.h" #include "belle-sip/bodyhandler.h" #ifdef __ANDROID__ #include "belle-sip/wakelock.h" #endif #define BELLE_SIP_POINTER_TO_INT(p) ((int)(intptr_t)(p)) #define BELLE_SIP_INT_TO_POINTER(i) ((void*)(intptr_t)(i)) #endif belle-sip-1.6.3/include/belle-sip/bodyhandler.h000066400000000000000000000132611313437522400213470ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2014 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_sip_body_handler_h #define belle_sip_body_handler_h BELLE_SIP_BEGIN_DECLS #define BELLE_SIP_BODY_HANDLER(obj) BELLE_SIP_CAST(obj,belle_sip_body_handler_t) /* * Body handler base class. **/ typedef void (*belle_sip_body_handler_progress_callback_t)(belle_sip_body_handler_t *obj, belle_sip_message_t *msg, void *user_data, size_t transfered, size_t expected_total); BELLESIP_EXPORT void belle_sip_body_handler_add_header(belle_sip_body_handler_t *obj, belle_sip_header_t *header); BELLESIP_EXPORT void belle_sip_body_handler_remove_header_from_ptr(belle_sip_body_handler_t *obj, belle_sip_header_t *header); BELLESIP_EXPORT const belle_sip_list_t* belle_sip_body_handler_get_headers(const belle_sip_body_handler_t *obj); BELLESIP_EXPORT size_t belle_sip_body_handler_get_size(const belle_sip_body_handler_t *obj); BELLESIP_EXPORT void belle_sip_body_handler_set_size(belle_sip_body_handler_t *obj, size_t size); BELLESIP_EXPORT size_t belle_sip_body_handler_get_transfered_size(const belle_sip_body_handler_t *obj); /* * body handler that read/write from a memory buffer. **/ #define BELLE_SIP_MEMORY_BODY_HANDLER(obj) BELLE_SIP_CAST(obj,belle_sip_memory_body_handler_t) BELLESIP_EXPORT belle_sip_memory_body_handler_t *belle_sip_memory_body_handler_new(belle_sip_body_handler_progress_callback_t progress_cb, void *data); BELLESIP_EXPORT belle_sip_memory_body_handler_t *belle_sip_memory_body_handler_new_copy_from_buffer(const void *buffer, size_t bufsize, belle_sip_body_handler_progress_callback_t cb, void *user_data); BELLESIP_EXPORT belle_sip_memory_body_handler_t *belle_sip_memory_body_handler_new_from_buffer(void *buffer, size_t bufsize, belle_sip_body_handler_progress_callback_t cb, void *user_data); BELLESIP_EXPORT void *belle_sip_memory_body_handler_get_buffer(const belle_sip_memory_body_handler_t *obj); BELLESIP_EXPORT void belle_sip_memory_body_handler_set_buffer(belle_sip_memory_body_handler_t *obj, void *buffer); BELLESIP_EXPORT void belle_sip_memory_body_handler_apply_encoding(belle_sip_memory_body_handler_t *obj, const char *encoding); BELLESIP_EXPORT int belle_sip_memory_body_handler_unapply_encoding(belle_sip_memory_body_handler_t *obj, const char *encoding); /* * body handler that get/puts data from application. **/ #define BELLE_SIP_USER_BODY_HANDLER(obj) BELLE_SIP_CAST(obj,belle_sip_user_body_handler_t) typedef void (*belle_sip_user_body_handler_start_callback_t)(belle_sip_user_body_handler_t *obj, void *user_data); typedef void (*belle_sip_user_body_handler_recv_callback_t)(belle_sip_user_body_handler_t *obj, belle_sip_message_t *msg, void *user_data, size_t offset, uint8_t* buffer, size_t size); typedef int (*belle_sip_user_body_handler_send_callback_t)(belle_sip_user_body_handler_t *obj, belle_sip_message_t *msg, void *user_data, size_t offset, uint8_t* buffer, size_t *size); typedef void (*belle_sip_user_body_handler_stop_callback_t)(belle_sip_user_body_handler_t *obj, void *user_data); BELLESIP_EXPORT belle_sip_user_body_handler_t *belle_sip_user_body_handler_new( size_t total_size, belle_sip_body_handler_progress_callback_t progress_cb, belle_sip_user_body_handler_start_callback_t start_cb, belle_sip_user_body_handler_recv_callback_t recv_cb, belle_sip_user_body_handler_send_callback_t send_cb, belle_sip_user_body_handler_stop_callback_t stop_cb, void *data); /** * Body handler that gets/puts data from/to a file. **/ #define BELLE_SIP_FILE_BODY_HANDLER(obj) BELLE_SIP_CAST(obj, belle_sip_file_body_handler_t) BELLESIP_EXPORT belle_sip_file_body_handler_t *belle_sip_file_body_handler_new(const char *filepath, belle_sip_body_handler_progress_callback_t progress_cb, void *data); BELLESIP_EXPORT size_t belle_sip_file_body_handler_get_file_size(belle_sip_file_body_handler_t *file_bh); BELLESIP_EXPORT void belle_sip_file_body_handler_set_user_body_handler(belle_sip_file_body_handler_t *file_bh, belle_sip_user_body_handler_t *user_bh); /* * Multipart body handler */ #define BELLE_SIP_MULTIPART_BODY_HANDLER(obj) BELLE_SIP_CAST(obj,belle_sip_multipart_body_handler_t) BELLESIP_EXPORT belle_sip_multipart_body_handler_t *belle_sip_multipart_body_handler_new(belle_sip_body_handler_progress_callback_t progress_cb, void *data, belle_sip_body_handler_t *first_part, const char *boundary); BELLESIP_EXPORT belle_sip_multipart_body_handler_t *belle_sip_multipart_body_handler_new_from_buffer(void *buffer, size_t bufsize, const char *boundary); BELLESIP_EXPORT void belle_sip_multipart_body_handler_add_part(belle_sip_multipart_body_handler_t *obj, belle_sip_body_handler_t *part); BELLESIP_EXPORT const belle_sip_list_t* belle_sip_multipart_body_handler_get_parts(const belle_sip_multipart_body_handler_t *obj); /* *multipar body in sens of rfc2387 */ BELLESIP_EXPORT unsigned int belle_sip_multipart_body_handler_is_related(const belle_sip_multipart_body_handler_t *obj); BELLESIP_EXPORT void belle_sip_multipart_body_handler_set_related(belle_sip_multipart_body_handler_t *obj, unsigned int yesno); BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/defs.h000066400000000000000000000033211313437522400177710ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010-2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SIP_DEFS_H #define BELLE_SIP_DEFS_H #ifdef __cplusplus #define BELLE_SIP_BEGIN_DECLS extern "C"{ #define BELLE_SIP_END_DECLS } #else #define BELLE_SIP_BEGIN_DECLS #define BELLE_SIP_END_DECLS #endif #ifdef _MSC_VER #define BELLESIP_INLINE __inline typedef signed char int8_t; typedef unsigned char uint8_t; typedef __int16 int16_t; typedef unsigned __int16 uint16_t; typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #else #include #define BELLESIP_INLINE inline #endif #ifdef _MSC_VER #ifdef BELLESIP_STATIC #define BELLESIP_EXPORT #else #ifdef BELLESIP_EXPORTS #define BELLESIP_EXPORT __declspec(dllexport) #else #define BELLESIP_EXPORT __declspec(dllimport) #endif #endif #else #define BELLESIP_EXPORT #endif #define BELLESIP_UNUSED(a) (void)a; #undef TRUE #define TRUE 1 #undef FALSE #define FALSE 0 #endif belle-sip-1.6.3/include/belle-sip/dialog.h000066400000000000000000000130371313437522400203140ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_sip_dialog_h #define belle_sip_dialog_h enum belle_sip_dialog_state{ BELLE_SIP_DIALOG_NULL, BELLE_SIP_DIALOG_EARLY, BELLE_SIP_DIALOG_CONFIRMED, BELLE_SIP_DIALOG_TERMINATED }; typedef enum belle_sip_dialog_state belle_sip_dialog_state_t; BELLE_SIP_BEGIN_DECLS BELLESIP_EXPORT const char* belle_sip_dialog_state_to_string(const belle_sip_dialog_state_t state); BELLESIP_EXPORT belle_sip_request_t *belle_sip_dialog_create_ack(belle_sip_dialog_t *dialog, unsigned int cseq); /** * Create a request part of this dialog. **/ BELLESIP_EXPORT belle_sip_request_t *belle_sip_dialog_create_request(belle_sip_dialog_t *dialog, const char *method); /** * Create a request within a dialog keeping non system header from an initial request. This function is very useful to resend request after expiration or chalenge. * @param obj dialog associated to the request * @param initial_req, all headers + body are re-used from this request except: Via,From, To, Allows, CSeq, Call-ID, Max-Forwards * */ BELLESIP_EXPORT belle_sip_request_t * belle_sip_dialog_create_request_from(belle_sip_dialog_t *obj, const belle_sip_request_t *initial_req); /** * Create a new request part of this dialog. If dialog is busy (pending transaction), the request can be created anyway and will be sent by the transaction * when the dialog becomes available. **/ BELLESIP_EXPORT belle_sip_request_t * belle_sip_dialog_create_queued_request(belle_sip_dialog_t *obj, const char *method); /** * Create a new request part of this dialog keeping non system header from an initial request. If dialog is busy (pending transaction), the request can be created anyway and will be sent by the transaction * when the dialog becomes available. * @param obj dialog associated to the request * @param initial_req, all headers + body are re-used from this request except: Via,From, To, Allows, CSeq, Call-ID, Max-Forwards **/ BELLESIP_EXPORT belle_sip_request_t *belle_sip_dialog_create_queued_request_from(belle_sip_dialog_t *obj, const belle_sip_request_t *initial_req); BELLESIP_EXPORT void belle_sip_dialog_delete(belle_sip_dialog_t *dialog); BELLESIP_EXPORT void *belle_sip_dialog_get_application_data(const belle_sip_dialog_t *dialog); BELLESIP_EXPORT void belle_sip_dialog_set_application_data(belle_sip_dialog_t *dialog, void *data); BELLESIP_EXPORT const belle_sip_header_call_id_t *belle_sip_dialog_get_call_id(const belle_sip_dialog_t *dialog); BELLESIP_EXPORT const belle_sip_header_address_t *belle_sip_dialog_get_local_party(const belle_sip_dialog_t *dialog); BELLESIP_EXPORT const belle_sip_header_address_t *belle_sip_dialog_get_remote_party(const belle_sip_dialog_t *dialog); /** * get the value of the last cseq used to issue a request * @return local cseq **/ BELLESIP_EXPORT unsigned int belle_sip_dialog_get_local_seq_number(const belle_sip_dialog_t *dialog); unsigned int belle_sip_dialog_get_remote_seq_number(const belle_sip_dialog_t *dialog); BELLESIP_EXPORT const char *belle_sip_dialog_get_local_tag(const belle_sip_dialog_t *dialog); BELLESIP_EXPORT const char *belle_sip_dialog_get_remote_tag(const belle_sip_dialog_t *dialog); BELLESIP_EXPORT const belle_sip_header_address_t *belle_sip_dialog_get_remote_target(belle_sip_dialog_t *dialog); BELLESIP_EXPORT const belle_sip_list_t* belle_sip_dialog_get_route_set(belle_sip_dialog_t *dialog); BELLESIP_EXPORT belle_sip_dialog_state_t belle_sip_dialog_get_state(const belle_sip_dialog_t *dialog); /** * return the dialog state before last transition. Can be useful to detect early avorted dialogs * @param dialog * @returns state **/ BELLESIP_EXPORT belle_sip_dialog_state_t belle_sip_dialog_get_previous_state(const belle_sip_dialog_t *dialog); BELLESIP_EXPORT int belle_sip_dialog_is_server(const belle_sip_dialog_t *dialog); BELLESIP_EXPORT int belle_sip_dialog_is_secure(const belle_sip_dialog_t *dialog); BELLESIP_EXPORT void belle_sip_dialog_send_ack(belle_sip_dialog_t *dialog, belle_sip_request_t *request); BELLESIP_EXPORT void belle_sip_dialog_terminate_on_bye(belle_sip_dialog_t *dialog, int val); /** * Give access to the last transaction processed by a dialog. Can be useful to get reason code for dialog terminated before reaching established state * @param dialog * @return last transaction */ BELLESIP_EXPORT belle_sip_transaction_t* belle_sip_dialog_get_last_transaction(const belle_sip_dialog_t *dialog); BELLESIP_EXPORT int belle_sip_dialog_request_pending(const belle_sip_dialog_t *dialog); /*for debugging purpose only, allow to disable checking for pending transaction*/ BELLESIP_EXPORT int belle_sip_dialog_pending_trans_checking_enabled( const belle_sip_dialog_t *dialog) ; BELLESIP_EXPORT int belle_sip_dialog_enable_pending_trans_checking(belle_sip_dialog_t *dialog, int value) ; BELLESIP_EXPORT int belle_sip_dialog_expired(const belle_sip_dialog_t *dialog); BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/dict.h000066400000000000000000000116541313437522400200030ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DICT_H #define DICT_H #ifndef _WIN32 #include #endif #include "belle-sip/object.h" BELLE_SIP_BEGIN_DECLS typedef struct belle_sip_dict belle_sip_dict_t; #define BELLE_SIP_DICT(obj) BELLE_SIP_CAST(obj,belle_sip_dict_t) /** * @brief belle_sip_dict_create * @return an instance of a belle_sip_dict_t object. * @note The object is not owned by default. * @note all belle_sip_dict_set_* functions will overwrite existing values. */ BELLESIP_EXPORT belle_sip_dict_t* belle_sip_dict_create(void); /** * @brief belle_sip_dict_set_int stores an integer into the dictionary * @param obj the dictionary instance * @param key the name of the integer to store * @param value value to store */ BELLESIP_EXPORT void belle_sip_dict_set_int(belle_sip_dict_t* obj, const char* key, int value); /** * @brief belle_sip_dict_get_int retrieves an integer from the dictionary * @param obj the dictionary instance * @param key name of the integer to retrieve * @param default_value value to return if the key is not found * @return the searched integer if the key exists, default_value if not found */ BELLESIP_EXPORT int belle_sip_dict_get_int(belle_sip_dict_t* obj, const char* key, int default_value); /** * @brief belle_sip_dict_set_string stores a string into the dictionary * @param obj the dictionary instance * @param key the name of the string to store * @param value value to store */ BELLESIP_EXPORT void belle_sip_dict_set_string(belle_sip_dict_t* obj, const char* key, const char*value); /** * @brief belle_sip_dict_get_string retrieves a string from the dictionary * @param obj the dictionary instance * @param key the name of the string to retrieve * @param default_value * @return the searched string if the key exists, default_value if not found */ BELLESIP_EXPORT const char* belle_sip_dict_get_string(belle_sip_dict_t* obj, const char* key, const char* default_value); /** * @brief belle_sip_dict_set_int64 stores an int64 in the dictionary * @param obj the dictionary instance * @param key the name of the integer to store * @param value value to store */ BELLESIP_EXPORT void belle_sip_dict_set_int64(belle_sip_dict_t* obj, const char* key, int64_t value); /** * @brief belle_sip_dict_get_int64 retrieves an int64 from the dictionary * @param obj the dictionary instance * @param key the name of the integer to retrieve * @param default_value value to return if the key is not found * @return the searched int64 if the key exists, default_value if not found */ BELLESIP_EXPORT int64_t belle_sip_dict_get_int64(belle_sip_dict_t* obj, const char* key, int64_t default_value); /** * @brief belle_sip_dict_remove will erase the value for a key * @param obj the dictionary instance * @param key the name of the integer to remove * @return 0 if the key was found, 1 otherwise */ BELLESIP_EXPORT int belle_sip_dict_remove(belle_sip_dict_t* obj, const char* key); /** * @brief belle_sip_dict_clear will clear the object's dictionary. * @param obj the dictionary instance */ BELLESIP_EXPORT void belle_sip_dict_clear(belle_sip_dict_t* obj); /** * Clones the source dictionary into the dst dictionary. The dst dictionary is cleared before the cloning * is done. * @param src source dictionary * @param dst destination dictionary */ BELLESIP_EXPORT void belle_sip_dict_clone( const belle_sip_dict_t* src, belle_sip_dict_t* dst); /** * Merge the source dictionary into the destination dictionary. * * Same function as #belle_sip_dict_clone, except the destination dictionary is not cleared before inserting the source data. * This overwrites common keys, and keeps existing keys. */ BELLESIP_EXPORT void belle_sip_dict_merge( const belle_sip_dict_t* src, belle_sip_dict_t* dst); /** * @brief belle_sip_dict_haskey tells if a key exists in the dictionary. * @param obj the dictionary instance * @param key the key to look for * @return 1 if the key exists, 0 otherwise * @todo create unit test */ BELLESIP_EXPORT int belle_sip_dict_haskey(const belle_sip_dict_t* obj, const char* key); /** * Apply a function for all keys stored in the dictionary */ BELLESIP_EXPORT void belle_sip_dict_foreach(const belle_sip_dict_t* obj, void (*apply_func)(const char*, void*, void*), void* userdata); BELLE_SIP_END_DECLS #endif // DICT_H belle-sip-1.6.3/include/belle-sip/generic-uri.h000066400000000000000000000106131313437522400212630ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2013 Belledonne Communications SARL, Grenoble, France This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_GENERIC_URI_H_ #define BELLE_GENERIC_URI_H_ #include "belle-sip/defs.h" #include "belle-sip/list.h" #include "belle-sip/utils.h" BELLE_SIP_BEGIN_DECLS /** * */ BELLESIP_EXPORT belle_generic_uri_t* belle_generic_uri_new(void); /** * */ BELLESIP_EXPORT belle_generic_uri_t* belle_generic_uri_parse (const char* uri); /* * Returns the host part of this uri. * */ BELLESIP_EXPORT const char* belle_generic_uri_get_host(const belle_generic_uri_t* uri) ; /** * Returns the value of the maddr parameter, or null if this is not set. * */ BELLESIP_EXPORT int belle_generic_uri_get_port(const belle_generic_uri_t* uri) ; /** * Returns the port of the uri, if not specified in the uri returns the well known port according to the transport. **/ BELLESIP_EXPORT int belle_generic_uri_get_listening_port(const belle_generic_uri_t *uri); /** * Returns the user part of this URI. * */ BELLESIP_EXPORT const char* belle_generic_uri_get_user(const belle_generic_uri_t* uri) ; /** * Gets user password of uri, or null if it is not set. * */ BELLESIP_EXPORT const char* belle_generic_uri_get_user_password(const belle_generic_uri_t* uri) ; /** * * Returns uri scheme. * */ BELLESIP_EXPORT const char* belle_generic_uri_get_scheme(const belle_generic_uri_t* uri) ; /** * * Returns uri path. * */ BELLESIP_EXPORT const char* belle_generic_uri_get_path(const belle_generic_uri_t* uri) ; /** * * Returns uri query. * */ BELLESIP_EXPORT const char* belle_generic_uri_get_query(const belle_generic_uri_t* uri) ; /** * Removes the port part of this uri. * */ BELLESIP_EXPORT void belle_generic_uri_remove_port(belle_generic_uri_t* uri) ; /** * Set the host part of this uri to the newly supplied host parameter. * */ BELLESIP_EXPORT void belle_generic_uri_set_host(belle_generic_uri_t* uri,const char*host) ; /** * Set the port part of this uri to the newly supplied port parameter. * */ BELLESIP_EXPORT void belle_generic_uri_set_port(belle_generic_uri_t* uri, int port) ; /** * Sets the scheme of this URI . * */ BELLESIP_EXPORT void belle_generic_uri_set_scheme(belle_generic_uri_t* uri,const char* scheme) ; /** * Sets the path of this URI . * */ BELLESIP_EXPORT void belle_generic_uri_set_path(belle_generic_uri_t* uri,const char* scheme) ; /** * Sets the query of this URI . * */ BELLESIP_EXPORT void belle_generic_uri_set_query(belle_generic_uri_t* uri,const char* scheme) ; /** * Sets the user of uri. * */ BELLESIP_EXPORT void belle_generic_uri_set_user(belle_generic_uri_t* uri,const char*user) ; /** * Sets the user password associated with the user of uri. * */ BELLESIP_EXPORT void belle_generic_uri_set_user_password(belle_generic_uri_t* uri,const char*userPassword) ; /** * This method returns the URI as a string. * */ BELLESIP_EXPORT char* belle_generic_uri_to_string(belle_generic_uri_t* uri) ; BELLESIP_EXPORT belle_sip_error_code belle_generic_uri_marshal(const belle_generic_uri_t* uri, char* buff, size_t buff_size, size_t *offset); /** * gets opaque part of this uri if hierarchies part not detected. * */ BELLESIP_EXPORT const char* belle_generic_uri_get_opaque_part(const belle_generic_uri_t* uri) ; /** * sets opaque part of this uri. Means hierarchies part is ignored if present. * */ BELLESIP_EXPORT void belle_generic_uri_set_opaque_part(belle_generic_uri_t* uri,const char * opaque_part) ; #define BELLE_GENERIC_URI(obj) BELLE_SIP_CAST(obj,belle_generic_uri_t) BELLE_SIP_END_DECLS #endif /* belle_generic_uri_H_ */ belle-sip-1.6.3/include/belle-sip/headers.h000066400000000000000000001357711313437522400205020ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef HEADERS_H_ #define HEADERS_H_ #include "belle-sip/defs.h" #include "belle-sip/sip-uri.h" #include "belle-sip/generic-uri.h" #include "belle-sip/utils.h" #include "belle-sip/parameters.h" #include BELLE_SIP_BEGIN_DECLS /*************************************************************************************** * header address * **************************************************************************************/ typedef struct _belle_sip_header_address belle_sip_header_address_t; BELLESIP_EXPORT belle_sip_header_address_t* belle_sip_header_address_new(void); /* * creates an address from a display name and an uri * Note the uri not copied but only its ref count is incremented * @param display display name. May be null. * @param uri uri set to the newly created header_address * @return * */ BELLESIP_EXPORT belle_sip_header_address_t* belle_sip_header_address_create(const char* display, belle_sip_uri_t* uri); /* * creates an address from a display name and an absolute uri * Note the uri not copied but only its ref count is incremented * @param display display name. May be null. * @param uri uri set to the newly created header_address * @return * */ BELLESIP_EXPORT belle_sip_header_address_t* belle_sip_header_address_create2(const char* display, belle_generic_uri_t* uri); BELLESIP_EXPORT belle_sip_header_address_t* belle_sip_header_address_parse (const char* address) ; /* same as belle_sip_header_address_parse but with less syntax checking */ BELLESIP_EXPORT belle_sip_header_address_t* belle_sip_header_address_fast_parse (const char* address) ; /** * returns a sip uri. A header address cannot have both a sip uri and an absolute uri. */ BELLESIP_EXPORT belle_sip_uri_t* belle_sip_header_address_get_uri(const belle_sip_header_address_t* address); /** * set an absolute uri. A header address cannot have both a sip uri and an absolute uri. This function also to absolute uri to NULL */ BELLESIP_EXPORT void belle_sip_header_address_set_uri(belle_sip_header_address_t* address, belle_sip_uri_t* uri); /** * returns an absolute uri. A header address cannot have both a sip uri and an absolute uri. */ BELLESIP_EXPORT belle_generic_uri_t* belle_sip_header_address_get_absolute_uri(const belle_sip_header_address_t* address); /** * set an absolute uri. A header address cannot have both a sip uri and an absolute uri. This function also to uri to NULL */ BELLESIP_EXPORT void belle_sip_header_address_set_absolute_uri(belle_sip_header_address_t* address, belle_generic_uri_t* uri); /** * */ BELLESIP_EXPORT const char* belle_sip_header_address_get_displayname(const belle_sip_header_address_t* address); /** * */ BELLESIP_EXPORT void belle_sip_header_address_set_displayname(belle_sip_header_address_t* address, const char* uri); #define BELLE_SIP_HEADER_ADDRESS(t) BELLE_SIP_CAST(t,belle_sip_header_address_t) /*************************************************************************************** * header common * **************************************************************************************/ BELLESIP_EXPORT belle_sip_header_t* belle_sip_header_parse (const char* header); BELLESIP_EXPORT belle_sip_header_t* belle_sip_header_create (const char* name,const char* value); BELLESIP_EXPORT belle_sip_header_t* belle_http_header_create (const char* name,const char* value); BELLESIP_EXPORT const char* belle_sip_header_get_name (const belle_sip_header_t* obj); BELLESIP_EXPORT void belle_sip_header_set_name (belle_sip_header_t* obj,const char* value); BELLESIP_EXPORT belle_sip_error_code belle_sip_header_marshal(belle_sip_header_t* header, char* buff, size_t buff_size, size_t *offset); BELLESIP_EXPORT const char *belle_sip_header_get_unparsed_value(belle_sip_header_t* obj); #define BELLE_SIP_HEADER(t) BELLE_SIP_CAST(t,belle_sip_header_t) /****************************** * * Allow header inherit from header * ******************************/ typedef struct _belle_sip_header_allow belle_sip_header_allow_t; belle_sip_header_allow_t* belle_sip_header_allow_new(void); BELLESIP_EXPORT belle_sip_header_allow_t* belle_sip_header_allow_parse (const char* allow) ; BELLESIP_EXPORT belle_sip_header_allow_t* belle_sip_header_allow_create (const char* methods) ; BELLESIP_EXPORT const char* belle_sip_header_allow_get_method(const belle_sip_header_allow_t* allow); BELLESIP_EXPORT void belle_sip_header_allow_set_method(belle_sip_header_allow_t* allow,const char* method); #define BELLE_SIP_HEADER_ALLOW(t) BELLE_SIP_CAST(t,belle_sip_header_allow_t) #define BELLE_SIP_ALLOW "Allow" /*********************** * Contact header object ************************/ typedef struct _belle_sip_header_contact belle_sip_header_contact_t; BELLESIP_EXPORT belle_sip_header_contact_t* belle_sip_header_contact_new(void); BELLESIP_EXPORT belle_sip_header_contact_t* belle_sip_header_contact_parse (const char* contact) ; BELLESIP_EXPORT belle_sip_header_contact_t* belle_sip_header_contact_create (const belle_sip_header_address_t* contact) ; /** * Returns the value of the expires parameter or -1 if no expires parameter was specified or if the parameter value cannot be parsed as an int. *@returns value of the expires parameter measured in delta-seconds, O implies removal of Registration specified in Contact Header. * */ BELLESIP_EXPORT int belle_sip_header_contact_get_expires(const belle_sip_header_contact_t* contact); /** * Returns the value of the q-value parameter of this ContactHeader. The q-value parameter indicates the relative preference amongst a set of locations. q-values are decimal numbers from 0 to 1, with higher values indicating higher preference. * @return the q-value parameter of this ContactHeader, -1 if the q-value is not set. */ BELLESIP_EXPORT float belle_sip_header_contact_get_qvalue(const belle_sip_header_contact_t* contact); /** * Returns a boolean value that indicates if the contact header has the format of Contact: *. * @return true if this is a wildcard address, false otherwise. */ BELLESIP_EXPORT unsigned int belle_sip_header_contact_is_wildcard(const belle_sip_header_contact_t* contact); /** * */ BELLESIP_EXPORT int belle_sip_header_contact_set_expires(belle_sip_header_contact_t* contact, int expires); /** * Sets the qValue value of the Name Address. */ BELLESIP_EXPORT int belle_sip_header_contact_set_qvalue(belle_sip_header_contact_t* contact, float qvalue); /** * Sets a wildcard on this contact address that is "*" is assigned to the contact header so that the header will have the format of Contact: *. * */ BELLESIP_EXPORT void belle_sip_header_contact_set_wildcard(belle_sip_header_contact_t* contact,unsigned int is_wildcard); /** Contact heaader equality function * @return 0 if not equals * * */ BELLESIP_EXPORT unsigned int belle_sip_header_contact_equals(const belle_sip_header_contact_t* a,const belle_sip_header_contact_t* b); /** Contact heaader equality function, same as #belle_sip_header_contact_equals but return 0 if equals, very useful with #belle_sip_list * @return 0 if equals * * */ BELLESIP_EXPORT unsigned int belle_sip_header_contact_not_equals(const belle_sip_header_contact_t* a,const belle_sip_header_contact_t* b); /** * Enable automatic filling of the contact ip, port and transport according to the channel that sends this message. **/ BELLESIP_EXPORT void belle_sip_header_contact_set_automatic(belle_sip_header_contact_t *a, int enabled); BELLESIP_EXPORT int belle_sip_header_contact_get_automatic(const belle_sip_header_contact_t *a); /** * Indicates whether a contact in automatic mode (see belle_sip_header_contact_set_automatic()) could be filled properly when the message was sent. * If a message is sent through a connection that has just been initiated, public IP and port are unknown, they will be learned after receiving the first response. * This can be used by the upper layer to decide to resubmit the request. **/ BELLESIP_EXPORT int belle_sip_header_contact_is_unknown(const belle_sip_header_contact_t *a); #define BELLE_SIP_RANDOM_TAG ((const char*)-1) #define BELLE_SIP_HEADER_CONTACT(t) BELLE_SIP_CAST(t,belle_sip_header_contact_t) #define BELLE_SIP_CONTACT "Contact" /****************************** * From header object inherent from header_address * ******************************/ typedef struct _belle_sip_header_from belle_sip_header_from_t; BELLESIP_EXPORT belle_sip_header_from_t* belle_sip_header_from_new(void); BELLESIP_EXPORT belle_sip_header_from_t* belle_sip_header_from_create(const belle_sip_header_address_t* address, const char *tag); BELLESIP_EXPORT belle_sip_header_from_t* belle_sip_header_from_create2(const char *address, const char *tag); BELLESIP_EXPORT belle_sip_header_from_t* belle_sip_header_from_parse(const char* from) ; BELLESIP_EXPORT void belle_sip_header_from_set_tag(belle_sip_header_from_t* from, const char* tag); BELLESIP_EXPORT const char* belle_sip_header_from_get_tag(const belle_sip_header_from_t* from); BELLESIP_EXPORT void belle_sip_header_from_set_random_tag(belle_sip_header_from_t *obj); #define BELLE_SIP_HEADER_FROM(t) BELLE_SIP_CAST(t,belle_sip_header_from_t) #define BELLE_SIP_FROM "From" /****************************** * To header object inherent from header_address * ******************************/ typedef struct _belle_sip_header_to belle_sip_header_to_t; BELLESIP_EXPORT belle_sip_header_to_t* belle_sip_header_to_new(void); BELLESIP_EXPORT belle_sip_header_to_t* belle_sip_header_to_parse(const char* to) ; BELLESIP_EXPORT belle_sip_header_to_t* belle_sip_header_to_create(const belle_sip_header_address_t *address, const char *tag); BELLESIP_EXPORT belle_sip_header_to_t* belle_sip_header_to_create2(const char *address, const char *tag); BELLESIP_EXPORT void belle_sip_header_to_set_tag(belle_sip_header_to_t* from, const char* tag); BELLESIP_EXPORT const char* belle_sip_header_to_get_tag(const belle_sip_header_to_t* from); BELLESIP_EXPORT void belle_sip_header_to_set_random_tag(belle_sip_header_to_t *obj); #define BELLE_SIP_HEADER_TO(t) BELLE_SIP_CAST(t,belle_sip_header_to_t) #define BELLE_SIP_TO "To" /****************************** * Diversion header object inherent from header_address * ******************************/ typedef struct _belle_sip_header_diversion belle_sip_header_diversion_t; BELLESIP_EXPORT belle_sip_header_diversion_t* belle_sip_header_diversion_new(void); BELLESIP_EXPORT belle_sip_header_diversion_t* belle_sip_header_diversion_parse(const char* diversion) ; BELLESIP_EXPORT belle_sip_header_diversion_t* belle_sip_header_diversion_create(const belle_sip_header_address_t *address, const char *tag); BELLESIP_EXPORT belle_sip_header_diversion_t* belle_sip_header_diversion_create2(const char *address, const char *tag); BELLESIP_EXPORT void belle_sip_header_diversion_set_tag(belle_sip_header_diversion_t* diversion, const char* tag); BELLESIP_EXPORT const char* belle_sip_header_diversion_get_tag(const belle_sip_header_diversion_t* from); BELLESIP_EXPORT void belle_sip_header_diversion_set_random_tag(belle_sip_header_diversion_t *obj); #define BELLE_SIP_HEADER_DIVERSION(t) BELLE_SIP_CAST(t,belle_sip_header_diversion_t) #define BELLE_SIP_DIVERSION "Diversion" /****************************** * Via header object inherent from header_address * ******************************/ typedef struct _belle_sip_header_via belle_sip_header_via_t; BELLESIP_EXPORT belle_sip_header_via_t* belle_sip_header_via_new(void); BELLESIP_EXPORT belle_sip_header_via_t* belle_sip_header_via_create(const char *host, int port, const char *transport, const char *branch); BELLESIP_EXPORT belle_sip_header_via_t* belle_sip_header_via_parse (const char* via) ; BELLESIP_EXPORT const char* belle_sip_header_via_get_branch(const belle_sip_header_via_t* via); BELLESIP_EXPORT const char* belle_sip_header_via_get_transport(const belle_sip_header_via_t* via); /** * Get lower case version of the transport * @return the lower case version of the transport if from tcp,udp,tls or dtls else, return the value from #belle_sip_header_via_get_transport */ BELLESIP_EXPORT const char* belle_sip_header_via_get_transport_lowercase(const belle_sip_header_via_t* via); BELLESIP_EXPORT const char* belle_sip_header_via_get_host(const belle_sip_header_via_t* via); BELLESIP_EXPORT int belle_sip_header_via_get_port(const belle_sip_header_via_t* via); BELLESIP_EXPORT int belle_sip_header_via_get_listening_port(const belle_sip_header_via_t *via); BELLESIP_EXPORT const char* belle_sip_header_via_get_maddr(const belle_sip_header_via_t* via); BELLESIP_EXPORT const char* belle_sip_header_via_get_protocol(const belle_sip_header_via_t* via); BELLESIP_EXPORT const char* belle_sip_header_via_get_received(const belle_sip_header_via_t* via); BELLESIP_EXPORT int belle_sip_header_via_get_rport(const belle_sip_header_via_t* via); BELLESIP_EXPORT int belle_sip_header_via_get_ttl(const belle_sip_header_via_t* via); BELLESIP_EXPORT void belle_sip_header_via_set_branch(belle_sip_header_via_t* via,const char* branch); BELLESIP_EXPORT void belle_sip_header_via_set_host(belle_sip_header_via_t* via, const char* host); BELLESIP_EXPORT int belle_sip_header_via_set_port(belle_sip_header_via_t* via,int port); BELLESIP_EXPORT void belle_sip_header_via_set_maddr(belle_sip_header_via_t* via, const char* maddr); BELLESIP_EXPORT void belle_sip_header_via_set_protocol(belle_sip_header_via_t* via, const char* protocol); BELLESIP_EXPORT void belle_sip_header_via_set_received(belle_sip_header_via_t* via, const char* received); BELLESIP_EXPORT int belle_sip_header_via_set_rport(belle_sip_header_via_t* via,int rport); BELLESIP_EXPORT void belle_sip_header_via_set_transport(belle_sip_header_via_t* via,const char* transport); BELLESIP_EXPORT int belle_sip_header_via_set_ttl(belle_sip_header_via_t* via, int ttl); #define BELLE_SIP_HEADER_VIA(t) BELLE_SIP_CAST(t,belle_sip_header_via_t) #define BELLE_SIP_VIA "Via" /****************************** * Call id object inherent from object * ******************************/ typedef struct _belle_sip_header_call_id belle_sip_header_call_id_t; BELLESIP_EXPORT belle_sip_header_call_id_t* belle_sip_header_call_id_new(void); BELLESIP_EXPORT belle_sip_header_call_id_t* belle_sip_header_call_id_parse (const char* call_id) ; BELLESIP_EXPORT const char* belle_sip_header_call_id_get_call_id(const belle_sip_header_call_id_t* call_id); BELLESIP_EXPORT void belle_sip_header_call_id_set_call_id(belle_sip_header_call_id_t* call_id,const char* id); unsigned int belle_sip_header_call_id_equals(const belle_sip_header_call_id_t* a,const belle_sip_header_call_id_t* b); #define BELLE_SIP_HEADER_CALL_ID(t) BELLE_SIP_CAST(t,belle_sip_header_call_id_t) #define BELLE_SIP_CALL_ID "Call-ID" /****************************** * cseq object inherent from object * ******************************/ typedef struct _belle_sip_header_cseq belle_sip_header_cseq_t; BELLESIP_EXPORT belle_sip_header_cseq_t* belle_sip_header_cseq_new(void); BELLESIP_EXPORT belle_sip_header_cseq_t* belle_sip_header_cseq_create(unsigned int number, const char *method); BELLESIP_EXPORT belle_sip_header_cseq_t* belle_sip_header_cseq_parse (const char* cseq) ; BELLESIP_EXPORT const char* belle_sip_header_cseq_get_method(const belle_sip_header_cseq_t* cseq); BELLESIP_EXPORT void belle_sip_header_cseq_set_method(belle_sip_header_cseq_t* cseq,const char* method); BELLESIP_EXPORT unsigned int belle_sip_header_cseq_get_seq_number(const belle_sip_header_cseq_t* cseq); BELLESIP_EXPORT void belle_sip_header_cseq_set_seq_number(belle_sip_header_cseq_t* cseq,unsigned int seq_number); #define BELLE_SIP_HEADER_CSEQ(t) BELLE_SIP_CAST(t,belle_sip_header_cseq_t) #define BELLE_SIP_CSEQ "CSeq" /****************************** * content type object inherent from parameters * ******************************/ typedef struct _belle_sip_header_content_type belle_sip_header_content_type_t; BELLESIP_EXPORT belle_sip_header_content_type_t* belle_sip_header_content_type_new(void); BELLESIP_EXPORT belle_sip_header_content_type_t* belle_sip_header_content_type_parse (const char* content_type) ; BELLESIP_EXPORT belle_sip_header_content_type_t* belle_sip_header_content_type_create (const char* type,const char* sub_type) ; BELLESIP_EXPORT belle_sip_header_content_type_t* belle_sip_header_content_type_parse (const char* content_type) ; BELLESIP_EXPORT const char* belle_sip_header_content_type_get_type(const belle_sip_header_content_type_t* content_type); BELLESIP_EXPORT void belle_sip_header_content_type_set_type(belle_sip_header_content_type_t* content_type,const char* type); BELLESIP_EXPORT const char* belle_sip_header_content_type_get_subtype(const belle_sip_header_content_type_t* content_type); BELLESIP_EXPORT void belle_sip_header_content_type_set_subtype(belle_sip_header_content_type_t* content_type,const char* sub_type); #define BELLE_SIP_HEADER_CONTENT_TYPE(t) BELLE_SIP_CAST(t,belle_sip_header_content_type_t) #define BELLE_SIP_CONTENT_TYPE "Content-Type" /****************************** * * Expires inherit from header * ******************************/ typedef struct _belle_sip_header_expires belle_sip_header_expires_t; BELLESIP_EXPORT belle_sip_header_expires_t* belle_sip_header_expires_new(void); BELLESIP_EXPORT belle_sip_header_expires_t* belle_sip_header_expires_parse (const char* expires) ; BELLESIP_EXPORT int belle_sip_header_expires_get_expires(const belle_sip_header_expires_t* expires); BELLESIP_EXPORT void belle_sip_header_expires_set_expires(belle_sip_header_expires_t* expires,int value); BELLESIP_EXPORT int belle_sip_header_expires_decrement_expires(belle_sip_header_expires_t* expires); BELLESIP_EXPORT belle_sip_header_expires_t* belle_sip_header_expires_create(int expires); #define BELLE_SIP_HEADER_EXPIRES(t) BELLE_SIP_CAST(t,belle_sip_header_expires_t) #define BELLE_SIP_EXPIRES "Expires" /****************************** * Route header object inherent from header_address * ******************************/ typedef struct _belle_sip_header_route belle_sip_header_route_t; BELLESIP_EXPORT belle_sip_header_route_t* belle_sip_header_route_new(void); BELLESIP_EXPORT belle_sip_header_route_t* belle_sip_header_route_parse (const char* route) ; BELLESIP_EXPORT belle_sip_header_route_t* belle_sip_header_route_create(const belle_sip_header_address_t* route); #define BELLE_SIP_HEADER_ROUTE(t) BELLE_SIP_CAST(t,belle_sip_header_route_t) #define BELLE_SIP_ROUTE "Route" /****************************** * Record route header object inherent from header_address * ******************************/ typedef struct _belle_sip_header_record_route belle_sip_header_record_route_t; BELLESIP_EXPORT belle_sip_header_record_route_t* belle_sip_header_record_route_new(void); BELLESIP_EXPORT belle_sip_header_record_route_t* belle_sip_header_record_route_parse (const char* route); BELLESIP_EXPORT belle_sip_header_record_route_t* belle_sip_header_record_route_new_auto_outgoing(void); BELLESIP_EXPORT unsigned char belle_sip_header_record_route_get_auto_outgoing(const belle_sip_header_record_route_t *a); #define BELLE_SIP_HEADER_RECORD_ROUTE(t) BELLE_SIP_CAST(t,belle_sip_header_record_route_t) #define BELLE_SIP_RECORD_ROUTE "Record-route" /****************************** * Service route header object inherent from header_address * ******************************/ typedef struct _belle_sip_header_service_route belle_sip_header_service_route_t; BELLESIP_EXPORT belle_sip_header_service_route_t* belle_sip_header_service_route_new(void); BELLESIP_EXPORT belle_sip_header_service_route_t* belle_sip_header_service_route_parse (const char* route) ; #define BELLE_SIP_HEADER_SERVICE_ROUTE(t) BELLE_SIP_CAST(t,belle_sip_header_service_route_t) #define BELLE_SIP_SERVICE_ROUTE "Service-route" /****************************** * * user-Agent header inherit from header * ******************************/ typedef struct _belle_sip_header_user_agent belle_sip_header_user_agent_t; BELLESIP_EXPORT belle_sip_header_user_agent_t* belle_sip_header_user_agent_new(void); BELLESIP_EXPORT belle_sip_header_user_agent_t* belle_sip_header_user_agent_parse (const char* user_agent) ; BELLESIP_EXPORT belle_sip_list_t* belle_sip_header_user_agent_get_products(const belle_sip_header_user_agent_t* user_agent); /** * concatenates products * @param user_agent [in] user agent header * @param value [out]buffer where to put result in * @param value_size [in] size of the buffer * @return number of written characters or -1 inca se of error; */ BELLESIP_EXPORT int belle_sip_header_user_agent_get_products_as_string(const belle_sip_header_user_agent_t* user_agent,char* value,unsigned int value_size); BELLESIP_EXPORT void belle_sip_header_user_agent_set_products(belle_sip_header_user_agent_t* user_agent,belle_sip_list_t* value); BELLESIP_EXPORT void belle_sip_header_user_agent_add_product(belle_sip_header_user_agent_t* user_agent,const char* product); #define BELLE_SIP_HEADER_USER_AGENT(t) BELLE_SIP_CAST(t,belle_sip_header_user_agent_t) #define BELLE_SIP_USER_AGENT "User-Agent" /****************************** * Content length inherent from object * ******************************/ typedef struct _belle_sip_header_content_length belle_sip_header_content_length_t; BELLESIP_EXPORT belle_sip_header_content_length_t* belle_sip_header_content_length_new(void); BELLESIP_EXPORT belle_sip_header_content_length_t* belle_sip_header_content_length_parse (const char* content_length) ; BELLESIP_EXPORT belle_sip_header_content_length_t* belle_sip_header_content_length_create (size_t content_length) ; BELLESIP_EXPORT size_t belle_sip_header_content_length_get_content_length(const belle_sip_header_content_length_t* content_length); BELLESIP_EXPORT void belle_sip_header_content_length_set_content_length(belle_sip_header_content_length_t* content_length,size_t length); #define BELLE_SIP_HEADER_CONTENT_LENGTH(t) BELLE_SIP_CAST(t,belle_sip_header_content_length_t) #define BELLE_SIP_CONTENT_LENGTH "Content-Length" /****************************** * authorization header inherit from parameters * ******************************/ typedef struct _belle_sip_header_authorization belle_sip_header_authorization_t; BELLESIP_EXPORT belle_sip_header_authorization_t* belle_sip_header_authorization_new(void); BELLESIP_EXPORT belle_sip_header_authorization_t* belle_sip_header_authorization_parse(const char* authorization); BELLESIP_EXPORT const char* belle_sip_header_authorization_get_algorithm(const belle_sip_header_authorization_t* authorization ); BELLESIP_EXPORT const char* belle_sip_header_authorization_get_cnonce(const belle_sip_header_authorization_t* authorization ); BELLESIP_EXPORT const char* belle_sip_header_authorization_get_nonce(const belle_sip_header_authorization_t* authorization); /*convert nonce count as string id present * @return 0 in case of success * */ BELLESIP_EXPORT int belle_sip_header_authorization_get_nonce_count_as_string(const belle_sip_header_authorization_t* authorization,char nounce_count[9]); BELLESIP_EXPORT int belle_sip_header_authorization_get_nonce_count(const belle_sip_header_authorization_t* authorization); BELLESIP_EXPORT const char* belle_sip_header_authorization_get_opaque(const belle_sip_header_authorization_t* authorization); BELLESIP_EXPORT const char* belle_sip_header_authorization_get_qop(const belle_sip_header_authorization_t* authorization); BELLESIP_EXPORT const char* belle_sip_header_authorization_get_realm(const belle_sip_header_authorization_t* authorization); BELLESIP_EXPORT const char* belle_sip_header_authorization_get_response(const belle_sip_header_authorization_t* authorization); BELLESIP_EXPORT const char* belle_sip_header_authorization_get_scheme(const belle_sip_header_authorization_t* authorization); BELLESIP_EXPORT belle_sip_uri_t* belle_sip_header_authorization_get_uri(const belle_sip_header_authorization_t* authorization); BELLESIP_EXPORT const char* belle_sip_header_authorization_get_username(const belle_sip_header_authorization_t* authorization); BELLESIP_EXPORT void belle_sip_header_authorization_set_algorithm(belle_sip_header_authorization_t* authorization, const char* algorithm); BELLESIP_EXPORT void belle_sip_header_authorization_set_cnonce(belle_sip_header_authorization_t* authorization, const char* cNonce); BELLESIP_EXPORT void belle_sip_header_authorization_set_nonce(belle_sip_header_authorization_t* authorization, const char* nonce); BELLESIP_EXPORT void belle_sip_header_authorization_set_nonce_count(belle_sip_header_authorization_t* authorization, int nonceCount); BELLESIP_EXPORT void belle_sip_header_authorization_set_opaque(belle_sip_header_authorization_t* authorization, const char* opaque); BELLESIP_EXPORT void belle_sip_header_authorization_set_qop(belle_sip_header_authorization_t* authorization, const char* qop); BELLESIP_EXPORT void belle_sip_header_authorization_add_qop(belle_sip_header_authorization_t* authorization, const char* qop); BELLESIP_EXPORT void belle_sip_header_authorization_set_realm(belle_sip_header_authorization_t* authorization, const char* realm); BELLESIP_EXPORT void belle_sip_header_authorization_set_response(belle_sip_header_authorization_t* authorization, const char* response); BELLESIP_EXPORT void belle_sip_header_authorization_set_scheme(belle_sip_header_authorization_t* authorization, const char* scheme); BELLESIP_EXPORT void belle_sip_header_authorization_set_uri(belle_sip_header_authorization_t* authorization, belle_sip_uri_t* uri); BELLESIP_EXPORT void belle_sip_header_authorization_set_username(belle_sip_header_authorization_t* authorization, const char* username); #define BELLE_SIP_HEADER_AUTHORIZATION(t) BELLE_SIP_CAST(t,belle_sip_header_authorization_t) #define BELLE_SIP_AUTHORIZATION "Authorization" /******************************* * proxy_authorization inherit from Authorization */ typedef struct _belle_sip_header_proxy_authorization belle_sip_header_proxy_authorization_t; BELLESIP_EXPORT belle_sip_header_proxy_authorization_t* belle_sip_header_proxy_authorization_new(void); BELLESIP_EXPORT belle_sip_header_proxy_authorization_t* belle_sip_header_proxy_authorization_parse(const char* proxy_authorization); #define BELLE_SIP_HEADER_PROXY_AUTHORIZATION(t) BELLE_SIP_CAST(t,belle_sip_header_proxy_authorization_t) #define BELLE_SIP_PROXY_AUTHORIZATION "Proxy-Authorization" /******************************* * http_authorization inherit from Authorization */ typedef struct _belle_http_header_authorization belle_http_header_authorization_t; BELLESIP_EXPORT belle_http_header_authorization_t* belle_http_header_authorization_new(void); /*cannot be parsed for now BELLESIP_EXPORT belle_http_header_authorization_t* belle_http_header_authorization_parse(const char* proxy_authorization); */ BELLESIP_EXPORT void belle_http_header_authorization_set_uri(belle_http_header_authorization_t* authorization, belle_generic_uri_t* uri); BELLESIP_EXPORT belle_generic_uri_t* belle_http_header_authorization_get_uri(const belle_http_header_authorization_t* authorization); #define BELLE_HTTP_HEADER_AUTHORIZATION(t) BELLE_SIP_CAST(t,belle_http_header_authorization_t) #define BELLE_HTTP_AUTHORIZATION "Authorization" /******************************* * www_authenticate inherit from parameters */ typedef struct _belle_sip_header_www_authenticate belle_sip_header_www_authenticate_t; BELLESIP_EXPORT belle_sip_header_www_authenticate_t* belle_sip_header_www_authenticate_new(void); BELLESIP_EXPORT belle_sip_header_www_authenticate_t* belle_sip_header_www_authenticate_parse(const char* www_authenticate); BELLESIP_EXPORT const char* belle_sip_header_www_authenticate_get_algorithm(const belle_sip_header_www_authenticate_t* www_authenticate ); BELLESIP_EXPORT const char* belle_sip_header_www_authenticate_get_nonce(const belle_sip_header_www_authenticate_t* www_authenticate); BELLESIP_EXPORT const char* belle_sip_header_www_authenticate_get_opaque(const belle_sip_header_www_authenticate_t* www_authenticate); BELLESIP_EXPORT belle_sip_list_t* belle_sip_header_www_authenticate_get_qop(const belle_sip_header_www_authenticate_t* www_authetication); BELLESIP_EXPORT const char* belle_sip_header_www_authenticate_get_qop_first(const belle_sip_header_www_authenticate_t* www_authetication); BELLESIP_EXPORT const char* belle_sip_header_www_authenticate_get_realm(const belle_sip_header_www_authenticate_t* www_authenticate); BELLESIP_EXPORT const char* belle_sip_header_www_authenticate_get_scheme(const belle_sip_header_www_authenticate_t* www_authenticate); BELLESIP_EXPORT const char* belle_sip_header_www_authenticate_get_domain(const belle_sip_header_www_authenticate_t* www_authenticate); BELLESIP_EXPORT unsigned int belle_sip_header_www_authenticate_is_stale(const belle_sip_header_www_authenticate_t* www_authenticate); BELLESIP_EXPORT void belle_sip_header_www_authenticate_set_algorithm(belle_sip_header_www_authenticate_t* www_authenticate, const char* algorithm); BELLESIP_EXPORT void belle_sip_header_www_authenticate_set_nonce(belle_sip_header_www_authenticate_t* www_authenticate, const char* nonce); BELLESIP_EXPORT void belle_sip_header_www_authenticate_set_opaque(belle_sip_header_www_authenticate_t* www_authenticate, const char* opaque); BELLESIP_EXPORT void belle_sip_header_www_authenticate_set_qop(belle_sip_header_www_authenticate_t* www_authentication, belle_sip_list_t* qop); BELLESIP_EXPORT void belle_sip_header_www_authenticate_add_qop(belle_sip_header_www_authenticate_t* www_authentication, const char* qop_param); BELLESIP_EXPORT void belle_sip_header_www_authenticate_set_realm(belle_sip_header_www_authenticate_t* www_authenticate, const char* realm); BELLESIP_EXPORT void belle_sip_header_www_authenticate_set_scheme(belle_sip_header_www_authenticate_t* www_authenticate, const char* scheme); BELLESIP_EXPORT void belle_sip_header_www_authenticate_set_domain(belle_sip_header_www_authenticate_t* www_authenticate,const char* domain); BELLESIP_EXPORT void belle_sip_header_www_authenticate_set_stale(belle_sip_header_www_authenticate_t* www_authenticate, unsigned int enable); #define BELLE_SIP_HEADER_WWW_AUTHENTICATE(t) BELLE_SIP_CAST(t,belle_sip_header_www_authenticate_t) #define BELLE_SIP_WWW_AUTHENTICATE "WWW-Authenticate" /******************************* * proxy_authenticate inherit from www_authenticate */ typedef struct _belle_sip_header_proxy_authenticate belle_sip_header_proxy_authenticate_t; BELLESIP_EXPORT belle_sip_header_proxy_authenticate_t* belle_sip_header_proxy_authenticate_new(void); BELLESIP_EXPORT belle_sip_header_proxy_authenticate_t* belle_sip_header_proxy_authenticate_parse(const char* proxy_authenticate); #define BELLE_SIP_HEADER_PROXY_AUTHENTICATE(t) BELLE_SIP_CAST(t,belle_sip_header_proxy_authenticate_t) #define BELLE_SIP_PROXY_AUTHENTICATE "Proxy-Authenticate" /****************************** * * Max forward inherit from header * ******************************/ typedef struct _belle_sip_header_max_forwards belle_sip_header_max_forwards_t; BELLESIP_EXPORT belle_sip_header_max_forwards_t* belle_sip_header_max_forwards_new(void); BELLESIP_EXPORT belle_sip_header_max_forwards_t* belle_sip_header_max_forwards_create(int value); BELLESIP_EXPORT belle_sip_header_max_forwards_t* belle_sip_header_max_forwards_parse (const char* max_forwards) ; BELLESIP_EXPORT int belle_sip_header_max_forwards_get_max_forwards(const belle_sip_header_max_forwards_t* max_forwards); BELLESIP_EXPORT void belle_sip_header_max_forwards_set_max_forwards(belle_sip_header_max_forwards_t* max_forwards,int value); BELLESIP_EXPORT int belle_sip_header_max_forwards_decrement_max_forwards(belle_sip_header_max_forwards_t* max_forwards); #define BELLE_SIP_HEADER_MAX_FORWARDS(t) BELLE_SIP_CAST(t,belle_sip_header_max_forwards_t) #define BELLE_SIP_MAX_FORWARDS "Max-Forwards" /****************************** * * Subscription state inherit from parameters * ******************************/ typedef struct _belle_sip_header_subscription_state belle_sip_header_subscription_state_t; BELLESIP_EXPORT belle_sip_header_subscription_state_t* belle_sip_header_subscription_state_new(void); BELLESIP_EXPORT belle_sip_header_subscription_state_t* belle_sip_header_subscription_state_parse (const char* subscription_state) ; BELLESIP_EXPORT belle_sip_header_subscription_state_t* belle_sip_header_subscription_state_create (const char* subscription_state,int expires); BELLESIP_EXPORT const char* belle_sip_header_subscription_state_get_state(const belle_sip_header_subscription_state_t* subscription_state); BELLESIP_EXPORT int belle_sip_header_subscription_state_get_expires(const belle_sip_header_subscription_state_t* subscription_state); BELLESIP_EXPORT const char* belle_sip_header_subscription_state_get_reason(const belle_sip_header_subscription_state_t* subscription_state); BELLESIP_EXPORT int belle_sip_header_subscription_state_get_retry_after(const belle_sip_header_subscription_state_t* subscription_state); BELLESIP_EXPORT void belle_sip_header_subscription_state_set_state(belle_sip_header_subscription_state_t* subscription_state,const char* state); BELLESIP_EXPORT void belle_sip_header_subscription_state_set_expires(belle_sip_header_subscription_state_t* subscription_state,int expire); BELLESIP_EXPORT void belle_sip_header_subscription_state_set_reason(belle_sip_header_subscription_state_t* subscription_state, const char* reason); BELLESIP_EXPORT void belle_sip_header_subscription_state_set_retry_after(belle_sip_header_subscription_state_t* subscription_state, int retry_after ); #define BELLE_SIP_HEADER_SUBSCRIPTION_STATE(t) BELLE_SIP_CAST(t,belle_sip_header_subscription_state_t) #define BELLE_SIP_SUBSCRIPTION_STATE "Subscription-State" #define BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE "active" #define BELLE_SIP_SUBSCRIPTION_STATE_PENDING "pending" #define BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED "terminated" /****************************** * Refer-To header object inherent from header_address * ******************************/ typedef struct _belle_sip_header_refer_to belle_sip_header_refer_to_t; BELLESIP_EXPORT belle_sip_header_refer_to_t* belle_sip_header_refer_to_new(void); BELLESIP_EXPORT belle_sip_header_refer_to_t* belle_sip_header_refer_to_parse(const char* refer_to) ; BELLESIP_EXPORT belle_sip_header_refer_to_t* belle_sip_header_refer_to_create(const belle_sip_header_address_t *address); #define BELLE_SIP_HEADER_REFER_TO(t) BELLE_SIP_CAST(t,belle_sip_header_refer_to_t) #define BELLE_SIP_REFER_TO "Refer-To" /****************************** * Referred-by header object inherent from header_address * ******************************/ typedef struct _belle_sip_header_referred_by belle_sip_header_referred_by_t; BELLESIP_EXPORT belle_sip_header_referred_by_t* belle_sip_header_referred_by_new(void); BELLESIP_EXPORT belle_sip_header_referred_by_t* belle_sip_header_referred_by_parse(const char* referred_by) ; BELLESIP_EXPORT belle_sip_header_referred_by_t* belle_sip_header_referred_by_create(const belle_sip_header_address_t *address); #define BELLE_SIP_HEADER_REFERRED_BY(t) BELLE_SIP_CAST(t,belle_sip_header_referred_by_t) #define BELLE_SIP_REFERRED_BY "Referred-By" /****************************** * Replace header object inherent from parameters * ******************************/ typedef struct _belle_sip_header_replaces belle_sip_header_replaces_t; BELLESIP_EXPORT belle_sip_header_replaces_t* belle_sip_header_replaces_new(void); BELLESIP_EXPORT belle_sip_header_replaces_t* belle_sip_header_replaces_parse(const char* replaces) ; BELLESIP_EXPORT belle_sip_header_replaces_t* belle_sip_header_replaces_create(const char* call_id,const char* from_tag,const char* to_tag); /* * Creates a Eeplaces header from an escaped value that can be found in Referred-by header * @param escaped_replace ex : 12345%40192.168.118.3%3Bto-tag%3D12345%3Bfrom-tag%3D5FFE-3994 * @return a newly allocated Replace header * */ BELLESIP_EXPORT belle_sip_header_replaces_t* belle_sip_header_replaces_create2(const char* escaped_replace); BELLESIP_EXPORT const char* belle_sip_header_replaces_get_call_id(const belle_sip_header_replaces_t* obj); BELLESIP_EXPORT const char* belle_sip_header_replaces_get_from_tag(const belle_sip_header_replaces_t* obj); BELLESIP_EXPORT const char* belle_sip_header_replaces_get_to_tag(const belle_sip_header_replaces_t* obj); BELLESIP_EXPORT void belle_sip_header_replaces_set_call_id(belle_sip_header_replaces_t* obj, const char* callid); BELLESIP_EXPORT void belle_sip_header_replaces_set_from_tag(belle_sip_header_replaces_t* obj,const char* from_tag); BELLESIP_EXPORT void belle_sip_header_replaces_set_to_tag(belle_sip_header_replaces_t* obj,const char* to_tag); /*return a newly allocated string with the content of the header value in escaped form. *
Purpose of this function is to be used to set Refer-To uri header Replaces * @param obj Replaces object * @return newly allocated string ex: 12345%40192.168.118.3%3Bto-tag%3D12345%3Bfrom-tag%3D5FFE-3994*/ BELLESIP_EXPORT char* belle_sip_header_replaces_value_to_escaped_string(const belle_sip_header_replaces_t* obj); #define BELLE_SIP_HEADER_REPLACES(t) BELLE_SIP_CAST(t,belle_sip_header_replaces_t) #define BELLE_SIP_REPLACES "Replaces" /******* * Date header *******/ typedef struct belle_sip_header_date belle_sip_header_date_t; BELLESIP_EXPORT belle_sip_header_date_t* belle_sip_header_date_new(void); BELLESIP_EXPORT belle_sip_header_date_t* belle_sip_header_date_parse(const char* date) ; BELLESIP_EXPORT belle_sip_header_date_t* belle_sip_header_date_create_from_time(const time_t *utc_time); BELLESIP_EXPORT time_t belle_sip_header_date_get_time(belle_sip_header_date_t *obj); BELLESIP_EXPORT void belle_sip_header_date_set_time(belle_sip_header_date_t *obj, const time_t *utc_time); BELLESIP_EXPORT const char * belle_sip_header_date_get_date(const belle_sip_header_date_t *obj); BELLESIP_EXPORT void belle_sip_header_date_set_date(belle_sip_header_date_t *obj, const char *date); #define BELLE_SIP_HEADER_DATE(obj) BELLE_SIP_CAST(obj,belle_sip_header_date_t) #define BELLE_SIP_DATE "Date" /****************************** * P-Preferred-Identity header object inherent from header_address * ******************************/ typedef struct _belle_sip_header_p_preferred_identity belle_sip_header_p_preferred_identity_t; BELLESIP_EXPORT belle_sip_header_p_preferred_identity_t* belle_sip_header_p_preferred_identity_new(void); BELLESIP_EXPORT belle_sip_header_p_preferred_identity_t* belle_sip_header_p_preferred_identity_parse(const char* p_preferred_identity) ; BELLESIP_EXPORT belle_sip_header_p_preferred_identity_t* belle_sip_header_p_preferred_identity_create(const belle_sip_header_address_t *address); #define BELLE_SIP_HEADER_P_PREFERRED_IDENTITY(t) BELLE_SIP_CAST(t,belle_sip_header_p_preferred_identity_t) #define BELLE_SIP_P_PREFERRED_IDENTITY "P-Preferred-Identity" /****************************** * Privacy header object inherent from header * ******************************/ typedef struct _belle_sip_header_privacy belle_sip_header_privacy_t; BELLESIP_EXPORT belle_sip_header_privacy_t* belle_sip_header_privacy_new(void); BELLESIP_EXPORT belle_sip_header_privacy_t* belle_sip_header_privacy_parse(const char* privacy) ; BELLESIP_EXPORT belle_sip_header_privacy_t* belle_sip_header_privacy_create(const char* privacy); BELLESIP_EXPORT void belle_sip_header_privacy_add_privacy(belle_sip_header_privacy_t* privacy, const char* value); BELLESIP_EXPORT void belle_sip_header_privacy_set_privacy(belle_sip_header_privacy_t* privacy, belle_sip_list_t* privacy_values); BELLESIP_EXPORT belle_sip_list_t* belle_sip_header_privacy_get_privacy(const belle_sip_header_privacy_t* privacy); #define BELLE_SIP_HEADER_PRIVACY(t) BELLE_SIP_CAST(t,belle_sip_header_privacy_t) #define BELLE_SIP_PRIVACY "Privacy" /****************************** * Event header object inherent from parameters * ******************************/ typedef struct _belle_sip_header_event belle_sip_header_event_t; BELLESIP_EXPORT belle_sip_header_event_t* belle_sip_header_event_new(void); BELLESIP_EXPORT belle_sip_header_event_t* belle_sip_header_event_parse(const char* event) ; BELLESIP_EXPORT belle_sip_header_event_t* belle_sip_header_event_create(const char* event); BELLESIP_EXPORT const char* belle_sip_header_event_get_package_name(const belle_sip_header_event_t* event); BELLESIP_EXPORT void belle_sip_header_event_set_package_name(belle_sip_header_event_t* event, const char* package_name); BELLESIP_EXPORT const char* belle_sip_header_event_get_id(const belle_sip_header_event_t* event); BELLESIP_EXPORT void belle_sip_header_event_set_id(belle_sip_header_event_t* event, const char* id); #define BELLE_SIP_HEADER_EVENT(t) BELLE_SIP_CAST(t,belle_sip_header_event_t) #define BELLE_SIP_EVENT "Event" /****************************** * Supported header object inherent from header * ******************************/ typedef struct _belle_sip_header_supported belle_sip_header_supported_t; BELLESIP_EXPORT belle_sip_header_supported_t* belle_sip_header_supported_new(void); BELLESIP_EXPORT belle_sip_header_supported_t* belle_sip_header_supported_parse(const char* supported) ; BELLESIP_EXPORT belle_sip_header_supported_t* belle_sip_header_supported_create(const char* supported); BELLESIP_EXPORT void belle_sip_header_supported_add_supported(belle_sip_header_supported_t* supported, const char* value); BELLESIP_EXPORT void belle_sip_header_supported_set_supported(belle_sip_header_supported_t* supported, belle_sip_list_t* supported_values); BELLESIP_EXPORT belle_sip_list_t* belle_sip_header_supported_get_supported(const belle_sip_header_supported_t* supported); #define BELLE_SIP_HEADER_SUPPORTED(t) BELLE_SIP_CAST(t,belle_sip_header_supported_t) #define BELLE_SIP_SUPPORTED "Supported" /****************************** * Content Disposition header object inherent from header * ******************************/ typedef struct _belle_sip_header_content_disposition belle_sip_header_content_disposition_t; BELLESIP_EXPORT belle_sip_header_content_disposition_t* belle_sip_header_content_disposition_new(void); BELLESIP_EXPORT belle_sip_header_content_disposition_t* belle_sip_header_content_disposition_parse (const char* content_disposition) ; BELLESIP_EXPORT belle_sip_header_content_disposition_t* belle_sip_header_content_disposition_create (const char* content_disposition); BELLESIP_EXPORT const char* belle_sip_header_content_disposition_get_content_disposition(const belle_sip_header_content_disposition_t* content_disposition); BELLESIP_EXPORT void belle_sip_header_content_disposition_set_content_disposition(belle_sip_header_content_disposition_t* obj,const char* content_disposition); #define BELLE_SIP_HEADER_CONTENT_DISPOSITION(t) BELLE_SIP_CAST(t,belle_sip_header_content_disposition_t) #define BELLE_SIP_CONTENT_DISPOSITION "Content-Disposition" /****************************** * Accept header object inherent from parameters * ******************************/ typedef struct _belle_sip_header_accept belle_sip_header_accept_t; BELLESIP_EXPORT belle_sip_header_accept_t* belle_sip_header_accept_new(void); BELLESIP_EXPORT belle_sip_header_accept_t* belle_sip_header_accept_parse (const char* accept) ; BELLESIP_EXPORT belle_sip_header_accept_t* belle_sip_header_accept_create (const char* type,const char* sub_type) ; BELLESIP_EXPORT belle_sip_header_accept_t* belle_sip_header_accept_parse (const char* accept) ; BELLESIP_EXPORT const char* belle_sip_header_accept_get_type(const belle_sip_header_accept_t* accept); BELLESIP_EXPORT void belle_sip_header_accept_set_type(belle_sip_header_accept_t* accept,const char* type); BELLESIP_EXPORT const char* belle_sip_header_accept_get_subtype(const belle_sip_header_accept_t* accept); BELLESIP_EXPORT void belle_sip_header_accept_set_subtype(belle_sip_header_accept_t* accept,const char* sub_type); #define BELLE_SIP_HEADER_ACCEPT(t) BELLE_SIP_CAST(t,belle_sip_header_accept_t) #define BELLE_SIP_ACCEPT "Accept" /****************************** * Reason header object inherent from parameters * ******************************/ typedef struct _belle_sip_header_reason belle_sip_header_reason_t; BELLESIP_EXPORT belle_sip_header_reason_t* belle_sip_header_reason_new(void); BELLESIP_EXPORT belle_sip_header_reason_t* belle_sip_header_reason_parse (const char* reason) ; BELLESIP_EXPORT const char* belle_sip_header_reason_get_protocol(const belle_sip_header_reason_t* reason); BELLESIP_EXPORT void belle_sip_header_reason_set_protocol(belle_sip_header_reason_t* reason,const char* protocol); BELLESIP_EXPORT int belle_sip_header_reason_get_cause(const belle_sip_header_reason_t* reason); BELLESIP_EXPORT void belle_sip_header_reason_set_cause(belle_sip_header_reason_t* reason,int cause); BELLESIP_EXPORT void belle_sip_header_reason_set_text(belle_sip_header_reason_t* reason,const char* text); BELLESIP_EXPORT const char* belle_sip_header_reason_get_text(const belle_sip_header_reason_t* reason); #define BELLE_SIP_HEADER_REASON(t) BELLE_SIP_CAST(t,belle_sip_header_reason_t) #define BELLE_SIP_REASON "Reason" /****************************** * Authentication-Info header inherit from header * ******************************/ typedef struct _belle_sip_header_authentication_info belle_sip_header_authentication_info_t; BELLESIP_EXPORT belle_sip_header_authentication_info_t* belle_sip_header_authentication_info_new(void); BELLESIP_EXPORT belle_sip_header_authentication_info_t* belle_sip_header_authentication_info_parse(const char* authentication_info ); BELLESIP_EXPORT const char* belle_sip_header_authentication_info_get_next_nonce(const belle_sip_header_authentication_info_t* authentication_info ); BELLESIP_EXPORT void belle_sip_header_authentication_info_set_next_nonce(belle_sip_header_authentication_info_t* authentication_info, const char* next_nonce); /*limited to a sinle value*/ BELLESIP_EXPORT const char* belle_sip_header_authentication_info_get_qop(const belle_sip_header_authentication_info_t* authentication_info); BELLESIP_EXPORT void belle_sip_header_authentication_info_set_qop(belle_sip_header_authentication_info_t* authentication_info, const char* qop); BELLESIP_EXPORT const char* belle_sip_header_authentication_info_get_rsp_auth(const belle_sip_header_authentication_info_t* rsp_auth); BELLESIP_EXPORT void belle_sip_header_authentication_info_set_rsp_auth(belle_sip_header_authentication_info_t* authentication_info, const char* rsp_auth); BELLESIP_EXPORT const char* belle_sip_header_authentication_info_get_cnonce(const belle_sip_header_authentication_info_t* authentication_info); BELLESIP_EXPORT void belle_sip_header_authentication_info_set_cnonce(belle_sip_header_authentication_info_t* authentication_info, const char* cNonce); BELLESIP_EXPORT void belle_sip_header_authentication_info_set_nonce_count(belle_sip_header_authentication_info_t* authentication_info, int nonceCount); BELLESIP_EXPORT int belle_sip_header_authentication_info_get_nonce_count(const belle_sip_header_authentication_info_t* authentication_info); #define BELLE_SIP_HEADER_AUTHENTICATION_INFO(t) BELLE_SIP_CAST(t,belle_sip_header_authentication_info_t) #define BELLE_SIP_AUTHENTICATION_INFO "Authentication-Info" BELLE_SIP_END_DECLS #endif /* HEADERS_H_ */ belle-sip-1.6.3/include/belle-sip/http-listener.h000066400000000000000000000045041313437522400216560ustar00rootroot00000000000000 /* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_http_listener_h #define belle_http_listener_h struct belle_http_response_event{ belle_sip_object_t *source; belle_http_request_t *request; belle_http_response_t *response; }; typedef struct belle_http_response_event belle_http_response_event_t; #define BELLE_HTTP_INTERFACE_FUNCS(argT) \ void (*process_response_headers)(argT *user_ctx, const belle_http_response_event_t *event); \ void (*process_response)(argT *user_ctx, const belle_http_response_event_t *event); \ void (*process_io_error)(argT *user_ctx, const belle_sip_io_error_event_t *event); \ void (*process_timeout)(argT *user_ctx, const belle_sip_timeout_event_t *event); \ void (*process_auth_requested)(argT *user_ctx, belle_sip_auth_event_t *event); BELLE_SIP_DECLARE_INTERFACE_BEGIN(belle_http_request_listener_t) BELLE_HTTP_INTERFACE_FUNCS(belle_http_request_listener_t) BELLE_SIP_DECLARE_INTERFACE_END struct belle_http_request_listener_callbacks{ BELLE_HTTP_INTERFACE_FUNCS(void) void (*listener_destroyed)(void *user_ctx); }; typedef struct belle_http_request_listener_callbacks belle_http_request_listener_callbacks_t; #define BELLE_HTTP_REQUEST_LISTENER(obj) BELLE_SIP_INTERFACE_CAST(obj,belle_http_request_listener_t) BELLE_SIP_BEGIN_DECLS /** * Creates an object implementing the belle_http_request_listener_t interface. * This object passes the events to the callbacks, providing also the user context. **/ BELLESIP_EXPORT belle_http_request_listener_t *belle_http_request_listener_create_from_callbacks(const belle_http_request_listener_callbacks_t *callbacks, void *user_ctx); BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/http-message.h000066400000000000000000000051761313437522400214630ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_HTTP_MESSAGE_H #define BELLE_HTTP_MESSAGE_H #include "belle-sip/generic-uri.h" #define BELLE_HTTP_REQUEST(obj) BELLE_SIP_CAST(obj,belle_http_request_t) #define BELLE_HTTP_RESPONSE(obj) BELLE_SIP_CAST(obj,belle_http_response_t) BELLE_SIP_BEGIN_DECLS /** * Create an http request. * @param method * @param uri the http uri * @param ... optional list of belle_sip_header_t* to be included in the request, ending with NULL. */ BELLESIP_EXPORT belle_http_request_t *belle_http_request_create(const char *method, belle_generic_uri_t *uri, ...); BELLESIP_EXPORT belle_http_request_t* belle_http_request_new(void); BELLESIP_EXPORT belle_http_request_t* belle_http_request_parse(const char* raw); BELLESIP_EXPORT int belle_http_request_is_cancelled(const belle_http_request_t *req); BELLESIP_EXPORT void belle_http_request_cancel(belle_http_request_t *req); BELLESIP_EXPORT belle_generic_uri_t* belle_http_request_get_uri(const belle_http_request_t* request); BELLESIP_EXPORT void belle_http_request_set_uri(belle_http_request_t* request, belle_generic_uri_t* uri); BELLESIP_EXPORT const char* belle_http_request_get_method(const belle_http_request_t* request); BELLESIP_EXPORT void belle_http_request_set_method(belle_http_request_t* request,const char* method); BELLESIP_EXPORT belle_http_response_t *belle_http_request_get_response(belle_http_request_t *req); /** * http response * */ BELLESIP_EXPORT int belle_http_response_get_status_code(const belle_http_response_t *response); BELLESIP_EXPORT void belle_http_response_set_status_code(belle_http_response_t *response,int status); BELLESIP_EXPORT const char* belle_http_response_get_reason_phrase(const belle_http_response_t *response); BELLESIP_EXPORT void belle_http_response_set_reason_phrase(belle_http_response_t *response,const char* reason_phrase); BELLESIP_EXPORT belle_http_response_t *belle_http_response_new(void); BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/http-provider.h000066400000000000000000000041371313437522400216650ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_sip_http_provider_h #define belle_sip_http_provider_h BELLE_SIP_BEGIN_DECLS #define BELLE_SIP_HTTP_PROVIDER(obj) BELLE_SIP_CAST(obj,belle_http_provider_t) /** * Set the certificate verify policy for the TLS connection * @return 0 on succes * @deprecated Use belle_http_provider_set_tls_crypto_config() instead */ BELLESIP_DEPRECATED BELLESIP_EXPORT int belle_http_provider_set_tls_verify_policy(belle_http_provider_t *obj, belle_tls_verify_policy_t *verify_ctx); /** * Set the certificate crypto configuration used by this TLS connection * @return 0 on succes */ BELLESIP_EXPORT int belle_http_provider_set_tls_crypto_config(belle_http_provider_t *obj, belle_tls_crypto_config_t *crypto_config); /** * Can be used to simulate network recv error, for tests. * @param obj * @param recv_error if <=0, will cause channel error to be reported **/ BELLESIP_EXPORT void belle_http_provider_set_recv_error(belle_http_provider_t *obj, int recv_error); BELLESIP_EXPORT int belle_http_provider_send_request(belle_http_provider_t *obj, belle_http_request_t *req, belle_http_request_listener_t *listener); BELLESIP_EXPORT void belle_http_provider_cancel_request(belle_http_provider_t *obj, belle_http_request_t *req); BELLESIP_EXPORT belle_sip_list_t** belle_http_provider_get_channels(belle_http_provider_t *obj, const char *transport_name); BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/list.h000066400000000000000000000044311313437522400200260ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SIP_LIST_H_ #define BELLE_SIP_LIST_H_ #include "bctoolbox/list.h" typedef struct _bctbx_list belle_sip_list_t; #define belle_sip_list_new bctbx_list_new #define belle_sip_list_append bctbx_list_append #define belle_sip_list_prepend bctbx_list_prepend #define belle_sip_list_prepend_link bctbx_list_prepend_link #define belle_sip_list_last_elem bctbx_list_last_elem #define belle_sip_list_free bctbx_list_free #define belle_sip_list_concat bctbx_list_concat #define belle_sip_list_remove bctbx_list_remove #define belle_sip_list_pop_front bctbx_list_pop_front #define belle_sip_list_size bctbx_list_size #define belle_sip_list_for_each bctbx_list_for_each #define belle_sip_list_for_each2 bctbx_list_for_each2 #define belle_sip_list_remove_link bctbx_list_unlink #define belle_sip_list_delete_link bctbx_list_erase_link #define belle_sip_list_find bctbx_list_find #define belle_sip_list_free bctbx_list_free #define belle_sip_list_free_with_data bctbx_list_free_with_data #define belle_sip_compare_func bctbx_compare_func #define belle_sip_list_find_custom bctbx_list_find_custom #define belle_sip_list_nth_data bctbx_list_nth_data #define belle_sip_list_position bctbx_list_position #define belle_sip_list_index bctbx_list_index #define belle_sip_list_insert_sorted bctbx_list_insert_sorted #define belle_sip_list_insert bctbx_list_insert #define belle_sip_list_copy bctbx_list_copy #define belle_sip_list_copy_with_data bctbx_list_copy_with_data #define belle_sip_list_copy_reverse_with_data bctbx_list_copy_reverse_with_data #endif /* BELLE_SIP_LIST_H_ */ belle-sip-1.6.3/include/belle-sip/listener.h000066400000000000000000000222631313437522400207030ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_sip_listener_h #define belle_sip_listener_h BELLE_SIP_BEGIN_DECLS typedef struct belle_sip_dialog_terminated_event belle_sip_dialog_terminated_event_t; typedef struct belle_sip_io_error_event belle_sip_io_error_event_t; typedef struct belle_sip_request_event belle_sip_request_event_t; typedef struct belle_sip_response_event belle_sip_response_event_t; typedef struct belle_sip_timeout_event belle_sip_timeout_event_t; typedef struct belle_sip_transaction_terminated_event belle_sip_transaction_terminated_event_t; typedef struct belle_sip_auth_event belle_sip_auth_event_t; typedef struct belle_sip_certificates_chain belle_sip_certificates_chain_t; typedef struct belle_sip_signing_key belle_sip_signing_key_t; BELLE_SIP_DECLARE_INTERFACE_BEGIN(belle_sip_listener_t) void (*process_dialog_terminated)(belle_sip_listener_t *user_ctx, const belle_sip_dialog_terminated_event_t *event); void (*process_io_error)(belle_sip_listener_t *user_ctx, const belle_sip_io_error_event_t *event); void (*process_request_event)(belle_sip_listener_t *user_ctx, const belle_sip_request_event_t *event); void (*process_response_event)(belle_sip_listener_t *user_ctx, const belle_sip_response_event_t *event); void (*process_timeout)(belle_sip_listener_t *user_ctx, const belle_sip_timeout_event_t *event); void (*process_transaction_terminated)(belle_sip_listener_t *user_ctx, const belle_sip_transaction_terminated_event_t *event); void (*process_auth_requested)(belle_sip_listener_t *user_ctx, belle_sip_auth_event_t *event); BELLE_SIP_DECLARE_INTERFACE_END #define BELLE_SIP_LISTENER(obj) BELLE_SIP_INTERFACE_CAST(obj,belle_sip_listener_t) /*Response event*/ BELLESIP_EXPORT belle_sip_response_t* belle_sip_response_event_get_response(const belle_sip_response_event_t* event); BELLESIP_EXPORT belle_sip_client_transaction_t *belle_sip_response_event_get_client_transaction(const belle_sip_response_event_t* event); BELLESIP_EXPORT belle_sip_dialog_t *belle_sip_response_event_get_dialog(const belle_sip_response_event_t* event); /*Request event*/ BELLESIP_EXPORT belle_sip_request_t* belle_sip_request_event_get_request(const belle_sip_request_event_t* event); BELLESIP_EXPORT belle_sip_server_transaction_t *belle_sip_request_event_get_server_transaction(const belle_sip_request_event_t* event); BELLESIP_EXPORT belle_sip_dialog_t *belle_sip_request_event_get_dialog(const belle_sip_request_event_t* event); /*Dialog terminated event*/ BELLESIP_EXPORT belle_sip_dialog_t* belle_sip_dialog_terminated_event_get_dialog(const belle_sip_dialog_terminated_event_t *event); BELLESIP_EXPORT int belle_sip_dialog_terminated_event_is_expired(const belle_sip_dialog_terminated_event_t *event); /** * Timeout Event */ BELLESIP_EXPORT belle_sip_client_transaction_t *belle_sip_timeout_event_get_client_transaction(const belle_sip_timeout_event_t* event); belle_sip_server_transaction_t *belle_sip_timeout_event_get_server_transaction(const belle_sip_timeout_event_t* event); /** * Transaction Termonated Event */ BELLESIP_EXPORT belle_sip_client_transaction_t *belle_sip_transaction_terminated_event_get_client_transaction(const belle_sip_transaction_terminated_event_t* event); BELLESIP_EXPORT belle_sip_server_transaction_t *belle_sip_transaction_terminated_event_get_server_transaction(const belle_sip_transaction_terminated_event_t* event); /** * auth event mode * */ typedef enum belle_sip_auth_mode { BELLE_SIP_AUTH_MODE_HTTP_DIGEST, /** Digest authentication has been requested by the server*/ BELLE_SIP_AUTH_MODE_TLS /** Client certificate has been requested by the server*/ } belle_sip_auth_mode_t; BELLESIP_EXPORT void belle_sip_auth_event_destroy(belle_sip_auth_event_t* event); BELLESIP_EXPORT const char* belle_sip_auth_event_get_username(const belle_sip_auth_event_t* event); BELLESIP_EXPORT void belle_sip_auth_event_set_username(belle_sip_auth_event_t* event, const char* value); BELLESIP_EXPORT const char* belle_sip_auth_event_get_userid(const belle_sip_auth_event_t* event); BELLESIP_EXPORT void belle_sip_auth_event_set_userid(belle_sip_auth_event_t* event, const char* value); BELLESIP_EXPORT const char* belle_sip_auth_event_get_realm(const belle_sip_auth_event_t* event); BELLESIP_EXPORT void belle_sip_auth_event_set_realm(belle_sip_auth_event_t* event, const char* value); BELLESIP_EXPORT const char* belle_sip_auth_event_get_domain(const belle_sip_auth_event_t* event); BELLESIP_EXPORT void belle_sip_auth_event_set_domain(belle_sip_auth_event_t* event, const char* value); BELLESIP_EXPORT const char* belle_sip_auth_event_get_passwd(const belle_sip_auth_event_t* event); BELLESIP_EXPORT void belle_sip_auth_event_set_passwd(belle_sip_auth_event_t* event, const char* value); BELLESIP_EXPORT const char* belle_sip_auth_event_get_ha1(const belle_sip_auth_event_t* event); BELLESIP_EXPORT void belle_sip_auth_event_set_ha1(belle_sip_auth_event_t* event, const char* value); /** * get the authentication mode requested by the server, can be either TLS client certificates of http digest * @param event * @return belle_sip_auth_mode_t * */ BELLESIP_EXPORT belle_sip_auth_mode_t belle_sip_auth_event_get_mode(const belle_sip_auth_event_t* event); /** * In case of TLS auth, get value of the distinguished name sent by the server * @param event * @return DN has sent by the server * */ BELLESIP_EXPORT const char* belle_sip_auth_event_get_distinguished_name(const belle_sip_auth_event_t* event); /** * get client certificate * @return belle_sip_certificate_t* * */ BELLESIP_EXPORT belle_sip_certificates_chain_t* belle_sip_auth_event_get_client_certificates_chain(const belle_sip_auth_event_t* event); /** * set client certificate to be sent in answer to the certificate request issued by the server for the DN belle_sip_auth_event_get_distinguished_name() name * @return belle_sip_certificate_t* * */ BELLESIP_EXPORT void belle_sip_auth_event_set_client_certificates_chain(belle_sip_auth_event_t* event, belle_sip_certificates_chain_t* value); /** * get client certificate private key * @return belle_sip_signing_key_t* * */ BELLESIP_EXPORT belle_sip_signing_key_t* belle_sip_auth_event_get_signing_key(const belle_sip_auth_event_t* event); /** * set the private key attached to the client certificate. * @param event belle_sip_auth_event_t * @param value belle_sip_signing_key_t signing key * */ BELLESIP_EXPORT void belle_sip_auth_event_set_signing_key(belle_sip_auth_event_t* event, belle_sip_signing_key_t* value); /*Io error event*/ /* * Give access to the remote host * @param event object * @return host value the socket is pointing to * */ const char* belle_sip_io_error_event_get_host(const belle_sip_io_error_event_t* event); /* * Give access to the used transport * @param event object * @return host value the socket is pointing to * */ const char* belle_sip_io_error_event_get_transport(const belle_sip_io_error_event_t* event); /* * Give access to the remote port * @param event object * @return port value the socket is pointing to * */ unsigned int belle_sip_io_error_event_port(const belle_sip_io_error_event_t* event); /* * Get access to the object involved in this error, can be either belle_sip_dialog_t or belle_sip_transaction_t or belle_sip_provider_t * @param event * @return belle_sip_object_t source, use belle_sip_object_is_instance_of to check returns type * */ BELLESIP_EXPORT belle_sip_object_t* belle_sip_io_error_event_get_source(const belle_sip_io_error_event_t* event); struct belle_sip_listener_callbacks{ void (*process_dialog_terminated)(void *user_ctx, const belle_sip_dialog_terminated_event_t *event); void (*process_io_error)(void *user_ctx, const belle_sip_io_error_event_t *event); void (*process_request_event)(void *user_ctx, const belle_sip_request_event_t *event); void (*process_response_event)(void *user_ctx, const belle_sip_response_event_t *event); void (*process_timeout)(void *user_ctx, const belle_sip_timeout_event_t *event); void (*process_transaction_terminated)(void *user_ctx, const belle_sip_transaction_terminated_event_t *event); void (*process_auth_requested)(void *user_ctx, belle_sip_auth_event_t *auth_event); void (*listener_destroyed)(void *user_ctx); }; typedef struct belle_sip_listener_callbacks belle_sip_listener_callbacks_t; /** * Creates an object implementing the belle_sip_listener_t interface. * This object passes the events to the callbacks, providing also the user context. **/ BELLESIP_EXPORT belle_sip_listener_t *belle_sip_listener_create_from_callbacks(const belle_sip_listener_callbacks_t *callbacks, void *user_ctx); BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/listeningpoint.h000066400000000000000000000100221313437522400221120ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SIP_TRANSPORT_H #define BELLE_SIP_TRANSPORT_H BELLE_SIP_BEGIN_DECLS /*This is passed as port information to listening point URIs and means that it shall use a random port.*/ #define BELLE_SIP_LISTENING_POINT_RANDOM_PORT (-1) /*This is passed as port information to listening point URIs and means that it shall not bind.*/ #define BELLE_SIP_LISTENING_POINT_DONT_BIND (-2) BELLESIP_EXPORT const char *belle_sip_listening_point_get_ip_address(const belle_sip_listening_point_t *lp); BELLESIP_EXPORT int belle_sip_listening_point_get_port(const belle_sip_listening_point_t *lp); BELLESIP_EXPORT const char *belle_sip_listening_point_get_transport(const belle_sip_listening_point_t *lp); BELLESIP_EXPORT const char *belle_sip_listening_point_get_ip_address(const belle_sip_listening_point_t *lp); /* * set keep alive frequency in ms * @param lp object * @param ms keep alive period in ms. Values <=0 disable keep alive * */ BELLESIP_EXPORT void belle_sip_listening_point_set_keep_alive(belle_sip_listening_point_t *lp,int ms); /* * get keep alive frequency in ms * @param lp object * @return keep alive period in ms. Values <=0 disable keep alive * */ BELLESIP_EXPORT int belle_sip_listening_point_get_keep_alive(const belle_sip_listening_point_t *lp); /** * get the listening information as an URI * @return IP/port/transport as an URI */ BELLESIP_EXPORT const belle_sip_uri_t* belle_sip_listening_point_get_uri(const belle_sip_listening_point_t *ip); BELLESIP_EXPORT int belle_sip_listening_point_is_reliable(const belle_sip_listening_point_t *lp); /** * Clean (close) all channels (connection) managed by this listening point. **/ BELLESIP_EXPORT void belle_sip_listening_point_clean_channels(belle_sip_listening_point_t *lp); /** * Get the number of channels managed by this listening point. **/ BELLESIP_EXPORT int belle_sip_listening_point_get_channel_count(const belle_sip_listening_point_t *lp); BELLESIP_EXPORT int belle_sip_listening_point_get_well_known_port(const char *transport); BELLESIP_DEPRECATED BELLESIP_EXPORT int belle_sip_tls_listening_point_set_root_ca(belle_sip_tls_listening_point_t *s, const char *path); #define BELLE_SIP_TLS_LISTENING_POINT_BADCERT_CN_MISMATCH BELLE_TLS_VERIFY_CN_MISMATCH #define BELLE_SIP_TLS_LISTENING_POINT_BADCERT_ANY_REASON BELLE_TLS_VERIFY_ANY_REASON BELLESIP_DEPRECATED BELLESIP_EXPORT int belle_sip_tls_listening_point_set_verify_exceptions(belle_sip_tls_listening_point_t *s, int flags); BELLESIP_DEPRECATED BELLESIP_EXPORT int belle_sip_tls_listening_point_set_verify_policy(belle_sip_tls_listening_point_t *s, belle_tls_verify_policy_t *pol); BELLESIP_EXPORT int belle_sip_tls_listening_point_set_crypto_config(belle_sip_tls_listening_point_t *s, belle_tls_crypto_config_t *crypto_config); BELLESIP_EXPORT belle_tls_crypto_config_t *belle_sip_tls_listening_point_get_crypto_config(belle_sip_tls_listening_point_t *s); BELLESIP_EXPORT belle_sip_listening_point_t * belle_sip_tunnel_listening_point_new(belle_sip_stack_t *s, void *tunnelclient); #define BELLE_SIP_UDP_LISTENING_POINT(obj) BELLE_SIP_CAST(obj,belle_sip_udp_listening_point_t) #define BELLE_SIP_STREAM_LISTENING_POINT(obj) BELLE_SIP_CAST(obj,belle_sip_stream_listening_point_t) #define BELLE_SIP_TLS_LISTENING_POINT(obj) BELLE_SIP_CAST(obj,belle_sip_tls_listening_point_t) BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/mainloop.h000066400000000000000000000204121313437522400206660ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SIP_MAINLOOP_H #define BELLE_SIP_MAINLOOP_H #define BELLE_SIP_EVENT_READ 1 #define BELLE_SIP_EVENT_WRITE (1<<1) #define BELLE_SIP_EVENT_ERROR (1<<2) #define BELLE_SIP_EVENT_TIMEOUT (1<<3) typedef struct belle_sip_source belle_sip_source_t; BELLESIP_EXPORT int belle_sip_source_set_events(belle_sip_source_t* source, int event_mask); BELLESIP_EXPORT belle_sip_socket_t belle_sip_source_get_socket(const belle_sip_source_t* source); /** * Callback function prototype for main loop notifications. * Return value is important: * BELLE_SIP_STOP => source is removed from main loop. * BELLE_SIP_CONTINUE => source is kept, timeout is restarted if any according to last expiry time * BELLE_SIP_CONTINUE_WITHOUT_CATCHUP => source is kept, timeout is restarted if any according to current time **/ typedef int (*belle_sip_source_func_t)(void *user_data, unsigned int events); /* * Call back fonction invoked when source is removed from main loop */ typedef void (*belle_sip_source_remove_callback_t)(belle_sip_source_t *); typedef void (*belle_sip_callback_t)(void *user_data); typedef struct belle_sip_main_loop belle_sip_main_loop_t; #define BELLE_SIP_CONTINUE_WITHOUT_CATCHUP 2 #define BELLE_SIP_CONTINUE 1 #define BELLE_SIP_STOP 0 BELLE_SIP_BEGIN_DECLS BELLESIP_EXPORT void belle_sip_main_loop_add_source(belle_sip_main_loop_t *ml, belle_sip_source_t *source); BELLESIP_EXPORT void belle_sip_main_loop_remove_source(belle_sip_main_loop_t *ml, belle_sip_source_t *source); /** * Creates a mainloop. **/ BELLESIP_EXPORT belle_sip_main_loop_t *belle_sip_main_loop_new(void); /** * Adds a timeout into the main loop * @param ml * @param func a callback function to be called to notify timeout expiration * @param data a pointer to be passed to the callback * @param timeout_value_ms duration of the timeout. * @returns timeout id **/ BELLESIP_EXPORT unsigned long belle_sip_main_loop_add_timeout(belle_sip_main_loop_t *ml, belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms); /** * Adds a timeout into the main loop * The caller of this function is responsible for freeing (with belle_sip_object_unref()) the returned belle_sip_source_t object when it is no longer * needed. * @param ml * @param func a callback function to be called to notify timeout expiration * @param data a pointer to be passed to the callback * @param timeout_value_ms duration of the timeout. * @param timer_name name of the timer, can be null * @returns timeout belle_sip_source_t with ref count = 1 **/ BELLESIP_EXPORT belle_sip_source_t* belle_sip_main_loop_create_timeout(belle_sip_main_loop_t *ml , belle_sip_source_func_t func , void *data , unsigned int timeout_value_ms ,const char* timer_name); /** * Adds a timeout into the main loop * The caller of this function is responsible for freeing (with belle_sip_object_unref()) the returned belle_sip_source_t object when it is no longer * needed. * @param ml * @param func a callback function to be called to notify timeout expiration * @param data a pointer to be passed to the callback * @param timeout_value_ms duration of the timeout. * @param timer_name name of the timer, can be null * @param function called when source is removed, can be null * @returns timeout belle_sip_source_t with ref count = 1 **/ BELLESIP_EXPORT belle_sip_source_t* belle_sip_main_loop_create_timeout_with_remove_cb (belle_sip_main_loop_t *ml , belle_sip_source_func_t func , void *data , unsigned int timeout_value_ms , const char* timer_name , belle_sip_source_remove_callback_t remove_func); /** * Schedule an arbitrary task at next main loop iteration. **/ BELLESIP_EXPORT void belle_sip_main_loop_do_later(belle_sip_main_loop_t *ml, belle_sip_callback_t func, void *data); /** * Creates a timeout source, similarly to belle_sip_main_loop_add_timeout(). * However in this case the timeout must be entered manually using belle_sip_main_loop_add_source(). * Its pointer can be used to remove it from the source (that is cancelling it). **/ BELLESIP_EXPORT belle_sip_source_t * belle_sip_timeout_source_new(belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms); BELLESIP_EXPORT void belle_sip_source_set_timeout(belle_sip_source_t *s, unsigned int value_ms); /** * Cancel a source. Will be removed at next iterate. It is not freed. **/ BELLESIP_EXPORT void belle_sip_source_cancel(belle_sip_source_t * src); BELLESIP_EXPORT unsigned int belle_sip_source_get_timeout(const belle_sip_source_t *s); BELLESIP_EXPORT belle_sip_source_t * belle_sip_socket_source_new(belle_sip_source_func_t func, void *data, belle_sip_socket_t fd, unsigned int events, unsigned int timeout_value_ms); /* * register a callback invoked once source is removed from mainloop */ BELLESIP_EXPORT void belle_sip_source_set_remove_cb(belle_sip_source_t *s, belle_sip_source_remove_callback_t func); BELLESIP_EXPORT unsigned long belle_sip_source_get_id(const belle_sip_source_t *s); BELLESIP_EXPORT void * belle_sip_source_get_user_data(const belle_sip_source_t *s); BELLESIP_EXPORT void belle_sip_source_set_user_data(belle_sip_source_t *s, void *user_data); BELLESIP_EXPORT belle_sip_source_t *belle_sip_main_loop_find_source(belle_sip_main_loop_t *ml, unsigned long id); /** * Executes the main loop forever (or until belle_sip_main_loop_quit() is called) **/ BELLESIP_EXPORT void belle_sip_main_loop_run(belle_sip_main_loop_t *ml); /** * Executes the main loop for the time specified in milliseconds. **/ BELLESIP_EXPORT void belle_sip_main_loop_sleep(belle_sip_main_loop_t *ml, int milliseconds); /** * Break out the main loop. **/ BELLESIP_EXPORT int belle_sip_main_loop_quit(belle_sip_main_loop_t *ml); /** * Cancel (removes) a source. It is not freed. **/ BELLESIP_EXPORT void belle_sip_main_loop_cancel_source(belle_sip_main_loop_t *ml, unsigned long id); BELLE_SIP_END_DECLS #ifndef BELLE_SIP_USE_STL #define BELLE_SIP_USE_STL 1 #endif #if __cplusplus >= 201103L && BELLE_SIP_USE_STL #include typedef std::function belle_sip_source_cpp_func_t; BELLESIP_EXPORT inline int belle_sip_source_cpp_func(belle_sip_source_cpp_func_t* user_data, unsigned int events) { int result = (*user_data)(events); return result; } BELLESIP_EXPORT inline void belle_sip_source_on_remove(belle_sip_source_t* source) { delete static_cast(belle_sip_source_get_user_data(source)); belle_sip_source_set_user_data(source,NULL); } /*purpose of this function is to simplify c++ timer integration. * ex: * std::string helloworld("Hello world): * belle_sip_source_cpp_func_t *func = new belle_sip_source_cpp_func_t([helloworld](unsigned int events) { * std::cout << helloworld << std::endl; * return BELLE_SIP_STOP; * }); *create timer *mTimer = belle_sip_main_loop_create_cpp_timeout( mainloop * , func * , 1000 * ,"timer for c++"); * */ BELLESIP_EXPORT inline belle_sip_source_t * belle_sip_main_loop_create_cpp_timeout(belle_sip_main_loop_t *ml , belle_sip_source_cpp_func_t *func , unsigned int timeout_value_ms , const char* timer_name) { belle_sip_source_t* source = belle_sip_main_loop_create_timeout( ml , (belle_sip_source_func_t)belle_sip_source_cpp_func , func , timeout_value_ms , timer_name); belle_sip_source_set_remove_cb(source,belle_sip_source_on_remove); return source; } #endif #endif belle-sip-1.6.3/include/belle-sip/message.h000066400000000000000000000167001313437522400205010ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SIP_MESSAGE_H #define BELLE_SIP_MESSAGE_H #include "belle-sip/headers.h" #define BELLE_SIP_MESSAGE(obj) BELLE_SIP_CAST(obj,belle_sip_message_t) #define BELLE_SIP_REQUEST(obj) BELLE_SIP_CAST(obj,belle_sip_request_t) #define BELLE_SIP_RESPONSE(obj) BELLE_SIP_CAST(obj,belle_sip_response_t) BELLE_SIP_BEGIN_DECLS BELLESIP_EXPORT belle_sip_message_t* belle_sip_message_parse(const char* raw); /** * Parse sip message from a raw buffer * @param [in] buff buffer to be parsed * @param [in] buff_length size of the buffer to be parsed * @param [out] message_length number of bytes read * @return parsed message */ BELLESIP_EXPORT belle_sip_message_t* belle_sip_message_parse_raw (const char* buff, size_t buff_length,size_t* message_length ); BELLESIP_EXPORT int belle_sip_message_is_request(belle_sip_message_t *msg); BELLESIP_EXPORT belle_sip_request_t* belle_sip_request_new(void); BELLESIP_EXPORT belle_sip_request_t* belle_sip_request_parse(const char* raw); BELLESIP_EXPORT belle_sip_request_t* belle_sip_request_create(belle_sip_uri_t *requri, const char* method, belle_sip_header_call_id_t *callid, belle_sip_header_cseq_t *cseq, belle_sip_header_from_t *from, belle_sip_header_to_t *to, belle_sip_header_via_t *via, int max_forwards); BELLESIP_EXPORT belle_sip_uri_t* belle_sip_request_get_uri(const belle_sip_request_t* request); BELLESIP_EXPORT void belle_sip_request_set_uri(belle_sip_request_t* request, belle_sip_uri_t* uri); BELLESIP_EXPORT const char* belle_sip_request_get_method(const belle_sip_request_t* request); BELLESIP_EXPORT void belle_sip_request_set_method(belle_sip_request_t* request,const char* method); /** * Guess the origin of the received sip message from VIA header (thanks to received/rport) * @param req request to be analyzed * @ return a newly allocated uri * */ BELLESIP_EXPORT belle_sip_uri_t* belle_sip_request_extract_origin(const belle_sip_request_t* req); /** * Clone all sip headers + body if any * @param req message to be cloned * @return newly allocated request */ BELLESIP_EXPORT belle_sip_request_t * belle_sip_request_clone_with_body(const belle_sip_request_t *initial_req); /** * returns an absolute uri. A header address cannot have both a sip uri and an absolute uri. */ BELLESIP_EXPORT belle_generic_uri_t* belle_sip_request_get_absolute_uri(const belle_sip_request_t* req); /** * set an absolute uri. A header address cannot have both a sip uri and an absolute uri. This function also to uri to NULL */ BELLESIP_EXPORT void belle_sip_request_set_absolute_uri(belle_sip_request_t* req, belle_generic_uri_t* uri); BELLESIP_EXPORT int belle_sip_message_is_response(const belle_sip_message_t *msg); BELLESIP_EXPORT belle_sip_header_t *belle_sip_message_get_header(const belle_sip_message_t *msg, const char *header_name); BELLESIP_EXPORT belle_sip_object_t *_belle_sip_message_get_header_by_type_id(const belle_sip_message_t *message, belle_sip_type_id_t id); #define belle_sip_message_get_header_by_type(msg,header_type)\ (header_type*)_belle_sip_message_get_header_by_type_id(BELLE_SIP_MESSAGE(msg),BELLE_SIP_TYPE_ID(header_type)) BELLESIP_EXPORT const belle_sip_list_t* belle_sip_message_get_headers(const belle_sip_message_t *message,const char* header_name); /** * Get list of all headers present in the message. * @param message * @return a newly allocated list of belle_sip_header_t * */ BELLESIP_EXPORT belle_sip_list_t* belle_sip_message_get_all_headers(const belle_sip_message_t *message); BELLESIP_EXPORT void belle_sip_message_add_first(belle_sip_message_t *msg, belle_sip_header_t* header); /** * add an header to this message * @param msg * @param header to add, must be one of header type */ BELLESIP_EXPORT void belle_sip_message_add_header(belle_sip_message_t *msg, belle_sip_header_t* header); BELLESIP_EXPORT void belle_sip_message_add_headers(belle_sip_message_t *message, const belle_sip_list_t *header_list); BELLESIP_EXPORT void belle_sip_message_set_header(belle_sip_message_t *msg, belle_sip_header_t* header); BELLESIP_EXPORT void belle_sip_message_remove_first(belle_sip_message_t *msg, const char *header_name); BELLESIP_EXPORT void belle_sip_message_remove_last(belle_sip_message_t *msg, const char *header_name); BELLESIP_EXPORT void belle_sip_message_remove_header(belle_sip_message_t *msg, const char *header_name); BELLESIP_EXPORT void belle_sip_message_remove_header_from_ptr(belle_sip_message_t *msg, belle_sip_header_t* header); BELLESIP_EXPORT char *belle_sip_message_to_string(belle_sip_message_t *msg); BELLESIP_EXPORT belle_sip_body_handler_t *belle_sip_message_get_body_handler(const belle_sip_message_t *msg); BELLESIP_EXPORT void belle_sip_message_set_body_handler(belle_sip_message_t *msg, belle_sip_body_handler_t *body_handler); BELLESIP_EXPORT const char* belle_sip_message_get_body(belle_sip_message_t *msg); BELLESIP_EXPORT size_t belle_sip_message_get_body_size(const belle_sip_message_t *msg); BELLESIP_EXPORT void belle_sip_message_set_body(belle_sip_message_t *msg,const char* body,size_t size); BELLESIP_EXPORT void belle_sip_message_assign_body(belle_sip_message_t *msg, char* body, size_t size); BELLESIP_EXPORT int belle_sip_response_get_status_code(const belle_sip_response_t *response); BELLESIP_EXPORT void belle_sip_response_set_status_code(belle_sip_response_t *response,int status); BELLESIP_EXPORT const char* belle_sip_response_get_reason_phrase(const belle_sip_response_t *response); BELLESIP_EXPORT void belle_sip_response_set_reason_phrase(belle_sip_response_t *response,const char* reason_phrase); BELLESIP_EXPORT belle_sip_response_t *belle_sip_response_new(void); BELLESIP_EXPORT belle_sip_response_t *belle_sip_response_create_from_request(belle_sip_request_t *req, int status_code); /** * This method takes received/rport/via value of the reponse and update the contact IP/port accordingly * @param response use to extract via/received/rport from top most via. * @param contact contact to be updated * @returns 0 if no error * */ BELLESIP_EXPORT int belle_sip_response_fix_contact(const belle_sip_response_t* response,belle_sip_header_contact_t* contact); /** * Check for mandatory headers and parameters. * If message does not satisfy minimum requirements return FALSE, otherwise return TRUE. **/ BELLESIP_EXPORT int belle_sip_message_check_headers(const belle_sip_message_t* message); /** * check uri components of headers and req uri. * return 0 if not compliant * */ BELLESIP_EXPORT int belle_sip_request_check_uris_components(const belle_sip_request_t* request); BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/object.h000066400000000000000000000522761313437522400203330ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_sip_object_h #define belle_sip_object_h #include "belle-sip/defs.h" #include "belle-sip/utils.h" #include "belle-sip/list.h" /* * typedefs, macros and functions for object definition and manipulation. */ #define BELLE_SIP_DEFAULT_BUFSIZE_HINT 0 #define BELLE_SIP_TYPE_ID(_type) _type##_id typedef unsigned int belle_sip_type_id_t; #define BELLE_SIP_DECLARE_TYPES_BEGIN(namezpace,unique_namespace_id) \ enum namezpace##type_ids{\ namezpace##type_id_first=unique_namespace_id, #define BELLE_SIP_DECLARE_TYPES_END }; #define BELLE_SIP_OBJECT_VPTR_NAME(object_type) object_type##_vptr #define BELLE_SIP_OBJECT_GET_VPTR_FUNC(object_type) object_type##_vptr_get #define BELLE_SIP_OBJECT_VPTR_TYPE(object_type) object_type##_vptr_t #define BELLE_SIP_DECLARE_VPTR(object_type) \ typedef belle_sip_object_vptr_t BELLE_SIP_OBJECT_VPTR_TYPE(object_type);\ BELLESIP_EXPORT BELLE_SIP_OBJECT_VPTR_TYPE(object_type) * BELLE_SIP_OBJECT_GET_VPTR_FUNC(object_type)(void); #define BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(object_type, parent_type) \ typedef struct object_type##_vptr_struct BELLE_SIP_OBJECT_VPTR_TYPE(object_type);\ BELLESIP_EXPORT BELLE_SIP_OBJECT_VPTR_TYPE(object_type) * BELLE_SIP_OBJECT_GET_VPTR_FUNC(object_type)(void); \ struct object_type##_vptr_struct{\ BELLE_SIP_OBJECT_VPTR_TYPE(parent_type) base; #define BELLE_SIP_DECLARE_VPTR_NO_EXPORT(object_type) \ typedef belle_sip_object_vptr_t BELLE_SIP_OBJECT_VPTR_TYPE(object_type);\ BELLE_SIP_OBJECT_VPTR_TYPE(object_type) * BELLE_SIP_OBJECT_GET_VPTR_FUNC(object_type)(void); #define BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN_NO_EXPORT(object_type, parent_type) \ typedef struct object_type##_vptr_struct BELLE_SIP_OBJECT_VPTR_TYPE(object_type);\ BELLE_SIP_OBJECT_VPTR_TYPE(object_type) * BELLE_SIP_OBJECT_GET_VPTR_FUNC(object_type)(void); \ struct object_type##_vptr_struct{\ BELLE_SIP_OBJECT_VPTR_TYPE(parent_type) base; #define BELLE_SIP_DECLARE_CUSTOM_VPTR_END }; #define BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(object_type) \ extern BELLE_SIP_OBJECT_VPTR_TYPE(object_type) BELLE_SIP_OBJECT_VPTR_NAME(object_type);\ BELLE_SIP_OBJECT_VPTR_TYPE(object_type) * BELLE_SIP_OBJECT_GET_VPTR_FUNC(object_type)(void){\ return &BELLE_SIP_OBJECT_VPTR_NAME(object_type); \ }\ BELLE_SIP_OBJECT_VPTR_TYPE(object_type) BELLE_SIP_OBJECT_VPTR_NAME(object_type)={ #define BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END }; #define BELLE_SIP_VPTR_INIT(object_type,parent_type,unowned) \ BELLE_SIP_TYPE_ID(object_type), \ sizeof(object_type), \ #object_type,\ unowned,\ (belle_sip_object_get_vptr_t)BELLE_SIP_OBJECT_GET_VPTR_FUNC(parent_type), \ (belle_sip_interface_desc_t**)object_type##interfaces_table #define BELLE_SIP_INSTANCIATE_VPTR(object_type,parent_type,destroy,clone,marshal,unowned) \ static BELLE_SIP_OBJECT_VPTR_TYPE(object_type) BELLE_SIP_OBJECT_VPTR_NAME(object_type)={ \ BELLE_SIP_VPTR_INIT(object_type,parent_type,unowned), \ (belle_sip_object_destroy_t)destroy, \ (belle_sip_object_clone_t)clone, \ (belle_sip_object_marshal_t)marshal,\ BELLE_SIP_DEFAULT_BUFSIZE_HINT\ }; \ BELLE_SIP_OBJECT_VPTR_TYPE(object_type) * BELLE_SIP_OBJECT_GET_VPTR_FUNC(object_type)(void){\ return &BELLE_SIP_OBJECT_VPTR_NAME(object_type); \ } /** * belle_sip_object_t is the base object. * It is the base class for all belle sip non trivial objects. * It owns a reference count which allows to trigger the destruction of the object when the last * user of it calls belle_sip_object_unref(). * * It contains a generic data store that allows users to store named data in it and retrieve them afterwards. * * About object lifecycle
* In belle-sip, objects can be, depending on their types, initially owned, that there are created with a ref count of 1, or * initially unowned, that is with reference count of 0. Such objets are also referred as "floating object". They are automatically destroyed * by the main loop iteration, so a floating object can be seen as a temporary object, until someones calls belle_sip_object_ref() on it. * * In order to know whether a kind of object is initially owned or initially unowned, you can use the test program tester/belle_sip_object_describe. * This tool gives the hierarchy and properties of the object type whose name is supplied in argument. For example: * *
./tester/belle_sip_object_describe belle_sip_request_t
* * The object memory management depends slightly on whether an object type is created initially owned or not. * In order not to be lost and make memory fault or leaks, consider the following rules: * * When an object is of type initially unowned: * * call belle_sip_object_ref() on it only if you need a pointer to this object to be used outside the scope of the current function. * * call belle_sip_object_unref() on it only if you previously called belle_sip_object_ref(). * * When an object is of type initially owned: * * you can safely store its pointer. * * use belle_sip_object_unref() when you no longer need it. * * Also, keep in mind that most objects of belle-sip are initially unowned, especially * * all objects who are usually required to be used inside another object (for example: an URI is part of a from header, a contact header is part of a message) * * all objects whose lifecyle is maintained by the stack: transactions, dialogs. * * On the contrary, top level objects whose lifecyle belongs only to the application are initially owned: * * belle_sip_provider_t, belle_sip_stack_t, belle_sip_source_t. * * Internally, belle-sip objects containing pointers to other objects must take a reference count on the other objects they hold; and leave this reference * when they no longer need it. This rule must be strictly followed by developers doing things inside belle-sip. **/ typedef struct _belle_sip_object belle_sip_object_t; typedef void (*belle_sip_object_destroy_t)(belle_sip_object_t*); typedef void (*belle_sip_object_clone_t)(belle_sip_object_t* obj, const belle_sip_object_t *orig); typedef belle_sip_error_code (*belle_sip_object_marshal_t)(belle_sip_object_t* obj, char* buff, size_t buff_size, size_t *offset); typedef struct _belle_sip_object_vptr *(*belle_sip_object_get_vptr_t)(void); struct _belle_sip_object_vptr{ belle_sip_type_id_t id; size_t size; /*the size of the object - not the vptr size*/ const char *type_name; int initially_unowned; belle_sip_object_get_vptr_t get_parent; struct belle_sip_interface_desc **interfaces; /*NULL terminated table of */ belle_sip_object_destroy_t destroy; belle_sip_object_clone_t clone; belle_sip_object_marshal_t marshal; int tostring_bufsize_hint; /*optimization: you can suggest here the typical size for a to_string() result.*/ }; typedef struct _belle_sip_object_vptr belle_sip_object_vptr_t; struct _belle_sip_object{ belle_sip_object_vptr_t *vptr; int ref; char* name; struct weak_ref *weak_refs; struct belle_sip_object_pool *pool; belle_sip_list_t *pool_iterator; belle_sip_list_t *data_store; }; BELLE_SIP_BEGIN_DECLS BELLESIP_EXPORT belle_sip_object_t * _belle_sip_object_new(size_t objsize, belle_sip_object_vptr_t *vptr); #define belle_sip_object_new(_type) (_type*)_belle_sip_object_new(sizeof(_type),(belle_sip_object_vptr_t*)BELLE_SIP_OBJECT_GET_VPTR_FUNC(_type)()) /** * Activates checks on object marshalling. * Useful for debug purposes. * @param enable TRUE to enable, FALSE to disable. **/ BELLESIP_EXPORT void belle_sip_object_enable_marshal_check(int enable); /** * Activates an object leak detector. When enabled, belle-sip will reference all created objects. * At program termination, application can check if objects remain alive using belle_sip_object_get_object_count() and dump them with * belle_sip_object_dump_active_objects(). * @warning this must not be used in multi-threaded programs (when multiple threads can access belle-sip at the same time) * Useful for debug purposes. * @param enable TRUE to enable, FALSE to disable. **/ BELLESIP_EXPORT void belle_sip_object_enable_leak_detector(int enable); BELLESIP_EXPORT int belle_sip_object_get_object_count(void); BELLESIP_EXPORT void belle_sip_object_flush_active_objects(void); BELLESIP_EXPORT void belle_sip_object_dump_active_objects(void); /** * Suspend leak detector from this point. If the leak detector wasn't activated, this function does nothing. * This can be useful to make object allocation that have to remain active beyond the scope of a test. **/ BELLESIP_EXPORT void belle_sip_object_inhibit_leak_detector(int yes); int belle_sip_object_is_unowed(const belle_sip_object_t *obj); /** * Increments reference counter, which prevents the object from being destroyed. * If the object is initially unowed, this acquires the first reference. * **/ BELLESIP_EXPORT belle_sip_object_t * belle_sip_object_ref(void *obj); /** * Decrements the reference counter. When it drops to zero, the object is destroyed. **/ BELLESIP_EXPORT void belle_sip_object_unref(void *obj); typedef void (*belle_sip_object_destroy_notify_t)(void *userpointer, belle_sip_object_t *obj_being_destroyed); /** * Add a weak reference to object. * When object will be destroyed, then the destroy_notify callback will be called. * This allows another object to be informed when object is destroyed, and then possibly * cleanups pointer it holds to this object. **/ BELLESIP_EXPORT belle_sip_object_t *belle_sip_object_weak_ref(void *obj, belle_sip_object_destroy_notify_t destroy_notify, void *userpointer); /** * Remove a weak reference to object. **/ BELLESIP_EXPORT void belle_sip_object_weak_unref(void *obj, belle_sip_object_destroy_notify_t destroy_notify, void *userpointer); /** * Set object name. **/ BELLESIP_EXPORT void belle_sip_object_set_name(belle_sip_object_t *obj,const char* name); /** * Get object name. **/ BELLESIP_EXPORT const char* belle_sip_object_get_name(belle_sip_object_t *obj); /*copy the content of ref object to new object, for the part they have in common in their inheritence diagram*/ void _belle_sip_object_copy(belle_sip_object_t *newobj, const belle_sip_object_t *ref); /** * Clone an object. * * This clone function makes a deep copy of all object internal structure, so that the new object and the reference object have no dependencies at all. * **/ BELLESIP_EXPORT belle_sip_object_t *belle_sip_object_clone(const belle_sip_object_t *obj); /** * Same as #belle_sip_object_clone but with ref count set to 1 * **/ belle_sip_object_t *belle_sip_object_clone_and_ref(const belle_sip_object_t *obj); typedef void (*belle_sip_data_destroy)(void* data); typedef void* (*belle_sip_data_clone)(const char* name, void* data); /** * Add an entry to the object's embedded data store, with the key name specified. * The destroy function is used when the data is cleaned. * * If an entry already exists, the existing data will be cleaned by calling its destroy function and the new data will be placed instead. * * Returns -1 in case of error, 0 in case the insertion was successful, and 1 if existing data was present. **/ BELLESIP_EXPORT int belle_sip_object_data_set( belle_sip_object_t *obj, const char* name, void* data, belle_sip_data_destroy destroy_func ); /** * Retrieve data that has been stored in the object data store. **/ BELLESIP_EXPORT void* belle_sip_object_data_get( belle_sip_object_t *obj, const char* name ); /** * Return 1 if the key exists in the data store, 0 otherwise **/ BELLESIP_EXPORT int belle_sip_object_data_exists( const belle_sip_object_t *obj, const char* name ); /** * Destroys the named data associated by the name provided. * * Returns 0 for success, -1 for error **/ BELLESIP_EXPORT int belle_sip_object_data_remove( belle_sip_object_t *obj, const char* name); /** * Retrieve the data from the data store and removes it from the data store, without calling the destructor. * This transfers ownership of the data to the caller, which will be in charge of releasing it. **/ BELLESIP_EXPORT void* belle_sip_object_data_grab( belle_sip_object_t* obj, const char* name); /** * Clears all data in the object's storage, invoking the destroy_func when possible **/ BELLESIP_EXPORT void belle_sip_object_data_clear( belle_sip_object_t* obj ); /** * clones the object's data store to another one, using the provided clone function to clone individual data items. * * The destination data store will be cleaned before pushing the source data into it. * For a merge, use #belle_sip_object_data_merge. * This is equivalent to the following code: * { * belle_sip_object_data_clear(dst); * belle_sip_object_data_merge(src, dst, clone_func); * } * * Note that providing NULL as a cloning function will simply assign the src object's data to the dst object. * **/ BELLESIP_EXPORT void belle_sip_object_data_clone( const belle_sip_object_t* src, belle_sip_object_t* dst, belle_sip_data_clone clone_func); /** * Merge the source data store into the destination data store. * * Same function as #belle_sip_object_data_clone, except the destination data store is not cleared before inserting the source data. * This overwrites common keys, and keeps existing keys. */ BELLESIP_EXPORT void belle_sip_object_data_merge( const belle_sip_object_t* src, belle_sip_object_t* dst, belle_sip_data_clone clone_func); /** * Apply a function for each entry in the data store */ BELLESIP_EXPORT void belle_sip_object_data_foreach( const belle_sip_object_t* obj, void (*apply_func)(const char* key, void* data, void* userdata), void* userdata); /** * Returns a string describing the inheritance diagram and implemented interfaces of object obj. **/ char *belle_sip_object_describe(void *obj); /** * Returns a string describing the inheritance diagram and implemented interfaces of an object given its type name. **/ BELLESIP_EXPORT char *belle_sip_object_describe_type_from_name(const char *name); BELLESIP_EXPORT void *belle_sip_object_cast(belle_sip_object_t *obj, belle_sip_type_id_t id, const char *castname, const char *file, int fileno); /** * Returns a newly allocated string representing the object. * WHen the object is a sip header, uri or message, this is the textual representation of the header, uri or message. * This function internally calls belle_sip_object_marshal(). **/ BELLESIP_EXPORT char* belle_sip_object_to_string(void* obj); /** * Writes a string representation of the object into the supplied buffer. * Same as belle_sip_object_to_string(), but without allocating space for the output string. **/ BELLESIP_EXPORT belle_sip_error_code belle_sip_object_marshal(belle_sip_object_t* obj, char* buff, size_t buff_size, size_t *offset); /* use BELLE_SIP_OBJECT_IS_INSTANCE_OF macro(), this function is for use by the macro only*/ BELLESIP_EXPORT int _belle_sip_object_is_instance_of(belle_sip_object_t * obj,belle_sip_type_id_t id); BELLE_SIP_END_DECLS #define BELLE_SIP_CAST(obj,_type) ((_type*)belle_sip_object_cast((belle_sip_object_t *)(obj), _type##_id, #_type, __FILE__, __LINE__)) #define BELLE_SIP_OBJECT(obj) BELLE_SIP_CAST(obj,belle_sip_object_t) #define BELLE_SIP_OBJECT_IS_INSTANCE_OF(obj,_type) _belle_sip_object_is_instance_of((belle_sip_object_t*)obj,_type##_id) #define BELLE_SIP_OBJECT_VPTR(obj,object_type) ((BELLE_SIP_OBJECT_VPTR_TYPE(object_type)*)(((belle_sip_object_t*)obj)->vptr)) /*deprecated*/ #define BELLE_SIP_IS_INSTANCE_OF(obj,_type) BELLE_SIP_OBJECT_IS_INSTANCE_OF(obj,_type) #define belle_sip_object_describe_type(type) \ belle_sip_object_describe_type_from_name(#type) /* * typedefs, macros and functions for interface definition and manipulation. */ #define BELLE_SIP_INTERFACE_ID(_interface) _interface##_id typedef unsigned int belle_sip_interface_id_t; BELLE_SIP_BEGIN_DECLS BELLESIP_EXPORT void *belle_sip_object_interface_cast(belle_sip_object_t *obj, belle_sip_interface_id_t id, const char *castname, const char *file, int fileno); int belle_sip_object_implements(belle_sip_object_t *obj, belle_sip_interface_id_t id); BELLE_SIP_END_DECLS #define BELLE_SIP_INTERFACE_METHODS_TYPE(interface_name) methods_##interface_name #define BELLE_SIP_INTERFACE_CAST(obj,_iface) ((_iface*)belle_sip_object_interface_cast((belle_sip_object_t*)(obj),_iface##_id,#_iface,__FILE__,__LINE__)) #define BELLE_SIP_IMPLEMENTS(obj,_iface) belle_sip_object_implements((belle_sip_object_t*)obj,_iface##_id) typedef struct belle_sip_interface_desc{ belle_sip_interface_id_t id; const char *ifname; }belle_sip_interface_desc_t; #define BELLE_SIP_DECLARE_INTERFACE_BEGIN(interface_name) \ typedef struct struct##interface_name interface_name;\ typedef struct struct_methods_##interface_name BELLE_SIP_INTERFACE_METHODS_TYPE(interface_name);\ struct struct_methods_##interface_name {\ belle_sip_interface_desc_t desc;\ #define BELLE_SIP_DECLARE_INTERFACE_END }; #define BELLE_SIP_IMPLEMENT_INTERFACE_BEGIN(object_type,interface_name) \ static BELLE_SIP_INTERFACE_METHODS_TYPE(interface_name) methods_##object_type##_##interface_name={\ { BELLE_SIP_INTERFACE_ID(interface_name),\ #interface_name }, #define BELLE_SIP_IMPLEMENT_INTERFACE_END }; #define BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(object_type)\ static belle_sip_interface_desc_t * object_type##interfaces_table[]={\ NULL \ } #define BELLE_SIP_DECLARE_IMPLEMENTED_INTERFACES_1(object_type,iface1) \ static belle_sip_interface_desc_t * object_type##interfaces_table[]={\ (belle_sip_interface_desc_t*)&methods_##object_type##_##iface1, \ NULL \ } #define BELLE_SIP_DECLARE_IMPLEMENTED_INTERFACES_2(object_type,iface1,iface2) \ static belle_sip_interface_desc_t * object_type##interfaces_table[]={\ (belle_sip_interface_desc_t*)&methods_##object_type##_##iface1, \ (belle_sip_interface_desc_t*)&methods_##object_type##_##iface2, \ NULL \ } /** * Object holding unowned objects - used as a kind of garbage collector for temporary objects. **/ typedef struct belle_sip_object_pool belle_sip_object_pool_t; BELLE_SIP_BEGIN_DECLS /** * Push a new object pool for use for creation of new objects. * When no longer needed, this pool can be destroyed with belle_sip_object_unref(). **/ BELLESIP_EXPORT belle_sip_object_pool_t * belle_sip_object_pool_push(void); belle_sip_object_pool_t * belle_sip_object_pool_get_current(void); int belle_sip_object_pool_cleanable(belle_sip_object_pool_t *pool); void belle_sip_object_pool_clean(belle_sip_object_pool_t *obj); BELLE_SIP_DECLARE_VPTR(belle_sip_object_t) BELLE_SIP_END_DECLS /** * Adding a new type in belle-sip in 5 steps * ========================================= * * Let's suppose you want to add an object called belle_sip_something_t * 1) Declare the type in the enum in belle-sip.h: * BELLE_SIP_TYPE_ID(belle_sip_something_t) * 2) Declare the api of the new object in .h, including a typedef and a cast macro: * typedef struct belle_sip_something belle_sip_something_t; * #define BELLE_SIP_SOMETHING(obj) BELLE_SIP_CAST(obj,belle_sip_something_t) * * belle_sip_something_t *belle_sip_something_create(int arg1, int arg2); * void belle_sip_something_do_cooking(belle_sip_something_t *obj); * Do not add any destructor, belle_sip_object_unref() does it for all objects. * * 3) in the c file contaning the object's implementation, define the internal structure for your object. * The first field of the struct must be the parent type. * struct belle_sip_something{ * belle_sip_object_t base; * int myint1; * int myint2; * char *mychar; * }; * 4) still in the C file contaning the object's implementation, define a destructor and all functions of its API: * The destructor must only manage the fields from the type, not the parent. * static void belle_sip_something_destroy(belle_sip_something_t *obj){ * if (obj->mychar) belle_sip_free(obj->mychar); * } * * belle_sip_something_t *belle_sip_something_create(int arg1, int arg2){ * belle_sip_something_t *obj=belle_sip_object_new(belle_sip_something_t); * obj->myint1=arg1; * obj->myint2=arg2; * obj->mychar=belle_sip_strdup("Hello world"); * return obj; * } * Declare the interfaces implemented by the object (to be documented) and instanciate its "vptr", necessary for dynamic casting. * BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_something_t); * BELLE_SIP_INSTANCIATE_VPTR(belle_sip_something_t, belle_sip_object_t,belle_sip_something_destroy, NULL, NULL,FALSE); * * 5) in .h file included everywhere in the source (typically belle_sip_internal.h), declare the vptr * BELLE_SIP_DECLARE_VPTR(belle_sip_dns_srv_t); */ #if defined(__cplusplus) && defined(BELLE_SIP_USE_STL) #include inline std::ostream& operator<<( std::ostream& __os, const belle_sip_object_t* object) { char* object_as_string = belle_sip_object_to_string((void*)object); __os << object_as_string; belle_sip_free(object_as_string); return __os; } #endif #endif belle-sip-1.6.3/include/belle-sip/parameters.h000066400000000000000000000055031313437522400212170ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PARAMETERS_H_ #define PARAMETERS_H_ #include "belle-sip/utils.h" #include "belle-sip/list.h" BELLE_SIP_BEGIN_DECLS /*** * parameters * */ belle_sip_parameters_t* belle_sip_parameters_new(void); /* * remove all parameters */ BELLESIP_EXPORT void belle_sip_parameters_clean(belle_sip_parameters_t* params); /*BELLESIP_EXPORT void belle_sip_parameters_destroy(belle_sip_parameters_t* params);*/ BELLESIP_EXPORT const char* belle_sip_parameters_get_parameter(const belle_sip_parameters_t* obj,const char* name); /* * same as #belle_sip_parameters_get_parameter but name is case insensitive */ BELLESIP_EXPORT const char* belle_sip_parameters_get_case_parameter(const belle_sip_parameters_t* params,const char* name); /** * returns 0 if not found */ BELLESIP_EXPORT unsigned int belle_sip_parameters_has_parameter(const belle_sip_parameters_t* obj,const char* name); BELLESIP_EXPORT void belle_sip_parameters_set_parameter(belle_sip_parameters_t* obj,const char* name,const char* value); /** * Assign a full set of parameters to the belle_sip_parameters_t object. * Parameters are given as string of key=value pairs separated with semicolons, where value is optional. * @example belle_sip_parameters_set(parameters,"param1=value1;param2;param3=value3"); **/ BELLESIP_EXPORT void belle_sip_parameters_set(belle_sip_parameters_t *parameters, const char* params); BELLESIP_EXPORT const belle_sip_list_t * belle_sip_parameters_get_parameter_names(const belle_sip_parameters_t* obj); BELLESIP_EXPORT const belle_sip_list_t * belle_sip_parameters_get_parameters(const belle_sip_parameters_t* obj); BELLESIP_EXPORT void belle_sip_parameters_remove_parameter(belle_sip_parameters_t* obj,const char* name); BELLESIP_EXPORT belle_sip_error_code belle_sip_parameters_marshal(const belle_sip_parameters_t* obj, char* buff, size_t buff_size, size_t *offset); BELLESIP_EXPORT void belle_sip_parameters_copy_parameters_from(belle_sip_parameters_t *params, const belle_sip_parameters_t *orig); #define BELLE_SIP_PARAMETERS(obj) BELLE_SIP_CAST(obj,belle_sip_parameters_t) BELLE_SIP_END_DECLS #endif /*PARAMETERS_H_*/ belle-sip-1.6.3/include/belle-sip/provider.h000066400000000000000000000140641313437522400207100ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_sip_provider_h #define belle_sip_provider_h #define BELLE_SIP_BRANCH_MAGIC_COOKIE "z9hG4bK" BELLE_SIP_BEGIN_DECLS BELLESIP_EXPORT belle_sip_uri_t *belle_sip_provider_create_inbound_record_route(belle_sip_provider_t *p, belle_sip_request_t *req); BELLESIP_EXPORT int belle_sip_provider_is_us(belle_sip_provider_t *p, belle_sip_uri_t*); BELLESIP_EXPORT int belle_sip_provider_add_listening_point(belle_sip_provider_t *p, belle_sip_listening_point_t *lp); BELLESIP_EXPORT void belle_sip_provider_remove_listening_point(belle_sip_provider_t *p, belle_sip_listening_point_t *lp); BELLESIP_EXPORT belle_sip_listening_point_t *belle_sip_provider_get_listening_point(belle_sip_provider_t *p, const char *transport); BELLESIP_EXPORT const belle_sip_list_t *belle_sip_provider_get_listening_points(belle_sip_provider_t *p); BELLESIP_EXPORT void belle_sip_provider_add_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l); BELLESIP_EXPORT void belle_sip_provider_remove_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l); BELLESIP_EXPORT belle_sip_header_call_id_t * belle_sip_provider_create_call_id(const belle_sip_provider_t *prov); BELLESIP_EXPORT belle_sip_dialog_t *belle_sip_provider_create_dialog(belle_sip_provider_t *prov, belle_sip_transaction_t *t); BELLESIP_EXPORT belle_sip_client_transaction_t *belle_sip_provider_create_client_transaction(belle_sip_provider_t *p, belle_sip_request_t *req); BELLESIP_EXPORT belle_sip_server_transaction_t *belle_sip_provider_create_server_transaction(belle_sip_provider_t *p, belle_sip_request_t *req); BELLESIP_EXPORT belle_sip_stack_t *belle_sip_provider_get_sip_stack(belle_sip_provider_t *p); BELLESIP_EXPORT void belle_sip_provider_send_request(belle_sip_provider_t *p, belle_sip_request_t *req); BELLESIP_EXPORT void belle_sip_provider_send_response(belle_sip_provider_t *p, belle_sip_response_t *resp); BELLESIP_EXPORT void belle_sip_provider_clean_channels(belle_sip_provider_t *p); /** * Add auth info to the request if found * @param p object * @param request to be updated * @param resp response to take authentication values from, might be NULL * @param from_uri optional - an uri to use instead of the from of the request, which can be anonymous. * @param auth_infos optional - A newly allocated belle_sip_auth_info_t object is added to this list. These object contains useful information like realm and username. * @param realm optional - If an outbound proxy realm is used, nounce can be reused from previous request to avoid re-authentication. * @returns 0 in case of success, * **/ BELLESIP_EXPORT int belle_sip_provider_add_authorization(belle_sip_provider_t *p, belle_sip_request_t* request,belle_sip_response_t *resp, belle_sip_uri_t *from_uri, belle_sip_list_t** auth_infos, const char* realm); /** * Can be used to simulate network recv error, for tests. * @param prov * @param recv_error if <=0, will cause channel error to be reported **/ BELLESIP_EXPORT void belle_sip_provider_set_recv_error(belle_sip_provider_t *prov, int recv_error); /** * Can be used to unconditionally answer to incoming sip messages. By default 480 is answered. * Can be enhanced by a new method belle_sip_provider_set_unconditional_answer to allows user to provide answer code * @param prov * @param enable 0 to disable **/ BELLESIP_EXPORT void belle_sip_provider_enable_unconditional_answer(belle_sip_provider_t *prov, int enable); /** * Can be used to choose unconditionally answer to incoming sip messages. * use belle_sip_provider_enable_unconditional_answer to enable/disable * @param prov * @param code 0 to sip response code **/ BELLESIP_EXPORT void belle_sip_provider_set_unconditional_answer(belle_sip_provider_t *prov, unsigned short code); /** * Provides access to a specific dialog * @param prov object * @param call_if of the dialog * @param local_tag of the dialog * @param remote_tag of the dialog * @returns dialog corresponding to this parameter or NULL if not found * **/ BELLESIP_EXPORT belle_sip_dialog_t* belle_sip_provider_find_dialog(const belle_sip_provider_t *prov, const char* call_id,const char* local_tag,const char* remote_tag); /** * Enable rport in via header. Enabled by default * @param prov * @return enable 0 to disable **/ BELLESIP_EXPORT void belle_sip_provider_enable_rport(belle_sip_provider_t *prov, int enable); /** * get Enable rport in via header. Enabled by default * @param prov * @param enable 0 to disable **/ BELLESIP_EXPORT int belle_sip_provider_is_rport_enabled(belle_sip_provider_t *prov); /** * Enable discovery of NAT's public address and port during SIP exchanges. * When activated, automatic contacts ( see belle_sip_header_contact_set_automatic() ) * will use discovered public IP address and port (if any) instead of local ones. * NAT public address and port are discovered using received and rport parameters in via header of responses. * As a result, disabling rport ( see belle_sip_provider_enable_rport() ) will also break this feature. **/ BELLESIP_EXPORT void belle_sip_provider_enable_nat_helper(belle_sip_provider_t *prov, int enabled); /** * Returns if nat helper behavior is enabled. * @see belle_sip_provider_enable_nat_helper() **/ BELLESIP_EXPORT int belle_sip_provider_nat_helper_enabled(const belle_sip_provider_t *prov); BELLE_SIP_END_DECLS #define BELLE_SIP_PROVIDER(obj) BELLE_SIP_CAST(obj,belle_sip_provider_t) #endif belle-sip-1.6.3/include/belle-sip/refresher.h000066400000000000000000000111461313437522400210410ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2012 Belledonne Communications SARL, Grenoble, France This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef REFRESHER_HELPER_H_ #define REFRESHER_HELPER_H_ #define BELLE_SIP_REFRESHER_REUSE_EXPIRES -1 BELLE_SIP_BEGIN_DECLS typedef struct belle_sip_refresher belle_sip_refresher_t; /** * Refresher listener invoked every time a refresh action is performed * @param refresher corresponding refresher object. * @param user_pointer user pointer * @param status_code status code for the last refresh action * @param reason_phrase * @param will_retry a boolean indicating wether the refresher is going to retry the request automatically. * */ typedef void (*belle_sip_refresher_listener_t) (belle_sip_refresher_t* refresher ,void* user_pointer ,unsigned int status_code ,const char* reason_phrase, int will_retry); /** * add a refresher listener */ BELLESIP_EXPORT void belle_sip_refresher_set_listener(belle_sip_refresher_t* refresher, belle_sip_refresher_listener_t listener,void* user_pointer); /** * start the refresher */ int belle_sip_refresher_start(belle_sip_refresher_t* refresher); /** * stop refresher. * If a transaction is pending, it will be terminated. */ BELLESIP_EXPORT void belle_sip_refresher_stop(belle_sip_refresher_t* refresher); /** * Manually initiate a new transaction . * @param refresher object * @param expires #BELLE_SIP_REFRESHER_REUSE_EXPIRES means value extracted from the transaction * @return 0 if succeed */ BELLESIP_EXPORT int belle_sip_refresher_refresh(belle_sip_refresher_t* refresher,int expires); /** * returns current expires value; */ BELLESIP_EXPORT int belle_sip_refresher_get_expires(const belle_sip_refresher_t* refresher); /** * returns delay in ms after which the refresher will retry in case of recoverable error (I.E 408, 480, 503, 504, io error); */ BELLESIP_EXPORT int belle_sip_refresher_get_retry_after(const belle_sip_refresher_t* refresher); /** * Delay in ms after which the refresher will retry in case of recoverable error (I.E 408, 480, 503, 504, io error); */ BELLESIP_EXPORT void belle_sip_refresher_set_retry_after(belle_sip_refresher_t* refresher, int delay_ms); /** * returns realm of the outbound proxy used for authentication, if any */ BELLESIP_EXPORT const char* belle_sip_refresher_get_realm(const belle_sip_refresher_t* refresher); /** * Realm of the outbound proxy used for authentication, if any */ BELLESIP_EXPORT void belle_sip_refresher_set_realm(belle_sip_refresher_t* refresher, const char* realm); /** * get current client transaction * @param refresher object * @return transaction */ BELLESIP_EXPORT const belle_sip_client_transaction_t* belle_sip_refresher_get_transaction(const belle_sip_refresher_t* refresher); /** * Get current list of auth info if any. Contains the list of filled #belle_sip_auth_event_t in case of a 401 or 407 is repported to the #belle_sip_refresher_listener_t ; * @param refresher object * @return list of #belle_sip_auth_info_t */ BELLESIP_EXPORT const belle_sip_list_t* belle_sip_refresher_get_auth_events(const belle_sip_refresher_t* refresher); /** * Enable manual mode: only belle_sip_refresher_refresh() called by application will cause requests to be resubmitted. **/ BELLESIP_EXPORT void belle_sip_refresher_enable_manual_mode(belle_sip_refresher_t *refresher, int enabled); /** * Retrieve current local address used by the underlying refresher's channel. **/ BELLESIP_EXPORT const char * belle_sip_refresher_get_local_address(belle_sip_refresher_t* refresher, int *port); /** * Retrieve current public address used by the underlying refresher's channel. **/ BELLESIP_EXPORT const char * belle_sip_refresher_get_public_address(belle_sip_refresher_t* refresher, int *port); /** * Retrieve last know contact header if known. Only available after a successful registration. **/ BELLESIP_EXPORT belle_sip_header_contact_t* belle_sip_refresher_get_contact(const belle_sip_refresher_t* refresher); BELLE_SIP_END_DECLS #endif /* REFRESHER_HELPER_H_ */ belle-sip-1.6.3/include/belle-sip/resolver.h000066400000000000000000000133451313437522400207200ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_sip_resolver_h #define belle_sip_resolver_h typedef struct belle_sip_dns_srv belle_sip_dns_srv_t; #define BELLE_SIP_DNS_SRV(obj) BELLE_SIP_CAST(obj,belle_sip_dns_srv_t) typedef struct belle_sip_resolver_context belle_sip_resolver_context_t; #define BELLE_SIP_RESOLVER_CONTEXT(obj) BELLE_SIP_CAST(obj,belle_sip_resolver_context_t) /** * Callback prototype for asynchronous DNS SRV resolution. * The srv_list contains struct dns_srv elements that must be taken and (possibly later) freed by the callee, using belle_sip_free(). */ typedef void (*belle_sip_resolver_srv_callback_t)(void *data, const char *name, belle_sip_list_t *srv_list, uint32_t ttl); /** * Callback prototype for asynchronous DNS A and AAAA resolution. * The ai_list contains addrinfo elements that must be taken and (possibly later) freed by the callee, using freeaddrinfo(). * These elements are linked by their ai_next field. **/ typedef void (*belle_sip_resolver_callback_t)(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl); BELLE_SIP_BEGIN_DECLS BELLESIP_EXPORT const char *belle_sip_dns_srv_get_target(const belle_sip_dns_srv_t *obj); BELLESIP_EXPORT unsigned short belle_sip_dns_srv_get_priority(const belle_sip_dns_srv_t *obj); BELLESIP_EXPORT unsigned short belle_sip_dns_srv_get_weight(const belle_sip_dns_srv_t *obj); BELLESIP_EXPORT unsigned short belle_sip_dns_srv_get_port(const belle_sip_dns_srv_t *obj); /** * Asynchronously performs DNS SRV followed A/AAAA query. Automatically fallbacks to A/AAAA if SRV isn't found. * @param stack the belle_sip_stack_t, used to schedule asynchronous execution. * @param service the queried service ("sip", "stun", "turn"...) * @param transport the queried transport ("udp", "tcp", "tls") * @param name the SIP domain name * @param port a port that will be filled in the addrinfo list returned by the callback, for the case where no SRV records are found. * @param family address family expected in the addrinfo result list. AF_INET or AF_INET6. If AF_INET6 is used but no IPv6 records are found, the IPv4 addresses * will be returned as IPv6 with v4 mapping (AI_V4MAPPED). * @param cb a callback function that will be called to notify the results. * @param data a user pointer passed through the callback as first argument. * @return a #belle_sip_resolver_context_t that can be used to cancel the resolution if needed. The context must have been ref'd with belle_sip_object_ref(). **/ BELLESIP_EXPORT belle_sip_resolver_context_t * belle_sip_stack_resolve(belle_sip_stack_t *stack, const char *service, const char *transport, const char *name, int port, int family, belle_sip_resolver_callback_t cb, void *data); /** * Asynchronously performs DNS A or AAAA query. * @param stack the belle_sip_stack_t, used to schedule asynchronous execution. * @param name the SIP domain name * @param port a port that will be filled in the addrinfo list returned by the callback, for the case where no SRV records are found. * @param family address family expected in the addrinfo result list. AF_INET or AF_INET6. If AF_INET6 is used but no IPv4 records are found, the IPv4 addresses * will be returned as IPv6 with v4 mapping (AI_V4MAPPED). * @param cb a callback function that will be called to notify the results. * @param data a user pointer passed through the callback as first argument. * @return a #belle_sip_resolver_context_t that can be used to cancel the resolution if needed. The context must have been ref'd with belle_sip_object_ref(). **/ BELLESIP_EXPORT belle_sip_resolver_context_t * belle_sip_stack_resolve_a(belle_sip_stack_t *stack, const char *name, int port, int family, belle_sip_resolver_callback_t cb, void *data); /** * Asynchronously performs DNS SRV query. * @return a #belle_sip_resolver_context_t that can be used to cancel the resolution if needed. The context must have been ref'd with belle_sip_object_ref(). **/ BELLESIP_EXPORT belle_sip_resolver_context_t * belle_sip_stack_resolve_srv(belle_sip_stack_t *stack, const char *service, const char *transport, const char *name, belle_sip_resolver_srv_callback_t cb, void *data); /** * Cancel a pending asynchronous DNS query. The context is unref'd automatically, as a result the context shall no longer be used after this call. * If the query is already performed, cancellation has no effect, but context is unref'd anyway. **/ BELLESIP_EXPORT void belle_sip_resolver_context_cancel(belle_sip_resolver_context_t *ctx); /** * Lookups the source address from local interface that can be used to connect to a destination address. * local_port is only used to be assigned into the result source address. * This function always puts something in src and srclen (the loopback address) even if anything fails. * The return code is 0 if successful, or -errno if an error was encoutered. Typical error is -ENETUNREACH when IPv6 network is not reachable. **/ BELLESIP_EXPORT int belle_sip_get_src_addr_for(const struct sockaddr *dest, socklen_t destlen, struct sockaddr *src, socklen_t *srclen, int local_port); BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/sip-uri.h000066400000000000000000000206351313437522400204470ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SIP_URI_H_ #define BELLE_SIP_URI_H_ #include "belle-sip/defs.h" #include "belle-sip/list.h" #include "belle-sip/utils.h" #include "belle-sip/types.h" BELLE_SIP_BEGIN_DECLS /** * */ BELLESIP_EXPORT belle_sip_uri_t* belle_sip_uri_new(void); /** * */ BELLESIP_EXPORT belle_sip_uri_t* belle_sip_uri_parse (const char* uri) ; /** * */ BELLESIP_EXPORT belle_sip_uri_t* belle_sip_fast_uri_parse (const char* uri) ; /** * same as belle_sip_uri_parse but with much less checks */ BELLESIP_EXPORT belle_sip_uri_t* belle_sip_fast_uri_parse (const char* uri) ; /** * */ BELLESIP_EXPORT belle_sip_uri_t* belle_sip_uri_create (const char* username,const char* host) ; /** * Returns the value of the named header, or null if it is not set. * */ BELLESIP_EXPORT const char* belle_sip_uri_get_header(const belle_sip_uri_t* uri,const char* name); /** * remove all headers * */ BELLESIP_EXPORT void belle_sip_uri_headers_clean(belle_sip_uri_t* uri); /** * Returns an Iterator over the const char*names of all headers present in this SipURI. * */ BELLESIP_EXPORT const belle_sip_list_t* belle_sip_uri_get_header_names(const belle_sip_uri_t* uri) ; /** * Returns the host part of this SipURI. * */ BELLESIP_EXPORT const char* belle_sip_uri_get_host(const belle_sip_uri_t* uri) ; /** * Returns the value of the maddr parameter, or null if this is not set. * */ BELLESIP_EXPORT const char* belle_sip_uri_get_maddr_param(const belle_sip_uri_t* uri) ; /** * Returns the value of the method parameter, or null if this is not set. * */ BELLESIP_EXPORT const char* belle_sip_uri_get_method_param(const belle_sip_uri_t* uri) ; /** * Returns the port part of this SipURI. * */ BELLESIP_EXPORT int belle_sip_uri_get_port(const belle_sip_uri_t* uri) ; /** * Returns the port of the uri, if not specified in the uri returns the well known port according to the transport. **/ BELLESIP_EXPORT int belle_sip_uri_get_listening_port(const belle_sip_uri_t *uri); /** * Returns the value of the "transport" parameter, or null if this is not set. * */ BELLESIP_EXPORT const char* belle_sip_uri_get_transport_param(const belle_sip_uri_t* uri) ; /** * Returns the value of the "ttl" parameter, or -1 if this is not set. * */ BELLESIP_EXPORT int belle_sip_uri_get_ttl_param(const belle_sip_uri_t* uri) ; /** * Returns the user part of this SipURI. * */ BELLESIP_EXPORT const char* belle_sip_uri_get_user(const belle_sip_uri_t* uri) ; /** * Returns the value of the userParam, or null if this is not set. * */ BELLESIP_EXPORT const char* belle_sip_uri_get_user_param(const belle_sip_uri_t* uri) ; /** * Gets user password of SipURI, or null if it is not set. * */ BELLESIP_EXPORT const char* belle_sip_uri_get_user_password(const belle_sip_uri_t* uri) ; /** * Returns whether the the lr parameter is set. * */ BELLESIP_EXPORT unsigned int belle_sip_uri_has_lr_param(const belle_sip_uri_t* uri) ; /** * * Returns true if this SipURI is secure i.e. if this SipURI represents a sips URI. * */ BELLESIP_EXPORT unsigned int belle_sip_uri_is_secure(const belle_sip_uri_t* uri) ; /** * Sets the value of the specified header fields to be included in a request constructed from the URI. * */ BELLESIP_EXPORT void belle_sip_uri_set_header(belle_sip_uri_t* uri,const char*name, const char*value) ; /** * Removes specified header from uri. **/ BELLESIP_EXPORT void belle_sip_uri_remove_header(belle_sip_uri_t *uri, const char *name); /** * Removes the port part of this SipURI. * */ BELLESIP_EXPORT void belle_sip_uri_remove_port(belle_sip_uri_t* uri) ; /** * Set the host part of this SipURI to the newly supplied host parameter. * */ BELLESIP_EXPORT void belle_sip_uri_set_host(belle_sip_uri_t* uri,const char*host) ; /** * Sets the value of the lr parameter of this SipURI. * */ BELLESIP_EXPORT void belle_sip_uri_set_lr_param(belle_sip_uri_t* uri,unsigned int param) ; /** * Sets the value of the maddr parameter of this SipURI. * */ BELLESIP_EXPORT void belle_sip_uri_set_maddr_param(belle_sip_uri_t* uri,const char*mAddr) ; /** * Sets the value of the method parameter. * */ BELLESIP_EXPORT void belle_sip_uri_set_method_param(belle_sip_uri_t* uri,const char*method) ; /** * Set the port part of this SipURI to the newly supplied port parameter. * */ BELLESIP_EXPORT void belle_sip_uri_set_port(belle_sip_uri_t* uri, int port) ; /** * Sets the scheme of this URI to sip or sips depending on whether the argument is true or false. * */ BELLESIP_EXPORT void belle_sip_uri_set_secure(belle_sip_uri_t* uri,unsigned int secure) ; /** * Sets the value of the "transport" parameter. * */ BELLESIP_EXPORT void belle_sip_uri_set_transport_param(belle_sip_uri_t* uri,const char*transport) ; /** * Sets the value of the ttl parameter. * */ BELLESIP_EXPORT void belle_sip_uri_set_ttl_param(belle_sip_uri_t* uri,int ttl) ; /** * Sets the user of SipURI. * */ BELLESIP_EXPORT void belle_sip_uri_set_user(belle_sip_uri_t* uri,const char*user) ; /** * Sets the value of the user parameter. * */ BELLESIP_EXPORT void belle_sip_uri_set_user_param(belle_sip_uri_t* uri,const char*userParam) ; /** * Sets the user password associated with the user of SipURI. * */ BELLESIP_EXPORT void belle_sip_uri_set_user_password(belle_sip_uri_t* uri,const char*userPassword) ; /** * This method returns the URI as a string. * */ BELLESIP_EXPORT char* belle_sip_uri_to_string(const belle_sip_uri_t* uri) ; belle_sip_error_code belle_sip_uri_marshal(const belle_sip_uri_t* uri, char* buff, size_t buff_size, size_t *offset); #define BELLE_SIP_URI(obj) BELLE_SIP_CAST(obj,belle_sip_uri_t) /**define URI equality as using comparison rules from RFC3261 section 19.1.4 * @param belle_sip_uri_t* uri_a * @param belle_sip_uri_t* uri_a * @return 0 if not matched. * * */ BELLESIP_EXPORT int belle_sip_uri_equals(const belle_sip_uri_t* uri_a,const belle_sip_uri_t* uri_b); /** * returns 0 if uri does follows components requirement for being a request uri * */ BELLESIP_EXPORT int belle_sip_uri_check_components_from_request_uri(const belle_sip_uri_t* uri); /** * returns 0 if uri does follows components requirement for a given method/header */ BELLESIP_EXPORT int belle_sip_uri_check_components_from_context(const belle_sip_uri_t* uri,const char* method,const char* header_name); BELLE_SIP_END_DECLS #ifndef BELLE_SIP_USE_STL #define BELLE_SIP_USE_STL 1 #endif #if __cplusplus >= 201103L && BELLE_SIP_USE_STL #include inline std::ostream& operator<<( std::ostream& __os, const belle_sip_uri_t* uri) { char* uri_as_string = belle_sip_uri_to_string(uri); __os << uri_as_string; belle_sip_free(uri_as_string); return __os; } namespace std { template <> struct hash { size_t operator()(const belle_sip_uri_t *x ) const { hash H; size_t h=0; if (belle_sip_uri_get_user(x)) h = H(belle_sip_uri_get_user(x)); if (belle_sip_uri_get_host(x)) h ^=H(belle_sip_uri_get_host(x)); if (belle_sip_uri_get_port(x)>0) { std::hash H2; h ^=H2(belle_sip_uri_get_port(x)); } if (belle_sip_uri_get_transport_param(x)) { h ^=H(belle_sip_uri_get_transport_param(x)); } if (belle_sip_uri_is_secure(x)) h+=1; return h; } }; } #include namespace bellesip { struct UriComparator : public std::binary_function { bool operator()(const belle_sip_uri_t* lhs, const belle_sip_uri_t* rhs) const { return belle_sip_uri_equals(lhs,rhs); } }; } #endif #endif /*BELLE_SIP_URI_H_*/ belle-sip-1.6.3/include/belle-sip/sipstack.h000066400000000000000000000151711313437522400206770ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_sip_stack_h #define belle_sip_stack_h struct belle_sip_timer_config{ int T1; int T2; int T3; int T4; }; typedef struct belle_sip_timer_config belle_sip_timer_config_t; BELLE_SIP_BEGIN_DECLS BELLESIP_EXPORT belle_sip_stack_t * belle_sip_stack_new(const char *properties); BELLESIP_EXPORT belle_sip_listening_point_t *belle_sip_stack_create_listening_point(belle_sip_stack_t *s, const char *ipaddress, int port, const char *transport); BELLESIP_EXPORT void belle_sip_stack_delete_listening_point(belle_sip_stack_t *s, belle_sip_listening_point_t *lp); BELLESIP_EXPORT belle_sip_provider_t *belle_sip_stack_create_provider(belle_sip_stack_t *s, belle_sip_listening_point_t *lp); BELLESIP_EXPORT belle_http_provider_t * belle_sip_stack_create_http_provider(belle_sip_stack_t *s, const char *bind_ip); BELLESIP_EXPORT belle_sip_main_loop_t* belle_sip_stack_get_main_loop(belle_sip_stack_t *stack); BELLESIP_EXPORT void belle_sip_stack_main(belle_sip_stack_t *stack); BELLESIP_EXPORT void belle_sip_stack_sleep(belle_sip_stack_t *stack, unsigned int milliseconds); /*the transport timeout is typically the maximum time given for making a connection*/ BELLESIP_EXPORT void belle_sip_stack_set_transport_timeout(belle_sip_stack_t *stack, int timeout_ms); BELLESIP_EXPORT int belle_sip_stack_get_transport_timeout(const belle_sip_stack_t *stack); BELLESIP_EXPORT int belle_sip_stack_get_dns_timeout(const belle_sip_stack_t *stack); BELLESIP_EXPORT void belle_sip_stack_set_dns_timeout(belle_sip_stack_t *stack, int timeout); BELLESIP_EXPORT unsigned char belle_sip_stack_dns_srv_enabled(const belle_sip_stack_t *stack); BELLESIP_EXPORT void belle_sip_stack_enable_dns_srv(belle_sip_stack_t *stack, unsigned char enable); BELLESIP_EXPORT unsigned char belle_sip_stack_dns_search_enabled(const belle_sip_stack_t *stack); BELLESIP_EXPORT void belle_sip_stack_enable_dns_search(belle_sip_stack_t *stack, unsigned char enable); /** * Override system's DNS servers used for DNS resolving by app-supplied list of dns servers. * @param stack the stack * @param servers a list of char*. It is copied internally. **/ BELLESIP_EXPORT void belle_sip_stack_set_dns_servers(belle_sip_stack_t *stack, const belle_sip_list_t *servers); /** * Can be used to simulate network transmission delays, for tests. **/ BELLESIP_EXPORT void belle_sip_stack_set_tx_delay(belle_sip_stack_t *stack, int delay_ms); /** * Can be used to simulate network sending error, for tests. * @param stack * @param send_error if <0, will cause channel error to be reported **/ BELLESIP_EXPORT void belle_sip_stack_set_send_error(belle_sip_stack_t *stack, int send_error); /** * Can be used to simulate network transmission delays, for tests. **/ BELLESIP_EXPORT void belle_sip_stack_set_resolver_tx_delay(belle_sip_stack_t *stack, int delay_ms); /** * Can be used to simulate network sending error, for tests. * @param stack * @param send_error if <0, will cause the resolver to fail with this error code. **/ BELLESIP_EXPORT void belle_sip_stack_set_resolver_send_error(belle_sip_stack_t *stack, int send_error); /** * Get the additional DNS hosts file. * @return The path to the additional DNS hosts file. **/ BELLESIP_EXPORT const char * belle_sip_stack_get_dns_user_hosts_file(const belle_sip_stack_t *stack); /** * Can be used to load an additional DNS hosts file for tests. * @param stack * @param hosts_file The path to the additional DNS hosts file to load. **/ BELLESIP_EXPORT void belle_sip_stack_set_dns_user_hosts_file(belle_sip_stack_t *stack, const char *hosts_file); /** * Get the overriding DNS resolv.conf file. * @return The path to the overriding DNS resolv.conf file. **/ BELLESIP_EXPORT const char * belle_sip_stack_get_dns_resolv_conf_file(const belle_sip_stack_t *stack); /** * Can be used to load an overriding DNS resolv.conf file for tests. * @param stack * @param hosts_file The path to the overriding DNS resolv.conf file to load. **/ BELLESIP_EXPORT void belle_sip_stack_set_dns_resolv_conf_file(belle_sip_stack_t *stack, const char *hosts_file); /** * Returns the time interval in seconds after which a connection must be closed when inactive. **/ BELLESIP_EXPORT int belle_sip_stack_get_inactive_transport_timeout(const belle_sip_stack_t *stack); /** * Sets the time interval in seconds after which a connection must be closed when inactive. **/ BELLESIP_EXPORT void belle_sip_stack_set_inactive_transport_timeout(belle_sip_stack_t *stack, int seconds); /** * Set the default dscp value to be used for all SIP sockets created and used in the stack. **/ BELLESIP_EXPORT void belle_sip_stack_set_default_dscp(belle_sip_stack_t *stack, int dscp); /** * Get the default dscp value to be used for all SIP sockets created and used in the stack. **/ BELLESIP_EXPORT int belle_sip_stack_get_default_dscp(belle_sip_stack_t *stack); /** * Returns TRUE if TLS support has been compiled into, FALSE otherwise. **/ BELLESIP_EXPORT int belle_sip_stack_tls_available(belle_sip_stack_t *stack); /** * Returns TRUE if the content encoding support has been compiled in, FALSE otherwise. **/ BELLESIP_EXPORT int belle_sip_stack_content_encoding_available(belle_sip_stack_t *stack, const char *content_encoding); /* * returns timer config for this stack **/ BELLESIP_EXPORT const belle_sip_timer_config_t *belle_sip_stack_get_timer_config(const belle_sip_stack_t *stack); /* * * set sip timer config to be used for this stack **/ BELLESIP_EXPORT void belle_sip_stack_set_timer_config(belle_sip_stack_t *stack, const belle_sip_timer_config_t *timer_config); BELLESIP_EXPORT void belle_sip_stack_set_http_proxy_host(belle_sip_stack_t *stack, const char* proxy_addr); BELLESIP_EXPORT void belle_sip_stack_set_http_proxy_port(belle_sip_stack_t *stack, int port); BELLESIP_EXPORT const char *belle_sip_stack_get_http_proxy_host(const belle_sip_stack_t *stack); BELLESIP_EXPORT int belle_sip_stack_get_http_proxy_port(const belle_sip_stack_t *stack); BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/transaction.h000066400000000000000000000114071313437522400214010ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SIP_TRANSACTION_H #define BELLE_SIP_TRANSACTION_H typedef enum belle_sip_transaction_state{ BELLE_SIP_TRANSACTION_INIT, BELLE_SIP_TRANSACTION_CALLING, BELLE_SIP_TRANSACTION_COMPLETED, BELLE_SIP_TRANSACTION_CONFIRMED, BELLE_SIP_TRANSACTION_ACCEPTED, /* BELLE_SIP_TRANSACTION_INIT, *
BELLE_SIP_TRANSACTION_CALLING, *
BELLE_SIP_TRANSACTION_PROCEEDING, *
BELLE_SIP_TRANSACTION_TRYING, * @param state * @return 0 if not transient * */ BELLESIP_EXPORT int belle_sip_transaction_state_is_transient(const belle_sip_transaction_state_t state); BELLESIP_EXPORT void *belle_sip_transaction_get_application_data(const belle_sip_transaction_t *t); BELLESIP_EXPORT void belle_sip_transaction_set_application_data(belle_sip_transaction_t *t, void *data); BELLESIP_EXPORT const char *belle_sip_transaction_get_branch_id(const belle_sip_transaction_t *t); BELLESIP_EXPORT belle_sip_transaction_state_t belle_sip_transaction_get_state(const belle_sip_transaction_t *t); BELLESIP_EXPORT const char *belle_sip_transaction_get_method(const belle_sip_transaction_t *t); BELLESIP_EXPORT void belle_sip_transaction_terminate(belle_sip_transaction_t *t); BELLESIP_EXPORT belle_sip_request_t *belle_sip_transaction_get_request(const belle_sip_transaction_t *t); BELLESIP_EXPORT belle_sip_response_t *belle_sip_transaction_get_response(const belle_sip_transaction_t *t); BELLESIP_EXPORT belle_sip_dialog_t* belle_sip_transaction_get_dialog(const belle_sip_transaction_t *t); BELLESIP_EXPORT void belle_sip_server_transaction_send_response(belle_sip_server_transaction_t *t, belle_sip_response_t *resp); BELLESIP_EXPORT belle_sip_request_t * belle_sip_client_transaction_create_cancel(belle_sip_client_transaction_t *t); BELLESIP_EXPORT int belle_sip_client_transaction_send_request(belle_sip_client_transaction_t *t); /* * Same as #belle_sip_client_transaction_send_request but with a predefined route. * @param t belle_sip_client_transaction_t * @param outbound_proxy uri use to directly send the request, useful for outbound proxy. * */ BELLESIP_EXPORT int belle_sip_client_transaction_send_request_to(belle_sip_client_transaction_t *t,belle_sip_uri_t* outbound_proxy); BELLESIP_EXPORT belle_sip_uri_t *belle_sip_client_transaction_get_route(belle_sip_client_transaction_t *t); /** * Creates an a sip refresher for transaction like REGISTER/SUBSCRIBE or INVITE which could be refreshed. * Transaction must in be in stated BELLE_SIP_TRANSACTION_COMPLETED. Refresher is created and started. A ref is taken on object transaction * */ BELLESIP_EXPORT belle_sip_refresher_t* belle_sip_client_transaction_create_refresher(belle_sip_client_transaction_t *t); /** * Create an authenticated request based on an existing terminated transaction. *
This function, update cseq, put route set and try to fill authorization headers. Initial request is not cloned. * @param transaction . must be in state completed * @param auth_infos if auth infos cannot be added for an authenticate header, * @param realm optional - If an outbound proxy realm is used, digestion authentication can be optimized. * a newly allocated belle_sip_auth_info_t object is added to this list. These object contains useful information like realm and username. May be NULL * */ BELLESIP_EXPORT belle_sip_request_t* belle_sip_client_transaction_create_authenticated_request(belle_sip_client_transaction_t *t,belle_sip_list_t** auth_infos,const char* realm); #define BELLE_SIP_TRANSACTION(t) BELLE_SIP_CAST(t,belle_sip_transaction_t) #define BELLE_SIP_SERVER_TRANSACTION(t) BELLE_SIP_CAST(t,belle_sip_server_transaction_t) #define BELLE_SIP_CLIENT_TRANSACTION(t) BELLE_SIP_CAST(t,belle_sip_client_transaction_t) BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/types.h000066400000000000000000000206421313437522400202210ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SIP_TYPES_H #define BELLE_SIP_TYPES_H #include "belle-sip/defs.h" #include "belle-sip/object.h" #include "belle-sip/dict.h" #ifndef BELLESIP_DEPRECATED #if defined(_MSC_VER) #define BELLESIP_DEPRECATED __declspec(deprecated) #else #define BELLESIP_DEPRECATED __attribute__ ((deprecated)) #endif #endif /** * This enum declares all object types used in belle-sip (see belle_sip_object_t) **/ BELLE_SIP_DECLARE_TYPES_BEGIN(belle_sip,1) BELLE_SIP_TYPE_ID(belle_sip_stack_t), BELLE_SIP_TYPE_ID(belle_sip_hop_t), BELLE_SIP_TYPE_ID(belle_sip_object_pool_t), BELLE_SIP_TYPE_ID(belle_sip_listening_point_t), BELLE_SIP_TYPE_ID(belle_sip_datagram_listening_point_t), BELLE_SIP_TYPE_ID(belle_sip_udp_listening_point_t), BELLE_SIP_TYPE_ID(belle_sip_stream_listening_point_t), BELLE_SIP_TYPE_ID(belle_sip_tls_listening_point_t), BELLE_SIP_TYPE_ID(belle_sip_tunnel_listening_point_t), BELLE_SIP_TYPE_ID(belle_sip_channel_t), BELLE_SIP_TYPE_ID(belle_sip_udp_channel_t), BELLE_SIP_TYPE_ID(belle_sip_stream_channel_t), BELLE_SIP_TYPE_ID(belle_sip_tls_channel_t), BELLE_SIP_TYPE_ID(belle_sip_tunnel_channel_t), BELLE_SIP_TYPE_ID(belle_sip_provider_t), BELLE_SIP_TYPE_ID(belle_sip_main_loop_t), BELLE_SIP_TYPE_ID(belle_sip_source_t), BELLE_SIP_TYPE_ID(belle_sip_resolver_context_t), BELLE_SIP_TYPE_ID(belle_sip_transaction_t), BELLE_SIP_TYPE_ID(belle_sip_server_transaction_t), BELLE_SIP_TYPE_ID(belle_sip_client_transaction_t), BELLE_SIP_TYPE_ID(belle_sip_ict_t), BELLE_SIP_TYPE_ID(belle_sip_nict_t), BELLE_SIP_TYPE_ID(belle_sip_ist_t), BELLE_SIP_TYPE_ID(belle_sip_nist_t), BELLE_SIP_TYPE_ID(belle_sip_dialog_t), BELLE_SIP_TYPE_ID(belle_sip_header_address_t), BELLE_SIP_TYPE_ID(belle_sip_header_contact_t), BELLE_SIP_TYPE_ID(belle_sip_header_from_t), BELLE_SIP_TYPE_ID(belle_sip_header_to_t), BELLE_SIP_TYPE_ID(belle_sip_header_via_t), BELLE_SIP_TYPE_ID(belle_sip_header_diversion_t), BELLE_SIP_TYPE_ID(belle_sip_uri_t), BELLE_SIP_TYPE_ID(belle_sip_message_t), BELLE_SIP_TYPE_ID(belle_sip_request_t), BELLE_SIP_TYPE_ID(belle_sip_response_t), BELLE_SIP_TYPE_ID(belle_sip_object_t), BELLE_SIP_TYPE_ID(belle_sip_parameters_t), BELLE_SIP_TYPE_ID(belle_sip_header_call_id_t), BELLE_SIP_TYPE_ID(belle_sip_header_cseq_t), BELLE_SIP_TYPE_ID(belle_sip_header_content_type_t), BELLE_SIP_TYPE_ID(belle_sip_header_route_t), BELLE_SIP_TYPE_ID(belle_sip_header_record_route_t), BELLE_SIP_TYPE_ID(belle_sip_header_user_agent_t), BELLE_SIP_TYPE_ID(belle_sip_header_content_length_t), BELLE_SIP_TYPE_ID(belle_sip_header_t), BELLE_SIP_TYPE_ID(belle_sip_header_extension_t), BELLE_SIP_TYPE_ID(belle_sip_header_authorization_t), BELLE_SIP_TYPE_ID(belle_sip_header_proxy_authorization_t), BELLE_SIP_TYPE_ID(belle_sip_header_www_authenticate_t), BELLE_SIP_TYPE_ID(belle_sip_header_proxy_authenticate_t), BELLE_SIP_TYPE_ID(belle_sip_header_max_forwards_t), BELLE_SIP_TYPE_ID(belle_sip_header_expires_t), BELLE_SIP_TYPE_ID(belle_sip_header_allow_t), BELLE_SIP_TYPE_ID(belle_sdp_attribute_t), BELLE_SIP_TYPE_ID(belle_sdp_bandwidth_t), BELLE_SIP_TYPE_ID(belle_sdp_connection_t), BELLE_SIP_TYPE_ID(belle_sdp_email_t), BELLE_SIP_TYPE_ID(belle_sdp_info_t), BELLE_SIP_TYPE_ID(belle_sdp_key_t), BELLE_SIP_TYPE_ID(belle_sdp_media_t), BELLE_SIP_TYPE_ID(belle_sdp_media_description_t), BELLE_SIP_TYPE_ID(belle_sdp_origin_t), BELLE_SIP_TYPE_ID(belle_sdp_phone_t), BELLE_SIP_TYPE_ID(belle_sdp_raw_attribute_t), BELLE_SIP_TYPE_ID(belle_sdp_repeate_time_t), BELLE_SIP_TYPE_ID(belle_sdp_rtcp_fb_attribute_t), BELLE_SIP_TYPE_ID(belle_sdp_rtcp_xr_attribute_t), BELLE_SIP_TYPE_ID(belle_sdp_session_description_t), BELLE_SIP_TYPE_ID(belle_sdp_session_name_t), BELLE_SIP_TYPE_ID(belle_sdp_time_t), BELLE_SIP_TYPE_ID(belle_sdp_time_description_t), BELLE_SIP_TYPE_ID(belle_sdp_uri_t), BELLE_SIP_TYPE_ID(belle_sdp_version_t), BELLE_SIP_TYPE_ID(belle_sdp_base_description_t), BELLE_SIP_TYPE_ID(belle_sdp_mime_parameter_t), BELLE_SIP_TYPE_ID(belle_sip_callbacks_t), BELLE_SIP_TYPE_ID(belle_sip_refresher_t), BELLE_SIP_TYPE_ID(belle_sip_header_subscription_state_t), BELLE_SIP_TYPE_ID(belle_sip_header_service_route_t), BELLE_SIP_TYPE_ID(belle_sip_header_refer_to_t), BELLE_SIP_TYPE_ID(belle_sip_header_referred_by_t), BELLE_SIP_TYPE_ID(belle_sip_header_replaces_t), BELLE_SIP_TYPE_ID(belle_sip_header_date_t), BELLE_SIP_TYPE_ID(belle_sip_header_p_preferred_identity_t), BELLE_SIP_TYPE_ID(belle_sip_header_privacy_t), BELLE_SIP_TYPE_ID(belle_sip_certificates_chain_t), BELLE_SIP_TYPE_ID(belle_sip_signing_key_t), BELLE_SIP_TYPE_ID(belle_sip_dns_srv_t), BELLE_SIP_TYPE_ID(belle_sip_simple_resolver_context_t), BELLE_SIP_TYPE_ID(belle_sip_combined_resolver_context_t), BELLE_SIP_TYPE_ID(belle_sip_dict_t), BELLE_SIP_TYPE_ID(belle_sip_dual_resolver_context_t), BELLE_SIP_TYPE_ID(belle_http_provider_t), BELLE_SIP_TYPE_ID(belle_http_request_t), BELLE_SIP_TYPE_ID(belle_http_response_t), BELLE_SIP_TYPE_ID(belle_http_channel_context_t), BELLE_SIP_TYPE_ID(belle_generic_uri_t), BELLE_SIP_TYPE_ID(belle_http_callbacks_t), BELLE_SIP_TYPE_ID(belle_tls_crypto_config_t), BELLE_SIP_TYPE_ID(belle_http_header_authorization_t), BELLE_SIP_TYPE_ID(belle_sip_body_handler_t), BELLE_SIP_TYPE_ID(belle_sip_memory_body_handler_t), BELLE_SIP_TYPE_ID(belle_sip_user_body_handler_t), BELLE_SIP_TYPE_ID(belle_sip_file_body_handler_t), BELLE_SIP_TYPE_ID(belle_sip_multipart_body_handler_t), BELLE_SIP_TYPE_ID(belle_sip_header_event_t), BELLE_SIP_TYPE_ID(belle_sip_header_supported_t), BELLE_SIP_TYPE_ID(belle_sip_header_content_disposition_t), BELLE_SIP_TYPE_ID(belle_sip_header_accept_t), BELLE_SIP_TYPE_ID(belle_sip_header_reason_t), BELLE_SIP_TYPE_ID(belle_sip_header_authentication_info_t) BELLE_SIP_DECLARE_TYPES_END enum belle_sip_interface_ids{ belle_sip_interface_id_first=1, BELLE_SIP_INTERFACE_ID(belle_sip_channel_listener_t), BELLE_SIP_INTERFACE_ID(belle_sip_listener_t), BELLE_SIP_INTERFACE_ID(belle_http_request_listener_t) }; /*these types are declared here because they are widely used in many headers included after*/ typedef struct belle_sip_listening_point belle_sip_listening_point_t; typedef struct belle_sip_tls_listening_point belle_sip_tls_listening_point_t; typedef struct belle_sip_stack belle_sip_stack_t; typedef struct belle_sip_provider belle_sip_provider_t; typedef struct belle_http_provider belle_http_provider_t; typedef struct belle_sip_dialog belle_sip_dialog_t; typedef struct belle_sip_transaction belle_sip_transaction_t; typedef struct belle_sip_server_transaction belle_sip_server_transaction_t; typedef struct belle_sip_client_transaction belle_sip_client_transaction_t; typedef struct _belle_sip_message belle_sip_message_t; typedef struct _belle_sip_request belle_sip_request_t; typedef struct _belle_sip_response belle_sip_response_t; typedef struct belle_http_request belle_http_request_t; typedef struct belle_http_response belle_http_response_t; typedef struct belle_sip_hop belle_sip_hop_t; typedef struct _belle_generic_uri belle_generic_uri_t; typedef struct _belle_sip_uri belle_sip_uri_t; typedef struct _belle_sip_parameters belle_sip_parameters_t; typedef struct belle_sip_param_pair belle_sip_param_pair_t; typedef struct _belle_sip_header belle_sip_header_t; typedef struct belle_tls_crypto_config belle_tls_crypto_config_t; typedef struct belle_tls_crypto_config belle_tls_verify_policy_t; /* belle_tls_verify_policy_t is deprecated, just for backward compatibility */ typedef struct belle_sip_body_handler belle_sip_body_handler_t; typedef struct belle_sip_memory_body_handler belle_sip_memory_body_handler_t; typedef struct belle_sip_user_body_handler belle_sip_user_body_handler_t; typedef struct belle_sip_file_body_handler belle_sip_file_body_handler_t; typedef struct belle_sip_multipart_body_handler belle_sip_multipart_body_handler_t; #endif belle-sip-1.6.3/include/belle-sip/utils.h000066400000000000000000000116271313437522400202200ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SIP_UTILS_H #define BELLE_SIP_UTILS_H #include #include #include #include #include "belle-sip/defs.h" #ifdef BCTBX_LOG_DOMAIN #undef BCTBX_LOG_DOMAIN #endif #ifndef BELLE_SIP_LOG_DOMAIN #define BELLE_SIP_LOG_DOMAIN "belle-sip" #endif #define BCTBX_LOG_DOMAIN BELLE_SIP_LOG_DOMAIN #include "bctoolbox/logging.h" #include "bctoolbox/vconnect.h" BELLE_SIP_BEGIN_DECLS #define belle_sip_malloc bctbx_malloc #define belle_sip_malloc0 bctbx_malloc0 #define belle_sip_realloc bctbx_realloc #define belle_sip_free bctbx_free #define belle_sip_strdup bctbx_strdup BELLE_SIP_END_DECLS /***************/ /* logging api */ /***************/ #define BELLE_SIP_LOG_FATAL BCTBX_LOG_FATAL #define BELLE_SIP_LOG_ERROR BCTBX_LOG_ERROR #define BELLE_SIP_LOG_WARNING BCTBX_LOG_WARNING #define BELLE_SIP_LOG_MESSAGE BCTBX_LOG_MESSAGE #define BELLE_SIP_LOG_DEBUG BCTBX_LOG_DEBUG #define BELLE_SIP_LOG_END BCTBX_LOG_END #define belle_sip_log_level BctbxLogLevel #define belle_sip_log_function_t BctoolboxLogFunc typedef enum { BELLE_SIP_NOT_IMPLEMENTED = -2, BELLE_SIP_BUFFER_OVERFLOW = -1, BELLE_SIP_OK = 0 } belle_sip_error_code; #ifdef __GNUC__ #define BELLE_SIP_CHECK_FORMAT_ARGS(m,n) __attribute__((format(printf,m,n))) #else #define BELLE_SIP_CHECK_FORMAT_ARGS(m,n) #endif BELLE_SIP_BEGIN_DECLS #define belle_sip_log_level_enabled(level) bctbx_log_level_enabled(BELLE_SIP_LOG_DOMAIN,level) #ifdef BELLE_SIP_DEBUG_MODE #define belle_sip_deb(...) bctbx_debug(...) #else #define belle_sip_debug(...) #endif #ifdef BELLE_SIP_NOMESSAGE_MODE #define belle_sip_log(...) #define belle_sip_message(...) #define belle_sip_warning(...) #else #define belle_sip_log bctbx_log #define belle_sip_message bctbx_message #define belle_sip_warning bctbx_warning #define belle_sip_error bctbx_error #define belle_sip_fatal bctbx_fatal #define belle_sip_logv bctbx_logv #endif #define belle_sip_set_log_file bctbx_set_log_file #define belle_sip_set_log_handler bctbx_set_log_handler #define belle_sip_get_log_handler bctbx_get_log_handler #define belle_sip_strdup_printf bctbx_strdup_printf #define belle_sip_strcat_vprintf bctbx_strcat_vprintf #define belle_sip_strcat_printf bctbx_strcat_printf BELLESIP_EXPORT belle_sip_error_code BELLE_SIP_CHECK_FORMAT_ARGS(4,5) belle_sip_snprintf(char *buff, size_t buff_size, size_t *offset, const char *fmt, ...); BELLESIP_EXPORT belle_sip_error_code belle_sip_snprintf_valist(char *buff, size_t buff_size, size_t *offset, const char *fmt, va_list args); #define belle_sip_set_log_level(level) bctbx_set_log_level(BELLE_SIP_LOG_DOMAIN,level); BELLESIP_EXPORT char * belle_sip_random_token(char *ret, size_t size); BELLESIP_EXPORT unsigned char * belle_sip_random_bytes(unsigned char *ret, size_t size); BELLESIP_EXPORT char * belle_sip_octets_to_text(const unsigned char *hash, size_t hash_len, char *ret, size_t size); BELLESIP_EXPORT char * belle_sip_create_tag(char *ret, size_t size); BELLESIP_EXPORT const char* belle_sip_version_to_string(void); /** * Returns string without surrounding quotes if any, else just call belle_sip_strdup(). **/ BELLESIP_EXPORT char *belle_sip_unquote_strdup(const char *str); BELLESIP_EXPORT uint64_t belle_sip_time_ms(void); BELLESIP_EXPORT unsigned int belle_sip_random(void); /** Connect API */ BELLESIP_EXPORT void belle_sip_set_socket_api(bctbx_vsocket_api_t* my_api); #if defined(_WIN32) #include #include typedef SOCKET belle_sip_socket_t; typedef HANDLE belle_sip_fd_t; #else #include #include #include typedef int belle_sip_socket_t; typedef int belle_sip_fd_t; #endif typedef void (*belle_sip_background_task_end_callback_t)(void *); BELLESIP_EXPORT unsigned long belle_sip_begin_background_task(const char *name, belle_sip_background_task_end_callback_t cb, void *data); BELLESIP_EXPORT void belle_sip_end_background_task(unsigned long id); /** * create a directory if it doesn't already exists * * @param[in] path The directory to be created * @return 0 in case of succes, -1 otherwise, note it returns -1 if the directory already exists */ BELLESIP_EXPORT int belle_sip_mkdir(const char *path); BELLE_SIP_END_DECLS #endif belle-sip-1.6.3/include/belle-sip/wakelock.h000066400000000000000000000012161313437522400206510ustar00rootroot00000000000000#ifndef WAKE_LOCK_H #define WAKE_LOCK_H #include #include "belle-sip/defs.h" BELLE_SIP_BEGIN_DECLS /** * Initialize the Android wake lock system inside Belle-SIP. * This function must be called only once when the program starts. * @param env A JNI environment * @parma pm An android.os.PowerManager java object. */ BELLESIP_EXPORT void belle_sip_wake_lock_init(JNIEnv *env, jobject pm); /** * Uninit the the Android wake lock system. This function may be called * while the program stopping. * @param env A JNI environment. */ BELLESIP_EXPORT void belle_sip_wake_lock_uninit(JNIEnv *env); BELLE_SIP_END_DECLS #endif // WALE_LOCK_H belle-sip-1.6.3/src/000077500000000000000000000000001313437522400141705ustar00rootroot00000000000000belle-sip-1.6.3/src/CMakeLists.txt000066400000000000000000000215401313437522400167320ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ function(require_c99 target) if(CMAKE_C_COMPILER_ID STREQUAL "QCC") target_compile_options(${target} PRIVATE "-std=gnu99") else() if (CMAKE_VERSION VERSION_LESS "3.1") if (CMAKE_C_COMPILER_ID STREQUAL "GNU") target_compile_options(${target} PRIVATE "-std=gnu99") endif() else() set_property(TARGET ${target} PROPERTY C_STANDARD 99) set_property(TARGET ${target} PROPERTY C_STANDARD_REQUIRED TRUE) endif() endif() endfunction() set(INCLUDES ${ANTLR3C_INCLUDE_DIRS}) set(LIBS ${ANTLR3C_LIBRARIES}) list(APPEND INCLUDES ${BCTOOLBOX_CORE_INCLUDE_DIR}) list(APPEND LIBS ${BCTOOLBOX_CORE_LIBRARIES}) if(Threads_FOUND) if(CMAKE_USE_PTHREADS_INIT AND NOT CMAKE_SYSTEM_NAME MATCHES "QNX" AND NOT ANDROID) list(APPEND LIBS ${CMAKE_THREAD_LIBS_INIT}) endif() endif() if(HAVE_LIBDL) list(APPEND LIBS dl) endif() if(HAVE_LIBRT) list(APPEND LIBS rt) endif() if(HAVE_RESINIT) list(APPEND LIBS resolv) endif() if(ZLIB_FOUND) list(APPEND LIBS ${ZLIB_LIBRARIES}) endif() if(WIN32) list(APPEND LIBS ws2_32) endif() set(SDP_GENERATED_SOURCE_FILES_C ${CMAKE_CURRENT_BINARY_DIR}/grammars/belle_sdpLexer.c ${CMAKE_CURRENT_BINARY_DIR}/grammars/belle_sdpLexer.h ${CMAKE_CURRENT_BINARY_DIR}/grammars/belle_sdpParser.c ${CMAKE_CURRENT_BINARY_DIR}/grammars/belle_sdpParser.h ) add_custom_command( OUTPUT ${SDP_GENERATED_SOURCE_FILES_C} COMMAND ${ANTLR3_COMMAND} -make -Xmultithreaded -Xconversiontimeout 10000 -fo ${CMAKE_CURRENT_BINARY_DIR}/grammars ${CMAKE_CURRENT_SOURCE_DIR}/grammars/belle_sdp.g DEPENDS grammars/belle_sdp.g ) set(SIP_MESSAGE_GENERATED_SOURCE_FILES_C ${CMAKE_CURRENT_BINARY_DIR}/grammars/belle_sip_messageLexer.c ${CMAKE_CURRENT_BINARY_DIR}/grammars/belle_sip_messageLexer.h ${CMAKE_CURRENT_BINARY_DIR}/grammars/belle_sip_messageParser.c ${CMAKE_CURRENT_BINARY_DIR}/grammars/belle_sip_messageParser.h ) add_custom_command( OUTPUT ${SIP_MESSAGE_GENERATED_SOURCE_FILES_C} COMMAND ${ANTLR3_COMMAND} -make -Xmultithreaded -Xconversiontimeout 10000 -fo ${CMAKE_CURRENT_BINARY_DIR}/grammars ${CMAKE_CURRENT_SOURCE_DIR}/grammars/belle_sip_message.g DEPENDS grammars/belle_sip_message.g ) set_source_files_properties(${SDP_GENERATED_SOURCE_FILES_C} ${SIP_MESSAGE_GENERATED_SOURCE_FILES_C} PROPERTIES GENERATED TRUE) if(ENABLE_TUNNEL) set(TUNNEL_SOURCE_FILES_C transports/tunnel_listeningpoint.c transports/tunnel_channel.c ) set(TUNNEL_SOURCE_FILES_CXX transports/tunnel_wrapper.cc ) list(APPEND LIBS ${TUNNEL_LIBRARIES}) endif() set(BELLE_SIP_SOURCE_FILES_C auth_event.c auth_helper.c belle_sdp_impl.c belle_sip_dict.c belle_sip_headers_impl.c belle_sip_internal.h belle_sip_loop.c belle_sip_object.c belle_sip_parameters.c belle_sip_resolver.c belle_sip_uri_impl.c belle_sip_utils.c bodyhandler.c channel.c channel.h clock_gettime.c clock_gettime.h dialog.c dns.c dns.h generic-uri.c http-listener.c http-message.c http-provider.c ict.c ist.c listeningpoint.c listeningpoint_internal.h md5.c md5.h message.c nict.c nist.c parserutils.h port.c port.h provider.c refresher.c siplistener.c sipstack.c transaction.c transports/stream_channel.c transports/stream_channel.h transports/stream_listeningpoint.c transports/udp_channel.c transports/udp_listeningpoint.c transports/tls_listeningpoint.c transports/tls_channel.c ${SDP_GENERATED_SOURCE_FILES_C} ${SIP_MESSAGE_GENERATED_SOURCE_FILES_C} ${TUNNEL_SOURCE_FILES_C} ) if(ANDROID) list(APPEND BELLE_SIP_SOURCE_FILES_C wakelock.c) endif() set(BELLE_SIP_SOURCE_FILES_CXX ${TUNNEL_SOURCE_FILES_CXX} ) if(APPLE) set(BELLE_SIP_SOURCE_FILES_OBJC backgroundtask.m ) endif() bc_apply_compile_flags(BELLE_SIP_SOURCE_FILES_C STRICT_OPTIONS_CPP STRICT_OPTIONS_C) bc_apply_compile_flags(BELLE_SIP_SOURCE_FILES_OBJC STRICT_OPTIONS_CPP STRICT_OPTIONS_OBJC) if(BELLE_SIP_SOURCE_FILES_CXX) set_source_files_properties(${BELLE_SIP_SOURCE_FILES_CXX} PROPERTIES LANGUAGE CXX) bc_apply_compile_flags(BELLE_SIP_SOURCE_FILES_CXX STRICT_OPTIONS_CPP STRICT_OPTIONS_CXX) endif() if(MSVC) get_source_file_property(DNS_C_COMPILE_FLAGS dns.c COMPILE_FLAGS) set(DNS_C_COMPILE_FLAGS "${DNS_C_COMPILE_FLAGS} /wd4267") # Disable "possible loss of data" warnings set(DNS_C_COMPILE_FLAGS "${DNS_C_COMPILE_FLAGS} /wd4804") # Disable "unsafe use of type 'bool' in operation" warnings set(DNS_C_COMPILE_FLAGS "${DNS_C_COMPILE_FLAGS} /wd4244") # Disable "conversion from 'intmax_t' to 'int', possible loss of data" warnings set(DNS_C_COMPILE_FLAGS "${DNS_C_COMPILE_FLAGS} /wd4305") # Disable "'initializing': truncation from 'unsigned int' to 'UCHAR'" warnings set(DNS_C_COMPILE_FLAGS "${DNS_C_COMPILE_FLAGS} /wd4018") # Disable "signed/unsigned mismatch" warnings set_source_files_properties(dns.c PROPERTIES COMPILE_FLAGS "${DNS_C_COMPILE_FLAGS}") get_source_file_property(SDP_PARSER_COMPILE_FLAGS grammars/belle_sdpParser.c COMPILE_FLAGS) set(SDP_PARSER_COMPILE_FLAGS "${SDP_PARSER_COMPILE_FLAGS} /wd4267") # Disable "possible loss of data" warnings set_source_files_properties(grammars/belle_sdpParser.c PROPERTIES COMPILE_FLAGS "${SDP_PARSER_COMPILE_FLAGS}") get_source_file_property(SIP_MESSAGE_PARSER_COMPILE_FLAGS grammars/belle_sip_messageParser.c COMPILE_FLAGS) set(SIP_MESSAGE_PARSER_COMPILE_FLAGS "${SIP_MESSAGE_PARSER_COMPILE_FLAGS} /wd4267") # Disable "possible loss of data" warnings set_source_files_properties(grammars/belle_sip_messageParser.c PROPERTIES COMPILE_FLAGS "${SIP_MESSAGE_PARSER_COMPILE_FLAGS}") else() get_source_file_property(DNS_C_COMPILE_FLAGS dns.c COMPILE_FLAGS) set(DNS_C_COMPILE_FLAGS "${DNS_C_COMPILE_FLAGS} -Wno-error") set_source_files_properties(dns.c PROPERTIES COMPILE_FLAGS "${DNS_C_COMPILE_FLAGS}") get_source_file_property(SIP_MESSAGE_PARSER_COMPILE_FLAGS grammars/belle_sip_messageParser.c COMPILE_FLAGS) set(SIP_MESSAGE_PARSER_COMPILE_FLAGS "${SIP_MESSAGE_PARSER_COMPILE_FLAGS} -Wno-sign-compare") set_source_files_properties(grammars/belle_sip_messageParser.c PROPERTIES COMPILE_FLAGS "${SIP_MESSAGE_PARSER_COMPILE_FLAGS}") endif() string(REPLACE ";" " " LINK_FLAGS_STR "${LINK_FLAGS}") if(ENABLE_STATIC) add_library(bellesip-static STATIC ${BELLE_SIP_HEADER_FILES} ${BELLE_SIP_SOURCE_FILES_C} ${BELLE_SIP_SOURCE_FILES_CXX} ${BELLE_SIP_SOURCE_FILES_OBJC}) set_target_properties(bellesip-static PROPERTIES OUTPUT_NAME bellesip) target_include_directories(bellesip-static PUBLIC ${INCLUDES}) target_link_libraries(bellesip-static INTERFACE ${LIBS}) if(NOT "${LINK_FLAGS_STR}" STREQUAL "") set_target_properties(bellesip-static PROPERTIES LINK_FLAGS "${LINK_FLAGS_STR}") endif() require_c99(bellesip-static) install(TARGETS bellesip-static EXPORT ${EXPORT_TARGETS_NAME}Targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() if(ENABLE_SHARED) add_library(bellesip SHARED ${BELLE_SIP_HEADER_FILES} ${BELLE_SIP_SOURCE_FILES_C} ${BELLE_SIP_SOURCE_FILES_CXX} ${BELLE_SIP_SOURCE_FILES_OBJC}) set_target_properties(bellesip PROPERTIES SOVERSION 0) set_target_properties(bellesip PROPERTIES LINKER_LANGUAGE CXX) target_include_directories(bellesip PUBLIC ${INCLUDES}) target_link_libraries(bellesip PRIVATE ${LIBS}) if(NOT "${LINK_FLAGS_STR}" STREQUAL "") set_target_properties(bellesip PROPERTIES LINK_FLAGS "${LINK_FLAGS_STR}") endif() require_c99(bellesip) if(MSVC) if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/bellesip.pdb DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() endif() install(TARGETS bellesip EXPORT ${EXPORT_TARGETS_NAME}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() belle-sip-1.6.3/src/Makefile.am000077500000000000000000000036221313437522400162320ustar00rootroot00000000000000SUBDIRS=grammars AUTOMAKE_OPTIONS = subdir-objects lib_LTLIBRARIES=libbellesip.la libbellesip_la_SOURCES= \ clock_gettime.c clock_gettime.h \ port.c port.h \ parserutils.h \ belle_sip_uri_impl.c \ belle_sip_headers_impl.c \ belle_sip_utils.c belle_sip_internal.h \ belle_sip_object.c \ belle_sip_loop.c \ belle_sip_resolver.c \ belle_sip_parameters.c \ belle_sdp_impl.c \ transaction.c \ listeningpoint.c listeningpoint_internal.h \ sipstack.c \ provider.c \ channel.c channel.h \ message.c \ md5.c md5.h \ auth_helper.c \ siplistener.c \ ict.c \ ist.c \ nict.c \ nist.c \ dialog.c \ auth_event.c \ transports/udp_listeningpoint.c \ transports/udp_channel.c \ transports/stream_channel.c \ transports/stream_channel.h \ transports/stream_listeningpoint.c \ transports/tls_listeningpoint.c \ transports/tls_channel.c \ refresher.c \ dns.c dns.h \ belle_sip_dict.c \ generic-uri.c \ http-provider.c \ http-message.c \ http-listener.c \ bodyhandler.c if BUILD_APPLE libbellesip_la_SOURCES+=backgroundtask.m endif if BUILD_TUNNEL libbellesip_la_SOURCES+=transports/tunnel_listeningpoint.c \ transports/tunnel_channel.c \ transports/tunnel_wrapper.cc endif libbellesip_la_CFLAGS=$(STRICT_OPTIONS) $(STRICT_OPTIONS_CC) $(ANTLR_CFLAGS) $(TLS_CFLAGS) $(TUNNEL_CFLAGS) $(LIBBELLESIP_CFLAGS) libbellesip_la_CXXFLAGS=$(STRICT_OPTIONS) $(STRICT_OPTIONS_CXX) $(TLS_CFLAGS) $(TUNNEL_CFLAGS) $(LIBBELLESIP_CFLAGS) libbellesip_la_LIBADD=grammars/libbellesip_generated.la $(ANTLR_LIBS) $(TLS_LIBS) $(TUNNEL_LIBS) $(ZLIB_LIBS) libbellesip_la_LDFLAGS=-no-undefined -version-info $(BELLESIP_SO_VERSION) AM_CPPFLAGS=-I$(top_srcdir)/include -I$(builddir)/grammars discovery: touch specs.c $(CC) $(CFLAGS) -include $(top_builddir)/config.h $(ANTLR_CFLAGS) $(BCTOOLBOXTESTER_CFLAGS) $(TLS_CFLAGS) -E -P -v -dD specs.c belle-sip-1.6.3/src/auth_event.c000066400000000000000000000130451313437522400165010ustar00rootroot00000000000000/* auth_info.c belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/auth-helper.h" #include "belle_sip_internal.h" GET_SET_STRING(belle_sip_auth_event,username) GET_SET_STRING(belle_sip_auth_event,userid) GET_SET_STRING(belle_sip_auth_event,realm) GET_SET_STRING(belle_sip_auth_event,domain) GET_SET_STRING(belle_sip_auth_event,passwd) GET_SET_STRING(belle_sip_auth_event,ha1) GET_SET_STRING(belle_sip_auth_event,distinguished_name) belle_sip_auth_event_t* belle_sip_auth_event_create(belle_sip_object_t *source, const char* realm, const belle_sip_uri_t *from_uri) { belle_sip_auth_event_t* result = belle_sip_new0(belle_sip_auth_event_t); result->source=source; belle_sip_auth_event_set_realm(result,realm); if (from_uri){ belle_sip_auth_event_set_username(result,belle_sip_uri_get_user(from_uri)); belle_sip_auth_event_set_domain(result,belle_sip_uri_get_host(from_uri)); } return result; } void belle_sip_auth_event_destroy(belle_sip_auth_event_t* event) { DESTROY_STRING(event,username); DESTROY_STRING(event,userid); DESTROY_STRING(event,realm); DESTROY_STRING(event,domain); DESTROY_STRING(event,passwd); DESTROY_STRING(event,ha1); DESTROY_STRING(event,distinguished_name); if (event->cert) belle_sip_object_unref(event->cert); if (event->key) belle_sip_object_unref(event->key); belle_sip_free(event); } belle_sip_certificates_chain_t* belle_sip_auth_event_get_client_certificates_chain(const belle_sip_auth_event_t* event) { return event->cert; } void belle_sip_auth_event_set_client_certificates_chain(belle_sip_auth_event_t* event, belle_sip_certificates_chain_t* value) { if (event->cert) belle_sip_object_unref(event->cert); event->cert=value; if (event->cert) belle_sip_object_ref(event->cert); } belle_sip_signing_key_t* belle_sip_auth_event_get_signing_key(const belle_sip_auth_event_t* event) { return event->key; } void belle_sip_auth_event_set_signing_key(belle_sip_auth_event_t* event, belle_sip_signing_key_t* value) { SET_OBJECT_PROPERTY(event,key,value); } belle_sip_auth_mode_t belle_sip_auth_event_get_mode(const belle_sip_auth_event_t* event) { return event->mode; } /* deprecated on 2016/02/02 */ belle_tls_verify_policy_t *belle_tls_verify_policy_new(){ return (belle_tls_verify_policy_t *)belle_tls_crypto_config_new(); } int belle_tls_verify_policy_set_root_ca(belle_tls_verify_policy_t *obj, const char *path) { return belle_tls_crypto_config_set_root_ca(obj, path); } void belle_tls_verify_policy_set_exceptions(belle_tls_verify_policy_t *obj, int flags){ belle_tls_crypto_config_set_verify_exceptions(obj, flags); } unsigned int belle_tls_verify_policy_get_exceptions(const belle_tls_verify_policy_t *obj){ return belle_tls_crypto_config_get_verify_exceptions(obj); } /* end of deprecated on 2016/02/02 */ static void crypto_config_uninit(belle_tls_crypto_config_t *obj) { if (obj->root_ca) belle_sip_free(obj->root_ca); if (obj->root_ca_data) belle_sip_free(obj->root_ca_data); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_tls_crypto_config_t); BELLE_SIP_INSTANCIATE_VPTR(belle_tls_crypto_config_t,belle_sip_object_t,crypto_config_uninit,NULL,NULL,FALSE); belle_tls_crypto_config_t *belle_tls_crypto_config_new(void){ belle_tls_crypto_config_t *obj=belle_sip_object_new(belle_tls_crypto_config_t); /*default to "system" default root ca, wihtout warranty...*/ #ifdef __linux belle_tls_crypto_config_set_root_ca(obj,"/etc/ssl/certs"); #elif defined(__APPLE__) belle_tls_crypto_config_set_root_ca(obj,"/opt/local/share/curl/curl-ca-bundle.crt"); #elif __QNX__ belle_tls_crypto_config_set_root_ca(obj,"/var/certs/web_trusted@personal@certmgr"); #endif obj->ssl_config = NULL; obj->exception_flags = BELLE_TLS_VERIFY_NONE; return obj; } int belle_tls_crypto_config_set_root_ca(belle_tls_crypto_config_t *obj, const char *path){ if (obj->root_ca) { belle_sip_free(obj->root_ca); obj->root_ca = NULL; } if (path) { obj->root_ca = belle_sip_strdup(path); belle_sip_message("Root ca path set to %s", obj->root_ca); } else { belle_sip_message("Root ca path disabled"); } return 0; } int belle_tls_crypto_config_set_root_ca_data(belle_tls_crypto_config_t *obj, const char *data) { if (obj->root_ca) { belle_sip_free(obj->root_ca); obj->root_ca = NULL; } if (obj->root_ca_data) { belle_sip_free(obj->root_ca_data); obj->root_ca_data = NULL; } if (data) { obj->root_ca_data = belle_sip_strdup(data); belle_sip_message("Root ca data set to %s", obj->root_ca_data); } else { belle_sip_message("Root ca data disabled"); } return 0; } void belle_tls_crypto_config_set_verify_exceptions(belle_tls_crypto_config_t *obj, int flags){ obj->exception_flags=flags; } unsigned int belle_tls_crypto_config_get_verify_exceptions(const belle_tls_crypto_config_t *obj){ return obj->exception_flags; } void belle_tls_crypto_config_set_ssl_config(belle_tls_crypto_config_t *obj, void *ssl_config) { obj->ssl_config = ssl_config; } belle-sip-1.6.3/src/auth_helper.c000066400000000000000000000255731313437522400166500ustar00rootroot00000000000000/* auth_helper.c belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/auth-helper.h" #include "belle_sip_internal.h" #include "md5.h" #include #ifndef BELLE_SIP_CNONCE_LENGTH #define BELLE_SIP_CNONCE_LENGTH 16 #endif #define CHECK_IS_PRESENT(obj,header_name,name) \ if (!belle_sip_header_##header_name##_get_##name(obj)) {\ belle_sip_error("parameter ["#name"]not found for header ["#header_name"]");\ return-1;\ } static void belle_sip_auth_helper_clone_authorization(belle_sip_header_authorization_t* authorization, const belle_sip_header_www_authenticate_t* authentication) { CLONE_STRING_GENERIC(belle_sip_header_www_authenticate,belle_sip_header_authorization,scheme,authorization,authentication) CLONE_STRING_GENERIC(belle_sip_header_www_authenticate,belle_sip_header_authorization,realm,authorization,authentication) CLONE_STRING_GENERIC(belle_sip_header_www_authenticate,belle_sip_header_authorization,nonce,authorization,authentication) CLONE_STRING_GENERIC(belle_sip_header_www_authenticate,belle_sip_header_authorization,algorithm,authorization,authentication) CLONE_STRING_GENERIC(belle_sip_header_www_authenticate,belle_sip_header_authorization,opaque,authorization,authentication) } static void belle_sip_auth_helper_clone_www_authenticate(belle_sip_header_www_authenticate_t* authentication, const belle_sip_header_authorization_t* authorization) { CLONE_STRING_GENERIC(belle_sip_header_authorization,belle_sip_header_www_authenticate,scheme, authentication, authorization) CLONE_STRING_GENERIC(belle_sip_header_authorization,belle_sip_header_www_authenticate,realm, authentication, authorization) CLONE_STRING_GENERIC(belle_sip_header_authorization,belle_sip_header_www_authenticate,nonce, authentication, authorization) CLONE_STRING_GENERIC(belle_sip_header_authorization,belle_sip_header_www_authenticate,algorithm,authentication ,authorization) CLONE_STRING_GENERIC(belle_sip_header_authorization,belle_sip_header_www_authenticate,opaque,authentication, authorization) } belle_sip_header_authorization_t* belle_sip_auth_helper_create_authorization(const belle_sip_header_www_authenticate_t* authentication) { belle_sip_header_authorization_t* authorization = belle_sip_header_authorization_new(); belle_sip_auth_helper_clone_authorization(authorization,authentication); return authorization; } belle_sip_header_www_authenticate_t* belle_sip_auth_helper_create_www_authenticate(const belle_sip_header_authorization_t* authorization) { belle_sip_header_www_authenticate_t* www_authenticate = belle_sip_header_www_authenticate_new(); belle_sip_auth_helper_clone_www_authenticate(www_authenticate, authorization); return www_authenticate; } belle_http_header_authorization_t* belle_http_auth_helper_create_authorization(const belle_sip_header_www_authenticate_t* authentication) { belle_http_header_authorization_t* authorization = belle_http_header_authorization_new(); belle_sip_auth_helper_clone_authorization(BELLE_SIP_HEADER_AUTHORIZATION(authorization),authentication); return authorization; } belle_sip_header_proxy_authorization_t* belle_sip_auth_helper_create_proxy_authorization(const belle_sip_header_proxy_authenticate_t* proxy_authentication){ belle_sip_header_proxy_authorization_t* authorization = belle_sip_header_proxy_authorization_new(); belle_sip_auth_helper_clone_authorization(BELLE_SIP_HEADER_AUTHORIZATION(authorization),BELLE_SIP_HEADER_WWW_AUTHENTICATE(proxy_authentication)); return authorization; } int belle_sip_auth_helper_compute_ha1(const char* userid,const char* realm,const char* password, char ha1[33]) { md5_byte_t out[16]; md5_state_t state; int di; if (!userid) { belle_sip_error("belle_sip_fill_authorization_header, username not found "); return -1; } if (!password) { belle_sip_error("belle_sip_fill_authorization_header, password not found "); return -1; } if (!realm) { belle_sip_error("belle_sip_fill_authorization_header, realm not found "); return -1; } belle_sip_md5_init(&state); belle_sip_md5_append(&state,(const md5_byte_t *)userid,(int)strlen(userid)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)realm,(int)strlen(realm)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)password,(int)strlen(password)); belle_sip_md5_finish(&state,out); for (di = 0; di < 16; ++di) sprintf(ha1 + di * 2, "%02x", out[di]); ha1[32]='\0'; return 0; } int belle_sip_auth_helper_compute_ha2(const char* method,const char* uri, char ha2[33]) { md5_byte_t out[16]; md5_state_t state; int di; ha2[32]='\0'; /*HA2=MD5(method:uri)*/ belle_sip_md5_init(&state); belle_sip_md5_append(&state,(const md5_byte_t *)method,(int)strlen(method)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)uri,(int)strlen(uri)); belle_sip_md5_finish(&state,out); for (di = 0; di < 16; ++di) sprintf(ha2 + di * 2, "%02x", out[di]); return 0; } int belle_sip_auth_helper_compute_response(const char* ha1,const char* nonce, const char* ha2, char response[33]) { md5_byte_t out[16]; md5_state_t state; int di; response[32]='\0'; belle_sip_md5_init(&state); belle_sip_md5_append(&state,(const md5_byte_t *)ha1,(int)strlen(ha1)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)nonce,(int)strlen(nonce)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)ha2,(int)strlen(ha2)); belle_sip_md5_finish(&state,out); /*copy values*/ for (di = 0; di < 16; ++di) sprintf(response + di * 2, "%02x", out[di]); return 0; } int belle_sip_auth_helper_compute_response_qop_auth(const char* ha1 , const char* nonce , unsigned int nonce_count , const char* cnonce , const char* qop , const char* ha2, char response[33]) { md5_byte_t out[16]; md5_state_t state; char nounce_count_as_string[9]; int di; response[32]='\0'; snprintf(nounce_count_as_string,sizeof(nounce_count_as_string),"%08x",nonce_count); /*response=MD5(HA1:nonce:nonce_count:cnonce:qop:HA2)*/ belle_sip_md5_init(&state); belle_sip_md5_append(&state,(const md5_byte_t *)ha1,(int)strlen(ha1)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)nonce,(int)strlen(nonce)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)nounce_count_as_string,(int)strlen(nounce_count_as_string)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)cnonce,(int)strlen(cnonce)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)qop,(int)strlen(qop)); belle_sip_md5_append(&state,(const md5_byte_t *)":",1); belle_sip_md5_append(&state,(const md5_byte_t *)ha2,(int)strlen(ha2)); belle_sip_md5_finish(&state,out); /*copy values*/ for (di = 0; di < 16; ++di) sprintf(response + di * 2, "%02x", out[di]); return 0; } int belle_sip_auth_helper_fill_authorization(belle_sip_header_authorization_t* authorization ,const char* method ,const char* ha1) { int auth_mode=0; char* uri; char ha2[16*2 + 1]; char response[16*2 + 1]; char cnonce[BELLE_SIP_CNONCE_LENGTH + 1]; response[32]=ha2[32]='\0'; if (belle_sip_header_authorization_get_scheme(authorization) != NULL && strcmp("Digest",belle_sip_header_authorization_get_scheme(authorization))!=0) { belle_sip_error("belle_sip_fill_authorization_header, unsupported schema [%s]" ,belle_sip_header_authorization_get_scheme(authorization)); return -1; } if (belle_sip_header_authorization_get_qop(authorization) && !(auth_mode=strcmp("auth",belle_sip_header_authorization_get_qop(authorization))==0)) { belle_sip_error("belle_sip_fill_authorization_header, unsupported qop [%s], use auth or nothing instead" ,belle_sip_header_authorization_get_qop(authorization)); return -1; } CHECK_IS_PRESENT(authorization,authorization,realm) CHECK_IS_PRESENT(authorization,authorization,nonce) if (BELLE_SIP_IS_INSTANCE_OF(authorization,belle_http_header_authorization_t)) { /*http case*/ if (!belle_http_header_authorization_get_uri(BELLE_HTTP_HEADER_AUTHORIZATION(authorization))) { belle_sip_error("parameter uri not found for http header authorization"); return-1; } } else { CHECK_IS_PRESENT(authorization,authorization,uri) } if (auth_mode) { CHECK_IS_PRESENT(authorization,authorization,nonce_count) if (!belle_sip_header_authorization_get_cnonce(authorization)) { belle_sip_header_authorization_set_cnonce(authorization, belle_sip_random_token((cnonce), sizeof(cnonce))); } } if (!method) { belle_sip_error("belle_sip_fill_authorization_header, method not found "); return -1; } if (BELLE_SIP_IS_INSTANCE_OF(authorization,belle_http_header_authorization_t)) { /*http case*/ uri=belle_generic_uri_to_string(belle_http_header_authorization_get_uri(BELLE_HTTP_HEADER_AUTHORIZATION(authorization))); } else { uri=belle_sip_uri_to_string(belle_sip_header_authorization_get_uri(authorization)); } belle_sip_auth_helper_compute_ha2(method,uri,ha2); belle_sip_free(uri); if (auth_mode) { /*response=MD5(HA1:nonce:nonce_count:cnonce:qop:HA2)*/ belle_sip_auth_helper_compute_response_qop_auth(ha1 ,belle_sip_header_authorization_get_nonce(authorization) ,belle_sip_header_authorization_get_nonce_count(authorization) ,belle_sip_header_authorization_get_cnonce(authorization) ,belle_sip_header_authorization_get_qop(authorization) ,ha2 ,response); } else { /*response=MD5(ha1:nonce:ha2)*/ belle_sip_auth_helper_compute_response(ha1,belle_sip_header_authorization_get_nonce(authorization),ha2,response); } belle_sip_header_authorization_set_response(authorization,(const char*)response); return 0; } int belle_sip_auth_helper_fill_proxy_authorization(belle_sip_header_proxy_authorization_t* proxy_authorization ,const char* method ,const char* ha1) { return belle_sip_auth_helper_fill_authorization(BELLE_SIP_HEADER_AUTHORIZATION(proxy_authorization) ,method, ha1); } belle-sip-1.6.3/src/backgroundtask.m000066400000000000000000000070371313437522400173570ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #if TARGET_OS_IPHONE==1 #include typedef struct fallback_callback_data{ unsigned long task_id; }fallback_callback_data_t; static void fallback_callback(void *data){ fallback_callback_data_t *fbd=(fallback_callback_data_t*)data; belle_sip_end_background_task(fbd->task_id); belle_sip_free(fbd); } unsigned long belle_sip_begin_background_task(const char *name, belle_sip_background_task_end_callback_t cb, void *data){ UIApplication *app=[UIApplication sharedApplication]; UIBackgroundTaskIdentifier bgid = UIBackgroundTaskInvalid; fallback_callback_data_t *fbd=NULL; if (cb==NULL){ cb=fallback_callback; data=fbd=(fallback_callback_data_t*)belle_sip_malloc0(sizeof(fallback_callback_data_t)); } void (^handler)() = ^{ cb(data); }; if([app respondsToSelector:@selector(beginBackgroundTaskWithName:expirationHandler:)]){ bgid = [app beginBackgroundTaskWithName:[NSString stringWithUTF8String:name] expirationHandler:handler]; } else { bgid = [app beginBackgroundTaskWithExpirationHandler:handler]; } if (bgid==UIBackgroundTaskInvalid){ belle_sip_error("Could not start background task %s.", name); return 0; } // backgroundTimeRemaining is properly set only when running background... but not immediately! if (app.applicationState != UIApplicationStateBackground || (app.backgroundTimeRemaining == DBL_MAX)) { belle_sip_message("Background task %s started. Unknown remaining time since application is not fully in background.", name); } else { belle_sip_message("Background task %s started. Remaining time %.1f secs", name, app.backgroundTimeRemaining); } if (fbd) fbd->task_id=bgid; return (unsigned long)bgid; } void belle_sip_end_background_task(unsigned long id){ UIApplication *app=[UIApplication sharedApplication]; [app endBackgroundTask:(UIBackgroundTaskIdentifier)id]; } #else /*mac*/ @import Foundation; static unsigned long dummy_id=0; static id activity_id=0; static int activity_refcnt=0; unsigned long belle_sip_begin_background_task(const char *name, belle_sip_background_task_end_callback_t cb, void *data){ activity_refcnt++; if (activity_refcnt==1){ NSProcessInfo *pinfo=[NSProcessInfo processInfo]; if (pinfo && [pinfo respondsToSelector:@selector(beginActivityWithOptions:reason:)]){ activity_id=[pinfo beginActivityWithOptions:NSActivityUserInitiatedAllowingIdleSystemSleep reason:@"Processing SIP signaling"]; [activity_id retain]; belle_sip_message("Activity started"); } } return ++dummy_id; } void belle_sip_end_background_task(unsigned long id){ activity_refcnt--; if (activity_refcnt==0){ if (activity_id!=0){ NSProcessInfo *pinfo=[NSProcessInfo processInfo]; [pinfo endActivity:activity_id]; [activity_id release]; belle_sip_message("Activity ended"); activity_id=0; } } } #endif belle-sip-1.6.3/src/belle_sdp_impl.c000066400000000000000000002130661313437522400173160ustar00rootroot00000000000000/* belle-sdp - SIP (RFC4566) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "grammars/belle_sdpParser.h" #include "grammars/belle_sdpLexer.h" #include "belle_sip_internal.h" struct _belle_sdp_mime_parameter { belle_sip_object_t base; int rate; int channel_count; int ptime; int max_ptime; int media_format; const char* type; const char* parameters; }; static void belle_sip_object_freefunc(void* obj) { belle_sip_object_unref(BELLE_SIP_OBJECT(obj)); } static void* belle_sip_object_copyfunc(void* obj) { return belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(obj)); } static void * belle_sip_string_copyfunc(void *obj) { return (void *)belle_sip_strdup((const char *)obj); } /*************************************************************************************** * Attribute * **************************************************************************************/ typedef belle_sdp_attribute_t* (*attribute_parse_func)(const char*) ; struct attribute_name_func_pair { const char* name; attribute_parse_func func; }; static struct attribute_name_func_pair attribute_table[] = { { "rtcp-fb", (attribute_parse_func)belle_sdp_rtcp_fb_attribute_parse }, { "rtcp-xr", (attribute_parse_func)belle_sdp_rtcp_xr_attribute_parse } }; struct _belle_sdp_attribute { belle_sip_object_t base; const char* name; char *unparsed_value; }; void belle_sdp_attribute_destroy(belle_sdp_attribute_t* attribute) { DESTROY_STRING(attribute,name) DESTROY_STRING(attribute,unparsed_value) } void belle_sdp_attribute_clone(belle_sdp_attribute_t *attribute, const belle_sdp_attribute_t *orig){ CLONE_STRING(belle_sdp_attribute,name,attribute,orig) } belle_sip_error_code belle_sdp_attribute_marshal(belle_sdp_attribute_t* attribute, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff, buff_size, offset, "a=%s", attribute->name); } belle_sdp_attribute_t* belle_sdp_attribute_create(const char* name, const char* value) { belle_sdp_attribute_t* ret; size_t i; size_t elements = sizeof(attribute_table) / sizeof(attribute_table[0]); if (!name || name[0] == '\0') { belle_sip_error("Cannot create SDP attribute without name"); return NULL; } for (i = 0; i < elements; i++) { if (strcasecmp(attribute_table[i].name, name) == 0) { char* raw; if (value) raw = belle_sip_strdup_printf("a=%s:%s", name, value); else raw = belle_sip_strdup_printf("a=%s", name); ret = attribute_table[i].func(raw); belle_sip_free(raw); return ret; } } /* Not a specialized SDP attribute */ return BELLE_SDP_ATTRIBUTE(belle_sdp_raw_attribute_create(name, value)); } const char *belle_sdp_attribute_get_value(belle_sdp_attribute_t *attribute) { char *ret; char *end; if (attribute->unparsed_value) { belle_sip_free(attribute->unparsed_value); attribute->unparsed_value = NULL; } attribute->unparsed_value = belle_sip_object_to_string(attribute); ret = attribute->unparsed_value; ret += strlen(attribute->name) + 2; /* "a=" + name*/ if (*ret==':') ret++; for (; *ret == ' '; ret++) {}; /* skip eventual spaces */ return ret; } unsigned int belle_sdp_attribute_has_value(belle_sdp_attribute_t* attribute) { return belle_sdp_attribute_get_value(attribute) != NULL; } BELLE_SDP_NEW(attribute,belle_sip_object) BELLE_SDP_PARSE(attribute) GET_SET_STRING(belle_sdp_attribute,name); /*************************************************************************************** * RAW Attribute * **************************************************************************************/ struct _belle_sdp_raw_attribute { belle_sdp_attribute_t base; const char* value; }; void belle_sdp_raw_attribute_destroy(belle_sdp_raw_attribute_t* attribute) { DESTROY_STRING(attribute,value) } void belle_sdp_raw_attribute_clone(belle_sdp_raw_attribute_t* attribute, const belle_sdp_raw_attribute_t* orig) { if (belle_sdp_attribute_get_value(BELLE_SDP_ATTRIBUTE(orig))) { belle_sdp_raw_attribute_set_value(attribute, belle_sdp_attribute_get_value(BELLE_SDP_ATTRIBUTE(orig))); } } belle_sip_error_code belle_sdp_raw_attribute_marshal(belle_sdp_raw_attribute_t* attribute, char* buff, size_t buff_size, size_t* offset) { belle_sip_error_code error = belle_sdp_attribute_marshal(BELLE_SDP_ATTRIBUTE(attribute), buff, buff_size, offset); if (error != BELLE_SIP_OK) return error; if (attribute->value) { error = belle_sip_snprintf(buff, buff_size, offset, ":%s", attribute->value); if (error != BELLE_SIP_OK) return error; } return error; } BELLE_SDP_NEW(raw_attribute,belle_sdp_attribute) belle_sdp_raw_attribute_t* belle_sdp_raw_attribute_create(const char* name, const char* value) { belle_sdp_raw_attribute_t* attribute = belle_sdp_raw_attribute_new(); belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), name); belle_sdp_raw_attribute_set_value(attribute, value); return attribute; } void belle_sdp_raw_attribute_set_value(belle_sdp_raw_attribute_t* attribute, const char* value) { if (attribute->value != NULL) belle_sip_free((void*)attribute->value); if (value) { attribute->value = belle_sip_strdup(value); } else attribute->value = NULL; } /*************************************************************************************** * RTCP-FB Attribute * **************************************************************************************/ struct _belle_sdp_rtcp_fb_attribute { belle_sdp_attribute_t base; belle_sdp_rtcp_fb_val_type_t type; belle_sdp_rtcp_fb_val_param_t param; uint32_t smaxpr; uint16_t trr_int; int8_t id; }; BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_pli(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_pli(belle_sdp_rtcp_fb_attribute_t* attribute, unsigned int enable); BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_sli(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_sli(belle_sdp_rtcp_fb_attribute_t* attribute, unsigned int enable); BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_rpsi(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_rpsi(belle_sdp_rtcp_fb_attribute_t* attribute, unsigned int enable); BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_app(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_app(belle_sdp_rtcp_fb_attribute_t* attribute, unsigned int enable); void belle_sdp_rtcp_fb_attribute_destroy(belle_sdp_rtcp_fb_attribute_t* attribute) { } void belle_sdp_rtcp_fb_attribute_clone(belle_sdp_rtcp_fb_attribute_t* attribute, const belle_sdp_rtcp_fb_attribute_t *orig) { attribute->type = orig->type; attribute->param = orig->param; attribute->trr_int = orig->trr_int; attribute->id = orig->id; attribute->smaxpr = orig->smaxpr; } belle_sip_error_code belle_sdp_rtcp_fb_attribute_marshal(belle_sdp_rtcp_fb_attribute_t* attribute, char * buff, size_t buff_size, size_t *offset) { int8_t id = belle_sdp_rtcp_fb_attribute_get_id(attribute); belle_sdp_rtcp_fb_val_type_t type = belle_sdp_rtcp_fb_attribute_get_type(attribute); belle_sdp_rtcp_fb_val_param_t param = belle_sdp_rtcp_fb_attribute_get_param(attribute); belle_sip_error_code error = belle_sdp_attribute_marshal(BELLE_SDP_ATTRIBUTE(attribute), buff, buff_size, offset); if (error != BELLE_SIP_OK) return error; if (id < 0) { error = belle_sip_snprintf(buff, buff_size, offset, ":* "); } else { error = belle_sip_snprintf(buff, buff_size, offset, ":%u ", id); } if (error != BELLE_SIP_OK) return error; switch (type) { case BELLE_SDP_RTCP_FB_ACK: error = belle_sip_snprintf(buff, buff_size, offset, "ack"); if (error != BELLE_SIP_OK) return error; switch (param) { default: case BELLE_SDP_RTCP_FB_NONE: break; case BELLE_SDP_RTCP_FB_RPSI: error = belle_sip_snprintf(buff, buff_size, offset, " rpsi"); break; case BELLE_SDP_RTCP_FB_APP: error = belle_sip_snprintf(buff, buff_size, offset, " app"); break; } break; case BELLE_SDP_RTCP_FB_NACK: error = belle_sip_snprintf(buff, buff_size, offset, "nack"); if (error != BELLE_SIP_OK) return error; switch (param) { default: case BELLE_SDP_RTCP_FB_NONE: break; case BELLE_SDP_RTCP_FB_PLI: error = belle_sip_snprintf(buff, buff_size, offset, " pli"); break; case BELLE_SDP_RTCP_FB_SLI: error = belle_sip_snprintf(buff, buff_size, offset, " sli"); break; case BELLE_SDP_RTCP_FB_RPSI: error = belle_sip_snprintf(buff, buff_size, offset, " rpsi"); break; case BELLE_SDP_RTCP_FB_APP: error = belle_sip_snprintf(buff, buff_size, offset, " app"); break; } break; case BELLE_SDP_RTCP_FB_TRR_INT: error = belle_sip_snprintf(buff, buff_size, offset, "trr-int %u", belle_sdp_rtcp_fb_attribute_get_trr_int(attribute)); break; case BELLE_SDP_RTCP_FB_CCM: error = belle_sip_snprintf(buff, buff_size, offset, "ccm"); if (error != BELLE_SIP_OK) return error; switch (param) { case BELLE_SDP_RTCP_FB_FIR: error = belle_sip_snprintf(buff, buff_size, offset, " fir"); break; case BELLE_SDP_RTCP_FB_TMMBR: error = belle_sip_snprintf(buff, buff_size, offset, " tmmbr"); if (belle_sdp_rtcp_fb_attribute_get_smaxpr(attribute) > 0) { error = belle_sip_snprintf(buff, buff_size, offset, " smaxpr=%u", belle_sdp_rtcp_fb_attribute_get_smaxpr(attribute)); } break; default: break; } break; } return error; } static void belle_sdp_rtcp_fb_attribute_init(belle_sdp_rtcp_fb_attribute_t* attribute) { belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "rtcp-fb"); attribute->id = -1; attribute->type = BELLE_SDP_RTCP_FB_TRR_INT; attribute->param = BELLE_SDP_RTCP_FB_NONE; attribute->trr_int = 0; attribute->smaxpr = 0; } BELLE_SDP_NEW_WITH_CTR(rtcp_fb_attribute,belle_sdp_attribute) BELLE_SDP_PARSE(rtcp_fb_attribute) GET_SET_INT(belle_sdp_rtcp_fb_attribute,id,int8_t) GET_SET_INT(belle_sdp_rtcp_fb_attribute,type,belle_sdp_rtcp_fb_val_type_t) GET_SET_INT(belle_sdp_rtcp_fb_attribute,param,belle_sdp_rtcp_fb_val_param_t) GET_SET_INT(belle_sdp_rtcp_fb_attribute,trr_int,uint16_t) GET_SET_INT(belle_sdp_rtcp_fb_attribute,smaxpr,uint32_t) /*************************************************************************************** * RTCP-XR Attribute * **************************************************************************************/ struct _belle_sdp_rtcp_xr_attribute { belle_sdp_attribute_t base; const char* rcvr_rtt_mode; int rcvr_rtt_max_size; unsigned int stat_summary; belle_sip_list_t* stat_summary_flags; unsigned int voip_metrics; }; const belle_sip_list_t* belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(const belle_sdp_rtcp_xr_attribute_t* attribute) { return attribute->stat_summary_flags; } void belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(belle_sdp_rtcp_xr_attribute_t* attribute, const char* flag) { attribute->stat_summary_flags = belle_sip_list_append(attribute->stat_summary_flags, belle_sip_strdup(flag)); } void belle_sdp_rtcp_xr_attribute_destroy(belle_sdp_rtcp_xr_attribute_t* attribute) { DESTROY_STRING(attribute,rcvr_rtt_mode) belle_sip_list_free_with_data(attribute->stat_summary_flags, belle_sip_free); } void belle_sdp_rtcp_xr_attribute_clone(belle_sdp_rtcp_xr_attribute_t* attribute, const belle_sdp_rtcp_xr_attribute_t *orig) { CLONE_STRING(belle_sdp_rtcp_xr_attribute,rcvr_rtt_mode,attribute,orig) attribute->rcvr_rtt_max_size = orig->rcvr_rtt_max_size; attribute->stat_summary = orig->stat_summary; attribute->stat_summary_flags = belle_sip_list_copy_with_data(orig->stat_summary_flags, belle_sip_string_copyfunc); attribute->voip_metrics = orig->voip_metrics; } belle_sip_error_code belle_sdp_rtcp_xr_attribute_marshal(belle_sdp_rtcp_xr_attribute_t* attribute, char * buff, size_t buff_size, size_t *offset) { const char *rcvr_rtt_mode = NULL; int rcvr_rtt_max_size = -1; int nb_xr_formats = 0; belle_sip_error_code error = belle_sdp_attribute_marshal(BELLE_SDP_ATTRIBUTE(attribute), buff, buff_size, offset); if (error != BELLE_SIP_OK) return error; rcvr_rtt_mode = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(attribute); if (rcvr_rtt_mode != NULL) { error = belle_sip_snprintf(buff, buff_size, offset, "%srcvr-rtt=%s", nb_xr_formats++ == 0 ? ":" : " ", rcvr_rtt_mode); if (error != BELLE_SIP_OK) return error; rcvr_rtt_max_size = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_max_size(attribute); if (rcvr_rtt_max_size > 0) { error = belle_sip_snprintf(buff, buff_size, offset, ":%u", rcvr_rtt_max_size); if (error != BELLE_SIP_OK) return error; } } if (belle_sdp_rtcp_xr_attribute_has_stat_summary(attribute)) { belle_sip_list_t* list; int nb_stat_flags = 0; error = belle_sip_snprintf(buff, buff_size, offset, "%sstat-summary", nb_xr_formats++ == 0 ? ":" : " "); if (error != BELLE_SIP_OK) return error; for (list = attribute->stat_summary_flags; list != NULL; list = list->next) { error = belle_sip_snprintf(buff, buff_size, offset, "%s%s", nb_stat_flags++ == 0 ? "=" : ",", (const char*)list->data); if (error != BELLE_SIP_OK) return error; } } if (belle_sdp_rtcp_xr_attribute_has_voip_metrics(attribute)) { error = belle_sip_snprintf(buff, buff_size, offset, "%svoip-metrics", nb_xr_formats++ == 0 ? ":" : " "); if (error != BELLE_SIP_OK) return error; } return error; } static void belle_sdp_rtcp_xr_attribute_init(belle_sdp_rtcp_xr_attribute_t* attribute) { belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "rtcp-xr"); } BELLE_SDP_NEW_WITH_CTR(rtcp_xr_attribute,belle_sdp_attribute) BELLE_SDP_PARSE(rtcp_xr_attribute) GET_SET_STRING(belle_sdp_rtcp_xr_attribute,rcvr_rtt_mode) GET_SET_INT(belle_sdp_rtcp_xr_attribute,rcvr_rtt_max_size,int) GET_SET_BOOL(belle_sdp_rtcp_xr_attribute,stat_summary,has) GET_SET_BOOL(belle_sdp_rtcp_xr_attribute,voip_metrics,has) /*************************************************************************************** * Bandwidth * **************************************************************************************/ struct _belle_sdp_bandwidth { belle_sip_object_t base; const char* type; int value; }; void belle_sdp_bandwidth_destroy(belle_sdp_bandwidth_t* bandwidth) { if (bandwidth->type) belle_sip_free((void*)bandwidth->type); } void belle_sdp_bandwidth_clone(belle_sdp_bandwidth_t *bandwidth, const belle_sdp_bandwidth_t *orig){ CLONE_STRING(belle_sdp_bandwidth,type,bandwidth,orig) bandwidth->value=orig->value; } belle_sip_error_code belle_sdp_bandwidth_marshal(belle_sdp_bandwidth_t* bandwidth, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"b=%s:%i",bandwidth->type,bandwidth->value); } BELLE_SDP_NEW(bandwidth,belle_sip_object) BELLE_SDP_PARSE(bandwidth) GET_SET_STRING(belle_sdp_bandwidth,type); GET_SET_INT(belle_sdp_bandwidth,value,int) /************************ * connection ***********************/ struct _belle_sdp_connection { belle_sip_object_t base; const char* network_type; const char* address_type; const char* address; int ttl; int range; }; void belle_sdp_connection_destroy(belle_sdp_connection_t* connection) { DESTROY_STRING(connection,network_type) DESTROY_STRING(connection,address_type) DESTROY_STRING(connection,address) } void belle_sdp_connection_clone(belle_sdp_connection_t *connection, const belle_sdp_connection_t *orig){ CLONE_STRING(belle_sdp_connection,network_type,connection,orig) CLONE_STRING(belle_sdp_connection,address_type,connection,orig) CLONE_STRING(belle_sdp_connection,address,connection,orig) connection->range=orig->range; connection->ttl=orig->ttl; } belle_sip_error_code belle_sdp_connection_marshal(belle_sdp_connection_t* connection, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error = belle_sip_snprintf(buff,buff_size,offset,"c=%s %s %s",connection->network_type,connection->address_type,connection->address); if (error!=BELLE_SIP_OK) return error; if (connection->ttl>0) error = belle_sip_snprintf(buff,buff_size,offset,"/%i",connection->ttl); if (error!=BELLE_SIP_OK) return error; if (connection->range>0) error = belle_sip_snprintf(buff,buff_size,offset,"/%i",connection->range); return error; } BELLE_SDP_NEW(connection,belle_sip_object) BELLE_SDP_PARSE(connection) belle_sdp_connection_t* belle_sdp_connection_create(const char* net_type, const char* addr_type, const char* addr) { belle_sdp_connection_t* connection = belle_sdp_connection_new(); belle_sdp_connection_set_network_type(connection,net_type); belle_sdp_connection_set_address_type(connection,addr_type); belle_sdp_connection_set_address(connection,addr); return connection; } GET_SET_STRING(belle_sdp_connection,network_type); GET_SET_STRING(belle_sdp_connection,address_type); GET_SET_STRING(belle_sdp_connection,address); GET_SET_INT(belle_sdp_connection,ttl,int); GET_SET_INT(belle_sdp_connection,range,int); /************************ * email ***********************/ struct _belle_sdp_email { belle_sip_object_t base; char* value; }; void belle_sdp_email_destroy(belle_sdp_email_t* email) { DESTROY_STRING(email,value) } void belle_sdp_email_clone(belle_sdp_email_t *email, const belle_sdp_email_t *orig){ CLONE_STRING(belle_sdp_email,value,email,orig) } belle_sip_error_code belle_sdp_email_marshal(belle_sdp_email_t* email, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"e=%s",email->value); } BELLE_SDP_NEW(email,belle_sip_object) BELLE_SDP_PARSE(email) GET_SET_STRING(belle_sdp_email,value); /************************ * info ***********************/ struct _belle_sdp_info { belle_sip_object_t base; const char* value; }; void belle_sdp_info_destroy(belle_sdp_info_t* info) { DESTROY_STRING(info,value) } void belle_sdp_info_clone(belle_sdp_info_t *info, const belle_sdp_info_t *orig){ CLONE_STRING(belle_sdp_info,value,info,orig) } belle_sip_error_code belle_sdp_info_marshal(belle_sdp_info_t* info, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"i=%s",info->value); } BELLE_SDP_NEW(info,belle_sip_object) BELLE_SDP_PARSE(info) GET_SET_STRING(belle_sdp_info,value); /************************ * media ***********************/ struct _belle_sdp_media { belle_sip_object_t base; const char* media_type; int media_port; belle_sip_list_t* media_formats; int port_count; const char* protocol; const char* raw_fmt; }; belle_sip_list_t* belle_sdp_media_get_media_formats(const belle_sdp_media_t* media) { return media->media_formats; } void belle_sdp_media_set_media_formats( belle_sdp_media_t* media, belle_sip_list_t* formats) { /*belle_sip_list_free(media->media_formats); to allow easy list management might be better to add an append format method*/ media->media_formats = formats; } void belle_sdp_media_destroy(belle_sdp_media_t* media) { DESTROY_STRING(media,media_type) belle_sip_list_free(media->media_formats); DESTROY_STRING(media,protocol) } static void belle_sdp_media_init(belle_sdp_media_t* media) { media->port_count=1; } void belle_sdp_media_clone(belle_sdp_media_t *media, const belle_sdp_media_t *orig){ CLONE_STRING(belle_sdp_media,media_type,media,orig) media->media_port=orig->media_port; media->media_formats = belle_sip_list_copy(orig->media_formats); media->port_count=orig->port_count; CLONE_STRING(belle_sdp_media,protocol,media,orig) } belle_sip_error_code belle_sdp_media_marshal(belle_sdp_media_t* media, char* buff, size_t buff_size, size_t *offset) { belle_sip_list_t* list=media->media_formats; belle_sip_error_code error=belle_sip_snprintf(buff,buff_size,offset,"m=%s %i",media->media_type,media->media_port); if (error!=BELLE_SIP_OK) return error; if (media->port_count>1) { error=belle_sip_snprintf(buff,buff_size,offset,"/%i",media->port_count); if (error!=BELLE_SIP_OK) return error; } error=belle_sip_snprintf(buff,buff_size,offset," %s",media->protocol); if (error!=BELLE_SIP_OK) return error; for(;list!=NULL;list=list->next){ error=belle_sip_snprintf(buff,buff_size,offset," %li",(long)(intptr_t)list->data); if (error!=BELLE_SIP_OK) return error; } return error; } BELLE_SDP_NEW_WITH_CTR(media,belle_sip_object) BELLE_SDP_PARSE(media) belle_sdp_media_t* belle_sdp_media_create(const char* media_type ,int media_port ,int port_count ,const char* protocol ,belle_sip_list_t* static_media_formats) { belle_sdp_media_t* media= belle_sdp_media_new(); belle_sdp_media_set_media_type(media,media_type); belle_sdp_media_set_media_port(media,media_port); belle_sdp_media_set_port_count(media,port_count); belle_sdp_media_set_protocol(media,protocol); if (static_media_formats) belle_sdp_media_set_media_formats(media,static_media_formats); return media; } GET_SET_STRING(belle_sdp_media,media_type); GET_SET_STRING(belle_sdp_media,protocol); GET_SET_INT(belle_sdp_media,media_port,int) GET_SET_INT(belle_sdp_media,port_count,int) /************************ * base_description ***********************/ typedef struct _belle_sdp_base_description { belle_sip_object_t base; belle_sdp_info_t* info; belle_sdp_connection_t* connection; belle_sip_list_t* bandwidths; belle_sip_list_t* attributes; } belle_sdp_base_description_t; static void belle_sdp_base_description_destroy(belle_sdp_base_description_t* base_description) { if (base_description->info) belle_sip_object_unref(BELLE_SIP_OBJECT(base_description->info)); if (base_description->connection) belle_sip_object_unref(BELLE_SIP_OBJECT(base_description->connection)); belle_sip_list_free_with_data(base_description->bandwidths,belle_sip_object_freefunc); belle_sip_list_free_with_data(base_description->attributes,belle_sip_object_freefunc); } static void belle_sdp_base_description_init(belle_sdp_base_description_t* base_description) { } static void belle_sdp_base_description_clone(belle_sdp_base_description_t *base_description, const belle_sdp_base_description_t *orig){ if (orig->info) base_description->info = BELLE_SDP_INFO(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->info))); if (orig->connection) base_description->connection = BELLE_SDP_CONNECTION(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->connection))); base_description->bandwidths = belle_sip_list_copy_with_data(orig->bandwidths,belle_sip_object_copyfunc); base_description->attributes = belle_sip_list_copy_with_data(orig->attributes,belle_sip_object_copyfunc); } belle_sip_error_code belle_sdp_base_description_marshal(belle_sdp_base_description_t* base_description, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=BELLE_SIP_OK; belle_sip_list_t* bandwidths; // belle_sip_list_t* attributes; if (base_description->info) { error=belle_sip_object_marshal(BELLE_SIP_OBJECT(base_description->info),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } if (base_description->connection) { error=belle_sip_object_marshal(BELLE_SIP_OBJECT(base_description->connection),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } for(bandwidths=base_description->bandwidths;bandwidths!=NULL;bandwidths=bandwidths->next){ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(bandwidths->data),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } // for(attributes=base_description->attributes;attributes!=NULL;attributes=attributes->next){ // error=belle_sip_object_marshal(BELLE_SIP_OBJECT(attributes->data),buff,buff_size,offset); // error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); // } return error; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sdp_base_description_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sdp_base_description_t ,belle_sip_object_t ,belle_sdp_base_description_destroy ,belle_sdp_base_description_clone ,belle_sdp_base_description_marshal ,FALSE); static int belle_sdp_base_description_attribute_comp_func(const belle_sdp_attribute_t* a, const char*b) { return strcmp(a->name,b); } belle_sdp_attribute_t* belle_sdp_base_description_get_attribute(const belle_sdp_base_description_t* base_description, const char* name) { belle_sip_list_t* attribute; attribute = belle_sip_list_find_custom(base_description->attributes, (belle_sip_compare_func)belle_sdp_base_description_attribute_comp_func, name); if (attribute) { return ((belle_sdp_attribute_t*)attribute->data); } else { return NULL; } } const char* belle_sdp_base_description_get_attribute_value(const belle_sdp_base_description_t* base_description, const char* name) { belle_sdp_attribute_t* attribute = belle_sdp_base_description_get_attribute(base_description,name); if (attribute) { return belle_sdp_attribute_get_value(attribute); } else return NULL; } belle_sip_list_t* belle_sdp_base_description_get_attributes(const belle_sdp_base_description_t* base_description) { return base_description->attributes; } static int belle_sdp_base_description_bandwidth_comp_func(const belle_sdp_bandwidth_t* a, const char*b) { return strcmp(a->type,b); } belle_sdp_bandwidth_t* belle_sdp_base_description_get_bandwidth(const belle_sdp_base_description_t *base_description, const char *name){ belle_sip_list_t* found = belle_sip_list_find_custom(base_description->bandwidths, (belle_sip_compare_func)belle_sdp_base_description_bandwidth_comp_func, name); if( found ){ return ((belle_sdp_bandwidth_t*)found->data); } else { return NULL; } } int belle_sdp_base_description_get_bandwidth_value(const belle_sdp_base_description_t* base_description, const char* name) { belle_sip_list_t* bandwidth; bandwidth = belle_sip_list_find_custom(base_description->bandwidths, (belle_sip_compare_func)belle_sdp_base_description_bandwidth_comp_func, name); if (bandwidth) { return ((belle_sdp_bandwidth_t*)bandwidth->data)->value; } else { return -1; } } void belle_sdp_base_description_remove_attribute(belle_sdp_base_description_t* base_description,const char* name) { belle_sip_list_t* attribute; attribute = belle_sip_list_find_custom(base_description->attributes, (belle_sip_compare_func)belle_sdp_base_description_attribute_comp_func, name); if (attribute) { belle_sip_object_unref(BELLE_SIP_OBJECT(attribute->data)); base_description->attributes = belle_sip_list_delete_link(base_description->attributes,attribute); } } void belle_sdp_base_description_remove_bandwidth(belle_sdp_base_description_t* base_description,const char* name) { belle_sip_list_t* bandwidth; bandwidth = belle_sip_list_find_custom(base_description->bandwidths, (belle_sip_compare_func)belle_sdp_base_description_bandwidth_comp_func, name); if (bandwidth) { belle_sip_object_unref(BELLE_SIP_OBJECT(bandwidth->data)); base_description->bandwidths = belle_sip_list_delete_link(base_description->bandwidths,bandwidth); } } void belle_sdp_base_description_set_attribute_value(belle_sdp_base_description_t* base_description, const char* name, const char* value) { belle_sdp_raw_attribute_t* attribute = belle_sdp_raw_attribute_new(); belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute),name); belle_sdp_raw_attribute_set_value(attribute,value); base_description->attributes = belle_sip_list_append(base_description->attributes,belle_sip_object_ref(attribute)); } void belle_sdp_base_description_add_attribute(belle_sdp_base_description_t* base_description, const belle_sdp_attribute_t* attribute) { base_description->attributes = belle_sip_list_append(base_description->attributes,(void*)belle_sip_object_ref(BELLE_SIP_OBJECT(attribute))); } #define SET_LIST(list_name,value) \ belle_sip_list_t* list;\ if (list_name) {\ belle_sip_list_free_with_data(list_name,belle_sip_object_unref);\ } \ for (list=value;list !=NULL; list=list->next) {\ belle_sip_object_ref(BELLE_SIP_OBJECT(list->data));\ }\ list_name=value; void belle_sdp_base_description_set_attributes(belle_sdp_base_description_t* base_description, belle_sip_list_t* attributes) { SET_LIST(base_description->attributes,attributes) } void belle_sdp_base_description_set_bandwidth(belle_sdp_base_description_t* base_description, const char* type, int value) { belle_sdp_bandwidth_t* bandwidth = BELLE_SDP_BANDWIDTH(belle_sdp_base_description_get_bandwidth(base_description, type)); if( bandwidth == NULL ){ bandwidth= belle_sdp_bandwidth_new(); belle_sdp_bandwidth_set_type(bandwidth,type); belle_sdp_bandwidth_set_value(bandwidth,value); base_description->bandwidths = belle_sip_list_append(base_description->bandwidths,belle_sip_object_ref(bandwidth)); } else { belle_sdp_bandwidth_set_value(bandwidth,value); } } void belle_sdp_base_description_add_bandwidth(belle_sdp_base_description_t* base_description, const belle_sdp_bandwidth_t* bandwidth) { base_description->bandwidths = belle_sip_list_append(base_description->bandwidths,(void *)belle_sip_object_ref((void *)bandwidth)); } void belle_sdp_base_description_set_bandwidths(belle_sdp_base_description_t* base_description, belle_sip_list_t* bandwidths) { SET_LIST(base_description->bandwidths,bandwidths) } /************************ * media_description ***********************/ struct _belle_sdp_media_description { belle_sdp_base_description_t base_description; belle_sdp_media_t* media; }; void belle_sdp_media_description_destroy(belle_sdp_media_description_t* media_description) { if (media_description->media) belle_sip_object_unref(BELLE_SIP_OBJECT((media_description->media))); } void belle_sdp_media_description_clone(belle_sdp_media_description_t *media_description, const belle_sdp_media_description_t *orig){ if (orig->media) media_description->media = BELLE_SDP_MEDIA(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT((orig->media)))); } belle_sip_error_code belle_sdp_media_description_marshal(belle_sdp_media_description_t* media_description, char* buff, size_t buff_size, size_t *offset) { belle_sip_list_t* attributes; belle_sip_error_code error=belle_sip_object_marshal(BELLE_SIP_OBJECT(media_description->media),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; error=belle_sdp_base_description_marshal(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; for(attributes=media_description->base_description.attributes;attributes!=NULL;attributes=attributes->next){ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(attributes->data),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } return error; } BELLE_SDP_NEW(media_description,belle_sdp_base_description) belle_sdp_media_description_t* belle_sdp_media_description_create(const char* media_type ,int media_port ,int port_count ,const char* protocol ,belle_sip_list_t* static_media_formats) { belle_sdp_media_description_t* media_desc=belle_sdp_media_description_new(); belle_sdp_media_description_set_media(media_desc,belle_sdp_media_create(media_type,media_port,port_count,protocol,static_media_formats)); return media_desc; } BELLE_SDP_PARSE(media_description) void belle_sdp_media_description_add_dynamic_payloads(belle_sdp_media_description_t* media_description, belle_sip_list_t* payloadNames, belle_sip_list_t* payloadValues) { belle_sip_error("belle_sdp_media_description_add_dynamic_payloads not implemented yet"); } belle_sdp_attribute_t* belle_sdp_media_description_get_attribute(const belle_sdp_media_description_t* media_description, const char* name) { return belle_sdp_base_description_get_attribute(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name); } const char* belle_sdp_media_description_get_attribute_value(const belle_sdp_media_description_t* media_description, const char* name) { return belle_sdp_base_description_get_attribute_value(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name); } belle_sip_list_t* belle_sdp_media_description_get_attributes(const belle_sdp_media_description_t* media_description) { return BELLE_SIP_CAST(media_description,belle_sdp_base_description_t)->attributes; } int belle_sdp_media_description_get_bandwidth(const belle_sdp_media_description_t* media_description, const char* name) { return belle_sdp_base_description_get_bandwidth_value(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name); } belle_sip_list_t* belle_sdp_media_description_get_bandwidths(const belle_sdp_media_description_t* media_description) { return BELLE_SIP_CAST(media_description,belle_sdp_base_description_t)->bandwidths; } belle_sdp_connection_t* belle_sdp_media_description_get_connection(const belle_sdp_media_description_t* media_description) { return BELLE_SIP_CAST(media_description,belle_sdp_base_description_t)->connection; } belle_sdp_info_t* belle_sdp_media_description_get_info(const belle_sdp_media_description_t* media_description) { return BELLE_SIP_CAST(media_description,belle_sdp_base_description_t)->info; } /*belle_sdp_key_t* belle_sdp_media_description_get_key(const belle_sdp_media_description_t* media_description);*/ belle_sdp_media_t* belle_sdp_media_description_get_media(const belle_sdp_media_description_t* media_description) { return media_description->media; } struct static_payload { unsigned char number; int channel_count; const char* type; int rate; }; #define STATIC_PAYLOAD_LIST_LENTH 8 /* * rfc 3551 * PT encoding media type clock rate channels name (Hz) ___________________________________________________ 0 PCMU A 8,000 1 1 reserved A 2 reserved A 3 GSM A 8,000 1 4 G723 A 8,000 1 5 DVI4 A 8,000 1 6 DVI4 A 16,000 1 7 LPC A 8,000 1 8 PCMA A 8,000 1 9 G722 A 8,000 1 10 L16 A 44,100 2 11 L16 A 44,100 1 12 QCELP A 8,000 1 13 CN A 8,000 1 14 MPA A 90,000 (see text) 15 G728 A 8,000 1 16 DVI4 A 11,025 1 17 DVI4 A 22,050 1 18 G729 A 8,000 1 Table 4: Payload types (PT) for audio encodings PT encoding media type clock rate name (Hz) _____________________________________________ 24 unassigned V 25 CelB V 90,000 26 JPEG V 90,000 27 unassigned V 28 nv V 90,000 29 unassigned V 30 unassigned V 31 H261 V 90,000 32 MPV V 90,000 33 MP2T AV 90,000 34 H263 V 90,000 Table 5: Payload types (PT) for video and combined encodings * * */ const struct static_payload static_payload_list [] ={ /*audio*/ {0,1,"PCMU",8000}, {3,1,"GSM",8000}, {4,1,"G723",8000}, {5,1,"DVI4",8000}, {6,1,"DVI4",16000}, {7,1,"LPC",8000}, {8,1,"PCMA",8000}, {9,1,"G722",8000}, {10,2,"L16",44100}, {11,1,"L16",44100}, {12,1,"QCELP",8000}, {13,1,"CN",8000}, {14,1,"MPA",90000}, {15,1,"G728",8000}, {16,1,"DVI4",11025}, {17,1,"DVI4",22050}, {18,1,"G729",8000}, /*video*/ {25,0,"CelB",90000}, {26,0,"JPEG",90000}, {28,0,"nv",90000}, {31,0,"H261",90000}, {32,0,"MPV",90000}, {33,0,"MP2T",90000}, {34,0,"H263",90000} }; static const size_t payload_list_elements=sizeof(static_payload_list)/sizeof(struct static_payload); static int mime_parameter_is_static(const belle_sdp_mime_parameter_t *param){ const struct static_payload* iterator; size_t i; for (iterator = static_payload_list,i=0;inumber == param->media_format && strcasecmp(iterator->type,param->type)==0 && iterator->channel_count==param->channel_count && iterator->rate==param->rate ) { return TRUE; } } return FALSE; } static int mime_parameter_fill_from_static(belle_sdp_mime_parameter_t *mime_parameter,int format) { const struct static_payload* iterator; size_t i; for (iterator = static_payload_list,i=0;inumber == format) { belle_sdp_mime_parameter_set_type(mime_parameter,iterator->type); belle_sdp_mime_parameter_set_rate(mime_parameter,iterator->rate); belle_sdp_mime_parameter_set_channel_count(mime_parameter,iterator->channel_count); break; } } return 0; } static int mime_parameter_fill_from_rtpmap(belle_sdp_mime_parameter_t *mime_parameter, const char *rtpmap, int is_audio){ char *mime=belle_sip_strdup(rtpmap); char *p=strchr(mime,'/'); if (p){ char *chans; *p='\0'; p++; chans=strchr(p,'/'); if (chans){ *chans='\0'; chans++; belle_sdp_mime_parameter_set_channel_count(mime_parameter,atoi(chans)); }else if (is_audio) belle_sdp_mime_parameter_set_channel_count(mime_parameter,1); /*in absence of channel count, 1 is implicit for audio streams*/ belle_sdp_mime_parameter_set_rate(mime_parameter,atoi(p)); } belle_sdp_mime_parameter_set_type(mime_parameter,mime); belle_sip_free(mime); return 0; } /* return the value of attr "field" for payload pt at line pos (field=rtpmap,fmtp...)*/ static const char *belle_sdp_media_description_a_attr_value_get_with_pt(const belle_sdp_media_description_t* media_description,int pt,const char *field) { int tmppt=0,scanned=0; const char *tmp; belle_sdp_attribute_t *attr; belle_sip_list_t* attribute_list; for ( attribute_list =belle_sdp_media_description_get_attributes(media_description) ;attribute_list!=NULL ;attribute_list=attribute_list->next) { attr = BELLE_SDP_ATTRIBUTE(attribute_list->data); if (strcmp(field,belle_sdp_attribute_get_name(attr))==0 && belle_sdp_attribute_get_value(attr)!=NULL){ int nb = sscanf(belle_sdp_attribute_get_value(attr),"%i %n",&tmppt,&scanned); /* the return value may depend on how %n is interpreted by the libc: see manpage*/ if (nb == 1 || nb==2 ){ if (pt==tmppt){ tmp=belle_sdp_attribute_get_value(attr)+scanned; if (strlen(tmp)>0) return tmp; } }else belle_sip_warning("sdp has a strange a= line (%s) nb=%i",belle_sdp_attribute_get_value(attr),nb); } } return NULL; } belle_sip_list_t* belle_sdp_media_description_build_mime_parameters(const belle_sdp_media_description_t* media_description) { /*First, get media type*/ belle_sdp_media_t* media = belle_sdp_media_description_get_media(media_description); belle_sip_list_t* mime_parameter_list=NULL; belle_sip_list_t* media_formats=NULL; belle_sdp_mime_parameter_t* mime_parameter; const char* rtpmap=NULL; const char* fmtp=NULL; const char* ptime=NULL; const char* max_ptime=NULL; int ptime_as_int=-1; int max_ptime_as_int=-1; int is_audio=0; if (!media) { belle_sip_error("belle_sdp_media_description_build_mime_parameters: no media"); return NULL; } if (strcasecmp(belle_sdp_media_get_media_type(media),"audio")==0) is_audio=1; ptime = belle_sdp_media_description_get_attribute_value(media_description,"ptime"); ptime?ptime_as_int=atoi(ptime):-1; max_ptime = belle_sdp_media_description_get_attribute_value(media_description,"maxptime"); max_ptime?max_ptime_as_int=atoi(max_ptime):-1; for (media_formats = belle_sdp_media_get_media_formats(media);media_formats!=NULL;media_formats=media_formats->next) { /*create mime parameters with format*/ mime_parameter = belle_sdp_mime_parameter_new(); belle_sdp_mime_parameter_set_ptime(mime_parameter,ptime_as_int); belle_sdp_mime_parameter_set_max_ptime(mime_parameter,max_ptime_as_int); belle_sdp_mime_parameter_set_media_format(mime_parameter,(int)(intptr_t)media_formats->data); /*get rtpmap*/ rtpmap = belle_sdp_media_description_a_attr_value_get_with_pt(media_description ,belle_sdp_mime_parameter_get_media_format(mime_parameter) ,"rtpmap"); if (rtpmap) { mime_parameter_fill_from_rtpmap(mime_parameter,rtpmap,is_audio); }else{ mime_parameter_fill_from_static(mime_parameter,belle_sdp_mime_parameter_get_media_format(mime_parameter)); } fmtp = belle_sdp_media_description_a_attr_value_get_with_pt(media_description ,belle_sdp_mime_parameter_get_media_format(mime_parameter) ,"fmtp"); if (fmtp) { belle_sdp_mime_parameter_set_parameters(mime_parameter,fmtp); } mime_parameter_list=belle_sip_list_append(mime_parameter_list,mime_parameter); } return mime_parameter_list; } #define MAX_FMTP_LENGTH 512 void belle_sdp_media_description_append_values_from_mime_parameter(belle_sdp_media_description_t* media_description, const belle_sdp_mime_parameter_t* mime_parameter) { belle_sdp_media_t* media = belle_sdp_media_description_get_media(media_description); char atribute_value [MAX_FMTP_LENGTH]; int current_ptime=0; int current_max_ptime=0; belle_sdp_media_set_media_formats(media,belle_sip_list_append(belle_sdp_media_get_media_formats(media) ,(void*)(intptr_t)(belle_sdp_mime_parameter_get_media_format(mime_parameter)))); if (belle_sdp_media_description_get_attribute_value(media_description,"ptime")) { current_ptime=atoi(belle_sdp_media_description_get_attribute_value(media_description,"ptime")); belle_sdp_media_description_remove_attribute(media_description,"ptime"); } if (belle_sdp_media_description_get_attribute_value(media_description,"maxptime")) { current_max_ptime=atoi(belle_sdp_media_description_get_attribute_value(media_description,"maxptime")); belle_sdp_media_description_remove_attribute(media_description,"maxptime"); } #ifndef BELLE_SDP_FORCE_RTP_MAP /* defined to for RTP map even for static codec*/ if (!mime_parameter_is_static(mime_parameter)) { /*dynamic payload*/ #endif if (belle_sdp_mime_parameter_get_channel_count(mime_parameter)>1) { snprintf(atribute_value,MAX_FMTP_LENGTH,"%i %s/%i/%i" ,belle_sdp_mime_parameter_get_media_format(mime_parameter) ,belle_sdp_mime_parameter_get_type(mime_parameter) ,belle_sdp_mime_parameter_get_rate(mime_parameter) ,belle_sdp_mime_parameter_get_channel_count(mime_parameter)); } else { snprintf(atribute_value,MAX_FMTP_LENGTH,"%i %s/%i" ,belle_sdp_mime_parameter_get_media_format(mime_parameter) ,belle_sdp_mime_parameter_get_type(mime_parameter) ,belle_sdp_mime_parameter_get_rate(mime_parameter)); } belle_sdp_media_description_set_attribute_value(media_description,"rtpmap",atribute_value); #ifndef BELLE_SDP_FORCE_RTP_MAP } #endif // always include fmtp parameters if available if (belle_sdp_mime_parameter_get_parameters(mime_parameter)) { snprintf(atribute_value,MAX_FMTP_LENGTH,"%i %s" ,belle_sdp_mime_parameter_get_media_format(mime_parameter) ,belle_sdp_mime_parameter_get_parameters(mime_parameter)); belle_sdp_media_description_set_attribute_value(media_description,"fmtp",atribute_value); } if (belle_sdp_mime_parameter_get_ptime(mime_parameter)>current_ptime) { current_ptime=belle_sdp_mime_parameter_get_ptime(mime_parameter); } if (current_ptime>0){ char ptime[10]; snprintf(ptime,sizeof(ptime),"%i",current_ptime); belle_sdp_media_description_set_attribute_value(media_description,"ptime",ptime); } if (belle_sdp_mime_parameter_get_max_ptime(mime_parameter)>current_max_ptime) { current_max_ptime=belle_sdp_mime_parameter_get_max_ptime(mime_parameter); } if (current_max_ptime>0){ char max_ptime[10]; snprintf(max_ptime,sizeof(max_ptime),"%i",current_max_ptime); belle_sdp_media_description_set_attribute_value(media_description,"maxptime",max_ptime); } } belle_sip_list_t* belle_sdp_media_description_get_mime_types(const belle_sdp_media_description_t* media_description) { belle_sip_error("belle_sdp_media_description_get_mime_types: not implemented yet"); return NULL; } void belle_sdp_media_description_remove_attribute(belle_sdp_media_description_t* media_description,const char* name) { belle_sdp_base_description_remove_attribute(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name); } void belle_sdp_media_description_remove_bandwidth(belle_sdp_media_description_t* media_description,const char* name) { belle_sdp_base_description_remove_bandwidth(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name); } void belle_sdp_media_description_set_attribute_value(belle_sdp_media_description_t* media_description, const char* name, const char* value) { belle_sdp_base_description_set_attribute_value(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name,value); } void belle_sdp_media_description_set_attributes(belle_sdp_media_description_t* media_description, belle_sip_list_t* value) { belle_sdp_base_description_set_attributes(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),value); } void belle_sdp_media_description_add_attribute(belle_sdp_media_description_t* media_description, const belle_sdp_attribute_t* attribute) { belle_sdp_base_description_add_attribute(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),attribute); } void belle_sdp_media_description_set_bandwidth(belle_sdp_media_description_t* media_description, const char* type, int value) { belle_sdp_base_description_set_bandwidth(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),type,value); } void belle_sdp_media_description_add_bandwidth(belle_sdp_media_description_t* media_description, const belle_sdp_bandwidth_t* bandwidth) { belle_sdp_base_description_add_bandwidth(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),bandwidth); } void belle_sdp_media_description_set_bandwidths(belle_sdp_media_description_t* media_description, belle_sip_list_t* bandwidths) { belle_sdp_base_description_set_bandwidths(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),bandwidths); } #define SET_OBJECT(object,param,param_type) \ param_type** current = &object->param; \ if (param) belle_sip_object_ref(param); \ if (*current) { \ belle_sip_object_unref(BELLE_SIP_OBJECT(*current)); \ } \ *current=param; \ void belle_sdp_media_description_set_connection(belle_sdp_media_description_t* media_description, belle_sdp_connection_t* connection) { SET_OBJECT(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),connection,belle_sdp_connection_t) } void belle_sdp_media_description_set_info(belle_sdp_media_description_t* media_description,belle_sdp_info_t* info) { SET_OBJECT(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),info,belle_sdp_info_t) } /*void belle_sdp_media_description_set_key(belle_sdp_media_description_t* media_description,belle_sdp_key_t* key);*/ void belle_sdp_media_description_set_media(belle_sdp_media_description_t* media_description, belle_sdp_media_t* media) { SET_OBJECT(media_description,media,belle_sdp_media_t) } /************************ * origin ***********************/ struct _belle_sdp_origin { belle_sip_object_t base; const char* address; const char* address_type; const char* network_type; const char* username; unsigned int session_id; unsigned int session_version; }; void belle_sdp_origin_destroy(belle_sdp_origin_t* origin) { DESTROY_STRING(origin,address) DESTROY_STRING(origin,address_type) DESTROY_STRING(origin,network_type) DESTROY_STRING(origin,username) } void belle_sdp_origin_clone(belle_sdp_origin_t *origin, const belle_sdp_origin_t *orig){ CLONE_STRING(belle_sdp_origin,username,origin,orig); CLONE_STRING(belle_sdp_origin,address,origin,orig); CLONE_STRING(belle_sdp_origin,address_type,origin,orig); CLONE_STRING(belle_sdp_origin,network_type,origin,orig); origin->session_id = orig->session_id; origin->session_version = orig->session_version; } belle_sip_error_code belle_sdp_origin_marshal(belle_sdp_origin_t* origin, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf( buff ,buff_size ,offset ,"o=%s %u %u %s %s %s" ,origin->username ,origin->session_id ,origin->session_version ,origin->network_type ,origin->address_type ,origin->address); } BELLE_SDP_NEW(origin,belle_sip_object) belle_sdp_origin_t* belle_sdp_origin_create(const char* user_name , unsigned int session_id , unsigned int session_version , const char* network_type , const char* addr_type , const char* address) { belle_sdp_origin_t* origin=belle_sdp_origin_new(); belle_sdp_origin_set_username(origin,user_name); belle_sdp_origin_set_session_id(origin,session_id); belle_sdp_origin_set_session_version(origin,session_version); belle_sdp_origin_set_network_type(origin,network_type); belle_sdp_origin_set_address_type(origin,addr_type); belle_sdp_origin_set_address(origin,address); return origin; } BELLE_SDP_PARSE(origin) GET_SET_STRING(belle_sdp_origin,username); GET_SET_STRING(belle_sdp_origin,address); GET_SET_STRING(belle_sdp_origin,address_type); GET_SET_STRING(belle_sdp_origin,network_type); GET_SET_INT(belle_sdp_origin,session_id,unsigned int); GET_SET_INT(belle_sdp_origin,session_version,unsigned int); /************************ * session_name ***********************/ struct _belle_sdp_session_name { belle_sip_object_t base; const char* value; }; void belle_sdp_session_name_destroy(belle_sdp_session_name_t* session_name) { DESTROY_STRING(session_name,value) } void belle_sdp_session_name_clone(belle_sdp_session_name_t *session_name, const belle_sdp_session_name_t *orig){ CLONE_STRING(belle_sdp_session_name,value,session_name,orig); } belle_sip_error_code belle_sdp_session_name_marshal(belle_sdp_session_name_t* session_name, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"s=%s",session_name->value); } BELLE_SDP_NEW(session_name,belle_sip_object) belle_sdp_session_name_t* belle_sdp_session_name_create (const char* name) { belle_sdp_session_name_t* n=belle_sdp_session_name_new(); belle_sdp_session_name_set_value(n,name); return n; } //BELLE_SDP_PARSE(session_name) GET_SET_STRING(belle_sdp_session_name,value); /************************ * session_description ***********************/ struct _belle_sdp_session_description { belle_sdp_base_description_t base_description; belle_sdp_version_t* version; belle_sip_list_t* emails; belle_sdp_origin_t* origin; belle_sdp_session_name_t* session_name; belle_sip_list_t* phones; belle_sip_list_t* times; belle_sdp_uri_t* uri; belle_sdp_uri_t* zone_adjustments; belle_sip_list_t* media_descriptions; }; void belle_sdp_session_description_destroy(belle_sdp_session_description_t* session_description) { if (session_description->version) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->version)); belle_sip_list_free_with_data(session_description->emails,belle_sip_object_freefunc); if (session_description->origin) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->origin)); if (session_description->session_name) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->session_name)); belle_sip_list_free_with_data(session_description->phones,belle_sip_object_freefunc); belle_sip_list_free_with_data(session_description->times,belle_sip_object_freefunc); if (session_description->uri) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->uri)); if (session_description->zone_adjustments) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->zone_adjustments)); belle_sip_list_free_with_data(session_description->media_descriptions,belle_sip_object_freefunc); } void belle_sdp_session_description_clone(belle_sdp_session_description_t *session_description, const belle_sdp_session_description_t *orig){ if (orig->version) session_description->version = BELLE_SDP_VERSION(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->version))); session_description->emails = belle_sip_list_copy_with_data(orig->emails,belle_sip_object_copyfunc); if (orig->origin) session_description->origin = BELLE_SDP_ORIGIN(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->origin))); if (orig->session_name) session_description->session_name = BELLE_SDP_SESSION_NAME(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->session_name))); session_description->phones = belle_sip_list_copy_with_data(orig->phones,belle_sip_object_copyfunc); session_description->times = belle_sip_list_copy_with_data(orig->times,belle_sip_object_copyfunc); if (orig->uri) session_description->uri = BELLE_SDP_URI(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->uri))); if (orig->zone_adjustments) session_description->zone_adjustments = BELLE_SDP_URI(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->zone_adjustments))); session_description->media_descriptions = belle_sip_list_copy_with_data(orig->media_descriptions,belle_sip_object_copyfunc); } belle_sip_error_code belle_sdp_session_description_marshal(belle_sdp_session_description_t* session_description, char* buff, size_t buff_size, size_t *offset) { /*session_description: proto_version CR LF origin_field session_name_field (info CR LF)? uri_field? (email CR LF)* phone_field* (connection CR LF)? (bandwidth CR LF)* time_field (repeat_time CR LF)? (zone_adjustments CR LF)? (key_field CR LF)? (attribute CR LF)* media_descriptions; */ belle_sip_error_code error=BELLE_SIP_OK; belle_sip_list_t* media_descriptions; belle_sip_list_t* times; belle_sip_list_t* attributes; if (session_description->version) { error=belle_sip_object_marshal(BELLE_SIP_OBJECT(session_description->version),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } if (session_description->origin) { error=belle_sip_object_marshal(BELLE_SIP_OBJECT(session_description->origin),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } if (session_description->session_name) { error=belle_sip_object_marshal(BELLE_SIP_OBJECT(session_description->session_name),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } error=belle_sdp_base_description_marshal((belle_sdp_base_description_t*)(&session_description->base_description),buff,buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "t="); if (error!=BELLE_SIP_OK) return error; for(times=session_description->times;times!=NULL;times=times->next){ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(times->data),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } for(attributes=session_description->base_description.attributes;attributes!=NULL;attributes=attributes->next){ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(attributes->data),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } for(media_descriptions=session_description->media_descriptions;media_descriptions!=NULL;media_descriptions=media_descriptions->next){ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(media_descriptions->data),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; } return error; } BELLE_SDP_NEW(session_description,belle_sdp_base_description) BELLE_SDP_PARSE(session_description) belle_sip_list_t * belle_sdp_session_description_get_attributes(const belle_sdp_session_description_t *session_description) { return belle_sdp_base_description_get_attributes(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t)); } const char* belle_sdp_session_description_get_attribute_value(const belle_sdp_session_description_t* session_description, const char* name) { return belle_sdp_base_description_get_attribute_value(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name); } const belle_sdp_attribute_t* belle_sdp_session_description_get_attribute(const belle_sdp_session_description_t* session_description, const char* name) { return belle_sdp_base_description_get_attribute(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name); } int belle_sdp_session_description_get_bandwidth(const belle_sdp_session_description_t* session_description, const char* name) { return belle_sdp_base_description_get_bandwidth_value(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name); } belle_sip_list_t* belle_sdp_session_description_get_bandwidths(const belle_sdp_session_description_t* session_description) { return BELLE_SIP_CAST(session_description,belle_sdp_base_description_t)->bandwidths; } belle_sdp_connection_t* belle_sdp_session_description_get_connection(const belle_sdp_session_description_t* session_description) { return BELLE_SIP_CAST(session_description,belle_sdp_base_description_t)->connection; } belle_sip_list_t* belle_sdp_session_description_get_emails(const belle_sdp_session_description_t* session_description){ return session_description->emails; } belle_sdp_info_t* belle_sdp_session_description_get_info(const belle_sdp_session_description_t* session_description) { return BELLE_SIP_CAST(session_description,belle_sdp_base_description_t)->info; } /*belle_sdp_key_t* belle_sdp_session_description_get_key(const belle_sdp_session_description_t* session_description);*/ belle_sip_list_t* belle_sdp_session_description_get_media_descriptions(const belle_sdp_session_description_t* session_description) { return session_description->media_descriptions; } belle_sdp_origin_t* belle_sdp_session_description_get_origin(const belle_sdp_session_description_t* session_description){ return session_description->origin; } belle_sip_list_t* belle_sdp_session_description_get_phones(const belle_sdp_session_description_t* session_description) { return session_description->phones; } belle_sdp_session_name_t* belle_sdp_session_description_get_session_name(const belle_sdp_session_description_t* session_description) { return session_description->session_name; } belle_sip_list_t* belle_sdp_session_description_get_time_descriptions(const belle_sdp_session_description_t* session_description) { return session_description->times; } belle_sdp_uri_t* belle_sdp_session_description_get_uri(const belle_sdp_session_description_t* session_description) { return session_description->uri; } belle_sdp_version_t* belle_sdp_session_description_get_version(const belle_sdp_session_description_t* session_description) { return session_description->version; } belle_sdp_uri_t* belle_sdp_session_description_get_zone_adjustments(const belle_sdp_session_description_t* session_description) { return session_description->zone_adjustments; } void belle_sdp_session_description_remove_attribute(belle_sdp_session_description_t* session_description, const char* name) { belle_sdp_base_description_remove_attribute(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name); } void belle_sdp_session_description_remove_bandwidth(belle_sdp_session_description_t* session_description, const char* name) { belle_sdp_base_description_remove_bandwidth(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name); } void belle_sdp_session_description_set_attribute_value(belle_sdp_session_description_t* session_description, const char* name, const char* value) { belle_sdp_base_description_set_attribute_value(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name,value); } void belle_sdp_session_description_set_attributes(belle_sdp_session_description_t* session_description, belle_sip_list_t* attributes) { belle_sdp_base_description_set_attributes(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),attributes); } void belle_sdp_session_description_add_attribute(belle_sdp_session_description_t* session_description, const belle_sdp_attribute_t* attribute) { belle_sdp_base_description_add_attribute(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),attribute); } void belle_sdp_session_description_set_bandwidth(belle_sdp_session_description_t* session_description, const char* type, int value) { belle_sdp_base_description_set_bandwidth(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),type,value); } void belle_sdp_session_description_set_bandwidths(belle_sdp_session_description_t* session_description, belle_sip_list_t* bandwidths) { belle_sdp_base_description_set_bandwidths(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),bandwidths); } void belle_sdp_session_description_add_bandwidth(belle_sdp_session_description_t* session_description, const belle_sdp_bandwidth_t* bandwidth) { belle_sdp_base_description_add_bandwidth(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),bandwidth); } void belle_sdp_session_description_set_connection(belle_sdp_session_description_t* session_description, belle_sdp_connection_t* connection) { SET_OBJECT(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),connection,belle_sdp_connection_t) } void belle_sdp_session_description_set_emails(belle_sdp_session_description_t* session_description, belle_sip_list_t* emails) { SET_LIST(session_description->emails,emails) } void belle_sdp_session_description_set_info(belle_sdp_session_description_t* session_description, belle_sdp_info_t* info) { SET_OBJECT(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),info,belle_sdp_info_t) } /*void belle_sdp_session_description_set_key(belle_sdp_session_description_t* session_description, belle_sdp_key_t* key);*/ void belle_sdp_session_description_set_media_descriptions(belle_sdp_session_description_t* session_description, belle_sip_list_t* media_descriptions) { SET_LIST(session_description->media_descriptions,media_descriptions) } void belle_sdp_session_description_add_media_description(belle_sdp_session_description_t* session_description, belle_sdp_media_description_t* media_description) { session_description->media_descriptions = belle_sip_list_append(session_description->media_descriptions,belle_sip_object_ref(media_description)); } void belle_sdp_session_description_set_origin(belle_sdp_session_description_t* session_description, belle_sdp_origin_t* origin) { SET_OBJECT(session_description,origin,belle_sdp_origin_t) } void belle_sdp_session_description_set_phones(belle_sdp_session_description_t* session_description, belle_sip_list_t* phones) { SET_LIST(session_description->phones,phones) } void belle_sdp_session_description_set_session_name(belle_sdp_session_description_t* session_description, belle_sdp_session_name_t* session_name) { SET_OBJECT(session_description,session_name,belle_sdp_session_name_t) } void belle_sdp_session_description_set_time_descriptions(belle_sdp_session_description_t* session_description, belle_sip_list_t* times) { SET_LIST(session_description->times,times) } void belle_sdp_session_description_set_time_description(belle_sdp_session_description_t* session_description, belle_sdp_time_description_t* time_desc) { belle_sdp_session_description_set_time_descriptions(session_description,belle_sip_list_new(time_desc)); } void belle_sdp_session_description_set_uri(belle_sdp_session_description_t* session_description, belle_sdp_uri_t* uri) { SET_OBJECT(session_description,uri,belle_sdp_uri_t) } void belle_sdp_session_description_set_version(belle_sdp_session_description_t* session_description, belle_sdp_version_t* version) { SET_OBJECT(session_description,version,belle_sdp_version_t) } void belle_sdp_session_description_set_zone_adjustments(belle_sdp_session_description_t* session_description, belle_sdp_uri_t* zone_adjustments) { SET_OBJECT(session_description,zone_adjustments,belle_sdp_uri_t) } /************************ * time ***********************/ struct _belle_sdp_time { belle_sip_object_t base; int start; int stop; }; void belle_sdp_time_destroy(belle_sdp_time_t* time) { } void belle_sdp_time_clone(belle_sdp_time_t *time, const belle_sdp_time_t *orig){ time->start=orig->start; time->stop=orig->stop; } belle_sip_error_code belle_sdp_time_marshal(belle_sdp_time_t* time, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"%i %i",time->start,time->stop); } BELLE_SDP_NEW(time,belle_sip_object) //BELLE_SDP_PARSE(version) GET_SET_INT(belle_sdp_time,start,int); GET_SET_INT(belle_sdp_time,stop,int); /************************ * time description ***********************/ struct _belle_sdp_time_description { belle_sip_object_t base; belle_sdp_time_t* time; }; void belle_sdp_time_description_destroy(belle_sdp_time_description_t* time_description) { if (time_description->time) belle_sip_object_unref(BELLE_SIP_OBJECT(time_description->time)); } void belle_sdp_time_description_clone(belle_sdp_time_description_t *time_description, const belle_sdp_time_description_t *orig){ if (orig->time) time_description->time = BELLE_SDP_TIME(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->time))); } belle_sip_error_code belle_sdp_time_description_marshal(belle_sdp_time_description_t* time_description, char* buff, size_t buff_size, size_t *offset) { return belle_sip_object_marshal(BELLE_SIP_OBJECT(time_description->time),buff,buff_size,offset); } BELLE_SDP_NEW(time_description,belle_sip_object) belle_sdp_time_description_t* belle_sdp_time_description_create (int start,int stop) { belle_sdp_time_description_t* time_desc= belle_sdp_time_description_new(); belle_sdp_time_t* time = belle_sdp_time_new(); belle_sdp_time_set_start(time,start); belle_sdp_time_set_stop(time,stop); belle_sdp_time_description_set_time(time_desc,time); return time_desc; } belle_sip_list_t* belle_sdp_time_description_get_repeate_times(const belle_sdp_time_description_t* time_description) { return NULL; } belle_sdp_time_t* belle_sdp_time_description_get_time(const belle_sdp_time_description_t* time_description) { return time_description->time; } void belle_sdp_time_description_set_repeate_times(belle_sdp_time_description_t* time_description, belle_sip_list_t* times) { belle_sip_error("time description repeat time not implemented"); } void belle_sdp_time_description_set_time(belle_sdp_time_description_t* time_description, belle_sdp_time_t* time) { SET_OBJECT(time_description,time,belle_sdp_time_t) } /************************ * version ***********************/ struct _belle_sdp_version { belle_sip_object_t base; int version; }; void belle_sdp_version_destroy(belle_sdp_version_t* version) { } void belle_sdp_version_clone(belle_sdp_version_t *version, const belle_sdp_version_t *orig){ version->version = orig->version; } belle_sip_error_code belle_sdp_version_marshal(belle_sdp_version_t* version, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"v=%i",version->version); } BELLE_SDP_NEW(version,belle_sip_object) belle_sdp_version_t* belle_sdp_version_create(int version) { belle_sdp_version_t* v = belle_sdp_version_new(); belle_sdp_version_set_version(v,version); return v; } //BELLE_SDP_PARSE(version) GET_SET_INT(belle_sdp_version,version,int); /*************************************************************************************** * mime_parameter * **************************************************************************************/ static void belle_sdp_mime_parameter_destroy(belle_sdp_mime_parameter_t *mime_parameter) { if (mime_parameter->type) belle_sip_free((void*)mime_parameter->type); if (mime_parameter->parameters) belle_sip_free((void*)mime_parameter->parameters); } static void belle_sdp_mime_parameter_clone(belle_sdp_mime_parameter_t *mime_parameter,belle_sdp_mime_parameter_t *orig) { mime_parameter->rate = orig->rate; mime_parameter->channel_count = orig->channel_count; mime_parameter->ptime = orig->ptime; mime_parameter->max_ptime = orig->max_ptime; mime_parameter->media_format = orig->media_format; CLONE_STRING(belle_sdp_mime_parameter,type,mime_parameter,orig); CLONE_STRING(belle_sdp_mime_parameter,parameters,mime_parameter,orig); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sdp_mime_parameter_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sdp_mime_parameter_t ,belle_sip_object_t ,belle_sdp_mime_parameter_destroy ,belle_sdp_mime_parameter_clone ,NULL ,TRUE); belle_sdp_mime_parameter_t* belle_sdp_mime_parameter_new() { belle_sdp_mime_parameter_t* l_param = belle_sip_object_new(belle_sdp_mime_parameter_t); l_param->ptime = -1; l_param->max_ptime = -1; return l_param; } belle_sdp_mime_parameter_t* belle_sdp_mime_parameter_create(const char* type, int media_format, int rate,int channel_count) { belle_sdp_mime_parameter_t* mime_param= belle_sdp_mime_parameter_new(); belle_sdp_mime_parameter_set_type(mime_param,type); belle_sdp_mime_parameter_set_media_format(mime_param,media_format); belle_sdp_mime_parameter_set_rate(mime_param,rate); belle_sdp_mime_parameter_set_channel_count(mime_param,channel_count); return mime_param; } GET_SET_INT(belle_sdp_mime_parameter,rate,int); GET_SET_INT(belle_sdp_mime_parameter,channel_count,int); GET_SET_INT(belle_sdp_mime_parameter,ptime,int); GET_SET_INT(belle_sdp_mime_parameter,max_ptime,int); GET_SET_INT(belle_sdp_mime_parameter,media_format,int); GET_SET_STRING(belle_sdp_mime_parameter,type); GET_SET_STRING(belle_sdp_mime_parameter,parameters); belle-sip-1.6.3/src/belle_sip_dict.c000066400000000000000000000075631313437522400173100ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/dict.h" #include "belle-sip/object.h" #include "belle-sip/belle-sip.h" #include "belle_sip_internal.h" #define BELLE_SIP_DICT(obj) BELLE_SIP_CAST(obj,belle_sip_dict_t) static void belle_sip_dict_string_destroy( void* data ) { belle_sip_free(data); } static void* belle_sip_dict_string_clone( const char* str, void* data ) { (void)str; return belle_sip_strdup((const char*)data); } belle_sip_dict_t* belle_sip_dict_create() { return belle_sip_object_new(belle_sip_dict_t); } static void belle_sip_dict_destroy( belle_sip_dict_t* obj) { } void belle_sip_dict_set_int(belle_sip_dict_t* obj, const char* key, int value) { char tmp[30]; snprintf(tmp,sizeof(tmp),"%i",value); belle_sip_dict_set_string(obj, key, tmp); } int belle_sip_dict_get_int(belle_sip_dict_t* obj, const char* key, int default_value) { const char *str=belle_sip_object_data_get(BELLE_SIP_OBJECT(obj),key); if (str!=NULL) { int ret=0; if (strstr(str,"0x")==str){ sscanf(str,"%x",&ret); }else ret=atoi(str); return ret; } else return default_value; } void belle_sip_dict_set_string(belle_sip_dict_t* obj, const char*key, const char*value) { belle_sip_object_data_set( BELLE_SIP_OBJECT(obj), key, (void*)belle_sip_strdup(value), belle_sip_dict_string_destroy ); } const char* belle_sip_dict_get_string(belle_sip_dict_t* obj, const char* key, const char* default_value) { void* data = belle_sip_object_data_get( BELLE_SIP_OBJECT(obj), key ); if( data ) return (const char *)data; else return default_value; } void belle_sip_dict_set_int64(belle_sip_dict_t* obj, const char* key, int64_t value) { char tmp[30]; #if defined (_MSC_VER) snprintf(tmp,sizeof(tmp),"%I64d",value); #else snprintf(tmp,sizeof(tmp),"%" PRId64"",value); #endif belle_sip_dict_set_string(obj,key,tmp); } int64_t belle_sip_dict_get_int64(belle_sip_dict_t* obj, const char* key, int64_t default_value) { const char *str= belle_sip_object_data_get( BELLE_SIP_OBJECT(obj), key ); if (str!=NULL) { #ifdef _WIN32 return (int64_t)_atoi64(str); #else return atoll(str); #endif } else return default_value; } int belle_sip_dict_remove(belle_sip_dict_t* obj, const char*key) { return belle_sip_object_data_remove(BELLE_SIP_OBJECT(obj), key); } void belle_sip_dict_clone( const belle_sip_dict_t* src, belle_sip_dict_t* dst) { belle_sip_dict_clear(dst); belle_sip_dict_merge(src, dst); } void belle_sip_dict_merge( const belle_sip_dict_t* src, belle_sip_dict_t* dst) { belle_sip_object_data_merge(BELLE_SIP_OBJECT(src), BELLE_SIP_OBJECT(dst), belle_sip_dict_string_clone); } int belle_sip_dict_haskey(const belle_sip_dict_t* obj, const char* key) { return belle_sip_object_data_exists(BELLE_SIP_OBJECT(obj), key); } void belle_sip_dict_foreach(const belle_sip_dict_t* obj, void (*apply_func)(const char*, void*, void*), void* userdata) { belle_sip_object_data_foreach(BELLE_SIP_OBJECT(obj),apply_func, userdata); } void belle_sip_dict_clear(belle_sip_dict_t* obj) { belle_sip_object_data_clear(BELLE_SIP_OBJECT(obj)); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_dict_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_dict_t, belle_sip_object_t, belle_sip_dict_destroy, NULL, NULL, TRUE); belle-sip-1.6.3/src/belle_sip_headers_impl.c000066400000000000000000002527461313437522400210260ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/headers.h" #include "belle-sip/parameters.h" #include #include #include #include "grammars/belle_sip_messageLexer.h" #include "grammars/belle_sip_messageParser.h" #include "belle_sip_internal.h" #include "listeningpoint_internal.h" /************************ * header ***********************/ GET_SET_STRING(belle_sip_header,name); #define PROTO_SIP 0x1 #define PROTO_HTTP 0x1<<1 typedef belle_sip_header_t* (*header_parse_func)(const char*) ; struct header_name_func_pair { int protocol; const char* name; header_parse_func func; }; static struct header_name_func_pair header_table[] = { {PROTO_SIP, "m", (header_parse_func)belle_sip_header_contact_parse} ,{PROTO_SIP, BELLE_SIP_CONTACT, (header_parse_func)belle_sip_header_contact_parse} ,{PROTO_SIP, "f", (header_parse_func)belle_sip_header_from_parse} ,{PROTO_SIP, BELLE_SIP_FROM, (header_parse_func)belle_sip_header_from_parse} ,{PROTO_SIP, "t", (header_parse_func)belle_sip_header_to_parse} ,{PROTO_SIP, BELLE_SIP_TO, (header_parse_func)belle_sip_header_to_parse} ,{PROTO_SIP, "d", (header_parse_func)belle_sip_header_diversion_parse} ,{PROTO_SIP, BELLE_SIP_DIVERSION, (header_parse_func)belle_sip_header_diversion_parse} ,{PROTO_SIP, "i", (header_parse_func)belle_sip_header_call_id_parse} ,{PROTO_SIP, BELLE_SIP_CALL_ID, (header_parse_func)belle_sip_header_call_id_parse} ,{PROTO_SIP, "l", (header_parse_func)belle_sip_header_content_length_parse} ,{PROTO_SIP|PROTO_HTTP, BELLE_SIP_CONTENT_LENGTH, (header_parse_func)belle_sip_header_content_length_parse} ,{PROTO_SIP, "c", (header_parse_func)belle_sip_header_content_type_parse} ,{PROTO_SIP|PROTO_HTTP, BELLE_SIP_CONTENT_TYPE, (header_parse_func)belle_sip_header_content_type_parse} ,{PROTO_SIP, BELLE_SIP_CSEQ, (header_parse_func)belle_sip_header_cseq_parse} ,{PROTO_SIP, BELLE_SIP_ROUTE, (header_parse_func)belle_sip_header_route_parse} ,{PROTO_SIP, BELLE_SIP_RECORD_ROUTE, (header_parse_func)belle_sip_header_record_route_parse} ,{PROTO_SIP, "v", (header_parse_func)belle_sip_header_via_parse} ,{PROTO_SIP, BELLE_SIP_VIA, (header_parse_func)belle_sip_header_via_parse} ,{PROTO_SIP, BELLE_SIP_AUTHORIZATION, (header_parse_func)belle_sip_header_authorization_parse} ,{PROTO_SIP, BELLE_SIP_PROXY_AUTHORIZATION, (header_parse_func)belle_sip_header_proxy_authorization_parse} ,{PROTO_SIP|PROTO_HTTP, BELLE_SIP_WWW_AUTHENTICATE, (header_parse_func)belle_sip_header_www_authenticate_parse} ,{PROTO_SIP|PROTO_HTTP, BELLE_SIP_PROXY_AUTHENTICATE, (header_parse_func)belle_sip_header_proxy_authenticate_parse} ,{PROTO_SIP, BELLE_SIP_MAX_FORWARDS, (header_parse_func)belle_sip_header_max_forwards_parse} ,{PROTO_SIP|PROTO_HTTP, BELLE_SIP_USER_AGENT, (header_parse_func)belle_sip_header_user_agent_parse} ,{PROTO_SIP, BELLE_SIP_EXPIRES, (header_parse_func)belle_sip_header_expires_parse} ,{PROTO_SIP|PROTO_HTTP, BELLE_SIP_ALLOW, (header_parse_func)belle_sip_header_allow_parse} ,{PROTO_SIP, BELLE_SIP_SUBSCRIPTION_STATE, (header_parse_func)belle_sip_header_subscription_state_parse} ,{PROTO_SIP, BELLE_SIP_SERVICE_ROUTE, (header_parse_func)belle_sip_header_service_route_parse} ,{PROTO_SIP, BELLE_SIP_REFER_TO, (header_parse_func)belle_sip_header_refer_to_parse} ,{PROTO_SIP, BELLE_SIP_REFERRED_BY, (header_parse_func)belle_sip_header_referred_by_parse} ,{PROTO_SIP, BELLE_SIP_REPLACES, (header_parse_func)belle_sip_header_replaces_parse} ,{PROTO_SIP, BELLE_SIP_DATE, (header_parse_func)belle_sip_header_date_parse} ,{PROTO_SIP, BELLE_SIP_P_PREFERRED_IDENTITY, (header_parse_func)belle_sip_header_p_preferred_identity_parse} ,{PROTO_SIP, BELLE_SIP_PRIVACY, (header_parse_func)belle_sip_header_privacy_parse} ,{PROTO_SIP, BELLE_SIP_EVENT, (header_parse_func)belle_sip_header_event_parse} ,{PROTO_SIP, BELLE_SIP_SUPPORTED, (header_parse_func)belle_sip_header_supported_parse} ,{PROTO_SIP, "k", (header_parse_func)belle_sip_header_supported_parse} ,{PROTO_SIP, BELLE_SIP_CONTENT_DISPOSITION, (header_parse_func)belle_sip_header_content_disposition_parse} ,{PROTO_SIP, BELLE_SIP_ACCEPT, (header_parse_func)belle_sip_header_accept_parse} ,{PROTO_SIP, BELLE_SIP_REASON, (header_parse_func)belle_sip_header_reason_parse} ,{PROTO_SIP, BELLE_SIP_AUTHENTICATION_INFO, (header_parse_func)belle_sip_header_authentication_info_parse} }; static belle_sip_header_t* belle_header_create(const char* name,const char* value,int protocol) { size_t i; belle_sip_header_t* ret; size_t elements =sizeof(header_table)/sizeof(struct header_name_func_pair); if (!name || name[0]=='\0') { belle_sip_error("Cannot create header without name"); return NULL; } for(i=0;iname) belle_sip_free(header->name); if (header->unparsed_value) belle_sip_free(header->unparsed_value); if (header->next) belle_sip_object_unref(BELLE_SIP_OBJECT(header->next)); } void belle_sip_header_set_next(belle_sip_header_t* header,belle_sip_header_t* next) { if (next) belle_sip_object_ref(next); if (header->next) belle_sip_object_unref(header->next); header->next = next; } belle_sip_header_t* belle_sip_header_get_next(const belle_sip_header_t* header) { return header->next; } const char *belle_sip_header_get_unparsed_value(belle_sip_header_t* obj){ if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(obj,belle_sip_header_extension_t)) { return belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(obj)); } else { char *tmp=belle_sip_object_to_string(obj); char *ret; char *end; if (obj->unparsed_value){ belle_sip_free(obj->unparsed_value); obj->unparsed_value=NULL; } obj->unparsed_value=tmp; ret=tmp; ret+=strlen(obj->name)+1; /* name + semicolon*/ for(;*ret==' ';ret++){};/*skip spaces*/ return ret; } } belle_sip_error_code belle_sip_header_marshal(belle_sip_header_t* header, char* buff, size_t buff_size, size_t *offset) { if (header->name) { return belle_sip_snprintf(buff,buff_size,offset,"%s: ",header->name); } else { belle_sip_warning("no header name found"); return BELLE_SIP_OK; } } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_header_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_header_t,belle_sip_object_t,belle_sip_header_destroy,belle_sip_header_clone,belle_sip_header_marshal,TRUE); BELLE_SIP_PARSE(header) /************************ * header_address ***********************/ struct _belle_sip_header_address { belle_sip_parameters_t base; char* displayname; belle_sip_uri_t* uri; belle_generic_uri_t* absolute_uri; }; static void belle_sip_header_address_init(belle_sip_header_address_t* object){ belle_sip_parameters_init((belle_sip_parameters_t*)object); /*super*/ } static void belle_sip_header_address_destroy(belle_sip_header_address_t* address) { if (address->displayname) belle_sip_free(address->displayname); if (address->uri) belle_sip_object_unref(address->uri); if (address->absolute_uri) belle_sip_object_unref(address->absolute_uri); } static void belle_sip_header_address_clone(belle_sip_header_address_t *addr, const belle_sip_header_address_t *orig){ CLONE_STRING(belle_sip_header_address,displayname,addr,orig) if (belle_sip_header_address_get_uri(orig)) { belle_sip_header_address_set_uri(addr,BELLE_SIP_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(belle_sip_header_address_get_uri(orig))))); } if (belle_sip_header_address_get_absolute_uri(orig)) { belle_sip_header_address_set_absolute_uri(addr,BELLE_GENERIC_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(belle_sip_header_address_get_absolute_uri(orig))))); } } static belle_sip_error_code _belle_sip_header_address_marshal(belle_sip_header_address_t* header, char* buff, size_t buff_size, size_t *offset, int force_angle_quote) { belle_sip_error_code error=BELLE_SIP_OK; /*1 display name*/ if (header->displayname) { char* escaped_display_name = belle_sip_display_name_to_backslashed_escaped_string(header->displayname); error=belle_sip_snprintf(buff,buff_size,offset,"\"%s\" ",escaped_display_name); belle_sip_free(escaped_display_name); if (error!=BELLE_SIP_OK) return error; } if (header->uri || header->absolute_uri) { /*cases where < is required*/ if (force_angle_quote || header->displayname || header->absolute_uri || belle_sip_parameters_get_parameter_names((belle_sip_parameters_t*)header->uri) || belle_sip_uri_get_header_names(header->uri) || belle_sip_parameters_get_parameter_names(&header->base)) { error=belle_sip_snprintf(buff,buff_size,offset,"%s","<"); if (error!=BELLE_SIP_OK) return error; } if (header->uri) { error=belle_sip_uri_marshal(header->uri,buff,buff_size,offset); } else { error=belle_generic_uri_marshal(header->absolute_uri,buff,buff_size,offset); } if (error!=BELLE_SIP_OK) return error; if (force_angle_quote || header->displayname || header->absolute_uri || belle_sip_parameters_get_parameter_names((belle_sip_parameters_t*)header->uri) || belle_sip_uri_get_header_names(header->uri) || belle_sip_parameters_get_parameter_names(&header->base)) { error=belle_sip_snprintf(buff,buff_size,offset,"%s",">"); if (error!=BELLE_SIP_OK) return error; } } error=belle_sip_parameters_marshal(&header->base,buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; return error; } belle_sip_error_code belle_sip_header_address_marshal(belle_sip_header_address_t* header, char* buff, size_t buff_size, size_t *offset){ return _belle_sip_header_address_marshal(header, buff, buff_size, offset, FALSE); } BELLE_SIP_NEW_HEADER(header_address,parameters,"header_address") BELLE_SIP_PARSE(header_address) GET_SET_STRING(belle_sip_header_address,displayname); void belle_sip_header_address_set_quoted_displayname(belle_sip_header_address_t* address,const char* value) { if (address->displayname != NULL) belle_sip_free(address->displayname); if (strlen(value)>2) address->displayname=_belle_sip_str_dup_and_unquote_string(value); else address->displayname=NULL; } belle_sip_uri_t* belle_sip_header_address_get_uri(const belle_sip_header_address_t* address) { return address->uri; } void belle_sip_header_address_set_uri(belle_sip_header_address_t* address, belle_sip_uri_t* uri) { if (uri) belle_sip_object_ref(uri); if (address->uri){ belle_sip_object_unref(address->uri); } address->uri=uri; if (address->absolute_uri && uri) { belle_sip_warning("sip absolute uri [%p] already set for header_address [%p], cleaning it",address->absolute_uri, address); belle_sip_header_address_set_absolute_uri(address,NULL); } } belle_generic_uri_t* belle_sip_header_address_get_absolute_uri(const belle_sip_header_address_t* address) { return address->absolute_uri; } void belle_sip_header_address_set_absolute_uri(belle_sip_header_address_t* address, belle_generic_uri_t* absolute_uri) { belle_sip_object_ref(absolute_uri); if (address->absolute_uri){ belle_sip_object_unref(address->absolute_uri); } address->absolute_uri=absolute_uri; if (address->uri && absolute_uri) { belle_sip_warning("sip uri [%p] already set for header_address [%p], cleaning it",address->uri, address); belle_sip_header_address_set_uri(address,NULL); } } belle_sip_header_address_t* belle_sip_header_address_create(const char* display, belle_sip_uri_t* uri) { belle_sip_header_address_t* address = belle_sip_header_address_new(); belle_sip_header_address_set_displayname(address,display); belle_sip_header_address_set_uri(address,uri); return address; } belle_sip_header_address_t* belle_sip_header_address_create2(const char* display, belle_generic_uri_t* uri) { belle_sip_header_address_t* address = belle_sip_header_address_new(); belle_sip_header_address_set_displayname(address,display); belle_sip_header_address_set_absolute_uri(address,uri); return address; } /*fast header address implemenation*/ typedef belle_sip_header_address_t belle_sip_fast_header_address_t; #define belle_sip_fast_header_address_parse belle_sip_header_address_fast_parse BELLE_SIP_PARSE(fast_header_address) /****************************** * Extension header inherits from header * ******************************/ struct _belle_sip_header_allow { belle_sip_header_t header; const char* method; }; static void belle_sip_header_allow_clone(belle_sip_header_allow_t *allow, const belle_sip_header_allow_t *orig){ CLONE_STRING(belle_sip_header_allow,method,allow,orig) } static void belle_sip_header_allow_destroy(belle_sip_header_allow_t* allow) { if (allow->method) belle_sip_free((void*)allow->method); } belle_sip_error_code belle_sip_header_allow_marshal(belle_sip_header_allow_t* allow, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(allow), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s",allow->method); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_allow,header,"Allow") BELLE_SIP_PARSE(header_allow) belle_sip_header_allow_t* belle_sip_header_allow_create (const char* methods) { belle_sip_header_allow_t* allow = belle_sip_header_allow_new(); belle_sip_header_allow_set_method(allow,methods); return allow; } GET_SET_STRING(belle_sip_header_allow,method); /************************ * header_contact ***********************/ struct _belle_sip_header_contact { belle_sip_header_address_t address; unsigned char wildcard; unsigned char automatic; unsigned char unknown; unsigned char pad[1]; }; void belle_sip_header_contact_destroy(belle_sip_header_contact_t* contact) { } void belle_sip_header_contact_clone(belle_sip_header_contact_t *contact, const belle_sip_header_contact_t *orig){ contact->wildcard=orig->wildcard; contact->automatic=orig->automatic; } belle_sip_error_code belle_sip_header_contact_marshal(belle_sip_header_contact_t* contact, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(contact), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; if (contact->wildcard) { error=belle_sip_snprintf(buff,buff_size,offset,"%s","*"); } else { error=belle_sip_header_address_marshal(&contact->address, buff, buff_size, offset); } return error; } BELLE_SIP_NEW_HEADER(header_contact,header_address,BELLE_SIP_CONTACT) BELLE_SIP_PARSE(header_contact) belle_sip_header_contact_t* belle_sip_header_contact_create (const belle_sip_header_address_t* contact) { belle_sip_header_contact_t* header = belle_sip_header_contact_new(); _belle_sip_object_copy(BELLE_SIP_OBJECT(header),BELLE_SIP_OBJECT(contact)); belle_sip_header_set_next(BELLE_SIP_HEADER(header),NULL); /*make sure only one header is kept*/ belle_sip_header_set_name(BELLE_SIP_HEADER(header),BELLE_SIP_CONTACT); /*restaure header name*/ return header; } GET_SET_INT_PARAM_PRIVATE(belle_sip_header_contact,expires,int,_) GET_SET_INT_PARAM_PRIVATE(belle_sip_header_contact,q,float,_); GET_SET_BOOL(belle_sip_header_contact,wildcard,is); int belle_sip_header_contact_set_expires(belle_sip_header_contact_t* contact, int expires) { if (expires < 0 ) { belle_sip_error("bad expires value [%i] for contact",expires); return -1; } _belle_sip_header_contact_set_expires(contact,expires); return 0; } int belle_sip_header_contact_set_qvalue(belle_sip_header_contact_t* contact, float qValue) { if (qValue != -1 && qValue < 0 && qValue >1) { belle_sip_error("bad q value [%f] for contact",qValue); return -1; } _belle_sip_header_contact_set_q(contact,qValue); return 0; } float belle_sip_header_contact_get_qvalue(const belle_sip_header_contact_t* contact) { return belle_sip_header_contact_get_q(contact); } unsigned int belle_sip_header_contact_equals(const belle_sip_header_contact_t* a,const belle_sip_header_contact_t* b) { if (!a | !b) return 0; return belle_sip_uri_equals(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(a)) ,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(b))); } unsigned int belle_sip_header_contact_not_equals(const belle_sip_header_contact_t* a,const belle_sip_header_contact_t* b) { return !belle_sip_header_contact_equals(a,b); } void belle_sip_header_contact_set_automatic(belle_sip_header_contact_t *a, int enabled){ a->automatic=enabled; } int belle_sip_header_contact_get_automatic(const belle_sip_header_contact_t *a){ return a->automatic; } void belle_sip_header_contact_set_unknown(belle_sip_header_contact_t *a, int value){ a->unknown=value; } int belle_sip_header_contact_is_unknown(const belle_sip_header_contact_t *a){ return a->unknown; } /************************** * From header object inherent from header_address **************************** */ #define BELLE_SIP_FROM_LIKE_MARSHAL(header,force_angle_quote) \ belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(header), buff, buff_size, offset);\ if (error!=BELLE_SIP_OK) return error;\ error=_belle_sip_header_address_marshal(&header->address, buff, buff_size, offset, force_angle_quote); \ if (error!=BELLE_SIP_OK) return error;\ return error; struct _belle_sip_header_from { belle_sip_header_address_t address; }; static void belle_sip_header_from_destroy(belle_sip_header_from_t* from) { } static void belle_sip_header_from_clone(belle_sip_header_from_t* from, const belle_sip_header_from_t* cloned) { } belle_sip_error_code belle_sip_header_from_marshal(belle_sip_header_from_t* from, char* buff, size_t buff_size, size_t *offset) { BELLE_SIP_FROM_LIKE_MARSHAL(from,FALSE); } belle_sip_header_from_t* belle_sip_header_from_create2(const char *uri, const char *tag){ belle_sip_header_address_t* address = belle_sip_header_address_parse(uri); if (address) { belle_sip_header_from_t* from = belle_sip_header_from_create(address,tag); belle_sip_object_unref(address); return from; } else return NULL; } belle_sip_header_from_t* belle_sip_header_from_create(const belle_sip_header_address_t* address, const char *tag) { belle_sip_header_from_t* header= belle_sip_header_from_new(); belle_sip_uri_t* uri; _belle_sip_object_copy((belle_sip_object_t*)header,(belle_sip_object_t*)address); /*clear unwanted uri components*/ if ((uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(header)))) { belle_sip_parameters_t* params=BELLE_SIP_PARAMETERS(uri); belle_sip_parameters_remove_parameter(params,"lr"); belle_sip_parameters_remove_parameter(params,"ttl"); belle_sip_parameters_remove_parameter(params,"method"); belle_sip_parameters_remove_parameter(params,"maddr"); belle_sip_parameters_remove_parameter(params,"transport"); belle_sip_uri_set_port(uri,0); belle_sip_uri_headers_clean(uri); } belle_sip_header_set_next(BELLE_SIP_HEADER(header),NULL); /*make sure only one header is kept*/ belle_sip_header_set_name(BELLE_SIP_HEADER(header),BELLE_SIP_FROM); /*restore header name*/ if (tag) belle_sip_header_from_set_tag(header,tag); return header; } BELLE_SIP_NEW_HEADER(header_from,header_address,BELLE_SIP_FROM) BELLE_SIP_PARSE(header_from) GET_SET_STRING_PARAM2(belle_sip_header_from,tag,raw_tag); void belle_sip_header_from_set_random_tag(belle_sip_header_from_t *obj){ char tmp[BELLE_SIP_TAG_LENGTH]; belle_sip_header_from_set_raw_tag(obj,belle_sip_random_token(tmp,sizeof(tmp))); } void belle_sip_header_from_set_tag(belle_sip_header_from_t *obj, const char *tag){ if (tag==BELLE_SIP_RANDOM_TAG) belle_sip_header_from_set_random_tag(obj); else belle_sip_header_from_set_raw_tag(obj,tag); } const char *belle_sip_header_from_get_tag(const belle_sip_header_from_t *obj){ return belle_sip_header_from_get_raw_tag(obj); } /************************** * To header object inherits from header_address **************************** */ struct _belle_sip_header_to { belle_sip_header_address_t address; }; static void belle_sip_header_to_destroy(belle_sip_header_to_t* to) { } void belle_sip_header_to_clone(belle_sip_header_to_t *contact, const belle_sip_header_to_t *orig){ } belle_sip_error_code belle_sip_header_to_marshal(belle_sip_header_to_t* to, char* buff, size_t buff_size, size_t *offset) { BELLE_SIP_FROM_LIKE_MARSHAL(to,FALSE) } BELLE_SIP_NEW_HEADER(header_to,header_address,BELLE_SIP_TO) BELLE_SIP_PARSE(header_to) GET_SET_STRING_PARAM2(belle_sip_header_to,tag,raw_tag); belle_sip_header_to_t* belle_sip_header_to_create2(const char *uri, const char *tag){ belle_sip_header_address_t* address = belle_sip_header_address_parse(uri); if (address) { belle_sip_header_to_t* to = belle_sip_header_to_create(address,tag); belle_sip_object_unref(address); return to; } else return NULL; } belle_sip_header_to_t* belle_sip_header_to_create(const belle_sip_header_address_t* address, const char *tag) { belle_sip_header_to_t* header= belle_sip_header_to_new(); belle_sip_uri_t* uri; _belle_sip_object_copy((belle_sip_object_t*)header,(belle_sip_object_t*)address); /*clear unwanted uri components*/ if ((uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(header)))) { belle_sip_parameters_t* params=BELLE_SIP_PARAMETERS(uri); belle_sip_parameters_remove_parameter(params,"lr"); belle_sip_parameters_remove_parameter(params,"ttl"); belle_sip_parameters_remove_parameter(params,"method"); belle_sip_parameters_remove_parameter(params,"maddr"); belle_sip_parameters_remove_parameter(params,"transport"); belle_sip_uri_set_port(uri,0); belle_sip_uri_headers_clean(uri); } belle_sip_header_set_next(BELLE_SIP_HEADER(header),NULL); /*make sure only one header is kept*/ belle_sip_header_set_name(BELLE_SIP_HEADER(header),BELLE_SIP_TO); /*restaure header name*/ if (tag) belle_sip_header_to_set_tag(header,tag); return header; } void belle_sip_header_to_set_random_tag(belle_sip_header_to_t *obj){ char tmp[8]; /*not less than 32bit */ belle_sip_header_to_set_tag(obj,belle_sip_random_token(tmp,sizeof(tmp))); } void belle_sip_header_to_set_tag(belle_sip_header_to_t *obj, const char *tag){ if (tag==BELLE_SIP_RANDOM_TAG) belle_sip_header_to_set_random_tag(obj); else belle_sip_header_to_set_raw_tag(obj,tag); } const char *belle_sip_header_to_get_tag(const belle_sip_header_to_t *obj){ return belle_sip_header_to_get_raw_tag(obj); } /************************** * Diversion header object inherits from header_address **************************** */ struct _belle_sip_header_diversion { belle_sip_header_address_t address; }; static void belle_sip_header_diversion_destroy(belle_sip_header_diversion_t* diversion) { } void belle_sip_header_diversion_clone(belle_sip_header_diversion_t *contact, const belle_sip_header_diversion_t *orig){ } belle_sip_error_code belle_sip_header_diversion_marshal(belle_sip_header_diversion_t* diversion, char* buff, size_t buff_size, size_t *offset) { BELLE_SIP_FROM_LIKE_MARSHAL(diversion,FALSE) } BELLE_SIP_NEW_HEADER(header_diversion,header_address,BELLE_SIP_DIVERSION) BELLE_SIP_PARSE(header_diversion) GET_SET_STRING_PARAM2(belle_sip_header_diversion,tag,raw_tag); belle_sip_header_diversion_t* belle_sip_header_diversion_create2(const char *uri, const char *tag){ belle_sip_header_address_t* address = belle_sip_header_address_parse(uri); if (address) { belle_sip_header_diversion_t* diversion = belle_sip_header_diversion_create(address,tag); belle_sip_object_unref(address); return diversion; } else return NULL; } belle_sip_header_diversion_t* belle_sip_header_diversion_create(const belle_sip_header_address_t* address, const char *tag) { belle_sip_header_diversion_t* header= belle_sip_header_diversion_new(); belle_sip_uri_t* uri; _belle_sip_object_copy((belle_sip_object_t*)header,(belle_sip_object_t*)address); /*clear unwanted uri components*/ if ((uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(header)))) { belle_sip_parameters_t* params=BELLE_SIP_PARAMETERS(uri); belle_sip_parameters_remove_parameter(params,"lr"); belle_sip_parameters_remove_parameter(params,"ttl"); belle_sip_parameters_remove_parameter(params,"method"); belle_sip_parameters_remove_parameter(params,"maddr"); belle_sip_parameters_remove_parameter(params,"transport"); belle_sip_uri_set_port(uri,0); belle_sip_uri_headers_clean(uri); } belle_sip_header_set_name(BELLE_SIP_HEADER(header),BELLE_SIP_DIVERSION); /*restaure header name*/ if (tag) belle_sip_header_diversion_set_tag(header,tag); return header; } void belle_sip_header_diversion_set_random_tag(belle_sip_header_diversion_t *obj){ char tmp[8]; /*not less than 32bit */ belle_sip_header_diversion_set_tag(obj,belle_sip_random_token(tmp,sizeof(tmp))); } void belle_sip_header_diversion_set_tag(belle_sip_header_diversion_t *obj, const char *tag){ if (tag==BELLE_SIP_RANDOM_TAG) belle_sip_header_diversion_set_random_tag(obj); else belle_sip_header_diversion_set_raw_tag(obj,tag); } const char *belle_sip_header_diversion_get_tag(const belle_sip_header_diversion_t *obj){ return belle_sip_header_diversion_get_raw_tag(obj); } /****************************** * User-Agent header inherits from header * ******************************/ struct _belle_sip_header_user_agent { belle_sip_header_t header; belle_sip_list_t* products; }; static void belle_sip_header_user_agent_destroy(belle_sip_header_user_agent_t* user_agent) { belle_sip_header_user_agent_set_products(user_agent,NULL); } static void belle_sip_header_user_agent_clone(belle_sip_header_user_agent_t* user_agent, const belle_sip_header_user_agent_t* orig){ belle_sip_list_t* list=orig->products; for(;list!=NULL;list=list->next){ belle_sip_header_user_agent_add_product(user_agent,(const char *)list->data); } } belle_sip_error_code belle_sip_header_user_agent_marshal(belle_sip_header_user_agent_t* user_agent, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=BELLE_SIP_OK; belle_sip_list_t* list = user_agent->products; error=belle_sip_header_marshal(BELLE_SIP_HEADER(user_agent), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; for(;list!=NULL;list=list->next){ error=belle_sip_snprintf(buff,buff_size,offset,list==user_agent->products ? "%s" : " %s",(const char *)list->data); if (error!=BELLE_SIP_OK) return error; } return error; } BELLE_SIP_NEW_HEADER(header_user_agent,header,"User-Agent") BELLE_SIP_PARSE(header_user_agent) belle_sip_list_t* belle_sip_header_user_agent_get_products(const belle_sip_header_user_agent_t* user_agent) { return user_agent->products; } void belle_sip_header_user_agent_set_products(belle_sip_header_user_agent_t* user_agent,belle_sip_list_t* products) { belle_sip_list_t* list; if (user_agent->products) { for (list=user_agent->products;list !=NULL; list=list->next) { belle_sip_free((void*)list->data); } belle_sip_list_free(user_agent->products); } user_agent->products=products; } void belle_sip_header_user_agent_add_product(belle_sip_header_user_agent_t* user_agent,const char* product) { user_agent->products = belle_sip_list_append(user_agent->products ,belle_sip_strdup(product)); } int belle_sip_header_user_agent_get_products_as_string(const belle_sip_header_user_agent_t* user_agent,char* value,unsigned int value_size) { size_t result = 0; belle_sip_error_code error=BELLE_SIP_OK; belle_sip_list_t* list = user_agent->products; for(;list!=NULL;list=list->next){ error=belle_sip_snprintf(value,value_size,&result,"%s ",(const char *)list->data); if (error!=BELLE_SIP_OK) return -1; } if (result>0) value[result-1]='\0'; /*remove last space */ return (int)result-1; } /************************** * Via header object inherits from parameters **************************** */ struct _belle_sip_header_via { belle_sip_parameters_t params_list; char* protocol; char* transport; char* host; int port; char* received; }; static void belle_sip_header_via_destroy(belle_sip_header_via_t* via) { if (via->protocol) belle_sip_free(via->protocol); if (via->transport) belle_sip_free(via->transport); if (via->host) belle_sip_free(via->host); DESTROY_STRING(via,received) } static void belle_sip_header_via_clone(belle_sip_header_via_t* via, const belle_sip_header_via_t*orig){ CLONE_STRING(belle_sip_header_via,protocol,via,orig) CLONE_STRING(belle_sip_header_via,transport,via,orig) CLONE_STRING(belle_sip_header_via,host,via,orig) CLONE_STRING(belle_sip_header_via,received,via,orig) via->port=orig->port; } belle_sip_error_code belle_sip_header_via_marshal(belle_sip_header_via_t* via, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(via), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s/%s",via->protocol,via->transport); if (error!=BELLE_SIP_OK) return error; if (via->host) { if (strchr(via->host,':')) { /*ipv6*/ error=belle_sip_snprintf(buff,buff_size,offset," [%s]",via->host); } else { error=belle_sip_snprintf(buff,buff_size,offset," %s",via->host); } if (error!=BELLE_SIP_OK) return error; } else { belle_sip_warning("no host found in this via"); } if (via->port > 0) { error=belle_sip_snprintf(buff,buff_size,offset,":%i",via->port); if (error!=BELLE_SIP_OK) return error; } if (via->received) { error=belle_sip_snprintf(buff,buff_size,offset,";received=%s",via->received); if (error!=BELLE_SIP_OK) return error; } error=belle_sip_parameters_marshal(&via->params_list, buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; return error; } belle_sip_header_via_t* belle_sip_header_via_create(const char *host, int port, const char *transport, const char *branch){ belle_sip_header_via_t *via=belle_sip_header_via_new(); via->host=belle_sip_strdup(host); via->port=port; via->transport=belle_sip_strdup(transport); via->protocol=belle_sip_strdup("SIP/2.0"); belle_sip_header_via_set_branch(via,branch); return via; } BELLE_SIP_NEW_HEADER(header_via,parameters,BELLE_SIP_VIA) BELLE_SIP_PARSE(header_via) GET_SET_STRING(belle_sip_header_via,protocol); GET_SET_STRING(belle_sip_header_via,transport); GET_SET_STRING(belle_sip_header_via,host); GET_SET_STRING(belle_sip_header_via,received); GET_SET_INT_PRIVATE(belle_sip_header_via,port,int,_); GET_SET_STRING_PARAM(belle_sip_header_via,branch); GET_SET_STRING_PARAM(belle_sip_header_via,maddr); GET_SET_INT_PARAM_PRIVATE(belle_sip_header_via,rport,int,_) GET_SET_INT_PARAM_PRIVATE(belle_sip_header_via,ttl,int,_) int belle_sip_header_via_set_rport (belle_sip_header_via_t* obj,int value) { if (value == -1) { belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(obj),"rport",NULL); return 0; } if (value>0 && value<65536) { _belle_sip_header_via_set_rport(obj,value); return 0; } else { belle_sip_error("bad rport value [%i] for via",value); return -1; } } int belle_sip_header_via_set_ttl (belle_sip_header_via_t* obj,int value) { if (value ==-1 || (value>0 && value<=255)) { _belle_sip_header_via_set_ttl(obj,value); return 0; } else { belle_sip_error("bad ttl value [%i] for via",value); return -1; } } int belle_sip_header_via_set_port (belle_sip_header_via_t* obj,int value) { if (value ==-1 || (value>0 && value<65536)) { _belle_sip_header_via_set_port(obj,value); return 0; } else { belle_sip_error("bad port value [%i] for via",value); return -1; } } int belle_sip_header_via_get_listening_port(const belle_sip_header_via_t *via){ int ret=belle_sip_header_via_get_port(via); if (ret==0) ret=belle_sip_listening_point_get_well_known_port(via->transport); return ret; } const char* belle_sip_header_via_get_transport_lowercase(const belle_sip_header_via_t* via) { if (strcasecmp("udp",via->transport)==0) return "udp"; else if (strcasecmp("tcp",via->transport)==0) return "tcp"; else if (strcasecmp("tls",via->transport)==0) return "tls"; else if (strcasecmp("dtls",via->transport)==0) return "dtls"; else { belle_sip_warning("Cannot convert [%s] to lower case",via->transport); return via->transport; } } /************************** * call_id header object inherits from object **************************** */ struct _belle_sip_header_call_id { belle_sip_header_t header; const char* call_id; }; static void belle_sip_header_call_id_destroy(belle_sip_header_call_id_t* call_id) { if (call_id->call_id) belle_sip_free((void*)call_id->call_id); } static void belle_sip_header_call_id_clone(belle_sip_header_call_id_t* call_id,const belle_sip_header_call_id_t *orig){ CLONE_STRING(belle_sip_header_call_id,call_id,call_id,orig); } belle_sip_error_code belle_sip_header_call_id_marshal(belle_sip_header_call_id_t* call_id, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(call_id), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s",call_id->call_id); if (error!=BELLE_SIP_OK) return error; return error; } unsigned int belle_sip_header_call_id_equals(const belle_sip_header_call_id_t* a,const belle_sip_header_call_id_t* b) { return strcasecmp(a->call_id,b->call_id) == 0; } BELLE_SIP_NEW_HEADER(header_call_id,header,BELLE_SIP_CALL_ID) BELLE_SIP_PARSE(header_call_id) GET_SET_STRING(belle_sip_header_call_id,call_id); /************************** * cseq header object inherent from object **************************** */ struct _belle_sip_header_cseq { belle_sip_header_t header; char* method; unsigned int seq_number; }; static void belle_sip_header_cseq_destroy(belle_sip_header_cseq_t* cseq) { if (cseq->method) belle_sip_free(cseq->method); } static void belle_sip_header_cseq_clone(belle_sip_header_cseq_t* cseq, const belle_sip_header_cseq_t *orig) { CLONE_STRING(belle_sip_header_cseq,method,cseq,orig) cseq->seq_number=orig->seq_number; } belle_sip_error_code belle_sip_header_cseq_marshal(belle_sip_header_cseq_t* cseq, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(cseq), buff,buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%i %s",cseq->seq_number,cseq->method); if (error!=BELLE_SIP_OK) return error; return error; } belle_sip_header_cseq_t * belle_sip_header_cseq_create(unsigned int number, const char *method){ belle_sip_header_cseq_t *cseq=belle_sip_header_cseq_new(); belle_sip_header_cseq_set_method(cseq,method); cseq->seq_number=number; return cseq; } BELLE_SIP_NEW_HEADER(header_cseq,header,BELLE_SIP_CSEQ) BELLE_SIP_PARSE(header_cseq) GET_SET_STRING(belle_sip_header_cseq,method); GET_SET_INT(belle_sip_header_cseq,seq_number,unsigned int) /************************** * content type header object inherent from parameters **************************** */ struct _belle_sip_header_content_type { belle_sip_parameters_t params_list; const char* type; const char* subtype; }; static void belle_sip_header_content_type_destroy(belle_sip_header_content_type_t* content_type) { if (content_type->type) belle_sip_free((void*)content_type->type); if (content_type->subtype) belle_sip_free((void*)content_type->subtype); } static void belle_sip_header_content_type_clone(belle_sip_header_content_type_t* content_type, const belle_sip_header_content_type_t* orig){ CLONE_STRING(belle_sip_header_content_type,type,content_type,orig); CLONE_STRING(belle_sip_header_content_type,subtype,content_type,orig); } belle_sip_error_code belle_sip_header_content_type_marshal(belle_sip_header_content_type_t* content_type, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(content_type), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s/%s",content_type->type, content_type->subtype); if (error!=BELLE_SIP_OK) return error; error=belle_sip_parameters_marshal(&content_type->params_list, buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_content_type,parameters,BELLE_SIP_CONTENT_TYPE) BELLE_SIP_PARSE(header_content_type) belle_sip_header_content_type_t* belle_sip_header_content_type_create (const char* type,const char* sub_type) { belle_sip_header_content_type_t* header = belle_sip_header_content_type_new(); belle_sip_header_content_type_set_type(header,type); belle_sip_header_content_type_set_subtype(header,sub_type); return header; } GET_SET_STRING(belle_sip_header_content_type,type); GET_SET_STRING(belle_sip_header_content_type,subtype); /************************** * Route header object inherent from header_address **************************** */ struct _belle_sip_header_route { belle_sip_header_address_t address; }; static void belle_sip_header_route_destroy(belle_sip_header_route_t* route) { } static void belle_sip_header_route_clone(belle_sip_header_route_t* route, const belle_sip_header_route_t* orig) { } belle_sip_error_code belle_sip_header_route_marshal(belle_sip_header_route_t* route, char* buff, size_t buff_size, size_t *offset) { BELLE_SIP_FROM_LIKE_MARSHAL(route,TRUE) } BELLE_SIP_NEW_HEADER(header_route,header_address,BELLE_SIP_ROUTE) BELLE_SIP_PARSE(header_route) belle_sip_header_route_t* belle_sip_header_route_create(const belle_sip_header_address_t* route) { belle_sip_header_route_t* header= belle_sip_header_route_new(); _belle_sip_object_copy((belle_sip_object_t*)header,(belle_sip_object_t*)route); belle_sip_header_set_next(BELLE_SIP_HEADER(header),NULL); /*make sure only one header is kept*/ belle_sip_header_set_name(BELLE_SIP_HEADER(header),BELLE_SIP_ROUTE); /*restore header name*/ return header; } /************************** * Record route header object inherent from header_address **************************** */ struct _belle_sip_header_record_route { belle_sip_header_address_t address; unsigned char auto_outgoing; unsigned char pad[3]; }; static void belle_sip_header_record_route_destroy(belle_sip_header_record_route_t* record_route) { } static void belle_sip_header_record_route_clone(belle_sip_header_record_route_t* record_route, const belle_sip_header_record_route_t* orig ) { } belle_sip_error_code belle_sip_header_record_route_marshal(belle_sip_header_record_route_t* record_route, char* buff, size_t buff_size, size_t *offset) { BELLE_SIP_FROM_LIKE_MARSHAL(record_route,TRUE) } belle_sip_header_record_route_t *belle_sip_header_record_route_new_auto_outgoing() { belle_sip_header_record_route_t *rr = belle_sip_header_record_route_new(); rr->auto_outgoing = TRUE; return rr; } unsigned char belle_sip_header_record_route_get_auto_outgoing(const belle_sip_header_record_route_t *a) { return a->auto_outgoing; } BELLE_SIP_NEW_HEADER(header_record_route,header_address,BELLE_SIP_RECORD_ROUTE) BELLE_SIP_PARSE(header_record_route) /************************** * Service route header object inherent from header_address **************************** */ struct _belle_sip_header_service_route { belle_sip_header_address_t address; }; static void belle_sip_header_service_route_destroy(belle_sip_header_service_route_t* service_route) { } static void belle_sip_header_service_route_clone(belle_sip_header_service_route_t* service_route, const belle_sip_header_service_route_t* orig ) { } belle_sip_error_code belle_sip_header_service_route_marshal(belle_sip_header_service_route_t* service_route, char* buff, size_t buff_size, size_t *offset) { BELLE_SIP_FROM_LIKE_MARSHAL(service_route,TRUE) } BELLE_SIP_NEW_HEADER(header_service_route,header_address,BELLE_SIP_SERVICE_ROUTE) BELLE_SIP_PARSE(header_service_route) /************************** * content length header object inherent from object **************************** */ struct _belle_sip_header_content_length { belle_sip_header_t header; size_t content_length; }; static void belle_sip_header_content_length_destroy(belle_sip_header_content_length_t* content_length) { } static void belle_sip_header_content_length_clone(belle_sip_header_content_length_t* content_length, const belle_sip_header_content_length_t *orig ) { content_length->content_length=orig->content_length; } belle_sip_error_code belle_sip_header_content_length_marshal(belle_sip_header_content_length_t* content_length, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(content_length), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,FORMAT_SIZE_T,content_length->content_length); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_content_length,header,BELLE_SIP_CONTENT_LENGTH) BELLE_SIP_PARSE(header_content_length) GET_SET_INT(belle_sip_header_content_length,content_length,size_t) belle_sip_header_content_length_t* belle_sip_header_content_length_create (size_t content_length) { belle_sip_header_content_length_t* obj; obj = belle_sip_header_content_length_new(); belle_sip_header_content_length_set_content_length(obj,content_length); return obj; } /************************** * Expires header object inherent from header **************************** */ struct _belle_sip_header_expires { belle_sip_header_t header; int expires; }; static void belle_sip_header_expires_destroy(belle_sip_header_expires_t* expires) { } static void belle_sip_header_expires_clone(belle_sip_header_expires_t* expires, const belle_sip_header_expires_t *orig ) { expires->expires=orig->expires; } belle_sip_error_code belle_sip_header_expires_marshal(belle_sip_header_expires_t* expires, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(expires), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%i",expires->expires); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_expires,header,BELLE_SIP_EXPIRES) BELLE_SIP_PARSE(header_expires) GET_SET_INT(belle_sip_header_expires,expires,int) belle_sip_header_expires_t* belle_sip_header_expires_create(int expires) { belle_sip_header_expires_t* obj = belle_sip_header_expires_new(); belle_sip_header_expires_set_expires(obj,expires); return obj; } /****************************** * Extension header hinerite from header * ******************************/ struct _belle_sip_header_extension { belle_sip_header_t header; const char* value; }; static void belle_sip_header_extension_destroy(belle_sip_header_extension_t* extension) { if (extension->value) belle_sip_free((void*)extension->value); } static void belle_sip_header_extension_clone(belle_sip_header_extension_t* extension, const belle_sip_header_extension_t* orig){ CLONE_STRING(belle_sip_header_extension,value,extension,orig) } belle_sip_error_code belle_sip_header_extension_marshal(belle_sip_header_extension_t* extension, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(extension), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; if (extension->value) error=belle_sip_snprintf(buff,buff_size,offset,"%s",extension->value); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_extension,header,NULL) belle_sip_header_extension_t* belle_sip_header_extension_create (const char* name,const char* value) { belle_sip_header_extension_t* ext = belle_sip_header_extension_new(); belle_sip_header_set_name(BELLE_SIP_HEADER(ext),name); belle_sip_header_extension_set_value(ext,value); return ext; } GET_SET_STRING(belle_sip_header_extension,value); /************************** *Authorization header object inherent from parameters **************************** */ #define AUTH_BASE \ belle_sip_parameters_t params_list; \ const char* scheme; \ const char* realm; \ const char* nonce; \ const char* algorithm; \ const char* opaque; #define AUTH_BASE_DESTROY(obj) \ if (obj->scheme) belle_sip_free((void*)obj->scheme);\ if (obj->realm) belle_sip_free((void*)obj->realm);\ if (obj->nonce) belle_sip_free((void*)obj->nonce);\ if (obj->algorithm) belle_sip_free((void*)obj->algorithm);\ if (obj->opaque) belle_sip_free((void*)obj->opaque);\ /*if (obj->params_list) FIXME free list*/ #define AUTH_BASE_CLONE(object_type,dest,src) \ CLONE_STRING(object_type,scheme,dest,src)\ CLONE_STRING(object_type,realm,dest,src)\ CLONE_STRING(object_type,nonce,dest,src)\ CLONE_STRING(object_type,algorithm,dest,src)\ CLONE_STRING(object_type,opaque,dest,src) \ #define AUTH_BASE_MARSHAL(header) \ char* border=" ";\ const belle_sip_list_t* list;\ belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(header), buff, buff_size, offset);\ if (error!=BELLE_SIP_OK) return error;\ list=belle_sip_parameters_get_parameters(&header->params_list);\ if (header->scheme) { \ error=belle_sip_snprintf(buff,buff_size,offset," %s",header->scheme);\ if (error!=BELLE_SIP_OK) return error;\ } else { \ belle_sip_error("missing mandatory scheme"); \ } \ for(;list!=NULL;list=list->next){\ belle_sip_param_pair_t* container = list->data;\ error=belle_sip_snprintf(buff,buff_size,offset,"%s%s=%s",border, container->name,container->value);\ if (error!=BELLE_SIP_OK) return error;\ border=", ";\ }\ if (header->realm) {\ error=belle_sip_snprintf(buff,buff_size,offset,"%srealm=\"%s\"",border,header->realm);\ if (error!=BELLE_SIP_OK) return error;\ border=", ";\ }\ if (header->nonce) {\ error=belle_sip_snprintf(buff,buff_size,offset,"%snonce=\"%s\"",border,header->nonce);\ if (error!=BELLE_SIP_OK) return error;\ border=", ";\ }\ if (header->algorithm) {\ const char* format;\ if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(header,belle_http_header_authorization_t)) {\ format="%salgorithm=\"%s\"";\ } else {\ format="%salgorithm=%s";\ }\ error=belle_sip_snprintf(buff,buff_size,offset,format,border,header->algorithm);\ if (error!=BELLE_SIP_OK) return error;\ border=", ";\ }\ if (header->opaque) {\ error=belle_sip_snprintf(buff,buff_size,offset,"%sopaque=\"%s\"",border,header->opaque);\ if (error!=BELLE_SIP_OK) return error;\ border=", ";\ } struct _belle_sip_header_authorization { AUTH_BASE const char* username; belle_sip_uri_t* uri; const char* response; const char* cnonce; int nonce_count; const char* qop; }; static void belle_sip_header_authorization_destroy(belle_sip_header_authorization_t* authorization) { if (authorization->username) belle_sip_free((void*)authorization->username); if (authorization->uri) { belle_sip_object_unref(authorization->uri); } if (authorization->cnonce) belle_sip_free((void*)authorization->cnonce); AUTH_BASE_DESTROY(authorization) DESTROY_STRING(authorization,response); DESTROY_STRING(authorization,qop); } static void belle_sip_header_authorization_clone(belle_sip_header_authorization_t* authorization, const belle_sip_header_authorization_t *orig ) { AUTH_BASE_CLONE(belle_sip_header_authorization,authorization,orig) CLONE_STRING(belle_sip_header_authorization,username,authorization,orig) if (belle_sip_header_authorization_get_uri(orig)) { belle_sip_header_authorization_set_uri(authorization,BELLE_SIP_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(belle_sip_header_authorization_get_uri(orig))))); } CLONE_STRING(belle_sip_header_authorization,response,authorization,orig) CLONE_STRING(belle_sip_header_authorization,cnonce,authorization,orig) authorization->nonce_count=orig->nonce_count; CLONE_STRING(belle_sip_header_authorization,qop,authorization,orig) } static void belle_sip_header_authorization_init(belle_sip_header_authorization_t* authorization) { } belle_sip_uri_t* belle_sip_header_authorization_get_uri(const belle_sip_header_authorization_t* authorization) { return authorization->uri; } void belle_sip_header_authorization_set_uri(belle_sip_header_authorization_t* authorization, belle_sip_uri_t* uri) { if (uri) belle_sip_object_ref(uri); if (authorization->uri) { belle_sip_object_unref(BELLE_SIP_OBJECT(authorization->uri)); } authorization->uri=uri; } belle_sip_error_code belle_sip_header_authorization_marshal(belle_sip_header_authorization_t* authorization, char* buff, size_t buff_size, size_t *offset) { char nonce_count[10]; AUTH_BASE_MARSHAL(authorization) if (authorization->username) { error=belle_sip_snprintf(buff,buff_size,offset,"%susername=\"%s\"",border,authorization->username); if (error!=BELLE_SIP_OK) return error; border=", "; } if (authorization->uri) { error=belle_sip_snprintf(buff,buff_size,offset,"%s uri=\"",border); if (error!=BELLE_SIP_OK) return error; border=", "; error=belle_sip_uri_marshal(authorization->uri,buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s","\""); if (error!=BELLE_SIP_OK) return error; } if (authorization->response) { error=belle_sip_snprintf(buff,buff_size,offset,"%sresponse=\"%s\"",border,authorization->response); if (error!=BELLE_SIP_OK) return error; border=", "; } if (authorization->cnonce) { error=belle_sip_snprintf(buff,buff_size,offset,"%scnonce=\"%s\"",border,authorization->cnonce); if (error!=BELLE_SIP_OK) return error; border=", "; } if (authorization->nonce_count>0) { belle_sip_header_authorization_get_nonce_count_as_string(authorization,nonce_count); error=belle_sip_snprintf(buff,buff_size,offset,"%snc=%s",border,nonce_count); if (error!=BELLE_SIP_OK) return error; border=", "; } if (authorization->qop) { const char* format; if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(authorization,belle_http_header_authorization_t)) { format="%sqop=\"%s\""; } else { format="%sqop=%s"; } error=belle_sip_snprintf(buff,buff_size,offset,format,border,authorization->qop); if (error!=BELLE_SIP_OK) return error; } return error; } BELLE_SIP_NEW_HEADER(header_authorization,parameters,BELLE_SIP_AUTHORIZATION) BELLE_SIP_PARSE(header_authorization) GET_SET_STRING(belle_sip_header_authorization,scheme); GET_SET_STRING(belle_sip_header_authorization,username); GET_SET_STRING(belle_sip_header_authorization,realm); GET_SET_STRING(belle_sip_header_authorization,nonce); GET_SET_STRING(belle_sip_header_authorization,response); GET_SET_STRING(belle_sip_header_authorization,algorithm); GET_SET_STRING(belle_sip_header_authorization,cnonce); GET_SET_STRING(belle_sip_header_authorization,opaque); GET_SET_STRING(belle_sip_header_authorization,qop); GET_SET_INT(belle_sip_header_authorization,nonce_count,int) int belle_sip_header_authorization_get_nonce_count_as_string(const belle_sip_header_authorization_t* authorization,char nounce_count[9]) { nounce_count[0]='\0'; if (authorization->nonce_count>0) { snprintf(nounce_count,9,"%08x",authorization->nonce_count); return 0; } else { return -1; } } /************************** *Proxy-Authorization header object inherent from parameters **************************** */ struct _belle_sip_header_proxy_authorization { belle_sip_header_authorization_t authorization; }; static void belle_sip_header_proxy_authorization_destroy(belle_sip_header_proxy_authorization_t* proxy_authorization) { } static void belle_sip_header_proxy_authorization_clone(belle_sip_header_proxy_authorization_t* proxy_authorization, const belle_sip_header_proxy_authorization_t *orig ) { } belle_sip_error_code belle_sip_header_proxy_authorization_marshal(belle_sip_header_proxy_authorization_t* proxy_authorization, char* buff, size_t buff_size, size_t *offset) { return belle_sip_header_authorization_marshal(&proxy_authorization->authorization,buff,buff_size,offset); } BELLE_SIP_NEW_HEADER(header_proxy_authorization,header_authorization,BELLE_SIP_PROXY_AUTHORIZATION) BELLE_SIP_PARSE(header_proxy_authorization) /************************** *HTTP Authorization header object inherent from Authorization **************************** */ struct _belle_http_header_authorization { belle_sip_header_authorization_t authorization; belle_generic_uri_t* uri; }; static void belle_http_header_authorization_init(belle_http_header_authorization_t* authorization) { belle_sip_header_set_name(BELLE_SIP_HEADER(authorization),BELLE_HTTP_AUTHORIZATION); } static void belle_http_header_authorization_destroy(belle_http_header_authorization_t* authorization) { if (authorization->uri) { belle_sip_object_unref(authorization->uri); } } static void belle_http_header_authorization_clone(belle_http_header_authorization_t* authorization, const belle_http_header_authorization_t *orig ) { if (belle_http_header_authorization_get_uri(orig)) { belle_http_header_authorization_set_uri(authorization,BELLE_GENERIC_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(belle_http_header_authorization_get_uri(orig))))); } } belle_sip_error_code belle_http_header_authorization_marshal(belle_http_header_authorization_t* authorization, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=BELLE_SIP_OK; /*first make sure there is no sip uri*/ if (belle_sip_header_authorization_get_uri(BELLE_SIP_HEADER_AUTHORIZATION(authorization))) { belle_sip_error ("Cannot marshal http_header_authorization because a sip uri is set. Use belle_http_authorization_set uri instead of belle_sip_header_authorization_set_uri"); return BELLE_SIP_NOT_IMPLEMENTED; } belle_sip_header_authorization_marshal(BELLE_SIP_HEADER_AUTHORIZATION(authorization),buff,buff_size,offset); if (authorization->uri) { error=belle_sip_snprintf(buff,buff_size,offset,", uri=\""); if (error!=BELLE_SIP_OK) return error; error=belle_generic_uri_marshal(authorization->uri,buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s","\""); if (error!=BELLE_SIP_OK) return error; } return error; } BELLE_NEW(belle_http_header_authorization,belle_sip_header_authorization) belle_generic_uri_t* belle_http_header_authorization_get_uri(const belle_http_header_authorization_t* authorization) { return authorization->uri; } void belle_http_header_authorization_set_uri( belle_http_header_authorization_t* authorization,belle_generic_uri_t* uri) { if (authorization->uri) belle_sip_object_unref(authorization->uri); if (uri) belle_sip_object_ref(uri); authorization->uri=uri; } /************************** *WWW-Authenticate header object inherent from parameters **************************** */ struct _belle_sip_header_www_authenticate { AUTH_BASE const char* domain; int stale; belle_sip_list_t* qop; }; static void belle_sip_header_www_authenticate_destroy(belle_sip_header_www_authenticate_t* www_authenticate) { AUTH_BASE_DESTROY(www_authenticate) if (www_authenticate->domain) belle_sip_free((void*)www_authenticate->domain); if (www_authenticate->qop) belle_sip_list_free_with_data(www_authenticate->qop,belle_sip_free); } void belle_sip_header_www_authenticate_init(belle_sip_header_www_authenticate_t* www_authenticate) { www_authenticate->stale=-1; } static void belle_sip_header_www_authenticate_clone(belle_sip_header_www_authenticate_t* www_authenticate, const belle_sip_header_www_authenticate_t *orig ) { AUTH_BASE_CLONE(belle_sip_header_www_authenticate,www_authenticate,orig) CLONE_STRING(belle_sip_header_www_authenticate,domain,www_authenticate,orig) www_authenticate->stale=orig->stale; www_authenticate->qop=belle_sip_list_copy_with_data(orig->qop,(void* (*)(void*))belle_sip_strdup); } belle_sip_error_code belle_sip_header_www_authenticate_marshal(belle_sip_header_www_authenticate_t* www_authenticate, char* buff, size_t buff_size, size_t *offset) { belle_sip_list_t* qops=www_authenticate->qop; AUTH_BASE_MARSHAL(www_authenticate) if (www_authenticate->domain) { error=belle_sip_snprintf(buff,buff_size,offset,"%sdomain=\"%s\"",border,www_authenticate->domain); if (error!=BELLE_SIP_OK) return error; border=", "; } if (www_authenticate->stale>=0) { error=belle_sip_snprintf(buff,buff_size,offset,"%sstale=%s",border,www_authenticate->stale?"true":"false"); if (error!=BELLE_SIP_OK) return error; } if (qops!=NULL && qops->data!=NULL) { error=belle_sip_snprintf(buff,buff_size,offset,"%sqop=\"",border); if (error!=BELLE_SIP_OK) return error; border=""; for(;qops!=NULL;qops=qops->next){ error=belle_sip_snprintf(buff,buff_size,offset,"%s%s",border, (const char*)qops->data); if (error!=BELLE_SIP_OK) return error; border=","; }\ error=belle_sip_snprintf(buff,buff_size,offset,"\""); if (error!=BELLE_SIP_OK) return error; border=", "; } return error; } #define SET_ADD_STRING_LIST(header,name) \ void header##_set_##name(header##_t* obj, belle_sip_list_t* value) {\ if (obj->name) {\ belle_sip_list_free_with_data(obj->name,belle_sip_free);\ } \ obj->name=value;\ }\ void header##_add_##name(header##_t* obj, const char* value) {\ obj->name=belle_sip_list_append(obj->name,strdup(value));\ } BELLE_SIP_NEW_HEADER_INIT(header_www_authenticate,parameters,BELLE_SIP_WWW_AUTHENTICATE,header_www_authenticate) BELLE_SIP_PARSE(header_www_authenticate) GET_SET_STRING(belle_sip_header_www_authenticate,scheme); GET_SET_STRING(belle_sip_header_www_authenticate,realm); GET_SET_STRING(belle_sip_header_www_authenticate,nonce); GET_SET_STRING(belle_sip_header_www_authenticate,algorithm); GET_SET_STRING(belle_sip_header_www_authenticate,opaque); /*GET_SET_STRING(belle_sip_header_www_authenticate,qop);*/ SET_ADD_STRING_LIST(belle_sip_header_www_authenticate,qop) GET_SET_STRING(belle_sip_header_www_authenticate,domain) GET_SET_BOOL(belle_sip_header_www_authenticate,stale,is) belle_sip_list_t* belle_sip_header_www_authenticate_get_qop(const belle_sip_header_www_authenticate_t* www_authetication) { return www_authetication->qop; } const char* belle_sip_header_www_authenticate_get_qop_first(const belle_sip_header_www_authenticate_t* www_authetication) { return www_authetication->qop?(const char*)www_authetication->qop->data:NULL; } /************************** *Proxy-authenticate header object inherent from www_authenticate **************************** */ struct _belle_sip_header_proxy_authenticate { belle_sip_header_www_authenticate_t www_authenticate; }; static void belle_sip_header_proxy_authenticate_destroy(belle_sip_header_proxy_authenticate_t* proxy_authenticate) { } static void belle_sip_header_proxy_authenticate_clone(belle_sip_header_proxy_authenticate_t* proxy_authenticate, const belle_sip_header_proxy_authenticate_t *orig ) { } belle_sip_error_code belle_sip_header_proxy_authenticate_marshal(belle_sip_header_proxy_authenticate_t* proxy_authenticate, char* buff, size_t buff_size, size_t *offset) { return belle_sip_header_www_authenticate_marshal(&proxy_authenticate->www_authenticate,buff,buff_size,offset); } BELLE_SIP_NEW_HEADER(header_proxy_authenticate,header_www_authenticate,BELLE_SIP_PROXY_AUTHENTICATE) BELLE_SIP_PARSE(header_proxy_authenticate) /************************** * max forwards header object inherent from header **************************** */ struct _belle_sip_header_max_forwards { belle_sip_header_t header; int max_forwards; }; static void belle_sip_header_max_forwards_destroy(belle_sip_header_max_forwards_t* max_forwards) { } static void belle_sip_header_max_forwards_clone(belle_sip_header_max_forwards_t* max_forwards, const belle_sip_header_max_forwards_t *orig ) { max_forwards->max_forwards=orig->max_forwards; } belle_sip_error_code belle_sip_header_max_forwards_marshal(belle_sip_header_max_forwards_t* max_forwards, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(max_forwards), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%i",max_forwards->max_forwards); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_max_forwards,header,"Max-Forwards") BELLE_SIP_PARSE(header_max_forwards) GET_SET_INT(belle_sip_header_max_forwards,max_forwards,int) int belle_sip_header_max_forwards_decrement_max_forwards(belle_sip_header_max_forwards_t* max_forwards) { return max_forwards->max_forwards--; } belle_sip_header_max_forwards_t* belle_sip_header_max_forwards_create(int value) { belle_sip_header_max_forwards_t* max_forwards=belle_sip_header_max_forwards_new(); max_forwards->max_forwards=value; return max_forwards; } /************************** * Subscription state header object inherent from parameters **************************** */ struct _belle_sip_header_subscription_state { belle_sip_parameters_t parameters; const char* state; }; static void belle_sip_header_subscription_state_destroy(belle_sip_header_subscription_state_t* subscription_state) { DESTROY_STRING(subscription_state,state); } static void belle_sip_header_subscription_state_clone(belle_sip_header_subscription_state_t* subscription_state, const belle_sip_header_subscription_state_t *orig ) { CLONE_STRING(belle_sip_header_subscription_state,state,subscription_state,orig) } belle_sip_error_code belle_sip_header_subscription_state_marshal(belle_sip_header_subscription_state_t* subscription_state, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(subscription_state), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s",subscription_state->state); if (error!=BELLE_SIP_OK) return error; error=belle_sip_parameters_marshal(BELLE_SIP_PARAMETERS(subscription_state), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_subscription_state,parameters,BELLE_SIP_SUBSCRIPTION_STATE) BELLE_SIP_PARSE(header_subscription_state) GET_SET_STRING(belle_sip_header_subscription_state,state); GET_SET_STRING_PARAM(belle_sip_header_subscription_state,reason); GET_SET_INT_PARAM2(belle_sip_header_subscription_state,retry-after,int,retry_after); GET_SET_INT_PARAM(belle_sip_header_subscription_state,expires,int) belle_sip_header_subscription_state_t* belle_sip_header_subscription_state_create (const char* subscription_state,int expires) { belle_sip_header_subscription_state_t* sub_state=belle_sip_header_subscription_state_new(); belle_sip_header_subscription_state_set_state(sub_state,subscription_state); belle_sip_header_subscription_state_set_expires(sub_state,expires); return sub_state; } #define HEADER_TO_LIKE_IMPL(name,header_name) \ struct _belle_sip_header_##name { \ belle_sip_header_address_t address; \ }; \ \ static void belle_sip_header_##name##_destroy(belle_sip_header_##name##_t * obj) { \ } \ void belle_sip_header_##name##_clone(belle_sip_header_##name##_t *contact, const belle_sip_header_##name##_t *orig){ }\ belle_sip_error_code belle_sip_header_##name##_marshal(belle_sip_header_##name##_t* name, char* buff, size_t buff_size, size_t *offset) {\ BELLE_SIP_FROM_LIKE_MARSHAL(name,FALSE)\ }\ BELLE_SIP_NEW_HEADER(header_##name,header_address,header_name)\ BELLE_SIP_PARSE(header_##name)\ belle_sip_header_##name##_t* belle_sip_header_##name##_create(const belle_sip_header_address_t* address) { \ belle_sip_header_##name##_t* header= belle_sip_header_##name##_new();\ _belle_sip_object_copy((belle_sip_object_t*)header,(belle_sip_object_t*)address);\ belle_sip_header_set_next(BELLE_SIP_HEADER(header),NULL); /*make sure only one header is kept*/\ belle_sip_header_set_name(BELLE_SIP_HEADER(header),header_name); \ return header;\ } /************************** * Refer-To header object inherits from header_address **************************** */ HEADER_TO_LIKE_IMPL(refer_to,BELLE_SIP_REFER_TO) /************************** * Referred-By header object inherits from header_address **************************** */ HEADER_TO_LIKE_IMPL(referred_by,BELLE_SIP_REFERRED_BY) /************************** * Replaces state header object inherent from parameters **************************** */ struct _belle_sip_header_replaces { belle_sip_parameters_t parameters; char* call_id; }; static void belle_sip_header_replaces_destroy(belle_sip_header_replaces_t* replaces) { DESTROY_STRING(replaces,call_id); } static void belle_sip_header_replaces_clone(belle_sip_header_replaces_t* replaces, const belle_sip_header_replaces_t *orig ) { CLONE_STRING(belle_sip_header_replaces,call_id,replaces,orig) } belle_sip_error_code belle_sip_header_replaces_marshal(belle_sip_header_replaces_t* replaces, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(replaces), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s",replaces->call_id); if (error!=BELLE_SIP_OK) return error; error=belle_sip_parameters_marshal(BELLE_SIP_PARAMETERS(replaces), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_replaces,parameters,BELLE_SIP_REPLACES) BELLE_SIP_PARSE(header_replaces) GET_SET_STRING(belle_sip_header_replaces,call_id); GET_SET_STRING_PARAM2(belle_sip_header_replaces,to-tag,to_tag); GET_SET_STRING_PARAM2(belle_sip_header_replaces,from-tag,from_tag); static void escaped_to_ascii(const char*a,char*b,size_t n) { size_t index_a=0,index_b=0; while (a[index_a]!='\0'&& index_acall_id); if (error!=BELLE_SIP_OK) return NULL; error=belle_sip_parameters_marshal(BELLE_SIP_PARAMETERS(replaces), buff, buff_size, &offset); if (error!=BELLE_SIP_OK) return NULL; buff[offset]='\0'; return strdup(buff); } belle_sip_header_replaces_t* belle_sip_header_replaces_create(const char* call_id,const char* from_tag,const char* to_tag) { belle_sip_header_replaces_t* replaces=belle_sip_header_replaces_new(); belle_sip_header_replaces_set_call_id(replaces,call_id); belle_sip_header_replaces_set_from_tag(replaces,from_tag); belle_sip_header_replaces_set_to_tag(replaces,to_tag); return replaces; } struct belle_sip_header_date{ belle_sip_header_t base; char *date; }; static void belle_sip_header_date_destroy(belle_sip_header_date_t* obj) { DESTROY_STRING(obj,date); } static void belle_sip_header_date_clone(belle_sip_header_date_t* obj, const belle_sip_header_date_t *orig ) { CLONE_STRING(belle_sip_header_date,date,obj,orig); } belle_sip_error_code belle_sip_header_date_marshal(belle_sip_header_date_t* obj, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(obj), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s",obj->date); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_date,header,BELLE_SIP_DATE) BELLE_SIP_PARSE(header_date) BELLESIP_EXPORT belle_sip_header_date_t* belle_sip_header_date_create_from_time(const time_t *utc_time){ belle_sip_header_date_t *obj=belle_sip_header_date_new(); belle_sip_header_date_set_time(obj,utc_time); return obj; } static const char *days[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; static const char *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; BELLESIP_EXPORT time_t belle_sip_header_date_get_time(belle_sip_header_date_t *obj){ struct tm ret ={0}; char tmp1[16] ={0}; char tmp2[16] ={0}; int i,j; time_t seconds; #if defined(BELLE_SIP_WINDOWS_UNIVERSAL) || defined(BELLE_SIP_MSC_VER_GREATER_19) long adjust_timezone; #else time_t adjust_timezone; #endif /* time headers are in GMT as spec says */ sscanf(obj->date,"%3c,%d %16s %d %d:%d:%d",tmp1,&ret.tm_mday,tmp2, &ret.tm_year,&ret.tm_hour,&ret.tm_min,&ret.tm_sec); ret.tm_year-=1900; for(i=0;i<7;i++) { if(strcmp(tmp1,days[i])==0) { ret.tm_wday=i; for(j=0;j<12;j++) { if(strcmp(tmp2,months[j])==0) { ret.tm_mon=j; goto success; } } } } belle_sip_warning("Failed to parse date %s",obj->date); return (time_t)-1; success: ret.tm_isdst=0; #if TARGET_IPHONE_SIMULATOR /* 'timezone' is buggy on iOS simulator, use the timegm() function to convert to UTC timestamp and discard the adjust timezone value */ seconds = timegm(&ret); adjust_timezone = 0; #else seconds = mktime(&ret); #if defined(BELLE_SIP_WINDOWS_UNIVERSAL) || defined(BELLE_SIP_MSC_VER_GREATER_19) _get_timezone(&adjust_timezone); #else adjust_timezone = timezone; #endif #endif if (seconds==(time_t)-1){ belle_sip_error("mktime() failed: %s",strerror(errno)); return (time_t)-1; } return seconds-(time_t)adjust_timezone; } BELLESIP_EXPORT void belle_sip_header_date_set_time(belle_sip_header_date_t *obj, const time_t *utc_time){ struct tm *ret; #ifndef _WIN32 struct tm gmt; ret=gmtime_r(utc_time,&gmt); #else ret=gmtime(utc_time); #endif /*cannot use strftime because it is locale dependant*/ if (obj->date){ belle_sip_free(obj->date); } /*SIP-date = rfc1123-date rfc1123-date = wkday "," SP date1 SP time SP "GMT" date1 = 2DIGIT SP month SP 4DIGIT ; day month year (e.g., 02 Jun 1982) time = 2DIGIT ":" 2DIGIT ":" 2DIGIT ; 00:00:00 - 23:59:59 wkday = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" */ obj->date=belle_sip_strdup_printf("%s, %02i %s %04i %02i:%02i:%02i GMT", days[ret->tm_wday],ret->tm_mday,months[ret->tm_mon],1900+ret->tm_year,ret->tm_hour,ret->tm_min,ret->tm_sec); } GET_SET_STRING(belle_sip_header_date,date); /************************ * header_p_prefered_identity ***********************/ struct _belle_sip_header_p_preferred_identity { belle_sip_header_address_t address; }; void belle_sip_header_p_preferred_identity_destroy(belle_sip_header_p_preferred_identity_t* p_preferred_identity) { } void belle_sip_header_p_preferred_identity_clone(belle_sip_header_p_preferred_identity_t *p_preferred_identity, const belle_sip_header_p_preferred_identity_t *orig){ } belle_sip_error_code belle_sip_header_p_preferred_identity_marshal(belle_sip_header_p_preferred_identity_t* p_preferred_identity, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(p_preferred_identity), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_header_address_marshal(&p_preferred_identity->address, buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_p_preferred_identity,header_address,BELLE_SIP_P_PREFERRED_IDENTITY) BELLE_SIP_PARSE(header_p_preferred_identity) belle_sip_header_p_preferred_identity_t* belle_sip_header_p_preferred_identity_create (const belle_sip_header_address_t* p_preferred_identity) { belle_sip_header_p_preferred_identity_t* header = belle_sip_header_p_preferred_identity_new(); _belle_sip_object_copy(BELLE_SIP_OBJECT(header),BELLE_SIP_OBJECT(p_preferred_identity)); belle_sip_header_set_next(BELLE_SIP_HEADER(header),NULL); /*make sure only one header is kept*/ belle_sip_header_set_name(BELLE_SIP_HEADER(header),BELLE_SIP_P_PREFERRED_IDENTITY); /*restaure header name*/ return header; } #define PRIVACY_LIKE_HEADER(header_name, string_name,separator) \ struct _belle_sip_header_##header_name { \ belle_sip_header_t header;\ belle_sip_list_t* header_name;\ }; \ \ static void belle_sip_header_##header_name##_destroy(belle_sip_header_##header_name##_t* p) {\ belle_sip_header_##header_name##_set_##header_name(p,NULL);\ }\ \ static void belle_sip_header_##header_name##_clone(belle_sip_header_##header_name##_t* p, const belle_sip_header_##header_name##_t* orig){\ belle_sip_list_t* list=orig->header_name;\ for(;list!=NULL;list=list->next){\ belle_sip_header_##header_name##_add_##header_name(p,(const char *)list->data);\ }\ }\ \ belle_sip_error_code belle_sip_header_##header_name##_marshal(belle_sip_header_##header_name##_t* p, char* buff, size_t buff_size, size_t *offset) {\ belle_sip_error_code error=BELLE_SIP_OK;\ belle_sip_list_t* list = p->header_name;\ error=belle_sip_header_marshal(BELLE_SIP_HEADER(p), buff, buff_size, offset);\ if (error!=BELLE_SIP_OK) return error;\ for(;list!=NULL;list=list->next){\ error=belle_sip_snprintf(buff,buff_size,offset,list==p->header_name ? "%s" : separator" %s",(const char *)list->data);\ if (error!=BELLE_SIP_OK) return error;\ }\ return error;\ }\ \ BELLE_SIP_NEW_HEADER(header_##header_name,header,string_name)\ BELLE_SIP_PARSE(header_##header_name)\ belle_sip_list_t* belle_sip_header_##header_name##_get_##header_name(const belle_sip_header_##header_name##_t* p) {\ return p->header_name;\ }\ SET_ADD_STRING_LIST(belle_sip_header_##header_name,header_name)\ \ belle_sip_header_##header_name##_t* belle_sip_header_##header_name##_create(const char* header_name) {\ belle_sip_header_##header_name##_t* header=belle_sip_header_##header_name##_new();\ belle_sip_header_##header_name##_add_##header_name(header,header_name);\ return header;\ } /****************************** * Privacy header inherits from header * ******************************/ PRIVACY_LIKE_HEADER(privacy,BELLE_SIP_PRIVACY,";") /************************** * Event header object inherent from parameters **************************** */ struct _belle_sip_header_event { belle_sip_parameters_t parameters; const char* package_name; }; static void belle_sip_header_event_destroy(belle_sip_header_event_t* event) { DESTROY_STRING(event,package_name); } static void belle_sip_header_event_clone(belle_sip_header_event_t* event, const belle_sip_header_event_t *orig ) { CLONE_STRING(belle_sip_header_event,package_name,event,orig) } belle_sip_error_code belle_sip_header_event_marshal(belle_sip_header_event_t* event, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(event), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s",event->package_name); if (error!=BELLE_SIP_OK) return error; error=belle_sip_parameters_marshal(BELLE_SIP_PARAMETERS(event), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_event,parameters,BELLE_SIP_EVENT) BELLE_SIP_PARSE(header_event) GET_SET_STRING(belle_sip_header_event,package_name); GET_SET_STRING_PARAM(belle_sip_header_event,id); belle_sip_header_event_t* belle_sip_header_event_create (const char* package_name) { belle_sip_header_event_t* event=belle_sip_header_event_new(); belle_sip_header_event_set_package_name(event,package_name); return event; } /****************************** * Supported header inherits from header * ******************************/ PRIVACY_LIKE_HEADER(supported,BELLE_SIP_SUPPORTED,",") /****************************** * Content-Disposition header inherits from header * ******************************/ struct _belle_sip_header_content_disposition { belle_sip_parameters_t parameters; const char* content_disposition; }; static void belle_sip_header_content_disposition_destroy(belle_sip_header_content_disposition_t* content_disposition) { DESTROY_STRING(content_disposition,content_disposition); } static void belle_sip_header_content_disposition_clone(belle_sip_header_content_disposition_t* content_disposition, const belle_sip_header_content_disposition_t *orig ) { CLONE_STRING(belle_sip_header_content_disposition,content_disposition,content_disposition,orig) } belle_sip_error_code belle_sip_header_content_disposition_marshal(belle_sip_header_content_disposition_t* content_disposition, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(content_disposition), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s",content_disposition->content_disposition); if (error!=BELLE_SIP_OK) return error; error=belle_sip_parameters_marshal(BELLE_SIP_PARAMETERS(content_disposition), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_content_disposition,parameters,BELLE_SIP_CONTENT_DISPOSITION) BELLE_SIP_PARSE(header_content_disposition) GET_SET_STRING(belle_sip_header_content_disposition,content_disposition); belle_sip_header_content_disposition_t* belle_sip_header_content_disposition_create (const char* value) { belle_sip_header_content_disposition_t* header=belle_sip_header_content_disposition_new(); belle_sip_header_content_disposition_set_content_disposition(header,value); return header; } /****************************** * Accept header inherits from parameters * ******************************/ struct _belle_sip_header_accept { belle_sip_parameters_t params_list; const char* type; const char* subtype; }; static void belle_sip_header_accept_destroy(belle_sip_header_accept_t* accept) { if (accept->type) belle_sip_free((void*)accept->type); if (accept->subtype) belle_sip_free((void*)accept->subtype); } static void belle_sip_header_accept_clone(belle_sip_header_accept_t* accept, const belle_sip_header_accept_t* orig){ CLONE_STRING(belle_sip_header_accept,type,accept,orig); CLONE_STRING(belle_sip_header_accept,subtype,accept,orig); } belle_sip_error_code belle_sip_header_accept_marshal(belle_sip_header_accept_t* accept, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(accept), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s/%s",accept->type, accept->subtype); if (error!=BELLE_SIP_OK) return error; error=belle_sip_parameters_marshal(&accept->params_list, buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW_HEADER(header_accept,parameters,BELLE_SIP_ACCEPT) BELLE_SIP_PARSE(header_accept) belle_sip_header_accept_t* belle_sip_header_accept_create (const char* type,const char* sub_type) { belle_sip_header_accept_t* header = belle_sip_header_accept_new(); belle_sip_header_accept_set_type(header,type); belle_sip_header_accept_set_subtype(header,sub_type); return header; } GET_SET_STRING(belle_sip_header_accept,type); GET_SET_STRING(belle_sip_header_accept,subtype); /****************************** * Reason header object inherent from parameters * ******************************/ struct _belle_sip_header_reason { belle_sip_parameters_t params_list; const char* protocol; const char* unquoted_text; }; static void belle_sip_header_reason_destroy(belle_sip_header_reason_t* reason) { DESTROY_STRING(reason,protocol); DESTROY_STRING(reason,unquoted_text); } static void belle_sip_header_reason_clone(belle_sip_header_reason_t* reason, const belle_sip_header_reason_t* orig){ CLONE_STRING(belle_sip_header_reason,protocol,reason,orig) } belle_sip_error_code belle_sip_header_reason_marshal(belle_sip_header_reason_t* reason, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(reason), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s ",reason->protocol); if (error!=BELLE_SIP_OK) return error; error=belle_sip_parameters_marshal(BELLE_SIP_PARAMETERS(reason), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; if (reason->unquoted_text) error=belle_sip_snprintf(buff,buff_size,offset,"; text=\"%s\"",reason->unquoted_text); return error; } GET_SET_STRING(belle_sip_header_reason,unquoted_text); void belle_sip_header_reason_set_text(belle_sip_header_reason_t* reason,const char* text) { belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(reason),"text"); /*just in case*/ belle_sip_header_reason_set_unquoted_text((belle_sip_header_reason_t*)reason, text); } BELLESIP_EXPORT const char* belle_sip_header_reason_get_text(const belle_sip_header_reason_t* reason) { if (!reason->unquoted_text) { /*try from params*/ const char * quoted = belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(reason), "text"); if (quoted) { char* unquoted = _belle_sip_str_dup_and_unquote_string(quoted); belle_sip_header_reason_set_unquoted_text((belle_sip_header_reason_t*)reason, unquoted); /*change internal param, ,even if reason is const*/ belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(reason),"text"); belle_sip_free(unquoted); } } return reason->unquoted_text; } GET_SET_STRING(belle_sip_header_reason,protocol); GET_SET_INT_PARAM(belle_sip_header_reason,cause,int); BELLE_SIP_PARSE(header_reason) BELLE_SIP_NEW_HEADER(header_reason,parameters,BELLE_SIP_REASON) /****************************** * AuthenticationInfo header hinerite from header * ******************************/ struct _belle_sip_header_authentication_info { belle_sip_header_t header; const char* rsp_auth; const char* cnonce; int nonce_count; const char* qop; const char* next_nonce; }; static void belle_sip_header_authentication_info_destroy(belle_sip_header_authentication_info_t* authentication_info) { DESTROY_STRING(authentication_info,rsp_auth); DESTROY_STRING(authentication_info,cnonce); DESTROY_STRING(authentication_info,qop); DESTROY_STRING(authentication_info,next_nonce); } static void belle_sip_header_authentication_info_clone( belle_sip_header_authentication_info_t* authentication_info , const belle_sip_header_authentication_info_t* orig){ CLONE_STRING(belle_sip_header_authentication_info,rsp_auth,authentication_info,orig) CLONE_STRING(belle_sip_header_authentication_info,cnonce,authentication_info,orig) CLONE_STRING(belle_sip_header_authentication_info,qop,authentication_info,orig) CLONE_STRING(belle_sip_header_authentication_info,next_nonce,authentication_info,orig) } belle_sip_error_code belle_sip_header_authentication_info_marshal(belle_sip_header_authentication_info_t* authentication_info, char* buff, size_t buff_size, size_t *offset) { char* border=""; belle_sip_error_code error=belle_sip_header_marshal(BELLE_SIP_HEADER(authentication_info), buff, buff_size, offset); if (error!=BELLE_SIP_OK) return error; if (authentication_info->rsp_auth) { error=belle_sip_snprintf(buff,buff_size,offset,"%srspauth=\"%s\"", border,authentication_info->rsp_auth); border=", "; } if (error!=BELLE_SIP_OK) return error; if (authentication_info->cnonce) { error=belle_sip_snprintf(buff,buff_size,offset,"%scnonce=\"%s\"", border, authentication_info->cnonce); border=", "; } if (error!=BELLE_SIP_OK) return error; if (authentication_info->nonce_count >= 0) { error=belle_sip_snprintf(buff,buff_size,offset,"%snc=%08x", border, authentication_info->nonce_count); border=", "; } if (error!=BELLE_SIP_OK) return error; if (authentication_info->qop) { error=belle_sip_snprintf(buff,buff_size,offset,"%sqop=%s", border, authentication_info->qop); border=", "; } if (error!=BELLE_SIP_OK) return error; if (authentication_info->next_nonce) { error=belle_sip_snprintf(buff,buff_size,offset,"%snextnonce=\"%s\"", border, authentication_info->next_nonce); } return error; } void belle_sip_header_authentication_info_init(belle_sip_header_authentication_info_t* header_authentication) { header_authentication->nonce_count=-1; } BELLE_SIP_NEW_HEADER_INIT(header_authentication_info,header,BELLE_SIP_AUTHENTICATION_INFO,header_authentication_info) BELLE_SIP_PARSE(header_authentication_info) GET_SET_STRING(belle_sip_header_authentication_info,rsp_auth); GET_SET_STRING(belle_sip_header_authentication_info,qop); GET_SET_STRING(belle_sip_header_authentication_info,next_nonce); GET_SET_STRING(belle_sip_header_authentication_info,cnonce); GET_SET_INT(belle_sip_header_authentication_info,nonce_count,int); belle-sip-1.6.3/src/belle_sip_internal.h000066400000000000000000001241441313437522400202010ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_utils_h #define belle_utils_h #include #include #include #include #include /* include all public headers*/ #include "belle-sip/belle-sip.h" #include "bctoolbox/map.h" #include "port.h" #include #include #ifdef HAVE_CONFIG_H #ifdef PACKAGE #undef PACKAGE #endif #ifdef PACKAGE_BUGREPORT #undef PACKAGE_BUGREPORT #endif #ifdef PACKAGE_NAME #undef PACKAGE_NAME #endif #ifdef PACKAGE_STRING #undef PACKAGE_STRING #endif #ifdef PACKAGE_TARNAME #undef PACKAGE_TARNAME #endif #ifdef VERSION #undef VERSION #endif #ifdef PACKAGE_VERSION #undef PACKAGE_VERSION #endif #include "config.h" #else #ifndef PACKAGE_VERSION #error "PACKAGE_VERSION must be defined and equal to the VERSION file included in the belle-sip repository" #endif #endif /*etc*/ #define BELLE_SIP_INTERFACE_GET_METHODS(obj,interface) \ ((BELLE_SIP_INTERFACE_METHODS_TYPE(interface)*)belle_sip_object_get_interface_methods((belle_sip_object_t*)obj,BELLE_SIP_INTERFACE_ID(interface))) #define __BELLE_SIP_INVOKE_LISTENER_BEGIN(list,interface_name,method) \ if (list!=NULL) {\ belle_sip_list_t *__copy=belle_sip_list_copy_with_data((list), (void* (*)(void*))belle_sip_object_ref);\ const belle_sip_list_t *__elem=__copy;\ do{\ void *__method;\ interface_name *__obj=(interface_name*)__elem->data;\ __method=BELLE_SIP_INTERFACE_GET_METHODS(__obj,interface_name)->method;\ if (__method) BELLE_SIP_INTERFACE_GET_METHODS(__obj,interface_name)-> #define __BELLE_SIP_INVOKE_LISTENER_REVERSE_BEGIN(list,interface_name,method) \ if (list!=NULL) {\ belle_sip_list_t *__copy=belle_sip_list_copy_reverse_with_data((list), (void* (*)(void*))belle_sip_object_ref);\ const belle_sip_list_t *__elem=__copy;\ do{\ void *__method;\ interface_name *__obj=(interface_name*)__elem->data;\ __method=BELLE_SIP_INTERFACE_GET_METHODS(__obj,interface_name)->method;\ if (__method) BELLE_SIP_INTERFACE_GET_METHODS(__obj,interface_name)-> #define __BELLE_SIP_INVOKE_LISTENER_END \ __elem=__elem->next;\ }while(__elem!=NULL);\ belle_sip_list_free_with_data(__copy,belle_sip_object_unref);\ } #define BELLE_SIP_INVOKE_LISTENERS_VOID(list,interface_name,method) \ __BELLE_SIP_INVOKE_LISTENER_BEGIN(list,interface_name,method)\ method(__obj);\ __BELLE_SIP_INVOKE_LISTENER_END #define BELLE_SIP_INVOKE_LISTENERS_ARG(list,interface_name,method,arg) \ __BELLE_SIP_INVOKE_LISTENER_BEGIN(list,interface_name,method)\ method(__obj,arg);\ __BELLE_SIP_INVOKE_LISTENER_END #define BELLE_SIP_INVOKE_LISTENERS_ARG1_ARG2(list,interface_name,method,arg1,arg2) \ __BELLE_SIP_INVOKE_LISTENER_BEGIN(list,interface_name,method)\ method(__obj,arg1,arg2);\ __BELLE_SIP_INVOKE_LISTENER_END #define BELLE_SIP_INVOKE_LISTENERS_REVERSE_ARG1_ARG2(list,interface_name,method,arg1,arg2) \ __BELLE_SIP_INVOKE_LISTENER_REVERSE_BEGIN(list,interface_name,method)\ method(__obj,arg1,arg2);\ __BELLE_SIP_INVOKE_LISTENER_END #define BELLE_SIP_INVOKE_LISTENERS_ARG1_ARG2_ARG3(list,interface_name,method,arg1,arg2,arg3) \ __BELLE_SIP_INVOKE_LISTENER_BEGIN(list,interface_name)\ method(__obj,arg1,arg2,arg3);\ __BELLE_SIP_INVOKE_LISTENER_END #define BELLE_SIP_INVOKE_LISTENER_ARG(listener,interface_name,method,arg) \ ((BELLE_SIP_INTERFACE_GET_METHODS((listener),interface_name)->method!=NULL) ? \ BELLE_SIP_INTERFACE_GET_METHODS((listener),interface_name)->method(listener,(arg)) : 0 ) typedef struct weak_ref{ struct weak_ref *next; belle_sip_object_destroy_notify_t notify; void *userpointer; }weak_ref_t; void *belle_sip_object_get_interface_methods(belle_sip_object_t *obj, belle_sip_interface_id_t ifid); /*used internally by unref()*/ void belle_sip_object_delete(void *obj); void belle_sip_object_pool_add(belle_sip_object_pool_t *pool, belle_sip_object_t *obj); void belle_sip_object_pool_remove(belle_sip_object_pool_t *pool, belle_sip_object_t *obj); #define belle_sip_object_init(obj) /*nothing*/ /*list of all vptrs (classes) used in belle-sip*/ BELLE_SIP_DECLARE_VPTR(belle_sip_stack_t); BELLE_SIP_DECLARE_VPTR(belle_sip_datagram_listening_point_t); BELLE_SIP_DECLARE_VPTR(belle_sip_provider_t); BELLE_SIP_DECLARE_VPTR(belle_sip_main_loop_t); BELLE_SIP_DECLARE_VPTR(belle_sip_source_t); BELLE_SIP_DECLARE_VPTR(belle_sip_dialog_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_address_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_contact_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_from_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_to_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_via_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_diversion_t); BELLE_SIP_DECLARE_VPTR(belle_sip_uri_t); BELLE_SIP_DECLARE_VPTR(belle_sip_message_t); BELLE_SIP_DECLARE_VPTR(belle_sip_request_t); BELLE_SIP_DECLARE_VPTR(belle_sip_response_t); BELLE_SIP_DECLARE_VPTR(belle_sip_parameters_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_call_id_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_cseq_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_content_type_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_route_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_record_route_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_user_agent_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_content_length_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_extension_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_authorization_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_www_authenticate_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_proxy_authenticate_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_proxy_authorization_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_max_forwards_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_expires_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_allow_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_attribute_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_bandwidth_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_connection_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_email_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_info_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_key_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_media_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_media_description_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_origin_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_phone_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_raw_attribute_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_repeate_time_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_rtcp_fb_attribute_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_rtcp_xr_attribute_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_session_description_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_session_name_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_time_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_time_description_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_uri_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_version_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_base_description_t); BELLE_SIP_DECLARE_VPTR(belle_sdp_mime_parameter_t); BELLE_SIP_DECLARE_VPTR(belle_sip_refresher_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_subscription_state_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_service_route_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_refer_to_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_referred_by_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_replaces_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_date_t); BELLE_SIP_DECLARE_VPTR(belle_sip_hop_t); BELLE_SIP_DECLARE_VPTR(belle_sip_object_pool_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_p_preferred_identity_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_privacy_t); BELLE_SIP_DECLARE_VPTR(belle_sip_certificates_chain_t); BELLE_SIP_DECLARE_VPTR(belle_sip_signing_key_t); BELLE_SIP_DECLARE_VPTR(belle_sip_dns_srv_t); BELLE_SIP_DECLARE_VPTR(belle_sip_dict_t) BELLE_SIP_DECLARE_VPTR(belle_http_provider_t); BELLE_SIP_DECLARE_VPTR(belle_http_channel_context_t); BELLE_SIP_DECLARE_VPTR(belle_http_request_t); BELLE_SIP_DECLARE_VPTR(belle_http_response_t); BELLE_SIP_DECLARE_VPTR(belle_generic_uri_t); BELLE_SIP_DECLARE_VPTR(belle_http_callbacks_t); BELLE_SIP_DECLARE_VPTR(belle_tls_crypto_config_t); BELLE_SIP_DECLARE_VPTR(belle_http_header_authorization_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_event_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_supported_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_content_disposition_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_accept_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_reason_t); BELLE_SIP_DECLARE_VPTR(belle_sip_header_authentication_info_t); BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_resolver_context_t,belle_sip_source_t) void (*cancel)(belle_sip_resolver_context_t *); void (*notify)(belle_sip_resolver_context_t *); BELLE_SIP_DECLARE_CUSTOM_VPTR_END BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_simple_resolver_context_t,belle_sip_resolver_context_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_combined_resolver_context_t,belle_sip_resolver_context_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_dual_resolver_context_t,belle_sip_resolver_context_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END void belle_sip_resolver_context_notify(belle_sip_resolver_context_t *ctx); struct belle_sip_source{ belle_sip_object_t base; belle_sip_list_t node; unsigned long id; belle_sip_fd_t fd; unsigned short events,revents; #ifdef _WIN32 long armed_events; unsigned short pad; #endif int timeout; void *data; uint64_t expire_ms; int index; /* index in pollfd table */ belle_sip_source_func_t notify; belle_sip_source_remove_callback_t on_remove; belle_sip_socket_t sock; unsigned char cancelled; unsigned char expired; unsigned char oneshot; unsigned char notify_required; /*for testing purpose, use to ask for being scheduled*/ bctbx_iterator_t *it; /*for fast removal*/ belle_sip_main_loop_t *ml; }; void belle_sip_socket_source_init(belle_sip_source_t *s, belle_sip_source_func_t func, void *data, belle_sip_socket_t fd, unsigned int events, unsigned int timeout_value_ms); void belle_sip_fd_source_init(belle_sip_source_t *s, belle_sip_source_func_t func, void *data, belle_sip_fd_t fd, unsigned int events, unsigned int timeout_value_ms); belle_sip_source_t * belle_sip_fd_source_new(belle_sip_source_func_t func, void *data, belle_sip_fd_t fd, unsigned int events, unsigned int timeout_value_ms); void belle_sip_source_uninit(belle_sip_source_t *s); void belle_sip_source_set_notify(belle_sip_source_t *s, belle_sip_source_func_t func); /* include private headers */ #include "channel.h" #define belle_sip_new(type) (type*)belle_sip_malloc(sizeof(type)) #define belle_sip_new0(type) (type*)belle_sip_malloc0(sizeof(type)) #define belle_sip_list_next(elem) ((elem)->next) /* dictionary */ struct belle_sip_dict { belle_sip_object_t base; }; #undef MIN #define MIN(a,b) ((a)>(b) ? (b) : (a)) #undef MAX #define MAX(a,b) ((a)>(b) ? (a) : (b)) #define belle_sip_concat bctbx_concat /*parameters accessors*/ #define GET_SET_STRING(object_type,attribute) \ const char* object_type##_get_##attribute (const object_type##_t* obj) {\ return obj->attribute;\ }\ void object_type##_set_##attribute (object_type##_t* obj,const char* value) {\ const char* previous_value = obj->attribute; /*preserve if same value re-asigned*/ \ if (value) {\ obj->attribute=belle_sip_strdup(value); \ } else obj->attribute=NULL;\ if (previous_value != NULL) belle_sip_free((void*)previous_value);\ } /*#define GET_SET_STRING_PARAM_NULL_ALLOWED(object_type,attribute) \ GET_STRING_PARAM2(object_type,attribute,attribute) \ void object_type##_set_##func_name (object_type##_t* obj,const char* value) {\ belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(obj),#attribute,value);\ } */ #define GET_SET_STRING_PARAM(object_type,attribute) GET_SET_STRING_PARAM2(object_type,attribute,attribute) #define GET_SET_STRING_PARAM2(object_type,attribute,func_name) \ GET_STRING_PARAM2(object_type,attribute,func_name) \ void object_type##_set_##func_name (object_type##_t* obj,const char* value) {\ if (belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(obj),#attribute) && !value) {\ belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(obj),#attribute); \ } else \ belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(obj),#attribute,value);\ } #define GET_STRING_PARAM2(object_type,attribute,func_name) \ const char* object_type##_get_##func_name (const object_type##_t* obj) {\ const char* l_value = belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(obj),#attribute);\ if (l_value == NULL) { \ /*belle_sip_warning("cannot find parameters [%s]",#attribute);*/\ return NULL;\ }\ return l_value;\ } #define DESTROY_STRING(object,attribute) if (object->attribute) belle_sip_free((void*)object->attribute); #define CLONE_STRING_GENERIC(object_type_src,object_type_dest,attribute,dest,src) \ if ( object_type_src##_get_##attribute (src)) {\ object_type_dest##_set_##attribute(dest,object_type_src##_get_##attribute(src));\ } #define CLONE_STRING(object_type,attribute,dest,src) CLONE_STRING_GENERIC(object_type,object_type,attribute,dest,src) #define GET_SET_INT(object_type,attribute,type) GET_SET_INT_PRIVATE(object_type,attribute,type,) #define GET_SET_INT_PRIVATE(object_type,attribute,type,set_prefix) \ type object_type##_get_##attribute (const object_type##_t* obj) {\ return obj->attribute;\ }\ void set_prefix##object_type##_set_##attribute (object_type##_t* obj,type value) {\ obj->attribute=value;\ } #define GET_SET_INT_PARAM(object_type,attribute,type) GET_SET_INT_PARAM_PRIVATE(object_type,attribute,type,) #define GET_SET_INT_PARAM2(object_type,attribute,type,func_name) GET_SET_INT_PARAM_PRIVATE2(object_type,attribute,type,,func_name) #define ATO_(type,value) ATO_##type(value) #define ATO_int(value) atoi(value) #define ATO_float(value) (float)strtod(value,NULL) #define FORMAT_(type) FORMAT_##type #define FORMAT_int "%i" #define FORMAT_float "%f" #define GET_SET_INT_PARAM_PRIVATE(object_type,attribute,type,set_prefix) GET_SET_INT_PARAM_PRIVATE2(object_type,attribute,type,set_prefix,attribute) #define GET_SET_INT_PARAM_PRIVATE2(object_type,attribute,type,set_prefix,func_name) \ type object_type##_get_##func_name (const object_type##_t* obj) {\ const char* l_value = belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(obj),#attribute);\ if (l_value == NULL) { \ /*belle_sip_error("cannot find parameters [%s]",#attribute);*/\ return -1;\ }\ return ATO_(type,l_value);\ }\ void set_prefix##object_type##_set_##func_name (object_type##_t* obj,type value) {\ char l_str_value[16];\ if (value == -1) { \ belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(obj),#attribute);\ return;\ }\ snprintf(l_str_value,16,FORMAT_(type),value);\ belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(obj),#attribute,(const char*)l_str_value);\ } #define GET_SET_BOOL(object_type,attribute,getter) \ unsigned int object_type##_##getter##_##attribute (const object_type##_t* obj) {\ return obj->attribute;\ }\ void object_type##_set_##attribute (object_type##_t* obj,unsigned int value) {\ obj->attribute=value;\ } #define GET_SET_BOOL_PARAM2(object_type,attribute,getter,func_name) \ unsigned int object_type##_##getter##_##func_name (const object_type##_t* obj) {\ return belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(obj),#attribute);\ }\ void object_type##_set_##func_name (object_type##_t* obj,unsigned int value) {\ belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(obj),#attribute,NULL);\ } #ifdef HAVE_ANTLR_STRING_STREAM_NEW #define ANTLR_STREAM_NEW(object_type, value,length) \ antlr3StringStreamNew((pANTLR3_UINT8)value,ANTLR3_ENC_8BIT,(ANTLR3_UINT32)length,(pANTLR3_UINT8)#object_type) #else #define ANTLR_STREAM_NEW(object_type, value, length) \ antlr3NewAsciiStringCopyStream((pANTLR3_UINT8)value,(ANTLR3_UINT32)length,NULL) #endif /*HAVE_ANTLR_STRING_STREAM_NEW*/ #define BELLE_PARSE(parser_name, object_type_prefix, object_type) \ object_type_prefix##object_type##_t* object_type_prefix##object_type##_parse (const char* value) { \ pANTLR3_INPUT_STREAM input; \ pbelle_sip_messageLexer lex; \ pANTLR3_COMMON_TOKEN_STREAM tokens; \ p##parser_name parser; \ object_type_prefix##object_type##_t* l_parsed_object; \ input = ANTLR_STREAM_NEW(object_type,value,strlen(value));\ lex = belle_sip_messageLexerNew (input);\ tokens = antlr3CommonTokenStreamSourceNew (ANTLR3_SIZE_HINT, TOKENSOURCE(lex));\ parser = parser_name##New (tokens);\ l_parsed_object = parser->object_type(parser);\ parser ->free(parser);\ tokens ->free(tokens);\ lex ->free(lex);\ input ->close(input);\ if (l_parsed_object == NULL) belle_sip_error(#object_type" parser error for [%s]",value);\ return l_parsed_object;\ } #define BELLE_SIP_PARSE(object_type) BELLE_PARSE(belle_sip_messageParser,belle_sip_,object_type) #define BELLE_NEW(object_type,super_type) \ BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(object_type##_t); \ BELLE_SIP_INSTANCIATE_VPTR( object_type##_t\ , super_type##_t\ , object_type##_destroy\ , object_type##_clone\ , object_type##_marshal, TRUE); \ object_type##_t* object_type##_new () { \ object_type##_t* l_object = belle_sip_object_new(object_type##_t);\ super_type##_init((super_type##_t*)l_object); \ object_type##_init((object_type##_t*) l_object); \ return l_object;\ } #define BELLE_SIP_NEW(object_type,super_type) BELLE_NEW (belle_sip_##object_type, belle_sip_##super_type) #define BELLE_SIP_NEW_HEADER(object_type,super_type,name) BELLE_SIP_NEW_HEADER_INIT(object_type,super_type,name,header) #define BELLE_SIP_NEW_HEADER_INIT(object_type,super_type,name,init_type) \ BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_##object_type##_t); \ BELLE_SIP_INSTANCIATE_VPTR( belle_sip_##object_type##_t\ , belle_sip_##super_type##_t\ , belle_sip_##object_type##_destroy\ , belle_sip_##object_type##_clone\ , belle_sip_##object_type##_marshal, TRUE); \ belle_sip_##object_type##_t* belle_sip_##object_type##_new () { \ belle_sip_##object_type##_t* l_object = belle_sip_object_new(belle_sip_##object_type##_t);\ belle_sip_##super_type##_init((belle_sip_##super_type##_t*)l_object); \ belle_sip_##init_type##_init((belle_sip_##init_type##_t*) l_object); \ if (name) belle_sip_header_set_name(BELLE_SIP_HEADER(l_object),name);\ return l_object;\ } struct belle_sip_param_pair { int ref; char* name; char* value; } ; void belle_sip_param_pair_destroy(belle_sip_param_pair_t* pair) ; int belle_sip_param_pair_comp_func(const belle_sip_param_pair_t *a, const char*b) ; int belle_sip_param_pair_case_comp_func(const belle_sip_param_pair_t *a, const char*b) ; belle_sip_param_pair_t* belle_sip_param_pair_ref(belle_sip_param_pair_t* obj); void belle_sip_param_pair_unref(belle_sip_param_pair_t* obj); /*calss header*/ struct _belle_sip_header { belle_sip_object_t base; belle_sip_header_t* next; char *name; char *unparsed_value; }; void belle_sip_response_fill_for_dialog(belle_sip_response_t *obj, belle_sip_request_t *req); void belle_sip_util_copy_headers(belle_sip_message_t *orig, belle_sip_message_t *dest, const char*header, int multiple); void belle_sip_header_init(belle_sip_header_t* obj); /*class parameters*/ struct _belle_sip_parameters { belle_sip_header_t base; belle_sip_list_t* param_list; belle_sip_list_t* paramnames_list; }; void belle_sip_parameters_init(belle_sip_parameters_t *obj); /* * Listening points */ #include "listeningpoint_internal.h" struct belle_sip_hop{ belle_sip_object_t base; char *cname; char *host; char *transport; int port; }; /* belle_sip_stack_t */ struct belle_sip_stack{ belle_sip_object_t base; belle_sip_main_loop_t *ml; belle_sip_timer_config_t timer_config; int transport_timeout; int inactive_transport_timeout; int dns_timeout; int tx_delay; /*used to simulate network transmission delay, for tests*/ int send_error; /* used to simulate network error. if <0, channel_send will return this value*/ int resolver_tx_delay; /*used to simulate network transmission delay, for tests*/ int resolver_send_error; /* used to simulate network error*/ int dscp; char *dns_user_hosts_file; /* used to load additional hosts file for tests */ char *dns_resolv_conf; /*used to load custom resolv.conf, for tests*/ belle_sip_list_t *dns_servers; /*used when dns servers are supplied by app layer*/ /*http proxy stuff to be used by both http and sip provider*/ char *http_proxy_host; int http_proxy_port; char *http_proxy_username; /*for future use*/ char *http_proxy_passwd; /*for future use*/ unsigned char dns_srv_enabled; unsigned char dns_search_enabled; }; BELLESIP_EXPORT belle_sip_hop_t* belle_sip_hop_new(const char* transport, const char *cname, const char* host,int port); BELLESIP_EXPORT belle_sip_hop_t* belle_sip_hop_new_from_uri(const belle_sip_uri_t *uri); BELLESIP_EXPORT belle_sip_hop_t* belle_sip_hop_new_from_generic_uri(const belle_generic_uri_t *uri); BELLESIP_EXPORT belle_sip_hop_t * belle_sip_stack_get_next_hop(belle_sip_stack_t *stack, belle_sip_request_t *req); /* belle_sip_provider_t */ struct belle_sip_provider{ belle_sip_object_t base; belle_sip_stack_t *stack; belle_sip_list_t *lps; /*listening points*/ belle_sip_list_t *listeners; belle_sip_list_t *internal_listeners; /*for transaction internaly managed by belle-sip. I.E by refreshers*/ belle_sip_list_t *client_transactions; belle_sip_list_t *server_transactions; belle_sip_list_t *dialogs; belle_sip_list_t *auth_contexts; unsigned char rport_enabled; /*0 if rport should not be set in via header*/ unsigned char nat_helper; unsigned char unconditional_answer_enabled; unsigned short unconditional_answer; }; BELLESIP_EXPORT belle_sip_provider_t *belle_sip_provider_new(belle_sip_stack_t *s, belle_sip_listening_point_t *lp); void belle_sip_provider_add_client_transaction(belle_sip_provider_t *prov, belle_sip_client_transaction_t *t); belle_sip_client_transaction_t *belle_sip_provider_find_matching_client_transaction(belle_sip_provider_t *prov, belle_sip_response_t *resp); void belle_sip_provider_remove_client_transaction(belle_sip_provider_t *prov, belle_sip_client_transaction_t *t); void belle_sip_provider_add_server_transaction(belle_sip_provider_t *prov, belle_sip_server_transaction_t *t); BELLESIP_EXPORT belle_sip_server_transaction_t * belle_sip_provider_find_matching_server_transaction(belle_sip_provider_t *prov,belle_sip_request_t *req); void belle_sip_provider_remove_server_transaction(belle_sip_provider_t *prov, belle_sip_server_transaction_t *t); void belle_sip_provider_set_transaction_terminated(belle_sip_provider_t *p, belle_sip_transaction_t *t); void *belle_sip_transaction_get_application_data_internal(const belle_sip_transaction_t *t); belle_sip_channel_t * belle_sip_provider_get_channel(belle_sip_provider_t *p, const belle_sip_hop_t *hop); void belle_sip_provider_add_dialog(belle_sip_provider_t *prov, belle_sip_dialog_t *dialog); void belle_sip_provider_remove_dialog(belle_sip_provider_t *prov, belle_sip_dialog_t *dialog); void belle_sip_provider_release_channel(belle_sip_provider_t *p, belle_sip_channel_t *chan); void belle_sip_provider_add_internal_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l, int prepend); void belle_sip_provider_remove_internal_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l); belle_sip_client_transaction_t * belle_sip_provider_find_matching_client_transaction_from_req(belle_sip_provider_t *prov, belle_sip_request_t *req); belle_sip_dialog_t *belle_sip_provider_find_dialog_from_message(belle_sip_provider_t *prov, belle_sip_message_t *msg, int as_uas); /*for testing purpose only:*/ BELLESIP_EXPORT void belle_sip_provider_dispatch_message(belle_sip_provider_t *prov, belle_sip_message_t *msg); typedef struct listener_ctx{ belle_sip_listener_t *listener; void *data; }listener_ctx_t; #define BELLE_SIP_PROVIDER_INVOKE_LISTENERS_FOR_TRANSACTION(t,callback,event) \ BELLE_SIP_PROVIDER_INVOKE_LISTENERS((t)->is_internal?(t)->provider->internal_listeners:(t)->provider->listeners,callback,event) #define BELLE_SIP_PROVIDER_INVOKE_LISTENERS_FOR_DIALOG(d,callback,event) \ BELLE_SIP_PROVIDER_INVOKE_LISTENERS((d)->is_internal?(d)->provider->internal_listeners:(d)->provider->listeners,callback,event) #define BELLE_SIP_PROVIDER_INVOKE_LISTENERS(listeners,callback,event) \ BELLE_SIP_INVOKE_LISTENERS_ARG((listeners),belle_sip_listener_t,callback,(event)) /* * http provider */ belle_http_provider_t *belle_http_provider_new(belle_sip_stack_t *s, const char *bind_ip); /* * SIP and http messages **/ #define BELLESIP_MULTIPART_BOUNDARY "---------------------------14737809831466499882746641449" void belle_sip_message_init(belle_sip_message_t *message); struct _belle_sip_message { belle_sip_object_t base; belle_sip_list_t* header_list; belle_sip_body_handler_t *body_handler; }; struct _belle_sip_request { belle_sip_message_t base; char* method; belle_sip_uri_t* uri; belle_generic_uri_t *absolute_uri; belle_sip_dialog_t *dialog;/*set if request was created by a dialog to avoid to search in dialog list*/ char *rfc2543_branch; /*computed 'branch' id in case we receive this request from an old RFC2543 stack*/ unsigned char dialog_queued; }; /** HTTP request**/ struct belle_http_request{ belle_sip_message_t base; belle_generic_uri_t *req_uri; char* method; belle_http_request_listener_t *listener; belle_generic_uri_t *orig_uri;/*original uri before removing host and user/passwd*/ belle_http_response_t *response; belle_sip_channel_t *channel; int auth_attempt_count; int background_task_id; int cancelled; }; void belle_http_request_set_listener(belle_http_request_t *req, belle_http_request_listener_t *l); void belle_http_request_set_channel(belle_http_request_t *req, belle_sip_channel_t *chan); void belle_http_request_set_response(belle_http_request_t *req, belle_http_response_t *resp); /* belle_sip_transaction_t */ struct belle_sip_transaction{ belle_sip_object_t base; belle_sip_provider_t *provider; /*the provider that created this transaction */ belle_sip_request_t *request; belle_sip_response_t *last_response; belle_sip_channel_t *channel; belle_sip_dialog_t *dialog; belle_sip_source_t *call_repair_timer; char *branch_id; belle_sip_transaction_state_t state; void *appdata; unsigned char is_internal; unsigned char timed_out; unsigned char sent_by_dialog_queue; unsigned long bg_task_id; }; BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_transaction_t,belle_sip_object_t) void (*on_terminate)(belle_sip_transaction_t *obj); BELLE_SIP_DECLARE_CUSTOM_VPTR_END static BELLESIP_INLINE const belle_sip_timer_config_t * belle_sip_transaction_get_timer_config(belle_sip_transaction_t *obj){ return belle_sip_stack_get_timer_config(obj->provider->stack); } static BELLESIP_INLINE void belle_sip_transaction_start_timer(belle_sip_transaction_t *obj, belle_sip_source_t *timer){ belle_sip_main_loop_add_source(obj->provider->stack->ml,timer); } /** */ static BELLESIP_INLINE void belle_sip_transaction_stop_timer(belle_sip_transaction_t *obj, belle_sip_source_t *timer){ belle_sip_main_loop_remove_source(obj->provider->stack->ml,timer); } int belle_sip_client_transaction_is_notify_matching_pending_subscribe(belle_sip_client_transaction_t *trans , belle_sip_request_t *notify); void belle_sip_transaction_notify_timeout(belle_sip_transaction_t *t); void belle_sip_transaction_set_dialog(belle_sip_transaction_t *t, belle_sip_dialog_t *dialog); void belle_sip_transaction_set_state(belle_sip_transaction_t *t, belle_sip_transaction_state_t state); /* * * * Client transaction * * */ struct belle_sip_client_transaction{ belle_sip_transaction_t base; belle_sip_uri_t* preset_route; /*use to store outbound proxy, will be helpful for refresher*/ belle_sip_hop_t* next_hop; /*use to send cancel request*/ }; BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_client_transaction_t,belle_sip_transaction_t) void (*send_request)(belle_sip_client_transaction_t *); void (*on_response)(belle_sip_client_transaction_t *obj, belle_sip_response_t *resp); BELLE_SIP_DECLARE_CUSTOM_VPTR_END void belle_sip_client_transaction_init(belle_sip_client_transaction_t *obj, belle_sip_provider_t *prov, belle_sip_request_t *req); void belle_sip_client_transaction_add_response(belle_sip_client_transaction_t *t, belle_sip_response_t *resp); void belle_sip_client_transaction_notify_response(belle_sip_client_transaction_t *t, belle_sip_response_t *resp); struct belle_sip_ict{ belle_sip_client_transaction_t base; belle_sip_source_t *timer_A; belle_sip_source_t *timer_B; belle_sip_source_t *timer_D; belle_sip_source_t *timer_M; belle_sip_request_t *ack; }; typedef struct belle_sip_ict belle_sip_ict_t; BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_ict_t,belle_sip_client_transaction_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END belle_sip_ict_t * belle_sip_ict_new(belle_sip_provider_t *prov, belle_sip_request_t *req); struct belle_sip_nict{ belle_sip_client_transaction_t base; belle_sip_source_t *timer_F; belle_sip_source_t *timer_E; belle_sip_source_t *timer_K; }; typedef struct belle_sip_nict belle_sip_nict_t; BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_nict_t,belle_sip_client_transaction_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END belle_sip_nict_t * belle_sip_nict_new(belle_sip_provider_t *prov, belle_sip_request_t *req); /* * * * Server transaction * * */ struct belle_sip_server_transaction{ belle_sip_transaction_t base; char to_tag[8]; }; BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_server_transaction_t,belle_sip_transaction_t) int (*send_new_response)(belle_sip_server_transaction_t *, belle_sip_response_t *resp); void (*on_request_retransmission)(belle_sip_server_transaction_t *obj); BELLE_SIP_DECLARE_CUSTOM_VPTR_END void belle_sip_server_transaction_init(belle_sip_server_transaction_t *t, belle_sip_provider_t *prov,belle_sip_request_t *req); void belle_sip_server_transaction_on_request(belle_sip_server_transaction_t *t, belle_sip_request_t *req); struct belle_sip_ist{ belle_sip_server_transaction_t base; belle_sip_source_t *timer_G; belle_sip_source_t *timer_H; belle_sip_source_t *timer_I; belle_sip_source_t *timer_L; }; typedef struct belle_sip_ist belle_sip_ist_t; BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_ist_t,belle_sip_server_transaction_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END belle_sip_ist_t * belle_sip_ist_new(belle_sip_provider_t *prov, belle_sip_request_t *req); /* returns 0 if the ack should be notified to TU, or -1 otherwise*/ int belle_sip_ist_process_ack(belle_sip_ist_t *obj, belle_sip_message_t *ack); struct belle_sip_nist{ belle_sip_server_transaction_t base; belle_sip_source_t *timer_J; }; typedef struct belle_sip_nist belle_sip_nist_t; BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_nist_t,belle_sip_server_transaction_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END belle_sip_nist_t * belle_sip_nist_new(belle_sip_provider_t *prov, belle_sip_request_t *req); typedef enum belle_sip_dialog_type{ BELLE_SIP_DIALOG_INVITE, BELLE_SIP_DIALOG_SUBSCRIBE_NOTIFY }belle_sip_dialog_type_t; /* * Dialogs */ struct belle_sip_dialog{ belle_sip_object_t base; void *appdata; belle_sip_dialog_type_t type; belle_sip_provider_t *provider; belle_sip_request_t *last_out_invite; belle_sip_request_t *last_out_ack; /*so that it can be retransmitted when needed*/ belle_sip_response_t *last_200Ok; belle_sip_source_t *timer_200Ok; belle_sip_source_t *timer_200Ok_end; belle_sip_dialog_state_t state; belle_sip_dialog_state_t previous_state; belle_sip_header_call_id_t *call_id; belle_sip_header_address_t *local_party; belle_sip_header_address_t *remote_party; belle_sip_list_t *route_set; belle_sip_header_address_t *remote_target; belle_sip_source_t *expiration_timer; char *local_tag; char *remote_tag; unsigned int local_cseq; unsigned int remote_cseq; belle_sip_transaction_t* last_transaction; belle_sip_header_privacy_t* privacy; belle_sip_list_t *queued_ct;/* queued client transactions*/ unsigned int remote_invite_cseq; /*needed because multiple trans can be handled whithin invite transaction (I.E UPDATE, PRACK,etc*/ unsigned char is_server; unsigned char is_secure; unsigned char terminate_on_bye; unsigned char needs_ack; unsigned char is_expired; unsigned char pending_trans_checking_enabled; /*use to disabled pending transaction check at request creation (testing)*/ unsigned char is_internal; /*Internal dialogs are those created by refreshers. */ }; belle_sip_dialog_t *belle_sip_dialog_new(belle_sip_transaction_t *t); belle_sip_dialog_t * belle_sip_provider_create_dialog_internal(belle_sip_provider_t *prov, belle_sip_transaction_t *t,unsigned int check_last_resp); int belle_sip_dialog_is_authorized_transaction(const belle_sip_dialog_t *dialog,const char* method) ; /*returns 1 if message belongs to the dialog, 0 otherwise */ int belle_sip_dialog_is_null_dialog_with_matching_subscribe(belle_sip_dialog_t *obj, const char *call_id, const char *local_tag, belle_sip_request_t *notify); int _belle_sip_dialog_match(belle_sip_dialog_t *obj, const char *call_id, const char *local_tag, const char *remote_tag); int belle_sip_dialog_match(belle_sip_dialog_t *obj, belle_sip_message_t *msg, int as_uas); int belle_sip_dialog_update(belle_sip_dialog_t *obj,belle_sip_transaction_t* transaction, int as_uas); void belle_sip_dialog_check_ack_sent(belle_sip_dialog_t*obj); int belle_sip_dialog_handle_ack(belle_sip_dialog_t *obj, belle_sip_request_t *ack); void belle_sip_dialog_queue_client_transaction(belle_sip_dialog_t *dialog, belle_sip_client_transaction_t *tr); void belle_sip_dialog_stop_200Ok_retrans(belle_sip_dialog_t *obj); /* belle_sip_response_t */ belle_sip_hop_t* belle_sip_response_get_return_hop(belle_sip_response_t *msg); /********************************************************* * SDP */ #define BELLE_SDP_PARSE(object_type) \ belle_sdp_##object_type##_t* belle_sdp_##object_type##_parse (const char* value) { \ pANTLR3_INPUT_STREAM input; \ pbelle_sdpLexer lex; \ pANTLR3_COMMON_TOKEN_STREAM tokens; \ pbelle_sdpParser parser; \ belle_sdp_##object_type##_t* l_parsed_object; \ input = ANTLR_STREAM_NEW(object_type, value,strlen(value));\ lex = belle_sdpLexerNew (input);\ tokens = antlr3CommonTokenStreamSourceNew (ANTLR3_SIZE_HINT, TOKENSOURCE(lex));\ parser = belle_sdpParserNew (tokens);\ l_parsed_object = parser->object_type(parser).ret;\ parser ->free(parser);\ tokens ->free(tokens);\ lex ->free(lex);\ input ->close(input);\ if (l_parsed_object == NULL) belle_sip_error(#object_type" parser error for [%s]",value);\ return l_parsed_object;\ } #define BELLE_SDP_NEW(object_type,super_type) \ BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sdp_##object_type##_t); \ BELLE_SIP_INSTANCIATE_VPTR( belle_sdp_##object_type##_t\ , super_type##_t\ , belle_sdp_##object_type##_destroy\ , belle_sdp_##object_type##_clone\ , belle_sdp_##object_type##_marshal, TRUE); \ belle_sdp_##object_type##_t* belle_sdp_##object_type##_new () { \ belle_sdp_##object_type##_t* l_object = belle_sip_object_new(belle_sdp_##object_type##_t);\ super_type##_init((super_type##_t*)l_object); \ return l_object;\ } #define BELLE_SDP_NEW_WITH_CTR(object_type,super_type) \ BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sdp_##object_type##_t); \ BELLE_SIP_INSTANCIATE_VPTR( belle_sdp_##object_type##_t\ , super_type##_t\ , belle_sdp_##object_type##_destroy\ , belle_sdp_##object_type##_clone\ , belle_sdp_##object_type##_marshal,TRUE); \ belle_sdp_##object_type##_t* belle_sdp_##object_type##_new () { \ belle_sdp_##object_type##_t* l_object = belle_sip_object_new(belle_sdp_##object_type##_t);\ super_type##_init((super_type##_t*)l_object); \ belle_sdp_##object_type##_init(l_object); \ return l_object;\ } struct belle_sip_dialog_terminated_event{ belle_sip_provider_t *source; belle_sip_dialog_t *dialog; int is_expired; }; struct belle_sip_io_error_event{ belle_sip_object_t *source; /*the object impacted by this error*/ const char *transport; const char *host; unsigned int port; }; struct belle_sip_request_event{ belle_sip_object_t *source; belle_sip_server_transaction_t *server_transaction; belle_sip_dialog_t *dialog; belle_sip_request_t *request; }; struct belle_sip_response_event{ belle_sip_object_t *source; belle_sip_client_transaction_t *client_transaction; belle_sip_dialog_t *dialog; belle_sip_response_t *response; }; struct belle_sip_timeout_event{ belle_sip_object_t *source; belle_sip_transaction_t *transaction; int is_server_transaction; }; struct belle_sip_transaction_terminated_event{ belle_sip_provider_t *source; belle_sip_transaction_t *transaction; int is_server_transaction; }; struct belle_sip_auth_event { belle_sip_object_t *source; belle_sip_auth_mode_t mode; char* username; char* userid; char* realm; char* passwd; char* ha1; char* domain; char* distinguished_name; belle_sip_certificates_chain_t * cert; belle_sip_signing_key_t* key; }; belle_sip_auth_event_t* belle_sip_auth_event_create(belle_sip_object_t *source, const char* realm,const belle_sip_uri_t * from_uri); void belle_sip_auth_event_set_distinguished_name(belle_sip_auth_event_t* event,const char* value); /* * refresher * */ belle_sip_refresher_t* belle_sip_refresher_new(belle_sip_client_transaction_t* transaction); /* * returns a char, even if entry is escaped*/ size_t belle_sip_get_char (const char*a,char*out); /*return an escaped string*/ BELLESIP_EXPORT char* belle_sip_uri_to_escaped_username(const char* buff) ; BELLESIP_EXPORT char* belle_sip_uri_to_escaped_userpasswd(const char* buff) ; BELLESIP_EXPORT char* belle_sip_uri_to_escaped_parameter(const char* buff) ; BELLESIP_EXPORT char* belle_sip_uri_to_escaped_header(const char* buff) ; /*(uri RFC 2396)*/ BELLESIP_EXPORT char* belle_generic_uri_to_escaped_query(const char* buff); BELLESIP_EXPORT char* belle_generic_uri_to_escaped_path(const char* buff); #define BELLE_SIP_SOCKET_TIMEOUT 30000 #define BELLE_SIP_BRANCH_ID_LENGTH 10 /*Shall not be less than 32bit */ #define BELLE_SIP_TAG_LENGTH 6 #define BELLE_SIP_MAX_TO_STRING_SIZE 2048 void belle_sip_header_contact_set_unknown(belle_sip_header_contact_t *a, int value); void belle_sip_request_set_dialog(belle_sip_request_t *req, belle_sip_dialog_t *dialog); void belle_sip_request_set_rfc2543_branch(belle_sip_request_t *req, const char *rfc2543branch); void belle_sip_dialog_update_request(belle_sip_dialog_t *dialog, belle_sip_request_t *req); belle_sip_error_code belle_sip_headers_marshal(belle_sip_message_t *message, char* buff, size_t buff_size, size_t *offset); #define SET_OBJECT_PROPERTY(obj,property_name,new_value) \ if (new_value) belle_sip_object_ref(new_value); \ if (obj->property_name){ \ belle_sip_object_unref(obj->property_name); \ }\ obj->property_name=new_value; #include "parserutils.h" /****************************** * * private Extension header inherit from header * ******************************/ typedef struct _belle_sip_header_extension belle_sip_header_extension_t; belle_sip_header_extension_t* belle_sip_header_extension_new(void); belle_sip_header_extension_t* belle_sip_header_extension_parse (const char* extension) ; belle_sip_header_extension_t* belle_sip_header_extension_create (const char* name,const char* value); BELLESIP_EXPORT const char* belle_sip_header_extension_get_value(const belle_sip_header_extension_t* extension); void belle_sip_header_extension_set_value(belle_sip_header_extension_t* extension,const char* value); #define BELLE_SIP_HEADER_EXTENSION(t) BELLE_SIP_CAST(t,belle_sip_header_extension_t) /**************** * belle_sip_body_handler_t object ***************/ BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_body_handler_t,belle_sip_object_t) void (*begin_recv_transfer)(belle_sip_body_handler_t *obj); void (*begin_send_transfer)(belle_sip_body_handler_t *obj); void (*end_transfer)(belle_sip_body_handler_t *obj); void (*chunk_recv)(belle_sip_body_handler_t *obj, belle_sip_message_t *msg, off_t offset, uint8_t *buf, size_t size); int (*chunk_send)(belle_sip_body_handler_t *obj, belle_sip_message_t *msg, off_t offset, uint8_t *buf, size_t * size); BELLE_SIP_DECLARE_CUSTOM_VPTR_END void belle_sip_body_handler_begin_recv_transfer(belle_sip_body_handler_t *obj); void belle_sip_body_handler_begin_send_transfer(belle_sip_body_handler_t *obj); void belle_sip_body_handler_recv_chunk(belle_sip_body_handler_t *obj, belle_sip_message_t *msg, uint8_t *buf, size_t size); int belle_sip_body_handler_send_chunk(belle_sip_body_handler_t *obj, belle_sip_message_t *msg, uint8_t *buf, size_t *size); void belle_sip_body_handler_end_transfer(belle_sip_body_handler_t *obj); BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_memory_body_handler_t,belle_sip_body_handler_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_user_body_handler_t,belle_sip_body_handler_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_file_body_handler_t,belle_sip_body_handler_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_multipart_body_handler_t,belle_sip_body_handler_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END void belle_sip_multipart_body_handler_progress_cb(belle_sip_body_handler_t *obj, belle_sip_message_t *msg, void *user_data, size_t transfered, size_t expected_total); /** * file manipulation */ /** * Parse a directory and return all files in it. * * @param[in] path The directory to be parsed * @param[in] file_type if not NULL return only the file with the given extension, must include the '.', ex:".pem" * @return a belle_sip list containing all found file or NULL if no file were found or directory doesn't exist. List must be destroyed using belle_sip_list_free_with_data(, belle_sip_free) */ belle_sip_list_t *belle_sip_parse_directory(const char *path, const char *file_type); typedef struct authorization_context authorization_context_t; BELLESIP_EXPORT void belle_sip_authorization_destroy(authorization_context_t* object); #endif belle-sip-1.6.3/src/belle_sip_loop.c000066400000000000000000000466441313437522400173410ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_internal.h" #include "bctoolbox/map.h" #include #ifndef _WIN32 #include #include typedef struct pollfd belle_sip_pollfd_t; static int belle_sip_poll(belle_sip_pollfd_t *pfd, int count, int duration){ int err; err=poll(pfd,count,duration); if (err==-1 && errno!=EINTR) belle_sip_error("poll() error: %s",strerror(errno)); return err; } /* Poll() based implementation of event loop. */ static int belle_sip_event_to_poll(unsigned int events){ int ret=0; if (events & BELLE_SIP_EVENT_READ) ret|=POLLIN; if (events & BELLE_SIP_EVENT_WRITE) ret|=POLLOUT; if (events & BELLE_SIP_EVENT_ERROR) ret|=POLLERR; return ret; } static unsigned int belle_sip_poll_to_event(belle_sip_pollfd_t * pfd){ unsigned int ret=0; short events=pfd->revents; if (events & POLLIN) ret|=BELLE_SIP_EVENT_READ; if (events & POLLOUT) ret|=BELLE_SIP_EVENT_WRITE; if (events & POLLERR) ret|=BELLE_SIP_EVENT_ERROR; return ret; } static void belle_sip_source_to_poll(belle_sip_source_t *s, belle_sip_pollfd_t *pfd, int i){ pfd[i].fd=s->fd; pfd[i].events=belle_sip_event_to_poll(s->events); pfd[i].revents=0; s->index=i; } static unsigned int belle_sip_source_get_revents(belle_sip_source_t *s,belle_sip_pollfd_t *pfd){ return belle_sip_poll_to_event(&pfd[s->index]); } #else #include typedef HANDLE belle_sip_pollfd_t; static void belle_sip_source_to_poll(belle_sip_source_t *s, belle_sip_pollfd_t *pfd,int i){ s->index=i; pfd[i]=s->fd; /*special treatments for windows sockets*/ if (s->sock!=(belle_sip_socket_t)-1){ int err; long events=0; if (s->events & BELLE_SIP_EVENT_READ) events|=FD_READ|FD_ACCEPT; if (s->events & BELLE_SIP_EVENT_WRITE) events|=FD_WRITE|FD_CONNECT; if (events!=s->armed_events){ s->armed_events=events; err=WSAEventSelect(s->sock,s->fd,events); if (err!=0) belle_sip_error("WSAEventSelect() failed: %s",belle_sip_get_socket_error_string()); } } } static unsigned int belle_sip_source_get_revents(belle_sip_source_t *s,belle_sip_pollfd_t *pfd){ WSANETWORKEVENTS revents={0}; int err; unsigned int ret=0; if (WaitForSingleObjectEx(s->fd,0,FALSE)==WAIT_OBJECT_0){ if (s->sock!=(belle_sip_socket_t)-1){ /*special treatments for windows sockets*/ err=WSAEnumNetworkEvents(s->sock,s->fd,&revents); if (err!=0){ belle_sip_error("WSAEnumNetworkEvents() failed: %s socket=%x",belle_sip_get_socket_error_string(),(unsigned int)s->sock); return 0; } if (revents.lNetworkEvents & FD_READ || revents.lNetworkEvents & FD_ACCEPT){ ret|=BELLE_SIP_EVENT_READ; } if (revents.lNetworkEvents & FD_WRITE || revents.lNetworkEvents & FD_CONNECT){ ret|=BELLE_SIP_EVENT_WRITE; } s->armed_events=0; }else{ ret=BELLE_SIP_EVENT_READ; ResetEvent(s->fd); } } return ret; } static int belle_sip_poll(belle_sip_pollfd_t *pfd, int count, int duration){ DWORD ret; if (count == 0) { belle_sip_sleep(duration); return 0; } ret=WaitForMultipleObjectsEx(count,pfd,FALSE,duration,FALSE); if (ret==WAIT_FAILED){ belle_sip_error("WaitForMultipleObjectsEx() failed."); return -1; } if (ret==WAIT_TIMEOUT){ return 0; } return ret-WAIT_OBJECT_0; } #endif static void belle_sip_source_destroy(belle_sip_source_t *obj){ if (obj->node.next || obj->node.prev){ belle_sip_fatal("Destroying source currently used in main loop !"); } belle_sip_source_uninit(obj); } static void belle_sip_source_init(belle_sip_source_t *s, belle_sip_source_func_t func, void *data, belle_sip_fd_t fd, unsigned int events, unsigned int timeout_value_ms){ static unsigned long global_id=1; s->node.data=s; if (s->id==0) s->id=global_id++; s->fd=fd; s->events=events; s->timeout=timeout_value_ms; s->data=data; s->notify=func; s->sock=(belle_sip_socket_t)-1; } void belle_sip_source_uninit(belle_sip_source_t *obj){ #ifdef _WIN32 if (obj->sock!=(belle_sip_socket_t)-1){ WSACloseEvent(obj->fd); obj->fd=(WSAEVENT)-1; } #endif obj->fd=(belle_sip_fd_t)-1; obj->sock=(belle_sip_socket_t)-1; /* if (obj->it) { bctbx_iterator_delete(obj->it); obj->it=NULL; }*/ } void belle_sip_source_set_notify(belle_sip_source_t *s, belle_sip_source_func_t func) { s->notify = func; } void belle_sip_socket_source_init(belle_sip_source_t *s, belle_sip_source_func_t func, void *data, belle_sip_socket_t sock, unsigned int events, unsigned int timeout_value_ms){ #ifdef _WIN32 /*on windows, the fd to poll is not the socket */ belle_sip_fd_t fd=(belle_sip_fd_t)-1; if (sock!=(belle_sip_socket_t)-1) fd=WSACreateEvent(); else fd=(WSAEVENT)-1; belle_sip_source_init(s,func,data,fd,events,timeout_value_ms); #else belle_sip_source_init(s,func,data,sock,events,timeout_value_ms); #endif s->sock=sock; if (sock!=(belle_sip_socket_t)-1) belle_sip_socket_set_nonblocking(sock); } void belle_sip_fd_source_init(belle_sip_source_t *s, belle_sip_source_func_t func, void *data, belle_sip_fd_t fd, unsigned int events, unsigned int timeout_value_ms){ belle_sip_source_init(s,func,data,fd,events,timeout_value_ms); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_source_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_source_t,belle_sip_object_t,belle_sip_source_destroy,NULL,NULL,FALSE); belle_sip_source_t * belle_sip_socket_source_new(belle_sip_source_func_t func, void *data, belle_sip_socket_t sock, unsigned int events, unsigned int timeout_value_ms){ belle_sip_source_t *s=belle_sip_object_new(belle_sip_source_t); belle_sip_socket_source_init(s,func,data,sock,events,timeout_value_ms); return s; } belle_sip_source_t * belle_sip_fd_source_new(belle_sip_source_func_t func, void *data, belle_sip_fd_t fd, unsigned int events, unsigned int timeout_value_ms){ belle_sip_source_t *s=belle_sip_object_new(belle_sip_source_t); belle_sip_fd_source_init(s,func,data,fd,events,timeout_value_ms); return s; } belle_sip_source_t * belle_sip_timeout_source_new(belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms){ return belle_sip_socket_source_new(func,data,(belle_sip_socket_t)-1,0,timeout_value_ms); } unsigned long belle_sip_source_get_id(const belle_sip_source_t *s){ return s->id; } void * belle_sip_source_get_user_data(const belle_sip_source_t *s) { return s->data; } void belle_sip_source_set_user_data(belle_sip_source_t *s, void *user_data) { s->data = user_data; } int belle_sip_source_set_events(belle_sip_source_t* source, int event_mask) { source->events = event_mask; return 0; } belle_sip_socket_t belle_sip_source_get_socket(const belle_sip_source_t* source) { return source->sock; } struct belle_sip_main_loop{ belle_sip_object_t base; belle_sip_list_t *fd_sources; bctbx_map_t *timer_sources; belle_sip_object_pool_t *pool; int nsources; int run; int in_loop; bctbx_mutex_t timer_sources_mutex; }; void belle_sip_main_loop_remove_source(belle_sip_main_loop_t *ml, belle_sip_source_t *source){ bool_t elem_removed = FALSE; if (source->node.next || source->node.prev || &source->node==ml->fd_sources) { ml->fd_sources=belle_sip_list_remove_link(ml->fd_sources,&source->node); belle_sip_object_unref(source); elem_removed = TRUE; } if (source->it) { bctbx_mutex_lock(&ml->timer_sources_mutex); bctbx_map_erase(ml->timer_sources, source->it); bctbx_iterator_delete(source->it); bctbx_mutex_unlock(&ml->timer_sources_mutex); source->it=NULL; belle_sip_object_unref(source); elem_removed = TRUE; } if (elem_removed) { source->cancelled=TRUE; ml->nsources--; if (source->on_remove) source->on_remove(source); } } static void belle_sip_main_loop_destroy(belle_sip_main_loop_t *ml){ while (ml->fd_sources){ belle_sip_main_loop_remove_source(ml,(belle_sip_source_t*)ml->fd_sources->data); } if (belle_sip_object_pool_cleanable(ml->pool)){ belle_sip_object_unref(ml->pool); } bctbx_mmap_ullong_delete(ml->timer_sources); bctbx_mutex_destroy(&ml->timer_sources_mutex); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_main_loop_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_main_loop_t,belle_sip_object_t,belle_sip_main_loop_destroy,NULL,NULL,FALSE); belle_sip_main_loop_t *belle_sip_main_loop_new(void){ belle_sip_main_loop_t*m=belle_sip_object_new(belle_sip_main_loop_t); m->pool=belle_sip_object_pool_push(); m->timer_sources = bctbx_mmap_ullong_new(); bctbx_mutex_init(&m->timer_sources_mutex,NULL); return m; } void belle_sip_main_loop_add_source(belle_sip_main_loop_t *ml, belle_sip_source_t *source){ if (source->node.next || source->node.prev){ belle_sip_fatal("Source is already linked somewhere else."); return; } if (source->node.data!=source){ belle_sip_fatal("Insane source passed to belle_sip_main_loop_add_source() !"); return; } source->ml=ml; if (source->timeout>=0){ belle_sip_object_ref(source); source->expire_ms=belle_sip_time_ms()+source->timeout; bctbx_mutex_lock(&ml->timer_sources_mutex); source->it = bctbx_map_insert_and_delete_with_returned_it(ml->timer_sources , (bctbx_pair_t*)bctbx_pair_ullong_new(source->expire_ms, source)); bctbx_mutex_unlock(&ml->timer_sources_mutex); } source->cancelled=FALSE; if (source->fd != (belle_sip_fd_t)-1 ) { belle_sip_object_ref(source); ml->fd_sources=belle_sip_list_prepend_link(ml->fd_sources,&source->node); } ml->nsources++; } belle_sip_source_t* belle_sip_main_loop_create_timeout_with_remove_cb( belle_sip_main_loop_t *ml , belle_sip_source_func_t func , void *data , unsigned int timeout_value_ms , const char* timer_name , belle_sip_source_remove_callback_t remove_func) { belle_sip_source_t * s=belle_sip_timeout_source_new(func,data,timeout_value_ms); belle_sip_object_set_name((belle_sip_object_t*)s,timer_name); if (remove_func) { belle_sip_source_set_remove_cb(s, remove_func); } belle_sip_main_loop_add_source(ml,s); return s; } belle_sip_source_t* belle_sip_main_loop_create_timeout(belle_sip_main_loop_t *ml , belle_sip_source_func_t func , void *data , unsigned int timeout_value_ms ,const char* timer_name) { return belle_sip_main_loop_create_timeout_with_remove_cb(ml, func, data, timeout_value_ms,timer_name,NULL); } unsigned long belle_sip_main_loop_add_timeout(belle_sip_main_loop_t *ml, belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms){ belle_sip_source_t * s=belle_sip_main_loop_create_timeout(ml,func,data,timeout_value_ms,"Timer"); belle_sip_object_unref(s); return s->id; } void belle_sip_main_loop_do_later(belle_sip_main_loop_t *ml, belle_sip_callback_t func, void *data){ belle_sip_source_t * s=belle_sip_main_loop_create_timeout(ml,(belle_sip_source_func_t)func,data,0,"defered task"); s->oneshot=TRUE; belle_sip_object_unref(s); } void belle_sip_source_set_timeout(belle_sip_source_t *s, unsigned int value_ms){ if (!s->expired){ belle_sip_main_loop_t *ml = s->ml; s->expire_ms=belle_sip_time_ms()+value_ms; if (s->it){ /*this timeout is already sorted in the timer_sources map, we need to move it to its new place*/ bctbx_mutex_lock(&ml->timer_sources_mutex); bctbx_map_erase(ml->timer_sources, s->it); bctbx_iterator_delete(s->it); s->it = bctbx_map_insert_and_delete_with_returned_it(ml->timer_sources, (bctbx_pair_t*)bctbx_pair_ullong_new(s->expire_ms, s)); bctbx_mutex_unlock(&ml->timer_sources_mutex); } } s->timeout=value_ms; } void belle_sip_source_set_remove_cb(belle_sip_source_t *s, belle_sip_source_remove_callback_t func) { s->on_remove=func; } unsigned int belle_sip_source_get_timeout(const belle_sip_source_t *s){ return s->timeout; } void belle_sip_source_cancel(belle_sip_source_t *s){ s->cancelled=TRUE; if (s->it) { bctbx_mutex_lock(&s->ml->timer_sources_mutex); bctbx_map_erase(s->ml->timer_sources, s->it); bctbx_iterator_delete(s->it); /*put on front*/ s->it = bctbx_map_insert_and_delete_with_returned_it(s->ml->timer_sources, (bctbx_pair_t*)bctbx_pair_ullong_new(0, s)); bctbx_mutex_unlock(&s->ml->timer_sources_mutex); } } static int match_source_id(const void *s, const void *pid){ if ( ((belle_sip_source_t*)s)->id==(unsigned long)(intptr_t)pid){ return 0; } return -1; } belle_sip_source_t *belle_sip_main_loop_find_source(belle_sip_main_loop_t *ml, unsigned long id){ bctbx_iterator_t *it; belle_sip_source_t *ret=NULL; belle_sip_list_t *elem=belle_sip_list_find_custom(ml->fd_sources,match_source_id,(const void*)(intptr_t)id); if (elem!=NULL) { ret = (belle_sip_source_t*)elem->data; } else if ((it = bctbx_map_find_custom(ml->timer_sources, match_source_id, (const void*)(intptr_t)id))) { ret = (belle_sip_source_t*)bctbx_pair_get_second(bctbx_iterator_get_pair(it)); bctbx_iterator_delete(it); } /*else ret = NULL;*/ return ret; } void belle_sip_main_loop_cancel_source(belle_sip_main_loop_t *ml, unsigned long id){ belle_sip_source_t *s=belle_sip_main_loop_find_source(ml,id); if (s) belle_sip_source_cancel(s); } static void belle_sip_main_loop_iterate(belle_sip_main_loop_t *ml){ size_t pfd_size = ml->nsources * sizeof(belle_sip_pollfd_t); belle_sip_pollfd_t *pfd=(belle_sip_pollfd_t*)belle_sip_malloc0(pfd_size); int i=0; belle_sip_source_t *s; belle_sip_list_t *elem,*next; int duration=-1; int ret; uint64_t cur; belle_sip_list_t *to_be_notified=NULL; int can_clean=belle_sip_object_pool_cleanable(ml->pool); /*iterate might not be called by the thread that created the main loop*/ belle_sip_object_pool_t *tmp_pool=NULL; bctbx_iterator_t *it,*end; if (!can_clean){ /*Push a temporary pool for the time of the iterate loop*/ tmp_pool=belle_sip_object_pool_push(); } /*Step 1: prepare the pollfd table and get the next timeout value */ for(elem=ml->fd_sources;elem!=NULL;elem=next) { next=elem->next; s=(belle_sip_source_t*)elem->data; if (!s->cancelled){ if (s->fd!=(belle_sip_fd_t)-1){ belle_sip_source_to_poll(s,pfd,i); ++i; } } } /*all source with timeout are in ml->timer_sources*/ if (bctbx_map_size(ml->timer_sources) >0) { int64_t diff; uint64_t next_wakeup_time; it = bctbx_map_begin(ml->timer_sources); /*use first because in case of canceled timer, key ==0 , key != s->expire_ms */ next_wakeup_time = bctbx_pair_ullong_get_first((const bctbx_pair_ullong_t *)bctbx_iterator_get_pair(it)); /* compute the amount of time to wait for shortest timeout*/ cur=belle_sip_time_ms(); diff=next_wakeup_time-cur; if (diff>0) duration=MIN((unsigned int)diff,INT_MAX); else duration=0; bctbx_iterator_delete(it); it = NULL; } /* do the poll */ ret=belle_sip_poll(pfd,i,duration); if (ret==-1){ goto end; } /* Step 2: examine poll results and determine the list of source to be notified */ cur=belle_sip_time_ms(); for(elem=ml->fd_sources;elem!=NULL;elem=elem->next){ unsigned revents=0; s=(belle_sip_source_t*)elem->data; if (!s->cancelled){ if (s->fd!=(belle_sip_fd_t)-1){ if (s->notify_required) { /*for testing purpose to force channel to read*/ revents=BELLE_SIP_EVENT_READ; s->notify_required=0; /*reset*/ } else { revents=belle_sip_source_get_revents(s,pfd); } s->revents=revents; } else { belle_sip_error("Source [%p] does not contains any fd !",s); } if (revents!=0){ to_be_notified=belle_sip_list_append(to_be_notified,belle_sip_object_ref(s)); } }else to_be_notified=belle_sip_list_append(to_be_notified,belle_sip_object_ref(s)); } /* Step 3: find timeouted sources */ bctbx_mutex_lock(&ml->timer_sources_mutex); /*iterator chain might be alterated by element insertion*/ it = bctbx_map_begin(ml->timer_sources); end = bctbx_map_end(ml->timer_sources); while (!bctbx_iterator_equals(it,end)) { /*use first because in case of canceled timer, key != s->expire_ms*/ uint64_t expire = bctbx_pair_ullong_get_first((const bctbx_pair_ullong_t *)bctbx_iterator_get_pair(it)); s = (belle_sip_source_t*)bctbx_pair_get_second(bctbx_iterator_get_pair(it)); if (expire > cur) { /* no need to continue looping because map is ordered*/ break; } else { if (s->revents==0) { s->expired=TRUE; to_be_notified=belle_sip_list_append(to_be_notified,belle_sip_object_ref(s)); } /*else already in to_be_notified by Step 2*/ s->revents|=BELLE_SIP_EVENT_TIMEOUT; it=bctbx_iterator_get_next(it); } } bctbx_iterator_delete(it); bctbx_iterator_delete(end); bctbx_mutex_unlock(&ml->timer_sources_mutex); /* Step 4: notify those to be notified */ for(elem=to_be_notified;elem!=NULL;){ s=(belle_sip_source_t*)elem->data; next=elem->next; if (!s->cancelled){ if (s->timeout > 0 && belle_sip_log_level_enabled(BELLE_SIP_LOG_DEBUG)) { /*to avoid too many traces*/ char *objdesc=belle_sip_object_to_string((belle_sip_object_t*)s); belle_sip_debug("source %s notified revents=%u, timeout=%i",objdesc,revents,s->timeout); belle_sip_free(objdesc); } ret=s->notify(s->data,s->revents); if (ret==BELLE_SIP_STOP || s->oneshot){ /*this source needs to be removed*/ belle_sip_main_loop_remove_source(ml,s); } else { if (s->expired && s->it) { bctbx_mutex_lock(&ml->timer_sources_mutex); bctbx_map_erase(ml->timer_sources, s->it); bctbx_iterator_delete(s->it); bctbx_mutex_unlock(&ml->timer_sources_mutex); s->it=NULL; belle_sip_object_unref(s); } if (!s->it && s->timeout >= 0){ /*timeout needs to be started again */ if (ret==BELLE_SIP_CONTINUE_WITHOUT_CATCHUP){ s->expire_ms=cur+s->timeout; }else{ s->expire_ms+=s->timeout; } s->expired=FALSE; bctbx_mutex_lock(&ml->timer_sources_mutex); s->it = bctbx_map_insert_and_delete_with_returned_it(ml->timer_sources, (bctbx_pair_t*)bctbx_pair_ullong_new(s->expire_ms, s)); bctbx_mutex_unlock(&ml->timer_sources_mutex); belle_sip_object_ref(s); } } } else { belle_sip_main_loop_remove_source(ml,s); } s->revents=0; belle_sip_object_unref(s); belle_sip_free(elem); /*free just the element*/ elem=next; } if (can_clean) belle_sip_object_pool_clean(ml->pool); else if (tmp_pool) { belle_sip_object_unref(tmp_pool); tmp_pool=NULL; } end: belle_sip_free(pfd); } void belle_sip_main_loop_run(belle_sip_main_loop_t *ml){ if (ml->in_loop){ belle_sip_warning("belle_sip_main_loop_run(): reentrancy detected, doing nothing"); return; } ml->run = TRUE; ml->in_loop = TRUE; while(ml->run){ belle_sip_main_loop_iterate(ml); } ml->in_loop = FALSE; } int belle_sip_main_loop_quit(belle_sip_main_loop_t *ml){ ml->run=0; return BELLE_SIP_STOP; } void belle_sip_main_loop_sleep(belle_sip_main_loop_t *ml, int milliseconds){ belle_sip_source_t * s=belle_sip_main_loop_create_timeout(ml,(belle_sip_source_func_t)belle_sip_main_loop_quit,ml,milliseconds,"Main loop sleep timer"); belle_sip_main_loop_run(ml); belle_sip_main_loop_remove_source(ml,s); belle_sip_object_unref(s); } belle-sip-1.6.3/src/belle_sip_object.c000066400000000000000000000626661313437522400176400ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" static void _belle_sip_object_pool_remove_from_stack(belle_sip_object_pool_t *pool); static int _belle_sip_object_marshal_check_enabled = FALSE; static int has_type(belle_sip_object_t *obj, belle_sip_type_id_t id){ belle_sip_object_vptr_t *vptr=obj->vptr; while(vptr!=NULL){ if (vptr->id==id) return TRUE; vptr=vptr->get_parent(); } return FALSE; } int _belle_sip_object_is_instance_of(belle_sip_object_t * obj,belle_sip_type_id_t id) { return has_type(obj,id); } void belle_sip_object_enable_marshal_check(int enable) { _belle_sip_object_marshal_check_enabled = (enable) ? TRUE : FALSE; } static belle_sip_list_t *all_objects=NULL; static int belle_sip_leak_detector_enabled=FALSE; static int belle_sip_leak_detector_inhibited=FALSE; static void add_new_object(belle_sip_object_t *obj){ if (belle_sip_leak_detector_enabled && !belle_sip_leak_detector_inhibited){ all_objects=belle_sip_list_prepend(all_objects,obj); } } static void remove_free_object(belle_sip_object_t *obj){ if (belle_sip_leak_detector_enabled && !belle_sip_leak_detector_inhibited){ belle_sip_list_t* it; it=belle_sip_list_find(all_objects,obj); if (it) all_objects = belle_sip_list_delete_link(all_objects,it); /*it may fail if the leak detector was inhibitted at the time the object was created*/ } } void belle_sip_object_inhibit_leak_detector(int yes){ belle_sip_leak_detector_inhibited=yes ? TRUE : FALSE; } void belle_sip_object_enable_leak_detector(int enable){ belle_sip_leak_detector_enabled=enable; } int belle_sip_object_get_object_count(void){ return (int)belle_sip_list_size(all_objects); } void belle_sip_object_flush_active_objects(void){ //do not free objects so that they are still detected as leaked by valgrind and such all_objects = belle_sip_list_free(all_objects); } void belle_sip_object_dump_active_objects(void){ belle_sip_list_t *elem; if (all_objects){ belle_sip_warning("List of leaked objects:"); for(elem=all_objects;elem!=NULL;elem=elem->next){ belle_sip_object_t *obj=(belle_sip_object_t*)elem->data; char* content= belle_sip_object_to_string(obj); belle_sip_warning("%s(%p) ref=%i, content [%10s...]",obj->vptr->type_name,obj,obj->ref,content); belle_sip_free(content); } }else belle_sip_warning("No objects leaked."); } belle_sip_object_t * _belle_sip_object_new(size_t objsize, belle_sip_object_vptr_t *vptr){ belle_sip_object_t *obj=(belle_sip_object_t *)belle_sip_malloc0(vptr->size); obj->ref=vptr->initially_unowned ? 0 : 1; obj->vptr=vptr; if (obj->ref==0){ belle_sip_object_pool_t *pool=belle_sip_object_pool_get_current(); if (pool) belle_sip_object_pool_add(pool,obj); } add_new_object(obj); return obj; } int belle_sip_object_is_initially_unowned(const belle_sip_object_t *obj){ return obj->vptr->initially_unowned; } belle_sip_object_t * belle_sip_object_ref(void *obj){ belle_sip_object_t *o=BELLE_SIP_OBJECT(obj); if (o->ref==0 && o->pool){ belle_sip_object_pool_remove(o->pool,obj); } o->ref++; return obj; } void belle_sip_object_unref(void *ptr){ belle_sip_object_t *obj=BELLE_SIP_OBJECT(ptr); if (obj->ref <= -1) { belle_sip_error("Object [%p] freed twice or corrupted !",obj); if (obj->vptr && obj->vptr->type_name) belle_sip_error("Object type might be [%s]",obj->vptr->type_name); if (obj->name) belle_sip_error("Object name might be [%s]",obj->name); belle_sip_fatal("Fatal object error encountered, aborting."); return; } if (obj->vptr->initially_unowned && obj->ref==0){ if (obj->pool) belle_sip_object_pool_remove(obj->pool,obj); obj->ref=-1; belle_sip_object_delete(obj); return; } obj->ref--; if (obj->ref == 0){ obj->ref = -1; belle_sip_object_delete(obj); } } static weak_ref_t *weak_ref_new(belle_sip_object_destroy_notify_t destroy_notify, void *userpointer){ weak_ref_t *r=belle_sip_new(weak_ref_t); r->next=NULL; r->notify=destroy_notify; r->userpointer=userpointer; return r; } belle_sip_object_t *belle_sip_object_weak_ref(void *obj, belle_sip_object_destroy_notify_t destroy_notify, void *userpointer){ belle_sip_object_t *o=BELLE_SIP_OBJECT(obj); weak_ref_t *old=o->weak_refs; o->weak_refs=weak_ref_new(destroy_notify,userpointer); o->weak_refs->next=old; return o; } void belle_sip_object_weak_unref(void *obj, belle_sip_object_destroy_notify_t destroy_notify, void *userpointer){ belle_sip_object_t *o=BELLE_SIP_OBJECT(obj); weak_ref_t *ref,*prevref=NULL,*next=NULL; int found=FALSE; if (o->ref==-1) return; /*too late and avoid recursions*/ for(ref=o->weak_refs;ref!=NULL;ref=next){ next=ref->next; if (ref->notify==destroy_notify && ref->userpointer==userpointer){ if (prevref==NULL) o->weak_refs=next; else prevref->next=next; belle_sip_free(ref); found=TRUE; /*do not break or return, someone could have put twice the same weak ref on the same object*/ }else{ prevref=ref; } } if (!found) belle_sip_fatal("Could not find weak_ref, you're a looser."); } static void belle_sip_object_loose_weak_refs(belle_sip_object_t *obj){ weak_ref_t *ref,*next; for(ref=obj->weak_refs;ref!=NULL;ref=next){ next=ref->next; ref->notify(ref->userpointer,obj); belle_sip_free(ref); } obj->weak_refs=NULL; } static void _belle_sip_object_uninit(belle_sip_object_t *obj){ if (obj->name) belle_sip_free(obj->name); } static void _belle_sip_object_clone(belle_sip_object_t *obj, const belle_sip_object_t *orig){ if (orig->name!=NULL) obj->name=belle_sip_strdup(orig->name); } static belle_sip_error_code _belle_object_marshal(belle_sip_object_t* obj, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"{%s::%s %p}",obj->vptr->type_name,obj->name ? obj->name : "(no name)",obj); } static belle_sip_object_vptr_t *no_parent(void){ return NULL; } belle_sip_object_vptr_t belle_sip_object_t_vptr={ BELLE_SIP_TYPE_ID(belle_sip_object_t), sizeof(belle_sip_object_t), "belle_sip_object_t", FALSE, no_parent, /*no parent, it's god*/ NULL, _belle_sip_object_uninit, _belle_sip_object_clone, _belle_object_marshal, BELLE_SIP_DEFAULT_BUFSIZE_HINT }; belle_sip_object_vptr_t *belle_sip_object_t_vptr_get(void){ return &belle_sip_object_t_vptr; } void belle_sip_object_delete(void *ptr){ belle_sip_object_t *obj=BELLE_SIP_OBJECT(ptr); belle_sip_object_vptr_t *vptr; belle_sip_object_loose_weak_refs(obj); remove_free_object(obj); vptr=obj->vptr; while(vptr!=NULL){ if (vptr->destroy) vptr->destroy(obj); vptr=vptr->get_parent(); } belle_sip_object_data_clear(obj); belle_sip_free(obj); } static belle_sip_object_vptr_t *find_common_floor(belle_sip_object_vptr_t *vptr1, belle_sip_object_vptr_t *vptr2){ belle_sip_object_vptr_t *it1,*it2; for (it1=vptr1;it1!=NULL;it1=it1->get_parent()){ if (it1==vptr2) return vptr2; } for(it2=vptr2;it2!=NULL;it2=it2->get_parent()){ if (vptr1==it2) return vptr1; } return find_common_floor(vptr1->get_parent(),vptr2); } /*copy the content of ref object to new object, for the part they have in common in their inheritence diagram*/ void _belle_sip_object_copy(belle_sip_object_t *newobj, const belle_sip_object_t *ref){ belle_sip_object_vptr_t *vptr; vptr=find_common_floor(newobj->vptr,ref->vptr); if (vptr==NULL){ belle_sip_fatal("Should not happen"); } while(vptr!=NULL){ if (vptr->clone==NULL){ belle_sip_fatal("Object of type %s cannot be cloned, it does not provide a clone() implementation.",vptr->type_name); return; }else vptr->clone(newobj,ref); vptr=vptr->get_parent(); } } belle_sip_object_t *belle_sip_object_clone(const belle_sip_object_t *obj){ belle_sip_object_t *newobj; newobj=belle_sip_malloc0(obj->vptr->size); newobj->ref=obj->vptr->initially_unowned ? 0 : 1; newobj->vptr=obj->vptr; _belle_sip_object_copy(newobj,obj); if (newobj->ref==0){ belle_sip_object_pool_t *pool=belle_sip_object_pool_get_current(); if (pool) belle_sip_object_pool_add(pool,newobj); } add_new_object(newobj); return newobj; } belle_sip_object_t *belle_sip_object_clone_and_ref(const belle_sip_object_t *obj) { return belle_sip_object_ref(belle_sip_object_clone(obj)); } struct belle_sip_object_data{ char* name; void* data; belle_sip_data_destroy destroy_func; }; static int belle_sip_object_data_find(const void* a, const void* b) { struct belle_sip_object_data* da = (struct belle_sip_object_data*)a; return strcmp(da->name, (const char*)b); } static void belle_sip_object_data_destroy(void* data) { struct belle_sip_object_data* da = (struct belle_sip_object_data*)data; if (da->destroy_func) da->destroy_func(da->data); belle_sip_free(da->name); belle_sip_free(da); } int belle_sip_object_data_set( belle_sip_object_t *obj, const char* name, void* data, belle_sip_data_destroy destroy_func ) { int ret = 0; belle_sip_list_t* list_entry = belle_sip_list_find_custom(obj->data_store, belle_sip_object_data_find, name); struct belle_sip_object_data* entry = (list_entry)? list_entry->data : NULL; if( entry == NULL){ entry = belle_sip_malloc0(sizeof( struct belle_sip_object_data)); obj->data_store = belle_sip_list_append(obj->data_store, entry); } else { // clean previous data if( entry->destroy_func ) entry->destroy_func(entry->data); belle_sip_free(entry->name); ret = 1; } if( entry ){ entry->data = data; entry->name = belle_sip_strdup(name); entry->destroy_func = destroy_func; } else { ret = -1; } return ret; } void* belle_sip_object_data_get( belle_sip_object_t *obj, const char* name ) { belle_sip_list_t *list_entry = belle_sip_list_find_custom(obj->data_store, belle_sip_object_data_find, name); struct belle_sip_object_data* entry = (list_entry)? list_entry->data : NULL; return entry? entry->data : NULL; } int belle_sip_object_data_remove( belle_sip_object_t *obj, const char* name) { belle_sip_list_t *list_entry = belle_sip_list_find_custom(obj->data_store, belle_sip_object_data_find, name); struct belle_sip_object_data* entry = (list_entry)? list_entry->data : NULL; if( entry ){ belle_sip_free(entry->name); if( entry->destroy_func ) entry->destroy_func(entry->data); belle_sip_free(entry); } if( list_entry ) obj->data_store = belle_sip_list_remove_link(obj->data_store, list_entry); return !(list_entry!= NULL); } int belle_sip_object_data_exists( const belle_sip_object_t *obj, const char* name ) { return (belle_sip_list_find_custom(obj->data_store, belle_sip_object_data_find, name) != NULL); } void* belle_sip_object_data_grab( belle_sip_object_t* obj, const char* name) { belle_sip_list_t *list_entry = belle_sip_list_find_custom(obj->data_store, belle_sip_object_data_find, name); struct belle_sip_object_data* entry = (list_entry)? list_entry->data : NULL; void* data =NULL; if( entry ){ belle_sip_free(entry->name); data = entry->data; } obj->data_store = belle_sip_list_remove_link(obj->data_store, list_entry); belle_sip_free(entry); return data; } void belle_sip_object_data_clear( belle_sip_object_t* obj ) { belle_sip_list_for_each(obj->data_store, belle_sip_object_data_destroy); obj->data_store = belle_sip_list_free(obj->data_store); } void belle_sip_object_data_clone( const belle_sip_object_t* src, belle_sip_object_t* dst, belle_sip_data_clone clone_func) { belle_sip_object_data_clear(dst); belle_sip_object_data_merge(src, dst, clone_func); } void belle_sip_object_data_merge( const belle_sip_object_t* src, belle_sip_object_t* dst, belle_sip_data_clone clone_func) { belle_sip_list_t *list = src->data_store; struct belle_sip_object_data* it = NULL; void* cloned_data = NULL; while( list ){ it = list->data; if( it ){ cloned_data = (clone_func)? clone_func( it->name, it->data ) : it->data; belle_sip_object_data_set(dst, it->name, cloned_data, it->destroy_func); } list = list->next; } } struct belle_sip_object_foreach_data { void (*apply_func)(const char*, void*, void*); void* userdata; }; static void belle_sip_object_for_each_cb(void* data, void* pvdata) { struct belle_sip_object_data* it = (struct belle_sip_object_data*)data; struct belle_sip_object_foreach_data* fd = (struct belle_sip_object_foreach_data*)pvdata; if( it && fd->apply_func ){ fd->apply_func(it->name, it->data, fd->userdata); } } void belle_sip_object_data_foreach( const belle_sip_object_t* obj, void (*apply_func)(const char* key, void* data, void* userdata), void* userdata) { struct belle_sip_object_foreach_data fd = { apply_func, userdata }; belle_sip_list_for_each2(obj->data_store, belle_sip_object_for_each_cb, &fd); } void *belle_sip_object_cast(belle_sip_object_t *obj, belle_sip_type_id_t id, const char *castname, const char *file, int fileno){ if (obj!=NULL){ if (has_type(obj,id)==0){ belle_sip_fatal("Bad cast to %s at %s:%i",castname,file,fileno); return NULL; } } return obj; } void *belle_sip_object_get_interface_methods(belle_sip_object_t *obj, belle_sip_interface_id_t ifid){ if (obj!=NULL){ belle_sip_object_vptr_t *vptr; for (vptr=obj->vptr;vptr!=NULL;vptr=vptr->get_parent()){ belle_sip_interface_desc_t **ifaces=vptr->interfaces; if (ifaces!=NULL){ for(;*ifaces!=0;++ifaces){ if ((*ifaces)->id==ifid){ return *ifaces; } } } } } return NULL; } int belle_sip_object_implements(belle_sip_object_t *obj, belle_sip_interface_id_t id){ return belle_sip_object_get_interface_methods(obj,id)!=NULL; } void *belle_sip_object_interface_cast(belle_sip_object_t *obj, belle_sip_interface_id_t ifid, const char *castname, const char *file, int fileno){ if (obj!=NULL){ if (belle_sip_object_get_interface_methods(obj,ifid)==0){ belle_sip_fatal("Bad cast to interface %s at %s:%i",castname,file,fileno); return NULL; } } return obj; } void belle_sip_object_set_name(belle_sip_object_t* object,const char* name) { if (object->name) { belle_sip_free(object->name); object->name=NULL; } if (name) object->name=belle_sip_strdup(name); } const char* belle_sip_object_get_name(belle_sip_object_t* object) { return object->name; } static belle_sip_error_code checked_marshal(belle_sip_object_vptr_t *vptr, belle_sip_object_t* obj, char* buff, size_t buff_size, size_t *offset){ size_t tmp_buf_size=buff_size*2; char *p=(char*)belle_sip_malloc0(tmp_buf_size); size_t i; size_t initial_offset=*offset; belle_sip_error_code error=vptr->marshal(obj,p,buff_size,offset); size_t written; for (i=initial_offset;itype_name,(int)written,(int)(*offset-initial_offset)); } memcpy(buff+initial_offset,p+initial_offset,*offset-initial_offset); }else if (error==BELLE_SIP_BUFFER_OVERFLOW){ /* Case where the object aborted the marshalling because of not enough room. * Should this happen, it is not allowed to write past buffer end anyway */ if (written > buff_size){ belle_sip_fatal("Object of type %s commited a buffer overflow by marshalling %i bytes", vptr->type_name,(int)(*offset-initial_offset)); } }else{ belle_sip_error("Object of type %s produced an error during marshalling: %i", vptr->type_name,error); } belle_sip_free(p); return error; } belle_sip_error_code belle_sip_object_marshal(belle_sip_object_t* obj, char* buff, size_t buff_size, size_t *offset) { belle_sip_object_vptr_t *vptr=obj->vptr; while (vptr != NULL) { if (vptr->marshal != NULL) { if (_belle_sip_object_marshal_check_enabled == TRUE) return checked_marshal(vptr,obj,buff,buff_size,offset); else return vptr->marshal(obj,buff,buff_size,offset); } else { vptr=vptr->get_parent(); } } return BELLE_SIP_NOT_IMPLEMENTED; /*no implementation found*/ } static char * belle_sip_object_to_alloc_string(belle_sip_object_t *obj, int size_hint){ char *buf=belle_sip_malloc(size_hint); size_t offset=0; belle_sip_error_code error = belle_sip_object_marshal(obj,buf,size_hint-1,&offset); obj->vptr->tostring_bufsize_hint=size_hint; if (error==BELLE_SIP_BUFFER_OVERFLOW){ belle_sip_message("belle_sip_object_to_alloc_string(): hint buffer was too short while doing to_string() for %s, retrying", obj->vptr->type_name); belle_sip_free(buf); return belle_sip_object_to_alloc_string(obj,2*size_hint); } buf=belle_sip_realloc(buf,offset+1); buf[offset]='\0'; return buf; } static int get_hint_size(int size){ if (size<128) return 128; return size; } char* belle_sip_object_to_string(void* _obj) { belle_sip_object_t *obj=BELLE_SIP_OBJECT(_obj); if (obj->vptr->tostring_bufsize_hint!=0){ return belle_sip_object_to_alloc_string(obj,obj->vptr->tostring_bufsize_hint); }else{ char buff[BELLE_SIP_MAX_TO_STRING_SIZE]; size_t offset=0; belle_sip_error_code error = belle_sip_object_marshal(obj,buff,sizeof(buff)-1,&offset); if (error==BELLE_SIP_BUFFER_OVERFLOW){ belle_sip_message("belle_sip_object_to_string(): temporary buffer is too short while doing to_string() for %s, retrying", obj->vptr->type_name); return belle_sip_object_to_alloc_string(obj,get_hint_size(2*(int)offset)); } buff[offset]='\0'; obj->vptr->tostring_bufsize_hint=get_hint_size(2*(int)offset); return belle_sip_strdup(buff); } } char * _belle_sip_object_describe_type(belle_sip_object_vptr_t *vptr){ const int maxbufsize=2048; char *ret=belle_sip_malloc(maxbufsize); belle_sip_object_vptr_t *it; size_t pos=0; belle_sip_list_t *l=NULL,*elem; belle_sip_snprintf(ret,maxbufsize,&pos,"Ownership:\n"); belle_sip_snprintf(ret,maxbufsize,&pos,"\t%s is created initially %s\n",vptr->type_name, vptr->initially_unowned ? "unowned" : "owned"); belle_sip_snprintf(ret,maxbufsize,&pos,"\nInheritance diagram:\n"); for(it=vptr;it!=NULL;it=it->get_parent()){ l=belle_sip_list_prepend(l,it); } for(elem=l;elem!=NULL;elem=elem->next){ it=(belle_sip_object_vptr_t*)elem->data; belle_sip_snprintf(ret,maxbufsize,&pos,"\t%s\n",it->type_name); if (elem->next) belle_sip_snprintf(ret,maxbufsize,&pos,"\t |\n"); } belle_sip_list_free(l); belle_sip_snprintf(ret,maxbufsize,&pos,"\nImplemented interfaces:\n"); for(it=vptr;it!=NULL;it=it->get_parent()){ belle_sip_interface_desc_t **desc=it->interfaces; if (desc!=NULL){ for(;*desc!=NULL;desc++){ belle_sip_snprintf(ret,maxbufsize,&pos,"\t* %s\n",(*desc)->ifname); } } } return ret; } char *belle_sip_object_describe(void *obj){ belle_sip_object_t *o=BELLE_SIP_OBJECT(obj); return _belle_sip_object_describe_type(o->vptr); } #if !defined(_WIN32) #include char *belle_sip_object_describe_type_from_name(const char *name){ char *vptr_name; void *handle; void *symbol; belle_sip_object_get_vptr_t vptr_getter; handle=dlopen(NULL,RTLD_LAZY); if (handle==NULL){ belle_sip_error("belle_sip_object_describe_type_from_name: dlopen() failed: %s",dlerror()); return NULL; } vptr_name=belle_sip_strdup_printf("%s_vptr_get",name); symbol=dlsym(handle,vptr_name); belle_sip_free(vptr_name); dlclose(handle); if (symbol==NULL){ belle_sip_error("belle_sip_object_describe_type_from_name: could not find vptr for type %s",name); return NULL; } vptr_getter=(belle_sip_object_get_vptr_t)symbol; return _belle_sip_object_describe_type(vptr_getter()); } #else char *belle_sip_object_describe_type_from_name(const char *name){ return belle_sip_strdup_printf("Sorry belle_sip_object_describe_type_from_name() is not implemented on this platform."); } #endif struct belle_sip_object_pool{ belle_sip_object_t base; belle_sip_list_t *objects; unsigned long thread_id; }; static void belle_sip_object_pool_destroy(belle_sip_object_pool_t *pool){ belle_sip_object_pool_clean(pool); _belle_sip_object_pool_remove_from_stack(pool); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_object_pool_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_object_pool_t,belle_sip_object_t,belle_sip_object_pool_destroy,NULL,NULL,FALSE); belle_sip_object_pool_t *belle_sip_object_pool_new(void){ belle_sip_object_pool_t *pool=belle_sip_object_new(belle_sip_object_pool_t); pool->thread_id=belle_sip_thread_self_id(); return pool; } void belle_sip_object_pool_add(belle_sip_object_pool_t *pool, belle_sip_object_t *obj){ if (obj->pool!=NULL){ belle_sip_fatal("It is not possible to add an object to multiple pools."); } pool->objects=belle_sip_list_prepend(pool->objects,obj); obj->pool_iterator=pool->objects; obj->pool=pool; } void belle_sip_object_pool_remove(belle_sip_object_pool_t *pool, belle_sip_object_t *obj){ unsigned long tid=belle_sip_thread_self_id(); if (obj->pool!=pool){ belle_sip_fatal("Attempting to remove object from an incorrect pool: obj->pool=%p, pool=%p",obj->pool,pool); return; } if (tid!=pool->thread_id){ belle_sip_fatal("It is forbidden (and unsafe()) to ref()/unref() an unowned object outside of the thread that created it."); return; } pool->objects=belle_sip_list_delete_link(pool->objects,obj->pool_iterator); obj->pool_iterator=NULL; obj->pool=NULL; } int belle_sip_object_pool_cleanable(belle_sip_object_pool_t *pool){ return pool->thread_id!=0 && belle_sip_thread_self_id()==pool->thread_id; } void belle_sip_object_pool_clean(belle_sip_object_pool_t *pool){ belle_sip_list_t *elem,*next; if (!belle_sip_object_pool_cleanable(pool)){ belle_sip_warning("Thread pool [%p] cannot be cleaned from thread [%lu] because it was created for thread [%lu]", pool,belle_sip_thread_self_id(),(unsigned long)pool->thread_id); return; } for(elem=pool->objects;elem!=NULL;elem=next){ belle_sip_object_t *obj=(belle_sip_object_t*)elem->data; if (obj->ref==0){ belle_sip_message("Garbage collecting unowned object of type %s",obj->vptr->type_name); obj->ref=-1; belle_sip_object_delete(obj); next=elem->next; belle_sip_free(elem); }else { belle_sip_fatal("Object %p is in unowned list but with ref count %i, bug.",obj,obj->ref); next=elem->next; } } pool->objects=NULL; } static void belle_sip_object_pool_detach_from_thread(belle_sip_object_pool_t *pool){ belle_sip_object_pool_clean(pool); pool->thread_id=(unsigned long)0; } static void cleanup_pool_stack(void *data){ belle_sip_list_t **pool_stack=(belle_sip_list_t**)data; if (*pool_stack){ /* * We would expect the pool_stack to be empty when the thread terminates. * Otherwise that means the management of object pool is not properly done by the application. * Since the object pools might be still referenced by the application, we can't destroy them. * Instead, we mark them as detached, so that when the thread that will attempt to destroy them will do it, * we'll accept (since anyway these object pool are no longer needed. */ belle_sip_warning("There were still [%u] object pools for thread [%lu] while the thread exited. ", (unsigned int)belle_sip_list_size(*pool_stack),belle_sip_thread_self_id()); belle_sip_list_free_with_data(*pool_stack,(void (*)(void*)) belle_sip_object_pool_detach_from_thread); } *pool_stack=NULL; belle_sip_free(pool_stack); } static belle_sip_list_t** get_current_pool_stack(int *first_time){ static belle_sip_thread_key_t pools_key; static int pools_key_created=0; belle_sip_list_t **pool_stack; if (first_time) *first_time=0; if (!pools_key_created){ pools_key_created=1; if (belle_sip_thread_key_create(&pools_key, cleanup_pool_stack)!=0){ return NULL; } } pool_stack=(belle_sip_list_t**)belle_sip_thread_getspecific(pools_key); if (pool_stack==NULL){ pool_stack=belle_sip_new(belle_sip_list_t*); *pool_stack=NULL; belle_sip_thread_setspecific(pools_key,pool_stack); if (first_time) *first_time=1; } return pool_stack; } static void _belle_sip_object_pool_remove_from_stack(belle_sip_object_pool_t *pool){ belle_sip_list_t **pools=get_current_pool_stack(NULL); unsigned long tid=belle_sip_thread_self_id(); if (tid!=pool->thread_id){ belle_sip_fatal("It is forbidden to destroy a pool outside the thread that created it."); return; } if (pools==NULL) { belle_sip_fatal("Not possible to pop a pool."); return; } if (*pools==NULL){ belle_sip_fatal("There is no current pool in stack."); return; } *pools=belle_sip_list_remove(*pools,pool); } belle_sip_object_pool_t * belle_sip_object_pool_push(void){ belle_sip_list_t **pools=get_current_pool_stack(NULL); belle_sip_object_pool_t *pool; if (pools==NULL) { belle_sip_error("Not possible to create a pool."); return NULL; } pool=belle_sip_object_pool_new(); *pools=belle_sip_list_prepend(*pools,pool); return pool; } belle_sip_object_pool_t *belle_sip_object_pool_get_current(void){ int first_time; belle_sip_list_t **pools=get_current_pool_stack(&first_time); if (pools==NULL) return NULL; if (*pools==NULL ){ if (first_time) { belle_sip_warning("There is no object pool created in thread [%lu]. " "Use belle_sip_object_pool_push() to create one. Unowned objects not unref'd will be leaked.", belle_sip_thread_self_id()); } return NULL; } return (belle_sip_object_pool_t*)(*pools)->data; } belle-sip-1.6.3/src/belle_sip_parameters.c000066400000000000000000000142201313437522400205140ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle-sip/parameters.h" #include "belle_sip_internal.h" #include "belle-sip/headers.h" void belle_sip_parameters_init(belle_sip_parameters_t *obj){ } void belle_sip_parameters_clean(belle_sip_parameters_t* params) { if (params->param_list) belle_sip_list_free_with_data (params->param_list, (void (*)(void*))belle_sip_param_pair_destroy); if (params->paramnames_list) belle_sip_list_free(params->paramnames_list); params->paramnames_list=NULL; params->param_list=NULL; } static void belle_sip_parameters_destroy(belle_sip_parameters_t* params) { belle_sip_parameters_clean(params); } void belle_sip_parameters_copy_parameters_from(belle_sip_parameters_t *params, const belle_sip_parameters_t *orig){ belle_sip_list_t* list=orig->param_list; for(;list!=NULL;list=list->next){ belle_sip_param_pair_t* container = (belle_sip_param_pair_t* )(list->data); belle_sip_parameters_set_parameter( params,container->name,container->value); } } static void belle_sip_parameters_clone(belle_sip_parameters_t *params, const belle_sip_parameters_t *orig){ belle_sip_parameters_copy_parameters_from(params,orig); } belle_sip_error_code belle_sip_parameters_marshal(const belle_sip_parameters_t* params, char* buff, size_t buff_size, size_t *offset) { belle_sip_list_t* list=params->param_list; belle_sip_error_code error=BELLE_SIP_OK; for(;list!=NULL;list=list->next){ belle_sip_param_pair_t* container = (belle_sip_param_pair_t* )(list->data); if (container->value) { error=belle_sip_snprintf(buff,buff_size,offset,";%s=%s", container->name, container->value); } else { error=belle_sip_snprintf(buff,buff_size,offset,";%s", container->name); } if (error!=BELLE_SIP_OK) return error; } return error; } BELLE_SIP_NEW_HEADER(parameters,header,"parameters") const belle_sip_list_t *belle_sip_parameters_get_parameters(const belle_sip_parameters_t* obj) { return obj->param_list; } const char* belle_sip_parameters_get_parameter_base(const belle_sip_parameters_t* params,const char* name,belle_sip_compare_func func) { belle_sip_list_t * lResult = belle_sip_list_find_custom(params->param_list, func, name); if (lResult) { return ((belle_sip_param_pair_t*)(lResult->data))->value; } else { return NULL; } } const char* belle_sip_parameters_get_parameter(const belle_sip_parameters_t* params,const char* name) { return belle_sip_parameters_get_parameter_base(params,name,(belle_sip_compare_func)belle_sip_param_pair_comp_func); } const char* belle_sip_parameters_get_case_parameter(const belle_sip_parameters_t* params,const char* name) { return belle_sip_parameters_get_parameter_base(params,name,(belle_sip_compare_func)belle_sip_param_pair_case_comp_func); } unsigned int belle_sip_parameters_has_parameter(const belle_sip_parameters_t* params,const char* name) { return belle_sip_list_find_custom(params->param_list, (belle_sip_compare_func)belle_sip_param_pair_comp_func, name) != NULL; } void belle_sip_parameters_set_parameter(belle_sip_parameters_t* params,const char* name,const char* value) { /*1 check if present*/ belle_sip_param_pair_t* lNewpair; belle_sip_list_t * lResult = belle_sip_list_find_custom(params->paramnames_list, (belle_sip_compare_func)strcmp, name); /* first remove from header names list*/ if (lResult) { params->paramnames_list=belle_sip_list_delete_link(params->paramnames_list,lResult); } /* next from header list*/ lResult = belle_sip_list_find_custom(params->param_list, (belle_sip_compare_func)belle_sip_param_pair_comp_func, name); if (lResult) { belle_sip_param_pair_destroy(lResult->data); params->param_list=belle_sip_list_delete_link(params->param_list,lResult); } /* 2 insert*/ lNewpair = belle_sip_param_pair_new(name,value); params->param_list=belle_sip_list_append(params->param_list,lNewpair); params->paramnames_list=belle_sip_list_append(params->paramnames_list,lNewpair->name); } void belle_sip_parameters_set(belle_sip_parameters_t *parameters, const char* params){ belle_sip_parameters_clean(parameters); if (params && *params!='\0'){ char *tmp=belle_sip_strdup(params); char *end_of_param; char *current=tmp; char *equal; char *next; do{ end_of_param=strchr(current,';'); equal=strchr(current,'='); if (!end_of_param) { end_of_param=current+strlen(current); next=end_of_param; }else{ *end_of_param='\0'; next=end_of_param+1; } if (equal && equalparamnames_list:NULL; } void belle_sip_parameters_remove_parameter(belle_sip_parameters_t* params,const char* name) { /*1 check if present*/ belle_sip_list_t * lResult = belle_sip_list_find_custom(params->paramnames_list, (belle_sip_compare_func)strcmp, name); /* first remove from header names list*/ if (lResult) { params->paramnames_list=belle_sip_list_delete_link(params->paramnames_list,lResult); /*next remove node*/ lResult = belle_sip_list_find_custom(params->param_list, (belle_sip_compare_func)belle_sip_param_pair_comp_func, name); if (lResult) { belle_sip_param_pair_destroy(lResult->data); params->param_list=belle_sip_list_delete_link(params->param_list,lResult); } } } belle-sip-1.6.3/src/belle_sip_resolver.c000066400000000000000000001256541313437522400202300ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #include "dns.h" #include #include #include #ifdef __APPLE__ #include "TargetConditionals.h" #endif #define DNS_EAGAIN EAGAIN typedef struct belle_sip_simple_resolver_context belle_sip_simple_resolver_context_t; #define BELLE_SIP_SIMPLE_RESOLVER_CONTEXT(obj) BELLE_SIP_CAST(obj,belle_sip_simple_resolver_context_t) typedef struct belle_sip_combined_resolver_context belle_sip_combined_resolver_context_t; #define BELLE_SIP_COMBINED_RESOLVER_CONTEXT(obj) BELLE_SIP_CAST(obj,belle_sip_combined_resolver_context_t) typedef struct belle_sip_dual_resolver_context belle_sip_dual_resolver_context_t; #define BELLE_SIP_DUAL_RESOLVER_CONTEXT(obj) BELLE_SIP_CAST(obj,belle_sip_dual_resolver_context_t) struct belle_sip_dns_srv{ belle_sip_object_t base; unsigned short priority; unsigned short weight; unsigned short port; unsigned char a_done; unsigned char pad; int cumulative_weight; /*used only temporarily*/ char *target; belle_sip_combined_resolver_context_t *root_resolver;/* used internally to combine SRV and A queries*/ belle_sip_resolver_context_t *a_resolver; /* used internally to combine SRV and A queries*/ struct addrinfo *a_results; /* used internally to combine SRV and A queries*/ }; static void belle_sip_dns_srv_destroy(belle_sip_dns_srv_t *obj){ if (obj->target) { belle_sip_free(obj->target); obj->target=NULL; } if (obj->a_resolver){ belle_sip_resolver_context_cancel(obj->a_resolver); belle_sip_object_unref(obj->a_resolver); obj->a_resolver=NULL; } if (obj->a_results){ bctbx_freeaddrinfo(obj->a_results); obj->a_results=NULL; } } belle_sip_dns_srv_t *belle_sip_dns_srv_create(struct dns_srv *srv){ belle_sip_dns_srv_t *obj=belle_sip_object_new(belle_sip_dns_srv_t); obj->priority=srv->priority; obj->weight=srv->weight; obj->port=srv->port; obj->target=belle_sip_strdup(srv->target); return obj; } const char *belle_sip_dns_srv_get_target(const belle_sip_dns_srv_t *obj){ return obj->target; } unsigned short belle_sip_dns_srv_get_priority(const belle_sip_dns_srv_t *obj){ return obj->priority; } unsigned short belle_sip_dns_srv_get_weight(const belle_sip_dns_srv_t *obj){ return obj->weight; } unsigned short belle_sip_dns_srv_get_port(const belle_sip_dns_srv_t *obj){ return obj->port; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_dns_srv_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_dns_srv_t, belle_sip_object_t,belle_sip_dns_srv_destroy, NULL, NULL,TRUE); struct belle_sip_resolver_context{ belle_sip_source_t source; belle_sip_stack_t *stack; uint32_t min_ttl; uint8_t notified; uint8_t cancelled; uint8_t pad[2]; }; struct belle_sip_simple_resolver_context{ belle_sip_resolver_context_t base; belle_sip_resolver_callback_t cb; belle_sip_resolver_srv_callback_t srv_cb; void *cb_data; void *srv_cb_data; struct dns_resolv_conf *resconf; struct dns_hosts *hosts; struct dns_resolver *R; enum dns_type type; char *name; int port; struct addrinfo *ai_list; belle_sip_list_t *srv_list; /*list of belle_sip_dns_srv_t*/ int family; int flags; uint64_t start_time; #ifdef USE_GETADDRINFO_FALLBACK struct addrinfo *getaddrinfo_ai_list; belle_sip_source_t *getaddrinfo_source; belle_sip_thread_t getaddrinfo_thread; unsigned char getaddrinfo_done; unsigned char getaddrinfo_cancelled; #ifdef _WIN32 HANDLE ctlevent; #else int ctlpipe[2]; #endif #endif }; struct belle_sip_combined_resolver_context{ belle_sip_resolver_context_t base; belle_sip_resolver_callback_t cb; void *cb_data; char *name; int port; int family; struct addrinfo *final_results; belle_sip_list_t *srv_results; belle_sip_resolver_context_t *srv_ctx; belle_sip_resolver_context_t *a_fallback_ctx; }; struct belle_sip_dual_resolver_context{ belle_sip_resolver_context_t base; belle_sip_resolver_callback_t cb; void *cb_data; char *name; belle_sip_resolver_context_t *a_ctx; belle_sip_resolver_context_t *aaaa_ctx; struct addrinfo *a_results; struct addrinfo *aaaa_results; uint8_t a_notified; uint8_t aaaa_notified; uint8_t pad[2]; }; void belle_sip_resolver_context_init(belle_sip_resolver_context_t *obj, belle_sip_stack_t *stack){ obj->stack=stack; obj->min_ttl = UINT32_MAX; belle_sip_init_sockets(); /* Need to be called for DNS resolution to work on Windows platform. */ } static int dns_resconf_nameservers_from_list(struct dns_resolv_conf *resconf, const belle_sip_list_t *l) { int max_servers = sizeof(resconf->nameserver)/sizeof(struct sockaddr_storage); int i; const belle_sip_list_t *elem; for (i = 0, elem = l; i < max_servers && elem != NULL; elem = elem->next) { int error = dns_resconf_pton(&resconf->nameserver[i], (const char *) elem->data); if (error == 0) ++i; } return i > 0 ? 0 : -1; } static struct dns_resolv_conf *resconf(belle_sip_simple_resolver_context_t *ctx) { const char *path; const belle_sip_list_t *servers; int error; if (ctx->resconf) return ctx->resconf; if (!(ctx->resconf = dns_resconf_open(&error))) { belle_sip_error("%s dns_resconf_open error: %s", __FUNCTION__, dns_strerror(error)); return NULL; } path = belle_sip_stack_get_dns_resolv_conf_file(ctx->base.stack); servers = ctx->base.stack->dns_servers; if (servers){ belle_sip_message("%s using application supplied dns server list.", __FUNCTION__); error = dns_resconf_nameservers_from_list(ctx->resconf, servers); }else if (!path){ #if defined(USE_FIXED_NAMESERVERS) error = dns_resconf_load_fixed_nameservers(ctx->resconf); if (error) { belle_sip_error("%s dns_resconf_load_fixed_nameservers error", __FUNCTION__); } #elif defined(USE_STRUCT_RES_STATE_NAMESERVERS) error = dns_resconf_load_struct_res_state_nameservers(ctx->resconf); if (error) { belle_sip_error("%s dns_resconf_load_struct_res_state_nameservers error", __FUNCTION__); } #elif defined(_WIN32) error = dns_resconf_loadwin(ctx->resconf); if (error) { belle_sip_error("%s dns_resconf_loadwin error", __FUNCTION__); } #elif defined(__ANDROID__) error = dns_resconf_loadandroid(ctx->resconf); if (error) { belle_sip_error("%s dns_resconf_loadandroid error", __FUNCTION__); } #elif defined(HAVE_RESINIT) /*#elif HAVE_RESINIT && TARGET_OS_IPHONE*/ error = dns_resconf_loadfromresolv(ctx->resconf); if (error) { belle_sip_error("%s dns_resconf_loadfromresolv error", __FUNCTION__); } #else path = "/etc/resolv.conf"; error = dns_resconf_loadpath(ctx->resconf, path); if (error) { belle_sip_error("%s dns_resconf_loadpath error [%s]: %s", __FUNCTION__, path, dns_strerror(error)); return NULL; } path = "/etc/nsswitch.conf"; error = dns_nssconf_loadpath(ctx->resconf, path); if (error) { belle_sip_message("%s dns_nssconf_loadpath error [%s]: %s", __FUNCTION__, path, dns_strerror(error)); } #endif }else{ error = dns_resconf_loadpath(ctx->resconf, path); if (error) { belle_sip_error("%s dns_resconf_loadpath() of custom file error [%s]: %s", __FUNCTION__, path, dns_strerror(error)); return NULL; } } if (error==0){ char ip[64]; char serv[10]; int using_ipv6=FALSE; size_t i; belle_sip_message("Resolver is using DNS server(s):"); for(i=0;iresconf->nameserver)/sizeof(ctx->resconf->nameserver[0]);++i){ struct sockaddr *ns_addr=(struct sockaddr*)&ctx->resconf->nameserver[i]; if (ns_addr->sa_family==AF_UNSPEC) break; bctbx_getnameinfo(ns_addr,ns_addr->sa_family==AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr) ,ip,sizeof(ip),serv,sizeof(serv),NI_NUMERICHOST|NI_NUMERICSERV); belle_sip_message("\t%s",ip); if (ns_addr->sa_family==AF_INET6) using_ipv6=TRUE; } ctx->resconf->iface.ss_family=using_ipv6 ? AF_INET6 : AF_INET; if (i==0) { belle_sip_error("- no DNS servers available - resolution aborted."); return NULL; } }else{ belle_sip_error("Error loading dns server addresses."); return NULL; } return ctx->resconf; } static struct dns_hosts *hosts(belle_sip_simple_resolver_context_t *ctx) { int error; if (ctx->hosts) return ctx->hosts; if (!(ctx->hosts = dns_hosts_local(&error))) { belle_sip_warning("%s dns_hosts_local error: %s", __FUNCTION__, dns_strerror(error)); /*in case of failure, create an empty host object to make further processing happy, knowing that we can live without /etc/hosts.*/ ctx->hosts=dns_hosts_open(&error); } if (ctx->base.stack->dns_user_hosts_file) { error = dns_hosts_loadpath(ctx->hosts, ctx->base.stack->dns_user_hosts_file); if (error) { belle_sip_error("%s dns_hosts_loadfile(\"%s\"): %s", __FUNCTION__,ctx->base.stack->dns_user_hosts_file,dns_strerror(error)); } } return ctx->hosts; } struct dns_cache *cache(belle_sip_simple_resolver_context_t *ctx) { return NULL; } static struct addrinfo * ai_list_append(struct addrinfo *ai_list, struct addrinfo *ai_to_append) { struct addrinfo *ai_current = ai_list; if (ai_to_append == NULL) return ai_list; if (ai_list == NULL) return ai_to_append; while (ai_current->ai_next != NULL) { ai_current = ai_current->ai_next; } ai_current->ai_next = ai_to_append; return ai_list; } static int srv_compare_prio(const void *psrv1, const void *psrv2){ belle_sip_dns_srv_t *srv1=(belle_sip_dns_srv_t*)psrv1; belle_sip_dns_srv_t *srv2=(belle_sip_dns_srv_t*)psrv2; if (srv1->priority < srv2->priority) return -1; if (srv1->priority == srv2->priority) return 0; return 1; } /* * see https://www.ietf.org/rfc/rfc2782.txt * 0 weighted must just appear first. **/ static int srv_sort_weight(const void *psrv1, const void *psrv2){ belle_sip_dns_srv_t *srv1=(belle_sip_dns_srv_t*)psrv1; if (srv1->weight==0) return -1; return 1; } static belle_sip_dns_srv_t *srv_elect_one(belle_sip_list_t *srv_list){ int sum=0; belle_sip_list_t *elem; belle_sip_dns_srv_t *srv; int rand_number; for(elem=srv_list;elem!=NULL;elem=elem->next){ srv=(belle_sip_dns_srv_t*)elem->data; sum+=srv->weight; srv->cumulative_weight=sum; } /*no weights given, return the first one*/ if (sum==0) return (belle_sip_dns_srv_t*)srv_list->data; rand_number=belle_sip_random() % sum; /*random number choosen in the range of the sum of weights*/ for(elem=srv_list;elem!=NULL;elem=elem->next){ srv=(belle_sip_dns_srv_t*)elem->data; if (rand_number<=srv->cumulative_weight) return srv; } return (belle_sip_dns_srv_t*)srv_list->data; } /* * Order an SRV list with entries having the same priority according to their weight */ static belle_sip_list_t *srv_elect(belle_sip_list_t **srv_list) { belle_sip_list_t *result = NULL; while (*srv_list != NULL) { belle_sip_list_t *it; belle_sip_dns_srv_t *entry = srv_elect_one(*srv_list); result = belle_sip_list_append(result, belle_sip_object_ref(entry)); it = belle_sip_list_find(*srv_list, entry); if (it) { *srv_list = belle_sip_list_remove_link(*srv_list, it); belle_sip_free(it); } } return result; } /* * this function will return a list of SRV, with only one SRV record per priority. */ static belle_sip_list_t *srv_select_by_weight(belle_sip_list_t *srv_list){ belle_sip_list_t *same_prio=NULL; belle_sip_list_t *elem; belle_sip_dns_srv_t *prev_srv=NULL; belle_sip_list_t *result=NULL; for (elem=srv_list;elem!=NULL;elem=elem->next){ belle_sip_dns_srv_t *srv=(belle_sip_dns_srv_t*)elem->data; if (prev_srv){ if (prev_srv->priority==srv->priority){ if (!same_prio){ same_prio=belle_sip_list_append(same_prio,prev_srv); } same_prio=belle_sip_list_insert_sorted(same_prio,srv,srv_sort_weight); }else{ if (same_prio){ result=belle_sip_list_concat(result,srv_elect(&same_prio)); } } } prev_srv=srv; } if (same_prio){ result=belle_sip_list_concat(result,srv_elect(&same_prio)); } if (result){ belle_sip_list_free_with_data(srv_list,belle_sip_object_unref); return result; } return srv_list;/*no weight election was necessary, return original list*/ } static void simple_resolver_context_notify(belle_sip_resolver_context_t *obj) { belle_sip_simple_resolver_context_t *ctx = BELLE_SIP_SIMPLE_RESOLVER_CONTEXT(obj); if ((ctx->type == DNS_T_A) || (ctx->type == DNS_T_AAAA)) { struct addrinfo **ai_list = &ctx->ai_list; #if USE_GETADDRINFO_FALLBACK if (ctx->getaddrinfo_ai_list != NULL) ai_list = &ctx->getaddrinfo_ai_list; #endif ctx->cb(ctx->cb_data, ctx->name, *ai_list, BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl); *ai_list = NULL; } else if (ctx->type == DNS_T_SRV) { ctx->srv_list = srv_select_by_weight(ctx->srv_list); ctx->srv_cb(ctx->srv_cb_data, ctx->name, ctx->srv_list, BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl); } } static void dual_resolver_context_notify(belle_sip_resolver_context_t *obj) { belle_sip_dual_resolver_context_t *ctx = BELLE_SIP_DUAL_RESOLVER_CONTEXT(obj); struct addrinfo *results = ctx->aaaa_results; results = ai_list_append(results, ctx->a_results); ctx->a_results = NULL; ctx->aaaa_results = NULL; ctx->cb(ctx->cb_data, ctx->name, results, BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl); } static void combined_resolver_context_cleanup(belle_sip_combined_resolver_context_t *ctx) { if (ctx->srv_ctx) { belle_sip_object_unref(ctx->srv_ctx); ctx->srv_ctx = NULL; } if (ctx->a_fallback_ctx) { belle_sip_object_unref(ctx->a_fallback_ctx); ctx->a_fallback_ctx = NULL; } belle_sip_list_free_with_data(ctx->srv_results, belle_sip_object_unref); ctx->srv_results = NULL; } static void combined_resolver_context_notify(belle_sip_resolver_context_t *obj) { belle_sip_combined_resolver_context_t *ctx = BELLE_SIP_COMBINED_RESOLVER_CONTEXT(obj); ctx->cb(ctx->cb_data, ctx->name, ctx->final_results, BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl); ctx->final_results = NULL; combined_resolver_context_cleanup(ctx); } static void append_dns_result(belle_sip_simple_resolver_context_t *ctx, struct addrinfo **ai_list, struct sockaddr *addr, socklen_t addrlen){ char host[NI_MAXHOST + 1]; int gai_err; int family=ctx->family; if ((gai_err=bctbx_getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST)) != 0){ belle_sip_error("append_dns_result(): getnameinfo() failed: %s",gai_strerror(gai_err)); return; } if (ctx->flags & AI_V4MAPPED) family=AF_INET6; *ai_list = ai_list_append(*ai_list, bctbx_ip_address_to_addrinfo(family, SOCK_STREAM, host, ctx->port)); belle_sip_message("%s resolved to %s", ctx->name, host); } static int resolver_process_data(belle_sip_simple_resolver_context_t *ctx, unsigned int revents) { struct dns_packet *ans; struct dns_rr_i *I; struct dns_rr_i dns_rr_it; int error; unsigned char simulated_timeout=0; int timeout=belle_sip_stack_get_dns_timeout(ctx->base.stack); unsigned char search_enabled = belle_sip_stack_dns_search_enabled(ctx->base.stack); /*Setting timeout to 0 can be used to simulate DNS timeout*/ if ((revents!=0) && timeout==0){ belle_sip_warning("Simulating DNS timeout"); simulated_timeout=1; } if (simulated_timeout || ((revents & BELLE_SIP_EVENT_TIMEOUT) && ((int)(belle_sip_time_ms()-ctx->start_time)>=timeout))) { belle_sip_error("%s timed-out", __FUNCTION__); belle_sip_resolver_context_notify(BELLE_SIP_RESOLVER_CONTEXT(ctx)); return BELLE_SIP_STOP; } dns_res_enable_search(ctx->R, search_enabled); /*belle_sip_message("resolver_process_data(): revents=%i",revents);*/ error = dns_res_check(ctx->R); if (!error) { struct dns_rr rr; union dns_any any; enum dns_section section = DNS_S_AN; ans = dns_res_fetch(ctx->R, &error); memset(&dns_rr_it, 0, sizeof dns_rr_it); I = dns_rr_i_init(&dns_rr_it, ans); while (dns_rr_grep(&rr, 1, I, ans, &error)) { if (rr.section == section) { if ((error = dns_any_parse(dns_any_init(&any, sizeof(any)), &rr, ans))) { belle_sip_error("%s dns_any_parse error: %s", __FUNCTION__, dns_strerror(error)); break; } if ((ctx->type == DNS_T_AAAA) && (rr.class == DNS_C_IN) && (rr.type == DNS_T_AAAA)) { struct dns_aaaa *aaaa = &any.aaaa; struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); memcpy(&sin6.sin6_addr, &aaaa->addr, sizeof(sin6.sin6_addr)); sin6.sin6_family = AF_INET6; sin6.sin6_port = ctx->port; append_dns_result(ctx,&ctx->ai_list,(struct sockaddr*)&sin6,sizeof(sin6)); if (rr.ttl < BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl = rr.ttl; } else if ((ctx->type == DNS_T_A) && (rr.class == DNS_C_IN) && (rr.type == DNS_T_A)) { struct dns_a *a = &any.a; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); memcpy(&sin.sin_addr, &a->addr, sizeof(sin.sin_addr)); sin.sin_family = AF_INET; sin.sin_port = ctx->port; append_dns_result(ctx,&ctx->ai_list,(struct sockaddr*)&sin,sizeof(sin)); if (rr.ttl < BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl = rr.ttl; } else if ((ctx->type == DNS_T_SRV) && (rr.class == DNS_C_IN) && (rr.type == DNS_T_SRV)) { char host[NI_MAXHOST + 1]; struct dns_srv *srv = &any.srv; belle_sip_dns_srv_t * b_srv=belle_sip_dns_srv_create(srv); snprintf(host, sizeof(host), "[target:%s port:%d prio:%d weight:%d]", srv->target, srv->port, srv->priority, srv->weight); ctx->srv_list = belle_sip_list_insert_sorted(ctx->srv_list, belle_sip_object_ref(b_srv), srv_compare_prio); belle_sip_message("SRV %s resolved to %s", ctx->name, host); if (rr.ttl < BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl = rr.ttl; } } } free(ans); #ifdef USE_GETADDRINFO_FALLBACK ctx->getaddrinfo_cancelled = TRUE; #endif if (dns_res_was_asymetric(ctx->R)){ belle_sip_warning("DNS answer was not received from the DNS server IP address the request was sent to. This seems to be a known issue with NAT64 networks created by Apple computers."); } belle_sip_resolver_context_notify(BELLE_SIP_RESOLVER_CONTEXT(ctx)); return BELLE_SIP_STOP; } if (error != DNS_EAGAIN) { belle_sip_error("%s dns_res_check() error: %s (%d)", __FUNCTION__, dns_strerror(error), error); #ifdef USE_GETADDRINFO_FALLBACK if (ctx->getaddrinfo_done) { return BELLE_SIP_STOP; } else { // Wait for the getaddrinfo result return BELLE_SIP_CONTINUE; } #else belle_sip_resolver_context_notify(BELLE_SIP_RESOLVER_CONTEXT(ctx)); return BELLE_SIP_STOP; #endif } else { #ifdef USE_GETADDRINFO_FALLBACK if (ctx->getaddrinfo_done) { return BELLE_SIP_STOP; } else #endif { belle_sip_message("%s dns_res_check() in progress", __FUNCTION__); } } return BELLE_SIP_CONTINUE; } #ifdef USE_GETADDRINFO_FALLBACK static void * _resolver_getaddrinfo_thread(void *ptr) { belle_sip_simple_resolver_context_t *ctx = (belle_sip_simple_resolver_context_t *)ptr; struct addrinfo *res = NULL; struct addrinfo hints = { 0 }; char serv[10]; int err; belle_sip_message("Resolver getaddrinfo thread started."); snprintf(serv, sizeof(serv), "%i", ctx->port); hints.ai_family = ctx->family; hints.ai_flags = AI_NUMERICSERV; err = getaddrinfo(ctx->name, serv, &hints, &res); if (err != 0) { belle_sip_error("getaddrinfo DNS resolution of %s failed: %s", ctx->name, gai_strerror(err)); } else if (!ctx->getaddrinfo_cancelled) { do { append_dns_result(ctx, &ctx->getaddrinfo_ai_list, res->ai_addr, (socklen_t)res->ai_addrlen); res = res->ai_next; } while (res != NULL); } if (res) freeaddrinfo(res); ctx->getaddrinfo_done = TRUE; #ifdef _WIN32 SetEvent(ctx->ctlevent); #else if (write(ctx->ctlpipe[1], "q", 1) == -1) { belle_sip_error("_resolver_getaddrinfo_thread(): Fail to write on pipe."); } #endif return NULL; } static int _resolver_getaddrinfo_callback(belle_sip_simple_resolver_context_t *ctx, unsigned int revents) { if (!ctx->getaddrinfo_cancelled) { belle_sip_resolver_context_notify(BELLE_SIP_RESOLVER_CONTEXT(ctx)); } belle_sip_object_unref(ctx); return BELLE_SIP_STOP; } static void _resolver_getaddrinfo_start(belle_sip_simple_resolver_context_t *ctx) { belle_sip_fd_t fd = (belle_sip_fd_t)-1; belle_sip_object_ref(ctx); #ifdef _WIN32 ctx->ctlevent = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); fd = (HANDLE)ctx->ctlevent; #else if (pipe(ctx->ctlpipe) == -1) { belle_sip_fatal("pipe() failed: %s", strerror(errno)); } fd = ctx->ctlpipe[0]; #endif belle_sip_thread_create(&ctx->getaddrinfo_thread, NULL, _resolver_getaddrinfo_thread, ctx); ctx->getaddrinfo_source = belle_sip_fd_source_new((belle_sip_source_func_t)_resolver_getaddrinfo_callback, ctx, fd, BELLE_SIP_EVENT_READ, -1); belle_sip_main_loop_add_source(ctx->base.stack->ml, ctx->getaddrinfo_source); } #endif static int _resolver_send_query(belle_sip_simple_resolver_context_t *ctx) { int error = 0; if (!ctx->base.stack->resolver_send_error) { error = dns_res_submit(ctx->R, ctx->name, ctx->type, DNS_C_IN); if (error) belle_sip_error("%s dns_res_submit error [%s]: %s", __FUNCTION__, ctx->name, dns_strerror(error)); } else { /* Error simulation */ error = ctx->base.stack->resolver_send_error; belle_sip_error("%s dns_res_submit error [%s]: simulated error %d", __FUNCTION__, ctx->name, error); } if (error < 0) { return -1; } if (resolver_process_data(ctx, 0) == BELLE_SIP_CONTINUE) { ctx->start_time=belle_sip_time_ms(); belle_sip_message("DNS resolution awaiting response, queued to main loop"); /*only init source if res inprogress*/ /*the timeout set to the source is 1 s, this is to allow dns.c to send request retransmissions*/ belle_sip_socket_source_init((belle_sip_source_t*)ctx, (belle_sip_source_func_t)resolver_process_data, ctx, dns_res_pollfd(ctx->R), BELLE_SIP_EVENT_READ | BELLE_SIP_EVENT_TIMEOUT, 1000); #ifdef USE_GETADDRINFO_FALLBACK { int timeout = belle_sip_stack_get_dns_timeout(ctx->base.stack); if ((timeout != 0) && ((ctx->type == DNS_T_A) || (ctx->type == DNS_T_AAAA))) { _resolver_getaddrinfo_start(ctx); } } #endif } return 0; } static int resolver_process_data_delayed(belle_sip_simple_resolver_context_t *ctx, unsigned int revents) { int err=_resolver_send_query(ctx); if (err==0) return BELLE_SIP_CONTINUE; return BELLE_SIP_STOP; } static int _resolver_start_query(belle_sip_simple_resolver_context_t *ctx) { struct dns_options opts; int error; struct dns_resolv_conf *conf; if (!ctx->name) return -1; conf=resconf(ctx); if (conf){ conf->options.recurse = 0; conf->options.timeout=2; conf->options.attempts=5; }else return -1; if (!hosts(ctx)) return -1; memset(&opts, 0, sizeof opts); /* When there are IPv6 nameservers, allow responses to arrive from an IP address that is not the IP address to which the request was sent originally. * Mac' NAT64 network tend to do this sometimes.*/ opts.udp_uses_connect = ctx->resconf->iface.ss_family != AF_INET6; if (!opts.udp_uses_connect) belle_sip_message("Resolver is not using connect()."); if (!(ctx->R = dns_res_open(ctx->resconf, ctx->hosts, dns_hints_mortal(dns_hints_local(ctx->resconf, &error)), cache(ctx), &opts, &error))) { belle_sip_error("%s dns_res_open error [%s]: %s", __FUNCTION__, ctx->name, dns_strerror(error)); return -1; } error=0; if (ctx->base.stack->resolver_tx_delay > 0) { belle_sip_socket_source_init((belle_sip_source_t*)ctx, (belle_sip_source_func_t)resolver_process_data_delayed, ctx, -1, BELLE_SIP_EVENT_TIMEOUT, ctx->base.stack->resolver_tx_delay + 1000); belle_sip_message("%s DNS resolution delayed by %d ms", __FUNCTION__, ctx->base.stack->resolver_tx_delay); } else { error=_resolver_send_query(ctx); } if (error==0 && !ctx->base.notified) belle_sip_main_loop_add_source(ctx->base.stack->ml,(belle_sip_source_t*)ctx); return error; } static belle_sip_simple_resolver_context_t * resolver_start_query(belle_sip_simple_resolver_context_t *ctx) { int error; /* Take a ref for this part of code because _resolver_start_query() can notify the results and free the ctx if this is not the case. */ belle_sip_object_ref(ctx); error = _resolver_start_query(ctx); if (error == 0) { if (!ctx->base.notified) { /* The resolution could not be done synchronously, return the context */ belle_sip_object_unref(ctx); return ctx; } /* Otherwise, resolution could be done synchronously */ } else { /* An error occured. We must notify the app. */ belle_sip_resolver_context_notify(BELLE_SIP_RESOLVER_CONTEXT(ctx)); } belle_sip_object_unref(ctx); return NULL; } static void belle_sip_combined_resolver_context_destroy(belle_sip_combined_resolver_context_t *obj){ if (obj->name != NULL) { belle_sip_free(obj->name); obj->name = NULL; } if (obj->srv_ctx){ belle_sip_object_unref(obj->srv_ctx); obj->srv_ctx=NULL; } if (obj->a_fallback_ctx){ belle_sip_object_unref(obj->a_fallback_ctx); obj->a_fallback_ctx=NULL; } } static void belle_sip_simple_resolver_context_destroy(belle_sip_simple_resolver_context_t *ctx){ /* Do not free elements of ctx->ai_list with bctbx_freeaddrinfo(). Let the caller do it, otherwise it will not be able to use them after the resolver has been destroyed. */ #ifdef USE_GETADDRINFO_FALLBACK if (ctx->getaddrinfo_thread != 0) { belle_sip_thread_join(ctx->getaddrinfo_thread, NULL); } if (ctx->getaddrinfo_source) belle_sip_object_unref(ctx->getaddrinfo_source); #ifdef _WIN32 if (ctx->ctlevent != (belle_sip_fd_t)-1) CloseHandle(ctx->ctlevent); #else close(ctx->ctlpipe[0]); close(ctx->ctlpipe[1]); #endif #endif if (ctx->ai_list != NULL) { bctbx_freeaddrinfo(ctx->ai_list); ctx->ai_list = NULL; } #ifdef USE_GETADDRINFO_FALLBACK if (ctx->getaddrinfo_ai_list != NULL) { bctbx_freeaddrinfo(ctx->getaddrinfo_ai_list); ctx->getaddrinfo_ai_list = NULL; } #endif if (ctx->name != NULL) { belle_sip_free(ctx->name); ctx->name = NULL; } if (ctx->R != NULL) { dns_res_close(ctx->R); ctx->R = NULL; } if (ctx->hosts != NULL) { dns_hosts_close(ctx->hosts); ctx->hosts = NULL; } if (ctx->resconf != NULL) { free(ctx->resconf); ctx->resconf = NULL; } } static void belle_sip_dual_resolver_context_destroy(belle_sip_dual_resolver_context_t *obj){ if (obj->a_ctx){ belle_sip_object_unref(obj->a_ctx); obj->a_ctx=NULL; } if (obj->aaaa_ctx){ belle_sip_object_unref(obj->aaaa_ctx); obj->aaaa_ctx=NULL; } if (obj->a_results){ bctbx_freeaddrinfo(obj->a_results); obj->a_results=NULL; } if (obj->aaaa_results){ bctbx_freeaddrinfo(obj->aaaa_results); obj->aaaa_results=NULL; } if (obj->name){ belle_sip_free(obj->name); obj->name=NULL; } } static void simple_resolver_context_cancel(belle_sip_resolver_context_t *obj) { belle_sip_main_loop_remove_source(obj->stack->ml, (belle_sip_source_t *)obj); } static void combined_resolver_context_cancel(belle_sip_resolver_context_t *obj) { belle_sip_combined_resolver_context_t *ctx = BELLE_SIP_COMBINED_RESOLVER_CONTEXT(obj); if (ctx->srv_ctx) { belle_sip_resolver_context_cancel(ctx->srv_ctx); belle_sip_object_unref(ctx->srv_ctx); ctx->srv_ctx = NULL; } if (ctx->a_fallback_ctx) { belle_sip_resolver_context_cancel(ctx->a_fallback_ctx); belle_sip_object_unref(ctx->a_fallback_ctx); ctx->a_fallback_ctx = NULL; } combined_resolver_context_cleanup(ctx); } static void dual_resolver_context_cancel(belle_sip_resolver_context_t *obj) { belle_sip_dual_resolver_context_t *ctx= BELLE_SIP_DUAL_RESOLVER_CONTEXT(obj); if (ctx->a_ctx) { belle_sip_resolver_context_cancel(ctx->a_ctx); belle_sip_object_unref(ctx->a_ctx); ctx->a_ctx = NULL; } if (ctx->aaaa_ctx) { belle_sip_resolver_context_cancel(ctx->aaaa_ctx); belle_sip_object_unref(ctx->aaaa_ctx); ctx->aaaa_ctx = NULL; } } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_resolver_context_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_resolver_context_t) { BELLE_SIP_VPTR_INIT(belle_sip_resolver_context_t,belle_sip_source_t,TRUE), (belle_sip_object_destroy_t) NULL, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, NULL BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_simple_resolver_context_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_simple_resolver_context_t) { { BELLE_SIP_VPTR_INIT(belle_sip_simple_resolver_context_t,belle_sip_resolver_context_t,TRUE), (belle_sip_object_destroy_t) belle_sip_simple_resolver_context_destroy, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, simple_resolver_context_cancel, simple_resolver_context_notify } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_dual_resolver_context_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_dual_resolver_context_t) { { BELLE_SIP_VPTR_INIT(belle_sip_dual_resolver_context_t,belle_sip_resolver_context_t,TRUE), (belle_sip_object_destroy_t) belle_sip_dual_resolver_context_destroy, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, dual_resolver_context_cancel, dual_resolver_context_notify } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_combined_resolver_context_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_combined_resolver_context_t) { { BELLE_SIP_VPTR_INIT(belle_sip_combined_resolver_context_t,belle_sip_resolver_context_t,TRUE), (belle_sip_object_destroy_t) belle_sip_combined_resolver_context_destroy, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, combined_resolver_context_cancel, combined_resolver_context_notify } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END static char * srv_prefix_from_service_and_transport(const char *service, const char *transport) { if (service == NULL) service = "sip"; if (strcasecmp(transport, "udp") == 0) { return belle_sip_strdup_printf("_%s._udp.", service); } else if (strcasecmp(transport, "tcp") == 0) { return belle_sip_strdup_printf("_%s._tcp.", service); } else if (strcasecmp(transport, "tls") == 0) { return belle_sip_strdup_printf("_%ss._tcp.", service); } return belle_sip_strdup_printf("_%s._udp.", service); } static void process_a_fallback_result(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl){ belle_sip_combined_resolver_context_t *ctx=(belle_sip_combined_resolver_context_t *)data; ctx->final_results=ai_list; belle_sip_resolver_context_notify(BELLE_SIP_RESOLVER_CONTEXT(ctx)); } static void combined_resolver_context_check_finished(belle_sip_combined_resolver_context_t *obj, uint32_t ttl){ belle_sip_list_t *elem; struct addrinfo *final=NULL; unsigned char finished=TRUE; if (ttl < BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl = ttl; for(elem=obj->srv_results;elem!=NULL;elem=elem->next){ belle_sip_dns_srv_t *srv=(belle_sip_dns_srv_t*)elem->data; if (!srv->a_done) { finished=FALSE; break; } } if (finished){ belle_sip_message("All A/AAAA results for combined resolution have arrived."); for(elem=obj->srv_results;elem!=NULL;elem=elem->next){ belle_sip_dns_srv_t *srv=(belle_sip_dns_srv_t*)elem->data; final=ai_list_append(final,srv->a_results); srv->a_results=NULL; } belle_sip_list_free_with_data(obj->srv_results,belle_sip_object_unref); obj->srv_results=NULL; obj->final_results=final; belle_sip_resolver_context_notify(BELLE_SIP_RESOLVER_CONTEXT(obj)); } } static void process_a_from_srv(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl){ belle_sip_dns_srv_t *srv=(belle_sip_dns_srv_t*)data; srv->a_results=ai_list; srv->a_done=TRUE; belle_sip_message("A query finished for srv result [%s]",srv->target); if (ttl < BELLE_SIP_RESOLVER_CONTEXT(srv->root_resolver)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(srv->root_resolver)->min_ttl = ttl; combined_resolver_context_check_finished(srv->root_resolver, ttl); } static void srv_resolve_a(belle_sip_combined_resolver_context_t *obj, belle_sip_dns_srv_t *srv){ belle_sip_message("Starting A/AAAA query for srv result [%s]",srv->target); srv->root_resolver=obj; /* take a ref of the srv object because the A resolution may terminate synchronously and destroy the srv object before to store the returned value of belle_sip_stack_resolve_a(). That would lead to an invalid write */ belle_sip_object_ref(srv); srv->a_resolver=belle_sip_stack_resolve_a(obj->base.stack,srv->target,srv->port,obj->family,process_a_from_srv,srv); if (srv->a_resolver){ belle_sip_object_ref(srv->a_resolver); } belle_sip_object_unref(srv); } static void process_srv_results(void *data, const char *name, belle_sip_list_t *srv_results, uint32_t ttl){ belle_sip_combined_resolver_context_t *ctx=(belle_sip_combined_resolver_context_t *)data; /*take a ref here, because the A resolution might succeed synchronously and terminate the context before exiting this function*/ belle_sip_object_ref(ctx); if (ttl < BELLE_SIP_RESOLVER_CONTEXT(data)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(data)->min_ttl = ttl; if (srv_results){ belle_sip_list_t *elem; /* take a ref of each srv_results because the last A resolution may terminate synchronously and destroy the list before the loop terminate */ ctx->srv_results = belle_sip_list_copy(srv_results); belle_sip_list_for_each(srv_results, (void(*)(void *))belle_sip_object_ref); for(elem=srv_results;elem!=NULL;elem=elem->next){ belle_sip_dns_srv_t *srv=(belle_sip_dns_srv_t*)elem->data; srv_resolve_a(ctx,srv); } srv_results = belle_sip_list_free_with_data(srv_results, belle_sip_object_unref); }else{ /*no SRV results, perform A query */ belle_sip_message("No SRV result for [%s], trying A/AAAA.",name); ctx->a_fallback_ctx=belle_sip_stack_resolve_a(ctx->base.stack,ctx->name,ctx->port,ctx->family,process_a_fallback_result,ctx); if (ctx->a_fallback_ctx) belle_sip_object_ref(ctx->a_fallback_ctx); } belle_sip_object_unref(ctx); } /** * Perform combined SRV + A / AAAA resolution. **/ belle_sip_resolver_context_t * belle_sip_stack_resolve(belle_sip_stack_t *stack, const char *service, const char *transport, const char *name, int port, int family, belle_sip_resolver_callback_t cb, void *data) { struct addrinfo *res = bctbx_ip_address_to_addrinfo(family, SOCK_STREAM, name, port); if (res == NULL) { /* First perform asynchronous DNS SRV query */ belle_sip_combined_resolver_context_t *ctx = belle_sip_object_new(belle_sip_combined_resolver_context_t); belle_sip_resolver_context_init((belle_sip_resolver_context_t*)ctx,stack); belle_sip_object_ref(ctx);/*we don't want the object to be destroyed until the end of this function*/ ctx->cb=cb; ctx->cb_data = data; ctx->name = belle_sip_strdup(name); ctx->port=port; belle_sip_object_set_name((belle_sip_object_t*)ctx, ctx->name); if (family == 0) family = AF_UNSPEC; ctx->family = family; /* Take a ref for the entire duration of the DNS procedure, it will be released when it is finished */ belle_sip_object_ref(ctx); ctx->srv_ctx=belle_sip_stack_resolve_srv(stack,service,transport,name,process_srv_results,ctx); if (ctx->srv_ctx) belle_sip_object_ref(ctx->srv_ctx); if (ctx->base.notified) { belle_sip_object_unref(ctx); return NULL; } belle_sip_object_unref(ctx); return BELLE_SIP_RESOLVER_CONTEXT(ctx); } else { /* There is no resolve to be done */ cb(data, name, res, UINT32_MAX); return NULL; } } static belle_sip_resolver_context_t * belle_sip_stack_resolve_single(belle_sip_stack_t *stack, const char *name, int port, int family, int flags, belle_sip_resolver_callback_t cb , void *data){ /* Then perform asynchronous DNS A or AAAA query */ belle_sip_simple_resolver_context_t *ctx = belle_sip_object_new(belle_sip_simple_resolver_context_t); belle_sip_resolver_context_init((belle_sip_resolver_context_t*)ctx,stack); ctx->cb_data = data; ctx->cb = cb; ctx->name = belle_sip_strdup(name); ctx->port = port; ctx->flags = flags; belle_sip_object_set_name((belle_sip_object_t*)ctx, ctx->name); /* Take a ref for the entire duration of the DNS procedure, it will be released when it is finished */ belle_sip_object_ref(ctx); if (family == 0) family = AF_UNSPEC; ctx->family = family; ctx->type = (ctx->family == AF_INET6) ? DNS_T_AAAA : DNS_T_A; #if defined(USE_GETADDRINFO_FALLBACK) && defined(_WIN32) ctx->ctlevent = (belle_sip_fd_t)-1; #endif return (belle_sip_resolver_context_t*)resolver_start_query(ctx); } static uint8_t belle_sip_resolver_context_can_be_cancelled(belle_sip_resolver_context_t *obj) { return ((obj->cancelled == TRUE) || (obj->notified == TRUE)) ? FALSE : TRUE; } #define belle_sip_resolver_context_can_be_notified(obj) belle_sip_resolver_context_can_be_cancelled(obj) static void dual_resolver_context_check_finished(belle_sip_dual_resolver_context_t *ctx) { if (belle_sip_resolver_context_can_be_notified(BELLE_SIP_RESOLVER_CONTEXT(ctx)) && (ctx->a_notified == TRUE) && (ctx->aaaa_notified == TRUE)) { belle_sip_resolver_context_notify(BELLE_SIP_RESOLVER_CONTEXT(ctx)); } } static void on_ipv4_results(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl) { belle_sip_dual_resolver_context_t *ctx = BELLE_SIP_DUAL_RESOLVER_CONTEXT(data); ctx->a_results = ai_list; ctx->a_notified = TRUE; dual_resolver_context_check_finished(ctx); } static void on_ipv6_results(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl) { belle_sip_dual_resolver_context_t *ctx = BELLE_SIP_DUAL_RESOLVER_CONTEXT(data); ctx->aaaa_results = ai_list; ctx->aaaa_notified = TRUE; dual_resolver_context_check_finished(ctx); } static belle_sip_resolver_context_t * belle_sip_stack_resolve_dual(belle_sip_stack_t *stack, const char *name, int port, belle_sip_resolver_callback_t cb , void *data){ /* Then perform asynchronous DNS A or AAAA query */ belle_sip_dual_resolver_context_t *ctx = belle_sip_object_new(belle_sip_dual_resolver_context_t); belle_sip_resolver_context_init((belle_sip_resolver_context_t*)ctx,stack); belle_sip_object_ref(ctx);/*we don't want the object to be destroyed until the end of this function*/ ctx->cb_data = data; ctx->cb = cb; ctx->name = belle_sip_strdup(name); belle_sip_object_set_name((belle_sip_object_t*)ctx, ctx->name); /* Take a ref for the entire duration of the DNS procedure, it will be released when it is finished */ belle_sip_object_ref(ctx); ctx->a_ctx=belle_sip_stack_resolve_single(stack,name,port,AF_INET, AI_V4MAPPED, on_ipv4_results,ctx); if (ctx->a_ctx) belle_sip_object_ref(ctx->a_ctx); ctx->aaaa_ctx=belle_sip_stack_resolve_single(stack, name, port, AF_INET6, 0, on_ipv6_results, ctx); if (ctx->aaaa_ctx) belle_sip_object_ref(ctx->aaaa_ctx); if (ctx->base.notified){ /* All results were found synchronously */ belle_sip_object_unref(ctx); ctx = NULL; } else belle_sip_object_unref(ctx); return BELLE_SIP_RESOLVER_CONTEXT(ctx); } belle_sip_resolver_context_t * belle_sip_stack_resolve_a(belle_sip_stack_t *stack, const char *name, int port, int family, belle_sip_resolver_callback_t cb , void *data) { struct addrinfo *res = bctbx_ip_address_to_addrinfo(family, SOCK_STREAM, name, port); if (res == NULL) { switch(family){ case AF_UNSPEC: family=AF_INET6; BCTBX_NO_BREAK; /*intentionally no break*/ case AF_INET6: return belle_sip_stack_resolve_dual(stack,name,port,cb,data); break; case AF_INET: return belle_sip_stack_resolve_single(stack,name,port,AF_INET,0,cb,data); break; default: belle_sip_error("belle_sip_stack_resolve_a(): unsupported address family [%i]",family); } } else { /* There is no resolve to be done */ cb(data, name, res, UINT32_MAX); } return NULL; } belle_sip_resolver_context_t * belle_sip_stack_resolve_srv(belle_sip_stack_t *stack, const char *service, const char *transport, const char *name, belle_sip_resolver_srv_callback_t cb, void *data) { belle_sip_simple_resolver_context_t *ctx = belle_sip_object_new(belle_sip_simple_resolver_context_t); char *srv_prefix = srv_prefix_from_service_and_transport(service, transport); belle_sip_resolver_context_init((belle_sip_resolver_context_t*)ctx,stack); ctx->srv_cb_data = data; ctx->srv_cb = cb; ctx->name = belle_sip_concat(srv_prefix, name, NULL); ctx->type = DNS_T_SRV; belle_sip_object_set_name((belle_sip_object_t*)ctx, ctx->name); /* Take a ref for the entire duration of the DNS procedure, it will be released when it is finished */ belle_sip_object_ref(ctx); belle_sip_free(srv_prefix); return (belle_sip_resolver_context_t*)resolver_start_query(ctx); } void belle_sip_resolver_context_cancel(belle_sip_resolver_context_t *obj) { if (belle_sip_resolver_context_can_be_cancelled(obj)) { obj->cancelled = TRUE; BELLE_SIP_OBJECT_VPTR(obj, belle_sip_resolver_context_t)->cancel(obj); belle_sip_object_unref(obj); } } void belle_sip_resolver_context_notify(belle_sip_resolver_context_t *obj) { if (belle_sip_resolver_context_can_be_notified(obj)) { obj->notified = TRUE; BELLE_SIP_OBJECT_VPTR(obj, belle_sip_resolver_context_t)->notify(obj); belle_sip_object_unref(obj); } } /* This function does the connect() method to get local ip address suitable to reach a given destination. It works on all platform except for windows using ipv6 sockets. TODO: find a workaround for win32+ipv6 socket */ int belle_sip_get_src_addr_for(const struct sockaddr *dest, socklen_t destlen, struct sockaddr *src, socklen_t *srclen, int local_port){ int af_type=dest->sa_family; int sock=(int)bctbx_socket(af_type,SOCK_DGRAM,IPPROTO_UDP); int ret = 0; if (sock==(belle_sip_socket_t)-1){ if (af_type == AF_INET){ belle_sip_fatal("Could not create socket: %s",belle_sip_get_socket_error_string()); } goto fail; } if (af_type==AF_INET6 && (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6*)dest)->sin6_addr))){ /*this is actually required only for windows, who is unable to provide an ipv4 mapped local address if the remote is ipv4 mapped, and unable to provide a correct local address if the remote address is true ipv6 address when in dual stack mode*/ belle_sip_socket_enable_dual_stack(sock); } if (bctbx_connect(sock,dest,destlen)==-1){ //if (connect(sock,dest,destlen)==-1){ ret = -get_socket_error(); belle_sip_error("belle_sip_get_src_addr_for: bctbx_connect() failed: %s",belle_sip_get_socket_error_string_from_code(-ret)); goto fail; } if (bctbx_getsockname(sock,src,srclen)==-1){ ret = -get_socket_error(); belle_sip_error("belle_sip_get_src_addr_for: bctbx_getsockname() failed: %s",belle_sip_get_socket_error_string_from_code(-ret)); goto fail; } if (af_type==AF_INET6){ struct sockaddr_in6 *sin6=(struct sockaddr_in6*)src; sin6->sin6_port=htons(local_port); }else{ struct sockaddr_in *sin=(struct sockaddr_in*)src; sin->sin_port=htons(local_port); } belle_sip_close_socket(sock); return ret; fail: { struct addrinfo *res = bctbx_ip_address_to_addrinfo(af_type, SOCK_STREAM, af_type == AF_INET ? "127.0.0.1" : "::1", local_port); if (res != NULL) { memcpy(src,res->ai_addr,MIN((size_t)*srclen,res->ai_addrlen)); *srclen=(socklen_t)res->ai_addrlen; bctbx_freeaddrinfo(res); } else { if (af_type == AF_INET) belle_sip_fatal("belle_sip_get_src_addr_for(): belle_sip_ip_address_to_addrinfo() failed"); } } if (sock!=(belle_sip_socket_t)-1) belle_sip_close_socket(sock); return ret; } belle-sip-1.6.3/src/belle_sip_uri_impl.c000066400000000000000000000452741313437522400202060ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/sip-uri.h" #include "belle-sip/parameters.h" #include #include #include #include "grammars/belle_sip_messageParser.h" #include "grammars/belle_sip_messageLexer.h" #include "belle_sip_internal.h" #include "listeningpoint_internal.h" #define SIP_URI_GET_SET_STRING(attribute) GET_SET_STRING(belle_sip_uri,attribute) #define SIP_URI_GET_SET_STRING_PARAM(attribute) GET_SET_STRING_PARAM2(belle_sip_uri,attribute,attribute##_param) #define SIP_URI_GET_SET_UINT(attribute) GET_SET_INT(belle_sip_uri,attribute,unsigned int) #define SIP_URI_GET_SET_INT(attribute) GET_SET_INT(belle_sip_uri,attribute,int) #define SIP_URI_GET_SET_INT_PARAM(attribute) GET_SET_INT_PARAM2(belle_sip_uri,attribute,int,attribute##_param) #define SIP_URI_GET_SET_BOOL(attribute) GET_SET_BOOL(belle_sip_uri,attribute,is) #define SIP_URI_HAS_SET_BOOL(attribute) GET_SET_BOOL(belle_sip_uri,attribute,has) #define SIP_URI_HAS_SET_BOOL_PARAM(attribute) GET_SET_BOOL_PARAM2(belle_sip_uri,attribute,has,attribute##_param) struct _belle_sip_uri { belle_sip_parameters_t params; unsigned int secure; char* user; char* user_password; char* host; int port; belle_sip_parameters_t * header_list; }; static void belle_sip_uri_destroy(belle_sip_uri_t* uri) { if (uri->user) belle_sip_free (uri->user); if (uri->host) belle_sip_free (uri->host); if (uri->user_password) belle_sip_free (uri->user_password); belle_sip_object_unref(BELLE_SIP_OBJECT(uri->header_list)); } static void belle_sip_uri_clone(belle_sip_uri_t* uri, const belle_sip_uri_t *orig){ uri->secure=orig->secure; uri->user=orig->user?belle_sip_strdup(orig->user):NULL; uri->user_password=orig->user_password?belle_sip_strdup(orig->user_password):NULL; uri->host=orig->host?belle_sip_strdup(orig->host):NULL; uri->port=orig->port; if (orig->header_list){ uri->header_list=(belle_sip_parameters_t*)belle_sip_object_clone(BELLE_SIP_OBJECT(orig->header_list)); belle_sip_object_ref(uri->header_list); } } static void encode_params(belle_sip_param_pair_t* container, belle_sip_list_t** newlist) { char *escapedName = belle_sip_uri_to_escaped_parameter(container->name); char *escapedValue = container->value? belle_sip_uri_to_escaped_parameter(container->value) : NULL; *newlist = belle_sip_list_append(*newlist, belle_sip_param_pair_new(escapedName, escapedValue)); if (escapedName) free(escapedName); if (escapedValue) free(escapedValue); } static void encode_headers(belle_sip_param_pair_t* container, belle_sip_list_t** newlist) { char *escapedName = belle_sip_uri_to_escaped_header(container->name); char *escapedValue = container->value? belle_sip_uri_to_escaped_header(container->value) : NULL; *newlist = belle_sip_list_append(*newlist, belle_sip_param_pair_new(escapedName, escapedValue)); if (escapedName) free(escapedName); if (escapedValue) free(escapedValue); } belle_sip_error_code belle_sip_uri_marshal(const belle_sip_uri_t* uri, char* buff, size_t buff_size, size_t *offset) { const belle_sip_list_t* list; belle_sip_error_code error=BELLE_SIP_OK; error=belle_sip_snprintf(buff,buff_size,offset,"%s:",uri->secure?"sips":"sip"); if (error!=BELLE_SIP_OK) return error; if (uri->user && uri->user[0]!='\0') { char* escaped_username=belle_sip_uri_to_escaped_username(uri->user); error=belle_sip_snprintf(buff,buff_size,offset,"%s",escaped_username); belle_sip_free(escaped_username); if (error!=BELLE_SIP_OK) return error; if (uri->user_password) { char* escaped_password=belle_sip_uri_to_escaped_userpasswd(uri->user_password); error=belle_sip_snprintf(buff,buff_size,offset,":%s",escaped_password); belle_sip_free(escaped_password); if (error!=BELLE_SIP_OK) return error; } error=belle_sip_snprintf(buff,buff_size,offset,"@"); if (error!=BELLE_SIP_OK) return error; } if (uri->host) { if (strchr(uri->host,':')) { /*ipv6*/ error=belle_sip_snprintf(buff,buff_size,offset,"[%s]",uri->host); } else { error=belle_sip_snprintf(buff,buff_size,offset,"%s",uri->host); } if (error!=BELLE_SIP_OK) return error; } else { belle_sip_warning("no host found in this uri"); } if (uri->port!=0) { error=belle_sip_snprintf(buff,buff_size,offset,":%i",uri->port); if (error!=BELLE_SIP_OK) return error; } { belle_sip_parameters_t *encparams = belle_sip_parameters_new(); belle_sip_list_for_each2(uri->params.param_list, (void (*)(void *, void *))encode_params, &encparams->param_list); error=belle_sip_parameters_marshal(encparams,buff,buff_size,offset); belle_sip_object_unref(encparams); if (error!=BELLE_SIP_OK) return error; } { belle_sip_list_t * encheaders = NULL; belle_sip_list_for_each2(uri->header_list->param_list, (void (*)(void *, void *))encode_headers, &encheaders); for(list=encheaders;list!=NULL;list=list->next){ belle_sip_param_pair_t* container = list->data; if (list == encheaders) { //first case error=belle_sip_snprintf(buff,buff_size,offset,"?%s=%s",container->name,container->value?container->value:""); } else { //subsequent headers error=belle_sip_snprintf(buff,buff_size,offset,"&%s=%s",container->name,container->value?container->value:""); } if (error!=BELLE_SIP_OK) break; } belle_sip_list_free_with_data(encheaders,(void (*)(void*))belle_sip_param_pair_destroy); } return error; } BELLE_SIP_PARSE(uri); BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_uri_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_uri_t,belle_sip_parameters_t,belle_sip_uri_destroy,belle_sip_uri_clone,belle_sip_uri_marshal,TRUE); belle_sip_uri_t* belle_sip_uri_new () { belle_sip_uri_t* l_object = belle_sip_object_new(belle_sip_uri_t); belle_sip_parameters_init((belle_sip_parameters_t*)l_object); /*super*/ l_object->header_list = belle_sip_parameters_new(); belle_sip_object_ref(l_object->header_list); return l_object; } belle_sip_uri_t* belle_sip_uri_create (const char* username,const char* host) { belle_sip_uri_t* uri = belle_sip_uri_new(); belle_sip_uri_set_user(uri,username); belle_sip_uri_set_host(uri,host); return uri; } char* belle_sip_uri_to_string(const belle_sip_uri_t* uri) { return belle_sip_object_to_string(BELLE_SIP_OBJECT(uri)); } const char* belle_sip_uri_get_header(const belle_sip_uri_t* uri,const char* name) { return belle_sip_parameters_get_parameter(uri->header_list,name); } void belle_sip_uri_set_header(belle_sip_uri_t* uri,const char* name,const char* value) { belle_sip_parameters_set_parameter(uri->header_list,name,value); } void belle_sip_uri_remove_header(belle_sip_uri_t *uri, const char *name){ belle_sip_parameters_remove_parameter(uri->header_list,name); } const belle_sip_list_t* belle_sip_uri_get_header_names(const belle_sip_uri_t* uri) { return belle_sip_parameters_get_parameter_names(uri->header_list); } int belle_sip_uri_get_listening_port(const belle_sip_uri_t *uri){ int port=belle_sip_uri_get_port(uri); const char *transport=belle_sip_uri_get_transport_param(uri); if (!transport) { transport=belle_sip_uri_is_secure(uri)?"tls":"udp"; } if (port==0) port=belle_sip_listening_point_get_well_known_port(transport); return port; } void belle_sip_uri_fix(belle_sip_uri_t *uri){ /*nop, to be removed*/ } SIP_URI_GET_SET_BOOL(secure) SIP_URI_GET_SET_STRING(user) SIP_URI_GET_SET_STRING(user_password) SIP_URI_GET_SET_STRING(host) SIP_URI_GET_SET_INT(port) SIP_URI_GET_SET_STRING_PARAM(transport) SIP_URI_GET_SET_STRING_PARAM(user) SIP_URI_GET_SET_STRING_PARAM(method) SIP_URI_GET_SET_STRING_PARAM(maddr) SIP_URI_GET_SET_INT_PARAM(ttl) SIP_URI_HAS_SET_BOOL_PARAM(lr) const belle_sip_parameters_t* belle_sip_uri_get_headers(const belle_sip_uri_t* uri) { return uri->header_list; } void belle_sip_uri_headers_clean(belle_sip_uri_t* uri) { belle_sip_parameters_clean(uri->header_list); } static int uri_strcmp(const char*a,const char*b,int case_sensitive) { int result = 0; size_t index_a=0,index_b=0; char char_a,char_b; if (a == NULL && b == NULL) { goto end; } if ((a != NULL && b == NULL) || (a == NULL && b != NULL)){ result = 1; goto end; } do { index_a+=belle_sip_get_char(a+index_a,&char_a); index_b+=belle_sip_get_char(b+index_b,&char_b); if (!case_sensitive && char_a<0x7B && char_a>0x60) char_a-=0x20; if (!case_sensitive && char_b<0x7B && char_b>0x60) char_b-=0x20; result=(char_a!=char_b); if (result) break; if (char_a == '\0' || char_b == '\0') break; }while(1); end: return result; } #define IS_EQUAL(a,b) (uri_strcmp(a,b,TRUE)==0) #define IS_EQUAL_CASE(a,b) (uri_strcmp(a,b,FALSE)==0) #define PARAM_CASE_CMP(uri_a,uri_b,param) \ a_param=belle_sip_parameters_get_case_parameter((belle_sip_parameters_t*) uri_a,param); \ b_param=belle_sip_parameters_get_case_parameter((belle_sip_parameters_t*) uri_b,param);\ if (!IS_EQUAL_CASE(a_param,b_param)) return 0; /* * RFC 3261 SIP: Session Initiation Protocol June 2002 * 19.1.4 URI Comparison Some operations in this specification require determining whether two SIP or SIPS URIs are equivalent. In this specification, registrars need to compare bindings in Contact URIs in REGISTER requests (see Section 10.3.). SIP and SIPS URIs are compared for equality according to the following rules: */ int belle_sip_uri_equals(const belle_sip_uri_t* uri_a,const belle_sip_uri_t* uri_b) { const belle_sip_list_t * params; const char* b_param; const char* a_param; /* o A SIP and SIPS URI are never equivalent. */ if (belle_sip_uri_is_secure(uri_a)!=belle_sip_uri_is_secure(uri_b)) { return 0; } /* o Comparison of the userinfo of SIP and SIPS URIs is case- sensitive. This includes userinfo containing passwords or formatted as telephone-subscribers. Comparison of all other components of the URI is case-insensitive unless explicitly defined otherwise. */ if (!IS_EQUAL(uri_a->user,uri_b->user)) return 0; /* o The ordering of parameters and header fields is not significant in comparing SIP and SIPS URIs. o Characters other than those in the "reserved" set (see RFC 2396 [5]) are equivalent to their ""%" HEX HEX" encoding. o An IP address that is the result of a DNS lookup of a host name does not match that host name. o For two URIs to be equal, the user, password, host, and port components must match. */ if (!IS_EQUAL_CASE(uri_a->host,uri_b->host)) { return 0; } if (uri_a->port != uri_b->port) return 0; /* A URI omitting the user component will not match a URI that includes one. A URI omitting the password component will not match a URI that includes one. A URI omitting any component with a default value will not match a URI explicitly containing that component with its default value. For instance, a URI omitting the optional port component will not match a URI explicitly declaring port 5060. The same is true for the transport-parameter, ttl-parameter, user-parameter, and method components. Defining sip:user@host to not be equivalent to sip:user@host:5060 is a change from RFC 2543. When deriving addresses from URIs, equivalent addresses are expected from equivalent URIs. The URI sip:user@host:5060 will always resolve to port 5060. The URI sip:user@host may resolve to other ports through the DNS SRV mechanisms detailed in [4]. o URI uri-parameter components are compared as follows: - Any uri-parameter appearing in both URIs must match. */ /* * - A user, ttl, or method uri-parameter appearing in only one URI never matches, even if it contains the default value. - A URI that includes an maddr parameter will not match a URI that contains no maddr parameter. * */ PARAM_CASE_CMP(uri_a,uri_b,"transport") PARAM_CASE_CMP(uri_a,uri_b,"user") PARAM_CASE_CMP(uri_a,uri_b,"ttl") PARAM_CASE_CMP(uri_a,uri_b,"method") PARAM_CASE_CMP(uri_a,uri_b,"maddr") for(params=belle_sip_parameters_get_parameters((belle_sip_parameters_t*) uri_a);params!=NULL;params=params->next) { if ((b_param=belle_sip_parameters_get_parameter((belle_sip_parameters_t*) uri_b,(const char*)params->data)) != NULL) { if (!IS_EQUAL_CASE(b_param,(const char*)params->data)) return 0; } } /* - All other uri-parameters appearing in only one URI are ignored when comparing the URIs. */ /* *fixme ignored for now*/ /* o URI header components are never ignored. Any present header component MUST be present in both URIs and match for the URIs to match. The matching rules are defined for each header field in Section 20. */ return 1; } /*uri checker*/ /* * From 19.1.1 SIP and SIPS URI Components * dialog reg./redir. Contact/ default Req.-URI To From Contact R-R/Route external user -- o o o o o o password -- o o o o o o host -- m m m m m m port (1) o - - o o o user-param ip o o o o o o method INVITE - - - - - o maddr-param -- o - - o o o ttl-param 1 o - - o - o transp.-param (2) o - - o o o lr-param -- o - - - o o other-param -- o o o o o o headers -- - - - o - o (1): The default port value is transport and scheme dependent. The default is 5060 for sip: using UDP, TCP, or SCTP. The default is 5061 for sip: using TLS over TCP and sips: over TCP. (2): The default transport is scheme dependent. For sip:, it is UDP. For sips:, it is TCP. Table 1: Use and default values of URI components for SIP header field values, Request-URI and references*/ typedef enum { m /*mandotory*/ , o /*optionnal*/ , na /*not allowd*/ } mark; static const char* mark_to_string(mark value) { switch (value) { case o: return "optionnal"; case m: return "mandatory"; case na: return "not allowed"; } return "unknown"; } typedef struct uri_components { const char * name; mark user; mark password; mark host; mark port; mark user_param; mark method; mark maddr_param; mark ttl_param; mark transp_param; mark lr_param; mark other_param; mark headers; } uri_components_t; /*belle sip allows contact header without host because stack will auutomatically put host if missing*/ static uri_components_t uri_component_use_for_request = {"Req.-URI" ,o ,o ,m ,o ,o ,na ,o ,o ,o ,o ,o ,na}; static uri_components_t uri_component_use_for_header_to = {"Header To" ,o ,o ,m ,na ,o ,na ,na ,na ,na ,na ,o ,na}; static uri_components_t uri_component_use_for_header_from = {"Header From" ,o ,o ,m ,na ,o ,na ,na ,na ,na ,na ,o ,na}; static uri_components_t uri_component_use_for_contact_in_reg = {"Contact in REG" ,o ,o ,/*m*/o ,o ,o ,na ,o ,o ,o ,na ,o ,o}; static uri_components_t uri_component_use_for_dialog_ct_rr_ro = {"Dialog Contact/R-R/Route" ,o ,o ,/*m*/o ,o ,o ,na ,o ,na ,o ,o ,o ,na}; static uri_components_t uri_component_use_for_external = {"External" ,o ,o ,m ,o ,o ,o ,o ,o ,o ,o ,o ,o}; static int check_component(int is_present,mark requirement) { switch (requirement) { case o: return TRUE; case m: return is_present; case na: return !is_present; } return 0; } #define CHECK_URI_COMPONENT(uri_component,uri_component_name,component_use_rule,component_use_rule_name) \ if (!check_component(uri_component,component_use_rule)) {\ belle_sip_error("Uri component [%s] does not follow reqs [%s] for context [%s]", uri_component_name,mark_to_string(component_use_rule),component_use_rule_name);\ return FALSE;\ } static int check_uri_components(const belle_sip_uri_t* uri, const uri_components_t* components_use) { CHECK_URI_COMPONENT(uri->user!=NULL,"user",components_use->user,components_use->name) CHECK_URI_COMPONENT(uri->host!=NULL,"host",components_use->host,components_use->name) CHECK_URI_COMPONENT(uri->port>0,"port",components_use->port,components_use->name) CHECK_URI_COMPONENT(belle_sip_parameters_has_parameter(&uri->params,"maddr"),"maddr-param",components_use->maddr_param,components_use->name) CHECK_URI_COMPONENT(belle_sip_parameters_has_parameter(&uri->params,"ttl"),"ttl-param",components_use->ttl_param,components_use->name) CHECK_URI_COMPONENT(belle_sip_parameters_has_parameter(&uri->params,"transport"),"transp.-param",components_use->transp_param,components_use->name) CHECK_URI_COMPONENT(belle_sip_parameters_has_parameter(&uri->params,"lr"),"lr-param",components_use->lr_param,components_use->name) /*..*/ CHECK_URI_COMPONENT(belle_sip_list_size(belle_sip_parameters_get_parameters(uri->header_list))>0,"headers",components_use->headers,components_use->name) return TRUE; } /*return 0 if not compliant*/ int belle_sip_uri_check_components_from_request_uri(const belle_sip_uri_t* uri) { return check_uri_components(uri,&uri_component_use_for_request); } int belle_sip_uri_check_components_from_context(const belle_sip_uri_t* uri,const char* method,const char* header_name) { if (strcasecmp(BELLE_SIP_FROM,header_name)==0) return check_uri_components(uri,&uri_component_use_for_header_from); else if (strcasecmp(BELLE_SIP_TO,header_name)==0) return check_uri_components(uri,&uri_component_use_for_header_to); else if (strcasecmp(BELLE_SIP_CONTACT,header_name)==0 && method && strcasecmp("REGISTER",method)==0) return check_uri_components(uri,&uri_component_use_for_contact_in_reg); else if (strcasecmp(BELLE_SIP_CONTACT,header_name)==0 || strcasecmp(BELLE_SIP_RECORD_ROUTE,header_name)==0 || strcasecmp(BELLE_SIP_ROUTE,header_name)==0) return check_uri_components(uri,&uri_component_use_for_dialog_ct_rr_ro); else return check_uri_components(uri,&uri_component_use_for_external); } /*fast uri implemenation*/ typedef belle_sip_uri_t belle_sip_fast_uri_t; BELLE_SIP_PARSE(fast_uri); belle-sip-1.6.3/src/belle_sip_utils.c000066400000000000000000000505461313437522400175240ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #define _CRT_RAND_S #include #include #include #include "belle_sip_internal.h" #include "bctoolbox/parser.h" #include "clock_gettime.h" /*for apple*/ #ifndef _WIN32 #include #include /*for gettimeofday*/ #include /* available on POSIX system only */ #else #include #endif belle_sip_error_code belle_sip_snprintf(char *buff, size_t buff_size, size_t *offset, const char *fmt, ...) { belle_sip_error_code ret; va_list args; va_start(args, fmt); ret = belle_sip_snprintf_valist(buff, buff_size, offset, fmt, args); va_end(args); return ret; } belle_sip_error_code belle_sip_snprintf_valist(char *buff, size_t buff_size, size_t *offset, const char *fmt, va_list args) { int ret; belle_sip_error_code error = BELLE_SIP_OK; ret = vsnprintf(buff + *offset, buff_size - *offset, fmt, args); if ((ret < 0) || (ret >= (int)(buff_size - *offset))) { error = BELLE_SIP_BUFFER_OVERFLOW; *offset = buff_size; } else { *offset += ret; } return error; } #if defined(_WIN32) || defined(_WIN32_WCE) #define ENDLINE "\r\n" #else #define ENDLINE "\n" #endif #ifdef _WIN32 static int belle_sip_gettimeofday (struct timeval *tv, void* tz) { union { __int64 ns100; /*time since 1 Jan 1601 in 100ns units */ FILETIME fileTime; } now; GetSystemTimeAsFileTime (&now.fileTime); tv->tv_usec = (long) ((now.ns100 / 10LL) % 1000000LL); tv->tv_sec = (long) ((now.ns100 - 116444736000000000LL) / 10000000LL); return 0; } #else #define belle_sip_gettimeofday gettimeofday #endif #ifndef _WIN32 static int find_best_clock_id (void) { #if 0 struct timespec ts; static int clock_id=-1; #ifndef __ANDROID__ #define DEFAULT_CLOCK_MODE CLOCK_MONOTONIC #else #define DEFAULT_CLOCK_MODE CLOCK_REALTIME /*monotonic clock stop during sleep mode*/ #endif if (clock_id==-1) { if (clock_gettime(DEFAULT_CLOCK_MODE,&ts)!=1){ clock_id=DEFAULT_CLOCK_MODE; } else if (clock_gettime(CLOCK_REALTIME,&ts)!=1){ clock_id=CLOCK_REALTIME; } else { belle_sip_fatal("Cannot find suitable clock mode"); } } return clock_id; #else /* Tt seems that both Linux, iOS, and MacOS stop incrementing the CLOCK_MONOTONIC during sleep time. * This is a real problem, because all refreshable requests (SUBSCRIBE, REGISTER, PUBLISH) won't be sent on time due to * system going to sleep. Let's take an example: a REGISTER is sent at T0 with expire 3600, then the macbook suspends at T0+60s. * When the macbook resumes at T0+8000, nothing happens. The REGISTER refresh will be sent at T0+8000+3600-60. * The only reason for seeing the register is if the network address has changed, in which case it will trigger a shutdown of all sockets. * As a result, we fallback to CLOCK_REALTIME until the OS correctly implement CLOCK_MONOTONIC according to POSIX specifications */ #ifdef __APPLE__ #ifdef CLOCK_REALTIME #undef CLOCK_REALTIME #endif #define CLOCK_REALTIME BC_CLOCK_REALTIME #endif return CLOCK_REALTIME; #endif } uint64_t belle_sip_time_ms(void){ #ifdef __APPLE__ #define clock_gettime bc_clock_gettime #endif struct timespec ts; if (clock_gettime(find_best_clock_id(),&ts)==-1){ belle_sip_error("clock_gettime() error for clock_id=%i: %s",find_best_clock_id(),strerror(errno)); return 0; } return (ts.tv_sec*1000LL) + (ts.tv_nsec/1000000LL); } #else uint64_t belle_sip_time_ms(void){ #ifdef BELLE_SIP_WINDOWS_DESKTOP return GetTickCount(); #else return GetTickCount64(); #endif } #endif /** * parser parameter pair */ belle_sip_param_pair_t* belle_sip_param_pair_new(const char* name,const char* value) { belle_sip_param_pair_t* lPair = (belle_sip_param_pair_t*)belle_sip_new0(belle_sip_param_pair_t); lPair->name=name?belle_sip_strdup(name):NULL; lPair->value=value?belle_sip_strdup(value):NULL; return lPair; } void belle_sip_param_pair_destroy(belle_sip_param_pair_t* pair) { if (pair->name) belle_sip_free(pair->name); if (pair->value) belle_sip_free(pair->value); belle_sip_free(pair); } int belle_sip_param_pair_comp_func(const belle_sip_param_pair_t *a, const char*b) { return strcmp(a->name,b); } int belle_sip_param_pair_case_comp_func(const belle_sip_param_pair_t *a, const char*b) { return strcasecmp(a->name,b); } char* _belle_sip_str_dup_and_unquote_string(const char* quoted_string) { size_t value_size = strlen(quoted_string); char* unquoted_string = belle_sip_malloc0(value_size-2+1); strncpy(unquoted_string,quoted_string+1,value_size-2); return unquoted_string; } char *belle_sip_unquote_strdup(const char *str){ const char *p; if (str==NULL) return NULL; for(p=str;*p!='\0';++p){ switch(*p){ case ' ': case '\t': break; case '"': return _belle_sip_str_dup_and_unquote_string(p); default: return belle_sip_strdup(str); break; } } return belle_sip_strdup(str); } #if defined(_WIN32) && !defined(_MSC_VER) #include static int belle_sip_wincrypto_random(unsigned int *rand_number){ static HCRYPTPROV hProv=(HCRYPTPROV)-1; static int initd=0; if (!initd){ if (!CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)){ belle_sip_error("Could not acquire a windows crypto context"); return -1; } initd=TRUE; } if (hProv==(HCRYPTPROV)-1) return -1; if (!CryptGenRandom(hProv,4,(BYTE*)rand_number)){ belle_sip_error("CryptGenRandom() failed."); return -1; } return 0; } #endif unsigned int belle_sip_random(void){ #if defined(__linux) || defined(__APPLE__) static int fd=-1; if (fd==-1) fd=open("/dev/urandom",O_RDONLY); if (fd!=-1){ unsigned int tmp; if (read(fd,&tmp,4)!=4){ belle_sip_error("Reading /dev/urandom failed."); }else return tmp; }else belle_sip_error("Could not open /dev/urandom"); #elif defined(_WIN32) static int initd=0; unsigned int ret; #ifdef _MSC_VER /*rand_s() is pretty nice and simple function but is not wrapped by mingw.*/ if (rand_s(&ret)==0){ return ret; } #else if (belle_sip_wincrypto_random(&ret)==0){ return ret; } #endif /* Windows's rand() is unsecure but is used as a fallback*/ if (!initd) { srand((unsigned int)belle_sip_time_ms()); initd=1; belle_sip_warning("Random generator is using rand(), this is unsecure !"); } return rand()<<16 | rand(); #endif /*fallback to UNIX random()*/ #ifndef _WIN32 return (unsigned int) random(); #endif } void belle_sip_set_socket_api(bctbx_vsocket_api_t* my_api){ bctbx_vsocket_api_set_default(my_api); } static const char *symbols="aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789-~"; /** * Write a random text token of supplied size. **/ char * belle_sip_random_token(char *ret, size_t size){ unsigned int val=0; unsigned int i; for(i=0;i>6; } ret[i]=0; return ret; } /** * Write random bytes of supplied size. **/ unsigned char * belle_sip_random_bytes(unsigned char *ret, size_t size){ unsigned int val=0; unsigned int i; for(i=0;i>8; } return ret; } typedef struct bits_reader{ const uint8_t *buffer; size_t buf_size; int bit_index; }bits_reader_t; static void bits_reader_init(bits_reader_t *reader, const uint8_t *buffer, size_t bufsize){ reader->buffer=buffer; reader->buf_size=bufsize; reader->bit_index=0; } static int bits_reader_read(bits_reader_t *reader, int count, unsigned int *ret){ unsigned int tmp; size_t byte_index=reader->bit_index/8; size_t bit_index=reader->bit_index % 8; int shift=32-(int)bit_index-count; if (count>=24){ belle_sip_error("This bit reader cannot read more than 24 bits at once."); return -1; } if (byte_indexbuf_size) tmp=((unsigned int)reader->buffer[byte_index++])<<24; else{ belle_sip_error("Bit reader goes end of stream."); return -1; } if (byte_indexbuf_size) tmp|=((unsigned int)reader->buffer[byte_index++])<<16; if (byte_indexbuf_size) tmp|=((unsigned int)reader->buffer[byte_index++])<<8; if (byte_indexbuf_size) tmp|=((unsigned int)reader->buffer[byte_index++]); tmp=tmp>>shift; tmp=tmp & ((1<bit_index+=count; *ret=tmp; return 0; } char * belle_sip_octets_to_text(const uint8_t *hash, size_t hash_len, char *ret, size_t size){ int i; bits_reader_t bitctx; bits_reader_init(&bitctx,hash,hash_len); for(i=0;i<(int)size-1;++i){ unsigned int val=0; if (bits_reader_read(&bitctx,6,&val)==0){ ret[i]=symbols[val]; }else break; } ret[i]=0; return ret; } void belle_sip_util_copy_headers(belle_sip_message_t *orig, belle_sip_message_t *dest, const char*header, int multiple){ const belle_sip_list_t *elem; elem=belle_sip_message_get_headers(orig,header); for (;elem!=NULL;elem=elem->next){ belle_sip_header_t *ref_header=(belle_sip_header_t*)elem->data; if (ref_header){ ref_header=(belle_sip_header_t*)belle_sip_object_clone((belle_sip_object_t*)ref_header); if (!multiple){ belle_sip_message_set_header(dest,ref_header); break; }else belle_sip_message_add_header(dest,ref_header); } } } char* belle_sip_to_unescaped_string(const char* buff) { return bctbx_unescaped_string(buff); } size_t belle_sip_get_char(const char* a, char *b) { return bctbx_get_char(a,b); } /* static void print_noescapes_map(char noescapes[BELLE_SIP_NO_ESCAPES_SIZE], const char *name) { unsigned int i; printf("Noescapes %s :", name); for (i=' '; i <= '~'; ++i) { if (noescapes[i] == 1) printf ("%c", i); //if (noescapes[i] == 1) printf ("%c %d - %d\n", i, (char)i, noescapes[i]); } printf ("init: %d\n", noescapes[BELLE_SIP_NO_ESCAPES_SIZE-1]); } */ static const bctbx_noescape_rules_t* get_sip_uri_username_noescapes(void) { static bctbx_noescape_rules_t noescapes = {0}; if (noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] == 0) { // concurrent initialization should not be an issue /*user = 1*( unreserved / escaped / user-unreserved ) unreserved = alphanum / mark mark = "-" / "_" / "." / "!" / "~" / "*" / "'" / "(" / ")" user-unreserved = "&" / "=" / "+" / "$" / "," / ";" / "?" / "/" */ bctbx_noescape_rules_add_alfanums(noescapes); /*mark*/ bctbx_noescape_rules_add_list(noescapes, "-_.!~*'()"); /*user-unreserved*/ bctbx_noescape_rules_add_list(noescapes, "&=+$,;?/"); noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] = 1; // initialized // print_noescapes_map(noescapes, "uri_username"); } return (const bctbx_noescape_rules_t*)&noescapes;/*gcc asks for a cast, clang not*/ } /* * * password = *( unreserved / escaped / "&" / "=" / "+" / "$" / "," ) * */ static const bctbx_noescape_rules_t* get_sip_uri_userpasswd_noescapes(void) { static bctbx_noescape_rules_t noescapes = {0}; if (noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] == 0) { // unreserved bctbx_noescape_rules_add_alfanums(noescapes); bctbx_noescape_rules_add_list(noescapes, "-_.!~*'()"); bctbx_noescape_rules_add_list(noescapes, "&=+$,"); noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] = 1; // initialized } return (const bctbx_noescape_rules_t*)&noescapes;/*gcc asks for a cast, clang not*/ } static const bctbx_noescape_rules_t* get_sip_uri_parameter_noescapes(void) { static bctbx_noescape_rules_t noescapes= {0}; if (noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] == 0) { /* other-param = pname [ "=" pvalue ] pname = 1*paramchar pvalue = 1*paramchar paramchar = param-unreserved / unreserved / escaped param-unreserved = "[" / "]" / "/" / ":" / "&" / "+" / "$" unreserved = alphanum / mark mark = "-" / "_" / "." / "!" / "~" / "*" / "'" / "(" / ")" escaped = "%" HEXDIG HEXDIG token = 1*(alphanum / "-" / "." / "!" / "%" / "*" / "_" / "+" / "`" / "'" / "~" ) */ //param-unreserved = bctbx_noescape_rules_add_list(noescapes,"[]/:&+$"); // token bctbx_noescape_rules_add_alfanums(noescapes); bctbx_noescape_rules_add_list(noescapes, "-.!%*_+`'~"); // unreserved bctbx_noescape_rules_add_list(noescapes, "-_.!~*'()"); noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] = 1; // initialized // print_noescapes_map(noescapes, "uri_parameter"); } return (const bctbx_noescape_rules_t*)&noescapes;/*gcc asks for a cast, clang not*/ } static const bctbx_noescape_rules_t* get_sip_uri_header_noescapes(void) { static bctbx_noescape_rules_t noescapes= {0}; if (noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] == 0) { /* unreserved = alphanum / mark mark = "-" / "_" / "." / "!" / "~" / "*" / "'" / "(" / ")" escaped = "%" HEXDIG HEXDIG //.... header = hname "=" hvalue hname = 1*( hnv-unreserved / unreserved / escaped ) hvalue = *( hnv-unreserved / unreserved / escaped ) hnv-unreserved = "[" / "]" / "/" / "?" / ":" / "+" / "$" */ // unreserved //alphanum bctbx_noescape_rules_add_alfanums(noescapes); //mark bctbx_noescape_rules_add_list(noescapes, "-_.!~*'()"); bctbx_noescape_rules_add_list(noescapes, "[]/?:+$"); //hnv-unreserved noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] = 1; // initialized // print_noescapes_map(noescapes, "uri_parameter"); } return (const bctbx_noescape_rules_t*)&noescapes;/*gcc asks for a cast, clang not*/ } char* belle_sip_uri_to_escaped_username(const char* buff) { return bctbx_escape(buff, *get_sip_uri_username_noescapes()); } char* belle_sip_uri_to_escaped_userpasswd(const char* buff) { return bctbx_escape(buff, *get_sip_uri_userpasswd_noescapes()); } char* belle_sip_uri_to_escaped_parameter(const char* buff) { return bctbx_escape(buff, *get_sip_uri_parameter_noescapes()); } char* belle_sip_uri_to_escaped_header(const char* buff) { return bctbx_escape(buff, *get_sip_uri_header_noescapes()); } /*uri (I.E RFC 2396)*/ static const bctbx_noescape_rules_t *get_generic_uri_query_noescapes(void) { static bctbx_noescape_rules_t noescapes= {0}; if (noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] == 0) { /* uric = reserved | unreserved | escaped reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," unreserved = alphanum | mark mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" 3.4. Query Component query = *uric Within a query component, the characters ";", "/", "?", ":", "@", "&", "=", "+", ",", and "$" are reserved. */ /*unreserved*/ bctbx_noescape_rules_add_alfanums(noescapes); /*mark*/ bctbx_noescape_rules_add_list(noescapes, "-_.!~*'()"); bctbx_noescape_rules_add_list(noescapes, "=&"); // otherwise how to pass parameters? noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] = 1; // initialized } return (const bctbx_noescape_rules_t*)&noescapes;/*gcc asks for a cast, clang not*/ } static const bctbx_noescape_rules_t *get_generic_uri_path_noescapes(void) { static bctbx_noescape_rules_t noescapes= {0}; if (noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] == 0) { /* 3.3. Path Component The path component contains data, specific to the authority (or the scheme if there is no authority component), identifying the resource within the scope of that scheme and authority. path = [ abs_path | opaque_part ] path_segments = segment *( "/" segment ) segment = *pchar *( ";" param ) param = *pchar pchar = unreserved | escaped | ":" | "@" | "&" | "=" | "+" | "$" | "," The path may consist of a sequence of path segments separated by a single slash "/" character. Within a path segment, the characters "/", ";", "=", and "?" are reserved. Each path segment may include a sequence of parameters, indicated by the semicolon ";" character. The parameters are not significant to the parsing of relative references. */ /*unreserved*/ bctbx_noescape_rules_add_alfanums(noescapes); /*mark*/ bctbx_noescape_rules_add_list(noescapes, "-_.!~*'()"); /*pchar*/ bctbx_noescape_rules_add_list(noescapes, ":@&=+$,"); /*;*/ bctbx_noescape_rules_add_list(noescapes, ";"); bctbx_noescape_rules_add_list(noescapes, "/"); noescapes[BCTBX_NOESCAPE_RULES_USER_INDEX] = 1; // initialized } return (const bctbx_noescape_rules_t*)&noescapes;/*gcc asks for a cast, clang not*/ } char* belle_generic_uri_to_escaped_query(const char* buff) { return bctbx_escape(buff, *get_generic_uri_query_noescapes()); } char* belle_generic_uri_to_escaped_path(const char* buff) { return bctbx_escape(buff, *get_generic_uri_path_noescapes()); } char* belle_sip_string_to_backslash_less_unescaped_string(const char* buff) { char *output_buff=belle_sip_malloc(strlen(buff)+1); unsigned int i; unsigned int out_buff_index=0; for(i=0; buff[i] != '\0'; i++) { if (buff[i] == '\\') { i++;/*skip \*/ } /*make sure to only remove one \ in case of \\*/ output_buff[out_buff_index++]=buff[i]; } output_buff[out_buff_index]='\0'; return output_buff; } char* belle_sip_display_name_to_backslashed_escaped_string(const char* buff) { char output_buff[BELLE_SIP_MAX_TO_STRING_SIZE]; unsigned int i; unsigned int out_buff_index=0; for(i=0; buff[i] != '\0' && out_buff_index < sizeof(output_buff)-2; i++) { /*-3 to make sure last param can be stored in escaped form*/ const char c = buff[i]; if (c == '\"' || c == '\\') { output_buff[out_buff_index++]='\\'; /*insert escape character*/ } output_buff[out_buff_index++]=c; } output_buff[out_buff_index]='\0'; return belle_sip_strdup(output_buff); } belle_sip_list_t *belle_sip_parse_directory(const char *path, const char *file_type) { belle_sip_list_t* file_list = NULL; #ifdef _WIN32 WIN32_FIND_DATA FileData; HANDLE hSearch; BOOL fFinished = FALSE; char szDirPath[1024]; #ifdef UNICODE wchar_t wszDirPath[1024]; #endif if (file_type == NULL) { file_type = ".*"; } snprintf(szDirPath, sizeof(szDirPath), "%s\\*%s", path, file_type); #ifdef UNICODE mbstowcs(wszDirPath, szDirPath, sizeof(wszDirPath)); hSearch = FindFirstFileExW(wszDirPath, FindExInfoStandard, &FileData, FindExSearchNameMatch, NULL, 0); #else hSearch = FindFirstFileExA(szDirPath, FindExInfoStandard, &FileData, FindExSearchNameMatch, NULL, 0); #endif if (hSearch == INVALID_HANDLE_VALUE) { belle_sip_message("No file (*%s) found in [%s] [%d].", file_type, szDirPath, (int)GetLastError()); return NULL; } snprintf(szDirPath, sizeof(szDirPath), "%s", path); while (!fFinished) { char szFilePath[1024]; #ifdef UNICODE char filename[512]; wcstombs(filename, FileData.cFileName, sizeof(filename)); snprintf(szFilePath, sizeof(szFilePath), "%s\\%s", szDirPath, filename); #else snprintf(szFilePath, sizeof(szFilePath), "%s\\%s", szDirPath, FileData.cFileName); #endif file_list = belle_sip_list_append(file_list, belle_sip_strdup(szFilePath)); if (!FindNextFile(hSearch, &FileData)) { if (GetLastError() == ERROR_NO_MORE_FILES) { fFinished = TRUE; } else { belle_sip_error("Couldn't find next (*%s) file.", file_type); fFinished = TRUE; } } } /* Close the search handle. */ FindClose(hSearch); #else DIR *dir; struct dirent *ent; if ((dir = opendir(path)) == NULL) { belle_sip_error("Could't open [%s] directory.", path); return NULL; } /* loop on all directory files */ errno = 0; ent = readdir(dir); while (ent != NULL) { /* filter on file type if given */ if (file_type==NULL || (strncmp(ent->d_name+strlen(ent->d_name)-strlen(file_type), file_type, strlen(file_type))==0) ) { char *name_with_path=belle_sip_strdup_printf("%s/%s",path,ent->d_name); file_list = belle_sip_list_append(file_list, name_with_path); } ent = readdir(dir); } if (errno != 0) { belle_sip_error("Error while reading the [%s] directory: %s.", path, strerror(errno)); } closedir(dir); #endif return file_list; } int belle_sip_mkdir(const char *path) { #ifdef _WIN32 return _mkdir(path); #else return mkdir(path, 0700); #endif } belle-sip-1.6.3/src/bodyhandler.c000066400000000000000000001113341313437522400166320ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2014 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #include "bctoolbox/vfs.h" #ifdef HAVE_ZLIB #include "zlib.h" #endif /* * Body handler base class implementation */ struct belle_sip_body_handler{ belle_sip_object_t base; belle_sip_body_handler_progress_callback_t progress_cb; size_t expected_size; /* 0 if unknown*/ size_t transfered_size; belle_sip_list_t *headers; /**> used when this body is part of a multipart message to store the header of this part */ char *headerStringBuffer; /**> buffer populated with a string created from marshaling the headers */ void *user_data; }; static void belle_sip_body_handler_clone(belle_sip_body_handler_t *obj, const belle_sip_body_handler_t *orig){ obj->progress_cb=orig->progress_cb; obj->user_data=orig->user_data; obj->expected_size=orig->expected_size; obj->transfered_size=orig->transfered_size; obj->headers=belle_sip_list_copy_with_data(orig->headers,(void *(*)(void*))belle_sip_object_clone_and_ref); if (orig->headerStringBuffer!=NULL) { obj->headerStringBuffer = strdup(orig->headerStringBuffer); } } static void belle_sip_body_handler_destroy(belle_sip_body_handler_t *obj){ belle_sip_list_free_with_data(obj->headers,belle_sip_object_unref); belle_sip_free(obj->headerStringBuffer); } static belle_sip_error_code belle_sip_body_handler_marshal(belle_sip_body_handler_t *obj, char *buff, size_t buff_size, size_t *offset) { int ret; size_t len; if (*offset == 0) belle_sip_body_handler_begin_send_transfer(obj); do { len = buff_size - *offset; ret = belle_sip_body_handler_send_chunk(obj, NULL, (uint8_t*)buff + *offset, &len); *offset += len; } while ((ret == BELLE_SIP_CONTINUE) && (len > 0)); if ((ret == BELLE_SIP_CONTINUE) || (ret == BELLE_SIP_BUFFER_OVERFLOW)) return BELLE_SIP_BUFFER_OVERFLOW; if (ret == BELLE_SIP_STOP) belle_sip_body_handler_end_transfer(obj); return BELLE_SIP_OK; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_body_handler_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_body_handler_t) { BELLE_SIP_VPTR_INIT(belle_sip_body_handler_t,belle_sip_object_t,TRUE), (belle_sip_object_destroy_t) belle_sip_body_handler_destroy, (belle_sip_object_clone_t) belle_sip_body_handler_clone, (belle_sip_object_marshal_t) belle_sip_body_handler_marshal, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, NULL, /* begin_recv_transfer */ NULL, /* begin_send_transfer */ NULL, /* end_transfer */ NULL, /*chunk_recv*/ NULL /*chunk_send*/ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END void belle_sip_body_handler_init(belle_sip_body_handler_t *obj, belle_sip_body_handler_progress_callback_t progress_cb, void *user_data){ obj->user_data=user_data; obj->progress_cb=progress_cb; obj->headers = NULL; /* header is not used in most of the case, set it using a dedicated function if needed */ obj->headerStringBuffer = NULL; /* header string buffer is set when adding a body handler to a multipart body handler */ } void belle_sip_body_handler_add_header(belle_sip_body_handler_t *obj, belle_sip_header_t *header) { if (header != NULL) { obj->headers=belle_sip_list_append(obj->headers,belle_sip_object_ref(header)); } } void belle_sip_body_handler_remove_header_from_ptr(belle_sip_body_handler_t *obj, belle_sip_header_t* header) { belle_sip_list_t* it = belle_sip_list_find(obj->headers, header); if (it) { belle_sip_object_unref(header); obj->headers = belle_sip_list_delete_link(obj->headers, it); } } const belle_sip_list_t* belle_sip_body_handler_get_headers(const belle_sip_body_handler_t *obj) { return obj->headers; } size_t belle_sip_body_handler_get_size(const belle_sip_body_handler_t *obj){ return obj->expected_size; } void belle_sip_body_handler_set_size(belle_sip_body_handler_t *obj, size_t size){ obj->expected_size=size; } size_t belle_sip_body_handler_get_transfered_size(const belle_sip_body_handler_t *obj){ return obj->transfered_size; } static void update_progress(belle_sip_body_handler_t *obj, belle_sip_message_t *msg){ if (obj->progress_cb) obj->progress_cb(obj,msg,obj->user_data,obj->transfered_size,obj->expected_size); } void belle_sip_body_handler_begin_recv_transfer(belle_sip_body_handler_t *obj){ BELLE_SIP_OBJECT_VPTR_TYPE(belle_sip_body_handler_t) *vptr = BELLE_SIP_OBJECT_VPTR(obj, belle_sip_body_handler_t); if (vptr->begin_recv_transfer != NULL) { vptr->begin_recv_transfer(obj); } obj->transfered_size=0; } void belle_sip_body_handler_begin_send_transfer(belle_sip_body_handler_t *obj) { BELLE_SIP_OBJECT_VPTR_TYPE(belle_sip_body_handler_t) *vptr = BELLE_SIP_OBJECT_VPTR(obj, belle_sip_body_handler_t); if (vptr->begin_send_transfer != NULL) { vptr->begin_send_transfer(obj); } obj->transfered_size = 0; } void belle_sip_body_handler_recv_chunk(belle_sip_body_handler_t *obj, belle_sip_message_t *msg, uint8_t *buf, size_t size){ BELLE_SIP_OBJECT_VPTR(obj,belle_sip_body_handler_t)->chunk_recv(obj,msg,(off_t)obj->transfered_size,buf,size); obj->transfered_size+=size; update_progress(obj,msg); } int belle_sip_body_handler_send_chunk(belle_sip_body_handler_t *obj, belle_sip_message_t *msg, uint8_t *buf, size_t *size){ int ret; size_t to_send = *size; if (obj->expected_size!=0){ to_send=MIN(*size,obj->expected_size-obj->transfered_size); } if (to_send==0 && obj->transfered_size==obj->expected_size) { // An eWouldBlock error added a call to the function, nothing to send so return // In some case to_send=0 because not buffer is available but sendings not finished. belle_sip_message("body handler [%p] : Nothing to send",obj); *size=0; return BELLE_SIP_STOP; } ret=BELLE_SIP_OBJECT_VPTR(obj,belle_sip_body_handler_t)->chunk_send(obj,msg,(off_t)obj->transfered_size,buf,&to_send); obj->transfered_size+=to_send; *size=to_send; update_progress(obj,msg); if (obj->expected_size!=0){ if (obj->transfered_size==obj->expected_size) { return BELLE_SIP_STOP; } if (ret==BELLE_SIP_STOP && obj->transfered_sizeexpected_size){ belle_sip_error("body handler [%p] transfered only [%i] bytes while [%i] were expected",obj, (int)obj->transfered_size,(int)obj->expected_size); } } return ret; } void belle_sip_body_handler_end_transfer(belle_sip_body_handler_t *obj){ BELLE_SIP_OBJECT_VPTR_TYPE(belle_sip_body_handler_t) *vptr = BELLE_SIP_OBJECT_VPTR(obj, belle_sip_body_handler_t); if (vptr->end_transfer != NULL) { vptr->end_transfer(obj); } if (obj->expected_size==0) obj->expected_size=obj->transfered_size; } /* * memory body handler implementation. **/ struct belle_sip_memory_body_handler{ belle_sip_body_handler_t base; uint8_t *buffer; uint8_t encoding_applied; }; static void belle_sip_memory_body_handler_destroy(belle_sip_memory_body_handler_t *obj){ if (obj->buffer) belle_sip_free(obj->buffer); } static void belle_sip_memory_body_handler_clone(belle_sip_memory_body_handler_t *obj, const belle_sip_memory_body_handler_t *orig){ if (orig->buffer) { obj->buffer=belle_sip_malloc(orig->base.expected_size+1); memcpy(obj->buffer,orig->buffer,orig->base.expected_size); obj->buffer[orig->base.expected_size]='\0'; } obj->encoding_applied = orig->encoding_applied; } static void belle_sip_memory_body_handler_recv_chunk(belle_sip_body_handler_t *base, belle_sip_message_t *msg, off_t offset, uint8_t *buf, size_t size){ belle_sip_memory_body_handler_t *obj=(belle_sip_memory_body_handler_t*)base; obj->buffer=belle_sip_realloc(obj->buffer,offset+size+1); memcpy(obj->buffer+offset,buf,size); obj->buffer[offset+size]='\0'; } static int belle_sip_memory_body_handler_send_chunk(belle_sip_body_handler_t *base, belle_sip_message_t *msg, off_t offset, uint8_t *buf, size_t *size){ belle_sip_memory_body_handler_t *obj=(belle_sip_memory_body_handler_t*)base; size_t to_send=MIN(*size,obj->base.expected_size-offset); if (obj->buffer == NULL) return BELLE_SIP_STOP; memcpy(buf,obj->buffer+offset,to_send); *size=to_send; return (obj->base.expected_size-offset==*size) ? BELLE_SIP_STOP : BELLE_SIP_CONTINUE; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_memory_body_handler_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_memory_body_handler_t) { { BELLE_SIP_VPTR_INIT(belle_sip_memory_body_handler_t,belle_sip_body_handler_t,TRUE), (belle_sip_object_destroy_t) belle_sip_memory_body_handler_destroy, (belle_sip_object_clone_t)belle_sip_memory_body_handler_clone, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, NULL, NULL, NULL, belle_sip_memory_body_handler_recv_chunk, belle_sip_memory_body_handler_send_chunk } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END void *belle_sip_memory_body_handler_get_buffer(const belle_sip_memory_body_handler_t *obj){ return obj->buffer; } void belle_sip_memory_body_handler_set_buffer(belle_sip_memory_body_handler_t *obj, void *buffer) { if (obj->buffer != NULL) belle_sip_free(obj->buffer); obj->buffer = (uint8_t *)buffer; } #define BELLE_SIP_MEMORY_BODY_HANDLER_ZLIB_INITIAL_SIZE 2048 void belle_sip_memory_body_handler_apply_encoding(belle_sip_memory_body_handler_t *obj, const char *encoding) { if ((obj->buffer == NULL) || (obj->encoding_applied == TRUE)) return; #ifdef HAVE_ZLIB if (strcmp(encoding, "deflate") == 0) { z_stream strm; size_t initial_size = belle_sip_body_handler_get_size(BELLE_SIP_BODY_HANDLER(obj)); size_t final_size; unsigned int avail_out = BELLE_SIP_MEMORY_BODY_HANDLER_ZLIB_INITIAL_SIZE; unsigned int outbuf_size = avail_out; unsigned char *outbuf = belle_sip_malloc(outbuf_size); unsigned char *outbuf_ptr = outbuf; int ret; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION); if (ret != Z_OK) return; strm.avail_in = (uInt)initial_size; strm.next_in = obj->buffer; do { if (avail_out < BELLE_SIP_MEMORY_BODY_HANDLER_ZLIB_INITIAL_SIZE) { unsigned int cursize = (unsigned int)(outbuf_ptr - outbuf); outbuf_size *= 2; outbuf = belle_sip_realloc(outbuf, outbuf_size); outbuf_ptr = outbuf + cursize; } strm.avail_out = avail_out; strm.next_out = outbuf_ptr; deflate(&strm, Z_FINISH); outbuf_ptr += avail_out - strm.avail_out; avail_out = outbuf_size - (unsigned int)(outbuf_ptr - outbuf); } while (strm.avail_out == 0); deflateEnd(&strm); final_size = outbuf_ptr - outbuf; belle_sip_message("Body has been compressed: %u->%u:\n%s", (unsigned int)initial_size, (unsigned int)final_size, obj->buffer); belle_sip_free(obj->buffer); obj->buffer = outbuf; belle_sip_body_handler_set_size(BELLE_SIP_BODY_HANDLER(obj), final_size); obj->encoding_applied = TRUE; } else #endif { belle_sip_warning("%s: unknown encoding '%s'", __FUNCTION__, encoding); } } int belle_sip_memory_body_handler_unapply_encoding(belle_sip_memory_body_handler_t *obj, const char *encoding) { if (obj->buffer == NULL) return -1; #ifdef HAVE_ZLIB if (strcmp(encoding, "deflate") == 0) { z_stream strm; size_t initial_size = belle_sip_body_handler_get_size(BELLE_SIP_BODY_HANDLER(obj)); size_t final_size; unsigned int avail_out = BELLE_SIP_MEMORY_BODY_HANDLER_ZLIB_INITIAL_SIZE; unsigned int outbuf_size = avail_out; unsigned char *outbuf = belle_sip_malloc(outbuf_size); unsigned char *outbuf_ptr = outbuf; bool_t outbuf_too_small = FALSE; int ret; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit(&strm); if (ret != Z_OK) return -1; strm.avail_in = (uInt)initial_size; strm.next_in = obj->buffer; do { if ((avail_out < BELLE_SIP_MEMORY_BODY_HANDLER_ZLIB_INITIAL_SIZE) || (outbuf_too_small == TRUE)) { unsigned int cursize = (unsigned int)(outbuf_ptr - outbuf); unsigned int increase = outbuf_size; outbuf_size *= 2; outbuf = belle_sip_realloc(outbuf, outbuf_size); outbuf_ptr = outbuf + cursize; avail_out += increase; } outbuf_too_small = FALSE; strm.avail_out = avail_out; strm.next_out = outbuf_ptr; ret = inflate(&strm, Z_NO_FLUSH); switch (ret) { case Z_OK: /* Error handling for truncated input buffer. Z_STREAM_END is not returned but there is no further input content */ if (strm.avail_out > 0) { belle_sip_error("%s: truncated compressed body. Cannot uncompress it...", __FUNCTION__); return -1; } break; case Z_STREAM_END: // Everything is ok, continue break; case Z_BUF_ERROR: // Ask for more output space outbuf_too_small = TRUE; break; case Z_NEED_DICT: case Z_DATA_ERROR: case Z_STREAM_ERROR: case Z_MEM_ERROR: default: inflateEnd(&strm); belle_sip_free(outbuf); return -1; } outbuf_ptr += avail_out - strm.avail_out; avail_out = outbuf_size - (unsigned int)(outbuf_ptr - outbuf); } while (ret != Z_STREAM_END); inflateEnd(&strm); final_size = outbuf_ptr - outbuf; if (final_size == outbuf_size) { /* If not enough space get it to be able to put the final NULL character. */ outbuf = belle_sip_realloc(outbuf, outbuf_size + 1); outbuf_ptr = outbuf + final_size; } *outbuf_ptr = '\0'; belle_sip_message("Body has been uncompressed: %u->%u:\n%s", (unsigned int)initial_size, (unsigned int)final_size, outbuf); belle_sip_free(obj->buffer); obj->buffer = outbuf; belle_sip_body_handler_set_size(BELLE_SIP_BODY_HANDLER(obj), final_size); return 0; } else #endif { belle_sip_warning("%s: unknown encoding '%s'", __FUNCTION__, encoding); return -1; } } belle_sip_memory_body_handler_t *belle_sip_memory_body_handler_new(belle_sip_body_handler_progress_callback_t cb, void *user_data){ belle_sip_memory_body_handler_t *obj=belle_sip_object_new(belle_sip_memory_body_handler_t); belle_sip_body_handler_init((belle_sip_body_handler_t*)obj,cb,user_data); return obj; } belle_sip_memory_body_handler_t *belle_sip_memory_body_handler_new_from_buffer(void *buffer, size_t bufsize, belle_sip_body_handler_progress_callback_t cb, void *user_data){ belle_sip_memory_body_handler_t *obj=belle_sip_object_new(belle_sip_memory_body_handler_t); belle_sip_body_handler_init((belle_sip_body_handler_t*)obj,cb,user_data); obj->buffer=(uint8_t*)buffer; obj->base.expected_size=bufsize; return obj; } belle_sip_memory_body_handler_t *belle_sip_memory_body_handler_new_copy_from_buffer(const void *buffer, size_t bufsize, belle_sip_body_handler_progress_callback_t cb, void *user_data){ belle_sip_memory_body_handler_t *obj=belle_sip_object_new(belle_sip_memory_body_handler_t); belle_sip_body_handler_init((belle_sip_body_handler_t*)obj,cb,user_data); obj->buffer=(uint8_t*)belle_sip_malloc(bufsize+1); obj->buffer[bufsize]='\0'; obj->base.expected_size=bufsize; memcpy(obj->buffer,buffer,bufsize); return obj; } /* * User body handler implementation */ struct belle_sip_user_body_handler{ belle_sip_body_handler_t base; belle_sip_user_body_handler_start_callback_t start_cb; belle_sip_user_body_handler_recv_callback_t recv_cb; belle_sip_user_body_handler_send_callback_t send_cb; belle_sip_user_body_handler_stop_callback_t stop_cb; }; static void belle_sip_user_body_handler_begin_transfer(belle_sip_body_handler_t *base) { belle_sip_user_body_handler_t *obj = (belle_sip_user_body_handler_t *)base; if (obj->start_cb) obj->start_cb((belle_sip_user_body_handler_t*)base, base->user_data); } static void belle_sip_user_body_handler_end_transfer(belle_sip_body_handler_t *base) { belle_sip_user_body_handler_t *obj = (belle_sip_user_body_handler_t *)base; if (obj->stop_cb) obj->stop_cb((belle_sip_user_body_handler_t*)base, base->user_data); } static void belle_sip_user_body_handler_recv_chunk(belle_sip_body_handler_t *base, belle_sip_message_t *msg, off_t offset, uint8_t *buf, size_t size){ belle_sip_user_body_handler_t *obj=(belle_sip_user_body_handler_t*)base; if (obj->recv_cb) obj->recv_cb((belle_sip_user_body_handler_t*)base, msg, base->user_data, offset, buf, size); else belle_sip_warning("belle_sip_user_body_handler_t ignoring received chunk."); } static int belle_sip_user_body_handler_send_chunk(belle_sip_body_handler_t *base, belle_sip_message_t *msg, off_t offset, uint8_t *buf, size_t *size){ belle_sip_user_body_handler_t *obj=(belle_sip_user_body_handler_t*)base; if (obj->send_cb) return obj->send_cb((belle_sip_user_body_handler_t*)base, msg, base->user_data, offset, buf, size); else belle_sip_warning("belle_sip_user_body_handler_t ignoring send chunk."); *size=0; return BELLE_SIP_STOP; } static void belle_sip_user_body_handler_clone(belle_sip_user_body_handler_t *obj, const belle_sip_user_body_handler_t *orig){ obj->start_cb=orig->start_cb; obj->recv_cb=orig->recv_cb; obj->send_cb=orig->send_cb; obj->stop_cb=orig->stop_cb; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_user_body_handler_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_user_body_handler_t) { { BELLE_SIP_VPTR_INIT(belle_sip_user_body_handler_t,belle_sip_body_handler_t,TRUE), (belle_sip_object_destroy_t) NULL, (belle_sip_object_clone_t)belle_sip_user_body_handler_clone, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, belle_sip_user_body_handler_begin_transfer, belle_sip_user_body_handler_begin_transfer, belle_sip_user_body_handler_end_transfer, belle_sip_user_body_handler_recv_chunk, belle_sip_user_body_handler_send_chunk } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END belle_sip_user_body_handler_t *belle_sip_user_body_handler_new( size_t total_size, belle_sip_body_handler_progress_callback_t progress_cb, belle_sip_user_body_handler_start_callback_t start_cb, belle_sip_user_body_handler_recv_callback_t recv_cb, belle_sip_user_body_handler_send_callback_t send_cb, belle_sip_user_body_handler_stop_callback_t stop_cb, void *data){ belle_sip_user_body_handler_t * obj=belle_sip_object_new(belle_sip_user_body_handler_t); belle_sip_body_handler_init((belle_sip_body_handler_t*)obj,progress_cb,data); obj->base.expected_size=total_size; obj->start_cb=start_cb; obj->recv_cb=recv_cb; obj->send_cb=send_cb; obj->stop_cb=stop_cb; return obj; } /** * File body handler implementation **/ struct belle_sip_file_body_handler{ belle_sip_body_handler_t base; char *filepath; bctbx_vfs_file_t *file; belle_sip_user_body_handler_t *user_bh; }; static void belle_sip_file_body_handler_destroy(belle_sip_file_body_handler_t *obj) { if (obj->filepath) belle_sip_free(obj->filepath); if (obj->file) { ssize_t ret; ret = bctbx_file_close(obj->file); if (ret == BCTBX_VFS_ERROR) { bctbx_error("Can't close file %s", obj->filepath); } obj->file = NULL; } if (obj->user_bh) { belle_sip_object_unref(obj->user_bh); obj->user_bh = NULL; } } static void belle_sip_file_body_handler_clone(belle_sip_file_body_handler_t *obj, const belle_sip_file_body_handler_t *orig) { obj->filepath = belle_sip_strdup(orig->filepath); obj->file = orig->file; obj->user_bh = orig->user_bh; if (obj->user_bh) { belle_sip_object_ref(obj->user_bh); } } static void belle_sip_file_body_handler_begin_recv_transfer(belle_sip_body_handler_t *base) { belle_sip_file_body_handler_t *obj = (belle_sip_file_body_handler_t *)base; bctbx_vfs_t *vfs = bctbx_vfs_get_default(); if (obj->filepath == NULL) return; obj->file = bctbx_file_open(vfs, obj->filepath, "r+"); if (!obj->file) { bctbx_error("Can't open file %s", obj->filepath); } if (obj->user_bh && obj->user_bh->start_cb) { obj->user_bh->start_cb((belle_sip_user_body_handler_t*)&(obj->user_bh->base), obj->user_bh->base.user_data); } } static void belle_sip_file_body_handler_begin_send_transfer(belle_sip_body_handler_t *base) { belle_sip_file_body_handler_t *obj = (belle_sip_file_body_handler_t *)base; bctbx_vfs_t *vfs = bctbx_vfs_get_default(); if (obj->filepath == NULL) return; obj->file = bctbx_file_open(vfs, obj->filepath, "r"); if (!obj->file) { bctbx_error("Can't open file %s", obj->filepath); } if (obj->user_bh && obj->user_bh->start_cb) { obj->user_bh->start_cb((belle_sip_user_body_handler_t*)&(obj->user_bh->base), obj->user_bh->base.user_data); } } static void belle_sip_file_body_handler_end_transfer(belle_sip_body_handler_t *base) { belle_sip_file_body_handler_t *obj = (belle_sip_file_body_handler_t *)base; if (obj->user_bh && obj->user_bh->stop_cb) { obj->user_bh->stop_cb((belle_sip_user_body_handler_t*)&(obj->user_bh->base), obj->user_bh->base.user_data); } if (obj->file) { ssize_t ret; ret = bctbx_file_close(obj->file); if (ret == BCTBX_VFS_ERROR) { bctbx_error("Can't close file %s", obj->filepath); } obj->file = NULL; } } static void belle_sip_file_body_handler_recv_chunk(belle_sip_body_handler_t *base, belle_sip_message_t *msg, off_t offset, uint8_t *buf, size_t size) { belle_sip_file_body_handler_t *obj = (belle_sip_file_body_handler_t *)base; ssize_t ret; if (obj->file == NULL) return; if (obj->user_bh && obj->user_bh->recv_cb) { obj->user_bh->recv_cb((belle_sip_user_body_handler_t*)&(obj->user_bh->base), msg, obj->user_bh->base.user_data, offset, buf, size); } ret = bctbx_file_write(obj->file, buf, size, offset); if (ret == BCTBX_VFS_ERROR) { bctbx_error("File body handler recv write error at offset %lu", (unsigned long)offset); } } static int belle_sip_file_body_handler_send_chunk(belle_sip_body_handler_t *base, belle_sip_message_t *msg, off_t offset, uint8_t *buf, size_t *size) { belle_sip_file_body_handler_t *obj = (belle_sip_file_body_handler_t *)base; ssize_t size_t_ret; size_t to_send = MIN(*size, obj->base.expected_size - offset); if (obj->file == NULL) return BELLE_SIP_STOP; size_t_ret = bctbx_file_read(obj->file, buf, to_send, offset); if (size_t_ret == BCTBX_VFS_ERROR) { bctbx_error("File body handler send read error at offset %lu", (unsigned long)offset); return BELLE_SIP_STOP; } *size = (size_t)size_t_ret; if (obj->user_bh && obj->user_bh->send_cb) { int result = obj->user_bh->send_cb((belle_sip_user_body_handler_t*)&(obj->user_bh->base), msg, obj->user_bh->base.user_data, offset, buf, size); if (result == BELLE_SIP_STOP) return result; } return (((obj->base.expected_size - offset) == (size_t)size_t_ret) || (*size == 0)) ? BELLE_SIP_STOP : BELLE_SIP_CONTINUE; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_file_body_handler_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_file_body_handler_t) { { BELLE_SIP_VPTR_INIT(belle_sip_file_body_handler_t,belle_sip_body_handler_t,TRUE), (belle_sip_object_destroy_t) belle_sip_file_body_handler_destroy, (belle_sip_object_clone_t)belle_sip_file_body_handler_clone, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, belle_sip_file_body_handler_begin_recv_transfer, belle_sip_file_body_handler_begin_send_transfer, belle_sip_file_body_handler_end_transfer, belle_sip_file_body_handler_recv_chunk, belle_sip_file_body_handler_send_chunk } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END belle_sip_file_body_handler_t *belle_sip_file_body_handler_new(const char *filepath, belle_sip_body_handler_progress_callback_t progress_cb, void *data) { struct stat statbuf; belle_sip_file_body_handler_t *obj = belle_sip_object_new(belle_sip_file_body_handler_t); belle_sip_body_handler_init((belle_sip_body_handler_t*)obj, progress_cb, data); obj->filepath = belle_sip_strdup(filepath); obj->user_bh = NULL; if (stat(obj->filepath, &statbuf) == 0) { obj->base.expected_size = statbuf.st_size; } return obj; } size_t belle_sip_file_body_handler_get_file_size(belle_sip_file_body_handler_t *file_bh) { return file_bh->base.expected_size; } void belle_sip_file_body_handler_set_user_body_handler(belle_sip_file_body_handler_t *file_bh, belle_sip_user_body_handler_t *user_bh) { if (file_bh) { file_bh->user_bh = user_bh; belle_sip_object_ref(file_bh->user_bh); } } /* * Multipart body handler implementation * TODO **/ struct belle_sip_multipart_body_handler{ belle_sip_body_handler_t base; belle_sip_list_t *parts; belle_sip_list_t *transfer_current_part; char *boundary; uint8_t *buffer; unsigned int related; }; GET_SET_BOOL(belle_sip_multipart_body_handler,related,is); static void belle_sip_multipart_body_handler_destroy(belle_sip_multipart_body_handler_t *obj){ belle_sip_list_free_with_data(obj->parts,belle_sip_object_unref); if (obj->buffer != NULL) belle_sip_free(obj->buffer); if (obj->boundary != NULL) belle_sip_free(obj->boundary); } static void belle_sip_multipart_body_handler_clone(belle_sip_multipart_body_handler_t *obj){ obj->parts=belle_sip_list_copy_with_data(obj->parts,(void *(*)(void*))belle_sip_object_clone_and_ref); } static void belle_sip_multipart_body_handler_begin_recv_transfer(belle_sip_body_handler_t *obj) { const belle_sip_list_t *it; belle_sip_multipart_body_handler_t *obj_multipart = (belle_sip_multipart_body_handler_t *)obj; for (it = obj_multipart->parts; it != NULL; it = it->next) { belle_sip_body_handler_t *bh = BELLE_SIP_BODY_HANDLER(it->data); belle_sip_body_handler_begin_recv_transfer(bh); } obj_multipart->transfer_current_part = obj_multipart->parts; } static void belle_sip_multipart_body_handler_begin_send_transfer(belle_sip_body_handler_t *obj) { const belle_sip_list_t *it; belle_sip_multipart_body_handler_t *obj_multipart = (belle_sip_multipart_body_handler_t *)obj; for (it = obj_multipart->parts; it != NULL; it = it->next) { belle_sip_body_handler_t *bh = BELLE_SIP_BODY_HANDLER(it->data); belle_sip_body_handler_begin_send_transfer(bh); } obj_multipart->transfer_current_part = obj_multipart->parts; } static void belle_sip_multipart_body_handler_end_transfer(belle_sip_body_handler_t *obj) { const belle_sip_list_t *it; belle_sip_multipart_body_handler_t *obj_multipart = (belle_sip_multipart_body_handler_t *)obj; for (it = obj_multipart->parts; it != NULL; it = it->next) { belle_sip_body_handler_t *bh = BELLE_SIP_BODY_HANDLER(it->data); belle_sip_body_handler_end_transfer(bh); } obj_multipart->transfer_current_part = NULL; } static void belle_sip_multipart_body_handler_recv_chunk(belle_sip_body_handler_t *obj, belle_sip_message_t *msg, off_t offset, uint8_t *buffer, size_t size){ /* Store the whole buffer, the parts will be split when belle_sip_multipart_body_handler_progress_cb() is called with transfered size equal to expected size. */ belle_sip_multipart_body_handler_t *obj_multipart = (belle_sip_multipart_body_handler_t *)obj; obj_multipart->buffer = belle_sip_realloc(obj_multipart->buffer,offset + size + 1); memcpy(obj_multipart->buffer + offset, buffer, size); obj_multipart->buffer[offset + size] = '\0'; } static int belle_sip_multipart_body_handler_send_chunk(belle_sip_body_handler_t *obj, belle_sip_message_t *msg, off_t offset, uint8_t *buffer, size_t *size){ belle_sip_multipart_body_handler_t *obj_multipart=(belle_sip_multipart_body_handler_t*)obj; if (obj_multipart->transfer_current_part->data) { /* we have a part, get its content from handler */ int retval = BELLE_SIP_STOP; size_t offsetSize = 0; /* used to store size of data added by this function and not given by the body handler of current part */ size_t boundary_len = strlen(obj_multipart->boundary); belle_sip_body_handler_t *current_part = (belle_sip_body_handler_t *)obj_multipart->transfer_current_part->data; *size -= strlen(obj_multipart->boundary) + 8; /* just in case it will be the end of the message, ask for less characters than possible in order to be able to add the multipart message termination. 8 is for "\r\n--" and "--\r\n" */ if (current_part->transfered_size == 0) { /* Nothing transfered yet on this part, include a separator and the header if exists */ size_t headersSize = 0; offsetSize = strlen(obj_multipart->boundary) + 4; /* 4 is for "--" and "\r\n" */ if (current_part->headerStringBuffer != NULL) { headersSize = strlen(current_part->headerStringBuffer); } /* check if buffer is large enough to get the whole header + separtor and at least a byte of data */ if (*size < headersSize+offsetSize+1) { return BELLE_SIP_BUFFER_OVERFLOW; } /* insert separator */ memcpy(buffer, "--", 2); memcpy(buffer + 2, obj_multipart->boundary, boundary_len); memcpy(buffer + 2 + boundary_len, "\r\n", 2); offsetSize = boundary_len + 4; /* insert part header */ if (headersSize!=0) { memcpy(buffer+offsetSize, current_part->headerStringBuffer, headersSize); offsetSize += headersSize; } *size -=offsetSize; /* decrease data length requested to the current part handler */ } retval = belle_sip_body_handler_send_chunk(current_part, msg, buffer+offsetSize, size); /* add offsetSize to the buffer address in order to point at the begining of free space (after header if included) */ *size +=offsetSize; /* restore total of data given including potential separator and header */ if (retval == BELLE_SIP_CONTINUE) { return BELLE_SIP_CONTINUE; /* there is still data to be sent, continue */ } else { /* this part has reach the end, pass to next one if there is one */ if (obj_multipart->transfer_current_part->next!=NULL) { /* there is an other part to be sent */ obj_multipart->transfer_current_part = belle_sip_list_next(obj_multipart->transfer_current_part); return BELLE_SIP_CONTINUE; } else { /* there is nothing else, close the message and return STOP */ size_t boundary_len = strlen(obj_multipart->boundary); memcpy(buffer + *size, "\r\n--", 4); memcpy(buffer + *size + 4, obj_multipart->boundary, boundary_len); memcpy(buffer + *size + 4 + boundary_len, "--\r\n", 4); *size+=boundary_len + 8; return BELLE_SIP_STOP; } } } return BELLE_SIP_STOP; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_multipart_body_handler_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_multipart_body_handler_t) { { BELLE_SIP_VPTR_INIT(belle_sip_multipart_body_handler_t,belle_sip_body_handler_t,TRUE), (belle_sip_object_destroy_t) belle_sip_multipart_body_handler_destroy, (belle_sip_object_clone_t)belle_sip_multipart_body_handler_clone, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, belle_sip_multipart_body_handler_begin_recv_transfer, belle_sip_multipart_body_handler_begin_send_transfer, belle_sip_multipart_body_handler_end_transfer, belle_sip_multipart_body_handler_recv_chunk, belle_sip_multipart_body_handler_send_chunk } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END static void belle_sip_multipart_body_handler_set_boundary(belle_sip_multipart_body_handler_t *obj, const char *boundary) { if (obj->boundary != NULL) { belle_sip_free(obj->boundary); } if (boundary != NULL) { obj->boundary = belle_sip_strdup(boundary); } else { obj->boundary = belle_sip_strdup(BELLESIP_MULTIPART_BOUNDARY); } } belle_sip_multipart_body_handler_t *belle_sip_multipart_body_handler_new(belle_sip_body_handler_progress_callback_t progress_cb, void *data, belle_sip_body_handler_t *first_part, const char *boundary){ belle_sip_multipart_body_handler_t *obj=belle_sip_object_new(belle_sip_multipart_body_handler_t); belle_sip_body_handler_init((belle_sip_body_handler_t*)obj,progress_cb,data); belle_sip_multipart_body_handler_set_boundary(obj, boundary); obj->base.expected_size = strlen(obj->boundary) + 8; /* body's length will be part length(including boundary) + multipart end. 8 is for "\r\n--" and "--\r\n" */ if (first_part) belle_sip_multipart_body_handler_add_part(obj,first_part); return obj; } belle_sip_multipart_body_handler_t *belle_sip_multipart_body_handler_new_from_buffer(void *buffer, size_t bufsize, const char *boundary) { belle_sip_multipart_body_handler_t *obj_multipart = belle_sip_object_new(belle_sip_multipart_body_handler_t); belle_sip_body_handler_t *obj = (belle_sip_body_handler_t *)obj_multipart; belle_sip_body_handler_init((belle_sip_body_handler_t *)obj, belle_sip_multipart_body_handler_progress_cb, NULL); belle_sip_multipart_body_handler_set_boundary(obj_multipart, boundary); obj_multipart->base.expected_size = bufsize; belle_sip_body_handler_begin_recv_transfer(obj); belle_sip_body_handler_recv_chunk(obj, NULL, (uint8_t *)buffer, bufsize); belle_sip_body_handler_end_transfer(obj); return obj_multipart; } #define DEFAULT_HEADER_STRING_SIZE 512 void belle_sip_multipart_body_handler_add_part(belle_sip_multipart_body_handler_t *obj, belle_sip_body_handler_t *part){ obj->base.expected_size+=part->expected_size+strlen(obj->boundary) + 4; /* add the separator length to the body length as each part start with a separator. 4 is for "--" and "\r\n" */ if (part->headers != NULL) { /* there is a declared header for this part, add its length to the expected total length */ size_t headerStringBufferSize = DEFAULT_HEADER_STRING_SIZE; size_t offset = 0; belle_sip_list_t *headerList = part->headers; part->headerStringBuffer = (char *)belle_sip_malloc(DEFAULT_HEADER_STRING_SIZE); while (headerList != NULL) { size_t offsetBackup=offset; /* we must backup the offset as it will be messed up by the marshal function in case of failure */ belle_sip_error_code returnCode = belle_sip_object_marshal(headerList->data, part->headerStringBuffer, headerStringBufferSize-5, &offset); /* -5 to leave room for carriage returns */ if (returnCode == BELLE_SIP_BUFFER_OVERFLOW) { /* increase buffer size */ offset=offsetBackup; /* restore the offset, no data were written to the buffer */ headerStringBufferSize+=DEFAULT_HEADER_STRING_SIZE; part->headerStringBuffer = (char *)belle_sip_realloc(part->headerStringBuffer, headerStringBufferSize); } else if (returnCode == BELLE_SIP_OK) { /* add the carriage return chars */ part->headerStringBuffer[offset++]='\r'; part->headerStringBuffer[offset++]='\n'; headerList = belle_sip_list_next(headerList); } } part->headerStringBuffer[offset++]='\r'; part->headerStringBuffer[offset++]='\n'; obj->base.expected_size += offset; part->headerStringBuffer[offset++]='\0'; /* null terminate the buffer in order to be able to get it length later using strlen */ } obj->parts=belle_sip_list_append(obj->parts,belle_sip_object_ref(part)); } const belle_sip_list_t* belle_sip_multipart_body_handler_get_parts(const belle_sip_multipart_body_handler_t *obj) { return obj->parts; } void belle_sip_multipart_body_handler_progress_cb(belle_sip_body_handler_t *obj, belle_sip_message_t *msg, void *user_data, size_t transfered, size_t expected_total) { if (transfered == expected_total) { /* The full multipart body has been received, we can now parse it and split the different parts, * creating a belle_sip_memory_body_handler for each part and adding them to the belle_sip_multipart_body_handler * parts list. */ belle_sip_multipart_body_handler_t *obj_multipart = (belle_sip_multipart_body_handler_t *)obj; belle_sip_memory_body_handler_t *memorypart; belle_sip_header_t *header; uint8_t *end_part_cursor; uint8_t *end_headers_cursor; uint8_t *end_header_cursor; uint8_t *cursor = obj_multipart->buffer; char *boundary = belle_sip_strdup_printf("--%s", obj_multipart->boundary); if (strncmp((char *)cursor, boundary, strlen(boundary))) { belle_sip_warning("belle_sip_multipart_body_handler [%p]: body not starting by specified boundary '%s'", obj_multipart, obj_multipart->boundary); belle_sip_free(boundary); return; } cursor += strlen(boundary); do { if (strncmp((char *)cursor, "\r\n", 2)) { belle_sip_warning("belle_sip_multipart_body_handler [%p]: no new-line after boundary", obj_multipart); return; } cursor += 2; end_part_cursor = (uint8_t *)strstr((char *)cursor, boundary); if (end_part_cursor == NULL) { belle_sip_warning("belle_sip_multipart_body_handler [%p]: cannot find next boundary", obj_multipart); return; } else { *end_part_cursor = 0; end_headers_cursor = (uint8_t *)strstr((char *)cursor, "\r\n\r\n"); if (end_headers_cursor == NULL) { memorypart = belle_sip_memory_body_handler_new_copy_from_buffer(cursor, strlen((char *)cursor), NULL, NULL); } else { uint8_t *begin_body_cursor = end_headers_cursor + 4; memorypart = belle_sip_memory_body_handler_new_copy_from_buffer(begin_body_cursor, strlen((char *)begin_body_cursor), NULL, NULL); do { end_header_cursor = (uint8_t *)strstr((char *)cursor, "\r\n"); *end_header_cursor = 0; header = belle_sip_header_parse((char *)cursor); if (header != NULL) { belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(memorypart), header); } cursor = end_header_cursor + 2; } while (end_header_cursor != end_headers_cursor); } belle_sip_multipart_body_handler_add_part(obj_multipart, BELLE_SIP_BODY_HANDLER(memorypart)); cursor = end_part_cursor + strlen(boundary); } } while (strcmp((char *)cursor, "--\r\n")); belle_sip_free(boundary); } } belle-sip-1.6.3/src/channel.c000066400000000000000000001664651313437522400157660ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #include #include #include #ifdef __ANDROID__ #include "wakelock_internal.h" #endif #define BELLE_SIP_CHANNEL_INVOKE_MESSAGE_HEADERS_LISTENERS(channel,msg) \ BELLE_SIP_INVOKE_LISTENERS_ARG1_ARG2(channel->full_listeners, belle_sip_channel_listener_t, on_message_headers, channel, msg) #define BELLE_SIP_CHANNEL_INVOKE_SENDING_LISTENERS(channel,msg) \ BELLE_SIP_INVOKE_LISTENERS_ARG1_ARG2(channel->full_listeners, belle_sip_channel_listener_t, on_sending, channel, msg) #define BELLE_SIP_CHANNEL_INVOKE_STATE_LISTENERS(channel,state) \ BELLE_SIP_INVOKE_LISTENERS_REVERSE_ARG1_ARG2(channel->full_listeners, belle_sip_channel_listener_t, on_state_changed, channel, state) \ BELLE_SIP_INVOKE_LISTENERS_REVERSE_ARG1_ARG2(channel->state_listeners, belle_sip_channel_listener_t, on_state_changed, channel, state) static void channel_prepare_continue(belle_sip_channel_t *obj); static void channel_process_queue(belle_sip_channel_t *obj); static void channel_begin_send_background_task(belle_sip_channel_t *obj); static void channel_end_send_background_task(belle_sip_channel_t *obj); static void channel_begin_recv_background_task(belle_sip_channel_t *obj); static void channel_end_recv_background_task(belle_sip_channel_t *obj); static void channel_process_queue(belle_sip_channel_t *obj); static char *make_logbuf(belle_sip_channel_t *obj, belle_sip_log_level level, const char *buffer, size_t size); static void channel_remove_listener(belle_sip_channel_t *obj, belle_sip_channel_listener_t *l); static void free_ewouldblock_buffer(belle_sip_channel_t *obj); const char *belle_sip_channel_state_to_string(belle_sip_channel_state_t state){ switch(state){ case BELLE_SIP_CHANNEL_INIT: return "INIT"; case BELLE_SIP_CHANNEL_RES_IN_PROGRESS: return "RES_IN_PROGRESS"; case BELLE_SIP_CHANNEL_RES_DONE: return "RES_DONE"; case BELLE_SIP_CHANNEL_CONNECTING: return "CONNECTING"; case BELLE_SIP_CHANNEL_RETRY: return "RETRY"; case BELLE_SIP_CHANNEL_READY: return "READY"; case BELLE_SIP_CHANNEL_ERROR: return "ERROR"; case BELLE_SIP_CHANNEL_DISCONNECTED: return "DISCONNECTED"; } return "BAD"; } static belle_sip_list_t * for_each_weak_unref_free(belle_sip_list_t *l, belle_sip_object_destroy_notify_t notify, void *ptr){ belle_sip_list_t *elem,*next; for(elem=l;elem!=NULL;elem=next){ next=elem->next; belle_sip_object_weak_unref(elem->data,notify,ptr); belle_sip_free(elem); } return NULL; } static void belle_sip_channel_input_stream_rewind(belle_sip_channel_input_stream_t* input_stream){ int remaining; remaining=(int)(input_stream->write_ptr-input_stream->read_ptr); if (remaining>0){ /* copy remaning bytes at top of buffer*/ memmove(input_stream->buff,input_stream->read_ptr,remaining); input_stream->read_ptr=input_stream->buff; input_stream->write_ptr=input_stream->buff+remaining; *input_stream->write_ptr='\0'; }else{ input_stream->read_ptr=input_stream->write_ptr=input_stream->buff; } } static void belle_sip_channel_input_stream_reset(belle_sip_channel_input_stream_t* input_stream) { belle_sip_channel_input_stream_rewind(input_stream); input_stream->state=WAITING_MESSAGE_START; if (input_stream->msg != NULL) belle_sip_object_unref(input_stream->msg); input_stream->msg=NULL; input_stream->chuncked_mode=FALSE; input_stream->content_length=-1; } static size_t belle_sip_channel_input_stream_get_buff_length(belle_sip_channel_input_stream_t* input_stream) { return sizeof(input_stream->buff) - (input_stream->write_ptr-input_stream->buff); } static void belle_sip_channel_destroy(belle_sip_channel_t *obj){ belle_sip_channel_input_stream_reset(&obj->input_stream); if (obj->peer_list) bctbx_freeaddrinfo(obj->peer_list); if (obj->peer_cname) belle_sip_free(obj->peer_cname); belle_sip_free(obj->peer_name); if (obj->local_ip) belle_sip_free(obj->local_ip); obj->state_listeners=for_each_weak_unref_free(obj->state_listeners,(belle_sip_object_destroy_notify_t)channel_remove_listener,obj); obj->full_listeners=for_each_weak_unref_free(obj->full_listeners,(belle_sip_object_destroy_notify_t)channel_remove_listener,obj); if (obj->resolver_ctx != NULL) { belle_sip_resolver_context_cancel(obj->resolver_ctx); belle_sip_object_unref(obj->resolver_ctx); } if (obj->inactivity_timer){ belle_sip_main_loop_remove_source(obj->stack->ml,obj->inactivity_timer); belle_sip_object_unref(obj->inactivity_timer); } if (obj->dns_ttl_timer) { belle_sip_main_loop_remove_source(obj->stack->ml, obj->dns_ttl_timer); belle_sip_object_unref(obj->dns_ttl_timer); } if (obj->public_ip) belle_sip_free(obj->public_ip); if (obj->outgoing_messages) belle_sip_list_free_with_data(obj->outgoing_messages,belle_sip_object_unref); if (obj->incoming_messages) belle_sip_list_free_with_data(obj->incoming_messages,belle_sip_object_unref); free_ewouldblock_buffer(obj); if (obj->cur_out_message){ belle_sip_object_unref(obj->cur_out_message); obj->cur_out_message=NULL; } channel_end_send_background_task(obj); channel_end_recv_background_task(obj); /*normally this should do nothing because it sould have been terminated already, however leaving a background task open is so dangerous that we have to be paranoid*/ belle_sip_message("Channel [%p] destroyed",obj); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_channel_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_channel_t) { BELLE_SIP_VPTR_INIT(belle_sip_channel_t,belle_sip_source_t,FALSE), (belle_sip_object_destroy_t)belle_sip_channel_destroy, NULL, /*clone*/ NULL, /*marshal*/ BELLE_SIP_DEFAULT_BUFSIZE_HINT }, NULL, /* transport */ 0, /* reliable */ NULL, /* connect */ NULL, /* channel_send */ NULL, /* channel_recv */ NULL /* close */ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END static void fix_incoming_via(belle_sip_request_t *msg, const struct addrinfo* origin){ char received[NI_MAXHOST]; char rport[NI_MAXSERV]; belle_sip_header_via_t *via; int err; struct sockaddr_storage saddr; socklen_t slen=sizeof(saddr); if (!origin) { belle_sip_warning("cannot fix via for message [%p], probably a test",msg); return; } bctbx_sockaddr_remove_v4_mapping(origin->ai_addr, (struct sockaddr*)&saddr, &slen); err=bctbx_getnameinfo((struct sockaddr*)&saddr,slen,received,sizeof(received), rport,sizeof(rport),NI_NUMERICHOST|NI_NUMERICSERV); if (err!=0){ belle_sip_error("fix_via: getnameinfo() failed: %s",gai_strerror(errno)); return; } via=BELLE_SIP_HEADER_VIA(belle_sip_message_get_header((belle_sip_message_t*)msg,"via")); if (via){ const char* host = belle_sip_header_via_get_host(via); if (strcmp(host,received)!=0) belle_sip_header_via_set_received(via,received); if (belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(via),"rport")){ int port = belle_sip_header_via_get_listening_port(via); int rport_int=atoi(rport); if (rport_int!=port) belle_sip_header_via_set_rport(via,atoi(rport)); } } } /*token = 1*(alphanum / "-" / "." / "!" / "%" / "*" / "_" / "+" / "`" / "'" / "~" ) * * */ static int is_token(const char* buff,size_t bufflen ) { size_t i; for (i=0; i='0' && buff[i]<='9') || (buff[i]>='A' && buff[i]<='Z') || (buff[i]>='a' && buff[i]<='z') || (buff[i]=='\0')) continue; else return 0; } } return 1; } static int get_message_start_pos(char *buff, size_t bufflen) { /*FIXME still to optimize and better test, specially REQUEST PATH and error path*/ int i; int res=0; int status_code; char method[17]={0}; char saved_char1; char sip_version[10]={0}; size_t saved_char1_index; for(i=0; i<(int)bufflen-12;i++) { /*9=strlen( SIP/2.0\r\n)*/ switch (buff[i]) { /*to avoid this character to be ignored by scanf*/ case '\r': case '\n': case ' ' : case '\t': continue; default: break; } saved_char1_index=bufflen-1; saved_char1=buff[saved_char1_index]; /*make sure buff is null terminated*/ buff[saved_char1_index]='\0'; res=sscanf(buff+i,"SIP/2.0 %d ",&status_code); if (res!=1) res=sscanf(buff+i,"HTTP/1.%*i %d ",&status_code); /*might be HTTP ?*/ if (res!=1) { res= sscanf(buff+i,"%16s %*s %9s\r\n",method,sip_version)==2 && is_token(method,sizeof(method)) && (strcmp("SIP/2.0",sip_version)==0 || strncmp("HTTP/1.",sip_version,strlen("HTTP/1."))==0); } buff[saved_char1_index]=saved_char1; if (res==1) return i; } return -1; } void belle_sip_channel_set_public_ip_port(belle_sip_channel_t *obj, const char *public_ip, int port){ if (obj->public_ip){ int ip_changed=0; int port_changed=0; if (public_ip && strcmp(obj->public_ip,public_ip)!=0){ ip_changed=1; } if (port!=obj->public_port){ port_changed=1; } if (ip_changed || port_changed){ belle_sip_warning("channel [%p]: public ip is changed from [%s:%i] to [%s:%i]",obj,obj->public_ip,obj->public_port,public_ip,port); } belle_sip_free(obj->public_ip); obj->public_ip=NULL; }else if (public_ip){ belle_sip_message("channel [%p]: discovered public ip and port are [%s:%i]",obj,public_ip,port); } if (public_ip){ obj->public_ip=belle_sip_strdup(public_ip); } obj->public_port=port; } static void belle_sip_channel_learn_public_ip_port(belle_sip_channel_t *obj, belle_sip_response_t *resp){ belle_sip_header_via_t *via=belle_sip_message_get_header_by_type(resp,belle_sip_header_via_t); const char *received; int rport; if (!via){ belle_sip_error("channel [%p]: no via in response.",obj); return; } if (!(received=belle_sip_header_via_get_received(via))) { /*use address from via*/; received=belle_sip_header_via_get_host(via); } rport=belle_sip_header_via_get_rport(via); if (rport<=0){ /* no rport, the via port might be good then*/ rport=belle_sip_header_via_get_listening_port(via); } belle_sip_channel_set_public_ip_port(obj,received,rport); obj->learnt_ip_port=TRUE; } static void uncompress_body_if_required(belle_sip_message_t *msg) { belle_sip_body_handler_t *bh = belle_sip_message_get_body_handler(msg); belle_sip_memory_body_handler_t *mbh = NULL; belle_sip_header_t *ceh = NULL; size_t body_len = 0; if (bh != NULL) { body_len = belle_sip_message_get_body_size(msg); ceh = belle_sip_message_get_header(msg, "Content-Encoding"); } if ((body_len > 0) && (ceh != NULL)) { const char *content_encoding = belle_sip_header_get_unparsed_value(ceh); if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(bh, belle_sip_memory_body_handler_t)) { mbh = BELLE_SIP_MEMORY_BODY_HANDLER(bh); if (belle_sip_memory_body_handler_unapply_encoding(mbh, content_encoding) == 0) { belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_type_t); belle_sip_message_remove_header_from_ptr(msg, ceh); if (content_type && (strcmp(belle_sip_header_content_type_get_type(content_type), "multipart") == 0)) { const char *unparsed_value = belle_sip_header_get_unparsed_value(BELLE_SIP_HEADER(content_type)); const char *boundary = strstr(unparsed_value, ";boundary="); if (boundary != NULL) boundary += 10; if (boundary[0] == '\0') boundary = NULL; bh = (belle_sip_body_handler_t *)belle_sip_multipart_body_handler_new_from_buffer( belle_sip_memory_body_handler_get_buffer(mbh), belle_sip_body_handler_get_size((belle_sip_body_handler_t *)mbh), boundary); belle_sip_message_set_body_handler(msg, bh); } } } else { belle_sip_warning("message [%p] has Content-Encoding [%s] that cannot be unapplied", msg, content_encoding); } } } static void belle_sip_channel_message_ready(belle_sip_channel_t *obj){ belle_sip_message_t *msg=obj->input_stream.msg; belle_sip_body_handler_t *bh=belle_sip_message_get_body_handler(msg); if (bh) belle_sip_body_handler_end_transfer(bh); if (belle_sip_message_is_response(msg)) belle_sip_channel_learn_public_ip_port(obj,BELLE_SIP_RESPONSE(msg)); uncompress_body_if_required(msg); obj->incoming_messages=belle_sip_list_append(obj->incoming_messages,belle_sip_object_ref(msg)); obj->stop_logging_buffer=0; belle_sip_channel_input_stream_reset(&obj->input_stream); } static void feed_body(belle_sip_channel_t *obj, size_t len){ belle_sip_message_t *msg=obj->input_stream.msg; belle_sip_body_handler_t *bh=belle_sip_message_get_body_handler(msg); belle_sip_body_handler_recv_chunk(bh,msg,(uint8_t*)obj->input_stream.read_ptr,len); obj->input_stream.read_ptr+=len; belle_sip_channel_input_stream_rewind(&obj->input_stream); } /*returns TRUE if a body is expected, and initialize a few things in the input stream context*/ static int check_body(belle_sip_channel_t *obj){ belle_sip_message_t *msg=obj->input_stream.msg; belle_sip_header_content_length_t* content_length_header = belle_sip_message_get_header_by_type(msg,belle_sip_header_content_length_t); int expect_body=FALSE; obj->input_stream.content_length= content_length_header ? belle_sip_header_content_length_get_content_length(content_length_header) : 0; expect_body=obj->input_stream.content_length>0; if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(msg,belle_http_response_t) || BELLE_SIP_OBJECT_IS_INSTANCE_OF(msg,belle_http_request_t)){ /*http chunked mode handling*/ if (belle_sip_message_get_header_by_type(msg, belle_sip_header_content_type_t)!=NULL){ belle_sip_header_t *transfer_encoding=belle_sip_message_get_header(msg,"Transfer-Encoding"); if (transfer_encoding){ const char *value=belle_sip_header_get_unparsed_value(transfer_encoding); if (strstr(value,"chunked")!=0){ obj->input_stream.chuncked_mode=1; obj->input_stream.content_length=0; obj->input_stream.chunk_size=-1; obj->input_stream.chunk_read_size=0; } } expect_body=TRUE; } } if (expect_body){ belle_sip_body_handler_t *bh; /*should notify the listeners*/ BELLE_SIP_CHANNEL_INVOKE_MESSAGE_HEADERS_LISTENERS(obj,msg); /*check if the listener has setup a body handler, otherwise create a default one*/ if ((bh=belle_sip_message_get_body_handler(msg))==NULL){ belle_sip_header_t *content_encoding = belle_sip_message_get_header(msg, "Content-Encoding"); belle_sip_header_content_type_t *content_type = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_type_t); if (content_encoding != NULL) { belle_sip_message_set_body_handler(msg, (bh = (belle_sip_body_handler_t *)belle_sip_memory_body_handler_new(NULL, NULL))); } else if (content_type && (strcmp(belle_sip_header_content_type_get_type(content_type), "multipart") == 0)) { const char *unparsed_value = belle_sip_header_get_unparsed_value(BELLE_SIP_HEADER(content_type)); const char *boundary = strstr(unparsed_value, ";boundary="); if (boundary != NULL) boundary += 10; if (boundary[0] == '\0') boundary = NULL; belle_sip_message_set_body_handler(msg, (bh = (belle_sip_body_handler_t *)belle_sip_multipart_body_handler_new(belle_sip_multipart_body_handler_progress_cb, NULL, NULL, boundary))); belle_sip_body_handler_set_size(bh, obj->input_stream.content_length); } else { belle_sip_message_set_body_handler(msg,(bh=(belle_sip_body_handler_t*)belle_sip_memory_body_handler_new(NULL,NULL))); } } belle_sip_body_handler_begin_recv_transfer(bh); } return expect_body; } static int acquire_body_simple(belle_sip_channel_t *obj, int end_of_stream){ size_t content_length=obj->input_stream.content_length; size_t to_read=obj->input_stream.write_ptr-obj->input_stream.read_ptr; belle_sip_message_t *msg=obj->input_stream.msg; belle_sip_body_handler_t *bh=belle_sip_message_get_body_handler(msg); size_t cursize=belle_sip_body_handler_get_transfered_size(bh); if ((cursize == 0) && (to_read == 0)) { /** * No data has been received yet, so do not call feed_body() with a size * of 0 that is meaning that the transfer is finished. */ } else { to_read=MIN(content_length-cursize, to_read); feed_body(obj,to_read); } if (end_of_stream || belle_sip_body_handler_get_transfered_size(bh)>=content_length){ /*great body completed*/ belle_sip_message("channel [%p] read [%i] bytes of body from [%s:%i]" ,obj ,(int)content_length ,obj->peer_name ,obj->peer_port); belle_sip_channel_message_ready(obj); return BELLE_SIP_CONTINUE; } /*body is not finished, we need more data*/ return BELLE_SIP_STOP; } static int acquire_chuncked_body(belle_sip_channel_t *obj){ belle_sip_channel_input_stream_t *st=&obj->input_stream; int readsize; do{ if (st->chunk_size==-1){ char *tmp; /*belle_sip_message("seeing: %s",st->read_ptr);*/ while ( (tmp=strstr(st->read_ptr,"\r\n"))==st->read_ptr){/*skip \r\n*/ st->read_ptr+=2; } if (tmp!=NULL){ /*the chunk length is there*/ long chunksize=strtol(st->read_ptr,NULL,16); if (chunksize>=0 && chunksize!=LONG_MAX){ if (chunksize==0){ belle_sip_message("Got end of chunked body"); st->read_ptr=tmp+4; /*last chunk indicator finishes with two \r\n*/ if (st->read_ptr>st->write_ptr) st->read_ptr=st->write_ptr; belle_sip_channel_message_ready(obj); return BELLE_SIP_CONTINUE; }else{ belle_sip_message("Will get a chunk of %i bytes",(int)chunksize); st->chunk_size=chunksize; st->chunk_read_size=0; st->read_ptr=tmp+2; } }else{ belle_sip_error("Chunk parse error"); belle_sip_channel_input_stream_reset(st); return BELLE_SIP_CONTINUE; } }else{ /*need more data*/ return BELLE_SIP_STOP; } } readsize=MIN((int)(st->write_ptr-st->read_ptr),st->chunk_size-st->chunk_read_size); if (readsize>0){ feed_body(obj,readsize); st->chunk_read_size+=readsize; } if (st->chunk_size==st->chunk_read_size){ /*we have a chunk completed*/ st->content_length+=st->chunk_size; belle_sip_message("Chunk of [%i] bytes completed",st->chunk_size); st->chunk_size=-1;/*wait for next chunk indicator*/ }else{ /*need more data*/ return BELLE_SIP_STOP; } }while(st->write_ptr-st->read_ptr>0); /*no need to continue if nothing to read*/ return BELLE_SIP_STOP; } static int acquire_body(belle_sip_channel_t *obj, int end_of_stream){ if (obj->input_stream.chuncked_mode) return acquire_chuncked_body(obj); else return acquire_body_simple(obj,end_of_stream); } static void notify_incoming_messages(belle_sip_channel_t *obj){ belle_sip_list_t *elem,*l_it; belle_sip_list_t *listeners=belle_sip_list_copy_with_data(obj->full_listeners,(void *(*)(void*))belle_sip_object_ref); for(l_it=listeners;l_it!=NULL;l_it=l_it->next){ belle_sip_channel_listener_t *listener=(belle_sip_channel_listener_t*)l_it->data; for(elem=obj->incoming_messages;elem!=NULL;elem=elem->next){ belle_sip_message_t *msg=(belle_sip_message_t*)elem->data; BELLE_SIP_INTERFACE_METHODS_TYPE(belle_sip_channel_listener_t) *methods; methods=BELLE_SIP_INTERFACE_GET_METHODS(listener,belle_sip_channel_listener_t); if (methods->on_message) methods->on_message(listener,obj,msg); } } belle_sip_list_free_with_data(listeners,belle_sip_object_unref); belle_sip_list_free_with_data(obj->incoming_messages,belle_sip_object_unref); obj->incoming_messages=NULL; } void belle_sip_channel_parse_stream(belle_sip_channel_t *obj, int end_of_stream){ int offset; size_t read_size=0; int num; while ((num=(int)(obj->input_stream.write_ptr-obj->input_stream.read_ptr))>0){ if (obj->input_stream.state == WAITING_MESSAGE_START) { int i; /*first, make sure there is \r\n in the buffer, otherwise, micro parser cannot conclude, because we need a complete request or response line somewhere*/ for (i=0;iinput_stream.read_ptr[i]=='\r' && obj->input_stream.read_ptr[i+1]=='\n') || belle_sip_channel_input_stream_get_buff_length(&obj->input_stream) <= 1 /*1 because null terminated*/ /*if buffer full try to parse in any case*/) { /*good, now we can start searching for request/response*/ if ((offset=get_message_start_pos(obj->input_stream.read_ptr,num)) >=0 ) { /*message found !*/ if (offset>0) { belle_sip_warning("trashing [%i] bytes in front of sip message on channel [%p]",offset,obj); obj->input_stream.read_ptr+=offset; } obj->input_stream.state=MESSAGE_AQUISITION; } else { belle_sip_debug("Unexpected [%s] received on channel [%p], trashing",obj->input_stream.read_ptr,obj); obj->input_stream.read_ptr=obj->input_stream.write_ptr; belle_sip_channel_input_stream_reset(&obj->input_stream); continue; } break; } } if (i >= num-1) { belle_sip_debug("[%s] received on channel [%p], cannot determine if expected or not, waiting for new data",obj->input_stream.read_ptr,obj); break; } } if (obj->input_stream.state==MESSAGE_AQUISITION) { /*search for \r\n\r\n*/ char* end_of_message=NULL; if ((end_of_message=strstr(obj->input_stream.read_ptr,"\r\n\r\n"))){ int bytes_to_parse; char tmp; /*end of message found*/ end_of_message+=4;/*add \r\n\r\n*/ bytes_to_parse=(int)(end_of_message-obj->input_stream.read_ptr); tmp=*end_of_message; *end_of_message='\0';/*this is in order for the following log to print the message only to its end.*/ /*belle_sip_message("channel [%p] read message of [%i] bytes:\n%.40s...",obj, bytes_to_parse, obj->input_stream.read_ptr);*/ obj->input_stream.msg=belle_sip_message_parse_raw(obj->input_stream.read_ptr ,bytes_to_parse ,&read_size); *end_of_message=tmp; obj->input_stream.read_ptr+=read_size; if (obj->input_stream.msg && read_size > 0){ belle_sip_message("channel [%p] [%i] bytes parsed",obj,(int)read_size); belle_sip_object_ref(obj->input_stream.msg); if (belle_sip_message_is_request(obj->input_stream.msg)) fix_incoming_via(BELLE_SIP_REQUEST(obj->input_stream.msg),obj->current_peer); /*check for body*/ if (check_body(obj)){ obj->input_stream.state=BODY_AQUISITION; } else { /*no body*/ belle_sip_channel_message_ready(obj); continue; } }else{ belle_sip_error("Could not parse [%s], on channel [%p] skipping to [%s]",obj->input_stream.read_ptr ,obj ,end_of_message); obj->input_stream.read_ptr=end_of_message; obj->input_stream.state=WAITING_MESSAGE_START; continue; } }else break; /*The message isn't finished to be receive, we need more data*/ } if (obj->input_stream.state==BODY_AQUISITION) { if (acquire_body(obj,end_of_stream)==BELLE_SIP_STOP) break; } } } static void belle_sip_channel_process_stream(belle_sip_channel_t *obj, int eos){ belle_sip_channel_parse_stream(obj,eos); if (obj->incoming_messages) { if (obj->simulated_recv_return == 1500) { belle_sip_list_t *elem; for(elem=obj->incoming_messages;elem!=NULL;elem=elem->next){ belle_sip_message_t *msg=(belle_sip_message_t*)elem->data; char* dump = belle_sip_message_to_string(msg); belle_sip_message("Silently discarding incoming message [%.50s...] on channel [%p]",dump, obj); belle_sip_free(dump); } belle_sip_list_free_with_data(obj->incoming_messages,belle_sip_object_unref); obj->incoming_messages=NULL; } else { notify_incoming_messages(obj); } } } static int belle_sip_channel_process_read_data(belle_sip_channel_t *obj){ int num; int ret=BELLE_SIP_CONTINUE; /*prevent system to suspend the process until we have finish reading everything from the socket and notified the upper layer*/ if (obj->input_stream.state == WAITING_MESSAGE_START) { channel_begin_recv_background_task(obj); } if (obj->simulated_recv_return>0) { num=belle_sip_channel_recv(obj,obj->input_stream.write_ptr,belle_sip_channel_input_stream_get_buff_length(&obj->input_stream)-1); } else { belle_sip_message("channel [%p]: simulating recv() returning %i",obj,obj->simulated_recv_return); num=obj->simulated_recv_return; } if (num>0){ char *begin=obj->input_stream.write_ptr; obj->input_stream.write_ptr+=num; /*first null terminate the read buff*/ *obj->input_stream.write_ptr='\0'; if (num>20 || obj->input_stream.state != WAITING_MESSAGE_START ) /*to avoid tracing server based keep alives*/ { char *logbuf = make_logbuf(obj, BELLE_SIP_LOG_MESSAGE ,begin,num); if (logbuf) { belle_sip_message("channel [%p]: received [%i] new bytes from [%s://%s:%i]:\n%s", obj, num, belle_sip_channel_get_transport_name(obj), obj->peer_name, obj->peer_port, logbuf); belle_sip_free(logbuf); } } belle_sip_channel_process_stream(obj,FALSE); if (obj->input_stream.state == WAITING_MESSAGE_START){ channel_end_recv_background_task(obj); }/*if still in message acquisition state, keep the backgroud task*/ } else if (num == 0) { /*before closing the channel, check if there was a pending message to receive, whose body acquisition is to be finished.*/ belle_sip_channel_process_stream(obj,TRUE); obj->closed_by_remote = TRUE; channel_set_state(obj,BELLE_SIP_CHANNEL_DISCONNECTED); ret=BELLE_SIP_STOP; } else if (belle_sip_error_code_is_would_block(-num)){ belle_sip_message("channel [%p]: recv() EWOULDBLOCK",obj); ret=BELLE_SIP_CONTINUE; }else{ belle_sip_error("Receive error on channel [%p]",obj); channel_set_state(obj,BELLE_SIP_CHANNEL_ERROR); ret=BELLE_SIP_STOP; } return ret; } int belle_sip_channel_process_data(belle_sip_channel_t *obj,unsigned int revents){ int ret=BELLE_SIP_CONTINUE; belle_sip_object_ref(obj); if (revents & BELLE_SIP_EVENT_READ) { int rret=belle_sip_channel_process_read_data(obj); if (rret==BELLE_SIP_STOP) ret=BELLE_SIP_STOP; } if (revents & BELLE_SIP_EVENT_WRITE){ /*if we are here, this is because we had an EWOULDBLOCK while sending a message*/ /*continue to send pending messages but before check the channel is still alive because it may have been closed by belle_sip_channel_process_read_data() above.*/ if (obj->state == BELLE_SIP_CHANNEL_READY){ channel_process_queue(obj); } } belle_sip_object_unref(obj); return ret; } static int channel_inactive_timeout(void *data, unsigned int event){ belle_sip_channel_t *obj=(belle_sip_channel_t *)data; belle_sip_message("Channel [%p]: inactivity timeout reached.",obj); channel_set_state(obj,BELLE_SIP_CHANNEL_DISCONNECTED); return BELLE_SIP_STOP; } static void update_inactivity_timer(belle_sip_channel_t *obj, int from_recv){ int inactive_timeout=belle_sip_stack_get_inactive_transport_timeout(obj->stack)*1000; if (inactive_timeout>0){ if (!obj->inactivity_timer ){ obj->inactivity_timer=belle_sip_main_loop_create_timeout(obj->stack->ml,channel_inactive_timeout,obj,inactive_timeout,"Channel inactivity timer"); }else{ /*restart the timer for new period*/ belle_sip_source_set_timeout(obj->inactivity_timer,inactive_timeout); } }else{ if (obj->inactivity_timer){ belle_sip_main_loop_remove_source(obj->stack->ml,obj->inactivity_timer); belle_sip_object_unref(obj->inactivity_timer); obj->inactivity_timer=NULL; } } if (from_recv) obj->last_recv_time=belle_sip_time_ms(); } /*constructor for channels creating an outgoing connection * bindip local ip address to bind on, typically 0.0.0.0 or ::0 * locaport locaport to use for binding, can be set to 0 if port doesn't matter * peer_cname canonical name of remote host, used for TLS verification * peername peer's hostname, either ip address or DNS name * pee_port peer's port to connect to. */ void belle_sip_channel_init(belle_sip_channel_t *obj, belle_sip_stack_t *stack,const char *bindip,int localport,const char *peer_cname, const char *peername, int peer_port){ /*to initialize our base class:*/ belle_sip_channel_set_socket(obj,-1,NULL); /*then initialize members*/ obj->ai_family=AF_INET; obj->peer_cname=peer_cname ? belle_sip_strdup(peer_cname) : NULL; obj->peer_name=belle_sip_strdup(peername); obj->peer_port=peer_port; obj->stack=stack; if (bindip){ if (strcmp(bindip,"::0")!=0 && strcmp(bindip,"0.0.0.0")!=0) obj->local_ip=belle_sip_strdup(bindip); if (strchr(bindip,':')!=NULL) obj->ai_family=AF_INET6; } obj->local_port=localport; obj->simulated_recv_return=1;/*not set*/ if (peername){ /*check if we are given a real dns name or just an ip address*/ struct addrinfo *ai=bctbx_ip_address_to_addrinfo(AF_UNSPEC,SOCK_STREAM,peername,peer_port); if (ai) bctbx_freeaddrinfo(ai); else obj->has_name=TRUE; } belle_sip_channel_input_stream_reset(&obj->input_stream); update_inactivity_timer(obj,FALSE); } /*constructor for channels created by incoming connections*/ void belle_sip_channel_init_with_addr(belle_sip_channel_t *obj, belle_sip_stack_t *stack, const char *bindip, int localport, const struct sockaddr *peer_addr, socklen_t addrlen){ char remoteip[64]; struct addrinfo ai; int peer_port; memset(&ai,0,sizeof(ai)); ai.ai_family=peer_addr->sa_family; ai.ai_addr=(struct sockaddr*)peer_addr; ai.ai_addrlen=addrlen; bctbx_addrinfo_to_ip_address(&ai,remoteip,sizeof(remoteip),&peer_port); belle_sip_channel_init(obj,stack,bindip,localport,NULL,remoteip,peer_port); obj->peer_list=obj->current_peer=bctbx_ip_address_to_addrinfo(ai.ai_family, ai.ai_socktype, obj->peer_name,obj->peer_port); obj->ai_family=ai.ai_family; } void belle_sip_channel_set_socket(belle_sip_channel_t *obj, belle_sip_socket_t sock, belle_sip_source_func_t datafunc){ belle_sip_socket_source_init((belle_sip_source_t*)obj , datafunc , obj , sock , BELLE_SIP_EVENT_READ|BELLE_SIP_EVENT_WRITE , -1); } static bool_t is_state_only_listener(const belle_sip_channel_listener_t *listener) { BELLE_SIP_INTERFACE_METHODS_TYPE(belle_sip_channel_listener_t) *methods; methods=BELLE_SIP_INTERFACE_GET_METHODS(listener,belle_sip_channel_listener_t); return methods->on_state_changed && !(methods->on_message_headers || methods->on_message || methods->on_sending || methods->on_auth_requested); } static void channel_remove_listener(belle_sip_channel_t *obj, belle_sip_channel_listener_t *l){ if (is_state_only_listener(l)) obj->state_listeners=belle_sip_list_remove(obj->state_listeners,l); else obj->full_listeners=belle_sip_list_remove(obj->full_listeners,l); } void belle_sip_channel_add_listener(belle_sip_channel_t *obj, belle_sip_channel_listener_t *l){ if (is_state_only_listener(l)) { obj->state_listeners=belle_sip_list_prepend(obj->state_listeners, belle_sip_object_weak_ref(l, (belle_sip_object_destroy_notify_t)channel_remove_listener,obj)); } else { obj->full_listeners=belle_sip_list_prepend(obj->full_listeners, belle_sip_object_weak_ref(l, (belle_sip_object_destroy_notify_t)channel_remove_listener,obj)); } } void belle_sip_channel_remove_listener(belle_sip_channel_t *obj, belle_sip_channel_listener_t *l){ belle_sip_object_weak_unref(l,(belle_sip_object_destroy_notify_t)channel_remove_listener,obj); channel_remove_listener(obj,l); } int belle_sip_channel_matches(const belle_sip_channel_t *obj, const belle_sip_hop_t *hop, const struct addrinfo *addr){ if (hop && strcmp(hop->host,obj->peer_name)==0 && (hop->port==obj->peer_port || obj->srv_overrides_port)){ if (hop->cname && obj->peer_cname && strcmp(hop->cname,obj->peer_cname)!=0) return 0; /*cname mismatch*/ return 1; } if (addr && obj->current_peer) return bctbx_sockaddr_equals(addr->ai_addr,obj->current_peer->ai_addr); return 0; } const char *belle_sip_channel_get_local_address(belle_sip_channel_t *obj, int *port){ if (port) *port=obj->local_port; return obj->local_ip; } const char *belle_sip_channel_get_public_address(belle_sip_channel_t *obj, int *port){ const char *ret = obj->public_ip ? obj->public_ip : obj->local_ip; if (*port) *port= obj->public_port; return ret; } belle_sip_uri_t *belle_sip_channel_create_routable_uri(belle_sip_channel_t *chan) { const char *transport = belle_sip_channel_get_transport_name_lower_case(chan); belle_sip_uri_t* uri = belle_sip_uri_new(); unsigned char natted = chan->public_ip && strcmp(chan->public_ip,chan->local_ip)!=0; if (natted) { belle_sip_uri_set_host(uri, chan->public_ip); belle_sip_uri_set_port(uri, chan->public_port); } else { belle_sip_uri_set_host(uri, chan->local_ip); // With streamed protocols listening port is what we want if (chan->lp) belle_sip_uri_set_port(uri, belle_sip_uri_get_port(chan->lp->listening_uri)); else belle_sip_uri_set_port(uri,chan->local_port); } belle_sip_uri_set_transport_param(uri, transport); belle_sip_uri_set_lr_param(uri, TRUE); return uri; } int belle_sip_channel_is_reliable(const belle_sip_channel_t *obj){ return BELLE_SIP_OBJECT_VPTR(obj,belle_sip_channel_t)->reliable; } const char * belle_sip_channel_get_transport_name_lower_case(const belle_sip_channel_t *obj){ const char* transport = belle_sip_channel_get_transport_name(obj); if (strcasecmp("udp",transport)==0) return "udp"; else if (strcasecmp("tcp",transport)==0) return "tcp"; else if (strcasecmp("tls",transport)==0) return "tls"; else if (strcasecmp("dtls",transport)==0) return "dtls"; else { belle_sip_message("Cannot convert [%s] to lower case",transport); return transport; } } const char * belle_sip_channel_get_transport_name(const belle_sip_channel_t *obj){ return BELLE_SIP_OBJECT_VPTR(obj,belle_sip_channel_t)->transport; } int belle_sip_channel_send(belle_sip_channel_t *obj, const void *buf, size_t buflen){ update_inactivity_timer(obj,FALSE); return BELLE_SIP_OBJECT_VPTR(obj,belle_sip_channel_t)->channel_send(obj,buf,buflen); } int belle_sip_channel_recv(belle_sip_channel_t *obj, void *buf, size_t buflen){ update_inactivity_timer(obj,TRUE); return BELLE_SIP_OBJECT_VPTR(obj,belle_sip_channel_t)->channel_recv(obj,buf,buflen); } void belle_sip_channel_close(belle_sip_channel_t *obj){ if (BELLE_SIP_OBJECT_VPTR(obj,belle_sip_channel_t)->close) BELLE_SIP_OBJECT_VPTR(obj,belle_sip_channel_t)->close(obj); /*udp channel doesn't have close function*/ /*removing the source (our base class) will decrement the ref count, this why this code needs to be protected by ref/unref.*/ belle_sip_main_loop_remove_source(obj->stack->ml,(belle_sip_source_t*)obj); belle_sip_source_uninit((belle_sip_source_t*)obj); } const struct addrinfo * belle_sip_channel_get_peer(belle_sip_channel_t *obj){ return obj->current_peer; } static void channel_on_send_background_task_ended(belle_sip_channel_t *obj){ belle_sip_warning("channel [%p]: send background task has to be ended now, but work isn't finished.",obj); channel_end_send_background_task(obj); } static void channel_begin_send_background_task(belle_sip_channel_t *obj){ if (obj->bg_task_id==0){ obj->bg_task_id=belle_sip_begin_background_task("belle-sip send channel",(void (*)(void*))channel_on_send_background_task_ended, obj); if (obj->bg_task_id) belle_sip_message("channel [%p]: starting send background task with id=[%lx].",obj,obj->bg_task_id); } } static void channel_end_send_background_task(belle_sip_channel_t *obj){ if (obj->bg_task_id){ belle_sip_message("channel [%p]: ending send background task with id=[%lx].",obj,obj->bg_task_id); belle_sip_end_background_task(obj->bg_task_id); obj->bg_task_id=0; } } static void channel_on_recv_background_task_ended(belle_sip_channel_t *obj){ belle_sip_warning("channel [%p]: recv background task has to be ended now, but work isn't finished.",obj); channel_end_recv_background_task(obj); } static void channel_begin_recv_background_task(belle_sip_channel_t *obj){ if (obj->recv_bg_task_id==0){ obj->recv_bg_task_id=belle_sip_begin_background_task("belle-sip recv channel",(void (*)(void*))channel_on_recv_background_task_ended, obj); if (obj->recv_bg_task_id) belle_sip_message("channel [%p]: starting recv background task with id=[%lx].",obj,obj->recv_bg_task_id); } } static void channel_end_recv_background_task(belle_sip_channel_t *obj){ if (obj->recv_bg_task_id){ belle_sip_message("channel [%p]: ending recv background task with id=[%lx].",obj,obj->recv_bg_task_id); belle_sip_end_background_task(obj->recv_bg_task_id); obj->recv_bg_task_id=0; } } static void channel_invoke_state_listener(belle_sip_channel_t *obj){ int close = FALSE; switch(obj->state){ case BELLE_SIP_CHANNEL_DISCONNECTED: case BELLE_SIP_CHANNEL_ERROR: /*the background tasks must be released "after" notifying the app of the disconnected or error state By "after" it is means not before the main loop iteration that will notify the app. This is the reason why these calls are done here rather than in the channel_set_state() function.*/ channel_end_send_background_task(obj); channel_end_recv_background_task(obj); close = TRUE; break; default: break; } /*Channel listeners may drop the last reference of the channel, so protect by ref/unref until we finish.*/ belle_sip_object_ref(obj); BELLE_SIP_CHANNEL_INVOKE_STATE_LISTENERS(obj,obj->state); if (close) belle_sip_channel_close(obj); belle_sip_object_unref(obj); } static void channel_notify_error_to_listeners(belle_sip_channel_t *obj){ /* The channel may have been passed to DISCONNECTED state due to _force_close() method. * Do not notify the error in this case, since the channel is already closed. */ if (obj->state == BELLE_SIP_CHANNEL_ERROR){ channel_invoke_state_listener(obj); } belle_sip_object_unref(obj); } static void channel_connect_next(belle_sip_channel_t *obj){ if (obj->state == BELLE_SIP_CHANNEL_RETRY){ belle_sip_channel_connect(obj); } belle_sip_object_unref(obj); } static void belle_sip_channel_handle_error(belle_sip_channel_t *obj){ if (obj->state!=BELLE_SIP_CHANNEL_READY || obj->soft_error){ /* Previous connection attempts were failed (channel could not get ready) OR soft error reported*/ obj->soft_error = FALSE; /* See if you can retry on an alternate ip address.*/ if (obj->current_peer && obj->current_peer->ai_next){ /*obj->current_peer may be null in case of dns error*/ obj->current_peer=obj->current_peer->ai_next; channel_set_state(obj,BELLE_SIP_CHANNEL_RETRY); belle_sip_channel_close(obj); belle_sip_main_loop_do_later(obj->stack->ml,(belle_sip_callback_t)channel_connect_next,belle_sip_object_ref(obj)); return; }/*else we have already tried all the ip addresses, so give up and notify the error*/ }/*else the channel was previously working good with the current ip address but now fails, so let's notify the error*/ obj->state=BELLE_SIP_CHANNEL_ERROR; /*Because error notification will in practice trigger the destruction of possible transactions and this channel, * it is safer to invoke the listener outside the current call stack. * Indeed the channel encounters network errors while being called for transmiting by a transaction. */ belle_sip_main_loop_do_later(obj->stack->ml,(belle_sip_callback_t)channel_notify_error_to_listeners,belle_sip_object_ref(obj)); } int belle_sip_channel_notify_timeout(belle_sip_channel_t *obj){ const int too_long=60; if (obj->state != BELLE_SIP_CHANNEL_READY){ /*no need to notify the timeout if the channel is already in error or retry state*/ return FALSE; } if ((int)(belle_sip_time_ms() - obj->last_recv_time) >= (too_long * 1000)){ belle_sip_message("A timeout related to this channel occured and no message received during last %i seconds. This channel is suspect, moving to error state",too_long); channel_set_state(obj,BELLE_SIP_CHANNEL_ERROR); return TRUE; } return FALSE; } void belle_sip_channel_notify_server_error(belle_sip_channel_t *obj){ belle_sip_message("channel[%p]: this server is encountering internal errors, moving to error state to eventually connect to another IP.", obj); obj->soft_error = TRUE; channel_set_state(obj,BELLE_SIP_CHANNEL_ERROR); } void channel_set_state(belle_sip_channel_t *obj, belle_sip_channel_state_t state) { belle_sip_message("channel %p: state %s",obj,belle_sip_channel_state_to_string(state)); if (state==BELLE_SIP_CHANNEL_ERROR){ belle_sip_channel_handle_error(obj); }else{ obj->state=state; channel_invoke_state_listener(obj); } } static void free_ewouldblock_buffer(belle_sip_channel_t *obj){ if (obj->ewouldblock_buffer){ belle_sip_free(obj->ewouldblock_buffer); obj->ewouldblock_buffer=NULL; obj->ewouldblock_size=0; obj->ewouldblock_offset=0; } } static void handle_ewouldblock(belle_sip_channel_t *obj, const char *buffer, size_t size){ belle_sip_source_set_events((belle_sip_source_t*)obj,BELLE_SIP_EVENT_READ|BELLE_SIP_EVENT_WRITE|BELLE_SIP_EVENT_ERROR); free_ewouldblock_buffer(obj); obj->ewouldblock_buffer=belle_sip_malloc(size); obj->ewouldblock_size=size; memcpy(obj->ewouldblock_buffer,buffer,size); } static size_t find_non_printable(const char *buffer, size_t size){ #if 0 size_t i; for(i=0;i 255)) return i; #endif if (!isprint(buffer[i]) && !isspace(buffer[i])) return i; } return size; #else size_t i=0; mbstate_t mbs; memset(&mbs, 0, sizeof(mbs)); do { size_t valid_multibyte_len = mbrlen(buffer+i, size-i, &mbs); if (valid_multibyte_len == (size_t)-1 || valid_multibyte_len == (size_t)-2 || valid_multibyte_len == 0) break; i += valid_multibyte_len; }while(1); return i; #endif } /* * this function is to avoid logging too much or non-ascii data received. */ static char *make_logbuf(belle_sip_channel_t *obj, belle_sip_log_level level, const char *buffer, size_t size){ char *logbuf; char truncate_msg[128]={0}; size_t limit=7000; /*big message when many ice candidates*/ if (!belle_sip_log_level_enabled(level)){ return NULL; } if (obj->stop_logging_buffer == 1) { return NULL; } size = MIN(size,limit); limit=find_non_printable(buffer, size); if (limit < size) { belle_sip_message("channel [%p]: found binary data in buffer, will stop logging it now.", obj); obj->stop_logging_buffer = 1; if (limit == 0){ snprintf(truncate_msg,sizeof(truncate_msg)-1,"... (binary data)"); } else { snprintf(truncate_msg,sizeof(truncate_msg)-1,"... (first %u bytes shown)",(unsigned int)limit); } } size = limit; size += strlen(truncate_msg); logbuf=belle_sip_malloc(size+1); strncpy(logbuf, buffer, size); if (truncate_msg[0]!=0){ strcpy(logbuf+limit,truncate_msg); } logbuf[size]='\0'; return logbuf; } static int send_buffer(belle_sip_channel_t *obj, const char *buffer, size_t size){ int ret=0; char *logbuf=NULL; if (obj->stack->send_error == 0){ ret=belle_sip_channel_send(obj,buffer,size); }else if (obj->stack->send_error<0){ /*for testing purpose only */ ret=obj->stack->send_error; } else { ret=(int)size; /*to silently discard message*/ } if (ret<0){ if (!belle_sip_error_code_is_would_block(-ret)){ belle_sip_error("channel [%p]: could not send [%i] bytes from [%s://%s:%i] to [%s:%i]" ,obj ,(int)size ,belle_sip_channel_get_transport_name(obj) ,obj->local_ip ,obj->local_port ,obj->peer_name ,obj->peer_port); channel_set_state(obj,BELLE_SIP_CHANNEL_ERROR); }/*ewouldblock error has to be handled by caller*/ }else if (size==(size_t)ret){ logbuf=make_logbuf(obj, BELLE_SIP_LOG_MESSAGE, buffer,size); if (logbuf) { belle_sip_message("channel [%p]: message %s to [%s://%s:%i], size: [%i] bytes\n%s" ,obj ,obj->stack->send_error==0?"sent":"silently discarded" ,belle_sip_channel_get_transport_name(obj) ,obj->peer_name ,obj->peer_port ,ret ,logbuf); } }else{ logbuf=make_logbuf(obj, BELLE_SIP_LOG_MESSAGE,buffer,ret); if (logbuf) { belle_sip_message("channel [%p]: message partly sent to [%s://%s:%i], sent: [%i/%i] bytes:\n%s" ,obj ,belle_sip_channel_get_transport_name(obj) ,obj->peer_name ,obj->peer_port ,ret ,(int)size ,logbuf); } } if (logbuf) belle_sip_free(logbuf); return ret; } static void check_content_length(belle_sip_message_t *msg, size_t body_len){ belle_sip_header_content_length_t *ctlen=belle_sip_message_get_header_by_type(msg,belle_sip_header_content_length_t); size_t value=ctlen ? belle_sip_header_content_length_get_content_length(ctlen) : 0; if (body_len){ if (ctlen==NULL){ belle_sip_message("message [%p] has body of size ["FORMAT_SIZE_T"] but no Content-Length, adding it.",msg,body_len); belle_sip_message_add_header(msg, (belle_sip_header_t*)belle_sip_header_content_length_create(body_len) ); }else{ if (value!=body_len){ belle_sip_warning("message [%p] has Content-Length ["FORMAT_SIZE_T"] and body size ["FORMAT_SIZE_T"] which are inconsistent, fixing it.", msg, value, body_len); belle_sip_header_content_length_set_content_length(ctlen,body_len); } } }else{ /*no body, or undetermined size body*/ if (ctlen && value!=0){ belle_sip_error("message [%p] has Content-Length ["FORMAT_SIZE_T"], but without body or body with undetermined size. Fix your app.", msg,value); } } } static void compress_body_if_required(belle_sip_message_t *msg) { belle_sip_body_handler_t *bh = belle_sip_message_get_body_handler(msg); belle_sip_memory_body_handler_t *mbh = NULL; belle_sip_header_t *ceh = NULL; size_t body_len = 0; if (bh != NULL) { body_len = belle_sip_message_get_body_size(msg); ceh = belle_sip_message_get_header(msg, "Content-Encoding"); } if ((body_len > 0) && (ceh != NULL)) { const char *content_encoding = belle_sip_header_get_unparsed_value(ceh); if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(bh, belle_sip_multipart_body_handler_t)) { char *marshalled_content = belle_sip_object_to_string(BELLE_SIP_OBJECT(bh)); mbh = belle_sip_memory_body_handler_new_from_buffer(marshalled_content, strlen(marshalled_content), NULL, NULL); bh = BELLE_SIP_BODY_HANDLER(mbh); belle_sip_message_set_body_handler(msg, bh); } if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(bh, belle_sip_memory_body_handler_t)) { mbh = BELLE_SIP_MEMORY_BODY_HANDLER(bh); belle_sip_memory_body_handler_apply_encoding(mbh, content_encoding); } else { belle_sip_warning("message [%p] has Content-Encoding [%s] that cannot be applied", msg, content_encoding); } } } static void _send_message(belle_sip_channel_t *obj){ char buffer[belle_sip_send_network_buffer_size]; size_t len=0; belle_sip_error_code error=BELLE_SIP_OK; belle_sip_message_t *msg=obj->cur_out_message; belle_sip_body_handler_t *bh=belle_sip_message_get_body_handler(msg); size_t body_len=bh ? belle_sip_body_handler_get_size(bh) : 0; int sendret; size_t off; int ret; while (obj->ewouldblock_buffer){ sendret=send_buffer(obj,(const char*)obj->ewouldblock_buffer+obj->ewouldblock_offset,obj->ewouldblock_size-obj->ewouldblock_offset); if (sendret>0){ obj->ewouldblock_offset+=sendret; if (obj->ewouldblock_offset==obj->ewouldblock_size){ free_ewouldblock_buffer(obj); } /* continue to expedite the ewouldblock error until we it is completed or get a new ewouldblock*/ }else if (belle_sip_error_code_is_would_block(-sendret)) { /*we got an ewouldblock again. Nothing to do, we'll be called later in order to retry*/ return; }else {/*error or disconnection case*/ goto done; } } if (obj->out_state==OUTPUT_STREAM_SENDING_HEADERS){ BELLE_SIP_CHANNEL_INVOKE_SENDING_LISTENERS(obj,msg); check_content_length(msg,body_len); error=belle_sip_object_marshal((belle_sip_object_t*)msg,buffer,sizeof(buffer)-1,&len); if (error!=BELLE_SIP_OK) { belle_sip_error("channel [%p] _send_message: marshaling failed.",obj); goto done; } /*send the headers and eventually the body if it fits in our buffer*/ if (bh){ size_t max_body_len=sizeof(buffer)-1-len; if (body_len>0 && body_len<=max_body_len){ /*if size is known and fits into our buffer, send together with headers*/ belle_sip_body_handler_begin_send_transfer(bh); do{ max_body_len=sizeof(buffer)-1-len; ret=belle_sip_body_handler_send_chunk(bh,msg,(uint8_t*)buffer+len,&max_body_len); if (max_body_len==0) belle_sip_warning("belle_sip_body_handler_send_chunk on channel [%p], 0 bytes read",obj); len+=max_body_len; }while(ret==BELLE_SIP_CONTINUE); belle_sip_body_handler_end_transfer(bh); }else{ if (body_len==0){ belle_sip_fatal("Sending bodies whose size is not known must be done in chunked mode, which is not supported yet."); } belle_sip_body_handler_begin_send_transfer(bh); obj->out_state=OUTPUT_STREAM_SENDING_BODY; } } off=0; do{ sendret=send_buffer(obj,buffer+off,len-off); if (sendret>0){ off+=sendret; if (off==len){ break; } }else if (belle_sip_error_code_is_would_block(-sendret)) { handle_ewouldblock(obj,buffer+off,len-off); return; }else {/*error or disconnection case*/ goto done; } }while(1); } if (obj->out_state==OUTPUT_STREAM_SENDING_BODY){ do{ size_t chunk_len=sizeof(buffer)-1; ret=belle_sip_body_handler_send_chunk(bh,msg,(uint8_t*)buffer,&chunk_len); if (chunk_len!=0){ off=0; do{ sendret=send_buffer(obj,buffer+off,chunk_len-off); if (sendret>0){ off+=sendret; if (off==chunk_len){ break; } }else if (belle_sip_error_code_is_would_block(-sendret)) { handle_ewouldblock(obj,buffer+off,chunk_len-off); return; }else {/*error or disconnection case*/ goto done; } }while(1); } }while(ret==BELLE_SIP_CONTINUE); belle_sip_body_handler_end_transfer(bh); } done: /*we get ready to send another message*/ belle_sip_source_set_events((belle_sip_source_t*)obj,BELLE_SIP_EVENT_READ|BELLE_SIP_EVENT_ERROR); free_ewouldblock_buffer(obj); obj->out_state=OUTPUT_STREAM_IDLE; obj->stop_logging_buffer=0; belle_sip_object_unref(obj->cur_out_message); obj->cur_out_message=NULL; } static void send_message(belle_sip_channel_t *obj, belle_sip_message_t *msg){ obj->cur_out_message=(belle_sip_message_t*)belle_sip_object_ref(msg); obj->out_state=OUTPUT_STREAM_SENDING_HEADERS; compress_body_if_required(obj->cur_out_message); _send_message(obj); } void belle_sip_channel_prepare(belle_sip_channel_t *obj){ channel_prepare_continue(obj); } static void channel_push_outgoing(belle_sip_channel_t *obj, belle_sip_message_t *msg){ obj->outgoing_messages=belle_sip_list_append(obj->outgoing_messages,msg); } static belle_sip_message_t *channel_pop_outgoing(belle_sip_channel_t *obj){ belle_sip_message_t *msg=NULL; if (obj->outgoing_messages){ msg=(belle_sip_message_t*)obj->outgoing_messages->data; obj->outgoing_messages=belle_sip_list_delete_link(obj->outgoing_messages,obj->outgoing_messages); } return msg; } static void channel_prepare_continue(belle_sip_channel_t *obj){ switch(obj->state){ case BELLE_SIP_CHANNEL_INIT: channel_begin_send_background_task(obj); belle_sip_channel_resolve(obj); break; case BELLE_SIP_CHANNEL_RES_DONE: belle_sip_channel_connect(obj); break; case BELLE_SIP_CHANNEL_READY: channel_process_queue(obj); break; default: break; } } static void channel_process_queue(belle_sip_channel_t *obj){ belle_sip_message_t *msg; belle_sip_object_ref(obj);/* we need to ref ourself because code below may trigger our destruction*/ if (obj->out_state!=OUTPUT_STREAM_IDLE) { _send_message(obj); } while((msg=channel_pop_outgoing(obj))!=NULL && obj->state==BELLE_SIP_CHANNEL_READY && obj->out_state==OUTPUT_STREAM_IDLE) { send_message(obj, msg); belle_sip_object_unref(msg); } if (obj->state == BELLE_SIP_CHANNEL_READY && obj->out_state == OUTPUT_STREAM_IDLE) { channel_end_send_background_task(obj); } belle_sip_object_unref(obj); } void belle_sip_channel_set_ready(belle_sip_channel_t *obj, const struct sockaddr *addr, socklen_t slen){ char name[NI_MAXHOST]; char serv[NI_MAXSERV]; if (obj->local_ip==NULL){ struct sockaddr_storage saddr; socklen_t slen2=sizeof(saddr); int err; bctbx_sockaddr_remove_v4_mapping(addr,(struct sockaddr*) &saddr,&slen2); err=bctbx_getnameinfo((struct sockaddr*)&saddr,slen2,name,sizeof(name),serv,sizeof(serv),NI_NUMERICHOST|NI_NUMERICSERV); if (err!=0){ belle_sip_error("belle_sip_channel_set_ready(): getnameinfo() failed: %s",gai_strerror(err)); }else{ obj->local_ip=belle_sip_strdup(name); obj->local_port=atoi(serv); belle_sip_message("Channel has local address %s:%s",name,serv); } } channel_set_state(obj,BELLE_SIP_CHANNEL_READY); channel_process_queue(obj); } static int channel_dns_ttl_timeout(void *data, unsigned int event) { belle_sip_channel_t *obj = (belle_sip_channel_t *)data; belle_sip_message("Channel [%p]: DNS TTL timeout reached.", obj); obj->dns_ttl_timedout = TRUE; return BELLE_SIP_STOP; } static bool_t addrinfo_in_list(const struct addrinfo *ai, const struct addrinfo *list) { const struct addrinfo *item = list; bool_t in_list = FALSE; while (item != NULL) { if ((ai->ai_family == item->ai_family) && (bctbx_sockaddr_equals(ai->ai_addr, item->ai_addr))) { in_list = TRUE; break; } item = item->ai_next; } return in_list; } static void channel_res_done(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl){ belle_sip_channel_t *obj=(belle_sip_channel_t*)data; if (obj->resolver_ctx){ belle_sip_object_unref(obj->resolver_ctx); obj->resolver_ctx=NULL; } if (ai_list){ if (!obj->current_peer) { obj->peer_list=obj->current_peer=ai_list; channel_set_state(obj,BELLE_SIP_CHANNEL_RES_DONE); } else { if (addrinfo_in_list(obj->current_peer, ai_list)) { belle_sip_message("channel[%p]: DNS resolution returned the currently used address, continue using it", obj); obj->peer_list = ai_list; channel_set_state(obj, BELLE_SIP_CHANNEL_READY); } else { belle_sip_message("channel[%p]: DNS resolution returned an address different than the one being used, reconnect to the new address", obj); obj->peer_list = obj->current_peer = ai_list; belle_sip_channel_close(obj); belle_sip_main_loop_do_later(obj->stack->ml, (belle_sip_callback_t)channel_connect_next, belle_sip_object_ref(obj)); channel_set_state(obj, BELLE_SIP_CHANNEL_RETRY); } } channel_prepare_continue(obj); if (!obj->dns_ttl_timer ) { obj->dns_ttl_timer = belle_sip_main_loop_create_timeout(obj->stack->ml, channel_dns_ttl_timeout, obj, ttl * 1000, "Channel DNS TTL timer"); } else { /* Restart the timer for new period. */ belle_sip_source_set_timeout(obj->dns_ttl_timer, ttl * 1000); belle_sip_main_loop_add_source(obj->stack->ml, obj->dns_ttl_timer); } }else{ belle_sip_error("%s: DNS resolution failed for %s", __FUNCTION__, name); channel_set_state(obj,BELLE_SIP_CHANNEL_ERROR); } } void belle_sip_channel_resolve(belle_sip_channel_t *obj){ belle_sip_message("channel [%p]: starting resolution of %s", obj, obj->peer_name); channel_set_state(obj,BELLE_SIP_CHANNEL_RES_IN_PROGRESS); if (belle_sip_stack_dns_srv_enabled(obj->stack) && obj->lp!=NULL) obj->resolver_ctx=belle_sip_stack_resolve(obj->stack, "sip", belle_sip_channel_get_transport_name_lower_case(obj), obj->peer_name, obj->peer_port, obj->ai_family, channel_res_done, obj); else obj->resolver_ctx=belle_sip_stack_resolve_a(obj->stack, obj->peer_name, obj->peer_port, obj->ai_family, channel_res_done, obj); if (obj->resolver_ctx){ belle_sip_object_ref(obj->resolver_ctx); } return ; } void belle_sip_channel_connect(belle_sip_channel_t *obj){ char ip[64]; int port=obj->peer_port; channel_set_state(obj,BELLE_SIP_CHANNEL_CONNECTING); bctbx_addrinfo_to_ip_address(obj->current_peer,ip,sizeof(ip),&port); /* update peer_port as it may have been overriden by SRV resolution*/ if (port!=obj->peer_port){ /*the SRV resolution provided a port number that must be used*/ obj->srv_overrides_port=TRUE; obj->peer_port=port; } belle_sip_message("Trying to connect to [%s://%s:%i]",belle_sip_channel_get_transport_name(obj),ip,obj->peer_port); if(BELLE_SIP_OBJECT_VPTR(obj,belle_sip_channel_t)->connect(obj,obj->current_peer)) { belle_sip_error("Cannot connect to [%s://%s:%i]",belle_sip_channel_get_transport_name(obj),obj->peer_name,obj->peer_port); channel_set_state(obj,BELLE_SIP_CHANNEL_ERROR); } return; } static void queue_message(belle_sip_channel_t *obj, belle_sip_message_t *msg){ belle_sip_object_ref(msg); channel_push_outgoing(obj,msg); if (obj->state==BELLE_SIP_CHANNEL_INIT){ belle_sip_channel_prepare(obj); }else if (obj->state==BELLE_SIP_CHANNEL_READY) { channel_process_queue(obj); } } typedef struct delay_send{ belle_sip_channel_t *chan; belle_sip_message_t *msg; }delay_send_t; /* just to emulate network transmission delay */ static int on_delayed_send_do(delay_send_t *ctx){ belle_sip_message("on_delayed_send_do(): sending now"); if (ctx->chan->state!=BELLE_SIP_CHANNEL_ERROR && ctx->chan->state!=BELLE_SIP_CHANNEL_DISCONNECTED){ queue_message(ctx->chan,ctx->msg); } belle_sip_object_unref(ctx->chan); belle_sip_object_unref(ctx->msg); belle_sip_free(ctx); return FALSE; } static void queue_message_delayed(belle_sip_channel_t *obj, belle_sip_message_t *msg){ delay_send_t *ctx=belle_sip_malloc(sizeof(delay_send_t)); ctx->chan=(belle_sip_channel_t*)belle_sip_object_ref(obj); ctx->msg=(belle_sip_message_t*)belle_sip_object_ref(msg); belle_sip_main_loop_add_timeout(obj->stack->ml,(belle_sip_source_func_t)on_delayed_send_do,ctx,obj->stack->tx_delay); belle_sip_message("channel %p: message sending delayed by %i ms",obj,obj->stack->tx_delay); } int belle_sip_channel_queue_message(belle_sip_channel_t *obj, belle_sip_message_t *msg){ if (obj->stack->tx_delay>0){ queue_message_delayed(obj,msg); }else queue_message(obj,msg); return 0; } void belle_sip_channel_force_close(belle_sip_channel_t *obj){ obj->force_close=1; channel_set_state(obj,BELLE_SIP_CHANNEL_DISCONNECTED); } belle_sip_channel_t *belle_sip_channel_find_from_list_with_addrinfo(belle_sip_list_t *l, const belle_sip_hop_t *hop, const struct addrinfo *addr){ belle_sip_list_t *elem; belle_sip_channel_t *chan; for(elem=l;elem!=NULL;elem=elem->next){ chan=(belle_sip_channel_t*)elem->data; if (!chan->about_to_be_closed && belle_sip_channel_matches(chan,hop,addr)){ return chan; } } return NULL; } /* search a matching channel from a list according to supplied hop. The ai_family tells which address family is supported by the list of channels*/ belle_sip_channel_t *belle_sip_channel_find_from_list(belle_sip_list_t *l, int ai_family, const belle_sip_hop_t *hop){ belle_sip_channel_t *chan=NULL; struct addrinfo *res = bctbx_ip_address_to_addrinfo(ai_family,SOCK_STREAM/*needed on some platforms that return an error otherwise (QNX)*/,hop->host,hop->port); chan=belle_sip_channel_find_from_list_with_addrinfo(l,hop,res); if (res) bctbx_freeaddrinfo(res); return chan; } void belle_sip_channel_check_dns_reusability(belle_sip_channel_t *obj) { if (obj->dns_ttl_timedout) { obj->dns_ttl_timedout = FALSE; belle_sip_channel_resolve(obj); } } #ifdef __ANDROID__ unsigned long belle_sip_begin_background_task(const char *name, belle_sip_background_task_end_callback_t cb, void *data){ return wake_lock_acquire(name); } void belle_sip_end_background_task(unsigned long id){ wake_lock_release(id); } #elif !TARGET_OS_IPHONE && !defined(__APPLE__) /*defines stubs*/ unsigned long belle_sip_begin_background_task(const char *name, belle_sip_background_task_end_callback_t cb, void *data){ return 0; } void belle_sip_end_background_task(unsigned long id){ return; } #endif belle-sip-1.6.3/src/channel.h000066400000000000000000000256461313437522400157660ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BELLE_SIP_CHANNEL_H #define BELLE_SIP_CHANNEL_H #ifndef _WIN32 #include #include #else #endif #define belle_sip_network_buffer_size 65535 #define belle_sip_send_network_buffer_size 16384 typedef enum belle_sip_channel_state{ BELLE_SIP_CHANNEL_INIT, BELLE_SIP_CHANNEL_RES_IN_PROGRESS, BELLE_SIP_CHANNEL_RES_DONE, BELLE_SIP_CHANNEL_CONNECTING, BELLE_SIP_CHANNEL_RETRY, BELLE_SIP_CHANNEL_READY, BELLE_SIP_CHANNEL_ERROR, BELLE_SIP_CHANNEL_DISCONNECTED }belle_sip_channel_state_t; const char * belle_sip_channel_state_to_string(belle_sip_channel_state_t state); /** * belle_sip_channel_t is an object representing a single communication channel ( socket or file descriptor), * unlike the belle_sip_listening_point_t that can owns several channels for TCP or TLS (incoming server child sockets or * outgoing client sockets). **/ typedef struct belle_sip_channel belle_sip_channel_t; BELLE_SIP_DECLARE_INTERFACE_BEGIN(belle_sip_channel_listener_t) void (*on_state_changed)(belle_sip_channel_listener_t *l, belle_sip_channel_t *, belle_sip_channel_state_t state); void (*on_message_headers)(belle_sip_channel_listener_t *l, belle_sip_channel_t *obj, belle_sip_message_t *msg); void (*on_message)(belle_sip_channel_listener_t *l, belle_sip_channel_t *obj, belle_sip_message_t *msg); void (*on_sending)(belle_sip_channel_listener_t *l, belle_sip_channel_t *obj, belle_sip_message_t *msg); int (*on_auth_requested)(belle_sip_channel_listener_t *l, belle_sip_channel_t *obj, const char* distinghised_name); BELLE_SIP_DECLARE_INTERFACE_END #define BELLE_SIP_CHANNEL_LISTENER(obj) BELLE_SIP_INTERFACE_CAST(obj,belle_sip_channel_listener_t) typedef enum input_stream_state { WAITING_MESSAGE_START, MESSAGE_AQUISITION, BODY_AQUISITION }input_stream_state_t; typedef enum output_stream_state{ OUTPUT_STREAM_IDLE, OUTPUT_STREAM_SENDING_HEADERS, OUTPUT_STREAM_SENDING_BODY }output_stream_state_t; typedef struct belle_sip_channel_input_stream{ input_stream_state_t state; char buff[belle_sip_network_buffer_size]; char* read_ptr; char* write_ptr; belle_sip_message_t *msg; size_t content_length; int chuncked_mode; int chunk_size; int chunk_read_size; }belle_sip_channel_input_stream_t; typedef struct belle_sip_stream_channel belle_sip_stream_channel_t; typedef struct belle_sip_tls_channel belle_sip_tls_channel_t; struct belle_sip_channel{ belle_sip_source_t base; belle_sip_stack_t *stack; belle_sip_listening_point_t *lp; /*the listening point that owns this channel*/ belle_sip_channel_state_t state; belle_sip_list_t *state_listeners; belle_sip_list_t *full_listeners; int ai_family; char *peer_cname; char *peer_name; int peer_port; char *local_ip; int local_port; char *public_ip; int public_port; belle_sip_resolver_context_t* resolver_ctx; struct addrinfo *peer_list; struct addrinfo *current_peer; belle_sip_list_t *outgoing_messages; belle_sip_message_t *cur_out_message; output_stream_state_t out_state; uint8_t *ewouldblock_buffer; size_t ewouldblock_size; size_t ewouldblock_offset; belle_sip_channel_input_stream_t input_stream; belle_sip_list_t* incoming_messages; belle_sip_source_t *inactivity_timer; belle_sip_source_t *dns_ttl_timer; uint64_t last_recv_time; int simulated_recv_return; /* used to simulate network error. 0= no data (disconnected) >0= do nothing -1= network error, 1500 special number to silently discard incoming buffer*/ unsigned long bg_task_id; unsigned long recv_bg_task_id; unsigned char force_close; /* when channel is intentionnaly disconnected, in order to prevent looping notifications*/ unsigned char learnt_ip_port; unsigned char has_name; /*set when the name of the peer is known, which is not the case of inboud connections*/ unsigned char about_to_be_closed; unsigned char srv_overrides_port; /*set when this channel was connected to destination port provided by SRV resolution*/ unsigned char soft_error; /*set when this channel enters ERROR state because of error detected in upper layer */ int stop_logging_buffer; /*log buffer content only if this is non binary data, and stop it at the first occurence*/ bool_t closed_by_remote; /*If the channel has been remotely closed*/ bool_t dns_ttl_timedout; }; #define BELLE_SIP_CHANNEL(obj) BELLE_SIP_CAST(obj,belle_sip_channel_t) BELLE_SIP_BEGIN_DECLS void belle_sip_channel_add_listener(belle_sip_channel_t *chan, belle_sip_channel_listener_t *l); void belle_sip_channel_remove_listener(belle_sip_channel_t *obj, belle_sip_channel_listener_t *l); int belle_sip_channel_matches(const belle_sip_channel_t *obj, const belle_sip_hop_t *hop, const struct addrinfo *addr); void belle_sip_channel_resolve(belle_sip_channel_t *obj); void belle_sip_channel_connect(belle_sip_channel_t *obj); void belle_sip_channel_prepare(belle_sip_channel_t *obj); void belle_sip_channel_close(belle_sip_channel_t *obj); /** * * returns number of send byte or <0 in case of error */ int belle_sip_channel_send(belle_sip_channel_t *obj, const void *buf, size_t buflen); int belle_sip_channel_recv(belle_sip_channel_t *obj, void *buf, size_t buflen); /*only used by channels implementation*/ void belle_sip_channel_set_ready(belle_sip_channel_t *obj, const struct sockaddr *addr, socklen_t slen); void belle_sip_channel_init(belle_sip_channel_t *obj, belle_sip_stack_t *stack, const char *bindip,int localport, const char *peer_cname, const char *peername, int peer_port); void belle_sip_channel_init_with_addr(belle_sip_channel_t *obj, belle_sip_stack_t *stack, const char *bindip, int localport, const struct sockaddr *peer_addr, socklen_t addrlen); void belle_sip_channel_set_socket(belle_sip_channel_t *obj, belle_sip_socket_t sock, belle_sip_source_func_t datafunc); /*end of channel implementations*/ /** * Get a received message from the receive queue. This caller takes the ownership of the message. */ belle_sip_message_t* belle_sip_channel_pick_message(belle_sip_channel_t *obj); int belle_sip_channel_queue_message(belle_sip_channel_t *obj, belle_sip_message_t *msg); int belle_sip_channel_is_reliable(const belle_sip_channel_t *obj); const char * belle_sip_channel_get_transport_name(const belle_sip_channel_t *obj); const char * belle_sip_channel_get_transport_name_lower_case(const belle_sip_channel_t *obj); const struct addrinfo * belle_sip_channel_get_peer(belle_sip_channel_t *obj); const char *belle_sip_channel_get_local_address(belle_sip_channel_t *obj, int *port); const char *belle_sip_channel_get_public_address(belle_sip_channel_t *obj, int *port); /* * Returns a sip-uri suitable for using in record-route. * If the channel is not natted, it will return the listening port of the listening point corresponding to the channel. **/ belle_sip_uri_t *belle_sip_channel_create_routable_uri(belle_sip_channel_t *chan); #define belle_sip_channel_get_state(chan) ((chan)->state) void channel_set_state(belle_sip_channel_t *obj, belle_sip_channel_state_t state); /* * Process incoming data and synchronously invoke the listeners if * complete message are received. The invocation of the listeners may * result in the channel being destroyed (ex: calling belle_sip_listening_point_clean_channels() within * a transaction completed notification). * WARNING: As a result, the caller of this function must be take into account that the channel no longer exists * in return from this function. */ int belle_sip_channel_process_data(belle_sip_channel_t *obj,unsigned int revents); /*this function is to be used only in belle_sip_listening_point_clean_channels()*/ void belle_sip_channel_force_close(belle_sip_channel_t *obj); /*this function is for transactions to report that a channel seems non working because a timeout occured for example. It results in the channel possibly entering error state, so that it gets cleaned. Next transactions will re-open a new one and get a better chance of receiving an answer. Returns TRUE if the channel enters error state, 0 otherwise (channel is given a second chance) */ int belle_sip_channel_notify_timeout(belle_sip_channel_t *obj); /*Used by transaction layer to report a server having internal errors, so that we can retry with another IP (in case of DNS SRV)*/ BELLESIP_EXPORT void belle_sip_channel_notify_server_error(belle_sip_channel_t *obj); /* * Check if the DNS TTL has expired. If this is the case, set the channel status to RES_IN_PROGRESS. */ void belle_sip_channel_check_dns_reusability(belle_sip_channel_t *obj); BELLE_SIP_END_DECLS BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_channel_t,belle_sip_source_t) const char *transport; int reliable; int (*connect)(belle_sip_channel_t *obj, const struct addrinfo *ai); int (*channel_send)(belle_sip_channel_t *obj, const void *buf, size_t buflen); int (*channel_recv)(belle_sip_channel_t *obj, void *buf, size_t buflen); void (*close)(belle_sip_channel_t *obj); BELLE_SIP_DECLARE_CUSTOM_VPTR_END /* * tls client certificate authentication. might be relevant for both tls and dtls channels.Only implemented in tls channel for now */ void belle_sip_tls_channel_set_client_certificates_chain(belle_sip_tls_channel_t *obj, belle_sip_certificates_chain_t* cert_chain); void belle_sip_tls_channel_set_client_certificate_key(belle_sip_tls_channel_t *obj, belle_sip_signing_key_t* key); belle_sip_channel_t *belle_sip_channel_find_from_list_with_addrinfo(belle_sip_list_t *l, const belle_sip_hop_t *hop, const struct addrinfo *addr); belle_sip_channel_t *belle_sip_channel_find_from_list(belle_sip_list_t *l, int ai_family, const belle_sip_hop_t *hop); #define BELLE_SIP_TLS_CHANNEL(obj) BELLE_SIP_CAST(obj,belle_sip_tls_channel_t) struct belle_tls_crypto_config{ belle_sip_object_t base; char *root_ca; /**< path to the trusted certificate chain used when verifiying peer certificate */ char *root_ca_data; /**< content of the trusted certificate chain used when verifiying peer certificate */ int exception_flags; /**< override some exception raised during certificate verification, can be: BELLE_TLS_VERIFY_NONE do not override any exception BELLE_TLS_VERIFY_CN_MISMATCH ignore Common Name mismatch exception BELLE_TLS_VERIFY_ANY_REASON(ignore any exception */ void *ssl_config; /**< externally provided ssl configuration context, will be casted and given to the underlying crypto library, use only if you really know what you're doing */ }; #endif belle-sip-1.6.3/src/clock_gettime.c000066400000000000000000000107221313437522400171470ustar00rootroot00000000000000/* * Copyright (c), MM Weiss * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the MM Weiss nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * clock_gettime_stub.c * gcc -Wall -c clock_gettime_stub.c * posix realtime functions; MacOS user space glue */ /* @comment * other possible implementation using intel builtin rdtsc * rdtsc-workaround: http://www.mcs.anl.gov/~kazutomo/rdtsc.html * * we could get the ticks by doing this * * __asm __volatile("mov %%ebx, %%esi\n\t" * "cpuid\n\t" * "xchg %%esi, %%ebx\n\t" * "rdtsc" * : "=a" (a), * "=d" (d) * ); * we could even replace our tricky sched_yield call by assembly code to get a better accurency, * anyway the following C stub will satisfy 99% of apps using posix clock_gettime call, * moreover, the setter version (clock_settime) could be easly written using mach primitives: * http://www.opensource.apple.com/source/xnu/xnu-${VERSION}/osfmk/man/ (clock_[set|get]_time) * * hackers don't be crackers, don't you use a flush toilet? * * * @see draft: ./posix-realtime-stub/posix-realtime-stub.c * */ #ifdef __APPLE__ #include #include #include #include #include #include #include #include #include "clock_gettime.h" static mach_timebase_info_data_t __clock_gettime_inf; static clock_serv_t belle_sip_calandar_clk; static clock_serv_t belle_sip_system_clk; static int belle_sip_clock_serv_ready=FALSE; int bc_clock_gettime(bc_clockid_t clk_id, struct timespec *tp) { kern_return_t ret; clock_serv_t clk_serv; mach_timespec_t tm; uint64_t start, end, delta, nano; int retval = -1; if (!belle_sip_clock_serv_ready) { /*host_get_clock_service is pretty slow*/ if (KERN_SUCCESS != host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &belle_sip_calandar_clk) || KERN_SUCCESS != host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &belle_sip_system_clk) ) { return -1; } else { belle_sip_clock_serv_ready=TRUE; } } switch (clk_id) { case BC_CLOCK_REALTIME: case BC_CLOCK_MONOTONIC: clk_serv = (clk_id == BC_CLOCK_REALTIME) ? belle_sip_calandar_clk : belle_sip_system_clk; if (KERN_SUCCESS == (ret = clock_get_time(clk_serv, &tm))) { tp->tv_sec = tm.tv_sec; tp->tv_nsec = tm.tv_nsec; retval = 0; } if (KERN_SUCCESS != ret) { errno = EINVAL; retval = -1; } break; case BC_CLOCK_PROCESS_CPUTIME_ID: case BC_CLOCK_THREAD_CPUTIME_ID: start = mach_absolute_time(); if (clk_id == BC_CLOCK_PROCESS_CPUTIME_ID) { getpid(); } else { sched_yield(); } end = mach_absolute_time(); delta = end - start; if (0 == __clock_gettime_inf.denom) { mach_timebase_info(&__clock_gettime_inf); } nano = delta * __clock_gettime_inf.numer / __clock_gettime_inf.denom; tp->tv_sec = nano * 1e-9; tp->tv_nsec = nano - (tp->tv_sec * 1e9); retval = 0; break; default: errno = EINVAL; retval = -1; } return retval; } #endif // __APPLE__ /* EOF */ belle-sip-1.6.3/src/clock_gettime.h000066400000000000000000000020361313437522400171530ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef CLOCK_GETTIME_H_ #define CLOCK_GETTIME_H_ #ifdef __APPLE__ typedef enum { BC_CLOCK_REALTIME, BC_CLOCK_MONOTONIC, BC_CLOCK_PROCESS_CPUTIME_ID, BC_CLOCK_THREAD_CPUTIME_ID } bc_clockid_t; int bc_clock_gettime(bc_clockid_t clk_id, struct timespec *tp) ; #endif #endif /* CLOCK_GETTIME_H_ */ belle-sip-1.6.3/src/dialog.c000066400000000000000000001336041313437522400156020ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #include static void belle_sip_dialog_init_200Ok_retrans(belle_sip_dialog_t *obj, belle_sip_response_t *resp); static int belle_sip_dialog_handle_200Ok(belle_sip_dialog_t *obj, belle_sip_response_t *msg); static void belle_sip_dialog_process_queue(belle_sip_dialog_t* dialog); static belle_sip_request_t *create_request(belle_sip_dialog_t *obj, const char *method, int full); static void belle_sip_dialog_uninit(belle_sip_dialog_t *obj){ if (obj->route_set) belle_sip_list_free_with_data(obj->route_set,belle_sip_object_unref); if (obj->remote_target) belle_sip_object_unref(obj->remote_target); if (obj->call_id) belle_sip_object_unref(obj->call_id); if (obj->local_party) belle_sip_object_unref(obj->local_party); if (obj->remote_party) belle_sip_object_unref(obj->remote_party); if (obj->local_tag) belle_sip_free(obj->local_tag); if (obj->remote_tag) belle_sip_free(obj->remote_tag); if (obj->last_out_invite) belle_sip_object_unref(obj->last_out_invite); if (obj->last_out_ack) belle_sip_object_unref(obj->last_out_ack); if (obj->last_transaction) belle_sip_object_unref(obj->last_transaction); if(obj->privacy) belle_sip_object_unref(obj->privacy); if (obj->expiration_timer){ /*In some situations, dialog deleted might not be called*/ belle_sip_main_loop_remove_source(obj->provider->stack->ml, obj->expiration_timer); belle_sip_object_unref(obj->expiration_timer); obj->expiration_timer = NULL; } } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_dialog_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_dialog_t) BELLE_SIP_VPTR_INIT(belle_sip_dialog_t, belle_sip_object_t,TRUE), (belle_sip_object_destroy_t)belle_sip_dialog_uninit, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END const char* belle_sip_dialog_state_to_string(const belle_sip_dialog_state_t state) { switch(state) { case BELLE_SIP_DIALOG_NULL: return "BELLE_SIP_DIALOG_NULL"; case BELLE_SIP_DIALOG_EARLY: return "BELLE_SIP_DIALOG_EARLY"; case BELLE_SIP_DIALOG_CONFIRMED: return "BELLE_SIP_DIALOG_CONFIRMED"; case BELLE_SIP_DIALOG_TERMINATED: return "BELLE_SIP_DIALOG_TERMINATED"; default: return "Unknown state"; } } static void set_state(belle_sip_dialog_t *obj,belle_sip_dialog_state_t state) { obj->previous_state=obj->state; obj->state=state; } static void set_to_tag(belle_sip_dialog_t *obj, belle_sip_header_to_t *to){ const char *to_tag=belle_sip_header_to_get_tag(to); if (obj->is_server){ if (to_tag && !obj->local_tag) obj->local_tag=belle_sip_strdup(to_tag); }else{ if (to_tag && !obj->remote_tag) obj->remote_tag=belle_sip_strdup(to_tag); } } static void check_route_set(belle_sip_list_t *rs){ if (rs){ belle_sip_header_route_t *r=(belle_sip_header_route_t*)rs->data; if (!belle_sip_uri_has_lr_param(belle_sip_header_address_get_uri((belle_sip_header_address_t*)r))){ belle_sip_warning("top uri of route set does not contain 'lr', not really supported."); } } } static int belle_sip_dialog_init_as_uas(belle_sip_dialog_t *obj, belle_sip_request_t *req){ const belle_sip_list_t *elem; belle_sip_header_contact_t *ct=belle_sip_message_get_header_by_type(req,belle_sip_header_contact_t); belle_sip_header_cseq_t *cseq=belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t); belle_sip_header_via_t *via=belle_sip_message_get_header_by_type(req,belle_sip_header_via_t); belle_sip_uri_t *requri=belle_sip_request_get_uri(req); belle_sip_header_to_t *to=belle_sip_message_get_header_by_type(req,belle_sip_header_to_t); if (!ct){ belle_sip_error("No contact in request."); return -1; } if (!cseq){ belle_sip_error("No cseq in request."); return -1; } if (!via){ belle_sip_error("No via in request."); return -1; } if (!to){ belle_sip_error("No to in request."); return -1; } if (strcasecmp(belle_sip_header_via_get_protocol(via),"TLS")==0 && belle_sip_uri_is_secure(requri)){ obj->is_secure=TRUE; } /* 12.1.1 *The route set MUST be set to the list of URIs in the Record-Route * header field from the request, taken in order and preserving all URI * parameters. If no Record-Route header field is present in the *request, the route set MUST be set to the empty set. */ obj->route_set=belle_sip_list_free_with_data(obj->route_set,belle_sip_object_unref); for(elem=belle_sip_message_get_headers((belle_sip_message_t*)req,BELLE_SIP_RECORD_ROUTE);elem!=NULL;elem=elem->next){ obj->route_set=belle_sip_list_append(obj->route_set,belle_sip_object_ref(belle_sip_header_route_create( (belle_sip_header_address_t*)elem->data))); } check_route_set(obj->route_set); obj->remote_target=(belle_sip_header_address_t*)belle_sip_object_ref(ct); obj->remote_cseq=belle_sip_header_cseq_get_seq_number(cseq); if (strcmp(belle_sip_request_get_method(req),"INVITE")==0){ obj->remote_invite_cseq = belle_sip_header_cseq_get_seq_number(cseq); } /*remote party already set */ obj->local_party=(belle_sip_header_address_t*)belle_sip_object_ref(to); return 0; } static void set_last_out_invite(belle_sip_dialog_t *obj, belle_sip_request_t *req){ if (obj->last_out_invite) belle_sip_object_unref(obj->last_out_invite); obj->last_out_invite=(belle_sip_request_t*)belle_sip_object_ref(req); } static int belle_sip_dialog_init_as_uac(belle_sip_dialog_t *obj, belle_sip_request_t *req){ belle_sip_header_cseq_t *cseq=belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t); belle_sip_header_to_t *to=belle_sip_message_get_header_by_type(req,belle_sip_header_to_t); belle_sip_header_via_t *via=belle_sip_message_get_header_by_type(req,belle_sip_header_via_t); belle_sip_uri_t *requri=belle_sip_request_get_uri(req); if (!to){ belle_sip_error("No to in request."); return -1; } if (!cseq){ belle_sip_error("No cseq in request."); return -1; } if (!via){ belle_sip_error("No via in request."); return -1; } if (belle_sip_header_via_get_protocol(via) /*might be null at dialog creation (uac case)*/ && strcasecmp(belle_sip_header_via_get_protocol(via),"TLS")==0 && belle_sip_uri_is_secure(requri)){ obj->is_secure=TRUE; } obj->local_cseq=belle_sip_header_cseq_get_seq_number(cseq); /*call id is already set */ /*local_tag is already set*/ obj->remote_party=(belle_sip_header_address_t*)belle_sip_object_ref(to); /*local party is already set*/ if (strcmp(belle_sip_request_get_method(req),"INVITE")==0){ set_last_out_invite(obj,req); } return 0; } static int belle_sip_dialog_on_expired(belle_sip_dialog_t *dialog){ belle_sip_message("Dialog [%p] expired", dialog); dialog->is_expired = TRUE; belle_sip_dialog_delete(dialog); return BELLE_SIP_STOP; } int belle_sip_dialog_expired(const belle_sip_dialog_t *dialog){ return dialog->is_expired; } /*returns BELLE_SIP_STOP if the dialog is to be terminated, BELLE_SIP_CONTINUE otherwise*/ static int belle_sip_dialog_schedule_expiration(belle_sip_dialog_t *dialog, belle_sip_message_t *request){ belle_sip_header_expires_t *expires = belle_sip_message_get_header_by_type(request, belle_sip_header_expires_t); int expires_value; if (!expires) return BELLE_SIP_CONTINUE; expires_value = belle_sip_header_expires_get_expires(expires); if (dialog->expiration_timer){ belle_sip_main_loop_remove_source(dialog->provider->stack->ml, dialog->expiration_timer); belle_sip_object_unref(dialog->expiration_timer); dialog->expiration_timer = NULL; } belle_sip_message("belle_sip_dialog_schedule_expiration() dialog=%p expires_value=%i", dialog, expires_value); if (expires_value == 0) return BELLE_SIP_STOP; dialog->expiration_timer = belle_sip_main_loop_create_timeout(dialog->provider->stack->ml, (belle_sip_source_func_t) belle_sip_dialog_on_expired, dialog, expires_value * 1000, "Dialog expiration"); return BELLE_SIP_CONTINUE; } int belle_sip_dialog_establish_full(belle_sip_dialog_t *obj, belle_sip_request_t *req, belle_sip_response_t *resp){ belle_sip_header_contact_t *ct=belle_sip_message_get_header_by_type(resp,belle_sip_header_contact_t); belle_sip_header_to_t *to=belle_sip_message_get_header_by_type(resp,belle_sip_header_to_t); const belle_sip_list_t *elem; if (strcmp(belle_sip_request_get_method(req),"INVITE")==0) obj->needs_ack=TRUE; if (obj->is_server){ if (strcmp(belle_sip_request_get_method(req),"INVITE")==0){ belle_sip_dialog_init_200Ok_retrans(obj,resp); } } else { if (!ct && !obj->remote_target) { belle_sip_error("Missing contact header in resp [%p] cannot set remote target for dialog [%p]",resp,obj); return -1; } /* 13.2.2.4 2xx Responses ... If the dialog identifier in the 2xx response matches the dialog identifier of an existing dialog, the dialog MUST be transitioned to the "confirmed" state, and the route set for the dialog MUST be recomputed based on the 2xx response using the procedures of Section 12.2.1.2. */ obj->route_set=belle_sip_list_free_with_data(obj->route_set,belle_sip_object_unref); for(elem=belle_sip_message_get_headers((belle_sip_message_t*)resp,BELLE_SIP_RECORD_ROUTE);elem!=NULL;elem=elem->next){ obj->route_set=belle_sip_list_prepend(obj->route_set,belle_sip_object_ref(belle_sip_header_route_create( (belle_sip_header_address_t*)elem->data))); } if (ct) { /*remote Contact header may have changed between early dialog to confirmed*/ if (obj->remote_target) belle_sip_object_unref(obj->remote_target); obj->remote_target=(belle_sip_header_address_t*)belle_sip_object_ref(ct); } } /*update to tag*/ set_to_tag(obj,to); set_state(obj,BELLE_SIP_DIALOG_CONFIRMED); return 0; } int belle_sip_dialog_establish(belle_sip_dialog_t *obj, belle_sip_request_t *req, belle_sip_response_t *resp){ belle_sip_header_to_t *to=belle_sip_message_get_header_by_type(resp,belle_sip_header_to_t); belle_sip_header_via_t *via=belle_sip_message_get_header_by_type(req,belle_sip_header_via_t); belle_sip_uri_t *requri=belle_sip_request_get_uri(req); if (obj->state != BELLE_SIP_DIALOG_NULL) { belle_sip_error("Dialog [%p] already established.",obj); return -1; } if (!to){ belle_sip_error("No to in response."); return -1; } if (!obj->is_server) { belle_sip_header_contact_t *ct=belle_sip_message_get_header_by_type(resp,belle_sip_header_contact_t); const belle_sip_list_t* elem; /*contact might be provided later*/ if (ct) obj->remote_target=(belle_sip_header_address_t*)belle_sip_object_ref(ct); /**12.1.2 * The route set MUST be set to the list of URIs in the Record-Route *header field from the response, taken in reverse order and preserving *all URI parameters. If no Record-Route header field is present in *the response, the route set MUST be set to the empty set. **/ obj->route_set=belle_sip_list_free_with_data(obj->route_set,belle_sip_object_unref); for(elem=belle_sip_message_get_headers((belle_sip_message_t*)resp,BELLE_SIP_RECORD_ROUTE);elem!=NULL;elem=elem->next){ obj->route_set=belle_sip_list_prepend(obj->route_set,belle_sip_object_ref(belle_sip_header_route_create( (belle_sip_header_address_t*)elem->data))); } check_route_set(obj->route_set); /*via might be unknown at dialog creation*/ if (strcasecmp(belle_sip_header_via_get_protocol(via),"TLS")==0 && belle_sip_uri_is_secure(requri)){ obj->is_secure=TRUE; } } set_to_tag(obj,to); return 0; } int belle_sip_dialog_check_incoming_request_ordering(belle_sip_dialog_t *obj, belle_sip_request_t *req){ belle_sip_header_cseq_t *cseqh=belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t); unsigned int cseq=belle_sip_header_cseq_get_seq_number(cseqh); if (obj->remote_cseq==0){ obj->remote_cseq=cseq; }else if (cseq>obj->remote_cseq){ return 0; } belle_sip_warning("Ignoring request because cseq is inconsistent."); return -1; } static int dialog_on_200Ok_timer(belle_sip_dialog_t *dialog){ /*reset the timer */ const belle_sip_timer_config_t *cfg=belle_sip_stack_get_timer_config(dialog->provider->stack); unsigned int prev_timeout=belle_sip_source_get_timeout(dialog->timer_200Ok); belle_sip_source_set_timeout(dialog->timer_200Ok,MIN(2*prev_timeout,(unsigned int)cfg->T2)); belle_sip_message("Dialog sending retransmission of 200Ok"); belle_sip_provider_send_response(dialog->provider,dialog->last_200Ok); return BELLE_SIP_CONTINUE; } static int dialog_on_200Ok_end(belle_sip_dialog_t *dialog){ belle_sip_request_t *bye; belle_sip_client_transaction_t *trn; belle_sip_dialog_stop_200Ok_retrans(dialog); belle_sip_error("Dialog [%p] was not ACK'd within T1*64 seconds, it is going to be terminated.",dialog); dialog->state=BELLE_SIP_DIALOG_CONFIRMED; bye=belle_sip_dialog_create_request(dialog,"BYE"); trn=belle_sip_provider_create_client_transaction(dialog->provider,bye); BELLE_SIP_TRANSACTION(trn)->is_internal=1; /*don't bother user with this transaction*/ belle_sip_client_transaction_send_request(trn); return BELLE_SIP_STOP; } static void belle_sip_dialog_init_200Ok_retrans(belle_sip_dialog_t *obj, belle_sip_response_t *resp){ const belle_sip_timer_config_t *cfg=belle_sip_stack_get_timer_config(obj->provider->stack); if (obj->timer_200Ok || obj->timer_200Ok_end) { belle_sip_error("dialog [%p] already has a 200ok retransmition timer ! skipping",obj); return; } obj->timer_200Ok=belle_sip_timeout_source_new((belle_sip_source_func_t)dialog_on_200Ok_timer,obj,cfg->T1); belle_sip_object_set_name((belle_sip_object_t*)obj->timer_200Ok,"dialog_200Ok_timer"); belle_sip_main_loop_add_source(obj->provider->stack->ml,obj->timer_200Ok); obj->timer_200Ok_end=belle_sip_timeout_source_new((belle_sip_source_func_t)dialog_on_200Ok_end,obj,cfg->T1*64); belle_sip_object_set_name((belle_sip_object_t*)obj->timer_200Ok_end,"dialog_200Ok_timer_end"); belle_sip_main_loop_add_source(obj->provider->stack->ml,obj->timer_200Ok_end); obj->last_200Ok=(belle_sip_response_t*)belle_sip_object_ref(resp); } void belle_sip_dialog_stop_200Ok_retrans(belle_sip_dialog_t *obj){ belle_sip_main_loop_t *ml=obj->provider->stack->ml; if (obj->timer_200Ok){ belle_sip_main_loop_remove_source(ml,obj->timer_200Ok); belle_sip_object_unref(obj->timer_200Ok); obj->timer_200Ok=NULL; } if (obj->timer_200Ok_end){ belle_sip_main_loop_remove_source(ml,obj->timer_200Ok_end); belle_sip_object_unref(obj->timer_200Ok_end); obj->timer_200Ok_end=NULL; } if (obj->last_200Ok){ belle_sip_object_unref(obj->last_200Ok); obj->last_200Ok=NULL; } } /* returns true if the dialog is terminated by the NOTIFY request or response*/ static int belle_sip_dialog_should_terminate_by_notify(belle_sip_dialog_t *obj, belle_sip_transaction_t* transaction, int as_uas){ int should_terminate = FALSE; if (obj->type == BELLE_SIP_DIALOG_SUBSCRIBE_NOTIFY) { belle_sip_request_t *req=belle_sip_transaction_get_request(transaction); belle_sip_response_t *resp=belle_sip_transaction_get_response(transaction); belle_sip_header_subscription_state_t* subscription_state_header=belle_sip_message_get_header_by_type(req,belle_sip_header_subscription_state_t); int code = resp ? belle_sip_response_get_status_code(resp) : 0; if (!subscription_state_header || strcasecmp(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,belle_sip_header_subscription_state_get_state(subscription_state_header)) ==0) { if (as_uas){ /*terminate the dialog when the application replies the 200 Ok*/ if (code == 200){ should_terminate = TRUE; } }else{ /*terminate the dialog when we receive the 200OK or the transaction terminates by timeout*/ if (code == 200 || belle_sip_transaction_get_state(transaction) == BELLE_SIP_TRANSACTION_TERMINATED){ should_terminate = TRUE; } } } } return should_terminate; } static void belle_sip_dialog_update_remote_target(belle_sip_dialog_t *obj, belle_sip_header_contact_t *ct) { int remote_target_changed = FALSE; if (obj->remote_target) { belle_sip_uri_t *remote_target_uri = belle_sip_header_address_get_uri(obj->remote_target); belle_sip_uri_t *ct_uri = belle_sip_header_address_get_uri((belle_sip_header_address_t *)ct); remote_target_changed = !belle_sip_uri_equals(remote_target_uri, ct_uri); belle_sip_object_unref(obj->remote_target); } obj->remote_target=(belle_sip_header_address_t*)belle_sip_object_ref(ct); if (remote_target_changed) { belle_sip_message("Dialog [%p]: remote target changed", obj); if (obj->last_out_ack) { belle_sip_request_set_uri(BELLE_SIP_REQUEST(obj->last_out_ack), belle_sip_header_address_get_uri(obj->remote_target)); } } } /* * return 0 if message should be delivered to the next listener, otherwise, its a retransmision, just keep it * */ int belle_sip_dialog_update(belle_sip_dialog_t *obj, belle_sip_transaction_t* transaction, int as_uas){ int is_retransmition=FALSE; int delete_dialog=FALSE; belle_sip_request_t *req=belle_sip_transaction_get_request(transaction); belle_sip_response_t *resp=belle_sip_transaction_get_response(transaction); int code=0; int ret = 0; int is_invite = strcmp(belle_sip_request_get_method(req),"INVITE")==0; int is_subscribe = strcmp(belle_sip_request_get_method(req),"SUBSCRIBE")==0; int is_notify = strcmp(belle_sip_request_get_method(req),"NOTIFY")==0; belle_sip_message("Dialog [%p]: now updated by transaction [%p].",obj, transaction); if (resp) code=belle_sip_response_get_status_code(resp); if (as_uas && code == 491) { /**/ belle_sip_message("Dialog [%p]: don't update last transaction by transaction [%p].",obj, transaction); } else { belle_sip_object_ref(transaction); if (obj->last_transaction) belle_sip_object_unref(obj->last_transaction); obj->last_transaction=transaction; } if (!as_uas){ belle_sip_header_privacy_t *privacy_header=belle_sip_message_get_header_by_type(req,belle_sip_header_privacy_t); SET_OBJECT_PROPERTY(obj,privacy,privacy_header); } /*first update local/remote cseq*/ if (as_uas) { belle_sip_header_cseq_t* cseq=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_cseq_t); obj->remote_cseq=belle_sip_header_cseq_get_seq_number(cseq); if (is_invite && code>=200 && code<300) obj->remote_invite_cseq = belle_sip_header_cseq_get_seq_number(cseq); /*else ACK is handled by transaction, not dialog*/ } switch (obj->state){ case BELLE_SIP_DIALOG_NULL: /*always establish a dialog*/ if (code>100 && code<300 && (is_invite || is_subscribe)) { belle_sip_dialog_establish(obj,req,resp); if (code<200){ set_state(obj,BELLE_SIP_DIALOG_EARLY); break; }/* no break for code >200 because need to call belle_sip_dialog_establish_full*/ } BCTBX_NO_BREAK; /*intentionally no break*/ case BELLE_SIP_DIALOG_EARLY: /*don't terminate dialog for UPDATE*/ if (code>=300 && (is_invite || is_subscribe)) { /*12.3 Termination of a Dialog Independent of the method, if a request outside of a dialog generates a non-2xx final response, any early dialogs created through provisional responses to that request are terminated. The mechanism for terminating confirmed dialogs is method specific.*/ delete_dialog=TRUE; break; } if (code>=200 && code<300 && (is_invite || is_subscribe)){ if (belle_sip_dialog_establish_full(obj,req,resp) != 0){ /*the dialog found this response invalid so notify the transaction layer by returning -1*/ ret = -1; goto end; } } if (is_subscribe){ if (belle_sip_dialog_schedule_expiration(obj, (belle_sip_message_t*)req) == BELLE_SIP_STOP && (code>=200 || (code==0 && belle_sip_transaction_get_state(transaction)==BELLE_SIP_TRANSACTION_TERMINATED))){ /*delete the dialog when the 200Ok for a SUBSCRIBE with expires=0 is received or when * no response is received at all*/ delete_dialog = TRUE; } } if (code < 200 && belle_sip_transaction_get_state(transaction)==BELLE_SIP_TRANSACTION_TERMINATED){ /*no response establishing the dialog, and transaction terminated (transport errors)*/ delete_dialog=TRUE; } break; case BELLE_SIP_DIALOG_CONFIRMED: if (code==481 && (is_invite || is_subscribe)) { /*Dialog is terminated in such case*/ delete_dialog=TRUE; break; } /*refreshing target is also true in case of subscribe*/ if ((is_invite || is_subscribe) && (code>=200 && code<300)) { /*refresh the remote_target*/ belle_sip_header_contact_t *ct; if (as_uas){ ct=belle_sip_message_get_header_by_type(req,belle_sip_header_contact_t); }else{ if (is_invite) set_last_out_invite(obj,req); ct=belle_sip_message_get_header_by_type(resp,belle_sip_header_contact_t); } if (ct){ belle_sip_dialog_update_remote_target(obj, ct); } } if (is_invite){ if (code>=200 && code<300){ /*handle possible retransmission of 200Ok */ if (!as_uas && (is_retransmition=(belle_sip_dialog_handle_200Ok(obj,resp)==0))) { return is_retransmition; } else { if (as_uas) belle_sip_dialog_init_200Ok_retrans(obj,resp); obj->needs_ack=TRUE; /*REINVITE case, ack needed by both uas and uac*/ } }else if (code>=300){ /*final response, ack will be automatically sent by transaction layer*/ /* do not need to do anything because not set yet or set by previous invite transaction obj->needs_ack=FALSE;*/ } } else if (strcmp(belle_sip_request_get_method(req),"BYE")==0){ /*15.1.1 UAC Behavior A BYE request is constructed as would any other request within a dialog, as described in Section 12. Once the BYE is constructed, the UAC core creates a new non-INVITE client transaction, and passes it the BYE request. The UAC MUST consider the session terminated (and therefore stop sending or listening for media) as soon as the BYE request is passed to the client transaction. If the response for the BYE is a 481 (Call/Transaction Does Not Exist) or a 408 (Request Timeout) or no response at all is received for the BYE (that is, a timeout is returned by the client transaction), the UAC MUST consider the session and the dialog terminated. */ /*what should we do with other reponse >300 ?? */ if (code>=200 || (code==0 && belle_sip_transaction_get_state(transaction)==BELLE_SIP_TRANSACTION_TERMINATED)){ obj->needs_ack=FALSE; /*no longuer need ACK*/ if (obj->terminate_on_bye) delete_dialog=TRUE; } }else if (is_subscribe){ if (belle_sip_dialog_schedule_expiration(obj, (belle_sip_message_t*)req) == BELLE_SIP_STOP && (code>=200 || (code==0 && belle_sip_transaction_get_state(transaction)==BELLE_SIP_TRANSACTION_TERMINATED))){ delete_dialog = TRUE; }else if (!as_uas){ if (code >= 300 || (code==0 && belle_sip_transaction_get_state(transaction)==BELLE_SIP_TRANSACTION_TERMINATED)){ /*case of a SUBSCRIBE refresh that is rejected or unanswered*/ if (code != 491){ /*request pending is not fatal for the dialog*/ delete_dialog = TRUE; } } } }else if (is_notify){ delete_dialog = belle_sip_dialog_should_terminate_by_notify(obj, transaction, as_uas); } break; case BELLE_SIP_DIALOG_TERMINATED: /*ignore*/ break; } end: if (delete_dialog) belle_sip_dialog_delete(obj); else { belle_sip_dialog_process_queue(obj); } return ret; } belle_sip_dialog_t *belle_sip_dialog_new(belle_sip_transaction_t *t){ belle_sip_dialog_t *obj; belle_sip_header_from_t *from; const char *from_tag; belle_sip_header_to_t *to; const char *to_tag=NULL; belle_sip_header_call_id_t *call_id=belle_sip_message_get_header_by_type(t->request,belle_sip_header_call_id_t); from=belle_sip_message_get_header_by_type(t->request,belle_sip_header_from_t); if (from==NULL){ belle_sip_error("belle_sip_dialog_new(): no from!"); return NULL; } from_tag=belle_sip_header_from_get_tag(from); if (from_tag==NULL){ belle_sip_error("belle_sip_dialog_new(): no from tag!"); return NULL; } to = belle_sip_message_get_header_by_type(t->request,belle_sip_header_to_t); if (to == NULL){ belle_sip_error("belle_sip_dialog_new(): no to in request!"); return NULL; } to_tag = belle_sip_header_to_get_tag(to); if (to_tag){ belle_sip_error("belle_sip_dialog_new(): there is a to tag in the request. This is not allowed" " to create a dialog on such a transaction."); return NULL; } if (!call_id){ belle_sip_error("No call-id in response."); return NULL; } if (t->last_response) { to=belle_sip_message_get_header_by_type(t->last_response,belle_sip_header_to_t); if (to==NULL){ belle_sip_error("belle_sip_dialog_new(): no to!"); return NULL; } to_tag = belle_sip_header_to_get_tag(to); } obj=belle_sip_object_new(belle_sip_dialog_t); obj->terminate_on_bye=1; obj->provider=t->provider; obj->pending_trans_checking_enabled=1; obj->call_id=(belle_sip_header_call_id_t*)belle_sip_object_ref(call_id); if (strcmp(belle_sip_request_get_method(t->request),"INVITE") == 0){ obj->type = BELLE_SIP_DIALOG_INVITE; }else if (strcmp(belle_sip_request_get_method(t->request),"SUBSCRIBE") == 0){ obj->type = BELLE_SIP_DIALOG_SUBSCRIBE_NOTIFY; }else{ belle_sip_error("belle_sip_dialog_new(): unsupported request [%s] for creating a dialog.", belle_sip_request_get_method(t->request)); } belle_sip_object_ref(t); obj->last_transaction=t; if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(t,belle_sip_server_transaction_t)){ obj->remote_tag=belle_sip_strdup(from_tag); obj->local_tag=belle_sip_strdup(BELLE_SIP_SERVER_TRANSACTION(t)->to_tag); obj->remote_party=(belle_sip_header_address_t*)belle_sip_object_ref(from); obj->is_server=TRUE; belle_sip_dialog_init_as_uas(obj,belle_sip_transaction_get_request(t)); }else{ const belle_sip_list_t *predefined_routes=NULL; obj->local_tag=belle_sip_strdup(from_tag); obj->remote_tag=to_tag?belle_sip_strdup(to_tag):NULL; /*might be null at dialog creation*/ obj->local_party=(belle_sip_header_address_t*)belle_sip_object_ref(from); obj->is_server=FALSE; for(predefined_routes=belle_sip_message_get_headers((belle_sip_message_t*)t->request,BELLE_SIP_ROUTE); predefined_routes!=NULL;predefined_routes=predefined_routes->next){ obj->route_set=belle_sip_list_append(obj->route_set,belle_sip_object_ref(predefined_routes->data)); } belle_sip_dialog_init_as_uac(obj,belle_sip_transaction_get_request(t)); } belle_sip_message("New %s dialog [%p] , local tag [%s], remote tag [%s]" ,obj->is_server?"server":"client" ,obj ,obj->local_tag?obj->local_tag:"" ,obj->remote_tag?obj->remote_tag:""); set_state(obj,BELLE_SIP_DIALOG_NULL); return obj; } belle_sip_request_t *belle_sip_dialog_create_ack(belle_sip_dialog_t *obj, unsigned int cseq){ belle_sip_header_cseq_t *cseqh; belle_sip_request_t *invite=obj->last_out_invite; belle_sip_request_t *ack; if (!invite){ belle_sip_error("No INVITE to ACK."); return NULL; } cseqh=belle_sip_message_get_header_by_type(invite,belle_sip_header_cseq_t); if (belle_sip_header_cseq_get_seq_number(cseqh)!=cseq){ belle_sip_error("No INVITE with cseq %i to create ack for.",cseq); return NULL; } ack=create_request(obj,"ACK",TRUE); /* 22 Usage of HTTP Authentication 22.1 Framework While a server can legitimately challenge most SIP requests, there are two requests defined by this document that require special handling for authentication: ACK and CANCEL. Under an authentication scheme that uses responses to carry values used to compute nonces (such as Digest), some problems come up for any requests that take no response, including ACK. For this reason, any credentials in the INVITE that were accepted by a server MUST be accepted by that server for the ACK. UACs creating an ACK message will duplicate all of the Authorization and Proxy-Authorization header field values that appeared in the INVITE to which the ACK corresponds. Servers MUST NOT attempt to challenge an ACK. */ if (ack){ const belle_sip_list_t *aut=belle_sip_message_get_headers((belle_sip_message_t*)obj->last_out_invite,"Authorization"); const belle_sip_list_t *prx_aut=belle_sip_message_get_headers((belle_sip_message_t*)obj->last_out_invite,"Proxy-Authorization"); if (aut) belle_sip_message_add_headers((belle_sip_message_t*)ack,aut); if (prx_aut) belle_sip_message_add_headers((belle_sip_message_t*)ack,prx_aut); /*the ack is sent statelessly, the transaction layer doesn't need the dialog information*/ belle_sip_request_set_dialog(ack,NULL); } return ack; } static belle_sip_request_t *create_request(belle_sip_dialog_t *obj, const char *method, int full){ belle_sip_request_t *req; char* from_tag=NULL, *to_tag=NULL; if (!obj->remote_target){ belle_sip_error("dialog [%p]: no remote_target set, unable to create request.", obj); return NULL; } if (!belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(obj->local_party), "tag")) { /*special case for dialog created by server transaction*/ from_tag = obj->local_tag; } if (!belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(obj->remote_party), "tag")) { /*special case for dialog created by server transaction*/ to_tag = obj->remote_tag; } req=belle_sip_request_create(belle_sip_header_address_get_uri(obj->remote_target), method, obj->call_id, belle_sip_header_cseq_create(obj->local_cseq,method), belle_sip_header_from_create(obj->local_party,from_tag), belle_sip_header_to_create(obj->remote_party,to_tag), belle_sip_header_via_new(), 0); if (full && obj->route_set) { belle_sip_message_add_headers((belle_sip_message_t*)req,obj->route_set); } if (obj->privacy) { /*repeat the last privacy set in new request. I could not find any requirement for this, but this might be safer * as proxies don't store information about dialogs*/ belle_sip_message_add_header((belle_sip_message_t*)req,BELLE_SIP_HEADER(obj->privacy)); } belle_sip_request_set_dialog(req,obj); return req; } static int dialog_can_create_request(belle_sip_dialog_t *obj, const char *method){ if (obj->state != BELLE_SIP_DIALOG_CONFIRMED && obj->state != BELLE_SIP_DIALOG_EARLY && (strcmp("NOTIFY", method) != 0 || !obj->is_server || !obj->last_transaction || strcmp("SUBSCRIBE", belle_sip_transaction_get_method(obj->last_transaction)) !=0)) { belle_sip_error("belle_sip_dialog_create_request(): cannot create [%s] request from dialog [%p] in state [%s]",method,obj,belle_sip_dialog_state_to_string(obj->state)); return FALSE; } return TRUE; } belle_sip_request_t * belle_sip_dialog_create_queued_request(belle_sip_dialog_t *obj, const char *method){ belle_sip_request_t *req; if (!dialog_can_create_request(obj, method)) return NULL; if (strcmp(method,"INVITE")==0 || strcmp(method,"SUBSCRIBE")==0){ /*we don't allow requests that can update the dialog's state to be sent asynchronously*/ belle_sip_error("belle_sip_dialog_create_queued_request([%p]): [%s] requests are forbidden using this method.",obj,method); return NULL; } req=create_request(obj,method,FALSE); if (req){ req->dialog_queued=TRUE; } return req; } static void belle_sip_dialog_update_local_cseq(belle_sip_dialog_t *obj, const char *method){ if (obj->local_cseq==0) obj->local_cseq=110; if (strcmp(method,"ACK")!=0) obj->local_cseq++; } belle_sip_request_t *belle_sip_dialog_create_request(belle_sip_dialog_t *obj, const char *method){ belle_sip_request_t *req; if (!dialog_can_create_request(obj, method)) return NULL; /*don't prevent to send a BYE in any case */ if ( obj->pending_trans_checking_enabled && strcmp(method,"BYE")!=0 && obj->last_transaction && belle_sip_transaction_state_is_transient(belle_sip_transaction_get_state(obj->last_transaction))){ if (obj->state != BELLE_SIP_DIALOG_EARLY && strcmp(method,"UPDATE")!=0 && strcmp(method,"NOTIFY")!=0) { belle_sip_error("belle_sip_dialog_create_request(): cannot create [%s] request from dialog [%p] while pending [%s] transaction in state [%s]",method,obj,belle_sip_transaction_get_method(obj->last_transaction), belle_sip_transaction_state_to_string(belle_sip_transaction_get_state(obj->last_transaction))); return NULL; } /*else UPDATE transaction can be send in // */ } belle_sip_dialog_update_local_cseq(obj,method); req=create_request(obj,method,TRUE); return req; } static unsigned int is_system_header(belle_sip_header_t* header) { const char* name=belle_sip_header_get_name(header); return strcasecmp(BELLE_SIP_VIA,name) ==0 || strcasecmp(BELLE_SIP_FROM,name) ==0 || strcasecmp(BELLE_SIP_TO,name) ==0 || strcasecmp(BELLE_SIP_CSEQ,name) ==0 || strcasecmp(BELLE_SIP_CALL_ID,name) ==0 || strcasecmp(BELLE_SIP_PROXY_AUTHORIZATION,name) == 0 || strcasecmp(BELLE_SIP_AUTHORIZATION,name) == 0 || strcasecmp(BELLE_SIP_MAX_FORWARDS,name) == 0 || strcasecmp(BELLE_SIP_ALLOW,name) ==0 || strcasecmp(BELLE_SIP_ROUTE,name) ==0; } static void copy_non_system_headers(belle_sip_header_t* header,belle_sip_request_t* req ) { if (!is_system_header(header)) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),header); } } static belle_sip_request_t *_belle_sip_dialog_create_request_from(belle_sip_dialog_t *obj, const belle_sip_request_t *initial_req, int queued){ belle_sip_request_t* req; const char *method=belle_sip_request_get_method(initial_req); belle_sip_header_content_length_t* content_lenth; belle_sip_list_t* headers; if (queued) req=belle_sip_dialog_create_queued_request(obj,method); else req=belle_sip_dialog_create_request(obj,method); if (req==NULL) return NULL; content_lenth = belle_sip_message_get_header_by_type(initial_req,belle_sip_header_content_length_t); /*first copy non system headers*/ headers = belle_sip_message_get_all_headers(BELLE_SIP_MESSAGE(initial_req)); belle_sip_list_for_each2(headers,(void (*)(void *, void *))copy_non_system_headers,req); belle_sip_list_free(headers); /*replicate via user parameters, if any, useful for 'alias' parameter in SUBSCRIBE requests*/ { belle_sip_header_via_t *orig_via=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(initial_req),belle_sip_header_via_t); belle_sip_header_via_t *new_via=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_via_t); belle_sip_parameters_copy_parameters_from(BELLE_SIP_PARAMETERS(new_via),BELLE_SIP_PARAMETERS(orig_via)); } /*copy body*/ if (content_lenth && belle_sip_header_content_length_get_content_length(content_lenth)>0) { belle_sip_message_set_body(BELLE_SIP_MESSAGE(req),belle_sip_message_get_body(BELLE_SIP_MESSAGE(initial_req)),belle_sip_header_content_length_get_content_length(content_lenth)); } return req; } belle_sip_request_t *belle_sip_dialog_create_request_from(belle_sip_dialog_t *obj, const belle_sip_request_t *initial_req){ return _belle_sip_dialog_create_request_from(obj,initial_req,FALSE); } belle_sip_request_t *belle_sip_dialog_create_queued_request_from(belle_sip_dialog_t *obj, const belle_sip_request_t *initial_req){ return _belle_sip_dialog_create_request_from(obj,initial_req,TRUE); } void belle_sip_dialog_delete(belle_sip_dialog_t *obj){ size_t dropped_transactions; if (obj->expiration_timer){ belle_sip_main_loop_remove_source(obj->provider->stack->ml, obj->expiration_timer); belle_sip_object_unref(obj->expiration_timer); obj->expiration_timer = NULL; } belle_sip_message("Dialog [%p] deleted (is_expired=%i)",obj, obj->is_expired); belle_sip_dialog_stop_200Ok_retrans(obj); /*if any*/ set_state(obj,BELLE_SIP_DIALOG_TERMINATED); dropped_transactions=belle_sip_list_size(obj->queued_ct); if (dropped_transactions>0) belle_sip_warning("dialog [%p]: leaves %u queued transaction aborted.",obj,(unsigned int)dropped_transactions); belle_sip_list_for_each(obj->queued_ct,(void(*)(void*))belle_sip_transaction_terminate); obj->queued_ct=belle_sip_list_free_with_data(obj->queued_ct,belle_sip_object_unref); belle_sip_provider_remove_dialog(obj->provider,obj); } void *belle_sip_dialog_get_application_data(const belle_sip_dialog_t *dialog){ return dialog->appdata; } void belle_sip_dialog_set_application_data(belle_sip_dialog_t *dialog, void *data){ dialog->appdata=data; } const belle_sip_header_call_id_t *belle_sip_dialog_get_call_id(const belle_sip_dialog_t *dialog){ return dialog->call_id; } const belle_sip_header_address_t *belle_sip_dialog_get_local_party(const belle_sip_dialog_t *dialog){ return dialog->local_party; } const belle_sip_header_address_t *belle_sip_dialog_get_remote_party(const belle_sip_dialog_t *dialog){ return dialog->remote_party; } unsigned int belle_sip_dialog_get_local_seq_number(const belle_sip_dialog_t *dialog){ return dialog->local_cseq; } unsigned int belle_sip_dialog_get_remote_seq_number(const belle_sip_dialog_t *dialog){ return dialog->remote_cseq; } const char *belle_sip_dialog_get_local_tag(const belle_sip_dialog_t *dialog){ return dialog->local_tag; } const char *belle_sip_dialog_get_remote_tag(const belle_sip_dialog_t *dialog){ return dialog->remote_tag; } const belle_sip_header_address_t *belle_sip_dialog_get_remote_target(belle_sip_dialog_t *dialog){ return dialog->remote_target; } const belle_sip_list_t* belle_sip_dialog_get_route_set(belle_sip_dialog_t *dialog){ return dialog->route_set; } belle_sip_dialog_state_t belle_sip_dialog_get_state(const belle_sip_dialog_t *dialog){ return dialog->state; } belle_sip_dialog_state_t belle_sip_dialog_get_previous_state(const belle_sip_dialog_t *dialog) { return dialog->previous_state; } int belle_sip_dialog_is_server(const belle_sip_dialog_t *dialog){ return dialog->is_server; } int belle_sip_dialog_is_secure(const belle_sip_dialog_t *dialog){ return dialog->is_secure; } void belle_sip_dialog_send_ack(belle_sip_dialog_t *obj, belle_sip_request_t *request){ if (obj->needs_ack){ obj->needs_ack=FALSE; if (obj->last_out_ack) belle_sip_object_unref(obj->last_out_ack); obj->last_out_ack=(belle_sip_request_t*)belle_sip_object_ref(request); belle_sip_provider_send_request(obj->provider,request); belle_sip_dialog_process_queue(obj); }else{ belle_sip_error("Why do you want to send an ACK ?"); } } void belle_sip_dialog_terminate_on_bye(belle_sip_dialog_t *obj, int val){ obj->terminate_on_bye=val; } /*returns 1 if message belongs to the dialog, 0 otherwise */ int belle_sip_dialog_match(belle_sip_dialog_t *obj, belle_sip_message_t *msg, int as_uas){ belle_sip_header_call_id_t *call_id=belle_sip_message_get_header_by_type(msg,belle_sip_header_call_id_t); belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(msg,belle_sip_header_from_t); belle_sip_header_to_t *to=belle_sip_message_get_header_by_type(msg,belle_sip_header_to_t); const char *from_tag; const char *to_tag; const char *call_id_value; if (call_id==NULL || from==NULL || to==NULL) return 0; call_id_value=belle_sip_header_call_id_get_call_id(call_id); from_tag=belle_sip_header_from_get_tag(from); to_tag=belle_sip_header_to_get_tag(to); return _belle_sip_dialog_match(obj,call_id_value,as_uas ? to_tag : from_tag, as_uas ? from_tag : to_tag); } int _belle_sip_dialog_match(belle_sip_dialog_t *obj, const char *call_id, const char *local_tag, const char *remote_tag){ const char *dcid; /*Dialog created by notify matching subscription are still in NULL state if (obj->state==BELLE_SIP_DIALOG_NULL) belle_sip_fatal("_belle_sip_dialog_match() must not be used for dialog in null state.");*/ dcid=belle_sip_header_call_id_get_call_id(obj->call_id); return strcmp(dcid,call_id)==0 && strcmp(obj->local_tag,local_tag)==0 && obj->remote_tag /* handle 180 without to tag */ && strcmp(obj->remote_tag,remote_tag)==0; } void belle_sip_dialog_check_ack_sent(belle_sip_dialog_t*obj){ belle_sip_client_transaction_t* client_trans; if (obj->needs_ack){ belle_sip_request_t *req; belle_sip_error("Your listener did not ACK'd the 200Ok for your INVITE request. The dialog will be terminated."); req=belle_sip_dialog_create_request(obj,"BYE"); if (req){ client_trans=belle_sip_provider_create_client_transaction(obj->provider,req); BELLE_SIP_TRANSACTION(client_trans)->is_internal=TRUE; /*internal transaction, don't bother user with 200ok*/ belle_sip_client_transaction_send_request(client_trans); /*call dialog terminated*/ }else{ /*If there is no way to signal the failure to the other party, then force immediate dialog deletion*/ belle_sip_dialog_delete(obj); } } } /* * return 0 if dialog handle the 200ok * */ static int belle_sip_dialog_handle_200Ok(belle_sip_dialog_t *obj, belle_sip_response_t *msg){ if (obj->last_out_ack){ belle_sip_header_cseq_t *cseq=belle_sip_message_get_header_by_type(msg,belle_sip_header_cseq_t); if (cseq){ belle_sip_header_cseq_t *ack_cseq=belle_sip_message_get_header_by_type(obj->last_out_ack,belle_sip_header_cseq_t); if (belle_sip_header_cseq_get_seq_number(cseq)==belle_sip_header_cseq_get_seq_number(ack_cseq)){ /*pass for retransmission*/ belle_sip_message("Dialog retransmitting last ack automatically"); belle_sip_provider_send_request(obj->provider,obj->last_out_ack); return 0; }else belle_sip_message("No already created ACK matching 200Ok for dialog [%p]",obj); } } return -1; } int belle_sip_dialog_handle_ack(belle_sip_dialog_t *obj, belle_sip_request_t *ack){ belle_sip_header_cseq_t *cseq=belle_sip_message_get_header_by_type(ack,belle_sip_header_cseq_t); if (obj->needs_ack && belle_sip_header_cseq_get_seq_number(cseq)==obj->remote_invite_cseq){ belle_sip_message("Incoming INVITE has ACK, dialog is happy"); obj->needs_ack=FALSE; belle_sip_dialog_stop_200Ok_retrans(obj); belle_sip_dialog_process_queue(obj); return 0; } belle_sip_message("Dialog ignoring incoming ACK (surely a retransmission)"); return -1; } belle_sip_transaction_t* belle_sip_dialog_get_last_transaction(const belle_sip_dialog_t *dialog) { return dialog->last_transaction; } int belle_sip_dialog_request_pending(const belle_sip_dialog_t *dialog){ return dialog->needs_ack || (dialog->last_transaction ? belle_sip_transaction_state_is_transient(belle_sip_transaction_get_state(dialog->last_transaction)) : FALSE); } /* for notify exception As per RFC 3265; 3.3.4. Dialog creation and termination If an initial SUBSCRIBE request is not sent on a pre-existing dialog, the subscriber will wait for a response to the SUBSCRIBE request or a matching NOTIFY. ... ... If an initial SUBSCRIBE is sent on a pre-existing dialog, a matching 200-class response or successful NOTIFY request merely creates a new subscription associated with that dialog. */ int belle_sip_dialog_is_authorized_transaction(const belle_sip_dialog_t *dialog,const char* method) { if (belle_sip_dialog_request_pending(dialog)){ const char* last_transaction_request; if (strcasecmp(method,"BYE")==0) return TRUE; /*don't reject a BYE*/ last_transaction_request = belle_sip_request_get_method(belle_sip_transaction_get_request(dialog->last_transaction)); if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(dialog->last_transaction,belle_sip_client_transaction_t) && strcmp(last_transaction_request,"SUBSCRIBE")==0 && strcmp(method,"NOTIFY")==0){ /*stupid as it is, you have to accept a NOTIFY for a SUBSCRIBE for which no answer is received yet...*/ return TRUE; } if (strcmp(last_transaction_request,"INVITE")==0 && (strcmp(method,"PRACK")==0 || strcmp(method,"UPDATE")==0)){ /*PRACK /UPDATE needs to be sent or received during reINVITEs.*/ return TRUE; } return FALSE; } else { return TRUE; } } void belle_sip_dialog_queue_client_transaction(belle_sip_dialog_t *dialog, belle_sip_client_transaction_t *tr){ dialog->queued_ct=belle_sip_list_append(dialog->queued_ct, belle_sip_object_ref(tr)); } static void _belle_sip_dialog_process_queue(belle_sip_dialog_t* dialog){ belle_sip_client_transaction_t *tr=NULL; if (dialog->state==BELLE_SIP_DIALOG_TERMINATED || belle_sip_dialog_request_pending(dialog)) goto end; dialog->queued_ct=belle_sip_list_pop_front(dialog->queued_ct,(void**)&tr); if (tr){ belle_sip_message("Dialog [%p]: sending queued request.",dialog); tr->base.sent_by_dialog_queue=TRUE; belle_sip_client_transaction_send_request(tr); belle_sip_object_unref(tr); } end: belle_sip_object_unref(dialog); } void belle_sip_dialog_process_queue(belle_sip_dialog_t* dialog){ /*process queue does not process synchronously. * This is to let the application handle responses and eventually submit new requests without being blocked. * Typically when a reINVITE is challenged, we want a chance to re-submit an authenticated request before processing * queued requests*/ if (dialog->queued_ct){ belle_sip_main_loop_do_later(dialog->provider->stack->ml,(belle_sip_callback_t)_belle_sip_dialog_process_queue,belle_sip_object_ref(dialog)); } } void belle_sip_dialog_update_request(belle_sip_dialog_t *dialog, belle_sip_request_t *req){ belle_sip_header_cseq_t *cseq=belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t); belle_sip_dialog_update_local_cseq(dialog,belle_sip_request_get_method(req)); if (dialog->route_set) belle_sip_message_add_headers((belle_sip_message_t*)req,dialog->route_set); belle_sip_request_set_uri(req,belle_sip_header_address_get_uri(dialog->remote_target)); belle_sip_header_cseq_set_seq_number(cseq,dialog->local_cseq); } int belle_sip_dialog_pending_trans_checking_enabled( const belle_sip_dialog_t *dialog) { return dialog->pending_trans_checking_enabled; } int belle_sip_dialog_enable_pending_trans_checking(belle_sip_dialog_t *dialog, int value) { dialog->pending_trans_checking_enabled = value; return 0; } belle-sip-1.6.3/src/dns.c000066400000000000000000007031771313437522400151370ustar00rootroot00000000000000/* ========================================================================== * dns.c - Recursive, Reentrant DNS Resolver. * -------------------------------------------------------------------------- * Copyright (c) 2008, 2009, 2010, 2012-2016 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #if !defined(__FreeBSD__) && !defined(__sun) #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif #undef _DEFAULT_SOURCE #define _DEFAULT_SOURCE #undef _BSD_SOURCE #define _BSD_SOURCE #undef _DARWIN_C_SOURCE #define _DARWIN_C_SOURCE #undef _NETBSD_SOURCE #define _NETBSD_SOURCE #endif #ifdef _MSC_VER #define DNS_INLINE __inline #else #define DNS_INLINE inline #endif #include /* INT_MAX */ #include /* offsetof() */ /*#ifdef _WIN32 #define uint16_t unsigned short #define uint32_t unsigned int #else*/ #include /* uint32_t */ //#endif #include /* malloc(3) realloc(3) free(3) rand(3) random(3) arc4random(3) */ #include /* FILE fopen(3) fclose(3) getc(3) rewind(3) */ #include /* memcpy(3) strlen(3) memmove(3) memchr(3) memcmp(3) strchr(3) strsep(3) strcspn(3) */ #ifndef _WIN32 #include /* strcasecmp(3) strncasecmp(3) */ #endif #include /* isspace(3) isdigit(3) */ #include /* time_t time(2) difftime(3) */ #include /* SIGPIPE sigemptyset(3) sigaddset(3) sigpending(2) sigprocmask(2) pthread_sigmask(3) sigtimedwait(2) */ #include /* errno EINVAL ENOENT */ #undef NDEBUG #include /* assert(3) */ #if _WIN32 #ifndef FD_SETSIZE #define FD_SETSIZE 2048 #endif #include #include #ifndef USE_FIXED_NAMESERVERS #include #pragma comment(lib, "IPHLPAPI.lib") #endif #if defined(__MINGW32__) || !defined(WINAPI_FAMILY_PARTITION) || !defined(WINAPI_PARTITION_DESKTOP) #define BELLE_SIP_WINDOWS_DESKTOP 1 #elif defined(WINAPI_FAMILY_PARTITION) #if defined(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) #define BELLE_SIP_WINDOWS_DESKTOP 1 #elif defined(WINAPI_PARTITION_PHONE_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) #define BELLE_SIP_WINDOWS_PHONE 1 #elif defined(WINAPI_PARTITION_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #define BELLE_SIP_WINDOWS_UNIVERSAL 1 #endif #endif #else #include /* FD_SETSIZE socklen_t */ #include /* FD_ZERO FD_SET fd_set select(2) */ #include /* AF_INET AF_INET6 AF_UNIX struct sockaddr struct sockaddr_in struct sockaddr_in6 socket(2) */ #if defined(AF_UNIX) #include /* struct sockaddr_un */ #endif #include /* F_SETFD F_GETFL F_SETFL O_NONBLOCK fcntl(2) */ #include /* _POSIX_THREADS gethostname(3) close(2) */ #include /* POLLIN POLLOUT */ #include /* struct sockaddr_in struct sockaddr_in6 */ #include /* inet_pton(3) inet_ntop(3) htons(3) ntohs(3) */ #include /* struct addrinfo */ #endif #include "dns.h" #if defined(HAVE_RESINIT) || defined(USE_STRUCT_RES_STATE_NAMESERVERS) #include #endif #ifdef __ANDROID__ #include #endif #include #include /* * C O M P I L E R V E R S I O N & F E A T U R E D E T E C T I O N * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_GNUC_2VER(M, m, p) (((M) * 10000) + ((m) * 100) + (p)) #define DNS_GNUC_PREREQ(M, m, p) (__GNUC__ > 0 && DNS_GNUC_2VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) >= DNS_GNUC_2VER((M), (m), (p))) #define DNS_MSC_2VER(M, m, p) ((((M) + 6) * 10000000) + ((m) * 1000000) + (p)) #define DNS_MSC_PREREQ(M, m, p) (_MSC_VER_FULL > 0 && _MSC_VER_FULL >= DNS_MSC_2VER((M), (m), (p))) #define DNS_SUNPRO_PREREQ(M, m, p) (__SUNPRO_C > 0 && __SUNPRO_C >= 0x ## M ## m ## p) #if defined __has_builtin #define dns_has_builtin(x) __has_builtin(x) #else #define dns_has_builtin(x) 0 #endif #if defined __has_extension #define dns_has_extension(x) __has_extension(x) #else #define dns_has_extension(x) 0 #endif #ifndef HAVE___ASSUME #define HAVE___ASSUME DNS_MSC_PREREQ(8,0,0) #endif #ifndef HAVE___BUILTIN_TYPES_COMPATIBLE_P #define HAVE___BUILTIN_TYPES_COMPATIBLE_P (DNS_GNUC_PREREQ(3,1,1) || __clang__) #endif #ifndef HAVE___BUILTIN_UNREACHABLE #define HAVE___BUILTIN_UNREACHABLE (DNS_GNUC_PREREQ(4,5,0) || dns_has_builtin(__builtin_unreachable)) #endif #ifndef HAVE_PRAGMA_MESSAGE #define HAVE_PRAGMA_MESSAGE (DNS_GNUC_PREREQ(4,4,0) || __clang__ || _MSC_VER) #endif /* * C O M P I L E R A N N O T A T I O N S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if __GNUC__ #define DNS_NOTUSED __attribute__((unused)) #define DNS_NORETURN __attribute__((noreturn)) #else #define DNS_NOTUSED #define DNS_NORETURN #endif #if __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wmissing-field-initializers" #elif DNS_GNUC_PREREQ(4,6,0) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif /* * S T A N D A R D M A C R O S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if HAVE___BUILTIN_TYPES_COMPATIBLE_P #define dns_same_type(a, b, def) __builtin_types_compatible_p(typeof (a), typeof (b)) #else #define dns_same_type(a, b, def) (def) #endif #define dns_isarray(a) (!dns_same_type((a), (&(a)[0]), 0)) #define dns_inline_assert(cond) ((void)(sizeof (struct { int:-!(cond); }))) #if HAVE___ASSUME #define dns_assume(cond) __assume(cond) #elif HAVE___BUILTIN_UNREACHABLE #define dns_assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) #else #define dns_assume(cond) do { (void)(cond); } while (0) #endif #ifndef lengthof #define lengthof(a) (dns_inline_assert(dns_isarray(a)), (sizeof (a) / sizeof (a)[0])) #endif #ifndef endof #define endof(a) (dns_inline_assert(dns_isarray(a)), &(a)[lengthof((a))]) #endif /* * M I S C E L L A N E O U S C O M P A T * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if _WIN32 || _WIN64 #define PRIuZ "Iu" #else #define PRIuZ "zu" #endif #ifndef DNS_THREAD_SAFE #if (defined _REENTRANT || defined _THREAD_SAFE) && _POSIX_THREADS > 0 #define DNS_THREAD_SAFE 1 #else #define DNS_THREAD_SAFE 0 #endif #endif #ifndef HAVE__STATIC_ASSERT #define HAVE__STATIC_ASSERT \ (dns_has_extension(c_static_assert) || DNS_GNUC_PREREQ(4,6,0) || \ __C11FEATURES__ || __STDC_VERSION__ >= 201112L) #endif #ifndef HAVE_STATIC_ASSERT #if DNS_GNUC_PREREQ(0,0,0) && !DNS_GNUC_PREREQ(4,6,0) #define HAVE_STATIC_ASSERT 0 /* glibc doesn't check GCC version */ #else #if (defined static_assert) #define HAVE_STATIC_ASSERT 1 #endif #endif #endif #if HAVE_STATIC_ASSERT #define dns_static_assert(cond, msg) static_assert(cond, msg) #elif HAVE__STATIC_ASSERT #define dns_static_assert(cond, msg) _Static_assert(cond, msg) #else #define dns_static_assert(cond, msg) extern char DNS_PP_XPASTE(dns_assert_, __LINE__)[sizeof (int[1 - 2*!(cond)])] #endif /* * D E B U G M A C R O S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int *dns_debug_p(void) { static int debug; return &debug; } /* dns_debug_p() */ #if DNS_DEBUG #define DNS_SAY_(fmt, ...) \ do { if (DNS_DEBUG > 0) fprintf(stderr, fmt "%.1s", __func__, __LINE__, __VA_ARGS__); } while (0) #define DNS_SAY(...) DNS_SAY_("@@ (%s:%d) " __VA_ARGS__, "\n") #define DNS_HAI DNS_SAY("HAI") #define DNS_SHOW_(P, fmt, ...) do { \ if (DNS_DEBUG >= 1) { \ fprintf(stderr, "@@ BEGIN * * * * * * * * * * * *\n"); \ fprintf(stderr, "@@ " fmt "%.0s\n", __VA_ARGS__); \ dns_p_dump((P), stderr); \ fprintf(stderr, "@@ END * * * * * * * * * * * * *\n\n"); \ } \ } while (0) #define DNS_SHOW(...) DNS_SHOW_(__VA_ARGS__, "") #else /* !DNS_DEBUG */ #undef DNS_DEBUG #define DNS_DEBUG 0 #define DNS_SAY(...) #define DNS_HAI #define DNS_SHOW(...) #endif /* DNS_DEBUG */ #define DNS_CARP(...) DNS_SAY(__VA_ARGS__) /* * V E R S I O N R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ const char *dns_vendor(void) { return DNS_VENDOR; } /* dns_vendor() */ int dns_v_rel(void) { return DNS_V_REL; } /* dns_v_rel() */ int dns_v_abi(void) { return DNS_V_ABI; } /* dns_v_abi() */ int dns_v_api(void) { return DNS_V_API; } /* dns_v_api() */ /* * E R R O R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if _WIN32 #define DNS_EINTR WSAEINTR #define DNS_EINPROGRESS WSAEINPROGRESS #define DNS_EISCONN WSAEISCONN #define DNS_EWOULDBLOCK WSAEWOULDBLOCK #define DNS_EALREADY WSAEALREADY #define DNS_EAGAIN EAGAIN #define DNS_ETIMEDOUT WSAETIMEDOUT #define DNS_ECONNREFUSED WSAECONNREFUSED #define DNS_ECONNRESET WSAECONNRESET #define DNS_ENETUNREACH WSAENETUNREACH #define DNS_EHOSTUNREACH WSAEHOSTUNREACH #define dns_syerr() ((int)GetLastError()) #define dns_soerr() ((int)WSAGetLastError()) #else #define DNS_EINTR EINTR #define DNS_EINPROGRESS EINPROGRESS #define DNS_EISCONN EISCONN #define DNS_EWOULDBLOCK EWOULDBLOCK #define DNS_EALREADY EALREADY #define DNS_EAGAIN EAGAIN #define DNS_ETIMEDOUT ETIMEDOUT #define DNS_ECONNREFUSED ECONNREFUSED #define DNS_ECONNRESET ECONNRESET #define DNS_ENETUNREACH ENETUNREACH #define DNS_EHOSTUNREACH EHOSTUNREACH #define dns_syerr() errno #define dns_soerr() errno #endif const char *dns_strerror(int error) { switch (error) { case DNS_ENOBUFS: return "DNS packet buffer too small"; case DNS_EILLEGAL: return "Illegal DNS RR name or data"; case DNS_EORDER: return "Attempt to push RR out of section order"; case DNS_ESECTION: return "Invalid section specified"; case DNS_EUNKNOWN: return "Unknown DNS error"; case DNS_EADDRESS: return "Invalid textual address form"; case DNS_ENOQUERY: return "Bad execution state (missing query packet)"; case DNS_ENOANSWER: return "Bad execution state (missing answer packet)"; case DNS_EFETCHED: return "Answer already fetched"; case DNS_ESERVICE: return "The service passed was not recognized for the specified socket type"; case DNS_ENONAME: return "The name does not resolve for the supplied parameters"; case DNS_EFAIL: return "A non-recoverable error occurred when attempting to resolve the name"; default: return strerror(error); } /* switch() */ } /* dns_strerror() */ /* * A T O M I C R O U T I N E S * * Use GCC's __atomic built-ins if possible. Unlike the __sync built-ins, we * can use the preprocessor to detect API and, more importantly, ISA * support. We want to avoid linking headaches where the API depends on an * external library if the ISA (e.g. i386) doesn't support lockless * operation. * * TODO: Support C11's atomic API. Although that may require some finesse * with how we define some public types, such as dns_atomic_t and struct * dns_resolv_conf. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef HAVE___ATOMIC_FETCH_ADD #if (defined __ATOMIC_RELAXED) #define HAVE___ATOMIC_FETCH_ADD 1 #endif #endif #ifndef HAVE___ATOMIC_FETCH_SUB #if (defined __ATOMIC_RELAXED) #define HAVE___ATOMIC_FETCH_SUB 1 #endif #endif #ifndef DNS_ATOMIC_FETCH_ADD #if HAVE___ATOMIC_FETCH_ADD && __GCC_ATOMIC_LONG_LOCK_FREE == 2 #define DNS_ATOMIC_FETCH_ADD(i) __atomic_fetch_add((i), 1, __ATOMIC_RELAXED) #else #pragma message("no atomic_fetch_add available") #define DNS_ATOMIC_FETCH_ADD(i) ((*(i))++) #endif #endif #ifndef DNS_ATOMIC_FETCH_SUB #if HAVE___ATOMIC_FETCH_SUB && __GCC_ATOMIC_LONG_LOCK_FREE == 2 #define DNS_ATOMIC_FETCH_SUB(i) __atomic_fetch_sub((i), 1, __ATOMIC_RELAXED) #else #pragma message("no atomic_fetch_sub available") #define DNS_ATOMIC_FETCH_SUB(i) ((*(i))--) #endif #endif static DNS_INLINE unsigned dns_atomic_fetch_add(dns_atomic_t *i) { return DNS_ATOMIC_FETCH_ADD(i); } /* dns_atomic_fetch_add() */ static DNS_INLINE unsigned dns_atomic_fetch_sub(dns_atomic_t *i) { return DNS_ATOMIC_FETCH_SUB(i); } /* dns_atomic_fetch_sub() */ /* * C R Y P T O R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * P R N G */ #ifndef DNS_RANDOM #if defined(HAVE_ARC4RANDOM) \ || defined(__OpenBSD__) \ || defined(__FreeBSD__) \ || defined(__NetBSD__) \ || defined(__APPLE__) #define DNS_RANDOM arc4random #elif __linux #define DNS_RANDOM random #else #define DNS_RANDOM rand #endif #endif #define DNS_RANDOM_arc4random 1 #define DNS_RANDOM_random 2 #define DNS_RANDOM_rand 3 #define DNS_RANDOM_RAND_bytes 4 #define DNS_RANDOM_OPENSSL (DNS_RANDOM_RAND_bytes == DNS_PP_XPASTE(DNS_RANDOM_, DNS_RANDOM)) #if DNS_RANDOM_OPENSSL #include #endif static unsigned dns_random_(void) { #if DNS_RANDOM_OPENSSL unsigned r; _Bool ok; ok = (1 == RAND_bytes((unsigned char *)&r, sizeof r)); assert(ok && "1 == RAND_bytes()"); return r; #else return DNS_RANDOM(); #endif } /* dns_random_() */ dns_random_f **dns_random_p(void) { static dns_random_f *random_f = &dns_random_; return &random_f; } /* dns_random_p() */ /* * P E R M U T A T I O N G E N E R A T O R */ #define DNS_K_TEA_KEY_SIZE 16 #define DNS_K_TEA_BLOCK_SIZE 8 #define DNS_K_TEA_CYCLES 32 #define DNS_K_TEA_MAGIC 0x9E3779B9U struct dns_k_tea { uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)]; unsigned cycles; }; /* struct dns_k_tea */ static void dns_k_tea_init(struct dns_k_tea *tea, uint32_t key[], unsigned cycles) { memcpy(tea->key, key, sizeof tea->key); tea->cycles = (cycles)? cycles : DNS_K_TEA_CYCLES; } /* dns_k_tea_init() */ static void dns_k_tea_encrypt(struct dns_k_tea *tea, uint32_t v[], uint32_t *w) { uint32_t y, z, sum, n; y = v[0]; z = v[1]; sum = 0; for (n = 0; n < tea->cycles; n++) { sum += DNS_K_TEA_MAGIC; y += ((z << 4) + tea->key[0]) ^ (z + sum) ^ ((z >> 5) + tea->key[1]); z += ((y << 4) + tea->key[2]) ^ (y + sum) ^ ((y >> 5) + tea->key[3]); } w[0] = y; w[1] = z; return /* void */; } /* dns_k_tea_encrypt() */ /* * Permutation generator, based on a Luby-Rackoff Feistel construction. * * Specifically, this is a generic balanced Feistel block cipher using TEA * (another block cipher) as the pseudo-random function, F. At best it's as * strong as F (TEA), notwithstanding the seeding. F could be AES, SHA-1, or * perhaps Bernstein's Salsa20 core; I am naively trying to keep things * simple. * * The generator can create a permutation of any set of numbers, as long as * the size of the set is an even power of 2. This limitation arises either * out of an inherent property of balanced Feistel constructions, or by my * own ignorance. I'll tackle an unbalanced construction after I wrap my * head around Schneier and Kelsey's paper. * * CAVEAT EMPTOR. IANAC. */ #define DNS_K_PERMUTOR_ROUNDS 8 struct dns_k_permutor { unsigned stepi, length, limit; unsigned shift, mask, rounds; struct dns_k_tea tea; }; /* struct dns_k_permutor */ static DNS_INLINE unsigned dns_k_permutor_powof(unsigned n) { unsigned m, i = 0; for (m = 1; m < n; m <<= 1, i++) ;; return i; } /* dns_k_permutor_powof() */ static void dns_k_permutor_init(struct dns_k_permutor *p, unsigned low, unsigned high) { uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)]; unsigned width, i; p->stepi = 0; p->length = (high - low) + 1; p->limit = high; width = dns_k_permutor_powof(p->length); width += width % 2; p->shift = width / 2; p->mask = (1U << p->shift) - 1; p->rounds = DNS_K_PERMUTOR_ROUNDS; for (i = 0; i < lengthof(key); i++) key[i] = dns_random(); dns_k_tea_init(&p->tea, key, 0); return /* void */; } /* dns_k_permutor_init() */ static unsigned dns_k_permutor_F(struct dns_k_permutor *p, unsigned k, unsigned x) { uint32_t in[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)], out[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)]; memset(in, '\0', sizeof in); in[0] = k; in[1] = x; dns_k_tea_encrypt(&p->tea, in, out); return p->mask & out[0]; } /* dns_k_permutor_F() */ static unsigned dns_k_permutor_E(struct dns_k_permutor *p, unsigned n) { unsigned l[2], r[2]; unsigned i; i = 0; l[i] = p->mask & (n >> p->shift); r[i] = p->mask & (n >> 0); do { l[(i + 1) % 2] = r[i % 2]; r[(i + 1) % 2] = l[i % 2] ^ dns_k_permutor_F(p, i, r[i % 2]); i++; } while (i < p->rounds - 1); return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0); } /* dns_k_permutor_E() */ DNS_NOTUSED static unsigned dns_k_permutor_D(struct dns_k_permutor *p, unsigned n) { unsigned l[2], r[2]; unsigned i; i = p->rounds - 1; l[i % 2] = p->mask & (n >> p->shift); r[i % 2] = p->mask & (n >> 0); do { i--; r[i % 2] = l[(i + 1) % 2]; l[i % 2] = r[(i + 1) % 2] ^ dns_k_permutor_F(p, i, l[(i + 1) % 2]); } while (i > 0); return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0); } /* dns_k_permutor_D() */ static unsigned dns_k_permutor_step(struct dns_k_permutor *p) { unsigned n; do { n = dns_k_permutor_E(p, p->stepi++); } while (n >= p->length); return n + (p->limit + 1 - p->length); } /* dns_k_permutor_step() */ /* * Simple permutation box. Useful for shuffling rrsets from an iterator. * Uses AES s-box to provide good diffusion. * * Seems to pass muster under runs test. * * $ for i in 0 1 2 3 4 5 6 7 8 9; do ./dns shuffle-16 > /tmp/out; done * $ R -q -f /dev/stdin 2>/dev/null <<-EOF | awk '/p-value/{ print $8 }' * library(lawstat) * runs.test(scan(file="/tmp/out")) * EOF */ static unsigned short dns_k_shuffle16(unsigned short n, unsigned s) { static const unsigned char sbox[256] = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; unsigned char a, b; unsigned i; a = 0xff & (n >> 0); b = 0xff & (n >> 8); for (i = 0; i < 4; i++) { a ^= 0xff & s; a = sbox[a] ^ b; b = sbox[b] ^ a; s >>= 8; } return ((0xff00 & (a << 8)) | (0x00ff & (b << 0))); } /* dns_k_shuffle16() */ /* * U T I L I T Y R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_MAXINTERVAL 300 struct dns_clock { time_t sample, elapsed; }; /* struct dns_clock */ static void dns_begin(struct dns_clock *clk) { clk->sample = time(0); clk->elapsed = 0; } /* dns_begin() */ static time_t dns_elapsed(struct dns_clock *clk) { time_t curtime; if ((time_t)-1 == time(&curtime)) return clk->elapsed; if (curtime > clk->sample) clk->elapsed += (time_t)DNS_PP_MIN(difftime(curtime, clk->sample), DNS_MAXINTERVAL); clk->sample = curtime; return clk->elapsed; } /* dns_elapsed() */ DNS_NOTUSED static size_t dns_strnlen(const char *src, size_t m) { size_t n = 0; while (*src++ && n < m) ++n; return n; } /* dns_strnlen() */ DNS_NOTUSED static size_t dns_strnlcpy(char *dst, size_t lim, const char *src, size_t max) { size_t len = dns_strnlen(src, max), n; if (lim > 0) { n = DNS_PP_MIN(lim - 1, len); memcpy(dst, src, n); dst[n] = '\0'; } return len; } /* dns_strnlcpy() */ #if (defined AF_UNIX && !defined _WIN32) #define DNS_HAVE_SOCKADDR_UN 1 #endif static size_t dns_af_len(int af) { static const size_t table[AF_MAX] = { [AF_INET6] = sizeof (struct sockaddr_in6), [AF_INET] = sizeof (struct sockaddr_in), #if DNS_HAVE_SOCKADDR_UN [AF_UNIX] = sizeof (struct sockaddr_un), #endif }; return table[af]; } /* dns_af_len() */ #define dns_sa_family(sa) (((struct sockaddr_storage *)(sa))->ss_family) #define dns_sa_len(sa) dns_af_len(dns_sa_family(sa)) #define DNS_SA_NOPORT &dns_sa_noport static unsigned short dns_sa_noport; static unsigned short *dns_sa_port(int af, void *sa) { switch (af) { case AF_INET6: return &((struct sockaddr_in6 *)sa)->sin6_port; case AF_INET: return &((struct sockaddr_in *)sa)->sin_port; default: return DNS_SA_NOPORT; } } /* dns_sa_port() */ static void *dns_sa_addr(int af, const void *sa, socklen_t *size) { switch (af) { case AF_INET6: { struct in6_addr *in6 = &((struct sockaddr_in6 *)sa)->sin6_addr; if (size) *size = sizeof *in6; return in6; } case AF_INET: { struct in_addr *in = &((struct sockaddr_in *)sa)->sin_addr; if (size) *size = sizeof *in; return in; } default: if (size) *size = 0; return 0; } } /* dns_sa_addr() */ #if DNS_HAVE_SOCKADDR_UN #define DNS_SUNPATHMAX (sizeof ((struct sockaddr_un *)0)->sun_path) #endif DNS_NOTUSED static void *dns_sa_path(void *sa, socklen_t *size) { switch (dns_sa_family(sa)) { #if DNS_HAVE_SOCKADDR_UN case AF_UNIX: { char *path = ((struct sockaddr_un *)sa)->sun_path; if (size) *size = dns_strnlen(path, DNS_SUNPATHMAX); return path; } #endif default: if (size) *size = 0; return NULL; } } /* dns_sa_path() */ static int dns_sa_cmp(void *a, void *b) { int cmp, af; if ((cmp = dns_sa_family(a) - dns_sa_family(b))) return cmp; switch ((af = dns_sa_family(a))) { case AF_INET: { struct in_addr *a4, *b4; if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b)))) return cmp; a4 = dns_sa_addr(af, a, NULL); b4 = dns_sa_addr(af, b, NULL); if (ntohl(a4->s_addr) < ntohl(b4->s_addr)) return -1; if (ntohl(a4->s_addr) > ntohl(b4->s_addr)) return 1; return 0; } case AF_INET6: { struct in6_addr *a6, *b6; size_t i; if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b)))) return cmp; a6 = dns_sa_addr(af, a, NULL); b6 = dns_sa_addr(af, b, NULL); /* XXX: do we need to use in6_clearscope()? */ for (i = 0; i < sizeof a6->s6_addr; i++) { if ((cmp = a6->s6_addr[i] - b6->s6_addr[i])) return cmp; } return 0; } #if DNS_HAVE_SOCKADDR_UN case AF_UNIX: { char a_path[DNS_SUNPATHMAX + 1], b_path[sizeof a_path]; dns_strnlcpy(a_path, sizeof a_path, dns_sa_path(a, NULL), DNS_SUNPATHMAX); dns_strnlcpy(b_path, sizeof b_path, dns_sa_path(b, NULL), DNS_SUNPATHMAX); return strcmp(a_path, b_path); } #endif default: return -1; } } /* dns_sa_cmp() */ #if _WIN32 static int dns_inet_pton(int af, const void *src, void *dst) { union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u; u.sin.sin_family = af; if (0 != WSAStringToAddressA((void *)src, af, (void *)0, (struct sockaddr *)&u, &(int){ sizeof u })) return -1; switch (af) { case AF_INET6: *(struct in6_addr *)dst = u.sin6.sin6_addr; return 1; case AF_INET: *(struct in_addr *)dst = u.sin.sin_addr; return 1; default: return 0; } } /* dns_inet_pton() */ static const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim) { union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u; /* NOTE: WSAAddressToString will print .sin_port unless zeroed. */ memset(&u, 0, sizeof u); u.sin.sin_family = af; switch (af) { case AF_INET6: u.sin6.sin6_addr = *(struct in6_addr *)src; break; case AF_INET: u.sin.sin_addr = *(struct in_addr *)src; break; default: return 0; } if (0 != WSAAddressToStringA((struct sockaddr *)&u, dns_sa_len(&u), (void *)0, dst, &lim)) return 0; return dst; } /* dns_inet_ntop() */ #else #define dns_inet_pton(...) inet_pton(__VA_ARGS__) #define dns_inet_ntop(...) inet_ntop(__VA_ARGS__) #endif static dns_error_t dns_pton(int af, const void *src, void *dst) { switch (dns_inet_pton(af, src, dst)) { case 1: return 0; case -1: return dns_soerr(); default: return DNS_EADDRESS; } } /* dns_pton() */ static dns_error_t dns_ntop(int af, const void *src, void *dst, unsigned long lim) { return (dns_inet_ntop(af, src, dst, lim))? 0 : dns_soerr(); } /* dns_ntop() */ size_t dns_strlcpy(char *dst, const char *src, size_t lim) { char *d = dst; char *e = &dst[lim]; const char *s = src; if (d < e) { do { if ('\0' == (*d++ = *s++)) return s - src - 1; } while (d < e); d[-1] = '\0'; } while (*s++ != '\0') ;; return s - src - 1; } /* dns_strlcpy() */ size_t dns_strlcat(char *dst, const char *src, size_t lim) { char *d = memchr(dst, '\0', lim); char *e = &dst[lim]; const char *s = src; const char *p; if (d && d < e) { do { if ('\0' == (*d++ = *s++)) return d - dst - 1; } while (d < e); d[-1] = '\0'; } p = s; while (*s++ != '\0') ;; return lim + (s - p - 1); } /* dns_strlcat() */ #if _WIN32 static char *dns_strsep(char **sp, const char *delim) { char *p; if (!(p = *sp)) return 0; *sp += strcspn(p, delim); if (**sp != '\0') { **sp = '\0'; ++*sp; } else *sp = NULL; return p; } /* dns_strsep() */ #else #define dns_strsep(...) strsep(__VA_ARGS__) #endif #if _WIN32 #ifndef strcasecmp #define strcasecmp(...) _stricmp(__VA_ARGS__) #endif #ifndef strncasecmp #define strncasecmp(...) _strnicmp(__VA_ARGS__) #endif #endif static DNS_INLINE _Bool dns_isalpha(unsigned char c) { return isalpha(c); } /* dns_isalpha() */ static DNS_INLINE _Bool dns_isdigit(unsigned char c) { return isdigit(c); } /* dns_isdigit() */ static DNS_INLINE _Bool dns_isalnum(unsigned char c) { return isalnum(c); } /* dns_isalnum() */ static DNS_INLINE _Bool dns_isspace(unsigned char c) { return isspace(c); } /* dns_isspace() */ static int dns_poll(int fd, short events, int timeout) { /* For our usage in belle sip, we are called by the main loop when something is to read on the dns socket. * As a result there is no need to perform a blocking select. **/ #if 0 fd_set rset, wset; if (!events) return 0; assert(fd >= 0 && (unsigned)fd < FD_SETSIZE); FD_ZERO(&rset); FD_ZERO(&wset); if (events & DNS_POLLIN) FD_SET(fd, &rset); if (events & DNS_POLLOUT) FD_SET(fd, &wset); select(fd + 1, &rset, &wset, 0, (timeout >= 0)? &(struct timeval){ timeout, 0 } : NULL); #endif return 0; } /* dns_poll() */ #if !_WIN32 DNS_NOTUSED static int dns_sigmask(int how, const sigset_t *set, sigset_t *oset) { #if DNS_THREAD_SAFE return pthread_sigmask(how, set, oset); #else return (0 == sigprocmask(how, set, oset))? 0 : errno; #endif } /* dns_sigmask() */ #endif static long dns_send(int fd, const void *src, size_t lim, int flags) { #if _WIN32 || !defined SIGPIPE || defined SO_NOSIGPIPE return send(fd, src, lim, flags); #elif defined MSG_NOSIGNAL return send(fd, src, lim, flags|MSG_NOSIGNAL); #elif _POSIX_REALTIME_SIGNALS > 0 /* require sigtimedwait */ /* * SIGPIPE handling similar to the approach described in * http://krokisplace.blogspot.com/2010/02/suppressing-sigpipe-in-library.html */ sigset_t pending, blocked, piped; long count; int saved, error; sigemptyset(&pending); sigpending(&pending); if (!sigismember(&pending, SIGPIPE)) { sigemptyset(&piped); sigaddset(&piped, SIGPIPE); sigemptyset(&blocked); if ((error = dns_sigmask(SIG_BLOCK, &piped, &blocked))) goto error; } count = send(fd, src, lim, flags); if (!sigismember(&pending, SIGPIPE)) { saved = errno; if (count == -1 && errno == EPIPE) { while (-1 == sigtimedwait(&piped, NULL, &(struct timespec){ 0, 0 }) && errno == EINTR) ;; } if ((error = dns_sigmask(SIG_SETMASK, &blocked, NULL))) goto error; errno = saved; } return count; error: errno = error; return -1; #else #error "unable to suppress SIGPIPE" return send(fd, src, lim, flags); #endif } /* dns_send() */ #define DNS_FOPEN_STDFLAGS "rwabt+" static dns_error_t dns_fopen_addflag(char *dst, const char *src, size_t lim, int fc) { char *p = dst, *pe = dst + lim; /* copy standard flags */ while (*src && strchr(DNS_FOPEN_STDFLAGS, *src)) { if (!(p < pe)) return ENOMEM; *p++ = *src++; } /* append flag to standard flags */ if (!(p < pe)) return ENOMEM; *p++ = fc; /* copy remaining mode string, including '\0' */ do { if (!(p < pe)) return ENOMEM; } while ((*p++ = *src++)); return 0; } /* dns_fopen_addflag() */ static FILE *dns_fopen(const char *path, const char *mode, dns_error_t *_error) { FILE *fp; char mode_cloexec[32]; int error; assert(path && mode && *mode); if (!*path) { error = EINVAL; goto error; } #if _WIN32 || _WIN64 if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'N'))) goto error; if (!(fp = fopen(path, mode_cloexec))) goto syerr; #else if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'e'))) goto error; if (!(fp = fopen(path, mode_cloexec))) { if (errno != EINVAL) goto syerr; if (!(fp = fopen(path, mode))) goto syerr; } #endif return fp; syerr: error = dns_syerr(); error: *_error = error; return NULL; } /* dns_fopen() */ /* * F I X E D - S I Z E D B U F F E R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_B_INIT(src, n) { \ (unsigned char *)(src), \ (unsigned char *)(src), \ (unsigned char *)(src) + (n), \ } #define DNS_B_FROM(src, n) DNS_B_INIT((src), (n)) #define DNS_B_INTO(src, n) DNS_B_INIT((src), (n)) struct dns_buf { const unsigned char *base; unsigned char *p; const unsigned char *pe; dns_error_t error; size_t overflow; }; /* struct dns_buf */ static DNS_INLINE size_t dns_b_tell(struct dns_buf *b) { return b->p - b->base; } static DNS_INLINE dns_error_t dns_b_setoverflow(struct dns_buf *b, size_t n, dns_error_t error) { b->overflow += n; return b->error = error; } DNS_NOTUSED static struct dns_buf * dns_b_into(struct dns_buf *b, void *src, size_t n) { *b = (struct dns_buf)DNS_B_INTO(src, n); return b; } static dns_error_t dns_b_putc(struct dns_buf *b, unsigned char uc) { if (!(b->p < b->pe)) return dns_b_setoverflow(b, 1, DNS_ENOBUFS); *b->p++ = uc; return 0; } static dns_error_t dns_b_pputc(struct dns_buf *b, unsigned char uc, size_t p) { size_t pe = b->pe - b->base; if (pe <= p) return dns_b_setoverflow(b, p - pe + 1, DNS_ENOBUFS); *((unsigned char *)b->base + p) = uc; return 0; } static DNS_INLINE dns_error_t dns_b_put16(struct dns_buf *b, uint16_t u) { return dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0); } static DNS_INLINE dns_error_t dns_b_pput16(struct dns_buf *b, uint16_t u, size_t p) { if (dns_b_pputc(b, u >> 8, p) || dns_b_pputc(b, u >> 0, p + 1)) return b->error; return 0; } DNS_NOTUSED static DNS_INLINE dns_error_t dns_b_put32(struct dns_buf *b, uint32_t u) { return dns_b_putc(b, u >> 24), dns_b_putc(b, u >> 16), dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0); } static dns_error_t dns_b_put(struct dns_buf *b, const void *src, size_t len) { size_t n = DNS_PP_MIN((size_t)(b->pe - b->p), len); memcpy(b->p, src, n); b->p += n; if (n < len) return dns_b_setoverflow(b, len - n, DNS_ENOBUFS); return 0; } static dns_error_t dns_b_puts(struct dns_buf *b, const void *src) { return dns_b_put(b, src, strlen(src)); } DNS_NOTUSED static DNS_INLINE dns_error_t dns_b_fmtju(struct dns_buf *b, const uintmax_t u, const unsigned width) { size_t digits, padding, overflow; uintmax_t r; unsigned char *tp, *te, tc; digits = 0; r = u; do { digits++; r /= 10; } while (r); padding = width - DNS_PP_MIN(digits, width); overflow = (digits + padding) - DNS_PP_MIN((size_t)(b->pe - b->p), (digits + padding)); while (padding--) { dns_b_putc(b, '0'); } digits = 0; tp = b->p; r = u; do { if (overflow < ++digits) dns_b_putc(b, '0' + (r % 10)); r /= 10; } while (r); te = b->p; while (tp < te) { tc = *--te; *te = *tp; *tp++ = tc; } return b->error; } static void dns_b_popc(struct dns_buf *b) { if (b->overflow && !--b->overflow) b->error = 0; if (b->p > b->base) b->p--; } static DNS_INLINE const char * dns_b_tolstring(struct dns_buf *b, size_t *n) { if (b->p < b->pe) { *b->p = '\0'; *n = b->p - b->base; return (const char *)b->base; } else if (b->p > b->base) { if (b->p[-1] != '\0') { dns_b_setoverflow(b, 1, DNS_ENOBUFS); b->p[-1] = '\0'; } *n = &b->p[-1] - b->base; return (const char *)b->base; } else { *n = 0; return ""; } } static DNS_INLINE const char * dns_b_tostring(struct dns_buf *b) { size_t n; return dns_b_tolstring(b, &n); } static DNS_INLINE size_t dns_b_strlen(struct dns_buf *b) { size_t n; dns_b_tolstring(b, &n); return n; } static DNS_INLINE size_t dns_b_strllen(struct dns_buf *b) { size_t n = dns_b_strlen(b); return n + b->overflow; } DNS_NOTUSED static const struct dns_buf * dns_b_from(const struct dns_buf *b, const void *src, size_t n) { *(struct dns_buf *)b = (struct dns_buf)DNS_B_FROM(src, n); return b; } static DNS_INLINE int dns_b_getc(const struct dns_buf *_b, const int eof) { struct dns_buf *b = (struct dns_buf *)_b; if (!(b->p < b->pe)) return dns_b_setoverflow(b, 1, DNS_EILLEGAL), eof; return *b->p++; } static DNS_INLINE intmax_t dns_b_get16(const struct dns_buf *b, const intmax_t eof) { intmax_t n; n = (dns_b_getc(b, 0) << 8); n |= (dns_b_getc(b, 0) << 0); return (!b->overflow)? n : eof; } DNS_NOTUSED static DNS_INLINE intmax_t dns_b_get32(const struct dns_buf *b, const intmax_t eof) { intmax_t n; n = (dns_b_get16(b, 0) << 16); n |= (dns_b_get16(b, 0) << 0); return (!b->overflow)? n : eof; } static DNS_INLINE dns_error_t dns_b_move(struct dns_buf *dst, const struct dns_buf *_src, size_t n) { struct dns_buf *src = (struct dns_buf *)_src; size_t src_n = DNS_PP_MIN((size_t)(src->pe - src->p), n); size_t src_r = n - src_n; dns_b_put(dst, src->p, src_n); src->p += src_n; if (src_r) return dns_b_setoverflow(src, src_r, DNS_EILLEGAL); return dst->error; } /* * P A C K E T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ unsigned dns_p_count(struct dns_packet *P, enum dns_section section) { unsigned count; switch (section) { case DNS_S_QD: return ntohs(dns_header(P)->qdcount); case DNS_S_AN: return ntohs(dns_header(P)->ancount); case DNS_S_NS: return ntohs(dns_header(P)->nscount); case DNS_S_AR: return ntohs(dns_header(P)->arcount); default: count = 0; if (section & DNS_S_QD) count += ntohs(dns_header(P)->qdcount); if (section & DNS_S_AN) count += ntohs(dns_header(P)->ancount); if (section & DNS_S_NS) count += ntohs(dns_header(P)->nscount); if (section & DNS_S_AR) count += ntohs(dns_header(P)->arcount); return count; } } /* dns_p_count() */ struct dns_packet *dns_p_init(struct dns_packet *P, size_t size) { if (!P) return 0; assert(size >= offsetof(struct dns_packet, data) + 12); memset(P, 0, sizeof *P); P->size = size - offsetof(struct dns_packet, data); P->end = 12; memset(P->data, '\0', 12); return P; } /* dns_p_init() */ static struct dns_packet *dns_p_reset(struct dns_packet *P) { return dns_p_init(P, offsetof(struct dns_packet, data) + P->size); } /* dns_p_reset() */ static unsigned short dns_p_qend(struct dns_packet *P) { unsigned short qend = 12; unsigned i, count = dns_p_count(P, DNS_S_QD); for (i = 0; i < count && qend < P->end; i++) { if (P->end == (qend = dns_d_skip(qend, P))) goto invalid; if (P->end - qend < 4) goto invalid; qend += 4; } return DNS_PP_MIN(qend, P->end); invalid: return P->end; } /* dns_p_qend() */ struct dns_packet *dns_p_make(size_t len, int *error) { struct dns_packet *P; size_t size = dns_p_calcsize(len); if (!(P = dns_p_init(malloc(size), size))) *error = dns_syerr(); return P; } /* dns_p_make() */ static void dns_p_free(struct dns_packet *P) { free(P); } /* dns_p_free() */ /* convience routine to free any existing packet before storing new packet */ static struct dns_packet *dns_p_setptr(struct dns_packet **dst, struct dns_packet *src) { dns_p_free(*dst); *dst = src; return src; } /* dns_p_setptr() */ static struct dns_packet *dns_p_movptr(struct dns_packet **dst, struct dns_packet **src) { dns_p_setptr(dst, *src); *src = NULL; return *dst; } /* dns_p_movptr() */ int dns_p_grow(struct dns_packet **P) { struct dns_packet *tmp; size_t size; int error; if (!*P) { if (!(*P = dns_p_make(DNS_P_QBUFSIZ, &error))) return error; return 0; } size = dns_p_sizeof(*P); size |= size >> 1; size |= size >> 2; size |= size >> 4; size |= size >> 8; size++; if (size > 65536) return DNS_ENOBUFS; if (!(tmp = realloc(*P, dns_p_calcsize(size)))) return dns_syerr(); tmp->size = size; *P = tmp; return 0; } /* dns_p_grow() */ struct dns_packet *dns_p_copy(struct dns_packet *P, const struct dns_packet *P0) { if (!P) return 0; P->end = DNS_PP_MIN(P->size, P0->end); memcpy(P->data, P0->data, P->end); return P; } /* dns_p_copy() */ struct dns_packet *dns_p_merge(struct dns_packet *A, enum dns_section Amask, struct dns_packet *B, enum dns_section Bmask, int *error_) { size_t bufsiz = DNS_PP_MIN(65535, ((A)? A->end : 0) + ((B)? B->end : 0)); struct dns_packet *M; enum dns_section section; struct dns_rr rr, mr; int error, copy; if (!A && B) { A = B; Amask = Bmask; B = 0; } merge: if (!(M = dns_p_make(bufsiz, &error))) goto error; for (section = DNS_S_QD; (DNS_S_ALL & section); section <<= 1) { if (A && (section & Amask)) { dns_rr_foreach(&rr, A, .section = section) { if ((error = dns_rr_copy(M, &rr, A))) goto error; } } if (B && (section & Bmask)) { dns_rr_foreach(&rr, B, .section = section) { copy = 1; dns_rr_foreach(&mr, M, .type = rr.type, .section = DNS_S_ALL) { if (!(copy = dns_rr_cmp(&rr, B, &mr, M))) break; } if (copy && (error = dns_rr_copy(M, &rr, B))) goto error; } } } return M; error: dns_p_setptr(&M, NULL); if (error == DNS_ENOBUFS && bufsiz < 65535) { bufsiz = DNS_PP_MIN(65535, bufsiz * 2); goto merge; } *error_ = error; return 0; } /* dns_p_merge() */ static unsigned short dns_l_skip(unsigned short, const unsigned char *, size_t); void dns_p_dictadd(struct dns_packet *P, unsigned short dn) { unsigned short lp, lptr, i; lp = dn; while (lp < P->end) { if (0xc0 == (0xc0 & P->data[lp]) && P->end - lp >= 2 && lp != dn) { lptr = ((0x3f & P->data[lp + 0]) << 8) | ((0xff & P->data[lp + 1]) << 0); for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) { if (P->dict[i] == lptr) { P->dict[i] = dn; return; } } } lp = dns_l_skip(lp, P->data, P->end); } for (i = 0; i < lengthof(P->dict); i++) { if (!P->dict[i]) { P->dict[i] = dn; break; } } } /* dns_p_dictadd() */ int dns_p_push(struct dns_packet *P, enum dns_section section, const void *dn, size_t dnlen, enum dns_type type, enum dns_class class, unsigned ttl, const void *any) { size_t end = P->end; int error; if ((error = dns_d_push(P, dn, dnlen))) goto error; if (P->size - P->end < 4) goto nobufs; P->data[P->end++] = 0xff & (type >> 8); P->data[P->end++] = 0xff & (type >> 0); P->data[P->end++] = 0xff & (class >> 8); P->data[P->end++] = 0xff & (class >> 0); if (section == DNS_S_QD) goto update; if (P->size - P->end < 6) goto nobufs; if (type != DNS_T_OPT) ttl = DNS_PP_MIN(ttl, 0x7fffffffU); P->data[P->end++] = ttl >> 24; P->data[P->end++] = ttl >> 16; P->data[P->end++] = ttl >> 8; P->data[P->end++] = ttl >> 0; if ((error = dns_any_push(P, (union dns_any *)any, type))) goto error; update: switch (section) { case DNS_S_QD: if (dns_p_count(P, DNS_S_AN|DNS_S_NS|DNS_S_AR)) goto order; if (!P->memo.qd.base && (error = dns_p_study(P))) goto error; dns_header(P)->qdcount = htons(ntohs(dns_header(P)->qdcount) + 1); P->memo.qd.end = P->end; P->memo.an.base = P->end; P->memo.an.end = P->end; P->memo.ns.base = P->end; P->memo.ns.end = P->end; P->memo.ar.base = P->end; P->memo.ar.end = P->end; break; case DNS_S_AN: if (dns_p_count(P, DNS_S_NS|DNS_S_AR)) goto order; if (!P->memo.an.base && (error = dns_p_study(P))) goto error; dns_header(P)->ancount = htons(ntohs(dns_header(P)->ancount) + 1); P->memo.an.end = P->end; P->memo.ns.base = P->end; P->memo.ns.end = P->end; P->memo.ar.base = P->end; P->memo.ar.end = P->end; break; case DNS_S_NS: if (dns_p_count(P, DNS_S_AR)) goto order; if (!P->memo.ns.base && (error = dns_p_study(P))) goto error; dns_header(P)->nscount = htons(ntohs(dns_header(P)->nscount) + 1); P->memo.ns.end = P->end; P->memo.ar.base = P->end; P->memo.ar.end = P->end; break; case DNS_S_AR: if (!P->memo.ar.base && (error = dns_p_study(P))) goto error; dns_header(P)->arcount = htons(ntohs(dns_header(P)->arcount) + 1); P->memo.ar.end = P->end; if (type == DNS_T_OPT && !P->memo.opt.p) { P->memo.opt.p = end; P->memo.opt.maxudp = class; P->memo.opt.ttl = ttl; } break; default: error = DNS_ESECTION; goto error; } /* switch() */ return 0; nobufs: error = DNS_ENOBUFS; goto error; order: error = DNS_EORDER; goto error; error: P->end = end; return error; } /* dns_p_push() */ static void dns_p_dump3(struct dns_packet *P, struct dns_rr_i *I, FILE *fp) { enum dns_section section; struct dns_rr rr; int error; union dns_any any; char pretty[sizeof any * 2]; size_t len; fputs(";; [HEADER]\n", fp); fprintf(fp, ";; qid : %d\n", ntohs(dns_header(P)->qid)); fprintf(fp, ";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr); fprintf(fp, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode); fprintf(fp, ";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa); fprintf(fp, ";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc); fprintf(fp, ";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd); fprintf(fp, ";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra); fprintf(fp, ";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P)); section = 0; while (dns_rr_grep(&rr, 1, I, P, &error)) { if (section != rr.section) fprintf(fp, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section)); if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error))) fprintf(fp, "%s\n", pretty); section = rr.section; } } /* dns_p_dump3() */ void dns_p_dump(struct dns_packet *P, FILE *fp) { dns_p_dump3(P, dns_rr_i_new(P, .section = 0), fp); } /* dns_p_dump() */ static void dns_s_unstudy(struct dns_s_memo *m) { m->base = 0; m->end = 0; } static void dns_m_unstudy(struct dns_p_memo *m) { dns_s_unstudy(&m->qd); dns_s_unstudy(&m->an); dns_s_unstudy(&m->ns); dns_s_unstudy(&m->ar); m->opt.p = 0; m->opt.maxudp = 0; m->opt.ttl = 0; } /* dns_m_unstudy() */ static int dns_s_study(struct dns_s_memo *m, enum dns_section section, unsigned short base, struct dns_packet *P) { unsigned short count, rp; count = dns_p_count(P, section); for (rp = base; count && rp < P->end; count--) rp = dns_rr_skip(rp, P); m->base = base; m->end = rp; return 0; } /* dns_s_study() */ static int dns_m_study(struct dns_p_memo *m, struct dns_packet *P) { struct dns_rr rr; int error; if ((error = dns_s_study(&m->qd, DNS_S_QD, 12, P))) goto error; if ((error = dns_s_study(&m->an, DNS_S_AN, m->qd.end, P))) goto error; if ((error = dns_s_study(&m->ns, DNS_S_NS, m->an.end, P))) goto error; if ((error = dns_s_study(&m->ar, DNS_S_AR, m->ns.end, P))) goto error; m->opt.p = 0; m->opt.maxudp = 0; m->opt.ttl = 0; dns_rr_foreach(&rr, P, .type = DNS_T_OPT, .section = DNS_S_AR) { m->opt.p = rr.dn.p; m->opt.maxudp = rr.class; m->opt.ttl = rr.ttl; break; } return 0; error: dns_m_unstudy(m); return error; } /* dns_m_study() */ int dns_p_study(struct dns_packet *P) { return dns_m_study(&P->memo, P); } /* dns_p_study() */ enum dns_rcode dns_p_rcode(struct dns_packet *P) { return 0xfff & ((P->memo.opt.ttl >> 20) | dns_header(P)->rcode); } /* dns_p_rcode() */ /* * Q U E R Y P A C K E T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_Q_RD 0x1 /* recursion desired */ #define DNS_Q_EDNS0 0x2 /* include OPT RR */ static dns_error_t dns_q_make2(struct dns_packet **_Q, const char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass, int qflags) { struct dns_packet *Q = NULL; int error; if (dns_p_movptr(&Q, _Q)) { dns_p_reset(Q); } else if (!(Q = dns_p_make(DNS_P_QBUFSIZ, &error))) { goto error; } if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, qtype, qclass, 0, 0))) goto error; dns_header(Q)->rd = !!(qflags & DNS_Q_RD); if (qflags & DNS_Q_EDNS0) { struct dns_opt opt = DNS_OPT_INIT(&opt); opt.version = 0; /* RFC 6891 version */ opt.maxudp = 4096; if ((error = dns_p_push(Q, DNS_S_AR, ".", 1, DNS_T_OPT, dns_opt_class(&opt), dns_opt_ttl(&opt), &opt))) goto error; } *_Q = Q; return 0; error: dns_p_free(Q); return error; } static dns_error_t dns_q_make(struct dns_packet **Q, const char *qname, enum dns_type qtype, enum dns_class qclass, int qflags) { return dns_q_make2(Q, qname, strlen(qname), qtype, qclass, qflags); } static dns_error_t dns_q_remake(struct dns_packet **Q, int qflags) { char qname[DNS_D_MAXNAME + 1]; size_t qlen; struct dns_rr rr; int error; assert(Q && *Q); if ((error = dns_rr_parse(&rr, 12, *Q))) return error; if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, *Q, &error))) return error; if (qlen >= sizeof qname) return DNS_EILLEGAL; return dns_q_make2(Q, qname, qlen, rr.type, rr.class, qflags); } /* * D O M A I N N A M E R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef DNS_D_MAXPTRS #define DNS_D_MAXPTRS 127 /* Arbitrary; possible, valid depth is something like packet size / 2 + fudge. */ #endif static size_t dns_l_expand(unsigned char *dst, size_t lim, unsigned short src, unsigned short *nxt, const unsigned char *data, size_t end) { unsigned short len; unsigned nptrs = 0; retry: if (src >= end) goto invalid; switch (0x03 & (data[src] >> 6)) { case 0x00: len = (0x3f & (data[src++])); if (end - src < len) goto invalid; if (lim > 0) { memcpy(dst, &data[src], DNS_PP_MIN(lim, len)); dst[DNS_PP_MIN(lim - 1, len)] = '\0'; } *nxt = src + len; return len; case 0x01: goto invalid; case 0x02: goto invalid; case 0x03: if (++nptrs > DNS_D_MAXPTRS) goto invalid; if (end - src < 2) goto invalid; src = ((0x3f & data[src + 0]) << 8) | ((0xff & data[src + 1]) << 0); goto retry; } /* switch() */ /* NOT REACHED */ invalid: *nxt = end; return 0; } /* dns_l_expand() */ static unsigned short dns_l_skip(unsigned short src, const unsigned char *data, size_t end) { unsigned short len; if (src >= end) goto invalid; switch (0x03 & (data[src] >> 6)) { case 0x00: len = (0x3f & (data[src++])); if (end - src < len) goto invalid; return (len)? src + len : end; case 0x01: goto invalid; case 0x02: goto invalid; case 0x03: return end; } /* switch() */ /* NOT REACHED */ invalid: return end; } /* dns_l_skip() */ static size_t dns_d_trim(void *dst_, size_t lim, const void *src_, size_t len, int flags) { unsigned char *dst = dst_; const unsigned char *src = src_; size_t dp = 0, sp = 0; int lc; /* trim any leading dot(s) */ while (sp < len && src[sp] == '.') sp++; for (lc = 0; sp < len; lc = src[sp++]) { /* trim extra dot(s) */ if (src[sp] == '.' && lc == '.') continue; if (dp < lim) dst[dp] = src[sp]; dp++; } if ((flags & DNS_D_ANCHOR) && lc != '.') { if (dp < lim) dst[dp] = '.'; dp++; } if (lim > 0) dst[DNS_PP_MIN(dp, lim - 1)] = '\0'; return dp; } /* dns_d_trim() */ char *dns_d_init(void *dst, size_t lim, const void *src, size_t len, int flags) { if (flags & DNS_D_TRIM) { dns_d_trim(dst, lim, src, len, flags); } if (flags & DNS_D_ANCHOR) { dns_d_anchor(dst, lim, src, len); } else { memmove(dst, src, DNS_PP_MIN(lim, len)); if (lim > 0) ((char *)dst)[DNS_PP_MIN(len, lim - 1)] = '\0'; } return dst; } /* dns_d_init() */ size_t dns_d_anchor(void *dst, size_t lim, const void *src, size_t len) { if (len == 0) return 0; memmove(dst, src, DNS_PP_MIN(lim, len)); if (((const char *)src)[len - 1] != '.') { if (len < lim) ((char *)dst)[len] = '.'; len++; } if (lim > 0) ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0'; return len; } /* dns_d_anchor() */ size_t dns_d_cleave(void *dst, size_t lim, const void *src, size_t len) { const char *dot; /* XXX: Skip any leading dot. Handles cleaving root ".". */ if (len == 0 || !(dot = memchr((const char *)src + 1, '.', len - 1))) return 0; len -= dot - (const char *)src; /* XXX: Unless root, skip the label's trailing dot. */ if (len > 1) { src = ++dot; len--; } else src = dot; memmove(dst, src, DNS_PP_MIN(lim, len)); if (lim > 0) ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0'; return len; } /* dns_d_cleave() */ size_t dns_d_comp(void *dst_, size_t lim, const void *src_, size_t len, struct dns_packet *P, int *error) { struct { unsigned char *b; size_t p, x; } dst, src; unsigned char ch = '.'; dst.b = dst_; dst.p = 0; dst.x = 1; src.b = (unsigned char *)src_; src.p = 0; src.x = 0; while (src.x < len) { ch = src.b[src.x]; if (ch == '.') { if (dst.p < lim) dst.b[dst.p] = (0x3f & (src.x - src.p)); dst.p = dst.x++; src.p = ++src.x; } else { if (dst.x < lim) dst.b[dst.x] = ch; dst.x++; src.x++; } } /* while() */ if (src.x > src.p) { if (dst.p < lim) dst.b[dst.p] = (0x3f & (src.x - src.p)); dst.p = dst.x; } if (dst.p > 1) { if (dst.p < lim) dst.b[dst.p] = 0x00; dst.p++; } #if 1 if (dst.p < lim) { struct { unsigned char label[DNS_D_MAXLABEL + 1]; size_t len; unsigned short p, x, y; } a, b; unsigned i; a.p = 0; while ((a.len = dns_l_expand(a.label, sizeof a.label, a.p, &a.x, dst.b, lim))) { for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) { b.p = P->dict[i]; while ((b.len = dns_l_expand(b.label, sizeof b.label, b.p, &b.x, P->data, P->end))) { a.y = a.x; b.y = b.x; while (a.len && b.len && 0 == strcasecmp((char *)a.label, (char *)b.label)) { a.len = dns_l_expand(a.label, sizeof a.label, a.y, &a.y, dst.b, lim); b.len = dns_l_expand(b.label, sizeof b.label, b.y, &b.y, P->data, P->end); } if (a.len == 0 && b.len == 0 && b.p <= 0x3fff) { dst.b[a.p++] = 0xc0 | (0x3f & (b.p >> 8)); dst.b[a.p++] = (0xff & (b.p >> 0)); /* silence static analyzers */ dns_assume(a.p > 0); return a.p; } b.p = b.x; } /* while() */ } /* for() */ a.p = a.x; } /* while() */ } /* if () */ #endif if (!dst.p) *error = DNS_EILLEGAL; return dst.p; } /* dns_d_comp() */ unsigned short dns_d_skip(unsigned short src, struct dns_packet *P) { unsigned short len; while (src < P->end) { switch (0x03 & (P->data[src] >> 6)) { case 0x00: /* FOLLOWS */ len = (0x3f & P->data[src++]); if (0 == len) { /* success ==> */ return src; } else if (P->end - src > len) { src += len; break; } else goto invalid; /* NOT REACHED */ case 0x01: /* RESERVED */ goto invalid; case 0x02: /* RESERVED */ goto invalid; case 0x03: /* POINTER */ if (P->end - src < 2) goto invalid; src += 2; /* success ==> */ return src; } /* switch() */ } /* while() */ invalid: return P->end; } /* dns_d_skip() */ #include size_t dns_d_expand(void *dst, size_t lim, unsigned short src, struct dns_packet *P, int *error) { size_t dstp = 0; unsigned nptrs = 0; unsigned char len; while (src < P->end) { switch ((0x03 & (P->data[src] >> 6))) { case 0x00: /* FOLLOWS */ len = (0x3f & P->data[src]); if (0 == len) { if (dstp == 0) { if (dstp < lim) ((unsigned char *)dst)[dstp] = '.'; dstp++; } /* NUL terminate */ if (lim > 0) ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; /* success ==> */ return dstp; } src++; if (P->end - src < len) goto toolong; if (dstp < lim) memcpy(&((unsigned char *)dst)[dstp], &P->data[src], DNS_PP_MIN(len, lim - dstp)); src += len; dstp += len; if (dstp < lim) ((unsigned char *)dst)[dstp] = '.'; dstp++; nptrs = 0; continue; case 0x01: /* RESERVED */ goto reserved; case 0x02: /* RESERVED */ goto reserved; case 0x03: /* POINTER */ if (++nptrs > DNS_D_MAXPTRS) goto toolong; if (P->end - src < 2) goto toolong; src = ((0x3f & P->data[src + 0]) << 8) | ((0xff & P->data[src + 1]) << 0); continue; } /* switch() */ } /* while() */ toolong: *error = DNS_EILLEGAL; if (lim > 0) ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; return 0; reserved: *error = DNS_EILLEGAL; if (lim > 0) ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; return 0; } /* dns_d_expand() */ int dns_d_push(struct dns_packet *P, const void *dn, size_t len) { size_t lim = P->size - P->end; unsigned dp = P->end; int error = DNS_EILLEGAL; /* silence compiler */ len = dns_d_comp(&P->data[dp], lim, dn, len, P, &error); if (len == 0) return error; if (len > lim) return DNS_ENOBUFS; P->end += len; dns_p_dictadd(P, dp); return 0; } /* dns_d_push() */ size_t dns_d_cname(void *dst, size_t lim, const void *dn, size_t len, struct dns_packet *P, int *error_) { char host[DNS_D_MAXNAME + 1]; struct dns_rr_i i; struct dns_rr rr; unsigned depth; int error; if (sizeof host <= dns_d_anchor(host, sizeof host, dn, len)) { error = ENAMETOOLONG; goto error; } for (depth = 0; depth < 7; depth++) { dns_rr_i_init(memset(&i, 0, sizeof i), P); i.section = DNS_S_ALL & ~DNS_S_QD; i.name = host; i.type = DNS_T_CNAME; if (!dns_rr_grep(&rr, 1, &i, P, &error)) break; if ((error = dns_cname_parse((struct dns_cname *)host, &rr, P))) goto error; } return dns_strlcpy(dst, host, lim); error: *error_ = error; return 0; } /* dns_d_cname() */ /* * R E S O U R C E R E C O R D R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int dns_rr_copy(struct dns_packet *P, struct dns_rr *rr, struct dns_packet *Q) { unsigned char dn[DNS_D_MAXNAME + 1]; union dns_any any; size_t len; int error; if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, Q, &error))) return error; else if (len >= sizeof dn) return DNS_EILLEGAL; if (rr->section != DNS_S_QD && (error = dns_any_parse(dns_any_init(&any, sizeof any), rr, Q))) return error; return dns_p_push(P, rr->section, dn, len, rr->type, rr->class, rr->ttl, &any); } /* dns_rr_copy() */ int dns_rr_parse(struct dns_rr *rr, unsigned short src, struct dns_packet *P) { unsigned short p = src; if (src >= P->end) goto invalid; rr->dn.p = p; rr->dn.len = (p = dns_d_skip(p, P)) - rr->dn.p; if (P->end - p < 4) goto invalid; rr->type = ((0xff & P->data[p + 0]) << 8) | ((0xff & P->data[p + 1]) << 0); rr->class = ((0xff & P->data[p + 2]) << 8) | ((0xff & P->data[p + 3]) << 0); p += 4; if (src < dns_p_qend(P)) { rr->section = DNS_S_QUESTION; rr->ttl = 0; rr->rd.p = 0; rr->rd.len = 0; return 0; } if (P->end - p < 4) goto invalid; rr->ttl = ((0xff & P->data[p + 0]) << 24) | ((0xff & P->data[p + 1]) << 16) | ((0xff & P->data[p + 2]) << 8) | ((0xff & P->data[p + 3]) << 0); if (rr->type != DNS_T_OPT) rr->ttl = DNS_PP_MIN(rr->ttl, 0x7fffffffU); p += 4; if (P->end - p < 2) goto invalid; rr->rd.len = ((0xff & P->data[p + 0]) << 8) | ((0xff & P->data[p + 1]) << 0); rr->rd.p = p + 2; p += 2; if (P->end - p < rr->rd.len) goto invalid; return 0; invalid: return DNS_EILLEGAL; } /* dns_rr_parse() */ static unsigned short dns_rr_len(const unsigned short src, struct dns_packet *P) { unsigned short rp, rdlen; rp = dns_d_skip(src, P); if (P->end - rp < 4) return P->end - src; rp += 4; /* TYPE, CLASS */ if (rp <= dns_p_qend(P)) return rp - src; if (P->end - rp < 6) return P->end - src; rp += 6; /* TTL, RDLEN */ rdlen = ((0xff & P->data[rp - 2]) << 8) | ((0xff & P->data[rp - 1]) << 0); if (P->end - rp < rdlen) return P->end - src; rp += rdlen; return rp - src; } /* dns_rr_len() */ unsigned short dns_rr_skip(unsigned short src, struct dns_packet *P) { return src + dns_rr_len(src, P); } /* dns_rr_skip() */ static enum dns_section dns_rr_section(unsigned short src, struct dns_packet *P) { enum dns_section section; unsigned count, index; unsigned short rp; if (src >= P->memo.qd.base && src < P->memo.qd.end) return DNS_S_QD; if (src >= P->memo.an.base && src < P->memo.an.end) return DNS_S_AN; if (src >= P->memo.ns.base && src < P->memo.ns.end) return DNS_S_NS; if (src >= P->memo.ar.base && src < P->memo.ar.end) return DNS_S_AR; /* NOTE: Possibly bad memoization. Try it the hard-way. */ for (rp = 12, index = 0; rp < src && rp < P->end; index++) rp = dns_rr_skip(rp, P); section = DNS_S_QD; count = dns_p_count(P, section); while (index >= count && section <= DNS_S_AR) { section <<= 1; count += dns_p_count(P, section); } return DNS_S_ALL & section; } /* dns_rr_section() */ static enum dns_type dns_rr_type(unsigned short src, struct dns_packet *P) { struct dns_rr rr; int error; if ((error = dns_rr_parse(&rr, src, P))) return 0; return rr.type; } /* dns_rr_type() */ int dns_rr_cmp(struct dns_rr *r0, struct dns_packet *P0, struct dns_rr *r1, struct dns_packet *P1) { char host0[DNS_D_MAXNAME + 1], host1[DNS_D_MAXNAME + 1]; union dns_any any0, any1; int cmp, error; size_t len; if ((cmp = r0->type - r1->type)) return cmp; if ((cmp = r0->class - r1->class)) return cmp; /* * FIXME: Do label-by-label comparison to handle illegally long names? */ if (!(len = dns_d_expand(host0, sizeof host0, r0->dn.p, P0, &error)) || len >= sizeof host0) return -1; if (!(len = dns_d_expand(host1, sizeof host1, r1->dn.p, P1, &error)) || len >= sizeof host1) return 1; if ((cmp = strcasecmp(host0, host1))) return cmp; if (DNS_S_QD & (r0->section | r1->section)) { if (r0->section == r1->section) return 0; return (r0->section == DNS_S_QD)? -1 : 1; } if ((error = dns_any_parse(&any0, r0, P0))) return -1; if ((error = dns_any_parse(&any1, r1, P1))) return 1; return dns_any_cmp(&any0, r0->type, &any1, r1->type); } /* dns_rr_cmp() */ static _Bool dns_rr_exists(struct dns_rr *rr0, struct dns_packet *P0, struct dns_packet *P1) { struct dns_rr rr1; dns_rr_foreach(&rr1, P1, .section = rr0->section, .type = rr0->type) { if (0 == dns_rr_cmp(rr0, P0, &rr1, P1)) return 1; } return 0; } /* dns_rr_exists() */ static unsigned short dns_rr_offset(struct dns_rr *rr) { return rr->dn.p; } /* dns_rr_offset() */ static _Bool dns_rr_i_match(struct dns_rr *rr, struct dns_rr_i *i, struct dns_packet *P) { if (i->section && !(rr->section & i->section)) return 0; if (i->type && rr->type != i->type && i->type != DNS_T_ALL) return 0; if (i->class && rr->class != i->class && i->class != DNS_C_ANY) return 0; if (i->name) { char dn[DNS_D_MAXNAME + 1]; size_t len; int error; if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, P, &error)) || len >= sizeof dn) return 0; if (0 != strcasecmp(dn, i->name)) return 0; } if (i->data && i->type && rr->section > DNS_S_QD) { union dns_any rd; int error; if ((error = dns_any_parse(&rd, rr, P))) return 0; if (0 != dns_any_cmp(&rd, rr->type, i->data, i->type)) return 0; } return 1; } /* dns_rr_i_match() */ static unsigned short dns_rr_i_start(struct dns_rr_i *i, struct dns_packet *P) { unsigned short rp; struct dns_rr r0, rr; int error; if ((i->section & DNS_S_QD) && P->memo.qd.base) rp = P->memo.qd.base; else if ((i->section & DNS_S_AN) && P->memo.an.base) rp = P->memo.an.base; else if ((i->section & DNS_S_NS) && P->memo.ns.base) rp = P->memo.ns.base; else if ((i->section & DNS_S_AR) && P->memo.ar.base) rp = P->memo.ar.base; else rp = 12; for (; rp < P->end; rp = dns_rr_skip(rp, P)) { if ((error = dns_rr_parse(&rr, rp, P))) continue; rr.section = dns_rr_section(rp, P); if (!dns_rr_i_match(&rr, i, P)) continue; r0 = rr; goto lower; } return P->end; lower: if (i->sort == &dns_rr_i_packet) return dns_rr_offset(&r0); while ((rp = dns_rr_skip(rp, P)) < P->end) { if ((error = dns_rr_parse(&rr, rp, P))) continue; rr.section = dns_rr_section(rp, P); if (!dns_rr_i_match(&rr, i, P)) continue; if (i->sort(&rr, &r0, i, P) < 0) r0 = rr; } return dns_rr_offset(&r0); } /* dns_rr_i_start() */ static unsigned short dns_rr_i_skip(unsigned short rp, struct dns_rr_i *i, struct dns_packet *P) { struct dns_rr r0, r1, rr; int error; if ((error = dns_rr_parse(&r0, rp, P))) return P->end; r0.section = dns_rr_section(rp, P); rp = (i->sort == &dns_rr_i_packet)? dns_rr_skip(rp, P) : 12; for (; rp < P->end; rp = dns_rr_skip(rp, P)) { if ((error = dns_rr_parse(&rr, rp, P))) continue; rr.section = dns_rr_section(rp, P); if (!dns_rr_i_match(&rr, i, P)) continue; if (i->sort(&rr, &r0, i, P) <= 0) continue; r1 = rr; goto lower; } return P->end; lower: if (i->sort == &dns_rr_i_packet) return dns_rr_offset(&r1); while ((rp = dns_rr_skip(rp, P)) < P->end) { if ((error = dns_rr_parse(&rr, rp, P))) continue; rr.section = dns_rr_section(rp, P); if (!dns_rr_i_match(&rr, i, P)) continue; if (i->sort(&rr, &r0, i, P) <= 0) continue; if (i->sort(&rr, &r1, i, P) >= 0) continue; r1 = rr; } return dns_rr_offset(&r1); } /* dns_rr_i_skip() */ int dns_rr_i_packet(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { (void)i; (void)P; return (int)a->dn.p - (int)b->dn.p; } /* dns_rr_i_packet() */ int dns_rr_i_order(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { int cmp; (void)i; if ((cmp = a->section - b->section)) return cmp; if (a->type != b->type) return (int)a->dn.p - (int)b->dn.p; return dns_rr_cmp(a, P, b, P); } /* dns_rr_i_order() */ int dns_rr_i_shuffle(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { int cmp; (void)i; (void)P; while (!i->state.regs[0]) i->state.regs[0] = dns_random(); if ((cmp = a->section - b->section)) return cmp; return dns_k_shuffle16(a->dn.p, i->state.regs[0]) - dns_k_shuffle16(b->dn.p, i->state.regs[0]); } /* dns_rr_i_shuffle() */ struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *i, struct dns_packet *P) { static const struct dns_rr_i i_initializer; (void)P; i->state = i_initializer.state; i->saved = i->state; return i; } /* dns_rr_i_init() */ unsigned dns_rr_grep(struct dns_rr *rr, unsigned lim, struct dns_rr_i *i, struct dns_packet *P, int *error_) { unsigned count = 0; int error; switch (i->state.exec) { case 0: if (!i->sort) i->sort = &dns_rr_i_packet; i->state.next = dns_rr_i_start(i, P); i->state.exec++; /* FALL THROUGH */ case 1: while (count < lim && i->state.next < P->end) { if ((error = dns_rr_parse(rr, i->state.next, P))) goto error; rr->section = dns_rr_section(i->state.next, P); rr++; count++; i->state.count++; i->state.next = dns_rr_i_skip(i->state.next, i, P); } /* while() */ break; } /* switch() */ return count; error: *error_ = error; return count; } /* dns_rr_grep() */ size_t dns_rr_print(void *_dst, size_t lim, struct dns_rr *rr, struct dns_packet *P, int *_error) { struct dns_buf dst = DNS_B_INTO(_dst, lim); union dns_any any; size_t n; int error; if (rr->section == DNS_S_QD) dns_b_putc(&dst, ';'); if (!(n = dns_d_expand(any.ns.host, sizeof any.ns.host, rr->dn.p, P, &error))) goto error; dns_b_put(&dst, any.ns.host, DNS_PP_MIN(n, sizeof any.ns.host - 1)); if (rr->section != DNS_S_QD) { dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, rr->ttl, 0); } dns_b_putc(&dst, ' '); dns_b_puts(&dst, dns_strclass(rr->class)); dns_b_putc(&dst, ' '); dns_b_puts(&dst, dns_strtype(rr->type)); if (rr->section == DNS_S_QD) goto epilog; dns_b_putc(&dst, ' '); if ((error = dns_any_parse(dns_any_init(&any, sizeof any), rr, P))) goto error; n = dns_any_print(dst.p, dst.pe - dst.p, &any, rr->type); dst.p += DNS_PP_MIN(n, (size_t)(dst.pe - dst.p)); epilog: return dns_b_strllen(&dst); error: *_error = error; return 0; } /* dns_rr_print() */ int dns_a_parse(struct dns_a *a, struct dns_rr *rr, struct dns_packet *P) { unsigned long addr; if (rr->rd.len != 4) return DNS_EILLEGAL; addr = ((0xffU & P->data[rr->rd.p + 0]) << 24) | ((0xffU & P->data[rr->rd.p + 1]) << 16) | ((0xffU & P->data[rr->rd.p + 2]) << 8) | ((0xffU & P->data[rr->rd.p + 3]) << 0); a->addr.s_addr = htonl(addr); return 0; } /* dns_a_parse() */ int dns_a_push(struct dns_packet *P, struct dns_a *a) { unsigned long addr; if (P->size - P->end < 6) return DNS_ENOBUFS; P->data[P->end++] = 0x00; P->data[P->end++] = 0x04; addr = ntohl(a->addr.s_addr); P->data[P->end++] = 0xffU & (addr >> 24); P->data[P->end++] = 0xffU & (addr >> 16); P->data[P->end++] = 0xffU & (addr >> 8); P->data[P->end++] = 0xffU & (addr >> 0); return 0; } /* dns_a_push() */ size_t dns_a_arpa(void *_dst, size_t lim, const struct dns_a *a) { struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned long octets = ntohl(a->addr.s_addr); unsigned i; for (i = 0; i < 4; i++) { dns_b_fmtju(&dst, 0xff & octets, 0); dns_b_putc(&dst, '.'); octets >>= 8; } dns_b_puts(&dst, "in-addr.arpa."); return dns_b_strllen(&dst); } /* dns_a_arpa() */ int dns_a_cmp(const struct dns_a *a, const struct dns_a *b) { if (ntohl(a->addr.s_addr) < ntohl(b->addr.s_addr)) return -1; if (ntohl(a->addr.s_addr) > ntohl(b->addr.s_addr)) return 1; return 0; } /* dns_a_cmp() */ size_t dns_a_print(void *dst, size_t lim, struct dns_a *a) { char addr[INET_ADDRSTRLEN + 1] = "0.0.0.0"; dns_inet_ntop(AF_INET, &a->addr, addr, sizeof addr); return dns_strlcpy(dst, addr, lim); } /* dns_a_print() */ int dns_aaaa_parse(struct dns_aaaa *aaaa, struct dns_rr *rr, struct dns_packet *P) { if (rr->rd.len != sizeof aaaa->addr.s6_addr) return DNS_EILLEGAL; memcpy(aaaa->addr.s6_addr, &P->data[rr->rd.p], sizeof aaaa->addr.s6_addr); return 0; } /* dns_aaaa_parse() */ int dns_aaaa_push(struct dns_packet *P, struct dns_aaaa *aaaa) { if (P->size - P->end < 2 + sizeof aaaa->addr.s6_addr) return DNS_ENOBUFS; P->data[P->end++] = 0x00; P->data[P->end++] = 0x10; memcpy(&P->data[P->end], aaaa->addr.s6_addr, sizeof aaaa->addr.s6_addr); P->end += sizeof aaaa->addr.s6_addr; return 0; } /* dns_aaaa_push() */ int dns_aaaa_cmp(const struct dns_aaaa *a, const struct dns_aaaa *b) { unsigned i; int cmp; for (i = 0; i < lengthof(a->addr.s6_addr); i++) { if ((cmp = (a->addr.s6_addr[i] - b->addr.s6_addr[i]))) return cmp; } return 0; } /* dns_aaaa_cmp() */ size_t dns_aaaa_arpa(void *_dst, size_t lim, const struct dns_aaaa *aaaa) { static const unsigned char hex[16] = "0123456789abcdef"; struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned nyble; int i, j; for (i = sizeof aaaa->addr.s6_addr - 1; i >= 0; i--) { nyble = aaaa->addr.s6_addr[i]; for (j = 0; j < 2; j++) { dns_b_putc(&dst, hex[0x0f & nyble]); dns_b_putc(&dst, '.'); nyble >>= 4; } } dns_b_puts(&dst, "ip6.arpa."); return dns_b_strllen(&dst); } /* dns_aaaa_arpa() */ size_t dns_aaaa_print(void *dst, size_t lim, struct dns_aaaa *aaaa) { char addr[INET6_ADDRSTRLEN + 1] = "::"; dns_inet_ntop(AF_INET6, &aaaa->addr, addr, sizeof addr); return dns_strlcpy(dst, addr, lim); } /* dns_aaaa_print() */ int dns_mx_parse(struct dns_mx *mx, struct dns_rr *rr, struct dns_packet *P) { size_t len; int error; if (rr->rd.len < 3) return DNS_EILLEGAL; mx->preference = (0xff00 & (P->data[rr->rd.p + 0] << 8)) | (0x00ff & (P->data[rr->rd.p + 1] << 0)); if (!(len = dns_d_expand(mx->host, sizeof mx->host, rr->rd.p + 2, P, &error))) return error; else if (len >= sizeof mx->host) return DNS_EILLEGAL; return 0; } /* dns_mx_parse() */ int dns_mx_push(struct dns_packet *P, struct dns_mx *mx) { size_t end, len; int error; if (P->size - P->end < 5) return DNS_ENOBUFS; end = P->end; P->end += 2; P->data[P->end++] = 0xff & (mx->preference >> 8); P->data[P->end++] = 0xff & (mx->preference >> 0); if ((error = dns_d_push(P, mx->host, strlen(mx->host)))) goto error; len = P->end - end - 2; P->data[end + 0] = 0xff & (len >> 8); P->data[end + 1] = 0xff & (len >> 0); return 0; error: P->end = end; return error; } /* dns_mx_push() */ int dns_mx_cmp(const struct dns_mx *a, const struct dns_mx *b) { int cmp; if ((cmp = a->preference - b->preference)) return cmp; return strcasecmp(a->host, b->host); } /* dns_mx_cmp() */ size_t dns_mx_print(void *_dst, size_t lim, struct dns_mx *mx) { struct dns_buf dst = DNS_B_INTO(_dst, lim); dns_b_fmtju(&dst, mx->preference, 0); dns_b_putc(&dst, ' '); dns_b_puts(&dst, mx->host); return dns_b_strllen(&dst); } /* dns_mx_print() */ size_t dns_mx_cname(void *dst, size_t lim, struct dns_mx *mx) { return dns_strlcpy(dst, mx->host, lim); } /* dns_mx_cname() */ int dns_ns_parse(struct dns_ns *ns, struct dns_rr *rr, struct dns_packet *P) { size_t len; int error; if (!(len = dns_d_expand(ns->host, sizeof ns->host, rr->rd.p, P, &error))) return error; else if (len >= sizeof ns->host) return DNS_EILLEGAL; return 0; } /* dns_ns_parse() */ int dns_ns_push(struct dns_packet *P, struct dns_ns *ns) { size_t end, len; int error; if (P->size - P->end < 3) return DNS_ENOBUFS; end = P->end; P->end += 2; if ((error = dns_d_push(P, ns->host, strlen(ns->host)))) goto error; len = P->end - end - 2; P->data[end + 0] = 0xff & (len >> 8); P->data[end + 1] = 0xff & (len >> 0); return 0; error: P->end = end; return error; } /* dns_ns_push() */ int dns_ns_cmp(const struct dns_ns *a, const struct dns_ns *b) { return strcasecmp(a->host, b->host); } /* dns_ns_cmp() */ size_t dns_ns_print(void *dst, size_t lim, struct dns_ns *ns) { return dns_strlcpy(dst, ns->host, lim); } /* dns_ns_print() */ size_t dns_ns_cname(void *dst, size_t lim, struct dns_ns *ns) { return dns_strlcpy(dst, ns->host, lim); } /* dns_ns_cname() */ int dns_cname_parse(struct dns_cname *cname, struct dns_rr *rr, struct dns_packet *P) { return dns_ns_parse((struct dns_ns *)cname, rr, P); } /* dns_cname_parse() */ int dns_cname_push(struct dns_packet *P, struct dns_cname *cname) { return dns_ns_push(P, (struct dns_ns *)cname); } /* dns_cname_push() */ int dns_cname_cmp(const struct dns_cname *a, const struct dns_cname *b) { return strcasecmp(a->host, b->host); } /* dns_cname_cmp() */ size_t dns_cname_print(void *dst, size_t lim, struct dns_cname *cname) { return dns_ns_print(dst, lim, (struct dns_ns *)cname); } /* dns_cname_print() */ size_t dns_cname_cname(void *dst, size_t lim, struct dns_cname *cname) { return dns_strlcpy(dst, cname->host, lim); } /* dns_cname_cname() */ int dns_soa_parse(struct dns_soa *soa, struct dns_rr *rr, struct dns_packet *P) { struct { void *dst; size_t lim; } dn[] = { { soa->mname, sizeof soa->mname }, { soa->rname, sizeof soa->rname } }; unsigned *ts[] = { &soa->serial, &soa->refresh, &soa->retry, &soa->expire, &soa->minimum }; unsigned short rp; unsigned i, j, n; int error; /* MNAME / RNAME */ if ((rp = rr->rd.p) >= P->end) return DNS_EILLEGAL; for (i = 0; i < lengthof(dn); i++) { if (!(n = dns_d_expand(dn[i].dst, dn[i].lim, rp, P, &error))) return error; else if (n >= dn[i].lim) return DNS_EILLEGAL; if ((rp = dns_d_skip(rp, P)) >= P->end) return DNS_EILLEGAL; } /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */ for (i = 0; i < lengthof(ts); i++) { for (j = 0; j < 4; j++, rp++) { if (rp >= P->end) return DNS_EILLEGAL; *ts[i] <<= 8; *ts[i] |= (0xff & P->data[rp]); } } return 0; } /* dns_soa_parse() */ int dns_soa_push(struct dns_packet *P, struct dns_soa *soa) { void *dn[] = { soa->mname, soa->rname }; unsigned ts[] = { (0xffffffff & soa->serial), (0x7fffffff & soa->refresh), (0x7fffffff & soa->retry), (0x7fffffff & soa->expire), (0xffffffff & soa->minimum) }; unsigned i, j; size_t end, len; int error; end = P->end; if ((P->end += 2) >= P->size) goto toolong; /* MNAME / RNAME */ for (i = 0; i < lengthof(dn); i++) { if ((error = dns_d_push(P, dn[i], strlen(dn[i])))) goto error; } /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */ for (i = 0; i < lengthof(ts); i++) { if ((P->end += 4) >= P->size) goto toolong; for (j = 1; j <= 4; j++) { P->data[P->end - j] = (0xff & ts[i]); ts[i] >>= 8; } } len = P->end - end - 2; P->data[end + 0] = (0xff & (len >> 8)); P->data[end + 1] = (0xff & (len >> 0)); return 0; toolong: error = DNS_ENOBUFS; /* FALL THROUGH */ error: P->end = end; return error; } /* dns_soa_push() */ int dns_soa_cmp(const struct dns_soa *a, const struct dns_soa *b) { int cmp; if ((cmp = strcasecmp(a->mname, b->mname))) return cmp; if ((cmp = strcasecmp(a->rname, b->rname))) return cmp; if (a->serial > b->serial) return -1; else if (a->serial < b->serial) return 1; if (a->refresh > b->refresh) return -1; else if (a->refresh < b->refresh) return 1; if (a->retry > b->retry) return -1; else if (a->retry < b->retry) return 1; if (a->expire > b->expire) return -1; else if (a->expire < b->expire) return 1; if (a->minimum > b->minimum) return -1; else if (a->minimum < b->minimum) return 1; return 0; } /* dns_soa_cmp() */ size_t dns_soa_print(void *_dst, size_t lim, struct dns_soa *soa) { struct dns_buf dst = DNS_B_INTO(_dst, lim); dns_b_puts(&dst, soa->mname); dns_b_putc(&dst, ' '); dns_b_puts(&dst, soa->rname); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, soa->serial, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, soa->refresh, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, soa->retry, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, soa->expire, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, soa->minimum, 0); return dns_b_strllen(&dst); } /* dns_soa_print() */ int dns_srv_parse(struct dns_srv *srv, struct dns_rr *rr, struct dns_packet *P) { unsigned short rp; unsigned i; size_t n; int error; memset(srv, '\0', sizeof *srv); rp = rr->rd.p; if (rr->rd.len < 7) return DNS_EILLEGAL; for (i = 0; i < 2; i++, rp++) { srv->priority <<= 8; srv->priority |= (0xff & P->data[rp]); } for (i = 0; i < 2; i++, rp++) { srv->weight <<= 8; srv->weight |= (0xff & P->data[rp]); } for (i = 0; i < 2; i++, rp++) { srv->port <<= 8; srv->port |= (0xff & P->data[rp]); } if (!(n = dns_d_expand(srv->target, sizeof srv->target, rp, P, &error))) return error; else if (n >= sizeof srv->target) return DNS_EILLEGAL; return 0; } /* dns_srv_parse() */ int dns_srv_push(struct dns_packet *P, struct dns_srv *srv) { size_t end, len; int error; end = P->end; if (P->size - P->end < 2) goto toolong; P->end += 2; if (P->size - P->end < 6) goto toolong; P->data[P->end++] = 0xff & (srv->priority >> 8); P->data[P->end++] = 0xff & (srv->priority >> 0); P->data[P->end++] = 0xff & (srv->weight >> 8); P->data[P->end++] = 0xff & (srv->weight >> 0); P->data[P->end++] = 0xff & (srv->port >> 8); P->data[P->end++] = 0xff & (srv->port >> 0); if (0 == (len = dns_d_comp(&P->data[P->end], P->size - P->end, srv->target, strlen(srv->target), P, &error))) goto error; else if (P->size - P->end < len) goto toolong; P->end += len; if (P->end > 65535) goto toolong; len = P->end - end - 2; P->data[end + 0] = 0xff & (len >> 8); P->data[end + 1] = 0xff & (len >> 0); return 0; toolong: error = DNS_ENOBUFS; /* FALL THROUGH */ error: P->end = end; return error; } /* dns_srv_push() */ int dns_srv_cmp(const struct dns_srv *a, const struct dns_srv *b) { int cmp; if ((cmp = a->priority - b->priority)) return cmp; /* * FIXME: We need some sort of random seed to implement the dynamic * weighting required by RFC 2782. */ if ((cmp = a->weight - b->weight)) return cmp; if ((cmp = a->port - b->port)) return cmp; return strcasecmp(a->target, b->target); } /* dns_srv_cmp() */ size_t dns_srv_print(void *_dst, size_t lim, struct dns_srv *srv) { struct dns_buf dst = DNS_B_INTO(_dst, lim); dns_b_fmtju(&dst, srv->priority, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, srv->weight, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, srv->port, 0); dns_b_putc(&dst, ' '); dns_b_puts(&dst, srv->target); return dns_b_strllen(&dst); } /* dns_srv_print() */ size_t dns_srv_cname(void *dst, size_t lim, struct dns_srv *srv) { return dns_strlcpy(dst, srv->target, lim); } /* dns_srv_cname() */ unsigned int dns_opt_ttl(const struct dns_opt *opt) { unsigned int ttl = 0; ttl |= (0xffU & opt->rcode) << 24; ttl |= (0xffU & opt->version) << 16; ttl |= (0xffffU & opt->flags) << 0; return ttl; } /* dns_opt_ttl() */ unsigned short dns_opt_class(const struct dns_opt *opt) { return opt->maxudp; } /* dns_opt_class() */ struct dns_opt *dns_opt_init(struct dns_opt *opt, size_t size) { assert(size >= offsetof(struct dns_opt, data)); opt->size = size - offsetof(struct dns_opt, data); opt->len = 0; opt->rcode = 0; opt->version = 0; opt->maxudp = 0; return opt; } /* dns_opt_init() */ static union dns_any *dns_opt_initany(union dns_any *any, size_t size) { return dns_opt_init(&any->opt, size), any; } /* dns_opt_initany() */ int dns_opt_parse(struct dns_opt *opt, struct dns_rr *rr, struct dns_packet *P) { const struct dns_buf src = DNS_B_FROM(&P->data[rr->rd.p], rr->rd.len); struct dns_buf dst = DNS_B_INTO(opt->data, opt->size); int error; opt->rcode = 0xfff & ((rr->ttl >> 20) | dns_header(P)->rcode); opt->version = 0xff & (rr->ttl >> 16); opt->flags = 0xffff & rr->ttl; opt->maxudp = 0xffff & rr->class; while (src.p < src.pe) { int code, len; if (-1 == (code = dns_b_get16(&src, -1))) return src.error; if (-1 == (len = dns_b_get16(&src, -1))) return src.error; switch (code) { default: dns_b_put16(&dst, code); dns_b_put16(&dst, len); if ((error = dns_b_move(&dst, &src, len))) return error; break; } } return 0; } /* dns_opt_parse() */ int dns_opt_push(struct dns_packet *P, struct dns_opt *opt) { const struct dns_buf src = DNS_B_FROM(opt->data, opt->len); struct dns_buf dst = DNS_B_INTO(&P->data[P->end], (P->size - P->end)); int error; /* rdata length (see below) */ if ((error = dns_b_put16(&dst, 0))) goto error; /* ... push known options here */ /* push opaque option data */ if ((error = dns_b_move(&dst, &src, (size_t)(src.pe - src.p)))) goto error; /* rdata length */ if ((error = dns_b_pput16(&dst, dns_b_tell(&dst) - 2, 0))) goto error; #if !DNS_DEBUG_OPT_FORMERR P->end += dns_b_tell(&dst); #endif return 0; error: return error; } /* dns_opt_push() */ int dns_opt_cmp(const struct dns_opt *a, const struct dns_opt *b) { (void)a; (void)b; return -1; } /* dns_opt_cmp() */ size_t dns_opt_print(void *_dst, size_t lim, struct dns_opt *opt) { struct dns_buf dst = DNS_B_INTO(_dst, lim); size_t p; dns_b_putc(&dst, '"'); for (p = 0; p < opt->len; p++) { dns_b_putc(&dst, '\\'); dns_b_fmtju(&dst, opt->data[p], 3); } dns_b_putc(&dst, '"'); return dns_b_strllen(&dst); } /* dns_opt_print() */ int dns_ptr_parse(struct dns_ptr *ptr, struct dns_rr *rr, struct dns_packet *P) { return dns_ns_parse((struct dns_ns *)ptr, rr, P); } /* dns_ptr_parse() */ int dns_ptr_push(struct dns_packet *P, struct dns_ptr *ptr) { return dns_ns_push(P, (struct dns_ns *)ptr); } /* dns_ptr_push() */ size_t dns_ptr_qname(void *dst, size_t lim, int af, void *addr) { switch (af) { case AF_INET6: return dns_aaaa_arpa(dst, lim, addr); case AF_INET: return dns_a_arpa(dst, lim, addr); default: return dns_a_arpa(dst, lim, &(struct dns_a){ { INADDR_NONE } }); } } /* dns_ptr_qname() */ int dns_ptr_cmp(const struct dns_ptr *a, const struct dns_ptr *b) { return strcasecmp(a->host, b->host); } /* dns_ptr_cmp() */ size_t dns_ptr_print(void *dst, size_t lim, struct dns_ptr *ptr) { return dns_ns_print(dst, lim, (struct dns_ns *)ptr); } /* dns_ptr_print() */ size_t dns_ptr_cname(void *dst, size_t lim, struct dns_ptr *ptr) { return dns_strlcpy(dst, ptr->host, lim); } /* dns_ptr_cname() */ int dns_sshfp_parse(struct dns_sshfp *fp, struct dns_rr *rr, struct dns_packet *P) { unsigned p = rr->rd.p, pe = rr->rd.p + rr->rd.len; if (pe - p < 2) return DNS_EILLEGAL; fp->algo = P->data[p++]; fp->type = P->data[p++]; switch (fp->type) { case DNS_SSHFP_SHA1: if (pe - p < sizeof fp->digest.sha1) return DNS_EILLEGAL; memcpy(fp->digest.sha1, &P->data[p], sizeof fp->digest.sha1); break; default: break; } /* switch() */ return 0; } /* dns_sshfp_parse() */ int dns_sshfp_push(struct dns_packet *P, struct dns_sshfp *fp) { unsigned p = P->end, pe = P->size, n; if (pe - p < 4) return DNS_ENOBUFS; p += 2; P->data[p++] = 0xff & fp->algo; P->data[p++] = 0xff & fp->type; switch (fp->type) { case DNS_SSHFP_SHA1: if (pe - p < sizeof fp->digest.sha1) return DNS_ENOBUFS; memcpy(&P->data[p], fp->digest.sha1, sizeof fp->digest.sha1); p += sizeof fp->digest.sha1; break; default: return DNS_EILLEGAL; } /* switch() */ n = p - P->end - 2; P->data[P->end++] = 0xff & (n >> 8); P->data[P->end++] = 0xff & (n >> 0); P->end = p; return 0; } /* dns_sshfp_push() */ int dns_sshfp_cmp(const struct dns_sshfp *a, const struct dns_sshfp *b) { int cmp; if ((cmp = a->algo - b->algo) || (cmp = a->type - b->type)) return cmp; switch (a->type) { case DNS_SSHFP_SHA1: return memcmp(a->digest.sha1, b->digest.sha1, sizeof a->digest.sha1); default: return 0; } /* switch() */ /* NOT REACHED */ } /* dns_sshfp_cmp() */ size_t dns_sshfp_print(void *_dst, size_t lim, struct dns_sshfp *fp) { static const unsigned char hex[16] = "0123456789abcdef"; struct dns_buf dst = DNS_B_INTO(_dst, lim); size_t i; dns_b_fmtju(&dst, fp->algo, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, fp->type, 0); dns_b_putc(&dst, ' '); switch (fp->type) { case DNS_SSHFP_SHA1: for (i = 0; i < sizeof fp->digest.sha1; i++) { dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 4)]); dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 0)]); } break; default: dns_b_putc(&dst, '0'); break; } /* switch() */ return dns_b_strllen(&dst); } /* dns_sshfp_print() */ struct dns_txt *dns_txt_init(struct dns_txt *txt, size_t size) { assert(size > offsetof(struct dns_txt, data)); txt->size = size - offsetof(struct dns_txt, data); txt->len = 0; return txt; } /* dns_txt_init() */ static union dns_any *dns_txt_initany(union dns_any *any, size_t size) { /* NB: union dns_any is already initialized as struct dns_txt */ (void)size; return any; } /* dns_txt_initany() */ int dns_txt_parse(struct dns_txt *txt, struct dns_rr *rr, struct dns_packet *P) { struct { unsigned char *b; size_t p, end; } dst, src; unsigned n; dst.b = txt->data; dst.p = 0; dst.end = txt->size; src.b = P->data; src.p = rr->rd.p; src.end = src.p + rr->rd.len; while (src.p < src.end) { n = 0xff & P->data[src.p++]; if (src.end - src.p < n || dst.end - dst.p < n) return DNS_EILLEGAL; memcpy(&dst.b[dst.p], &src.b[src.p], n); dst.p += n; src.p += n; } txt->len = dst.p; return 0; } /* dns_txt_parse() */ int dns_txt_push(struct dns_packet *P, struct dns_txt *txt) { struct { unsigned char *b; size_t p, end; } dst, src; unsigned n; dst.b = P->data; dst.p = P->end; dst.end = P->size; src.b = txt->data; src.p = 0; src.end = txt->len; if (dst.end - dst.p < 2) return DNS_ENOBUFS; n = txt->len + ((txt->len + 254) / 255); dst.b[dst.p++] = 0xff & (n >> 8); dst.b[dst.p++] = 0xff & (n >> 0); while (src.p < src.end) { n = DNS_PP_MIN(255, src.end - src.p); if (dst.p >= dst.end) return DNS_ENOBUFS; dst.b[dst.p++] = n; if (dst.end - dst.p < n) return DNS_ENOBUFS; memcpy(&dst.b[dst.p], &src.b[src.p], n); dst.p += n; src.p += n; } P->end = dst.p; return 0; } /* dns_txt_push() */ int dns_txt_cmp(const struct dns_txt *a, const struct dns_txt *b) { (void)a; (void)b; return -1; } /* dns_txt_cmp() */ size_t dns_txt_print(void *_dst, size_t lim, struct dns_txt *txt) { struct dns_buf src = DNS_B_FROM(txt->data, txt->len); struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned i; if (src.p < src.pe) { do { dns_b_putc(&dst, '"'); for (i = 0; i < 256 && src.p < src.pe; i++, src.p++) { if (*src.p < 32 || *src.p > 126 || *src.p == '"' || *src.p == '\\') { dns_b_putc(&dst, '\\'); dns_b_fmtju(&dst, *src.p, 3); } else { dns_b_putc(&dst, *src.p); } } dns_b_putc(&dst, '"'); dns_b_putc(&dst, ' '); } while (src.p < src.pe); dns_b_popc(&dst); } else { dns_b_putc(&dst, '"'); dns_b_putc(&dst, '"'); } return dns_b_strllen(&dst); } /* dns_txt_print() */ static const struct dns_rrtype { enum dns_type type; const char *name; union dns_any *(*init)(union dns_any *, size_t); int (*parse)(); int (*push)(); int (*cmp)(); size_t (*print)(); size_t (*cname)(); } dns_rrtypes[] = { { DNS_T_A, "A", 0, &dns_a_parse, &dns_a_push, &dns_a_cmp, &dns_a_print, 0, }, { DNS_T_AAAA, "AAAA", 0, &dns_aaaa_parse, &dns_aaaa_push, &dns_aaaa_cmp, &dns_aaaa_print, 0, }, { DNS_T_MX, "MX", 0, &dns_mx_parse, &dns_mx_push, &dns_mx_cmp, &dns_mx_print, &dns_mx_cname, }, { DNS_T_NS, "NS", 0, &dns_ns_parse, &dns_ns_push, &dns_ns_cmp, &dns_ns_print, &dns_ns_cname, }, { DNS_T_CNAME, "CNAME", 0, &dns_cname_parse, &dns_cname_push, &dns_cname_cmp, &dns_cname_print, &dns_cname_cname, }, { DNS_T_SOA, "SOA", 0, &dns_soa_parse, &dns_soa_push, &dns_soa_cmp, &dns_soa_print, 0, }, { DNS_T_SRV, "SRV", 0, &dns_srv_parse, &dns_srv_push, &dns_srv_cmp, &dns_srv_print, &dns_srv_cname, }, { DNS_T_OPT, "OPT", &dns_opt_initany, &dns_opt_parse, &dns_opt_push, &dns_opt_cmp, &dns_opt_print, 0, }, { DNS_T_PTR, "PTR", 0, &dns_ptr_parse, &dns_ptr_push, &dns_ptr_cmp, &dns_ptr_print, &dns_ptr_cname, }, { DNS_T_TXT, "TXT", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, }, { DNS_T_SPF, "SPF", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, }, { DNS_T_SSHFP, "SSHFP", 0, &dns_sshfp_parse, &dns_sshfp_push, &dns_sshfp_cmp, &dns_sshfp_print, 0, }, { DNS_T_AXFR, "AXFR", 0, 0, 0, 0, 0, 0, }, }; /* dns_rrtypes[] */ static const struct dns_rrtype *dns_rrtype(enum dns_type type) { const struct dns_rrtype *t; for (t = dns_rrtypes; t < endof(dns_rrtypes); t++) { if (t->type == type && t->parse) { return t; } } return NULL; } /* dns_rrtype() */ union dns_any *dns_any_init(union dns_any *any, size_t size) { dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type"); return (union dns_any *)dns_txt_init(&any->rdata, size); } /* dns_any_init() */ static size_t dns_any_sizeof(union dns_any *any) { dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type"); return offsetof(struct dns_txt, data) + any->rdata.size; } /* dns_any_sizeof() */ static union dns_any *dns_any_reinit(union dns_any *any, const struct dns_rrtype *t) { return (t->init)? t->init(any, dns_any_sizeof(any)) : any; } /* dns_any_reinit() */ int dns_any_parse(union dns_any *any, struct dns_rr *rr, struct dns_packet *P) { const struct dns_rrtype *t; if ((t = dns_rrtype(rr->type))) return t->parse(dns_any_reinit(any, t), rr, P); if (rr->rd.len > any->rdata.size) return DNS_EILLEGAL; memcpy(any->rdata.data, &P->data[rr->rd.p], rr->rd.len); any->rdata.len = rr->rd.len; return 0; } /* dns_any_parse() */ int dns_any_push(struct dns_packet *P, union dns_any *any, enum dns_type type) { const struct dns_rrtype *t; if ((t = dns_rrtype(type))) return t->push(P, any); if (P->size - P->end < any->rdata.len + 2) return DNS_ENOBUFS; P->data[P->end++] = 0xff & (any->rdata.len >> 8); P->data[P->end++] = 0xff & (any->rdata.len >> 0); memcpy(&P->data[P->end], any->rdata.data, any->rdata.len); P->end += any->rdata.len; return 0; } /* dns_any_push() */ int dns_any_cmp(const union dns_any *a, enum dns_type x, const union dns_any *b, enum dns_type y) { const struct dns_rrtype *t; int cmp; if ((cmp = x - y)) return cmp; if ((t = dns_rrtype(x))) return t->cmp(a, b); return -1; } /* dns_any_cmp() */ size_t dns_any_print(void *_dst, size_t lim, union dns_any *any, enum dns_type type) { const struct dns_rrtype *t; struct dns_buf src, dst; if ((t = dns_rrtype(type))) return t->print(_dst, lim, any); dns_b_from(&src, any->rdata.data, any->rdata.len); dns_b_into(&dst, _dst, lim); dns_b_putc(&dst, '"'); while (src.p < src.pe) { dns_b_putc(&dst, '\\'); dns_b_fmtju(&dst, *src.p++, 3); } dns_b_putc(&dst, '"'); return dns_b_strllen(&dst); } /* dns_any_print() */ size_t dns_any_cname(void *dst, size_t lim, union dns_any *any, enum dns_type type) { const struct dns_rrtype *t; if ((t = dns_rrtype(type)) && t->cname) return t->cname(dst, lim, any); return 0; } /* dns_any_cname() */ /* * H O S T S R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_hosts { struct dns_hosts_entry { char host[DNS_D_MAXNAME + 1]; char arpa[73 + 1]; int af; union { struct in_addr a4; struct in6_addr a6; } addr; _Bool alias; struct dns_hosts_entry *next; } *head, **tail; dns_atomic_t refcount; }; /* struct dns_hosts */ struct dns_hosts *dns_hosts_open(int *error) { static const struct dns_hosts hosts_initializer = { .refcount = 1 }; struct dns_hosts *hosts; if (!(hosts = malloc(sizeof *hosts))) goto syerr; *hosts = hosts_initializer; hosts->tail = &hosts->head; return hosts; syerr: *error = dns_syerr(); free(hosts); return 0; } /* dns_hosts_open() */ void dns_hosts_close(struct dns_hosts *hosts) { struct dns_hosts_entry *ent, *xnt; if (!hosts || 1 != dns_hosts_release(hosts)) return; for (ent = hosts->head; ent; ent = xnt) { xnt = ent->next; free(ent); } free(hosts); return; } /* dns_hosts_close() */ dns_refcount_t dns_hosts_acquire(struct dns_hosts *hosts) { return dns_atomic_fetch_add(&hosts->refcount); } /* dns_hosts_acquire() */ dns_refcount_t dns_hosts_release(struct dns_hosts *hosts) { return dns_atomic_fetch_sub(&hosts->refcount); } /* dns_hosts_release() */ struct dns_hosts *dns_hosts_mortal(struct dns_hosts *hosts) { if (hosts) dns_hosts_release(hosts); return hosts; } /* dns_hosts_mortal() */ #if defined(BELLE_SIP_WINDOWS_PHONE) || defined(BELLE_SIP_WINDOWS_UNIVERSAL) static int dns_hosts_add_localhost(struct dns_hosts *hosts) { struct dns_hosts_entry ent; memset(&ent, '\0', sizeof(ent)); ent.af = AF_INET; dns_inet_pton(ent.af, "127.0.0.1", &ent.addr); dns_d_anchor(ent.host, sizeof(ent.host), "localhost", 9); dns_hosts_insert(hosts, ent.af, &ent.addr, ent.host, 0); memset(&ent, '\0', sizeof(ent)); ent.af = AF_INET6; dns_inet_pton(ent.af, "::1", &ent.addr); dns_d_anchor(ent.host, sizeof(ent.host), "localhost", 9); dns_hosts_insert(hosts, ent.af, &ent.addr, ent.host, 1); return 0; } #endif struct dns_hosts *dns_hosts_local(int *error_) { struct dns_hosts *hosts; int error; if (!(hosts = dns_hosts_open(&error))) goto error; #ifdef _WIN32 #if defined(BELLE_SIP_WINDOWS_PHONE) || defined(BELLE_SIP_WINDOWS_UNIVERSAL) if ((error = dns_hosts_add_localhost(hosts))) #else if ((error = dns_hosts_loadpath(hosts, "C:/Windows/System32/drivers/etc/hosts"))) #endif #else if ((error = dns_hosts_loadpath(hosts, "/etc/hosts"))) #endif goto error; return hosts; error: *error_ = error; dns_hosts_close(hosts); return 0; } /* dns_hosts_local() */ #define dns_hosts_issep(ch) (dns_isspace(ch)) #define dns_hosts_iscom(ch) ((ch) == '#' || (ch) == ';') int dns_hosts_loadfile(struct dns_hosts *hosts, FILE *fp) { struct dns_hosts_entry ent; char word[DNS_PP_MAX(INET6_ADDRSTRLEN, DNS_D_MAXNAME) + 1]; unsigned wp, wc, skip; int ch, error; rewind(fp); do { memset(&ent, '\0', sizeof ent); wc = 0; skip = 0; do { memset(word, '\0', sizeof word); wp = 0; while (EOF != (ch = fgetc(fp)) && ch != '\n') { skip |= !!dns_hosts_iscom(ch); if (skip) continue; if (dns_hosts_issep(ch)) break; if (wp < sizeof word - 1) word[wp] = ch; wp++; } if (!wp) continue; wc++; switch (wc) { case 0: break; case 1: ent.af = (strchr(word, ':'))? AF_INET6 : AF_INET; // Normalize some strange IPv4 addresses, eg. 127.1 --> 127.0.0.1 if (ent.af == AF_INET) { int nbdots = 0; char *p = word; while ((p = strchr(p, '.')) != NULL) { nbdots++; p++; } if (nbdots == 1) { p = strchr(word, '.'); p++; memmove(p + 4, p, strlen(p)); memcpy(p, "0.0.", 4); } } skip = (1 != dns_inet_pton(ent.af, word, &ent.addr)); break; default: if (!wp) break; dns_d_anchor(ent.host, sizeof ent.host, word, wp); if ((error = dns_hosts_insert(hosts, ent.af, &ent.addr, ent.host, (wc > 2)))) return error; break; } /* switch() */ } while (ch != EOF && ch != '\n'); } while (ch != EOF); return 0; } /* dns_hosts_loadfile() */ int dns_hosts_loadpath(struct dns_hosts *hosts, const char *path) { FILE *fp; int error; if (!(fp = dns_fopen(path, "rt", &error))) return error; error = dns_hosts_loadfile(hosts, fp); fclose(fp); return error; } /* dns_hosts_loadpath() */ int dns_hosts_dump(struct dns_hosts *hosts, FILE *fp) { struct dns_hosts_entry *ent, *xnt; char addr[INET6_ADDRSTRLEN + 1]; unsigned i; for (ent = hosts->head; ent; ent = xnt) { xnt = ent->next; dns_inet_ntop(ent->af, &ent->addr, addr, sizeof addr); fputs(addr, fp); for (i = strlen(addr); i < INET_ADDRSTRLEN; i++) fputc(' ', fp); fputc(' ', fp); fputs(ent->host, fp); fputc('\n', fp); } return 0; } /* dns_hosts_dump() */ int dns_hosts_insert(struct dns_hosts *hosts, int af, const void *addr, const void *host, _Bool alias) { struct dns_hosts_entry *ent; int error; if (!(ent = malloc(sizeof *ent))) goto syerr; dns_d_anchor(ent->host, sizeof ent->host, host, strlen(host)); switch ((ent->af = af)) { case AF_INET6: memcpy(&ent->addr.a6, addr, sizeof ent->addr.a6); dns_aaaa_arpa(ent->arpa, sizeof ent->arpa, addr); break; case AF_INET: memcpy(&ent->addr.a4, addr, sizeof ent->addr.a4); dns_a_arpa(ent->arpa, sizeof ent->arpa, addr); break; default: error = EINVAL; goto error; } /* switch() */ ent->alias = alias; ent->next = 0; *hosts->tail = ent; hosts->tail = &ent->next; return 0; syerr: error = dns_syerr(); error: free(ent); return error; } /* dns_hosts_insert() */ struct dns_packet *dns_hosts_query(struct dns_hosts *hosts, struct dns_packet *Q, int *error_) { struct dns_packet *P = dns_p_new(512); struct dns_packet *A = 0; struct dns_rr rr; struct dns_hosts_entry *ent; int error, af; char qname[DNS_D_MAXNAME + 1]; size_t qlen; if ((error = dns_rr_parse(&rr, 12, Q))) goto error; if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, Q, &error))) goto error; else if (qlen >= sizeof qname) goto toolong; if ((error = dns_p_push(P, DNS_S_QD, qname, qlen, rr.type, rr.class, 0, 0))) goto error; switch (rr.type) { case DNS_T_PTR: for (ent = hosts->head; ent; ent = ent->next) { if (ent->alias || 0 != strcasecmp(qname, ent->arpa)) continue; if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, ent->host))) goto error; } break; case DNS_T_AAAA: af = AF_INET6; goto loop; case DNS_T_A: af = AF_INET; loop: for (ent = hosts->head; ent; ent = ent->next) { if (ent->af != af || 0 != strcasecmp(qname, ent->host)) continue; if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, &ent->addr))) goto error; } break; default: break; } /* switch() */ if (!(A = dns_p_copy(dns_p_make(P->end, &error), P))) goto error; return A; toolong: error = DNS_EILLEGAL; error: *error_ = error; dns_p_free(A); return 0; } /* dns_hosts_query() */ /* * R E S O L V . C O N F R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_resolv_conf *dns_resconf_open(int *error) { static const struct dns_resolv_conf resconf_initializer = { .lookup = "fb", .family = { AF_INET, AF_INET6 }, .options = { .ndots = 1, .timeout = 5, .attempts = 2, .tcp = DNS_RESCONF_TCP_ENABLE, }, .iface = { .ss_family = AF_INET }, }; struct dns_resolv_conf *resconf; struct sockaddr_in *sin; if (!(resconf = malloc(sizeof *resconf))) goto syerr; *resconf = resconf_initializer; sin = (struct sockaddr_in *)&resconf->nameserver[0]; sin->sin_family = AF_INET; sin->sin_addr.s_addr = INADDR_ANY; sin->sin_port = htons(53); #if defined(SA_LEN) sin->sin_len = sizeof *sin; #endif if (0 != gethostname(resconf->search[0], sizeof resconf->search[0])) goto syerr; dns_d_anchor(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); dns_d_cleave(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); /* * XXX: If gethostname() returned a string without any label * separator, then search[0][0] should be NUL. */ dns_resconf_acquire(resconf); return resconf; syerr: *error = dns_syerr(); free(resconf); return 0; } /* dns_resconf_open() */ void dns_resconf_close(struct dns_resolv_conf *resconf) { if (!resconf || 1 != dns_resconf_release(resconf)) return /* void */; free(resconf); } /* dns_resconf_close() */ dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *resconf) { return dns_atomic_fetch_add(&resconf->_.refcount); } /* dns_resconf_acquire() */ dns_refcount_t dns_resconf_release(struct dns_resolv_conf *resconf) { return dns_atomic_fetch_sub(&resconf->_.refcount); } /* dns_resconf_release() */ struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *resconf) { if (resconf) dns_resconf_release(resconf); return resconf; } /* dns_resconf_mortal() */ struct dns_resolv_conf *dns_resconf_local(int *error_) { struct dns_resolv_conf *resconf; int error; if (!(resconf = dns_resconf_open(&error))) goto error; if ((error = dns_resconf_loadpath(resconf, "/etc/resolv.conf"))) { /* * NOTE: Both the glibc and BIND9 resolvers ignore a missing * /etc/resolv.conf, defaulting to a nameserver of * 127.0.0.1. See also dns_hints_insert_resconf, and the * default initialization of nameserver[0] in * dns_resconf_open. */ if (error != ENOENT) goto error; } if ((error = dns_nssconf_loadpath(resconf, "/etc/nsswitch.conf"))) { if (error != ENOENT) goto error; } return resconf; error: *error_ = error; dns_resconf_close(resconf); return 0; } /* dns_resconf_local() */ struct dns_resolv_conf *dns_resconf_root(int *error) { struct dns_resolv_conf *resconf; if ((resconf = dns_resconf_local(error))) resconf->options.recurse = 1; return resconf; } /* dns_resconf_root() */ static time_t dns_resconf_timeout(const struct dns_resolv_conf *resconf) { return (time_t)DNS_PP_MIN(INT_MAX, resconf->options.timeout); } /* dns_resconf_timeout() */ enum dns_resconf_keyword { DNS_RESCONF_NAMESERVER, DNS_RESCONF_DOMAIN, DNS_RESCONF_SEARCH, DNS_RESCONF_LOOKUP, DNS_RESCONF_FILE, DNS_RESCONF_BIND, DNS_RESCONF_CACHE, DNS_RESCONF_FAMILY, DNS_RESCONF_INET4, DNS_RESCONF_INET6, DNS_RESCONF_OPTIONS, DNS_RESCONF_EDNS0, DNS_RESCONF_NDOTS, DNS_RESCONF_TIMEOUT, DNS_RESCONF_ATTEMPTS, DNS_RESCONF_ROTATE, DNS_RESCONF_RECURSE, DNS_RESCONF_SMART, DNS_RESCONF_TCP, DNS_RESCONF_TCPx, DNS_RESCONF_INTERFACE, DNS_RESCONF_ZERO, DNS_RESCONF_ONE, DNS_RESCONF_ENABLE, DNS_RESCONF_ONLY, DNS_RESCONF_DISABLE, }; /* enum dns_resconf_keyword */ static enum dns_resconf_keyword dns_resconf_keyword(const char *word) { static const char *words[] = { [DNS_RESCONF_NAMESERVER] = "nameserver", [DNS_RESCONF_DOMAIN] = "domain", [DNS_RESCONF_SEARCH] = "search", [DNS_RESCONF_LOOKUP] = "lookup", [DNS_RESCONF_FILE] = "file", [DNS_RESCONF_BIND] = "bind", [DNS_RESCONF_CACHE] = "cache", [DNS_RESCONF_FAMILY] = "family", [DNS_RESCONF_INET4] = "inet4", [DNS_RESCONF_INET6] = "inet6", [DNS_RESCONF_OPTIONS] = "options", [DNS_RESCONF_EDNS0] = "edns0", [DNS_RESCONF_ROTATE] = "rotate", [DNS_RESCONF_RECURSE] = "recurse", [DNS_RESCONF_SMART] = "smart", [DNS_RESCONF_TCP] = "tcp", [DNS_RESCONF_INTERFACE] = "interface", [DNS_RESCONF_ZERO] = "0", [DNS_RESCONF_ONE] = "1", [DNS_RESCONF_ENABLE] = "enable", [DNS_RESCONF_ONLY] = "only", [DNS_RESCONF_DISABLE] = "disable", }; unsigned i; for (i = 0; i < lengthof(words); i++) { if (words[i] && 0 == strcasecmp(words[i], word)) return i; } if (0 == strncasecmp(word, "ndots:", sizeof "ndots:" - 1)) return DNS_RESCONF_NDOTS; if (0 == strncasecmp(word, "timeout:", sizeof "timeout:" - 1)) return DNS_RESCONF_TIMEOUT; if (0 == strncasecmp(word, "attempts:", sizeof "attempts:" - 1)) return DNS_RESCONF_ATTEMPTS; if (0 == strncasecmp(word, "tcp:", sizeof "tcp:" - 1)) return DNS_RESCONF_TCPx; return -1; } /* dns_resconf_keyword() */ /** OpenBSD-style "[1.2.3.4]:53" nameserver syntax */ int dns_resconf_pton(struct sockaddr_storage *ss, const char *src) { struct { char buf[128], *p; } addr = { "", addr.buf }; unsigned short port = 0; struct addrinfo *ai; int ch, af = AF_INET; while ((ch = *src++)) { switch (ch) { case ' ': /* FALL THROUGH */ case '\t': break; case '[': break; case ']': while ((ch = *src++)) { if (dns_isdigit(ch)) { port *= 10; port += ch - '0'; } } goto inet; case ':': af = AF_INET6; /* FALL THROUGH */ default: if (addr.p < endof(addr.buf) - 1) *addr.p++ = ch; break; } /* switch() */ } /* while() */ inet: port = (!port)? 53 : port; ai = bctbx_ip_address_to_addrinfo(af, SOCK_DGRAM, addr.buf, port); if (ai == NULL) return dns_soerr(); memcpy(ss, ai->ai_addr, ai->ai_addrlen); bctbx_freeaddrinfo(ai); return 0; } /* dns_resconf_pton() */ #define dns_resconf_issep(ch) (dns_isspace(ch) || (ch) == ',') #define dns_resconf_iscom(ch) ((ch) == '#' || (ch) == ';') int dns_resconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) { unsigned sa_count = 0; char words[6][DNS_D_MAXNAME + 1]; unsigned wp, wc, i, j, n; int ch, error; rewind(fp); do { memset(words, '\0', sizeof words); wp = 0; wc = 0; while (EOF != (ch = getc(fp)) && ch != '\n') { if (dns_resconf_issep(ch)) { if (wp > 0) { wp = 0; if (++wc >= lengthof(words)) goto skip; } } else if (dns_resconf_iscom(ch)) { skip: do { ch = getc(fp); } while (ch != EOF && ch != '\n'); break; } else if (wp < sizeof words[wc] - 1) { words[wc][wp++] = ch; } else { wp = 0; /* drop word */ goto skip; } } if (wp > 0) wc++; if (wc < 2) continue; switch (dns_resconf_keyword(words[0])) { case DNS_RESCONF_NAMESERVER: if (sa_count >= lengthof(resconf->nameserver)) continue; if ((error = dns_resconf_pton(&resconf->nameserver[sa_count], words[1]))) continue; sa_count++; break; case DNS_RESCONF_DOMAIN: case DNS_RESCONF_SEARCH: memset(resconf->search, '\0', sizeof resconf->search); for (i = 1, j = 0; i < wc && j < lengthof(resconf->search); i++, j++) dns_d_anchor(resconf->search[j], sizeof resconf->search[j], words[i], strlen(words[i])); break; case DNS_RESCONF_LOOKUP: for (i = 1, j = 0; i < wc && j < lengthof(resconf->lookup); i++) { switch (dns_resconf_keyword(words[i])) { case DNS_RESCONF_FILE: resconf->lookup[j++] = 'f'; break; case DNS_RESCONF_BIND: resconf->lookup[j++] = 'b'; break; case DNS_RESCONF_CACHE: resconf->lookup[j++] = 'c'; break; default: break; } /* switch() */ } /* for() */ break; case DNS_RESCONF_FAMILY: for (i = 1, j = 0; i < wc && j < lengthof(resconf->family); i++) { switch (dns_resconf_keyword(words[i])) { case DNS_RESCONF_INET4: resconf->family[j++] = AF_INET; break; case DNS_RESCONF_INET6: resconf->family[j++] = AF_INET6; break; default: break; } } break; case DNS_RESCONF_OPTIONS: for (i = 1; i < wc; i++) { switch (dns_resconf_keyword(words[i])) { case DNS_RESCONF_EDNS0: resconf->options.edns0 = 1; break; case DNS_RESCONF_NDOTS: for (j = sizeof "ndots:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { n *= 10; n += words[i][j] - '0'; } /* for() */ resconf->options.ndots = n; break; case DNS_RESCONF_TIMEOUT: for (j = sizeof "timeout:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { n *= 10; n += words[i][j] - '0'; } /* for() */ resconf->options.timeout = n; break; case DNS_RESCONF_ATTEMPTS: for (j = sizeof "attempts:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { n *= 10; n += words[i][j] - '0'; } /* for() */ resconf->options.attempts = n; break; case DNS_RESCONF_ROTATE: resconf->options.rotate = 1; break; case DNS_RESCONF_RECURSE: resconf->options.recurse = 1; break; case DNS_RESCONF_SMART: resconf->options.smart = 1; break; case DNS_RESCONF_TCP: resconf->options.tcp = DNS_RESCONF_TCP_ONLY; break; case DNS_RESCONF_TCPx: switch (dns_resconf_keyword(&words[i][sizeof "tcp:" - 1])) { case DNS_RESCONF_ENABLE: resconf->options.tcp = DNS_RESCONF_TCP_ENABLE; break; case DNS_RESCONF_ONE: case DNS_RESCONF_ONLY: resconf->options.tcp = DNS_RESCONF_TCP_ONLY; break; case DNS_RESCONF_ZERO: case DNS_RESCONF_DISABLE: resconf->options.tcp = DNS_RESCONF_TCP_DISABLE; break; default: break; } /* switch() */ break; default: break; } /* switch() */ } /* for() */ break; case DNS_RESCONF_INTERFACE: for (i = 0, n = 0; dns_isdigit(words[2][i]); i++) { n *= 10; n += words[2][i] - '0'; } dns_resconf_setiface(resconf, words[1], n); break; default: break; } /* switch() */ } while (ch != EOF); return 0; } /* dns_resconf_loadfile() */ int dns_resconf_loadpath(struct dns_resolv_conf *resconf, const char *path) { FILE *fp; int error; if (!(fp = dns_fopen(path, "rt", &error))) return error; error = dns_resconf_loadfile(resconf, fp); fclose(fp); return error; } /* dns_resconf_loadpath() */ #ifdef USE_FIXED_NAMESERVERS int dns_resconf_load_fixed_nameservers(struct dns_resolv_conf *resconf) { const char * const nameservers[] = { "8.8.8.8", "8.8.4.4" }; int i; int error = 0; for (i = 0; !error && (i < lengthof(nameservers)); i++) { error = dns_resconf_pton(&resconf->nameserver[i], nameservers[i]); } return error; } #endif /* USE_FIXED_NAMESERVERS */ #ifdef USE_STRUCT_RES_STATE_NAMESERVERS int dns_resconf_load_struct_res_state_nameservers(struct dns_resolv_conf *resconf) { int i; struct __res_state *rs = __res_get_state(); for (i = 0; i < rs->nscount; i++) { memcpy(&resconf->nameserver[i], (struct sockaddr_storage *)&rs->nsaddr_list[i], sizeof(struct sockaddr_in)); } return 0; } #endif /* USE_STRUCT_RES_STATE_NAMESERVERS */ #if defined(_WIN32) && !defined(USE_FIXED_NAMESERVERS) int dns_resconf_loadwin(struct dns_resolv_conf *resconf) { FIXED_INFO *pFixedInfo; ULONG ulOutBufLen; DWORD dwRetVal; IP_ADDR_STRING *pIPAddr; unsigned sa_count = 0; int error; pFixedInfo = (FIXED_INFO *) malloc(sizeof(FIXED_INFO)); if (pFixedInfo == NULL) { return -1; } ulOutBufLen = sizeof(FIXED_INFO); if (GetNetworkParams(pFixedInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) { free(pFixedInfo); pFixedInfo = (FIXED_INFO *) malloc(ulOutBufLen); if (pFixedInfo == NULL) { return -1; } } if ((dwRetVal = GetNetworkParams(pFixedInfo, &ulOutBufLen)) == NO_ERROR) { memset(resconf->search, '\0', sizeof resconf->search); memcpy(resconf->search[0], pFixedInfo->DomainName, sizeof pFixedInfo->DomainName); pIPAddr = &pFixedInfo->DnsServerList; do { error = dns_resconf_pton(&resconf->nameserver[sa_count], pIPAddr->IpAddress.String); pIPAddr = pIPAddr->Next; sa_count++; } while (!error && pIPAddr && (sa_count < lengthof(resconf->nameserver))); } free(pFixedInfo); return 0; } #endif /* dns_resconf_loadwin() */ #ifdef __ANDROID__ int dns_resconf_loadandroid(struct dns_resolv_conf *resconf) { char dns[PROP_VALUE_MAX]; char prop_name[PROP_NAME_MAX]; unsigned int sa_count = 0; int error = 0; int i; for (i = 1; !error && (i <= lengthof(resconf->nameserver)); i++) { snprintf(prop_name, sizeof(prop_name), "net.dns%d", i); if (__system_property_get(prop_name, dns) > 0) { if (dns_resconf_pton(&resconf->nameserver[sa_count], dns) == 0){ sa_count++; } } } if (sa_count == 0) { /* No net.dnsX property found, return an error. */ error = -1; } return error; } /* dns_resconf_loadandroid */ #endif #ifdef HAVE_RESINIT int dns_resconf_loadfromresolv(struct dns_resolv_conf *resconf) { struct __res_state res; union res_sockaddr_union addresses[3]; int i,error; if ((error = res_ninit(&res))) { return error; } error=res_getservers(&res,addresses,3); if (error>0){ for (i = 0; inameserver[i],&addresses[i],sizeof(union res_sockaddr_union)); } error=0; }else error=-1; res_ndestroy(&res); return error; } #endif /*HAVE_RESINIT*/ struct dns_anyconf { char *token[16]; unsigned count; char buffer[1024], *tp, *cp; }; /* struct dns_anyconf */ static void dns_anyconf_reset(struct dns_anyconf *cf) { cf->count = 0; cf->tp = cf->cp = cf->buffer; } /* dns_anyconf_reset() */ static int dns_anyconf_push(struct dns_anyconf *cf) { if (!(cf->cp < endof(cf->buffer) && cf->count < lengthof(cf->token))) return ENOMEM; *cf->cp++ = '\0'; cf->token[cf->count++] = cf->tp; cf->tp = cf->cp; return 0; } /* dns_anyconf_push() */ static void dns_anyconf_pop(struct dns_anyconf *cf) { if (cf->count > 0) { --cf->count; cf->tp = cf->cp = cf->token[cf->count]; cf->token[cf->count] = 0; } } /* dns_anyconf_pop() */ static int dns_anyconf_addc(struct dns_anyconf *cf, int ch) { if (!(cf->cp < endof(cf->buffer))) return ENOMEM; *cf->cp++ = ch; return 0; } /* dns_anyconf_addc() */ static _Bool dns_anyconf_match(const char *pat, int mc) { _Bool match; int pc; if (*pat == '^') { match = 0; ++pat; } else { match = 1; } while ((pc = *(const unsigned char *)pat++)) { switch (pc) { case '%': if (!(pc = *(const unsigned char *)pat++)) return !match; switch (pc) { case 'a': if (dns_isalpha(mc)) return match; break; case 'd': if (dns_isdigit(mc)) return match; break; case 'w': if (dns_isalnum(mc)) return match; break; case 's': if (dns_isspace(mc)) return match; break; default: if (mc == pc) return match; break; } /* switch() */ break; default: if (mc == pc) return match; break; } /* switch() */ } /* while() */ return !match; } /* dns_anyconf_match() */ static int dns_anyconf_peek(FILE *fp) { int ch; ch = getc(fp); ungetc(ch, fp); return ch; } /* dns_anyconf_peek() */ static size_t dns_anyconf_skip(const char *pat, FILE *fp) { size_t count = 0; int ch; while (EOF != (ch = getc(fp))) { if (dns_anyconf_match(pat, ch)) { count++; continue; } ungetc(ch, fp); break; } return count; } /* dns_anyconf_skip() */ static size_t dns_anyconf_scan(struct dns_anyconf *cf, const char *pat, FILE *fp, int *error) { size_t len; int ch; while (EOF != (ch = getc(fp))) { if (dns_anyconf_match(pat, ch)) { if ((*error = dns_anyconf_addc(cf, ch))) return 0; continue; } else { ungetc(ch, fp); break; } } if ((len = cf->cp - cf->tp)) { if ((*error = dns_anyconf_push(cf))) return 0; return len; } else { *error = 0; return 0; } } /* dns_anyconf_scan() */ DNS_NOTUSED static void dns_anyconf_dump(struct dns_anyconf *cf, FILE *fp) { unsigned i; fprintf(fp, "tokens:"); for (i = 0; i < cf->count; i++) { fprintf(fp, " %s", cf->token[i]); } fputc('\n', fp); } /* dns_anyconf_dump() */ enum dns_nssconf_keyword { DNS_NSSCONF_INVALID = 0, DNS_NSSCONF_HOSTS = 1, DNS_NSSCONF_SUCCESS, DNS_NSSCONF_NOTFOUND, DNS_NSSCONF_UNAVAIL, DNS_NSSCONF_TRYAGAIN, DNS_NSSCONF_CONTINUE, DNS_NSSCONF_RETURN, DNS_NSSCONF_FILES, DNS_NSSCONF_DNS, DNS_NSSCONF_MDNS, DNS_NSSCONF_LAST, }; /* enum dns_nssconf_keyword */ static enum dns_nssconf_keyword dns_nssconf_keyword(const char *word) { static const char *list[] = { [DNS_NSSCONF_HOSTS] = "hosts", [DNS_NSSCONF_SUCCESS] = "success", [DNS_NSSCONF_NOTFOUND] = "notfound", [DNS_NSSCONF_UNAVAIL] = "unavail", [DNS_NSSCONF_TRYAGAIN] = "tryagain", [DNS_NSSCONF_CONTINUE] = "continue", [DNS_NSSCONF_RETURN] = "return", [DNS_NSSCONF_FILES] = "files", [DNS_NSSCONF_DNS] = "dns", [DNS_NSSCONF_MDNS] = "mdns", }; unsigned i; for (i = 1; i < lengthof(list); i++) { if (list[i] && 0 == strcasecmp(list[i], word)) return i; } return DNS_NSSCONF_INVALID; } /* dns_nssconf_keyword() */ static enum dns_nssconf_keyword dns_nssconf_c2k(int ch) { static const char map[] = { ['S'] = DNS_NSSCONF_SUCCESS, ['N'] = DNS_NSSCONF_NOTFOUND, ['U'] = DNS_NSSCONF_UNAVAIL, ['T'] = DNS_NSSCONF_TRYAGAIN, ['C'] = DNS_NSSCONF_CONTINUE, ['R'] = DNS_NSSCONF_RETURN, ['f'] = DNS_NSSCONF_FILES, ['F'] = DNS_NSSCONF_FILES, ['d'] = DNS_NSSCONF_DNS, ['D'] = DNS_NSSCONF_DNS, ['b'] = DNS_NSSCONF_DNS, ['B'] = DNS_NSSCONF_DNS, ['m'] = DNS_NSSCONF_MDNS, ['M'] = DNS_NSSCONF_MDNS, }; return (ch >= 0 && ch < (int)lengthof(map))? map[ch] : DNS_NSSCONF_INVALID; } /* dns_nssconf_c2k() */ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET static int dns_nssconf_k2c(int k) { static const char map[DNS_NSSCONF_LAST] = { [DNS_NSSCONF_SUCCESS] = 'S', [DNS_NSSCONF_NOTFOUND] = 'N', [DNS_NSSCONF_UNAVAIL] = 'U', [DNS_NSSCONF_TRYAGAIN] = 'T', [DNS_NSSCONF_CONTINUE] = 'C', [DNS_NSSCONF_RETURN] = 'R', [DNS_NSSCONF_FILES] = 'f', [DNS_NSSCONF_DNS] = 'b', [DNS_NSSCONF_MDNS] = 'm', }; return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : '?') : '?'; } /* dns_nssconf_k2c() */ static const char *dns_nssconf_k2s(int k) { static const char *const map[DNS_NSSCONF_LAST] = { [DNS_NSSCONF_SUCCESS] = "SUCCESS", [DNS_NSSCONF_NOTFOUND] = "NOTFOUND", [DNS_NSSCONF_UNAVAIL] = "UNAVAIL", [DNS_NSSCONF_TRYAGAIN] = "TRYAGAIN", [DNS_NSSCONF_CONTINUE] = "continue", [DNS_NSSCONF_RETURN] = "return", [DNS_NSSCONF_FILES] = "files", [DNS_NSSCONF_DNS] = "dns", [DNS_NSSCONF_MDNS] = "mdns", }; return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : "") : ""; } /* dns_nssconf_k2s() */ DNS_PRAGMA_POP int dns_nssconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) { enum dns_nssconf_keyword source, status, action; char lookup[sizeof resconf->lookup] = "", *lp; struct dns_anyconf cf; size_t i; int error; while (!feof(fp) && !ferror(fp)) { dns_anyconf_reset(&cf); dns_anyconf_skip("%s", fp); if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) goto nextent; if (DNS_NSSCONF_HOSTS != dns_nssconf_keyword(cf.token[0])) goto nextent; dns_anyconf_pop(&cf); if (!dns_anyconf_skip(": \t", fp)) goto nextent; *(lp = lookup) = '\0'; while (dns_anyconf_scan(&cf, "%w_", fp, &error)) { dns_anyconf_skip(" \t", fp); if ('[' == dns_anyconf_peek(fp)) { dns_anyconf_skip("[ \t", fp); while (dns_anyconf_scan(&cf, "%w_!", fp, &error)) { dns_anyconf_skip("= \t", fp); if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) { dns_anyconf_pop(&cf); /* discard status */ dns_anyconf_skip("^#;]\n", fp); /* skip to end of criteria */ break; } dns_anyconf_skip(" \t", fp); } dns_anyconf_skip("] \t", fp); } if ((size_t)(endof(lookup) - lp) < cf.count + 1) /* +1 for '\0' */ goto nextsrc; source = dns_nssconf_keyword(cf.token[0]); switch (source) { case DNS_NSSCONF_DNS: case DNS_NSSCONF_MDNS: case DNS_NSSCONF_FILES: *lp++ = dns_nssconf_k2c(source); break; default: goto nextsrc; } for (i = 1; i + 1 < cf.count; i += 2) { status = dns_nssconf_keyword(cf.token[i]); action = dns_nssconf_keyword(cf.token[i + 1]); switch (status) { case DNS_NSSCONF_SUCCESS: case DNS_NSSCONF_NOTFOUND: case DNS_NSSCONF_UNAVAIL: case DNS_NSSCONF_TRYAGAIN: *lp++ = dns_nssconf_k2c(status); break; default: continue; } switch (action) { case DNS_NSSCONF_CONTINUE: case DNS_NSSCONF_RETURN: break; default: action = (status == DNS_NSSCONF_SUCCESS) ? DNS_NSSCONF_RETURN : DNS_NSSCONF_CONTINUE; break; } *lp++ = dns_nssconf_k2c(action); } nextsrc: *lp = '\0'; dns_anyconf_reset(&cf); } nextent: dns_anyconf_skip("^\n", fp); } if (*lookup) strncpy(resconf->lookup, lookup, sizeof resconf->lookup); return 0; } /* dns_nssconf_loadfile() */ int dns_nssconf_loadpath(struct dns_resolv_conf *resconf, const char *path) { FILE *fp; int error; if (!(fp = dns_fopen(path, "rt", &error))) return error; error = dns_nssconf_loadfile(resconf, fp); fclose(fp); return error; } /* dns_nssconf_loadpath() */ struct dns_nssconf_source { enum dns_nssconf_keyword source, success, notfound, unavail, tryagain; }; /* struct dns_nssconf_source */ typedef unsigned dns_nssconf_i; static DNS_INLINE int dns_nssconf_peek(const struct dns_resolv_conf *resconf, dns_nssconf_i state) { return (state < lengthof(resconf->lookup) && resconf->lookup[state])? resconf->lookup[state] : 0; } /* dns_nssconf_peek() */ static _Bool dns_nssconf_next(struct dns_nssconf_source *src, const struct dns_resolv_conf *resconf, dns_nssconf_i *state) { int source, status, action; src->source = DNS_NSSCONF_INVALID; src->success = DNS_NSSCONF_RETURN; src->notfound = DNS_NSSCONF_CONTINUE; src->unavail = DNS_NSSCONF_CONTINUE; src->tryagain = DNS_NSSCONF_CONTINUE; while ((source = dns_nssconf_peek(resconf, *state))) { source = dns_nssconf_c2k(source); ++*state; switch (source) { case DNS_NSSCONF_FILES: case DNS_NSSCONF_DNS: case DNS_NSSCONF_MDNS: src->source = source; break; default: continue; } while ((status = dns_nssconf_peek(resconf, *state)) && (action = dns_nssconf_peek(resconf, *state + 1))) { status = dns_nssconf_c2k(status); action = dns_nssconf_c2k(action); switch (action) { case DNS_NSSCONF_RETURN: case DNS_NSSCONF_CONTINUE: break; default: goto done; } switch (status) { case DNS_NSSCONF_SUCCESS: src->success = action; break; case DNS_NSSCONF_NOTFOUND: src->notfound = action; break; case DNS_NSSCONF_UNAVAIL: src->unavail = action; break; case DNS_NSSCONF_TRYAGAIN: src->tryagain = action; break; default: goto done; } *state += 2; } break; } done: return src->source != DNS_NSSCONF_INVALID; } /* dns_nssconf_next() */ static int dns_nssconf_dump_status(int status, int action, unsigned *count, FILE *fp) { switch (status) { case DNS_NSSCONF_SUCCESS: if (action == DNS_NSSCONF_RETURN) return 0; break; default: if (action == DNS_NSSCONF_CONTINUE) return 0; break; } fputc(' ', fp); if (!*count) fputc('[', fp); fprintf(fp, "%s=%s", dns_nssconf_k2s(status), dns_nssconf_k2s(action)); ++*count; return 0; } /* dns_nssconf_dump_status() */ int dns_nssconf_dump(struct dns_resolv_conf *resconf, FILE *fp) { struct dns_nssconf_source src; dns_nssconf_i i = 0; fputs("hosts:", fp); while (dns_nssconf_next(&src, resconf, &i)) { unsigned n = 0; fprintf(fp, " %s", dns_nssconf_k2s(src.source)); dns_nssconf_dump_status(DNS_NSSCONF_SUCCESS, src.success, &n, fp); dns_nssconf_dump_status(DNS_NSSCONF_NOTFOUND, src.notfound, &n, fp); dns_nssconf_dump_status(DNS_NSSCONF_UNAVAIL, src.unavail, &n, fp); dns_nssconf_dump_status(DNS_NSSCONF_TRYAGAIN, src.tryagain, &n, fp); if (n) fputc(']', fp); } fputc('\n', fp); return 0; } /* dns_nssconf_dump() */ int dns_resconf_setiface(struct dns_resolv_conf *resconf, const char *addr, unsigned short port) { int af = (strchr(addr, ':'))? AF_INET6 : AF_INET; int error; if ((error = dns_pton(af, addr, dns_sa_addr(af, &resconf->iface, NULL)))) return error; *dns_sa_port(af, &resconf->iface) = htons(port); resconf->iface.ss_family = af; return 0; } /* dns_resconf_setiface() */ size_t dns_resconf_search(void *dst, size_t lim, const void *qname, size_t qlen, struct dns_resolv_conf *resconf, dns_resconf_i_t *state) { unsigned srchi = 0xff & (*state >> 8); unsigned ndots = 0xff & (*state >> 16); unsigned len = 0; const char *qp, *qe; switch (0xff & *state) { case 0: qp = qname; qe = qp + qlen; while ((qp = memchr(qp, '.', qe - qp))) { ndots++; qp++; } ++*state; if (ndots >= resconf->options.ndots) { len = dns_d_anchor(dst, lim, qname, qlen); break; } /* FALL THROUGH */ case 1: if (srchi < lengthof(resconf->search) && resconf->search[srchi][0] && strcmp(resconf->search[srchi], ".")) { len = dns_d_anchor(dst, lim, qname, qlen); len += dns_strlcpy((char *)dst + DNS_PP_MIN(len, lim), resconf->search[srchi], lim - DNS_PP_MIN(len, lim)); srchi++; break; } ++*state; /* FALL THROUGH */ case 2: ++*state; if (ndots < resconf->options.ndots) { len = dns_d_anchor(dst, lim, qname, qlen); break; } /* FALL THROUGH */ default: break; } /* switch() */ *state = ((0xff & *state) << 0) | ((0xff & srchi) << 8) | ((0xff & ndots) << 16); if (lim > 0) ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0'; return len; } /* dns_resconf_search() */ int dns_resconf_dump(struct dns_resolv_conf *resconf, FILE *fp) { unsigned i; int af; for (i = 0; i < lengthof(resconf->nameserver) && (af = resconf->nameserver[i].ss_family) != AF_UNSPEC; i++) { char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]"; unsigned short port; dns_inet_ntop(af, dns_sa_addr(af, &resconf->nameserver[i], NULL), addr, sizeof addr); port = ntohs(*dns_sa_port(af, &resconf->nameserver[i])); if (port == 53) fprintf(fp, "nameserver %s\n", addr); else fprintf(fp, "nameserver [%s]:%hu\n", addr, port); } fprintf(fp, "search"); for (i = 0; i < lengthof(resconf->search) && resconf->search[i][0]; i++) fprintf(fp, " %s", resconf->search[i]); fputc('\n', fp); fputs("; ", fp); dns_nssconf_dump(resconf, fp); fprintf(fp, "lookup"); for (i = 0; i < lengthof(resconf->lookup) && resconf->lookup[i]; i++) { switch (resconf->lookup[i]) { case 'b': fprintf(fp, " bind"); break; case 'f': fprintf(fp, " file"); break; case 'c': fprintf(fp, " cache"); break; } } fputc('\n', fp); fprintf(fp, "options ndots:%u timeout:%u attempts:%u", resconf->options.ndots, resconf->options.timeout, resconf->options.attempts); if (resconf->options.edns0) fprintf(fp, " edns0"); if (resconf->options.rotate) fprintf(fp, " rotate"); if (resconf->options.recurse) fprintf(fp, " recurse"); if (resconf->options.smart) fprintf(fp, " smart"); switch (resconf->options.tcp) { case DNS_RESCONF_TCP_ENABLE: break; case DNS_RESCONF_TCP_ONLY: fprintf(fp, " tcp"); break; case DNS_RESCONF_TCP_DISABLE: fprintf(fp, " tcp:disable"); break; } fputc('\n', fp); if ((af = resconf->iface.ss_family) != AF_UNSPEC) { char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]"; dns_inet_ntop(af, dns_sa_addr(af, &resconf->iface, NULL), addr, sizeof addr); fprintf(fp, "interface %s %hu\n", addr, ntohs(*dns_sa_port(af, &resconf->iface))); } return 0; } /* dns_resconf_dump() */ /* * H I N T S E R V E R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_hints_soa { unsigned char zone[DNS_D_MAXNAME + 1]; struct { struct sockaddr_storage ss; unsigned priority; } addrs[16]; unsigned count; struct dns_hints_soa *next; }; /* struct dns_hints_soa */ struct dns_hints { dns_atomic_t refcount; struct dns_hints_soa *head; }; /* struct dns_hints */ struct dns_hints *dns_hints_open(struct dns_resolv_conf *resconf, int *error) { static const struct dns_hints H_initializer; struct dns_hints *H; (void)resconf; if (!(H = malloc(sizeof *H))) goto syerr; *H = H_initializer; dns_hints_acquire(H); return H; syerr: *error = dns_syerr(); free(H); return 0; } /* dns_hints_open() */ void dns_hints_close(struct dns_hints *H) { struct dns_hints_soa *soa, *nxt; if (!H || 1 != dns_hints_release(H)) return /* void */; for (soa = H->head; soa; soa = nxt) { nxt = soa->next; free(soa); } free(H); return /* void */; } /* dns_hints_close() */ dns_refcount_t dns_hints_acquire(struct dns_hints *H) { return dns_atomic_fetch_add(&H->refcount); } /* dns_hints_acquire() */ dns_refcount_t dns_hints_release(struct dns_hints *H) { return dns_atomic_fetch_sub(&H->refcount); } /* dns_hints_release() */ struct dns_hints *dns_hints_mortal(struct dns_hints *hints) { if (hints) dns_hints_release(hints); return hints; } /* dns_hints_mortal() */ struct dns_hints *dns_hints_local(struct dns_resolv_conf *resconf, int *error_) { struct dns_hints *hints = 0; int error; if (resconf) dns_resconf_acquire(resconf); else if (!(resconf = dns_resconf_local(&error))) goto error; if (!(hints = dns_hints_open(resconf, &error))) goto error; error = 0; if (0 == dns_hints_insert_resconf(hints, ".", resconf, &error) && error) goto error; dns_resconf_close(resconf); return hints; error: *error_ = error; dns_resconf_close(resconf); dns_hints_close(hints); return 0; } /* dns_hints_local() */ struct dns_hints *dns_hints_root(struct dns_resolv_conf *resconf, int *error_) { static const struct { int af; char addr[INET6_ADDRSTRLEN]; } root_hints[] = { { AF_INET, "198.41.0.4" }, /* A.ROOT-SERVERS.NET. */ { AF_INET6, "2001:503:ba3e::2:30" }, /* A.ROOT-SERVERS.NET. */ { AF_INET, "192.228.79.201" }, /* B.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:84::b" }, /* B.ROOT-SERVERS.NET. */ { AF_INET, "192.33.4.12" }, /* C.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:2::c" }, /* C.ROOT-SERVERS.NET. */ { AF_INET, "199.7.91.13" }, /* D.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:2d::d" }, /* D.ROOT-SERVERS.NET. */ { AF_INET, "192.203.230.10" }, /* E.ROOT-SERVERS.NET. */ { AF_INET, "192.5.5.241" }, /* F.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:2f::f" }, /* F.ROOT-SERVERS.NET. */ { AF_INET, "192.112.36.4" }, /* G.ROOT-SERVERS.NET. */ { AF_INET, "128.63.2.53" }, /* H.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:1::803f:235" }, /* H.ROOT-SERVERS.NET. */ { AF_INET, "192.36.148.17" }, /* I.ROOT-SERVERS.NET. */ { AF_INET6, "2001:7FE::53" }, /* I.ROOT-SERVERS.NET. */ { AF_INET, "192.58.128.30" }, /* J.ROOT-SERVERS.NET. */ { AF_INET6, "2001:503:c27::2:30" }, /* J.ROOT-SERVERS.NET. */ { AF_INET, "193.0.14.129" }, /* K.ROOT-SERVERS.NET. */ { AF_INET6, "2001:7FD::1" }, /* K.ROOT-SERVERS.NET. */ { AF_INET, "199.7.83.42" }, /* L.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:3::42" }, /* L.ROOT-SERVERS.NET. */ { AF_INET, "202.12.27.33" }, /* M.ROOT-SERVERS.NET. */ { AF_INET6, "2001:DC3::35" }, /* M.ROOT-SERVERS.NET. */ }; struct dns_hints *hints = 0; struct sockaddr_storage ss; unsigned i; int error, af; if (!(hints = dns_hints_open(resconf, &error))) goto error; for (i = 0; i < lengthof(root_hints); i++) { af = root_hints[i].af; if ((error = dns_pton(af, root_hints[i].addr, dns_sa_addr(af, &ss, NULL)))) goto error; *dns_sa_port(af, &ss) = htons(53); ss.ss_family = af; if ((error = dns_hints_insert(hints, ".", (struct sockaddr *)&ss, 1))) goto error; } return hints; error: *error_ = error; dns_hints_close(hints); return 0; } /* dns_hints_root() */ static struct dns_hints_soa *dns_hints_fetch(struct dns_hints *H, const char *zone) { struct dns_hints_soa *soa; for (soa = H->head; soa; soa = soa->next) { if (0 == strcasecmp(zone, (char *)soa->zone)) return soa; } return 0; } /* dns_hints_fetch() */ int dns_hints_insert(struct dns_hints *H, const char *zone, const struct sockaddr *sa, unsigned priority) { static const struct dns_hints_soa soa_initializer; struct dns_hints_soa *soa; unsigned i; if (!(soa = dns_hints_fetch(H, zone))) { if (!(soa = malloc(sizeof *soa))) return dns_syerr(); *soa = soa_initializer; dns_strlcpy((char *)soa->zone, zone, sizeof soa->zone); soa->next = H->head; H->head = soa; } i = soa->count % lengthof(soa->addrs); memcpy(&soa->addrs[i].ss, sa, dns_sa_len(sa)); soa->addrs[i].priority = DNS_PP_MAX(1, priority); if (soa->count < lengthof(soa->addrs)) soa->count++; return 0; } /* dns_hints_insert() */ static _Bool dns_hints_isinaddr_any(const void *sa) { struct in_addr *addr; if (dns_sa_family(sa) != AF_INET) return 0; addr = dns_sa_addr(AF_INET, sa, NULL); return addr->s_addr == htonl(INADDR_ANY); } unsigned dns_hints_insert_resconf(struct dns_hints *H, const char *zone, const struct dns_resolv_conf *resconf, int *error_) { unsigned i, n, p; int error; for (i = 0, n = 0, p = 1; i < lengthof(resconf->nameserver) && resconf->nameserver[i].ss_family != AF_UNSPEC; i++, n++) { union { struct sockaddr_in sin; } tmp; struct sockaddr *ns; /* * dns_resconf_open initializes nameserver[0] to INADDR_ANY. * * Traditionally the semantics of 0.0.0.0 meant the default * interface, which evolved to mean the loopback interface. * See comment block preceding resolv/res_init.c:res_init in * glibc 2.23. As of 2.23, glibc no longer translates * 0.0.0.0 despite the code comment, but it does default to * 127.0.0.1 when no nameservers are present. * * BIND9 as of 9.10.3 still translates 0.0.0.0 to 127.0.0.1. * See lib/lwres/lwconfig.c:lwres_create_addr and the * convert_zero flag. 127.0.0.1 is also the default when no * nameservers are present. */ if (dns_hints_isinaddr_any(&resconf->nameserver[i])) { memcpy(&tmp.sin, &resconf->nameserver[i], sizeof tmp.sin); tmp.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); ns = (struct sockaddr *)&tmp.sin; } else { ns = (struct sockaddr *)&resconf->nameserver[i]; } if ((error = dns_hints_insert(H, zone, ns, p))) goto error; p += !resconf->options.rotate; } return n; error: *error_ = error; return n; } /* dns_hints_insert_resconf() */ static int dns_hints_i_cmp(unsigned a, unsigned b, struct dns_hints_i *i, struct dns_hints_soa *soa) { int cmp; if ((cmp = soa->addrs[a].priority - soa->addrs[b].priority)) return cmp; return dns_k_shuffle16(a, i->state.seed) - dns_k_shuffle16(b, i->state.seed); } /* dns_hints_i_cmp() */ static unsigned dns_hints_i_start(struct dns_hints_i *i, struct dns_hints_soa *soa) { unsigned p0, p; p0 = 0; for (p = 1; p < soa->count; p++) { if (dns_hints_i_cmp(p, p0, i, soa) < 0) p0 = p; } return p0; } /* dns_hints_i_start() */ static unsigned dns_hints_i_skip(unsigned p0, struct dns_hints_i *i, struct dns_hints_soa *soa) { unsigned pZ, p; for (pZ = 0; pZ < soa->count; pZ++) { if (dns_hints_i_cmp(pZ, p0, i, soa) > 0) goto cont; } return soa->count; cont: for (p = pZ + 1; p < soa->count; p++) { if (dns_hints_i_cmp(p, p0, i, soa) <= 0) continue; if (dns_hints_i_cmp(p, pZ, i, soa) >= 0) continue; pZ = p; } return pZ; } /* dns_hints_i_skip() */ static struct dns_hints_i *dns_hints_i_init(struct dns_hints_i *i, struct dns_hints *hints) { static const struct dns_hints_i i_initializer; struct dns_hints_soa *soa; i->state = i_initializer.state; do { i->state.seed = dns_random(); } while (0 == i->state.seed); if ((soa = dns_hints_fetch(hints, i->zone))) { i->state.next = dns_hints_i_start(i, soa); } return i; } /* dns_hints_i_init() */ unsigned dns_hints_grep(struct sockaddr **sa, socklen_t *sa_len, unsigned lim, struct dns_hints_i *i, struct dns_hints *H) { struct dns_hints_soa *soa; unsigned n; if (!(soa = dns_hints_fetch(H, i->zone))) return 0; n = 0; while (i->state.next < soa->count && n < lim) { *sa = (struct sockaddr *)&soa->addrs[i->state.next].ss; *sa_len = dns_sa_len(*sa); sa++; sa_len++; n++; i->state.next = dns_hints_i_skip(i->state.next, i, soa); } return n; } /* dns_hints_grep() */ struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q, int *error_) { struct dns_packet *A, *P; struct dns_rr rr; char zone[DNS_D_MAXNAME + 1]; size_t zlen; struct dns_hints_i i; struct sockaddr *sa; socklen_t slen; int error; if (!dns_rr_grep(&rr, 1, dns_rr_i_new(Q, .section = DNS_S_QUESTION), Q, &error)) goto error; if (!(zlen = dns_d_expand(zone, sizeof zone, rr.dn.p, Q, &error))) goto error; else if (zlen >= sizeof zone) goto toolong; P = dns_p_new(512); dns_header(P)->qr = 1; if ((error = dns_rr_copy(P, &rr, Q))) goto error; if ((error = dns_p_push(P, DNS_S_AUTHORITY, ".", strlen("."), DNS_T_NS, DNS_C_IN, 0, "hints.local."))) goto error; do { i.zone = zone; dns_hints_i_init(&i, hints); while (dns_hints_grep(&sa, &slen, 1, &i, hints)) { int af = sa->sa_family; int rtype = (af == AF_INET6)? DNS_T_AAAA : DNS_T_A; if ((error = dns_p_push(P, DNS_S_ADDITIONAL, "hints.local.", strlen("hints.local."), rtype, DNS_C_IN, 0, dns_sa_addr(af, sa, NULL)))) goto error; } } while ((zlen = dns_d_cleave(zone, sizeof zone, zone, zlen))); if (!(A = dns_p_copy(dns_p_make(P->end, &error), P))) goto error; return A; toolong: error = DNS_EILLEGAL; error: *error_ = error; return 0; } /* dns_hints_query() */ /** * Fill a whole sockaddr from the the nameservers' sockaddr contained in the hints. This is needed for scope link IPv6 nameservers. **/ static void dns_fill_sockaddr_from_hints(struct dns_hints *hints, int af, struct sockaddr *addr) { struct dns_hints_soa *soa; unsigned i; for (soa = hints->head; soa; soa = soa->next) { for (i = 0; i < soa->count; i++) { if (af != soa->addrs[i].ss.ss_family) continue; if ((af == AF_INET) && (memcmp(&((struct sockaddr_in *)&soa->addrs[i].ss)->sin_addr, &((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr)))) continue; if ((af == AF_INET6) && (memcmp(&((struct sockaddr_in6 *)&soa->addrs[i].ss)->sin6_addr, &((struct sockaddr_in6 *)addr)->sin6_addr, sizeof(struct in6_addr)))) continue; memcpy(addr, &soa->addrs[i].ss, dns_af_len(af)); return; } } } int dns_hints_dump(struct dns_hints *hints, FILE *fp) { struct dns_hints_soa *soa; char addr[INET6_ADDRSTRLEN]; unsigned i; int af, error; for (soa = hints->head; soa; soa = soa->next) { fprintf(fp, "ZONE \"%s\"\n", soa->zone); for (i = 0; i < soa->count; i++) { af = soa->addrs[i].ss.ss_family; if ((error = dns_ntop(af, dns_sa_addr(af, &soa->addrs[i].ss, NULL), addr, sizeof addr))) return error; fprintf(fp, "\t(%d) [%s]:%hu\n", (int)soa->addrs[i].priority, addr, ntohs(*dns_sa_port(af, &soa->addrs[i].ss))); } } return 0; } /* dns_hints_dump() */ /* * C A C H E R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static dns_refcount_t dns_cache_acquire(struct dns_cache *cache) { return dns_atomic_fetch_add(&cache->_.refcount); } /* dns_cache_acquire() */ static dns_refcount_t dns_cache_release(struct dns_cache *cache) { return dns_atomic_fetch_sub(&cache->_.refcount); } /* dns_cache_release() */ static struct dns_packet *dns_cache_query(struct dns_packet *query, struct dns_cache *cache, int *error) { (void)query; (void)cache; (void)error; return NULL; } /* dns_cache_query() */ static int dns_cache_submit(struct dns_packet *query, struct dns_cache *cache) { (void)query; (void)cache; return 0; } /* dns_cache_submit() */ static int dns_cache_check(struct dns_cache *cache) { (void)cache; return 0; } /* dns_cache_check() */ static struct dns_packet *dns_cache_fetch(struct dns_cache *cache, int *error) { (void)cache; (void)error; return NULL; } /* dns_cache_fetch() */ static int dns_cache_pollfd(struct dns_cache *cache) { (void)cache; return -1; } /* dns_cache_pollfd() */ static short dns_cache_events(struct dns_cache *cache) { (void)cache; return 0; } /* dns_cache_events() */ static void dns_cache_clear(struct dns_cache *cache) { (void)cache; return; } /* dns_cache_clear() */ struct dns_cache *dns_cache_init(struct dns_cache *cache) { static const struct dns_cache c_init = { .acquire = &dns_cache_acquire, .release = &dns_cache_release, .query = &dns_cache_query, .submit = &dns_cache_submit, .check = &dns_cache_check, .fetch = &dns_cache_fetch, .pollfd = &dns_cache_pollfd, .events = &dns_cache_events, .clear = &dns_cache_clear, ._ = { .refcount = 1, }, }; *cache = c_init; return cache; } /* dns_cache_init() */ void dns_cache_close(struct dns_cache *cache) { if (cache) cache->release(cache); } /* dns_cache_close() */ /* * S O C K E T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static void dns_socketclose(int *fd, const struct dns_options *opts) { if (opts && opts->closefd.cb) opts->closefd.cb(fd, opts->closefd.arg); if (*fd != -1) { #if _WIN32 closesocket(*fd); #else close(*fd); #endif *fd = -1; } } /* dns_socketclose() */ #ifndef HAVE_IOCTLSOCKET #define HAVE_IOCTLSOCKET (_WIN32 || _WIN64) #endif #ifndef HAVE_SOCK_CLOEXEC #if (defined SOCK_CLOEXEC) #define HAVE_SOCK_CLOEXEC 1 #endif #endif #ifndef HAVE_SOCK_NONBLOCK #if (defined SOCK_NONBLOCK) #define HAVE_SOCK_NONBLOCK 1 #endif #endif #define DNS_SO_MAXTRY 7 static int dns_socket(struct sockaddr *local, int type, int *error_) { int fd = -1, flags, error; #if defined FIONBIO unsigned long opt; #endif flags = 0; #if HAVE_SOCK_CLOEXEC flags |= SOCK_CLOEXEC; #endif #if HAVE_SOCK_NONBLOCK flags |= SOCK_NONBLOCK; #endif if (-1 == (fd = socket(local->sa_family, type|flags, 0))) goto soerr; if (local->sa_family == AF_INET6){ int value=0; setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&value, sizeof(value)); } #if defined F_SETFD && !HAVE_SOCK_CLOEXEC if (-1 == fcntl(fd, F_SETFD, 1)) goto syerr; #endif #if defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK if (-1 == (flags = fcntl(fd, F_GETFL))) goto syerr; if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK)) goto syerr; #elif defined FIONBIO && HAVE_IOCTLSOCKET opt = 1; if (0 != ioctlsocket(fd, FIONBIO, &opt)) goto soerr; #endif #if defined SO_NOSIGPIPE if (type != SOCK_DGRAM) { if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, sizeof (int))) goto soerr; } #endif if (local->sa_family != AF_INET && local->sa_family != AF_INET6) return fd; if (type != SOCK_DGRAM) return fd; /* * FreeBSD, Linux, OpenBSD, OS X, and Solaris use random ports by * default. Though the ephemeral range is quite small on OS X * (49152-65535 on 10.10) and Linux (32768-60999 on 4.4.0, Ubuntu * Xenial). See also RFC 6056. * * TODO: Optionally rely on the kernel to select a random port. */ if (*dns_sa_port(local->sa_family, local) == 0) { /*bellesip: let the system find a random port*/ return fd; /* NB: continue to next bind statement */ } if (0 == bind(fd, local, dns_sa_len(local))) return fd; /* FALL THROUGH */ soerr: error = dns_soerr(); goto error; #if (defined F_SETFD && !HAVE_SOCK_CLOEXEC) || (defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK) syerr: error = dns_syerr(); goto error; #endif error: *error_ = error; dns_socketclose(&fd, NULL); return -1; } /* dns_socket() */ enum { DNS_SO_UDP_INIT = 1, DNS_SO_UDP_CONN, DNS_SO_UDP_SEND, DNS_SO_UDP_RECV, DNS_SO_UDP_DONE, DNS_SO_TCP_INIT, DNS_SO_TCP_CONN, DNS_SO_TCP_SEND, DNS_SO_TCP_RECV, DNS_SO_TCP_DONE, }; struct dns_socket { struct dns_options opts; int udp; int tcp; int *old; unsigned onum, olim; int type; struct sockaddr_storage local, remote; struct dns_k_permutor qids; struct dns_stat stat; /* * NOTE: dns_so_reset() zeroes everything from here down. */ int state; unsigned short qid; char qname[DNS_D_MAXNAME + 1]; size_t qlen; enum dns_type qtype; enum dns_class qclass; struct dns_packet *query; size_t qout; struct dns_clock elapsed; struct dns_packet *answer; size_t alen, apos; int ip_differ; /*set when remote address and response from which it is received are different*/ }; /* struct dns_socket */ /* * NOTE: Actual closure delayed so that kqueue(2) and epoll(2) callers have * a chance to recognize a state change after installing a persistent event * and where sequential descriptors with the same integer value returned * from _pollfd() would be ambiguous. See dns_so_closefds(). */ static int dns_so_closefd(struct dns_socket *so, int *fd) { int error; if (*fd == -1) return 0; if (so->opts.closefd.cb) { if ((error = so->opts.closefd.cb(fd, so->opts.closefd.arg))) { return error; } else if (*fd == -1) return 0; } if (!(so->onum < so->olim)) { unsigned olim = DNS_PP_MAX(4, so->olim * 2); void *old; if (!(old = realloc(so->old, sizeof so->old[0] * olim))) return dns_syerr(); so->old = old; so->olim = olim; } so->old[so->onum++] = *fd; *fd = -1; return 0; } /* dns_so_closefd() */ #define DNS_SO_CLOSE_UDP 0x01 #define DNS_SO_CLOSE_TCP 0x02 #define DNS_SO_CLOSE_OLD 0x04 #define DNS_SO_CLOSE_ALL (DNS_SO_CLOSE_UDP|DNS_SO_CLOSE_TCP|DNS_SO_CLOSE_OLD) static void dns_so_closefds(struct dns_socket *so, int which) { if (DNS_SO_CLOSE_UDP & which) dns_socketclose(&so->udp, &so->opts); if (DNS_SO_CLOSE_TCP & which) dns_socketclose(&so->tcp, &so->opts); if (DNS_SO_CLOSE_OLD & which) { unsigned i; for (i = 0; i < so->onum; i++) dns_socketclose(&so->old[i], &so->opts); so->onum = 0; free(so->old); so->old = 0; so->olim = 0; } } /* dns_so_closefds() */ static void dns_so_destroy(struct dns_socket *); static struct dns_socket *dns_so_init(struct dns_socket *so, const struct sockaddr *local, int type, const struct dns_options *opts, int *error) { static const struct dns_socket so_initializer = { .opts = DNS_OPTS_INITIALIZER, .udp = -1, .tcp = -1}; *so = so_initializer; so->type = type; if (opts) so->opts = *opts; if (local) memcpy(&so->local, local, dns_sa_len(local)); if (-1 == (so->udp = dns_socket((struct sockaddr *)&so->local, SOCK_DGRAM, error))) goto error; dns_k_permutor_init(&so->qids, 1, 65535); return so; error: dns_so_destroy(so); return 0; } /* dns_so_init() */ struct dns_socket *dns_so_open(const struct sockaddr *local, int type, const struct dns_options *opts, int *error) { struct dns_socket *so; if (!(so = malloc(sizeof *so))) goto syerr; if (!dns_so_init(so, local, type, opts, error)) goto error; return so; syerr: *error = dns_syerr(); error: dns_so_close(so); return 0; } /* dns_so_open() */ static void dns_so_destroy(struct dns_socket *so) { dns_so_reset(so); dns_so_closefds(so, DNS_SO_CLOSE_ALL); } /* dns_so_destroy() */ void dns_so_close(struct dns_socket *so) { if (!so) return; dns_so_destroy(so); free(so); } /* dns_so_close() */ void dns_so_reset(struct dns_socket *so) { dns_p_setptr(&so->answer, NULL); memset(&so->state, '\0', sizeof *so - offsetof(struct dns_socket, state)); } /* dns_so_reset() */ unsigned short dns_so_mkqid(struct dns_socket *so) { return dns_k_permutor_step(&so->qids); } /* dns_so_mkqid() */ #define DNS_SO_MINBUF 768 static int dns_so_newanswer(struct dns_socket *so, size_t len) { size_t size = offsetof(struct dns_packet, data) + DNS_PP_MAX(len, DNS_SO_MINBUF); void *p; if (!(p = realloc(so->answer, size))) return dns_syerr(); so->answer = dns_p_init(p, size); return 0; } /* dns_so_newanswer() */ int dns_so_submit(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host) { struct dns_rr rr; int error = DNS_EUNKNOWN; dns_so_reset(so); if ((error = dns_rr_parse(&rr, 12, Q))) goto error; if (!(so->qlen = dns_d_expand(so->qname, sizeof so->qname, rr.dn.p, Q, &error))) goto error; /* * NOTE: Don't bail if expansion is too long; caller may be * intentionally sending long names. However, we won't be able to * verify it on return. */ so->qtype = rr.type; so->qclass = rr.class; if ((error = dns_so_newanswer(so, (Q->memo.opt.maxudp)? Q->memo.opt.maxudp : DNS_SO_MINBUF))) goto syerr; if (so->local.ss_family==AF_INET6 && host->sa_family==AF_INET){ socklen_t addrlen = sizeof(so->remote); uint32_t *addr=(uint32_t*)dns_sa_addr(AF_INET6,&so->remote, &addrlen); /* add v4mapping*/ so->remote.ss_family=AF_INET6; addr[0]=0; addr[1]=0; addr[2]=ntohl(0xffff); addr[3]=((struct sockaddr_in*)host)->sin_addr.s_addr; *dns_sa_port(AF_INET6,&so->remote)=((struct sockaddr_in*)host)->sin_port; }else memcpy(&so->remote, host, dns_sa_len(host)); so->query = Q; so->qout = 0; dns_begin(&so->elapsed); if (dns_header(so->query)->qid == 0) dns_header(so->query)->qid = dns_so_mkqid(so); so->qid = dns_header(so->query)->qid; so->state = (so->type == SOCK_STREAM)? DNS_SO_TCP_INIT : DNS_SO_UDP_INIT; so->stat.queries++; return 0; syerr: error = dns_syerr(); error: dns_so_reset(so); return error; } /* dns_so_submit() */ static int dns_so_verify(struct dns_socket *so, struct dns_packet *P) { char qname[DNS_D_MAXNAME + 1]; size_t qlen; struct dns_rr rr; int error = -1; if (so->qid != dns_header(so->answer)->qid) goto reject; if (!dns_p_count(so->answer, DNS_S_QD)) goto reject; if (0 != dns_rr_parse(&rr, 12, so->answer)) goto reject; if (rr.type != so->qtype || rr.class != so->qclass) goto reject; if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, P, &error))) goto error; else if (qlen >= sizeof qname || qlen != so->qlen) goto reject; if (0 != strcasecmp(so->qname, qname)) goto reject; return 0; reject: error = DNS_EUNKNOWN; error: DNS_SHOW(P, "rejecting packet (%s)", dns_strerror(error)); return error; } /* dns_so_verify() */ static _Bool dns_so_tcp_keep(struct dns_socket *so) { struct sockaddr_storage remote; if (so->tcp == -1) return 0; if (0 != getpeername(so->tcp, (struct sockaddr *)&remote, &(socklen_t){ sizeof remote })) return 0; return 0 == dns_sa_cmp(&remote, &so->remote); } /* dns_so_tcp_keep() */ #if defined __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warray-bounds" #endif static int dns_so_tcp_send(struct dns_socket *so) { unsigned char *qsrc; size_t qend; long n; so->query->data[-2] = 0xff & (so->query->end >> 8); so->query->data[-1] = 0xff & (so->query->end >> 0); qsrc = &so->query->data[-2] + so->qout; qend = so->query->end + 2; while (so->qout < qend) { if (0 > (n = dns_send(so->tcp, (void *)&qsrc[so->qout], qend - so->qout, 0))) return dns_soerr(); so->qout += n; so->stat.tcp.sent.bytes += n; } so->stat.tcp.sent.count++; return 0; } /* dns_so_tcp_send() */ static int dns_so_tcp_recv(struct dns_socket *so) { unsigned char *asrc; size_t aend, alen; int error; long n; aend = so->alen + 2; while (so->apos < aend) { asrc = &so->answer->data[-2]; if (0 > (n = recv(so->tcp, (void *)&asrc[so->apos], aend - so->apos, 0))) return dns_soerr(); else if (n == 0) return DNS_EUNKNOWN; /* FIXME */ so->apos += n; so->stat.tcp.rcvd.bytes += n; if (so->alen == 0 && so->apos >= 2) { alen = ((0xff & so->answer->data[-2]) << 8) | ((0xff & so->answer->data[-1]) << 0); if ((error = dns_so_newanswer(so, alen))) return error; so->alen = alen; aend = alen + 2; } } so->answer->end = so->alen; so->stat.tcp.rcvd.count++; return 0; } /* dns_so_tcp_recv() */ #if __clang__ #pragma clang diagnostic pop #endif int dns_so_check(struct dns_socket *so) { struct sockaddr_storage tmp_ss; socklen_t tmp_ss_len = sizeof(tmp_ss); int error; long n; retry: switch (so->state) { case DNS_SO_UDP_INIT: so->state++; case DNS_SO_UDP_CONN: memset(&tmp_ss, 0, sizeof(tmp_ss)); connect(so->udp, (struct sockaddr *)&tmp_ss, sizeof(tmp_ss)); // Reset the previous connection if (so->opts.udp_uses_connect){ if (0 != connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote))) goto soerr; } so->state++; case DNS_SO_UDP_SEND: if (so->opts.udp_uses_connect){ if (0 > (n = send(so->udp, (void *)so->query->data, so->query->end, 0))) goto soerr; }else{ if (0 > (n = sendto(so->udp, (void *)so->query->data, so->query->end, 0, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote)))) goto soerr; } so->stat.udp.sent.bytes += n; so->stat.udp.sent.count++; so->state++; case DNS_SO_UDP_RECV: if (so->opts.udp_uses_connect){ if (0 > (n = recv(so->udp, (void *)so->answer->data, so->answer->size, 0))) goto soerr; }else{ memset(&tmp_ss, 0, sizeof(tmp_ss)); tmp_ss_len = sizeof(tmp_ss); if (0 > (n = recvfrom(so->udp, (void *)so->answer->data, so->answer->size, 0, (struct sockaddr *)&tmp_ss, &tmp_ss_len))) goto soerr; if (memcmp(&tmp_ss, &so->remote, tmp_ss_len) != 0){ so->ip_differ = 1; } } so->stat.udp.rcvd.bytes += n; so->stat.udp.rcvd.count++; if ((so->answer->end = n) < 12) goto trash; if ((error = dns_so_verify(so, so->answer))) goto trash; so->state++; case DNS_SO_UDP_DONE: if (!dns_header(so->answer)->tc || so->type == SOCK_DGRAM) return 0; so->state++; case DNS_SO_TCP_INIT: if (dns_so_tcp_keep(so)) { so->state = DNS_SO_TCP_SEND; goto retry; } if ((error = dns_so_closefd(so, &so->tcp))) goto error; if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error))) goto error; so->state++; case DNS_SO_TCP_CONN: if (0 != connect(so->tcp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote))) { if (dns_soerr() != DNS_EISCONN) goto soerr; } so->state++; case DNS_SO_TCP_SEND: if ((error = dns_so_tcp_send(so))) goto error; so->state++; case DNS_SO_TCP_RECV: if ((error = dns_so_tcp_recv(so))) goto error; so->state++; case DNS_SO_TCP_DONE: /* close unless DNS_RESCONF_TCP_ONLY (see dns_res_tcp2type) */ if (so->type != SOCK_STREAM) { if ((error = dns_so_closefd(so, &so->tcp))) goto error; } if (so->answer->end < 12) return DNS_EILLEGAL; if ((error = dns_so_verify(so, so->answer))) goto error; return 0; default: error = DNS_EUNKNOWN; goto error; } /* switch() */ trash: DNS_CARP("discarding packet"); goto retry; soerr: error = dns_soerr(); goto error; error: switch (error) { case DNS_EINTR: goto retry; case DNS_EINPROGRESS: /* FALL THROUGH */ case DNS_EALREADY: /* FALL THROUGH */ #if DNS_EWOULDBLOCK != DNS_EAGAIN case DNS_EWOULDBLOCK: /* FALL THROUGH */ #endif error = DNS_EAGAIN; break; } /* switch() */ return error; } /* dns_so_check() */ struct dns_packet *dns_so_fetch(struct dns_socket *so, int *error) { struct dns_packet *answer; switch (so->state) { case DNS_SO_UDP_DONE: case DNS_SO_TCP_DONE: answer = so->answer; so->answer = 0; return answer; default: *error = DNS_EUNKNOWN; return 0; } } /* dns_so_fetch() */ struct dns_packet *dns_so_query(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host, int *error_) { struct dns_packet *A; int error; if (!so->state) { if ((error = dns_so_submit(so, Q, host))) goto error; } if ((error = dns_so_check(so))) goto error; if (!(A = dns_so_fetch(so, &error))) goto error; dns_so_reset(so); return A; error: *error_ = error; return 0; } /* dns_so_query() */ time_t dns_so_elapsed(struct dns_socket *so) { return dns_elapsed(&so->elapsed); } /* dns_so_elapsed() */ void dns_so_clear(struct dns_socket *so) { dns_so_closefds(so, DNS_SO_CLOSE_OLD); } /* dns_so_clear() */ static int dns_so_events2(struct dns_socket *so, enum dns_events type) { int events = 0; switch (so->state) { case DNS_SO_UDP_CONN: case DNS_SO_UDP_SEND: events |= DNS_POLLOUT; break; case DNS_SO_UDP_RECV: events |= DNS_POLLIN; break; case DNS_SO_TCP_CONN: case DNS_SO_TCP_SEND: events |= DNS_POLLOUT; break; case DNS_SO_TCP_RECV: events |= DNS_POLLIN; break; } /* switch() */ switch (type) { case DNS_LIBEVENT: return DNS_POLL2EV(events); default: return events; } /* switch() */ } /* dns_so_events2() */ int dns_so_events(struct dns_socket *so) { return dns_so_events2(so, so->opts.events); } /* dns_so_events() */ int dns_so_pollfd(struct dns_socket *so) { switch (so->state) { case DNS_SO_UDP_CONN: case DNS_SO_UDP_SEND: case DNS_SO_UDP_RECV: return so->udp; case DNS_SO_TCP_CONN: case DNS_SO_TCP_SEND: case DNS_SO_TCP_RECV: return so->tcp; } /* switch() */ return -1; } /* dns_so_pollfd() */ int dns_so_poll(struct dns_socket *so, int timeout) { return dns_poll(dns_so_pollfd(so), dns_so_events2(so, DNS_SYSPOLL), timeout); } /* dns_so_poll() */ const struct dns_stat *dns_so_stat(struct dns_socket *so) { return &so->stat; } /* dns_so_stat() */ /* * R E S O L V E R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ enum dns_res_state { DNS_R_INIT, DNS_R_GLUE, DNS_R_SWITCH, /* (B)IND, (F)ILE, (C)ACHE */ DNS_R_FILE, /* Lookup in local hosts database */ DNS_R_CACHE, /* Lookup in application cache */ DNS_R_SUBMIT, DNS_R_CHECK, DNS_R_FETCH, DNS_R_BIND, /* Lookup in the network */ DNS_R_SEARCH, DNS_R_HINTS, DNS_R_ITERATE, DNS_R_FOREACH_NS, DNS_R_RESOLV0_NS, /* Prologue: Setup next frame and recurse */ DNS_R_RESOLV1_NS, /* Epilog: Inspect answer */ DNS_R_FOREACH_A, DNS_R_QUERY_A, DNS_R_CNAME0_A, DNS_R_CNAME1_A, DNS_R_FINISH, DNS_R_SMART0_A, DNS_R_SMART1_A, DNS_R_DONE, DNS_R_SERVFAIL, }; /* enum dns_res_state */ #define DNS_R_MAXDEPTH 8 #define DNS_R_ENDFRAME (DNS_R_MAXDEPTH - 1) struct dns_resolver { struct dns_socket so; struct dns_resolv_conf *resconf; struct dns_hosts *hosts; struct dns_hints *hints; struct dns_cache *cache; dns_atomic_t refcount; /* Reset zeroes everything below here. */ char qname[DNS_D_MAXNAME + 1]; size_t qlen; enum dns_type qtype; enum dns_class qclass; struct dns_clock elapsed; dns_resconf_i_t search; struct dns_rr_i smart; struct dns_packet *nodata; /* answer if nothing better */ unsigned sp; struct dns_res_frame { enum dns_res_state state; int error; int which; /* (B)IND, (F)ILE; index into resconf->lookup */ int qflags; unsigned attempts; struct dns_packet *query, *answer, *hints; struct dns_rr_i hints_i, hints_j; struct dns_rr hints_ns, ans_cname; } stack[DNS_R_MAXDEPTH]; unsigned char search_enabled; }; /* struct dns_resolver */ static int dns_res_tcp2type(int tcp) { switch (tcp) { case DNS_RESCONF_TCP_ONLY: return SOCK_STREAM; case DNS_RESCONF_TCP_DISABLE: return SOCK_DGRAM; default: return 0; } } /* dns_res_tcp2type() */ struct dns_resolver *dns_res_open(struct dns_resolv_conf *resconf, struct dns_hosts *hosts, struct dns_hints *hints, struct dns_cache *cache, const struct dns_options *opts, int *_error) { static const struct dns_resolver R_initializer = { .refcount = 1, }; struct dns_resolver *R = 0; int type, error; /* * Grab ref count early because the caller may have passed us a mortal * reference, and we want to do the right thing if we return early * from an error. */ if (resconf) dns_resconf_acquire(resconf); if (hosts) dns_hosts_acquire(hosts); if (hints) dns_hints_acquire(hints); if (cache) dns_cache_acquire(cache); /* * Don't try to load it ourselves because a NULL object might be an * error from, say, dns_resconf_root(), and loading * dns_resconf_local() by default would create undesirable surpises. */ if (!resconf || !hosts || !hints) goto _error; if (!(R = malloc(sizeof *R))) goto syerr; *R = R_initializer; type = dns_res_tcp2type(resconf->options.tcp); if (!dns_so_init(&R->so, (struct sockaddr *)&resconf->iface, type, opts, &error)) goto error; R->resconf = resconf; R->hosts = hosts; R->hints = hints; R->cache = cache; return R; syerr: error = dns_syerr(); error: *_error = error; _error: dns_res_close(R); dns_resconf_close(resconf); dns_hosts_close(hosts); dns_hints_close(hints); dns_cache_close(cache); return 0; } /* dns_res_open() */ struct dns_resolver *dns_res_stub(const struct dns_options *opts, int *error) { struct dns_resolv_conf *resconf = 0; struct dns_hosts *hosts = 0; struct dns_hints *hints = 0; struct dns_resolver *res = 0; if (!(resconf = dns_resconf_local(error))) goto epilog; if (!(hosts = dns_hosts_local(error))) goto epilog; if (!(hints = dns_hints_local(resconf, error))) goto epilog; if (!(res = dns_res_open(resconf, hosts, hints, NULL, opts, error))) goto epilog; epilog: dns_resconf_close(resconf); dns_hosts_close(hosts); dns_hints_close(hints); return res; } /* dns_res_stub() */ static void dns_res_frame_destroy(struct dns_resolver *R, struct dns_res_frame *frame) { (void)R; dns_p_setptr(&frame->query, NULL); dns_p_setptr(&frame->answer, NULL); dns_p_setptr(&frame->hints, NULL); } /* dns_res_frame_destroy() */ static void dns_res_frame_init(struct dns_resolver *R, struct dns_res_frame *frame) { memset(frame, '\0', sizeof *frame); if (!R->resconf->options.recurse) frame->qflags |= DNS_Q_RD; if (R->resconf->options.edns0) frame->qflags |= DNS_Q_EDNS0; } /* dns_res_frame_init() */ static void dns_res_frame_reset(struct dns_resolver *R, struct dns_res_frame *frame) { dns_res_frame_destroy(R, frame); dns_res_frame_init(R, frame); } /* dns_res_frame_reset() */ static dns_error_t dns_res_frame_prepare(struct dns_resolver *R, struct dns_res_frame *F, const char *qname, enum dns_type qtype, enum dns_class qclass) { struct dns_packet *P = NULL; if (!(F < endof(R->stack))) return DNS_EUNKNOWN; dns_p_movptr(&P, &F->query); dns_res_frame_reset(R, F); dns_p_movptr(&F->query, &P); return dns_q_make(&F->query, qname, qtype, qclass, F->qflags); } /* dns_res_frame_prepare() */ void dns_res_reset(struct dns_resolver *R) { unsigned i; dns_so_reset(&R->so); dns_p_setptr(&R->nodata, NULL); for (i = 0; i < lengthof(R->stack); i++) dns_res_frame_destroy(R, &R->stack[i]); memset(&R->qname, '\0', sizeof *R - offsetof(struct dns_resolver, qname)); for (i = 0; i < lengthof(R->stack); i++) dns_res_frame_init(R, &R->stack[i]); } /* dns_res_reset() */ void dns_res_close(struct dns_resolver *R) { if (!R || 1 < dns_res_release(R)) return; dns_res_reset(R); dns_so_destroy(&R->so); dns_hints_close(R->hints); dns_hosts_close(R->hosts); dns_resconf_close(R->resconf); dns_cache_close(R->cache); free(R); } /* dns_res_close() */ dns_refcount_t dns_res_acquire(struct dns_resolver *R) { return dns_atomic_fetch_add(&R->refcount); } /* dns_res_acquire() */ dns_refcount_t dns_res_release(struct dns_resolver *R) { return dns_atomic_fetch_sub(&R->refcount); } /* dns_res_release() */ struct dns_resolver *dns_res_mortal(struct dns_resolver *res) { if (res) dns_res_release(res); return res; } /* dns_res_mortal() */ static struct dns_packet *dns_res_merge(struct dns_packet *P0, struct dns_packet *P1, int *error_) { size_t bufsiz = P0->end + P1->end; struct dns_packet *P[3] = { P0, P1, 0 }; struct dns_rr rr[3]; int error, copy, i; enum dns_section section; retry: if (!(P[2] = dns_p_make(bufsiz, &error))) goto error; dns_rr_foreach(&rr[0], P[0], .section = DNS_S_QD) { if ((error = dns_rr_copy(P[2], &rr[0], P[0]))) goto error; } for (section = DNS_S_AN; (DNS_S_ALL & section); section <<= 1) { for (i = 0; i < 2; i++) { dns_rr_foreach(&rr[i], P[i], .section = section) { copy = 1; dns_rr_foreach(&rr[2], P[2], .type = rr[i].type, .section = (DNS_S_ALL & ~DNS_S_QD)) { if (0 == dns_rr_cmp(&rr[i], P[i], &rr[2], P[2])) { copy = 0; break; } } if (copy && (error = dns_rr_copy(P[2], &rr[i], P[i]))) { if (error == DNS_ENOBUFS && bufsiz < 65535) { dns_p_setptr(&P[2], NULL); bufsiz = DNS_PP_MAX(65535, bufsiz * 2); goto retry; } goto error; } } /* foreach(rr) */ } /* foreach(packet) */ } /* foreach(section) */ return P[2]; error: *error_ = error; dns_p_free(P[2]); return 0; } /* dns_res_merge() */ static struct dns_packet *dns_res_glue(struct dns_resolver *R, struct dns_packet *Q) { struct dns_packet *P = dns_p_new(512); char qname[DNS_D_MAXNAME + 1]; size_t qlen; enum dns_type qtype; struct dns_rr rr; unsigned sp; int error; if (!(qlen = dns_d_expand(qname, sizeof qname, 12, Q, &error)) || qlen >= sizeof qname) return 0; if (!(qtype = dns_rr_type(12, Q))) return 0; if ((error = dns_p_push(P, DNS_S_QD, qname, strlen(qname), qtype, DNS_C_IN, 0, 0))) return 0; for (sp = 0; sp <= R->sp; sp++) { if (!R->stack[sp].answer) continue; dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = qtype, .section = (DNS_S_ALL & ~DNS_S_QD)) { rr.section = DNS_S_AN; if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer))) return 0; } } if (dns_p_count(P, DNS_S_AN) > 0) goto copy; /* Otherwise, look for a CNAME */ for (sp = 0; sp <= R->sp; sp++) { if (!R->stack[sp].answer) continue; dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = DNS_T_CNAME, .section = (DNS_S_ALL & ~DNS_S_QD)) { rr.section = DNS_S_AN; if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer))) return 0; } } if (!dns_p_count(P, DNS_S_AN)) return 0; copy: return dns_p_copy(dns_p_make(P->end, &error), P); } /* dns_res_glue() */ /* * Sort NS records by three criteria: * * 1) Whether glue is present. * 2) Whether glue record is original or of recursive lookup. * 3) Randomly shuffle records which share the above criteria. * * NOTE: Assumes only NS records passed, AND ASSUMES no new NS records will * be added during an iteration. * * FIXME: Only groks A glue, not AAAA glue. */ static int dns_res_nameserv_cmp(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { _Bool glued[2] = { 0 }; struct dns_rr x = { 0 }, y = { 0 }; struct dns_ns ns; int cmp, error; if (!(error = dns_ns_parse(&ns, a, P))) glued[0] = !!dns_rr_grep(&x, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error); if (!(error = dns_ns_parse(&ns, b, P))) glued[1] = !!dns_rr_grep(&y, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error); if ((cmp = glued[1] - glued[0])) { return cmp; } else if ((cmp = (dns_rr_offset(&y) < i->args[0]) - (dns_rr_offset(&x) < i->args[0]))) { return cmp; } else { return dns_rr_i_shuffle(a, b, i, P); } } /* dns_res_nameserv_cmp() */ #define dgoto(sp, i) \ do { R->stack[(sp)].state = (i); goto exec; } while (0) static int dns_res_exec(struct dns_resolver *R) { struct dns_res_frame *F; struct dns_packet *P; union { char host[DNS_D_MAXNAME + 1]; char name[DNS_D_MAXNAME + 1]; struct dns_ns ns; struct dns_cname cname; } u; size_t len; struct dns_rr rr; int error; exec: F = &R->stack[R->sp]; switch (F->state) { case DNS_R_INIT: F->state++; case DNS_R_GLUE: if (R->sp == 0) dgoto(R->sp, DNS_R_SWITCH); if (!F->query) goto noquery; if (!(F->answer = dns_res_glue(R, F->query))) dgoto(R->sp, DNS_R_SWITCH); if (!(len = dns_d_expand(u.name, sizeof u.name, 12, F->query, &error))) goto error; else if (len >= sizeof u.name) goto toolong; dns_rr_foreach(&rr, F->answer, .name = u.name, .type = dns_rr_type(12, F->query), .section = DNS_S_AN) { dgoto(R->sp, DNS_R_FINISH); } dns_rr_foreach(&rr, F->answer, .name = u.name, .type = DNS_T_CNAME, .section = DNS_S_AN) { F->ans_cname = rr; dgoto(R->sp, DNS_R_CNAME0_A); } F->state++; case DNS_R_SWITCH: while (F->which < (int)sizeof R->resconf->lookup && R->resconf->lookup[F->which]) { switch (R->resconf->lookup[F->which++]) { case 'b': case 'B': dgoto(R->sp, DNS_R_BIND); case 'f': case 'F': dgoto(R->sp, DNS_R_FILE); case 'c': case 'C': if (R->cache) dgoto(R->sp, DNS_R_CACHE); break; default: break; } } /* * FIXME: Examine more closely whether our logic is correct * and DNS_R_SERVFAIL is the correct default response. * * Case 1: We got here because we never got an answer on the * wire. All queries timed-out and we reached maximum * attempts count. See DNS_R_FOREACH_NS. In that case * DNS_R_SERVFAIL is the correct state, unless we want to * return DNS_ETIMEDOUT. * * Case 2: We were a stub resolver and got an unsatisfactory * answer (empty ANSWER section) which caused us to jump * back to DNS_R_SEARCH and ultimately to DNS_R_SWITCH. We * return the answer returned from the wire, which we * stashed in R->nodata. * * Case 3: We reached maximum attempts count as in case #1, * but never got an authoritative response which caused us * to short-circuit. See end of DNS_R_QUERY_A case. We * should probably prepare R->nodata as in case #2. */ if (R->sp == 0 && R->nodata) { /* XXX: can we just return nodata regardless? */ dns_p_movptr(&F->answer, &R->nodata); dgoto(R->sp, DNS_R_FINISH); } dgoto(R->sp, DNS_R_SERVFAIL); case DNS_R_FILE: if (R->sp > 0) { if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error))) goto error; if (dns_p_count(F->answer, DNS_S_AN) > 0) dgoto(R->sp, DNS_R_FINISH); dns_p_setptr(&F->answer, NULL); } else { R->search = 0; while ((len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search))) { if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags))) goto error; if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error))) goto error; if (dns_p_count(F->answer, DNS_S_AN) > 0) dgoto(R->sp, DNS_R_FINISH); dns_p_setptr(&F->answer, NULL); } } dgoto(R->sp, DNS_R_SWITCH); case DNS_R_CACHE: error = 0; if (!F->query && (error = dns_q_make(&F->query, R->qname, R->qtype, R->qclass, F->qflags))) goto error; if (dns_p_setptr(&F->answer, R->cache->query(F->query, R->cache, &error))) { if (dns_p_count(F->answer, DNS_S_AN) > 0) dgoto(R->sp, DNS_R_FINISH); dns_p_setptr(&F->answer, NULL); dgoto(R->sp, DNS_R_SWITCH); } else if (error) goto error; F->state++; case DNS_R_SUBMIT: if ((error = R->cache->submit(F->query, R->cache))) goto error; F->state++; case DNS_R_CHECK: if ((error = R->cache->check(R->cache))) goto error; F->state++; case DNS_R_FETCH: error = 0; if (dns_p_setptr(&F->answer, R->cache->fetch(R->cache, &error))) { if (dns_p_count(F->answer, DNS_S_AN) > 0) dgoto(R->sp, DNS_R_FINISH); dns_p_setptr(&F->answer, NULL); dgoto(R->sp, DNS_R_SWITCH); } else if (error) goto error; dgoto(R->sp, DNS_R_SWITCH); case DNS_R_BIND: if (R->sp > 0) { if (!F->query) goto noquery; dgoto(R->sp, DNS_R_HINTS); } R->search = 0; F->state++; case DNS_R_SEARCH: /* * XXX: We probably should only apply the domain search * algorithm if R->sp == 0. */ if (!(len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search))) dgoto(R->sp, DNS_R_SWITCH); if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags))) goto error; F->state++; case DNS_R_HINTS: if (!dns_p_setptr(&F->hints, dns_hints_query(R->hints, F->query, &error))) goto error; F->state++; case DNS_R_ITERATE: dns_rr_i_init(&F->hints_i, F->hints); F->hints_i.section = DNS_S_AUTHORITY; F->hints_i.type = DNS_T_NS; F->hints_i.sort = &dns_res_nameserv_cmp; F->hints_i.args[0] = F->hints->end; F->state++; case DNS_R_FOREACH_NS: dns_rr_i_save(&F->hints_i); /* Load our next nameserver host. */ if (!dns_rr_grep(&F->hints_ns, 1, &F->hints_i, F->hints, &error)) { if (++F->attempts < R->resconf->options.attempts) dgoto(R->sp, DNS_R_ITERATE); dgoto(R->sp, DNS_R_SWITCH); } dns_rr_i_init(&F->hints_j, F->hints); /* Assume there are glue records */ dgoto(R->sp, DNS_R_FOREACH_A); case DNS_R_RESOLV0_NS: /* Have we reached our max depth? */ if (&F[1] >= endof(R->stack)) dgoto(R->sp, DNS_R_FOREACH_NS); if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints))) goto error; if ((error = dns_res_frame_prepare(R, &F[1], u.ns.host, DNS_T_A, DNS_C_IN))) goto error; F->state++; dgoto(++R->sp, DNS_R_INIT); case DNS_R_RESOLV1_NS: if (!(len = dns_d_expand(u.host, sizeof u.host, 12, F[1].query, &error))) goto error; else if (len >= sizeof u.host) goto toolong; dns_rr_foreach(&rr, F[1].answer, .name = u.host, .type = DNS_T_A, .section = (DNS_S_ALL & ~DNS_S_QD)) { rr.section = DNS_S_AR; if ((error = dns_rr_copy(F->hints, &rr, F[1].answer))) goto error; dns_rr_i_rewind(&F->hints_i); /* Now there's glue. */ } dgoto(R->sp, DNS_R_FOREACH_NS); case DNS_R_FOREACH_A: { struct sockaddr_storage saddr={0}; socklen_t saddr_len = sizeof(saddr); /* * NOTE: Iterator initialized in DNS_R_FOREACH_NS because * this state is re-entrant, but we need to reset * .name to a valid pointer each time. */ if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints))) goto error; F->hints_j.name = u.ns.host; F->hints_j.type = DNS_T_ALL; F->hints_j.section = DNS_S_ALL & ~DNS_S_QD; if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) { if (!dns_rr_i_count(&F->hints_j)) dgoto(R->sp, DNS_R_RESOLV0_NS); dgoto(R->sp, DNS_R_FOREACH_NS); } saddr.ss_family = rr.type==DNS_T_AAAA ? AF_INET6 : AF_INET; if (saddr.ss_family==AF_INET){ if ((error = dns_a_parse((struct dns_a *)dns_sa_addr(saddr.ss_family, &saddr, &saddr_len), &rr, F->hints))) goto error; }else{ if ((error = dns_aaaa_parse((struct dns_aaaa *)dns_sa_addr(saddr.ss_family, &saddr, &saddr_len), &rr, F->hints))) goto error; } if (R->sp == 0) dns_fill_sockaddr_from_hints(R->hints, saddr.ss_family, (struct sockaddr *)&saddr); else *dns_sa_port(saddr.ss_family, &saddr) = htons(53); if (DNS_DEBUG) { char addr[INET_ADDRSTRLEN + 1]; if (saddr.ss_family==AF_INET) dns_a_print(addr, sizeof addr, (struct dns_a *)dns_sa_addr(saddr.ss_family, &saddr, &saddr_len)); else dns_aaaa_print(addr, sizeof addr, (struct dns_aaaa *)dns_sa_addr(saddr.ss_family, &saddr, &saddr_len)); dns_header(F->query)->qid = dns_so_mkqid(&R->so); DNS_SHOW(F->query, "ASKING: %s/%s @ DEPTH: %u)", u.ns.host, addr, R->sp); } if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&saddr))) goto error; F->state++; } case DNS_R_QUERY_A: if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf)) dgoto(R->sp, DNS_R_FOREACH_A); if ((error = dns_so_check(&R->so)) != 0){ if (error == DNS_ENETUNREACH || error == DNS_ECONNREFUSED || error == DNS_ECONNRESET || error == EINVAL || error == DNS_EHOSTUNREACH) { /* maybe even more case*/ dgoto(R->sp, DNS_R_FOREACH_A); }else goto error; } if (!dns_p_setptr(&F->answer, dns_so_fetch(&R->so, &error))) goto error; if (DNS_DEBUG) { DNS_SHOW(F->answer, "ANSWER @ DEPTH: %u)", R->sp); } if (dns_p_rcode(F->answer) == DNS_RC_FORMERR || dns_p_rcode(F->answer) == DNS_RC_NOTIMP || dns_p_rcode(F->answer) == DNS_RC_BADVERS) { /* Temporarily disable EDNS0 and try again. */ if (F->qflags & DNS_Q_EDNS0) { F->qflags &= ~DNS_Q_EDNS0; if ((error = dns_q_remake(&F->query, F->qflags))) goto error; dgoto(R->sp, DNS_R_FOREACH_A); } } if ((error = dns_rr_parse(&rr, 12, F->query))) goto error; if (!(len = dns_d_expand(u.name, sizeof u.name, rr.dn.p, F->query, &error))) goto error; else if (len >= sizeof u.name) goto toolong; dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = rr.type) { dgoto(R->sp, DNS_R_FINISH); /* Found */ } dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = DNS_T_CNAME) { F->ans_cname = rr; dgoto(R->sp, DNS_R_CNAME0_A); } /* * XXX: The condition here should probably check whether * R->sp == 0, because DNS_R_SEARCH runs regardless of * options.recurse. See DNS_R_BIND. */ if (!R->resconf->options.recurse) { /* Make first answer our tentative answer */ if (!R->nodata) dns_p_movptr(&R->nodata, &F->answer); if (R->search_enabled) dgoto(R->sp, DNS_R_SEARCH); else dgoto(R->sp, DNS_R_FINISH); } dns_rr_foreach(&rr, F->answer, .section = DNS_S_NS, .type = DNS_T_NS) { dns_p_movptr(&F->hints, &F->answer); dgoto(R->sp, DNS_R_ITERATE); } /* XXX: Should this go further up? */ if (dns_header(F->answer)->aa) dgoto(R->sp, DNS_R_FINISH); /* XXX: Should we copy F->answer to R->nodata? */ dgoto(R->sp, DNS_R_FOREACH_A); case DNS_R_CNAME0_A: if (&F[1] >= endof(R->stack)) dgoto(R->sp, DNS_R_FINISH); if ((error = dns_cname_parse(&u.cname, &F->ans_cname, F->answer))) goto error; if ((error = dns_res_frame_prepare(R, &F[1], u.cname.host, dns_rr_type(12, F->query), DNS_C_IN))) goto error; F->state++; dgoto(++R->sp, DNS_R_INIT); case DNS_R_CNAME1_A: if (!(P = dns_res_merge(F->answer, F[1].answer, &error))) goto error; dns_p_setptr(&F->answer, P); dgoto(R->sp, DNS_R_FINISH); case DNS_R_FINISH: if (!F->answer) goto noanswer; if (!R->resconf->options.smart || R->sp > 0) dgoto(R->sp, DNS_R_DONE); R->smart.section = DNS_S_AN; R->smart.type = R->qtype; dns_rr_i_init(&R->smart, F->answer); F->state++; case DNS_R_SMART0_A: if (&F[1] >= endof(R->stack)) dgoto(R->sp, DNS_R_DONE); while (dns_rr_grep(&rr, 1, &R->smart, F->answer, &error)) { union { struct dns_ns ns; struct dns_mx mx; struct dns_srv srv; } rd; const char *qname; enum dns_type qtype; enum dns_class qclass; switch (rr.type) { case DNS_T_NS: if ((error = dns_ns_parse(&rd.ns, &rr, F->answer))) goto error; qname = rd.ns.host; qtype = DNS_T_A; qclass = DNS_C_IN; break; case DNS_T_MX: if ((error = dns_mx_parse(&rd.mx, &rr, F->answer))) goto error; qname = rd.mx.host; qtype = DNS_T_A; qclass = DNS_C_IN; break; case DNS_T_SRV: if ((error = dns_srv_parse(&rd.srv, &rr, F->answer))) goto error; qname = rd.srv.target; qtype = DNS_T_A; qclass = DNS_C_IN; break; default: continue; } /* switch() */ if ((error = dns_res_frame_prepare(R, &F[1], qname, qtype, qclass))) goto error; F->state++; dgoto(++R->sp, DNS_R_INIT); } /* while() */ /* * NOTE: SMTP specification says to fallback to A record. * * XXX: Should we add a mock MX answer? */ if (R->qtype == DNS_T_MX && R->smart.state.count == 0) { if ((error = dns_res_frame_prepare(R, &F[1], R->qname, DNS_T_A, DNS_C_IN))) goto error; R->smart.state.count++; F->state++; dgoto(++R->sp, DNS_R_INIT); } dgoto(R->sp, DNS_R_DONE); case DNS_R_SMART1_A: if (!F[1].answer) goto noanswer; /* * FIXME: For CNAME chains (which are typically illegal in * this context), we should rewrite the record host name * to the original smart qname. All the user cares about * is locating that A/AAAA record. */ dns_rr_foreach(&rr, F[1].answer, .section = DNS_S_AN, .type = DNS_T_A) { rr.section = DNS_S_AR; if (dns_rr_exists(&rr, F[1].answer, F->answer)) continue; while ((error = dns_rr_copy(F->answer, &rr, F[1].answer))) { if (error != DNS_ENOBUFS) goto error; if ((error = dns_p_grow(&F->answer))) goto error; } } dgoto(R->sp, DNS_R_SMART0_A); case DNS_R_DONE: if (!F->answer) goto noanswer; if (R->sp > 0) dgoto(--R->sp, F[-1].state); break; case DNS_R_SERVFAIL: if (!dns_p_setptr(&F->answer, dns_p_make(DNS_P_QBUFSIZ, &error))) goto error; dns_header(F->answer)->qr = 1; dns_header(F->answer)->rcode = DNS_RC_SERVFAIL; if ((error = dns_p_push(F->answer, DNS_S_QD, R->qname, strlen(R->qname), R->qtype, R->qclass, 0, 0))) goto error; dgoto(R->sp, DNS_R_DONE); default: error = EINVAL; goto error; } /* switch () */ return 0; noquery: error = DNS_ENOQUERY; goto error; noanswer: error = DNS_ENOANSWER; goto error; toolong: error = DNS_EILLEGAL; /* FALL THROUGH */ error: return error; } /* dns_res_exec() */ #undef goto void dns_res_clear(struct dns_resolver *R) { switch (R->stack[R->sp].state) { case DNS_R_CHECK: R->cache->clear(R->cache); break; default: dns_so_clear(&R->so); break; } } /* dns_res_clear() */ static int dns_res_events2(struct dns_resolver *R, enum dns_events type) { int events; switch (R->stack[R->sp].state) { case DNS_R_CHECK: events = R->cache->events(R->cache); return (type == DNS_LIBEVENT)? DNS_POLL2EV(events) : events; default: return dns_so_events2(&R->so, type); } } /* dns_res_events2() */ int dns_res_events(struct dns_resolver *R) { return dns_res_events2(R, R->so.opts.events); } /* dns_res_events() */ int dns_res_pollfd(struct dns_resolver *R) { switch (R->stack[R->sp].state) { case DNS_R_CHECK: return R->cache->pollfd(R->cache); default: return dns_so_pollfd(&R->so); } } /* dns_res_pollfd() */ time_t dns_res_timeout(struct dns_resolver *R) { time_t elapsed; switch (R->stack[R->sp].state) { #if 0 case DNS_R_QUERY_AAAA: #endif case DNS_R_QUERY_A: elapsed = dns_so_elapsed(&R->so); if (elapsed <= dns_resconf_timeout(R->resconf)) return R->resconf->options.timeout - elapsed; break; default: break; } /* switch() */ /* * NOTE: We're not in a pollable state, or the user code hasn't * called dns_res_check properly. The calling code is probably * broken. Put them into a slow-burn pattern. */ return 1; } /* dns_res_timeout() */ time_t dns_res_elapsed(struct dns_resolver *R) { return dns_elapsed(&R->elapsed); } /* dns_res_elapsed() */ int dns_res_poll(struct dns_resolver *R, int timeout) { return dns_poll(dns_res_pollfd(R), dns_res_events2(R, DNS_SYSPOLL), timeout); } /* dns_res_poll() */ int dns_res_submit2(struct dns_resolver *R, const char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass) { dns_res_reset(R); /* Don't anchor; that can conflict with searchlist generation. */ dns_d_init(R->qname, sizeof R->qname, qname, (R->qlen = qlen), 0); R->qtype = qtype; R->qclass = qclass; dns_begin(&R->elapsed); return 0; } /* dns_res_submit2() */ int dns_res_submit(struct dns_resolver *R, const char *qname, enum dns_type qtype, enum dns_class qclass) { return dns_res_submit2(R, qname, strlen(qname), qtype, qclass); } /* dns_res_submit() */ int dns_res_check(struct dns_resolver *R) { int error; if (R->stack[0].state != DNS_R_DONE) { if ((error = dns_res_exec(R))) return error; } return 0; } /* dns_res_check() */ struct dns_packet *dns_res_fetch(struct dns_resolver *R, int *error) { struct dns_packet *P = NULL; if (R->stack[0].state != DNS_R_DONE) { *error = DNS_EUNKNOWN; return NULL; } if (!dns_p_movptr(&P, &R->stack[0].answer)) { *error = DNS_EFETCHED; return NULL; } return P; } /* dns_res_fetch() */ static struct dns_packet *dns_res_fetch_and_study(struct dns_resolver *R, int *_error) { struct dns_packet *P = NULL; int error; if (!(P = dns_res_fetch(R, &error))) goto error; if ((error = dns_p_study(P))) goto error; return P; error: *_error = error; dns_p_free(P); return NULL; } /* dns_res_fetch_and_study() */ struct dns_packet *dns_res_query(struct dns_resolver *res, const char *qname, enum dns_type qtype, enum dns_class qclass, int timeout, int *error_) { int error; if ((error = dns_res_submit(res, qname, qtype, qclass))) goto error; while ((error = dns_res_check(res))) { if (dns_res_elapsed(res) > timeout) error = DNS_ETIMEDOUT; if (error != DNS_EAGAIN) goto error; if ((error = dns_res_poll(res, 1))) goto error; } return dns_res_fetch(res, error_); error: *error_ = error; return 0; } /* dns_res_query() */ const struct dns_stat *dns_res_stat(struct dns_resolver *res) { return dns_so_stat(&res->so); } /* dns_res_stat() */ void dns_res_sethints(struct dns_resolver *res, struct dns_hints *hints) { dns_hints_acquire(hints); /* acquire first in case same hints object */ dns_hints_close(res->hints); res->hints = hints; } /* dns_res_sethints() */ void dns_res_enable_search(struct dns_resolver *res, unsigned char enable) { res->search_enabled = enable; } int dns_res_was_asymetric(struct dns_resolver *res){ return res->so.ip_differ; } /* * A D D R I N F O R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_addrinfo { struct addrinfo hints; struct dns_resolver *res; char qname[DNS_D_MAXNAME + 1]; enum dns_type qtype; unsigned short qport, port; struct { unsigned long todo; int atype; enum dns_type qtype; } af; struct dns_packet *answer; struct dns_packet *glue; struct dns_rr_i i, g; struct dns_rr rr; char cname[DNS_D_MAXNAME + 1]; char i_cname[DNS_D_MAXNAME + 1], g_cname[DNS_D_MAXNAME + 1]; int state; int found; struct dns_stat st; }; /* struct dns_addrinfo */ #define DNS_AI_AFMAX 32 #define DNS_AI_AF2INDEX(af) (1UL << ((af) - 1)) static int dns_ai_nextaf(struct dns_addrinfo *ai) { int qtype = 0, atype = 0; unsigned i; dns_static_assert(AF_UNSPEC == 0, "AF_UNSPEC constant not 0"); dns_static_assert(AF_INET <= DNS_AI_AFMAX, "AF_INET constant too large"); dns_static_assert(AF_INET6 <= DNS_AI_AFMAX, "AF_INET6 constant too large"); for (i = 0; i < lengthof(ai->res->resconf->family); i++) { int af = ai->res->resconf->family[i]; if (af == AF_UNSPEC) break; if (af < 0 || af > DNS_AI_AFMAX) continue; if (!(DNS_AI_AF2INDEX(af) & ai->af.todo)) continue; switch (af) { case AF_INET: atype = AF_INET; qtype = DNS_T_A; goto update; case AF_INET6: atype = AF_INET6; qtype = DNS_T_AAAA; goto update; } } update: ai->af.atype = atype; ai->af.qtype = qtype; if (atype) ai->af.todo &= ~DNS_AI_AF2INDEX(atype); return atype; } /* dns_ai_nextaf() */ static enum dns_type dns_ai_qtype(struct dns_addrinfo *ai) { return (ai->qtype)? ai->qtype : ai->af.qtype; } /* dns_ai_qtype() */ static dns_error_t dns_ai_parseport(unsigned short *port, const char *serv, const struct addrinfo *hints) { const char *cp = serv; unsigned long n = 0; while (*cp >= '0' && *cp <= '9' && n < 65536) { n *= 10; n += *cp++ - '0'; } if (*cp == '\0') { if (cp == serv || n >= 65536) return DNS_ESERVICE; *port = n; return 0; } if (hints->ai_flags & AI_NUMERICSERV) return DNS_ESERVICE; /* TODO: try getaddrinfo(NULL, serv, { .ai_flags = AI_NUMERICSERV }) */ return DNS_ESERVICE; } /* dns_ai_parseport() */ struct dns_addrinfo *dns_ai_open(const char *host, const char *serv, enum dns_type qtype, const struct addrinfo *hints, struct dns_resolver *res, int *error_) { static const struct dns_addrinfo ai_initializer; struct dns_addrinfo *ai; int error; if (res) { dns_res_acquire(res); } else if (!(hints->ai_flags & AI_NUMERICHOST)) { /* * NOTE: it's assumed that *_error is set from a previous * API function call, such as dns_res_stub(). Should change * this semantic, but it's applied elsewhere, too. */ return NULL; } if (!(ai = malloc(sizeof *ai))) goto syerr; *ai = ai_initializer; ai->hints = *hints; ai->res = res; res = NULL; if (sizeof ai->qname <= dns_strlcpy(ai->qname, host, sizeof ai->qname)) { error = ENAMETOOLONG; goto error; } ai->qtype = qtype; ai->qport = 0; if (serv && (error = dns_ai_parseport(&ai->qport, serv, hints))) goto error; ai->port = ai->qport; switch (ai->qtype) { case DNS_T_A: ai->af.todo = DNS_AI_AF2INDEX(AF_INET); break; case DNS_T_AAAA: ai->af.todo = DNS_AI_AF2INDEX(AF_INET6); break; default: /* 0, MX, SRV, etc */ switch (ai->hints.ai_family) { case AF_UNSPEC: ai->af.todo = DNS_AI_AF2INDEX(AF_INET) | DNS_AI_AF2INDEX(AF_INET6); break; case AF_INET: ai->af.todo = DNS_AI_AF2INDEX(AF_INET); break; case AF_INET6: ai->af.todo = DNS_AI_AF2INDEX(AF_INET6); break; default: break; } } return ai; syerr: error = dns_syerr(); error: *error_ = error; dns_ai_close(ai); dns_res_close(res); return NULL; } /* dns_ai_open() */ void dns_ai_close(struct dns_addrinfo *ai) { if (!ai) return; dns_res_close(ai->res); if (ai->answer != ai->glue) dns_p_free(ai->glue); dns_p_free(ai->answer); free(ai); } /* dns_ai_close() */ static int dns_ai_setent(struct addrinfo **ent, union dns_any *any, enum dns_type type, struct dns_addrinfo *ai) { struct sockaddr *saddr; struct sockaddr_in sin; struct sockaddr_in6 sin6; const char *cname; size_t clen; switch (type) { case DNS_T_A: saddr = memset(&sin, '\0', sizeof sin); sin.sin_family = AF_INET; sin.sin_port = htons(ai->port); memcpy(&sin.sin_addr, any, sizeof sin.sin_addr); break; case DNS_T_AAAA: saddr = memset(&sin6, '\0', sizeof sin6); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(ai->port); memcpy(&sin6.sin6_addr, any, sizeof sin6.sin6_addr); break; default: return EINVAL; } /* switch() */ if (ai->hints.ai_flags & AI_CANONNAME) { cname = (*ai->cname)? ai->cname : ai->qname; clen = strlen(cname); } else { cname = NULL; clen = 0; } if (!(*ent = malloc(sizeof **ent + dns_sa_len(saddr) + ((ai->hints.ai_flags & AI_CANONNAME)? clen + 1 : 0)))) return dns_syerr(); memset(*ent, '\0', sizeof **ent); (*ent)->ai_family = saddr->sa_family; (*ent)->ai_socktype = ai->hints.ai_socktype; (*ent)->ai_protocol = ai->hints.ai_protocol; (*ent)->ai_addr = memcpy((unsigned char *)*ent + sizeof **ent, saddr, dns_sa_len(saddr)); (*ent)->ai_addrlen = dns_sa_len(saddr); if (ai->hints.ai_flags & AI_CANONNAME) (*ent)->ai_canonname = memcpy((unsigned char *)*ent + sizeof **ent + dns_sa_len(saddr), cname, clen + 1); ai->found++; return 0; } /* dns_ai_setent() */ enum dns_ai_state { DNS_AI_S_INIT, DNS_AI_S_NEXTAF, DNS_AI_S_NUMERIC, DNS_AI_S_SUBMIT, DNS_AI_S_CHECK, DNS_AI_S_FETCH, DNS_AI_S_FOREACH_I, DNS_AI_S_ITERATE_G, DNS_AI_S_FOREACH_G, DNS_AI_S_SUBMIT_G, DNS_AI_S_CHECK_G, DNS_AI_S_FETCH_G, DNS_AI_S_DONE, }; /* enum dns_ai_state */ #define dns_ai_goto(which) do { ai->state = (which); goto exec; } while (0) int dns_ai_nextent(struct addrinfo **ent, struct dns_addrinfo *ai) { struct dns_packet *ans, *glue; struct dns_rr rr; char qname[DNS_D_MAXNAME + 1]; union dns_any any; size_t qlen, clen; int error; *ent = 0; exec: switch (ai->state) { case DNS_AI_S_INIT: ai->state++; case DNS_AI_S_NEXTAF: if (!dns_ai_nextaf(ai)) dns_ai_goto(DNS_AI_S_DONE); ai->state++; case DNS_AI_S_NUMERIC: if (1 == dns_inet_pton(AF_INET, ai->qname, &any.a)) { if (ai->af.atype == AF_INET) { ai->state = DNS_AI_S_NEXTAF; return dns_ai_setent(ent, &any, DNS_T_A, ai); } else { dns_ai_goto(DNS_AI_S_NEXTAF); } } if (1 == dns_inet_pton(AF_INET6, ai->qname, &any.aaaa)) { if (ai->af.atype == AF_INET6) { ai->state = DNS_AI_S_NEXTAF; return dns_ai_setent(ent, &any, DNS_T_AAAA, ai); } else { dns_ai_goto(DNS_AI_S_NEXTAF); } } if (ai->hints.ai_flags & AI_NUMERICHOST) dns_ai_goto(DNS_AI_S_NEXTAF); ai->state++; case DNS_AI_S_SUBMIT: assert(ai->res); if ((error = dns_res_submit(ai->res, ai->qname, dns_ai_qtype(ai), DNS_C_IN))) return error; ai->state++; case DNS_AI_S_CHECK: if ((error = dns_res_check(ai->res))) return error; ai->state++; case DNS_AI_S_FETCH: if (!(ans = dns_res_fetch_and_study(ai->res, &error))) return error; if (ai->glue != ai->answer) dns_p_free(ai->glue); ai->glue = dns_p_movptr(&ai->answer, &ans); /* Search generator may have changed the qname. */ if (!(qlen = dns_d_expand(qname, sizeof qname, 12, ai->answer, &error))) return error; else if (qlen >= sizeof qname) return DNS_EILLEGAL; if (!dns_d_cname(ai->cname, sizeof ai->cname, qname, qlen, ai->answer, &error)) return error; dns_strlcpy(ai->i_cname, ai->cname, sizeof ai->i_cname); dns_rr_i_init(&ai->i, ai->answer); ai->i.section = DNS_S_AN; ai->i.name = ai->i_cname; ai->i.type = dns_ai_qtype(ai); ai->i.sort = &dns_rr_i_order; ai->state++; case DNS_AI_S_FOREACH_I: if (!dns_rr_grep(&rr, 1, &ai->i, ai->answer, &error)) dns_ai_goto(DNS_AI_S_NEXTAF); if ((error = dns_any_parse(&any, &rr, ai->answer))) return error; ai->port = ai->qport; switch (rr.type) { case DNS_T_A: case DNS_T_AAAA: return dns_ai_setent(ent, &any, rr.type, ai); default: if (!(clen = dns_any_cname(ai->cname, sizeof ai->cname, &any, rr.type))) dns_ai_goto(DNS_AI_S_FOREACH_I); /* * Find the "real" canonical name. Some authorities * publish aliases where an RFC defines a canonical * name. We trust that the resolver followed any * CNAME chains on it's own, regardless of whether * the "smart" option is enabled. */ if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->cname, clen, ai->answer, &error)) return error; if (rr.type == DNS_T_SRV) ai->port = any.srv.port; break; } /* switch() */ ai->state++; case DNS_AI_S_ITERATE_G: dns_strlcpy(ai->g_cname, ai->cname, sizeof ai->g_cname); dns_rr_i_init(&ai->g, ai->glue); ai->g.section = DNS_S_ALL & ~DNS_S_QD; ai->g.name = ai->g_cname; ai->g.type = ai->af.qtype; ai->state++; case DNS_AI_S_FOREACH_G: if (!dns_rr_grep(&rr, 1, &ai->g, ai->glue, &error)) { if (dns_rr_i_count(&ai->g) > 0) dns_ai_goto(DNS_AI_S_FOREACH_I); else dns_ai_goto(DNS_AI_S_SUBMIT_G); } if ((error = dns_any_parse(&any, &rr, ai->glue))) return error; return dns_ai_setent(ent, &any, rr.type, ai); case DNS_AI_S_SUBMIT_G: /* skip if already queried */ if (dns_rr_grep(&rr, 1, dns_rr_i_new(ai->glue, .section = DNS_S_QD, .name = ai->g.name, .type = ai->g.type), ai->glue, &error)) dns_ai_goto(DNS_AI_S_FOREACH_I); if ((error = dns_res_submit(ai->res, ai->g.name, ai->g.type, DNS_C_IN))) return error; ai->state++; case DNS_AI_S_CHECK_G: if ((error = dns_res_check(ai->res))) return error; ai->state++; case DNS_AI_S_FETCH_G: if (!(ans = dns_res_fetch_and_study(ai->res, &error))) return error; glue = dns_p_merge(ai->glue, DNS_S_ALL, ans, DNS_S_ALL, &error); dns_p_setptr(&ans, NULL); if (!glue) return error; if (ai->glue != ai->answer) dns_p_free(ai->glue); ai->glue = glue; if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->g.name, strlen(ai->g.name), ai->glue, &error)) dns_ai_goto(DNS_AI_S_FOREACH_I); dns_ai_goto(DNS_AI_S_ITERATE_G); case DNS_AI_S_DONE: if (ai->found) { return ENOENT; /* TODO: Just return 0 */ } else if (ai->answer) { switch (dns_p_rcode(ai->answer)) { case DNS_RC_NOERROR: /* FALL THROUGH */ case DNS_RC_NXDOMAIN: return DNS_ENONAME; default: return DNS_EFAIL; } } else { return DNS_EFAIL; } default: return EINVAL; } /* switch() */ } /* dns_ai_nextent() */ time_t dns_ai_elapsed(struct dns_addrinfo *ai) { return (ai->res)? dns_res_elapsed(ai->res) : 0; } /* dns_ai_elapsed() */ void dns_ai_clear(struct dns_addrinfo *ai) { if (ai->res) dns_res_clear(ai->res); } /* dns_ai_clear() */ int dns_ai_events(struct dns_addrinfo *ai) { return (ai->res)? dns_res_events(ai->res) : 0; } /* dns_ai_events() */ int dns_ai_pollfd(struct dns_addrinfo *ai) { return (ai->res)? dns_res_pollfd(ai->res) : -1; } /* dns_ai_pollfd() */ time_t dns_ai_timeout(struct dns_addrinfo *ai) { return (ai->res)? dns_res_timeout(ai->res) : 0; } /* dns_ai_timeout() */ int dns_ai_poll(struct dns_addrinfo *ai, int timeout) { return (ai->res)? dns_res_poll(ai->res, timeout) : 0; } /* dns_ai_poll() */ size_t dns_ai_print(void *_dst, size_t lim, struct addrinfo *ent, struct dns_addrinfo *ai) { struct dns_buf dst = DNS_B_INTO(_dst, lim); char addr[DNS_PP_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; dns_b_puts(&dst, "[ "); dns_b_puts(&dst, ai->qname); dns_b_puts(&dst, " IN "); if (ai->qtype) { dns_b_puts(&dst, dns_strtype(ai->qtype)); } else if (ent->ai_family == AF_INET) { dns_b_puts(&dst, dns_strtype(DNS_T_A)); } else if (ent->ai_family == AF_INET6) { dns_b_puts(&dst, dns_strtype(DNS_T_AAAA)); } else { dns_b_puts(&dst, "0"); } dns_b_puts(&dst, " ]\n"); dns_b_puts(&dst, ".ai_family = "); switch (ent->ai_family) { case AF_INET: dns_b_puts(&dst, "AF_INET"); break; case AF_INET6: dns_b_puts(&dst, "AF_INET6"); break; default: dns_b_fmtju(&dst, ent->ai_family, 0); break; } dns_b_putc(&dst, '\n'); dns_b_puts(&dst, ".ai_socktype = "); switch (ent->ai_socktype) { case SOCK_STREAM: dns_b_puts(&dst, "SOCK_STREAM"); break; case SOCK_DGRAM: dns_b_puts(&dst, "SOCK_DGRAM"); break; default: dns_b_fmtju(&dst, ent->ai_socktype, 0); break; } dns_b_putc(&dst, '\n'); dns_inet_ntop(dns_sa_family(ent->ai_addr), dns_sa_addr(dns_sa_family(ent->ai_addr), ent->ai_addr, NULL), addr, sizeof addr); dns_b_puts(&dst, ".ai_addr = ["); dns_b_puts(&dst, addr); dns_b_puts(&dst, "]:"); dns_b_fmtju(&dst, ntohs(*dns_sa_port(dns_sa_family(ent->ai_addr), ent->ai_addr)), 0); dns_b_putc(&dst, '\n'); dns_b_puts(&dst, ".ai_canonname = "); dns_b_puts(&dst, (ent->ai_canonname)? ent->ai_canonname : "[NULL]"); dns_b_putc(&dst, '\n'); return dns_b_strllen(&dst); } /* dns_ai_print() */ const struct dns_stat *dns_ai_stat(struct dns_addrinfo *ai) { return (ai->res)? dns_res_stat(ai->res) : &ai->st; } /* dns_ai_stat() */ /* * M I S C E L L A N E O U S R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static const struct { char name[16]; enum dns_section type; } dns_sections[] = { { "QUESTION", DNS_S_QUESTION }, { "QD", DNS_S_QUESTION }, { "ANSWER", DNS_S_ANSWER }, { "AN", DNS_S_ANSWER }, { "AUTHORITY", DNS_S_AUTHORITY }, { "NS", DNS_S_AUTHORITY }, { "ADDITIONAL", DNS_S_ADDITIONAL }, { "AR", DNS_S_ADDITIONAL }, }; const char *(dns_strsection)(enum dns_section section, void *_dst, size_t lim) { struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned i; for (i = 0; i < lengthof(dns_sections); i++) { if (dns_sections[i].type & section) { dns_b_puts(&dst, dns_sections[i].name); section &= ~dns_sections[i].type; if (section) dns_b_putc(&dst, '|'); } } if (section || dst.p == dst.base) dns_b_fmtju(&dst, (0xffff & section), 0); return dns_b_tostring(&dst); } /* dns_strsection() */ enum dns_section dns_isection(const char *src) { enum dns_section section = 0; char sbuf[128]; char *name, *next; unsigned i; dns_strlcpy(sbuf, src, sizeof sbuf); next = sbuf; while ((name = dns_strsep(&next, "|+, \t"))) { for (i = 0; i < lengthof(dns_sections); i++) { if (!strcasecmp(dns_sections[i].name, name)) { section |= dns_sections[i].type; break; } } } return section; } /* dns_isection() */ static const struct { char name[8]; enum dns_class type; } dns_classes[] = { { "IN", DNS_C_IN }, }; const char *(dns_strclass)(enum dns_class type, void *_dst, size_t lim) { struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned i; for (i = 0; i < lengthof(dns_classes); i++) { if (dns_classes[i].type == type) { dns_b_puts(&dst, dns_classes[i].name); break; } } if (dst.p == dst.base) dns_b_fmtju(&dst, (0xffff & type), 0); return dns_b_tostring(&dst); } /* dns_strclass() */ enum dns_class dns_iclass(const char *name) { unsigned i, class; for (i = 0; i < lengthof(dns_classes); i++) { if (!strcasecmp(dns_classes[i].name, name)) return dns_classes[i].type; } class = 0; while (dns_isdigit(*name)) { class *= 10; class += *name++ - '0'; } return DNS_PP_MIN(class, 0xffff); } /* dns_iclass() */ const char *(dns_strtype)(enum dns_type type, void *_dst, size_t lim) { struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned i; for (i = 0; i < lengthof(dns_rrtypes); i++) { if (dns_rrtypes[i].type == type) { dns_b_puts(&dst, dns_rrtypes[i].name); break; } } if (dst.p == dst.base) dns_b_fmtju(&dst, (0xffff & type), 0); return dns_b_tostring(&dst); } /* dns_strtype() */ enum dns_type dns_itype(const char *name) { unsigned i, type; for (i = 0; i < lengthof(dns_rrtypes); i++) { if (!strcasecmp(dns_rrtypes[i].name, name)) return dns_rrtypes[i].type; } type = 0; while (dns_isdigit(*name)) { type *= 10; type += *name++ - '0'; } return DNS_PP_MIN(type, 0xffff); } /* dns_itype() */ static char dns_opcodes[16][16] = { [DNS_OP_QUERY] = "QUERY", [DNS_OP_IQUERY] = "IQUERY", [DNS_OP_STATUS] = "STATUS", [DNS_OP_NOTIFY] = "NOTIFY", [DNS_OP_UPDATE] = "UPDATE", }; static const char *dns__strcode(int code, volatile char *dst, size_t lim) { char _tmp[48] = ""; struct dns_buf tmp; size_t p; assert(lim > 0); dns_b_fmtju(dns_b_into(&tmp, _tmp, DNS_PP_MIN(sizeof _tmp, lim - 1)), code, 0); /* copy downwards so first byte is copied last (see below) */ p = (size_t)(tmp.p - tmp.base); dst[p] = '\0'; while (p--) dst[p] = _tmp[p]; return (const char *)dst; } const char *dns_stropcode(enum dns_opcode opcode) { opcode = (unsigned)opcode % lengthof(dns_opcodes); if ('\0' == dns_opcodes[opcode][0]) return dns__strcode(opcode, dns_opcodes[opcode], sizeof dns_opcodes[opcode]); return dns_opcodes[opcode]; } /* dns_stropcode() */ enum dns_opcode dns_iopcode(const char *name) { unsigned opcode; for (opcode = 0; opcode < lengthof(dns_opcodes); opcode++) { if (!strcasecmp(name, dns_opcodes[opcode])) return opcode; } opcode = 0; while (dns_isdigit(*name)) { opcode *= 10; opcode += *name++ - '0'; } return DNS_PP_MIN(opcode, 0x0f); } /* dns_iopcode() */ static char dns_rcodes[32][16] = { [DNS_RC_NOERROR] = "NOERROR", [DNS_RC_FORMERR] = "FORMERR", [DNS_RC_SERVFAIL] = "SERVFAIL", [DNS_RC_NXDOMAIN] = "NXDOMAIN", [DNS_RC_NOTIMP] = "NOTIMP", [DNS_RC_REFUSED] = "REFUSED", [DNS_RC_YXDOMAIN] = "YXDOMAIN", [DNS_RC_YXRRSET] = "YXRRSET", [DNS_RC_NXRRSET] = "NXRRSET", [DNS_RC_NOTAUTH] = "NOTAUTH", [DNS_RC_NOTZONE] = "NOTZONE", /* EDNS(0) extended RCODEs ... */ [DNS_RC_BADVERS] = "BADVERS", }; const char *dns_strrcode(enum dns_rcode rcode) { rcode = (unsigned)rcode % lengthof(dns_rcodes); if ('\0' == dns_rcodes[rcode][0]) return dns__strcode(rcode, dns_rcodes[rcode], sizeof dns_rcodes[rcode]); return dns_rcodes[rcode]; } /* dns_strrcode() */ enum dns_rcode dns_ircode(const char *name) { unsigned rcode; for (rcode = 0; rcode < lengthof(dns_rcodes); rcode++) { if (!strcasecmp(name, dns_rcodes[rcode])) return rcode; } rcode = 0; while (dns_isdigit(*name)) { rcode *= 10; rcode += *name++ - '0'; } return DNS_PP_MIN(rcode, 0xfff); } /* dns_ircode() */ /* * C O M M A N D - L I N E / R E G R E S S I O N R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if DNS_MAIN #include #include #include #include #if _WIN32 #include #endif #if !_WIN32 #include #endif struct { struct { const char *path[8]; unsigned count; } resconf, nssconf, hosts, cache; const char *qname; enum dns_type qtype; int (*sort)(); int verbose; } MAIN = { .sort = &dns_rr_i_packet, }; static void hexdump(const unsigned char *src, size_t len, FILE *fp) { static const unsigned char hex[] = "0123456789abcdef"; static const unsigned char tmpl[] = " | |\n"; unsigned char ln[sizeof tmpl]; const unsigned char *sp, *se; unsigned char *h, *g; unsigned i, n; sp = src; se = sp + len; while (sp < se) { memcpy(ln, tmpl, sizeof ln); h = &ln[2]; g = &ln[53]; for (n = 0; n < 2; n++) { for (i = 0; i < 8 && se - sp > 0; i++, sp++) { h[0] = hex[0x0f & (*sp >> 4)]; h[1] = hex[0x0f & (*sp >> 0)]; h += 3; *g++ = (isgraph(*sp))? *sp : '.'; } h++; } fputs((char *)ln, fp); } return /* void */; } /* hexdump() */ DNS_NORETURN static void panic(const char *fmt, ...) { va_list ap; va_start(ap, fmt); #if _WIN32 vfprintf(stderr, fmt, ap); exit(EXIT_FAILURE); #else verrx(EXIT_FAILURE, fmt, ap); #endif } /* panic() */ #define panic_(fn, ln, fmt, ...) \ panic(fmt "%0s", (fn), (ln), __VA_ARGS__) #define panic(...) \ panic_(__func__, __LINE__, "(%s:%d) " __VA_ARGS__, "") static void *grow(unsigned char *p, size_t size) { void *tmp; if (!(tmp = realloc(p, size))) panic("realloc(%"PRIuZ"): %s", size, dns_strerror(errno)); return tmp; } /* grow() */ static size_t add(size_t a, size_t b) { if (~a < b) panic("%"PRIuZ" + %"PRIuZ": integer overflow", a, b); return a + b; } /* add() */ static size_t append(unsigned char **dst, size_t osize, const void *src, size_t len) { size_t size = add(osize, len); *dst = grow(*dst, size); memcpy(*dst + osize, src, len); return size; } /* append() */ static size_t slurp(unsigned char **dst, size_t osize, FILE *fp, const char *path) { size_t size = osize; unsigned char buf[1024]; size_t count; while ((count = fread(buf, 1, sizeof buf, fp))) size = append(dst, size, buf, count); if (ferror(fp)) panic("%s: %s", path, dns_strerror(errno)); return size; } /* slurp() */ static struct dns_resolv_conf *resconf(void) { static struct dns_resolv_conf *resconf; const char *path; unsigned i; int error; if (resconf) return resconf; if (!(resconf = dns_resconf_open(&error))) panic("dns_resconf_open: %s", dns_strerror(error)); if (!MAIN.resconf.count) MAIN.resconf.path[MAIN.resconf.count++] = "/etc/resolv.conf"; for (i = 0; i < MAIN.resconf.count; i++) { path = MAIN.resconf.path[i]; if (0 == strcmp(path, "-")) error = dns_resconf_loadfile(resconf, stdin); else error = dns_resconf_loadpath(resconf, path); if (error) panic("%s: %s", path, dns_strerror(error)); } for (i = 0; i < MAIN.nssconf.count; i++) { path = MAIN.nssconf.path[i]; if (0 == strcmp(path, "-")) error = dns_nssconf_loadfile(resconf, stdin); else error = dns_nssconf_loadpath(resconf, path); if (error) panic("%s: %s", path, dns_strerror(error)); } if (!MAIN.nssconf.count) { path = "/etc/nsswitch.conf"; if (!(error = dns_nssconf_loadpath(resconf, path))) MAIN.nssconf.path[MAIN.nssconf.count++] = path; else if (error != ENOENT) panic("%s: %s", path, dns_strerror(error)); } return resconf; } /* resconf() */ static struct dns_hosts *hosts(void) { static struct dns_hosts *hosts; const char *path; unsigned i; int error; if (hosts) return hosts; if (!MAIN.hosts.count) { MAIN.hosts.path[MAIN.hosts.count++] = "/etc/hosts"; /* Explicitly test dns_hosts_local() */ if (!(hosts = dns_hosts_local(&error))) panic("%s: %s", "/etc/hosts", dns_strerror(error)); return hosts; } if (!(hosts = dns_hosts_open(&error))) panic("dns_hosts_open: %s", dns_strerror(error)); for (i = 0; i < MAIN.hosts.count; i++) { path = MAIN.hosts.path[i]; if (0 == strcmp(path, "-")) error = dns_hosts_loadfile(hosts, stdin); else error = dns_hosts_loadpath(hosts, path); if (error) panic("%s: %s", path, dns_strerror(error)); } return hosts; } /* hosts() */ #if DNS_CACHE #include "cache.h" static struct dns_cache *cache(void) { static struct cache *cache; const char *path; unsigned i; int error; if (cache) return cache_resi(cache); if (!MAIN.cache.count) return NULL; if (!(cache = cache_open(&error))) panic("%s: %s", MAIN.cache.path[0], dns_strerror(error)); for (i = 0; i < MAIN.cache.count; i++) { path = MAIN.cache.path[i]; if (!strcmp(path, "-")) { if ((error = cache_loadfile(cache, stdin, NULL, 0))) panic("%s: %s", path, dns_strerror(error)); } else if ((error = cache_loadpath(cache, path, NULL, 0))) panic("%s: %s", path, dns_strerror(error)); } return cache_resi(cache); } /* cache() */ #else static struct dns_cache *cache(void) { return NULL; } #endif static void print_packet(struct dns_packet *P, FILE *fp) { dns_p_dump3(P, dns_rr_i_new(P, .sort = MAIN.sort), fp); if (MAIN.verbose > 2) hexdump(P->data, P->end, fp); } /* print_packet() */ static int parse_packet(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { struct dns_packet *P = dns_p_new(512); struct dns_packet *Q = dns_p_new(512); enum dns_section section; struct dns_rr rr; int error; union dns_any any; char pretty[sizeof any * 2]; size_t len; P->end = fread(P->data, 1, P->size, stdin); fputs(";; [HEADER]\n", stdout); fprintf(stdout, ";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr); fprintf(stdout, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode); fprintf(stdout, ";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa); fprintf(stdout, ";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc); fprintf(stdout, ";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd); fprintf(stdout, ";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra); fprintf(stdout, ";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P)); section = 0; dns_rr_foreach(&rr, P, .sort = MAIN.sort) { if (section != rr.section) fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section)); if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error))) fprintf(stdout, "%s\n", pretty); dns_rr_copy(Q, &rr, P); section = rr.section; } fputs("; ; ; ; ; ; ; ;\n\n", stdout); section = 0; #if 0 dns_rr_foreach(&rr, Q, .name = "ns8.yahoo.com.") { #else struct dns_rr rrset[32]; struct dns_rr_i *rri = dns_rr_i_new(Q, .name = dns_d_new("ns8.yahoo.com", DNS_D_ANCHOR), .sort = MAIN.sort); unsigned rrcount = dns_rr_grep(rrset, lengthof(rrset), rri, Q, &error); for (unsigned i = 0; i < rrcount; i++) { rr = rrset[i]; #endif if (section != rr.section) fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(Q, rr.section)); if ((len = dns_rr_print(pretty, sizeof pretty, &rr, Q, &error))) fprintf(stdout, "%s\n", pretty); section = rr.section; } if (MAIN.verbose > 1) { fprintf(stderr, "orig:%"PRIuZ"\n", P->end); hexdump(P->data, P->end, stdout); fprintf(stderr, "copy:%"PRIuZ"\n", Q->end); hexdump(Q->data, Q->end, stdout); } return 0; } /* parse_packet() */ static int parse_domain(int argc, char *argv[]) { char *dn; dn = (argc > 1)? argv[1] : "f.l.google.com"; printf("[%s]\n", dn); dn = dns_d_new(dn); do { puts(dn); } while (dns_d_cleave(dn, strlen(dn) + 1, dn, strlen(dn))); return 0; } /* parse_domain() */ static int trim_domain(int argc, char **argv) { for (argc--, argv++; argc > 0; argc--, argv++) { char name[DNS_D_MAXNAME + 1]; dns_d_trim(name, sizeof name, *argv, strlen(*argv), DNS_D_ANCHOR); puts(name); } return 0; } /* trim_domain() */ static int expand_domain(int argc, char *argv[]) { unsigned short rp = 0; unsigned char *src = NULL; unsigned char *dst; struct dns_packet *pkt; size_t lim = 0, len; int error; if (argc > 1) rp = atoi(argv[1]); len = slurp(&src, 0, stdin, "-"); if (!(pkt = dns_p_make(len, &error))) panic("malloc(%"PRIuZ"): %s", len, dns_strerror(error)); memcpy(pkt->data, src, len); pkt->end = len; lim = 1; dst = grow(NULL, lim); while (lim <= (len = dns_d_expand(dst, lim, rp, pkt, &error))) { lim = add(len, 1); dst = grow(dst, lim); } if (!len) panic("expand: %s", dns_strerror(error)); fwrite(dst, 1, len, stdout); fflush(stdout); free(src); free(dst); free(pkt); return 0; } /* expand_domain() */ static int show_resconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { unsigned i; resconf(); /* load it */ fputs("; SOURCES\n", stdout); for (i = 0; i < MAIN.resconf.count; i++) fprintf(stdout, "; %s\n", MAIN.resconf.path[i]); for (i = 0; i < MAIN.nssconf.count; i++) fprintf(stdout, "; %s\n", MAIN.nssconf.path[i]); fputs(";\n", stdout); dns_resconf_dump(resconf(), stdout); return 0; } /* show_resconf() */ static int show_nssconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { unsigned i; resconf(); fputs("# SOURCES\n", stdout); for (i = 0; i < MAIN.resconf.count; i++) fprintf(stdout, "# %s\n", MAIN.resconf.path[i]); for (i = 0; i < MAIN.nssconf.count; i++) fprintf(stdout, "# %s\n", MAIN.nssconf.path[i]); fputs("#\n", stdout); dns_nssconf_dump(resconf(), stdout); return 0; } /* show_nssconf() */ static int show_hosts(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { unsigned i; hosts(); fputs("# SOURCES\n", stdout); for (i = 0; i < MAIN.hosts.count; i++) fprintf(stdout, "# %s\n", MAIN.hosts.path[i]); fputs("#\n", stdout); dns_hosts_dump(hosts(), stdout); return 0; } /* show_hosts() */ static int query_hosts(int argc, char *argv[]) { struct dns_packet *Q = dns_p_new(512); struct dns_packet *A; char qname[DNS_D_MAXNAME + 1]; size_t qlen; int error; if (!MAIN.qname) MAIN.qname = (argc > 1)? argv[1] : "localhost"; if (!MAIN.qtype) MAIN.qtype = DNS_T_A; hosts(); if (MAIN.qtype == DNS_T_PTR && !strstr(MAIN.qname, "arpa")) { union { struct in_addr a; struct in6_addr a6; } addr; int af = (strchr(MAIN.qname, ':'))? AF_INET6 : AF_INET; if ((error = dns_pton(af, MAIN.qname, &addr))) panic("%s: %s", MAIN.qname, dns_strerror(error)); qlen = dns_ptr_qname(qname, sizeof qname, af, &addr); } else qlen = dns_strlcpy(qname, MAIN.qname, sizeof qname); if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, MAIN.qtype, DNS_C_IN, 0, 0))) panic("%s: %s", qname, dns_strerror(error)); if (!(A = dns_hosts_query(hosts(), Q, &error))) panic("%s: %s", qname, dns_strerror(error)); print_packet(A, stdout); free(A); return 0; } /* query_hosts() */ static int search_list(int argc, char *argv[]) { const char *qname = (argc > 1)? argv[1] : "f.l.google.com"; unsigned long i = 0; char name[DNS_D_MAXNAME + 1]; printf("[%s]\n", qname); while (dns_resconf_search(name, sizeof name, qname, strlen(qname), resconf(), &i)) puts(name); return 0; } /* search_list() */ static int permute_set(int argc, char *argv[]) { unsigned lo, hi, i; struct dns_k_permutor p; hi = (--argc > 0)? atoi(argv[argc]) : 8; lo = (--argc > 0)? atoi(argv[argc]) : 0; fprintf(stderr, "[%u .. %u]\n", lo, hi); dns_k_permutor_init(&p, lo, hi); for (i = lo; i <= hi; i++) fprintf(stdout, "%u\n", dns_k_permutor_step(&p)); // printf("%u -> %u -> %u\n", i, dns_k_permutor_E(&p, i), dns_k_permutor_D(&p, dns_k_permutor_E(&p, i))); return 0; } /* permute_set() */ static int shuffle_16(int argc, char *argv[]) { unsigned n, r; if (--argc > 0) { n = 0xffff & atoi(argv[argc]); r = (--argc > 0)? (unsigned)atoi(argv[argc]) : dns_random(); fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r)); } else { r = dns_random(); for (n = 0; n < 65536; n++) fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r)); } return 0; } /* shuffle_16() */ static int dump_random(int argc, char *argv[]) { unsigned char b[32]; unsigned i, j, n, r; n = (argc > 1)? atoi(argv[1]) : 32; while (n) { i = 0; do { r = dns_random(); for (j = 0; j < sizeof r && i < n && i < sizeof b; i++, j++) { b[i] = 0xff & r; r >>= 8; } } while (i < n && i < sizeof b); hexdump(b, i, stdout); n -= i; } return 0; } /* dump_random() */ static int send_query(int argc, char *argv[]) { struct dns_packet *A, *Q = dns_p_new(512); char host[INET6_ADDRSTRLEN + 1]; struct sockaddr_storage ss; struct dns_socket *so; int error, type; if (argc > 1) { ss.ss_family = (strchr(argv[1], ':'))? AF_INET6 : AF_INET; if ((error = dns_pton(ss.ss_family, argv[1], dns_sa_addr(ss.ss_family, &ss, NULL)))) panic("%s: %s", argv[1], dns_strerror(error)); *dns_sa_port(ss.ss_family, &ss) = htons(53); } else memcpy(&ss, &resconf()->nameserver[0], dns_sa_len(&resconf()->nameserver[0])); if (!dns_inet_ntop(ss.ss_family, dns_sa_addr(ss.ss_family, &ss, NULL), host, sizeof host)) panic("bad host address, or none provided"); if (!MAIN.qname) MAIN.qname = "ipv6.google.com"; if (!MAIN.qtype) MAIN.qtype = DNS_T_AAAA; if ((error = dns_p_push(Q, DNS_S_QD, MAIN.qname, strlen(MAIN.qname), MAIN.qtype, DNS_C_IN, 0, 0))) panic("dns_p_push: %s", dns_strerror(error)); dns_header(Q)->rd = 1; if (strstr(argv[0], "udp")) type = SOCK_DGRAM; else if (strstr(argv[0], "tcp")) type = SOCK_STREAM; else type = dns_res_tcp2type(resconf()->options.tcp); fprintf(stderr, "querying %s for %s IN %s\n", host, MAIN.qname, dns_strtype(MAIN.qtype)); if (!(so = dns_so_open((struct sockaddr *)&resconf()->iface, type, dns_opts(), &error))) panic("dns_so_open: %s", dns_strerror(error)); while (!(A = dns_so_query(so, Q, (struct sockaddr *)&ss, &error))) { if (error != DNS_EAGAIN) panic("dns_so_query: %s (%d)", dns_strerror(error), error); if (dns_so_elapsed(so) > 10) panic("query timed-out"); dns_so_poll(so, 1); } print_packet(A, stdout); dns_so_close(so); return 0; } /* send_query() */ static int print_arpa(int argc, char *argv[]) { const char *ip = (argc > 1)? argv[1] : "::1"; int af = (strchr(ip, ':'))? AF_INET6 : AF_INET; union { struct in_addr a4; struct in6_addr a6; } addr; char host[DNS_D_MAXNAME + 1]; if (1 != dns_inet_pton(af, ip, &addr) || 0 == dns_ptr_qname(host, sizeof host, af, &addr)) panic("%s: invalid address", ip); fprintf(stdout, "%s\n", host); return 0; } /* print_arpa() */ static int show_hints(int argc, char *argv[]) { struct dns_hints *(*load)(struct dns_resolv_conf *, int *); const char *which, *how, *who; struct dns_hints *hints; int error; which = (argc > 1)? argv[1] : "local"; how = (argc > 2)? argv[2] : "plain"; who = (argc > 3)? argv[3] : "google.com"; load = (0 == strcmp(which, "local")) ? &dns_hints_local : &dns_hints_root; if (!(hints = load(resconf(), &error))) panic("%s: %s", argv[0], dns_strerror(error)); if (0 == strcmp(how, "plain")) { dns_hints_dump(hints, stdout); } else { struct dns_packet *query, *answer; query = dns_p_new(512); if ((error = dns_p_push(query, DNS_S_QUESTION, who, strlen(who), DNS_T_A, DNS_C_IN, 0, 0))) panic("%s: %s", who, dns_strerror(error)); if (!(answer = dns_hints_query(hints, query, &error))) panic("%s: %s", who, dns_strerror(error)); print_packet(answer, stdout); free(answer); } dns_hints_close(hints); return 0; } /* show_hints() */ static int resolve_query(int argc DNS_NOTUSED, char *argv[]) { _Bool recurse = !!strstr(argv[0], "recurse"); struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local; struct dns_resolver *R; struct dns_packet *ans; const struct dns_stat *st; int error; if (!MAIN.qname) MAIN.qname = "www.google.com"; if (!MAIN.qtype) MAIN.qtype = DNS_T_A; resconf()->options.recurse = recurse; if (!(R = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error))) panic("%s: %s", MAIN.qname, dns_strerror(error)); if ((error = dns_res_submit(R, MAIN.qname, MAIN.qtype, DNS_C_IN))) panic("%s: %s", MAIN.qname, dns_strerror(error)); while ((error = dns_res_check(R))) { if (error != DNS_EAGAIN) panic("dns_res_check: %s (%d)", dns_strerror(error), error); if (dns_res_elapsed(R) > 30) panic("query timed-out"); dns_res_poll(R, 1); } ans = dns_res_fetch(R, &error); print_packet(ans, stdout); free(ans); st = dns_res_stat(R); putchar('\n'); printf(";; queries: %"PRIuZ"\n", st->queries); printf(";; udp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.sent.count, st->udp.sent.bytes); printf(";; udp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.rcvd.count, st->udp.rcvd.bytes); printf(";; tcp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.sent.count, st->tcp.sent.bytes); printf(";; tcp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.rcvd.count, st->tcp.rcvd.bytes); dns_res_close(R); return 0; } /* resolve_query() */ static int resolve_addrinfo(int argc DNS_NOTUSED, char *argv[]) { _Bool recurse = !!strstr(argv[0], "recurse"); struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local; struct dns_resolver *res = NULL; struct dns_addrinfo *ai = NULL; struct addrinfo ai_hints = { .ai_family = PF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_CANONNAME }; struct addrinfo *ent; char pretty[512]; int error; if (!MAIN.qname) MAIN.qname = "www.google.com"; /* NB: MAIN.qtype of 0 means obey hints.ai_family */ resconf()->options.recurse = recurse; if (!(res = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error))) panic("%s: %s", MAIN.qname, dns_strerror(error)); if (!(ai = dns_ai_open(MAIN.qname, "80", MAIN.qtype, &ai_hints, res, &error))) panic("%s: %s", MAIN.qname, dns_strerror(error)); do { switch (error = dns_ai_nextent(&ent, ai)) { case 0: dns_ai_print(pretty, sizeof pretty, ent, ai); fputs(pretty, stdout); free(ent); break; case ENOENT: break; case DNS_EAGAIN: if (dns_ai_elapsed(ai) > 30) panic("query timed-out"); dns_ai_poll(ai, 1); break; default: panic("dns_ai_nextent: %s (%d)", dns_strerror(error), error); } } while (error != ENOENT); dns_res_close(res); dns_ai_close(ai); return 0; } /* resolve_addrinfo() */ static int echo_port(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { union { struct sockaddr sa; struct sockaddr_in sin; } port; int fd; memset(&port, 0, sizeof port); port.sin.sin_family = AF_INET; port.sin.sin_port = htons(5354); port.sin.sin_addr.s_addr = inet_addr("127.0.0.1"); if (-1 == (fd = socket(PF_INET, SOCK_DGRAM, 0))) panic("socket: %s", strerror(errno)); if (0 != bind(fd, &port.sa, sizeof port.sa)) panic("127.0.0.1:5353: %s", dns_strerror(errno)); for (;;) { struct dns_packet *pkt = dns_p_new(512); struct sockaddr_storage ss; socklen_t slen = sizeof ss; ssize_t count; #if defined(MSG_WAITALL) /* MinGW issue */ int rflags = MSG_WAITALL; #else int rflags = 0; #endif count = recvfrom(fd, (char *)pkt->data, pkt->size, rflags, (struct sockaddr *)&ss, &slen); if (!count || count < 0) panic("recvfrom: %s", strerror(errno)); pkt->end = count; dns_p_dump(pkt, stdout); (void)sendto(fd, (char *)pkt->data, pkt->end, 0, (struct sockaddr *)&ss, slen); } return 0; } /* echo_port() */ static int isection(int argc, char *argv[]) { const char *name = (argc > 1)? argv[1] : ""; int type; type = dns_isection(name); name = dns_strsection(type); printf("%s (%d)\n", name, type); return 0; } /* isection() */ static int iclass(int argc, char *argv[]) { const char *name = (argc > 1)? argv[1] : ""; int type; type = dns_iclass(name); name = dns_strclass(type); printf("%s (%d)\n", name, type); return 0; } /* iclass() */ static int itype(int argc, char *argv[]) { const char *name = (argc > 1)? argv[1] : ""; int type; type = dns_itype(name); name = dns_strtype(type); printf("%s (%d)\n", name, type); return 0; } /* itype() */ static int iopcode(int argc, char *argv[]) { const char *name = (argc > 1)? argv[1] : ""; int type; type = dns_iopcode(name); name = dns_stropcode(type); printf("%s (%d)\n", name, type); return 0; } /* iopcode() */ static int ircode(int argc, char *argv[]) { const char *name = (argc > 1)? argv[1] : ""; int type; type = dns_ircode(name); name = dns_strrcode(type); printf("%s (%d)\n", name, type); return 0; } /* ircode() */ #define SIZE1(x) { DNS_PP_STRINGIFY(x), sizeof (x) } #define SIZE2(x, ...) SIZE1(x), SIZE1(__VA_ARGS__) #define SIZE3(x, ...) SIZE1(x), SIZE2(__VA_ARGS__) #define SIZE4(x, ...) SIZE1(x), SIZE3(__VA_ARGS__) #define SIZE(...) DNS_PP_CALL(DNS_PP_XPASTE(SIZE, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) static int sizes(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { static const struct { const char *name; size_t size; } type[] = { SIZE(struct dns_header, struct dns_packet, struct dns_rr, struct dns_rr_i), SIZE(struct dns_a, struct dns_aaaa, struct dns_mx, struct dns_ns), SIZE(struct dns_cname, struct dns_soa, struct dns_ptr, struct dns_srv), SIZE(struct dns_sshfp, struct dns_txt, union dns_any), SIZE(struct dns_resolv_conf, struct dns_hosts, struct dns_hints, struct dns_hints_i), SIZE(struct dns_options, struct dns_socket, struct dns_resolver, struct dns_addrinfo), SIZE(struct dns_cache), SIZE(size_t), SIZE(void *), SIZE(long) }; unsigned i, max; for (i = 0, max = 0; i < lengthof(type); i++) max = DNS_PP_MAX(max, strlen(type[i].name)); for (i = 0; i < lengthof(type); i++) printf("%*s : %"PRIuZ"\n", max, type[i].name, type[i].size); return 0; } /* sizes() */ static const struct { const char *cmd; int (*run)(); const char *help; } cmds[] = { { "parse-packet", &parse_packet, "parse binary packet from stdin" }, { "parse-domain", &parse_domain, "anchor and iteratively cleave domain" }, { "trim-domain", &trim_domain, "trim and anchor domain name" }, { "expand-domain", &expand_domain, "expand domain at offset NN in packet from stdin" }, { "show-resconf", &show_resconf, "show resolv.conf data" }, { "show-hosts", &show_hosts, "show hosts data" }, { "show-nssconf", &show_nssconf, "show nsswitch.conf data" }, { "query-hosts", &query_hosts, "query A, AAAA or PTR in hosts data" }, { "search-list", &search_list, "generate query search list from domain" }, { "permute-set", &permute_set, "generate random permutation -> (0 .. N or N .. M)" }, { "shuffle-16", &shuffle_16, "simple 16-bit permutation" }, { "dump-random", &dump_random, "generate random bytes" }, { "send-query", &send_query, "send query to host" }, { "send-query-udp", &send_query, "send udp query to host" }, { "send-query-tcp", &send_query, "send tcp query to host" }, { "print-arpa", &print_arpa, "print arpa. zone name of address" }, { "show-hints", &show_hints, "print hints: show-hints [local|root] [plain|packet]" }, { "resolve-stub", &resolve_query, "resolve as stub resolver" }, { "resolve-recurse", &resolve_query, "resolve as recursive resolver" }, { "addrinfo-stub", &resolve_addrinfo, "resolve through getaddrinfo clone" }, { "addrinfo-recurse", &resolve_addrinfo, "resolve through getaddrinfo clone" }, /* { "resolve-nameinfo", &resolve_query, "resolve as recursive resolver" }, */ { "echo", &echo_port, "server echo mode, for nmap fuzzing" }, { "isection", &isection, "parse section string" }, { "iclass", &iclass, "parse class string" }, { "itype", &itype, "parse type string" }, { "iopcode", &iopcode, "parse opcode string" }, { "ircode", &ircode, "parse rcode string" }, { "sizes", &sizes, "print data structure sizes" }, }; static void print_usage(const char *progname, FILE *fp) { static const char *usage = " [OPTIONS] COMMAND [ARGS]\n" " -c PATH Path to resolv.conf\n" " -n PATH Path to nsswitch.conf\n" " -l PATH Path to local hosts\n" " -z PATH Path to zone cache\n" " -q QNAME Query name\n" " -t QTYPE Query type\n" " -s HOW Sort records\n" " -v Be more verbose (-vv show packets; -vvv hexdump packets)\n" " -V Print version info\n" " -h Print this usage message\n" "\n"; unsigned i, n, m; fputs(progname, fp); fputs(usage, fp); for (i = 0, m = 0; i < lengthof(cmds); i++) { if (strlen(cmds[i].cmd) > m) m = strlen(cmds[i].cmd); } for (i = 0; i < lengthof(cmds); i++) { fprintf(fp, " %s ", cmds[i].cmd); for (n = strlen(cmds[i].cmd); n < m; n++) putc(' ', fp); fputs(cmds[i].help, fp); putc('\n', fp); } fputs("\nReport bugs to William Ahern \n", fp); } /* print_usage() */ static void print_version(const char *progname, FILE *fp) { fprintf(fp, "%s (dns.c) %.8X\n", progname, dns_v_rel()); fprintf(fp, "vendor %s\n", dns_vendor()); fprintf(fp, "release %.8X\n", dns_v_rel()); fprintf(fp, "abi %.8X\n", dns_v_abi()); fprintf(fp, "api %.8X\n", dns_v_api()); } /* print_version() */ int main(int argc, char **argv) { extern int optind; extern char *optarg; const char *progname = argv[0]; unsigned i; int ch; while (-1 != (ch = getopt(argc, argv, "q:t:c:n:l:z:s:vVh"))) { switch (ch) { case 'c': assert(MAIN.resconf.count < lengthof(MAIN.resconf.path)); MAIN.resconf.path[MAIN.resconf.count++] = optarg; break; case 'n': assert(MAIN.nssconf.count < lengthof(MAIN.nssconf.path)); MAIN.nssconf.path[MAIN.nssconf.count++] = optarg; break; case 'l': assert(MAIN.hosts.count < lengthof(MAIN.hosts.path)); MAIN.hosts.path[MAIN.hosts.count++] = optarg; break; case 'z': assert(MAIN.cache.count < lengthof(MAIN.cache.path)); MAIN.cache.path[MAIN.cache.count++] = optarg; break; case 'q': MAIN.qname = optarg; break; case 't': for (i = 0; i < lengthof(dns_rrtypes); i++) { if (0 == strcasecmp(dns_rrtypes[i].name, optarg)) { MAIN.qtype = dns_rrtypes[i].type; break; } } if (MAIN.qtype) break; for (i = 0; dns_isdigit(optarg[i]); i++) { MAIN.qtype *= 10; MAIN.qtype += optarg[i] - '0'; } if (!MAIN.qtype) panic("%s: invalid query type", optarg); break; case 's': if (0 == strcasecmp(optarg, "packet")) MAIN.sort = &dns_rr_i_packet; else if (0 == strcasecmp(optarg, "shuffle")) MAIN.sort = &dns_rr_i_shuffle; else if (0 == strcasecmp(optarg, "order")) MAIN.sort = &dns_rr_i_order; else panic("%s: invalid sort method", optarg); break; case 'v': dns_debug = ++MAIN.verbose; break; case 'V': print_version(progname, stdout); return 0; case 'h': print_usage(progname, stdout); return 0; default: print_usage(progname, stderr); return EXIT_FAILURE; } /* switch() */ } /* while() */ argc -= optind; argv += optind; for (i = 0; i < lengthof(cmds) && argv[0]; i++) { if (0 == strcmp(cmds[i].cmd, argv[0])) return cmds[i].run(argc, argv); } print_usage(progname, stderr); return EXIT_FAILURE; } /* main() */ #endif /* DNS_MAIN */ /* * pop file-scoped compiler annotations */ #if __clang__ #pragma clang diagnostic pop #elif DNS_GNUC_PREREQ(4,6,0) #pragma GCC diagnostic pop #endif belle-sip-1.6.3/src/dns.h000066400000000000000000001051131313437522400151260ustar00rootroot00000000000000/* ========================================================================== * dns.h - Recursive, Reentrant DNS Resolver. * -------------------------------------------------------------------------- * Copyright (c) 2009, 2010, 2012-2015 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #ifndef DNS_H #define DNS_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include /* size_t offsetof() */ #include /* FILE */ #include /* strlen(3) */ #include /* time_t */ #if _WIN32 #include #include #else #include /* BYTE_ORDER BIG_ENDIAN _BIG_ENDIAN */ #include /* socklen_t */ #include /* struct socket */ #include /* POLLIN POLLOUT */ #include /* struct in_addr struct in6_addr */ #include /* struct addrinfo */ #endif /* * V I S I B I L I T Y * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef DNS_PUBLIC #define DNS_PUBLIC #endif /* * V E R S I O N * * Vendor: Entity for which versions numbers are relevant. (If forking * change DNS_VENDOR to avoid confusion.) * * Three versions: * * REL Official "release"--bug fixes, new features, etc. * ABI Changes to existing object sizes or parameter types. * API Changes that might effect application source. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_VENDOR "william@25thandClement.com" #define DNS_V_REL 0x20160608 #define DNS_V_ABI 0x20160608 #define DNS_V_API 0x20160608 DNS_PUBLIC const char *dns_vendor(void); DNS_PUBLIC int dns_v_rel(void); DNS_PUBLIC int dns_v_abi(void); DNS_PUBLIC int dns_v_api(void); /* * E R R O R S * * Errors and exceptions are always returned through an int. This should * hopefully make integration easier in the majority of circumstances, and * also cut down on useless compiler warnings. * * System and library errors are returned together. POSIX guarantees that * all system errors are positive integers. Library errors are always * negative integers in the range DNS_EBASE to DNS_ELAST, with the high bits * set to the three magic ASCII characters "dns". * * dns_strerror() returns static English string descriptions of all known * errors, and punts the remainder to strerror(3). * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_EBASE -(('d' << 24) | ('n' << 16) | ('s' << 8) | 64) #define dns_error_t int /* for documentation only */ enum dns_errno { DNS_ENOBUFS = DNS_EBASE, DNS_EILLEGAL, DNS_EORDER, DNS_ESECTION, DNS_EUNKNOWN, DNS_EADDRESS, DNS_ENOQUERY, DNS_ENOANSWER, DNS_EFETCHED, DNS_ESERVICE, /* EAI_SERVICE */ DNS_ENONAME, /* EAI_NONAME */ DNS_EFAIL, /* EAI_FAIL */ DNS_ELAST, }; /* dns_errno */ DNS_PUBLIC const char *dns_strerror(dns_error_t); DNS_PUBLIC int *dns_debug_p(void); #define dns_debug (*dns_debug_p()) /* was extern int dns_debug before 20160523 API */ /* * C O M P I L E R A N N O T A T I O N S * * GCC with -Wextra, and clang by default, complain about overrides in * initializer lists. Overriding previous member initializers is well * defined behavior in C. dns.c relies on this behavior to define default, * overrideable member values when instantiating configuration objects. * * dns_quietinit() guards a compound literal expression with pragmas to * silence these shrill warnings. This alleviates the burden of requiring * third-party projects to adjust their compiler flags. * * NOTE: If you take the address of the compound literal, take the address * of the transformed expression, otherwise the compound literal lifetime is * tied to the scope of the GCC statement expression. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if defined __clang__ #define DNS_PRAGMA_PUSH _Pragma("clang diagnostic push") #define DNS_PRAGMA_QUIET _Pragma("clang diagnostic ignored \"-Winitializer-overrides\"") #define DNS_PRAGMA_POP _Pragma("clang diagnostic pop") #define dns_quietinit(...) \ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__ DNS_PRAGMA_POP #elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 #define DNS_PRAGMA_PUSH _Pragma("GCC diagnostic push") #define DNS_PRAGMA_QUIET _Pragma("GCC diagnostic ignored \"-Woverride-init\"") #define DNS_PRAGMA_POP _Pragma("GCC diagnostic pop") /* GCC parses the _Pragma operator less elegantly than clang. */ #define dns_quietinit(...) \ __extension__ ({ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__; DNS_PRAGMA_POP }) #else #define DNS_PRAGMA_PUSH #define DNS_PRAGMA_QUIET #define DNS_PRAGMA_POP #define dns_quietinit(...) __VA_ARGS__ #endif #if defined __GNUC__ #define DNS_PRAGMA_EXTENSION __extension__ #else #define DNS_PRAGMA_EXTENSION #endif /* * E V E N T S I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if defined(POLLIN) #define DNS_POLLIN POLLIN #else #define DNS_POLLIN 1 #endif #if defined(POLLOUT) #define DNS_POLLOUT POLLOUT #else #define DNS_POLLOUT 2 #endif /* * See Application Interface below for configuring libevent bitmasks instead * of poll(2) bitmasks. */ #define DNS_EVREAD 2 #define DNS_EVWRITE 4 #define DNS_POLL2EV(set) \ (((set) & DNS_POLLIN)? DNS_EVREAD : 0) | (((set) & DNS_POLLOUT)? DNS_EVWRITE : 0) #define DNS_EV2POLL(set) \ (((set) & DNS_EVREAD)? DNS_POLLIN : 0) | (((set) & DNS_EVWRITE)? DNS_POLLOUT : 0) /* * E N U M E R A T I O N I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ enum dns_section { DNS_S_QD = 0x01, #define DNS_S_QUESTION DNS_S_QD DNS_S_AN = 0x02, #define DNS_S_ANSWER DNS_S_AN DNS_S_NS = 0x04, #define DNS_S_AUTHORITY DNS_S_NS DNS_S_AR = 0x08, #define DNS_S_ADDITIONAL DNS_S_AR DNS_S_ALL = 0x0f }; /* enum dns_section */ enum dns_class { DNS_C_IN = 1, DNS_C_ANY = 255 }; /* enum dns_class */ enum dns_type { DNS_T_A = 1, DNS_T_NS = 2, DNS_T_CNAME = 5, DNS_T_SOA = 6, DNS_T_PTR = 12, DNS_T_MX = 15, DNS_T_TXT = 16, DNS_T_AAAA = 28, DNS_T_SRV = 33, DNS_T_OPT = 41, DNS_T_SSHFP = 44, DNS_T_SPF = 99, DNS_T_AXFR = 252, DNS_T_ALL = 255 }; /* enum dns_type */ enum dns_opcode { DNS_OP_QUERY = 0, DNS_OP_IQUERY = 1, DNS_OP_STATUS = 2, DNS_OP_NOTIFY = 4, DNS_OP_UPDATE = 5, }; /* dns_opcode */ enum dns_rcode { DNS_RC_NOERROR = 0, DNS_RC_FORMERR = 1, DNS_RC_SERVFAIL = 2, DNS_RC_NXDOMAIN = 3, DNS_RC_NOTIMP = 4, DNS_RC_REFUSED = 5, DNS_RC_YXDOMAIN = 6, DNS_RC_YXRRSET = 7, DNS_RC_NXRRSET = 8, DNS_RC_NOTAUTH = 9, DNS_RC_NOTZONE = 10, /* EDNS(0) extended RCODEs */ DNS_RC_BADVERS = 16, }; /* dns_rcode */ /* * NOTE: These string functions need a small buffer in case the literal * integer value needs to be printed and returned. UNLESS this buffer is * SPECIFIED, the returned string has ONLY BLOCK SCOPE. */ #define DNS_STRMAXLEN 47 /* "QUESTION|ANSWER|AUTHORITY|ADDITIONAL" */ DNS_PUBLIC const char *dns_strsection(enum dns_section, void *, size_t); #define dns_strsection3(a, b, c) \ dns_strsection((a), (b), (c)) #define dns_strsection1(a) dns_strsection((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) #define dns_strsection(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strsection, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) DNS_PUBLIC enum dns_section dns_isection(const char *); DNS_PUBLIC const char *dns_strclass(enum dns_class, void *, size_t); #define dns_strclass3(a, b, c) dns_strclass((a), (b), (c)) #define dns_strclass1(a) dns_strclass((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) #define dns_strclass(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strclass, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) DNS_PUBLIC enum dns_class dns_iclass(const char *); DNS_PUBLIC const char *dns_strtype(enum dns_type, void *, size_t); #define dns_strtype3(a, b, c) dns_strtype((a), (b), (c)) #define dns_strtype1(a) dns_strtype((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) #define dns_strtype(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strtype, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) DNS_PUBLIC enum dns_type dns_itype(const char *); DNS_PUBLIC const char *dns_stropcode(enum dns_opcode); DNS_PUBLIC enum dns_opcode dns_iopcode(const char *); DNS_PUBLIC const char *dns_strrcode(enum dns_rcode); DNS_PUBLIC enum dns_rcode dns_ircode(const char *); /* * A T O M I C I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ typedef unsigned long dns_atomic_t; typedef unsigned long dns_refcount_t; /* must be same value type as dns_atomic_t */ /* * C R Y P T O I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ typedef unsigned dns_random_f(void); DNS_PUBLIC dns_random_f **dns_random_p(void); #define dns_random (*dns_random_p()) /* was extern unsigned (*dns_random)(void) before 20160523 API */ /* * P A C K E T I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_header { unsigned qid:16; #if (defined BYTE_ORDER && BYTE_ORDER == BIG_ENDIAN) || (defined __sun && defined _BIG_ENDIAN) unsigned qr:1; unsigned opcode:4; unsigned aa:1; unsigned tc:1; unsigned rd:1; unsigned ra:1; unsigned unused:3; unsigned rcode:4; #else unsigned rd:1; unsigned tc:1; unsigned aa:1; unsigned opcode:4; unsigned qr:1; unsigned rcode:4; unsigned unused:3; unsigned ra:1; #endif unsigned qdcount:16; unsigned ancount:16; unsigned nscount:16; unsigned arcount:16; }; /* struct dns_header */ #define dns_header(p) (&(p)->header) #ifndef DNS_P_QBUFSIZ #define DNS_P_QBUFSIZ dns_p_calcsize(256 + 4) #endif #ifndef DNS_P_DICTSIZE #define DNS_P_DICTSIZE 16 #endif struct dns_packet { unsigned short dict[DNS_P_DICTSIZE]; struct dns_p_memo { struct dns_s_memo { unsigned short base, end; } qd, an, ns, ar; struct { unsigned short p; unsigned short maxudp; unsigned ttl; } opt; } memo; struct { struct dns_packet *cqe_next, *cqe_prev; } cqe; size_t size, end; int:16; /* tcp padding */ DNS_PRAGMA_EXTENSION union { struct dns_header header; unsigned char data[1]; }; }; /* struct dns_packet */ #define dns_p_calcsize(n) (offsetof(struct dns_packet, data) + DNS_PP_MAX(12, (n))) #define dns_p_sizeof(P) dns_p_calcsize((P)->end) /** takes size of maximum desired payload */ #define dns_p_new(n) (dns_p_init((struct dns_packet *)&(union { unsigned char b[dns_p_calcsize((n))]; struct dns_packet p; }){ { 0 } }, dns_p_calcsize((n)))) /** takes size of entire packet structure as allocated */ DNS_PUBLIC struct dns_packet *dns_p_init(struct dns_packet *, size_t); /** takes size of maximum desired payload */ DNS_PUBLIC struct dns_packet *dns_p_make(size_t, int *); DNS_PUBLIC int dns_p_grow(struct dns_packet **); DNS_PUBLIC struct dns_packet *dns_p_copy(struct dns_packet *, const struct dns_packet *); #define dns_p_opcode(P) (dns_header(P)->opcode) DNS_PUBLIC enum dns_rcode dns_p_rcode(struct dns_packet *); DNS_PUBLIC unsigned dns_p_count(struct dns_packet *, enum dns_section); DNS_PUBLIC int dns_p_push(struct dns_packet *, enum dns_section, const void *, size_t, enum dns_type, enum dns_class, unsigned, const void *); DNS_PUBLIC void dns_p_dictadd(struct dns_packet *, unsigned short); DNS_PUBLIC struct dns_packet *dns_p_merge(struct dns_packet *, enum dns_section, struct dns_packet *, enum dns_section, int *); DNS_PUBLIC void dns_p_dump(struct dns_packet *, FILE *); DNS_PUBLIC int dns_p_study(struct dns_packet *); /* * D O M A I N N A M E I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_D_MAXLABEL 63 /* + 1 '\0' */ #define DNS_D_MAXNAME 255 /* + 1 '\0' */ #define DNS_D_ANCHOR 1 /* anchor domain w/ root "." */ #define DNS_D_CLEAVE 2 /* cleave sub-domain */ #define DNS_D_TRIM 4 /* remove superfluous dots */ #define dns_d_new3(a, b, f) dns_d_init(&(char[DNS_D_MAXNAME + 1]){ 0 }, DNS_D_MAXNAME + 1, (a), (b), (f)) #define dns_d_new2(a, f) dns_d_new3((a), strlen((a)), (f)) #define dns_d_new1(a) dns_d_new3((a), strlen((a)), DNS_D_ANCHOR) #define dns_d_new(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_d_new, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) DNS_PUBLIC char *dns_d_init(void *, size_t, const void *, size_t, int); DNS_PUBLIC size_t dns_d_anchor(void *, size_t, const void *, size_t); DNS_PUBLIC size_t dns_d_cleave(void *, size_t, const void *, size_t); DNS_PUBLIC size_t dns_d_comp(void *, size_t, const void *, size_t, struct dns_packet *, int *); DNS_PUBLIC size_t dns_d_expand(void *, size_t, unsigned short, struct dns_packet *, int *); DNS_PUBLIC unsigned short dns_d_skip(unsigned short, struct dns_packet *); DNS_PUBLIC int dns_d_push(struct dns_packet *, const void *, size_t); DNS_PUBLIC size_t dns_d_cname(void *, size_t, const void *, size_t, struct dns_packet *, int *error); /* * R E S O U R C E R E C O R D I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_rr { enum dns_section section; struct { unsigned short p; unsigned short len; } dn; enum dns_type type; enum dns_class class; unsigned ttl; struct { unsigned short p; unsigned short len; } rd; }; /* struct dns_rr */ DNS_PUBLIC int dns_rr_copy(struct dns_packet *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_rr_parse(struct dns_rr *, unsigned short, struct dns_packet *); DNS_PUBLIC unsigned short dns_rr_skip(unsigned short, struct dns_packet *); DNS_PUBLIC int dns_rr_cmp(struct dns_rr *, struct dns_packet *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC size_t dns_rr_print(void *, size_t, struct dns_rr *, struct dns_packet *, int *); #define dns_rr_i_new(P, ...) \ dns_rr_i_init(&dns_quietinit((struct dns_rr_i){ 0, __VA_ARGS__ }), (P)) struct dns_rr_i { enum dns_section section; const void *name; enum dns_type type; enum dns_class class; const void *data; int follow; int (*sort)(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); unsigned args[2]; struct { unsigned short next; unsigned short count; unsigned exec; unsigned regs[2]; } state, saved; }; /* struct dns_rr_i */ DNS_PUBLIC int dns_rr_i_packet(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); DNS_PUBLIC int dns_rr_i_order(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); DNS_PUBLIC int dns_rr_i_shuffle(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); DNS_PUBLIC struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *, struct dns_packet *); #define dns_rr_i_save(i) ((i)->saved = (i)->state) #define dns_rr_i_rewind(i) ((i)->state = (i)->saved) #define dns_rr_i_count(i) ((i)->state.count) DNS_PUBLIC unsigned dns_rr_grep(struct dns_rr *, unsigned, struct dns_rr_i *, struct dns_packet *, int *); #define dns_rr_foreach(rr, P, ...) \ for (struct dns_rr_i DNS_PP_XPASTE(i, __LINE__) = *dns_rr_i_new((P), __VA_ARGS__); dns_rr_grep((rr), 1, &DNS_PP_XPASTE(i, __LINE__), (P), &(int){ 0 }); ) /* * A R E S O U R C E R E C O R D */ struct dns_a { struct in_addr addr; }; /* struct dns_a */ DNS_PUBLIC int dns_a_parse(struct dns_a *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_a_push(struct dns_packet *, struct dns_a *); DNS_PUBLIC int dns_a_cmp(const struct dns_a *, const struct dns_a *); DNS_PUBLIC size_t dns_a_print(void *, size_t, struct dns_a *); DNS_PUBLIC size_t dns_a_arpa(void *, size_t, const struct dns_a *); /* * AAAA R E S O U R C E R E C O R D */ struct dns_aaaa { struct in6_addr addr; }; /* struct dns_aaaa */ DNS_PUBLIC int dns_aaaa_parse(struct dns_aaaa *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_aaaa_push(struct dns_packet *, struct dns_aaaa *); DNS_PUBLIC int dns_aaaa_cmp(const struct dns_aaaa *, const struct dns_aaaa *); DNS_PUBLIC size_t dns_aaaa_print(void *, size_t, struct dns_aaaa *); DNS_PUBLIC size_t dns_aaaa_arpa(void *, size_t, const struct dns_aaaa *); /* * MX R E S O U R C E R E C O R D */ struct dns_mx { unsigned short preference; char host[DNS_D_MAXNAME + 1]; }; /* struct dns_mx */ DNS_PUBLIC int dns_mx_parse(struct dns_mx *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_mx_push(struct dns_packet *, struct dns_mx *); DNS_PUBLIC int dns_mx_cmp(const struct dns_mx *, const struct dns_mx *); DNS_PUBLIC size_t dns_mx_print(void *, size_t, struct dns_mx *); DNS_PUBLIC size_t dns_mx_cname(void *, size_t, struct dns_mx *); /* * NS R E S O U R C E R E C O R D */ struct dns_ns { char host[DNS_D_MAXNAME + 1]; }; /* struct dns_ns */ DNS_PUBLIC int dns_ns_parse(struct dns_ns *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_ns_push(struct dns_packet *, struct dns_ns *); DNS_PUBLIC int dns_ns_cmp(const struct dns_ns *, const struct dns_ns *); DNS_PUBLIC size_t dns_ns_print(void *, size_t, struct dns_ns *); DNS_PUBLIC size_t dns_ns_cname(void *, size_t, struct dns_ns *); /* * CNAME R E S O U R C E R E C O R D */ struct dns_cname { char host[DNS_D_MAXNAME + 1]; }; /* struct dns_cname */ DNS_PUBLIC int dns_cname_parse(struct dns_cname *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_cname_push(struct dns_packet *, struct dns_cname *); DNS_PUBLIC int dns_cname_cmp(const struct dns_cname *, const struct dns_cname *); DNS_PUBLIC size_t dns_cname_print(void *, size_t, struct dns_cname *); DNS_PUBLIC size_t dns_cname_cname(void *, size_t, struct dns_cname *); /* * SOA R E S O U R C E R E C O R D */ struct dns_soa { char mname[DNS_D_MAXNAME + 1]; char rname[DNS_D_MAXNAME + 1]; unsigned serial, refresh, retry, expire, minimum; }; /* struct dns_soa */ DNS_PUBLIC int dns_soa_parse(struct dns_soa *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_soa_push(struct dns_packet *, struct dns_soa *); DNS_PUBLIC int dns_soa_cmp(const struct dns_soa *, const struct dns_soa *); DNS_PUBLIC size_t dns_soa_print(void *, size_t, struct dns_soa *); /* * PTR R E S O U R C E R E C O R D */ struct dns_ptr { char host[DNS_D_MAXNAME + 1]; }; /* struct dns_ptr */ DNS_PUBLIC int dns_ptr_parse(struct dns_ptr *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_ptr_push(struct dns_packet *, struct dns_ptr *); DNS_PUBLIC int dns_ptr_cmp(const struct dns_ptr *, const struct dns_ptr *); DNS_PUBLIC size_t dns_ptr_print(void *, size_t, struct dns_ptr *); DNS_PUBLIC size_t dns_ptr_cname(void *, size_t, struct dns_ptr *); DNS_PUBLIC size_t dns_ptr_qname(void *, size_t, int, void *); /* * SRV R E S O U R C E R E C O R D */ struct dns_srv { unsigned short priority; unsigned short weight; unsigned short port; char target[DNS_D_MAXNAME + 1]; }; /* struct dns_srv */ DNS_PUBLIC int dns_srv_parse(struct dns_srv *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_srv_push(struct dns_packet *, struct dns_srv *); DNS_PUBLIC int dns_srv_cmp(const struct dns_srv *, const struct dns_srv *); DNS_PUBLIC size_t dns_srv_print(void *, size_t, struct dns_srv *); DNS_PUBLIC size_t dns_srv_cname(void *, size_t, struct dns_srv *); /* * OPT R E S O U R C E R E C O R D */ #ifndef DNS_OPT_MINDATA #define DNS_OPT_MINDATA 256 #endif #define DNS_OPT_DNSSEC 0x8000 struct dns_opt { enum dns_rcode rcode; unsigned char version; unsigned short flags; union { unsigned short maxsize; /* deprecated as confusing */ unsigned short maxudp; /* maximum UDP payload size */ }; size_t size, len; unsigned char data[DNS_OPT_MINDATA]; }; /* struct dns_opt */ #define DNS_OPT_INIT(opt) { .size = sizeof (*opt) - offsetof(struct dns_opt, data) } DNS_PUBLIC struct dns_opt *dns_opt_init(struct dns_opt *, size_t); DNS_PUBLIC int dns_opt_parse(struct dns_opt *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_opt_push(struct dns_packet *, struct dns_opt *); DNS_PUBLIC int dns_opt_cmp(const struct dns_opt *, const struct dns_opt *); DNS_PUBLIC size_t dns_opt_print(void *, size_t, struct dns_opt *); DNS_PUBLIC unsigned int dns_opt_ttl(const struct dns_opt *); DNS_PUBLIC unsigned short dns_opt_class(const struct dns_opt *); DNS_PUBLIC dns_error_t dns_opt_data_push(struct dns_opt *, unsigned char, unsigned short, const void *); /* * SSHFP R E S O U R C E R E C O R D */ struct dns_sshfp { enum dns_sshfp_key { DNS_SSHFP_RSA = 1, DNS_SSHFP_DSA = 2, } algo; enum dns_sshfp_digest { DNS_SSHFP_SHA1 = 1, } type; union { unsigned char sha1[20]; } digest; }; /* struct dns_sshfp */ DNS_PUBLIC int dns_sshfp_parse(struct dns_sshfp *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_sshfp_push(struct dns_packet *, struct dns_sshfp *); DNS_PUBLIC int dns_sshfp_cmp(const struct dns_sshfp *, const struct dns_sshfp *); DNS_PUBLIC size_t dns_sshfp_print(void *, size_t, struct dns_sshfp *); /* * TXT R E S O U R C E R E C O R D */ #ifndef DNS_TXT_MINDATA #define DNS_TXT_MINDATA 1024 #endif struct dns_txt { size_t size, len; unsigned char data[DNS_TXT_MINDATA]; }; /* struct dns_txt */ DNS_PUBLIC struct dns_txt *dns_txt_init(struct dns_txt *, size_t); DNS_PUBLIC int dns_txt_parse(struct dns_txt *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_txt_push(struct dns_packet *, struct dns_txt *); DNS_PUBLIC int dns_txt_cmp(const struct dns_txt *, const struct dns_txt *); DNS_PUBLIC size_t dns_txt_print(void *, size_t, struct dns_txt *); /* * ANY R E S O U R C E R E C O R D */ union dns_any { struct dns_a a; struct dns_aaaa aaaa; struct dns_mx mx; struct dns_ns ns; struct dns_cname cname; struct dns_soa soa; struct dns_ptr ptr; struct dns_srv srv; struct dns_opt opt; struct dns_sshfp sshfp; struct dns_txt txt, spf, rdata; }; /* union dns_any */ #define DNS_ANY_INIT(any) { .rdata = { .size = sizeof *(any) - offsetof(struct dns_txt, data) } } DNS_PUBLIC union dns_any *dns_any_init(union dns_any *, size_t); DNS_PUBLIC int dns_any_parse(union dns_any *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_any_push(struct dns_packet *, union dns_any *, enum dns_type); DNS_PUBLIC int dns_any_cmp(const union dns_any *, enum dns_type, const union dns_any *, enum dns_type); DNS_PUBLIC size_t dns_any_print(void *, size_t, union dns_any *, enum dns_type); DNS_PUBLIC size_t dns_any_cname(void *, size_t, union dns_any *, enum dns_type); /* * H O S T S I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_hosts; DNS_PUBLIC struct dns_hosts *dns_hosts_open(int *); DNS_PUBLIC void dns_hosts_close(struct dns_hosts *); DNS_PUBLIC dns_refcount_t dns_hosts_acquire(struct dns_hosts *); DNS_PUBLIC dns_refcount_t dns_hosts_release(struct dns_hosts *); DNS_PUBLIC struct dns_hosts *dns_hosts_mortal(struct dns_hosts *); DNS_PUBLIC struct dns_hosts *dns_hosts_local(int *); DNS_PUBLIC int dns_hosts_loadfile(struct dns_hosts *, FILE *); DNS_PUBLIC int dns_hosts_loadpath(struct dns_hosts *, const char *); DNS_PUBLIC int dns_hosts_dump(struct dns_hosts *, FILE *); DNS_PUBLIC int dns_hosts_insert(struct dns_hosts *, int, const void *, const void *, _Bool); DNS_PUBLIC struct dns_packet *dns_hosts_query(struct dns_hosts *, struct dns_packet *, int *); /* * R E S O L V . C O N F I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_resolv_conf { struct sockaddr_storage nameserver[3]; char search[4][DNS_D_MAXNAME + 1]; /* (f)ile, (b)ind, (c)ache */ char lookup[4 * (1 + (4 * 2))]; /* getaddrinfo family by preference order ("inet4", "inet6") */ int family[3]; struct { _Bool edns0; unsigned ndots; unsigned timeout; unsigned attempts; _Bool rotate; _Bool recurse; _Bool smart; enum { DNS_RESCONF_TCP_ENABLE, DNS_RESCONF_TCP_ONLY, DNS_RESCONF_TCP_DISABLE, } tcp; } options; struct sockaddr_storage iface; struct { /* PRIVATE */ dns_atomic_t refcount; } _; }; /* struct dns_resolv_conf */ DNS_PUBLIC struct dns_resolv_conf *dns_resconf_open(int *); DNS_PUBLIC void dns_resconf_close(struct dns_resolv_conf *); DNS_PUBLIC dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *); DNS_PUBLIC dns_refcount_t dns_resconf_release(struct dns_resolv_conf *); DNS_PUBLIC struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *); DNS_PUBLIC struct dns_resolv_conf *dns_resconf_local(int *); DNS_PUBLIC struct dns_resolv_conf *dns_resconf_root(int *); DNS_PUBLIC int dns_resconf_pton(struct sockaddr_storage *, const char *); DNS_PUBLIC int dns_resconf_loadfile(struct dns_resolv_conf *, FILE *); DNS_PUBLIC int dns_resconf_loadpath(struct dns_resolv_conf *, const char *); #ifdef USE_FIXED_NAMESERVERS int dns_resconf_load_fixed_nameservers(struct dns_resolv_conf *resconf); #endif /* USE_FIXED_NAMESERVERS */ #ifdef USE_STRUCT_RES_STATE_NAMESERVERS int dns_resconf_load_struct_res_state_nameservers(struct dns_resolv_conf *resconf); #endif /* USE_STRUCT_RES_STATE_NAMESERVERS */ #ifdef _WIN32 DNS_PUBLIC int dns_resconf_loadwin(struct dns_resolv_conf *); #endif #ifdef __ANDROID__ DNS_PUBLIC int dns_resconf_loadandroid(struct dns_resolv_conf *resconf); #endif #ifdef HAVE_RESINIT DNS_PUBLIC int dns_resconf_loadfromresolv(struct dns_resolv_conf *resconf); #endif /*HAVE_RESINIT*/ DNS_PUBLIC int dns_nssconf_loadfile(struct dns_resolv_conf *, FILE *); DNS_PUBLIC int dns_nssconf_loadpath(struct dns_resolv_conf *, const char *); DNS_PUBLIC int dns_resconf_pton(struct sockaddr_storage *ss, const char *src); DNS_PUBLIC int dns_resconf_dump(struct dns_resolv_conf *, FILE *); DNS_PUBLIC int dns_nssconf_dump(struct dns_resolv_conf *, FILE *); DNS_PUBLIC int dns_resconf_setiface(struct dns_resolv_conf *, const char *, unsigned short); typedef unsigned long dns_resconf_i_t; DNS_PUBLIC size_t dns_resconf_search(void *, size_t, const void *, size_t, struct dns_resolv_conf *, dns_resconf_i_t *); /* * H I N T S E R V E R I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_hints; DNS_PUBLIC struct dns_hints *dns_hints_open(struct dns_resolv_conf *, int *); DNS_PUBLIC void dns_hints_close(struct dns_hints *); DNS_PUBLIC dns_refcount_t dns_hints_acquire(struct dns_hints *); DNS_PUBLIC dns_refcount_t dns_hints_release(struct dns_hints *); DNS_PUBLIC struct dns_hints *dns_hints_mortal(struct dns_hints *); DNS_PUBLIC int dns_hints_insert(struct dns_hints *, const char *, const struct sockaddr *, unsigned); DNS_PUBLIC unsigned dns_hints_insert_resconf(struct dns_hints *, const char *, const struct dns_resolv_conf *, int *); DNS_PUBLIC struct dns_hints *dns_hints_local(struct dns_resolv_conf *, int *); DNS_PUBLIC struct dns_hints *dns_hints_root(struct dns_resolv_conf *, int *); DNS_PUBLIC struct dns_packet *dns_hints_query(struct dns_hints *, struct dns_packet *, int *); DNS_PUBLIC int dns_hints_dump(struct dns_hints *, FILE *); struct dns_hints_i { const char *zone; struct { unsigned next; unsigned seed; } state; }; /* struct dns_hints_i */ #define dns_hints_i_new(...) (&(struct dns_hints_i){ __VA_ARGS__ }) DNS_PUBLIC unsigned dns_hints_grep(struct sockaddr **, socklen_t *, unsigned, struct dns_hints_i *, struct dns_hints *); /* * C A C H E I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_cache { void *state; dns_refcount_t (*acquire)(struct dns_cache *); dns_refcount_t (*release)(struct dns_cache *); struct dns_packet *(*query)(struct dns_packet *, struct dns_cache *, int *); int (*submit)(struct dns_packet *, struct dns_cache *); int (*check)(struct dns_cache *); struct dns_packet *(*fetch)(struct dns_cache *, int *); int (*pollfd)(struct dns_cache *); short (*events)(struct dns_cache *); void (*clear)(struct dns_cache *); union { long i; void *p; } arg[3]; struct { /* PRIVATE */ dns_atomic_t refcount; } _; }; /* struct dns_cache */ DNS_PUBLIC struct dns_cache *dns_cache_init(struct dns_cache *); DNS_PUBLIC void dns_cache_close(struct dns_cache *); /* * A P P L I C A T I O N I N T E R F A C E * * Options to change the behavior of the API. Applies across all the * different components. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_OPTS_INITIALIZER_ { 0, 0 }, 0 #define DNS_OPTS_INITIALIZER { DNS_OPTS_INITIALIZER_ } #define DNS_OPTS_INIT(...) { DNS_OPTS_INITIALIZER_, __VA_ARGS__ } #define dns_opts(...) (&dns_quietinit((struct dns_options)DNS_OPTS_INIT(__VA_ARGS__))) struct dns_options { /* * If the callback closes *fd, it must set it to -1. Otherwise, the * descriptor is queued and lazily closed at object destruction or * by an explicit call to _clear(). This allows safe use of * kqueue(2), epoll(2), et al -style persistent events. */ struct { void *arg; int (*cb)(int *fd, void *arg); } closefd; /* bitmask for _events() routines */ enum dns_events { DNS_SYSPOLL, DNS_LIBEVENT, } events; int udp_uses_connect; }; /* struct dns_options */ /* * S T A T S I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_stat { size_t queries; struct { struct { size_t count, bytes; } sent, rcvd; } udp, tcp; }; /* struct dns_stat */ /* * S O C K E T I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_socket; DNS_PUBLIC struct dns_socket *dns_so_open(const struct sockaddr *, int, const struct dns_options *, int *error); DNS_PUBLIC void dns_so_close(struct dns_socket *); DNS_PUBLIC void dns_so_reset(struct dns_socket *); DNS_PUBLIC unsigned short dns_so_mkqid(struct dns_socket *so); DNS_PUBLIC struct dns_packet *dns_so_query(struct dns_socket *, struct dns_packet *, struct sockaddr *, int *); DNS_PUBLIC int dns_so_submit(struct dns_socket *, struct dns_packet *, struct sockaddr *); DNS_PUBLIC int dns_so_check(struct dns_socket *); DNS_PUBLIC struct dns_packet *dns_so_fetch(struct dns_socket *, int *); DNS_PUBLIC time_t dns_so_elapsed(struct dns_socket *); DNS_PUBLIC void dns_so_clear(struct dns_socket *); DNS_PUBLIC int dns_so_events(struct dns_socket *); DNS_PUBLIC int dns_so_pollfd(struct dns_socket *); DNS_PUBLIC int dns_so_poll(struct dns_socket *, int); DNS_PUBLIC const struct dns_stat *dns_so_stat(struct dns_socket *); /* * R E S O L V E R I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_resolver; DNS_PUBLIC struct dns_resolver *dns_res_open(struct dns_resolv_conf *, struct dns_hosts *hosts, struct dns_hints *, struct dns_cache *, const struct dns_options *, int *); DNS_PUBLIC struct dns_resolver *dns_res_stub(const struct dns_options *, int *); DNS_PUBLIC void dns_res_reset(struct dns_resolver *); DNS_PUBLIC void dns_res_close(struct dns_resolver *); DNS_PUBLIC dns_refcount_t dns_res_acquire(struct dns_resolver *); DNS_PUBLIC dns_refcount_t dns_res_release(struct dns_resolver *); DNS_PUBLIC struct dns_resolver *dns_res_mortal(struct dns_resolver *); DNS_PUBLIC int dns_res_submit(struct dns_resolver *, const char *, enum dns_type, enum dns_class); DNS_PUBLIC int dns_res_submit2(struct dns_resolver *, const char *, size_t, enum dns_type, enum dns_class); DNS_PUBLIC int dns_res_check(struct dns_resolver *); DNS_PUBLIC struct dns_packet *dns_res_fetch(struct dns_resolver *, int *); DNS_PUBLIC time_t dns_res_elapsed(struct dns_resolver *); DNS_PUBLIC void dns_res_clear(struct dns_resolver *); DNS_PUBLIC int dns_res_events(struct dns_resolver *); DNS_PUBLIC int dns_res_pollfd(struct dns_resolver *); DNS_PUBLIC time_t dns_res_timeout(struct dns_resolver *); DNS_PUBLIC int dns_res_poll(struct dns_resolver *, int); DNS_PUBLIC struct dns_packet *dns_res_query(struct dns_resolver *, const char *, enum dns_type, enum dns_class, int, int *); DNS_PUBLIC const struct dns_stat *dns_res_stat(struct dns_resolver *); DNS_PUBLIC void dns_res_sethints(struct dns_resolver *, struct dns_hints *); DNS_PUBLIC void dns_res_enable_search(struct dns_resolver *, unsigned char enable); DNS_PUBLIC int dns_res_was_asymetric(struct dns_resolver *); /* * A D D R I N F O I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_addrinfo; DNS_PUBLIC struct dns_addrinfo *dns_ai_open(const char *, const char *, enum dns_type, const struct addrinfo *, struct dns_resolver *, int *); DNS_PUBLIC void dns_ai_close(struct dns_addrinfo *); DNS_PUBLIC int dns_ai_nextent(struct addrinfo **, struct dns_addrinfo *); DNS_PUBLIC size_t dns_ai_print(void *, size_t, struct addrinfo *, struct dns_addrinfo *); DNS_PUBLIC time_t dns_ai_elapsed(struct dns_addrinfo *); DNS_PUBLIC void dns_ai_clear(struct dns_addrinfo *); DNS_PUBLIC int dns_ai_events(struct dns_addrinfo *); DNS_PUBLIC int dns_ai_pollfd(struct dns_addrinfo *); DNS_PUBLIC time_t dns_ai_timeout(struct dns_addrinfo *); DNS_PUBLIC int dns_ai_poll(struct dns_addrinfo *, int); DNS_PUBLIC const struct dns_stat *dns_ai_stat(struct dns_addrinfo *); /* * U T I L I T Y I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ DNS_PUBLIC size_t dns_strlcpy(char *, const char *, size_t); DNS_PUBLIC size_t dns_strlcat(char *, const char *, size_t); /* * M A C R O M A G I C S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_PP_MIN(a, b) (((a) < (b))? (a) : (b)) #define DNS_PP_MAX(a, b) (((a) > (b))? (a) : (b)) #define DNS_PP_NARG_(a, b, c, d, e, f, g, h, i, j, k, N,...) N #define DNS_PP_NARG(...) DNS_PP_NARG_(__VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define DNS_PP_CALL(F, ...) F(__VA_ARGS__) #define DNS_PP_PASTE(x, y) x##y #define DNS_PP_XPASTE(x, y) DNS_PP_PASTE(x, y) #define DNS_PP_STRINGIFY_(s) #s #define DNS_PP_STRINGIFY(s) DNS_PP_STRINGIFY_(s) #define DNS_PP_D1 0 #define DNS_PP_D2 1 #define DNS_PP_D3 2 #define DNS_PP_D4 3 #define DNS_PP_D5 4 #define DNS_PP_D6 5 #define DNS_PP_D7 6 #define DNS_PP_D8 7 #define DNS_PP_D9 8 #define DNS_PP_D10 9 #define DNS_PP_D11 10 #define DNS_PP_DEC(N) DNS_PP_XPASTE(DNS_PP_D, N) #endif /* DNS_H */ belle-sip-1.6.3/src/generic-uri.c000066400000000000000000000107451313437522400165540ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2013 Belledonne Communications SARL, Grenoble, France This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "grammars/belle_sip_messageLexer.h" #include "grammars/belle_sip_messageParser.h" #include "belle-sip/generic-uri.h" #include "belle_sip_internal.h" struct _belle_generic_uri { belle_sip_object_t object; char* scheme; char* user; char* user_password; char* host; int port; char* path; char* query; char* opaque_part; }; void belle_generic_uri_init(belle_generic_uri_t *uri) { uri->port=-1; } static void belle_generic_uri_destroy(belle_generic_uri_t* uri) { DESTROY_STRING(uri,scheme) DESTROY_STRING(uri,user) DESTROY_STRING(uri,user_password) DESTROY_STRING(uri,host) DESTROY_STRING(uri,path) DESTROY_STRING(uri,query) DESTROY_STRING(uri,opaque_part) } static void belle_generic_uri_clone(belle_generic_uri_t* uri, const belle_generic_uri_t *orig){ CLONE_STRING(belle_generic_uri,scheme,uri,orig) CLONE_STRING(belle_generic_uri,user,uri,orig) CLONE_STRING(belle_generic_uri,user_password,uri,orig) CLONE_STRING(belle_generic_uri,host,uri,orig) uri->port=orig->port; CLONE_STRING(belle_generic_uri,path,uri,orig) CLONE_STRING(belle_generic_uri,query,uri,orig) CLONE_STRING(belle_generic_uri,opaque_part,uri,orig) } belle_sip_error_code belle_generic_uri_marshal(const belle_generic_uri_t* uri, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=BELLE_SIP_OK; if (uri->scheme) { error=belle_sip_snprintf(buff,buff_size,offset,"%s:",uri->scheme); if (error!=BELLE_SIP_OK) return error; } if (uri->opaque_part) { error=belle_sip_snprintf(buff,buff_size,offset,"%s",uri->opaque_part); if (error!=BELLE_SIP_OK) return error; } else { if (uri->host) { error=belle_sip_snprintf(buff,buff_size,offset,"//"); if (error!=BELLE_SIP_OK) return error; } if (uri->user) { char* escaped_username=belle_sip_uri_to_escaped_username(uri->user); error=belle_sip_snprintf(buff,buff_size,offset,"%s",escaped_username); belle_sip_free(escaped_username); if (error!=BELLE_SIP_OK) return error; if (uri->user_password) { char* escaped_password=belle_sip_uri_to_escaped_userpasswd(uri->user_password); error=belle_sip_snprintf(buff,buff_size,offset,":%s",escaped_password); belle_sip_free(escaped_password); if (error!=BELLE_SIP_OK) return error; } error=belle_sip_snprintf(buff,buff_size,offset,"@"); if (error!=BELLE_SIP_OK) return error; } if (uri->host) { if (strchr(uri->host,':')) { /*ipv6*/ error=belle_sip_snprintf(buff,buff_size,offset,"[%s]",uri->host); } else { error=belle_sip_snprintf(buff,buff_size,offset,"%s",uri->host); } if (error!=BELLE_SIP_OK) return error; } if (uri->port>0) { error=belle_sip_snprintf(buff,buff_size,offset,":%i",uri->port); if (error!=BELLE_SIP_OK) return error; } if (uri->path) { char* escaped_path=belle_generic_uri_to_escaped_path(uri->path); error=belle_sip_snprintf(buff,buff_size,offset,"%s",escaped_path); belle_sip_free(escaped_path); if (error!=BELLE_SIP_OK) return error; } if (uri->query) { char* escaped_query=belle_generic_uri_to_escaped_query(uri->query); error=belle_sip_snprintf(buff,buff_size,offset,"?%s",escaped_query); belle_sip_free(escaped_query); if (error!=BELLE_SIP_OK) return error; } } return BELLE_SIP_OK; } GET_SET_STRING(belle_generic_uri,scheme); GET_SET_STRING(belle_generic_uri,user); GET_SET_STRING(belle_generic_uri,user_password); GET_SET_STRING(belle_generic_uri,host); GET_SET_STRING(belle_generic_uri,path); GET_SET_STRING(belle_generic_uri,query); GET_SET_STRING(belle_generic_uri,opaque_part); GET_SET_INT(belle_generic_uri,port,int) BELLE_NEW(belle_generic_uri,belle_sip_object) BELLE_PARSE(belle_sip_messageParser,belle_,generic_uri) char* belle_generic_uri_to_string(belle_generic_uri_t* uri) { return belle_sip_object_to_string(uri); } belle-sip-1.6.3/src/grammars/000077500000000000000000000000001313437522400160015ustar00rootroot00000000000000belle-sip-1.6.3/src/grammars/.gitignore000066400000000000000000000000211313437522400177620ustar00rootroot00000000000000*.c *.h *.tokens belle-sip-1.6.3/src/grammars/Makefile.am000077500000000000000000000021431313437522400200400ustar00rootroot00000000000000 noinst_LTLIBRARIES=libbellesip_generated.la libbellesip_generated_la_CFLAGS=$(LIBBELLESIP_CFLAGS) $(ANTLR_CFLAGS) $(STRICT_OPTIONS) $(STRICT_OPTIONS_CC) $(LESS_STRICT_OPTIONS) nodist_libbellesip_generated_la_SOURCES= \ belle_sip_messageParser.c belle_sip_messageParser.h \ belle_sip_messageLexer.c belle_sip_messageLexer.h \ belle_sdpParser.c belle_sdpParser.h \ belle_sdpLexer.c belle_sdpLexer.h $(builddir)/belle_sip_messageParser.c $(builddir)/belle_sip_messageParser.h \ $(builddir)/belle_sip_messageLexer.c $(builddir)/belle_sip_messageLexer.h : belle_sip_message.g $(ANTLR) -make -Xmultithreaded -Xconversiontimeout 10000 -fo $(builddir) $(srcdir)/belle_sip_message.g $(builddir)/belle_sdpLexer.c \ $(builddir)/belle_sdpParser.c \ $(builddir)/belle_sdpLexer.h \ $(builddir)/belle_sdpParser.h : $(srcdir)/belle_sdp.g $(ANTLR) -make -Xmultithreaded -Xconversiontimeout 10000 -fo $(builddir) $< AM_CPPFLAGS=-I$(top_srcdir)/include -I$(top_srcdir)/src EXTRA_DIST= belle_sdp.g belle_sip_message.g CLEANFILES=$(nodist_libbellesip_generated_la_SOURCES) *.tokens .NOTPARALLEL: belle-sip-1.6.3/src/grammars/belle_sdp.g000066400000000000000000000605501313437522400201100ustar00rootroot00000000000000/* belle-sdp - SDP (RFC4566) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ grammar belle_sdp; options { language = C; output=AST; } @lexer::header { /* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #pragma GCC diagnostic ignored "-Wparentheses" #pragma GCC diagnostic ignored "-Wunused" #ifdef __clang__ #pragma GCC diagnostic ignored "-Wtautological-compare" #endif #ifndef __clang__ #pragma GCC diagnostic ignored "-Wunused-variable" #endif } @parser::header { /* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #pragma GCC diagnostic ignored "-Wparentheses" #pragma GCC diagnostic ignored "-Wunused" #ifndef __clang__ #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wunused-function" #endif } @rulecatch { if (HASEXCEPTION()) { // This is ugly. We set the exception type to ANTLR3_RECOGNITION_EXCEPTION so we can always // catch them. //PREPORTERROR(); EXCEPTION->type = ANTLR3_RECOGNITION_EXCEPTION; } } @includes { #include "belle-sip/defs.h" #include "belle-sip/types.h" #include "belle-sip/belle-sdp.h" #include "parserutils.h" } session_description returns [belle_sdp_session_description_t* ret] scope { belle_sdp_session_description_t* current; } @init {$session_description::current = belle_sdp_session_description_new(); $ret=$session_description::current; } : version CR LF origin {belle_sdp_session_description_set_origin($session_description::current,$origin.ret);}CR LF session_name CR LF (info CR LF)? (uri_field CR LF)? (email CR LF)* phone_field* (connection {belle_sdp_session_description_set_connection($session_description::current,$connection.ret);} CR LF)? (bandwidth {belle_sdp_session_description_add_bandwidth($session_description::current,$bandwidth.ret);} CR LF)* time_field CR LF (repeat_time CR LF)* (zone_adjustments CR LF)? (key_field CR LF)? (attribute {belle_sdp_session_description_add_attribute($session_description::current,$attribute.ret);} CR LF)* (media_description {belle_sdp_session_description_add_media_description($session_description::current,$media_description.ret);}) *; catch [ANTLR3_MISMATCHED_TOKEN_EXCEPTION] { ANTLR3_LOG_EXCEPTION(); belle_sip_object_unref($session_description::current); $ret=NULL; } version: {IS_TOKEN(v)}?alpha_num EQUAL v=DIGIT+ {belle_sdp_version_t* version =belle_sdp_version_new(); belle_sdp_version_set_version(version,atoi((const char*)$v.text->chars)); belle_sdp_session_description_set_version($session_description::current,version);}; // ;this memo describes version 0 origin returns [belle_sdp_origin_t* ret] scope { belle_sdp_origin_t* current; } @init {$origin::current = belle_sdp_origin_new(); $ret=$origin::current; } : {IS_TOKEN(o)}?alpha_num EQUAL username {belle_sdp_origin_set_username($origin::current,(const char*)$username.text->chars);} SPACE sess_id {if ($sess_id.text->chars) belle_sdp_origin_set_session_id($origin::current,strtoul((const char*)$sess_id.text->chars,NULL,10));} SPACE sess_version {if ($sess_version.text->chars) belle_sdp_origin_set_session_version($origin::current,strtoul((const char*)$sess_version.text->chars,NULL,10));} SPACE nettype {belle_sdp_origin_set_network_type($origin::current,(const char*)$nettype.text->chars);} SPACE addrtype {belle_sdp_origin_set_address_type($origin::current,(const char*)$addrtype.text->chars);} SPACE addr {belle_sdp_origin_set_address($origin::current,(const char*)$addr.text->chars);} ; catch [ANTLR3_MISMATCHED_TOKEN_EXCEPTION] { ANTLR3_LOG_EXCEPTION(); belle_sip_object_unref($origin::current); $ret=NULL; } session_name: {IS_TOKEN(s)}? alpha_num EQUAL text {belle_sdp_session_name_t* session_name =belle_sdp_session_name_new(); belle_sdp_session_name_set_value(session_name,(const char*)$text.text->chars); belle_sdp_session_description_set_session_name($session_description::current,session_name);}; info returns [belle_sdp_info_t* ret] scope { belle_sdp_info_t* current; } @init {$info::current = belle_sdp_info_new(); $ret=$info::current; } : {IS_TOKEN(i)}? alpha_num EQUAL info_value {belle_sdp_info_set_value($info::current,(const char*) $info_value.text->chars);} ; info_value options { greedy = false; }: ~(CR|LF)*; uri_field: {IS_TOKEN(u)}?alpha_num EQUAL uri ; email returns [belle_sdp_email_t* ret] scope { belle_sdp_email_t* current; } @init {$email::current = belle_sdp_email_new(); $ret=$email::current; } : {IS_TOKEN(e)}?alpha_num EQUAL email_address {belle_sdp_email_set_value($email::current,(const char*)$email_address.text->chars);}; phone_field: {IS_TOKEN(p)}?alpha_num EQUAL phone_number CR LF; connection returns [belle_sdp_connection_t* ret] scope { belle_sdp_connection_t* current; } @init {$connection::current = belle_sdp_connection_new(); $ret=$connection::current; } : {IS_TOKEN(c)}?alpha_num EQUAL nettype { belle_sdp_connection_set_network_type($connection::current,(const char*)$nettype.text->chars);} SPACE addrtype{ belle_sdp_connection_set_address_type($connection::current,(const char*)$addrtype.text->chars);} SPACE connection_address ; //;a connection field must be present //;in every media description or at the //;session-level catch [ANTLR3_MISMATCHED_TOKEN_EXCEPTION] { ANTLR3_LOG_EXCEPTION(); belle_sip_object_unref($ret); $ret=NULL; } bandwidth returns [belle_sdp_bandwidth_t* ret] scope { belle_sdp_bandwidth_t* current; } @init {$bandwidth::current = belle_sdp_bandwidth_new(); $ret=$bandwidth::current; } : {IS_TOKEN(b)}?alpha_num EQUAL bwtype {belle_sdp_bandwidth_set_type($bandwidth::current,(const char*)$bwtype.text->chars); } COLON bandwidth_value {belle_sdp_bandwidth_set_value($bandwidth::current,atoi((const char*)$bandwidth_value.text->chars));}; catch [ANTLR3_MISMATCHED_TOKEN_EXCEPTION] { ANTLR3_LOG_EXCEPTION(); belle_sip_object_unref($ret); $ret=NULL; } time_field: {IS_TOKEN(t)}?alpha_num EQUAL start_time SPACE stop_time {belle_sdp_time_description_t* time_description =belle_sdp_time_description_new(); belle_sdp_time_t* time_value =belle_sdp_time_new(); belle_sip_list_t* time_description_list; belle_sdp_time_set_start(time_value,atoi((const char*)$start_time.text->chars)); belle_sdp_time_set_stop(time_value,atoi((const char*)$stop_time.text->chars)); belle_sdp_time_description_set_time(time_description,time_value); time_description_list = belle_sip_list_append(NULL,time_description); belle_sdp_session_description_set_time_descriptions($session_description::current,time_description_list);}; repeat_time: {IS_TOKEN(r)}?alpha_num EQUAL repeat_interval (SPACE typed_time)+; zone_adjustments: {IS_TOKEN(z)}? alpha_num EQUAL sdp_time SPACE '-'? typed_time (SPACE sdp_time SPACE '-'? typed_time)*; key_field: {IS_TOKEN(k)}?alpha_num EQUAL key_value ; key_value options { greedy = false; }: (~(CR|LF))*; //key_type: {IS_TOKEN(prompt)}? alpha_num* /*'prompt'*/ | // {IS_TOKEN(clear)}? alpha_num* /*'clear'*/ COLON key_data | // {IS_TOKEN(base64)}? alpha_num* /*'base64*/ COLON key_data | // {IS_TOKEN(base64)}? alpha_num* /*'uri*/ COLON uri; // //key_data: email_safe; attribute returns [belle_sdp_attribute_t* ret] scope {int has_value;} @init {$ret=NULL;} : {IS_TOKEN(a)}?alpha_num EQUAL attribute_content{$ret=$attribute_content.ret;}; catch [ANTLR3_MISMATCHED_TOKEN_EXCEPTION] { ANTLR3_LOG_EXCEPTION(); if ($ret) belle_sip_object_unref($ret); $ret=NULL; } attribute_content returns [belle_sdp_attribute_t* ret] @init {$ret=NULL; } : attribute_name (COLON val=attribute_value {$ret=belle_sdp_attribute_create((const char*)$attribute_name.text->chars,(const char*)$attribute_value.text->chars);})? { if (!val.tree) $ret=belle_sdp_attribute_create((const char*)$attribute_name.text->chars,NULL); }; rtcp_xr_attribute returns [belle_sdp_rtcp_xr_attribute_t* ret] scope { belle_sdp_rtcp_xr_attribute_t* current; } @init { $rtcp_xr_attribute::current = belle_sdp_rtcp_xr_attribute_new();$ret = $rtcp_xr_attribute::current;} : {IS_TOKEN(a)}?alpha_num EQUAL {IS_TOKEN(rtcp-xr)}? attribute_name /*'rtcp-xr'*/ (COLON rtcp_xr_attribute_value (SPACE rtcp_xr_attribute_value)*)?; catch [ANTLR3_MISMATCHED_TOKEN_EXCEPTION] { ANTLR3_LOG_EXCEPTION(); belle_sip_object_unref($ret); $ret=NULL; } rtcp_xr_attribute_value : (pkt_loss_rle)=> pkt_loss_rle | (pkt_dup_rle)=> pkt_dup_rle | (pkt_rcpt_times)=> pkt_rcpt_times | (rcvr_rtt)=> rcvr_rtt | (stat_summary)=> stat_summary | (voip_metrics)=> voip_metrics; pkt_loss_rle : {IS_TOKEN(pkt-loss-rle)}? rtcp_xr_attribute_name /*'pkt-loss-rle'*/ (EQUAL rtcp_xr_max_size)? ; pkt_dup_rle : {IS_TOKEN(pkt-dup-rle)}? rtcp_xr_attribute_name /*'pkt-dup-rle'*/ (EQUAL rtcp_xr_max_size)? ; pkt_rcpt_times : {IS_TOKEN(pkt-rcpt-times)}? rtcp_xr_attribute_name /*'pkt-rcpt-times'*/ (EQUAL rtcp_xr_max_size)? ; rcvr_rtt : {IS_TOKEN(rcvr-rtt)}? rtcp_xr_attribute_name /*'rcvr-rtt'*/ EQUAL {IS_TOKEN(all) || IS_TOKEN(sender)}? rtcp_xr_rcvr_rtt_mode (COLON val=rtcp_xr_max_size)? { belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_mode($rtcp_xr_attribute::current,(const char*)$rtcp_xr_rcvr_rtt_mode.text->chars); if (val.tree) belle_sdp_rtcp_xr_attribute_set_rcvr_rtt_max_size($rtcp_xr_attribute::current,atoi((const char*)$rtcp_xr_max_size.text->chars)); }; stat_summary : {IS_TOKEN(stat-summary)}? rtcp_xr_attribute_name /*'stat-summary'*/ { belle_sdp_rtcp_xr_attribute_set_stat_summary($rtcp_xr_attribute::current,1); } (EQUAL rtcp_xr_stat_summary_flag (COMMA rtcp_xr_stat_summary_flag)*)?; voip_metrics : {IS_TOKEN(voip-metrics)}? rtcp_xr_attribute_name /*'voip-metrics'*/ { belle_sdp_rtcp_xr_attribute_set_voip_metrics($rtcp_xr_attribute::current,1); }; rtcp_xr_stat_summary_flag : {IS_TOKEN(loss) || IS_TOKEN(dup) || IS_TOKEN(jitt) || IS_TOKEN(TTL) || IS_TOKEN(HL)}?rtcp_xr_stat_summary_flag_value { belle_sdp_rtcp_xr_attribute_add_stat_summary_flag($rtcp_xr_attribute::current,(const char*)$rtcp_xr_stat_summary_flag_value.text->chars); }; rtcp_xr_max_size : DIGIT+; rtcp_fb_attribute returns [belle_sdp_rtcp_fb_attribute_t* ret] scope { belle_sdp_rtcp_fb_attribute_t* current; } @init { $rtcp_fb_attribute::current = belle_sdp_rtcp_fb_attribute_new();$ret = $rtcp_fb_attribute::current;} : {IS_TOKEN(a)}?alpha_num EQUAL {IS_TOKEN(rtcp-fb)}? attribute_name /*'rtcp-fb'*/ COLON rtcp_fb_pt SPACE rtcp_fb_val; catch [ANTLR3_MISMATCHED_TOKEN_EXCEPTION] { ANTLR3_LOG_EXCEPTION(); belle_sip_object_unref($ret); $ret=NULL; } rtcp_fb_pt: STAR { belle_sdp_rtcp_fb_attribute_set_id($rtcp_fb_attribute::current,-1); } | integer { belle_sdp_rtcp_fb_attribute_set_id($rtcp_fb_attribute::current,atoi((const char*)$integer.text->chars)); }; rtcp_fb_val : (rtcp_fb_ack_val)=>rtcp_fb_ack_val | (rtcp_fb_nack_val)=>rtcp_fb_nack_val | (rtcp_fb_trr_int_val)=>rtcp_fb_trr_int_val | (rtcp_fb_ccm_val)=>rtcp_fb_ccm_val | (rtcp_fb_id_val)=>rtcp_fb_id_val; rtcp_fb_ack_val: {IS_TOKEN(ack)}? rtcp_fb_attribute_name /*'ack'*/ (SPACE rtcp_fb_ack_param)? { belle_sdp_rtcp_fb_attribute_set_type($rtcp_fb_attribute::current,BELLE_SDP_RTCP_FB_ACK); }; rtcp_fb_nack_val: {IS_TOKEN(nack)}? rtcp_fb_attribute_name /*'nack'*/ (SPACE rtcp_fb_nack_param)? { belle_sdp_rtcp_fb_attribute_set_type($rtcp_fb_attribute::current,BELLE_SDP_RTCP_FB_NACK); }; rtcp_fb_trr_int_val: {IS_TOKEN(trr-int)}? rtcp_fb_attribute_name /*'trr-int'*/ SPACE integer { belle_sdp_rtcp_fb_attribute_set_type($rtcp_fb_attribute::current,BELLE_SDP_RTCP_FB_TRR_INT); belle_sdp_rtcp_fb_attribute_set_trr_int($rtcp_fb_attribute::current,(uint16_t)atoi((const char*)$integer.text->chars)); }; rtcp_fb_ccm_val: {IS_TOKEN(ccm)}? rtcp_fb_attribute_name /*'ccm'*/ (SPACE rtcp_fb_ccm_param)? { /* TODO: rtcp_fb_ccm_param should be mandatory */ belle_sdp_rtcp_fb_attribute_set_type($rtcp_fb_attribute::current,BELLE_SDP_RTCP_FB_CCM); }; rtcp_fb_id_val: rtcp_fb_attribute_name (SPACE rtcp_fb_param)?; rtcp_fb_param: (rtcp_fb_app_param)=>rtcp_fb_app_param | (rtcp_fb_token_param)=>rtcp_fb_token_param; rtcp_fb_ack_param: (rtcp_fb_rpsi_param)=>rtcp_fb_rpsi_param | (rtcp_fb_app_param)=>rtcp_fb_app_param | (rtcp_fb_token_param)=>rtcp_fb_token_param; rtcp_fb_nack_param: (rtcp_fb_pli_param)=>rtcp_fb_pli_param | (rtcp_fb_sli_param)=>rtcp_fb_sli_param | (rtcp_fb_rpsi_param)=>rtcp_fb_rpsi_param | (rtcp_fb_app_param)=>rtcp_fb_app_param | (rtcp_fb_token_param)=>rtcp_fb_token_param; rtcp_fb_pli_param: {IS_TOKEN(pli)}? rtcp_fb_attribute_name /*'pli'*/ { belle_sdp_rtcp_fb_attribute_set_param($rtcp_fb_attribute::current,BELLE_SDP_RTCP_FB_PLI); }; rtcp_fb_sli_param: {IS_TOKEN(sli)}? rtcp_fb_attribute_name /*'sli'*/ { belle_sdp_rtcp_fb_attribute_set_param($rtcp_fb_attribute::current,BELLE_SDP_RTCP_FB_SLI); }; rtcp_fb_rpsi_param: {IS_TOKEN(rpsi)}? rtcp_fb_attribute_name /*'rpsi'*/ { belle_sdp_rtcp_fb_attribute_set_param($rtcp_fb_attribute::current,BELLE_SDP_RTCP_FB_RPSI); }; rtcp_fb_app_param: {IS_TOKEN(app)}? rtcp_fb_attribute_name /*'app'*/ (SPACE byte_string) { belle_sdp_rtcp_fb_attribute_set_param($rtcp_fb_attribute::current,BELLE_SDP_RTCP_FB_APP); }; rtcp_fb_ccm_param: (rtcp_fb_fir_param)=>rtcp_fb_fir_param | (rtcp_fb_tmmbr_param)=>rtcp_fb_tmmbr_param | (rtcp_fb_token_param)=>rtcp_fb_token_param; rtcp_fb_fir_param: {IS_TOKEN(fir)}? rtcp_fb_attribute_name /*'fir'*/ { belle_sdp_rtcp_fb_attribute_set_param($rtcp_fb_attribute::current,BELLE_SDP_RTCP_FB_FIR); }; rtcp_fb_tmmbr_param: {IS_TOKEN(tmmbr)}? rtcp_fb_attribute_name /*'tmmbr'*/ (SPACE rtcp_fb_tmmbr_smaxpr_param)? { belle_sdp_rtcp_fb_attribute_set_param($rtcp_fb_attribute::current,BELLE_SDP_RTCP_FB_TMMBR); }; rtcp_fb_tmmbr_smaxpr_param: {IS_TOKEN(smaxpr)}? rtcp_fb_attribute_name /*'smaxpr'*/ EQUAL val=rtcp_fb_tmmbr_smaxpr { if (val.tree) belle_sdp_rtcp_fb_attribute_set_smaxpr($rtcp_fb_attribute::current,atoi((const char*)$rtcp_fb_tmmbr_smaxpr.text->chars)); }; rtcp_fb_tmmbr_smaxpr : DIGIT+; rtcp_fb_token_param: rtcp_fb_attribute_name (SPACE byte_string)?; media_description returns [belle_sdp_media_description_t* ret] scope { belle_sdp_media_description_t* current; } @init {$media_description::current = belle_sdp_media_description_new(); $ret=$media_description::current; } : media CR LF {belle_sdp_media_description_set_media($media_description::current,$media.ret);} (info {belle_sdp_media_description_set_info($media_description::current,$info.ret);} CR LF)? (connection { belle_sdp_media_description_set_connection($media_description::current,$connection.ret);} CR LF)? (bandwidth {belle_sdp_media_description_add_bandwidth($media_description::current,$bandwidth.ret);} CR LF)* (key_field CR LF)? (attribute {if ($attribute.ret)belle_sdp_media_description_add_attribute($media_description::current,$attribute.ret);} CR LF)*; catch [ANTLR3_MISMATCHED_TOKEN_EXCEPTION] { ANTLR3_LOG_EXCEPTION(); belle_sip_object_unref($ret); $ret=NULL; } media returns [belle_sdp_media_t* ret] scope { belle_sdp_media_t* current; } @init {$media::current = belle_sdp_media_new(); $ret=$media::current; } : {IS_TOKEN(m)}?alpha_num EQUAL media_value {belle_sdp_media_set_media_type($media::current,(const char*)$media_value.text->chars);} SPACE port {belle_sdp_media_set_media_port($media::current,atoi((const char*)$port.text->chars));} (SLASH integer{belle_sdp_media_set_port_count($media::current,atoi((const char*)$integer.text->chars));})? SPACE proto {belle_sdp_media_set_protocol($media::current,(const char*)$proto.text->chars);} (SPACE fmt)+; catch [ANTLR3_MISMATCHED_TOKEN_EXCEPTION] { ANTLR3_LOG_EXCEPTION(); belle_sip_object_unref($ret); $ret=NULL; } media_value: alpha_num+; // ;typically "audio", "video", "application" // ;or "data" fmt scope { int is_number; } @init { $fmt::is_number=0;}: ((DIGIT+)=>(DIGIT+){$fmt::is_number=1;} | token ) {belle_sdp_media_set_media_formats($media::current ,belle_sip_list_append(belle_sdp_media_get_media_formats($media::current) ,(void*)($fmt::is_number?(void*)(intptr_t)atoi((const char*)$fmt.text->chars):$fmt.text->chars)));}; //;typically an RTP payload type for audio //;and video media proto options { greedy = false; }: ~(SPACE|CR|LF)*; //;typically "RTP/AVP" or "udp" for IP4 port: DIGIT+; // ;should in the range "1024" to "65535" inclusive // ;for UDP based media attribute_name: token; attribute_value options { greedy = false; }: ~(CR|LF)*; rtcp_xr_attribute_name: word; rtcp_xr_rcvr_rtt_mode: word; rtcp_xr_stat_summary_flag_value: word; rtcp_fb_attribute_name: word; sess_id: DIGIT+; // ;should be unique for this originating username/host sess_version: DIGIT+; //;0 is a new session connection_address: /*multicast_address |*/addr {belle_sdp_connection_set_address($connection::current,(const char*)$addr.text->chars);} multicast_part?; multicast_address: unicast_address '/' ttl; // (decimal_uchar DOT decimal_uchar DOT decimal_uchar DOT) decimal_uchar '/' ttl ( '/' integer )?; //;multicast addresses may be in the range //;224.0.0.0 to 239.255.255.255 ttl: decimal_uchar; start_time: DIGIT+ ; stop_time: DIGIT+ ; sdp_time: DIGIT+; // ;sufficient for 2 more centuries repeat_interval: typed_time; typed_time: DIGIT* fixed_len_time_unit?; fixed_len_time_unit: {IS_TOKEN(d)}? alpha_num | {IS_TOKEN(h)}? alpha_num | {IS_TOKEN(m)}? alpha_num | {IS_TOKEN(s)}? alpha_num; bwtype: alpha_num+; bandwidth_value: DIGIT+; username: email_safe; //;pretty wide definition, but doesn't include SPACE email_address options { greedy = false; }: ~(CR|LF)* ; //| email '(' email_safe ')' | //email_safe '<' email '>'; uri: text ;//defined in RFC1630 phone_number: phone;/*(phone '(') => (phone '(') email_safe ')' | (phone) => phone | email_safe LQUOTE phone RQUOTE;*/ phone: text;//'+' DIGIT*POS_DIGIT (SPACE | '-' | DIGIT)*; //;there must be a SPACE or hyphen between the //;international code and the rest of the number. nettype: alpha_num+;//'IN'; // ;list to be extended addrtype: alpha_num+ ; //'IP4' | 'IP6'; //;list to be extended addr: unicast_address ; multicast_part: (SLASH num+=integer { if (strcmp( belle_sdp_connection_get_address_type($connection::current),"IP6")==0) belle_sdp_connection_set_range($connection::current,atoi((const char*)$integer.text->chars)); else if ($num->count ==1) belle_sdp_connection_set_ttl($connection::current,atoi((const char*)$integer.text->chars)); else if ($num->count ==2) belle_sdp_connection_set_range($connection::current,atoi((const char*)$integer.text->chars)); })+; fqdn : ( domainlabel DOT )* toplabel DOT? ; domainlabel : alpha_num | (alpha_num ( alpha_num | DASH )* alpha_num) ; toplabel : alpha | (alpha ( alpha_num | DASH )* alpha_num) ; unicast_address : (alpha_num | DOT | COLON| DASH)*; /*might be better defined*/ /* ipv4_address |ipv6_address |fqdn*/ ipv4_address : decimal_uchar DOT decimal_uchar DOT decimal_uchar DOT decimal_uchar ; ipv6_address : (hexpart)=>hexpart ( COLON ipv4_address )? ; hexpart : hexseq | hexseq COLON COLON hexseq? | COLON COLON hexseq? ; hexseq : hex4 ( COLON hex4)* ; hex4 : hexdigit+; /* hexdigit hexdigit hexdigit ;*/ text : ~(CR|LF)* ; //default is to interpret this as IS0-10646 UTF8 //ISO 8859-1 requires a "a=charset:ISO-8859-1" //session-level attribute to be used byte_string options { greedy = false; }: (OCTET)* ; //any byte except NUL, CR or LF decimal_uchar: integer;// (d+=DIGIT+ {$d !=NULL && $d->count<=3}?) integer: DIGIT+; email_safe : ~(SPACE)*; token : (alpha_num | '!' | '#' | '$' |'&'| '%'| '\'' | '*' |'+' | DASH | DOT | '^' | '_' | '`' | '{' | '|' | '}' | '~')+; alpha_num: (alpha | DIGIT) ; hexdigit: (HEX_CHAR | DIGIT) ; word: (alpha | DASH)+; alpha: COMMON_CHAR | HEX_CHAR; DIGIT: ZERO | POS_DIGIT; fragment ZERO: '0'; fragment POS_DIGIT : '1'..'9'; //ALPHA: 'a'..'z'|'A'..'Z'; COMMON_CHAR : 'g'..'z' | 'G'..'Z' ; HEX_CHAR: 'a'..'f' |'A'..'F'; SPACE: ' '; //CRLF : CR LF { USER1 = (int)(ctx->pLexer->input->currentLine - ctx->pLexer->input->data);}; LQUOTE: '<'; RQUOTE: '>'; CR:'\r'; LF:'\n'; DOT: '.'; EQUAL: '='; COLON: ':'; SLASH: '/'; DASH: '-'; COMMA: ','; STAR: '*'; OCTET : .; belle-sip-1.6.3/src/grammars/belle_sip_message.g000066400000000000000000002440131313437522400216170ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ grammar belle_sip_message; options { language = C; } @parser::header { /* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #pragma GCC diagnostic ignored "-Wparentheses" #pragma GCC diagnostic ignored "-Wunused" #ifndef __clang__ #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wunused-function" #endif } @lexer::header { /* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #pragma GCC diagnostic ignored "-Wparentheses" #pragma GCC diagnostic ignored "-Wunused" } @includes { #include "belle-sip/defs.h" #include "belle-sip/types.h" #include "belle-sip/message.h" #include "belle-sip/http-message.h" #include "parserutils.h" BELLESIP_EXPORT void belle_sip_header_address_set_quoted_displayname(belle_sip_header_address_t* address,const char* value); } @rulecatch { if (HASEXCEPTION()) { // This is ugly. We set the exception type to ANTLR3_RECOGNITION_EXCEPTION so we can always // catch them. //PREPORTERROR(); EXCEPTION->type = ANTLR3_RECOGNITION_EXCEPTION; } } message returns [belle_sip_message_t* ret] scope { size_t message_length; } @init {$ret=NULL;} : message_raw[&($message::message_length)] {$ret=$message_raw.ret;}; message_raw [size_t* length] returns [belle_sip_message_t* ret] scope { size_t* message_length; } @init {$message_raw::message_length=length;$ret=NULL;} : common_request {$ret = $common_request.ret;} | common_response {$ret = $common_response.ret;} ; request returns [belle_sip_request_t* ret=NULL] :common_request {$ret=BELLE_SIP_REQUEST($common_request.ret);}; common_request returns [belle_sip_message_t* ret] scope { belle_sip_message_t* current; } @init {$ret=$common_request::current=NULL; } : ( ( ~(CRLF)* sip_version CRLF) => request_line {$ret=$common_request::current=BELLE_SIP_MESSAGE($request_line.ret);} | ( ~(CRLF)* http_version CRLF) => http_request_line{$ret=$common_request::current=BELLE_SIP_MESSAGE($http_request_line.ret);}) message_header[BELLE_SIP_MESSAGE($common_request::current)]+ last_crlf=CRLF {*($message_raw::message_length)=$last_crlf->user1;} /*message_body ?*/ ; request_line returns [belle_sip_request_t* ret=NULL] scope { belle_sip_request_t* current; } @init {$request_line::current = belle_sip_request_new(); $ret=$request_line::current; } : method {belle_sip_request_set_method($request_line::current,(const char*)($method.text->chars));} SP ( uri {belle_sip_request_set_uri($request_line::current,$uri.ret);} | generic_uri {belle_sip_request_set_absolute_uri($request_line::current,$generic_uri.ret);} ) SP sip_version CRLF ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($request_line::current); $ret=NULL; } sip_version : {IS_TOKEN(SIP/)}? generic_version; // 'SIP/' DIGIT '.' DIGIT; generic_version: alpha+ SLASH DIGIT DOT DIGIT; message_header [belle_sip_message_t* message] : (/*accept // | accept_encoding // | accept_language // | alert_info // | allow // | authentication_info // | authorization // | header_call_id {belle_sip_message_add_header(message,BELLE_SIP_HEADER($header_call_id.ret));}/* // | call_info // | header_contact {belle_sip_message_add_header(message,BELLE_SIP_HEADER($header_contact.ret));} // | content_disposition // | content_encoding // | content_language*/ // | header_content_length {belle_sip_message_add_header(message,BELLE_SIP_HEADER($header_content_length.ret));} // | header_content_type {belle_sip_message_add_header(message,BELLE_SIP_HEADER($header_content_type.ret));} // | header_cseq {belle_sip_message_add_header(message,BELLE_SIP_HEADER($header_cseq.ret));}/* // | date // | error_info // | expires*/ // | header_from {belle_sip_message_add_header(message,BELLE_SIP_HEADER($header_from.ret));}/* // | in_reply_to // | max_forwards // | mime_version // | min_expires // | organization // | priority // | proxy_authenticate // | proxy_authorization // | proxy_require*/ // | header_record_route {belle_sip_message_add_header(message,BELLE_SIP_HEADER($header_record_route.ret));}/* // | reply_to // | require // | retry_after*/ // | header_route {belle_sip_message_add_header(message,BELLE_SIP_HEADER($header_route.ret));}/* // | server // | subject // | supported // | timestamp*/ // | header_to {belle_sip_message_add_header(message,BELLE_SIP_HEADER($header_to.ret));}/* // | header_diversion {belle_sip_message_add_header(message,BELLE_SIP_HEADER($header_diversion.ret));}/* // | unsupported // | user_agent*/ // | header_via {belle_sip_message_add_header(message,BELLE_SIP_HEADER($header_via.ret));}/* // | warning // | www_authenticate*/ header_extension_base[(BELLE_SIP_OBJECT_IS_INSTANCE_OF($message,belle_http_request_t) ||BELLE_SIP_OBJECT_IS_INSTANCE_OF($message,belle_http_response_t)) ] { belle_sip_header_t* lheader = BELLE_SIP_HEADER($header_extension_base.ret); do { if (lheader == NULL) break; /*sanity check*/ belle_sip_message_add_header(message,lheader); } while((lheader=belle_sip_header_get_next(lheader)) != NULL); } ) CRLF ; /* invitem : 'INVITE' ; //INVITE in caps ackm : 'ACK'; //ACK in caps optionsm: 'OPTION'; //OPTIONS in caps byem : 'BYE' ; //BYE in caps cancelm : 'CANCEL' ; //CANCEL in caps registerm : 'REGISTER' ; //REGISTER in caps optionm : 'OPTION'; */ method : /* invitem | ackm | optionm | byem | cancelm | registerm |*/extension_method ; extension_method : token; common_response returns [belle_sip_message_t* ret=NULL] : ( (sip_version ~(CRLF)* CRLF) => status_line {$ret=BELLE_SIP_MESSAGE($status_line.ret);} | (http_version ~(CRLF)* CRLF) => http_status_line{$ret=BELLE_SIP_MESSAGE($http_status_line.ret);}) (message_header[BELLE_SIP_MESSAGE($ret)]+ last_crlf=CRLF {*($message_raw::message_length)=$last_crlf->user1;} /*message_body*/)? ; response returns [belle_sip_response_t* ret=NULL] : common_response {$ret=BELLE_SIP_RESPONSE($common_response.ret);} ; status_line returns [belle_sip_response_t* ret=NULL] scope { belle_sip_response_t* current; } @init {$ret = belle_sip_response_new(); } : sip_version SP status_code {belle_sip_response_set_status_code($ret,atoi((char*)$status_code.text->chars));} SP reason_phrase {belle_sip_response_set_reason_phrase($ret,(char*)$reason_phrase.text->chars);} CRLF ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref( $ret); $ret=NULL; } status_code : extension_code; extension_code : DIGIT DIGIT DIGIT; reason_phrase : ~(CRLF)*; //****************http**********************/ /*Request-Line = Method SP Request-URI SP HTTP-Version CRLF*/ http_request returns [belle_http_request_t* ret=NULL] :common_request {$ret=BELLE_HTTP_REQUEST($common_request.ret);}; http_request_line returns [belle_http_request_t *ret=NULL] scope { belle_http_request_t* current; } @init {$http_request_line::current = belle_http_request_new(); $ret=$http_request_line::current; } : method {belle_http_request_set_method($http_request_line::current,(const char*)($method.text->chars));} SP (generic_uri)=>generic_uri {belle_http_request_set_uri($http_request_line::current,$generic_uri.ret);} SP http_version CRLF; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($http_request_line::current); $ret=NULL; } http_version: {IS_TOKEN(HTTP/)}? generic_version; http_response returns [belle_http_response_t* ret=NULL] : common_response {$ret=BELLE_HTTP_RESPONSE($common_response.ret);} ; http_status_line returns [belle_http_response_t* ret] @init {$ret = belle_http_response_new(); } : http_version SP status_code {belle_http_response_set_status_code($ret,atoi((char*)$status_code.text->chars));} SP reason_phrase {belle_http_response_set_reason_phrase($ret,(char*)$reason_phrase.text->chars);} CRLF ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref( $ret); $ret=NULL; } /* absoluteURI = scheme ":" ( hier_part | opaque_part ) hier_part = ( net_path | abs_path ) [ "?" query ] net_path = "//" authority [ abs_path ] abs_path = "/" path_segments */ /*comma, semicolon, or question mark*/ opaque_part_for_from_to_contact_addr_spec : uric_no_slash_for_from_to_contact_addr_spec uric_for_from_to_contact_addr_spec*; opaque_part : uric_no_slash uric*; uric_no_slash_for_from_to_contact_addr_spec : unreserved | escaped | COLON | AT| AND | EQUAL | PLUS | DOLLARD; uric_no_slash :COMMA | SEMI | QMARK | uric_no_slash_for_from_to_contact_addr_spec ; scheme: alpha ( alphanum | PLUS | DASH | DOT )*; /*remove hiearachy part because complex to handle the :comma, semicolon, or question markexception see rfc3261 section 20.10 Contact*/ generic_uri_for_from_to_contact_addr_spec returns [belle_generic_uri_t* ret=NULL] scope { belle_generic_uri_t* current; } @init { $generic_uri_for_from_to_contact_addr_spec::current = $ret = belle_generic_uri_new(); } : scheme {belle_generic_uri_set_scheme($generic_uri_for_from_to_contact_addr_spec::current,(const char*)$scheme.text->chars);} COLON opaque_part_for_from_to_contact_addr_spec {belle_generic_uri_set_opaque_part($generic_uri_for_from_to_contact_addr_spec::current,(const char*)$opaque_part_for_from_to_contact_addr_spec.text->chars) ;} ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($generic_uri::current); $ret=NULL; } generic_uri returns [belle_generic_uri_t* ret=NULL] scope { belle_generic_uri_t* current; } @init { $generic_uri::current = $ret = belle_generic_uri_new(); } : hier_part[$generic_uri::current] | (scheme {belle_generic_uri_set_scheme($generic_uri::current,(const char*)$scheme.text->chars);} COLON (opaque_part {belle_generic_uri_set_opaque_part($generic_uri::current,(const char*)$opaque_part.text->chars) ;} | hier_part[$generic_uri::current]) ) ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($generic_uri::current); $ret=NULL; } hier_part[belle_generic_uri_t* uri] returns [belle_generic_uri_t* ret=NULL] : ( (SLASH SLASH path_segments[NULL])=>( SLASH SLASH path_segments[uri]) | (SLASH SLASH authority[NULL] (path_segments[NULL])?)=>( SLASH SLASH authority[uri] (path_segments[uri])?) | ( path_segments[uri]) ) (QMARK query { char* unescaped_query; unescaped_query=belle_sip_to_unescaped_string((const char *)$query.text->chars); belle_generic_uri_set_query(uri,(const char*)unescaped_query); belle_sip_free(unescaped_query); }) ?; path_segments[belle_generic_uri_t* uri] : SLASH (segment ( SLASH segment )*) { char* unescaped_path; unescaped_path=belle_sip_to_unescaped_string((const char *)$path_segments.text->chars); belle_generic_uri_set_path(uri,(const char*)unescaped_path); belle_sip_free(unescaped_path); }; segment: pchar* ( SEMI param )*; param: pchar*; pchar: unreserved | escaped | COLON | AT | AND | EQUAL | PLUS | DOLLARD | COMMA; query: uric+; uric: reserved | unreserved | escaped; uric_for_from_to_contact_addr_spec: reserved_for_from_to_contact_addr_spec | unreserved | escaped; authority[belle_generic_uri_t* uri] : ((authority_userinfo[NULL]) =>authority_userinfo[uri] )? authority_hostport[uri] /*| authority_hostport[uri]*/ ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); } authority_userinfo[belle_generic_uri_t* uri] scope { belle_generic_uri_t* current; } @init {$authority_userinfo::current=uri;} : authority_user ( COLON authority_password )? AT ; authority_user : ( unreserved | escaped | user_unreserved )+ { char* unescaped_username; unescaped_username=belle_sip_to_unescaped_string((const char *)$text->chars); belle_generic_uri_set_user($authority_userinfo::current,unescaped_username); belle_sip_free(unescaped_username); }; authority_password : ( unreserved | escaped | AND | EQUAL | PLUS | DOLLARD | COMMA )* { char* unescaped_userpasswd; const char* source = (const char*)$text->chars; if( source != NULL ){ unescaped_userpasswd=belle_sip_to_unescaped_string((const char *)source); belle_generic_uri_set_user_password($authority_userinfo::current,unescaped_userpasswd); belle_sip_free(unescaped_userpasswd); } }; authority_hostport[belle_generic_uri_t* uri] scope { belle_generic_uri_t* current; } @init {$authority_hostport::current=uri;} : host ( COLON port {belle_generic_uri_set_port($authority_hostport::current,$port.ret);})? {belle_generic_uri_set_host($authority_hostport::current,$host.ret);}; //****************http end**********************/ //****************SIP**********************/ generic_param [belle_sip_parameters_t* object] returns [belle_sip_param_pair_t* ret=NULL] scope{int is_value; char* gen_value_string;} @init { $generic_param::is_value=0; $generic_param::gen_value_string=NULL;} : token ( equal gen_value {$generic_param::gen_value_string=(char*)($gen_value.text->chars);} )? { if (object == NULL) { $ret=belle_sip_param_pair_new((const char*)($token.text->chars) ,$generic_param::gen_value_string); } else { belle_sip_parameters_set_parameter(object ,(const char*)($token.text->chars) ,$generic_param::gen_value_string); $ret=NULL; } }; gen_value : token | quoted_string; quoted_string options { greedy = false; } : DQUOTE (~(DQUOTE | BSLASH) | (BSLASH .))* DQUOTE ; /* accept_encoding : 'Accept-Encoding' HCOLON ( encoding (COMMA encoding)* ); encoding : codings (SEMI accept_param)*; codings : content_coding | '*'; content_coding : token; accept_language : 'Accept-Language' HCOLON ( language (COMMA language)* )?; language : language_range (SEMI accept_param)*; language_range : ( ( alpha alpha alpha alpha alpha alpha alpha alpha ( '-' alpha alpha alpha alpha alpha alpha alpha alpha )* ) | '*' ); alert_info : 'Alert-Info' HCOLON alert_param (COMMA alert_param)*; alert_param : LAQUOT absoluteURI RAQUOT ( SEMI generic_param )*; absoluteURI : token ':' token; */ header_allow returns [belle_sip_header_allow_t* ret] scope { belle_sip_header_allow_t* current; } @init {$header_allow::current = belle_sip_header_allow_new(); $ret=$header_allow::current; } : {IS_TOKEN(Allow)}? token /*'Allow'*/ hcolon methods {belle_sip_header_allow_set_method($header_allow::current,(const char*)($methods.text->chars));} ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_allow::current); $ret=NULL; } methods : /*LWS?*/ method (comma method)*; authorization_token: {IS_TOKEN(Authorization)}? token; digest_token: {IS_TOKEN(Digest)}? token; header_authorization returns [belle_sip_header_authorization_t* ret] scope { belle_sip_header_authorization_t* current; } @init {$header_authorization::current = belle_sip_header_authorization_new(); $ret=$header_authorization::current; } : authorization_token /*'Authorization'*/ hcolon credentials[$header_authorization::current]; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_authorization::current); $ret=NULL; } credentials [belle_sip_header_authorization_t* header_authorization_base] : (digest_token /*'Digest'*/ {belle_sip_header_authorization_set_scheme(header_authorization_base,"Digest");} lws digest_response[header_authorization_base]) | other_response[header_authorization_base]; digest_response [belle_sip_header_authorization_t* header_authorization_base] : dig_resp[header_authorization_base] (comma dig_resp[header_authorization_base])*; dig_resp [belle_sip_header_authorization_t* header_authorization_base] : username { belle_sip_header_authorization_set_username(header_authorization_base,$username.ret); belle_sip_free($username.ret); } | realm { belle_sip_header_authorization_set_realm(header_authorization_base,(char*)$realm.ret); belle_sip_free($realm.ret); } | nonce { belle_sip_header_authorization_set_nonce(header_authorization_base,(char*)$nonce.ret); belle_sip_free($nonce.ret); } | digest_uri[header_authorization_base] | dresponse { belle_sip_header_authorization_set_response(header_authorization_base,(char*)$dresponse.ret); belle_sip_free($dresponse.ret); } | algorithm { belle_sip_header_authorization_set_algorithm(header_authorization_base,(char*)$algorithm.ret); } | cnonce{ belle_sip_header_authorization_set_cnonce(header_authorization_base,(char*)$cnonce.ret); belle_sip_free($cnonce.ret); } | opaque { belle_sip_header_authorization_set_opaque(header_authorization_base,(char*)$opaque.ret); belle_sip_free($opaque.ret); } | message_qop{ belle_sip_header_authorization_set_qop(header_authorization_base,$message_qop.ret); } | nonce_count{ belle_sip_header_authorization_set_nonce_count(header_authorization_base,atoi((char*)$nonce_count.ret)); } | auth_param[header_authorization_base] ; username_token: {IS_TOKEN(username)}? token; username returns [char* ret=NULL] : username_token /*'username'*/ equal username_value { $ret = _belle_sip_str_dup_and_unquote_string((char*)$username_value.text->chars); }; username_value : quoted_string; uri_token: {IS_TOKEN(uri)}? token; digest_uri [belle_sip_header_authorization_t* header_authorization_base] : uri_token /*'uri'*/ equal DQUOTE uri DQUOTE {belle_sip_header_authorization_set_uri(header_authorization_base,$uri.ret); }; /* digest_uri_value : rquest_uri ; rquest_uri : uri; */ // Equal to request-uri as specified by HTTP/1.1 message_qop returns [const char* ret=NULL] : {IS_TOKEN(qop)}? token/*'qop'*/ equal qop_value {$ret = (const char*)$qop_value.text->chars;}; qop_value : token; cnonce returns [char* ret=NULL] : {IS_TOKEN(cnonce)}? token /*'cnonce'*/ equal cnonce_value { $ret = _belle_sip_str_dup_and_unquote_string((char*)$cnonce_value.text->chars); }; cnonce_value : nonce_value; nonce_count returns [const char* ret=NULL] : {IS_TOKEN(nc)}? token /*'nc'*/ equal nc_value {$ret=(char*)$nc_value.text->chars;}; nc_value : huit_lhex; dresponse returns [char* ret=NULL] : {IS_TOKEN(response)}? token /*'response'*/ equal request_digest{ $ret = _belle_sip_str_dup_and_unquote_string((char*)$request_digest.text->chars); }; request_digest : quoted_string ;/*sp_laquot_sp huit_lhex huit_lhex huit_lhex huit_lhex sp_raquot_sp; */ huit_lhex : hexdigit+; auth_param [belle_sip_header_authorization_t* header_authorization_base] : auth_param_name equal auth_param_value {belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(header_authorization_base) ,(char*)$auth_param_name.text->chars ,(char*)$auth_param_value.text->chars); } ; auth_param_value : token | quoted_string ; auth_param_name : token; other_response [belle_sip_header_authorization_t* header_authorization_base] : auth_scheme {belle_sip_header_authorization_set_scheme(header_authorization_base,(const char*)$auth_scheme.text->chars);} lws auth_param[header_authorization_base] (comma auth_param[header_authorization_base])*; auth_scheme : token; /* authentication_info : 'Authentication-Info' HCOLON ainfo (COMMA ainfo)*; ainfo : nextnonce | message_qop | response_auth | cnonce | nonce_count; nextnonce : 'nextnonce' EQUAL nonce_value; */ nonce_value : quoted_string; /* response_auth : 'rspauth' EQUAL response_digest; response_digest : LDQUOT hexdigit* RDQUOT; */ /*callid header*/ call_id_token: {IS_HEADER_NAMED(Call-ID,i)}? token; header_call_id returns [belle_sip_header_call_id_t* ret=NULL] scope { belle_sip_header_call_id_t* current; } @init {$header_call_id::current = belle_sip_header_call_id_new(); $ret=$header_call_id::current; } : call_id_token /*( 'Call-ID' | 'i' )*/ hcolon call_id{belle_sip_header_call_id_set_call_id($header_call_id::current,(const char*) $call_id.text->chars); }; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_call_id::current); $ret=NULL; } call_id : word ( AT word )? ; /* call_info : 'Call-Info' HCOLON info (COMMA info)*; info : LAQUOT absoluteURI RAQUOT ( SEMI info_param)*; info_param : ( 'purpose' EQUAL ( 'icon' | 'info' | 'card' | token ) ) | generic_param; */ /* contact header */ contact_token: {IS_HEADER_NAMED(Contact,m)}? token; header_contact returns [belle_sip_header_contact_t* ret] scope { belle_sip_header_contact_t* current; belle_sip_header_contact_t* first; } @init { $header_contact::current =NULL; $header_contact::first =NULL; $ret=NULL; } : (contact_token /*'Contact'*/ /*| 'm'*/ ) sp_tab_colon ( (lws? STAR) { $header_contact::current = belle_sip_header_contact_new(); belle_sip_header_contact_set_wildcard($header_contact::current,1);} | (contact_param ( COMMA contact_param)*)) {$ret = $header_contact::first; }; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); $ret = $header_contact::first; if ($ret) belle_sip_object_unref($ret); $ret=NULL; } contact_param scope { belle_sip_header_contact_t* prev;} @init { if ($header_contact::current == NULL) { $header_contact::current = belle_sip_header_contact_new(); $header_contact::first = $header_contact::current; $contact_param::prev=NULL; } else { $contact_param::prev=$header_contact::current; belle_sip_header_set_next(BELLE_SIP_HEADER($header_contact::current),(belle_sip_header_t*)belle_sip_header_contact_new()); $header_contact::current = (belle_sip_header_contact_t*)belle_sip_header_get_next(BELLE_SIP_HEADER($header_contact::current)); } } : ( name_addr[BELLE_SIP_HEADER_ADDRESS($header_contact::current)] | paramless_addr_spec[BELLE_SIP_HEADER_ADDRESS($header_contact::current)]) (SEMI lws? contact_params lws?)*; header_address returns [belle_sip_header_address_t* ret] @init { $ret=NULL; } : header_address_base[belle_sip_header_address_new()] {$ret=$header_address_base.ret;}; header_address_base[belle_sip_header_address_t* obj] returns [belle_sip_header_address_t* ret] @init { $ret=obj; } : ( name_addr_with_generic_uri[BELLE_SIP_HEADER_ADDRESS($ret)] | addr_spec_with_generic_uri[BELLE_SIP_HEADER_ADDRESS($ret)]) { if (!belle_sip_header_address_get_uri($ret) && !belle_sip_header_address_get_absolute_uri(($ret))) { belle_sip_object_unref($ret); $ret=NULL; } }; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_debug("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); if ($ret) belle_sip_object_unref($ret); $ret=NULL; } fast_header_address returns [belle_sip_header_address_t* ret] @init { $ret=belle_sip_header_address_new(); } : ( fast_name_addr_with_generic_uri[BELLE_SIP_HEADER_ADDRESS($ret)] | fast_addr_spec_with_generic_uri[BELLE_SIP_HEADER_ADDRESS($ret)]) { if (!belle_sip_header_address_get_uri($ret) && !belle_sip_header_address_get_absolute_uri(($ret))) { belle_sip_object_unref($ret); $ret=NULL; } }; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_debug("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); if ($ret) belle_sip_object_unref($ret); $ret=NULL; } name_addr[belle_sip_header_address_t* object] : (lws? display_name[object])? sp_laquot addr_spec[object] raquot_sp; name_addr_with_generic_uri[belle_sip_header_address_t* object] : (lws? display_name[object])? sp_laquot addr_spec_with_generic_uri[object] raquot_sp; fast_name_addr_with_generic_uri[belle_sip_header_address_t* object] : (lws? display_name[object])? sp_laquot fast_addr_spec_with_generic_uri[object] raquot_sp; addr_spec[belle_sip_header_address_t* object] : lws? uri {belle_sip_header_address_set_uri(object,$uri.ret);} lws?;//| absoluteURI; addr_spec_with_generic_uri[belle_sip_header_address_t* object] : lws? ( uri {belle_sip_header_address_set_uri(object,$uri.ret);} | generic_uri { if ( strcasecmp(belle_generic_uri_get_scheme($generic_uri.ret),"sip") != 0 && strcasecmp(belle_generic_uri_get_scheme($generic_uri.ret),"sips") != 0 ) { belle_sip_header_address_set_absolute_uri(object,$generic_uri.ret); } else { belle_sip_message("Cannot parse a sip/sips uri as a generic uri"); belle_sip_object_unref($generic_uri.ret); }} ) lws?; fast_addr_spec_with_generic_uri[belle_sip_header_address_t* object] : lws? ( fast_uri {belle_sip_header_address_set_uri(object,$fast_uri.ret);} | generic_uri { if ( strcasecmp(belle_generic_uri_get_scheme($generic_uri.ret),"sip") != 0 && strcasecmp(belle_generic_uri_get_scheme($generic_uri.ret),"sips") != 0 ) { belle_sip_header_address_set_absolute_uri(object,$generic_uri.ret); } else { belle_sip_message("Cannot parse a sip/sips uri as a generic uri"); belle_sip_object_unref($generic_uri.ret); }} ) lws?; paramless_addr_spec[belle_sip_header_address_t* object] : lws? paramless_uri {belle_sip_header_address_set_uri(object,$paramless_uri.ret);} lws? ;//| absoluteURI; paramless_addr_spec_with_generic_uri[belle_sip_header_address_t* object] : lws? ( paramless_uri {belle_sip_header_address_set_uri(object,$paramless_uri.ret);} | generic_uri_for_from_to_contact_addr_spec{belle_sip_header_address_set_absolute_uri(object,$generic_uri_for_from_to_contact_addr_spec.ret);} ) lws? ; display_name_tokens :token (lws token)* ; display_name[belle_sip_header_address_t* object] : display_name_tokens {belle_sip_header_address_set_displayname(object,(const char*)($display_name_tokens.text->chars));} | quoted_string { char* unescaped_char = belle_sip_string_to_backslash_less_unescaped_string((const char*)($quoted_string.text->chars)); belle_sip_header_address_set_quoted_displayname(object,(const char*)unescaped_char); belle_sip_free(unescaped_char); } ; contact_params : /*c_p_q | c_p_expires |*/ contact_extension; /*c_p_q : 'q' EQUAL qvalue; c_p_expires : 'expires' EQUAL delta_seconds;*/ contact_extension : generic_param [BELLE_SIP_PARAMETERS($header_contact::current)]; delta_seconds : DIGIT+; /* content_disposition : 'Content-Disposition' HCOLON disp_type ( SEMI disp_param )*; disp_type : 'render' | 'session' | 'icon' | 'alert' | disp_extension_token; disp_param : handling_param | generic_param; handling_param : 'handling' EQUAL ( 'optional' | 'required' | other_handling ); other_handling : token; disp_extension_token : token; content_encoding : ( 'Content-Encoding' | 'e' ) HCOLON content_coding (COMMA content_coding)*; content_language : 'Content-Language' HCOLON language_tag (COMMA language_tag)*; language_tag : primary_tag ( '-' subtag )*; primary_tag : huit_alpha; subtag : huit_alpha; huit_alpha : alpha+; ; */ /*content_length_token : {strcmp("Content-Length",(const char*)(INPUT->toStringTT(INPUT,LT(1),LT(14)))->chars) == 0}? token;*/ content_length_token : {IS_HEADER_NAMED(Content-Length,l)}? token; header_content_length returns [belle_sip_header_content_length_t* ret] scope { belle_sip_header_content_length_t* current; } @init {$header_content_length::current = belle_sip_header_content_length_new(); $ret=$header_content_length::current; } : content_length_token /*( 'Content-Length' | 'l' )*/ hcolon content_length {belle_sip_header_content_length_set_content_length($header_content_length::current,atoi((const char*)$content_length.text->chars));}; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_content_length::current); $ret=NULL; } content_length:DIGIT+; content_type_token : {IS_HEADER_NAMED(Content-Type,c)}? token; header_content_type returns [belle_sip_header_content_type_t* ret=NULL] scope { belle_sip_header_content_type_t* current;} @init { $header_content_type::current = belle_sip_header_content_type_new();$ret=$header_content_type::current; } : content_type_token/* ( 'Content-Type' | 'c' )*/ hcolon media_type; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_content_type::current); $ret=NULL; } media_type : m_type {belle_sip_header_content_type_set_type($header_content_type::current,(const char*)$m_type.text->chars);} slash m_subtype {belle_sip_header_content_type_set_subtype($header_content_type::current,(const char*)$m_subtype.text->chars);} (semi type_param)? (semi generic_param [BELLE_SIP_PARAMETERS($header_content_type::current)])*; m_type : token; type_param: {IS_TOKEN(type)}? token equal type_param_value; type_param_value : m_type slash m_subtype {belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS($header_content_type::current) ,"type" ,(const char*)$type_param_value.text->chars);}; /* discrete_type | composite_type; discrete_type : 'text' | 'image' | 'audio' | 'video' | 'application' | extension_token; composite_type : 'message' | 'multipart' | extension_token; extension_token : ietf_token | x_token; ietf_token : token; x_token : 'x-' token;*/ m_subtype : token ;/* extension_token | iana_token; iana_token : token; m_parameter : m_attribute EQUAL m_value; m_attribute : token; m_value : token | quoted_string; */ cseq_token : {IS_TOKEN(CSeq)}? token; header_cseq returns [belle_sip_header_cseq_t* ret] scope { belle_sip_header_cseq_t* current; } @init { $header_cseq::current = belle_sip_header_cseq_new();$ret = $header_cseq::current; } : cseq_token hcolon seq_number {belle_sip_header_cseq_set_seq_number($header_cseq::current,atoi((const char*)$seq_number.text->chars));} lws method {belle_sip_header_cseq_set_method($header_cseq::current,(const char*)$method.text->chars);} ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_cseq::current); $ret=NULL; } seq_number:DIGIT+; /*Date header*/ date_token: {IS_TOKEN(Date)}? token; header_date returns [belle_sip_header_date_t* ret] scope { belle_sip_header_date_t* current; } @init {$header_date::current = belle_sip_header_date_new(); $ret=$header_date::current; } : date_token /*( 'Date' )*/ hcolon sip_date{belle_sip_header_date_set_date($header_date::current,(const char*) $sip_date.text->chars); }; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_date::current); $ret=NULL; } date : sip_date; sip_date : /*wkday*/alpha alpha alpha COMMA SP /*date1*/DIGIT DIGIT SP alpha alpha alpha SP DIGIT DIGIT DIGIT DIGIT SP /*time*/DIGIT DIGIT COLON DIGIT DIGIT COLON DIGIT DIGIT SP /*GMTT*/alpha alpha alpha; /* // rfc1123-date //rfc1123-date = wkday "," SP date1 SP time SP "GMT" //date1 = 2DIGIT SP month SP 4DIGIT // ; day month year (e.g., 02 Jun 1982) //time = 2DIGIT ":" 2DIGIT ":" 2DIGIT // ; 00:00:00 - 23:59:59 //wkday = "Mon" / "Tue" / "Wed" // / "Thu" / "Fri" / "Sat" / "Sun" //month = "Jan" / "Feb" / "Mar" / "Apr" // / "May" / "Jun" / "Jul" / "Aug" // / "Sep" / "Oct" / "Nov" / "Dec" error_info : 'Error-Info' HCOLON error_uri (COMMA error_uri)*; error_uri : LAQUOT absoluteURI RAQUOT ( SEMI generic_param )*; */ header_expires returns [belle_sip_header_expires_t* ret] scope { belle_sip_header_expires_t* current; } @init { $header_expires::current = belle_sip_header_expires_new();$ret = $header_expires::current; } : {IS_TOKEN(Expires)}? token /*'Expires'*/ hcolon delta_seconds {belle_sip_header_expires_set_expires($header_expires::current,atoi((const char *)$delta_seconds.text->chars));}; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_expires::current); $ret=NULL; } from_token: {IS_HEADER_NAMED(From,f)}? token; header_from returns [belle_sip_header_from_t* ret] scope { belle_sip_header_from_t* current; } @init { $header_from::current = belle_sip_header_from_new();$ret = $header_from::current; } : from_token/* ( 'From' | 'f' )*/ sp_tab_colon from_spec ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_from::current); $ret=NULL; } from_spec : ( name_addr_with_generic_uri[BELLE_SIP_HEADER_ADDRESS($header_from::current)] | paramless_addr_spec_with_generic_uri[BELLE_SIP_HEADER_ADDRESS($header_from::current)] ) ( SEMI lws? from_param lws?)*; from_param : /*tag_param |*/ generic_param [BELLE_SIP_PARAMETERS($header_from::current)]; /* tag_param : 'tag' EQUAL token; */ /* in_reply_to : 'In-Reply-To' HCOLON callid (COMMA callid); */ header_max_forwards returns [belle_sip_header_max_forwards_t* ret] scope { belle_sip_header_max_forwards_t* current; } @init { $header_max_forwards::current = belle_sip_header_max_forwards_new();$ret = $header_max_forwards::current; } : {IS_TOKEN(Max-Forwards)}? token /*'Max-Forwards'*/ hcolon max_forwards {belle_sip_header_max_forwards_set_max_forwards($header_max_forwards::current,atoi((const char*)$max_forwards.text->chars));}; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_max_forwards::current); $ret=NULL; } max_forwards:DIGIT+; /* mime_version : 'MIME-Version' HCOLON DIGIT+ '.' DIGIT+; min_expires : 'Min-Expires' HCOLON delta_seconds; organization : 'Organization' HCOLON (text_utf8_trim)?; text_utf8_trim : ; priority : 'Priority' HCOLON priority_value; priority_value : 'emergency' | 'urgent' | 'normal' | 'non-urgent' | other_priority; other_priority : token; */ header_proxy_authenticate returns [belle_sip_header_proxy_authenticate_t* ret] scope { belle_sip_header_proxy_authenticate_t* current; } @init { $header_proxy_authenticate::current = belle_sip_header_proxy_authenticate_new();$ret = $header_proxy_authenticate::current; } : {IS_TOKEN(Proxy-Authenticate)}? token /*'Proxy-Authenticate'*/ hcolon challenge[BELLE_SIP_HEADER_WWW_AUTHENTICATE($header_proxy_authenticate::current)]; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_proxy_authenticate::current); $ret=NULL; } challenge [belle_sip_header_www_authenticate_t* www_authenticate] : ({IS_TOKEN(Digest)}? token /*'Digest'*/ {belle_sip_header_www_authenticate_set_scheme(www_authenticate,"Digest");} lws digest_cln[www_authenticate] (comma digest_cln[www_authenticate])*) | other_challenge [www_authenticate]; other_challenge [belle_sip_header_www_authenticate_t* www_authenticate] : auth_scheme {belle_sip_header_www_authenticate_set_scheme(www_authenticate,(char*)$auth_scheme.text->chars);} lws auth_param[(belle_sip_header_authorization_t*)www_authenticate] (comma auth_param[(belle_sip_header_authorization_t*)www_authenticate])*; digest_cln [belle_sip_header_www_authenticate_t* www_authenticate] : realm {belle_sip_header_www_authenticate_set_realm(www_authenticate,(char*)$realm.ret); belle_sip_free($realm.ret);} | nonce {belle_sip_header_www_authenticate_set_nonce(www_authenticate,(char*)$nonce.ret); belle_sip_free($nonce.ret);} | algorithm {belle_sip_header_www_authenticate_set_algorithm(www_authenticate,$algorithm.ret);} | opaque {belle_sip_header_www_authenticate_set_opaque(www_authenticate,$opaque.ret); belle_sip_free($opaque.ret);} | qop_opts {belle_sip_header_www_authenticate_set_qop(www_authenticate,$qop_opts.ret); /*belle_sip_free($qop_opts.ret);*/} | domain {belle_sip_header_www_authenticate_set_domain(www_authenticate,$domain.ret); belle_sip_free($domain.ret);} | stale { if (strcmp("true",$stale.ret)==0) { belle_sip_header_www_authenticate_set_stale(www_authenticate,1); } } | auth_param[(belle_sip_header_authorization_t*)www_authenticate]; /* the cast above is very BAD, but auth_param works on fields common to the two structures*/ realm returns [char* ret=NULL] : {IS_TOKEN(realm)}? token /*'realm'*/ equal realm_value { $ret = _belle_sip_str_dup_and_unquote_string((char*)$realm_value.text->chars); }; realm_value : quoted_string ; domain returns [char* ret=NULL] : {IS_TOKEN(domain)}? token /*'domain'*/ equal quoted_string { $ret = _belle_sip_str_dup_and_unquote_string((char*)$quoted_string.text->chars);}; /* LDQUOT uri ( SP+ uri )* RDQUOT; uri : absoluteURI | '/'.; */ nonce returns [char* ret=NULL] : {IS_TOKEN(nonce)}? token /*'nonce'*/ equal nonce_value{ $ret = _belle_sip_str_dup_and_unquote_string((char*)$nonce_value.text->chars); }; opaque returns [char* ret=NULL] : {IS_TOKEN(opaque)}? token /*'opaque'*/ equal quoted_string{ $ret = _belle_sip_str_dup_and_unquote_string((char*)$quoted_string.text->chars); }; stale returns [const char* ret=NULL] : {IS_TOKEN(stale)}? token /*'stale'*/ equal stale_value {$ret=(char*)$stale_value.text->chars;} /* ( 'true' | 'false' )*/; stale_value:token; algorithm returns [const char* ret=NULL] : {IS_TOKEN(algorithm)}? token /*'algorithm'*/ equal /* ( 'MD5' | 'MD5-sess' |*/ alg_value=token {$ret=(char*)$alg_value.text->chars;}/*)*/ ; qop_opts returns [belle_sip_list_t* ret=NULL] scope { belle_sip_list_t* list; } @init{$qop_opts::list=NULL;} : {IS_TOKEN(qop)}? token /*'qop'*/ equal /*ldquot*/DQUOTE qop_opts_value (COMMA qop_opts_value)* /*rdquot*/DQUOTE {$ret=$qop_opts::list;} ; qop_opts_value : lws? token lws? {$qop_opts::list=belle_sip_list_append($qop_opts::list,belle_sip_strdup((const char*)$token.text->chars));}; header_proxy_authorization returns [belle_sip_header_proxy_authorization_t* ret=NULL] scope { belle_sip_header_proxy_authorization_t* current; } @init { $header_proxy_authorization::current = belle_sip_header_proxy_authorization_new();$ret = $header_proxy_authorization::current; } : {IS_TOKEN(Proxy-Authorization)}? token /*'Proxy-Authorization'*/ hcolon credentials[(belle_sip_header_authorization_t*)$header_proxy_authorization::current]; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_proxy_authorization::current); $ret=NULL; } /* proxy_require : 'Proxy-Require' HCOLON option_tag (COMMA option_tag)*; option_tag : token; */ /*FIXME service-route = recorde-route = route, too many copy/past*/ service_route_token: {IS_TOKEN(Service-Route)}? token; header_service_route returns [belle_sip_header_service_route_t* ret=NULL] scope { belle_sip_header_service_route_t* current; belle_sip_header_service_route_t* first;} @init { $header_service_route::current = NULL;$header_service_route::first =NULL;} : service_route_token /*'Service-Route'*/ sp_tab_colon srv_route (COMMA srv_route)* {$ret = $header_service_route::first;}; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); $ret = $header_service_route::first; if ($ret) belle_sip_object_unref($ret); $ret=NULL; } srv_route scope { belle_sip_header_service_route_t* prev;} @init { if ($header_service_route::current == NULL) { $header_service_route::first = $header_service_route::current = belle_sip_header_service_route_new(); $srv_route::prev=NULL; } else { belle_sip_header_t* header = BELLE_SIP_HEADER($header_service_route::current); $srv_route::prev=$header_service_route::current; belle_sip_header_set_next(header,(belle_sip_header_t*)($header_service_route::current = belle_sip_header_service_route_new())); } } : name_addr[BELLE_SIP_HEADER_ADDRESS($header_service_route::current)] ( SEMI lws? sr_param lws?)*; sr_param : generic_param[BELLE_SIP_PARAMETERS($header_service_route::current)]; record_route_token: {IS_TOKEN(Record-Route)}? token; header_record_route returns [belle_sip_header_record_route_t* ret=NULL] scope { belle_sip_header_record_route_t* current; belle_sip_header_record_route_t* first;} @init { $header_record_route::current = NULL;$header_record_route::first =NULL;} : record_route_token /*'Record-Route'*/ sp_tab_colon rec_route ( COMMA rec_route)* {$ret = $header_record_route::first;}; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); $ret = $header_record_route::first; if ($ret) belle_sip_object_unref($ret); $ret=NULL; } rec_route scope { belle_sip_header_record_route_t* prev;} @init { if ($header_record_route::current == NULL) { $header_record_route::first = $header_record_route::current = belle_sip_header_record_route_new(); $rec_route::prev=NULL; } else { belle_sip_header_t* header = BELLE_SIP_HEADER($header_record_route::current); $rec_route::prev=$header_record_route::current; belle_sip_header_set_next(header,(belle_sip_header_t*)($header_record_route::current = belle_sip_header_record_route_new())); } } : name_addr[BELLE_SIP_HEADER_ADDRESS($header_record_route::current)] ( SEMI lws? rr_param lws? )*; rr_param : generic_param[BELLE_SIP_PARAMETERS($header_record_route::current)]; /* reply_to : 'Reply-To' HCOLON rplyto_spec; rplyto_spec : ( name_addr | addr_spec ) ( SEMI rplyto_param )*; rplyto_param : generic_param; require : 'Require' HCOLON option_tag (COMMA option_tag)*; retry_after : 'Retry-After' HCOLON delta_seconds comment? ( SEMI retry_param )*; */ comment : LPAREN . RPAREN; /* retry_param : ('duration' EQUAL delta_seconds) | generic_param; */ route_token: {IS_TOKEN(Route)}? token; header_route returns [belle_sip_header_route_t* ret=NULL] scope { belle_sip_header_route_t* current;belle_sip_header_route_t* first; } @init { $header_route::current = NULL; $header_route::first =NULL;} : route_token /*'Route'*/ sp_tab_colon route_param ( COMMA route_param)*{$ret = $header_route::first;}; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); $ret = $header_route::first; if ($ret) belle_sip_object_unref($ret); $ret=NULL; } route_param scope { belle_sip_header_route_t* prev;} @init { if ($header_route::current == NULL) { $header_route::first = $header_route::current = belle_sip_header_route_new(); $route_param::prev=NULL; } else { belle_sip_header_t* header = BELLE_SIP_HEADER($header_route::current); $route_param::prev=$header_route::current; belle_sip_header_set_next(header,(belle_sip_header_t*)($header_route::current = belle_sip_header_route_new())); } } : name_addr[BELLE_SIP_HEADER_ADDRESS($header_route::current)] ( SEMI lws? r_param lws?)*; r_param : generic_param[BELLE_SIP_PARAMETERS($header_route::current)]; /* server : 'Server' HCOLON server_val (lws server_val)*; */ /* subject : ( 'Subject' | 's' ) HCOLON (text_utf_huit)?; text_utf_huit : ; supported : ( 'Supported' | 'k' ) HCOLON (option_tag (COMMA option_tag)*)?; timestamp : 'Timestamp' HCOLON (DIGIT)+ ( '.' (DIGIT)* )? ( lws delay )?; delay : (DIGIT)* ( '.' (DIGIT)* )?; */ to_token: {IS_HEADER_NAMED(To,t)}? token; header_to returns [belle_sip_header_to_t* ret=NULL] scope { belle_sip_header_to_t* current; } @init { $header_to::current = belle_sip_header_to_new(); $ret = $header_to::current;} : to_token /*'To' ( 'To' | 't' )*/ sp_tab_colon to_spec; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_to::current); $ret=NULL; } to_spec : ( name_addr_with_generic_uri[BELLE_SIP_HEADER_ADDRESS($header_to::current)] | paramless_addr_spec_with_generic_uri[BELLE_SIP_HEADER_ADDRESS($header_to::current)] ) ( SEMI lws? to_param lws?)*; to_param : /*tag_param |*/ generic_param [BELLE_SIP_PARAMETERS($header_to::current)]; diversion_token: {IS_HEADER_NAMED(Diversion,d)}? token; header_diversion returns [belle_sip_header_diversion_t* ret=NULL] scope { belle_sip_header_diversion_t* current; } @init { $header_diversion::current = belle_sip_header_diversion_new(); $ret = $header_diversion::current;} : diversion_token /*'Diversion' ( 'Diversion' | 'd' )*/ sp_tab_colon diversion_spec; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_diversion::current); $ret=NULL; } diversion_spec : ( name_addr_with_generic_uri[BELLE_SIP_HEADER_ADDRESS($header_diversion::current)] | paramless_addr_spec_with_generic_uri[BELLE_SIP_HEADER_ADDRESS($header_diversion::current)] ) ( SEMI lws? diversion_param lws?)*; diversion_param : /*tag_param |*/ generic_param [BELLE_SIP_PARAMETERS($header_diversion::current)]; refer_to_token: {IS_TOKEN(Refer-To)}? token; header_refer_to returns [belle_sip_header_refer_to_t* ret=NULL] : refer_to_token /*'Refer-To'*/ sp_tab_colon refer_to_spec[BELLE_SIP_HEADER_ADDRESS(belle_sip_header_refer_to_new())] {$ret = BELLE_SIP_HEADER_REFER_TO($refer_to_spec.ret);}; referred_by_token: {IS_TOKEN(Referred-By)}? token; header_referred_by returns [belle_sip_header_referred_by_t* ret=NULL] : referred_by_token /*'Referred-By'*/ sp_tab_colon refer_to_spec[BELLE_SIP_HEADER_ADDRESS(belle_sip_header_referred_by_new())] {$ret = BELLE_SIP_HEADER_REFERRED_BY($refer_to_spec.ret);}; refer_to_spec [belle_sip_header_address_t* address] returns [belle_sip_header_address_t* ret] @init {$ret=address;} : (( name_addr[address] | paramless_addr_spec[address]) ( SEMI lws? generic_param [BELLE_SIP_PARAMETERS(address)] lws? )* ); catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref(address); $ret=NULL; } /* unsupported : 'Unsupported' HCOLON option_tag (COMMA option_tag)*; */ header_user_agent returns [belle_sip_header_user_agent_t* ret] scope { belle_sip_header_user_agent_t* current; } @init { $header_user_agent::current = belle_sip_header_user_agent_new();$ret = $header_user_agent::current;} : {IS_TOKEN(User-Agent)}? token /*'User-Agent'*/ hcolon server_val (lws server_val)*; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_user_agent::current); $ret=NULL; } server_val : word {belle_sip_header_user_agent_add_product($header_user_agent::current,(const char*)$word.text->chars); }; /*serval_item : product | comment ; product : token (SLASH product_version)?; product_version : token; */ via_token: {IS_HEADER_NAMED(Via,v)}? token; header_via returns [belle_sip_header_via_t* ret] scope { belle_sip_header_via_t* current; belle_sip_header_via_t* first; } @init { $header_via::current = NULL;$header_via::first =NULL;$ret = NULL;} : via_token/* ( 'via' | 'v' )*/ hcolon via_parm (comma via_parm)* {$ret = $header_via::first;} ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); $ret = $header_via::first; if ($ret) belle_sip_object_unref($ret); $ret=NULL; } via_parm scope { belle_sip_header_via_t* prev;} @init { if ($header_via::current == NULL) { $header_via::first = $header_via::current = belle_sip_header_via_new(); $via_parm::prev=NULL; } else { belle_sip_header_t* header; $via_parm::prev=$header_via::current; header = BELLE_SIP_HEADER($header_via::current); belle_sip_header_set_next(header,(belle_sip_header_t*)($header_via::current = belle_sip_header_via_new())); } } : sent_protocol lws sent_by ( semi via_params )*; via_params : /*via_ttl | via_maddr | via_branch | via_extension */ via_received[$header_via::current] | generic_param [BELLE_SIP_PARAMETERS($header_via::current)]; /*via_ttl : 'ttl' EQUAL ttl; via_maddr : 'maddr' EQUAL host;*/ via_received [belle_sip_header_via_t* object] : {IS_TOKEN(received)}? token EQUAL via_address {belle_sip_header_via_set_received(object,(const char*)$via_address.text->chars);}; via_address: ipv4address | ipv6address; /* via_branch : 'branch' EQUAL token; via_extension : generic_param;*/ sent_protocol : (protocol_name slash protocol_version) {belle_sip_header_via_set_protocol($header_via::current,(const char*)$text->chars);} slash transport {belle_sip_header_via_set_transport($header_via::current,(const char*)$transport.text->chars);} ; protocol_name : /*'SIP' |*/ token; protocol_version : token; transport : /* 'UDP' | 'TCP' | 'TLS' | 'SCTP' | */ other_transport; other_transport : token; sent_by : host {belle_sip_header_via_set_host($header_via::current,$host.ret);} ( COLON port {belle_sip_header_via_set_port($header_via::current,$port.ret);} )? ; /* warning : 'Warning' HCOLON warning_value (COMMA warning_value)*; warning_value : warn_code SP warn_agent SP warn_text; warn_code : DIGIT DIGIT DIGIT; warn_agent : hostport | pseudonym; // the name or pseudonym of the server adding // the Warning header, for use in debugging warn_text : quoted_string; pseudonym : token; */ header_www_authenticate returns [belle_sip_header_www_authenticate_t* ret] scope { belle_sip_header_www_authenticate_t* current; } @init { $header_www_authenticate::current = belle_sip_header_www_authenticate_new();$ret = $header_www_authenticate::current; } : {IS_TOKEN(WWW-Authenticate)}? token /*'WWW-Authenticate'*/ hcolon challenge[$header_www_authenticate::current]; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_www_authenticate::current); $ret=NULL; } state_value: token ; header_subscription_state returns [belle_sip_header_subscription_state_t* ret] scope { belle_sip_header_subscription_state_t* current; } @init { $header_subscription_state::current = belle_sip_header_subscription_state_new();$ret = $header_subscription_state::current; } : {IS_TOKEN(Subscription-State)}? token /*"Subscription-State"*/ hcolon state_value {belle_sip_header_subscription_state_set_state($header_subscription_state::current,(const char*)$state_value.text->chars);} (semi generic_param [BELLE_SIP_PARAMETERS($header_subscription_state::current)])* ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_subscription_state::current); $ret=NULL; } header_event returns [belle_sip_header_event_t* ret] scope { belle_sip_header_event_t* current; } @init { $header_event::current = belle_sip_header_event_new();$ret = $header_event::current; } : {IS_TOKEN(Event)}? token /*"Event"*/ hcolon event_package {belle_sip_header_event_set_package_name($header_event::current,(const char*)$event_package.text->chars);} (semi generic_param [BELLE_SIP_PARAMETERS($header_event::current)])* ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_event::current); $ret=NULL; } event_package : token; /* Replaces = "Replaces" HCOLON replaces-values *(COMMA replaces-values) replaces-values = callid *( SEMI replaces-param ) callid = token [ "@" token ] replaces-param = to-tag | from-tag | extension-param to-tag = "to-tag" EQUAL ( UUID | "*" ) from-tag = "from-tag" EQUAL UUID extension-param = token [ EQUAL ( token | quoted-string ) ] */ header_replaces returns [belle_sip_header_replaces_t* ret] scope { belle_sip_header_replaces_t* current; } @init { $header_replaces::current = belle_sip_header_replaces_new();$ret = $header_replaces::current; } : {IS_TOKEN(Replaces)}? token /*"Replaces"*/ hcolon call_id {belle_sip_header_replaces_set_call_id($header_replaces::current,(const char*)$call_id.text->chars);} (semi generic_param [BELLE_SIP_PARAMETERS($header_replaces::current)])* ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_replaces::current); $ret=NULL; } //**********************************Privacy*******************************// header_p_preferred_identity returns [belle_sip_header_p_preferred_identity_t* ret] @init { $ret=NULL; } : {IS_TOKEN(P-Preferred-Identity)}? token /*"P-Preferred-Identity"*/ sp_tab_colon header_address_base[(belle_sip_header_address_t*)belle_sip_header_p_preferred_identity_new()] {$ret=(belle_sip_header_p_preferred_identity_t*)$header_address_base.ret;}; header_privacy returns [belle_sip_header_privacy_t* ret] scope { belle_sip_header_privacy_t* current; } @init { $header_privacy::current = belle_sip_header_privacy_new();$ret = $header_privacy::current;} : {IS_TOKEN(Privacy)}? token /*'Privacy'*/ hcolon privacy_val (semi privacy_val)*; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($ret); $ret=NULL; } privacy_val: token {belle_sip_header_privacy_add_privacy($header_privacy::current,(const char*)$token.text->chars);}; //**********************************Supported*******************************// header_supported returns [belle_sip_header_supported_t* ret] scope { belle_sip_header_supported_t* current; } @init { $header_supported::current = belle_sip_header_supported_new();$ret = $header_supported::current;} : {IS_TOKEN(Supported)}? token /*'Supported'*/ hcolon supported_val (comma supported_val)*; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($ret); $ret=NULL; } supported_val: token {belle_sip_header_supported_add_supported($header_supported::current,(const char*)$token.text->chars);}; //**********************************Content-Disposition*******************************// content_disposition_value: token ; header_content_disposition returns [belle_sip_header_content_disposition_t* ret] scope { belle_sip_header_content_disposition_t* current; } @init { $header_content_disposition::current = belle_sip_header_content_disposition_new();$ret = $header_content_disposition::current; } : {IS_TOKEN(Content-Disposition)}? token /*"Content-Disposition"*/ hcolon content_disposition_value {belle_sip_header_content_disposition_set_content_disposition($header_content_disposition::current,(const char*)$content_disposition_value.text->chars);} (semi generic_param [BELLE_SIP_PARAMETERS($header_content_disposition::current)])* ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_content_disposition::current); $ret=NULL; } //**********************************Accept*******************************// accept_token : {IS_TOKEN(Accept)}? token; header_accept returns [belle_sip_header_accept_t* ret=NULL] scope { belle_sip_header_accept_t* current; belle_sip_header_accept_t* first; } @init { $header_accept::current = NULL;$header_accept::first =NULL;$ret = NULL;} : accept_token/* ( 'Accept')*/ hcolon accept_param (comma accept_param)* {$ret = $header_accept::first;} ; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); $ret = $header_accept::first; if ($ret) belle_sip_object_unref($ret); $ret=NULL; } accept_param scope { belle_sip_header_accept_t* prev;} @init { if ($header_accept::current == NULL) { $header_accept::first = $header_accept::current = belle_sip_header_accept_new(); $accept_param::prev=NULL; } else { belle_sip_header_t* header; $accept_param::prev=$header_accept::current; header = BELLE_SIP_HEADER($header_accept::current); belle_sip_header_set_next(header,(belle_sip_header_t*)($header_accept::current = belle_sip_header_accept_new())); } } : accept_main_media_type {belle_sip_header_accept_set_type($header_accept::current,(const char*)$accept_main_media_type.text->chars);} slash accept_sub_media_type {belle_sip_header_accept_set_subtype($header_accept::current,(const char*)$accept_sub_media_type.text->chars);} (semi generic_param [BELLE_SIP_PARAMETERS($header_accept::current)])*; accept_main_media_type: token; accept_sub_media_type: token; //****************************Reason***********************************// /* Reason = "Reason" HCOLON reason-value *(COMMA reason-value) reason-value = protocol *(SEMI reason-params) protocol = "SIP" / "Q.850" / token reason-params = protocol-cause / reason-text / reason-extension protocol-cause = "cause" EQUAL cause cause = 1*DIGIT reason-text = "text" EQUAL quoted-string reason-extension = generic-param */ header_reason returns [belle_sip_header_reason_t* ret=NULL] scope { belle_sip_header_reason_t* current;belle_sip_header_reason_t* first; } @init { $header_reason::current = NULL; $header_reason::first =NULL;} : {IS_TOKEN(Reason)}? token /*'Reason'*/ hcolon header_reason_param ( comma header_reason_param)* {$ret = $header_reason::first; }; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); $ret = $header_reason::first; if ($ret) belle_sip_object_unref($ret); $ret=NULL; } header_reason_param scope { belle_sip_header_reason_t* prev;} @init { if ($header_reason::current == NULL) { $header_reason::first = $header_reason::current = belle_sip_header_reason_new(); $header_reason_param::prev=NULL; } else { belle_sip_header_t* header = BELLE_SIP_HEADER($header_reason::current); $header_reason_param::prev=$header_reason::current; belle_sip_header_set_next(header,(belle_sip_header_t*)($header_reason::current = belle_sip_header_reason_new())); } } :header_reason_protocol {belle_sip_header_reason_set_protocol($header_reason::current,(const char*)$header_reason_protocol.text->chars);} (semi generic_param [BELLE_SIP_PARAMETERS($header_reason::current)])*; header_reason_protocol : token; //*********************************************// /* AuthenticationInfo = "Authentication-Info" ":" auth-info auth-info = 1#(nextnonce | [ message-qop ] | [ response-auth ] | [ cnonce ] | [nonce-count] ) nextnonce = "nextnonce" "=" nonce-value response-auth = "rspauth" "=" response-digest response-digest = <"> *LHEX <"> */ header_authentication_info returns [belle_sip_header_authentication_info_t* ret=NULL] scope { belle_sip_header_authentication_info_t* current; } @init {$header_authentication_info::current = belle_sip_header_authentication_info_new(); $ret=$header_authentication_info::current; } : {IS_TOKEN( Authentication-Info)}? token /*' Authentication-Info'*/ hcolon auth_info ( comma auth_info)*; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($header_authentication_info::current); $ret=NULL; } auth_info : ( next_nonce { belle_sip_header_authentication_info_set_next_nonce($header_authentication_info::current,(char*)$next_nonce.ret); belle_sip_free($next_nonce.ret); } |cnonce{ belle_sip_header_authentication_info_set_cnonce($header_authentication_info::current,(char*)$cnonce.ret); belle_sip_free($cnonce.ret); } | authentication_info_message_qop{ belle_sip_header_authentication_info_set_qop($header_authentication_info::current,$authentication_info_message_qop.ret); } |nonce_count{ belle_sip_header_authentication_info_set_nonce_count($header_authentication_info::current,atoi((char*)$nonce_count.ret)); } |rspauth { belle_sip_header_authentication_info_set_rsp_auth($header_authentication_info::current,$rspauth.ret); belle_sip_free($rspauth.ret); }) ; qop_token : {IS_TOKEN(qop)}? token/*'qop'*/; /*as defined by 3261*/ authentication_info_message_qop returns [const char* ret=NULL] : qop_token equal token { $ret = (const char*)$token.text->chars;}; rspauth returns [char* ret=NULL] : {IS_TOKEN(rspauth)}? token /*'nonce'*/ equal quoted_string{ $ret = _belle_sip_str_dup_and_unquote_string((char*)$quoted_string.text->chars); }; next_nonce returns [char* ret=NULL] : {IS_TOKEN(nextnonce)}? token /*'nonce'*/ equal nonce_value{ $ret = _belle_sip_str_dup_and_unquote_string((char*)$nonce_value.text->chars); }; quoted_algorithm returns [char* ret=NULL] : {IS_TOKEN(nextnonce)}? token /*'nonce'*/ equal nonce_value{ $ret = _belle_sip_str_dup_and_unquote_string((char*)$nonce_value.text->chars); }; //**********************************************// header returns [belle_sip_header_t* ret=NULL] : header_extension_base[FALSE] {$ret=$header_extension_base.ret;}; //********************************************************************************************// header_extension_base[ANTLR3_BOOLEAN is_http] returns [belle_sip_header_t* ret] scope {int as_value;} @init {$header_extension_base::as_value=0;$ret=NULL;} : (header_name hcolon /*sp_tab_colon*/ /*because LWS can be in both colon or header_value*/ (header_value[(const char*)$header_name.text->chars,$is_http ]{$header_extension_base::as_value=1;$ret=$header_value.ret;})?) { if (!$ret && !$header_extension_base::as_value) { /*to handle value parsing error*/ /*special case: header without value*/ $ret=belle_sip_header_create((const char*)$header_name.text->chars,NULL); } }; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); if ($ret) belle_sip_object_unref($ret); $ret=NULL; } header_name : token; header_value[const char* name, ANTLR3_BOOLEAN is_http] returns [belle_sip_header_t* ret] options { greedy = false; } @init {$ret=NULL;} : (~(SP|CRLF) ((CRLF SP) | ~CRLF)* ) { if ($is_http) { $ret=belle_http_header_create($name,(const char*)$header_value.text->chars); }else { $ret=belle_sip_header_create($name,(const char*)$header_value.text->chars); } } ; message_body options { greedy = false; } : OCTET+; paramless_uri returns [belle_sip_uri_t* ret=NULL] scope { belle_sip_uri_t* current; } @init { $paramless_uri::current = belle_sip_uri_new(); } : sip_schema[$paramless_uri::current] ( (userinfo[$paramless_uri::current]) =>(userinfo[$paramless_uri::current] hostport[$paramless_uri::current]) | hostport[$paramless_uri::current]) headers[$paramless_uri::current]? {$ret = $paramless_uri::current;}; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($paramless_uri::current); $ret=NULL; } uri returns [belle_sip_uri_t* ret=NULL] scope { belle_sip_uri_t* current; } @init { $uri::current = belle_sip_uri_new(); } : sip_schema[$uri::current] ( ((userinfo[NULL])=>userinfo[$uri::current] hostport[$uri::current]) | hostport[$uri::current] ) uri_parameters[$uri::current]? headers[$uri::current]? {$ret = $uri::current;}; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($uri::current); $ret=NULL; } fast_uri returns [belle_sip_uri_t* ret=NULL] scope { belle_sip_uri_t* current; } @init { $fast_uri::current = belle_sip_uri_new(); } : sip_schema[$fast_uri::current] ( ((userinfo[NULL])=>userinfo[$fast_uri::current] fast_hostport[$fast_uri::current]) | fast_hostport[$fast_uri::current] ) uri_parameters[$fast_uri::current]? headers[$fast_uri::current]? {$ret = $fast_uri::current;}; catch [ANTLR3_RECOGNITION_EXCEPTION] { belle_sip_message("[\%s] reason [\%s]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message); belle_sip_object_unref($fast_uri::current); $ret=NULL; } sip_token: {IS_TOKEN(sip)}? token; sips_token: {IS_TOKEN(sips)}? token; sip_schema[belle_sip_uri_t* uri] : (sips_token {belle_sip_uri_set_secure(uri,1);} | sip_token) COLON ; userinfo[belle_sip_uri_t* uri] scope { belle_sip_uri_t* current; } @init {$userinfo::current=uri;} : user ( COLON password )? AT; user : ( unreserved | escaped | user_unreserved )+ { char* unescaped_username; unescaped_username=belle_sip_to_unescaped_string((const char *)$text->chars); belle_sip_uri_set_user($userinfo::current,unescaped_username); belle_sip_free(unescaped_username); }; password : ( unreserved | escaped |AND | EQUAL | PLUS | DOLLARD | COMMA )* { char* unescaped_userpasswd; const char* source = (const char*)$text->chars; if( source != NULL ){ unescaped_userpasswd=belle_sip_to_unescaped_string((const char *)source); belle_sip_uri_set_user_password($userinfo::current,unescaped_userpasswd); belle_sip_free(unescaped_userpasswd); } }; hostport[belle_sip_uri_t* uri] scope { belle_sip_uri_t* current; } @init {$hostport::current=uri;} : host ( COLON port {belle_sip_uri_set_port($hostport::current,$port.ret);})? {belle_sip_uri_set_host($hostport::current,$host.ret);}; fast_hostport[belle_sip_uri_t* uri] scope { belle_sip_uri_t* current; } @init {$fast_hostport::current=uri;} : fast_host ( COLON port {belle_sip_uri_set_port($fast_hostport::current,$port.ret);})? {belle_sip_uri_set_host($fast_hostport::current,$fast_host.ret);}; uri_parameters[belle_sip_uri_t* uri] scope { belle_sip_uri_t* current; } @init {$uri_parameters::current=uri;} : ( (semi uri_parameter)|(lws? SEMI) )+; /*allow semi only teven if not in the rfc*/ uri_parameter //all parameters are considered as other : other_param ; other_param : pname { char* unescaped_parameters = belle_sip_to_unescaped_string((const char *) $pname.text->chars); belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS($uri_parameters::current) ,unescaped_parameters ,NULL); belle_sip_free(unescaped_parameters); } | (pname EQUAL pvalue) { char* unescaped_pname = belle_sip_to_unescaped_string((const char *) $pname.text->chars); char* unescaped_pvalue = belle_sip_to_unescaped_string((const char *) $pvalue.text->chars); belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS($uri_parameters::current) ,unescaped_pname ,unescaped_pvalue); belle_sip_free(unescaped_pname); belle_sip_free(unescaped_pvalue); } ; pname : paramchar+; pvalue : paramchar+; paramchar : (param_unreserved)=>param_unreserved | unreserved | escaped; param_unreserved : LSBRAQUET | RSBRAQUET | SLASH | COLON | AND | PLUS | DOLLARD | DOT; headers[belle_sip_uri_t* uri] scope { belle_sip_uri_t* current; int is_hvalue; } @init {$headers::current=uri; $headers::is_hvalue=0;} : QMARK uri_header ( AND uri_header )* ; uri_header scope {int is_hvalue; } @init {$uri_header::is_hvalue=0;} : hname EQUAL (hvalue{$uri_header::is_hvalue = 1;})? { char* unescaped_hname = belle_sip_to_unescaped_string((const char *)$hname.text->chars); char* unescaped_hvalue = ($uri_header::is_hvalue)?belle_sip_to_unescaped_string((const char *)$hvalue.text->chars):NULL; belle_sip_uri_set_header($headers::current,unescaped_hname,unescaped_hvalue); belle_sip_free(unescaped_hname); if (unescaped_hvalue) belle_sip_free(unescaped_hvalue); }; hname : ( hnv_unreserved | unreserved | escaped )+; hvalue : ( hnv_unreserved | unreserved | escaped )+; /*hnv-unreserved = "[" / "]" / "/" / "?" / ":" / "+" / "$"*/ hnv_unreserved : LSBRAQUET | RSBRAQUET | SLASH | QMARK | COLON | PLUS | DOLLARD ; fast_host returns [const char* ret] scope { const char* current; } @init {$fast_host::current=$ret=NULL;} : (fast_hostname {$fast_host::current=(const char *)$fast_hostname.text->chars;} /*| ipv4address {$fast_host::current=(const char *)$ipv4address.text->chars;} because cannot discriminate quickly betwenn 192.168.0.1 and 192.168.0.com*/ | ipv6reference {$fast_host::current=(const char *)$ipv6reference.ret;}) {$ret=$fast_host::current;}; fast_hostname: alphanum ( alphanum | DASH | DOT )*; //*************************common tokens*******************************/ user_unreserved : AND | EQUAL | PLUS | DOLLARD | COMMA | SEMI | QMARK | SLASH; host returns [const char* ret] scope { const char* current; } @init {$host::current=$ret=NULL;} : (hostname {$host::current=(const char *)$hostname.text->chars;} | ipv4address {$host::current=(const char *)$ipv4address.text->chars;} | ipv6reference {$host::current=(const char *)$ipv6reference.ret;}) {$ret=$host::current;}; hostname : ( domainlabel DOT )* (toplabel)=>toplabel DOT? ; domainlabel : alphanum | (alphanum ( alphanum | DASH )* alphanum) ; toplabel : alpha | (alpha ( DASH? alphanum)+) ; ipv4address : three_digit DOT three_digit DOT three_digit DOT three_digit ; ipv6reference returns [const char* ret=NULL] : LSBRAQUET ipv6address RSBRAQUET {$ret=(const char *)$ipv6address.text->chars;}; ipv6address : hexpart ( COLON ipv4address )? ; hexpart : hexseq | hexseq COLON COLON ( hexseq )? | COLON COLON ( hexseq )?; hexseq : hex4 ( COLON hex4)*; hex4 : hexdigit+;/* hexdigit hexdigit hexdigit ;*/ port returns [int ret] @init {$ret=-1;} : DIGIT+ { $ret=atoi((const char *)$text->chars); }; escaped : PERCENT hexdigit hexdigit; ttl : three_digit; three_digit: (DIGIT) => DIGIT | (DIGIT DIGIT) => (DIGIT DIGIT) | (DIGIT DIGIT DIGIT) =>(DIGIT DIGIT DIGIT) ; token : (alphanum | mark | PERCENT | PLUS | BQUOTE )+; reserved_for_from_to_contact_addr_spec: COLON | AT | AND | EQUAL | PLUS | DOLLARD | SLASH; reserved : SEMI | COMMA | QMARK | reserved_for_from_to_contact_addr_spec ; unreserved : alphanum |mark; alphanum : alpha | DIGIT ; hexdigit : HEX_CHAR|DIGIT; alpha : HEX_CHAR | COMMON_CHAR; word : (alphanum | mark | PERCENT | PLUS | BQUOTE | LAQUOT | RAQUOT | COLON | BSLASH | DQUOTE | SLASH | LSBRAQUET | RSBRAQUET | QMARK | LBRACE | RBRACE )+; mark : DASH | USCORE | DOT | EMARK | TILDE | STAR | SQUOTE | LPAREN | RPAREN ; sp_tab_colon :( SP | HTAB )* COLON ; hcolon : sp_tab_colon lws? //SWS; ;//|( SP | HTAB )* COLON LWS+; ldquot : lws? DQUOTE ; rdquot : DQUOTE lws?; semi: lws? SEMI lws?; comma : lws? COMMA lws?; sp_laquot : lws? LAQUOT ; raquot_sp : RAQUOT lws?; equal: lws? EQUAL lws?; slash : lws? SLASH lws?; lws : (SP* CRLF SP+) | SP+ ; //linear whitespace //*************************************common tokens*****************/ COMMON_CHAR : 'g'..'z' | 'G'..'Z' ; HEX_CHAR: 'a'..'f' |'A'..'F'; DIGIT : '0'..'9' ; AT: '@'; AND: '&'; DOLLARD: '$'; QMARK: '?'; EMARK: '!'; DASH: '-'; CRLF : '\r\n' { USER1 = (int)((char*)ctx->pLexer->input->currentLine - (char*)ctx->pLexer->input->data); /*GETCHARINDEX()*/;}; HTAB : ' '; OR : '|'; PERCENT: '%'; DQUOTE : '"'; SQUOTE : '\''; BQUOTE: '`'; BSLASH: '\\'; LBRACE: '{'; RBRACE: '}'; USCORE: '_'; TILDE: '~'; DOT: '.'; PLUS: '+'; COLON : ':' ; SEMI : ';' ; COMMA : ',' ; LAQUOT : '<' ; RAQUOT : '>' ; RPAREN : ')' ; LPAREN : '(' ; RSBRAQUET : ']' ; LSBRAQUET : '[' ; EQUAL : '=' ; SLASH : '/' ; STAR : '*' ; SP : ' ' ; OCTET : . ; belle-sip-1.6.3/src/http-listener.c000066400000000000000000000063221313437522400171410ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" typedef struct belle_http_callbacks belle_http_callbacks_t; struct belle_http_callbacks{ belle_sip_object_t base; belle_http_request_listener_callbacks_t cbs; void *user_ctx; }; static void process_response_headers(belle_http_request_listener_t *l, const belle_http_response_event_t *event){ belle_http_callbacks_t *obj=(belle_http_callbacks_t*)l; if (obj->cbs.process_response_headers) obj->cbs.process_response_headers(obj->user_ctx,event); } static void process_response_event(belle_http_request_listener_t *l, const belle_http_response_event_t *event){ belle_http_callbacks_t *obj=(belle_http_callbacks_t*)l; if (obj->cbs.process_response) obj->cbs.process_response(obj->user_ctx,event); } static void process_io_error(belle_http_request_listener_t *l, const belle_sip_io_error_event_t *event){ belle_http_callbacks_t *obj=(belle_http_callbacks_t*)l; if (obj->cbs.process_io_error) obj->cbs.process_io_error(obj->user_ctx,event); } static void process_timeout(belle_http_request_listener_t *l, const belle_sip_timeout_event_t *event){ belle_http_callbacks_t *obj=(belle_http_callbacks_t*)l; if (obj->cbs.process_timeout) obj->cbs.process_timeout(obj->user_ctx,event); } static void process_auth_requested(belle_http_request_listener_t *l, belle_sip_auth_event_t *event){ belle_http_callbacks_t *obj=(belle_http_callbacks_t*)l; if (obj->cbs.process_auth_requested) obj->cbs.process_auth_requested(obj->user_ctx,event); } /*BELLE_SIP_DECLARE_VPTR(belle_http_callbacks_t);*/ BELLE_SIP_IMPLEMENT_INTERFACE_BEGIN(belle_http_callbacks_t,belle_http_request_listener_t) process_response_headers, process_response_event, process_io_error, process_timeout, process_auth_requested BELLE_SIP_IMPLEMENT_INTERFACE_END BELLE_SIP_DECLARE_IMPLEMENTED_INTERFACES_1(belle_http_callbacks_t,belle_http_request_listener_t); static void belle_http_callbacks_destroy(belle_http_callbacks_t *obj){ if (obj->cbs.listener_destroyed) obj->cbs.listener_destroyed(obj->user_ctx); } BELLE_SIP_INSTANCIATE_VPTR(belle_http_callbacks_t,belle_sip_object_t,belle_http_callbacks_destroy,NULL,NULL,FALSE); belle_http_request_listener_t *belle_http_request_listener_create_from_callbacks(const belle_http_request_listener_callbacks_t *callbacks, void *user_ctx){ belle_http_callbacks_t *obj=belle_sip_object_new(belle_http_callbacks_t); memcpy(&obj->cbs,callbacks,sizeof(belle_http_request_listener_callbacks_t)); obj->user_ctx=user_ctx; return BELLE_HTTP_REQUEST_LISTENER(obj); } belle-sip-1.6.3/src/http-message.c000066400000000000000000000143511313437522400167410ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "grammars/belle_sip_messageLexer.h" #include "grammars/belle_sip_messageParser.h" #include "belle_sip_internal.h" static void belle_http_request_init(belle_http_request_t *req){ /*nop*/ } static void belle_http_request_listener_destroyed(belle_http_request_t *req){ req->listener=NULL; } static void belle_http_request_destroy(belle_http_request_t *req){ if (req->req_uri) belle_sip_object_unref(req->req_uri); DESTROY_STRING(req,method) belle_http_request_set_listener(req,NULL); belle_http_request_set_channel(req,NULL); SET_OBJECT_PROPERTY(req,orig_uri,NULL); SET_OBJECT_PROPERTY(req,response,NULL); } static void belle_http_request_clone(belle_http_request_t *obj, const belle_http_request_t *orig){ if (orig->req_uri) obj->req_uri=(belle_generic_uri_t*)belle_sip_object_clone((belle_sip_object_t*)orig->req_uri); CLONE_STRING(belle_http_request,method,obj,orig) } static belle_sip_error_code belle_http_request_marshal(const belle_http_request_t* request, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_snprintf(buff,buff_size,offset,"%s ",belle_http_request_get_method(request)); if (error!=BELLE_SIP_OK) return error; error=belle_generic_uri_marshal(belle_http_request_get_uri(request),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset," %s","HTTP/1.1\r\n"); if (error!=BELLE_SIP_OK) return error; error=belle_sip_headers_marshal(BELLE_SIP_MESSAGE(request),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; return error; } GET_SET_STRING(belle_http_request,method); BELLE_NEW(belle_http_request,belle_sip_message) BELLE_PARSE(belle_sip_messageParser,belle_,http_request) belle_http_request_t *belle_http_request_create(const char *method, belle_generic_uri_t *url, ...){ va_list vl; belle_http_request_t *obj; belle_sip_header_t *header; if (belle_generic_uri_get_host(url) == NULL) { belle_sip_error("%s: NULL host in url", __FUNCTION__); return NULL; } obj=belle_http_request_new(); obj->method=belle_sip_strdup(method); obj->req_uri=(belle_generic_uri_t*)belle_sip_object_ref(url); va_start(vl,url); while((header=va_arg(vl,belle_sip_header_t*))!=NULL){ belle_sip_message_add_header(BELLE_SIP_MESSAGE(obj),header); } va_end(vl); return obj; } int belle_http_request_is_cancelled(const belle_http_request_t *req) { return req->cancelled; } void belle_http_request_cancel(belle_http_request_t *req) { req->cancelled = TRUE; } void belle_http_request_set_listener(belle_http_request_t *req, belle_http_request_listener_t *l){ if (req->listener){ belle_sip_object_weak_unref(req->listener,(belle_sip_object_destroy_notify_t)belle_http_request_listener_destroyed,req); req->listener=NULL; } if (l){ belle_sip_object_weak_ref(l,(belle_sip_object_destroy_notify_t)belle_http_request_listener_destroyed,req); req->listener=l; } } static void notify_http_request_of_channel_destruction(belle_http_request_t *obj, belle_sip_channel_t *chan_being_destroyed){ obj->channel=NULL; } void belle_http_request_set_channel(belle_http_request_t *req, belle_sip_channel_t* chan){ if (req->channel){ belle_sip_object_weak_unref(req->channel, (belle_sip_object_destroy_notify_t)notify_http_request_of_channel_destruction, req); req->channel=NULL; } if (chan){ belle_sip_object_weak_ref(chan, (belle_sip_object_destroy_notify_t)notify_http_request_of_channel_destruction, req); req->channel=chan; } } belle_http_request_listener_t * belle_http_request_get_listener(const belle_http_request_t *req){ return req->listener; } belle_generic_uri_t *belle_http_request_get_uri(const belle_http_request_t *req){ return req->req_uri; } void belle_http_request_set_uri(belle_http_request_t* request, belle_generic_uri_t* uri) { SET_OBJECT_PROPERTY(request,req_uri,uri); } void belle_http_request_set_response(belle_http_request_t *req, belle_http_response_t *resp){ SET_OBJECT_PROPERTY(req,response,resp); } belle_http_response_t *belle_http_request_get_response(belle_http_request_t *req){ return req->response; } /*response*/ struct belle_http_response{ belle_sip_message_t base; char *http_version; int status_code; char *reason_phrase; }; void belle_http_response_destroy(belle_http_response_t *resp){ if (resp->http_version) belle_sip_free(resp->http_version); if (resp->reason_phrase) belle_sip_free(resp->reason_phrase); } static void belle_http_response_init(belle_http_response_t *resp){ } static void belle_http_response_clone(belle_http_response_t *resp, const belle_http_response_t *orig){ if (orig->http_version) resp->http_version=belle_sip_strdup(orig->http_version); resp->status_code=orig->status_code; if (orig->reason_phrase) resp->reason_phrase=belle_sip_strdup(orig->reason_phrase); } belle_sip_error_code belle_http_response_marshal(belle_http_response_t *resp, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_snprintf( buff ,buff_size ,offset ,"HTTP/1.1 %i %s\r\n" ,belle_http_response_get_status_code(resp) ,belle_http_response_get_reason_phrase(resp)?belle_http_response_get_reason_phrase(resp):""); if (error!=BELLE_SIP_OK) return error; error=belle_sip_headers_marshal(BELLE_SIP_MESSAGE(resp),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_NEW(belle_http_response,belle_sip_message) BELLE_PARSE(belle_sip_messageParser,belle_,http_response) GET_SET_STRING(belle_http_response,reason_phrase); GET_SET_INT(belle_http_response,status_code,int) belle-sip-1.6.3/src/http-provider.c000066400000000000000000000506671313437522400171610ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "belle_sip_internal.h" typedef struct belle_http_channel_context belle_http_channel_context_t; #define BELLE_HTTP_CHANNEL_CONTEXT(obj) BELLE_SIP_CAST(obj,belle_http_channel_context_t) static void provider_remove_channel(belle_http_provider_t *obj, belle_sip_channel_t *chan); struct belle_http_channel_context{ belle_sip_object_t base; belle_http_provider_t *provider; belle_sip_list_t *pending_requests; }; struct belle_http_provider{ belle_sip_object_t base; belle_sip_stack_t *stack; char *bind_ip; int ai_family; belle_sip_list_t *tcp_channels; belle_sip_list_t *tls_channels; belle_tls_crypto_config_t *crypto_config; }; #define BELLE_HTTP_REQUEST_INVOKE_LISTENER(obj,method,arg) \ obj->listener ? BELLE_SIP_INVOKE_LISTENER_ARG(obj->listener,belle_http_request_listener_t,method,arg) : 0 static int http_channel_context_handle_authentication(belle_http_channel_context_t *ctx, belle_http_request_t *req){ const char *realm=NULL; belle_sip_auth_event_t *ev=NULL; belle_http_response_t *resp=belle_http_request_get_response(req); const char *username=NULL; const char *passwd=NULL; const char *ha1=NULL; char computed_ha1[33]; belle_sip_header_www_authenticate_t* authenticate; int ret=0; if (req->auth_attempt_count>1){ req->auth_attempt_count=0; return -1; } if (resp == NULL ) { belle_sip_error("Missing response for req [%p], cannot authenticate", req); return -1; } if (!(authenticate = belle_sip_message_get_header_by_type(resp,belle_sip_header_www_authenticate_t))) { if (belle_sip_message_get_header_by_type(resp,belle_sip_header_proxy_authenticate_t)) { belle_sip_error("Proxy authentication not supported yet, cannot authenticate for resp [%p]", resp); } belle_sip_error("Missing auth header in response [%p], cannot authenticate", resp); return -1; } if (strcasecmp("Digest",belle_sip_header_www_authenticate_get_scheme(authenticate)) != 0) { belle_sip_error("Unsupported auth scheme [%s] in response [%p], cannot authenticate", belle_sip_header_www_authenticate_get_scheme(authenticate),resp); return -1; } /*find if username, passwd were already supplied in original request uri*/ if (req->orig_uri){ username=belle_generic_uri_get_user(req->orig_uri); passwd=belle_generic_uri_get_user_password(req->orig_uri); } realm = belle_sip_header_www_authenticate_get_realm(authenticate); if (!username || !passwd) { ev=belle_sip_auth_event_create((belle_sip_object_t*)ctx->provider,realm,NULL); BELLE_HTTP_REQUEST_INVOKE_LISTENER(req,process_auth_requested,ev); username=ev->username; passwd=ev->passwd; ha1=ev->ha1; } if (!ha1 && username && passwd) { belle_sip_auth_helper_compute_ha1(username,realm,passwd, computed_ha1); ha1=computed_ha1; } else if (!ha1){ belle_sip_error("No auth info found for request [%p], cannot authenticate",req); ret=-1; } if (ha1) { belle_http_header_authorization_t* authorization; req->auth_attempt_count++; authorization = belle_http_auth_helper_create_authorization(authenticate); /*select first qop mode*/ belle_sip_header_authorization_set_qop(BELLE_SIP_HEADER_AUTHORIZATION(authorization),belle_sip_header_www_authenticate_get_qop_first(authenticate)); belle_sip_header_authorization_set_nonce_count(BELLE_SIP_HEADER_AUTHORIZATION(authorization),1); /*we don't store nonce count for now*/ belle_sip_header_authorization_set_username(BELLE_SIP_HEADER_AUTHORIZATION(authorization),username); belle_http_header_authorization_set_uri(authorization,belle_http_request_get_uri(req)); if (belle_sip_auth_helper_fill_authorization(BELLE_SIP_HEADER_AUTHORIZATION(authorization),belle_http_request_get_method(req),ha1)) { belle_sip_error("Cannot fill auth header for request [%p]",req); if (authorization) belle_sip_object_unref(authorization); ret=-1; } else { belle_sip_message_remove_header(BELLE_SIP_MESSAGE(req),BELLE_HTTP_AUTHORIZATION); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(authorization)); belle_http_provider_send_request(ctx->provider,req,NULL); } } if (ev) belle_sip_auth_event_destroy(ev); return ret; } static void http_channel_context_handle_response_headers(belle_http_channel_context_t *ctx , belle_sip_channel_t *chan, belle_http_response_t *response){ belle_http_request_t *req=ctx->pending_requests ? (belle_http_request_t*) ctx->pending_requests->data : NULL; belle_http_response_event_t ev={0}; int code; if (req==NULL){ belle_sip_error("Receiving http response headers not matching any request."); return; } if (belle_http_request_is_cancelled(req)) { belle_sip_warning("Receiving http response headers for a cancelled request."); return; } code=belle_http_response_get_status_code(response); if (code!=401 && code!=407){ /*else notify the app about the response headers received*/ ev.source=(belle_sip_object_t*)ctx->provider; ev.request=req; ev.response=response; BELLE_HTTP_REQUEST_INVOKE_LISTENER(req,process_response_headers,&ev); } } static void http_channel_context_handle_response(belle_http_channel_context_t *ctx , belle_sip_channel_t *chan, belle_http_response_t *response){ belle_http_request_t *req=NULL; belle_http_response_event_t ev={0}; int code; belle_sip_header_t *connection; /*pop the request matching this response*/ ctx->pending_requests=belle_sip_list_pop_front(ctx->pending_requests,(void**)&req); if (req==NULL){ belle_sip_error("Receiving http response not matching any request."); return; } if (belle_http_request_is_cancelled(req)) { belle_sip_warning("Receiving http response for a cancelled request."); return; } connection=belle_sip_message_get_header((belle_sip_message_t *)response,"Connection"); if (connection && strstr(belle_sip_header_get_unparsed_value(connection),"close")!=NULL) chan->about_to_be_closed=TRUE; belle_http_request_set_response(req,response); code=belle_http_response_get_status_code(response); if ((code==401 || code==407) && http_channel_context_handle_authentication(ctx,req)==0 ){ /*nothing to do, the request has been resubmitted with authentication*/ }else{ /*else notify the app about the response received*/ ev.source=(belle_sip_object_t*)ctx->provider; ev.request=req; ev.response=response; BELLE_HTTP_REQUEST_INVOKE_LISTENER(req,process_response,&ev); if( req->background_task_id ){ belle_sip_warning("HTTP request finished: ending bg task id=[%x]", req->background_task_id); belle_sip_end_background_task(req->background_task_id); req->background_task_id = 0; } } belle_sip_object_unref(req); } static void http_channel_context_handle_io_error(belle_http_channel_context_t *ctx, belle_sip_channel_t *chan){ belle_http_request_t *req=NULL; belle_sip_io_error_event_t ev={0}; belle_sip_list_t *elem; /*if the error happens before attempting to send the message, the pending_requests is empty*/ if (ctx->pending_requests==NULL) elem=chan->outgoing_messages; else elem=ctx->pending_requests; /*pop the requests for which this error is reported*/ for(;elem!=NULL;elem=elem->next){ req=(belle_http_request_t *)elem->data; /*else notify the app about the response received*/ /*TODO: would be nice to put the message in the event*/ ev.source=(belle_sip_object_t*)ctx->provider; ev.host=chan->peer_cname; ev.port=chan->peer_port; ev.transport=belle_sip_channel_get_transport_name(chan); BELLE_HTTP_REQUEST_INVOKE_LISTENER(req,process_io_error,&ev); if( req->background_task_id ){ belle_sip_warning("IO Error on HTTP request: ending bg task id=[%x]", req->background_task_id); belle_sip_end_background_task(req->background_task_id); req->background_task_id = 0; } } } /* we are called here by the channel when receiving a message for which a body is expected. * We can notify the application so that it can setup an appropriate body handler. */ static void channel_on_message_headers(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, belle_sip_message_t *msg){ belle_http_channel_context_t *ctx=BELLE_HTTP_CHANNEL_CONTEXT(obj); if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(msg,belle_http_response_t)){ http_channel_context_handle_response_headers(ctx,chan,(belle_http_response_t*)msg); }/*ignore requests*/ } static void channel_on_message(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, belle_sip_message_t *msg){ belle_http_channel_context_t *ctx=BELLE_HTTP_CHANNEL_CONTEXT(obj); if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(msg,belle_http_response_t)){ http_channel_context_handle_response(ctx,chan,(belle_http_response_t*)msg); }/*ignore requests*/ } static int channel_on_auth_requested(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, const char* distinguished_name){ belle_http_channel_context_t *ctx=BELLE_HTTP_CHANNEL_CONTEXT(obj); if (BELLE_SIP_IS_INSTANCE_OF(chan,belle_sip_tls_channel_t)) { belle_sip_auth_event_t* auth_event = belle_sip_auth_event_create((belle_sip_object_t*)ctx->provider,NULL,NULL); belle_sip_tls_channel_t *tls_chan=BELLE_SIP_TLS_CHANNEL(chan); belle_http_request_t *req=(belle_http_request_t*)chan->outgoing_messages->data; auth_event->mode=BELLE_SIP_AUTH_MODE_TLS; belle_sip_auth_event_set_distinguished_name(auth_event,distinguished_name); BELLE_HTTP_REQUEST_INVOKE_LISTENER(req,process_auth_requested,auth_event); belle_sip_tls_channel_set_client_certificates_chain(tls_chan,auth_event->cert); belle_sip_tls_channel_set_client_certificate_key(tls_chan,auth_event->key); belle_sip_auth_event_destroy(auth_event); } return 0; } static void channel_on_sending(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, belle_sip_message_t *msg){ belle_http_channel_context_t *ctx=BELLE_HTTP_CHANNEL_CONTEXT(obj); ctx->pending_requests=belle_sip_list_append(ctx->pending_requests,belle_sip_object_ref(msg)); } static void channel_state_changed(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, belle_sip_channel_state_t state){ belle_http_channel_context_t *ctx=BELLE_HTTP_CHANNEL_CONTEXT(obj); switch(state){ case BELLE_SIP_CHANNEL_INIT: case BELLE_SIP_CHANNEL_RES_IN_PROGRESS: case BELLE_SIP_CHANNEL_RES_DONE: case BELLE_SIP_CHANNEL_CONNECTING: case BELLE_SIP_CHANNEL_READY: case BELLE_SIP_CHANNEL_RETRY: break; case BELLE_SIP_CHANNEL_ERROR: http_channel_context_handle_io_error(ctx, chan); BCTBX_NO_BREAK; /*intentionally no break*/ case BELLE_SIP_CHANNEL_DISCONNECTED: if (!chan->force_close) provider_remove_channel(ctx->provider,chan); break; } } static void belle_http_channel_context_uninit(belle_http_channel_context_t *obj){ belle_sip_list_free_with_data(obj->pending_requests,belle_sip_object_unref); } static void on_channel_destroyed(belle_http_channel_context_t *obj, belle_sip_channel_t *chan_being_destroyed){ belle_sip_channel_remove_listener(chan_being_destroyed,BELLE_SIP_CHANNEL_LISTENER(obj)); belle_sip_object_unref(obj); } /* * The http channel context stores pending requests so that they can be matched with response received. * It is associated with the channel when the channel is created, and automatically destroyed when the channel is destroyed. **/ belle_http_channel_context_t * belle_http_channel_context_new(belle_sip_channel_t *chan, belle_http_provider_t *prov){ belle_http_channel_context_t *obj=belle_sip_object_new(belle_http_channel_context_t); obj->provider=prov; belle_sip_channel_add_listener(chan,(belle_sip_channel_listener_t*)obj); belle_sip_object_weak_ref(chan,(belle_sip_object_destroy_notify_t)on_channel_destroyed,obj); return obj; } int belle_http_channel_is_busy(belle_sip_channel_t *obj) { belle_sip_list_t *it; if (obj->incoming_messages != NULL || obj->outgoing_messages != NULL) { return 1; } /*fixme, a litle bit intrusive*/ for (it = obj->full_listeners; it != NULL; it = it->next) { if (BELLE_SIP_IS_INSTANCE_OF(it->data, belle_http_channel_context_t)) { belle_http_channel_context_t *obj = it->data; return obj->pending_requests != NULL; } } return 0; } BELLE_SIP_IMPLEMENT_INTERFACE_BEGIN(belle_http_channel_context_t,belle_sip_channel_listener_t) channel_state_changed, channel_on_message_headers, channel_on_message, channel_on_sending, channel_on_auth_requested BELLE_SIP_IMPLEMENT_INTERFACE_END BELLE_SIP_DECLARE_IMPLEMENTED_INTERFACES_1(belle_http_channel_context_t,belle_sip_channel_listener_t); BELLE_SIP_INSTANCIATE_VPTR(belle_http_channel_context_t,belle_sip_object_t,belle_http_channel_context_uninit,NULL,NULL,FALSE); static void http_provider_uninit(belle_http_provider_t *obj){ belle_sip_message("http provider destroyed."); belle_sip_free(obj->bind_ip); belle_sip_list_for_each(obj->tcp_channels,(void (*)(void*))belle_sip_channel_force_close); belle_sip_list_free_with_data(obj->tcp_channels,belle_sip_object_unref); belle_sip_list_for_each(obj->tls_channels,(void (*)(void*))belle_sip_channel_force_close); belle_sip_list_free_with_data(obj->tls_channels,belle_sip_object_unref); belle_sip_object_unref(obj->crypto_config); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_http_provider_t); BELLE_SIP_INSTANCIATE_VPTR(belle_http_provider_t,belle_sip_object_t,http_provider_uninit,NULL,NULL,FALSE); belle_http_provider_t *belle_http_provider_new(belle_sip_stack_t *s, const char *bind_ip){ belle_http_provider_t *p=belle_sip_object_new(belle_http_provider_t); p->stack=s; p->bind_ip=belle_sip_strdup(bind_ip); p->ai_family=strchr(p->bind_ip,':') ? AF_INET6 : AF_INET; p->crypto_config=belle_tls_crypto_config_new(); return p; } static void split_request_url(belle_http_request_t *req){ belle_generic_uri_t *uri=belle_http_request_get_uri(req); belle_generic_uri_t *new_uri; char *host_value; const char *path; if (belle_generic_uri_get_host(uri)==NULL && req->orig_uri!=NULL) return;/*already processed request uri*/ path=belle_generic_uri_get_path(uri); if (path==NULL) path="/"; new_uri=belle_generic_uri_new(); belle_generic_uri_set_path(new_uri,path); belle_generic_uri_set_query(new_uri, belle_generic_uri_get_query(uri)); if (belle_generic_uri_get_port(uri)>0) host_value=belle_sip_strdup_printf("%s:%i",belle_generic_uri_get_host(uri),belle_generic_uri_get_port(uri)); else host_value=belle_sip_strdup(belle_generic_uri_get_host(uri)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),belle_sip_header_create("Host",host_value)); belle_sip_free(host_value); SET_OBJECT_PROPERTY(req,orig_uri,uri); belle_http_request_set_uri(req,new_uri); } static void fix_request(belle_http_request_t *req){ size_t size=belle_sip_message_get_body_size((belle_sip_message_t*)req); belle_sip_header_content_length_t *ctlen=belle_sip_message_get_header_by_type(req, belle_sip_header_content_length_t); if (size>0 && !ctlen){ belle_sip_message_add_header((belle_sip_message_t*)req,(belle_sip_header_t*)belle_sip_header_content_length_create(size)); } } belle_sip_list_t **belle_http_provider_get_channels(belle_http_provider_t *obj, const char *transport_name){ if (strcasecmp(transport_name,"tcp")==0) return &obj->tcp_channels; else if (strcasecmp(transport_name,"tls")==0) return &obj->tls_channels; else{ belle_sip_error("belle_http_provider_send_request(): unsupported transport %s",transport_name); return NULL; } } static void provider_remove_channel(belle_http_provider_t *obj, belle_sip_channel_t *chan){ belle_sip_list_t **channels=belle_http_provider_get_channels(obj,belle_sip_channel_get_transport_name(chan)); *channels=belle_sip_list_remove(*channels,chan); belle_sip_message("channel [%p] removed from http provider.", chan); belle_sip_object_unref(chan); } static void belle_http_end_background_task(void* data) { belle_http_request_t *req = BELLE_HTTP_REQUEST(data); belle_sip_warning("Ending unfinished HTTP transfer background task id=[%x]", req->background_task_id); if( req->background_task_id ){ belle_sip_end_background_task(req->background_task_id); req->background_task_id = 0; } } int belle_http_provider_send_request(belle_http_provider_t *obj, belle_http_request_t *req, belle_http_request_listener_t *listener){ belle_sip_channel_t *chan; belle_sip_list_t **channels; belle_sip_hop_t *hop=belle_sip_hop_new_from_generic_uri(req->orig_uri ? req->orig_uri : req->req_uri); if (hop->host == NULL){ belle_sip_error("belle_http_provider_send_request(): no host defined in request uri."); belle_sip_object_unref(hop); return -1; } channels = belle_http_provider_get_channels(obj,hop->transport); if (listener) belle_http_request_set_listener(req,listener); chan=belle_sip_channel_find_from_list(*channels,obj->ai_family, hop); if (chan) { // we cannot use the same channel for multiple requests yet since only the first // one will be processed. Instead of queuing/serializing requests on a single channel, // we currently create one channel per request if needed. if (belle_http_channel_is_busy(chan)) { belle_sip_message("%s: found an available channel but was busy, creating a new one", __FUNCTION__); chan = NULL; } } if (!chan){ if (strcasecmp(hop->transport,"tcp")==0){ chan=belle_sip_stream_channel_new_client(obj->stack,obj->bind_ip,0,hop->cname,hop->host,hop->port); } else if (strcasecmp(hop->transport,"tls")==0){ chan=belle_sip_channel_new_tls(obj->stack,obj->crypto_config,obj->bind_ip,0,hop->cname,hop->host,hop->port); } if (!chan){ belle_sip_error("%s: cannot create channel for [%s:%s:%i]", __FUNCTION__, hop->transport, hop->cname, hop->port); belle_sip_object_unref(hop); return -1; } belle_http_channel_context_new(chan,obj); *channels=belle_sip_list_prepend(*channels,chan); } belle_sip_object_unref(hop); split_request_url(req); fix_request(req); belle_http_request_set_channel(req,chan); if( req->background_task_id != 0){ req->background_task_id = belle_sip_begin_background_task("belle-sip http", belle_http_end_background_task, req); } belle_sip_channel_queue_message(chan,BELLE_SIP_MESSAGE(req)); return 0; } static void reenqueue_request(belle_http_request_t *req, belle_http_provider_t *prov){ belle_http_provider_send_request(prov,req,req->listener); } void belle_http_provider_cancel_request(belle_http_provider_t *obj, belle_http_request_t *req){ belle_sip_list_t *outgoing_messages; belle_http_request_cancel(req); if (req->channel){ // Keep the list of the outgoing messages of the channel... outgoing_messages = belle_sip_list_copy_with_data(req->channel->outgoing_messages,(void* (*)(void*))belle_sip_object_ref); if (outgoing_messages && outgoing_messages->data == req){ /*our request didn't go out; so drop it.*/ outgoing_messages = belle_sip_list_remove(outgoing_messages ,req); belle_sip_object_unref(req); } /*protect the channel from being destroyed before removing it (removing it will unref it)*/ belle_sip_object_ref(req->channel); provider_remove_channel(obj, req->channel); // ... close the channel... belle_sip_channel_force_close(req->channel); belle_sip_object_unref(req->channel); // ... and reenqueue the previously queued outgoing messages into a new channel belle_sip_list_for_each2(outgoing_messages,(void (*)(void*,void*))reenqueue_request,obj); belle_sip_list_free_with_data(outgoing_messages,belle_sip_object_unref); } } int belle_http_provider_set_tls_verify_policy(belle_http_provider_t *obj, belle_tls_verify_policy_t *verify_ctx){ SET_OBJECT_PROPERTY(obj,crypto_config,verify_ctx); return 0; } int belle_http_provider_set_tls_crypto_config(belle_http_provider_t *obj, belle_tls_crypto_config_t *crypto_config){ SET_OBJECT_PROPERTY(obj,crypto_config,crypto_config); return 0; } void belle_http_provider_set_recv_error(belle_http_provider_t *obj, int recv_error) { belle_sip_list_t *it; for(it=obj->tcp_channels;it!=NULL;it=it->next){ belle_sip_channel_t *chan = (belle_sip_channel_t*)it->data; chan->simulated_recv_return=recv_error; chan->base.notify_required=(recv_error<=0); } for(it=obj->tls_channels;it!=NULL;it=it->next){ belle_sip_channel_t *chan = (belle_sip_channel_t*)it->data; chan->simulated_recv_return=recv_error; chan->base.notify_required=(recv_error<=0); } } belle-sip-1.6.3/src/ict.c000066400000000000000000000177201313437522400151220ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * INVITE client transaction implementation. **/ #include #include "belle_sip_internal.h" static void on_ict_terminate(belle_sip_ict_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; if (obj->timer_A){ belle_sip_transaction_stop_timer(base,obj->timer_A); belle_sip_object_unref(obj->timer_A); obj->timer_A=NULL; } if (obj->timer_B){ belle_sip_transaction_stop_timer(base,obj->timer_B); belle_sip_object_unref(obj->timer_B); obj->timer_B=NULL; } if (obj->timer_D){ belle_sip_transaction_stop_timer(base,obj->timer_D); belle_sip_object_unref(obj->timer_D); obj->timer_D=NULL; } if (obj->timer_M){ belle_sip_transaction_stop_timer(base,obj->timer_M); belle_sip_object_unref(obj->timer_M); obj->timer_M=NULL; } if (obj->ack){ belle_sip_object_unref(obj->ack); obj->ack=NULL; } } static void ict_destroy(belle_sip_ict_t *obj){ on_ict_terminate(obj); } static belle_sip_request_t *make_ack(belle_sip_ict_t *obj, belle_sip_response_t *resp){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; if (obj->ack==NULL){ obj->ack=belle_sip_request_new(); belle_sip_object_ref(obj->ack); belle_sip_request_set_method(obj->ack,"ACK"); belle_sip_request_set_uri(obj->ack,belle_sip_request_get_uri(base->request)); belle_sip_util_copy_headers((belle_sip_message_t*)base->request,(belle_sip_message_t*)obj->ack,BELLE_SIP_VIA,FALSE); belle_sip_util_copy_headers((belle_sip_message_t*)base->request,(belle_sip_message_t*)obj->ack,BELLE_SIP_CALL_ID,FALSE); belle_sip_util_copy_headers((belle_sip_message_t*)base->request,(belle_sip_message_t*)obj->ack,BELLE_SIP_FROM,FALSE); belle_sip_util_copy_headers((belle_sip_message_t*)resp,(belle_sip_message_t*)obj->ack,BELLE_SIP_TO,FALSE); belle_sip_util_copy_headers((belle_sip_message_t*)base->request,(belle_sip_message_t*)obj->ack,BELLE_SIP_CONTACT,TRUE); belle_sip_util_copy_headers((belle_sip_message_t*)base->request,(belle_sip_message_t*)obj->ack,BELLE_SIP_ROUTE,TRUE); belle_sip_util_copy_headers((belle_sip_message_t*)base->request,(belle_sip_message_t*)obj->ack,BELLE_SIP_MAX_FORWARDS,FALSE); belle_sip_message_add_header((belle_sip_message_t*)obj->ack, (belle_sip_header_t*)belle_sip_header_cseq_create( belle_sip_header_cseq_get_seq_number((belle_sip_header_cseq_t*)belle_sip_message_get_header((belle_sip_message_t*)base->request,BELLE_SIP_CSEQ)), "ACK")); } return obj->ack; } /* Timer D: Wait time for response retransmits */ static int ict_on_timer_D(belle_sip_ict_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; if (base->state==BELLE_SIP_TRANSACTION_COMPLETED){ belle_sip_transaction_terminate(base); } return BELLE_SIP_STOP; } /* Timer M: Wait time for retransmission of 2xx to INVITE or additional 2xx from other branches of a forked INVITE */ static int ict_on_timer_M(belle_sip_ict_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; if (base->state==BELLE_SIP_TRANSACTION_ACCEPTED){ belle_sip_transaction_terminate(base); } return BELLE_SIP_STOP; } static void ict_on_response(belle_sip_ict_t *obj, belle_sip_response_t *resp){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; int code=belle_sip_response_get_status_code(resp); const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base); switch (base->state){ case BELLE_SIP_TRANSACTION_CALLING: belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_PROCEEDING); BCTBX_NO_BREAK; /*intentionally no break*/ case BELLE_SIP_TRANSACTION_PROCEEDING: if (code>=300){ belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_COMPLETED); belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)make_ack(obj,resp)); belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp); obj->timer_D=belle_sip_timeout_source_new((belle_sip_source_func_t)ict_on_timer_D,obj,cfg->T1*64); belle_sip_transaction_start_timer(base,obj->timer_D); }else if (code>=200){ obj->timer_M=belle_sip_timeout_source_new((belle_sip_source_func_t)ict_on_timer_M,obj,cfg->T1*64); belle_sip_transaction_start_timer(base,obj->timer_M); belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_ACCEPTED); belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp); }else if (code>=100){ belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp); } break; case BELLE_SIP_TRANSACTION_ACCEPTED: if (code>=200 && code<300){ belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp); } break; case BELLE_SIP_TRANSACTION_COMPLETED: if (code>=300 && obj->ack){ belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)obj->ack); } break; default: break; } } /* Timer A: INVITE request retransmit interval, for UDP only */ static int ict_on_timer_A(belle_sip_ict_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; if (!base->channel) return BELLE_SIP_STOP; switch(base->state){ case BELLE_SIP_TRANSACTION_CALLING: { /*reset the timer to twice the previous value, and retransmit */ unsigned int prev_timeout=belle_sip_source_get_timeout(obj->timer_A); belle_sip_source_set_timeout(obj->timer_A,2*prev_timeout); belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)base->request); } break; default: break; } return BELLE_SIP_CONTINUE; } /* Timer B: INVITE transaction timeout timer */ static int ict_on_timer_B(belle_sip_ict_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; switch (base->state){ case BELLE_SIP_TRANSACTION_CALLING: belle_sip_transaction_notify_timeout(base); break; default: break; } return BELLE_SIP_STOP; } static void ict_send_request(belle_sip_ict_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base); belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_CALLING); if (!belle_sip_channel_is_reliable(base->channel)){ obj->timer_A=belle_sip_timeout_source_new((belle_sip_source_func_t)ict_on_timer_A,obj,cfg->T1); belle_sip_transaction_start_timer(base,obj->timer_A); } obj->timer_B=belle_sip_timeout_source_new((belle_sip_source_func_t)ict_on_timer_B,obj,cfg->T1*64); belle_sip_transaction_start_timer(base,obj->timer_B); belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)base->request); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_ict_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_ict_t) { { { BELLE_SIP_VPTR_INIT(belle_sip_ict_t,belle_sip_client_transaction_t,TRUE), (belle_sip_object_destroy_t)ict_destroy, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, (void (*)(belle_sip_transaction_t*))on_ict_terminate }, (void (*)(belle_sip_client_transaction_t*))ict_send_request, (void (*)(belle_sip_client_transaction_t*,belle_sip_response_t*))ict_on_response } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END belle_sip_ict_t *belle_sip_ict_new(belle_sip_provider_t *prov, belle_sip_request_t *req){ belle_sip_ict_t *obj=belle_sip_object_new(belle_sip_ict_t); belle_sip_client_transaction_init((belle_sip_client_transaction_t*)obj,prov,req); return obj; } belle-sip-1.6.3/src/ist.c000066400000000000000000000150621313437522400151370ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * INVITE server transaction implementation. **/ #include "belle_sip_internal.h" static void ist_on_terminate(belle_sip_ist_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; /*timer pointers are set to NULL because they can be released later*/ if (obj->timer_G){ belle_sip_transaction_stop_timer(base,obj->timer_G); belle_sip_object_unref(obj->timer_G); obj->timer_G=NULL; } if (obj->timer_H){ belle_sip_transaction_stop_timer(base,obj->timer_H); belle_sip_object_unref(obj->timer_H); obj->timer_H=NULL; } if (obj->timer_I){ belle_sip_transaction_stop_timer(base,obj->timer_I); belle_sip_object_unref(obj->timer_I); obj->timer_I=NULL; } if (obj->timer_L){ belle_sip_transaction_stop_timer(base,obj->timer_L); belle_sip_object_unref(obj->timer_L); obj->timer_L=NULL; } } static void ist_destroy(belle_sip_ist_t *obj){ ist_on_terminate(obj); } /* Timer G: INVITE response retransmit interval */ static int ist_on_timer_G(belle_sip_ist_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; if (base->state==BELLE_SIP_TRANSACTION_COMPLETED){ const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base); int interval=belle_sip_source_get_timeout(obj->timer_G); belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)base->last_response); belle_sip_source_set_timeout(obj->timer_G,MIN(2*interval,cfg->T2)); return BELLE_SIP_CONTINUE; } return BELLE_SIP_STOP; } /* Timer H: Wait time for ACK receipt */ static int ist_on_timer_H(belle_sip_ist_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; if (base->state==BELLE_SIP_TRANSACTION_COMPLETED){ belle_sip_transaction_terminate(base); /*FIXME: no ACK was received, should report the failure */ } return BELLE_SIP_STOP; } /* Timer I: Wait time for ACK retransmits */ static int ist_on_timer_I(belle_sip_ist_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; belle_sip_transaction_terminate(base); return BELLE_SIP_STOP; } /* Timer L: Wait time for accepted INVITE request retransmits */ static int ist_on_timer_L(belle_sip_ist_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; belle_sip_transaction_terminate(base); return BELLE_SIP_STOP; } int belle_sip_ist_process_ack(belle_sip_ist_t *obj, belle_sip_message_t *ack){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; int ret=-1; switch(base->state){ case BELLE_SIP_TRANSACTION_COMPLETED: /*clear timer G*/ if (obj->timer_G){ belle_sip_transaction_stop_timer(base,obj->timer_G); belle_sip_object_unref(obj->timer_G); obj->timer_G=NULL; } belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_CONFIRMED); if (!belle_sip_channel_is_reliable(base->channel)){ const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base); obj->timer_I=belle_sip_timeout_source_new((belle_sip_source_func_t)ist_on_timer_I,obj,cfg->T4); belle_sip_transaction_start_timer(base,obj->timer_I); }else ist_on_timer_I(obj); break; case BELLE_SIP_TRANSACTION_ACCEPTED: ret=0; /*let the ACK be reported to TU */ break; default: break; } return ret; } static int ist_send_new_response(belle_sip_ist_t *obj, belle_sip_response_t *resp){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; int code=belle_sip_response_get_status_code(resp); int ret=-1; switch(base->state){ case BELLE_SIP_TRANSACTION_PROCEEDING: { const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base); ret=0; belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)resp); if (code>=200 && code<300){ belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_ACCEPTED); obj->timer_L=belle_sip_timeout_source_new((belle_sip_source_func_t)ist_on_timer_L,obj,64*cfg->T1); belle_sip_transaction_start_timer(base,obj->timer_L); }else if (code>=300){ belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_COMPLETED); if (!belle_sip_channel_is_reliable(base->channel)){ obj->timer_G=belle_sip_timeout_source_new((belle_sip_source_func_t)ist_on_timer_G,obj,cfg->T1); belle_sip_transaction_start_timer(base,obj->timer_G); } obj->timer_H=belle_sip_timeout_source_new((belle_sip_source_func_t)ist_on_timer_H,obj,64*cfg->T1); belle_sip_transaction_start_timer(base,obj->timer_H); } } break; case BELLE_SIP_TRANSACTION_ACCEPTED: if (code>=200 && code<300){ ret=0; /*let the response go to transport layer*/ } default: break; } return ret; } static void ist_on_request_retransmission(belle_sip_nist_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; switch(base->state){ case BELLE_SIP_TRANSACTION_PROCEEDING: case BELLE_SIP_TRANSACTION_COMPLETED: belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)base->last_response); break; default: break; } } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_ist_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_ist_t) { { { BELLE_SIP_VPTR_INIT(belle_sip_ist_t,belle_sip_server_transaction_t,TRUE), (belle_sip_object_destroy_t)ist_destroy, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, (void (*)(belle_sip_transaction_t *))ist_on_terminate }, (int (*)(belle_sip_server_transaction_t*, belle_sip_response_t *))ist_send_new_response, (void (*)(belle_sip_server_transaction_t*))ist_on_request_retransmission, } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END belle_sip_ist_t *belle_sip_ist_new(belle_sip_provider_t *prov, belle_sip_request_t *req){ belle_sip_ist_t *obj=belle_sip_object_new(belle_sip_ist_t); belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; belle_sip_server_transaction_init((belle_sip_server_transaction_t*)obj,prov,req); belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_PROCEEDING); return obj; } belle-sip-1.6.3/src/listeningpoint.c000066400000000000000000000204451313437522400174070ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" void belle_sip_listening_point_init(belle_sip_listening_point_t *lp, belle_sip_stack_t *s, const char *address, int port){ char *tmp; belle_sip_init_sockets(); lp->stack=s; lp->listening_uri=belle_sip_uri_create(NULL,address); belle_sip_object_ref(lp->listening_uri); belle_sip_uri_set_port(lp->listening_uri,port); belle_sip_uri_set_transport_param(lp->listening_uri,BELLE_SIP_OBJECT_VPTR(lp,belle_sip_listening_point_t)->transport); tmp=belle_sip_object_to_string((belle_sip_object_t*)BELLE_SIP_LISTENING_POINT(lp)->listening_uri); if (strchr(address,':')) { lp->ai_family=AF_INET6; } else { lp->ai_family=AF_INET; } belle_sip_message("Creating listening point [%p] on [%s]",lp, tmp); belle_sip_free(tmp); } static void belle_sip_listening_point_uninit(belle_sip_listening_point_t *lp){ char *tmp=belle_sip_object_to_string((belle_sip_object_t*)BELLE_SIP_LISTENING_POINT(lp)->listening_uri); belle_sip_listening_point_clean_channels(lp); belle_sip_message("Listening point [%p] on [%s] destroyed",lp, tmp); belle_sip_object_unref(lp->listening_uri); belle_sip_free(tmp); lp->channel_listener=NULL; /*does not unref provider*/ belle_sip_uninit_sockets(); belle_sip_listening_point_set_keep_alive(lp,-1); } void belle_sip_listening_point_add_channel(belle_sip_listening_point_t *lp, belle_sip_channel_t *chan){ chan->lp=lp; belle_sip_channel_add_listener(chan,lp->channel_listener); /*add channel listener*/ /*channel is already owned, no ref needed - REVISIT: channel should be initally unowned probably.*/ /* The channel with names must be treated with higher priority by the get_channel() method so queued on front. * This is to prevent the UDP listening point to dispatch incoming messages to channels that were created by inbound connection * where name cannot be determined. When this arrives, there can be 2 channels for the same destination IP and strange problems can occur * where requests are sent through name qualified channel and response received through name unqualified channel. */ if (chan->has_name) lp->channels=belle_sip_list_prepend(lp->channels,chan); else lp->channels=belle_sip_list_append(lp->channels,chan); } belle_sip_channel_t *belle_sip_listening_point_create_channel(belle_sip_listening_point_t *obj, const belle_sip_hop_t *hop){ belle_sip_channel_t *chan=BELLE_SIP_OBJECT_VPTR(obj,belle_sip_listening_point_t)->create_channel(obj,hop); if (chan){ belle_sip_listening_point_add_channel(obj,chan); } return chan; } void belle_sip_listening_point_remove_channel(belle_sip_listening_point_t *lp, belle_sip_channel_t *chan){ belle_sip_channel_remove_listener(chan,lp->channel_listener); lp->channels=belle_sip_list_remove(lp->channels,chan); belle_sip_object_unref(chan); } void belle_sip_listening_point_clean_channels(belle_sip_listening_point_t *lp){ int existing_channels = belle_sip_listening_point_get_channel_count(lp); belle_sip_list_t* iterator; if (existing_channels > 0) { belle_sip_message("Listening point destroying [%i] channels",existing_channels); } for (iterator=lp->channels;iterator!=NULL;iterator=iterator->next) { belle_sip_channel_t *chan=(belle_sip_channel_t*)iterator->data; belle_sip_channel_force_close(chan); } lp->channels=belle_sip_list_free_with_data(lp->channels,(void (*)(void*))belle_sip_object_unref); } int belle_sip_listening_point_get_channel_count(const belle_sip_listening_point_t *lp){ return (int)belle_sip_list_size(lp->channels); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_listening_point_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_listening_point_t) { BELLE_SIP_VPTR_INIT(belle_sip_listening_point_t, belle_sip_object_t,FALSE), (belle_sip_object_destroy_t)belle_sip_listening_point_uninit, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, NULL, NULL BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END const char *belle_sip_listening_point_get_ip_address(const belle_sip_listening_point_t *lp){ return belle_sip_uri_get_host(lp->listening_uri); } int belle_sip_listening_point_get_port(const belle_sip_listening_point_t *lp){ return belle_sip_uri_get_listening_port(lp->listening_uri); } const char *belle_sip_listening_point_get_transport(const belle_sip_listening_point_t *lp){ return belle_sip_uri_get_transport_param(lp->listening_uri); } const belle_sip_uri_t* belle_sip_listening_point_get_uri(const belle_sip_listening_point_t *lp) { return lp->listening_uri; } int belle_sip_listening_point_get_well_known_port(const char *transport){ if (strcasecmp(transport,"UDP")==0 || strcasecmp(transport,"TCP")==0 ) return 5060; if (strcasecmp(transport,"DTLS")==0 || strcasecmp(transport,"TLS")==0 ) return 5061; belle_sip_error("No well known port for transport %s", transport); return -1; } belle_sip_channel_t *_belle_sip_listening_point_get_channel(belle_sip_listening_point_t *lp, const belle_sip_hop_t *hop, const struct addrinfo *addr){ return belle_sip_channel_find_from_list_with_addrinfo(lp->channels,hop,addr); } belle_sip_channel_t *belle_sip_listening_point_get_channel(belle_sip_listening_point_t *lp,const belle_sip_hop_t *hop){ return belle_sip_channel_find_from_list(lp->channels,lp->ai_family,hop); } static int send_keep_alive(belle_sip_channel_t* obj) { /*keep alive*/ const char* crlfcrlf = "\r\n\r\n"; size_t size=strlen(crlfcrlf); int err=belle_sip_channel_send(obj,crlfcrlf,size); if (err<=0 && !belle_sip_error_code_is_would_block(-err) && err!=-EINTR){ belle_sip_error("channel [%p]: could not send [%u] bytes of keep alive from [%s://%s:%i] to [%s:%i]" ,obj ,(unsigned int)size ,belle_sip_channel_get_transport_name(obj) ,obj->local_ip ,obj->local_port ,obj->peer_name ,obj->peer_port); return -1; }else{ belle_sip_message("channel [%p]: keep alive sent to [%s://%s:%i]" ,obj ,belle_sip_channel_get_transport_name(obj) ,obj->peer_name ,obj->peer_port); return 0; } } static int keep_alive_timer_func(void *user_data, unsigned int events) { belle_sip_listening_point_t* lp=(belle_sip_listening_point_t*)user_data; belle_sip_list_t* iterator; belle_sip_channel_t* channel; belle_sip_list_t *to_be_closed=NULL; for (iterator=lp->channels;iterator!=NULL;iterator=iterator->next) { channel=(belle_sip_channel_t*)iterator->data; if (channel->state == BELLE_SIP_CHANNEL_READY && send_keep_alive(channel)==-1) { /*only send keep alive if ready*/ to_be_closed=belle_sip_list_append(to_be_closed,channel); } } for (iterator=to_be_closed;iterator!=NULL;iterator=iterator->next){ channel=(belle_sip_channel_t*)iterator->data; channel_set_state(channel,BELLE_SIP_CHANNEL_ERROR); belle_sip_channel_close(channel); } belle_sip_list_free(to_be_closed); return BELLE_SIP_CONTINUE_WITHOUT_CATCHUP; } void belle_sip_listening_point_set_keep_alive(belle_sip_listening_point_t *lp,int ms) { if (ms <=0) { if (lp->keep_alive_timer) { belle_sip_main_loop_remove_source(lp->stack->ml,lp->keep_alive_timer); belle_sip_object_unref(lp->keep_alive_timer); lp->keep_alive_timer=NULL; } return; } if (!lp->keep_alive_timer) { lp->keep_alive_timer = belle_sip_main_loop_create_timeout(lp->stack->ml , keep_alive_timer_func , lp , ms ,"keep alive") ; } else { belle_sip_source_set_timeout(lp->keep_alive_timer,ms); } return; } int belle_sip_listening_point_get_keep_alive(const belle_sip_listening_point_t *lp) { return lp->keep_alive_timer?(int)belle_sip_source_get_timeout(lp->keep_alive_timer):-1; } void belle_sip_listening_point_set_channel_listener(belle_sip_listening_point_t *lp,belle_sip_channel_listener_t* channel_listener) { lp->channel_listener=channel_listener; } belle-sip-1.6.3/src/listeningpoint_internal.h000066400000000000000000000125061313437522400213070ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef LISTENINGPOINT_INTERNAL_H_ #define LISTENINGPOINT_INTERNAL_H_ BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_listening_point_t,belle_sip_object_t) const char *transport; belle_sip_channel_t * (*create_channel)(belle_sip_listening_point_t *, const belle_sip_hop_t *hop); BELLE_SIP_DECLARE_CUSTOM_VPTR_END #define BELLE_SIP_LISTENING_POINT(obj) BELLE_SIP_CAST(obj,belle_sip_listening_point_t) /* Listening points: base, udp */ struct belle_sip_listening_point{ belle_sip_object_t base; belle_sip_stack_t *stack; belle_sip_list_t *channels; belle_sip_uri_t* listening_uri; belle_sip_source_t* keep_alive_timer; belle_sip_channel_listener_t* channel_listener; /*initial channel listener used for channel creation, specially for socket server*/ int ai_family; /*AF_INET or AF_INET6*/ }; BELLE_SIP_BEGIN_DECLS void belle_sip_listening_point_init(belle_sip_listening_point_t *lp, belle_sip_stack_t *s, const char *address, int port); belle_sip_channel_t *_belle_sip_listening_point_get_channel(belle_sip_listening_point_t *lp,const belle_sip_hop_t *hop, const struct addrinfo *addr); belle_sip_channel_t *belle_sip_listening_point_create_channel(belle_sip_listening_point_t *ip, const belle_sip_hop_t *hop); void belle_sip_listening_point_remove_channel(belle_sip_listening_point_t *lp, belle_sip_channel_t *chan); int belle_sip_listening_point_get_well_known_port(const char *transport); belle_sip_channel_t *belle_sip_listening_point_get_channel(belle_sip_listening_point_t *lp, const belle_sip_hop_t *hop); void belle_sip_listening_point_add_channel(belle_sip_listening_point_t *lp, belle_sip_channel_t *chan); void belle_sip_listening_point_set_channel_listener(belle_sip_listening_point_t *lp,belle_sip_channel_listener_t* channel_listener); BELLE_SIP_END_DECLS /**udp*/ typedef struct belle_sip_udp_listening_point belle_sip_udp_listening_point_t; belle_sip_channel_t * belle_sip_channel_new_udp(belle_sip_stack_t *stack, int sock, const char *bindip, int localport, const char *peername, int peerport); belle_sip_channel_t * belle_sip_channel_new_udp_with_addr(belle_sip_stack_t *stack, int sock, const char *bindip, int localport, const struct addrinfo *ai); belle_sip_listening_point_t * belle_sip_udp_listening_point_new(belle_sip_stack_t *s, const char *ipaddress, int port); BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_udp_listening_point_t,belle_sip_listening_point_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END /*stream*/ typedef struct belle_sip_stream_listening_point belle_sip_stream_listening_point_t; struct belle_sip_stream_listening_point{ belle_sip_listening_point_t base; belle_sip_socket_t server_sock; belle_sip_source_t *source; }; BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_stream_listening_point_t,belle_sip_listening_point_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END void belle_sip_stream_listening_point_setup_server_socket(belle_sip_stream_listening_point_t *obj, belle_sip_source_func_t on_new_connection_cb ); void belle_sip_stream_listening_point_destroy_server_socket(belle_sip_stream_listening_point_t *lp); void belle_sip_stream_listening_point_init(belle_sip_stream_listening_point_t *obj, belle_sip_stack_t *s, const char *ipaddress, int port, belle_sip_source_func_t on_new_connection_cb ); belle_sip_listening_point_t * belle_sip_stream_listening_point_new(belle_sip_stack_t *s, const char *ipaddress, int port); /*tls*/ struct belle_sip_tls_listening_point{ belle_sip_stream_listening_point_t base; belle_tls_crypto_config_t *crypto_config; }; int belle_sip_tls_listening_point_available(void); BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_tls_listening_point_t,belle_sip_listening_point_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END #define BELLE_SIP_TLS_LISTENING_POINT(obj) BELLE_SIP_CAST(obj,belle_sip_tls_listening_point_t) belle_sip_listening_point_t * belle_sip_tls_listening_point_new(belle_sip_stack_t *s, const char *ipaddress, int port); belle_sip_channel_t * belle_sip_channel_new_tls(belle_sip_stack_t *s, belle_tls_verify_policy_t* verify_ctx, const char *bindip, int localport,const char *cname, const char *name, int port); /*tunnel*/ #ifdef HAVE_TUNNEL typedef struct belle_sip_tunnel_listening_point belle_sip_tunnel_listening_point_t; BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_tunnel_listening_point_t,belle_sip_listening_point_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END #define BELLE_SIP_TUNNEL_LISTENING_POINT(obj) BELLE_SIP_CAST(obj,belle_sip_tunnel_listening_point_t) belle_sip_channel_t * belle_sip_channel_new_tunnel(belle_sip_stack_t *s, void *tunnelclient, const char *bindip, int localport, const char *name, int port); #endif #include "transports/stream_channel.h" #endif /* LISTENINGPOINT_INTERNAL_H_ */ belle-sip-1.6.3/src/md5.c000066400000000000000000000303041313437522400150210ustar00rootroot00000000000000/* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "md5.h" #include #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void belle_sip_md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void belle_sip_md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void belle_sip_md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ belle_sip_md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ belle_sip_md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } belle-sip-1.6.3/src/md5.h000066400000000000000000000065361313437522400150400ustar00rootroot00000000000000/* Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke . 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED # define md5_INCLUDED /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus extern "C" { #endif /* Initialize the algorithm. */ void belle_sip_md5_init(md5_state_t *pms); /* Append a string to the message. */ void belle_sip_md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void belle_sip_md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ belle-sip-1.6.3/src/message.c000066400000000000000000001312241313437522400157630ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "grammars/belle_sip_messageLexer.h" #include "grammars/belle_sip_messageParser.h" #include "belle_sip_internal.h" typedef struct _headers_container { char* name; belle_sip_list_t* header_list; } headers_container_t; /*reference is * http://www.iana.org/assignments/sip-parameters/sip-parameters.xhtml#sip-parameters-2 */ static const char * expand_name(const char *name){ const char *full_name=NULL; if (strlen(name)>1) return name; switch(name[0]){ case 'a': full_name="Accept-Contact"; break; case 'u': full_name="Allow-Events"; break; case 'e': full_name="Content-Encoding"; break; case 'o': full_name="Event"; break; case 'y': full_name="Identity"; break; case 'n': full_name="Identity-Info"; break; case 'r': full_name="Refer-To"; break; case 'b': full_name="Referred-By"; break; case 'j': full_name="Reject-Contact"; break; case 'd': full_name="Request-Disposition"; break; case 'x': full_name="Session-Expires"; break; case 's': full_name="Subject"; break; case 'k': full_name="Supported"; break; default: full_name=name; } return full_name; } static headers_container_t* belle_sip_message_headers_container_new(const char* name) { headers_container_t* headers_container = belle_sip_new0(headers_container_t); headers_container->name = belle_sip_strdup(expand_name(name)); return headers_container; } static void belle_sip_headers_container_delete(headers_container_t *obj){ belle_sip_free(obj->name); belle_sip_list_free_with_data(obj->header_list,(void (*)(void*))belle_sip_object_unref); belle_sip_free(obj); } static void belle_sip_message_destroy(belle_sip_message_t *msg){ belle_sip_list_free_with_data(msg->header_list,(void (*)(void*))belle_sip_headers_container_delete); if (msg->body_handler) belle_sip_object_unref(msg->body_handler); } /*very sub-optimal clone method */ static void belle_sip_message_clone(belle_sip_message_t *obj, const belle_sip_message_t *orig){ headers_container_t *c; const belle_sip_list_t *l; for(l=orig->header_list;l!=NULL;l=l->next){ c=(headers_container_t*)l->data; if (c->header_list){ belle_sip_list_t * ll=belle_sip_list_copy_with_data(c->header_list,(void *(*)(void*))belle_sip_object_clone); belle_sip_message_add_headers(obj,ll); belle_sip_list_free(ll); } } } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_message_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_message_t,belle_sip_object_t,belle_sip_message_destroy,belle_sip_message_clone,NULL,TRUE); belle_sip_message_t* belle_sip_message_parse (const char* value) { size_t message_length; return belle_sip_message_parse_raw(value,strlen(value),&message_length); } belle_sip_message_t* belle_sip_message_parse_raw (const char* buff, size_t buff_length,size_t* message_length ) { \ pANTLR3_INPUT_STREAM input; pbelle_sip_messageLexer lex; pANTLR3_COMMON_TOKEN_STREAM tokens; pbelle_sip_messageParser parser; belle_sip_message_t* l_parsed_object; input = ANTLR_STREAM_NEW("message",buff,buff_length); lex = belle_sip_messageLexerNew (input); tokens = antlr3CommonTokenStreamSourceNew (1025, lex->pLexer->rec->state->tokSource); parser = belle_sip_messageParserNew (tokens); l_parsed_object = parser->message_raw(parser,message_length); /* if (*message_length < buff_length) {*/ /*there is a body*/ /* l_parsed_object->body_length=buff_length-*message_length; l_parsed_object->body = belle_sip_malloc(l_parsed_object->body_length+1); memcpy(l_parsed_object->body,buff+*message_length,l_parsed_object->body_length); l_parsed_object->body[l_parsed_object->body_length]='\0'; }*/ parser ->free(parser); tokens ->free(tokens); lex ->free(lex); input ->close(input); return l_parsed_object; } static int belle_sip_headers_container_comp_func(const headers_container_t *a, const char*b) { return strcasecmp(a->name,b); } void belle_sip_message_init(belle_sip_message_t *message){ } headers_container_t* belle_sip_headers_container_get(const belle_sip_message_t* message,const char* header_name) { belle_sip_list_t * result = belle_sip_list_find_custom( message->header_list , (belle_sip_compare_func)belle_sip_headers_container_comp_func , header_name); return result?(headers_container_t*)(result->data):NULL; } headers_container_t * get_or_create_container(belle_sip_message_t *message, const char *header_name){ // first check if already exist headers_container_t* headers_container = belle_sip_headers_container_get(message,header_name); if (headers_container == NULL) { headers_container = belle_sip_message_headers_container_new(header_name); message->header_list=belle_sip_list_append(message->header_list,headers_container); } return headers_container; } void belle_sip_message_add_first(belle_sip_message_t *message,belle_sip_header_t* header) { headers_container_t *headers_container=get_or_create_container(message,belle_sip_header_get_name(header)); headers_container->header_list=belle_sip_list_prepend(headers_container->header_list,belle_sip_object_ref(header)); } void belle_sip_message_add_header(belle_sip_message_t *message,belle_sip_header_t* header) { headers_container_t *headers_container=get_or_create_container(message,belle_sip_header_get_name(header)); headers_container->header_list=belle_sip_list_append(headers_container->header_list,belle_sip_object_ref(header)); } void belle_sip_message_add_headers(belle_sip_message_t *message, const belle_sip_list_t *header_list){ const char *hname; headers_container_t *headers_container; if (header_list == NULL) return; hname=belle_sip_header_get_name(BELLE_SIP_HEADER((header_list->data))); headers_container=get_or_create_container(message,hname); for(;header_list!=NULL;header_list=header_list->next){ belle_sip_header_t *h=BELLE_SIP_HEADER(header_list->data); if (strcmp(belle_sip_header_get_name(h),hname)!=0){ belle_sip_fatal("Bad use of belle_sip_message_add_headers(): all headers of the list must be of the same type."); return ; } headers_container->header_list=belle_sip_list_append(headers_container->header_list,belle_sip_object_ref(h)); } } void belle_sip_message_set_header(belle_sip_message_t *msg, belle_sip_header_t* header){ headers_container_t *headers_container=get_or_create_container(msg,belle_sip_header_get_name(header)); belle_sip_object_ref(header); headers_container->header_list=belle_sip_list_free_with_data(headers_container->header_list,belle_sip_object_unref); headers_container->header_list=belle_sip_list_append(headers_container->header_list,header); } const belle_sip_list_t* belle_sip_message_get_headers(const belle_sip_message_t *message,const char* header_name) { headers_container_t* headers_container = belle_sip_headers_container_get(message,header_name); return headers_container ? headers_container->header_list:NULL; } belle_sip_object_t *_belle_sip_message_get_header_by_type_id(const belle_sip_message_t *message, belle_sip_type_id_t id){ const belle_sip_list_t *e1; for(e1=message->header_list;e1!=NULL;e1=e1->next){ headers_container_t* headers_container=(headers_container_t*)e1->data; if (headers_container->header_list){ belle_sip_object_t *ret=headers_container->header_list->data; if (ret->vptr->id==id) return ret; } } return NULL; } void belle_sip_message_remove_first(belle_sip_message_t *msg, const char *header_name){ headers_container_t* headers_container = belle_sip_headers_container_get(msg,header_name); if (headers_container && headers_container->header_list){ belle_sip_list_t *to_be_removed=headers_container->header_list; headers_container->header_list=belle_sip_list_remove_link(headers_container->header_list,to_be_removed); belle_sip_list_free_with_data(to_be_removed,belle_sip_object_unref); } } void belle_sip_message_remove_last(belle_sip_message_t *msg, const char *header_name){ headers_container_t* headers_container = belle_sip_headers_container_get(msg,header_name); if (headers_container && headers_container->header_list){ belle_sip_list_t *to_be_removed=belle_sip_list_last_elem(headers_container->header_list); headers_container->header_list=belle_sip_list_remove_link(headers_container->header_list,to_be_removed); belle_sip_list_free_with_data(to_be_removed,belle_sip_object_unref); } } void belle_sip_message_remove_header(belle_sip_message_t *msg, const char *header_name){ headers_container_t* headers_container = belle_sip_headers_container_get(msg,header_name); if (headers_container){ msg->header_list = belle_sip_list_remove(msg->header_list,headers_container); belle_sip_headers_container_delete(headers_container); } } void belle_sip_message_remove_header_from_ptr(belle_sip_message_t *msg, belle_sip_header_t* header) { headers_container_t* headers_container = belle_sip_headers_container_get(msg,belle_sip_header_get_name(header)); belle_sip_list_t* it; it=belle_sip_list_find(headers_container->header_list,header); if (it) { belle_sip_object_unref(header); headers_container->header_list=belle_sip_list_delete_link(headers_container->header_list,it); if (belle_sip_list_size(headers_container->header_list) == 0) { msg->header_list = belle_sip_list_remove(msg->header_list,headers_container); belle_sip_headers_container_delete(headers_container); } } } /* belle_sip_error_code belle_sip_message_named_headers_marshal(belle_sip_message_t *message, const char* header_name, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=BELLE_SIP_OK; belle_sip_list_t* header_list = belle_sip_message_get_headers(message,header_name); if (!header_list) { belle_sip_error("headers [%s] not found",header_name); return 0; } for(;header_list!=NULL;header_list=header_list->next){ belle_sip_header_t *h=BELLE_SIP_HEADER(header_list->data); error=belle_sip_object_marshal(BELLE_SIP_OBJECT(h),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s","\r\n"); if (error!=BELLE_SIP_OK) return error; } return error; } #define MARSHAL_AND_CHECK_HEADER(header) \ if (current_offset == (current_offset+=(header))) {\ belle_sip_error("missing mandatory header");\ return current_offset;\ } else {\ current_offset+=snprintf(buff+current_offset,buff_size-current_offset,"%s","\r\n");\ } */ typedef void (*each_header_cb)(const belle_sip_header_t* header,void* userdata); static void belle_sip_message_for_each_header(const belle_sip_message_t *message,each_header_cb cb,void* user_data) { belle_sip_list_t* headers_list; belle_sip_list_t* header_list; for(headers_list=message->header_list;headers_list!=NULL;headers_list=headers_list->next){ for(header_list=((headers_container_t*)(headers_list->data))->header_list ;header_list!=NULL ;header_list=header_list->next) { cb(BELLE_SIP_HEADER(header_list->data),user_data); } } return; } static void append_header(const belle_sip_header_t* header,void* user_data) { *(belle_sip_list_t**)user_data=belle_sip_list_append((*(belle_sip_list_t**)user_data),(void*)header); } belle_sip_list_t* belle_sip_message_get_all_headers(const belle_sip_message_t *message) { belle_sip_list_t* headers=NULL; belle_sip_message_for_each_header(message,append_header,&headers); return headers; } belle_sip_error_code belle_sip_headers_marshal(belle_sip_message_t *message, char* buff, size_t buff_size, size_t *offset) { /*FIXME, replace this code by belle_sip_message_for_each_header*/ belle_sip_list_t* headers_list; belle_sip_list_t* header_list; belle_sip_error_code error=BELLE_SIP_OK; #ifdef BELLE_SIP_WORKAROUND_TECHNICOLOR_SIP_ALG_ROUTER_BUG belle_sip_header_t *content_length=NULL; #endif for(headers_list=message->header_list;headers_list!=NULL;headers_list=headers_list->next){ for(header_list=((headers_container_t*)(headers_list->data))->header_list ;header_list!=NULL ;header_list=header_list->next) { belle_sip_header_t *h=BELLE_SIP_HEADER(header_list->data); #ifdef BELLE_SIP_WORKAROUND_TECHNICOLOR_SIP_ALG_ROUTER_BUG if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(h,belle_sip_header_content_length_t)){ content_length=h; }else #endif { while (h!=NULL) { /*header can be chained*/ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(h),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s","\r\n"); if (error!=BELLE_SIP_OK) return error; h= belle_sip_header_get_next(h); } } } } #ifdef BELLE_SIP_WORKAROUND_TECHNICOLOR_SIP_ALG_ROUTER_BUG if (content_length){ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(content_length),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset,"%s","\r\n"); if (error!=BELLE_SIP_OK) return error; } #endif error=belle_sip_snprintf(buff,buff_size,offset,"%s","\r\n"); if (error!=BELLE_SIP_OK) return error; return error; } static void belle_sip_request_destroy(belle_sip_request_t* request) { if (request->method) belle_sip_free(request->method); if (request->uri) belle_sip_object_unref(request->uri); if (request->absolute_uri) belle_sip_object_unref(request->absolute_uri); if (request->dialog) belle_sip_object_unref(request->dialog); if (request->rfc2543_branch) belle_sip_free(request->rfc2543_branch); } static void belle_sip_request_init(belle_sip_request_t *message){ } static void belle_sip_request_clone(belle_sip_request_t *request, const belle_sip_request_t *orig){ if (orig->method) request->method=belle_sip_strdup(orig->method); if (orig->uri) request->uri=(belle_sip_uri_t*)belle_sip_object_ref(belle_sip_object_clone((belle_sip_object_t*)orig->uri)); if (orig->absolute_uri) request->absolute_uri=(belle_generic_uri_t*)belle_sip_object_ref(belle_sip_object_clone((belle_sip_object_t*)orig->absolute_uri)); if (orig->rfc2543_branch) request->rfc2543_branch=belle_sip_strdup(orig->rfc2543_branch); } belle_sip_error_code belle_sip_request_marshal(belle_sip_request_t* request, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_snprintf(buff,buff_size,offset,"%s ",belle_sip_request_get_method(request) ? belle_sip_request_get_method(request) : ""); if (error!=BELLE_SIP_OK) return error; if (request->uri) error=belle_sip_uri_marshal(belle_sip_request_get_uri(request),buff,buff_size,offset); else if (request->absolute_uri) error=belle_generic_uri_marshal(belle_sip_request_get_absolute_uri(request),buff,buff_size,offset); else { belle_sip_error("Missing uri for marshaling request [%p]",request); /*fixme better to have an error code*/ error=BELLE_SIP_OK; } if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff,buff_size,offset," %s","SIP/2.0\r\n"); if (error!=BELLE_SIP_OK) return error; error=belle_sip_headers_marshal(BELLE_SIP_MESSAGE(request),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW(request,message) BELLE_SIP_PARSE(request) GET_SET_STRING(belle_sip_request,method); GET_SET_STRING(belle_sip_request,rfc2543_branch); /*caching of the dialog in the request, used when creating a request in dialog to avoid dialog lookup*/ void belle_sip_request_set_dialog(belle_sip_request_t *req, belle_sip_dialog_t *dialog){ SET_OBJECT_PROPERTY(req,dialog,dialog); } void belle_sip_request_set_uri(belle_sip_request_t* request,belle_sip_uri_t* uri) { SET_OBJECT_PROPERTY(request,uri,uri); if (request->absolute_uri && uri) { belle_sip_warning("absolute uri [%p] already set for request [%p], cleaning it",request->absolute_uri, request); belle_sip_request_set_absolute_uri(request,NULL); } } belle_sip_uri_t * belle_sip_request_get_uri(const belle_sip_request_t *request){ return request->uri; } void belle_sip_request_set_absolute_uri(belle_sip_request_t* request,belle_generic_uri_t* absolute_uri) { SET_OBJECT_PROPERTY(request,absolute_uri,absolute_uri); if (request->uri && absolute_uri) { belle_sip_warning("sip uri [%p] already set for request [%p], cleaning it",request->uri, request); belle_sip_request_set_uri(request,NULL); } } belle_generic_uri_t * belle_sip_request_get_absolute_uri(const belle_sip_request_t *request){ return request->absolute_uri; } belle_sip_uri_t* belle_sip_request_extract_origin(const belle_sip_request_t* req) { belle_sip_header_via_t* via_header = belle_sip_message_get_header_by_type(req,belle_sip_header_via_t); belle_sip_uri_t* uri=NULL; const char* received = belle_sip_header_via_get_received(via_header); int rport = belle_sip_header_via_get_rport(via_header); uri = belle_sip_uri_new(); if (received!=NULL) { belle_sip_uri_set_host(uri,received); } else { belle_sip_uri_set_host(uri,belle_sip_header_via_get_host(via_header)); } if (rport>0) { belle_sip_uri_set_port(uri,rport); } else if (belle_sip_header_via_get_port(via_header)) { belle_sip_uri_set_port(uri,belle_sip_header_via_get_port(via_header)); } if (belle_sip_header_via_get_transport(via_header)) { belle_sip_uri_set_transport_param(uri,belle_sip_header_via_get_transport_lowercase(via_header)); } return uri; } int belle_sip_message_is_request(belle_sip_message_t *msg){ return BELLE_SIP_IS_INSTANCE_OF(BELLE_SIP_OBJECT(msg),belle_sip_request_t); } int belle_sip_message_is_response(const belle_sip_message_t *msg){ return BELLE_SIP_IS_INSTANCE_OF(BELLE_SIP_OBJECT(msg),belle_sip_response_t); } belle_sip_header_t *belle_sip_message_get_header(const belle_sip_message_t *msg, const char *header_name){ const belle_sip_list_t *l=belle_sip_message_get_headers(msg,header_name); if (l!=NULL) return (belle_sip_header_t*)l->data; return NULL; } char *belle_sip_message_to_string(belle_sip_message_t *msg){ return belle_sip_object_to_string(BELLE_SIP_OBJECT(msg)); } belle_sip_body_handler_t *belle_sip_message_get_body_handler(const belle_sip_message_t *msg){ return msg->body_handler; } void belle_sip_message_set_body_handler(belle_sip_message_t *msg, belle_sip_body_handler_t *body_handler){ belle_sip_header_content_length_t *content_length_header = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_length_t); belle_sip_header_content_type_t *content_type_header = belle_sip_message_get_header_by_type(msg, belle_sip_header_content_type_t); /* In case of multipart message, we must add the message Content-Type header containing the boundary */ if (body_handler != NULL) { if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(body_handler, belle_sip_multipart_body_handler_t)){ belle_sip_multipart_body_handler_t *multipart_body_handler = BELLE_SIP_MULTIPART_BODY_HANDLER(body_handler); belle_sip_header_content_type_t * content_type = belle_sip_header_content_type_new(); belle_sip_header_content_type_set_type(content_type, "multipart"); if (belle_sip_multipart_body_handler_is_related(multipart_body_handler)) { const belle_sip_list_t *parts = belle_sip_multipart_body_handler_get_parts(multipart_body_handler); if (parts) { belle_sip_body_handler_t *first_part=BELLE_SIP_BODY_HANDLER(parts->data); const belle_sip_list_t *first_part_headers = belle_sip_body_handler_get_headers(first_part); belle_sip_list_t *it; belle_sip_header_content_type_t *first_part_content_type=NULL;; for(it = (belle_sip_list_t *)first_part_headers;it!=NULL;it=it->next) { belle_sip_header_t *header = BELLE_SIP_HEADER(it->data); if(strcasecmp("Content-Type",belle_sip_header_get_name(header)) == 0) { first_part_content_type=BELLE_SIP_HEADER_CONTENT_TYPE(header); break; } } if (first_part_content_type) { char *type_slash_subtype = belle_sip_strdup_printf("%s/%s" , belle_sip_header_content_type_get_type(first_part_content_type) , belle_sip_header_content_type_get_subtype(first_part_content_type)); belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(content_type), "type", type_slash_subtype); belle_sip_free(type_slash_subtype); } else { belle_sip_error("Multipart related body handler [%p] cannot be set without first part content type header",body_handler); } } else { belle_sip_error("Multipart related body handler [%p] cannot be set without first part",body_handler); } belle_sip_header_content_type_set_subtype(content_type, "related"); } else { belle_sip_header_content_type_set_subtype(content_type, "form-data"); } belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(content_type), "boundary", BELLESIP_MULTIPART_BOUNDARY); belle_sip_message_add_header(BELLE_SIP_MESSAGE(msg), BELLE_SIP_HEADER(content_type)); } else { const belle_sip_list_t *headers = belle_sip_body_handler_get_headers(body_handler); for(; headers != NULL; headers = headers->next) { belle_sip_header_t *header = BELLE_SIP_HEADER(headers->data); if (strcasecmp(belle_sip_header_get_name(header),BELLE_SIP_CONTENT_LENGTH ) == 0 && content_length_header) belle_sip_message_remove_header_from_ptr(msg, BELLE_SIP_HEADER(content_length_header)); if (strcasecmp(belle_sip_header_get_name(header),BELLE_SIP_CONTENT_TYPE ) == 0 && content_type_header) belle_sip_message_remove_header_from_ptr(msg, BELLE_SIP_HEADER(content_type_header)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(msg), header); } } } else { if (content_length_header != NULL) belle_sip_message_remove_header_from_ptr(msg, BELLE_SIP_HEADER(content_length_header)); if (content_type_header != NULL) belle_sip_message_remove_header_from_ptr(msg, BELLE_SIP_HEADER(content_type_header)); } SET_OBJECT_PROPERTY(msg,body_handler,body_handler); } const char* belle_sip_message_get_body(belle_sip_message_t *msg) { if (msg->body_handler==NULL) return NULL; if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(msg->body_handler, belle_sip_memory_body_handler_t)){ return (const char*)belle_sip_memory_body_handler_get_buffer( BELLE_SIP_MEMORY_BODY_HANDLER(msg->body_handler) ); } belle_sip_error("belle_sip_message_get_body(): body cannot be returned as pointer."); return NULL; } size_t belle_sip_message_get_body_size(const belle_sip_message_t *msg){ if (msg->body_handler==NULL) return 0; return belle_sip_body_handler_get_size(msg->body_handler); } void belle_sip_message_set_body(belle_sip_message_t *msg, const char* body, size_t size) { belle_sip_body_handler_t *bh=NULL; if (body && size) bh=(belle_sip_body_handler_t*)belle_sip_memory_body_handler_new_copy_from_buffer(body,size,NULL,NULL); belle_sip_message_set_body_handler(msg,bh); } void belle_sip_message_assign_body(belle_sip_message_t *msg, char* body, size_t size) { belle_sip_body_handler_t *bh=(belle_sip_body_handler_t*)belle_sip_memory_body_handler_new_from_buffer(body,size,NULL,NULL); belle_sip_message_set_body_handler(msg,bh); } struct _belle_sip_response{ belle_sip_message_t base; char *sip_version; int status_code; char *reason_phrase; }; typedef struct code_phrase{ int code; const char *phrase; } code_phrase_t; static code_phrase_t well_known_codes[]={ { 100 , "Trying" }, { 101 , "Dialog establishment" }, { 180 , "Ringing" }, { 181 , "Call is being forwarded" }, { 182 , "Queued" }, { 183 , "Session progress" }, { 200 , "Ok" }, { 202 , "Accepted" }, { 300 , "Multiple choices" }, { 301 , "Moved permanently" }, { 302 , "Moved temporarily" }, { 305 , "Use proxy" }, { 380 , "Alternate contact" }, { 400 , "Bad request" }, { 401 , "Unauthorized" }, { 402 , "Payment required" }, { 403 , "Forbidden" }, { 404 , "Not found" }, { 405 , "Method not allowed" }, { 406 , "Not acceptable" }, { 407 , "Proxy authentication required" }, { 408 , "Request timeout" }, { 410 , "Gone" }, { 412 , "Conditional Request Failed" }, /*rfc3903*/ { 413 , "Request entity too large" }, { 414 , "Request-URI too long" }, { 415 , "Unsupported media type" }, { 416 , "Unsupported URI scheme" }, { 420 , "Bad extension" }, { 421 , "Extension required" }, { 423 , "Interval too brief" }, { 480 , "Temporarily unavailable" }, { 481 , "Call/transaction does not exist" }, { 482 , "Loop detected" }, { 483 , "Too many hops" }, { 484 , "Address incomplete" }, { 485 , "Ambiguous" }, { 486 , "Busy here" }, { 487 , "Request terminated" }, { 488 , "Not acceptable here" }, { 489 , "Bad Event" }, /*rfc3265*/ { 491 , "Request pending" }, { 493 , "Undecipherable" }, { 500 , "Server internal error" }, { 501 , "Not implemented" }, { 502 , "Bad gateway" }, { 503 , "Service unavailable" }, { 504 , "Server time-out" }, { 505 , "Version not supported" }, { 513 , "Message too large" }, { 600 , "Busy everywhere" }, { 603 , "Decline" }, { 604 , "Does not exist anywhere" }, { 606 , "Not acceptable" }, { 0 , NULL } }; const char *belle_sip_get_well_known_reason_phrase(int status_code){ int i; for(i=0;well_known_codes[i].code!=0;++i){ if (well_known_codes[i].code==status_code) return well_known_codes[i].phrase; } return "Unknown reason"; } void belle_sip_response_destroy(belle_sip_response_t *resp){ if (resp->sip_version) belle_sip_free(resp->sip_version); if (resp->reason_phrase) belle_sip_free(resp->reason_phrase); } static void belle_sip_response_init(belle_sip_response_t *resp){ } static void belle_sip_response_clone(belle_sip_response_t *resp, const belle_sip_response_t *orig){ if (orig->sip_version) resp->sip_version=belle_sip_strdup(orig->sip_version); if (orig->reason_phrase) resp->reason_phrase=belle_sip_strdup(orig->reason_phrase); } belle_sip_error_code belle_sip_response_marshal(belle_sip_response_t *resp, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=belle_sip_snprintf( buff ,buff_size ,offset ,"SIP/2.0 %i %s\r\n" ,belle_sip_response_get_status_code(resp) ,belle_sip_response_get_reason_phrase(resp)?belle_sip_response_get_reason_phrase(resp):""); if (error!=BELLE_SIP_OK) return error; error=belle_sip_headers_marshal(BELLE_SIP_MESSAGE(resp),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; return error; } BELLE_SIP_NEW(response,message); BELLE_SIP_PARSE(response) GET_SET_STRING(belle_sip_response,reason_phrase); GET_SET_INT(belle_sip_response,status_code,int) static int is_authorized_uri_header(const char* header_name) { /*From, Call-ID, CSeq, Via, and Record-Route*/ /*Accept, Accept-Encoding, Accept-Language, Allow, Contact (in its dialog usage), Organization, Supported, and User-Agent*/ return (strcasecmp("From",header_name) != 0 && strcasecmp("Call-ID",header_name) != 0 && strcasecmp("CSeq",header_name) != 0 && strcasecmp("Via",header_name) != 0 && strcasecmp("Record-Route",header_name) != 0 && strcasecmp("Accept",header_name) != 0 && strcasecmp("Accept-Encoding",header_name) != 0 && strcasecmp("Accept-Language",header_name) != 0 && strcasecmp("Allow",header_name) != 0 && strcasecmp("Contact",header_name) != 0 && strcasecmp("Organization",header_name) != 0 && strcasecmp("Supported",header_name) != 0 && strcasecmp("User-Agent",header_name) != 0); } belle_sip_request_t* belle_sip_request_create(belle_sip_uri_t *requri, const char* method, belle_sip_header_call_id_t *callid, belle_sip_header_cseq_t * cseq, belle_sip_header_from_t *from, belle_sip_header_to_t *to, belle_sip_header_via_t *via, int max_forward) { belle_sip_request_t *ret=belle_sip_request_new(); belle_sip_header_max_forwards_t *mf=belle_sip_header_max_forwards_new(); belle_sip_list_t* iterator; if (max_forward==0) max_forward=70; belle_sip_header_max_forwards_set_max_forwards(mf,max_forward); belle_sip_request_set_method(ret,method); belle_sip_message_add_header((belle_sip_message_t*)ret,BELLE_SIP_HEADER(via)); belle_sip_message_add_header((belle_sip_message_t*)ret,BELLE_SIP_HEADER(from)); if (to) belle_sip_message_add_header((belle_sip_message_t*)ret,BELLE_SIP_HEADER(to)); /*to might be in header uri*/ belle_sip_message_add_header((belle_sip_message_t*)ret,BELLE_SIP_HEADER(cseq)); belle_sip_message_add_header((belle_sip_message_t*)ret,BELLE_SIP_HEADER(callid)); if (!belle_sip_message_get_header_by_type(ret,belle_sip_header_max_forwards_t)) belle_sip_message_add_header((belle_sip_message_t*)ret,BELLE_SIP_HEADER(mf)); else belle_sip_object_unref(mf); /* 19.1.5 Forming Requests from a URI An implementation needs to take care when forming requests directly from a URI. URIs from business cards, web pages, and even from sources inside the protocol such as registered contacts may contain inappropriate header fields or body parts. An implementation MUST include any provided transport, maddr, ttl, or user parameter in the Request-URI of the formed request. If the URI contains a method parameter, its value MUST be used as the method of the request. The method parameter MUST NOT be placed in the Request-URI. Unknown URI parameters MUST be placed in the message's Request-URI. An implementation SHOULD treat the presence of any headers or body parts in the URI as a desire to include them in the message, and choose to honor the request on a per-component basis. An implementation SHOULD NOT honor these obviously dangerous header fields: From, Call-ID, CSeq, Via, and Record-Route. An implementation SHOULD NOT honor any requested Route header field values in order to not be used as an unwitting agent in malicious attacks. An implementation SHOULD NOT honor requests to include header fields that may cause it to falsely advertise its location or capabilities. These include: Accept, Accept-Encoding, Accept-Language, Allow, Contact (in its dialog usage), Organization, Supported, and User- Agent. An implementation SHOULD verify the accuracy of any requested descriptive header fields, including: Content-Disposition, Content- Encoding, Content-Language, Content-Length, Content-Type, Date, Mime-Version, and Timestamp.*/ if (belle_sip_uri_get_header_names(requri)) { for (iterator=(belle_sip_list_t*)belle_sip_uri_get_header_names(requri);iterator!=NULL;iterator=iterator->next) { const char* header_name=(const char*)iterator->data; /*1 check header name*/ if (is_authorized_uri_header(header_name)) { belle_sip_header_extension_t* extended_header = belle_sip_header_extension_create(header_name, belle_sip_uri_get_header(requri, header_name)); if (extended_header) { belle_sip_message_add_header((belle_sip_message_t*)ret,BELLE_SIP_HEADER(extended_header)); } } else { belle_sip_warning("Skiping uri header [%s] for request [%p]",header_name,requri); } } } belle_sip_uri_headers_clean(requri); /*remove all headers*/ belle_sip_request_set_uri(ret,requri); return ret; } static void belle_sip_response_init_default(belle_sip_response_t *resp, int status_code, const char *phrase){ resp->status_code=status_code; resp->sip_version=belle_sip_strdup("SIP/2.0"); if (phrase==NULL) phrase=belle_sip_get_well_known_reason_phrase(status_code); resp->reason_phrase=belle_sip_strdup(phrase); } /* * note: we must not assume the request to be well formed because this function may be used to generate 400 Bad request response. */ belle_sip_response_t *belle_sip_response_create_from_request(belle_sip_request_t *req, int status_code){ belle_sip_response_t *resp=belle_sip_response_new(); belle_sip_header_t *h; belle_sip_header_to_t *to; const belle_sip_list_t *vias; belle_sip_response_init_default(resp,status_code,NULL); if (status_code==100 && (h=belle_sip_message_get_header((belle_sip_message_t*)req,"timestamp"))){ belle_sip_message_add_header((belle_sip_message_t*)resp,h); } vias=belle_sip_message_get_headers ((belle_sip_message_t*)req,"via"); belle_sip_message_add_headers((belle_sip_message_t*)resp,vias); h=belle_sip_message_get_header((belle_sip_message_t*)req,"from"); if (h) belle_sip_message_add_header((belle_sip_message_t*)resp,h); h=belle_sip_message_get_header((belle_sip_message_t*)req,"to"); if (h){ if (status_code!=100){ //so that to tag can be added to=(belle_sip_header_to_t*)belle_sip_object_clone((belle_sip_object_t*)h); }else{ to=(belle_sip_header_to_t*)h; } belle_sip_message_add_header((belle_sip_message_t*)resp,(belle_sip_header_t*)to); } h=belle_sip_message_get_header((belle_sip_message_t*)req,"call-id"); if (h) belle_sip_message_add_header((belle_sip_message_t*)resp,h); h=belle_sip_message_get_header((belle_sip_message_t*)req,"cseq"); if (h){ belle_sip_message_add_header((belle_sip_message_t*)resp,h); } return resp; } /* 12.1.1 UAS behavior When a UAS responds to a request with a response that establishes a dialog (such as a 2xx to INVITE), the UAS MUST copy all Record-Route header field values from the request into the response (including the URIs, URI parameters, and any Record-Route header field parameters, whether they are known or unknown to the UAS) and MUST maintain the order of those values. */ void belle_sip_response_fill_for_dialog(belle_sip_response_t *obj, belle_sip_request_t *req){ const belle_sip_list_t *rr=belle_sip_message_get_headers((belle_sip_message_t*)req,BELLE_SIP_RECORD_ROUTE); belle_sip_header_contact_t *ct=belle_sip_message_get_header_by_type(obj,belle_sip_header_contact_t); belle_sip_message_remove_header((belle_sip_message_t*)obj,BELLE_SIP_RECORD_ROUTE); if (rr) belle_sip_message_add_headers((belle_sip_message_t*)obj,rr); if (belle_sip_response_get_status_code(obj)>=200 && belle_sip_response_get_status_code(obj)<300 && !ct){ const char *method=belle_sip_request_get_method(req); if (strcmp(method,"INVITE")==0 || strcmp(method,"SUBSCRIBE")==0){ /*add a dummy contact to be filled by channel later*/ belle_sip_message_add_header((belle_sip_message_t*)obj,(belle_sip_header_t*)belle_sip_header_contact_new()); } } } belle_sip_hop_t* belle_sip_response_get_return_hop(belle_sip_response_t *msg){ belle_sip_header_via_t *via=BELLE_SIP_HEADER_VIA(belle_sip_message_get_header(BELLE_SIP_MESSAGE(msg),"via")); if (via){ const char *host=belle_sip_header_via_get_received(via) ? belle_sip_header_via_get_received(via) : belle_sip_header_via_get_host(via); int port=belle_sip_header_via_get_rport(via)>0 ? belle_sip_header_via_get_rport(via) : belle_sip_header_via_get_listening_port(via); return belle_sip_hop_new(belle_sip_header_via_get_transport_lowercase(via),NULL,host,port); } return NULL; } int belle_sip_response_fix_contact(const belle_sip_response_t* response,belle_sip_header_contact_t* contact) { belle_sip_header_via_t* via_header; belle_sip_uri_t* contact_uri; const char* received; int rport; int contact_port; /*first check received/rport*/ via_header= (belle_sip_header_via_t*)belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_VIA); received = belle_sip_header_via_get_received(via_header); rport = belle_sip_header_via_get_rport(via_header); contact_uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(contact)); if (received) { /*need to update host*/ belle_sip_uri_set_host(contact_uri,received); } else { belle_sip_uri_set_host(contact_uri,belle_sip_header_via_get_host(via_header)); } contact_port = belle_sip_uri_get_port(contact_uri); if (rport>0 ) { /*need to update port*/ if ((rport+contact_port)!=5060) belle_sip_uri_set_port(contact_uri,rport); } else if ((belle_sip_header_via_get_port(via_header)+contact_port)!=5060) { belle_sip_uri_set_port(contact_uri,belle_sip_header_via_get_port(via_header)); } /*try to fix transport if needed (very unlikely)*/ if (strcasecmp(belle_sip_header_via_get_transport(via_header),"UDP")!=0) { if (!belle_sip_uri_get_transport_param(contact_uri) ||strcasecmp(belle_sip_uri_get_transport_param(contact_uri),belle_sip_header_via_get_transport(via_header))!=0) { belle_sip_uri_set_transport_param(contact_uri,belle_sip_header_via_get_transport_lowercase(via_header)); } } else { if (belle_sip_uri_get_transport_param(contact_uri)) { belle_sip_uri_set_transport_param(contact_uri,NULL); } } return 0; } belle_sip_request_t * belle_sip_request_clone_with_body(const belle_sip_request_t *initial_req) { belle_sip_request_t* req=BELLE_SIP_REQUEST(belle_sip_object_clone(BELLE_SIP_OBJECT(initial_req))); if (initial_req->base.body_handler) req->base.body_handler=BELLE_SIP_BODY_HANDLER(belle_sip_object_clone_and_ref( (belle_sip_object_t*)initial_req->base.body_handler)); return req; } typedef struct message_header_list { const char* method; const char* headers[10]; /*MAX headers*/ } message_header_list_t; /** RFC 3261 SIP: Session Initiation Protocol June 2002 Header field where proxy ACK BYE CAN INV OPT REG __________________________________________________________________ Accept R - o - o m* o Accept 2xx - - - o m* o Accept 415 - c - c c c Accept-Encoding R - o - o o o Accept-Encoding 2xx - - - o m* o Accept-Encoding 415 - c - c c c Accept-Language R - o - o o o Accept-Language 2xx - - - o m* o Accept-Language 415 - c - c c c Alert-Info R ar - - - o - - Alert-Info 180 ar - - - o - - Allow R - o - o o o Allow 2xx - o - m* m* o Allow r - o - o o o Allow 405 - m - m m m Authentication-Info 2xx - o - o o o Authorization R o o o o o o Call-ID c r m m m m m m Call-Info ar - - - o o o Contact R o - - m o o Contact 1xx - - - o - - Contact 2xx - - - m o o Contact 3xx d - o - o o o Contact 485 - o - o o o Content-Disposition o o - o o o Content-Encoding o o - o o o Content-Language o o - o o o Content-Length ar t t t t t t Content-Type * * - * * * CSeq c r m m m m m m Date a o o o o o o Error-Info 300-699 a - o o o o o Expires - - - o - o From c r m m m m m m In-Reply-To R - - - o - - Max-Forwards R amr m m m m m m Min-Expires 423 - - - - - m MIME-Version o o - o o o Organization ar - - - o o o Priority R ar - - - o - - Proxy-Authenticate 407 ar - m - m m m Proxy-Authenticate 401 ar - o o o o o Proxy-Authorization R dr o o - o o o Proxy-Require R ar - o - o o o Record-Route R ar o o o o o - Record-Route 2xx,18x mr - o o o o - Reply-To - - - o - - Require ar - c - c c c Retry-After 404,413,480,486 - o o o o o 500,503 - o o o o o 600,603 - o o o o o Route R adr c c c c c c Server r - o o o o o Subject R - - - o - - Supported R - o o m* o o Supported 2xx - o o m* m* o Timestamp o o o o o o To c(1) r m m m m m m Unsupported 420 - m - m m m User-Agent o o o o o o Via R amr m m m m m m Via rc dr m m m m m m Warning r - o o o o o WWW-Authenticate 401 ar - m - m m m WWW-Authenticate 407 ar - o - o o o Table 3: Summary of header fields, A--Z; (1): copied with possible addition of tag */ static message_header_list_t mandatory_headers[] = { {"REGISTER",{"Call-ID","CSeq","From", "Max-Forwards","To","Via",NULL}}, {"INVITE",{"Contact","Call-ID","CSeq","From", "Max-Forwards","To","Via",NULL}}, {"CANCEL",{"Call-ID","CSeq","From", "Max-Forwards","To","Via",NULL}}, {"BYE",{"Call-ID","CSeq","From", "Max-Forwards","To","Via",NULL}}, {"ACK",{"Call-ID","CSeq","From", "Max-Forwards","To","Via",NULL}}, {"*", { "To", "From", "CSeq", "Via", NULL}}, /* catch-all, these fields are required all the time. */ {NULL,{NULL}} }; /*static int belle_sip_message_is_mandatody_header(const char* method, const char* header_name) { int i; for (i=0;mandatory_headers[i].method!=NULL;i++) { if (strcasecmp(method,mandatory_headers[i].method)==0) { int j; for(j=0;mandatory_headers[i].headers[j]!=NULL;j++) { if (strcasecmp(header_name,mandatory_headers[i].headers[j])==0) { return 1; } } } } return 0; } */ int belle_sip_message_check_headers(const belle_sip_message_t* message) { if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(message,belle_sip_request_t)) { int i; belle_sip_header_via_t *via; const char * method = belle_sip_request_get_method(BELLE_SIP_REQUEST(message)); for (i=0;mandatory_headers[i].method!=NULL;i++) { if ( (strcasecmp(method,mandatory_headers[i].method)==0) || (mandatory_headers[i].method[0] == '*') ){ int j; for(j=0;mandatory_headers[i].headers[j]!=NULL;j++) { if (belle_sip_message_get_header(message,mandatory_headers[i].headers[j])==NULL) { belle_sip_error("Missing mandatory header [%s] for message [%s]",mandatory_headers[i].headers[j],method); return 0; } } return 1; } } via=belle_sip_message_get_header_by_type(message,belle_sip_header_via_t); if (!via || belle_sip_header_via_get_branch(via)==NULL) return 0; } /*else fixme should also check responses*/ return 1; } int belle_sip_request_check_uris_components(const belle_sip_request_t* request) { belle_sip_list_t* new_list = belle_sip_message_get_all_headers(BELLE_SIP_MESSAGE(request)); belle_sip_list_t* iterator = new_list; for (;iterator!=NULL;iterator=iterator->next) { belle_sip_header_t* header=(belle_sip_header_t*)iterator->data; if (BELLE_SIP_IS_INSTANCE_OF(header,belle_sip_header_address_t)) { belle_sip_uri_t* uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(header)); if (uri && !belle_sip_uri_check_components_from_context(uri,belle_sip_request_get_method(request),belle_sip_header_get_name(header))) { char* header_string=belle_sip_object_to_string(header); belle_sip_error("Malformed header [%s] for request [%p]",header_string,request); belle_sip_free(header_string); belle_sip_list_free(new_list); return FALSE; } } } belle_sip_list_free(new_list); return belle_sip_uri_check_components_from_request_uri(belle_sip_request_get_uri((const belle_sip_request_t*)request)); } belle-sip-1.6.3/src/nict.c000066400000000000000000000136631313437522400153020ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * non-INVITE client transaction implementation. **/ #include "belle_sip_internal.h" static int nict_on_timer_K(belle_sip_nict_t *obj){ belle_sip_transaction_terminate((belle_sip_transaction_t*)obj); return BELLE_SIP_STOP; } static void nict_set_completed(belle_sip_nict_t *obj, belle_sip_response_t *resp){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base); belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_COMPLETED); if (obj->timer_K) belle_sip_fatal("Should never happen."); belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp); if (!belle_sip_channel_is_reliable(base->channel)){ obj->timer_K=belle_sip_timeout_source_new((belle_sip_source_func_t)nict_on_timer_K,obj,cfg->T4); belle_sip_object_set_name((belle_sip_object_t*)obj->timer_K,"timer_K"); belle_sip_transaction_start_timer(base,obj->timer_K); }else belle_sip_transaction_terminate(base); } static void nict_on_response(belle_sip_nict_t *obj, belle_sip_response_t *resp){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; int code=belle_sip_response_get_status_code(resp); switch(base->state){ case BELLE_SIP_TRANSACTION_TRYING: if (code<200){ belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_PROCEEDING); belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp); } else { nict_set_completed(obj,resp); } break; case BELLE_SIP_TRANSACTION_PROCEEDING: if (code>=200){ nict_set_completed(obj,resp); } break; default: break; } } static void nict_on_terminate(belle_sip_nict_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; if (obj->timer_F){ belle_sip_transaction_stop_timer(base,obj->timer_F); belle_sip_object_unref(obj->timer_F); obj->timer_F=NULL; } if (obj->timer_E){ belle_sip_transaction_stop_timer(base,obj->timer_E); belle_sip_object_unref(obj->timer_E); obj->timer_E=NULL; } if (obj->timer_K){ belle_sip_transaction_stop_timer(base,obj->timer_K); belle_sip_object_unref(obj->timer_K); obj->timer_K=NULL; } } static int nict_on_timer_F(belle_sip_nict_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; switch (base->state){ case BELLE_SIP_TRANSACTION_TRYING: case BELLE_SIP_TRANSACTION_PROCEEDING: belle_sip_transaction_notify_timeout(base); break; default: break; } return BELLE_SIP_STOP; } static int nict_on_timer_E(belle_sip_nict_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base); switch(base->state){ case BELLE_SIP_TRANSACTION_TRYING: { /*reset the timer */ unsigned int prev_timeout=belle_sip_source_get_timeout(obj->timer_E); belle_sip_source_set_timeout(obj->timer_E,MIN(2*prev_timeout,(unsigned int)cfg->T2)); belle_sip_message("nict_on_timer_E: sending retransmission"); belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)base->request); } break; case BELLE_SIP_TRANSACTION_PROCEEDING: belle_sip_source_set_timeout(obj->timer_E,cfg->T2); belle_sip_message("nict_on_timer_E: sending retransmission"); belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)base->request); break; default: /*if we are not in these cases, timer_E does nothing, so remove it*/ return BELLE_SIP_STOP; break; } return BELLE_SIP_CONTINUE; } static void nict_send_request(belle_sip_nict_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base); belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_TRYING); obj->timer_F=belle_sip_timeout_source_new((belle_sip_source_func_t)nict_on_timer_F,obj,cfg->T1*64); belle_sip_object_set_name((belle_sip_object_t*)obj->timer_F,"timer_F"); belle_sip_transaction_start_timer(base,obj->timer_F); if (!belle_sip_channel_is_reliable(base->channel)){ obj->timer_E=belle_sip_timeout_source_new((belle_sip_source_func_t)nict_on_timer_E,obj,cfg->T1); belle_sip_object_set_name((belle_sip_object_t*)obj->timer_E,"timer_E"); belle_sip_transaction_start_timer(base,obj->timer_E); } belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)base->request); } static void nict_destroy(belle_sip_nict_t *obj){ nict_on_terminate(obj); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_nict_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_nict_t) { { { BELLE_SIP_VPTR_INIT(belle_sip_nict_t,belle_sip_client_transaction_t,TRUE), (belle_sip_object_destroy_t)nict_destroy, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, (void (*)(belle_sip_transaction_t *))nict_on_terminate }, (void (*)(belle_sip_client_transaction_t*))nict_send_request, (void (*)(belle_sip_client_transaction_t*,belle_sip_response_t*))nict_on_response } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END belle_sip_nict_t *belle_sip_nict_new(belle_sip_provider_t *prov, belle_sip_request_t *req){ belle_sip_nict_t *obj=belle_sip_object_new(belle_sip_nict_t); belle_sip_client_transaction_init((belle_sip_client_transaction_t*)obj,prov,req); return obj; } belle-sip-1.6.3/src/nist.c000066400000000000000000000101721313437522400153120ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * non-INVITE server transaction implementation. **/ #include #include "belle_sip_internal.h" static void nist_on_terminate(belle_sip_nist_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; if (obj->timer_J){ belle_sip_transaction_stop_timer(base,obj->timer_J); belle_sip_object_unref(obj->timer_J); obj->timer_J=NULL; } } static void nist_destroy(belle_sip_nist_t *obj){ nist_on_terminate(obj); } static int nist_on_timer_J(belle_sip_nist_t *obj){ belle_sip_transaction_terminate((belle_sip_transaction_t *)obj); return BELLE_SIP_STOP; } static void nist_set_completed(belle_sip_nist_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base); int tval; if (!belle_sip_channel_is_reliable(base->channel)) tval=cfg->T1*64; else tval=0; obj->timer_J=belle_sip_timeout_source_new((belle_sip_source_func_t)nist_on_timer_J,obj,tval); belle_sip_transaction_start_timer(base,obj->timer_J); belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_COMPLETED); } static int nist_send_new_response(belle_sip_nist_t *obj, belle_sip_response_t *resp){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; int code=belle_sip_response_get_status_code(resp); int ret=0; switch(base->state){ case BELLE_SIP_TRANSACTION_TRYING: if (code<200){ belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_PROCEEDING); belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)resp); break; } BCTBX_NO_BREAK; /* no break nist can directly pass from TRYING to PROCEEDING*/ case BELLE_SIP_TRANSACTION_PROCEEDING: if (code>=200){ nist_set_completed(obj); } belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)resp); break; case BELLE_SIP_TRANSACTION_COMPLETED: belle_sip_warning("nist_send_new_response(): not allowed to send a response while transaction is completed."); ret=-1; /*not allowed to send a response at this time*/ break; default: //ignore break; } return ret; } static void nist_on_request_retransmission(belle_sip_nist_t *obj){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; switch(base->state){ case BELLE_SIP_TRANSACTION_PROCEEDING: case BELLE_SIP_TRANSACTION_COMPLETED: belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)base->last_response); break; default: //ignore break; } } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_nist_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_nist_t) { { { BELLE_SIP_VPTR_INIT(belle_sip_nist_t,belle_sip_server_transaction_t,TRUE), (belle_sip_object_destroy_t)nist_destroy, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, (void (*)(belle_sip_transaction_t *))nist_on_terminate }, (int (*)(belle_sip_server_transaction_t*, belle_sip_response_t *))nist_send_new_response, (void (*)(belle_sip_server_transaction_t*))nist_on_request_retransmission, } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END belle_sip_nist_t *belle_sip_nist_new(belle_sip_provider_t *prov, belle_sip_request_t *req){ belle_sip_nist_t *obj=belle_sip_object_new(belle_sip_nist_t); belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj; belle_sip_server_transaction_init((belle_sip_server_transaction_t*)obj,prov,req); belle_sip_transaction_set_state(base,BELLE_SIP_TRANSACTION_TRYING); return obj; } belle-sip-1.6.3/src/parserutils.h000066400000000000000000000046101313437522400167170ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_sip_parserutils_h #define belle_sip_parserutils_h #include "port.h" static BELLESIP_INLINE int belle_sip_strcasecmp(const char*a, const char* b) { if (!a || !b) return 0; return strcasecmp(a,b) == 0; } #define IS_TOKEN(token) \ belle_sip_strcasecmp(#token,(const char*)INPUT->toStringTT(INPUT,LT(1),LT(strlen(#token)))->chars) #define IS_HEADER_NAMED(name,compressed_name) (IS_TOKEN(compressed_name) || IS_TOKEN(name)) #define STRCASECMP_HEADER_NAMED(name,compressed_name,value) \ (strcasecmp(compressed_name,(const char*)value) == 0 || strcasecmp(name,(const char*)value) == 0 ) #define ANTLR3_LOG_EXCEPTION() belle_sip_message("[\%s] reason [\%s] at line[\%u] position[\%d]",(const char*)EXCEPTION->name,(const char*)EXCEPTION->message,EXCEPTION->line,EXCEPTION->charPositionInLine); BELLESIP_EXPORT belle_sip_header_t* belle_sip_header_get_next(const belle_sip_header_t* headers); BELLESIP_EXPORT void belle_sip_header_set_next(belle_sip_header_t* header,belle_sip_header_t* next); BELLESIP_EXPORT char* belle_sip_to_unescaped_string(const char* buff); belle_sip_param_pair_t* belle_sip_param_pair_new(const char* name,const char* value); char* _belle_sip_str_dup_and_unquote_string(const char* quoted_string); /** * quoted-string = SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE qdtext = LWS / %x21 / %x23-5B / %x5D-7E / UTF8-NONASCII quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) remove any \ * */ BELLESIP_EXPORT char* belle_sip_string_to_backslash_less_unescaped_string(const char* buff); BELLESIP_EXPORT char* belle_sip_display_name_to_backslashed_escaped_string(const char* buff); #endif belle-sip-1.6.3/src/port.c000066400000000000000000000144061313437522400153250ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #ifdef _WIN32 #include #include #ifdef HAVE_COMPILER_TLS static __declspec(thread) const void *current_thread_data = NULL; #endif static int sockets_initd=0; int belle_sip_init_sockets(void){ if (sockets_initd==0){ WSADATA data; int err = WSAStartup(MAKEWORD(2,2), &data); if (err != 0) { belle_sip_error("WSAStartup failed with error: %d\n", err); return -1; } } sockets_initd++; return 0; } void belle_sip_uninit_sockets(void){ sockets_initd--; if (sockets_initd==0) WSACleanup(); } typedef struct thread_param { void * (*func)(void *); void * arg; } thread_param_t; static unsigned WINAPI thread_starter(void *data) { thread_param_t *params = (thread_param_t*)data; params->func(params->arg); belle_sip_free(data); return 0; } int belle_sip_thread_create(belle_sip_thread_t *thread, void *attr, void * (*func)(void *), void *data) { thread_param_t *params = belle_sip_new(thread_param_t); params->func = func; params->arg = data; *thread = (HANDLE)_beginthreadex(NULL, 0, thread_starter, params, 0, NULL); return 0; } int belle_sip_thread_join(belle_sip_thread_t thread, void **unused) { if (thread != NULL) { WaitForSingleObjectEx(thread, INFINITE, FALSE); CloseHandle(thread); } return 0; } int belle_sip_mutex_init(belle_sip_mutex_t *mutex, void *attr) { #ifdef BELLE_SIP_WINDOWS_DESKTOP *mutex = CreateMutex(NULL, FALSE, NULL); #else InitializeSRWLock(mutex); #endif return 0; } int belle_sip_mutex_lock(belle_sip_mutex_t * hMutex) { #ifdef BELLE_SIP_WINDOWS_DESKTOP WaitForSingleObject(*hMutex, INFINITE); #else AcquireSRWLockExclusive(hMutex); #endif return 0; } int belle_sip_mutex_unlock(belle_sip_mutex_t * hMutex) { #ifdef BELLE_SIP_WINDOWS_DESKTOP ReleaseMutex(*hMutex); #else ReleaseSRWLockExclusive(hMutex); #endif return 0; } int belle_sip_mutex_destroy(belle_sip_mutex_t * hMutex) { #ifdef BELLE_SIP_WINDOWS_DESKTOP CloseHandle(*hMutex); #endif return 0; } int belle_sip_socket_set_nonblocking(belle_sip_socket_t sock) { unsigned long nonBlock = 1; return ioctlsocket(sock, FIONBIO , &nonBlock); } int belle_sip_socket_set_dscp(belle_sip_socket_t sock, int ai_family, int dscp){ belle_sip_warning("belle_sip_socket_set_dscp(): not implemented."); return -1; } const char *belle_sip_get_socket_error_string(){ return belle_sip_get_socket_error_string_from_code(WSAGetLastError()); } const char *belle_sip_get_socket_error_string_from_code(int code){ static CHAR msgBuf[256]; #ifdef _UNICODE static WCHAR wMsgBuf[256]; size_t ret; FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, code, 0, // Default language (LPWSTR) &wMsgBuf, sizeof(wMsgBuf), NULL); ret = wcstombs(msgBuf, wMsgBuf, sizeof(msgBuf)); if (ret == sizeof(msgBuf)) msgBuf[sizeof(msgBuf) - 1] = '\0'; #else FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, code, 0, // Default language (LPTSTR) &msgBuf, sizeof(msgBuf), NULL); /*FIXME: should convert from TCHAR to UTF8 */ #endif return (const char *)msgBuf; } int belle_sip_thread_key_create(belle_sip_thread_key_t *key, void (*destructor)(void*) ){ #ifdef HAVE_COMPILER_TLS *key = (belle_sip_thread_key_t)¤t_thread_data; #else *key = (belle_sip_thread_key_t)TlsAlloc(); if (*key==TLS_OUT_OF_INDEXES){ belle_sip_error("TlsAlloc(): TLS_OUT_OF_INDEXES."); return -1; } #endif return 0; } int belle_sip_thread_setspecific(belle_sip_thread_key_t key,const void *value){ #ifdef HAVE_COMPILER_TLS current_thread_data = value; return 0; #else return TlsSetValue((DWORD)key,(void*)value) ? 0 : -1; #endif } const void* belle_sip_thread_getspecific(belle_sip_thread_key_t key){ #ifdef HAVE_COMPILER_TLS return current_thread_data; #else return TlsGetValue((DWORD)key); #endif } int belle_sip_thread_key_delete(belle_sip_thread_key_t key){ #ifdef HAVE_COMPILER_TLS current_thread_data = NULL; return 0; #else return TlsFree((DWORD)key) ? 0 : -1; #endif } #ifndef BELLE_SIP_WINDOWS_DESKTOP void belle_sip_sleep(unsigned int ms) { HANDLE sleepEvent = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); if (!sleepEvent) return; WaitForSingleObjectEx(sleepEvent, ms, FALSE); } #endif #else #include int belle_sip_init_sockets(){ signal(SIGPIPE,SIG_IGN); return 0; } void belle_sip_uninit_sockets(){ } int belle_sip_socket_set_nonblocking(belle_sip_socket_t sock){ return fcntl (sock, F_SETFL, fcntl(sock,F_GETFL) | O_NONBLOCK); } int belle_sip_socket_set_dscp(belle_sip_socket_t sock, int ai_family, int dscp){ int tos; int proto; int value_type; int retval; tos = (dscp << 2) & 0xFC; switch (ai_family) { case AF_INET: proto=IPPROTO_IP; value_type=IP_TOS; break; case AF_INET6: proto=IPPROTO_IPV6; #ifdef IPV6_TCLASS /*seems not defined by my libc*/ value_type=IPV6_TCLASS; #else value_type=IP_TOS; #endif break; default: belle_sip_error("Cannot set DSCP because socket family is unspecified."); return -1; } retval = bctbx_setsockopt(sock, proto, value_type, (const char*)&tos, sizeof(tos)); if (retval==-1) belle_sip_error("Fail to set DSCP value on socket: %s",belle_sip_get_socket_error_string()); return retval; } #endif int belle_sip_socket_enable_dual_stack(belle_sip_socket_t sock){ int value=0; int err=bctbx_setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&value, sizeof(value)); if (err==-1){ belle_sip_warning("belle_sip_socket_enable_dual_stack: bctbx_setsockopt(IPV6_ONLY) failed: %s",belle_sip_get_socket_error_string()); } return err; } belle-sip-1.6.3/src/port.h000066400000000000000000000136511313437522400153330ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef belle_sip_port_h #define belle_sip_port_h #include #ifndef _WIN32 #include #include #include #include #include #include #include #include #include #else #if defined(__MINGW32__) || !defined(WINAPI_FAMILY_PARTITION) || !defined(WINAPI_PARTITION_DESKTOP) #define BELLE_SIP_WINDOWS_DESKTOP 1 #elif defined(WINAPI_FAMILY_PARTITION) #if defined(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) #define BELLE_SIP_WINDOWS_DESKTOP 1 #elif defined(WINAPI_PARTITION_PHONE_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) #define BELLE_SIP_WINDOWS_PHONE 1 #elif defined(WINAPI_PARTITION_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #define BELLE_SIP_WINDOWS_UNIVERSAL 1 #endif #endif #ifdef _MSC_VER #if (_MSC_VER >= 1900) #define BELLE_SIP_MSC_VER_GREATER_19 #endif #endif #include #include #define strcasecmp _stricmp #ifdef _MSC_VER #define snprintf _snprintf #define vsnprintf _vsnprintf #define strdup _strdup #else #include #endif /*AI_NUMERICSERV is not defined for windows XP. Since it is not essential, we define it to 0 (does nothing)*/ #ifndef AI_NUMERICSERV #define AI_NUMERICSERV 0 #endif #endif #if defined(_WIN32) || defined(__QNX__) /* Mingw32 does not define AI_V4MAPPED, however it is supported starting from Windows Vista. QNX also does not define AI_V4MAPPED. */ # ifndef AI_V4MAPPED # define AI_V4MAPPED 0x00000800 # endif # ifndef AI_ALL # define AI_ALL 0x00000100 # endif # ifndef IPV6_V6ONLY # define IPV6_V6ONLY 27 # endif #endif /* * Socket abstraction layer */ BELLESIP_EXPORT int belle_sip_init_sockets(void); BELLESIP_EXPORT void belle_sip_uninit_sockets(void); int belle_sip_socket_set_nonblocking (belle_sip_socket_t sock); int belle_sip_socket_set_dscp(belle_sip_socket_t sock, int ai_family, int dscp); int belle_sip_socket_enable_dual_stack(belle_sip_socket_t sock); #if defined(_WIN32) typedef HANDLE belle_sip_thread_t; #define belle_sip_thread_self_id() (unsigned long)GetCurrentThreadId() #define belle_sip_thread_get_id(thread) (unsigned long)GetThreadId(thread) typedef intptr_t belle_sip_thread_key_t; int belle_sip_thread_key_create(belle_sip_thread_key_t *key, void (*destructor)(void*) ); int belle_sip_thread_setspecific(belle_sip_thread_key_t key,const void *value); const void* belle_sip_thread_getspecific(belle_sip_thread_key_t key); int belle_sip_thread_key_delete(belle_sip_thread_key_t key); static BELLESIP_INLINE void belle_sip_close_socket(belle_sip_socket_t s){ closesocket(s); } static BELLESIP_INLINE int get_socket_error(void){ return WSAGetLastError(); } const char *belle_sip_get_socket_error_string(); const char *belle_sip_get_socket_error_string_from_code(int code); /* * Thread abstraction layer */ #ifdef _WIN32 typedef HANDLE belle_sip_thread_t; #ifdef BELLE_SIP_WINDOWS_DESKTOP typedef HANDLE belle_sip_mutex_t; #else typedef SRWLOCK belle_sip_mutex_t; #endif int belle_sip_thread_join(belle_sip_thread_t thread, void **retptr); int belle_sip_thread_create(belle_sip_thread_t *thread, void *attr, void * (*routine)(void*), void *arg); int belle_sip_mutex_init(belle_sip_mutex_t *m, void *attr_unused); int belle_sip_mutex_lock(belle_sip_mutex_t *mutex); int belle_sip_mutex_unlock(belle_sip_mutex_t *mutex); int belle_sip_mutex_destroy(belle_sip_mutex_t *mutex); #else #include typedef pthread_t belle_sip_thread_t; typedef pthread_mutex_t belle_sip_mutex_t; #define belle_sip_thread_join(thread, retptr) pthread_join(thread, retptr) #define belle_sip_thread_create(thread, attr, routine, arg) pthread_create(thread, attr, routine, arg) #define belle_sip_mutex_init pthread_mutex_init #define belle_sip_mutex_lock pthread_mutex_lock #define belle_sip_mutex_unlock pthread_mutex_unlock #define belle_sip_mutex_destroy pthread_mutex_destroy #endif #ifndef BELLE_SIP_WINDOWS_DESKTOP BELLESIP_EXPORT void belle_sip_sleep(unsigned int ms); #else #define belle_sip_sleep Sleep #endif #define usleep(us) belle_sip_sleep((us)/1000) static BELLESIP_INLINE int inet_aton(const char *ip, struct in_addr *p){ *(long*)p=inet_addr(ip); return 0; } #else typedef pthread_t belle_sip_thread_t; #define belle_sip_thread_self_id() (unsigned long)pthread_self() #define belle_sip_thread_get_id(thread) (unsigned long)thread typedef pthread_key_t belle_sip_thread_key_t; #define belle_sip_thread_key_create(key,destructor) pthread_key_create(key,destructor) #define belle_sip_thread_setspecific(key,value) pthread_setspecific(key,value) #define belle_sip_thread_getspecific(key) pthread_getspecific(key) #define belle_sip_thread_key_delete(key) pthread_key_delete(key) static BELLESIP_INLINE void belle_sip_close_socket(belle_sip_socket_t s){ close(s); } static BELLESIP_INLINE int get_socket_error(void){ return errno; } #define belle_sip_get_socket_error_string() strerror(errno) #define belle_sip_get_socket_error_string_from_code(code) strerror(code) #endif #define BELLESIP_EWOULDBLOCK BCTBX_EWOULDBLOCK #define BELLESIP_EINPROGRESS BCTBX_EINPROGRESS #define belle_sip_error_code_is_would_block(err) ((err)==BELLESIP_EWOULDBLOCK || (err)==BELLESIP_EINPROGRESS) #endif belle-sip-1.6.3/src/provider.c000066400000000000000000001606361313437522400162020ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #include "listeningpoint_internal.h" #include "md5.h" #include "belle-sip/message.h" static void belle_sip_provider_update_or_create_auth_context(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id,belle_sip_header_www_authenticate_t* authenticate,belle_sip_uri_t *from_uri,const char* realm) ; struct authorization_context { belle_sip_header_call_id_t* callid; const char* scheme; const char* realm; const char* nonce; const char* qop; const char* opaque; const char* user_id; const char* algorithm; int nonce_count; int is_proxy; }; GET_SET_STRING(authorization_context,realm) GET_SET_STRING(authorization_context,nonce) GET_SET_STRING(authorization_context,qop) GET_SET_STRING(authorization_context,scheme) GET_SET_STRING(authorization_context,opaque) GET_SET_STRING(authorization_context,user_id) GET_SET_STRING(authorization_context,algorithm) GET_SET_INT(authorization_context,nonce_count,int) static authorization_context_t* belle_sip_authorization_create(belle_sip_header_call_id_t* call_id) { authorization_context_t* result = malloc(sizeof(authorization_context_t)); memset(result,0,sizeof(authorization_context_t)); result->callid=call_id; belle_sip_object_ref(result->callid); return result; } void belle_sip_authorization_destroy(authorization_context_t* object) { DESTROY_STRING(object,scheme); DESTROY_STRING(object,realm); DESTROY_STRING(object,nonce); DESTROY_STRING(object,qop); DESTROY_STRING(object,opaque); DESTROY_STRING(object,user_id); DESTROY_STRING(object,algorithm); belle_sip_object_unref(object->callid); belle_sip_free(object); } static void finalize_transaction(belle_sip_transaction_t *tr){ belle_sip_transaction_state_t state=belle_sip_transaction_get_state(tr); if (state!=BELLE_SIP_TRANSACTION_TERMINATED){ belle_sip_message("Transaction [%p] still in state [%s], will force termination.",tr,belle_sip_transaction_state_to_string(state)); belle_sip_transaction_terminate(tr); } } static void finalize_transactions(const belle_sip_list_t *l){ belle_sip_list_t *copy=belle_sip_list_copy(l); belle_sip_list_free_with_data(copy,(void (*)(void*))finalize_transaction); } static void belle_sip_provider_uninit(belle_sip_provider_t *p){ finalize_transactions(p->client_transactions); p->client_transactions=NULL; finalize_transactions(p->server_transactions); p->server_transactions=NULL; p->listeners=belle_sip_list_free(p->listeners); p->internal_listeners=belle_sip_list_free(p->internal_listeners); p->auth_contexts=belle_sip_list_free_with_data(p->auth_contexts,(void(*)(void*))belle_sip_authorization_destroy); p->dialogs=belle_sip_list_free_with_data(p->dialogs,belle_sip_object_unref); p->lps=belle_sip_list_free_with_data(p->lps,belle_sip_object_unref); } static void channel_state_changed(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, belle_sip_channel_state_t state){ belle_sip_io_error_event_t ev; belle_sip_provider_t* prov=BELLE_SIP_PROVIDER(obj); if (state == BELLE_SIP_CHANNEL_ERROR || state == BELLE_SIP_CHANNEL_DISCONNECTED) { ev.transport=belle_sip_channel_get_transport_name(chan); ev.port=chan->peer_port; ev.host=chan->peer_name; ev.source=BELLE_SIP_OBJECT(prov); BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_io_error,&ev); /*IO error is also relevant for internal listener like refreshers*/ BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->internal_listeners,process_io_error,&ev); if (!chan->force_close) belle_sip_provider_release_channel(prov,chan); } } static int notify_client_transaction_match(const void *transaction, const void *notify){ belle_sip_client_transaction_t *tr=(belle_sip_client_transaction_t*)transaction; belle_sip_request_t *notify_req=(belle_sip_request_t*)notify; return !belle_sip_client_transaction_is_notify_matching_pending_subscribe(tr,notify_req); } belle_sip_client_transaction_t * belle_sip_provider_find_matching_pending_subscribe_client_transaction_from_notify_req(belle_sip_provider_t *prov, belle_sip_request_t *req) { belle_sip_list_t* elem; if (strcmp("NOTIFY",belle_sip_request_get_method(req)) != 0) { belle_sip_error("belle_sip_provider_find_matching_pending_subscribe_client_transaction_from_notify_req requires a NOTIFY request, not a [%s], on prov [%p]" ,belle_sip_request_get_method(req) ,prov); } elem=belle_sip_list_find_custom(prov->client_transactions,notify_client_transaction_match,req); return elem?BELLE_SIP_CLIENT_TRANSACTION(elem->data):NULL; } static void belle_sip_provider_dispatch_request(belle_sip_provider_t* prov, belle_sip_request_t *req){ belle_sip_server_transaction_t *t; belle_sip_request_event_t ev; t=belle_sip_provider_find_matching_server_transaction(prov,req); if (t){ belle_sip_object_ref(t); belle_sip_server_transaction_on_request(t,req); belle_sip_object_unref(t); }else{ const char *method=belle_sip_request_get_method(req); ev.dialog=NULL; /* Should we limit to ACK ? */ /*Search for a dialog if exist */ if (strcmp("CANCEL",method) == 0) { /* Call leg does not exist */ belle_sip_server_transaction_t *tr = belle_sip_provider_create_server_transaction(prov, req); belle_sip_server_transaction_send_response(tr, belle_sip_response_create_from_request(req, 481)); return; } ev.dialog=belle_sip_provider_find_dialog_from_message(prov,(belle_sip_message_t*)req,1/*request=uas*/); if (ev.dialog){ if (strcmp("ACK",method)==0){ if (belle_sip_dialog_handle_ack(ev.dialog,req)==-1){ /*absorbed ACK retransmission, ignore */ return; } }else if ((strcmp("INVITE",method)==0)&&(ev.dialog->needs_ack)){ belle_sip_dialog_stop_200Ok_retrans(ev.dialog); }else if (!belle_sip_dialog_is_authorized_transaction(ev.dialog,method)){ belle_sip_server_transaction_t *tr=belle_sip_provider_create_server_transaction(prov,req); belle_sip_server_transaction_send_response(tr, belle_sip_response_create_from_request(req,491)); return; } } else if (strcmp("NOTIFY",method) == 0) { /*search for matching subscribe*/ belle_sip_client_transaction_t *sub = belle_sip_provider_find_matching_pending_subscribe_client_transaction_from_notify_req(prov,req); if (sub) { belle_sip_message("Found matching subscribe for NOTIFY [%p], creating dialog",req); ev.dialog=belle_sip_provider_create_dialog_internal(prov,BELLE_SIP_TRANSACTION(sub),FALSE); } } if (prov->unconditional_answer_enabled && strcmp("ACK",method)!=0) { /*always answer predefined value (I.E 480 by default)*/ belle_sip_server_transaction_t *tr=belle_sip_provider_create_server_transaction(prov,req); belle_sip_server_transaction_send_response(tr,belle_sip_response_create_from_request(req,prov->unconditional_answer)); return; } else { ev.source=(belle_sip_object_t*)prov; ev.server_transaction=NULL; ev.request=req; BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_request_event,&ev); } } } static belle_sip_list_t* belle_sip_provider_get_auth_context_by_realm_or_call_id(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id,belle_sip_uri_t *from_uri,const char* realm); static int belle_sip_auth_context_find_by_nonce(const void* elem, const void* nonce_value){ authorization_context_t * a = (authorization_context_t*)elem; return strcmp(a->nonce, (const char*)nonce_value); } static void belle_sip_provider_dispatch_response(belle_sip_provider_t* p, belle_sip_response_t *msg){ belle_sip_client_transaction_t *t; t=belle_sip_provider_find_matching_client_transaction(p,msg); /*good opportunity to cleanup auth context if answer = 401|407|403*/ switch (belle_sip_response_get_status_code(msg)) { case 401: case 403: case 407: { if (t!=NULL){ const char* nonce = NULL; belle_sip_message_t* req = BELLE_SIP_MESSAGE(belle_sip_transaction_get_request((belle_sip_transaction_t*)t)); belle_sip_header_authorization_t* authorization=BELLE_SIP_HEADER_AUTHORIZATION(belle_sip_message_get_header_by_type(req, belle_sip_header_proxy_authorization_t)); if (authorization==NULL) authorization=belle_sip_message_get_header_by_type(req, belle_sip_header_authorization_t); if (authorization!=NULL){ nonce = belle_sip_header_authorization_get_nonce(authorization); if (nonce != NULL){ belle_sip_list_t * auth_context_with_nonce = NULL; while ((auth_context_with_nonce = belle_sip_list_find_custom(p->auth_contexts, belle_sip_auth_context_find_by_nonce, nonce)) != NULL){ belle_sip_authorization_destroy(auth_context_with_nonce->data); p->auth_contexts = belle_sip_list_delete_link(p->auth_contexts, auth_context_with_nonce); } } } } break; } default: if (t!=NULL){ belle_sip_message_t* req = BELLE_SIP_MESSAGE(belle_sip_transaction_get_request((belle_sip_transaction_t*)t)); belle_sip_header_authentication_info_t *authentication_info = belle_sip_message_get_header_by_type(msg,belle_sip_header_authentication_info_t); belle_sip_list_t *authorization_lst = NULL; belle_sip_header_call_id_t *call_id = belle_sip_message_get_header_by_type(msg,belle_sip_header_call_id_t); belle_sip_header_from_t *from = belle_sip_message_get_header_by_type(req,belle_sip_header_from_t); belle_sip_uri_t *from_uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)from); /*searching for authentication headers*/ authorization_lst = belle_sip_list_copy(belle_sip_message_get_headers(BELLE_SIP_MESSAGE(req),BELLE_SIP_AUTHORIZATION)); /*search for proxy authenticate*/ authorization_lst=belle_sip_list_concat(authorization_lst,belle_sip_list_copy(belle_sip_message_get_headers(BELLE_SIP_MESSAGE(req),BELLE_SIP_PROXY_AUTHORIZATION))); /*update auth contexts with authenticate headers from response*/ for (;authentication_info && authorization_lst!=NULL;authorization_lst=authorization_lst->next) { belle_sip_header_authorization_t *authorization=BELLE_SIP_HEADER_AUTHORIZATION(authorization_lst->data); belle_sip_header_www_authenticate_t *www_authenticate = belle_sip_auth_helper_create_www_authenticate(authorization); belle_sip_header_www_authenticate_set_nonce(www_authenticate, belle_sip_header_authentication_info_get_next_nonce(authentication_info)); belle_sip_message( "Updating auth context for ream [%s] next nonce is going to be [%s]" , belle_sip_header_www_authenticate_get_realm(www_authenticate) , belle_sip_header_authentication_info_get_next_nonce(authentication_info)); belle_sip_provider_update_or_create_auth_context(p , call_id , www_authenticate , from_uri ,belle_sip_header_www_authenticate_get_realm(www_authenticate)); belle_sip_object_unref(www_authenticate); } if (authorization_lst) belle_sip_list_free(authorization_lst); } } if (t){ /*In some re-connection case, specially over udp, transaction may be found, but without associated channel*/ if (t->base.channel == NULL) { belle_sip_channel_t *chan; belle_sip_message("Transaction [%p] does not have any channel associated, searching for a new one",t); chan=belle_sip_provider_get_channel(p,t->next_hop); /*might be faster to get channel directly from upper level*/ if (chan){ belle_sip_object_ref(chan); belle_sip_channel_add_listener(chan,BELLE_SIP_CHANNEL_LISTENER(t)); t->base.channel=chan; } } } /* * If a transaction is found and have a channel, pass it to the transaction and let it decide what to do. * Else notifies directly. */ if (t && t->base.channel){ /*since the add_response may indirectly terminate the transaction, we need to guarantee the transaction is not freed * until full completion*/ belle_sip_object_ref(t); belle_sip_client_transaction_add_response(t,msg); belle_sip_object_unref(t); }else{ belle_sip_response_event_t event; event.source=(belle_sip_object_t*)p; event.client_transaction=NULL; event.dialog=NULL; event.response=msg; BELLE_SIP_PROVIDER_INVOKE_LISTENERS(p->listeners,process_response_event,&event); } } void belle_sip_provider_dispatch_message(belle_sip_provider_t *prov, belle_sip_message_t *msg){ if (TRUE #ifndef BELLE_SIP_DONT_CHECK_HEADERS_IN_MESSAGE && belle_sip_message_check_headers(msg) #endif ){ if (belle_sip_message_is_request(msg)){ belle_sip_provider_dispatch_request(prov,(belle_sip_request_t*)msg); }else{ belle_sip_provider_dispatch_response(prov,(belle_sip_response_t*)msg); } }else{ /* incorrect message received, answer bad request if it was a request.*/ if (belle_sip_message_is_request(msg)){ belle_sip_response_t *resp=belle_sip_response_create_from_request(BELLE_SIP_REQUEST(msg),400); if (resp){ belle_sip_provider_send_response(prov,resp); } }/*otherwise what can we do ?*/ } belle_sip_object_unref(msg); } /* * takes example on 16.11 of RFC3261 */ static void compute_hash_from_invariants(belle_sip_message_t *msg, char *branchid, size_t branchid_size, const char *initial){ md5_state_t ctx; char tmp[256]={0}; uint8_t digest[16]; belle_sip_header_call_id_t* callid_hdr = belle_sip_message_get_header_by_type(msg,belle_sip_header_call_id_t); belle_sip_header_cseq_t* cseq_hdr = belle_sip_message_get_header_by_type(msg,belle_sip_header_cseq_t); belle_sip_header_from_t* from_hdr = belle_sip_message_get_header_by_type(msg,belle_sip_header_from_t); belle_sip_header_to_t* to_hdr = belle_sip_message_get_header_by_type(msg,belle_sip_header_to_t); unsigned int cseq = cseq_hdr ? belle_sip_header_cseq_get_seq_number(cseq_hdr) : 0; const char *callid = callid_hdr ? belle_sip_header_call_id_get_call_id(callid_hdr) : ""; const char *from_tag = from_hdr ? belle_sip_header_from_get_tag(from_hdr) : ""; const char *to_tag = to_hdr ? belle_sip_header_to_get_tag(to_hdr) : ""; belle_sip_uri_t *requri=NULL; belle_sip_header_via_t *via=NULL; belle_sip_header_via_t *prev_via=NULL; const belle_sip_list_t *vias=belle_sip_message_get_headers(msg,"via"); int is_request=belle_sip_message_is_request(msg); if (vias){ via=(belle_sip_header_via_t*)vias->data; if (vias->next){ prev_via=(belle_sip_header_via_t*)vias->next->data; } } if (is_request){ requri=belle_sip_request_get_uri(BELLE_SIP_REQUEST(msg)); } belle_sip_md5_init(&ctx); if (initial) belle_sip_md5_append(&ctx,(uint8_t*)initial,(int)strlen(initial)); if (requri){ size_t offset=0; belle_sip_object_marshal((belle_sip_object_t*)requri,tmp,sizeof(tmp)-1,&offset); belle_sip_md5_append(&ctx,(uint8_t*)tmp,(int)strlen(tmp)); } if (from_tag) belle_sip_md5_append(&ctx,(uint8_t*)from_tag,(int)strlen(from_tag)); if (to_tag) belle_sip_md5_append(&ctx,(uint8_t*)to_tag,(int)strlen(to_tag)); belle_sip_md5_append(&ctx,(uint8_t*)callid,(int)strlen(callid)); belle_sip_md5_append(&ctx,(uint8_t*)&cseq,sizeof(cseq)); if (is_request){ if (prev_via){ size_t offset=0; belle_sip_object_marshal((belle_sip_object_t*)prev_via,tmp,sizeof(tmp)-1,&offset); belle_sip_md5_append(&ctx,(uint8_t*)tmp,(int)offset); } }else{ if (via){ size_t offset=0; belle_sip_object_marshal((belle_sip_object_t*)via,tmp,sizeof(tmp)-1,&offset); belle_sip_md5_append(&ctx,(uint8_t*)tmp,(int)offset); } } belle_sip_md5_finish(&ctx,digest); belle_sip_octets_to_text(digest,sizeof(digest),branchid,branchid_size); } /* * RFC2543 10.1.2: * "Responses are mapped to requests by the matching To, From, Call-ID, * CSeq headers and the branch parameter of the first Via header." * * to-tag must not be used because an ACK will contain one while original INVITE will not. * Cseq's method is changed for CANCEL so we must not use it as well. **/ static char *compute_rfc2543_branch(belle_sip_request_t *req, char *branchid, size_t branchid_size){ md5_state_t ctx; unsigned int cseq=belle_sip_header_cseq_get_seq_number(belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t)); uint8_t digest[16]; const char* callid=belle_sip_header_call_id_get_call_id(belle_sip_message_get_header_by_type(req,belle_sip_header_call_id_t)); belle_sip_header_via_t *via=belle_sip_message_get_header_by_type(req,belle_sip_header_via_t); const char *v_branch=belle_sip_header_via_get_branch(via); belle_sip_header_from_t *from=belle_sip_message_get_header_by_type(req,belle_sip_header_from_t); char *from_str=belle_sip_object_to_string(from); belle_sip_header_to_t *to=belle_sip_message_get_header_by_type(req,belle_sip_header_to_t); char *to_str=belle_sip_object_to_string(belle_sip_header_address_get_uri((belle_sip_header_address_t*)to)); belle_sip_md5_init(&ctx); belle_sip_md5_append(&ctx,(uint8_t*)from_str,(int)strlen(from_str)); belle_sip_md5_append(&ctx,(uint8_t*)to_str,(int)strlen(to_str)); belle_sip_md5_append(&ctx,(uint8_t*)callid,(int)strlen(callid)); belle_sip_md5_append(&ctx,(uint8_t*)&cseq,sizeof(cseq)); belle_sip_free(from_str); belle_sip_free(to_str); if (v_branch) belle_sip_md5_append(&ctx,(uint8_t*)v_branch,(int)strlen(v_branch)); belle_sip_md5_finish(&ctx,digest); belle_sip_octets_to_text(digest,sizeof(digest),branchid,branchid_size); return branchid; } static void fix_outgoing_via(belle_sip_provider_t *p, belle_sip_channel_t *chan, belle_sip_message_t *msg){ belle_sip_header_via_t *via=BELLE_SIP_HEADER_VIA(belle_sip_message_get_header(msg,"via")); if (p->rport_enabled) belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(via),"rport",NULL); belle_sip_header_via_set_host(via,chan->local_ip); belle_sip_header_via_set_port(via,chan->local_port); belle_sip_header_via_set_protocol(via,"SIP/2.0"); belle_sip_header_via_set_transport(via,belle_sip_channel_get_transport_name(chan)); if (belle_sip_header_via_get_branch(via)==NULL){ /*branch id should not be set random here (stateless forwarding): but rather a hash of message invariants*/ char branchid[24]; char token[BELLE_SIP_BRANCH_ID_LENGTH]; compute_hash_from_invariants(msg,token,sizeof(token),NULL); snprintf(branchid,sizeof(branchid)-1,BELLE_SIP_BRANCH_MAGIC_COOKIE ".%s",token); belle_sip_header_via_set_branch(via,branchid); belle_sip_message("Computing branch id %s for message sent statelessly", branchid); } } static void channel_on_message_headers(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, belle_sip_message_t *msg){ /*not used*/ } static void channel_on_message(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, belle_sip_message_t *msg){ belle_sip_object_ref(msg); belle_sip_provider_dispatch_message(BELLE_SIP_PROVIDER(obj),msg); } static int channel_on_auth_requested(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, const char* distinguished_name){ if (BELLE_SIP_IS_INSTANCE_OF(chan,belle_sip_tls_channel_t)) { belle_sip_provider_t *prov=BELLE_SIP_PROVIDER(obj); belle_sip_auth_event_t* auth_event = belle_sip_auth_event_create((belle_sip_object_t*)prov,NULL,NULL); belle_sip_tls_channel_t *tls_chan=BELLE_SIP_TLS_CHANNEL(chan); auth_event->mode=BELLE_SIP_AUTH_MODE_TLS; belle_sip_auth_event_set_distinguished_name(auth_event,distinguished_name); BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_auth_requested,auth_event); belle_sip_tls_channel_set_client_certificates_chain(tls_chan,auth_event->cert); belle_sip_tls_channel_set_client_certificate_key(tls_chan,auth_event->key); belle_sip_auth_event_destroy(auth_event); } return 0; } static void channel_on_sending(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, belle_sip_message_t *msg){ belle_sip_header_contact_t* contact; belle_sip_header_content_length_t* content_length = (belle_sip_header_content_length_t*)belle_sip_message_get_header(msg,"Content-Length"); belle_sip_uri_t* contact_uri; const belle_sip_list_t *contacts; const char *ip=NULL; int port=0; belle_sip_provider_t *prov=BELLE_SIP_PROVIDER(obj); if (belle_sip_message_is_request(msg)){ const belle_sip_list_t *rroutes; /*probably better to be in channel*/ fix_outgoing_via(prov, chan, msg); for (rroutes=belle_sip_message_get_headers(msg,"Record-Route");rroutes!=NULL;rroutes=rroutes->next){ belle_sip_header_record_route_t* rr=(belle_sip_header_record_route_t*)rroutes->data; if (belle_sip_header_record_route_get_auto_outgoing(rr)) { belle_sip_uri_t *rr_uri = belle_sip_channel_create_routable_uri(chan); belle_sip_header_address_set_uri((belle_sip_header_address_t*) rr, rr_uri); } } } for (contacts=belle_sip_message_get_headers(msg,"Contact");contacts!=NULL;contacts=contacts->next){ const char *transport; contact=(belle_sip_header_contact_t*)contacts->data; if (belle_sip_header_contact_is_wildcard(contact)) continue; /* fix the contact if in automatic mode or null uri (for backward compatibility)*/ if (!(contact_uri = belle_sip_header_address_get_uri((belle_sip_header_address_t*)contact))) { contact_uri = belle_sip_uri_new(); belle_sip_header_address_set_uri((belle_sip_header_address_t*)contact,contact_uri); belle_sip_header_contact_set_automatic(contact,TRUE); }else if (belle_sip_uri_get_host(contact_uri)==NULL){ belle_sip_header_contact_set_automatic(contact,TRUE); } if (!belle_sip_header_contact_get_automatic(contact)) continue; if (ip==NULL){ if (prov->nat_helper){ ip=chan->public_ip ? chan->public_ip : chan->local_ip; port=chan->public_port ? chan->public_port : chan->local_port; belle_sip_header_contact_set_unknown(contact,!chan->learnt_ip_port); }else{ ip=chan->local_ip; port=chan->local_port; } } belle_sip_uri_set_host(contact_uri,ip); transport=belle_sip_channel_get_transport_name_lower_case(chan); /* Enforce a transport name in "sip" scheme. * RFC3263 (locating SIP servers) says that UDP SHOULD be used in absence of transport parameter, * when port or numeric IP are provided. It is a SHOULD, not a must. * We need in this case that the automatic Contact exactly matches the socket that is going * to be used for sending the messages. * TODO: we may need to do the same for sips, but dtls is currently not supported. **/ if (!belle_sip_uri_is_secure(contact_uri)) belle_sip_uri_set_transport_param(contact_uri,transport); if (port!=belle_sip_listening_point_get_well_known_port(transport)) { belle_sip_uri_set_port(contact_uri,port); }else{ belle_sip_uri_set_port(contact_uri,0); } } /* * According to RFC3261, content-length is mandatory for stream based transport, but optional for datagram transport. * However some servers (opensips) are confused when they receive a SIP/UDP packet without Content-Length (they shouldn't). */ if (!content_length && belle_sip_message_get_body_size(msg) == 0 /*if body present, content_length is automatically added at channel level*/ #ifndef BELLE_SIP_FORCE_CONTENT_LENGTH && strcasecmp("udp",belle_sip_channel_get_transport_name(chan))!=0 #endif ) { content_length = belle_sip_header_content_length_create(0); belle_sip_message_add_header(msg,(belle_sip_header_t*)content_length); } } BELLE_SIP_IMPLEMENT_INTERFACE_BEGIN(belle_sip_provider_t,belle_sip_channel_listener_t) channel_state_changed, channel_on_message_headers, channel_on_message, channel_on_sending, channel_on_auth_requested BELLE_SIP_IMPLEMENT_INTERFACE_END BELLE_SIP_DECLARE_IMPLEMENTED_INTERFACES_1(belle_sip_provider_t,belle_sip_channel_listener_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_provider_t,belle_sip_object_t,belle_sip_provider_uninit,NULL,NULL,FALSE); belle_sip_provider_t *belle_sip_provider_new(belle_sip_stack_t *s, belle_sip_listening_point_t *lp){ belle_sip_provider_t *p=belle_sip_object_new(belle_sip_provider_t); p->stack=s; p->rport_enabled=1; p->unconditional_answer = 480; if (lp) belle_sip_provider_add_listening_point(p,lp); return p; } /* This function is used by a proxy to set its call side record route. * It must be called before adding any VIA header to the message. */ belle_sip_uri_t *belle_sip_provider_create_inbound_record_route(belle_sip_provider_t *p, belle_sip_request_t *req) { belle_sip_uri_t* origin = belle_sip_request_extract_origin(req); belle_sip_hop_t *hop = belle_sip_hop_new_from_uri(origin); belle_sip_channel_t *inChan = belle_sip_provider_get_channel(p, hop); return belle_sip_channel_create_routable_uri(inChan); } static belle_sip_channel_t* _belle_sip_provider_find_channel_using_routable(belle_sip_provider_t *p, const belle_sip_uri_t* routable_uri) { const char *transport; belle_sip_listening_point_t *lp; belle_sip_list_t *elem; belle_sip_channel_t *chan; belle_sip_uri_t* chan_uri; if (!routable_uri) return NULL; transport = belle_sip_uri_is_secure(routable_uri) ? "TLS" : belle_sip_uri_get_transport_param(routable_uri); lp = belle_sip_provider_get_listening_point(p, transport); if (!lp) return NULL; for(elem=lp->channels; elem ;elem=elem->next){ chan=(belle_sip_channel_t*)elem->data; chan_uri = belle_sip_channel_create_routable_uri(chan); if (belle_sip_uri_get_port(routable_uri) == belle_sip_uri_get_port(chan_uri) && 0 == strcmp(belle_sip_uri_get_host(routable_uri), belle_sip_uri_get_host(chan_uri))) { return chan; } } return NULL; } /* * This function is not efficient at all, REVISIT. * Its goal is to determine whether a routable (route or record route) matches the local provider instance. * In order to do that, we go through all the channels and ask them their routable uri, and see if it matches the uri passed in argument. * This creates a lot of temporary objects and iterates through a potentially long list of routables. * Some more efficient solutions could be: * 1- insert a magic cookie parameter in each routable created by the provider, so that recognition is immediate. * Drawback: use of non-standard, possibly conflicting parameter. * 2- check the listening point's uri first (but need to match the ip address to any local ip if it is INADDR_ANY), then use belle_sip_listening_point_get_channel() * to see if a channel is matching. * belle_sip_listening_point_get_channel() is not optimized currently but will have to be, so at least we leverage on something that will be optimized. **/ int belle_sip_provider_is_us(belle_sip_provider_t *p, belle_sip_uri_t* uri) { belle_sip_channel_t* chan = _belle_sip_provider_find_channel_using_routable(p, uri); return !!chan; } int belle_sip_provider_add_listening_point(belle_sip_provider_t *p, belle_sip_listening_point_t *lp){ if (lp == NULL) { belle_sip_error("Cannot add NULL lp to provider [%p]",p); return -1; } belle_sip_listening_point_set_channel_listener(lp,BELLE_SIP_CHANNEL_LISTENER(p)); p->lps=belle_sip_list_append(p->lps,belle_sip_object_ref(lp)); return 0; } void belle_sip_provider_remove_listening_point(belle_sip_provider_t *p, belle_sip_listening_point_t *lp) { p->lps=belle_sip_list_remove(p->lps,lp); belle_sip_object_unref(lp); return; } belle_sip_listening_point_t *belle_sip_provider_get_listening_point(belle_sip_provider_t *p, const char *transport){ belle_sip_list_t *l; for(l=p->lps;l!=NULL;l=l->next){ belle_sip_listening_point_t *lp=(belle_sip_listening_point_t*)l->data; if (strcasecmp(belle_sip_listening_point_get_transport(lp),transport)==0) return lp; } return NULL; } const belle_sip_list_t *belle_sip_provider_get_listening_points(belle_sip_provider_t *p){ return p->lps; } void belle_sip_provider_add_internal_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l, int prepend){ if (prepend) p->internal_listeners=belle_sip_list_prepend(p->internal_listeners,l); else p->internal_listeners=belle_sip_list_append(p->internal_listeners,l); } void belle_sip_provider_remove_internal_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l){ p->internal_listeners=belle_sip_list_remove(p->internal_listeners,l); } void belle_sip_provider_add_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l){ p->listeners=belle_sip_list_append(p->listeners,l); } void belle_sip_provider_remove_sip_listener(belle_sip_provider_t *p, belle_sip_listener_t *l){ p->listeners=belle_sip_list_remove(p->listeners,l); } belle_sip_header_call_id_t * belle_sip_provider_create_call_id(const belle_sip_provider_t *prov){ belle_sip_header_call_id_t *cid=belle_sip_header_call_id_new(); char tmp[11]; belle_sip_header_call_id_set_call_id(cid,belle_sip_random_token(tmp,sizeof(tmp))); return cid; } belle_sip_dialog_t * belle_sip_provider_create_dialog(belle_sip_provider_t *prov, belle_sip_transaction_t *t) { return belle_sip_provider_create_dialog_internal(prov,t,TRUE); } belle_sip_dialog_t * belle_sip_provider_create_dialog_internal(belle_sip_provider_t *prov, belle_sip_transaction_t *t,unsigned int check_last_resp){ belle_sip_dialog_t *dialog=NULL; if (check_last_resp && t->last_response){ int code=belle_sip_response_get_status_code(t->last_response); if (code>=200 && code<300){ belle_sip_fatal("You must not create dialog after sending the response that establish the dialog."); return NULL; } } dialog=belle_sip_dialog_new(t); if (dialog) { belle_sip_transaction_set_dialog(t,dialog); belle_sip_provider_add_dialog(prov,dialog); } return dialog; } /*find a dialog given the call id, local-tag and to-tag*/ belle_sip_dialog_t* belle_sip_provider_find_dialog(const belle_sip_provider_t *prov, const char* call_id, const char* local_tag, const char* remote_tag) { belle_sip_list_t* iterator; belle_sip_dialog_t*returned_dialog=NULL; if (call_id == NULL || local_tag == NULL || remote_tag == NULL) { return NULL; } for(iterator=prov->dialogs;iterator!=NULL;iterator=iterator->next) { belle_sip_dialog_t* dialog=(belle_sip_dialog_t*)iterator->data; dialog=(belle_sip_dialog_t*)iterator->data; /*ignore dialog in state BELLE_SIP_DIALOG_NULL, is it really the correct things to do*/ if (belle_sip_dialog_get_state(dialog) != BELLE_SIP_DIALOG_NULL && _belle_sip_dialog_match(dialog,call_id,local_tag,remote_tag)) { if (!returned_dialog) returned_dialog=dialog; else belle_sip_fatal("More than 1 dialog is matching, check your app"); } } return returned_dialog; } /*finds an existing dialog for an outgoing or incoming message */ belle_sip_dialog_t *belle_sip_provider_find_dialog_from_message(belle_sip_provider_t *prov, belle_sip_message_t *msg, int as_uas){ belle_sip_header_call_id_t *call_id; belle_sip_header_from_t *from; belle_sip_header_to_t *to; const char *from_tag; const char *to_tag; const char *call_id_value; const char *local_tag,*remote_tag; if (belle_sip_message_is_request(msg)){ belle_sip_request_t *req=BELLE_SIP_REQUEST(msg); if (req->dialog) return req->dialog; } to=belle_sip_message_get_header_by_type(msg,belle_sip_header_to_t); if (to==NULL || (to_tag=belle_sip_header_to_get_tag(to))==NULL){ /* a request without to tag cannot be part of a dialog */ return NULL; } call_id=belle_sip_message_get_header_by_type(msg,belle_sip_header_call_id_t); from=belle_sip_message_get_header_by_type(msg,belle_sip_header_from_t); if (call_id==NULL || from==NULL || (from_tag=belle_sip_header_from_get_tag(from))==NULL) return NULL; call_id_value=belle_sip_header_call_id_get_call_id(call_id); local_tag=as_uas ? to_tag : from_tag; remote_tag=as_uas ? from_tag : to_tag; return belle_sip_provider_find_dialog(prov,call_id_value,local_tag,remote_tag); } void belle_sip_provider_add_dialog(belle_sip_provider_t *prov, belle_sip_dialog_t *dialog){ prov->dialogs=belle_sip_list_prepend(prov->dialogs,belle_sip_object_ref(dialog)); } static void notify_dialog_terminated(belle_sip_dialog_terminated_event_t* ev) { BELLE_SIP_PROVIDER_INVOKE_LISTENERS_FOR_DIALOG(ev->dialog,process_dialog_terminated,ev); belle_sip_object_unref(ev->dialog); belle_sip_free(ev); } void belle_sip_provider_remove_dialog(belle_sip_provider_t *prov, belle_sip_dialog_t *dialog){ belle_sip_dialog_terminated_event_t* ev=belle_sip_malloc(sizeof(belle_sip_dialog_terminated_event_t)); ev->source=prov; ev->dialog=dialog; ev->is_expired=dialog->is_expired; prov->dialogs=belle_sip_list_remove(prov->dialogs,dialog); belle_sip_main_loop_do_later(belle_sip_stack_get_main_loop(prov->stack) ,(belle_sip_callback_t) notify_dialog_terminated , ev); } belle_sip_client_transaction_t *belle_sip_provider_create_client_transaction(belle_sip_provider_t *prov, belle_sip_request_t *req){ const char *method=belle_sip_request_get_method(req); belle_sip_client_transaction_t *t; belle_sip_client_transaction_t *inv_transaction; if (strcmp(method,"INVITE")==0) t=(belle_sip_client_transaction_t*)belle_sip_ict_new(prov,req); else if (strcmp(method,"ACK")==0){ belle_sip_error("belle_sip_provider_create_client_transaction() cannot be used for ACK requests."); return NULL; } else { t=(belle_sip_client_transaction_t*)belle_sip_nict_new(prov,req); if (strcmp(method,"CANCEL")==0){ /*force next hop*/ inv_transaction=belle_sip_provider_find_matching_client_transaction_from_req(prov,req); if (inv_transaction && inv_transaction->next_hop) { /*found corresponding ict, taking next hop*/ /*9.1 Client Behavior * The destination address, port, and transport for the CANCEL MUST be identical to those used to send the original request.*/ t->next_hop=(belle_sip_hop_t*)belle_sip_object_ref(inv_transaction->next_hop); } else { belle_sip_error ("No corresponding ict nor dest found for cancel request attached to transaction [%p]",t); } } } belle_sip_transaction_set_dialog((belle_sip_transaction_t*)t,belle_sip_provider_find_dialog_from_message(prov,(belle_sip_message_t*)req,FALSE)); belle_sip_request_set_dialog(req,NULL);/*get rid of the reference to the dialog, which is no longer needed in the message. This is to avoid circular references.*/ return t; } belle_sip_server_transaction_t *belle_sip_provider_create_server_transaction(belle_sip_provider_t *prov, belle_sip_request_t *req){ belle_sip_server_transaction_t* t; belle_sip_response_t *resp = NULL; if (strcmp(belle_sip_request_get_method(req),"INVITE")==0){ t=(belle_sip_server_transaction_t*)belle_sip_ist_new(prov,req); /*create a 100 Trying response to immediately stop client retransmissions*/ resp=belle_sip_response_create_from_request(req,100); }else if (strcmp(belle_sip_request_get_method(req),"ACK")==0){ belle_sip_error("Creating a server transaction for an ACK is not a good idea, probably"); return NULL; }else t=(belle_sip_server_transaction_t*)belle_sip_nist_new(prov,req); belle_sip_transaction_set_dialog((belle_sip_transaction_t*)t,belle_sip_provider_find_dialog_from_message(prov,(belle_sip_message_t*)req,TRUE)); belle_sip_provider_add_server_transaction(prov,t); if (resp){ /*the response must be sent after the server transaction is refd by belle_sip_provider_add_server_transaction , otherwise * through callbacks we'll reach a point where it is unrefed before leaving from this function*/ belle_sip_server_transaction_send_response(t, resp); } return t; } belle_sip_stack_t *belle_sip_provider_get_sip_stack(belle_sip_provider_t *p){ return p->stack; } belle_sip_channel_t * belle_sip_provider_get_channel(belle_sip_provider_t *p, const belle_sip_hop_t *hop){ belle_sip_list_t *l; belle_sip_listening_point_t *candidate=NULL,*lp; belle_sip_channel_t *chan; if (hop->transport!=NULL) { for(l=p->lps;l!=NULL;l=l->next){ lp=(belle_sip_listening_point_t*)l->data; if (strcasecmp(belle_sip_listening_point_get_transport(lp),hop->transport)==0){ chan=belle_sip_listening_point_get_channel(lp,hop); if (chan) { belle_sip_channel_check_dns_reusability(chan); return chan; } candidate=lp; } } if (candidate){ chan=belle_sip_listening_point_create_channel(candidate,hop); if (!chan) belle_sip_error("Could not create channel to [%s://%s:%i]",hop->transport,hop->host,hop->port); return chan; } } belle_sip_error("No listening point matching for [%s://%s:%i]",hop->transport,hop->host,hop->port); return NULL; } void belle_sip_provider_release_channel(belle_sip_provider_t *p, belle_sip_channel_t *chan){ belle_sip_listening_point_remove_channel(chan->lp,chan); } void belle_sip_provider_clean_channels(belle_sip_provider_t *p){ belle_sip_list_t *l; belle_sip_listening_point_t *lp; for(l=p->lps;l!=NULL;l=l->next){ lp=(belle_sip_listening_point_t*)l->data; belle_sip_listening_point_clean_channels(lp); } } void belle_sip_provider_send_request(belle_sip_provider_t *p, belle_sip_request_t *req){ belle_sip_hop_t* hop; belle_sip_channel_t *chan; hop=belle_sip_stack_get_next_hop(p->stack,req); chan=belle_sip_provider_get_channel(p,hop); if (chan) { belle_sip_channel_queue_message(chan,BELLE_SIP_MESSAGE(req)); } } void belle_sip_provider_send_response(belle_sip_provider_t *p, belle_sip_response_t *resp){ belle_sip_hop_t* hop; belle_sip_channel_t *chan; belle_sip_header_to_t *to=(belle_sip_header_to_t*)belle_sip_message_get_header((belle_sip_message_t*)resp,"to"); if (belle_sip_response_get_status_code(resp)!=100 && to && belle_sip_header_to_get_tag(to)==NULL){ char token[BELLE_SIP_TAG_LENGTH]; compute_hash_from_invariants((belle_sip_message_t*)resp,token,sizeof(token),"tag"); belle_sip_header_to_set_tag(to,token); } hop=belle_sip_response_get_return_hop(resp); if (hop){ chan=belle_sip_provider_get_channel(p,hop); if (chan) belle_sip_channel_queue_message(chan,BELLE_SIP_MESSAGE(resp)); belle_sip_object_unref(hop); } } /*private provider API*/ void belle_sip_provider_set_transaction_terminated(belle_sip_provider_t *p, belle_sip_transaction_t *t){ belle_sip_transaction_terminated_event_t ev; BELLE_SIP_OBJECT_VPTR(t,belle_sip_transaction_t)->on_terminate(t); ev.source=t->provider; ev.transaction=t; ev.is_server_transaction=BELLE_SIP_IS_INSTANCE_OF(t,belle_sip_server_transaction_t); BELLE_SIP_PROVIDER_INVOKE_LISTENERS_FOR_TRANSACTION(t,process_transaction_terminated,&ev); if (!ev.is_server_transaction){ belle_sip_provider_remove_client_transaction(p,(belle_sip_client_transaction_t*)t); }else{ belle_sip_provider_remove_server_transaction(p,(belle_sip_server_transaction_t*)t); } } void belle_sip_provider_add_client_transaction(belle_sip_provider_t *prov, belle_sip_client_transaction_t *t){ prov->client_transactions=belle_sip_list_prepend(prov->client_transactions,belle_sip_object_ref(t)); } struct client_transaction_matcher{ const char *branchid; const char *method; }; static int client_transaction_match(const void *p_tr, const void *p_matcher){ belle_sip_client_transaction_t *tr=(belle_sip_client_transaction_t*)p_tr; struct client_transaction_matcher *matcher=(struct client_transaction_matcher*)p_matcher; const char *req_method=belle_sip_request_get_method(tr->base.request); if (strcmp(matcher->branchid,tr->base.branch_id)==0 && strcmp(matcher->method,req_method)==0) return 0; return -1; } belle_sip_client_transaction_t * belle_sip_provider_find_matching_client_transaction(belle_sip_provider_t *prov, belle_sip_response_t *resp){ struct client_transaction_matcher matcher; belle_sip_header_via_t *via=(belle_sip_header_via_t*)belle_sip_message_get_header((belle_sip_message_t*)resp,"via"); belle_sip_header_cseq_t *cseq=(belle_sip_header_cseq_t*)belle_sip_message_get_header((belle_sip_message_t*)resp,"cseq"); belle_sip_client_transaction_t *ret=NULL; belle_sip_list_t *elem; if (via==NULL){ belle_sip_warning("Response has no via."); return NULL; } if (cseq==NULL){ belle_sip_warning("Response has no cseq."); return NULL; } matcher.branchid=belle_sip_header_via_get_branch(via); matcher.method=belle_sip_header_cseq_get_method(cseq); elem=belle_sip_list_find_custom(prov->client_transactions,client_transaction_match,&matcher); if (elem){ ret=(belle_sip_client_transaction_t*)elem->data; belle_sip_message("Found transaction matching response."); } return ret; } void belle_sip_provider_remove_client_transaction(belle_sip_provider_t *prov, belle_sip_client_transaction_t *t){ belle_sip_list_t* elem=belle_sip_list_find(prov->client_transactions,t); if (elem) { prov->client_transactions=belle_sip_list_delete_link(prov->client_transactions,elem); belle_sip_object_unref(t); } else { belle_sip_error("trying to remove transaction [%p] not part of provider [%p]",t,prov); } } void belle_sip_provider_add_server_transaction(belle_sip_provider_t *prov, belle_sip_server_transaction_t *t){ prov->server_transactions=belle_sip_list_prepend(prov->server_transactions,belle_sip_object_ref(t)); } struct transaction_matcher{ const char *branchid; const char *method; const char *sentby; int is_ack_or_cancel; }; static int transaction_match(const void *p_tr, const void *p_matcher){ belle_sip_transaction_t *tr=(belle_sip_transaction_t*)p_tr; struct transaction_matcher *matcher=(struct transaction_matcher*)p_matcher; const char *req_method=belle_sip_request_get_method(tr->request); if (strcmp(matcher->branchid,tr->branch_id)==0){ if (strcmp(matcher->method,req_method)==0) return 0; if (matcher->is_ack_or_cancel && strcmp(req_method,"INVITE")==0) return 0; } return -1; } belle_sip_transaction_t * belle_sip_provider_find_matching_transaction(belle_sip_list_t *transactions, belle_sip_request_t *req){ struct transaction_matcher matcher; belle_sip_header_via_t *via=(belle_sip_header_via_t*)belle_sip_message_get_header((belle_sip_message_t*)req,"via"); belle_sip_transaction_t *ret=NULL; belle_sip_list_t *elem=NULL; const char *branch; char token[BELLE_SIP_BRANCH_ID_LENGTH]; matcher.method=belle_sip_request_get_method(req); matcher.is_ack_or_cancel=(strcmp(matcher.method,"ACK")==0 || strcmp(matcher.method,"CANCEL")==0); if (via!=NULL && (branch=belle_sip_header_via_get_branch(via))!=NULL && strncmp(branch,BELLE_SIP_BRANCH_MAGIC_COOKIE,strlen(BELLE_SIP_BRANCH_MAGIC_COOKIE))==0){ matcher.branchid=branch; }else{ /*this request comes from an old equipment, we need to compute our own branch for this request.*/ matcher.branchid=compute_rfc2543_branch(req,token,sizeof(token)); belle_sip_request_set_rfc2543_branch(req,token); belle_sip_message("Message from old RFC2543 stack, computed branch is %s", token); } elem=belle_sip_list_find_custom(transactions,transaction_match,&matcher); if (elem){ ret=(belle_sip_transaction_t*)elem->data; belle_sip_message("Found transaction [%p] matching request.",ret); } return ret; } belle_sip_server_transaction_t * belle_sip_provider_find_matching_server_transaction(belle_sip_provider_t *prov, belle_sip_request_t *req) { belle_sip_transaction_t *ret=belle_sip_provider_find_matching_transaction(prov->server_transactions,req); return ret?BELLE_SIP_SERVER_TRANSACTION(ret):NULL; } belle_sip_client_transaction_t * belle_sip_provider_find_matching_client_transaction_from_req(belle_sip_provider_t *prov, belle_sip_request_t *req) { belle_sip_transaction_t *ret=belle_sip_provider_find_matching_transaction(prov->client_transactions,req); return ret?BELLE_SIP_CLIENT_TRANSACTION(ret):NULL; } void belle_sip_provider_remove_server_transaction(belle_sip_provider_t *prov, belle_sip_server_transaction_t *t){ prov->server_transactions=belle_sip_list_remove(prov->server_transactions,t); belle_sip_object_unref(t); } static void authorization_context_fill_from_auth(authorization_context_t* auth_context,belle_sip_header_www_authenticate_t* authenticate,belle_sip_uri_t *from_uri) { authorization_context_set_realm(auth_context,belle_sip_header_www_authenticate_get_realm(authenticate)); if (auth_context->nonce && strcmp(belle_sip_header_www_authenticate_get_nonce(authenticate),auth_context->nonce)!=0) { /*new nonce, resetting nounce_count*/ auth_context->nonce_count=0; } authorization_context_set_nonce(auth_context,belle_sip_header_www_authenticate_get_nonce(authenticate)); authorization_context_set_algorithm(auth_context,belle_sip_header_www_authenticate_get_algorithm(authenticate)); authorization_context_set_qop(auth_context,belle_sip_header_www_authenticate_get_qop_first(authenticate)); authorization_context_set_scheme(auth_context,belle_sip_header_www_authenticate_get_scheme(authenticate)); authorization_context_set_opaque(auth_context,belle_sip_header_www_authenticate_get_opaque(authenticate)); authorization_context_set_user_id(auth_context, from_uri?belle_sip_uri_get_user(from_uri):NULL); if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(authenticate,belle_sip_header_proxy_authenticate_t)) { auth_context->is_proxy=1; } } static belle_sip_list_t* belle_sip_provider_get_auth_context_by_realm_or_call_id(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id,belle_sip_uri_t *from_uri,const char* realm) { belle_sip_list_t* auth_context_lst=NULL; belle_sip_list_t* result=NULL; authorization_context_t* auth_context; for (auth_context_lst=p->auth_contexts;auth_context_lst!=NULL;auth_context_lst=auth_context_lst->next) { auth_context=(authorization_context_t*)auth_context_lst->data; if (belle_sip_header_call_id_equals(auth_context->callid,call_id) ) { result=belle_sip_list_append(result,auth_context_lst->data); } } /* According to the RFC3261 22.3, if the outbound proxy realm is set, we could reuse its nonce value: * "If a UA receives a Proxy-Authenticate header field value in a 401/407 * response to a request with a particular Call-ID, it should * incorporate credentials for that realm in all subsequent requests * that contain the same Call-ID. These credentials MUST NOT be cached * across dialogs; however, if a UA is configured with the realm of its * local outbound proxy, when one exists, then the UA MAY cache * credentials for that realm across dialogs." */ if (result == NULL){ const char * from_user=from_uri?belle_sip_uri_get_user(from_uri):NULL; belle_sip_debug("belle_sip_provider_auth: no auth context registered with [call_id=%s], looking for realm..." , call_id?belle_sip_header_call_id_get_call_id(call_id):"(null)"); for (auth_context_lst=p->auth_contexts;auth_context_lst!=NULL;auth_context_lst=auth_context_lst->next) { auth_context=(authorization_context_t*)auth_context_lst->data; belle_sip_debug("belle_sip_provider_auth: \t[realm=%s] [user_id=%s] [call_id=%s]", auth_context->realm?auth_context->realm:"(null)", auth_context->user_id?auth_context->user_id:"(null)", auth_context->callid?belle_sip_header_call_id_get_call_id(auth_context->callid):"(null)" ); /* We also verify that user matches in case of multi-account to avoid use nonce from another account. For a * single user, from_uri user id and realm user id COULD be different but we assume here that this is not the case * in order to avoid adding another field in auth_context struct. **/ if ((realm && strcmp(auth_context->realm,realm)==0) && (from_user && auth_context->user_id && strcmp(auth_context->user_id,from_user)==0)) { result=belle_sip_list_append(result,auth_context_lst->data); belle_sip_debug("belle_sip_provider_auth: found a MATCHING realm auth context!"); } } } return result; } static void belle_sip_provider_update_or_create_auth_context(belle_sip_provider_t *p,belle_sip_header_call_id_t* call_id,belle_sip_header_www_authenticate_t* authenticate,belle_sip_uri_t *from_uri,const char* realm) { belle_sip_list_t* auth_context_lst = NULL; authorization_context_t* auth_context; for (auth_context_lst=belle_sip_provider_get_auth_context_by_realm_or_call_id(p,call_id,from_uri,realm);auth_context_lst!=NULL;auth_context_lst=auth_context_lst->next) { auth_context=(authorization_context_t*)auth_context_lst->data; if (strcmp(auth_context->realm,belle_sip_header_www_authenticate_get_realm(authenticate))==0) { authorization_context_fill_from_auth(auth_context,authenticate,from_uri); if (auth_context_lst) belle_sip_free(auth_context_lst); return; /*only one realm is supposed to be found for now*/ } } /*no auth context found, creating one*/ auth_context=belle_sip_authorization_create(call_id); belle_sip_debug("belle_sip_provider_auth: no matching auth context, creating one for [realm=%s][user_id=%s][call_id=%s]" , belle_sip_header_www_authenticate_get_realm(authenticate)?belle_sip_header_www_authenticate_get_realm(authenticate):"(null)" , from_uri?belle_sip_uri_get_user(from_uri):"(null)" , call_id?belle_sip_header_call_id_get_call_id(call_id):"(null)"); authorization_context_fill_from_auth(auth_context,authenticate,from_uri); p->auth_contexts=belle_sip_list_append(p->auth_contexts,auth_context); if (auth_context_lst) belle_sip_free(auth_context_lst); return; } int belle_sip_provider_add_authorization(belle_sip_provider_t *p, belle_sip_request_t* request, belle_sip_response_t *resp, belle_sip_uri_t *from_uri, belle_sip_list_t** auth_infos, const char* realm) { belle_sip_header_call_id_t* call_id; belle_sip_list_t* auth_context_iterator; belle_sip_list_t* authenticate_lst; belle_sip_list_t* head; belle_sip_header_www_authenticate_t* authenticate; belle_sip_header_authorization_t* authorization; belle_sip_header_from_t* from; belle_sip_auth_event_t* auth_event; authorization_context_t* auth_context; const char* ha1; char computed_ha1[33]; int result=0; const char* request_method; /*check params*/ if (!p || !request) { belle_sip_error("belle_sip_provider_add_authorization bad parameters"); return-1; } request_method=belle_sip_request_get_method(request); /*22 Usage of HTTP Authentication 22.1 Framework While a server can legitimately challenge most SIP requests, there are two requests defined by this document that require special handling for authentication: ACK and CANCEL. Under an authentication scheme that uses responses to carry values used to compute nonces (such as Digest), some problems come up for any requests that take no response, including ACK. For this reason, any credentials in the INVITE that were accepted by a server MUST be accepted by that server for the ACK. UACs creating an ACK message will duplicate all of the Authorization and Proxy-Authorization header field values that appeared in the INVITE to which the ACK corresponds. Servers MUST NOT attempt to challenge an ACK. Although the CANCEL method does take a response (a 2xx), servers MUST NOT attempt to challenge CANCEL requests since these requests cannot be resubmitted. Generally, a CANCEL request SHOULD be accepted by a server if it comes from the same hop that sent the request being canceled (provided that some sort of transport or network layer security association, as described in Section 26.2.1, is in place). */ if (strcmp("CANCEL",request_method)==0 || strcmp("ACK",request_method)==0) { belle_sip_debug("no authorization header needed for method [%s]",request_method); return 0; } if (from_uri==NULL){ from = belle_sip_message_get_header_by_type(request,belle_sip_header_from_t); from_uri=belle_sip_header_address_get_uri((belle_sip_header_address_t*)from); } /*get authenticates value from response*/ if (resp) { belle_sip_list_t *it; call_id = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(resp),belle_sip_header_call_id_t); /*searching for authentication headers*/ authenticate_lst = belle_sip_list_copy(belle_sip_message_get_headers(BELLE_SIP_MESSAGE(resp),BELLE_SIP_WWW_AUTHENTICATE)); /*search for proxy authenticate*/ authenticate_lst=belle_sip_list_concat(authenticate_lst,belle_sip_list_copy(belle_sip_message_get_headers(BELLE_SIP_MESSAGE(resp),BELLE_SIP_PROXY_AUTHENTICATE))); /*update auth contexts with authenticate headers from response*/ for (it=authenticate_lst;it!=NULL;it=it->next) { authenticate=BELLE_SIP_HEADER_WWW_AUTHENTICATE(it->data); belle_sip_provider_update_or_create_auth_context(p,call_id,authenticate,from_uri,realm); } belle_sip_list_free(authenticate_lst); } /*put authorization header if passwd found*/ call_id = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_call_id_t); belle_sip_debug("belle_sip_provider_auth: looking an auth context for [method=%s][realm=%s][user_id=%s][call_id=%s]" , request_method , realm?realm:"(null)" , from_uri?belle_sip_uri_get_user(from_uri):"(null)" , call_id?belle_sip_header_call_id_get_call_id(call_id):"(null)" ); head=belle_sip_provider_get_auth_context_by_realm_or_call_id(p,call_id,from_uri,realm); /*we assume there no existing auth headers*/ for (auth_context_iterator=head;auth_context_iterator!=NULL;auth_context_iterator=auth_context_iterator->next) { /*clear auth info*/ auth_context=(authorization_context_t*)auth_context_iterator->data; auth_event = belle_sip_auth_event_create((belle_sip_object_t*)p,auth_context->realm,from_uri); /*put data*/ /*call listener*/ BELLE_SIP_PROVIDER_INVOKE_LISTENERS(p->listeners,process_auth_requested,auth_event); if (auth_event->passwd || auth_event->ha1) { if (!auth_event->userid) { /*if no userid, username = userid*/ belle_sip_auth_event_set_userid(auth_event,auth_event->username); } belle_sip_message("Auth info found for [%s] realm [%s]",auth_event->userid,auth_event->realm); if (auth_context->is_proxy || (!belle_sip_header_call_id_equals(call_id,auth_context->callid) &&realm &&strcmp(realm,auth_context->realm)==0 &&from_uri &&strcmp(auth_event->username,belle_sip_uri_get_user(from_uri))==0 &&strcmp("REGISTER",request_method)!=0)) /* Relying on method name for choosing between authorization and proxy-authorization is not very strong but I don't see any better solution as we don't know all the time what type of challange we will have*/{ authorization=BELLE_SIP_HEADER_AUTHORIZATION(belle_sip_header_proxy_authorization_new()); } else { authorization=belle_sip_header_authorization_new(); } belle_sip_header_authorization_set_scheme(authorization,auth_context->scheme); belle_sip_header_authorization_set_realm(authorization,auth_context->realm); belle_sip_header_authorization_set_username(authorization,auth_event->userid); belle_sip_header_authorization_set_nonce(authorization,auth_context->nonce); belle_sip_header_authorization_set_qop(authorization,auth_context->qop); belle_sip_header_authorization_set_opaque(authorization,auth_context->opaque); belle_sip_header_authorization_set_algorithm(authorization,auth_context->algorithm); belle_sip_header_authorization_set_uri(authorization,(belle_sip_uri_t*)belle_sip_request_get_uri(request)); if (auth_context->qop){ ++auth_context->nonce_count; belle_sip_header_authorization_set_nonce_count(authorization,auth_context->nonce_count); } if (auth_event->ha1) { ha1=auth_event->ha1; } else { belle_sip_auth_helper_compute_ha1(auth_event->userid,auth_context->realm,auth_event->passwd, computed_ha1); ha1=computed_ha1; } if (belle_sip_auth_helper_fill_authorization(authorization ,belle_sip_request_get_method(request) ,ha1)) { belle_sip_object_unref(authorization); } else belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(authorization)); result=1; } else { belle_sip_message("No auth info found for call id [%s]",belle_sip_header_call_id_get_call_id(call_id)); } /*provides auth info in any cases, usefull even if found because auth info can contain wrong password*/ if (auth_infos) { /*stored to give user information on realm/username which requires authentications*/ *auth_infos=belle_sip_list_append(*auth_infos,auth_event); } else { belle_sip_auth_event_destroy(auth_event); } } belle_sip_list_free(head); return result; } void belle_sip_provider_set_recv_error(belle_sip_provider_t *prov, int recv_error) { belle_sip_list_t *lps; belle_sip_list_t *channels; for(lps=prov->lps;lps!=NULL;lps=lps->next){ for(channels=((belle_sip_listening_point_t*)lps->data)->channels;channels!=NULL;channels=channels->next){ ((belle_sip_channel_t*)channels->data)->simulated_recv_return=recv_error; ((belle_sip_source_t*)channels->data)->notify_required=(recv_error<=0); } } } void belle_sip_provider_enable_rport(belle_sip_provider_t *prov, int enable) { prov->rport_enabled=enable; } int belle_sip_provider_is_rport_enabled(belle_sip_provider_t *prov) { return prov->rport_enabled; } void belle_sip_provider_enable_nat_helper(belle_sip_provider_t *prov, int enabled){ prov->nat_helper=enabled; } int belle_sip_provider_nat_helper_enabled(const belle_sip_provider_t *prov){ return prov->nat_helper; } void belle_sip_provider_enable_unconditional_answer(belle_sip_provider_t *prov, int enable) { prov->unconditional_answer_enabled=enable; } void belle_sip_provider_set_unconditional_answer(belle_sip_provider_t *prov, unsigned short code) { prov->unconditional_answer=code; } belle-sip-1.6.3/src/refresher.c000066400000000000000000001145061313437522400163300ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2012 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "belle_sip_internal.h" #include "belle-sip/refresher.h" #define DEFAULT_RETRY_AFTER 60000 #define DEFAULT_INITIAL_RETRY_AFTER_ON_IO_ERROR 500 static void belle_sip_refresher_stop_internal(belle_sip_refresher_t* refresher,int cancel_pending_transaction) ; typedef enum belle_sip_refresher_state { started, stopped }belle_sip_refresher_state_t; typedef enum timer_purpose{ NORMAL_REFRESH, RETRY }timer_purpose_t; struct belle_sip_refresher { belle_sip_object_t obj; belle_sip_refresher_listener_t listener; belle_sip_source_t* timer; belle_sip_client_transaction_t* transaction; belle_sip_request_t* first_acknoleged_request; /*store first request sucessfully acknoleged, usefull to re-build a dialog iff needed*/ belle_sip_dialog_t* dialog; /*Cannot rely on transaction to store dialog because of belle_sip_transaction_reset_dialog*/ char* realm; int target_expires; int obtained_expires; belle_sip_refresher_state_t state; void* user_data; int retry_after; belle_sip_list_t* auth_events; int auth_failures; int on_io_error; /*flag to avoid multiple error notification*/ int number_of_retry; /*counter to count number of unsuccesfull retry, used to know when to retry*/ timer_purpose_t timer_purpose; unsigned char manual; unsigned int publish_pending; }; static void set_or_update_dialog(belle_sip_refresher_t* refresher, belle_sip_dialog_t* dialog); static int set_expires_from_trans(belle_sip_refresher_t* refresher); static int timer_cb(void *user_data, unsigned int events) ; static int belle_sip_refresher_refresh_internal(belle_sip_refresher_t* refresher,int expires,int auth_mandatory, belle_sip_list_t** auth_infos, belle_sip_uri_t *requri); static void cancel_retry(belle_sip_refresher_t* refresher) { if (refresher->timer){ belle_sip_main_loop_remove_source(belle_sip_stack_get_main_loop(refresher->transaction->base.provider->stack),refresher->timer); belle_sip_object_unref(refresher->timer); refresher->timer=NULL; } } static void schedule_timer_at(belle_sip_refresher_t* refresher,int delay, timer_purpose_t purpose) { belle_sip_message("Refresher[%p]: scheduling next timer in %i ms for purpose [%s]",refresher, delay, purpose == NORMAL_REFRESH ? "normal refresh" : "retry"); refresher->timer_purpose=purpose; /*cancel timer if any*/ cancel_retry(refresher); refresher->timer=belle_sip_timeout_source_new(timer_cb,refresher,delay); belle_sip_object_set_name((belle_sip_object_t*)refresher->timer,"Refresher timeout"); belle_sip_main_loop_add_source(belle_sip_stack_get_main_loop(refresher->transaction->base.provider->stack),refresher->timer); } static void retry_later(belle_sip_refresher_t* refresher) { refresher->number_of_retry++; schedule_timer_at(refresher,refresher->retry_after,RETRY); } static void retry_later_on_io_error(belle_sip_refresher_t* refresher) { /*if first retry, sent it in 500 ms*/ if (refresher->number_of_retry < 1) { schedule_timer_at(refresher,DEFAULT_INITIAL_RETRY_AFTER_ON_IO_ERROR,RETRY); refresher->number_of_retry++; } else { retry_later(refresher); } } static void schedule_timer(belle_sip_refresher_t* refresher) { schedule_timer_at(refresher,refresher->obtained_expires*900,NORMAL_REFRESH); } static void process_dialog_terminated(belle_sip_listener_t *user_ctx, const belle_sip_dialog_terminated_event_t *event){ belle_sip_refresher_t* refresher=(belle_sip_refresher_t*)user_ctx; belle_sip_dialog_t *dialog =belle_sip_dialog_terminated_event_get_dialog(event); if (refresher && refresher->transaction && dialog != belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(refresher->transaction))) return; /*not for me*/ if (belle_sip_dialog_expired(dialog) && refresher->state == started) { /* We notify the app that the dialog is expired due to failure to refresh the subscription on time. * However the transaction to renew the dialog is either pending or already failed, and has scheduled a retry already, * so there is no need to reschedule a retry here.*/ belle_sip_warning("Refresher [%p] still started but expired, retrying",refresher); if (refresher->listener) refresher->listener(refresher,refresher->user_data,481, "dialod terminated", TRUE); } } static void process_io_error(belle_sip_listener_t *user_ctx, const belle_sip_io_error_event_t *event){ belle_sip_refresher_t* refresher=(belle_sip_refresher_t*)user_ctx; belle_sip_client_transaction_t*client_transaction; if (refresher->on_io_error==1) { return; /*refresher already on error*/ } if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(belle_sip_io_error_event_get_source(event),belle_sip_client_transaction_t)) { client_transaction=BELLE_SIP_CLIENT_TRANSACTION(belle_sip_io_error_event_get_source(event)); if (!refresher || (refresher && ((refresher->state==stopped && belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)) != BELLE_SIP_TRANSACTION_TRYING && belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)) != BELLE_SIP_TRANSACTION_INIT /*to cover dns or certificate error*/) || client_transaction !=refresher->transaction ))) return; /*not for me or no longuer involved*/ if (refresher->target_expires==0 && belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)) != BELLE_SIP_TRANSACTION_TRYING && belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)) != BELLE_SIP_TRANSACTION_INIT ) { return; /*not for me or no longuer involved because expire=0*/ } if (refresher->state==started) retry_later_on_io_error(refresher); if (refresher->listener) refresher->listener(refresher,refresher->user_data,503, "io error", refresher->state == started); return; } else if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(belle_sip_io_error_event_get_source(event),belle_sip_provider_t)) { /*something went wrong on this provider, checking if my channel is still up*/ if (refresher->state==started /*refresher started or trying to refresh */ && belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)) == BELLE_SIP_TRANSACTION_TERMINATED /*else we are notified by transaction error*/ && refresher->transaction->base.channel /*transaction may not have any channel*/ && (belle_sip_channel_get_state(refresher->transaction->base.channel) == BELLE_SIP_CHANNEL_DISCONNECTED ||belle_sip_channel_get_state(refresher->transaction->base.channel) == BELLE_SIP_CHANNEL_ERROR)) { belle_sip_message("refresher [%p] has channel [%p] in state [%s], reporting error" ,refresher ,refresher->transaction->base.channel ,belle_sip_channel_state_to_string(belle_sip_channel_get_state(refresher->transaction->base.channel))); if (refresher->state==started) retry_later_on_io_error(refresher); if (refresher->listener) refresher->listener(refresher,refresher->user_data,503, "io error", refresher->state == started); refresher->on_io_error=1; } return; }else { /*belle_sip_error("Refresher process_io_error not implemented yet for non transaction/provider source");*/ /*nop, because already handle at transaction layer*/ } } belle_sip_header_contact_t* get_first_contact_in_unknown_state(belle_sip_request_t *req){ /*check if automatic contacts could be set by provider, if not resubmit the request immediately.*/ belle_sip_header_contact_t *contact; const belle_sip_list_t *l; for (l=belle_sip_message_get_headers((belle_sip_message_t*)req,"Contact");l!=NULL;l=l->next){ contact=(belle_sip_header_contact_t*)l->data; if (belle_sip_header_contact_is_unknown(contact)){ return contact; } } return NULL; } static int is_contact_address_acurate(const belle_sip_refresher_t* refresher,belle_sip_request_t* request) { belle_sip_header_contact_t* contact; if ((contact = get_first_contact_in_unknown_state(request))){ /*check if contact ip/port is consistant with public channel ip/port*/ int channel_public_port = refresher->transaction->base.channel->public_port; int contact_port = belle_sip_uri_get_listening_port(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(contact))); const char* channel_public_ip = refresher->transaction->base.channel->public_ip; const char* contact_ip = belle_sip_uri_get_host(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(contact))); if (channel_public_port == contact_port && channel_public_ip && contact_ip && strcmp(channel_public_ip,contact_ip) == 0) { /*nothing to do contact is accurate*/ belle_sip_header_contact_set_unknown(contact,FALSE); return TRUE; } else { belle_sip_message("Refresher [%p]: contact address [%s:%i] does not match channel address[%s:%i] on channel [%p]" ,refresher ,contact_ip ,contact_port ,channel_public_ip ,channel_public_port ,refresher->transaction->base.channel); return FALSE; } } else { belle_sip_message("Refresher [%p]: has no contact for request [%p].", refresher,request); return TRUE; } } static void process_response_event(belle_sip_listener_t *user_ctx, const belle_sip_response_event_t *event){ belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event); belle_sip_response_t* response = belle_sip_response_event_get_response(event); belle_sip_request_t* request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); int response_code = belle_sip_response_get_status_code(response); belle_sip_refresher_t* refresher=(belle_sip_refresher_t*)user_ctx; belle_sip_header_contact_t *contact; int will_retry = TRUE; /*most error codes are retryable*/ if (refresher && (client_transaction !=refresher->transaction)) return; /*not for me*/ set_or_update_dialog(refresher,belle_sip_response_event_get_dialog(event)); /*success case:*/ if (response_code>=200 && response_code<300){ refresher->auth_failures=0; refresher->number_of_retry=0; /*great, success*/ if (strcmp(belle_sip_request_get_method(request),"PUBLISH")==0) { /*search for etag*/ belle_sip_header_t* etag=belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"SIP-ETag"); if (etag) { belle_sip_header_t* sip_if_match = belle_sip_header_create("SIP-If-Match",belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(etag))); /*update request for next refresh*/ belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),"SIP-If-Match"); belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),sip_if_match); } else if (refresher->target_expires > 0){ belle_sip_warning("Refresher [%p] received 200ok to a publish without etag",refresher); } } /*update expire if needed*/ set_expires_from_trans(refresher); if (refresher->target_expires<=0) { belle_sip_refresher_stop(refresher); /*doesn't not make sense to refresh if expire =0;*/ } else { /*remove all contact with expire = 0 from request if any, because no need to refresh them*/ const belle_sip_list_t * contact_list= belle_sip_message_get_headers(BELLE_SIP_MESSAGE(request),BELLE_SIP_CONTACT); belle_sip_list_t *iterator, *head; if (contact_list) { for (iterator=head=belle_sip_list_copy(contact_list);iterator!=NULL;iterator=iterator->next) { belle_sip_header_contact_t *contact_for_expire = (belle_sip_header_contact_t *)(iterator->data); if (belle_sip_header_contact_get_expires(contact_for_expire) == 0) { belle_sip_message_remove_header_from_ptr(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(contact_for_expire)); } } belle_sip_list_free(head); } } if (refresher->state==started) { if (!refresher->first_acknoleged_request) belle_sip_object_ref(refresher->first_acknoleged_request = request); if (is_contact_address_acurate(refresher,request) || !belle_sip_provider_nat_helper_enabled(client_transaction->base.provider)) { schedule_timer(refresher); /*re-arm timer*/ } else { belle_sip_message("belle_sip_refresher_start(): refresher [%p] is resubmitting request because contact sent was not correct in original request.",refresher); belle_sip_refresher_refresh(refresher,refresher->target_expires); return; } } else belle_sip_message("Refresher [%p] not scheduling next refresh, because it was stopped",refresher); }else if (response_code >= 300) {/*special error cases*/ switch (response_code) { case 301: case 302: contact=belle_sip_message_get_header_by_type(response,belle_sip_header_contact_t); if (contact){ belle_sip_uri_t *uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(contact)); if (uri && belle_sip_refresher_refresh_internal(refresher,refresher->target_expires,TRUE,&refresher->auth_events,uri)==0) return; } break; case 401: case 407: refresher->auth_failures++; if (refresher->auth_failures>1){ /*avoid looping with 407 or 401 */ belle_sip_warning("Authentication is failing constantly, %s",(refresher->target_expires>0)? "will retry later":"giving up."); if (refresher->target_expires>0) retry_later(refresher); refresher->auth_failures=0; /*reset auth failure*/ break; } if (refresher->auth_events) { refresher->auth_events=belle_sip_list_free_with_data(refresher->auth_events,(void (*)(void*))belle_sip_auth_event_destroy); } if (belle_sip_refresher_refresh_internal(refresher,refresher->target_expires,TRUE,&refresher->auth_events,NULL)==0) return; /*ok, keep 401 internal*/ break; /*Else notify user of registration failure*/ case 403: /*In case of 403, we will retry later, just in case*/ if (refresher->target_expires>0) retry_later(refresher); else will_retry = FALSE; break; case 412: if (strcmp(belle_sip_request_get_method(request),"PUBLISH")==0) { belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),"SIP-If-Match"); if (refresher->target_expires>0) { retry_later_on_io_error(refresher); return; /*do not notify this kind of error*/ }else{ will_retry = FALSE; } } else { if (refresher->target_expires>0) retry_later(refresher); else will_retry = FALSE; } break; case 423:{ belle_sip_header_extension_t *min_expires=BELLE_SIP_HEADER_EXTENSION(belle_sip_message_get_header((belle_sip_message_t*)response,"Min-Expires")); if (min_expires){ const char *value=belle_sip_header_extension_get_value(min_expires); if (value){ int new_expires=atoi(value); if (new_expires>0 && refresher->state==started){ refresher->target_expires=new_expires; belle_sip_refresher_refresh(refresher,refresher->target_expires); return; } } }else belle_sip_warning("Receiving 423 but no min-expires header."); will_retry = FALSE; break; } case 491: { if (refresher->target_expires>0) { int delay = belle_sip_random() % 10000; /*schedule a retry between 0 and 10 seconds*/ schedule_timer_at(refresher, delay, RETRY); return; /*do not notify this kind of error*/ } } BCTBX_NO_BREAK; /*intentionally no break*/ case 505: case 501: /*irrecoverable errors, probably no need to retry later*/ will_retry = FALSE; break; case 481: case 503: if (refresher->target_expires>0) { if (refresher->dialog) retry_later_on_io_error(refresher); else retry_later(refresher); }else will_retry = FALSE; break; default: /*for all other errors <600, retry later*/ if (response_code < 600 && refresher->target_expires>0) retry_later(refresher); else will_retry = FALSE; break; } } if (refresher->listener) refresher->listener(refresher,refresher->user_data,response_code, belle_sip_response_get_reason_phrase(response), will_retry); } static void process_timeout(belle_sip_listener_t *user_ctx, const belle_sip_timeout_event_t *event) { belle_sip_refresher_t* refresher=(belle_sip_refresher_t*)user_ctx; belle_sip_client_transaction_t*client_transaction =belle_sip_timeout_event_get_client_transaction(event); if (refresher && (client_transaction !=refresher->transaction)) return; /*not for me*/ if (refresher->state==started) { /*retry in 2 seconds but not immediately to let the current transaction be cleaned*/ schedule_timer_at(refresher,2000,RETRY); } if (refresher->listener) refresher->listener(refresher,refresher->user_data,408, "timeout", refresher->state==started); } static void process_transaction_terminated(belle_sip_listener_t *user_ctx, const belle_sip_transaction_terminated_event_t *event) { belle_sip_refresher_t* refresher=(belle_sip_refresher_t*)user_ctx; belle_sip_client_transaction_t*client_transaction = belle_sip_transaction_terminated_event_get_client_transaction(event); if (refresher && (client_transaction !=refresher->transaction)) return; /*not for me*/ if (refresher->publish_pending && refresher->state==started) { refresher->publish_pending = FALSE; belle_sip_message("Publish pending on refresher [%p], doing it",refresher); belle_sip_refresher_refresh(refresher,refresher->target_expires); } else { refresher->publish_pending = FALSE; } } static void process_auth_requested(belle_sip_listener_t *l, belle_sip_auth_event_t *event){ } static void process_request_event(belle_sip_listener_t *user_ctx, const belle_sip_request_event_t *event){ } static void destroy(belle_sip_refresher_t *refresher){ belle_sip_refresher_stop(refresher); belle_sip_provider_remove_internal_sip_listener(refresher->transaction->base.provider,BELLE_SIP_LISTENER(refresher)); belle_sip_object_unref(refresher->transaction); refresher->transaction=NULL; if (refresher->realm) belle_sip_free(refresher->realm); if (refresher->auth_events) refresher->auth_events=belle_sip_list_free_with_data(refresher->auth_events,(void (*)(void*))belle_sip_auth_event_destroy); if (refresher->first_acknoleged_request) belle_sip_object_unref(refresher->first_acknoleged_request); if (refresher->dialog) belle_sip_object_unref(refresher->dialog); } BELLE_SIP_IMPLEMENT_INTERFACE_BEGIN(belle_sip_refresher_t,belle_sip_listener_t) process_dialog_terminated, process_io_error, process_request_event, process_response_event, process_timeout, process_transaction_terminated, process_auth_requested BELLE_SIP_IMPLEMENT_INTERFACE_END BELLE_SIP_DECLARE_IMPLEMENTED_INTERFACES_1(belle_sip_refresher_t, belle_sip_listener_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_refresher_t, belle_sip_object_t,destroy, NULL, NULL,FALSE); void belle_sip_refresher_set_listener(belle_sip_refresher_t* refresher, belle_sip_refresher_listener_t listener,void* user_pointer) { refresher->listener=listener; refresher->user_data=user_pointer; } int belle_sip_refresher_refresh(belle_sip_refresher_t* refresher,int expires) { /*first cancel any current retry*/ cancel_retry(refresher); refresher->auth_failures=0;/*reset the auth_failures to get a chance to authenticate again*/ return belle_sip_refresher_refresh_internal(refresher,expires,FALSE,NULL,NULL); } static int unfilled_auth_info(const void* info,const void* userptr) { belle_sip_auth_event_t* auth_info = (belle_sip_auth_event_t*)info; return auth_info->passwd || auth_info->ha1; } static int belle_sip_refresher_refresh_internal(belle_sip_refresher_t* refresher, int expires, int auth_mandatory, belle_sip_list_t** auth_infos, belle_sip_uri_t *requri) { belle_sip_request_t*old_request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(refresher->transaction)); belle_sip_response_t*old_response=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(refresher->transaction)); belle_sip_dialog_t* dialog = refresher->dialog; belle_sip_client_transaction_t* client_transaction; belle_sip_request_t* request; belle_sip_header_expires_t* expires_header; belle_sip_uri_t* preset_route=refresher->transaction->preset_route; belle_sip_provider_t* prov=refresher->transaction->base.provider; belle_sip_header_contact_t* contact; /*first remove timer if any*/ if (expires >=0) { refresher->target_expires=expires; } else { /*-1 keep last value*/ } if (!dialog) { const belle_sip_transaction_state_t state=belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)); /*create new request*/ if (belle_sip_transaction_state_is_transient(state)) { /*operation pending, cannot update authorization headers*/ belle_sip_header_cseq_t* cseq; belle_sip_message("Refresher [%p] already has transaction [%p] in state [%s]" ,refresher ,refresher->transaction ,belle_sip_transaction_state_to_string(state)); if (strcmp(belle_sip_request_get_method(old_request),"PUBLISH")==0) { belle_sip_message("Refresher [%p] new publish is delayed to end of ongoing transaction" ,refresher); refresher->publish_pending = TRUE; return 0; } else if (strcmp(belle_sip_request_get_method(old_request),"SUBSCRIBE")==0) { belle_sip_message("Cannot refresh now, there is a pending request for refresher [%p].",refresher); return -1; }else { request=belle_sip_request_clone_with_body(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(refresher->transaction))); cseq=belle_sip_message_get_header_by_type(request,belle_sip_header_cseq_t); belle_sip_header_cseq_set_seq_number(cseq,belle_sip_header_cseq_get_seq_number(cseq)+1); } } else { request=belle_sip_client_transaction_create_authenticated_request(refresher->transaction,auth_infos,refresher->realm); } if (requri){ /*case where we are redirected*/ belle_sip_request_set_uri(request,requri); /*remove auth headers, they are not valid for new destination*/ belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_AUTHORIZATION); belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_PROXY_AUTHORIZATION); } } else { switch (belle_sip_dialog_get_state(dialog)) { case BELLE_SIP_DIALOG_CONFIRMED: { if (belle_sip_dialog_request_pending(dialog)){ belle_sip_message("Cannot refresh now, there is a pending request in the dialog."); return -1; } request=belle_sip_dialog_create_request_from(dialog,old_request); if (strcmp(belle_sip_request_get_method(request),"SUBSCRIBE")==0) { belle_sip_header_content_type_t *content_type; /*put expire header*/ if (!(expires_header = belle_sip_message_get_header_by_type(request,belle_sip_header_expires_t))) { expires_header = belle_sip_header_expires_new(); belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(expires_header)); } if ((content_type = belle_sip_message_get_header_by_type(request, belle_sip_header_content_type_t)) && strcasecmp("application", belle_sip_header_content_type_get_type(content_type)) == 0 && strcasecmp("resource-lists+xml", belle_sip_header_content_type_get_subtype(content_type)) == 0) { /*rfc5367 3.2. Subsequent SUBSCRIBE Requests ... At this point, there are no semantics associated with resource-list bodies in subsequent SUBSCRIBE requests (although future extensions can define them). Therefore, UACs SHOULD NOT include resource-list bodies in subsequent SUBSCRIBE requests to a resource list server. */ belle_sip_message("Removing body, content type and content length for refresher [%p]",refresher); belle_sip_message_set_body(BELLE_SIP_MESSAGE(request), NULL, 0); belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CONTENT_TYPE); belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CONTENT_LENGTH); } } belle_sip_provider_add_authorization(prov,request,old_response,NULL,auth_infos,refresher->realm); break; } case BELLE_SIP_DIALOG_TERMINATED: { if (refresher->first_acknoleged_request) { belle_sip_message("Dialog [%p] is in state terminated, recreating a new one for refresher [%p]",dialog,refresher); request = refresher->first_acknoleged_request; belle_sip_header_cseq_set_seq_number(belle_sip_message_get_header_by_type(request,belle_sip_header_cseq_t) ,20); belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(belle_sip_message_get_header_by_type(request,belle_sip_header_to_t)),"tag"); belle_sip_message_set_header((belle_sip_message_t*)request, (belle_sip_header_t*)belle_sip_provider_create_call_id(prov)); break; } BCTBX_NO_BREAK; /*else nop, error case*/ } default: { belle_sip_error("Unexpected dialog state [%s] for dialog [%p], cannot refresh [%s]" ,belle_sip_dialog_state_to_string(belle_sip_dialog_get_state(dialog)) ,dialog ,belle_sip_request_get_method(old_request)); return -1; } } } if (auth_mandatory && auth_infos && belle_sip_list_find_custom(*auth_infos, unfilled_auth_info, NULL)) { belle_sip_message("Auth info not found for this refresh operation on [%p]",refresher); if (request) belle_sip_object_unref(request); return -1; } refresher->on_io_error=0; /*reset this flag*/ /*update expires in any cases*/ expires_header = belle_sip_message_get_header_by_type(request,belle_sip_header_expires_t); if (expires_header) belle_sip_header_expires_set_expires(expires_header,refresher->target_expires); contact=belle_sip_message_get_header_by_type(request,belle_sip_header_contact_t); if (contact && belle_sip_header_contact_get_expires(contact)>=0) belle_sip_header_contact_set_expires(contact,refresher->target_expires); /*update the Date header if it exists*/ { belle_sip_header_date_t *date=belle_sip_message_get_header_by_type(request,belle_sip_header_date_t); if (date){ time_t curtime=time(NULL); belle_sip_header_date_set_time(date,&curtime); } } client_transaction = belle_sip_provider_create_client_transaction(prov,request); client_transaction->base.is_internal=1; if (request == refresher->first_acknoleged_request) { /*request is now ref by transaction so no need to keepo it*/ belle_sip_object_unref(refresher->first_acknoleged_request); refresher->first_acknoleged_request = NULL; } switch (belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction))) { case BELLE_SIP_TRANSACTION_INIT: case BELLE_SIP_TRANSACTION_CALLING: case BELLE_SIP_TRANSACTION_TRYING: /*very early state, we can assume nobody will answer, stop retransmiting*/ belle_sip_transaction_terminate(BELLE_SIP_TRANSACTION(refresher->transaction)); break; default: /*we preserve the transaction "as is"*/ break; } /*update reference transaction for next refresh*/ belle_sip_object_unref(refresher->transaction); refresher->transaction=client_transaction; belle_sip_object_ref(refresher->transaction); if (belle_sip_client_transaction_send_request_to(client_transaction,requri?requri:preset_route)) { /*send imediatly to requri in case of redirect*/ belle_sip_error("Cannot send refresh method [%s] for refresher [%p]" ,belle_sip_request_get_method(request) ,refresher); return -1; } if (expires==0) belle_sip_refresher_stop_internal(refresher,0); /*unregister transaction must be preserved*/ return 0; } static int timer_cb(void *user_data, unsigned int events) { belle_sip_refresher_t* refresher = (belle_sip_refresher_t*)user_data; if (refresher->timer_purpose==NORMAL_REFRESH && refresher->manual) { belle_sip_message("Refresher [%p] is in manual mode, skipping refresh.",refresher); /*call listener with special code 0 to indicate request is about to expire*/ if (refresher->listener) refresher->listener(refresher,refresher->user_data,0, "about to expire", FALSE); return BELLE_SIP_STOP; } if (belle_sip_refresher_refresh(refresher,refresher->target_expires)==-1){ retry_later(refresher); } return BELLE_SIP_STOP; } belle_sip_header_contact_t* belle_sip_refresher_get_contact(const belle_sip_refresher_t* refresher) { belle_sip_transaction_t* transaction = BELLE_SIP_TRANSACTION(refresher->transaction); belle_sip_request_t*request=belle_sip_transaction_get_request(transaction); belle_sip_response_t*response=transaction->last_response; const belle_sip_list_t* contact_header_list; belle_sip_header_contact_t* unfixed_local_contact; belle_sip_header_contact_t* fixed_local_contact; char* tmp_string; char* tmp_string2; if (!response) return NULL; /*we assume, there is only one contact in request*/ unfixed_local_contact= belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(request),belle_sip_header_contact_t); fixed_local_contact= BELLE_SIP_HEADER_CONTACT(belle_sip_object_clone(BELLE_SIP_OBJECT(unfixed_local_contact))); /*first fix contact using received/rport*/ belle_sip_response_fix_contact(response,fixed_local_contact); contact_header_list = belle_sip_message_get_headers(BELLE_SIP_MESSAGE(response),BELLE_SIP_CONTACT); if (contact_header_list) { contact_header_list = belle_sip_list_find_custom((belle_sip_list_t*)contact_header_list ,(belle_sip_compare_func)belle_sip_header_contact_not_equals , (const void*)fixed_local_contact); if (!contact_header_list) { /*reset header list*/ contact_header_list = belle_sip_message_get_headers(BELLE_SIP_MESSAGE(response),BELLE_SIP_CONTACT); contact_header_list = belle_sip_list_find_custom((belle_sip_list_t*)contact_header_list ,(belle_sip_compare_func)belle_sip_header_contact_not_equals ,unfixed_local_contact); } if (!contact_header_list) { tmp_string=belle_sip_object_to_string(BELLE_SIP_OBJECT(fixed_local_contact)); tmp_string2=belle_sip_object_to_string(BELLE_SIP_OBJECT(unfixed_local_contact)); belle_sip_message("No matching contact neither for [%s] nor [%s]", tmp_string, tmp_string2); belle_sip_object_unref(fixed_local_contact); belle_sip_free(tmp_string); belle_sip_free(tmp_string2); return NULL; } else { belle_sip_object_unref(fixed_local_contact); return BELLE_SIP_HEADER_CONTACT(contact_header_list->data); } } else { return NULL; } } static int set_expires_from_trans(belle_sip_refresher_t* refresher) { belle_sip_transaction_t* transaction = BELLE_SIP_TRANSACTION(refresher->transaction); belle_sip_response_t*response=transaction->last_response; belle_sip_request_t*request=belle_sip_transaction_get_request(transaction); belle_sip_header_expires_t* expires_header=belle_sip_message_get_header_by_type(request,belle_sip_header_expires_t); belle_sip_header_contact_t* contact_header; refresher->obtained_expires=-1; if (strcmp("REGISTER",belle_sip_request_get_method(request))==0 || expires_header /*if request has an expire header, refresher can always work*/) { if (expires_header) refresher->target_expires = belle_sip_header_expires_get_expires(expires_header); /*An "expires" parameter on the "Contact" header has no semantics for * SUBSCRIBE and is explicitly not equivalent to an "Expires" header in * a SUBSCRIBE request or response. */ if (strcmp("REGISTER",belle_sip_request_get_method(request))==0){ if (!expires_header && (contact_header=belle_sip_message_get_header_by_type((belle_sip_message_t*)request,belle_sip_header_contact_t))){ int ct_expires=belle_sip_header_contact_get_expires(BELLE_SIP_HEADER_CONTACT(contact_header)); if (ct_expires!=-1) refresher->target_expires=ct_expires; } /*check in response also to get the obtained expires*/ if ((contact_header=belle_sip_refresher_get_contact(refresher))!=NULL){ /*matching contact, check for its possible expires param*/ refresher->obtained_expires=belle_sip_header_contact_get_expires(BELLE_SIP_HEADER_CONTACT(contact_header)); } } if (refresher->obtained_expires==-1){ /*no contact with expire or not relevant, looking for Expires header*/ if (response && (expires_header=(belle_sip_header_expires_t*)belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_EXPIRES))) { refresher->obtained_expires = belle_sip_header_expires_get_expires(expires_header); } } if (refresher->obtained_expires==-1) { belle_sip_message("Neither Expires header nor corresponding Contact header found, checking from original request"); refresher->obtained_expires=refresher->target_expires; }else if (refresher->target_expires>0 && refresher->obtained_expires==0){ const char* reason = response ? belle_sip_response_get_reason_phrase(response) : NULL; /*check this case because otherwise we are going to loop fast in sending refresh requests.*/ /*"Test account created" is a special reason given by testers when we create temporary account. Since this is a bit of hack, we can ignore logging in that case*/ if (reason && strcmp(reason, "Test account created") != 0) { belle_sip_warning("Server replied with 0 expires, what does that mean?"); } /*suppose it's a server bug and assume our target_expires is understood.*/ refresher->obtained_expires=refresher->target_expires; } } else if (strcmp("INVITE",belle_sip_request_get_method(request))==0) { belle_sip_error("Refresher does not support INVITE yet"); return -1; } else { belle_sip_error("Refresher does not support [%s] yet",belle_sip_request_get_method(request)); return -1; } return 0; } int belle_sip_refresher_start(belle_sip_refresher_t* refresher) { if(refresher->state==started) { belle_sip_warning("Refresher [%p] already started",refresher); } else { if (refresher->target_expires>0) { belle_sip_request_t* request = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(refresher->transaction)); refresher->state=started; if (is_contact_address_acurate(refresher,request)) { schedule_timer(refresher); /*re-arm timer*/ } else { belle_sip_message("belle_sip_refresher_start(): refresher [%p] is resubmitting request because contact sent was not correct in original request.",refresher); belle_sip_refresher_refresh(refresher,refresher->target_expires); return 0; } belle_sip_message("Refresher [%p] started, next refresh in [%i] s",refresher,refresher->obtained_expires); }else{ belle_sip_message("Refresher [%p] stopped, expires=%i",refresher,refresher->target_expires); refresher->state=stopped; } } return 0; } void belle_sip_refresher_stop(belle_sip_refresher_t* refresher) { belle_sip_refresher_stop_internal(refresher, 1); } static void belle_sip_refresher_stop_internal(belle_sip_refresher_t* refresher,int cancel_pending_transaction) { belle_sip_message("Refresher [%p] stopped.",refresher); if (refresher->timer){ belle_sip_main_loop_remove_source(belle_sip_stack_get_main_loop(refresher->transaction->base.provider->stack), refresher->timer); belle_sip_object_unref(refresher->timer); refresher->timer=NULL; } if (cancel_pending_transaction && refresher->transaction && belle_sip_transaction_state_is_transient(belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)))) { belle_sip_transaction_terminate(BELLE_SIP_TRANSACTION(refresher->transaction)); /*refresher cancelled, no need to continue to retransmit*/ } refresher->state=stopped; } belle_sip_refresher_t* belle_sip_refresher_new(belle_sip_client_transaction_t* transaction) { belle_sip_refresher_t* refresher; belle_sip_transaction_state_t state=belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(transaction)); belle_sip_request_t* request = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(transaction)); int is_register=strcmp("REGISTER",belle_sip_request_get_method(request))==0; refresher = (belle_sip_refresher_t*)belle_sip_object_new(belle_sip_refresher_t); refresher->transaction=transaction; refresher->state=stopped; refresher->number_of_retry=0; belle_sip_object_ref(transaction); refresher->retry_after=DEFAULT_RETRY_AFTER; if (belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(transaction))) { set_or_update_dialog(refresher, belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(transaction))); } belle_sip_provider_add_internal_sip_listener(transaction->base.provider,BELLE_SIP_LISTENER(refresher), is_register); if (set_expires_from_trans(refresher)==-1){ belle_sip_error("Unable to extract refresh value from transaction [%p]",transaction); } if (belle_sip_transaction_state_is_transient(state)) { belle_sip_message("Refresher [%p] takes ownership of transaction [%p]",refresher,transaction); transaction->base.is_internal=1; refresher->state=started; }else{ belle_sip_refresher_start(refresher); } return refresher; } int belle_sip_refresher_get_expires(const belle_sip_refresher_t* refresher) { return refresher->target_expires; } int belle_sip_refresher_get_retry_after(const belle_sip_refresher_t* refresher){ return refresher->retry_after; } void belle_sip_refresher_set_retry_after(belle_sip_refresher_t* refresher, int delay_ms) { refresher->retry_after=delay_ms; } const char* belle_sip_refresher_get_realm(const belle_sip_refresher_t* refresher){ return refresher->realm; } void belle_sip_refresher_set_realm(belle_sip_refresher_t* refresher, const char* realm) { if (refresher->realm){ belle_sip_free(refresher->realm); refresher->realm = NULL; } if (realm!=NULL){ refresher->realm=belle_sip_strdup(realm); } } const belle_sip_client_transaction_t* belle_sip_refresher_get_transaction(const belle_sip_refresher_t* refresher) { return refresher->transaction; } const belle_sip_list_t* belle_sip_refresher_get_auth_events(const belle_sip_refresher_t* refresher) { return refresher->auth_events; } void belle_sip_refresher_enable_manual_mode(belle_sip_refresher_t *refresher, int enabled){ refresher->manual=enabled; } const char * belle_sip_refresher_get_local_address(belle_sip_refresher_t* refresher, int *port){ belle_sip_channel_t *chan = refresher->transaction->base.channel; if (chan) return belle_sip_channel_get_local_address(chan, port); return NULL; } const char * belle_sip_refresher_get_public_address(belle_sip_refresher_t* refresher, int *port){ belle_sip_channel_t *chan = refresher->transaction->base.channel; if (chan) return belle_sip_channel_get_public_address(chan, port); return NULL; } static void set_or_update_dialog(belle_sip_refresher_t* refresher, belle_sip_dialog_t* dialog) { if (refresher->dialog!=dialog){ belle_sip_message("refresher [%p] : set_or_update_dialog() current=[%p] new=[%p]",refresher,refresher->dialog,dialog); if (refresher->dialog){ belle_sip_object_unref(refresher->dialog); } if (dialog) { belle_sip_object_ref(dialog); /*make sure dialog is internal now*/ dialog->is_internal = TRUE; } refresher->dialog=dialog; } } belle-sip-1.6.3/src/siplistener.c000066400000000000000000000141551313437522400167030ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" belle_sip_response_t* belle_sip_response_event_get_response(const belle_sip_response_event_t* event) { return event->response; } belle_sip_client_transaction_t *belle_sip_response_event_get_client_transaction(const belle_sip_response_event_t* event){ return event->client_transaction; } belle_sip_dialog_t *belle_sip_response_event_get_dialog(const belle_sip_response_event_t* event){ return event->dialog; } belle_sip_request_t* belle_sip_request_event_get_request(const belle_sip_request_event_t* event){ return event->request; } belle_sip_server_transaction_t *belle_sip_request_event_get_server_transaction(const belle_sip_request_event_t* event){ return event->server_transaction; } belle_sip_dialog_t *belle_sip_request_event_get_dialog(const belle_sip_request_event_t* event){ return event->dialog; } belle_sip_dialog_t* belle_sip_dialog_terminated_event_get_dialog(const belle_sip_dialog_terminated_event_t *event) { return event->dialog; } int belle_sip_dialog_terminated_event_is_expired(const belle_sip_dialog_terminated_event_t *event){ return event->is_expired; } const char* belle_sip_io_error_event_get_host(const belle_sip_io_error_event_t* event) { return event->host; } const char* belle_sip_io_error_event_get_transport(const belle_sip_io_error_event_t* event) { return event->transport; } unsigned int belle_sip_io_error_event_port(const belle_sip_io_error_event_t* event) { return event->port; } belle_sip_object_t* belle_sip_io_error_event_get_source(const belle_sip_io_error_event_t* event) { return event->source; } typedef struct belle_sip_callbacks belle_sip_callbacks_t; struct belle_sip_callbacks{ belle_sip_object_t base; belle_sip_listener_callbacks_t cbs; void *user_ctx; }; static void process_dialog_terminated(belle_sip_listener_t *l, const belle_sip_dialog_terminated_event_t *event){ belle_sip_callbacks_t *obj=(belle_sip_callbacks_t*)l; if (obj->cbs.process_dialog_terminated) obj->cbs.process_dialog_terminated(obj->user_ctx,event); } static void process_io_error(belle_sip_listener_t *l, const belle_sip_io_error_event_t *event){ belle_sip_callbacks_t *obj=(belle_sip_callbacks_t*)l; if (obj->cbs.process_io_error) obj->cbs.process_io_error(obj->user_ctx,event); } static void process_request_event(belle_sip_listener_t *l, const belle_sip_request_event_t *event){ belle_sip_callbacks_t *obj=(belle_sip_callbacks_t*)l; if (obj->cbs.process_request_event) obj->cbs.process_request_event(obj->user_ctx,event); } static void process_response_event(belle_sip_listener_t *l, const belle_sip_response_event_t *event){ belle_sip_callbacks_t *obj=(belle_sip_callbacks_t*)l; if (obj->cbs.process_response_event) obj->cbs.process_response_event(obj->user_ctx,event); } static void process_timeout(belle_sip_listener_t *l, const belle_sip_timeout_event_t *event){ belle_sip_callbacks_t *obj=(belle_sip_callbacks_t*)l; if (obj->cbs.process_timeout) obj->cbs.process_timeout(obj->user_ctx,event); } static void process_transaction_terminated(belle_sip_listener_t *l, const belle_sip_transaction_terminated_event_t *event){ belle_sip_callbacks_t *obj=(belle_sip_callbacks_t*)l; if (obj->cbs.process_transaction_terminated) obj->cbs.process_transaction_terminated(obj->user_ctx,event); } static void process_auth_requested(belle_sip_listener_t *l, belle_sip_auth_event_t *event){ belle_sip_callbacks_t *obj=(belle_sip_callbacks_t*)l; if (obj->cbs.process_auth_requested) obj->cbs.process_auth_requested(obj->user_ctx,event); } BELLE_SIP_DECLARE_VPTR(belle_sip_callbacks_t); BELLE_SIP_IMPLEMENT_INTERFACE_BEGIN(belle_sip_callbacks_t,belle_sip_listener_t) process_dialog_terminated, process_io_error, process_request_event, process_response_event, process_timeout, process_transaction_terminated, process_auth_requested BELLE_SIP_IMPLEMENT_INTERFACE_END BELLE_SIP_DECLARE_IMPLEMENTED_INTERFACES_1(belle_sip_callbacks_t,belle_sip_listener_t); static void belle_sip_callbacks_destroy(belle_sip_callbacks_t *obj){ if (obj->cbs.listener_destroyed) obj->cbs.listener_destroyed(obj->user_ctx); } BELLE_SIP_INSTANCIATE_VPTR(belle_sip_callbacks_t,belle_sip_object_t,belle_sip_callbacks_destroy,NULL,NULL,FALSE); belle_sip_listener_t *belle_sip_listener_create_from_callbacks(const belle_sip_listener_callbacks_t *callbacks, void *user_ctx){ belle_sip_callbacks_t *obj=belle_sip_object_new(belle_sip_callbacks_t); memcpy(&obj->cbs,callbacks,sizeof(belle_sip_listener_callbacks_t)); obj->user_ctx=user_ctx; return BELLE_SIP_LISTENER(obj); } belle_sip_client_transaction_t *belle_sip_transaction_terminated_event_get_client_transaction(const belle_sip_transaction_terminated_event_t* event) { return event->is_server_transaction ? NULL:BELLE_SIP_CLIENT_TRANSACTION(event->transaction); } belle_sip_server_transaction_t *belle_sip_transaction_terminated_event_get_server_transaction(const belle_sip_transaction_terminated_event_t* event) { return event->is_server_transaction ? BELLE_SIP_SERVER_TRANSACTION(event->transaction):NULL; } belle_sip_client_transaction_t *belle_sip_timeout_event_get_client_transaction(const belle_sip_timeout_event_t* event) { return event->is_server_transaction ? NULL:BELLE_SIP_CLIENT_TRANSACTION(event->transaction); } belle_sip_server_transaction_t *belle_sip_timeout_event_get_server_transaction(const belle_sip_timeout_event_t* event) { return event->is_server_transaction ? BELLE_SIP_SERVER_TRANSACTION(event->transaction):NULL; } belle-sip-1.6.3/src/sipstack.c000066400000000000000000000243021313437522400161560ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #include "listeningpoint_internal.h" belle_sip_hop_t* belle_sip_hop_new(const char* transport, const char *cname, const char* host,int port) { belle_sip_hop_t* hop = belle_sip_object_new(belle_sip_hop_t); if (transport) hop->transport=belle_sip_strdup(transport); if (host) { if (host[0] == '[' && host[1] != '\0'){ /*IPv6 case */ hop->host = belle_sip_strdup(host+1); hop->host[strlen(hop->host)-1] = '\0'; }else{ hop->host=belle_sip_strdup(host); } } if (cname) hop->cname=belle_sip_strdup(cname); hop->port=port; return hop; } belle_sip_hop_t* belle_sip_hop_new_from_uri(const belle_sip_uri_t *uri){ const char *host; const char *cname=NULL; const char * transport=belle_sip_uri_get_transport_param(uri); if (!transport) { transport=belle_sip_uri_is_secure(uri)?"tls":"udp"; } host=belle_sip_uri_get_maddr_param(uri); if (!host) host=belle_sip_uri_get_host(uri); else cname=belle_sip_uri_get_host(uri); return belle_sip_hop_new( transport, cname, host, belle_sip_uri_get_listening_port(uri)); } belle_sip_hop_t* belle_sip_hop_new_from_generic_uri(const belle_generic_uri_t *uri){ const char *host; const char * transport="TCP"; const char *scheme=belle_generic_uri_get_scheme(uri); int port=belle_generic_uri_get_port(uri); int well_known_port=0; host=belle_generic_uri_get_host(uri); if (strcasecmp(scheme,"http")==0) { transport="TCP"; well_known_port=80; }else if (strcasecmp(scheme,"https")==0) { transport="TLS"; well_known_port=443; } return belle_sip_hop_new(transport, host, host, port > 0 ? port : well_known_port); } static void belle_sip_hop_destroy(belle_sip_hop_t *hop){ if (hop->host) { belle_sip_free(hop->host); hop->host=NULL; } if (hop->cname){ belle_sip_free(hop->cname); hop->cname=NULL; } if (hop->transport){ belle_sip_free(hop->transport); hop->transport=NULL; } } static void belle_sip_hop_clone(belle_sip_hop_t *hop, const belle_sip_hop_t *orig){ if (orig->host) hop->host=belle_sip_strdup(orig->host); if (orig->cname) hop->cname=belle_sip_strdup(orig->cname); if (orig->transport) hop->transport=belle_sip_strdup(orig->transport); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_hop_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_hop_t,belle_sip_object_t,belle_sip_hop_destroy,belle_sip_hop_clone,NULL,TRUE); static void belle_sip_stack_destroy(belle_sip_stack_t *stack){ belle_sip_message("stack [%p] destroyed.",stack); if (stack->dns_user_hosts_file) belle_sip_free(stack->dns_user_hosts_file); if (stack->dns_resolv_conf) belle_sip_free(stack->dns_resolv_conf); belle_sip_object_unref(stack->ml); if (stack->http_proxy_host) belle_sip_free(stack->http_proxy_host); if (stack->http_proxy_passwd) belle_sip_free(stack->http_proxy_passwd); if (stack->http_proxy_username) belle_sip_free(stack->http_proxy_username); belle_sip_list_free_with_data(stack->dns_servers, belle_sip_free); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_stack_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_stack_t,belle_sip_object_t,belle_sip_stack_destroy,NULL,NULL,FALSE); belle_sip_stack_t * belle_sip_stack_new(const char *properties){ belle_sip_stack_t *stack=belle_sip_object_new(belle_sip_stack_t); stack->ml=belle_sip_main_loop_new (); stack->timer_config.T1=500; stack->timer_config.T2=4000; stack->timer_config.T4=5000; stack->transport_timeout=63000; stack->dns_timeout=15000; stack->dns_srv_enabled=TRUE; stack->dns_search_enabled=TRUE; stack->inactive_transport_timeout=3600; /*one hour*/ return stack; } const belle_sip_timer_config_t *belle_sip_stack_get_timer_config(const belle_sip_stack_t *stack){ return &stack->timer_config; } void belle_sip_stack_set_timer_config(belle_sip_stack_t *stack,const belle_sip_timer_config_t *timer_config){ belle_sip_message("Setting timer config to T1 [%i], T2 [%i], T3 [%i], T4 [%i] on stack [%p]", timer_config->T1 , timer_config->T2 , timer_config->T3 , timer_config->T4 , stack); stack->timer_config=*timer_config; } void belle_sip_stack_set_transport_timeout(belle_sip_stack_t *stack, int timeout_ms){ stack->transport_timeout=timeout_ms; } int belle_sip_stack_get_transport_timeout(const belle_sip_stack_t *stack){ return stack->transport_timeout; } int belle_sip_stack_get_dns_timeout(const belle_sip_stack_t *stack) { return stack->dns_timeout; } void belle_sip_stack_set_dns_timeout(belle_sip_stack_t *stack, int timeout) { stack->dns_timeout = timeout; } unsigned char belle_sip_stack_dns_srv_enabled(const belle_sip_stack_t *stack) { return stack->dns_srv_enabled; } void belle_sip_stack_enable_dns_srv(belle_sip_stack_t *stack, unsigned char enable) { stack->dns_srv_enabled = enable; } unsigned char belle_sip_stack_dns_search_enabled(const belle_sip_stack_t *stack) { return stack->dns_search_enabled; } void belle_sip_stack_enable_dns_search(belle_sip_stack_t *stack, unsigned char enable) { stack->dns_search_enabled = enable; } belle_sip_listening_point_t *belle_sip_stack_create_listening_point(belle_sip_stack_t *s, const char *ipaddress, int port, const char *transport){ belle_sip_listening_point_t *lp=NULL; if (strcasecmp(transport,"UDP")==0) { lp=belle_sip_udp_listening_point_new(s,ipaddress,port); } else if (strcasecmp(transport,"TCP") == 0) { lp=belle_sip_stream_listening_point_new(s,ipaddress,port); }else if (strcasecmp(transport,"TLS") == 0) { lp=belle_sip_tls_listening_point_new(s,ipaddress,port); } else { belle_sip_fatal("Unsupported transport %s",transport); } return lp; } void belle_sip_stack_delete_listening_point(belle_sip_stack_t *s, belle_sip_listening_point_t *lp){ belle_sip_object_unref(lp); } belle_sip_provider_t *belle_sip_stack_create_provider(belle_sip_stack_t *s, belle_sip_listening_point_t *lp){ belle_sip_provider_t *p=belle_sip_provider_new(s,lp); return p; } belle_http_provider_t *belle_sip_stack_create_http_provider(belle_sip_stack_t *s, const char *bind_ip){ belle_http_provider_t *p=belle_http_provider_new(s, bind_ip); return p; } void belle_sip_stack_delete_provider(belle_sip_stack_t *s, belle_sip_provider_t *p){ belle_sip_object_unref(p); } belle_sip_main_loop_t * belle_sip_stack_get_main_loop(belle_sip_stack_t *stack){ return stack->ml; } void belle_sip_stack_main(belle_sip_stack_t *stack){ belle_sip_main_loop_run(stack->ml); } void belle_sip_stack_sleep(belle_sip_stack_t *stack, unsigned int milliseconds){ belle_sip_main_loop_sleep (stack->ml,milliseconds); } belle_sip_hop_t * belle_sip_stack_get_next_hop(belle_sip_stack_t *stack, belle_sip_request_t *req) { belle_sip_header_route_t *route=BELLE_SIP_HEADER_ROUTE(belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"route")); belle_sip_uri_t *uri; if (route!=NULL){ uri=belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(route)); }else{ uri=belle_sip_request_get_uri(req); } return belle_sip_hop_new_from_uri(uri); } void belle_sip_stack_set_tx_delay(belle_sip_stack_t *stack, int delay_ms){ stack->tx_delay=delay_ms; } void belle_sip_stack_set_send_error(belle_sip_stack_t *stack, int send_error){ stack->send_error=send_error; } void belle_sip_stack_set_resolver_tx_delay(belle_sip_stack_t *stack, int delay_ms) { stack->resolver_tx_delay = delay_ms; } void belle_sip_stack_set_resolver_send_error(belle_sip_stack_t *stack, int send_error) { stack->resolver_send_error = send_error; } const char * belle_sip_stack_get_dns_user_hosts_file(const belle_sip_stack_t *stack) { return stack->dns_user_hosts_file; } void belle_sip_stack_set_dns_user_hosts_file(belle_sip_stack_t *stack, const char *hosts_file) { if (stack->dns_user_hosts_file) belle_sip_free(stack->dns_user_hosts_file); stack->dns_user_hosts_file = hosts_file?belle_sip_strdup(hosts_file):NULL; } const char * belle_sip_stack_get_dns_resolv_conf_file(const belle_sip_stack_t *stack){ return stack->dns_resolv_conf; } void belle_sip_stack_set_dns_resolv_conf_file(belle_sip_stack_t *stack, const char *resolv_conf_file){ if (stack->dns_resolv_conf) belle_sip_free(stack->dns_resolv_conf); stack->dns_resolv_conf = resolv_conf_file?belle_sip_strdup(resolv_conf_file):NULL; } void belle_sip_stack_set_dns_servers(belle_sip_stack_t *stack, const belle_sip_list_t *servers){ belle_sip_list_t *newservers = NULL; if (servers) newservers = belle_sip_list_copy_with_data(servers, (void *(*)(void*))belle_sip_strdup); if (stack->dns_servers){ belle_sip_list_free_with_data(stack->dns_servers, belle_sip_free); } stack->dns_servers = newservers; } const char* belle_sip_version_to_string() { #ifdef BELLESIP_VERSION return BELLESIP_VERSION; #else return PACKAGE_VERSION; #endif } int belle_sip_stack_get_inactive_transport_timeout(const belle_sip_stack_t *stack){ return stack->inactive_transport_timeout; } void belle_sip_stack_set_inactive_transport_timeout(belle_sip_stack_t *stack, int seconds){ stack->inactive_transport_timeout=seconds; } void belle_sip_stack_set_default_dscp(belle_sip_stack_t *stack, int dscp){ stack->dscp=dscp; } int belle_sip_stack_get_default_dscp(belle_sip_stack_t *stack){ return stack->dscp; } int belle_sip_stack_tls_available(belle_sip_stack_t *stack){ return belle_sip_tls_listening_point_available(); } int belle_sip_stack_content_encoding_available(belle_sip_stack_t *stack, const char *content_encoding) { #ifdef HAVE_ZLIB if (strcmp(content_encoding, "deflate") == 0) return TRUE; #endif return FALSE; } GET_SET_STRING(belle_sip_stack,http_proxy_host) GET_SET_INT(belle_sip_stack,http_proxy_port, int) belle-sip-1.6.3/src/transaction.c000066400000000000000000000745511313437522400166750ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" const char *belle_sip_transaction_state_to_string(belle_sip_transaction_state_t state){ switch(state){ case BELLE_SIP_TRANSACTION_INIT: return "INIT"; case BELLE_SIP_TRANSACTION_TRYING: return "TRYING"; case BELLE_SIP_TRANSACTION_CALLING: return "CALLING"; case BELLE_SIP_TRANSACTION_COMPLETED: return "COMPLETED"; case BELLE_SIP_TRANSACTION_CONFIRMED: return "CONFIRMED"; case BELLE_SIP_TRANSACTION_ACCEPTED: return "ACCEPTED"; case BELLE_SIP_TRANSACTION_PROCEEDING: return "PROCEEDING"; case BELLE_SIP_TRANSACTION_TERMINATED: return "TERMINATED"; } belle_sip_fatal("Invalid transaction state."); return "INVALID"; } void belle_sip_transaction_set_state(belle_sip_transaction_t *t, belle_sip_transaction_state_t state) { belle_sip_message("Changing [%s] [%s] transaction [%p], from state [%s] to [%s]", BELLE_SIP_OBJECT_IS_INSTANCE_OF(t,belle_sip_client_transaction_t) ? "client" : "server", belle_sip_request_get_method(t->request), t, belle_sip_transaction_state_to_string(t->state), belle_sip_transaction_state_to_string(state)); t->state=state; } BELLESIP_EXPORT const char *belle_sip_transaction_get_method(const belle_sip_transaction_t *t){ return belle_sip_request_get_method(t->request); } static void transaction_end_background_task(belle_sip_transaction_t *obj){ if (obj->bg_task_id){ belle_sip_message("transaction [%p]: ending transaction background task with id=[%lx].",obj,obj->bg_task_id); belle_sip_end_background_task(obj->bg_task_id); obj->bg_task_id=0; } } static void transaction_background_task_ended(belle_sip_transaction_t *obj){ belle_sip_warning("transaction [%p]: transaction background task has to be ended now, but work isn't finished.",obj); transaction_end_background_task(obj); } static void transaction_begin_background_task(belle_sip_transaction_t *obj){ if (obj->bg_task_id==0){ char *transaction = bctbx_strdup_printf("belle-sip transaction(%p)", obj); obj->bg_task_id=belle_sip_begin_background_task(transaction,(void (*)(void*))transaction_background_task_ended, obj); if (obj->bg_task_id) belle_sip_message("transaction [%p]: starting transaction background task with id=[%lx].",obj,obj->bg_task_id); bctbx_free(transaction); } } static void belle_sip_transaction_init(belle_sip_transaction_t *t, belle_sip_provider_t *prov, belle_sip_request_t *req){ transaction_begin_background_task(t); t->request=(belle_sip_request_t*)belle_sip_object_ref(req); t->provider=prov; } static void transaction_destroy(belle_sip_transaction_t *t){ if (t->call_repair_timer) { belle_sip_transaction_stop_timer(t, t->call_repair_timer); belle_sip_object_unref(t->call_repair_timer); t->call_repair_timer = NULL; } if (t->request) belle_sip_object_unref(t->request); if (t->last_response) belle_sip_object_unref(t->last_response); if (t->channel) belle_sip_object_unref(t->channel); if (t->branch_id) belle_sip_free(t->branch_id); belle_sip_transaction_set_dialog(t,NULL); belle_sip_message("Transaction [%p] deleted",t); } static void notify_timeout(belle_sip_transaction_t *t){ belle_sip_timeout_event_t ev; ev.source=(belle_sip_object_t*)t->provider; ev.transaction=t; ev.is_server_transaction=BELLE_SIP_OBJECT_IS_INSTANCE_OF(t,belle_sip_server_transaction_t); BELLE_SIP_PROVIDER_INVOKE_LISTENERS_FOR_TRANSACTION(t,process_timeout,&ev); } static int server_transaction_on_call_repair_timer(belle_sip_transaction_t *t) { belle_sip_server_transaction_send_response(BELLE_SIP_SERVER_TRANSACTION(t), belle_sip_response_create_from_request(t->request, 503)); return BELLE_SIP_STOP; } static int client_transaction_on_call_repair_timer(belle_sip_transaction_t *t) { belle_sip_transaction_terminate(t); return BELLE_SIP_STOP; } static void on_channel_state_changed(belle_sip_channel_listener_t *l, belle_sip_channel_t *chan, belle_sip_channel_state_t state){ belle_sip_transaction_t *t=(belle_sip_transaction_t*)l; belle_sip_io_error_event_t ev; const belle_sip_timer_config_t *timercfg = NULL; belle_sip_transaction_state_t tr_state=belle_sip_transaction_get_state((belle_sip_transaction_t*)t); belle_sip_message("transaction [%p] channel state changed to [%s]" ,t ,belle_sip_channel_state_to_string(state)); switch(state){ case BELLE_SIP_CHANNEL_READY: if (tr_state==BELLE_SIP_TRANSACTION_INIT && BELLE_SIP_OBJECT_IS_INSTANCE_OF(t,belle_sip_client_transaction_t) ){ belle_sip_client_transaction_t *ct = (belle_sip_client_transaction_t*) t; BELLE_SIP_OBJECT_VPTR(ct,belle_sip_client_transaction_t)->send_request(ct); } break; case BELLE_SIP_CHANNEL_DISCONNECTED: case BELLE_SIP_CHANNEL_ERROR: belle_sip_object_ref(t); /*take a ref in the case where the app calls belle_sip_transaction_terminate() within the listener*/ ev.transport=belle_sip_channel_get_transport_name(chan); ev.source=BELLE_SIP_OBJECT(t); ev.port=chan->peer_port; ev.host=chan->peer_name; if ( tr_state!=BELLE_SIP_TRANSACTION_COMPLETED && tr_state!=BELLE_SIP_TRANSACTION_CONFIRMED && tr_state!=BELLE_SIP_TRANSACTION_ACCEPTED && tr_state!=BELLE_SIP_TRANSACTION_TERMINATED) { BELLE_SIP_PROVIDER_INVOKE_LISTENERS_FOR_TRANSACTION(((belle_sip_transaction_t*)t),process_io_error,&ev); } if (t->timed_out) { notify_timeout((belle_sip_transaction_t*)t); } else { if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(t, belle_sip_ist_t) || BELLE_SIP_OBJECT_IS_INSTANCE_OF(t, belle_sip_ict_t)) { timercfg = belle_sip_transaction_get_timer_config(t); if (t->call_repair_timer) { belle_sip_transaction_stop_timer(t, t->call_repair_timer); belle_sip_object_unref(t->call_repair_timer); t->call_repair_timer = NULL; } } } if (!t->timed_out && belle_sip_transaction_state_is_transient(t->state) && BELLE_SIP_OBJECT_IS_INSTANCE_OF(t, belle_sip_ist_t)) { t->call_repair_timer = belle_sip_timeout_source_new((belle_sip_source_func_t)server_transaction_on_call_repair_timer, t, 32 * timercfg->T1); belle_sip_transaction_start_timer(t, t->call_repair_timer); } else if (!t->timed_out && belle_sip_transaction_state_is_transient(t->state) && BELLE_SIP_OBJECT_IS_INSTANCE_OF(t, belle_sip_ict_t)) { t->call_repair_timer = belle_sip_timeout_source_new((belle_sip_source_func_t)client_transaction_on_call_repair_timer, t, 32 * timercfg->T1); belle_sip_transaction_start_timer(t, t->call_repair_timer); } else { belle_sip_transaction_terminate(t); } if (t->channel){ belle_sip_channel_remove_listener(t->channel, l); belle_sip_object_unref(t->channel); t->channel = NULL; } belle_sip_object_unref(t); break; default: /*ignored*/ break; } } BELLE_SIP_IMPLEMENT_INTERFACE_BEGIN(belle_sip_transaction_t,belle_sip_channel_listener_t) on_channel_state_changed, NULL, /* on_message_headers */ NULL, /* on_message */ NULL, /* on_sending */ NULL /* on_auth_requested */ BELLE_SIP_IMPLEMENT_INTERFACE_END BELLE_SIP_DECLARE_IMPLEMENTED_INTERFACES_1(belle_sip_transaction_t, belle_sip_channel_listener_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_transaction_t) { BELLE_SIP_VPTR_INIT(belle_sip_transaction_t,belle_sip_object_t,FALSE), (belle_sip_object_destroy_t) transaction_destroy, NULL,/*no clone*/ NULL,/*no marshal*/ BELLE_SIP_DEFAULT_BUFSIZE_HINT }, NULL /*on_terminate*/ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END void *belle_sip_transaction_get_application_data_internal(const belle_sip_transaction_t *t){ return t->appdata; } void *belle_sip_transaction_get_application_data(const belle_sip_transaction_t *t){ if (t->is_internal) { belle_sip_error("belle_sip_transaction_get_application_data should not be used on internal transaction [%p]",t); return NULL; } else { return belle_sip_transaction_get_application_data_internal(t); }; } void belle_sip_transaction_set_application_data(belle_sip_transaction_t *t, void *data){ t->appdata=data; } const char *belle_sip_transaction_get_branch_id(const belle_sip_transaction_t *t){ return t->branch_id; } belle_sip_transaction_state_t belle_sip_transaction_get_state(const belle_sip_transaction_t *t){ return t->state; } int belle_sip_transaction_state_is_transient(const belle_sip_transaction_state_t state) { switch(state){ case BELLE_SIP_TRANSACTION_INIT: case BELLE_SIP_TRANSACTION_TRYING: case BELLE_SIP_TRANSACTION_CALLING: case BELLE_SIP_TRANSACTION_PROCEEDING: return 1; default: return 0; } } void belle_sip_transaction_terminate(belle_sip_transaction_t *t){ belle_sip_object_ref(t); if (t->call_repair_timer) { belle_sip_transaction_stop_timer(t, t->call_repair_timer); belle_sip_object_unref(t->call_repair_timer); t->call_repair_timer = NULL; } if (belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(t))!=BELLE_SIP_TRANSACTION_TERMINATED) { int is_client=BELLE_SIP_OBJECT_IS_INSTANCE_OF(t,belle_sip_client_transaction_t); belle_sip_transaction_set_state(t,BELLE_SIP_TRANSACTION_TERMINATED); if (t->dialog && (!t->last_response || belle_sip_response_get_status_code(t->last_response)<200)){ /*inform the dialog if a transaction terminates without final response.*/ belle_sip_dialog_update(t->dialog,t,!is_client); } belle_sip_message("%s%s %s transaction [%p] terminated" ,is_client ? "Client":"Server" ,t->is_internal ? " internal":"" ,belle_sip_request_get_method(belle_sip_transaction_get_request(t)) ,t); BELLE_SIP_OBJECT_VPTR(t,belle_sip_transaction_t)->on_terminate(t); belle_sip_provider_set_transaction_terminated(t->provider,t); } transaction_end_background_task(t); belle_sip_object_unref(t); } belle_sip_request_t *belle_sip_transaction_get_request(const belle_sip_transaction_t *t){ return t->request; } belle_sip_response_t *belle_sip_transaction_get_response(const belle_sip_transaction_t *t) { return t->last_response; } void belle_sip_transaction_notify_timeout(belle_sip_transaction_t *t){ /*report the channel as possibly dead. If an alternate IP can be tryied, the channel will notify us with the RETRY state. * Otherwise it will report the error. * We limit this dead channel reporting to REGISTER transactions, who are unlikely to be unresponded. **/ belle_sip_object_ref(t); /*take a ref in the case where the app calls belle_sip_transaction_terminate() within the timeout listener*/ if (strcmp(belle_sip_request_get_method(t->request),"REGISTER")==0){ if ( belle_sip_channel_notify_timeout(t->channel)==TRUE){ belle_sip_warning("Transaction [%p] has timeout, reported to channel.",t); t->timed_out=TRUE; } }else { notify_timeout(t); belle_sip_transaction_terminate(t); } belle_sip_object_unref(t); } belle_sip_dialog_t* belle_sip_transaction_get_dialog(const belle_sip_transaction_t *t) { return t->dialog; } static void belle_sip_transaction_reset_dialog(belle_sip_transaction_t *tr, belle_sip_dialog_t *dialog_disapeearing){ if (tr->dialog!=dialog_disapeearing) belle_sip_error("belle_sip_transaction_reset_dialog(): inconsistency."); tr->dialog=NULL; } void belle_sip_transaction_set_dialog(belle_sip_transaction_t *t, belle_sip_dialog_t *dialog){ if (t->dialog==dialog) return; if (dialog) belle_sip_object_weak_ref(dialog,(belle_sip_object_destroy_notify_t)belle_sip_transaction_reset_dialog,t); if (t->dialog) belle_sip_object_weak_unref(t->dialog,(belle_sip_object_destroy_notify_t)belle_sip_transaction_reset_dialog,t); t->dialog=dialog; } /* * Server transaction */ static void server_transaction_destroy(belle_sip_server_transaction_t *t){ } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_server_transaction_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_server_transaction_t) { { BELLE_SIP_VPTR_INIT(belle_sip_server_transaction_t,belle_sip_transaction_t,FALSE), (belle_sip_object_destroy_t) server_transaction_destroy, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, NULL }, NULL, /* send_new_response */ NULL /* on_request_retransmission */ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END void belle_sip_server_transaction_init(belle_sip_server_transaction_t *t, belle_sip_provider_t *prov,belle_sip_request_t *req){ const char *branch; belle_sip_header_via_t *via=BELLE_SIP_HEADER_VIA(belle_sip_message_get_header((belle_sip_message_t*)req,"via")); branch=belle_sip_header_via_get_branch(via); if (branch==NULL || strncmp(branch,BELLE_SIP_BRANCH_MAGIC_COOKIE,strlen(BELLE_SIP_BRANCH_MAGIC_COOKIE))!=0){ branch=req->rfc2543_branch; if (branch==NULL) belle_sip_fatal("No computed branch for RFC2543 style of message, this should never happen."); } t->base.branch_id=belle_sip_strdup(branch); belle_sip_transaction_init((belle_sip_transaction_t*)t,prov,req); belle_sip_random_token(t->to_tag,sizeof(t->to_tag)); } void belle_sip_server_transaction_send_response(belle_sip_server_transaction_t *t, belle_sip_response_t *resp){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)t; belle_sip_header_to_t *to=(belle_sip_header_to_t*)belle_sip_message_get_header((belle_sip_message_t*)resp,"to"); belle_sip_dialog_t *dialog=base->dialog; int status_code; belle_sip_object_ref(resp); if (!base->last_response || !base->channel){ belle_sip_hop_t* hop=belle_sip_response_get_return_hop(resp); base->channel=belle_sip_provider_get_channel(base->provider,hop); belle_sip_object_unref(hop); if (!base->channel){ belle_sip_error("Transaction [%p]: No channel available for sending response.",t); return; } belle_sip_object_ref(base->channel); belle_sip_channel_add_listener(base->channel, BELLE_SIP_CHANNEL_LISTENER(t)); } status_code=belle_sip_response_get_status_code(resp); if (status_code!=100){ if (belle_sip_header_to_get_tag(to)==NULL){ //add a random to tag belle_sip_header_to_set_tag(to,t->to_tag); } /*12.1 Creation of a Dialog Dialogs are created through the generation of non-failure responses to requests with specific methods. Within this specification, only 2xx and 101-199 responses with a To tag, where the request was INVITE, will establish a dialog.*/ if (dialog && status_code>100 && status_code<300){ belle_sip_response_fill_for_dialog(resp,base->request); } } if (BELLE_SIP_OBJECT_VPTR(t,belle_sip_server_transaction_t)->send_new_response(t,resp)==0){ if (base->last_response) belle_sip_object_unref(base->last_response); base->last_response=resp; } if (dialog) belle_sip_dialog_update(dialog,BELLE_SIP_TRANSACTION(t),TRUE); } static void server_transaction_notify(belle_sip_server_transaction_t *t, belle_sip_request_t *req, belle_sip_dialog_t *dialog){ belle_sip_request_event_t event; event.source=(belle_sip_object_t*)t->base.provider; event.server_transaction=t; event.dialog=dialog; event.request=req; BELLE_SIP_PROVIDER_INVOKE_LISTENERS_FOR_TRANSACTION(((belle_sip_transaction_t*) t),process_request_event,&event); } void belle_sip_server_transaction_on_request(belle_sip_server_transaction_t *t, belle_sip_request_t *req){ const char *method=belle_sip_request_get_method(req); if (strcmp(method,"ACK")==0){ /*this must be for an INVITE server transaction */ if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(t,belle_sip_ist_t)){ belle_sip_ist_t *ist=(belle_sip_ist_t*)t; if (belle_sip_ist_process_ack(ist,(belle_sip_message_t*)req)==0){ belle_sip_dialog_t *dialog=t->base.dialog; if (dialog && belle_sip_dialog_handle_ack(dialog,req)==0) server_transaction_notify(t,req,dialog); /*else nothing to do because retransmission of ACK*/ } }else{ belle_sip_warning("ACK received for non-invite server transaction ?"); } }else if (strcmp(method,"CANCEL")==0){ server_transaction_notify(t,req,t->base.dialog); }else BELLE_SIP_OBJECT_VPTR(t,belle_sip_server_transaction_t)->on_request_retransmission(t); } /* * client transaction */ belle_sip_request_t * belle_sip_client_transaction_create_cancel(belle_sip_client_transaction_t *t){ belle_sip_message_t *orig=(belle_sip_message_t*)t->base.request; belle_sip_request_t *req; const char *orig_method=belle_sip_request_get_method((belle_sip_request_t*)orig); if (strcmp(orig_method,"ACK")==0 || strcmp(orig_method,"INVITE")!=0){ belle_sip_error("belle_sip_client_transaction_create_cancel() cannot be used for ACK or non-INVITE transactions."); return NULL; } if ((t->base.state != BELLE_SIP_TRANSACTION_PROCEEDING) && (t->base.state != BELLE_SIP_TRANSACTION_CALLING)) { belle_sip_error("belle_sip_client_transaction_create_cancel() can only be used in state PROCEEDING or CALLING" " but current transaction state is %s",belle_sip_transaction_state_to_string(t->base.state)); return NULL; } req=belle_sip_request_new(); belle_sip_request_set_method(req,"CANCEL"); /* 9.1 Client Behavior Since requests other than INVITE are responded to immediately, sending a CANCEL for a non-INVITE request would always create a race condition. The following procedures are used to construct a CANCEL request. The Request-URI, Call-ID, To, the numeric part of CSeq, and From header fields in the CANCEL request MUST be identical to those in the request being cancelled, including tags. A CANCEL constructed by a client MUST have only a single Via header field value matching the top Via value in the request being cancelled.*/ belle_sip_request_set_uri(req,(belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)belle_sip_request_get_uri((belle_sip_request_t*)orig))); belle_sip_util_copy_headers(orig,(belle_sip_message_t*)req,"via",FALSE); belle_sip_util_copy_headers(orig,(belle_sip_message_t*)req,"call-id",FALSE); belle_sip_util_copy_headers(orig,(belle_sip_message_t*)req,"from",FALSE); belle_sip_util_copy_headers(orig,(belle_sip_message_t*)req,"to",FALSE); belle_sip_util_copy_headers(orig,(belle_sip_message_t*)req,"route",TRUE); belle_sip_util_copy_headers(orig,(belle_sip_message_t*)req,BELLE_SIP_MAX_FORWARDS,FALSE); belle_sip_message_add_header((belle_sip_message_t*)req, (belle_sip_header_t*)belle_sip_header_cseq_create( belle_sip_header_cseq_get_seq_number((belle_sip_header_cseq_t*)belle_sip_message_get_header(orig,"cseq")), "CANCEL")); return req; } int belle_sip_client_transaction_send_request(belle_sip_client_transaction_t *t){ return belle_sip_client_transaction_send_request_to(t,NULL); } int belle_sip_client_transaction_send_request_to(belle_sip_client_transaction_t *t,belle_sip_uri_t* outbound_proxy) { belle_sip_channel_t *chan; belle_sip_provider_t *prov=t->base.provider; belle_sip_dialog_t *dialog=t->base.dialog; belle_sip_request_t *req=t->base.request; int result=-1; if (t->base.state!=BELLE_SIP_TRANSACTION_INIT){ belle_sip_error("belle_sip_client_transaction_send_request: bad state."); return -1; } /*check uris components compliance*/ if (!belle_sip_request_check_uris_components(t->base.request)) { belle_sip_error("belle_sip_client_transaction_send_request: bad request for transaction [%p]",t); return -1; } /*store preset route for future use by refresher*/ if (outbound_proxy){ t->preset_route=outbound_proxy; belle_sip_object_ref(t->preset_route); } if (t->base.sent_by_dialog_queue){ /*it can be sent immediately, so update the request with latest cseq and route_set */ /*update route and contact just in case they changed*/ belle_sip_dialog_update_request(dialog,req); } else if (t->base.request->dialog_queued){ /*this request was created by belle_sip_dialog_create_queued_request().*/ if (dialog == NULL){ belle_sip_error("belle_sip_client_transaction_send_request(): transaction [%p], cannot send" " request because it was created in the context of a dialog that appears to be " " no longer existing.", t); belle_sip_transaction_terminate(BELLE_SIP_TRANSACTION(t)); return -1; } if (belle_sip_dialog_request_pending(dialog) || dialog->queued_ct!=NULL){ /*it cannot be sent immediately, queue the transaction into dialog*/ belle_sip_message("belle_sip_client_transaction_send_request(): transaction [%p], cannot send request now because dialog [%p] is busy" " or other transactions are queued, so queuing into dialog.",t, dialog); belle_sip_dialog_queue_client_transaction(dialog,t); return 0; } belle_sip_dialog_update_request(dialog,req); } if (dialog){ belle_sip_dialog_update(dialog,(belle_sip_transaction_t*)t,BELLE_SIP_OBJECT_IS_INSTANCE_OF(t,belle_sip_server_transaction_t)); } if (!t->next_hop) { if (t->preset_route) { t->next_hop=belle_sip_hop_new_from_uri(t->preset_route); } else { t->next_hop = belle_sip_stack_get_next_hop(prov->stack,t->base.request); } belle_sip_object_ref(t->next_hop); } else { /*next hop already preset, probably in case of CANCEL*/ } belle_sip_provider_add_client_transaction(t->base.provider,t); /*add it in any case*/ chan=belle_sip_provider_get_channel(prov,t->next_hop); if (chan){ belle_sip_object_ref(chan); belle_sip_channel_add_listener(chan,BELLE_SIP_CHANNEL_LISTENER(t)); t->base.channel=chan; if (belle_sip_channel_get_state(chan)==BELLE_SIP_CHANNEL_INIT){ belle_sip_message("belle_sip_client_transaction_send_request(): waiting channel to be ready"); belle_sip_channel_prepare(chan); /*the channel will notify us when it is ready*/ } else if (belle_sip_channel_get_state(chan)==BELLE_SIP_CHANNEL_READY){ /*otherwise we can send immediately*/ BELLE_SIP_OBJECT_VPTR(t,belle_sip_client_transaction_t)->send_request(t); } result=0; }else { belle_sip_error("belle_sip_client_transaction_send_request(): no channel available"); belle_sip_transaction_terminate(BELLE_SIP_TRANSACTION(t)); result=-1; } return result; } static unsigned int should_dialog_be_created(belle_sip_client_transaction_t *t, belle_sip_response_t *resp){ belle_sip_request_t* req = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(t)); const char* method = belle_sip_request_get_method(req); int status_code = belle_sip_response_get_status_code(resp); return status_code>=101 && status_code<300 && (strcmp(method,"INVITE")==0 || strcmp(method,"SUBSCRIBE")==0); } void belle_sip_client_transaction_notify_response(belle_sip_client_transaction_t *t, belle_sip_response_t *resp){ belle_sip_transaction_t *base=(belle_sip_transaction_t*)t; belle_sip_request_t* req = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(t)); const char* method = belle_sip_request_get_method(req); belle_sip_response_event_t event; belle_sip_dialog_t *dialog=base->dialog; int status_code = belle_sip_response_get_status_code(resp); if (base->last_response) belle_sip_object_unref(base->last_response); base->last_response=(belle_sip_response_t*)belle_sip_object_ref(resp); if (dialog){ if (status_code>=101 && status_code<300 && strcmp(method,"INVITE")==0 && (dialog->state==BELLE_SIP_DIALOG_EARLY || dialog->state==BELLE_SIP_DIALOG_CONFIRMED)){ /*make sure this response matches the current dialog, or creates a new one*/ if (!belle_sip_dialog_match(dialog,(belle_sip_message_t*)resp,FALSE)){ dialog=belle_sip_provider_find_dialog_from_message(t->base.provider,(belle_sip_message_t*)resp,FALSE); if (!dialog){ dialog=belle_sip_provider_create_dialog_internal(t->base.provider,BELLE_SIP_TRANSACTION(t),FALSE);/*belle_sip_dialog_new(base);*/ belle_sip_message("Handling response creating a new dialog !"); } } } } else if (should_dialog_be_created(t,resp)) { dialog=belle_sip_provider_create_dialog_internal(t->base.provider,BELLE_SIP_TRANSACTION(t),FALSE); } if (dialog && belle_sip_dialog_update(dialog,BELLE_SIP_TRANSACTION(t),FALSE)) { /* retransmition, just return*/ belle_sip_message("Response [%p] absorbed by dialog [%p], skipped from transaction layer.",resp,dialog); return; } event.source=(belle_sip_object_t*)base->provider; event.client_transaction=t; event.dialog=dialog; event.response=(belle_sip_response_t*)resp; BELLE_SIP_PROVIDER_INVOKE_LISTENERS_FOR_TRANSACTION(((belle_sip_transaction_t*)t),process_response_event,&event); /*check that 200Ok for INVITEs have been acknowledged by listener*/ if (dialog && status_code>=200 && status_code<300 && strcmp(method,"INVITE")==0){ belle_sip_dialog_check_ack_sent(dialog); } /*report a server having internal errors for REGISTER to the channel, in order to go to a fallback IP*/ if (status_code == 500 && strcmp(method,"REGISTER") == 0){ belle_sip_channel_notify_server_error(base->channel); } } void belle_sip_client_transaction_add_response(belle_sip_client_transaction_t *t, belle_sip_response_t *resp){ BELLE_SIP_OBJECT_VPTR(t,belle_sip_client_transaction_t)->on_response(t,resp); } belle_sip_uri_t *belle_sip_client_transaction_get_route(belle_sip_client_transaction_t *t){ return t->preset_route; } static void client_transaction_destroy(belle_sip_client_transaction_t *t ){ if (t->preset_route) belle_sip_object_unref(t->preset_route); if (t->next_hop) belle_sip_object_unref(t->next_hop); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_client_transaction_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_client_transaction_t) { { BELLE_SIP_VPTR_INIT(belle_sip_client_transaction_t,belle_sip_transaction_t,FALSE), (belle_sip_object_destroy_t)client_transaction_destroy, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, NULL }, NULL, NULL BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END void belle_sip_client_transaction_init(belle_sip_client_transaction_t *obj, belle_sip_provider_t *prov, belle_sip_request_t *req){ belle_sip_header_via_t *via=BELLE_SIP_HEADER_VIA(belle_sip_message_get_header((belle_sip_message_t*)req,"via")); char token[BELLE_SIP_BRANCH_ID_LENGTH]; if (!via){ belle_sip_fatal("belle_sip_client_transaction_init(): No via in request."); } if (strcmp(belle_sip_request_get_method(req),"CANCEL")!=0){ obj->base.branch_id=belle_sip_strdup_printf(BELLE_SIP_BRANCH_MAGIC_COOKIE ".%s",belle_sip_random_token(token,sizeof(token))); belle_sip_header_via_set_branch(via,obj->base.branch_id); }else{ obj->base.branch_id=belle_sip_strdup(belle_sip_header_via_get_branch(via)); } belle_sip_transaction_init((belle_sip_transaction_t*)obj, prov,req); } belle_sip_refresher_t* belle_sip_client_transaction_create_refresher(belle_sip_client_transaction_t *t) { return belle_sip_refresher_new(t); } belle_sip_request_t* belle_sip_client_transaction_create_authenticated_request(belle_sip_client_transaction_t *t,belle_sip_list_t** auth_infos,const char* realm) { belle_sip_request_t* initial_request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(t)); belle_sip_request_t* req=belle_sip_request_clone_with_body(initial_request); belle_sip_header_cseq_t* cseq=belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t); belle_sip_header_cseq_set_seq_number(cseq,belle_sip_header_cseq_get_seq_number(cseq)+1); if (belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(t)) != BELLE_SIP_TRANSACTION_COMPLETED && belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(t)) != BELLE_SIP_TRANSACTION_TERMINATED) { belle_sip_error("Invalid state [%s] for transaction [%p], should be BELLE_SIP_TRANSACTION_COMPLETED | BELLE_SIP_TRANSACTION_TERMINATED" ,belle_sip_transaction_state_to_string(belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(t))) ,t); belle_sip_object_unref(req); return NULL; } /*remove auth headers*/ belle_sip_message_remove_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_AUTHORIZATION); belle_sip_message_remove_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_PROXY_AUTHORIZATION); /*put auth header*/ belle_sip_provider_add_authorization(t->base.provider,req,t->base.last_response,NULL,auth_infos,realm); return req; } /* rfc 3265 3.3.4. Dialog creation and termination ... NOTIFY requests are matched to such SUBSCRIBE requests if they contain the same "Call-ID", a "To" header "tag" parameter which matches the "From" header "tag" parameter of the SUBSCRIBE, and the same "Event" header field. Rules for comparisons of the "Event" headers are described in section 7.2.1. If a matching NOTIFY request contains a "Subscription-State" of "active" or "pending", it creates a new subscription and a new dialog (unless they have already been created by a matching response, as described above). */ int belle_sip_client_transaction_is_notify_matching_pending_subscribe(belle_sip_client_transaction_t *trans , belle_sip_request_t *notify) { belle_sip_request_t *subscription; belle_sip_header_event_t *sub_event, *notif_event; belle_sip_header_call_id_t *sub_call_id, *notif_call_id; const char* sub_from_tag, *notif_to_tag; if (!belle_sip_transaction_state_is_transient(belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(trans))) || strcmp("SUBSCRIBE", belle_sip_transaction_get_method(BELLE_SIP_TRANSACTION(trans)))!=0) return 0; if (strcmp("NOTIFY",belle_sip_request_get_method(notify)) != 0) { belle_sip_error("belle_sip_client_transaction_is_notify_matching_pending_subscribe for dialog [%p], requires a notify request",notify); } subscription = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(trans)); sub_event = belle_sip_message_get_header_by_type(subscription, belle_sip_header_event_t); if (!sub_event || !belle_sip_header_event_get_package_name(sub_event)) return 0; notif_event = belle_sip_message_get_header_by_type(notify, belle_sip_header_event_t); if (!notif_event || !belle_sip_header_event_get_package_name(notif_event)) return 0; sub_call_id = belle_sip_message_get_header_by_type(subscription, belle_sip_header_call_id_t); notif_call_id = belle_sip_message_get_header_by_type(notify, belle_sip_header_call_id_t); sub_from_tag = belle_sip_header_from_get_tag(belle_sip_message_get_header_by_type(subscription, belle_sip_header_from_t)); notif_to_tag = belle_sip_header_to_get_tag(belle_sip_message_get_header_by_type(notify, belle_sip_header_to_t)); return strcmp(belle_sip_header_call_id_get_call_id(sub_call_id),belle_sip_header_call_id_get_call_id(notif_call_id))==0 && sub_from_tag && notif_to_tag && strcmp(sub_from_tag,notif_to_tag)==0 && strcasecmp(belle_sip_header_event_get_package_name(sub_event),belle_sip_header_event_get_package_name(notif_event))==0; } belle-sip-1.6.3/src/transports/000077500000000000000000000000001313437522400164075ustar00rootroot00000000000000belle-sip-1.6.3/src/transports/stream_channel.c000066400000000000000000000260131313437522400215400ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #include "belle-sip/mainloop.h" #include "stream_channel.h" static void set_tcp_nodelay(belle_sip_socket_t sock){ int tmp=1; int err=bctbx_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,(char*)&tmp,sizeof(tmp)); if (err == -1){ belle_sip_warning ("Fail to set TCP_NODELAY: %s.", belle_sip_get_socket_error_string()); } } /*************TCP********/ static int stream_channel_process_data(belle_sip_stream_channel_t *obj,unsigned int revents); static void stream_channel_uninit(belle_sip_stream_channel_t *obj){ belle_sip_socket_t sock = belle_sip_source_get_socket((belle_sip_source_t*)obj); if (sock!=(belle_sip_socket_t)-1) stream_channel_close(obj); } int stream_channel_send(belle_sip_stream_channel_t *obj, const void *buf, size_t buflen){ belle_sip_socket_t sock = belle_sip_source_get_socket((belle_sip_source_t*)obj); int err=bctbx_send(sock,buf,buflen,0); if (err==(belle_sip_socket_t)-1){ int errnum=get_socket_error(); if (!belle_sip_error_code_is_would_block(errnum)){ belle_sip_error("Could not send stream packet on channel [%p]: %s",obj,belle_sip_get_socket_error_string_from_code(errnum)); } return -errnum; } return err; } int stream_channel_recv(belle_sip_stream_channel_t *obj, void *buf, size_t buflen){ belle_sip_socket_t sock = belle_sip_source_get_socket((belle_sip_source_t*)obj); int err=bctbx_recv(sock,buf,buflen,0); if (err==(belle_sip_socket_t)-1){ int errnum=get_socket_error(); if (errnum == BCTBX_ENOTCONN) { //Do NOT treat it as an error belle_sip_message("Socket is not connected because of IOS10 background policy"); obj->base.closed_by_remote = TRUE; return 0; } if (!belle_sip_error_code_is_would_block(errnum)){ belle_sip_error("Could not receive stream packet: %s",belle_sip_get_socket_error_string_from_code(errnum)); } return -errnum; } return err; } void stream_channel_close(belle_sip_stream_channel_t *obj){ belle_sip_socket_t sock = belle_sip_source_get_socket((belle_sip_source_t*)obj); if (sock!=(belle_sip_socket_t)-1){ #if TARGET_OS_IPHONE if (obj->read_stream != NULL) { CFReadStreamClose (obj->read_stream); CFRelease (obj->read_stream); obj->read_stream=NULL; } if (obj->write_stream != NULL) { CFWriteStreamClose (obj->write_stream); CFRelease (obj->write_stream); obj->write_stream=NULL; } #endif belle_sip_close_socket(sock); } } #if TARGET_OS_IPHONE static void stream_channel_enable_ios_background_mode(belle_sip_stream_channel_t *obj){ int sock=belle_sip_source_get_socket((belle_sip_source_t*)obj); CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock, &obj->read_stream, &obj->write_stream); if (obj->read_stream){ if (!CFReadStreamSetProperty (obj->read_stream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP)){ belle_sip_warning("CFReadStreamSetProperty() could not set VoIP service type on read stream."); } }else belle_sip_warning("CFStreamCreatePairWithSocket() could not create the read stream."); if (obj->write_stream){ if (!CFWriteStreamSetProperty (obj->write_stream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP)){ belle_sip_warning("CFReadStreamSetProperty() could not set VoIP service type on write stream."); } }else belle_sip_warning("CFStreamCreatePairWithSocket() could not create the write stream."); if (!CFReadStreamOpen (obj->read_stream)) { belle_sip_warning("CFReadStreamOpen() failed."); } if (!CFWriteStreamOpen (obj->write_stream)) { belle_sip_warning("CFWriteStreamOpen() failed."); } } #endif int stream_channel_connect(belle_sip_stream_channel_t *obj, const struct addrinfo *ai){ int err; int tmp; belle_sip_socket_t sock; tmp=1; obj->base.ai_family=ai->ai_family; sock=bctbx_socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP); if (sock==(belle_sip_socket_t)-1){ belle_sip_error("Could not create socket: %s",belle_sip_get_socket_error_string()); return -1; } err=bctbx_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,(char*)&tmp,sizeof(tmp)); if (err!=0){ belle_sip_error("bctbx_setsockopt TCP_NODELAY failed: [%s]",belle_sip_get_socket_error_string()); } belle_sip_socket_set_nonblocking(sock); if (ai->ai_family==AF_INET6){ belle_sip_socket_enable_dual_stack(sock); } err = bctbx_connect(sock,ai->ai_addr,(socklen_t)ai->ai_addrlen); if (err != 0 && get_socket_error()!=BELLESIP_EINPROGRESS && get_socket_error()!=BELLESIP_EWOULDBLOCK) { belle_sip_error("stream connect failed %s",belle_sip_get_socket_error_string()); belle_sip_close_socket(sock); return -1; } belle_sip_channel_set_socket((belle_sip_channel_t*)obj,sock,(belle_sip_source_func_t)stream_channel_process_data); belle_sip_source_set_events((belle_sip_source_t*)obj,BELLE_SIP_EVENT_READ|BELLE_SIP_EVENT_WRITE|BELLE_SIP_EVENT_ERROR); belle_sip_source_set_timeout((belle_sip_source_t*)obj,belle_sip_stack_get_transport_timeout(obj->base.stack)); belle_sip_main_loop_add_source(obj->base.stack->ml,(belle_sip_source_t*)obj); return 0; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_stream_channel_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_stream_channel_t) { { BELLE_SIP_VPTR_INIT(belle_sip_stream_channel_t,belle_sip_channel_t,FALSE), (belle_sip_object_destroy_t)stream_channel_uninit, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, "TCP", 1, /*is_reliable*/ (int (*)(belle_sip_channel_t *, const struct addrinfo *))stream_channel_connect, (int (*)(belle_sip_channel_t *, const void *, size_t ))stream_channel_send, (int (*)(belle_sip_channel_t *, void *, size_t ))stream_channel_recv, (void (*)(belle_sip_channel_t *))stream_channel_close, } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END int finalize_stream_connection(belle_sip_stream_channel_t *obj, unsigned int revents, struct sockaddr *addr, socklen_t* slen) { int err, errnum; socklen_t optlen=sizeof(errnum); belle_sip_socket_t sock=belle_sip_source_get_socket((belle_sip_source_t*)obj); if (revents==BELLE_SIP_EVENT_TIMEOUT){ belle_sip_warning("channel [%p]: user-defined transport timeout.",obj); return -1; } if (!(revents & BELLE_SIP_EVENT_WRITE) && !(revents & BELLE_SIP_EVENT_READ)){ belle_sip_warning("channel [%p]: getting unexpected event while connecting",obj); return -1; } err=bctbx_getsockopt(sock,SOL_SOCKET,SO_ERROR,(void*)&errnum,&optlen); if (err!=0){ belle_sip_error("Failed to retrieve connection status for fd [%i]: cause [%s]",sock,belle_sip_get_socket_error_string()); return -1; }else{ if (errnum==0){ /*obtain bind address for client*/ err=bctbx_getsockname(sock,addr,slen); if (err<0){ belle_sip_error("Failed to retrieve sockname for fd [%i]: cause [%s]",sock,belle_sip_get_socket_error_string()); return -1; } #if TARGET_OS_IPHONE stream_channel_enable_ios_background_mode(obj); #endif if (obj->base.stack->dscp && obj->base.lp){ /*apply dscp only to channel belonging to a SIP listening point*/ belle_sip_socket_set_dscp(sock,obj->base.ai_family,obj->base.stack->dscp); } set_tcp_nodelay(sock); return 0; }else{ belle_sip_error("Connection failed for fd [%i]: cause [%s]",sock,belle_sip_get_socket_error_string_from_code(errnum)); return -1; } } } static int stream_channel_process_data(belle_sip_stream_channel_t *obj,unsigned int revents){ struct sockaddr_storage ss; socklen_t addrlen=sizeof(ss); belle_sip_channel_state_t state=belle_sip_channel_get_state((belle_sip_channel_t*)obj); belle_sip_channel_t *base=(belle_sip_channel_t*)obj; /*belle_sip_message("TCP channel process_data");*/ if (state == BELLE_SIP_CHANNEL_CONNECTING ) { if (finalize_stream_connection(obj,revents,(struct sockaddr*)&ss,&addrlen)) { belle_sip_error("Cannot connect to [%s://%s:%i]",belle_sip_channel_get_transport_name(base),base->peer_name,base->peer_port); channel_set_state(base,BELLE_SIP_CHANNEL_ERROR); return BELLE_SIP_STOP; } belle_sip_source_set_events((belle_sip_source_t*)obj,BELLE_SIP_EVENT_READ|BELLE_SIP_EVENT_ERROR); belle_sip_source_set_timeout((belle_sip_source_t*)obj,-1); belle_sip_channel_set_ready(base,(struct sockaddr*)&ss,addrlen); return BELLE_SIP_CONTINUE; } else if (state == BELLE_SIP_CHANNEL_READY) { return belle_sip_channel_process_data(base,revents); } else { belle_sip_warning("Unexpected event [%i], in state [%s] for channel [%p]",revents,belle_sip_channel_state_to_string(state),obj); return BELLE_SIP_STOP; } return BELLE_SIP_CONTINUE; } void belle_sip_stream_channel_init_client(belle_sip_stream_channel_t *obj, belle_sip_stack_t *stack, const char *bindip, int localport, const char *peer_cname, const char *dest, int port){ belle_sip_channel_init((belle_sip_channel_t*)obj, stack ,bindip,localport,peer_cname,dest,port); } belle_sip_channel_t * belle_sip_stream_channel_new_client(belle_sip_stack_t *stack,const char *bindip, int localport, const char *peer_cname, const char *dest, int port){ belle_sip_stream_channel_t *obj=belle_sip_object_new(belle_sip_stream_channel_t); belle_sip_stream_channel_init_client(obj,stack,bindip,localport,peer_cname,dest,port); return (belle_sip_channel_t*)obj; } /*child of server socket*/ belle_sip_channel_t * belle_sip_stream_channel_new_child(belle_sip_stack_t *stack, belle_sip_socket_t sock, struct sockaddr *remote_addr, socklen_t slen){ struct sockaddr_storage localaddr; socklen_t local_len=sizeof(localaddr); belle_sip_stream_channel_t *obj; int err; int optval=1; err = bctbx_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof (optval)); if (err == -1){ belle_sip_warning ("Fail to set SIP/TCP address reusable: %s.", belle_sip_get_socket_error_string()); } set_tcp_nodelay(sock); if (bctbx_getsockname(sock,(struct sockaddr*)&localaddr,&local_len)==-1){ belle_sip_error("bctbx_getsockname() failed: %s",belle_sip_get_socket_error_string()); return NULL; } obj=belle_sip_object_new(belle_sip_stream_channel_t); belle_sip_channel_init_with_addr((belle_sip_channel_t*)obj,stack,NULL,0,remote_addr,slen); belle_sip_socket_set_nonblocking(sock); belle_sip_channel_set_socket((belle_sip_channel_t*)obj,sock,(belle_sip_source_func_t)stream_channel_process_data); belle_sip_source_set_events((belle_sip_source_t*)obj,BELLE_SIP_EVENT_READ|BELLE_SIP_EVENT_ERROR); belle_sip_channel_set_ready((belle_sip_channel_t*)obj,(struct sockaddr*)&localaddr,local_len); belle_sip_main_loop_add_source(stack->ml,(belle_sip_source_t*)obj); return (belle_sip_channel_t*)obj; } belle-sip-1.6.3/src/transports/stream_channel.h000066400000000000000000000045641313437522400215540ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef STREAM_CHANNEL_H_ #define STREAM_CHANNEL_H_ #ifdef __APPLE_ #include "TargetConditionals.h" #endif #if TARGET_OS_IPHONE #include #include #endif #include "channel.h" struct belle_sip_stream_channel{ belle_sip_channel_t base; #if TARGET_OS_IPHONE CFReadStreamRef read_stream; CFWriteStreamRef write_stream; #endif }; BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_stream_channel_t,belle_sip_channel_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END void belle_sip_stream_channel_init_client(belle_sip_stream_channel_t *obj, belle_sip_stack_t *stack, const char *bindip, int localport,const char *peer_cname, const char *dest, int port); BELLESIP_EXPORT belle_sip_channel_t * belle_sip_stream_channel_new_client(belle_sip_stack_t *stack, const char *bindip, int localport, const char *peer_cname, const char *name, int port); belle_sip_channel_t * belle_sip_stream_channel_new_child(belle_sip_stack_t *stack, belle_sip_socket_t sock, struct sockaddr *remote_addr, socklen_t slen); void stream_channel_close(belle_sip_stream_channel_t *obj); int stream_channel_connect(belle_sip_stream_channel_t *obj, const struct addrinfo *ai); /*return 0 if succeed*/ int finalize_stream_connection(belle_sip_stream_channel_t *obj, unsigned int revents, struct sockaddr *addr, socklen_t* slen); int stream_channel_send(belle_sip_stream_channel_t *obj, const void *buf, size_t buflen); int stream_channel_recv(belle_sip_stream_channel_t *obj, void *buf, size_t buflen); /*for testing purpose*/ BELLESIP_EXPORT void belle_sip_channel_parse_stream(belle_sip_channel_t *obj, int end_of_stream); #endif /* STREAM_CHANNEL_H_ */ belle-sip-1.6.3/src/transports/stream_listeningpoint.c000066400000000000000000000155501313437522400232020ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" static int on_new_connection(void *userdata, unsigned int events); void belle_sip_stream_listening_point_destroy_server_socket(belle_sip_stream_listening_point_t *lp){ if (lp->server_sock!=(belle_sip_socket_t)-1){ belle_sip_close_socket(lp->server_sock); lp->server_sock=-1; } if (lp->source){ belle_sip_main_loop_remove_source(lp->base.stack->ml,lp->source); belle_sip_object_unref(lp->source); lp->source=NULL; } } static void belle_sip_stream_listening_point_uninit(belle_sip_stream_listening_point_t *lp){ belle_sip_stream_listening_point_destroy_server_socket(lp); } static belle_sip_channel_t *stream_create_channel(belle_sip_listening_point_t *lp, const belle_sip_hop_t *hop){ belle_sip_channel_t *chan=belle_sip_stream_channel_new_client(lp->stack ,belle_sip_uri_get_host(lp->listening_uri) ,belle_sip_uri_get_port(lp->listening_uri) ,hop->cname,hop->host,hop->port); return chan; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_stream_listening_point_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_stream_listening_point_t) { { BELLE_SIP_VPTR_INIT(belle_sip_stream_listening_point_t, belle_sip_listening_point_t,TRUE), (belle_sip_object_destroy_t)belle_sip_stream_listening_point_uninit, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, "TCP", stream_create_channel } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END static belle_sip_socket_t create_server_socket(const char *addr, int * port, int *family){ struct addrinfo hints={0}; struct addrinfo *res=NULL; int err; belle_sip_socket_t sock; char portnum[10]; int optval=1; if (*port==-1) *port=0; /*random port for bind()*/ belle_sip_set_socket_api(NULL); snprintf(portnum,sizeof(portnum),"%i",*port); hints.ai_family=AF_UNSPEC; hints.ai_socktype=SOCK_STREAM; hints.ai_protocol=IPPROTO_TCP; hints.ai_flags=AI_NUMERICSERV; err=getaddrinfo(addr,portnum,&hints,&res); if (err!=0){ belle_sip_error("getaddrinfo() failed for %s port %i: %s",addr,*port,gai_strerror(err)); return -1; } *family=res->ai_family; sock=bctbx_socket(res->ai_family,res->ai_socktype,res->ai_protocol); if (sock==(belle_sip_socket_t)-1){ belle_sip_error("Cannot create TCP socket: %s",belle_sip_get_socket_error_string()); freeaddrinfo(res); return -1; } err = bctbx_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof (optval)); if (err == -1){ belle_sip_warning ("Fail to set SIP/TCP address reusable: %s.", belle_sip_get_socket_error_string()); } if (res->ai_family==AF_INET6){ belle_sip_socket_enable_dual_stack(sock); } err=bctbx_bind(sock,res->ai_addr,(socklen_t)res->ai_addrlen); if (err==-1){ belle_sip_error("TCP bind() failed for %s port %i: %s",addr,*port,belle_sip_get_socket_error_string()); belle_sip_close_socket(sock); freeaddrinfo(res); return -1; } freeaddrinfo(res); if (*port==0){ struct sockaddr_storage saddr; socklen_t saddr_len=sizeof(saddr); err=bctbx_getsockname(sock,(struct sockaddr*)&saddr,&saddr_len); if (err==0){ err=bctbx_getnameinfo((struct sockaddr*)&saddr,saddr_len,NULL,0,portnum,sizeof(portnum),NI_NUMERICSERV|NI_NUMERICHOST); if (err==0){ *port=atoi(portnum); belle_sip_message("Random TCP port is %i",*port); }else belle_sip_error("TCP bind failed, getnameinfo(): %s",gai_strerror(err)); }else belle_sip_error("TCP bind failed, bctbx_getsockname(): %s",belle_sip_get_socket_error_string()); } err=listen(sock,64); if (err==-1){ belle_sip_error("TCP listen() failed for %s port %i: %s",addr,*port,belle_sip_get_socket_error_string()); belle_sip_close_socket(sock); return -1; } return sock; } void belle_sip_stream_listening_point_setup_server_socket(belle_sip_stream_listening_point_t *obj, belle_sip_source_func_t on_new_connection_cb ){ int port=belle_sip_uri_get_port(obj->base.listening_uri); obj->server_sock=create_server_socket(belle_sip_uri_get_host(obj->base.listening_uri), &port, &obj->base.ai_family); if (obj->server_sock==(belle_sip_socket_t)-1) return; belle_sip_uri_set_port(((belle_sip_listening_point_t*)obj)->listening_uri,port); if (obj->base.stack->dscp) belle_sip_socket_set_dscp(obj->server_sock,obj->base.ai_family,obj->base.stack->dscp); obj->source=belle_sip_socket_source_new(on_new_connection_cb,obj,obj->server_sock,BELLE_SIP_EVENT_READ,-1); belle_sip_main_loop_add_source(obj->base.stack->ml,obj->source); } static int on_new_connection(void *userdata, unsigned int events){ belle_sip_socket_t child; struct sockaddr_storage addr; socklen_t slen=sizeof(addr); belle_sip_stream_listening_point_t *lp=(belle_sip_stream_listening_point_t*)userdata; belle_sip_channel_t *chan; child=accept(lp->server_sock,(struct sockaddr*)&addr,&slen); if (child==(belle_sip_socket_t)-1){ belle_sip_error("Listening point [%p] accept() failed on TCP server socket: %s",lp,belle_sip_get_socket_error_string()); belle_sip_stream_listening_point_destroy_server_socket(lp); belle_sip_stream_listening_point_setup_server_socket(lp,on_new_connection); return BELLE_SIP_STOP; } belle_sip_message("New connection arriving !"); chan=belle_sip_stream_channel_new_child(lp->base.stack,child,(struct sockaddr*)&addr,slen); if (chan) belle_sip_listening_point_add_channel((belle_sip_listening_point_t*)lp,chan); return BELLE_SIP_CONTINUE; } void belle_sip_stream_listening_point_init(belle_sip_stream_listening_point_t *obj, belle_sip_stack_t *s, const char *ipaddress, int port, belle_sip_source_func_t on_new_connection_cb ){ belle_sip_listening_point_init((belle_sip_listening_point_t*)obj,s,ipaddress,port); if (port != BELLE_SIP_LISTENING_POINT_DONT_BIND) belle_sip_stream_listening_point_setup_server_socket(obj, on_new_connection_cb); } belle_sip_listening_point_t * belle_sip_stream_listening_point_new(belle_sip_stack_t *s, const char *ipaddress, int port){ belle_sip_stream_listening_point_t *lp=belle_sip_object_new(belle_sip_stream_listening_point_t); belle_sip_stream_listening_point_init(lp,s,ipaddress,port,on_new_connection); if (port != BELLE_SIP_LISTENING_POINT_DONT_BIND && lp->server_sock==(belle_sip_socket_t)-1){ belle_sip_object_unref(lp); return NULL; } return BELLE_SIP_LISTENING_POINT(lp); } belle-sip-1.6.3/src/transports/tls_channel.c000066400000000000000000001014571313437522400210550ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #include "stream_channel.h" #include "bctoolbox/crypto.h" static int belle_sip_tls_channel_init_bctbx_ssl(belle_sip_tls_channel_t *obj); static void belle_sip_tls_channel_deinit_bctbx_ssl(belle_sip_tls_channel_t *obj); /*****************************************************************************/ /*** signing key structure and methods ***/ /*****************************************************************************/ struct belle_sip_signing_key { belle_sip_object_t objet; bctbx_signing_key_t *key; }; /*************************************/ /*** Internal functions ***/ static void belle_sip_signing_key_destroy(belle_sip_signing_key_t *signing_key){ bctbx_signing_key_free(signing_key->key); } static void belle_sip_signing_key_clone(belle_sip_signing_key_t *signing_key, const belle_sip_signing_key_t *orig){ belle_sip_error("belle_sip_signing_key_clone not supported"); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_signing_key_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_signing_key_t,belle_sip_object_t,belle_sip_signing_key_destroy,belle_sip_signing_key_clone,NULL,TRUE); /*************************************/ /** Exposed functions **/ /** * Retrieve key in a string(PEM format) */ char *belle_sip_signing_key_get_pem(belle_sip_signing_key_t *key) { if (key == NULL) return NULL; return bctbx_signing_key_get_pem(key->key); } belle_sip_signing_key_t *belle_sip_signing_key_new(void) { belle_sip_signing_key_t* key = belle_sip_object_new(belle_sip_signing_key_t); key->key = bctbx_signing_key_new(); return key; } belle_sip_signing_key_t* belle_sip_signing_key_parse(const char* buff, size_t size, const char* passwd) { belle_sip_signing_key_t *signing_key = belle_sip_signing_key_new(); int ret; /* check size, buff is the key in PEM format and thus shall include a NULL termination char, make size includes this termination */ if (strlen(buff) == size) { size++; } ret = bctbx_signing_key_parse(signing_key->key, buff, size, (const unsigned char *)passwd, passwd?strlen(passwd):0); if (ret < 0) { char tmp[128]; bctbx_strerror(ret,tmp,sizeof(tmp)); belle_sip_error("cannot parse x509 signing key because [%s]",tmp); belle_sip_object_unref(signing_key); return NULL; } return signing_key; } belle_sip_signing_key_t* belle_sip_signing_key_parse_file(const char* path,const char* passwd) { belle_sip_signing_key_t *signing_key = belle_sip_signing_key_new(); int ret; ret = bctbx_signing_key_parse_file(signing_key->key, path, passwd); if (ret < 0) { char tmp[128]; bctbx_strerror(ret,tmp,sizeof(tmp)); belle_sip_error("cannot parse x509 signing key because [%s]",tmp); belle_sip_object_unref(signing_key); return NULL; } return signing_key; } /*****************************************************************************/ /*** end of signing key structure and methods ***/ /*****************************************************************************/ /*****************************************************************************/ /*** certificate structure and methods ***/ /*****************************************************************************/ struct belle_sip_certificates_chain { belle_sip_object_t objet; bctbx_x509_certificate_t *cert; }; /*************************************/ /*** Internal functions ***/ static void belle_sip_certificates_chain_destroy(belle_sip_certificates_chain_t *certificate){ bctbx_x509_certificate_free(certificate->cert); } static void belle_sip_certificates_chain_clone(belle_sip_certificates_chain_t *certificate, const belle_sip_certificates_chain_t *orig){ belle_sip_error("belle_sip_certificate_clone not supported"); } static int belle_sip_certificate_fill(belle_sip_certificates_chain_t* certificate, const char* buff, size_t size, belle_sip_certificate_raw_format_t format) { int err; if (format == BELLE_SIP_CERTIFICATE_RAW_FORMAT_PEM) { /* if format is PEM, make sure the null termination char is included in the buffer given size */ if (strlen(buff) == size) { size++; } } if ((err=bctbx_x509_certificate_parse(certificate->cert, buff, size)) <0) { char tmp[128]; bctbx_strerror(err,tmp,sizeof(tmp)); belle_sip_error("cannot parse x509 cert because [%s]",tmp); return -1; } return 0; } static int belle_sip_certificate_fill_from_file(belle_sip_certificates_chain_t* certificate, const char* path, belle_sip_certificate_raw_format_t format) { int err; if ((err=bctbx_x509_certificate_parse_file(certificate->cert, path)) <0) { char tmp[128]; bctbx_strerror(err,tmp,sizeof(tmp)); belle_sip_error("cannot parse x509 cert because [%s]",tmp); return -1; } return 0; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_certificates_chain_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sip_certificates_chain_t,belle_sip_object_t,belle_sip_certificates_chain_destroy,belle_sip_certificates_chain_clone,NULL,TRUE); /*************************************/ /** Exposed functions **/ /** * Retrieve key or certificate in a string(PEM format) */ char *belle_sip_certificates_chain_get_pem(belle_sip_certificates_chain_t *cert) { if (cert == NULL) return NULL; return bctbx_x509_certificates_chain_get_pem(cert->cert); } belle_sip_certificates_chain_t *belle_sip_certificate_chain_new(void) { belle_sip_certificates_chain_t* certificate = belle_sip_object_new(belle_sip_certificates_chain_t); certificate->cert = bctbx_x509_certificate_new(); return certificate; } belle_sip_certificates_chain_t* belle_sip_certificates_chain_parse(const char* buff, size_t size,belle_sip_certificate_raw_format_t format) { belle_sip_certificates_chain_t* certificate = belle_sip_certificate_chain_new(); if (belle_sip_certificate_fill(certificate, buff, size, format)) { belle_sip_object_unref(certificate); certificate=NULL; } return certificate; } belle_sip_certificates_chain_t* belle_sip_certificates_chain_parse_file(const char* path, belle_sip_certificate_raw_format_t format) { belle_sip_certificates_chain_t* certificate = belle_sip_certificate_chain_new(); if (belle_sip_certificate_fill_from_file(certificate, path, format)) { belle_sip_object_unref(certificate); certificate=NULL; } return certificate; } /* * Parse all *.pem files in a given dir(non recursively) and return the one matching the given subject */ int belle_sip_get_certificate_and_pkey_in_dir(const char *path, const char *subject, belle_sip_certificates_chain_t **certificate, belle_sip_signing_key_t **pkey, belle_sip_certificate_raw_format_t format) { /* get all *.pem file from given path */ belle_sip_list_t *file_list = belle_sip_parse_directory(path, ".pem"); char *filename = NULL; file_list = belle_sip_list_pop_front(file_list, (void **)&filename); while (filename != NULL) { belle_sip_certificates_chain_t *found_certificate = belle_sip_certificates_chain_parse_file(filename, format); if (found_certificate != NULL) { /* there is a certificate in this file */ char *subject_CNAME_begin, *subject_CNAME_end; belle_sip_signing_key_t *found_key; char name[500]; memset( name, 0, sizeof(name) ); if (bctbx_x509_certificate_get_subject_dn(found_certificate->cert, name, sizeof(name)) > 0) { /* parse subject to find the CN=xxx, field. There may be no , at the and but a \0 */ subject_CNAME_begin = strstr(name, "CN="); if (subject_CNAME_begin!=NULL) { subject_CNAME_begin+=3; subject_CNAME_end = strstr(subject_CNAME_begin, ","); if (subject_CNAME_end != NULL) { *subject_CNAME_end = '\0'; } if (strcmp(subject_CNAME_begin, subject)==0) { /* subject CNAME match the one we are looking for*/ /* do we have a key too ? */ found_key = belle_sip_signing_key_parse_file(filename, NULL); if (found_key!=NULL) { *certificate = found_certificate; *pkey = found_key; belle_sip_free(filename); belle_sip_list_free_with_data(file_list, belle_sip_free); /* free possible rest of list */ return 0; } } else { /* doesn't match, unref the created certificate */ belle_sip_object_unref(found_certificate); } } } else { /* no DN, just free it then */ belle_sip_object_unref(found_certificate); } } belle_sip_free(filename); file_list = belle_sip_list_pop_front(file_list, (void **)&filename); } return -1; } int belle_sip_generate_self_signed_certificate(const char* path, const char *subject, belle_sip_certificates_chain_t **certificate, belle_sip_signing_key_t **pkey) { char pem_buffer[8192]; int ret = 0; /* allocate certificate and key */ *pkey = belle_sip_signing_key_new(); *certificate = belle_sip_certificate_chain_new(); ret = bctbx_x509_certificate_generate_selfsigned(subject, (*certificate)->cert, (*pkey)->key, (path==NULL)?NULL:pem_buffer, (path==NULL)?0:8192); if ( ret != 0) { belle_sip_error("Unable to generate self signed certificate : -%x", -ret); belle_sip_object_unref(*pkey); belle_sip_object_unref(*certificate); *pkey = NULL; *certificate = NULL; return ret; } /* write the file if needed */ if (path!=NULL) { FILE *fd; char *name_with_path; size_t path_length; name_with_path = (char *)belle_sip_malloc(strlen(path)+257); /* max filename is 256 bytes in dirent structure, +1 for / */ path_length = strlen(path); memcpy(name_with_path, path, path_length); name_with_path[path_length] = '/'; path_length++; memcpy(name_with_path+path_length, subject, strlen(subject)); memcpy(name_with_path+path_length+strlen(subject), ".pem", 5); /* check if directory exists and if not, create it */ belle_sip_mkdir(path); if ( (fd = fopen(name_with_path, "w") ) == NULL) { belle_sip_error("Certificate generation can't open/create file %s", name_with_path); free(name_with_path); belle_sip_object_unref(*pkey); belle_sip_object_unref(*certificate); *pkey = NULL; *certificate = NULL; belle_sip_free(name_with_path); return -1; } if ( fwrite(pem_buffer, 1, strlen(pem_buffer), fd) != strlen(pem_buffer) ) { belle_sip_error("Certificate generation can't write into file %s", name_with_path); fclose(fd); belle_sip_object_unref(*pkey); belle_sip_object_unref(*certificate); *pkey = NULL; *certificate = NULL; belle_sip_free(name_with_path); return -1; } fclose(fd); belle_sip_free(name_with_path); } return 0; } char *belle_sip_certificates_chain_get_fingerprint(belle_sip_certificates_chain_t *certificate) { int ret; char *fingerprint=belle_sip_malloc0(200); /* compute the certificate using the hash algorithm used in the certificate signature */ ret = bctbx_x509_certificate_get_fingerprint(certificate->cert, fingerprint, 200, BCTBX_MD_UNDEFINED); if ( ret <=0) { belle_sip_error("Unable to generate fingerprint from certificate [-0x%x]", -ret); belle_sip_free(fingerprint); return NULL; } return fingerprint; } /*****************************************************************************/ /*** end of certificate structure and methods ***/ /*****************************************************************************/ /*************tls********/ // SSL verification callback prototype // der - raw certificate data, in DER format // length - length of certificate DER data // depth - position of certificate in cert chain, ending at 0 = root or top // flags - verification state for CURRENT certificate only typedef int (*verify_cb_error_cb_t)(unsigned char* der, int length, int depth, uint32_t* flags); static verify_cb_error_cb_t tls_verify_cb_error_cb = NULL; static int tls_process_data(belle_sip_channel_t *obj,unsigned int revents); struct belle_sip_tls_channel{ belle_sip_stream_channel_t base; bctbx_ssl_context_t *sslctx; bctbx_ssl_config_t *sslcfg; bctbx_x509_certificate_t *root_ca; struct sockaddr_storage ss; socklen_t socklen; int socket_connected; char *cur_debug_msg; belle_sip_certificates_chain_t* client_cert_chain; belle_sip_signing_key_t* client_cert_key; belle_tls_crypto_config_t *crypto_config; int http_proxy_connected; belle_sip_resolver_context_t *http_proxy_resolver_ctx; }; static void tls_channel_close(belle_sip_tls_channel_t *obj){ belle_sip_socket_t sock = belle_sip_source_get_socket((belle_sip_source_t*)obj); if (sock!=-1 && belle_sip_channel_get_state((belle_sip_channel_t*)obj)!=BELLE_SIP_CHANNEL_ERROR && !obj->base.base.closed_by_remote) { if (obj->sslctx) bctbx_ssl_close_notify(obj->sslctx); } stream_channel_close((belle_sip_stream_channel_t*)obj); belle_sip_tls_channel_deinit_bctbx_ssl(obj); obj->socket_connected=0; } static void tls_channel_uninit(belle_sip_tls_channel_t *obj){ belle_sip_socket_t sock = belle_sip_source_get_socket((belle_sip_source_t*)obj); if (sock!=(belle_sip_socket_t)-1) tls_channel_close(obj); belle_sip_tls_channel_deinit_bctbx_ssl(obj); if (obj->cur_debug_msg) belle_sip_free(obj->cur_debug_msg); belle_sip_object_unref(obj->crypto_config); if (obj->client_cert_chain) belle_sip_object_unref(obj->client_cert_chain); if (obj->client_cert_key) belle_sip_object_unref(obj->client_cert_key); if (obj->http_proxy_resolver_ctx) belle_sip_object_unref(obj->http_proxy_resolver_ctx); } static int tls_channel_send(belle_sip_channel_t *obj, const void *buf, size_t buflen){ belle_sip_tls_channel_t* channel = (belle_sip_tls_channel_t*)obj; int err = bctbx_ssl_write(channel->sslctx,buf,buflen); if (err<0){ char tmp[256]={0}; if (err==BCTBX_ERROR_NET_WANT_WRITE) return -BELLESIP_EWOULDBLOCK; bctbx_strerror(err,tmp,sizeof(tmp)); belle_sip_error("Channel [%p]: ssl_write() error [%i]: %s",obj,err,tmp); } return err; } static int tls_channel_recv(belle_sip_channel_t *obj, void *buf, size_t buflen){ belle_sip_tls_channel_t* channel = (belle_sip_tls_channel_t*)obj; int err = bctbx_ssl_read(channel->sslctx,buf,buflen); if (err==BCTBX_ERROR_SSL_PEER_CLOSE_NOTIFY) return 0; if (err<0){ char tmp[256]={0}; if (err==BCTBX_ERROR_NET_WANT_READ) return -BELLESIP_EWOULDBLOCK; bctbx_strerror(err,tmp,sizeof(tmp)); belle_sip_error("Channel [%p]: ssl_read() error [%i]: %s",obj, err, tmp); } return err; } static int tls_channel_connect_to(belle_sip_channel_t *obj, const struct addrinfo *ai){ int err; if (belle_sip_tls_channel_init_bctbx_ssl((belle_sip_tls_channel_t*)obj)==-1) return -1; err= stream_channel_connect((belle_sip_stream_channel_t*)obj,ai); if (err==0){ belle_sip_source_set_notify((belle_sip_source_t *)obj, (belle_sip_source_func_t)tls_process_data); return 0; } return -1; } static void http_proxy_res_done(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl){ belle_sip_tls_channel_t *obj=(belle_sip_tls_channel_t*)data; if (obj->http_proxy_resolver_ctx){ belle_sip_object_unref(obj->http_proxy_resolver_ctx); obj->http_proxy_resolver_ctx=NULL; } if (ai_list){ tls_channel_connect_to((belle_sip_channel_t *)obj,ai_list); bctbx_freeaddrinfo(ai_list); }else{ belle_sip_error("%s: DNS resolution failed for %s", __FUNCTION__, name); channel_set_state((belle_sip_channel_t*)obj,BELLE_SIP_CHANNEL_ERROR); } } static int tls_channel_connect(belle_sip_channel_t *obj, const struct addrinfo *ai){ belle_sip_tls_channel_t *channel=(belle_sip_tls_channel_t*)obj; if (obj->stack->http_proxy_host) { belle_sip_message("Resolving http proxy addr [%s] for channel [%p]",obj->stack->http_proxy_host,obj); /*assume ai family is the same*/ channel->http_proxy_resolver_ctx = belle_sip_stack_resolve_a(obj->stack, obj->stack->http_proxy_host, obj->stack->http_proxy_port, obj->ai_family, http_proxy_res_done, obj); if (channel->http_proxy_resolver_ctx) belle_sip_object_ref(channel->http_proxy_resolver_ctx); return 0; } else { return tls_channel_connect_to(obj, ai); } } BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_tls_channel_t,belle_sip_stream_channel_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_tls_channel_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_tls_channel_t) { { { BELLE_SIP_VPTR_INIT(belle_sip_tls_channel_t,belle_sip_stream_channel_t,FALSE), (belle_sip_object_destroy_t)tls_channel_uninit, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, "TLS", 1, /*is_reliable*/ tls_channel_connect, tls_channel_send, tls_channel_recv, (void (*)(belle_sip_channel_t*))tls_channel_close } } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END static int belle_sip_client_certificate_request_callback(void *data, bctbx_ssl_context_t *ssl_ctx, unsigned char *dn, size_t dn_length) { belle_sip_tls_channel_t *channel = (belle_sip_tls_channel_t *)data; /* ask certificate */ BELLE_SIP_INVOKE_LISTENERS_ARG1_ARG2( channel->base.base.full_listeners ,belle_sip_channel_listener_t ,on_auth_requested ,&channel->base.base ,(char *)dn); /* if we got one, set it in the ssl handshake context */ if (channel->client_cert_chain && channel->client_cert_key) { int err; char tmp[512]={0}; bctbx_x509_certificate_get_info_string(tmp,sizeof(tmp)-1,"",channel->client_cert_chain->cert); belle_sip_message("Channel [%p] found client certificate:\n%s",channel,tmp); if ((err=bctbx_ssl_set_hs_own_cert(channel->sslctx,channel->client_cert_chain->cert,channel->client_cert_key->key))) { bctbx_strerror(err,tmp,sizeof(tmp)-1); belle_sip_error("Channel [%p] cannot set retrieved ssl own certificate [%s]",channel,tmp); return -1; /* we were not able to set the client certificate, something is going wrong, this will abort the handshake*/ } return 0; } belle_sip_warning("Channel [%p] cannot get client certificate to answer server request for dn [%s]", channel, (dn==NULL)?"null":(char *)dn); return 0; /* we couldn't find any certificate, just keep on going, server may decide to abort the handshake */ } static int tls_process_handshake(belle_sip_channel_t *obj){ belle_sip_tls_channel_t* channel=(belle_sip_tls_channel_t*)obj; int err=bctbx_ssl_handshake(channel->sslctx); if (err==0){ belle_sip_message("Channel [%p]: SSL handshake finished, SSL version is [%s], selected ciphersuite is [%s]",obj, bctbx_ssl_get_version(channel->sslctx), bctbx_ssl_get_ciphersuite(channel->sslctx)); belle_sip_source_set_timeout((belle_sip_source_t*)obj,-1); belle_sip_channel_set_ready(obj,(struct sockaddr*)&channel->ss,channel->socklen); }else if (err==BCTBX_ERROR_NET_WANT_READ || err==BCTBX_ERROR_NET_WANT_WRITE){ belle_sip_message("Channel [%p]: SSL handshake in progress...",obj); }else{ char tmp[128]; bctbx_strerror(err,tmp,sizeof(tmp)); belle_sip_error("Channel [%p]: SSL handshake failed : %s",obj,tmp); return -1; } return 0; } static int tls_process_http_connect(belle_sip_tls_channel_t *obj) { char* request; belle_sip_channel_t *channel = (belle_sip_channel_t *)obj; int err; char ip[64]; char *host_ip; char url_ipport[64]; int port; bctbx_addrinfo_to_printable_ip_address(channel->current_peer,url_ipport,sizeof(url_ipport)); bctbx_addrinfo_to_ip_address(channel->current_peer,ip,sizeof(ip),&port); if (channel->current_peer->ai_family == AF_INET6) { host_ip = belle_sip_strdup_printf("[%s]",ip); } else { host_ip = belle_sip_strdup_printf("%s",ip); /*just to simplify code*/ } request = belle_sip_strdup_printf("CONNECT %s HTTP/1.1\r\nProxy-Connection: keep-alive\r\nConnection: keep-alive\r\nHost: %s\r\nUser-Agent: Mozilla/5.0\r\n" ,url_ipport ,host_ip); belle_sip_free(host_ip); if (channel->stack->http_proxy_username && channel->stack->http_proxy_passwd) { char *username_passwd = belle_sip_strdup_printf("%s:%s",channel->stack->http_proxy_username,channel->stack->http_proxy_passwd); size_t username_passwd_length = strlen(username_passwd); size_t encoded_username_paswd_length = username_passwd_length*2; unsigned char *encoded_username_paswd = belle_sip_malloc(2*username_passwd_length); bctbx_base64_encode(encoded_username_paswd,&encoded_username_paswd_length,(const unsigned char *)username_passwd,username_passwd_length); request = belle_sip_strcat_printf(request, "Proxy-Authorization: Basic %s\r\n",encoded_username_paswd); belle_sip_free(username_passwd); belle_sip_free(encoded_username_paswd); } request = belle_sip_strcat_printf(request,"\r\n"); err = bctbx_send(belle_sip_source_get_socket((belle_sip_source_t*)obj),request,strlen(request),0); belle_sip_free(request); if (err <= 0) { belle_sip_error("tls_process_http_connect: fail to send connect request to http proxy [%s:%i] status [%s]" ,channel->stack->http_proxy_host ,channel->stack->http_proxy_port ,strerror(errno)); return -1; } return 0; } static int tls_process_data(belle_sip_channel_t *obj,unsigned int revents){ belle_sip_tls_channel_t* channel=(belle_sip_tls_channel_t*)obj; int err; if (obj->state == BELLE_SIP_CHANNEL_CONNECTING ) { if (!channel->socket_connected) { channel->socklen=sizeof(channel->ss); if (finalize_stream_connection((belle_sip_stream_channel_t*)obj,revents,(struct sockaddr*)&channel->ss,&channel->socklen)) { goto process_error; } channel->socket_connected=1; belle_sip_source_set_events((belle_sip_source_t*)channel,BELLE_SIP_EVENT_READ|BELLE_SIP_EVENT_ERROR); belle_sip_source_set_timeout((belle_sip_source_t*)obj,belle_sip_stack_get_transport_timeout(obj->stack)); if (obj->stack->http_proxy_host) { belle_sip_message("Channel [%p]: Connected at TCP level, now doing http proxy connect",obj); if (tls_process_http_connect(channel)) goto process_error; } else { belle_sip_message("Channel [%p]: Connected at TCP level, now doing TLS handshake with cname=%s",obj, obj->peer_cname ? obj->peer_cname : obj->peer_name); if (tls_process_handshake(obj)==-1) goto process_error; } } else if (obj->stack->http_proxy_host && !channel->http_proxy_connected) { char response[256]; err = stream_channel_recv((belle_sip_stream_channel_t*)obj,response,sizeof(response)); if (err<0 ){ belle_sip_error("Channel [%p]: connection refused by http proxy [%s:%i] status [%s]" ,channel ,obj->stack->http_proxy_host ,obj->stack->http_proxy_port ,strerror(errno)); goto process_error; } else if (strstr(response,"407")) { belle_sip_error("Channel [%p]: auth requested, provide user/passwd by http proxy [%s:%i]" ,channel ,obj->stack->http_proxy_host ,obj->stack->http_proxy_port); goto process_error; } else if (strstr(response,"200")) { belle_sip_message("Channel [%p]: connected to http proxy, doing TLS handshake [%s:%i] " ,channel ,obj->stack->http_proxy_host ,obj->stack->http_proxy_port); channel->http_proxy_connected = 1; if (tls_process_handshake(obj)==-1) goto process_error; } else { belle_sip_error("Channel [%p]: connection refused by http proxy [%s:%i]" ,channel ,obj->stack->http_proxy_host ,obj->stack->http_proxy_port); goto process_error; } } else { if (revents & BELLE_SIP_EVENT_READ){ if (tls_process_handshake(obj)==-1) goto process_error; }else if (revents==BELLE_SIP_EVENT_TIMEOUT){ belle_sip_error("channel [%p]: SSL handshake took too much time.",obj); goto process_error; }else{ belle_sip_warning("channel [%p]: unexpected event [%i] during TLS handshake.",obj,revents); } } } else if ( obj->state == BELLE_SIP_CHANNEL_READY) { return belle_sip_channel_process_data(obj,revents); } else { belle_sip_warning("Unexpected event [%i], for channel [%p]",revents,channel); return BELLE_SIP_STOP; } return BELLE_SIP_CONTINUE; process_error: belle_sip_error("Cannot connect to [%s://%s:%i]",belle_sip_channel_get_transport_name(obj),obj->peer_name,obj->peer_port); channel_set_state(obj,BELLE_SIP_CHANNEL_ERROR); return BELLE_SIP_STOP; } static int tls_callback_read(void * ctx, unsigned char *buf, size_t len ){ belle_sip_stream_channel_t *super=(belle_sip_stream_channel_t *)ctx; int ret = stream_channel_recv(super,buf,len); if (ret<0){ ret=-ret; if (ret==BELLESIP_EWOULDBLOCK || ret==BELLESIP_EINPROGRESS || ret == EINTR ) return BCTBX_ERROR_NET_WANT_READ; return BCTBX_ERROR_NET_CONN_RESET; } return ret; } static int tls_callback_write(void * ctx, const unsigned char *buf, size_t len ){ belle_sip_stream_channel_t *super=(belle_sip_stream_channel_t *)ctx; int ret = stream_channel_send(super, buf, len); if (ret<0){ ret=-ret; if (ret==BELLESIP_EWOULDBLOCK || ret==BELLESIP_EINPROGRESS || ret == EINTR ) return BCTBX_ERROR_NET_WANT_WRITE; return BCTBX_ERROR_NET_CONN_RESET; } return ret; } static int random_generator(void *ctx, unsigned char *ptr, size_t size){ belle_sip_random_bytes(ptr, size); return 0; } // shim the default certificate handling by adding an external callback // see "verify_cb_error_cb_t" for the function signature int belle_sip_tls_set_verify_error_cb(void * callback) { if (callback) { tls_verify_cb_error_cb = (verify_cb_error_cb_t)callback; belle_sip_message("belle_sip_tls_set_verify_error_cb: callback set"); } else { tls_verify_cb_error_cb = NULL; belle_sip_message("belle_sip_tls_set_verify_error_cb: callback cleared"); } return 0; } // // Augment certificate verification with certificates stored outside rootca.pem // PolarSSL calls the verify_cb with each cert in the chain; flags apply to the // current certificate until depth is 0; // // NOTES: // 1) rootca.pem *must* have at least one valid certificate, or PolarSSL // does not attempt to verify any certificates // 2) callback must return 0; non-zero indicates that the verification process failed // 3) flags should be saved off and cleared for each certificate where depth>0 // 4) return final verification result in *flags when depth == 0 // 5) callback must disable calls to linphone_core_iterate while running // int belle_sip_verify_cb_error_wrapper(bctbx_x509_certificate_t *cert, int depth, uint32_t *flags) { int rc = 0; unsigned char *der = NULL; int der_length = 0; // do nothing if the callback is not set if (!tls_verify_cb_error_cb) { return 0; } belle_sip_message("belle_sip_verify_cb_error_wrapper: depth=[%d], flags=[0x%x]:\n", depth, *flags); der_length = bctbx_x509_certificate_get_der_length(cert); der = belle_sip_malloc(der_length+1); // +1 for null termination char if (der == NULL) { // leave the flags alone and just return to the library belle_sip_error("belle_sip_verify_cb_error_wrapper: memory error\n"); return 0; } bctbx_x509_certificate_get_der(cert, der, der_length+1); rc = tls_verify_cb_error_cb(der, der_length, depth, flags); belle_sip_message("belle_sip_verify_cb_error_wrapper: callback return rc: %d, flags: 0x%x", rc, *flags); belle_sip_free(der); return rc; } static int belle_sip_ssl_verify(void *data , bctbx_x509_certificate_t *cert , int depth, uint32_t *flags){ belle_tls_crypto_config_t *crypto_config=(belle_tls_crypto_config_t*)data; const int tmp_size = 2048, flags_str_size = 256; char *tmp = belle_sip_malloc0(tmp_size); char *flags_str = belle_sip_malloc0(flags_str_size); int ret; bctbx_x509_certificate_get_info_string(tmp, tmp_size-1, "", cert); bctbx_x509_certificate_flags_to_string(flags_str, flags_str_size-1, *flags); belle_sip_message("Found certificate depth=[%i], flags=[%s]:\n%s", depth, flags_str, tmp); if (crypto_config->exception_flags==BELLE_TLS_VERIFY_ANY_REASON){ belle_sip_warning("Certificate verification bypassed (requested by application)."); /* verify context ask to ignore any exception: reset all flags */ bctbx_x509_certificate_unset_flag(flags, BCTBX_CERTIFICATE_VERIFY_ALL_FLAGS); }else if (crypto_config->exception_flags & BELLE_TLS_VERIFY_CN_MISMATCH){ /* verify context ask to ignore CN mismatch exception : reset this flag */ belle_sip_warning("Allowing CN-mistmatch exception."); bctbx_x509_certificate_unset_flag(flags, BCTBX_CERTIFICATE_VERIFY_BADCERT_CN_MISMATCH); } ret = belle_sip_verify_cb_error_wrapper(cert, depth, flags); belle_sip_free(flags_str); belle_sip_free(tmp); return ret; } static int belle_sip_tls_channel_load_root_ca(belle_sip_tls_channel_t *obj, const char *path){ struct stat statbuf; if (stat(path,&statbuf)==0){ int error; if (obj->root_ca) { bctbx_x509_certificate_free(obj->root_ca); } obj->root_ca = bctbx_x509_certificate_new(); if (statbuf.st_mode & S_IFDIR){ error = bctbx_x509_certificate_parse_path(obj->root_ca,path); }else{ error = bctbx_x509_certificate_parse_file(obj->root_ca,path); } if (error<0){ char errorstr[512]; bctbx_strerror(error, errorstr, sizeof(errorstr)); belle_sip_error("Failed to load root ca from %s: %s",path,errorstr); return -1; } else { return 0; } } belle_sip_error("Could not load root ca from %s: %s",path,strerror(errno)); return -1; } static int belle_sip_tls_channel_load_root_ca_from_buffer(belle_sip_tls_channel_t *obj, const char *data) { int err = 0; if (data != NULL) { if (obj->root_ca) { bctbx_x509_certificate_free(obj->root_ca); } obj->root_ca = bctbx_x509_certificate_new(); //certificate data must to contain in size the NULL character err = bctbx_x509_certificate_parse(obj->root_ca, data, strlen(data) + 1); if (err) { belle_sip_error("Failed to load root ca from string data: 0x%x", err); return -1; } belle_sip_message("Root ca loaded from string data"); return 0; } belle_sip_error("Could not load root ca from null string"); return -1; } static void belle_sip_tls_channel_deinit_bctbx_ssl(belle_sip_tls_channel_t *obj){ if (obj->sslctx) { bctbx_ssl_context_free(obj->sslctx); obj->sslctx = NULL; } if (obj->sslcfg) { bctbx_ssl_config_free(obj->sslcfg); obj->sslcfg = NULL; } if (obj->root_ca) { bctbx_x509_certificate_free(obj->root_ca); obj->root_ca = NULL; } } static int belle_sip_tls_channel_init_bctbx_ssl(belle_sip_tls_channel_t *obj){ belle_sip_stream_channel_t* super=(belle_sip_stream_channel_t*)obj; belle_tls_crypto_config_t *crypto_config = obj->crypto_config; /* create and initialise ssl context and configuration */ obj->sslctx = bctbx_ssl_context_new(); obj->sslcfg = bctbx_ssl_config_new(); if (crypto_config->ssl_config == NULL) { bctbx_ssl_config_defaults(obj->sslcfg, BCTBX_SSL_IS_CLIENT, BCTBX_SSL_TRANSPORT_STREAM); bctbx_ssl_config_set_authmode(obj->sslcfg, BCTBX_SSL_VERIFY_REQUIRED); } else { /* an SSL config is provided, use it*/ int ret = bctbx_ssl_config_set_crypto_library_config(obj->sslcfg, crypto_config->ssl_config); if (ret<0) { belle_sip_error("Unable to set external config for SSL context at TLS channel creation ret [-0x%x]", -ret); belle_sip_object_unref(obj); return -1; } belle_sip_message("Use externally provided SSL configuration when creating TLS channel [%p]", obj); } bctbx_ssl_config_set_rng(obj->sslcfg, random_generator, NULL); bctbx_ssl_set_io_callbacks(obj->sslctx, obj, tls_callback_write, tls_callback_read); if ((crypto_config->root_ca_data && belle_sip_tls_channel_load_root_ca_from_buffer(obj, crypto_config->root_ca_data) == 0) || (crypto_config->root_ca && belle_sip_tls_channel_load_root_ca(obj, crypto_config->root_ca) == 0)){ bctbx_ssl_config_set_ca_chain(obj->sslcfg, obj->root_ca); } bctbx_ssl_config_set_callback_verify(obj->sslcfg, belle_sip_ssl_verify, crypto_config); bctbx_ssl_config_set_callback_cli_cert(obj->sslcfg, belle_sip_client_certificate_request_callback, obj); bctbx_ssl_context_setup(obj->sslctx, obj->sslcfg); bctbx_ssl_set_hostname(obj->sslctx, super->base.peer_cname ? super->base.peer_cname : super->base.peer_name); return 0; } belle_sip_channel_t * belle_sip_channel_new_tls(belle_sip_stack_t *stack, belle_tls_crypto_config_t *crypto_config, const char *bindip, int localport, const char *peer_cname, const char *dest, int port) { belle_sip_tls_channel_t *obj=belle_sip_object_new(belle_sip_tls_channel_t); belle_sip_stream_channel_t* super=(belle_sip_stream_channel_t*)obj; belle_sip_stream_channel_init_client(super ,stack ,bindip,localport,peer_cname,dest,port); obj->crypto_config=(belle_tls_crypto_config_t*)belle_sip_object_ref(crypto_config); return (belle_sip_channel_t*)obj; } void belle_sip_tls_channel_set_client_certificates_chain(belle_sip_tls_channel_t *channel, belle_sip_certificates_chain_t* cert_chain) { SET_OBJECT_PROPERTY(channel,client_cert_chain,cert_chain); } void belle_sip_tls_channel_set_client_certificate_key(belle_sip_tls_channel_t *channel, belle_sip_signing_key_t* key){ SET_OBJECT_PROPERTY(channel,client_cert_key,key); } belle-sip-1.6.3/src/transports/tls_listeningpoint.c000066400000000000000000000076501313437522400225130ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" static void belle_sip_tls_listening_point_uninit(belle_sip_tls_listening_point_t *lp){ belle_sip_object_unref(lp->crypto_config); } static belle_sip_channel_t *tls_create_channel(belle_sip_listening_point_t *lp, const belle_sip_hop_t *hop){ belle_sip_channel_t *chan=belle_sip_channel_new_tls(lp->stack, ((belle_sip_tls_listening_point_t*) lp)->crypto_config ,belle_sip_uri_get_host(lp->listening_uri) ,belle_sip_uri_get_port(lp->listening_uri) ,hop->cname ,hop->host,hop->port); return chan; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_tls_listening_point_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_tls_listening_point_t) { { BELLE_SIP_VPTR_INIT(belle_sip_tls_listening_point_t, belle_sip_stream_listening_point_t,TRUE), (belle_sip_object_destroy_t)belle_sip_tls_listening_point_uninit, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, "TLS", tls_create_channel } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END static int on_new_connection(void *userdata, unsigned int revents){ belle_sip_socket_t child; struct sockaddr_storage addr; socklen_t slen=sizeof(addr); belle_sip_tls_listening_point_t *lp=(belle_sip_tls_listening_point_t*)userdata; belle_sip_stream_listening_point_t *super=(belle_sip_stream_listening_point_t*)lp; child=accept(super->server_sock,(struct sockaddr*)&addr,&slen); if (child==(belle_sip_socket_t)-1){ belle_sip_error("Listening point [%p] accept() failed on TLS server socket: %s",lp,belle_sip_get_socket_error_string()); belle_sip_stream_listening_point_destroy_server_socket(super); belle_sip_stream_listening_point_setup_server_socket(super,on_new_connection); return BELLE_SIP_STOP; } belle_sip_message("New connection arriving on TLS, not handled !"); belle_sip_close_socket(child); return BELLE_SIP_CONTINUE; } belle_sip_listening_point_t * belle_sip_tls_listening_point_new(belle_sip_stack_t *s, const char *ipaddress, int port){ belle_sip_tls_listening_point_t *lp=belle_sip_object_new(belle_sip_tls_listening_point_t); belle_sip_stream_listening_point_init((belle_sip_stream_listening_point_t*)lp,s,ipaddress,port,on_new_connection); lp->crypto_config=belle_tls_crypto_config_new(); return BELLE_SIP_LISTENING_POINT(lp); } int belle_sip_tls_listening_point_set_root_ca(belle_sip_tls_listening_point_t *lp, const char *path){ return belle_tls_crypto_config_set_root_ca(lp->crypto_config,path); } int belle_sip_tls_listening_point_set_verify_exceptions(belle_sip_tls_listening_point_t *lp, int flags){ belle_tls_crypto_config_set_verify_exceptions(lp->crypto_config,flags); return 0; } int belle_sip_tls_listening_point_set_verify_policy(belle_sip_tls_listening_point_t *s, belle_tls_verify_policy_t *pol){ SET_OBJECT_PROPERTY(s,crypto_config,(belle_tls_crypto_config_t *)pol); return 0; } int belle_sip_tls_listening_point_set_crypto_config(belle_sip_tls_listening_point_t *s, belle_tls_crypto_config_t *crypto_config){ SET_OBJECT_PROPERTY(s,crypto_config,crypto_config); return 0; } belle_tls_crypto_config_t *belle_sip_tls_listening_point_get_crypto_config(belle_sip_tls_listening_point_t *s){ return s->crypto_config; } int belle_sip_tls_listening_point_available(void){ return TRUE; } belle-sip-1.6.3/src/transports/tunnel_channel.c000066400000000000000000000141041313437522400215500ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010-2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #include "channel.h" #define TUNNEL_POLLING_DURATION 20 /* in ms */ bool_t tunnel_client_is_dual(void *tunnelClient); void * tunnel_client_create_socket(void *tunnelclient, int minLocalPort, int maxLocalPort); void * tunnel_client_create_send_only_socket(void *tunnelclient, int minLocalPort, int maxLocalPort); void * tunnel_client_create_recv_only_socket(void *tunnelclient, int minLocalPort, int maxLocalPort); void tunnel_client_close_socket(void *tunnelclient, void *tunnelsocket); void tunnel_client_close_one_dir_socket(void *tunnelclient, void *tunnelsocket); int tunnel_socket_has_data(void *tunnelsocket); int tunnel_socket_sendto(void *tunnelsocket, const void *buffer, size_t bufsize, const struct sockaddr *dest, socklen_t socklen); int tunnel_socket_recvfrom(void *tunnelsocket, void *buffer, size_t bufsize, struct sockaddr *src, socklen_t socklen); BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_tunnel_channel_t, belle_sip_channel_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END struct belle_sip_tunnel_channel { belle_sip_channel_t base; belle_sip_source_t *pollingtimer; void *tunnelclient; void *tunnelsocket; void *tunnelsocket2; }; typedef struct belle_sip_tunnel_channel belle_sip_tunnel_channel_t; static void createTunnelSockets(belle_sip_tunnel_channel_t *chan) { if (!chan->tunnelsocket) { if (tunnel_client_is_dual(chan->tunnelclient)) { chan->tunnelsocket = tunnel_client_create_recv_only_socket(chan->tunnelclient, 5060, 6060); chan->tunnelsocket2 = tunnel_client_create_send_only_socket(chan->tunnelclient, 5060, 6060); } else { chan->tunnelsocket = tunnel_client_create_socket(chan->tunnelclient, 5060, 6060); chan->tunnelsocket2 = NULL; } } } static int tunnel_channel_send(belle_sip_channel_t *obj, const void *buf, size_t buflen) { belle_sip_tunnel_channel_t *chan = (belle_sip_tunnel_channel_t *)obj; createTunnelSockets(chan); if (chan->tunnelsocket2) { return tunnel_socket_sendto(chan->tunnelsocket2, buf, buflen, obj->current_peer->ai_addr, obj->current_peer->ai_addrlen); } else if (chan->tunnelsocket) { return tunnel_socket_sendto(chan->tunnelsocket, buf, buflen, obj->current_peer->ai_addr, obj->current_peer->ai_addrlen); } return 0; } static int tunnel_channel_recv(belle_sip_channel_t *obj, void *buf, size_t buflen) { belle_sip_tunnel_channel_t *chan = (belle_sip_tunnel_channel_t *)obj; struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); createTunnelSockets(chan); if (chan->tunnelsocket) { return tunnel_socket_recvfrom(chan->tunnelsocket, buf, buflen, (struct sockaddr *)&addr, addrlen); } return 0; } static int tunnel_channel_connect(belle_sip_channel_t *obj, const struct addrinfo *ai) { struct sockaddr_storage laddr; socklen_t lslen = sizeof(laddr); if (obj->local_ip == NULL) { belle_sip_get_src_addr_for(ai->ai_addr, ai->ai_addrlen, (struct sockaddr *)&laddr, &lslen, obj->local_port); } belle_sip_channel_set_ready(obj, (struct sockaddr *)&laddr, lslen); return 0; } static void tunnel_channel_close(belle_sip_channel_t *obj) { belle_sip_tunnel_channel_t *chan = (belle_sip_tunnel_channel_t *)obj; if (chan->tunnelsocket2 != NULL) { tunnel_client_close_one_dir_socket(chan->tunnelclient, chan->tunnelsocket); chan->tunnelsocket2 = NULL; if (chan->tunnelsocket != NULL) { tunnel_client_close_one_dir_socket(chan->tunnelclient, chan->tunnelsocket); chan->tunnelsocket = NULL; } } else { if (chan->tunnelsocket != NULL) { tunnel_client_close_socket(chan->tunnelclient, chan->tunnelsocket); chan->tunnelsocket = NULL; } } } static void tunnel_channel_uninit(belle_sip_channel_t *obj) { belle_sip_tunnel_channel_t *chan = (belle_sip_tunnel_channel_t *)obj; if (chan->tunnelsocket != NULL) { tunnel_channel_close(obj); } if (chan->pollingtimer != NULL) { belle_sip_main_loop_remove_source(obj->stack->ml, chan->pollingtimer); belle_sip_object_unref(chan->pollingtimer); chan->pollingtimer = NULL; } } static int tunnel_polling_timer(belle_sip_tunnel_channel_t *chan) { if ((chan->tunnelsocket != NULL) && tunnel_socket_has_data(chan->tunnelsocket)) { belle_sip_channel_process_data((belle_sip_channel_t *)chan, BELLE_SIP_EVENT_READ); } return BELLE_SIP_CONTINUE; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_tunnel_channel_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_tunnel_channel_t) { { BELLE_SIP_VPTR_INIT(belle_sip_tunnel_channel_t,belle_sip_channel_t,FALSE), (belle_sip_object_destroy_t)tunnel_channel_uninit, NULL, NULL }, "UDP", 0, /*is_reliable*/ tunnel_channel_connect, tunnel_channel_send, tunnel_channel_recv, tunnel_channel_close } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END belle_sip_channel_t * belle_sip_channel_new_tunnel(belle_sip_stack_t *stack, void *tunnelclient, const char *bindip, int localport, const char *dest, int port){ belle_sip_tunnel_channel_t *obj = belle_sip_object_new(belle_sip_tunnel_channel_t); belle_sip_channel_init((belle_sip_channel_t*)obj, stack, bindip, localport, NULL, dest, port); obj->tunnelclient = tunnelclient; createTunnelSockets(obj); obj->pollingtimer = belle_sip_timeout_source_new((belle_sip_source_func_t)tunnel_polling_timer, obj, TUNNEL_POLLING_DURATION); belle_sip_object_set_name((belle_sip_object_t*)obj->pollingtimer, "tunnel_polling_timer"); belle_sip_main_loop_add_source(stack->ml, obj->pollingtimer); return (belle_sip_channel_t*)obj; } belle-sip-1.6.3/src/transports/tunnel_listeningpoint.c000066400000000000000000000042311313437522400232060ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010-2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" struct belle_sip_tunnel_listening_point{ belle_sip_listening_point_t base; void *tunnelclient; }; static belle_sip_channel_t *tunnel_create_channel(belle_sip_listening_point_t *lp, const belle_sip_hop_t *hop){ belle_sip_channel_t *chan=belle_sip_channel_new_tunnel(lp->stack, ((belle_sip_tunnel_listening_point_t*)lp)->tunnelclient, belle_sip_uri_get_host(lp->listening_uri), belle_sip_uri_get_port(lp->listening_uri), hop->host, hop->port); return chan; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_tunnel_listening_point_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_tunnel_listening_point_t) { { BELLE_SIP_VPTR_INIT(belle_sip_tunnel_listening_point_t, belle_sip_listening_point_t,TRUE), NULL, NULL, NULL }, "UDP", tunnel_create_channel } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END static void belle_sip_tunnel_listening_point_init(belle_sip_tunnel_listening_point_t *lp, belle_sip_stack_t *s, void *tunnelclient) { belle_sip_listening_point_init((belle_sip_listening_point_t*)lp,s,"0.0.0.0",5060); lp->tunnelclient = tunnelclient; } belle_sip_listening_point_t * belle_sip_tunnel_listening_point_new(belle_sip_stack_t *s, void *tunnelclient){ belle_sip_tunnel_listening_point_t *lp=belle_sip_object_new(belle_sip_tunnel_listening_point_t); belle_sip_tunnel_listening_point_init(lp,s,tunnelclient); return (belle_sip_listening_point_t*)lp; } belle-sip-1.6.3/src/transports/tunnel_wrapper.cc000066400000000000000000000056251313437522400217730ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010-2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include using namespace belledonnecomm; extern "C" int tunnel_client_is_dual(void *tunnelClient) { TunnelClientI *tc = static_cast(tunnelClient); DualTunnelClient * dtc = dynamic_cast(tc); return dtc != NULL; } extern "C" void * tunnel_client_create_socket(void *tunnelclient, int minLocalPort, int maxLocalPort) { TunnelClient *tc = static_cast(tunnelclient); return tc->createSocket(minLocalPort, maxLocalPort); } extern "C" void * tunnel_client_create_send_only_socket(void *tunnelclient, int minLocalPort, int maxLocalPort) { DualTunnelClient *tc = static_cast(tunnelclient); return tc->createSocket(TunnelSendOnly, minLocalPort, maxLocalPort); } extern "C" void * tunnel_client_create_recv_only_socket(void *tunnelclient, int minLocalPort, int maxLocalPort) { DualTunnelClient *tc = static_cast(tunnelclient); return tc->createSocket(TunnelRecvOnly, minLocalPort, maxLocalPort); } extern "C" void tunnel_client_close_socket(void *tunnelclient, void *tunnelsocket) { TunnelClient *tc = static_cast(tunnelclient); TunnelSocket *ts = static_cast(tunnelsocket); tc->closeSocket(ts); } extern "C" void tunnel_client_close_one_dir_socket(void *tunnelclient, void *tunnelsocket) { DualTunnelClient *tc = static_cast(tunnelclient); TunnelSocket *ts = static_cast(tunnelsocket); tc->closeSocket(ts); } extern "C" int tunnel_socket_has_data(void *tunnelsocket) { TunnelSocket *ts = static_cast(tunnelsocket); return ts->hasData(); } extern "C" int tunnel_socket_sendto(void *tunnelsocket, const void *buffer, size_t bufsize, const struct sockaddr *dest, socklen_t socklen) { TunnelSocket *ts = static_cast(tunnelsocket); return ts->sendto(buffer, bufsize, dest, socklen); } extern "C" int tunnel_socket_recvfrom(void *tunnelsocket, void *buffer, size_t bufsize, struct sockaddr *src, socklen_t socklen) { TunnelSocket *ts = static_cast(tunnelsocket); return ts->recvfrom(buffer, bufsize, src, socklen); } belle-sip-1.6.3/src/transports/udp_channel.c000066400000000000000000000112421313437522400210330ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" #include "channel.h" BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_udp_channel_t,belle_sip_channel_t) BELLE_SIP_DECLARE_CUSTOM_VPTR_END struct belle_sip_udp_channel{ belle_sip_channel_t base; belle_sip_socket_t shared_socket; /*the socket that belongs to the listening point. It is stored here because the channel parent class may erase its value in the belle_sip_source_t base class*/ }; typedef struct belle_sip_udp_channel belle_sip_udp_channel_t; static void udp_channel_uninit(belle_sip_udp_channel_t *obj){ /*no close of the socket, because it is owned by the listerning point and shared between all channels*/ } static int udp_channel_send(belle_sip_channel_t *obj, const void *buf, size_t buflen){ belle_sip_udp_channel_t *chan=(belle_sip_udp_channel_t *)obj; int err; belle_sip_socket_t sock=belle_sip_source_get_socket((belle_sip_source_t*)chan); err=(int)bctbx_sendto(sock,buf,buflen,0,obj->current_peer->ai_addr,(socklen_t)obj->current_peer->ai_addrlen); if (err==-1){ belle_sip_error("channel [%p]: could not send UDP packet because [%s]",obj,belle_sip_get_socket_error_string()); return -errno; } return err; } static int udp_channel_recv(belle_sip_channel_t *obj, void *buf, size_t buflen){ belle_sip_udp_channel_t *chan=(belle_sip_udp_channel_t *)obj; int err; int errnum; struct sockaddr_storage addr; socklen_t addrlen=sizeof(addr); belle_sip_socket_t sock=belle_sip_source_get_socket((belle_sip_source_t*)chan); err=(int)bctbx_recvfrom(sock,buf,buflen,0,(struct sockaddr*)&addr,&addrlen); errnum = get_socket_error(); if (err==-1 && errnum!=BELLESIP_EWOULDBLOCK){ if (errnum == BCTBX_ENOTCONN) { //Do NOT treat it as an error belle_sip_message("Socket is not connected because of IOS10 background policy"); obj->closed_by_remote = TRUE; return 0; } belle_sip_error("Could not receive UDP packet: %s",belle_sip_get_socket_error_string_from_code(errnum)); return -errnum; } return err; } int udp_channel_connect(belle_sip_channel_t *obj, const struct addrinfo *ai){ belle_sip_udp_channel_t *chan=(belle_sip_udp_channel_t *)obj; struct sockaddr_storage laddr={0}; socklen_t lslen=sizeof(laddr); if (obj->local_ip==NULL){ int err = belle_sip_get_src_addr_for(ai->ai_addr,(socklen_t)ai->ai_addrlen,(struct sockaddr*)&laddr,&lslen,obj->local_port); if (err == -BCTBX_ENETUNREACH || err == -BCTBX_EHOSTUNREACH){ return -1; } } belle_sip_channel_set_socket(obj, chan->shared_socket, NULL); belle_sip_channel_set_ready(obj, (struct sockaddr*)&laddr, lslen); return 0; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_udp_channel_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_udp_channel_t) { { BELLE_SIP_VPTR_INIT(belle_sip_udp_channel_t,belle_sip_channel_t,FALSE), (belle_sip_object_destroy_t)udp_channel_uninit, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, "UDP", 0, /*is_reliable*/ udp_channel_connect, udp_channel_send, udp_channel_recv, NULL /*no close method*/ } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END belle_sip_channel_t * belle_sip_channel_new_udp(belle_sip_stack_t *stack, int sock, const char *bindip, int localport, const char *dest, int port){ belle_sip_udp_channel_t *obj=belle_sip_object_new(belle_sip_udp_channel_t); belle_sip_channel_init((belle_sip_channel_t*)obj,stack,bindip,localport,NULL,dest,port); obj->shared_socket = sock; return (belle_sip_channel_t*)obj; } belle_sip_channel_t * belle_sip_channel_new_udp_with_addr(belle_sip_stack_t *stack, int sock, const char *bindip, int localport, const struct addrinfo *peer){ belle_sip_udp_channel_t *obj=belle_sip_object_new(belle_sip_udp_channel_t); belle_sip_channel_init_with_addr((belle_sip_channel_t*)obj, stack, bindip, localport, peer->ai_addr, (socklen_t)peer->ai_addrlen); obj->base.local_port=localport; obj->shared_socket = sock; /*this lookups the local address*/ udp_channel_connect((belle_sip_channel_t*)obj,peer); return (belle_sip_channel_t*)obj; } belle-sip-1.6.3/src/transports/udp_listeningpoint.c000066400000000000000000000173571313437522400225060ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle_sip_internal.h" struct belle_sip_udp_listening_point{ belle_sip_listening_point_t base; belle_sip_socket_t sock; belle_sip_source_t *source; }; static void belle_sip_udp_listening_point_uninit(belle_sip_udp_listening_point_t *lp){ if (lp->source) { belle_sip_main_loop_remove_source(lp->base.stack->ml,lp->source); belle_sip_object_unref(lp->source); lp->source = NULL; } if (lp->sock!=-1) belle_sip_close_socket(lp->sock); } static belle_sip_channel_t *udp_create_channel(belle_sip_listening_point_t *lp, const belle_sip_hop_t *hop){ belle_sip_channel_t *chan=belle_sip_channel_new_udp(lp->stack ,(int)((belle_sip_udp_listening_point_t*)lp)->sock ,belle_sip_uri_get_host(lp->listening_uri) ,belle_sip_uri_get_port(lp->listening_uri) ,hop->host ,hop->port); return chan; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_udp_listening_point_t); BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_BEGIN(belle_sip_udp_listening_point_t) { { BELLE_SIP_VPTR_INIT(belle_sip_udp_listening_point_t, belle_sip_listening_point_t,TRUE), (belle_sip_object_destroy_t)belle_sip_udp_listening_point_uninit, NULL, NULL, BELLE_SIP_DEFAULT_BUFSIZE_HINT }, "UDP", udp_create_channel } BELLE_SIP_INSTANCIATE_CUSTOM_VPTR_END static belle_sip_socket_t create_udp_socket(const char *addr, int *port, int *family){ struct addrinfo hints={0}; struct addrinfo *res=NULL; int err; belle_sip_socket_t sock; char portnum[10]; int optval=1; if (*port==-1) *port=0; /*random port for bind()*/ belle_sip_set_socket_api(NULL); snprintf(portnum,sizeof(portnum),"%i",*port); hints.ai_family=AF_UNSPEC; hints.ai_socktype=SOCK_DGRAM; hints.ai_protocol=IPPROTO_UDP; hints.ai_flags=AI_NUMERICSERV; err=getaddrinfo(addr,portnum,&hints,&res); if (err!=0){ belle_sip_error("getaddrinfo() failed for %s port %i: %s",addr,*port,gai_strerror(err)); return -1; } *family=res->ai_family; sock=bctbx_socket(res->ai_family,res->ai_socktype,res->ai_protocol); if (sock==-1){ belle_sip_error("Cannot create UDP socket: %s",belle_sip_get_socket_error_string()); freeaddrinfo(res); return -1; } err = bctbx_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof (optval)); if (err == -1){ belle_sip_warning ("Fail to set SIP/UDP address reusable: %s.", belle_sip_get_socket_error_string()); } if (res->ai_family==AF_INET6){ belle_sip_socket_enable_dual_stack(sock); } err=bctbx_bind(sock,res->ai_addr,(socklen_t)res->ai_addrlen); if (err==-1){ belle_sip_error("udp bind() failed for %s port %i: %s",addr,*port,belle_sip_get_socket_error_string()); belle_sip_close_socket(sock); freeaddrinfo(res); return -1; } freeaddrinfo(res); if (*port==0){ struct sockaddr_storage saddr; socklen_t saddr_len=sizeof(saddr); err=bctbx_getsockname(sock,(struct sockaddr*)&saddr,&saddr_len); if (err==0){ err=bctbx_getnameinfo((struct sockaddr*)&saddr,saddr_len,NULL,0,portnum,sizeof(portnum),NI_NUMERICSERV|NI_NUMERICHOST); if (err==0){ *port=atoi(portnum); belle_sip_message("Random UDP port is %i",*port); }else belle_sip_error("udp bind failed, getnameinfo(): %s",gai_strerror(err)); }else belle_sip_error("udp bind failed, bctbx_getsockname(): %s",belle_sip_get_socket_error_string()); } return sock; } static int on_udp_data(belle_sip_udp_listening_point_t *lp, unsigned int events); static int belle_sip_udp_listening_point_init_socket(belle_sip_udp_listening_point_t *lp){ int port=belle_sip_uri_get_listening_port(((belle_sip_listening_point_t*)lp)->listening_uri); lp->sock=create_udp_socket(belle_sip_uri_get_host(((belle_sip_listening_point_t*)lp)->listening_uri) ,&port,&lp->base.ai_family); if (lp->sock==(belle_sip_socket_t)-1){ return -1; } belle_sip_uri_set_port(((belle_sip_listening_point_t*)lp)->listening_uri,port); if (lp->base.stack->dscp) belle_sip_socket_set_dscp(lp->sock,lp->base.ai_family,lp->base.stack->dscp); lp->source=belle_sip_socket_source_new((belle_sip_source_func_t)on_udp_data,lp,lp->sock,BELLE_SIP_EVENT_READ,-1); belle_sip_main_loop_add_source(((belle_sip_listening_point_t*)lp)->stack->ml,lp->source); return 0; } static void belle_sip_udp_listening_point_init(belle_sip_udp_listening_point_t *lp, belle_sip_stack_t *s, const char *ipaddress, int port) { belle_sip_listening_point_init((belle_sip_listening_point_t*)lp,s,ipaddress,port); belle_sip_udp_listening_point_init_socket(lp); } /*peek data from the master socket to see where it comes from, and dispatch to matching channel. * If the channel does not exist, create it */ static int on_udp_data(belle_sip_udp_listening_point_t *lp, unsigned int events){ int err; unsigned char buf[4096]; struct sockaddr_storage addr; socklen_t addrlen=sizeof(addr); if (events & BELLE_SIP_EVENT_READ){ belle_sip_debug("udp_listening_point: data to read."); err=recvfrom(lp->sock,(char*)buf,sizeof(buf),MSG_PEEK,(struct sockaddr*)&addr,&addrlen); if (err==-1){ char *tmp=belle_sip_object_to_string((belle_sip_object_t*) ((belle_sip_listening_point_t*)lp)->listening_uri); belle_sip_error("udp_listening_point: recvfrom() failed on [%s], : [%s] reopening server socket" ,tmp ,belle_sip_get_socket_error_string()); belle_sip_free(tmp); belle_sip_udp_listening_point_uninit(lp); /*clean all udp channels that are actually sharing the server socket with the listening points*/ belle_sip_listening_point_clean_channels((belle_sip_listening_point_t*)lp); belle_sip_udp_listening_point_init_socket(lp); }else{ belle_sip_channel_t *chan; struct addrinfo ai={0}; /*preserve the V4 mapping*/ ai.ai_family=addr.ss_family; ai.ai_addr=(struct sockaddr*)&addr; ai.ai_addrlen=addrlen; chan=_belle_sip_listening_point_get_channel((belle_sip_listening_point_t*)lp,NULL,&ai); if (chan==NULL){ /*TODO: should rather create the channel with real local ip and port and not just 0.0.0.0"*/ chan=belle_sip_channel_new_udp_with_addr(lp->base.stack ,(int)lp->sock ,belle_sip_uri_get_host(lp->base.listening_uri) ,belle_sip_uri_get_port(lp->base.listening_uri) ,&ai); if (chan!=NULL){ belle_sip_message("udp_listening_point: new channel created to %s:%i",chan->peer_name,chan->peer_port); belle_sip_listening_point_add_channel((belle_sip_listening_point_t*)lp,chan); } } if (chan){ /*notify the channel*/ belle_sip_debug("Notifying udp channel, local [%s:%i] remote [%s:%i]" ,chan->local_ip ,chan->local_port ,chan->peer_name ,chan->peer_port); belle_sip_channel_process_data(chan,events); } } } return BELLE_SIP_CONTINUE; } belle_sip_listening_point_t * belle_sip_udp_listening_point_new(belle_sip_stack_t *s, const char *ipaddress, int port){ belle_sip_udp_listening_point_t *lp=belle_sip_object_new(belle_sip_udp_listening_point_t); belle_sip_udp_listening_point_init(lp,s,ipaddress, port); if (lp->sock==(belle_sip_socket_t)-1){ belle_sip_object_unref(lp); return NULL; } return (belle_sip_listening_point_t*)lp; } belle-sip-1.6.3/src/wakelock.c000066400000000000000000000113441313437522400161370ustar00rootroot00000000000000#include "wakelock_internal.h" #include "belle-sip/utils.h" #include struct _WakeLock { JavaVM *jvm; jobject powerManager; pthread_key_t jniEnvKey; jint PARTIAL_WAKE_LOCK; jmethodID newWakeLockID; jmethodID acquireID; jmethodID releaseID; }; typedef struct _WakeLock WakeLock; static WakeLock ctx = { .jvm = NULL, .powerManager = NULL }; static JNIEnv *get_jni_env(void); static void jni_key_cleanup(void *data); void belle_sip_wake_lock_init(JNIEnv *env, jobject pm) { if (ctx.jvm == NULL) { jclass powerManagerClass; jclass wakeLockClass; jfieldID fieldID; (*env)->GetJavaVM(env, &ctx.jvm); pthread_key_create(&ctx.jniEnvKey, jni_key_cleanup); powerManagerClass = (*env)->FindClass(env, "android/os/PowerManager"); wakeLockClass = (*env)->FindClass(env, "android/os/PowerManager$WakeLock"); fieldID = (*env)->GetStaticFieldID(env, powerManagerClass, "PARTIAL_WAKE_LOCK", "I"); ctx.PARTIAL_WAKE_LOCK = (*env)->GetStaticIntField(env, powerManagerClass, fieldID); ctx.newWakeLockID = (*env)->GetMethodID(env, powerManagerClass, "newWakeLock", "(ILjava/lang/String;)Landroid/os/PowerManager$WakeLock;"); ctx.acquireID = (*env)->GetMethodID(env, wakeLockClass, "acquire", "()V"); ctx.releaseID = (*env)->GetMethodID(env, wakeLockClass, "release", "()V"); belle_sip_message("bellesip_wake_lock_init(): initialization succeed"); } else { belle_sip_warning("bellesip_wake_lock_init(): the wakelock system has already been initialized"); } if (ctx.powerManager == NULL) { ctx.powerManager = (*env)->NewGlobalRef(env, pm); } } void belle_sip_wake_lock_uninit(JNIEnv *env) { ctx.jvm = NULL; if(ctx.powerManager != NULL) { (*env)->DeleteGlobalRef(env, ctx.powerManager); ctx.powerManager = NULL; } } /** * @brief Callback called when a thread terminates it-self. * It detaches the thread from the JVM. * @param data Unused. */ static void jni_key_cleanup(void *data) { JNIEnv *env = (JNIEnv*) data; belle_sip_message("Thread end. Cleanup wake lock jni environment"); if (env != NULL) { if (ctx.jvm != NULL) { (*ctx.jvm)->DetachCurrentThread(ctx.jvm); pthread_setspecific(ctx.jniEnvKey, NULL); } else { belle_sip_error("Wake lock cleanup. No JVM found"); } } } /** * @brief Get a JNI environment. * Get a JNI by attaching the current thread * to the internaly stored JVM. That function can be safely * called several times. * @return JNI environement pointer. NULL if the function fails. */ static JNIEnv *get_jni_env(void) { JNIEnv *jenv = NULL; if(ctx.jvm != NULL) { jenv = pthread_getspecific(ctx.jniEnvKey); if(jenv == NULL) { if((*ctx.jvm)->AttachCurrentThread(ctx.jvm, &jenv, NULL) == JNI_OK) { pthread_setspecific(ctx.jniEnvKey, jenv); belle_sip_message("get_jni_env(): thread successfuly attached"); } else { belle_sip_fatal("get_jni_env(): thread could not be attached to the JVM"); jenv = NULL; } } } else { belle_sip_error("get_jni_env(): no JVM found"); } return jenv; } unsigned long wake_lock_acquire(const char *tag) { if(ctx.jvm != NULL && ctx.powerManager != NULL) { JNIEnv *env; if((env = get_jni_env())) { jstring tagString = (*env)->NewStringUTF(env, tag); jobject lock = (*env)->CallObjectMethod(env, ctx.powerManager, ctx.newWakeLockID, ctx.PARTIAL_WAKE_LOCK, tagString); (*env)->DeleteLocalRef(env, tagString); if(lock != NULL) { (*env)->CallVoidMethod(env, lock, ctx.acquireID); lock = (*env)->NewGlobalRef(env, lock); belle_sip_message("bellesip_wake_lock_acquire(): Android wake lock acquired [ref=%p]", (void *)lock); return (unsigned long)lock; } else { belle_sip_message("bellesip_wake_lock_acquire(): wake lock creation failed"); } } else { belle_sip_error("bellesip_wake_lock_acquire(): cannot attach current thread to the JVM"); } } else { if (ctx.jvm == NULL) belle_sip_error("bellesip_wake_lock_acquire(): cannot acquire wake lock. No JVM found"); else belle_sip_error("bellesip_wake_lock_acquire(): cannot acquire wake lock. No PowerManager found"); } return 0; } void wake_lock_release(unsigned long id) { if(ctx.jvm != NULL && ctx.powerManager != NULL) { if(id != 0) { jobject lock = (jobject)id; JNIEnv *env; if((env = get_jni_env())) { (*env)->CallVoidMethod(env, lock, ctx.releaseID); belle_sip_message("bellesip_wake_lock_release(): Android wake lock released [ref=%p]", (void *)lock); (*env)->DeleteGlobalRef(env, lock); } else { belle_sip_error("bellesip_wake_lock_release(): cannot attach current thread to the JVM"); } } } else { if(ctx.jvm == NULL) belle_sip_error("bellesip_wake_lock_release(): cannot release wake lock. No JVM found"); else belle_sip_error("bellesip_wake_lock_release(): cannot release wake lock. No PowerManager found"); } } belle-sip-1.6.3/src/wakelock_internal.h000066400000000000000000000011521313437522400200340ustar00rootroot00000000000000#include "belle-sip/wakelock.h" /** * @brief Acquire a wake lock. * Ask to Android to prevent the system sleeping. That function * do nothing if the wake lock system has not been initialized * by the function bellesip_wake_lock_init(). * @param tag * @return An ID that anthentificates the taken wake lock. */ unsigned long wake_lock_acquire(const char *tag); /** * @brief Release a wake lock. * Ask to Android to release a prevously aquired * wake lock. After calling this function, the system * can sleep again. * @param id ID of the wake lock to release. */ void wake_lock_release(unsigned long id); belle-sip-1.6.3/tester/000077500000000000000000000000001313437522400147075ustar00rootroot00000000000000belle-sip-1.6.3/tester/CMakeLists.txt000066400000000000000000000123371313437522400174550ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ if(ENABLE_STATIC) set(PROJECT_LIBS bellesip-static) else() set(PROJECT_LIBS bellesip) endif() set(SOURCE_FILES_C auth_helper_tester.c belle_generic_uri_tester.c belle_http_tester.c belle_sdp_tester.c belle_sip_core_tester.c belle_sip_dialog_tester.c belle_sip_headers_tester.c belle_sip_message_tester.c belle_sip_refresher_tester.c belle_sip_register_tester.c belle_sip_resolver_tester.c belle_sip_tester.c belle_sip_tester.h belle_sip_uri_tester.c belle_sip_fast_uri_tester.c cast_test.c register_tester.h ) set(SOURCE_FILES_OBJC ) if (IOS) list(APPEND SOURCE_FILES_OBJC belle_sip_tester_ios.m) endif() string(REPLACE ";" " " LINK_FLAGS_STR "${LINK_FLAGS}") bc_apply_compile_flags(SOURCE_FILES_C STRICT_OPTIONS_CPP STRICT_OPTIONS_C) bc_apply_compile_flags(SOURCE_FILES_OBJC STRICT_OPTIONS_CPP STRICT_OPTIONS_OBJC) if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") add_library(belle_sip_tester_static STATIC ${SOURCE_FILES_C}) target_include_directories(belle_sip_tester_static PUBLIC ${BCTOOLBOX_TESTER_INCLUDE_DIRS}) target_link_libraries(belle_sip_tester_static ${BCTOOLBOX_LIBRARIES} ${PROJECT_LIBS}) set(RUNTIME_COMPONENT_SOURCES belle_sip_tester_windows.cpp belle_sip_tester_windows.h ) add_library(belle_sip_tester_runtime MODULE ${RUNTIME_COMPONENT_SOURCES}) target_link_libraries(belle_sip_tester_runtime belle_sip_tester_static) set_target_properties(belle_sip_tester_runtime PROPERTIES VS_WINRT_COMPONENT TRUE) set_target_properties(belle_sip_tester_runtime PROPERTIES LIBRARY_OUTPUT_NAME "BelledonneCommunications.BelleSip.Tester") set_target_properties(belle_sip_tester_runtime PROPERTIES LINK_FLAGS "/WINMDFILE:BelledonneCommunications.BelleSip.Tester.winmd") install(TARGETS belle_sip_tester_runtime RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/BelledonneCommunications.BelleSip.Tester.winmd" DESTINATION ${CMAKE_INSTALL_LIBDIR}) if(CMAKE_BUILD_TYPE STREQUAL "Debug") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Debug/BelledonneCommunications.BelleSip.Tester.pdb DESTINATION ${CMAKE_INSTALL_LIBDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() else() set(USE_BUNDLE ) if (IOS) set(USE_BUNDLE MACOSX_BUNDLE) endif() add_executable(belle_sip_tester ${USE_BUNDLE} ${SOURCE_FILES_C} ${SOURCE_FILES_OBJC}) if(NOT "${LINK_FLAGS_STR}" STREQUAL "") set_target_properties(belle_sip_tester PROPERTIES LINK_FLAGS "${LINK_FLAGS_STR}") endif() if(WIN32) target_link_libraries(belle_sip_tester "Ws2_32") endif() target_include_directories(belle_sip_tester PUBLIC ${BCTOOLBOX_TESTER_INCLUDE_DIRS}) target_link_libraries(belle_sip_tester ${BCTOOLBOX_LIBRARIES} ${PROJECT_LIBS}) add_test(NAME belle_sip_tester COMMAND belle_sip_tester --verbose) if(NOT IOS) install(TARGETS belle_sip_tester RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() set(OBJECT_DESCRIBE_SOURCES describe.c) bc_apply_compile_flags(OBJECT_DESCRIBE_SOURCES STRICT_OPTIONS_CPP STRICT_OPTIONS_C) add_executable(belle_sip_object_describe ${USE_BUNDLE} ${OBJECT_DESCRIBE_SOURCES}) set_target_properties(belle_sip_object_describe PROPERTIES LINKER_LANGUAGE CXX) if(NOT "${LINK_FLAGS_STR}" STREQUAL "") set_target_properties(belle_sip_object_describe PROPERTIES LINK_FLAGS "${LINK_FLAGS_STR}") endif() target_link_libraries(belle_sip_object_describe ${BCTOOLBOX_CORE_LIBRARIES} ${PROJECT_LIBS}) set(PARSE_SOURCES parse.c) bc_apply_compile_flags(PARSE_SOURCES STRICT_OPTIONS_CPP STRICT_OPTIONS_C) add_executable(belle_sip_parse ${USE_BUNDLE} ${PARSE_SOURCES}) set_target_properties(belle_sip_parse PROPERTIES LINKER_LANGUAGE CXX) if(NOT "${LINK_FLAGS_STR}" STREQUAL "") set_target_properties(belle_sip_parse PROPERTIES LINK_FLAGS "${LINK_FLAGS_STR}") endif() target_link_libraries(belle_sip_parse ${BCTOOLBOX_CORE_LIBRARIES} ${PROJECT_LIBS}) endif() belle-sip-1.6.3/tester/Makefile.am000066400000000000000000000027501313437522400167470ustar00rootroot00000000000000 if ENABLE_TESTS noinst_PROGRAMS=belle_sip_tester belle_sip_object_describe belle_sip_parse belle_http_get belle_sip_resolve EXTRA_DIST= belle_sip_base_uri_tester.c belle_sip_tester_SOURCES= \ auth_helper_tester.c \ belle_generic_uri_tester.c \ belle_http_tester.c \ belle_sdp_tester.c \ belle_sip_core_tester.c \ belle_sip_dialog_tester.c \ belle_sip_headers_tester.c \ belle_sip_message_tester.c \ belle_sip_refresher_tester.c \ belle_sip_register_tester.c \ belle_sip_resolver_tester.c \ belle_sip_tester.c belle_sip_tester.h\ belle_sip_uri_tester.c \ belle_sip_fast_uri_tester.c \ cast_test.c \ register_tester.h belle_sip_tester_CFLAGS=\ -DBC_CONFIG_FILE=\"config.h\" \ $(BCTOOLBOXTESTER_CFLAGS) \ $(STRICT_OPTIONS) \ $(STRICT_OPTIONS_CC) \ $(TLS_CFLAGS) belle_sip_tester_LDADD=$(top_builddir)/src/libbellesip.la $(TLS_LIBS) $(BCTOOLBOXTESTER_LIBS) belle_sip_object_describe_SOURCES=describe.c belle_sip_parse_SOURCES=parse.c belle_http_get_SOURCES=get.c belle_sip_resolve_SOURCES=resolve.c belle_sip_resolve_CFLAGS=$(TLS_CFLAGS) AM_CPPFLAGS=-I$(top_srcdir)/include -I$(top_srcdir)/src LDADD=$(top_builddir)/src/libbellesip.la $(TLS_LIBS) AM_LDFLAGS=-no-undefined -export-dynamic #AM_LDFLAGS+=-Wl,-rpath -Wl,$(libdir) AM_CFLAGS=$(STRICT_OPTIONS) $(STRICT_OPTIONS_CC) test: belle_sip_tester ./belle_sip_tester $(TEST_OPTIONS) else test: @echo "bctoolbox-tester must be installed to be able to run the tests!" endif belle-sip-1.6.3/tester/afl.md000066400000000000000000000033351313437522400157770ustar00rootroot00000000000000# Using the afl-fuzz to look for bugs in the belle-sip parser This guide expects that you have installed the afl-fuzz package for your distribution, or on Mac using Homebrew or port. Windows is not supported right now. Then follow these steps: 1. Configure belle-sip with the afl instrumentation tools as CC and OBJC, and with static linking: # Linux CC=`which afl-gcc` ./configure --disable-shared # Mac CC=`which afl-clang` OBJC=`which afl-clang` ./configure --disable-shared 2. Compile and make sure the testers are build. You should have an executable file named testers/belle_sip_parser make clean && make 3. You can now run the afl fuzzy tester in the tester/ directory to test the parser for SDP, HTTP or SIP. afl-fuzz -i afl/sip -o afl_sip_results -- ./belle_sip_parse --protocol sip @@ With this command: - It will show you a screen with informations on the current state of the fuzzing steps. - The `afl/sip` directory contains valid SIP messages that the fuzzer will use as a base for its investigations. You can add - The results of the investigations will be placed in a directory named `afl_sip_results/`. You will have access to the SIP messages that provoked a crash in the `crashes/` directory. # Notes The afl directory contains test messages that will be the base for mutation with the afl fuzzer. They are saved using the CRLF line endings. This is important since the parser expects two "\r\n\r\n" at the end of a message. The hangs usually occur when the message passed to belle_sip_parse is not correctly formed, and the underlying implementation fails at some point. These are not false positives, they are actual problems! ## TODO: 1. add HTTP and SDP fuzzy testsbelle-sip-1.6.3/tester/afl/000077500000000000000000000000001313437522400154515ustar00rootroot00000000000000belle-sip-1.6.3/tester/afl/generate_dictfiles.rb000066400000000000000000000011121313437522400216110ustar00rootroot00000000000000# this script just reads each line in the sip_dict.txt and creates a file with the line content in it. # this is foe AFL to get an idea of important lexemes to use to mutate SIP messages. lines = File.open("sip_dict.txt", "r").read # remove empty lines (this is bound to happen) lines = lines.split(/\n/).reject{ |l| l.chomp.empty? }.join("\n") lines.each_line { |line| line.gsub!(/\n/, "") file = "sip_dict/#{line}" file.gsub!(/[\=\:]/,"_") print "Create file #{file}\n" # comment this line for a dry run #File.open(file, "w") { |file| file.write(line) } }belle-sip-1.6.3/tester/afl/sip/000077500000000000000000000000001313437522400162445ustar00rootroot00000000000000belle-sip-1.6.3/tester/afl/sip/401Unauthorized.txt000066400000000000000000000007331313437522400217160ustar00rootroot00000000000000SIP/2.0 401 Unauthorized Via: SIP/2.0/TLS 192.168.0.109:49949;alias;branch=z9hG4bK.gWH~ix41h;rport=49949;received=82.216.250.246 From: ;tag=5exJTQayL To: ;tag=jv0pZ0cH8ejaF Call-ID: PkUb7A2nxg CSeq: 20 REGISTER Server: Flexisip/1.0.2 (sofia-sip-nta/2.0) WWW-Authenticate: Digest realm="sip.linphone.org", nonce="WR5i2QAAAAA9uY8IAADQ03f3p94AAAAA", opaque="+GNywA==", algorithm=MD5, qop="auth" Content-Length: 0 belle-sip-1.6.3/tester/afl/sip/REGISTER.txt000066400000000000000000000014461313437522400202360ustar00rootroot00000000000000REGISTER sip:sip.linphone.org SIP/2.0 Via: SIP/2.0/TLS 192.168.0.109:49949;alias;branch=z9hG4bK.gdQJOpBN5;rport From: ;tag=5exJTQayL To: sip:gbi@sip.linphone.org CSeq: 21 REGISTER Call-ID: PkUb7A2nxg Max-Forwards: 70 Supported: outbound Accept: application/sdp, text/plain, application/vnd.gsma.rcs-ft-http+xml Contact: ;+sip.instance="" Expires: 3600 User-Agent: Linphone/3.8.5 (belle-sip/1.4.1) Content-Length: 0 Authorization: Digest realm="sip.linphone.org", nonce="WR5i2QAAAAA9uY8IAADQ03f3p94AAAAA", algorithm=MD5, opaque="+GNywA==", username="gbi", uri="sip:sip.linphone.org", response="124f0c0ea212786c9f8b5b6f1de92b43", cnonce="faaac8d5", nc=00000001, qop=auth belle-sip-1.6.3/tester/afl/sip/SIP200RegSuccess.txt000066400000000000000000000012641313437522400216540ustar00rootroot00000000000000SIP/2.0 200 Registration successful Via: SIP/2.0/TLS 192.168.0.109:49949;alias;branch=z9hG4bK.gdQJOpBN5;rport=49949;received=82.216.250.246 From: ;tag=5exJTQayL To: ;tag=K5SF1UXm5Q8va Call-ID: PkUb7A2nxg CSeq: 21 REGISTER Contact: ;expires=3600;q=0.00 Contact: ;expires=604442;q=0.00 Server: Flexisip/1.0.2 (sofia-sip-nta/2.0) Content-Length: 0 belle-sip-1.6.3/tester/afl/sip_dict.txt000066400000000000000000000020541313437522400200110ustar00rootroot00000000000000Accept-Encoding: Accept-Language: Accept: alert Alert-Info: algorithm= Allow: application Apr audio Aug auth auth-int Authentication-Info: Authorization: branch branch= c: Call-ID: Call-Info: card cnonce= Contact: Content-Disposition: Content-Encoding: Content-Language: Content-Length: Content-Type: CSeq: Date: Dec Digest: domain= duration= e: emergency Error-Info: Expires: expires= f: false Feb Fri From: GMT handling= i: icon image In-Reply-To: info ip Jan Jul Jun k: l: lr m: maddr= Mar Max-Forwards: May message MIME-Version: Min-Expires: Mon multipart nc= nextnonce= non-urgent nonce= normal Nov Oct opaque= optional Organization: phone Priority: Proxy-Authenticate: Proxy-Authorization: Proxy-Require: purpose= q= qop= realm= received= Record-Route: render Reply-To: Require: required response= Retry-After: Route: rspauth= s: Sat SCTP sctp Sep Server: session SIP stale= Subject: Sun Supported: t: tag= TCP tcp text Thu Timestamp: TLS tls To true ttl= Tue UDP udp Unsupported: urgent uri= User-Agent: username= v: Via: video Warning: Wed WWW-Authenticate: x-belle-sip-1.6.3/tester/afl/sip_dict/000077500000000000000000000000001313437522400172475ustar00rootroot00000000000000belle-sip-1.6.3/tester/afl/sip_dict/Accept-Encoding_000066400000000000000000000000201313437522400222440ustar00rootroot00000000000000Accept-Encoding:belle-sip-1.6.3/tester/afl/sip_dict/Accept-Language_000066400000000000000000000000201313437522400222410ustar00rootroot00000000000000Accept-Language:belle-sip-1.6.3/tester/afl/sip_dict/Accept_000066400000000000000000000000071313437522400205250ustar00rootroot00000000000000Accept:belle-sip-1.6.3/tester/afl/sip_dict/Alert-Info_000066400000000000000000000000131313437522400212630ustar00rootroot00000000000000Alert-Info:belle-sip-1.6.3/tester/afl/sip_dict/Allow_000066400000000000000000000000061313437522400204030ustar00rootroot00000000000000Allow:belle-sip-1.6.3/tester/afl/sip_dict/Apr000066400000000000000000000000031313437522400177050ustar00rootroot00000000000000Aprbelle-sip-1.6.3/tester/afl/sip_dict/Aug000066400000000000000000000000031313437522400176770ustar00rootroot00000000000000Augbelle-sip-1.6.3/tester/afl/sip_dict/Authentication-Info_000066400000000000000000000000241313437522400231750ustar00rootroot00000000000000Authentication-Info:belle-sip-1.6.3/tester/afl/sip_dict/Authorization_000066400000000000000000000000161313437522400221660ustar00rootroot00000000000000Authorization:belle-sip-1.6.3/tester/afl/sip_dict/CSeq_000066400000000000000000000000051313437522400201570ustar00rootroot00000000000000CSeq:belle-sip-1.6.3/tester/afl/sip_dict/Call-ID_000066400000000000000000000000101313437522400204650ustar00rootroot00000000000000Call-ID:belle-sip-1.6.3/tester/afl/sip_dict/Call-Info_000066400000000000000000000000121313437522400210660ustar00rootroot00000000000000Call-Info:belle-sip-1.6.3/tester/afl/sip_dict/Contact_000066400000000000000000000000101313437522400207130ustar00rootroot00000000000000Contact:belle-sip-1.6.3/tester/afl/sip_dict/Content-Disposition_000066400000000000000000000000241313437522400232410ustar00rootroot00000000000000Content-Disposition:belle-sip-1.6.3/tester/afl/sip_dict/Content-Encoding_000066400000000000000000000000211313437522400224600ustar00rootroot00000000000000Content-Encoding:belle-sip-1.6.3/tester/afl/sip_dict/Content-Language_000066400000000000000000000000211313437522400224550ustar00rootroot00000000000000Content-Language:belle-sip-1.6.3/tester/afl/sip_dict/Content-Length_000066400000000000000000000000171313437522400221600ustar00rootroot00000000000000Content-Length:belle-sip-1.6.3/tester/afl/sip_dict/Content-Type_000066400000000000000000000000151313437522400216560ustar00rootroot00000000000000Content-Type:belle-sip-1.6.3/tester/afl/sip_dict/Date_000066400000000000000000000000051313437522400202010ustar00rootroot00000000000000Date:belle-sip-1.6.3/tester/afl/sip_dict/Dec000066400000000000000000000000031313437522400176560ustar00rootroot00000000000000Decbelle-sip-1.6.3/tester/afl/sip_dict/Digest_000066400000000000000000000000071313437522400205450ustar00rootroot00000000000000Digest:belle-sip-1.6.3/tester/afl/sip_dict/Error-Info_000066400000000000000000000000131313437522400213050ustar00rootroot00000000000000Error-Info:belle-sip-1.6.3/tester/afl/sip_dict/Expires_000066400000000000000000000000101313437522400207370ustar00rootroot00000000000000expires=belle-sip-1.6.3/tester/afl/sip_dict/Feb000066400000000000000000000000031313437522400176570ustar00rootroot00000000000000Febbelle-sip-1.6.3/tester/afl/sip_dict/Fri000066400000000000000000000000031313437522400177030ustar00rootroot00000000000000Fribelle-sip-1.6.3/tester/afl/sip_dict/From_000066400000000000000000000000051313437522400202270ustar00rootroot00000000000000From:belle-sip-1.6.3/tester/afl/sip_dict/GMT000066400000000000000000000000031313437522400176120ustar00rootroot00000000000000GMTbelle-sip-1.6.3/tester/afl/sip_dict/In-Reply-To_000066400000000000000000000000141313437522400213430ustar00rootroot00000000000000In-Reply-To:belle-sip-1.6.3/tester/afl/sip_dict/Jan000066400000000000000000000000031313437522400176730ustar00rootroot00000000000000Janbelle-sip-1.6.3/tester/afl/sip_dict/Jul000066400000000000000000000000031313437522400177150ustar00rootroot00000000000000Julbelle-sip-1.6.3/tester/afl/sip_dict/Jun000066400000000000000000000000031313437522400177170ustar00rootroot00000000000000Junbelle-sip-1.6.3/tester/afl/sip_dict/MIME-Version_000066400000000000000000000000151313437522400214770ustar00rootroot00000000000000MIME-Version:belle-sip-1.6.3/tester/afl/sip_dict/Mar000066400000000000000000000000031313437522400177020ustar00rootroot00000000000000Marbelle-sip-1.6.3/tester/afl/sip_dict/Max-Forwards_000066400000000000000000000000151313437522400216370ustar00rootroot00000000000000Max-Forwards:belle-sip-1.6.3/tester/afl/sip_dict/May000066400000000000000000000000031313437522400177110ustar00rootroot00000000000000Maybelle-sip-1.6.3/tester/afl/sip_dict/Min-Expires_000066400000000000000000000000141313437522400214640ustar00rootroot00000000000000Min-Expires:belle-sip-1.6.3/tester/afl/sip_dict/Mon000066400000000000000000000000031313437522400177140ustar00rootroot00000000000000Monbelle-sip-1.6.3/tester/afl/sip_dict/Nov000066400000000000000000000000031313437522400177250ustar00rootroot00000000000000Novbelle-sip-1.6.3/tester/afl/sip_dict/Oct000066400000000000000000000000031313437522400177100ustar00rootroot00000000000000Octbelle-sip-1.6.3/tester/afl/sip_dict/Organization_000066400000000000000000000000151313437522400217710ustar00rootroot00000000000000Organization:belle-sip-1.6.3/tester/afl/sip_dict/Priority_000066400000000000000000000000111313437522400211420ustar00rootroot00000000000000Priority:belle-sip-1.6.3/tester/afl/sip_dict/Proxy-Authenticate_000066400000000000000000000000231313437522400230610ustar00rootroot00000000000000Proxy-Authenticate:belle-sip-1.6.3/tester/afl/sip_dict/Proxy-Authorization_000066400000000000000000000000241313437522400233040ustar00rootroot00000000000000Proxy-Authorization:belle-sip-1.6.3/tester/afl/sip_dict/Proxy-Require_000066400000000000000000000000161313437522400220610ustar00rootroot00000000000000Proxy-Require:belle-sip-1.6.3/tester/afl/sip_dict/Record-Route_000066400000000000000000000000151313437522400216370ustar00rootroot00000000000000Record-Route:belle-sip-1.6.3/tester/afl/sip_dict/Reply-To_000066400000000000000000000000111313437522400207740ustar00rootroot00000000000000Reply-To:belle-sip-1.6.3/tester/afl/sip_dict/Require_000066400000000000000000000000101313437522400207340ustar00rootroot00000000000000Require:belle-sip-1.6.3/tester/afl/sip_dict/Retry-After_000066400000000000000000000000141313437522400214700ustar00rootroot00000000000000Retry-After:belle-sip-1.6.3/tester/afl/sip_dict/Route_000066400000000000000000000000061313437522400204230ustar00rootroot00000000000000Route:belle-sip-1.6.3/tester/afl/sip_dict/SCTP000066400000000000000000000000041313437522400177350ustar00rootroot00000000000000sctpbelle-sip-1.6.3/tester/afl/sip_dict/SIP000066400000000000000000000000031313437522400176160ustar00rootroot00000000000000SIPbelle-sip-1.6.3/tester/afl/sip_dict/Sat000066400000000000000000000000031313437522400177120ustar00rootroot00000000000000Satbelle-sip-1.6.3/tester/afl/sip_dict/Sep000066400000000000000000000000031313437522400177120ustar00rootroot00000000000000Sepbelle-sip-1.6.3/tester/afl/sip_dict/Server_000066400000000000000000000000071313437522400205740ustar00rootroot00000000000000Server:belle-sip-1.6.3/tester/afl/sip_dict/Subject_000066400000000000000000000000101313437522400207170ustar00rootroot00000000000000Subject:belle-sip-1.6.3/tester/afl/sip_dict/Sun000066400000000000000000000000031313437522400177300ustar00rootroot00000000000000Sunbelle-sip-1.6.3/tester/afl/sip_dict/Supported_000066400000000000000000000000121313437522400213070ustar00rootroot00000000000000Supported:belle-sip-1.6.3/tester/afl/sip_dict/TCP000066400000000000000000000000031313437522400176110ustar00rootroot00000000000000tcpbelle-sip-1.6.3/tester/afl/sip_dict/TLS000066400000000000000000000000031313437522400176250ustar00rootroot00000000000000tlsbelle-sip-1.6.3/tester/afl/sip_dict/Thu000066400000000000000000000000031313437522400177230ustar00rootroot00000000000000Thubelle-sip-1.6.3/tester/afl/sip_dict/Timestamp_000066400000000000000000000000121313437522400212650ustar00rootroot00000000000000Timestamp:belle-sip-1.6.3/tester/afl/sip_dict/To000066400000000000000000000000021313437522400175440ustar00rootroot00000000000000Tobelle-sip-1.6.3/tester/afl/sip_dict/Tue000066400000000000000000000000031313437522400177200ustar00rootroot00000000000000Tuebelle-sip-1.6.3/tester/afl/sip_dict/UDP000066400000000000000000000000031313437522400176130ustar00rootroot00000000000000udpbelle-sip-1.6.3/tester/afl/sip_dict/Unsupported_000066400000000000000000000000141313437522400216540ustar00rootroot00000000000000Unsupported:belle-sip-1.6.3/tester/afl/sip_dict/User-Agent_000066400000000000000000000000131313437522400212750ustar00rootroot00000000000000User-Agent:belle-sip-1.6.3/tester/afl/sip_dict/Via_000066400000000000000000000000041313437522400200420ustar00rootroot00000000000000Via:belle-sip-1.6.3/tester/afl/sip_dict/WWW-Authenticate_000066400000000000000000000000211313437522400224220ustar00rootroot00000000000000WWW-Authenticate:belle-sip-1.6.3/tester/afl/sip_dict/Warning_000066400000000000000000000000101313437522400207250ustar00rootroot00000000000000Warning:belle-sip-1.6.3/tester/afl/sip_dict/Wed000066400000000000000000000000031313437522400177020ustar00rootroot00000000000000Wedbelle-sip-1.6.3/tester/afl/sip_dict/alert000066400000000000000000000000051313437522400202740ustar00rootroot00000000000000alertbelle-sip-1.6.3/tester/afl/sip_dict/algorithm_000066400000000000000000000000121313437522400213100ustar00rootroot00000000000000algorithm=belle-sip-1.6.3/tester/afl/sip_dict/application000066400000000000000000000000131313437522400214670ustar00rootroot00000000000000applicationbelle-sip-1.6.3/tester/afl/sip_dict/audio000066400000000000000000000000051313437522400202660ustar00rootroot00000000000000audiobelle-sip-1.6.3/tester/afl/sip_dict/auth000066400000000000000000000000041313437522400201250ustar00rootroot00000000000000authbelle-sip-1.6.3/tester/afl/sip_dict/auth-int000066400000000000000000000000101313437522400207120ustar00rootroot00000000000000auth-intbelle-sip-1.6.3/tester/afl/sip_dict/branch000066400000000000000000000000061313437522400204230ustar00rootroot00000000000000branchbelle-sip-1.6.3/tester/afl/sip_dict/branch_000066400000000000000000000000071313437522400205630ustar00rootroot00000000000000branch=belle-sip-1.6.3/tester/afl/sip_dict/c_000066400000000000000000000000021313437522400175430ustar00rootroot00000000000000c:belle-sip-1.6.3/tester/afl/sip_dict/card000066400000000000000000000000041313437522400200750ustar00rootroot00000000000000cardbelle-sip-1.6.3/tester/afl/sip_dict/cnonce_000066400000000000000000000000071313437522400205730ustar00rootroot00000000000000cnonce=belle-sip-1.6.3/tester/afl/sip_dict/domain_000066400000000000000000000000071313437522400205750ustar00rootroot00000000000000domain=belle-sip-1.6.3/tester/afl/sip_dict/duration_000066400000000000000000000000111313437522400211460ustar00rootroot00000000000000duration=belle-sip-1.6.3/tester/afl/sip_dict/e_000066400000000000000000000000021313437522400175450ustar00rootroot00000000000000e:belle-sip-1.6.3/tester/afl/sip_dict/emergency000066400000000000000000000000111313437522400211400ustar00rootroot00000000000000emergencybelle-sip-1.6.3/tester/afl/sip_dict/f_000066400000000000000000000000021313437522400175460ustar00rootroot00000000000000f:belle-sip-1.6.3/tester/afl/sip_dict/false000066400000000000000000000000051313437522400202570ustar00rootroot00000000000000falsebelle-sip-1.6.3/tester/afl/sip_dict/handling_000066400000000000000000000000111313437522400211050ustar00rootroot00000000000000handling=belle-sip-1.6.3/tester/afl/sip_dict/i_000066400000000000000000000000021313437522400175510ustar00rootroot00000000000000i:belle-sip-1.6.3/tester/afl/sip_dict/icon000066400000000000000000000000041313437522400201140ustar00rootroot00000000000000iconbelle-sip-1.6.3/tester/afl/sip_dict/image000066400000000000000000000000051313437522400202470ustar00rootroot00000000000000imagebelle-sip-1.6.3/tester/afl/sip_dict/info000066400000000000000000000000041313437522400201170ustar00rootroot00000000000000infobelle-sip-1.6.3/tester/afl/sip_dict/ip000066400000000000000000000000021313437522400175720ustar00rootroot00000000000000ipbelle-sip-1.6.3/tester/afl/sip_dict/k_000066400000000000000000000000021313437522400175530ustar00rootroot00000000000000k:belle-sip-1.6.3/tester/afl/sip_dict/l_000066400000000000000000000000021313437522400175540ustar00rootroot00000000000000l:belle-sip-1.6.3/tester/afl/sip_dict/lr000066400000000000000000000000021313437522400175770ustar00rootroot00000000000000lrbelle-sip-1.6.3/tester/afl/sip_dict/m_000066400000000000000000000000021313437522400175550ustar00rootroot00000000000000m:belle-sip-1.6.3/tester/afl/sip_dict/maddr_000066400000000000000000000000061313437522400204140ustar00rootroot00000000000000maddr=belle-sip-1.6.3/tester/afl/sip_dict/message000066400000000000000000000000071313437522400206130ustar00rootroot00000000000000messagebelle-sip-1.6.3/tester/afl/sip_dict/multipart000066400000000000000000000000111313437522400212030ustar00rootroot00000000000000multipartbelle-sip-1.6.3/tester/afl/sip_dict/nc_000066400000000000000000000000031313437522400177220ustar00rootroot00000000000000nc=belle-sip-1.6.3/tester/afl/sip_dict/nextnonce_000066400000000000000000000000121313437522400213230ustar00rootroot00000000000000nextnonce=belle-sip-1.6.3/tester/afl/sip_dict/non-urgent000066400000000000000000000000121313437522400212570ustar00rootroot00000000000000non-urgentbelle-sip-1.6.3/tester/afl/sip_dict/nonce_000066400000000000000000000000061313437522400204270ustar00rootroot00000000000000nonce=belle-sip-1.6.3/tester/afl/sip_dict/normal000066400000000000000000000000061313437522400204560ustar00rootroot00000000000000normalbelle-sip-1.6.3/tester/afl/sip_dict/opaque_000066400000000000000000000000071313437522400206200ustar00rootroot00000000000000opaque=belle-sip-1.6.3/tester/afl/sip_dict/optional000066400000000000000000000000101313437522400210060ustar00rootroot00000000000000optionalbelle-sip-1.6.3/tester/afl/sip_dict/phone000066400000000000000000000000051313437522400202760ustar00rootroot00000000000000phonebelle-sip-1.6.3/tester/afl/sip_dict/purpose_000066400000000000000000000000101313437522400210150ustar00rootroot00000000000000purpose=belle-sip-1.6.3/tester/afl/sip_dict/q_000066400000000000000000000000021313437522400175610ustar00rootroot00000000000000q=belle-sip-1.6.3/tester/afl/sip_dict/qop_000066400000000000000000000000041313437522400201220ustar00rootroot00000000000000qop=belle-sip-1.6.3/tester/afl/sip_dict/realm_000066400000000000000000000000061313437522400204250ustar00rootroot00000000000000realm=belle-sip-1.6.3/tester/afl/sip_dict/received_000066400000000000000000000000111313437522400211070ustar00rootroot00000000000000received=belle-sip-1.6.3/tester/afl/sip_dict/render000066400000000000000000000000061313437522400204450ustar00rootroot00000000000000renderbelle-sip-1.6.3/tester/afl/sip_dict/required000066400000000000000000000000101313437522400210010ustar00rootroot00000000000000requiredbelle-sip-1.6.3/tester/afl/sip_dict/response_000066400000000000000000000000111313437522400211570ustar00rootroot00000000000000response=belle-sip-1.6.3/tester/afl/sip_dict/rspauth_000066400000000000000000000000101313437522400210060ustar00rootroot00000000000000rspauth=belle-sip-1.6.3/tester/afl/sip_dict/s_000066400000000000000000000000021313437522400175630ustar00rootroot00000000000000s:belle-sip-1.6.3/tester/afl/sip_dict/session000066400000000000000000000000071313437522400206520ustar00rootroot00000000000000sessionbelle-sip-1.6.3/tester/afl/sip_dict/stale_000066400000000000000000000000061313437522400204350ustar00rootroot00000000000000stale=belle-sip-1.6.3/tester/afl/sip_dict/t_000066400000000000000000000000021313437522400175640ustar00rootroot00000000000000t:belle-sip-1.6.3/tester/afl/sip_dict/tag_000066400000000000000000000000041313437522400200760ustar00rootroot00000000000000tag=belle-sip-1.6.3/tester/afl/sip_dict/text000066400000000000000000000000041313437522400201500ustar00rootroot00000000000000textbelle-sip-1.6.3/tester/afl/sip_dict/true000066400000000000000000000000041313437522400201430ustar00rootroot00000000000000truebelle-sip-1.6.3/tester/afl/sip_dict/ttl_000066400000000000000000000000041313437522400201260ustar00rootroot00000000000000ttl=belle-sip-1.6.3/tester/afl/sip_dict/urgent000066400000000000000000000000061313437522400204720ustar00rootroot00000000000000urgentbelle-sip-1.6.3/tester/afl/sip_dict/uri_000066400000000000000000000000041313437522400201220ustar00rootroot00000000000000uri=belle-sip-1.6.3/tester/afl/sip_dict/username_000066400000000000000000000000111313437522400211400ustar00rootroot00000000000000username=belle-sip-1.6.3/tester/afl/sip_dict/v_000066400000000000000000000000021313437522400175660ustar00rootroot00000000000000v:belle-sip-1.6.3/tester/afl/sip_dict/video000066400000000000000000000000051313437522400202730ustar00rootroot00000000000000videobelle-sip-1.6.3/tester/afl/sip_dict/x-000066400000000000000000000000021313437522400175060ustar00rootroot00000000000000x-belle-sip-1.6.3/tester/auth_helper_tester.c000066400000000000000000000250101313437522400207370ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "belle-sip/auth-helper.h" #include "belle_sip_tester.h" #include static void test_authentication(void) { const char* l_raw_header = "WWW-Authenticate: Digest " "algorithm=MD5, realm=\"sip.linphone.org\", opaque=\"1bc7f9097684320\"," " nonce=\"cz3h0gAAAAC06TKKAABmTz1V9OcAAAAA\""; char ha1[33]; belle_sip_header_www_authenticate_t* www_authenticate=belle_sip_header_www_authenticate_parse(l_raw_header); belle_sip_header_authorization_t* authorization = belle_sip_auth_helper_create_authorization(www_authenticate); belle_sip_header_authorization_set_uri(authorization,belle_sip_uri_parse("sip:sip.linphone.org")); BC_ASSERT_EQUAL(0,belle_sip_auth_helper_compute_ha1("jehan-mac","sip.linphone.org","toto",ha1), int, "%d"); BC_ASSERT_EQUAL(0,belle_sip_auth_helper_fill_authorization(authorization,"REGISTER",ha1), int, "%d"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_response(authorization),"77ebf3de72e41934d806175586086508"); belle_sip_object_unref(www_authenticate); belle_sip_object_unref(authorization); } static void test_authentication_qop_auth(void) { const char* l_raw_header = "WWW-Authenticate: Digest " "algorithm=MD5, realm=\"sip.linphone.org\", opaque=\"1bc7f9097684320\"," " qop=\"auth,auth-int\", nonce=\"cz3h0gAAAAC06TKKAABmTz1V9OcAAAAA\""; char ha1[33]; belle_sip_header_www_authenticate_t* www_authenticate=belle_sip_header_www_authenticate_parse(l_raw_header); belle_sip_header_authorization_t* authorization = belle_sip_auth_helper_create_authorization(www_authenticate); belle_sip_header_authorization_set_uri(authorization,belle_sip_uri_parse("sip:sip.linphone.org")); belle_sip_header_authorization_set_nonce_count(authorization,1); belle_sip_header_authorization_set_qop(authorization,"auth"); belle_sip_header_authorization_set_cnonce(authorization,"8302210f"); /*for testing purpose*/ BC_ASSERT_EQUAL(0,belle_sip_auth_helper_compute_ha1("jehan-mac","sip.linphone.org","toto",ha1), int, "%d"); BC_ASSERT_EQUAL(0,belle_sip_auth_helper_fill_authorization(authorization,"REGISTER",ha1), int, "%d"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_qop(authorization),"auth"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_response(authorization),"694dab8dfe7d50d28ba61e8c43e30666"); BC_ASSERT_EQUAL(belle_sip_header_authorization_get_nonce_count(authorization),1, int, "%d"); belle_sip_object_unref(www_authenticate); belle_sip_object_unref(authorization); } static void test_proxy_authentication(void) { const char* l_raw_header = "Proxy-Authenticate: Digest " "algorithm=MD5, realm=\"sip.linphone.org\", opaque=\"1bc7f9097684320\"," " qop=\"auth,auth-int\", nonce=\"cz3h0gAAAAC06TKKAABmTz1V9OcAAAAA\""; char ha1[33]; belle_sip_header_proxy_authenticate_t* proxy_authenticate=belle_sip_header_proxy_authenticate_parse(l_raw_header); belle_sip_header_proxy_authorization_t* proxy_authorization = belle_sip_auth_helper_create_proxy_authorization(proxy_authenticate); belle_sip_header_authorization_set_uri(BELLE_SIP_HEADER_AUTHORIZATION(proxy_authorization),belle_sip_uri_parse("sip:sip.linphone.org")); BC_ASSERT_EQUAL(0,belle_sip_auth_helper_compute_ha1("jehan-mac","sip.linphone.org","toto",ha1), int, "%d"); BC_ASSERT_EQUAL(0,belle_sip_auth_helper_fill_proxy_authorization(proxy_authorization,"REGISTER",ha1), int, "%d"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_response(BELLE_SIP_HEADER_AUTHORIZATION(proxy_authorization)) ,"77ebf3de72e41934d806175586086508"); belle_sip_object_unref(proxy_authenticate); belle_sip_object_unref(proxy_authorization); } #define TEMPORARY_CERTIFICATE_DIR "/belle_sip_tester_crt" static void test_generate_and_parse_certificates(void) { belle_sip_certificates_chain_t *certificate, *parsed_certificate; belle_sip_signing_key_t *key, *parsed_key; char *pem_certificate, *pem_parsed_certificate, *pem_key, *pem_parsed_key; int ret = 0; char *belle_sip_certificate_temporary_dir = bc_tester_file(TEMPORARY_CERTIFICATE_DIR); /* create 2 certificates in the temporary certificate directory (TODO : set the directory in a absolute path?? where?)*/ ret = belle_sip_generate_self_signed_certificate(belle_sip_certificate_temporary_dir, "test_certificate1", &certificate, &key); if (ret == BCTBX_ERROR_UNAVAILABLE_FUNCTION) { belle_sip_warning("Test skipped, self signed certificate generation not available."); return; } BC_ASSERT_EQUAL(0, ret, int, "%d"); if (ret == 0) { belle_sip_object_unref(certificate); belle_sip_object_unref(key); } ret = belle_sip_generate_self_signed_certificate(belle_sip_certificate_temporary_dir, "test_certificate2", &certificate, &key); BC_ASSERT_EQUAL(0, ret, int, "%d"); /* parse directory to get the certificate2 */ ret = belle_sip_get_certificate_and_pkey_in_dir(belle_sip_certificate_temporary_dir, "test_certificate2", &parsed_certificate, &parsed_key, BELLE_SIP_CERTIFICATE_RAW_FORMAT_PEM); free(belle_sip_certificate_temporary_dir); BC_ASSERT_EQUAL(0, ret, int, "%d"); /* get pem version of generated and parsed certificate and compare them */ pem_certificate = belle_sip_certificates_chain_get_pem(certificate); pem_parsed_certificate = belle_sip_certificates_chain_get_pem(parsed_certificate); if (BC_ASSERT_PTR_NOT_NULL(pem_certificate) && BC_ASSERT_PTR_NOT_NULL(pem_parsed_certificate)) { BC_ASSERT_STRING_EQUAL(pem_certificate, pem_parsed_certificate); } /* get pem version of generated and parsed key and compare them */ pem_key = belle_sip_signing_key_get_pem(key); pem_parsed_key = belle_sip_signing_key_get_pem(parsed_key); if (BC_ASSERT_PTR_NOT_NULL(pem_key) && BC_ASSERT_PTR_NOT_NULL(pem_parsed_key)) { BC_ASSERT_STRING_EQUAL(pem_key, pem_parsed_key); } belle_sip_free(pem_certificate); belle_sip_free(pem_parsed_certificate); belle_sip_free(pem_key); belle_sip_free(pem_parsed_key); belle_sip_object_unref(certificate); belle_sip_object_unref(parsed_certificate); belle_sip_object_unref(key); belle_sip_object_unref(parsed_key); } const char* belle_sip_tester_fingerprint256_cert = /*for URI:sip:tester@client.example.org*/ "-----BEGIN CERTIFICATE-----\n" "MIIDtTCCAh2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDExF0ZXN0\n" "X2NlcnRpZmljYXRlMTAeFw0wMTAxMDEwMDAwMDBaFw0zMDAxMDEwMDAwMDBaMBwx\n" "GjAYBgNVBAMTEXRlc3RfY2VydGlmaWNhdGUxMIIBojANBgkqhkiG9w0BAQEFAAOC\n" "AY8AMIIBigKCAYEAoI6Dpdyc8ARM9KTIkuagImUgpybuWrKayPfrAeUE/gnyd8bO\n" "Bf7CkGdpHv82c1BdUxE5Z1j19TMR0MHCtFD5z0PWtW3erWQqUdxdFYIUknIi5ObU\n" "AlXgqAIYLCSMaGWzmavdsC95HfHiuPC+YTLwr1vhNC6IWCSKt9N7xek/InY73cBh\n" "pNw/kJOB/AzB9r40uxcye6+6Hp3dAd2YOGOiuKlAFBlAeq/T70VKBvdw/D8QFi5Z\n" "BJ2+xX9jQBshzHi9JdMS6ZhLdtjBHwi37k1l1KyRh+qVTbze5pN7YCRmj8Q4dS0S\n" "3ozV27AXM60kXbX4+PWQG9nuL/PO2NxTx0olIaTkzjM+roxWE6srhAEQ+aXn3tCq\n" "bHND6AN2Yjm/mzQI2ig143gHraLRaHx+uTtRonMeWMvTeUlX/BwUoffjppmWqICd\n" "OiBFNXOpp3hlzZDdoEhwKgIVMu3WbEsOTG7uphkUGZo/VaTVW0zvYAS2JXC/0s/S\n" "85dB5M3Y9l/8v0T7AgMBAAGjAjAAMA0GCSqGSIb3DQEBCwUAA4IBgQBm5N00W7+G\n" "ygF6OUM3143N5B/41vTk5FDZ/iU/UJaPSLBM/aZhA2FjoTswjpFfY8V6IkALrtUH\n" "20FVip3lguMc7md9L9qMRVYj/2H94A2Bg/zx+PlhJNI0bshITzS6pHgM2qKk+KRB\n" "yZaHQTa8DjRCYuAp1roh4NKNDa16WdY4Dk5ncRORqzcxczBJ2LSbq4b78pdEl/iL\n" "nHOoFOSmiQQ2ui7H89bSUxRmVJFiNfPlTeYUKjc753LJCuri30rQVnHE+HMBmE5y\n" "sM6FiGawJxUKAcS0zuKeroHNXLzL0qIGgeLkoPb267se0tCAcJZImiqyK0y1cuHw\n" "o9BZ5t/I6UvTJLE9+p+wG7nR8TdszaZ+bLzSdHWDRPS2Ux4J+Ux3dnIAH/ZcD5CD\n" "/mj4F12yW0ZNukFVkptneS6ab1lQb3PT7tzkuzKud00QNHswZLbORQrXnvuk5LrR\n" "V7PbeVUz1FxaOjFwHXkkvFqrbwRdBc7GVqQZDVV40WVvciGGcBhemqc=\n" "-----END CERTIFICATE-----"; /* fingerprint of certificate generated using openssl x509 -fingerprint -sha256 */ const char* belle_sip_tester_fingerprint256_cert_fingerprint = "SHA-256 A0:98:2D:3E:68:F3:14:8D:ED:50:40:DB:ED:A4:28:BC:1E:1A:6A:05:59:9E:69:3F:02:E2:F8:22:BF:4C:92:14"; static void test_certificate_fingerprint(void) { char *fingerprint; belle_sip_certificates_chain_t *cert; /* check underlying bctoolbox function availability */ if (bctbx_x509_certificate_get_fingerprint(NULL, NULL, 0, 0) == BCTBX_ERROR_UNAVAILABLE_FUNCTION) { belle_sip_warning("Test skipped, certificate fingerprint generation not available."); return; } /* parse certificate defined in belle_sip_register_tester.c */ cert = belle_sip_certificates_chain_parse(belle_sip_tester_client_cert,strlen(belle_sip_tester_client_cert),BELLE_SIP_CERTIFICATE_RAW_FORMAT_PEM); /* generate fingerprint */ fingerprint = belle_sip_certificates_chain_get_fingerprint(cert); BC_ASSERT_PTR_NOT_NULL(fingerprint); if (fingerprint) { BC_ASSERT_STRING_EQUAL(fingerprint, belle_sip_tester_client_cert_fingerprint); } belle_sip_free(fingerprint); belle_sip_object_unref(cert); /* parse certificate defined above, signing algo is sha256 */ cert = belle_sip_certificates_chain_parse(belle_sip_tester_fingerprint256_cert,strlen(belle_sip_tester_fingerprint256_cert),BELLE_SIP_CERTIFICATE_RAW_FORMAT_PEM); /* generate fingerprint */ fingerprint = belle_sip_certificates_chain_get_fingerprint(cert); BC_ASSERT_PTR_NOT_NULL(fingerprint); if (fingerprint) { BC_ASSERT_STRING_EQUAL(fingerprint, belle_sip_tester_fingerprint256_cert_fingerprint); } belle_sip_free(fingerprint); belle_sip_object_unref(cert); } test_t authentication_helper_tests[] = { TEST_NO_TAG("Proxy-Authenticate", test_proxy_authentication), TEST_NO_TAG("WWW-Authenticate", test_authentication), TEST_NO_TAG("WWW-Authenticate (with qop)", test_authentication_qop_auth), TEST_NO_TAG("generate and parse self signed certificates", test_generate_and_parse_certificates), TEST_NO_TAG("generate certificate fingerprint", test_certificate_fingerprint) }; test_suite_t authentication_helper_test_suite = { "Authentication helper", NULL, NULL, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(authentication_helper_tests) / sizeof(authentication_helper_tests[0]), authentication_helper_tests}; belle-sip-1.6.3/tester/belle_generic_uri_tester.c000066400000000000000000000142011313437522400220750ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2013 Belledonne Communications SARL, Grenoble, France This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_tester.h" static void test_basic_uri(void) { belle_generic_uri_t* source_uri = belle_generic_uri_parse("http://www.linphone.org/index.html"); char* source_uri_raw = belle_sip_object_to_string(source_uri); belle_generic_uri_t* first_uri = belle_generic_uri_parse(source_uri_raw); belle_generic_uri_t* uri=BELLE_GENERIC_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(first_uri))); belle_sip_free(source_uri_raw); belle_sip_object_unref(source_uri); belle_sip_object_unref(first_uri); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_scheme(uri),"http"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_host(uri),"www.linphone.org"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_path(uri),"/index.html"); belle_sip_object_unref(uri); source_uri = belle_generic_uri_parse("http://www.linphone.org/"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_path(source_uri),"/"); belle_sip_object_unref(source_uri); source_uri = belle_generic_uri_parse("http://www.linphone.org/a/b/c"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_path(source_uri),"/a/b/c"); BC_ASSERT_STRING_EQUAL("http://www.linphone.org/a/b/c",source_uri_raw = belle_sip_object_to_string(source_uri)); belle_sip_free(source_uri_raw); belle_sip_object_unref(source_uri); } static void test_complex_uri(void) { belle_generic_uri_t* source_uri = belle_generic_uri_parse("ftp://toto:secret@ftp.linphone.fr:1234/url?sa=t&rct=j&url=http%3A%2F%2Ftranslate.google.fr"); char* source_uri_raw = belle_generic_uri_to_string(source_uri); belle_generic_uri_t* first_uri = belle_generic_uri_parse(source_uri_raw); belle_generic_uri_t* uri=BELLE_GENERIC_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(first_uri))); belle_sip_free(source_uri_raw); belle_sip_object_unref(source_uri); belle_sip_object_unref(first_uri); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_scheme(uri),"ftp"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_host(uri),"ftp.linphone.fr"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_user(uri),"toto"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_user_password(uri),"secret"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_host(uri),"ftp.linphone.fr"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_path(uri),"/url"); BC_ASSERT_EQUAL(belle_generic_uri_get_port(uri),1234,int,"%d"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_query(uri),"sa=t&rct=j&url=http://translate.google.fr"); belle_sip_object_unref(uri); } static void test_file_path(void) { belle_generic_uri_t* source_uri = belle_generic_uri_parse("/index.html"); char* source_uri_raw = belle_sip_object_to_string(source_uri); belle_generic_uri_t* first_uri = belle_generic_uri_parse(source_uri_raw); belle_generic_uri_t* uri=BELLE_GENERIC_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(first_uri))); belle_sip_free(source_uri_raw); belle_sip_object_unref(source_uri); belle_sip_object_unref(first_uri); BC_ASSERT_PTR_NULL(belle_generic_uri_get_scheme(uri)); BC_ASSERT_PTR_NULL(belle_generic_uri_get_host(uri)); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_path(uri),"/index.html"); belle_sip_object_unref(uri); source_uri = belle_generic_uri_parse("file:///tmp/absolute-file"); BC_ASSERT_PTR_NOT_NULL(source_uri); if (source_uri!=NULL){ BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_scheme(source_uri),"file"); BC_ASSERT_PTR_NULL(belle_generic_uri_get_host(source_uri)); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_path(source_uri),"/tmp/absolute-file"); belle_sip_object_unref(source_uri); } /*this is INVALID*/ source_uri = belle_generic_uri_parse("file://./relative-file"); BC_ASSERT_PTR_NOT_NULL(source_uri); if (source_uri) belle_sip_object_unref(source_uri); /* PATH segment always start by / */ source_uri = belle_generic_uri_parse("./relative-file"); BC_ASSERT_PTR_NULL(source_uri); if (source_uri!=NULL){ belle_sip_object_unref(source_uri); } } static void test_absolute_uri(void) { belle_generic_uri_t* source_uri = belle_generic_uri_parse("tel:+33123457"); char* source_uri_raw = belle_generic_uri_to_string(source_uri); belle_generic_uri_t* first_uri = belle_generic_uri_parse(source_uri_raw); belle_generic_uri_t* uri=BELLE_GENERIC_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(first_uri))); belle_sip_free(source_uri_raw); belle_sip_object_unref(source_uri); belle_sip_object_unref(first_uri); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_scheme(uri),"tel"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_opaque_part(uri),"+33123457"); belle_sip_object_unref(uri); source_uri = belle_generic_uri_parse("tel:11234567888;phone-context=vzims.com"); source_uri_raw = belle_generic_uri_to_string(source_uri); first_uri = belle_generic_uri_parse(source_uri_raw); uri=BELLE_GENERIC_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(first_uri))); belle_sip_free(source_uri_raw); belle_sip_object_unref(source_uri); belle_sip_object_unref(first_uri); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_scheme(uri),"tel"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_opaque_part(uri),"11234567888;phone-context=vzims.com"); belle_sip_object_unref(uri); } static test_t tests[] = { TEST_NO_TAG("Simple uri", test_basic_uri), TEST_NO_TAG("Complex uri", test_complex_uri), TEST_NO_TAG("File path", test_file_path), TEST_NO_TAG("Absolute uri", test_absolute_uri) }; test_suite_t generic_uri_test_suite = {"Generic uri", NULL, NULL, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(tests) / sizeof(tests[0]), tests}; belle-sip-1.6.3/tester/belle_http_tester.c000066400000000000000000000323721313437522400205720ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_tester.h" #include typedef struct http_counters{ int response_headers_count; int response_count; int io_error_count; int two_hundred; int three_hundred; int four_hundred; }http_counters_t; static int wait_for(belle_sip_stack_t*s1,int* counter,int value,int timeout) { int retry=0; #define SLEEP_TIME 100 while (*counter!=value && retry++ <(timeout/SLEEP_TIME)) { if (s1) belle_sip_stack_sleep(s1,SLEEP_TIME); } if (*counter!=value) return FALSE; else return TRUE; } static void process_response(void *data, const belle_http_response_event_t *event){ http_counters_t *counters=(http_counters_t*)data; counters->response_count++; BC_ASSERT_PTR_NOT_NULL(event->response); if (event->response){ int code=belle_http_response_get_status_code(event->response); belle_sip_body_handler_t *body=belle_sip_message_get_body_handler(BELLE_SIP_MESSAGE(event->response)); if (code>=200 && code <300) counters->two_hundred++; else if (code>=300 && code <400) counters->three_hundred++; else if (code>=300 && code <400) counters->four_hundred++; BC_ASSERT_PTR_NOT_NULL(body); } } static void process_io_error(void *data, const belle_sip_io_error_event_t *event){ http_counters_t *counters=(http_counters_t*)data; counters->io_error_count++; } static void process_auth_requested(void *data, belle_sip_auth_event_t *event){ if (belle_sip_auth_event_get_mode(event)==BELLE_SIP_AUTH_MODE_TLS){ belle_sip_certificates_chain_t* cert = belle_sip_certificates_chain_parse(belle_sip_tester_client_cert,strlen(belle_sip_tester_client_cert),BELLE_SIP_CERTIFICATE_RAW_FORMAT_PEM); belle_sip_signing_key_t* key = belle_sip_signing_key_parse(belle_sip_tester_private_key,strlen(belle_sip_tester_private_key),belle_sip_tester_private_key_passwd); belle_sip_auth_event_set_client_certificates_chain(event,cert); belle_sip_auth_event_set_signing_key(event,key); belle_sip_message("process_auth_requested requested for DN [%s]" ,belle_sip_auth_event_get_distinguished_name(event)); } } static belle_sip_stack_t *stack=NULL; static belle_http_provider_t *prov=NULL; static int http_before_all(void) { stack=belle_sip_stack_new(NULL); prov=belle_sip_stack_create_http_provider(stack,"0.0.0.0"); if (belle_sip_tester_get_root_ca_path() != NULL) { belle_tls_crypto_config_t *crypto_config=belle_tls_crypto_config_new(); belle_tls_crypto_config_set_root_ca(crypto_config,belle_sip_tester_get_root_ca_path()); belle_http_provider_set_tls_crypto_config(prov,crypto_config); belle_sip_object_unref(crypto_config); } return 0; } static int http_after_all(void) { belle_sip_object_unref(prov); belle_sip_object_unref(stack); return 0; } static int url_supported(const char *url) { if (url && strstr(url,"https://")==url && !belle_sip_stack_tls_available(stack)) { belle_sip_error("No TLS support, test skipped."); return -1; } return 0; } static int one_get(const char *url,http_counters_t* counters, int *counter){ if (url_supported(url)==-1) { return -1; } else { belle_http_request_listener_callbacks_t cbs={0}; belle_http_request_listener_t *l; belle_generic_uri_t *uri; belle_http_request_t *req; uri=belle_generic_uri_parse(url); req=belle_http_request_create("GET", uri, belle_sip_header_create("User-Agent","belle-sip/"PACKAGE_VERSION), NULL); cbs.process_response=process_response; cbs.process_io_error=process_io_error; cbs.process_auth_requested=process_auth_requested; l=belle_http_request_listener_create_from_callbacks(&cbs,counters); belle_http_provider_send_request(prov,req,l); wait_for(stack,counter,1,10000); belle_sip_object_unref(l); return 0; } } static void one_http_get(void){ http_counters_t counters={0}; if (one_get("http://smtp.linphone.org",&counters,&counters.response_count) == 0) { BC_ASSERT_EQUAL(counters.response_count,1,int,"%d"); BC_ASSERT_EQUAL(counters.io_error_count,0,int,"%d"); BC_ASSERT_EQUAL(counters.two_hundred,1,int,"%d"); } } static void http_get_empty_body(void){ http_counters_t counters={0}; if (one_get("http://smtp.linphone.org/marie_invalid",&counters,&counters.response_count) == 0) { BC_ASSERT_EQUAL(counters.response_count,1,int,"%d"); BC_ASSERT_EQUAL(counters.io_error_count,0,int,"%d"); BC_ASSERT_EQUAL(counters.two_hundred,1,int,"%d"); } } static void http_get_io_error(void){ http_counters_t counters={0}; if (one_get("http://blablabla.fail",&counters,&counters.io_error_count) == 0) { BC_ASSERT_EQUAL(counters.response_count,0,int,"%d"); BC_ASSERT_EQUAL(counters.io_error_count,1,int,"%d"); } } static void one_https_get(void){ http_counters_t counters={0}; if (one_get("https://smtp.linphone.org",&counters,&counters.response_count) == 0) { BC_ASSERT_EQUAL(counters.response_count, 1, int, "%d"); BC_ASSERT_EQUAL(counters.io_error_count, 0, int, "%d"); BC_ASSERT_EQUAL(counters.two_hundred,1,int,"%d"); } } static void https_get_long_body(void){ http_counters_t counters={0}; if (one_get("https://smtp.linphone.org/linphone.html",&counters, &counters.response_count) == 0) { BC_ASSERT_EQUAL(counters.response_count, 1, int, "%d"); BC_ASSERT_EQUAL(counters.io_error_count, 0, int, "%d"); BC_ASSERT_EQUAL(counters.two_hundred,1,int,"%d"); } } static void https_digest_get(void){ http_counters_t counters={0}; if (one_get("https://pauline:pouet@smtp.linphone.org/restricted",&counters,&counters.response_count) == 0) { BC_ASSERT_EQUAL(counters.response_count, 1, int, "%d"); BC_ASSERT_EQUAL(counters.io_error_count, 0, int, "%d"); BC_ASSERT_EQUAL(counters.three_hundred,1,int,"%d"); } } #if 0 static void https_client_cert_connection(void){ belle_tls_verify_policy_t *policy=belle_tls_verify_policy_new(); http_counters_t counters={0}; belle_tls_verify_policy_set_exceptions(policy,BELLE_TLS_VERIFY_ANY_REASON);/*ignore the server verification because we don't have a true certificate*/ belle_http_provider_set_tls_verify_policy(prov,policy); if (one_get("https://sip2.linphone.org:5063",&counters) == 0) { BC_ASSERT_EQUAL(counters.two_hundred,1,int,"%d"); } belle_tls_verify_policy_set_exceptions(policy,0); belle_sip_object_unref(policy); } #endif static void on_progress(belle_sip_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, size_t total){ if (total!=0){ double frac=100.0*(double)offset/(double)total; belle_sip_message("transfer %g %% done",frac); }else belle_sip_message("%i bytes transfered",(int)offset); } #define MULTIPART_BEGIN "somehash.jpg\r\n" \ "--" MULTIPART_BOUNDARY "\r\n" \ "Content-Disposition: form-data; name=\"userfile\"; filename=\"belle_http_sip_tester.jpg\"\r\n" \ "Content-Type: application/octet-stream\r\n\r\n" #define MULTIPART_END "\r\n--" MULTIPART_BOUNDARY "--\r\n" const char *multipart_boudary=MULTIPART_BOUNDARY; const int image_size=250000; static int on_send_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *msg, void *data, size_t offset, uint8_t *buffer, size_t *size){ size_t end_of_img=sizeof(MULTIPART_BEGIN)+image_size; if (offset==0){ size_t partlen=sizeof(MULTIPART_BEGIN); BC_ASSERT_LOWER_STRICT((unsigned int)partlen,(unsigned int)*size,unsigned int,"%u"); memcpy(buffer,MULTIPART_BEGIN,partlen); *size=partlen; }else if (offsetresponse_headers_count++; BC_ASSERT_PTR_NOT_NULL(event->response); if (event->response){ /*we are receiving a response, set a specific body handler to acquire the response. * if not done, belle-sip will create a memory body handler, the default*/ FILE *file=belle_sip_object_data_get(BELLE_SIP_OBJECT(event->request),"file"); belle_sip_message_set_body_handler( (belle_sip_message_t*)event->response, (belle_sip_body_handler_t*)belle_sip_user_body_handler_new(0,on_progress, NULL,on_recv_body,NULL, NULL,file) ); } } static void http_get_long_user_body(void){ belle_http_request_listener_callbacks_t cbs={0}; belle_http_request_listener_t *l; belle_generic_uri_t *uri; belle_http_request_t *req; http_counters_t counters={0}; const char *url="http://download-mirror.savannah.gnu.org/releases/linphone/belle-sip/belle-sip-1.3.0.tar.gz"; belle_sip_body_handler_t *bh; belle_http_response_t *resp; FILE *outfile=fopen("download.tar.gz","w"); uri=belle_generic_uri_parse(url); req=belle_http_request_create("GET", uri, belle_sip_header_create("User-Agent","belle-sip/" PACKAGE_VERSION), NULL); cbs.process_response_headers=process_response_headers; cbs.process_response=process_response; cbs.process_io_error=process_io_error; cbs.process_auth_requested=process_auth_requested; l=belle_http_request_listener_create_from_callbacks(&cbs,&counters); belle_sip_object_ref(req); belle_sip_object_data_set(BELLE_SIP_OBJECT(req),"file",outfile,NULL); belle_http_provider_send_request(prov,req,l); BC_ASSERT_TRUE(wait_for(stack,&counters.two_hundred,1,20000)); BC_ASSERT_EQUAL(counters.response_headers_count,1,int,"%d"); resp=belle_http_request_get_response(req); BC_ASSERT_PTR_NOT_NULL(resp); if (resp){ bh=belle_sip_message_get_body_handler((belle_sip_message_t*)resp); BC_ASSERT_GREATER_STRICT((unsigned int)belle_sip_body_handler_get_size(bh),0,unsigned int,"%u"); } belle_sip_object_unref(req); belle_sip_object_unref(l); if (outfile) fclose(outfile); } extern const char *test_http_proxy_addr; extern int test_http_proxy_port; static void one_https_get_with_proxy(void){ http_counters_t counters={0}; belle_sip_stack_set_http_proxy_host(stack, test_http_proxy_addr); belle_sip_stack_set_http_proxy_port(stack, test_http_proxy_port); if (one_get("https://smtp.linphone.org",&counters,&counters.response_count) == 0) { BC_ASSERT_EQUAL(counters.response_count, 1, int, "%d"); BC_ASSERT_EQUAL(counters.io_error_count, 0, int, "%d"); BC_ASSERT_EQUAL(counters.two_hundred,1,int,"%d"); } belle_sip_stack_set_http_proxy_host(stack, NULL); belle_sip_stack_set_http_proxy_port(stack, 0); } test_t http_tests[] = { TEST_NO_TAG("One http GET", one_http_get), TEST_NO_TAG("http GET of empty body", http_get_empty_body), TEST_NO_TAG("One https GET", one_https_get), TEST_NO_TAG("One https GET with http proxy", one_https_get_with_proxy), TEST_NO_TAG("http request with io error", http_get_io_error), TEST_NO_TAG("https GET with long body", https_get_long_body), TEST_NO_TAG("https digest GET", https_digest_get),/*, FIXME, need a server for testing TEST_NO_TAG("https with client certificate", https_client_cert_connection),*/ TEST_NO_TAG("https POST with long body", https_post_long_body), TEST_NO_TAG("http GET with long user body", http_get_long_user_body) }; test_suite_t http_test_suite = {"HTTP stack", http_before_all, http_after_all, NULL, NULL, sizeof(http_tests) / sizeof(http_tests[0]), http_tests}; belle-sip-1.6.3/tester/belle_sdp_tester.c000066400000000000000000001071751313437522400204050ustar00rootroot00000000000000/* belle-sdp - SDP (RFC) library. Copyright (C) 2011 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_tester.h" #include "port.h" //v=0 //o=jehan-mac 1239 1239 IN IP4 192.168.0.18 //s=Talk //c=IN IP4 192.168.0.18 //t=0 0 //m=audio 7078 RTP/AVP 111 110 3 0 8 101 //a=rtpmap:111 speex/16000 //a=fmtp:111 vbr=on //a=rtpmap:110 speex/8000 //a=fmtp:110 vbr=on //a=rtpmap:101 telephone-event/8000 //a=fmtp:101 0-11 //m=video 8078 RTP/AVP 99 97 98 //a=rtpmap:99 MP4V-ES/90000 //a=fmtp:99 profile-level-id=3 //a=rtpmap:97 theora/90000 //a=rtpmap:98 H263-1998/90000 //a=fmtp:98 CIF=1;QCIF=1 static belle_sdp_attribute_t* attribute_parse_marshall_parse_clone(const char* raw_attribute) { belle_sdp_attribute_t* lTmp; belle_sdp_attribute_t* lAttribute = belle_sdp_attribute_parse(raw_attribute); char* l_raw_attribute = belle_sip_object_to_string(BELLE_SIP_OBJECT(lAttribute)); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); lTmp = belle_sdp_attribute_parse(l_raw_attribute); belle_sip_free(l_raw_attribute); lAttribute = BELLE_SDP_ATTRIBUTE(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); return lAttribute; } static void test_attribute(void) { belle_sdp_attribute_t* lAttribute = attribute_parse_marshall_parse_clone("a=rtpmap:101 telephone-event/8000"); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(lAttribute), "rtpmap"); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_value(lAttribute), "101 telephone-event/8000"); BC_ASSERT_TRUE(belle_sdp_attribute_has_value(lAttribute)); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); } static void test_attribute_2(void) { belle_sdp_attribute_t* lAttribute = attribute_parse_marshall_parse_clone("a=ice-pwd:31ec21eb38b2ec6d36e8dc7b"); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(lAttribute), "ice-pwd"); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_value(lAttribute), "31ec21eb38b2ec6d36e8dc7b"); BC_ASSERT_TRUE(belle_sdp_attribute_has_value(lAttribute)); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); lAttribute = attribute_parse_marshall_parse_clone("a=alt:1 1 : e2br+9PL Eu1qGlQ9 10.211.55.3 8988"); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(lAttribute), "alt"); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_value(lAttribute), "1 1 : e2br+9PL Eu1qGlQ9 10.211.55.3 8988"); BC_ASSERT_TRUE(belle_sdp_attribute_has_value(lAttribute)); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); } static void test_rtcp_fb_attribute(void) { belle_sdp_rtcp_fb_attribute_t* lAttribute; lAttribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute_parse_marshall_parse_clone("a=rtcp-fb:* ack")); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(BELLE_SDP_ATTRIBUTE(lAttribute)), "rtcp-fb"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_id(lAttribute), -1, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_type(lAttribute), BELLE_SDP_RTCP_FB_ACK, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_param(lAttribute), BELLE_SDP_RTCP_FB_NONE, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); lAttribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute_parse_marshall_parse_clone("a=rtcp-fb:98 nack rpsi")); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(BELLE_SDP_ATTRIBUTE(lAttribute)), "rtcp-fb"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_id(lAttribute), 98, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_type(lAttribute), BELLE_SDP_RTCP_FB_NACK, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_param(lAttribute), BELLE_SDP_RTCP_FB_RPSI, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); lAttribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute_parse_marshall_parse_clone("a=rtcp-fb:* trr-int 3")); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(BELLE_SDP_ATTRIBUTE(lAttribute)), "rtcp-fb"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_id(lAttribute), -1, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_type(lAttribute), BELLE_SDP_RTCP_FB_TRR_INT, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_trr_int(lAttribute), 3, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); lAttribute = BELLE_SDP_RTCP_FB_ATTRIBUTE(attribute_parse_marshall_parse_clone("a=rtcp-fb:103 ccm fir")); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(BELLE_SDP_ATTRIBUTE(lAttribute)), "rtcp-fb"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_id(lAttribute), 103, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_type(lAttribute), BELLE_SDP_RTCP_FB_CCM, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_rtcp_fb_attribute_get_param(lAttribute), BELLE_SDP_RTCP_FB_FIR, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); } static void test_rtcp_xr_attribute(void) { belle_sdp_rtcp_xr_attribute_t* lAttribute; lAttribute = BELLE_SDP_RTCP_XR_ATTRIBUTE(attribute_parse_marshall_parse_clone("a=rtcp-xr")); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(BELLE_SDP_ATTRIBUTE(lAttribute)), "rtcp-xr"); BC_ASSERT_FALSE(belle_sdp_rtcp_xr_attribute_has_stat_summary(lAttribute)); BC_ASSERT_FALSE(belle_sdp_rtcp_xr_attribute_has_voip_metrics(lAttribute)); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); lAttribute = BELLE_SDP_RTCP_XR_ATTRIBUTE(attribute_parse_marshall_parse_clone("a=rtcp-xr:rcvr-rtt=all:10")); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(BELLE_SDP_ATTRIBUTE(lAttribute)), "rtcp-xr"); BC_ASSERT_STRING_EQUAL(belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(lAttribute), "all"); BC_ASSERT_EQUAL(belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_max_size(lAttribute), 10, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); lAttribute = BELLE_SDP_RTCP_XR_ATTRIBUTE(attribute_parse_marshall_parse_clone("a=rtcp-xr:stat-summary")); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(BELLE_SDP_ATTRIBUTE(lAttribute)), "rtcp-xr"); BC_ASSERT_PTR_NULL(belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(lAttribute)); BC_ASSERT_TRUE(belle_sdp_rtcp_xr_attribute_has_stat_summary(lAttribute)); BC_ASSERT_FALSE(belle_sdp_rtcp_xr_attribute_has_voip_metrics(lAttribute)); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); lAttribute = BELLE_SDP_RTCP_XR_ATTRIBUTE(attribute_parse_marshall_parse_clone("a=rtcp-xr:stat-summary=loss,jitt")); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(BELLE_SDP_ATTRIBUTE(lAttribute)), "rtcp-xr"); BC_ASSERT_TRUE(belle_sdp_rtcp_xr_attribute_has_stat_summary(lAttribute)); BC_ASSERT_PTR_NOT_NULL(belle_sip_list_find_custom(belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(lAttribute), (belle_sip_compare_func)strcasecmp, "loss")); BC_ASSERT_PTR_NOT_NULL(belle_sip_list_find_custom(belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(lAttribute), (belle_sip_compare_func)strcasecmp, "jitt")); BC_ASSERT_PTR_NULL(belle_sip_list_find_custom(belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(lAttribute), (belle_sip_compare_func)strcasecmp, "HL")); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); lAttribute = BELLE_SDP_RTCP_XR_ATTRIBUTE(attribute_parse_marshall_parse_clone("a=rtcp-xr:voip-metrics")); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(BELLE_SDP_ATTRIBUTE(lAttribute)), "rtcp-xr"); BC_ASSERT_FALSE(belle_sdp_rtcp_xr_attribute_has_stat_summary(lAttribute)); BC_ASSERT_TRUE(belle_sdp_rtcp_xr_attribute_has_voip_metrics(lAttribute)); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); lAttribute = BELLE_SDP_RTCP_XR_ATTRIBUTE(attribute_parse_marshall_parse_clone("a=rtcp-xr:rcvr-rtt=sender stat-summary=loss,dup,jitt,TTL voip-metrics")); BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_name(BELLE_SDP_ATTRIBUTE(lAttribute)), "rtcp-xr"); BC_ASSERT_STRING_EQUAL(belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(lAttribute), "sender"); BC_ASSERT_TRUE(belle_sdp_rtcp_xr_attribute_has_stat_summary(lAttribute)); BC_ASSERT_PTR_NOT_NULL(belle_sip_list_find_custom(belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(lAttribute), (belle_sip_compare_func)strcasecmp, "loss")); BC_ASSERT_PTR_NOT_NULL(belle_sip_list_find_custom(belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(lAttribute), (belle_sip_compare_func)strcasecmp, "dup")); BC_ASSERT_PTR_NOT_NULL(belle_sip_list_find_custom(belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(lAttribute), (belle_sip_compare_func)strcasecmp, "jitt")); BC_ASSERT_PTR_NOT_NULL(belle_sip_list_find_custom(belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(lAttribute), (belle_sip_compare_func)strcasecmp, "TTL")); BC_ASSERT_TRUE(belle_sdp_rtcp_xr_attribute_has_voip_metrics(lAttribute)); belle_sip_object_unref(BELLE_SIP_OBJECT(lAttribute)); } static void test_bandwidth(void) { belle_sdp_bandwidth_t* lTmp; belle_sdp_bandwidth_t* l_bandwidth = belle_sdp_bandwidth_parse("b=AS:380"); char* l_raw_bandwidth = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_bandwidth)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_bandwidth)); lTmp = belle_sdp_bandwidth_parse(l_raw_bandwidth); l_bandwidth = BELLE_SDP_BANDWIDTH(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_STRING_EQUAL(belle_sdp_bandwidth_get_type(l_bandwidth), "AS"); BC_ASSERT_EQUAL(belle_sdp_bandwidth_get_value(l_bandwidth),380, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(l_bandwidth)); belle_sip_free(l_raw_bandwidth); } static void test_origin(void) { belle_sdp_origin_t* lTmp; belle_sdp_origin_t* lOrigin = belle_sdp_origin_parse("o=jehan-mac 3800 2558 IN IP4 192.168.0.165"); char* l_raw_origin = belle_sip_object_to_string(BELLE_SIP_OBJECT(lOrigin)); belle_sip_object_unref(BELLE_SIP_OBJECT(lOrigin)); lTmp = belle_sdp_origin_parse(l_raw_origin); lOrigin = BELLE_SDP_ORIGIN(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_STRING_EQUAL(belle_sdp_origin_get_address(lOrigin), "192.168.0.165"); BC_ASSERT_STRING_EQUAL(belle_sdp_origin_get_address_type(lOrigin), "IP4"); BC_ASSERT_STRING_EQUAL(belle_sdp_origin_get_network_type(lOrigin), "IN"); belle_sip_object_unref(BELLE_SIP_OBJECT(lOrigin)); belle_sip_free(l_raw_origin); } static void test_malformed_origin(void) { belle_sdp_origin_t* lOrigin = belle_sdp_origin_parse("o=Jehan Monnier 3800 2558 IN IP4 192.168.0.165"); BC_ASSERT_PTR_NULL(lOrigin); } static void test_connection(void) { belle_sdp_connection_t* lTmp; belle_sdp_connection_t* lConnection = belle_sdp_connection_parse("c=IN IP4 192.168.0.18"); char* l_raw_connection = belle_sip_object_to_string(BELLE_SIP_OBJECT(lConnection)); belle_sip_object_unref(BELLE_SIP_OBJECT(lConnection)); lTmp = belle_sdp_connection_parse(l_raw_connection); lConnection = BELLE_SDP_CONNECTION(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address(lConnection), "192.168.0.18"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address_type(lConnection), "IP4"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_network_type(lConnection), "IN"); BC_ASSERT_EQUAL(belle_sdp_connection_get_ttl(lConnection), 0, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_connection_get_ttl(lConnection), 0, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(lConnection)); belle_sip_free(l_raw_connection); } static void test_connection_6(void) { belle_sdp_connection_t* lTmp; belle_sdp_connection_t* lConnection = belle_sdp_connection_parse("c=IN IP6 2a01:e35:1387:1020:6233:4bff:fe0b:5663"); char* l_raw_connection = belle_sip_object_to_string(BELLE_SIP_OBJECT(lConnection)); belle_sip_object_unref(BELLE_SIP_OBJECT(lConnection)); lTmp = belle_sdp_connection_parse(l_raw_connection); lConnection = BELLE_SDP_CONNECTION(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address(lConnection), "2a01:e35:1387:1020:6233:4bff:fe0b:5663"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address_type(lConnection), "IP6"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_network_type(lConnection), "IN"); belle_sip_object_unref(BELLE_SIP_OBJECT(lConnection)); belle_sip_free(l_raw_connection); } static void test_connection_multicast(void) { belle_sdp_connection_t* lTmp; belle_sdp_connection_t* lConnection = belle_sdp_connection_parse("c=IN IP4 224.2.1.1/127/3"); char* l_raw_connection = belle_sip_object_to_string(BELLE_SIP_OBJECT(lConnection)); belle_sip_object_unref(BELLE_SIP_OBJECT(lConnection)); lTmp = belle_sdp_connection_parse(l_raw_connection); lConnection = BELLE_SDP_CONNECTION(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address(lConnection), "224.2.1.1"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address_type(lConnection), "IP4"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_network_type(lConnection), "IN"); BC_ASSERT_EQUAL(belle_sdp_connection_get_ttl(lConnection), 127, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_connection_get_range(lConnection), 3, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(lConnection)); belle_sip_free(l_raw_connection); lConnection = belle_sdp_connection_parse("c=IN IP4 224.2.1.1/127"); l_raw_connection = belle_sip_object_to_string(BELLE_SIP_OBJECT(lConnection)); belle_sip_object_unref(BELLE_SIP_OBJECT(lConnection)); lTmp = belle_sdp_connection_parse(l_raw_connection); lConnection = BELLE_SDP_CONNECTION(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address(lConnection), "224.2.1.1"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address_type(lConnection), "IP4"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_network_type(lConnection), "IN"); BC_ASSERT_EQUAL(belle_sdp_connection_get_ttl(lConnection), 127, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_connection_get_range(lConnection), 0, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(lConnection)); belle_sip_free(l_raw_connection); lConnection = belle_sdp_connection_parse("c=IN IP6 ::1/3"); l_raw_connection = belle_sip_object_to_string(BELLE_SIP_OBJECT(lConnection)); belle_sip_object_unref(BELLE_SIP_OBJECT(lConnection)); lTmp = belle_sdp_connection_parse(l_raw_connection); lConnection = BELLE_SDP_CONNECTION(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address(lConnection), "::1"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address_type(lConnection), "IP6"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_network_type(lConnection), "IN"); BC_ASSERT_EQUAL(belle_sdp_connection_get_ttl(lConnection), 0, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_connection_get_range(lConnection), 3, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(lConnection)); belle_sip_free(l_raw_connection); } static void test_email(void) { belle_sdp_email_t* lTmp; belle_sdp_email_t* l_email = belle_sdp_email_parse("e= jehan "); char* l_raw_email = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_email)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_email)); lTmp = belle_sdp_email_parse(l_raw_email); l_email = BELLE_SDP_EMAIL(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_STRING_EQUAL(belle_sdp_email_get_value(l_email), " jehan "); belle_sip_object_unref(BELLE_SIP_OBJECT(l_email)); belle_sip_free(l_raw_email); } static void test_info(void) { belle_sdp_info_t* lTmp; belle_sdp_info_t* l_info = belle_sdp_info_parse("i=A Seminar on the session description protocol"); char* l_raw_info = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_info)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_info)); lTmp = belle_sdp_info_parse(l_raw_info); l_info = BELLE_SDP_INFO(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_STRING_EQUAL(belle_sdp_info_get_value(l_info), "A Seminar on the session description protocol"); belle_sip_object_unref(BELLE_SIP_OBJECT(l_info)); belle_sip_free(l_raw_info); } static void test_media(void) { belle_sdp_media_t* lTmp; belle_sip_list_t* list; belle_sdp_media_t* l_media = belle_sdp_media_parse("m=audio 7078 RTP/AVP 111 110 3 0 8 101"); char* l_raw_media = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_media)); int fmt[] ={111,110,3,0,8,101}; int i=0; belle_sip_object_unref(BELLE_SIP_OBJECT(l_media)); lTmp = belle_sdp_media_parse(l_raw_media); l_media = BELLE_SDP_MEDIA(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_STRING_EQUAL(belle_sdp_media_get_media_type(l_media), "audio"); BC_ASSERT_EQUAL(belle_sdp_media_get_media_port(l_media), 7078, int, "%d"); BC_ASSERT_STRING_EQUAL(belle_sdp_media_get_protocol(l_media), "RTP/AVP"); list = belle_sdp_media_get_media_formats(l_media); BC_ASSERT_PTR_NOT_NULL(list); for(;list!=NULL;list=list->next){ BC_ASSERT_EQUAL(BELLE_SIP_POINTER_TO_INT(list->data),fmt[i++], int, "%d"); } belle_sip_object_unref(BELLE_SIP_OBJECT(l_media)); belle_sip_free(l_raw_media); } static void test_media_description_base(belle_sdp_media_description_t* media_description) { const char* attr[] ={"99 MP4V-ES/90000" ,"99 profile-level-id=3" ,"97 theora/90000" ,"98 H263-1998/90000" ,"98 CIF=1;QCIF=1"}; belle_sdp_connection_t* lConnection; belle_sdp_media_description_t* l_media_description=media_description; belle_sdp_media_t* l_media = belle_sdp_media_description_get_media(l_media_description); belle_sip_list_t* list; int fmt[] ={99,97,98}; int i=0; BC_ASSERT_PTR_NOT_NULL(l_media); BC_ASSERT_STRING_EQUAL(belle_sdp_media_get_media_type(l_media), "video"); BC_ASSERT_EQUAL(belle_sdp_media_get_media_port(l_media), 8078, int, "%d"); BC_ASSERT_STRING_EQUAL(belle_sdp_media_get_protocol(l_media), "RTP/AVP"); list = belle_sdp_media_get_media_formats(l_media); BC_ASSERT_PTR_NOT_NULL(list); for(;list!=NULL;list=list->next){ BC_ASSERT_EQUAL(BELLE_SIP_POINTER_TO_INT(list->data),fmt[i++], int, "%d"); } /*connection*/ lConnection = belle_sdp_media_description_get_connection(l_media_description); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address(lConnection), "192.168.0.18"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_address_type(lConnection), "IP4"); BC_ASSERT_STRING_EQUAL(belle_sdp_connection_get_network_type(lConnection), "IN"); /*bandwidth*/ BC_ASSERT_EQUAL(belle_sdp_media_description_get_bandwidth(l_media_description,"AS"),380, int, "%d"); /*attributes*/ list = belle_sdp_media_description_get_attributes(l_media_description); BC_ASSERT_PTR_NOT_NULL(list); i=0; for(;list!=NULL;list=list->next){ BC_ASSERT_STRING_EQUAL(belle_sdp_attribute_get_value((belle_sdp_attribute_t*)(list->data)),attr[i++]); } } static void test_media_description(void) { const char* l_src = "m=video 8078 RTP/AVP 99 97 98\r\n"\ "c=IN IP4 192.168.0.18\r\n"\ "b=AS:380\r\n"\ "a=rtpmap:99 MP4V-ES/90000\r\n"\ "a=fmtp:99 profile-level-id=3\r\n"\ "a=rtpmap:97 theora/90000\r\n"\ "a=rtpmap:98 H263-1998/90000\r\n"\ "a=fmtp:98 CIF=1;QCIF=1\r\n"; belle_sdp_media_description_t* lTmp; belle_sdp_media_description_t* l_media_description = belle_sdp_media_description_parse(l_src); char* l_raw_media_description = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_media_description)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_media_description)); lTmp = belle_sdp_media_description_parse(l_raw_media_description); l_media_description = BELLE_SDP_MEDIA_DESCRIPTION(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); test_media_description_base(l_media_description); belle_sip_object_unref(BELLE_SIP_OBJECT(l_media_description)); belle_sip_free(l_raw_media_description); return; } static void test_simple_session_description(void) { const char* l_src = "v=0\r\n"\ "o=jehan-mac 2463217870 2463217870 IN IP4 192.168.0.18\r\n"\ "s=Talk\r\n"\ "c=IN IP4 192.168.0.18\r\n"\ "t=0 0\r\n"\ "m=audio 7078 RTP/AVP 111 110 3 0 8 101\r\n"\ "a=alt:1 1 : e2br+9PL Eu1qGlQ9 10.211.55.3 8988\r\n"\ "a=rtpmap:111 speex/16000\r\n"\ "a=fmtp:111 vbr=on\r\n"\ "a=rtpmap:110 speex/8000\r\n"\ "a=fmtp:110 vbr=on\r\n"\ "a=rtpmap:101 telephone-event/8000\r\n"\ "a=fmtp:101 0-11\r\n"\ "m=video 8078 RTP/AVP 99 97 98\r\n"\ "c=IN IP4 192.168.0.18\r\n"\ "b=AS:380\r\n"\ "a=rtpmap:99 MP4V-ES/90000\r\n"\ "a=fmtp:99 profile-level-id=3\r\n"\ "a=rtpmap:97 theora/90000\r\n"\ "a=rtpmap:98 H263-1998/90000\r\n"\ "a=fmtp:98 CIF=1;QCIF=1\r\n"; belle_sdp_origin_t* l_origin; belle_sip_list_t* media_descriptions; belle_sdp_session_description_t* lTmp; belle_sdp_session_description_t* l_session_description = belle_sdp_session_description_parse(l_src); char* l_raw_session_description = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_session_description)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_session_description)); lTmp = belle_sdp_session_description_parse(l_raw_session_description); belle_sip_free(l_raw_session_description); l_session_description = BELLE_SDP_SESSION_DESCRIPTION(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_version(l_session_description)); BC_ASSERT_EQUAL(belle_sdp_version_get_version(belle_sdp_session_description_get_version(l_session_description)),0, int, "%d"); l_origin = belle_sdp_session_description_get_origin(l_session_description); BC_ASSERT_PTR_NOT_NULL(l_origin); BC_ASSERT_STRING_EQUAL(belle_sdp_origin_get_address(l_origin),"192.168.0.18"); BC_ASSERT_STRING_EQUAL(belle_sdp_origin_get_address_type(l_origin),"IP4"); BC_ASSERT_STRING_EQUAL(belle_sdp_origin_get_network_type(l_origin),"IN"); BC_ASSERT_EQUAL(belle_sdp_origin_get_session_id(l_origin), 2463217870U, unsigned, "%u"); BC_ASSERT_EQUAL(belle_sdp_origin_get_session_version(l_origin), 2463217870U, unsigned, "%u"); BC_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_session_name(l_session_description)); BC_ASSERT_STRING_EQUAL(belle_sdp_session_name_get_value(belle_sdp_session_description_get_session_name(l_session_description)),"Talk"); BC_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_connection(l_session_description)); BC_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_time_descriptions(l_session_description)); BC_ASSERT_EQUAL(belle_sdp_time_get_start(belle_sdp_time_description_get_time((belle_sdp_time_description_t*)(belle_sdp_session_description_get_time_descriptions(l_session_description)->data))),0, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_time_get_stop(belle_sdp_time_description_get_time((belle_sdp_time_description_t*)(belle_sdp_session_description_get_time_descriptions(l_session_description)->data))),0, int, "%d"); media_descriptions = belle_sdp_session_description_get_media_descriptions(l_session_description); BC_ASSERT_PTR_NOT_NULL(media_descriptions); BC_ASSERT_STRING_EQUAL (belle_sdp_media_get_media_type(belle_sdp_media_description_get_media((belle_sdp_media_description_t*)(media_descriptions->data))),"audio"); media_descriptions=media_descriptions->next; BC_ASSERT_PTR_NOT_NULL(media_descriptions); test_media_description_base((belle_sdp_media_description_t*)(media_descriptions->data)); belle_sip_object_unref(l_session_description); return; } static void test_image_mline(void) { const char * sdp = "v=0\r\n" "o=cp10 138884701697 138884701699 IN IP4 10.7.1.133\r\n" "s=SIP Call\r\n" "c=IN IP4 91.121.128.144\r\n" "t=0 0\r\n" "m=image 33802 udptl t38\r\n" "a=sendrecv\r\n" "a=T38FaxVersion:0\r\n" "a=T38MaxBitRate:9600\r\n" "a=T38FaxRateManagement:transferredTCF\r\n" "a=T38FaxMaxBuffer:1000\r\n" "a=T38FaxMaxDatagram:200\r\n" "a=T38FaxUdpEC:t38UDPRedundancy\r\n"; belle_sdp_session_description_t* l_session_description = belle_sdp_session_description_parse(sdp); belle_sip_object_unref(l_session_description); } static const char* big_sdp = "v=0\r\n"\ "o=jehan-mac 1239 1239 IN IP6 2a01:e35:1387:1020:6233:4bff:fe0b:5663\r\n"\ "s=SIP Talk\r\n"\ "c=IN IP4 192.168.0.18\r\n"\ "b=AS:380\r\n"\ "t=0 0\r\n"\ "a=ice-pwd:31ec21eb38b2ec6d36e8dc7b\r\n"\ "m=audio 7078 RTP/AVP 111 110 3 0 8 101\r\n"\ "a=rtpmap:111 speex/16000\r\n"\ "a=fmtp:111 vbr=on\r\n"\ "a=rtpmap:110 speex/8000\r\n"\ "a=fmtp:110 vbr=on\r\n"\ "a=rtpmap:101 telephone-event/8000\r\n"\ "a=fmtp:101 0-11\r\n"\ "m=video 8078 RTP/AVP 99 97 98\r\n"\ "c=IN IP4 192.168.0.18\r\n"\ "b=AS:380\r\n"\ "a=rtpmap:99 MP4V-ES/90000\r\n"\ "a=fmtp:99 profile-level-id=3\r\n"\ "a=rtpmap:97 theora/90000\r\n"\ "a=rtpmap:98 H263-1998/90000\r\n"\ "a=fmtp:98 CIF=1;QCIF=1\r\n"; static void test_session_description(void) { const char* l_src = big_sdp; belle_sdp_origin_t* l_origin; belle_sdp_session_description_t* lTmp; belle_sip_list_t* media_descriptions; belle_sdp_session_description_t* l_session_description = belle_sdp_session_description_parse(l_src); char* l_raw_session_description = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_session_description)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_session_description)); lTmp = belle_sdp_session_description_parse(l_raw_session_description); belle_sip_free(l_raw_session_description); l_session_description = BELLE_SDP_SESSION_DESCRIPTION(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(lTmp)); BC_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_version(l_session_description)); BC_ASSERT_EQUAL(belle_sdp_version_get_version(belle_sdp_session_description_get_version(l_session_description)),0, int, "%d"); l_origin = belle_sdp_session_description_get_origin(l_session_description); BC_ASSERT_PTR_NOT_NULL(l_origin); BC_ASSERT_STRING_EQUAL(belle_sdp_origin_get_address(l_origin),"2a01:e35:1387:1020:6233:4bff:fe0b:5663"); BC_ASSERT_STRING_EQUAL(belle_sdp_origin_get_address_type(l_origin),"IP6"); BC_ASSERT_STRING_EQUAL(belle_sdp_origin_get_network_type(l_origin),"IN"); BC_ASSERT_EQUAL(belle_sdp_origin_get_session_id(l_origin),1239, unsigned, "%u"); BC_ASSERT_EQUAL(belle_sdp_origin_get_session_version(l_origin),1239, unsigned, "%u"); BC_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_session_name(l_session_description)); BC_ASSERT_STRING_EQUAL(belle_sdp_session_name_get_value(belle_sdp_session_description_get_session_name(l_session_description)),"SIP Talk"); BC_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_connection(l_session_description)); BC_ASSERT_PTR_NOT_NULL(belle_sdp_session_description_get_time_descriptions(l_session_description)); BC_ASSERT_EQUAL(belle_sdp_time_get_start(belle_sdp_time_description_get_time((belle_sdp_time_description_t*)(belle_sdp_session_description_get_time_descriptions(l_session_description)->data))),0, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_time_get_stop(belle_sdp_time_description_get_time((belle_sdp_time_description_t*)(belle_sdp_session_description_get_time_descriptions(l_session_description)->data))),0, int, "%d"); media_descriptions = belle_sdp_session_description_get_media_descriptions(l_session_description); BC_ASSERT_PTR_NOT_NULL(media_descriptions); BC_ASSERT_STRING_EQUAL (belle_sdp_media_get_media_type(belle_sdp_media_description_get_media((belle_sdp_media_description_t*)(media_descriptions->data))),"audio"); media_descriptions=media_descriptions->next; BC_ASSERT_PTR_NOT_NULL(media_descriptions); test_media_description_base((belle_sdp_media_description_t*)(media_descriptions->data)); belle_sip_object_unref(l_session_description); return; } static void test_overflow(void){ belle_sdp_session_description_t* sdp; belle_sip_list_t *mds; belle_sdp_media_description_t *vmd; int i; const size_t orig_buffsize=1024; size_t buffsize=orig_buffsize; char *buffer=belle_sip_malloc0(buffsize); size_t offset=0; sdp=belle_sdp_session_description_parse(big_sdp); BC_ASSERT_PTR_NOT_NULL(sdp); mds=belle_sdp_session_description_get_media_descriptions(sdp); BC_ASSERT_PTR_NOT_NULL(mds); BC_ASSERT_PTR_NOT_NULL(mds->next); vmd=(belle_sdp_media_description_t*)mds->next->data; for(i=0;i<16;i++){ belle_sdp_media_description_add_attribute(vmd,belle_sdp_attribute_create("candidate","2 1 UDP 1694498815 82.65.223.97 9078 typ srflx raddr 192.168.0.2 rport 9078")); } BC_ASSERT_EQUAL(belle_sip_object_marshal(BELLE_SIP_OBJECT(sdp),buffer,buffsize,&offset),BELLE_SIP_BUFFER_OVERFLOW, int, "%d"); belle_sip_message("marshal size is %i",(int)offset); BC_ASSERT_EQUAL((unsigned int)offset,(unsigned int)buffsize,unsigned int,"%u"); belle_sip_object_unref(sdp); belle_sip_free(buffer); } static belle_sdp_mime_parameter_t* find_mime_parameter(belle_sip_list_t* list,const int format) { for(;list!=NULL;list=list->next){ if (belle_sdp_mime_parameter_get_media_format((belle_sdp_mime_parameter_t*)list->data) == format) { return (belle_sdp_mime_parameter_t*)list->data; } } return NULL; } static void check_mime_param (belle_sdp_mime_parameter_t* mime_param ,int rate ,int channel_count ,int ptime ,int max_ptime ,int media_format ,const char* type ,const char* parameters) { BC_ASSERT_PTR_NOT_NULL(mime_param); BC_ASSERT_EQUAL(belle_sdp_mime_parameter_get_rate(mime_param),rate, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_mime_parameter_get_channel_count(mime_param),channel_count, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_mime_parameter_get_ptime(mime_param),ptime, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_mime_parameter_get_max_ptime(mime_param),max_ptime, int, "%d"); BC_ASSERT_EQUAL(belle_sdp_mime_parameter_get_media_format(mime_param),media_format, int, "%d"); if (type) BC_ASSERT_STRING_EQUAL(belle_sdp_mime_parameter_get_type(mime_param),type); if (parameters) BC_ASSERT_STRING_EQUAL(belle_sdp_mime_parameter_get_parameters(mime_param),parameters); } static int compare_attribute(belle_sdp_attribute_t* attr, const char* value) { return strcasecmp(belle_sdp_attribute_get_name(attr),"rtpmap")==0 || strcasecmp(belle_sdp_attribute_get_value(attr),value)==0; } static void test_mime_parameter(void) { const char* l_src = "m=audio 7078 RTP/AVP 111 110 0 8 9 3 18 101\r\n"\ "a=rtpmap:111 speex/16000\r\n"\ "a=fmtp:111 vbr=on\r\n"\ "a=rtpmap:110 speex/8000\r\n"\ "a=fmtp:110 vbr=on\r\n"\ "a=rtpmap:8 PCMA/8000\r\n"\ "a=rtpmap:101 telephone-event/8000\r\n"\ "a=fmtp:101 0-11\r\n"\ "a=ptime:40\r\n"; belle_sdp_mime_parameter_t* l_param; belle_sdp_mime_parameter_t* lTmp; belle_sdp_media_t* l_media; belle_sip_list_t* mime_parameter_list; belle_sip_list_t* mime_parameter_list_iterator; belle_sdp_media_description_t* l_media_description_tmp = belle_sdp_media_description_parse(l_src); belle_sdp_media_description_t* l_media_description = belle_sdp_media_description_parse(belle_sip_object_to_string(l_media_description_tmp)); belle_sip_object_unref(l_media_description_tmp); mime_parameter_list = belle_sdp_media_description_build_mime_parameters(l_media_description); mime_parameter_list_iterator = mime_parameter_list; BC_ASSERT_PTR_NOT_NULL(mime_parameter_list); belle_sip_object_unref(BELLE_SIP_OBJECT(l_media_description)); l_media_description = belle_sdp_media_description_new(); belle_sdp_media_description_set_media(l_media_description,l_media=belle_sdp_media_parse("m=audio 7078 RTP/AVP 0")); belle_sdp_media_set_media_formats(l_media,belle_sip_list_free(belle_sdp_media_get_media_formats(l_media))); /*to remove 0*/ for (;mime_parameter_list_iterator!=NULL;mime_parameter_list_iterator=mime_parameter_list_iterator->next) { belle_sdp_media_description_append_values_from_mime_parameter(l_media_description,(belle_sdp_mime_parameter_t*)mime_parameter_list_iterator->data); } belle_sip_list_free_with_data(mime_parameter_list, (void (*)(void*))belle_sip_object_unref); /*marshal/unmarshal again*/ l_media_description_tmp = l_media_description; l_media_description= belle_sdp_media_description_parse(belle_sip_object_to_string(l_media_description)); belle_sip_object_unref(l_media_description_tmp); /*belle_sip_message("%s",belle_sip_object_to_string(l_media_description));*/ { belle_sip_list_t* attributes=belle_sdp_media_description_get_attributes(l_media_description); #ifdef BELLE_SDP_FORCE_RTP_MAP BC_ASSERT_PTR_NOT_NULL(belle_sip_list_find_custom(attributes,(belle_sip_compare_func)compare_attribute,"8 PCMA/8000")); BC_ASSERT_PTR_NOT_NULL(belle_sip_list_find_custom(attributes,(belle_sip_compare_func)compare_attribute,"18 G729/8000")); #else BC_ASSERT_PTR_NOT_NULL(belle_sip_list_find_custom(attributes,(belle_sip_compare_func)compare_attribute,"8 PCMA/8000")); BC_ASSERT_PTR_NOT_NULL(belle_sip_list_find_custom(attributes,(belle_sip_compare_func)compare_attribute,"18 G729/8000")); #endif } mime_parameter_list = belle_sdp_media_description_build_mime_parameters(l_media_description); belle_sip_object_unref(l_media_description); lTmp = find_mime_parameter(mime_parameter_list,111); l_param = BELLE_SDP_MIME_PARAMETER(belle_sip_object_clone(BELLE_SIP_OBJECT(lTmp))); BC_ASSERT_PTR_NOT_NULL(l_param); check_mime_param(l_param,16000,1,40,-1,111,"speex","vbr=on"); belle_sip_object_unref(l_param); l_param = find_mime_parameter(mime_parameter_list,110); BC_ASSERT_PTR_NOT_NULL(l_param); check_mime_param(l_param,8000,1,40,-1,110,"speex","vbr=on"); l_param = find_mime_parameter(mime_parameter_list,3); BC_ASSERT_PTR_NOT_NULL(l_param); check_mime_param(l_param,8000,1,40,-1,3,"GSM",NULL); l_param = find_mime_parameter(mime_parameter_list,0); BC_ASSERT_PTR_NOT_NULL(l_param); check_mime_param(l_param,8000,1,40,-1,0,"PCMU",NULL); l_param = find_mime_parameter(mime_parameter_list,8); BC_ASSERT_PTR_NOT_NULL(l_param); check_mime_param(l_param,8000,1,40,-1,8,"PCMA",NULL); l_param = find_mime_parameter(mime_parameter_list,9); BC_ASSERT_PTR_NOT_NULL(l_param); check_mime_param(l_param,8000,1,40,-1,9,"G722",NULL); l_param = find_mime_parameter(mime_parameter_list,101); BC_ASSERT_PTR_NOT_NULL(l_param); check_mime_param(l_param,8000,1,40,-1,101,"telephone-event","0-11"); belle_sip_list_free_with_data(mime_parameter_list, (void (*)(void*))belle_sip_object_unref); } test_t sdp_tests[] = { TEST_NO_TAG("a= (attribute)", test_attribute), TEST_NO_TAG("a= (attribute) 2", test_attribute_2), TEST_NO_TAG("a=rtcp-fb", test_rtcp_fb_attribute), TEST_NO_TAG("a=rtcp-xr", test_rtcp_xr_attribute), TEST_NO_TAG("b= (bandwidth)", test_bandwidth), TEST_NO_TAG("o= (IPv4 origin)", test_origin), TEST_NO_TAG("o= (malformed origin)", test_malformed_origin), TEST_NO_TAG("c= (IPv4 connection)", test_connection), TEST_NO_TAG("c= (IPv6 connection)", test_connection_6), TEST_NO_TAG("c= (multicast)", test_connection_multicast), TEST_NO_TAG("e= (email)", test_email), TEST_NO_TAG("i= (info)", test_info), TEST_NO_TAG("m= (media)", test_media), TEST_NO_TAG("mime parameter", test_mime_parameter), TEST_NO_TAG("Media description", test_media_description), TEST_NO_TAG("Simple session description", test_simple_session_description), TEST_NO_TAG("Session description", test_session_description), TEST_NO_TAG("Session description for fax", test_image_mline), TEST_NO_TAG("Marshal buffer overflow", test_overflow) }; test_suite_t sdp_test_suite = {"SDP", NULL, NULL, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(sdp_tests) / sizeof(sdp_tests[0]), sdp_tests}; belle-sip-1.6.3/tester/belle_sip_base_uri_tester.c000066400000000000000000000507721313437522400222630ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_tester.h" #include "belle_sip_internal.h" static void testSIMPLEURI(void) { belle_sip_uri_t* L_tmp; belle_sip_uri_t* L_uri = belle_sip_uri_parse("sip:sip.titi.com"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_tmp = belle_sip_uri_parse(l_raw_uri); L_uri = BELLE_SIP_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_uri); BC_ASSERT_PTR_NULL(belle_sip_uri_get_user(L_uri)); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "sip.titi.com"); BC_ASSERT_PTR_NULL(belle_sip_uri_get_transport_param(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void testCOMPLEXURI(void) { belle_sip_uri_t* L_tmp; belle_sip_uri_t * L_uri = belle_sip_uri_parse("sip:toto@1titi.com:5060;transport=tcp"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_tmp = belle_sip_uri_parse(l_raw_uri); L_uri = BELLE_SIP_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_uri); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user(L_uri), "toto"); BC_ASSERT_EQUAL(belle_sip_uri_get_port(L_uri), 5060, int, "%d"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "1titi.com"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_transport_param(L_uri), "tcp"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void testIPV6URI_base(const char* ip6) { belle_sip_uri_t* L_tmp; belle_sip_uri_t * L_uri; char* l_raw_uri; char uri[256]; snprintf(uri,sizeof(uri),"sip:toto@[%s]:5060;transport=tcp",ip6); L_uri = belle_sip_uri_parse(uri); l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_tmp = belle_sip_uri_parse(l_raw_uri); L_uri = BELLE_SIP_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_uri); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user(L_uri), "toto"); BC_ASSERT_EQUAL(belle_sip_uri_get_port(L_uri), 5060, int, "%d"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri),ip6); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_transport_param(L_uri), "tcp"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void testIPV6URI(void) { testIPV6URI_base("fe80::1"); testIPV6URI_base("2a01:e35:1387:1020:6233:4bff:fe0b:5663"); testIPV6URI_base("2a01:e35:1387:1020:6233::5663"); testIPV6URI_base("::1"); } static void testSIPSURI(void) { belle_sip_uri_t * L_uri = belle_sip_uri_parse("sips:linphone.org"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_uri = belle_sip_uri_parse(l_raw_uri); belle_sip_free(l_raw_uri); BC_ASSERT_EQUAL(belle_sip_uri_is_secure(L_uri), 1, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_uri = belle_sip_uri_parse("sip:linphone.org"); BC_ASSERT_EQUAL(belle_sip_uri_is_secure(L_uri), 0, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void test_ip_host(void) { belle_sip_uri_t * L_uri = belle_sip_uri_parse("sip:192.168.0.1"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_uri = belle_sip_uri_parse(l_raw_uri); belle_sip_free(l_raw_uri); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "192.168.0.1"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void test_lr(void) { belle_sip_uri_t * L_uri = belle_sip_uri_parse("sip:192.168.0.1;lr"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_uri = belle_sip_uri_parse(l_raw_uri); belle_sip_free(l_raw_uri); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "192.168.0.1"); BC_ASSERT_EQUAL(belle_sip_uri_has_lr_param(L_uri), 1, int, "%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void test_maddr(void) { belle_sip_uri_t * L_uri = belle_sip_uri_parse("sip:192.168.0.1;lr;maddr=linphone.org"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_uri = belle_sip_uri_parse(l_raw_uri); belle_sip_free(l_raw_uri); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_maddr_param(L_uri), "linphone.org"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void test_user_passwd(void) { belle_sip_uri_t * L_uri = belle_sip_uri_parse("sip:toto:tata@bla;"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_uri = belle_sip_uri_parse(l_raw_uri); belle_sip_free(l_raw_uri); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user_password(L_uri), "tata"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void test_uri_parameters (void) { char* l_raw_uri; belle_sip_uri_t* L_tmp; belle_sip_uri_t * L_uri = belle_sip_uri_parse("sip:192.168.0.1;ttl=12"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_uri = belle_sip_uri_parse("sip:maddr=@192.168.0.1;lr;maddr=192.168.0.1;user=ip;ttl=140;transport=sctp;method=INVITE;rport=5060"); l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_tmp = belle_sip_uri_parse(l_raw_uri); L_uri = BELLE_SIP_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_uri); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_maddr_param(L_uri), "192.168.0.1"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user_param(L_uri), "ip"); BC_ASSERT_EQUAL(belle_sip_uri_get_ttl_param(L_uri),140, int, "%d"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_transport_param(L_uri), "sctp"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_method_param(L_uri), "INVITE"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void test_headers(void) { belle_sip_uri_t * L_uri = belle_sip_uri_parse("sip:192.168.0.1?toto=titi"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_uri = belle_sip_uri_parse(l_raw_uri); belle_sip_free(l_raw_uri); if (!BC_ASSERT_PTR_NOT_NULL(belle_sip_uri_get_header(L_uri,"toto"))) return; BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_header(L_uri,"toto"), "titi"); BC_ASSERT_PTR_NULL(belle_sip_uri_get_header(L_uri,"bla")); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_uri = belle_sip_uri_parse("sip:192.168.0.1?toto=titi&header2=popo&header3="); l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_uri = belle_sip_uri_parse(l_raw_uri); belle_sip_free(l_raw_uri); if (!BC_ASSERT_PTR_NOT_NULL(belle_sip_uri_get_header(L_uri,"toto"))) return; BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_header(L_uri,"header2"), "popo"); belle_sip_object_unref(L_uri); } static void test_escaped_headers(void) { const char* raw_uri_2= "sip:eNgwBpkNcH6EdTHlX0cq8@toto.com?" "P-Group-Id=Fu0hHIQ23H4hveVT:New%20Group" "&P-Expert-Profile-Id=zKQOBOB2jTmUOjkB:New%20Group" "&P-Reverse-Charging=0&P-Campaign-Id=none" "&P-Embed-Url=https://toto.com/caller/?1.4.0-dev-42-91bdf0c%26id%3DFu0hHIQ23H4hveVT%26CAMPAIGN_ID%3Dnone"; belle_sip_uri_t * L_uri = belle_sip_uri_parse("sip:toto@sip.linhone.org?User-to-User=323a313030363a3230385a48363039313941364b4342463845495936%3Bencoding%3Dhex"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_uri = belle_sip_uri_parse(l_raw_uri); belle_sip_free(l_raw_uri); if (!BC_ASSERT_PTR_NOT_NULL(belle_sip_uri_get_header(L_uri,"User-to-User"))) return; BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_header(L_uri,"User-to-User"), "323a313030363a3230385a48363039313941364b4342463845495936;encoding=hex"); belle_sip_object_unref(L_uri); L_uri = belle_sip_uri_parse(raw_uri_2); l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_free(l_raw_uri); if (!BC_ASSERT_PTR_NOT_NULL(belle_sip_uri_get_header(L_uri,"P-Embed-Url"))) return; BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_header(L_uri,"P-Embed-Url"), "https://toto.com/caller/?1.4.0-dev-42-91bdf0c&id=Fu0hHIQ23H4hveVT&CAMPAIGN_ID=none"); belle_sip_object_unref(L_uri); } static void testSIMPLEURI_error(void) { belle_sip_uri_t* L_uri = belle_sip_uri_parse("siptcom"); BC_ASSERT_PTR_NULL(L_uri); } static void test_escaped_username(void) { belle_sip_uri_t* L_tmp; belle_sip_uri_t * L_uri = belle_sip_uri_parse("sip:toto%40linphone.org@titi.com"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_tmp = belle_sip_uri_parse(l_raw_uri); L_uri = BELLE_SIP_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_uri); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user(L_uri), "toto@linphone.org"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "titi.com"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void test_escaped_passwd(void) { belle_sip_uri_t* L_tmp; belle_sip_uri_t * L_uri = belle_sip_uri_parse("sips:%22jehan%22%20%3cjehan%40sip2.linphone.org:544%3e@sip.linphone.org"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_tmp = belle_sip_uri_parse(l_raw_uri); L_uri = BELLE_SIP_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_uri); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user(L_uri), "\"jehan\" "); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void test_escaped_parameter(void) { belle_sip_uri_t* L_tmp; belle_sip_uri_t * L_uri = belle_sip_uri_parse("sip:toto@titi.com;pa%3Dram=aa%40bb:5060[];o%40"); char* l_raw_uri = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); L_tmp = belle_sip_uri_parse(l_raw_uri); L_uri = BELLE_SIP_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_uri); BC_ASSERT_STRING_EQUAL(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(L_uri), "pa=ram"), "aa@bb:5060[]"); BC_ASSERT_TRUE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_uri), "o@")); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "titi.com"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_uri)); } static void test_uri_equals(void) { belle_sip_uri_t* a; belle_sip_uri_t* b; /* * The URIs within each of the following sets are equivalent: sip:%61lice@atlanta.com;transport=TCP sip:alice@AtLanTa.CoM;Transport=tcp */ a = belle_sip_uri_parse("sip:%61lice@atlanta.com;transport=TCP"); if (!BC_ASSERT_PTR_NOT_NULL(a)) return; b = belle_sip_uri_parse("sip:alice@AtLanTa.CoM;Transport=tcp"); if (!BC_ASSERT_PTR_NOT_NULL(b)) return; BC_ASSERT_TRUE(belle_sip_uri_equals(a,b)); belle_sip_object_unref(a); belle_sip_object_unref(b); /* sip:carol@chicago.com sip:carol@chicago.com;newparam=5 sip:carol@chicago.com;security=on */ a = belle_sip_uri_parse("sip:carol@chicago.com"); if (!BC_ASSERT_PTR_NOT_NULL(a)) return; b = belle_sip_uri_parse("sip:carol@chicago.com;newparam=5"); if (!BC_ASSERT_PTR_NOT_NULL(b)) return; BC_ASSERT_TRUE(belle_sip_uri_equals(a,b)); belle_sip_object_unref(a); belle_sip_object_unref(b); /* sip:biloxi.com;transport=tcp;method=REGISTER?to=sip:bob%40biloxi.com sip:biloxi.com;method=REGISTER;transport=tcp?to=sip:bob%40biloxi.com */ a = belle_sip_uri_parse("sip:biloxi.com;transport=tcp;method=REGISTER?to=sip:bob%40biloxi.com"); if (!BC_ASSERT_PTR_NOT_NULL(a)) return; b = belle_sip_uri_parse("sip:biloxi.com;method=REGISTER;transport=tcp?to=sip:bob%40biloxi.com"); if (!BC_ASSERT_PTR_NOT_NULL(b)) return; BC_ASSERT_TRUE(belle_sip_uri_equals(a,b)); belle_sip_object_unref(a); belle_sip_object_unref(b); /* sip:alice@atlanta.com?subject=project%20x&priority=urgent sip:alice@atlanta.com?priority=urgent&subject=project%20x The URIs within each of the following sets are not equivalent: SIP:ALICE@AtLanTa.CoM;Transport=udp (different usernames) sip:alice@AtLanTa.CoM;Transport=UDP */ a = belle_sip_uri_parse("sip:ALICE@AtLanTa.CoM;Transport=udp"); if (!BC_ASSERT_PTR_NOT_NULL(a)) return; b = belle_sip_uri_parse("sip:alice@AtLanTa.CoM;Transport=UDP"); if (!BC_ASSERT_PTR_NOT_NULL(b)) return; BC_ASSERT_FALSE(belle_sip_uri_equals(a,b)); belle_sip_object_unref(a); belle_sip_object_unref(b); /* sip:bob@biloxi.com (can resolve to different ports) sip:bob@biloxi.com:5060 */ a = belle_sip_uri_parse("sip:ALICE@AtLanTa.CoM;Transport=udp"); if (!BC_ASSERT_PTR_NOT_NULL(a)) return; b = belle_sip_uri_parse("sip:alice@AtLanTa.CoM;Transport=UDP"); if (!BC_ASSERT_PTR_NOT_NULL(b)) return; BC_ASSERT_FALSE(belle_sip_uri_equals(a,b)); belle_sip_object_unref(a); belle_sip_object_unref(b); /* sip:bob@biloxi.com (can resolve to different transports) sip:bob@biloxi.com;transport=udp */ a = belle_sip_uri_parse("sip:bob@biloxi.com"); if (!BC_ASSERT_PTR_NOT_NULL(a)) return; b = belle_sip_uri_parse("sip:bob@biloxi.com;transport=udp"); if (!BC_ASSERT_PTR_NOT_NULL(b)) return; BC_ASSERT_FALSE(belle_sip_uri_equals(a,b)); belle_sip_object_unref(a); belle_sip_object_unref(b); /* sip:bob@biloxi.com (can resolve to different port and transports) sip:bob@biloxi.com:6000;transport=tcp */ a = belle_sip_uri_parse("sip:bob@biloxi.com"); if (!BC_ASSERT_PTR_NOT_NULL(a)) return; b = belle_sip_uri_parse("sip:bob@biloxi.com:6000;transport=tcp"); if (!BC_ASSERT_PTR_NOT_NULL(b)) return; BC_ASSERT_FALSE(belle_sip_uri_equals(a,b)); belle_sip_object_unref(a); belle_sip_object_unref(b); a = belle_sip_uri_parse("sip:bob@biloxi.com"); if (!BC_ASSERT_PTR_NOT_NULL(a)) return; b = belle_sip_uri_parse("sip:boba@biloxi.com"); if (!BC_ASSERT_PTR_NOT_NULL(b)) return; BC_ASSERT_FALSE(belle_sip_uri_equals(a,b)); belle_sip_object_unref(a); belle_sip_object_unref(b); /* sip:carol@chicago.com (different header component) sip:carol@chicago.com?Subject=next%20meeting sip:bob@phone21.boxesbybob.com (even though that's what sip:bob@192.0.2.4 phone21.boxesbybob.com resolves to) Note that equality is not transitive: o sip:carol@chicago.com and sip:carol@chicago.com;security=on are equivalent o sip:carol@chicago.com and sip:carol@chicago.com;security=off are equivalent o sip:carol@chicago.com;security=on and sip:carol@chicago.com;security=off are not equivalent Rosenberg, et. al. Standards Track [Page 155] RFC 3261 SIP: Session Initiation Protocol June 2002 */ } /* * From 19.1.1 SIP and SIPS URI Components * dialog reg./redir. Contact/ default Req.-URI To From Contact R-R/Route external user -- o o o o o o password -- o o o o o o host -- m m m m m m port (1) o - - o o o user-param ip o o o o o o method INVITE - - - - - o maddr-param -- o - - o o o ttl-param 1 o - - o - o transp.-param (2) o - - o o o lr-param -- o - - - o o other-param -- o o o o o o headers -- - - - o - o*/ static void testUriComponentsChecker(void) { belle_sip_uri_t* uri = belle_sip_uri_parse("sip:hostonly"); BC_ASSERT_TRUE(belle_sip_uri_check_components_from_request_uri(uri)); belle_sip_object_unref(uri); { belle_sip_header_from_t* header = belle_sip_header_from_parse("From: sip:linphone.org:5061"); BC_ASSERT_FALSE(belle_sip_uri_check_components_from_context(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(header)),NULL,"From")); belle_sip_object_unref(header); } { belle_sip_header_to_t* header = belle_sip_header_to_parse("To: sip:linphone.org?header=interdit"); BC_ASSERT_FALSE(belle_sip_uri_check_components_from_context(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(header)),NULL,"To")); belle_sip_object_unref(header); } { belle_sip_header_contact_t* header = belle_sip_header_contact_parse("Contact: "); BC_ASSERT_FALSE(belle_sip_uri_check_components_from_context(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(header)),"REGISTER","Contact")); BC_ASSERT_TRUE(belle_sip_uri_check_components_from_context(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(header)),NULL,"Contact")); belle_sip_object_unref(header); } { belle_sip_header_record_route_t* header = belle_sip_header_record_route_parse("Record-Route: "); BC_ASSERT_FALSE(belle_sip_uri_check_components_from_context(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(header)),NULL,"Record-Route")); belle_sip_object_unref(header); } { belle_sip_uri_t* uri = belle_sip_uri_parse("sip:linphone.org:5061?header=toto"); BC_ASSERT_TRUE(belle_sip_uri_check_components_from_context(uri,NULL,"Any")); belle_sip_object_unref(uri); } } static void test_escaping_bad_chars(void){ char bad_uri[13] = { 'h', 'e', 'l', 'l', 'o', (char)0xa0, (char)0xc8, 'w', 'o', 'r', 'l', 'd', 0x0 }; char *escaped = belle_sip_uri_to_escaped_username(bad_uri); const char *expected="hello%a0%c8world"; BC_ASSERT_STRING_EQUAL(escaped, expected); belle_sip_free(escaped); } static belle_sip_header_address_t* test_header_address_parsing(const char* address, int expect_fail){ belle_sip_header_address_t* header_address = belle_sip_header_address_parse(address); if( expect_fail == TRUE ){ BC_ASSERT_PTR_NULL(header_address); } else { BC_ASSERT_PTR_NOT_NULL(header_address); } return header_address; } static void test_empty_password(void){ const char *address_fail = "sip:France:@+123456789"; const char *address_valid = "sip:France:@toto"; const char* passwd; belle_sip_header_address_t* headerAddr; belle_sip_uri_t* uri; (void)test_header_address_parsing(address_fail, TRUE); headerAddr = test_header_address_parsing(address_valid, FALSE); BC_ASSERT_PTR_NOT_NULL(headerAddr); uri = belle_sip_header_address_get_uri(headerAddr); BC_ASSERT_PTR_NOT_NULL(uri); passwd = belle_sip_uri_get_user_password(uri); BC_ASSERT_PTR_EQUAL(passwd, NULL); if (headerAddr) belle_sip_object_unref(headerAddr); } static test_t uri_tests[] = { TEST_NO_TAG("Simple URI", testSIMPLEURI), TEST_NO_TAG("Complex URI", testCOMPLEXURI), TEST_NO_TAG("Escaped username", test_escaped_username), TEST_NO_TAG("Escaped username with bad chars", test_escaping_bad_chars), TEST_NO_TAG("Escaped parameter", test_escaped_parameter), TEST_NO_TAG("Escaped passwd", test_escaped_passwd), TEST_NO_TAG("User passwd", test_user_passwd), TEST_NO_TAG("IP host", test_ip_host), TEST_NO_TAG("lr", test_lr), TEST_NO_TAG("maddr", test_maddr), TEST_NO_TAG("headers", test_headers), TEST_NO_TAG("Escaped headers", test_escaped_headers), TEST_NO_TAG("URI parameters", test_uri_parameters), TEST_NO_TAG("SIPS URI", testSIPSURI), TEST_NO_TAG("URI equals", test_uri_equals), TEST_NO_TAG("Simple URI error", testSIMPLEURI_error), TEST_NO_TAG("IPv6 URI", testIPV6URI), TEST_NO_TAG("URI components", testUriComponentsChecker), TEST_NO_TAG("Empty password", test_empty_password), }; belle-sip-1.6.3/tester/belle_sip_core_tester.c000066400000000000000000000310771313437522400214170ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle-sip/object.h" #include "belle-sip/dict.h" #include "belle_sip_tester.h" #include "belle_sip_internal.h" #include "register_tester.h" #ifndef _WIN32 #include #include #endif #define INT_TO_VOIDPTR(i) ((void*)(intptr_t)(i)) #define VOIDPTR_TO_INT(p) ((int)(intptr_t)(p)) static int foreach_called = 0; static int destroy_called = 0; static int clone_called = 0; static void test_object_data_string_destroy(void* data){ belle_sip_free(data); destroy_called++; } static void* test_object_data_string_clone(const char*name, void* data){ clone_called++; if( strcmp(name, "test_str") == 0) return belle_sip_strdup(data); else return data; } static void test_object_data_foreach_cb(const char*name, void*data, void*udata) { (void)name; (void)data; (void)udata; foreach_called++; } static void test_object_data(void) { belle_sip_object_t* obj = belle_sip_object_new(belle_sip_object_t); belle_sip_object_t* cloned = belle_sip_object_new(belle_sip_object_t); int i = 123; const char* str = "toto", *str2 = "titi"; /* normal insertion with no destroy callback */ // should return 0 BC_ASSERT_EQUAL(belle_sip_object_data_set(obj, "test_i", INT_TO_VOIDPTR(i), NULL), 0, int, "%d"); // should return the value we put in it BC_ASSERT_EQUAL( VOIDPTR_TO_INT(belle_sip_object_data_get(obj, "test_i")), i, int, "%d"); /* * Overwriting insertion */ // overwrite data: should return 1 when set() i = 124; BC_ASSERT_EQUAL(belle_sip_object_data_set(obj, "test_i", INT_TO_VOIDPTR(i), NULL), 1 , int, "%d"); // should return the new value we put in it BC_ASSERT_EQUAL( VOIDPTR_TO_INT(belle_sip_object_data_get(obj, "test_i")), i, int, "%d"); /* * normal insertion with destroy callback */ BC_ASSERT_EQUAL(belle_sip_object_data_set(obj, "test_str", (void*)belle_sip_strdup(str), test_object_data_string_destroy), 0, int, "%d"); // we should get back the same string BC_ASSERT_STRING_EQUAL( (const char*)belle_sip_object_data_get(obj, "test_str"), str ); BC_ASSERT_EQUAL(belle_sip_object_data_remove(obj, "test_str"),0, int, "%d"); // we expect the destroy() function to be called on removal BC_ASSERT_EQUAL(destroy_called, 1, int, "%d"); destroy_called = 0; /* * string insertion and replace */ belle_sip_object_data_set(obj, "test_str", (void*)belle_sip_strdup(str), test_object_data_string_destroy); belle_sip_object_data_set(obj, "test_str", (void*)belle_sip_strdup(str2), test_object_data_string_destroy); BC_ASSERT_EQUAL(destroy_called, 1, int, "%d"); // we expect the dtor to have been called to free the first string /* * Get non-existent key */ BC_ASSERT_PTR_NULL(belle_sip_object_data_get(obj, "non-exist")); /* * test cloning the dictionary */ belle_sip_object_data_clone(obj, cloned, test_object_data_string_clone); BC_ASSERT_EQUAL(clone_called,2,int,"%d"); // we expect the clone function to be called for "test_i" and "test_st, int, "%d"r" // the values should be equal BC_ASSERT_EQUAL( VOIDPTR_TO_INT(belle_sip_object_data_get(obj, "test_i")), VOIDPTR_TO_INT(belle_sip_object_data_get(cloned, "test_i")) , int, "%d"); BC_ASSERT_STRING_EQUAL( (const char*)belle_sip_object_data_get(obj, "test_str"), (const char*)belle_sip_object_data_get(cloned, "test_str")); // but the pointers should be different BC_ASSERT_PTR_NOT_EQUAL( belle_sip_object_data_get(obj, "test_str"), belle_sip_object_data_get(cloned, "test_str")); /* * Foreach test */ belle_sip_object_data_foreach(obj, test_object_data_foreach_cb, NULL); BC_ASSERT_EQUAL( foreach_called, 2 , int, "%d"); belle_sip_object_unref(obj); belle_sip_object_unref(cloned); } static void test_dictionary(void) { belle_sip_dict_t* obj = belle_sip_object_new(belle_sip_dict_t); const char* str = ""; int i = 5; int64_t i64 = 0xF2345678 << 1; // gcc doesn't like 0x1234567890 as a 64 bit literal.. belle_sip_dict_set_int(obj, "test_i", i); BC_ASSERT_EQUAL(belle_sip_dict_get_int(obj,"test_i",-1),i, int, "%d"); // return default int value BC_ASSERT_EQUAL(belle_sip_dict_get_int(obj,"unexistent",-1),-1, int, "%d"); // remove existing entry BC_ASSERT_EQUAL(belle_sip_dict_remove(obj, "test_i"),0, int, "%d"); // test_i should't be present anymore BC_ASSERT_EQUAL(belle_sip_dict_get_int(obj,"test_i",-1),-1, int, "%d"); // remove unknown entry BC_ASSERT_NOT_EQUAL(belle_sip_dict_remove(obj, "unexistent"),0,int,"%d"); belle_sip_dict_set_string(obj, "test_str", str); BC_ASSERT_STRING_EQUAL( (const char*)belle_sip_dict_get_string(obj, "test_str", ""),str); // unknown string value BC_ASSERT_STRING_EQUAL( (const char*)belle_sip_dict_get_string(obj, "unexistent", "toto"),"toto"); belle_sip_dict_set_int64(obj, "test_i64", i64); BC_ASSERT_EQUAL(belle_sip_dict_get_int64(obj,"test_i64",-1),i64, long long, "%lld"); belle_sip_dict_clear(obj); // test_str shouldn't exist anymore BC_ASSERT_STRING_EQUAL(belle_sip_dict_get_string(obj,"test_str","toto"),"toto"); belle_sip_object_unref(obj); } static const char *parts_id[] = { "6h-Yqv3@sip.linphonecyberattack.fr", "Azgwg6Q@sip.linphonecyberattack.fr", "0NKY7am@sip.linphonecyberattack.fr", "p8AxOjL@sip.linphonecyberattack.fr", "HqquRnL@sip.linphonecyberattack.fr", "ftVeEXj@sip.linphonecyberattack.fr", "3OQQ~t4@sip.linphonecyberattack.fr" }; static const char *parts_content[] = { "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n", "\n" "\n" " \n" " \n" " open\n" " \n" " sip:francois@sip.linphonecyberattack.fr\n" " 2016-06-13T16:12:48\n" " \n" "", "\n" "\n" " \n" " \n" " open\n" " \n" " sip:simon@sip.linphonecyberattack.fr\n" " 2016-06-13T16:12:48\n" " \n" "", "\n" "\n" " \n" " \n" " open\n" " \n" " sip:gautier@sip.linphonecyberattack.fr\n" " 2016-06-13T16:12:48\n" " \n" "", "\n" "\n" " \n" " \n" " open\n" " \n" " sip:margaux@sip.linphonecyberattack.fr\n" " 2016-06-13T16:12:48\n" " \n" "", "\n" "\n" " \n" " \n" " open\n" " \n" " sip:marielle@sip.linphonecyberattack.fr\n" " 2016-06-13T16:12:48\n" " \n" "", "\n" "\n" " \n" " \n" " open\n" " \n" " sip:sylvain@sip.linphonecyberattack.fr\n" " 2016-06-13T16:12:48\n" " \n" "" }; static void test_presence_marshal(void) { char *desc; unsigned int i; belle_sip_memory_body_handler_t *mbh; belle_sip_multipart_body_handler_t *mpbh = belle_sip_multipart_body_handler_new(NULL, NULL, NULL, MULTIPART_BOUNDARY); belle_sip_object_ref(mpbh); for (i = 0; i < 7; i++) { belle_sip_header_t *content_transfer_encoding = belle_sip_header_create("Content-Transfer-Encoding", "binary"); belle_sip_header_t *content_id = belle_sip_header_create("Content-Id", parts_id[i]); belle_sip_header_t *content_type; if (i == 0) { content_type = BELLE_SIP_HEADER(belle_sip_header_content_type_create("application", "rlmi+xml; charset=\"UTF-8\"")); } else { content_type = BELLE_SIP_HEADER(belle_sip_header_content_type_create("application", "pidf+xml; charset=\"UTF-8\"")); } mbh = belle_sip_memory_body_handler_new_copy_from_buffer((void *)parts_content[i], strlen(parts_content[i]), NULL, NULL); belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(mbh), content_transfer_encoding); belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(mbh), content_id); belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(mbh), content_type); belle_sip_multipart_body_handler_add_part(mpbh, BELLE_SIP_BODY_HANDLER(mbh)); } desc = belle_sip_object_to_string(mpbh); BC_ASSERT_PTR_NOT_NULL(desc); if (desc != NULL) { BC_ASSERT_EQUAL((unsigned int)strlen(desc), 4676, unsigned int, "%u"); } belle_sip_object_unref(mpbh); } static void test_compressed_body(void) { belle_sip_memory_body_handler_t *mbh = belle_sip_memory_body_handler_new_copy_from_buffer((void *)parts_content[0], strlen(parts_content[0]), NULL, NULL); belle_sip_memory_body_handler_apply_encoding(mbh, "deflate"); BC_ASSERT_EQUAL(belle_sip_memory_body_handler_unapply_encoding(mbh, "deflate"), 0, int, "%i"); belle_sip_object_unref(mbh); } static void test_truncated_compressed_body(void) { belle_sip_memory_body_handler_t *mbh = belle_sip_memory_body_handler_new_copy_from_buffer((void *)parts_content[0], strlen(parts_content[0]), NULL, NULL); belle_sip_memory_body_handler_apply_encoding(mbh, "deflate"); /* Truncate the body */ belle_sip_body_handler_set_size(BELLE_SIP_BODY_HANDLER(mbh), belle_sip_body_handler_get_size(BELLE_SIP_BODY_HANDLER(mbh)) - 5); BC_ASSERT_EQUAL(belle_sip_memory_body_handler_unapply_encoding(mbh, "deflate"), -1, int, "%i"); belle_sip_object_unref(mbh); } test_t core_tests[] = { TEST_NO_TAG("Object Data", test_object_data), TEST_NO_TAG("Dictionary", test_dictionary), TEST_NO_TAG("Presence marshal", test_presence_marshal), TEST_NO_TAG("Compressed body", test_compressed_body), TEST_NO_TAG("Truncated compressed body", test_truncated_compressed_body) }; test_suite_t core_test_suite = {"Core", NULL, NULL, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(core_tests) / sizeof(core_tests[0]), core_tests}; belle-sip-1.6.3/tester/belle_sip_dialog_tester.c000066400000000000000000000433761313437522400217330ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_tester.h" #include "register_tester.h" static const char* sdp = "v=0\r\n"\ "o=jehan-mac 1239 1239 IN IP4 192.168.0.18\r\n"\ "s=Talk\r\n"\ "c=IN IP4 192.168.0.18\r\n"\ "t=0 0\r\n"\ "m=audio 7078 RTP/AVP 111 110 3 0 8 101\r\n"\ "a=rtpmap:111 speex/16000\r\n"\ "a=fmtp:111 vbr=on\r\n"\ "a=rtpmap:110 speex/8000\r\n"\ "a=fmtp:110 vbr=on\r\n"\ "a=rtpmap:101 telephone-event/8000\r\n"\ "a=fmtp:101 0-11\r\n"\ "m=video 8078 RTP/AVP 99 97 98\r\n"\ "c=IN IP4 192.168.0.18\r\n"\ "b=AS:380\r\n"\ "a=rtpmap:99 MP4V-ES/90000\r\n"\ "a=fmtp:99 profile-level-id=3\r\n"\ "a=rtpmap:97 theora/90000\r\n"\ "a=rtpmap:98 H263-1998/90000\r\n"\ "a=fmtp:98 CIF=1;QCIF=1\r\n"; static belle_sip_dialog_t* caller_dialog; static belle_sip_dialog_t* callee_dialog; static belle_sip_response_t* ok_response; static belle_sip_server_transaction_t* inserv_transaction; belle_sip_request_t* build_request(belle_sip_stack_t * stack , belle_sip_provider_t *prov ,belle_sip_header_address_t* from ,belle_sip_header_address_t* to ,belle_sip_header_address_t* route ,const char* method) { belle_sip_header_from_t* from_header; belle_sip_header_to_t* to_header; belle_sip_request_t *req; belle_sip_uri_t* req_uri; belle_sip_header_contact_t* contact_header; BELLESIP_UNUSED(stack); from_header = belle_sip_header_from_create(from,BELLE_SIP_RANDOM_TAG); to_header = belle_sip_header_to_create(to,NULL); req_uri = (belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)belle_sip_header_address_get_uri((belle_sip_header_address_t*)to_header)); contact_header= belle_sip_header_contact_new(); belle_sip_header_address_set_uri((belle_sip_header_address_t*)contact_header,belle_sip_uri_new()); belle_sip_uri_set_user(belle_sip_header_address_get_uri((belle_sip_header_address_t*)contact_header),belle_sip_uri_get_user(req_uri)); req=belle_sip_request_create( req_uri, method, belle_sip_provider_create_call_id(prov), belle_sip_header_cseq_create(20,method), from_header, to_header, belle_sip_header_via_new(), 70); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(contact_header)); if (route) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_route_create(route))); } return req; } static void process_dialog_terminated(void *user_ctx, const belle_sip_dialog_terminated_event_t *event){ BELLESIP_UNUSED(user_ctx); BELLESIP_UNUSED(event); belle_sip_message("process_dialog_terminated not implemented yet"); } static void process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){ BELLESIP_UNUSED(user_ctx); BELLESIP_UNUSED(event); belle_sip_message("process_io_error not implemented yet"); } static void caller_process_request_event(void *user_ctx, const belle_sip_request_event_t *event) { belle_sip_server_transaction_t* server_transaction; belle_sip_response_t* resp; belle_sip_dialog_t* dialog; belle_sip_header_to_t* to=belle_sip_message_get_header_by_type(belle_sip_request_event_get_request(event),belle_sip_header_to_t); if (!belle_sip_uri_equals(BELLE_SIP_URI(user_ctx),belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to)))) { belle_sip_message("Message [%p] not for caller, skipping",belle_sip_request_event_get_request(event)); return; /*not for the caller*/ } belle_sip_message("caller_process_request_event received [%s] message",belle_sip_request_get_method(belle_sip_request_event_get_request(event))); server_transaction=belle_sip_provider_create_server_transaction(prov,belle_sip_request_event_get_request(event)); BC_ASSERT_STRING_EQUAL("BYE",belle_sip_request_get_method(belle_sip_request_event_get_request(event))); dialog = belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(server_transaction)); if (BC_ASSERT_PTR_NOT_NULL(dialog)) { BC_ASSERT_EQUAL(belle_sip_dialog_get_state(dialog) , BELLE_SIP_DIALOG_CONFIRMED, int, "%d"); } resp=belle_sip_response_create_from_request(belle_sip_request_event_get_request(event),200); belle_sip_server_transaction_send_response(server_transaction,resp); } static void callee_process_request_event(void *user_ctx, const belle_sip_request_event_t *event) { belle_sip_dialog_t* dialog; belle_sip_response_t* ringing_response; belle_sip_header_content_type_t* content_type ; belle_sip_header_content_length_t* content_length; belle_sip_server_transaction_t* server_transaction = belle_sip_request_event_get_server_transaction(event); belle_sip_header_to_t* to=belle_sip_message_get_header_by_type(belle_sip_request_event_get_request(event),belle_sip_header_to_t); const char* method; if (!belle_sip_uri_equals(BELLE_SIP_URI(user_ctx),belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(to)))) { belle_sip_message("Message [%p] not for callee, skipping",belle_sip_request_event_get_request(event)); return; /*not for the callee*/ } method = belle_sip_request_get_method(belle_sip_request_event_get_request(event)); if (!server_transaction && strcmp(method,"ACK")!=0) { server_transaction= belle_sip_provider_create_server_transaction(prov,belle_sip_request_event_get_request(event)); } belle_sip_message("callee_process_request_event received [%s] message",method); dialog = belle_sip_request_event_get_dialog(event); if (!dialog ) { BC_ASSERT_STRING_EQUAL("INVITE",method); dialog=belle_sip_provider_create_dialog(prov,BELLE_SIP_TRANSACTION(server_transaction)); callee_dialog=dialog; inserv_transaction=server_transaction; } if (belle_sip_dialog_get_state(dialog) == BELLE_SIP_DIALOG_NULL) { ringing_response = belle_sip_response_create_from_request(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction)),180); /*prepare 200ok*/ ok_response = belle_sip_response_create_from_request(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction)),200); content_length= belle_sip_header_content_length_create(strlen(sdp)); content_type = belle_sip_header_content_type_create("application","sdp"); belle_sip_message_add_header(BELLE_SIP_MESSAGE(ok_response),BELLE_SIP_HEADER(content_type)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(ok_response),BELLE_SIP_HEADER(content_length)); belle_sip_message_set_body(BELLE_SIP_MESSAGE(ok_response),sdp,strlen(sdp)); belle_sip_object_ref(ok_response); /*only send ringing*/ belle_sip_server_transaction_send_response(server_transaction,ringing_response); } else if (belle_sip_dialog_get_state(dialog) == BELLE_SIP_DIALOG_CONFIRMED) { /*time to send bye*/ belle_sip_client_transaction_t* client_transaction = belle_sip_provider_create_client_transaction(prov,belle_sip_dialog_create_request(dialog,"BYE")); belle_sip_client_transaction_send_request(client_transaction); } else { belle_sip_warning("Unexpected state [%s] for dialog [%p]",belle_sip_dialog_state_to_string(belle_sip_dialog_get_state(dialog)),dialog ); } } static void caller_process_response_event(void *user_ctx, const belle_sip_response_event_t *event){ belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event); belle_sip_header_from_t* from=belle_sip_message_get_header_by_type(belle_sip_response_event_get_response(event),belle_sip_header_from_t); belle_sip_header_cseq_t* invite_cseq=belle_sip_message_get_header_by_type(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)),belle_sip_header_cseq_t); belle_sip_request_t* ack; belle_sip_dialog_t* dialog; int status; if (!belle_sip_uri_equals(BELLE_SIP_URI(user_ctx),belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from)))) { belle_sip_message("Message [%p] not for caller, skipping",belle_sip_response_event_get_response(event)); return; /*not for the caller*/ } status = belle_sip_response_get_status_code(belle_sip_response_event_get_response(event)); belle_sip_message("caller_process_response_event [%i]",status); if (BC_ASSERT_PTR_NOT_NULL(client_transaction)) { dialog = belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(client_transaction)); if (BC_ASSERT_PTR_NOT_NULL(dialog)) { BC_ASSERT_PTR_EQUAL(caller_dialog,dialog); if (belle_sip_dialog_get_state(dialog) == BELLE_SIP_DIALOG_NULL) { BC_ASSERT_EQUAL(status,100, int, "%d"); } else if (belle_sip_dialog_get_state(dialog) == BELLE_SIP_DIALOG_EARLY){ BC_ASSERT_EQUAL(status,180, int, "%d"); /*send 200ok from callee*/ belle_sip_server_transaction_send_response(inserv_transaction,ok_response); belle_sip_object_unref(ok_response); ok_response=NULL; } else if (belle_sip_dialog_get_state(dialog) == BELLE_SIP_DIALOG_CONFIRMED) { ack=belle_sip_dialog_create_ack(dialog,belle_sip_header_cseq_get_seq_number(invite_cseq)); belle_sip_dialog_send_ack(dialog,ack); } } } } static void callee_process_response_event(void *user_ctx, const belle_sip_response_event_t *event){ belle_sip_dialog_t* dialog; belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event); belle_sip_header_from_t* from=belle_sip_message_get_header_by_type(belle_sip_response_event_get_response(event),belle_sip_header_from_t); int status = belle_sip_response_get_status_code(belle_sip_response_event_get_response(event)); if (!belle_sip_uri_equals(BELLE_SIP_URI(user_ctx),belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from)))) { belle_sip_message("Message [%p] not for callee, skipping",belle_sip_response_event_get_response(event)); return; /*not for the callee*/ } if (BC_ASSERT_PTR_NOT_NULL(client_transaction)) { dialog = belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(client_transaction)); if (BC_ASSERT_PTR_NOT_NULL(dialog)) { BC_ASSERT_PTR_EQUAL(callee_dialog,dialog); if (belle_sip_dialog_get_state(dialog) == BELLE_SIP_DIALOG_TERMINATED) { call_endeed=1; belle_sip_main_loop_quit(belle_sip_stack_get_main_loop(stack)); } belle_sip_message("callee_process_response_event [%i] on dialog [%p] for state [%s]",status ,dialog ,belle_sip_dialog_state_to_string(belle_sip_dialog_get_state(dialog))); } } } static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) { BELLESIP_UNUSED(user_ctx); /* belle_sip_client_transaction_t* client_transaction = belle_sip_timeout_event_get_client_transaction(event); SalOp* op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction)); if (op->callbacks.process_timeout) { op->callbacks.process_timeout(op,event); } else*/ { belle_sip_message("Unhandled event timeout [%p]",event); } } static void process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event) { BELLESIP_UNUSED(user_ctx); /* belle_sip_client_transaction_t* client_transaction = belle_sip_transaction_terminated_event_get_client_transaction(event); SalOp* op = (SalOp*)belle_sip_transaction_get_application_data(client_transaction); if (op->calbacks.process_transaction_terminated) { op->calbacks.process_transaction_terminated(op,event); } else */{ belle_sip_message("Unhandled transaction terminated [%p]",event); } } static void listener_destroyed(void *user_ctx){ belle_sip_object_unref(user_ctx); } static void do_simple_call(void) { #define CALLER "marie" #define CALLEE "pauline" belle_sip_request_t *pauline_register_req; belle_sip_request_t *marie_register_req; belle_sip_listener_callbacks_t caller_listener_callbacks; belle_sip_listener_t* caller_listener; belle_sip_listener_callbacks_t callee_listener_callbacks; belle_sip_listener_t* callee_listener; belle_sip_request_t* req; belle_sip_header_address_t* from; belle_sip_header_address_t* to; belle_sip_header_address_t* route; belle_sip_header_allow_t* header_allow; belle_sip_header_content_type_t* content_type ; belle_sip_header_content_length_t* content_length; belle_sip_client_transaction_t* client_transaction; char cookie[4]; caller_listener_callbacks.process_dialog_terminated=process_dialog_terminated; caller_listener_callbacks.process_io_error=process_io_error; caller_listener_callbacks.process_request_event=caller_process_request_event; caller_listener_callbacks.process_response_event=caller_process_response_event; caller_listener_callbacks.process_timeout=process_timeout; caller_listener_callbacks.process_transaction_terminated=process_transaction_terminated; caller_listener_callbacks.listener_destroyed=listener_destroyed; callee_listener_callbacks.process_dialog_terminated=process_dialog_terminated; callee_listener_callbacks.process_io_error=process_io_error; callee_listener_callbacks.process_request_event=callee_process_request_event; callee_listener_callbacks.process_response_event=callee_process_response_event; callee_listener_callbacks.process_timeout=process_timeout; callee_listener_callbacks.process_transaction_terminated=process_transaction_terminated; callee_listener_callbacks.listener_destroyed=listener_destroyed; pauline_register_req=register_user(stack, prov, "TCP" ,1 ,CALLER,NULL); if (belle_sip_provider_get_listening_point(prov, "tls")) { marie_register_req=register_user(stack, prov, "TLS" ,1 ,CALLEE,NULL); } else { marie_register_req=register_user(stack, prov, "TCP" ,1 ,CALLEE,NULL); } from=belle_sip_header_address_create(NULL,belle_sip_uri_create(CALLER,test_domain)); /*to make sure unexpected messages are ignored*/ belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(from),"cookie",belle_sip_random_token(cookie,sizeof(cookie))); /*to make sure unexpected messages are ignored*/ to=belle_sip_header_address_create(NULL,belle_sip_uri_create(CALLEE,test_domain)); belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(to),"cookie",belle_sip_random_token(cookie,sizeof(cookie))); belle_sip_provider_add_sip_listener(prov,caller_listener=belle_sip_listener_create_from_callbacks(&caller_listener_callbacks,belle_sip_object_ref(belle_sip_header_address_get_uri((belle_sip_header_address_t*)from)))); belle_sip_provider_add_sip_listener(prov,callee_listener=belle_sip_listener_create_from_callbacks(&callee_listener_callbacks,belle_sip_object_ref(belle_sip_header_address_get_uri((belle_sip_header_address_t*)to)))); route = belle_sip_header_address_create(NULL,belle_sip_uri_create(NULL,test_domain)); belle_sip_uri_set_transport_param(belle_sip_header_address_get_uri(route),"tcp"); req=build_request(stack,prov,from,to,route,"INVITE"); header_allow = belle_sip_header_allow_create("INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(header_allow)); content_length= belle_sip_header_content_length_create(strlen(sdp)); content_type = belle_sip_header_content_type_create("application","sdp"); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(content_type)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(content_length)); belle_sip_message_set_body(BELLE_SIP_MESSAGE(req),sdp,strlen(sdp)); client_transaction = belle_sip_provider_create_client_transaction(prov,req); caller_dialog=belle_sip_provider_create_dialog(prov,BELLE_SIP_TRANSACTION(client_transaction)); BC_ASSERT_PTR_NOT_NULL(belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(client_transaction))); //belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),op); call_endeed=0; belle_sip_client_transaction_send_request(client_transaction); //int i=0; //for(i=0;i<10 &&!call_endeed;i++) belle_sip_stack_sleep(stack,30000); BC_ASSERT_EQUAL(call_endeed,1, int, "%d"); belle_sip_provider_remove_sip_listener(prov,caller_listener); belle_sip_provider_remove_sip_listener(prov,callee_listener); belle_sip_object_unref(caller_listener); belle_sip_object_unref(callee_listener); unregister_user(stack, prov, pauline_register_req ,1); belle_sip_object_unref(pauline_register_req); unregister_user(stack, prov, marie_register_req ,1); belle_sip_object_unref(marie_register_req); } static void simple_call(void){ belle_sip_stack_set_tx_delay(stack,0); do_simple_call(); } static void simple_call_with_delay(void){ belle_sip_stack_set_tx_delay(stack,2000); do_simple_call(); belle_sip_stack_set_tx_delay(stack,0); } /*static void simple_call_udp_tcp_with_delay(void){ belle_sip_listening_point_t* lp=belle_sip_provider_get_listening_point(prov,"tls"); belle_sip_object_ref(lp); belle_sip_stack_set_tx_delay(stack,2000); belle_sip_provider_remove_listening_point(prov,lp); do_simple_call(); belle_sip_stack_set_tx_delay(stack,0); belle_sip_provider_add_listening_point(prov,lp); belle_sip_object_unref(lp); }*/ test_t dialog_tests[] = { TEST_ONE_TAG("Simple call", simple_call, "LeaksMemory"), TEST_ONE_TAG("Simple call with delay", simple_call_with_delay, "LeaksMemory"), }; test_suite_t dialog_test_suite = {"Dialog", register_before_all, register_after_all, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(dialog_tests) / sizeof(dialog_tests[0]), dialog_tests}; belle-sip-1.6.3/tester/belle_sip_fast_uri_tester.c000066400000000000000000000043061313437522400222760ustar00rootroot00000000000000 /* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_tester.h" #include "belle_sip_internal.h" #include #define belle_sip_uri_parse belle_sip_fast_uri_parse /*test body*/ #include "belle_sip_base_uri_tester.c" #undef belle_sip_uri_parse static void perf(void) { uint64_t t1, t2, start=bctbx_get_cur_time_ms(); int i=0; for (i=0;i<1000;i++) { belle_sip_uri_t * uri = belle_sip_uri_parse("sip:+331231231231@sip.exmaple.org;user=phone"); belle_sip_object_unref(uri); } t1 = bctbx_get_cur_time_ms() - start; start=bctbx_get_cur_time_ms(); belle_sip_message("t1 = %" PRIu64 "",t1); for (i=0;i<1000;i++) { belle_sip_uri_t * uri = belle_sip_fast_uri_parse("sip:+331231231231@sip.exmaple.org;user=phone"); belle_sip_object_unref(uri); } t2 = bctbx_get_cur_time_ms() - start; belle_sip_message("t2 = %" PRIu64 "",t2); #ifdef __APPLE__ /*antlr3.4 seems much more sensitive to belle_sip_fast_uri_parse optimisation than 3.2, so reserving this test to apple platform where antlr3.2 is unlikely to be found*/ BC_ASSERT_GREATER(((float)(t1-t2))/(float)(t1), 0.4, float, "%f"); #endif } static test_t tests[] ={TEST_NO_TAG("perf", perf)}; test_suite_t fast_sip_uri_test_suite = {"FAST SIP URI", NULL, NULL, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(uri_tests) / sizeof(uri_tests[0]), uri_tests}; test_suite_t perf_sip_uri_test_suite = {"FAST SIP URI 2", NULL, NULL, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(tests) / sizeof(tests[0]), tests}; belle-sip-1.6.3/tester/belle_sip_headers_tester.c000066400000000000000000001777561313437522400221210ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_internal.h" #include "belle_sip_tester.h" static void test_simple_contact_header(void) { belle_sip_header_contact_t* L_tmp; belle_sip_uri_t* L_uri; belle_sip_header_contact_t* L_contact = belle_sip_header_contact_parse("m:sip:titi.com"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_contact)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_contact)); L_tmp = belle_sip_header_contact_parse(l_raw_header); L_contact = BELLE_SIP_HEADER_CONTACT(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); L_uri = belle_sip_header_address_get_uri((belle_sip_header_address_t*)L_contact); BC_ASSERT_PTR_NULL(belle_sip_uri_get_user(L_uri)); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "titi.com"); BC_ASSERT_PTR_NULL(belle_sip_uri_get_transport_param(L_uri)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_contact)); BC_ASSERT_PTR_NULL(belle_sip_header_contact_parse("nimportequoi")); } static void test_complex_contact_header(void) { belle_sip_header_contact_t* L_contact; belle_sip_uri_t* L_uri; belle_sip_header_contact_t* L_tmp = belle_sip_header_contact_parse("Contact: \"j\x8eremis\" ;expires=3600;q=0.7, sip:titi.com"); belle_sip_header_t* l_next; belle_sip_header_contact_t* L_next_contact; char* l_raw_header; float l_qvalue; L_contact = BELLE_SIP_HEADER_CONTACT(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); L_uri = belle_sip_header_address_get_uri((belle_sip_header_address_t*)L_contact); BC_ASSERT_PTR_NOT_NULL(L_uri); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "sip.linphone.org"); BC_ASSERT_STRING_EQUAL(belle_sip_header_address_get_displayname((belle_sip_header_address_t*)L_contact), "j\x8eremis"); BC_ASSERT_EQUAL(belle_sip_header_contact_get_expires(L_contact),3600,int,"%d"); l_qvalue = belle_sip_header_contact_get_qvalue(L_contact); BC_ASSERT_EQUAL(l_qvalue,0.7f,float,"%f"); l_next = belle_sip_header_get_next(BELLE_SIP_HEADER(L_contact)); L_next_contact = BELLE_SIP_HEADER_CONTACT(l_next); BC_ASSERT_PTR_NOT_NULL(L_next_contact); BC_ASSERT_PTR_NOT_NULL( belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_next_contact))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_contact)); L_contact = belle_sip_header_contact_parse("Contact: super toto ;expires=3600; q=0.7"); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_contact)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_contact)); L_contact = belle_sip_header_contact_parse(l_raw_header); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_address_get_displayname((belle_sip_header_address_t*)L_contact), "super toto"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_contact)); BC_ASSERT_PTR_NULL(belle_sip_header_contact_parse("m:sip:titi.com, nimportequoi")); } static void test_from_header(void) { belle_sip_header_from_t* L_tmp; belle_sip_uri_t* L_uri; belle_generic_uri_t* L_absoluteUri; belle_sip_header_from_t* L_from = belle_sip_header_from_parse("f:;tag=dlfjklcn6545614XX"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_from)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_from)); L_tmp = belle_sip_header_from_parse(l_raw_header); L_from = BELLE_SIP_HEADER_FROM(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_from)); BC_ASSERT_PTR_NULL(belle_sip_uri_get_user(L_uri)); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "titi.com"); BC_ASSERT_STRING_EQUAL(belle_sip_header_from_get_tag(L_from),"dlfjklcn6545614XX"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_from)); L_from = belle_sip_header_from_parse("From: ;tag=dlfjklcn6545614XX"); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_from)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_from)); L_tmp = belle_sip_header_from_parse(l_raw_header); L_from = BELLE_SIP_HEADER_FROM(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); L_absoluteUri = belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(L_from)); BC_ASSERT_PTR_NULL(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_from))); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_opaque_part(L_absoluteUri), "+33124585454;toto=titi"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_scheme(L_absoluteUri), "tel"); BC_ASSERT_STRING_EQUAL(belle_sip_header_from_get_tag(L_from),"dlfjklcn6545614XX"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_from)); /*test factory*/ L_from = belle_sip_header_from_create2("super ","12345-abc"); if (!BC_ASSERT_PTR_NOT_NULL(L_from)) return; L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_from)); if (!BC_ASSERT_PTR_NOT_NULL(L_uri)) return; BC_ASSERT_FALSE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_uri),"lr")); BC_ASSERT_FALSE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_uri),"ttl")); BC_ASSERT_FALSE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_uri),"method")); BC_ASSERT_FALSE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_uri),"maddr")); BC_ASSERT_FALSE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_uri),"transport")); BC_ASSERT_FALSE(belle_sip_uri_get_port(L_uri)>0); BC_ASSERT_EQUAL((unsigned int)belle_sip_list_size(belle_sip_uri_get_header_names(L_uri)),0,unsigned int,"%u"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "titi.com"); BC_ASSERT_STRING_EQUAL(belle_sip_header_from_get_tag(L_from),"12345-abc"); belle_sip_object_unref(L_from); BC_ASSERT_PTR_NULL(belle_sip_header_from_parse("nimportequoi")); } static void test_from_header_with_paramless_address_spec(void) { belle_generic_uri_t *L_absoluteUri; belle_sip_header_from_t* L_from = belle_sip_header_from_parse("From: sip:bob@titi.com;tag=dlfjklcn6545614XX"); if (!BC_ASSERT_PTR_NOT_NULL(belle_sip_header_from_get_tag(L_from))) return; BC_ASSERT_STRING_EQUAL(belle_sip_header_from_get_tag(L_from),"dlfjklcn6545614XX"); belle_sip_object_unref(L_from); L_from = belle_sip_header_from_parse("From: tel:1234567;tag=dlfjklcn6545614XX"); if (!BC_ASSERT_PTR_NOT_NULL(belle_sip_header_from_get_tag(L_from))) return; L_absoluteUri = belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(L_from)); BC_ASSERT_PTR_NULL(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_from))); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_opaque_part(L_absoluteUri), "1234567"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_scheme(L_absoluteUri), "tel"); BC_ASSERT_STRING_EQUAL(belle_sip_header_from_get_tag(L_from),"dlfjklcn6545614XX"); belle_sip_object_unref(L_from); } static void test_to_header_with_paramless_address_spec(void) { belle_generic_uri_t *L_absoluteUri; belle_sip_header_to_t* L_to = belle_sip_header_to_parse("To: sip:bob@titi.com;tag=dlfjklcn6545614XX"); belle_sip_uri_t *L_uri; if (!BC_ASSERT_PTR_NOT_NULL(belle_sip_header_to_get_tag(L_to))) return; BC_ASSERT_STRING_EQUAL(belle_sip_header_to_get_tag(L_to),"dlfjklcn6545614XX"); belle_sip_object_unref(L_to); L_to = belle_sip_header_to_parse("To:sip:1002@192.168.1.199;tag=as1f0a0817"); if (!BC_ASSERT_PTR_NOT_NULL(belle_sip_header_to_get_tag(L_to))) return; L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_to)); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user(L_uri),"1002"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "192.168.1.199"); BC_ASSERT_STRING_EQUAL(belle_sip_header_to_get_tag(L_to),"as1f0a0817"); belle_sip_object_unref(L_to); L_to = belle_sip_header_to_parse("To: tel:1234567;tag=dlfjklcn6545614XX"); if (!BC_ASSERT_PTR_NOT_NULL(belle_sip_header_to_get_tag(L_to))) return; L_absoluteUri = belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(L_to)); BC_ASSERT_PTR_NULL(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_to))); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_opaque_part(L_absoluteUri), "1234567"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_scheme(L_absoluteUri), "tel"); BC_ASSERT_STRING_EQUAL(belle_sip_header_to_get_tag(L_to),"dlfjklcn6545614XX"); belle_sip_object_unref(L_to); BC_ASSERT_PTR_NULL(belle_sip_header_to_parse("nimportequoi")); } static void test_contact_header_with_paramless_address_spec(void) { belle_sip_header_contact_t* L_contact = belle_sip_header_contact_parse("Contact: sip:bob@titi.com;expires=60"); BC_ASSERT_EQUAL(belle_sip_header_contact_get_expires(L_contact),60,int,"%d"); belle_sip_object_unref(L_contact); } static void test_to_header(void) { belle_sip_uri_t* L_uri; belle_generic_uri_t* L_absoluteUri; belle_sip_header_to_t *L_tmp, *L_to = belle_sip_header_to_parse("t : < sip:titi.com;transport=tcp> ; tag = dlfjklcn6545614XX"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_to)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_to)); L_to = belle_sip_header_to_parse(l_raw_header); belle_sip_free(l_raw_header); L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_to)); BC_ASSERT_PTR_NULL(belle_sip_uri_get_user(L_uri)); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "titi.com"); BC_ASSERT_STRING_EQUAL(belle_sip_header_to_get_tag(L_to),"dlfjklcn6545614XX"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_to)); /*test factory*/ L_to = belle_sip_header_to_create2("\"super man\" ","12345-abc"); if (!BC_ASSERT_PTR_NOT_NULL(L_to)) return; L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_to)); BC_ASSERT_FALSE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_uri),"lr")); BC_ASSERT_FALSE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_uri),"ttl")); BC_ASSERT_FALSE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_uri),"method")); BC_ASSERT_FALSE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_uri),"maddr")); BC_ASSERT_FALSE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_uri),"transport")); BC_ASSERT_FALSE(belle_sip_uri_get_port(L_uri)>0); BC_ASSERT_EQUAL((unsigned int)belle_sip_list_size(belle_sip_uri_get_header_names(L_uri)),0,unsigned int,"%u"); if (!BC_ASSERT_PTR_NOT_NULL(L_uri)) return; BC_ASSERT_STRING_EQUAL(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(L_to)), "super man"); BC_ASSERT_STRING_EQUAL(belle_sip_header_to_get_tag(L_to),"12345-abc"); belle_sip_object_unref(L_to); L_to = belle_sip_header_to_parse("To: ;tag=dlfjklcn6545614XX"); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_to)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_to)); L_tmp = belle_sip_header_to_parse(l_raw_header); L_to = BELLE_SIP_HEADER_TO(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); L_absoluteUri = belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(L_to)); BC_ASSERT_PTR_NULL(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_to))); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_opaque_part(L_absoluteUri), "+33124585454;toto=titi"); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_scheme(L_absoluteUri), "tel"); BC_ASSERT_STRING_EQUAL(belle_sip_header_to_get_tag(L_to),"dlfjklcn6545614XX"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_to)); } static void test_via_header(void) { belle_sip_header_via_t* L_tmp; belle_sip_header_t* l_next; belle_sip_header_via_t* L_next_via; belle_sip_header_via_t* L_via = belle_sip_header_via_parse("v: SIP/2.0/UDP [::1]:5062;rport;received=::1;branch=z9hG4bK368560724"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_via)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_via)); L_tmp = belle_sip_header_via_parse(l_raw_header); L_via = BELLE_SIP_HEADER_VIA(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_via_get_protocol(L_via), "SIP/2.0"); BC_ASSERT_STRING_EQUAL(belle_sip_header_via_get_transport(L_via), "UDP"); BC_ASSERT_STRING_EQUAL(belle_sip_header_via_get_host(L_via), "::1"); BC_ASSERT_EQUAL(belle_sip_header_via_get_port(L_via),5062,int,"%d"); BC_ASSERT_TRUE(belle_sip_parameters_has_parameter(BELLE_SIP_PARAMETERS(L_via),"rport")); BC_ASSERT_STRING_EQUAL(belle_sip_header_via_get_received(L_via),"::1"); BC_ASSERT_STRING_EQUAL(belle_sip_header_via_get_branch(L_via),"z9hG4bK368560724"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_via)); L_via = belle_sip_header_via_parse("Via: SIP/2.0/UDP 192.168.0.19:5062;received=192.169.0.4;rport=1234;branch=z9hG4bK368560724, SIP/2.0/UDP 192.168.0.19:5062"); l_next = belle_sip_header_get_next(BELLE_SIP_HEADER(L_via)); L_next_via = BELLE_SIP_HEADER_VIA(l_next); BC_ASSERT_PTR_NOT_NULL(L_next_via); BC_ASSERT_STRING_EQUAL(belle_sip_header_via_get_host(L_next_via),"192.168.0.19"); BC_ASSERT_STRING_EQUAL(belle_sip_header_via_get_received(L_via),"192.169.0.4"); BC_ASSERT_EQUAL(belle_sip_header_via_get_rport(L_via),1234,int,"%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_via)); BC_ASSERT_PTR_NULL(belle_sip_header_via_parse("nimportequoi")); BC_ASSERT_PTR_NULL(belle_sip_header_contact_parse("Via: SIP/2.0/UDP 192.168.0.19:5062;branch=z9hG4bK368560724, nimportequoi")); } static void test_call_id_header(void) { belle_sip_header_call_id_t* L_tmp; belle_sip_header_call_id_t* L_call_id = belle_sip_header_call_id_parse("i: 1665237789@titi.com"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_call_id)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_call_id)); L_tmp= belle_sip_header_call_id_parse(l_raw_header); L_call_id = BELLE_SIP_HEADER_CALL_ID(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_call_id_get_call_id(L_call_id), "1665237789@titi.com"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_call_id)); BC_ASSERT_PTR_NULL(belle_sip_header_call_id_parse("nimportequoi")); } static void test_cseq_header(void) { belle_sip_header_cseq_t* L_tmp; belle_sip_header_cseq_t* L_cseq = belle_sip_header_cseq_parse("CSeq: 21 INVITE"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_cseq)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_cseq)); L_tmp = belle_sip_header_cseq_parse(l_raw_header); L_cseq = BELLE_SIP_HEADER_CSEQ(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_EQUAL(belle_sip_header_cseq_get_seq_number(L_cseq),21,int,"%d"); BC_ASSERT_STRING_EQUAL(belle_sip_header_cseq_get_method(L_cseq),"INVITE"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_cseq)); /*test factory*/ L_cseq = belle_sip_header_cseq_create(1,"INFO"); BC_ASSERT_EQUAL(belle_sip_header_cseq_get_seq_number(L_cseq),1,int,"%d"); BC_ASSERT_STRING_EQUAL(belle_sip_header_cseq_get_method(L_cseq),"INFO"); belle_sip_object_unref(L_cseq); BC_ASSERT_PTR_NULL(belle_sip_header_cseq_parse("nimportequoi")); } static void test_content_type_header(void) { belle_sip_header_content_type_t* L_tmp; belle_sip_header_content_type_t* L_content_type = belle_sip_header_content_type_parse("c: text/html; charset=ISO-8859-4"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_content_type)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_type)); L_tmp = belle_sip_header_content_type_parse(l_raw_header); L_content_type = BELLE_SIP_HEADER_CONTENT_TYPE(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_content_type_get_type(L_content_type),"text"); BC_ASSERT_STRING_EQUAL(belle_sip_header_content_type_get_subtype(L_content_type),"html"); BC_ASSERT_STRING_EQUAL(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(L_content_type),"charset"),"ISO-8859-4"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_type)); L_content_type = belle_sip_header_content_type_parse("Content-Type: application/sdp"); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_content_type)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_type)); L_content_type = belle_sip_header_content_type_parse(l_raw_header); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_content_type_get_type(L_content_type),"application"); BC_ASSERT_STRING_EQUAL(belle_sip_header_content_type_get_subtype(L_content_type),"sdp"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_type)); L_content_type = belle_sip_header_content_type_parse("Content-Type: application/pkcs7-mime; smime-type=enveloped-data; \r\n name=smime.p7m"); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_content_type)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_type)); L_content_type = belle_sip_header_content_type_parse(l_raw_header); belle_sip_free(l_raw_header); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_type)); L_content_type = belle_sip_header_content_type_parse("Content-Type: multipart/related; type=application/rlmi+xml"); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_content_type)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_type)); L_tmp = belle_sip_header_content_type_parse(l_raw_header); L_content_type = BELLE_SIP_HEADER_CONTENT_TYPE(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_content_type_get_type(L_content_type),"multipart"); BC_ASSERT_STRING_EQUAL(belle_sip_header_content_type_get_subtype(L_content_type),"related"); BC_ASSERT_STRING_EQUAL(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(L_content_type),"type"),"application/rlmi+xml"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_type)); BC_ASSERT_PTR_NULL(belle_sip_header_content_type_parse("nimportequoi")); } static void test_record_route_header(void) { belle_sip_uri_t* L_uri; belle_sip_header_t* l_next; belle_sip_header_record_route_t* L_next_route; belle_sip_header_record_route_t* L_record_route = belle_sip_header_record_route_parse("Record-Route: ;charset=ISO-8859-4"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_record_route)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_record_route)); L_record_route = belle_sip_header_record_route_parse(l_raw_header); belle_sip_free(l_raw_header); L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_record_route)); BC_ASSERT_PTR_NULL(belle_sip_uri_get_user(L_uri)); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "212.27.52.5"); BC_ASSERT_STRING_EQUAL(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(L_record_route),"charset"),"ISO-8859-4"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_record_route)); L_record_route = belle_sip_header_record_route_parse("Record-Route: ;charset=ISO-8859-4, "); l_next = belle_sip_header_get_next(BELLE_SIP_HEADER(L_record_route)); L_next_route = BELLE_SIP_HEADER_RECORD_ROUTE(l_next); BC_ASSERT_PTR_NOT_NULL(L_next_route); BC_ASSERT_PTR_NOT_NULL( belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_next_route))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_record_route)); BC_ASSERT_PTR_NULL(belle_sip_header_record_route_parse("nimportequoi")); BC_ASSERT_PTR_NULL(belle_sip_header_record_route_parse("Record-Route: , nimportequoi")); L_record_route=belle_sip_header_record_route_parse("Record-route: "); l_raw_header=belle_sip_object_to_string(L_record_route); belle_sip_object_unref(L_record_route); L_record_route=belle_sip_header_record_route_parse(l_raw_header); BC_ASSERT_PTR_NOT_NULL(L_record_route); if (L_record_route) belle_sip_object_unref(L_record_route); belle_sip_free(l_raw_header); } static void test_route_header(void) { belle_sip_header_route_t* L_route; belle_sip_uri_t* L_uri; belle_sip_header_t* l_next; belle_sip_header_route_t* L_next_route; belle_sip_header_address_t* address = belle_sip_header_address_parse(""); char* l_raw_header; belle_sip_header_record_route_t* L_record_route; if (!BC_ASSERT_PTR_NOT_NULL(address)) return; L_route = belle_sip_header_route_create(address); belle_sip_object_unref(address); if (!BC_ASSERT_PTR_NOT_NULL(L_route)) return; l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_route)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_route)); L_route = belle_sip_header_route_parse(l_raw_header); belle_sip_free(l_raw_header); L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_route)); BC_ASSERT_PTR_NULL(belle_sip_uri_get_user(L_uri)); BC_ASSERT_EQUAL(belle_sip_uri_get_port(L_uri), 5060,int,"%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_route)); L_route = belle_sip_header_route_parse("Route: ;charset=ISO-8859-4, "); l_next = belle_sip_header_get_next(BELLE_SIP_HEADER(L_route)); L_next_route = BELLE_SIP_HEADER_ROUTE(l_next); BC_ASSERT_PTR_NOT_NULL(L_next_route); BC_ASSERT_PTR_NOT_NULL( belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_next_route))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_route)); BC_ASSERT_PTR_NULL(belle_sip_header_route_parse("nimportequoi")); BC_ASSERT_PTR_NULL(belle_sip_header_contact_parse("Route: , nimportequoi")); L_route=belle_sip_header_route_parse("Route: "); l_raw_header=belle_sip_object_to_string(L_route); belle_sip_object_unref(L_route); L_route=belle_sip_header_route_parse(l_raw_header); BC_ASSERT_PTR_NOT_NULL(L_route); if (L_route) belle_sip_object_unref(L_route); L_record_route = belle_sip_header_record_route_parse("Record-Route: ;charset=ISO-8859-4, "); L_route = belle_sip_header_route_create(BELLE_SIP_HEADER_ADDRESS(L_record_route)); L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_route)); BC_ASSERT_PTR_NULL(belle_sip_header_get_next(BELLE_SIP_HEADER(L_route))); BC_ASSERT_PTR_NULL(belle_sip_uri_get_user(L_uri)); BC_ASSERT_EQUAL(belle_sip_uri_get_port(L_uri), 5060,int,"%d"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "212.27.52.5"); belle_sip_object_unref(L_record_route); belle_sip_object_unref(L_route); belle_sip_free(l_raw_header); } static void test_service_route_header(void) { belle_sip_header_service_route_t* L_service_route = belle_sip_header_service_route_parse("Service-Route: "); belle_sip_uri_t* L_uri; char* l_raw_header; if (!BC_ASSERT_PTR_NOT_NULL(L_service_route)) return; l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_service_route)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_service_route)); L_service_route = belle_sip_header_service_route_parse(l_raw_header); belle_sip_free(l_raw_header); L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_service_route)); BC_ASSERT_PTR_NOT_NULL(belle_sip_uri_get_user(L_uri)); BC_ASSERT_EQUAL(belle_sip_uri_get_port(L_uri), 6060,int,"%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_service_route)); BC_ASSERT_PTR_NULL(belle_sip_header_service_route_parse("nimportequoi")); } static void test_content_length_header(void) { belle_sip_header_content_length_t* L_tmp; belle_sip_header_content_length_t* L_content_length = belle_sip_header_content_length_parse("Content-Length: 3495"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_content_length)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_length)); L_tmp = belle_sip_header_content_length_parse(l_raw_header); L_content_length = BELLE_SIP_HEADER_CONTENT_LENGTH(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_EQUAL((unsigned int)belle_sip_header_content_length_get_content_length(L_content_length), 3495, unsigned int, "%u"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_length)); BC_ASSERT_PTR_NULL(belle_sip_header_content_length_parse("nimportequoi")); } static belle_sip_header_t* test_header_extension(const char* name,const char* value) { belle_sip_header_t* L_tmp; belle_sip_header_t* L_extension; char header[256]; char* l_raw_header=NULL; snprintf(header,sizeof(header),"%s:%s",name,value); L_extension = belle_sip_header_parse(header); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_extension)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_extension)); L_tmp = belle_sip_header_parse(l_raw_header); L_extension = BELLE_SIP_HEADER(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_get_unparsed_value(L_extension), value); /*FIXME jehan check why missing colon does not lead an exception BC_ASSERT_PTR_NULL(belle_sip_header_extension_parse("nimportequoi"));*/ return L_extension; } static void test_header_extension_1(void) { belle_sip_object_unref(test_header_extension("toto","titi")); } static void test_header_extension_2(void) { belle_sip_header_t* header = test_header_extension("From","sip:localhost"); BC_ASSERT_TRUE(BELLE_SIP_OBJECT_IS_INSTANCE_OF(header,belle_sip_header_from_t)); belle_sip_object_unref(header); } static void test_authorization_header(void) { const char* l_header = "Authorization: Digest username=\"0033482532176\", "\ "realm=\"sip.ovh.net\", nonce=\"1bcdcb194b30df5f43973d4c69bdf54f\", uri=\"sip:sip.ovh.net\", response=\"eb36c8d5c8642c1c5f44ec3404613c81\","\ "algorithm=MD5, opaque=\"1bc7f9097684320\"," "\r\n qop=auth, nc=00000001,cnonce=\"0a4f113b\", blabla=\"toto\""; belle_sip_header_authorization_t* L_tmp; belle_sip_header_authorization_t* L_authorization = belle_sip_header_authorization_parse(l_header); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_authorization)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_authorization)); L_tmp = belle_sip_header_authorization_parse(l_raw_header); L_authorization = BELLE_SIP_HEADER_AUTHORIZATION(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); if (!BC_ASSERT_PTR_NOT_NULL(L_authorization)) return; BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_scheme(L_authorization), "Digest"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_username(L_authorization), "0033482532176"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_realm(L_authorization), "sip.ovh.net"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_nonce(L_authorization), "1bcdcb194b30df5f43973d4c69bdf54f"); BC_ASSERT_PTR_NOT_NULL(belle_sip_header_authorization_get_uri(L_authorization)); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_response(L_authorization), "eb36c8d5c8642c1c5f44ec3404613c81"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_algorithm(L_authorization), "MD5"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_opaque(L_authorization), "1bc7f9097684320"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_qop(L_authorization), "auth"); BC_ASSERT_EQUAL(belle_sip_header_authorization_get_nonce_count(L_authorization), 1,int,"%d"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_cnonce(L_authorization), "0a4f113b"); BC_ASSERT_STRING_EQUAL(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(L_authorization), "blabla"),"\"toto\""); belle_sip_object_unref(BELLE_SIP_OBJECT(L_authorization)); BC_ASSERT_PTR_NULL(belle_sip_header_authorization_parse("nimportequoi")); } static void test_proxy_authorization_header(void) { const char* l_header = "Proxy-Authorization: Digest username=\"Alice\"" ", realm=\"atlanta.com\", nonce=\"c60f3082ee1212b402a21831ae\"" ", response=\"245f23415f11432b3434341c022\""; belle_sip_header_proxy_authorization_t* L_authorization = belle_sip_header_proxy_authorization_parse(l_header); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_authorization)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_authorization)); L_authorization = belle_sip_header_proxy_authorization_parse(l_raw_header); belle_sip_free(l_raw_header); BC_ASSERT_PTR_NOT_NULL(L_authorization); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_username(BELLE_SIP_HEADER_AUTHORIZATION(L_authorization)), "Alice"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_realm(BELLE_SIP_HEADER_AUTHORIZATION(L_authorization)), "atlanta.com"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authorization_get_nonce(BELLE_SIP_HEADER_AUTHORIZATION(L_authorization)), "c60f3082ee1212b402a21831ae"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_authorization)); BC_ASSERT_PTR_NULL(belle_sip_header_proxy_authorization_parse("nimportequoi")); } static void check_header_authenticate(belle_sip_header_www_authenticate_t* authenticate) { belle_sip_list_t* qop; BC_ASSERT_PTR_NOT_NULL(authenticate); BC_ASSERT_STRING_EQUAL(belle_sip_header_www_authenticate_get_realm(authenticate), "atlanta.com"); BC_ASSERT_STRING_EQUAL(belle_sip_header_www_authenticate_get_domain(authenticate), "sip:boxesbybob.com"); BC_ASSERT_STRING_EQUAL(belle_sip_header_www_authenticate_get_nonce(authenticate), "c60f3082ee1212b402a21831ae"); BC_ASSERT_STRING_EQUAL(belle_sip_header_www_authenticate_get_algorithm(authenticate), "MD5"); BC_ASSERT_STRING_EQUAL(belle_sip_header_www_authenticate_get_opaque(authenticate), "1bc7f9097684320"); if (!BC_ASSERT_PTR_NOT_NULL(qop=belle_sip_header_www_authenticate_get_qop(authenticate))) return; BC_ASSERT_STRING_EQUAL((const char*)qop->data, "auth"); if (!BC_ASSERT_PTR_NOT_NULL(qop=qop->next)) return; BC_ASSERT_STRING_EQUAL((const char*)qop->data, "auth-int"); BC_ASSERT_STRING_EQUAL(belle_sip_header_www_authenticate_get_qop(authenticate)->data, "auth"); BC_ASSERT_STRING_EQUAL(belle_sip_header_www_authenticate_get_scheme(authenticate), "Digest"); BC_ASSERT_EQUAL(belle_sip_header_www_authenticate_is_stale(authenticate),1,int,"%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(authenticate)); } static void test_www_authenticate_header(void) { const char* l_header = "WWW-Authenticate: Digest " "algorithm=MD5,\r\n realm=\"atlanta.com\",\r\n opaque=\"1bc7f9097684320\"," " qop=\"auth,auth-int\", nonce=\"c60f3082ee1212b402a21831ae\", stale=true, domain=\"sip:boxesbybob.com\""; const char* l_header_1 = "WWW-Authenticate: Digest realm=\"toto.com\",\r\n nonce=\"b543409177c9036dbf3054aea940e9703dc8f84c0108\"" ",\r\n opaque=\"ALU:QbkRBthOEgEQAkgVEwwHRAIBHgkdHwQCQ1lFRkRWEAkic203MSEzJCgoZS8iI3VlYWRj\",\r\n algorithm=MD5" ",\r\n qop=\"auth\""; const char* l_header_2 = "WWW-Authenticate: Basic realm=\"WallyWorld\""; belle_sip_header_www_authenticate_t* L_tmp; belle_sip_header_www_authenticate_t *l_authenticate = belle_sip_header_www_authenticate_parse(l_header); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_authenticate)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_authenticate)); L_tmp = belle_sip_header_www_authenticate_parse(l_raw_header); l_authenticate = BELLE_SIP_HEADER_WWW_AUTHENTICATE(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); check_header_authenticate(l_authenticate); l_authenticate = belle_sip_header_www_authenticate_parse(l_header_1); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_authenticate)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_authenticate)); L_tmp = belle_sip_header_www_authenticate_parse(l_raw_header); belle_sip_free(l_raw_header); l_authenticate = BELLE_SIP_HEADER_WWW_AUTHENTICATE(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_authenticate)); l_authenticate = belle_sip_header_www_authenticate_parse(l_header_2); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(l_authenticate)); belle_sip_object_unref(BELLE_SIP_OBJECT(l_authenticate)); L_tmp = belle_sip_header_www_authenticate_parse(l_raw_header); belle_sip_free(l_raw_header); l_authenticate = BELLE_SIP_HEADER_WWW_AUTHENTICATE(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); BC_ASSERT_STRING_EQUAL(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(l_authenticate),"realm"),"\"WallyWorld\""); BC_ASSERT_STRING_EQUAL(belle_sip_header_www_authenticate_get_scheme(l_authenticate),"Basic"); belle_sip_object_unref(BELLE_SIP_OBJECT(l_authenticate)); BC_ASSERT_PTR_NULL(belle_sip_header_www_authenticate_parse("nimportequoi")); } static void test_proxy_authenticate_header(void) { const char* l_header = "Proxy-Authenticate: Digest " "algorithm=MD5, realm=\"atlanta.com\", opaque=\"1bc7f9097684320\"," " qop=\"auth,auth-int\", nonce=\"c60f3082ee1212b402a21831ae\", stale=true, domain=\"sip:boxesbybob.com\""; belle_sip_header_proxy_authenticate_t* L_tmp; belle_sip_header_proxy_authenticate_t* L_proxy_authorization = belle_sip_header_proxy_authenticate_parse(l_header); //belle_sip_list_t* qop; char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_proxy_authorization)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_proxy_authorization)); L_tmp = belle_sip_header_proxy_authenticate_parse(l_raw_header); L_proxy_authorization = BELLE_SIP_HEADER_PROXY_AUTHENTICATE(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); check_header_authenticate(BELLE_SIP_HEADER_WWW_AUTHENTICATE(L_proxy_authorization)); BC_ASSERT_PTR_NULL(belle_sip_header_proxy_authenticate_parse("nimportequoi")); } static void test_max_forwards_header(void) { const char* l_header = "Max-Forwards: 6"; belle_sip_header_max_forwards_t* L_tmp; belle_sip_header_max_forwards_t* L_max_forwards = belle_sip_header_max_forwards_parse(l_header); char* l_raw_header; belle_sip_header_max_forwards_decrement_max_forwards(L_max_forwards); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_max_forwards)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_max_forwards)); L_tmp = belle_sip_header_max_forwards_parse(l_raw_header); L_max_forwards = BELLE_SIP_HEADER_MAX_FORWARDS(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_PTR_NOT_NULL(L_max_forwards); BC_ASSERT_EQUAL(belle_sip_header_max_forwards_get_max_forwards(L_max_forwards), 5,int,"%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_max_forwards)); BC_ASSERT_PTR_NULL(belle_sip_header_max_forwards_parse("nimportequoi")); } static void test_user_agent_header(void) { const char* l_header = "User-Agent: Linphone/3.4.99.1 (eXosip2/3.3.0)"; const char* values[] ={"Linphone/3.4.99.1" ,"(eXosip2/3.3.0)"}; belle_sip_list_t* products; belle_sip_header_user_agent_t* L_tmp; belle_sip_header_user_agent_t* L_user_agent = belle_sip_header_user_agent_parse(l_header); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_user_agent)); int i=0; belle_sip_object_unref(BELLE_SIP_OBJECT(L_user_agent)); L_tmp = belle_sip_header_user_agent_parse(l_raw_header); L_user_agent = BELLE_SIP_HEADER_USER_AGENT(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); products = belle_sip_header_user_agent_get_products(L_user_agent); for(i=0;i<2;i++){ BC_ASSERT_PTR_NOT_NULL(products); BC_ASSERT_STRING_EQUAL((const char *)(products->data),values[i]); products=products->next; } belle_sip_object_unref(BELLE_SIP_OBJECT(L_user_agent)); BC_ASSERT_PTR_NULL(belle_sip_header_user_agent_parse("nimportequoi")); } static void test_expires_header(void) { belle_sip_header_expires_t* L_tmp; belle_sip_header_expires_t* L_expires = belle_sip_header_expires_parse("Expires: 3600"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_expires)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_expires)); L_tmp= belle_sip_header_expires_parse(l_raw_header); L_expires = BELLE_SIP_HEADER_EXPIRES(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_EQUAL(belle_sip_header_expires_get_expires(L_expires), 3600,int,"%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_expires)); /*test factory*/ L_expires = belle_sip_header_expires_create(600); BC_ASSERT_EQUAL(belle_sip_header_expires_get_expires(L_expires), 600,int,"%d"); belle_sip_object_unref(L_expires); BC_ASSERT_PTR_NULL(belle_sip_header_expires_parse("nimportequoi")); } static void test_allow_header(void) { belle_sip_header_allow_t* L_tmp; belle_sip_header_allow_t* L_allow = belle_sip_header_allow_parse("Allow:INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_allow)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_allow)); L_tmp = belle_sip_header_allow_parse(l_raw_header); L_allow = BELLE_SIP_HEADER_ALLOW(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_allow_get_method(L_allow), "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_allow)); BC_ASSERT_PTR_NULL(belle_sip_header_allow_parse("nimportequoi")); } static void test_address_with_error_header(void) { belle_sip_header_address_t* laddress = belle_sip_header_address_parse("sip:liblinphone_tester@=auth1.example.org"); BC_ASSERT_PTR_NULL(laddress); laddress = belle_sip_header_address_parse("liblinphone_tester"); BC_ASSERT_PTR_NULL(laddress); } static void test_address_header(void) { belle_sip_uri_t* L_uri; char* L_raw; belle_sip_header_address_t* laddress = belle_sip_header_address_fast_parse("\"toto\" "); BC_ASSERT_PTR_NOT_NULL(laddress); L_raw = belle_sip_object_to_string(BELLE_SIP_OBJECT(laddress)); BC_ASSERT_PTR_NOT_NULL(L_raw); belle_sip_object_unref(BELLE_SIP_OBJECT(laddress)); laddress = belle_sip_header_address_parse(L_raw); belle_sip_free(L_raw); BC_ASSERT_STRING_EQUAL("toto",belle_sip_header_address_get_displayname(laddress)); L_uri = belle_sip_header_address_get_uri(laddress); BC_ASSERT_PTR_NOT_NULL(belle_sip_uri_get_user(L_uri)); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "81.56.11.2"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user(L_uri), "liblinphone_tester"); BC_ASSERT_EQUAL(belle_sip_uri_get_port(L_uri), 5060,int,"%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(laddress)); laddress = belle_sip_header_address_parse("\"\\\"to\\\\to\\\"\" "); BC_ASSERT_PTR_NOT_NULL(laddress); L_raw = belle_sip_object_to_string(BELLE_SIP_OBJECT(laddress)); BC_ASSERT_PTR_NOT_NULL(L_raw); belle_sip_object_unref(BELLE_SIP_OBJECT(laddress)); laddress = belle_sip_header_address_parse(L_raw); belle_sip_free(L_raw); BC_ASSERT_STRING_EQUAL("\"to\\to\"",belle_sip_header_address_get_displayname(laddress)); belle_sip_object_unref(BELLE_SIP_OBJECT(laddress)); laddress = belle_sip_header_address_parse("sips:example.com;transport=tls;maddr=sip1.example.com"); BC_ASSERT_PTR_NOT_NULL(laddress); L_raw = belle_sip_object_to_string(BELLE_SIP_OBJECT(laddress)); BC_ASSERT_PTR_NOT_NULL(L_raw); belle_sip_object_unref(BELLE_SIP_OBJECT(laddress)); laddress = belle_sip_header_address_parse(L_raw); belle_sip_free(L_raw); BC_ASSERT_STRING_EQUAL("sip1.example.com",belle_sip_uri_get_maddr_param(belle_sip_header_address_get_uri(laddress))); belle_sip_object_unref(BELLE_SIP_OBJECT(laddress)); } static void test_address_header_with_tel_uri(void) { belle_generic_uri_t* L_uri; char* L_raw; belle_sip_header_address_t* laddress = belle_sip_header_address_fast_parse("\"toto\" "); BC_ASSERT_PTR_NOT_NULL(laddress); L_raw = belle_sip_object_to_string(BELLE_SIP_OBJECT(laddress)); BC_ASSERT_PTR_NOT_NULL(L_raw); belle_sip_object_unref(BELLE_SIP_OBJECT(laddress)); laddress = belle_sip_header_address_parse(L_raw); belle_sip_free(L_raw); BC_ASSERT_STRING_EQUAL("toto",belle_sip_header_address_get_displayname(laddress)); L_uri = belle_sip_header_address_get_absolute_uri(laddress); BC_ASSERT_PTR_NOT_NULL(belle_generic_uri_get_opaque_part(L_uri)); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_opaque_part(L_uri), "123456"); belle_sip_object_unref(BELLE_SIP_OBJECT(laddress)); } static void test_address_header_with_urn(void) { belle_generic_uri_t* L_uri; char* L_raw; belle_sip_header_address_t* laddress = belle_sip_header_address_fast_parse("urn:service:sos"); BC_ASSERT_PTR_NOT_NULL(laddress); L_raw = belle_sip_object_to_string(BELLE_SIP_OBJECT(laddress)); BC_ASSERT_PTR_NOT_NULL(L_raw); belle_sip_object_unref(BELLE_SIP_OBJECT(laddress)); laddress = belle_sip_header_address_parse(L_raw); belle_sip_free(L_raw); BC_ASSERT_PTR_NULL(belle_sip_header_address_get_displayname(laddress)); L_uri = belle_sip_header_address_get_absolute_uri(laddress); BC_ASSERT_PTR_NOT_NULL(belle_generic_uri_get_scheme(L_uri)); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_scheme(L_uri), "urn"); BC_ASSERT_PTR_NOT_NULL(belle_generic_uri_get_opaque_part(L_uri)); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_opaque_part(L_uri), "service:sos"); belle_sip_object_unref(BELLE_SIP_OBJECT(laddress)); } static void test_very_long_address_header(void) { belle_sip_uri_t* L_uri; const char* raw = ""; /*not compliant but*/ belle_sip_header_address_t* laddress = belle_sip_header_address_parse(raw); if (!BC_ASSERT_PTR_NOT_NULL(laddress)) return; L_uri = belle_sip_header_address_get_uri(laddress); BC_ASSERT_PTR_NOT_NULL(belle_sip_uri_get_user(L_uri)); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "sip.linphone.org"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user(L_uri), "jehan"); do { char format[512]; const char * cactual = (belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(L_uri),"pn-tok")); const char * cexpected = ("APA91bHPVa4PuKOMnr6ppWb3XYUL06QO-ND4eeiw7dG49q4o_Ywzal7BxVRgH-wvqH9iB9V7h6kfb-DCiVdSpnl6CeWO25FAkM4eh6DJyWcbP7SzhKdku_-r9936kJW7-4drI6-Om4qp"); sprintf(format, "name" "(" "#actual" ", " "#expected" ") - " "Expected %s but was %s.\n", cexpected, cactual); printf("%s\n", format); } while (0); BC_ASSERT_STRING_EQUAL(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(L_uri),"pn-tok") ,"APA91bHPVa4PuKOMnr6ppWb3XYUL06QO-ND4eeiw7dG49q4o_Ywzal7BxVRgH-wvqH9iB9V7h6kfb-DCiVdSpnl6CeWO25FAkM4eh6DJyWcbP7SzhKdku_-r9936kJW7-4drI6-Om4qp"); belle_sip_object_unref(BELLE_SIP_OBJECT(laddress)); } static void test_subscription_state_header(void) { belle_sip_header_subscription_state_t* L_tmp; belle_sip_header_subscription_state_t* L_subscription_state = belle_sip_header_subscription_state_parse("Subscription-State: terminated;expires=600"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_subscription_state)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_subscription_state)); L_tmp = belle_sip_header_subscription_state_parse(l_raw_header); L_subscription_state = BELLE_SIP_HEADER_SUBSCRIPTION_STATE(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_subscription_state_get_state(L_subscription_state), "terminated"); BC_ASSERT_EQUAL(belle_sip_header_subscription_state_get_expires(L_subscription_state), 600,int,"%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_subscription_state)); BC_ASSERT_PTR_NULL(belle_sip_header_subscription_state_parse("nimportequoi")); } static void test_refer_to_header(void) { belle_sip_uri_t* L_uri; belle_sip_header_address_t *ha; belle_sip_header_refer_to_t* L_refer_to = belle_sip_header_refer_to_parse("Refer-To: "); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_refer_to)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_refer_to)); L_refer_to = belle_sip_header_refer_to_parse(l_raw_header); belle_sip_free(l_raw_header); L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_refer_to)); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user(L_uri),"dave"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "denver.example.org"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_refer_to)); /*test factory*/ L_refer_to = belle_sip_header_refer_to_create((ha=belle_sip_header_address_parse("\"super man\" "))); belle_sip_object_unref(ha); if (!BC_ASSERT_PTR_NOT_NULL(L_refer_to)) return; L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_refer_to)); if (!BC_ASSERT_PTR_NOT_NULL(L_uri)) return; BC_ASSERT_STRING_EQUAL(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(L_refer_to)), "super man"); belle_sip_object_unref(L_refer_to); BC_ASSERT_PTR_NULL(belle_sip_header_refer_to_parse("nimportequoi")); } static void test_referred_by_header(void) { belle_sip_uri_t* L_uri; belle_sip_header_referred_by_t* L_referred_by = belle_sip_header_referred_by_parse("Referred-By: sip:r@ref.example;cid=\"2UWQFN309shb3@ref.example\""); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_referred_by)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_referred_by)); L_referred_by = belle_sip_header_referred_by_parse(l_raw_header); belle_sip_free(l_raw_header); L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_referred_by)); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user(L_uri),"r"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "ref.example"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_referred_by)); BC_ASSERT_PTR_NULL(belle_sip_header_referred_by_parse("nimportequoi")); } static void test_replaces_header(void) { belle_sip_header_replaces_t* L_tmp; belle_sip_header_replaces_t* L_replaces = belle_sip_header_replaces_parse("Replaces: 12345@149.112.118.3;to-tag=12345;from-tag=54321"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_replaces)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_replaces)); L_tmp= belle_sip_header_replaces_parse(l_raw_header); L_replaces = BELLE_SIP_HEADER_REPLACES(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_replaces_get_call_id(L_replaces), "12345@149.112.118.3"); BC_ASSERT_STRING_EQUAL(belle_sip_header_replaces_get_from_tag(L_replaces), "54321"); BC_ASSERT_STRING_EQUAL(belle_sip_header_replaces_get_to_tag(L_replaces), "12345"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_replaces)); BC_ASSERT_PTR_NULL(belle_sip_header_replaces_parse("nimportequoi")); } static void test_replaces_escaped_header(void) { belle_sip_header_replaces_t* L_replaces; char* escaped_to_string; char* l_raw_header; belle_sip_header_replaces_t* L_tmp = belle_sip_header_replaces_create("12345@192.168.118.3","5FFE-3994","12345"); escaped_to_string=belle_sip_header_replaces_value_to_escaped_string(L_tmp); L_replaces=belle_sip_header_replaces_create2(escaped_to_string); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_replaces)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_replaces)); belle_sip_object_unref(L_tmp); L_tmp= belle_sip_header_replaces_parse(l_raw_header); L_replaces = BELLE_SIP_HEADER_REPLACES(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); belle_sip_free(escaped_to_string); BC_ASSERT_STRING_EQUAL(belle_sip_header_replaces_get_call_id(L_replaces), "12345@192.168.118.3"); BC_ASSERT_STRING_EQUAL(belle_sip_header_replaces_get_from_tag(L_replaces), "5FFE-3994"); BC_ASSERT_STRING_EQUAL(belle_sip_header_replaces_get_to_tag(L_replaces), "12345"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_replaces)); } #ifndef _WIN32 static void _test_date_header(void){ belle_sip_header_date_t *date,*date2; time_t utc; #define DATE_EXAMPLE "Thu, 21 Feb 2002 13:02:03 GMT" #define DATE_UTC_EXAMPLE 1014296523L /* the above date in UTC */ date=belle_sip_header_date_parse("Date: " DATE_EXAMPLE); BC_ASSERT_PTR_NOT_NULL(date); if (date){ utc=belle_sip_header_date_get_time(date); BC_ASSERT_EQUAL(utc,DATE_UTC_EXAMPLE,int,"%d"); belle_sip_object_unref(date); } date2=belle_sip_header_date_create_from_time(&utc); BC_ASSERT_PTR_NOT_NULL(date2); if (date2){ BC_ASSERT_STRING_EQUAL(belle_sip_header_date_get_date(date2),DATE_EXAMPLE); BC_ASSERT_PTR_NULL(belle_sip_header_date_parse("nimportequoi")); belle_sip_object_unref(date2); } } #endif static void test_date_header(void){ #ifdef _WIN32 // TODO: setenv and unsetenv are not available for Windows #else char *tz; /* test in our timezone */ _test_date_header(); /* test within another timezone */ tz = getenv("TZ"); setenv("TZ","Etc/GMT+4",1); tzset(); _test_date_header(); /* reset to original timezone */ if(tz) setenv("TZ", tz, 1); else unsetenv("TZ"); tzset(); #endif } static void test_p_preferred_identity_header(void) { belle_sip_uri_t* L_uri; belle_sip_header_p_preferred_identity_t* L_p_preferred_identity = belle_sip_header_p_preferred_identity_parse("P-Preferred-Identity: \"Cullen Jennings\" "); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_p_preferred_identity)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_p_preferred_identity)); L_p_preferred_identity = belle_sip_header_p_preferred_identity_parse(l_raw_header); belle_sip_free(l_raw_header); L_uri = belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(L_p_preferred_identity)); BC_ASSERT_STRING_EQUAL(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(L_p_preferred_identity)),"Cullen Jennings"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_user(L_uri),"fluffy"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(L_uri), "cisco.com"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_p_preferred_identity)); BC_ASSERT_PTR_NULL(belle_sip_header_p_preferred_identity_parse("nimportequoi")); } static void test_privacy(const char* raw_header,const char* values[],size_t number_values) { belle_sip_list_t* list; belle_sip_header_privacy_t* L_tmp; belle_sip_header_privacy_t* L_privacy = belle_sip_header_privacy_parse(raw_header); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_privacy)); size_t i=0; belle_sip_object_unref(BELLE_SIP_OBJECT(L_privacy)); L_tmp = belle_sip_header_privacy_parse(l_raw_header); L_privacy = BELLE_SIP_HEADER_PRIVACY(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); list = belle_sip_header_privacy_get_privacy(L_privacy); for(i=0;idata),values[i]); list=list->next; } belle_sip_object_unref(BELLE_SIP_OBJECT(L_privacy)); BC_ASSERT_PTR_NULL(belle_sip_header_privacy_parse("nimportequoi")); } static void test_privacy_header(void) { const char* value1[] ={"user","critical"}; const char* value2[] ={"id"}; test_privacy("Privacy: user; critical",value1,2); test_privacy("Privacy: id",value2,1); } static void test_event_header(void) { belle_sip_header_event_t* L_tmp; belle_sip_header_event_t* L_event = belle_sip_header_event_parse("Event: presence;id=blabla1"); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_event)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_event)); L_tmp = belle_sip_header_event_parse(l_raw_header); L_event = BELLE_SIP_HEADER_EVENT(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_event_get_package_name(L_event), "presence"); BC_ASSERT_STRING_EQUAL(belle_sip_header_event_get_id(L_event), "blabla1"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_event)); BC_ASSERT_PTR_NULL(belle_sip_header_event_parse("nimportequoi")); } static void test_supported(const char* header_name, const char * header_value, const char* values[],size_t number_values) { belle_sip_list_t* list; belle_sip_header_supported_t* L_tmp; belle_sip_header_supported_t* L_supported = BELLE_SIP_HEADER_SUPPORTED(belle_sip_header_create(header_name,header_value)); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_supported)); size_t i=0; belle_sip_object_unref(BELLE_SIP_OBJECT(L_supported)); L_tmp = belle_sip_header_supported_parse(l_raw_header); L_supported = BELLE_SIP_HEADER_SUPPORTED(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); list = belle_sip_header_supported_get_supported(L_supported); for(i=0;idata),values[i]); list=list->next; } } belle_sip_object_unref(BELLE_SIP_OBJECT(L_supported)); BC_ASSERT_PTR_NULL(belle_sip_header_supported_parse("nimportequoi")); } static void test_supported_header(void) { const char* value1[] ={"user","critical"}; const char* value2[] ={"id"}; test_supported("Supported","user, critical",value1,2); test_supported("Supported", "id",value2,1); } static void test_content_disposition_header(void) { belle_sip_header_content_disposition_t* L_tmp; belle_sip_header_content_disposition_t* L_content_disposition = BELLE_SIP_HEADER_CONTENT_DISPOSITION(belle_sip_header_create("Content-Disposition","form-data; name=\"File\"; filename=\"toto.jpeg\"")); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_content_disposition)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_disposition)); L_tmp = belle_sip_header_content_disposition_parse(l_raw_header); L_content_disposition = BELLE_SIP_HEADER_CONTENT_DISPOSITION(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_content_disposition_get_content_disposition(L_content_disposition), "form-data"); BC_ASSERT_STRING_EQUAL(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(L_content_disposition), "name"), "\"File\""); BC_ASSERT_STRING_EQUAL(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(L_content_disposition), "filename"), "\"toto.jpeg\""); belle_sip_object_unref(BELLE_SIP_OBJECT(L_content_disposition)); BC_ASSERT_PTR_NULL(belle_sip_header_content_disposition_parse("nimportequoi")); } static void test_accept_header(void) { belle_sip_header_accept_t* L_tmp; belle_sip_header_accept_t* L_accept = BELLE_SIP_HEADER_ACCEPT(belle_sip_header_create("Accept", "text/html; charset=ISO-8859-4")); char* l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_accept)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_accept)); L_tmp = belle_sip_header_accept_parse(l_raw_header); L_accept = BELLE_SIP_HEADER_ACCEPT(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_accept_get_type(L_accept),"text"); BC_ASSERT_STRING_EQUAL(belle_sip_header_accept_get_subtype(L_accept),"html"); BC_ASSERT_STRING_EQUAL(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(L_accept),"charset"),"ISO-8859-4"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_accept)); L_accept = belle_sip_header_accept_parse("Accept: application/sdp"); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_accept)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_accept)); L_accept = belle_sip_header_accept_parse(l_raw_header); belle_sip_free(l_raw_header); BC_ASSERT_STRING_EQUAL(belle_sip_header_accept_get_type(L_accept),"application"); BC_ASSERT_STRING_EQUAL(belle_sip_header_accept_get_subtype(L_accept),"sdp"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_accept)); L_accept = belle_sip_header_accept_parse("Accept: application/pkcs7-mime; smime-type=enveloped-data; \r\n name=smime.p7m"); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_accept)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_accept)); L_accept = belle_sip_header_accept_parse(l_raw_header); belle_sip_free(l_raw_header); belle_sip_object_unref(BELLE_SIP_OBJECT(L_accept)); L_accept = belle_sip_header_accept_parse("Accept: application/sdp, text/plain, application/vnd.gsma.rcs-ft-http+xml"); BC_ASSERT_STRING_EQUAL(belle_sip_header_accept_get_type(L_accept),"application"); BC_ASSERT_STRING_EQUAL(belle_sip_header_accept_get_subtype(L_accept),"sdp"); L_tmp = BELLE_SIP_HEADER_ACCEPT(belle_sip_header_get_next(BELLE_SIP_HEADER(L_accept))); BC_ASSERT_STRING_EQUAL(belle_sip_header_accept_get_type(L_tmp),"text"); BC_ASSERT_STRING_EQUAL(belle_sip_header_accept_get_subtype(L_tmp),"plain"); L_tmp = BELLE_SIP_HEADER_ACCEPT(belle_sip_header_get_next(BELLE_SIP_HEADER(L_tmp))); BC_ASSERT_STRING_EQUAL(belle_sip_header_accept_get_type(L_tmp),"application"); BC_ASSERT_STRING_EQUAL(belle_sip_header_accept_get_subtype(L_tmp),"vnd.gsma.rcs-ft-http+xml"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_accept)); BC_ASSERT_PTR_NULL(belle_sip_header_accept_parse("nimportequoi")); } static void test_reason_header(void) { belle_sip_header_reason_t *L_tmp, *l_next; belle_sip_header_reason_t* L_reason = BELLE_SIP_HEADER_REASON(belle_sip_header_create("Reason", "Q.850 ;cause=16 ;text=\"Busy Everywhere\"")); char* l_raw_header; BC_ASSERT_STRING_EQUAL(belle_sip_header_reason_get_text(L_reason),"Busy Everywhere"); belle_sip_header_reason_set_text(L_reason, "Terminated"); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_reason)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_reason)); L_tmp = belle_sip_header_reason_parse(l_raw_header); L_reason = BELLE_SIP_HEADER_REASON(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); BC_ASSERT_STRING_EQUAL(belle_sip_header_reason_get_text(L_reason),"Terminated"); BC_ASSERT_STRING_EQUAL(belle_sip_header_reason_get_protocol(L_reason),"Q.850"); BC_ASSERT_EQUAL(belle_sip_header_reason_get_cause(L_reason),16 , int,"%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_reason)); belle_sip_free(l_raw_header); L_reason = belle_sip_header_reason_parse("Reason: SIP ;cause=600 ;text=\"Busy Everywhere\", SIP ;cause=580 ;text=\"Precondition Failure\""); l_next =BELLE_SIP_HEADER_REASON(belle_sip_header_get_next(BELLE_SIP_HEADER(L_reason))); BC_ASSERT_PTR_NOT_NULL(l_next); BC_ASSERT_EQUAL(belle_sip_header_reason_get_cause(l_next),580 , int,"%d"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_reason)); BC_ASSERT_PTR_NULL(belle_sip_header_reason_parse("nimportequoi")); } static void test_authentication_info_header(void) { belle_sip_header_authentication_info_t *L_tmp; belle_sip_header_authentication_info_t* L_authentication_info = BELLE_SIP_HEADER_AUTHENTICATION_INFO(belle_sip_header_create("Authentication-Info" , "nextnonce=\"47364c23432d2e131a5fb210812c\"" ", qop=auth")); char* l_raw_header; BC_ASSERT_STRING_EQUAL(belle_sip_header_authentication_info_get_next_nonce(L_authentication_info),"47364c23432d2e131a5fb210812c"); belle_sip_header_authentication_info_set_next_nonce(L_authentication_info, "31a5fb210812c47364c23432d2e1"); l_raw_header = belle_sip_object_to_string(BELLE_SIP_OBJECT(L_authentication_info)); belle_sip_object_unref(BELLE_SIP_OBJECT(L_authentication_info)); L_tmp = belle_sip_header_authentication_info_parse(l_raw_header); L_authentication_info = BELLE_SIP_HEADER_AUTHENTICATION_INFO(belle_sip_object_clone(BELLE_SIP_OBJECT(L_tmp))); belle_sip_object_unref(BELLE_SIP_OBJECT(L_tmp)); BC_ASSERT_STRING_EQUAL(belle_sip_header_authentication_info_get_next_nonce(L_authentication_info),"31a5fb210812c47364c23432d2e1"); BC_ASSERT_STRING_EQUAL(belle_sip_header_authentication_info_get_qop(L_authentication_info),"auth"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_authentication_info)); belle_sip_free(l_raw_header); L_authentication_info = belle_sip_header_authentication_info_parse("Authentication-Info:nextnonce=\"WRSE5VkUg7kOjnP5sBZZzsxrsOQ8Eyp6ZNttXIA=\", qop=auth , rspauth=\"5e8518291c1320989df6966405e0b3d1\", cnonce=\"05bc00e63a7794182f9425da9266d00d\", nc=00000001"); BC_ASSERT_EQUAL(belle_sip_header_authentication_info_get_nonce_count(L_authentication_info),1, int, "%i"); belle_sip_object_unref(BELLE_SIP_OBJECT(L_authentication_info)); BC_ASSERT_PTR_NULL(belle_sip_header_authentication_info_parse("nimportequoi")); } test_t headers_tests[] = { TEST_NO_TAG("Address", test_address_header), TEST_NO_TAG("Address tel uri", test_address_header_with_tel_uri), TEST_NO_TAG("Address urn", test_address_header_with_urn), TEST_NO_TAG("Header address (very long)", test_very_long_address_header), TEST_NO_TAG("Address with error", test_address_with_error_header), TEST_NO_TAG("Allow", test_allow_header), TEST_NO_TAG("Authorization", test_authorization_header), TEST_NO_TAG("Call-ID", test_call_id_header), TEST_NO_TAG("Contact (Simple)", test_simple_contact_header), TEST_NO_TAG("Contact (Complex)", test_complex_contact_header), TEST_NO_TAG("Contact (Param-less address spec)", test_contact_header_with_paramless_address_spec), TEST_NO_TAG("Content-Length", test_content_length_header), TEST_NO_TAG("Content-Type", test_content_type_header), TEST_NO_TAG("CSeq", test_cseq_header), TEST_NO_TAG("Date", test_date_header), TEST_NO_TAG("Expires", test_expires_header), TEST_NO_TAG("From", test_from_header), TEST_NO_TAG("From (Param-less address spec)", test_from_header_with_paramless_address_spec), TEST_NO_TAG("Max-Forwards", test_max_forwards_header), TEST_NO_TAG("Privacy", test_privacy_header), TEST_NO_TAG("P-Preferred-Identity", test_p_preferred_identity_header), TEST_NO_TAG("Proxy-Authenticate", test_proxy_authenticate_header), TEST_NO_TAG("Proxy-Authorization", test_proxy_authorization_header), TEST_NO_TAG("Record-Route", test_record_route_header), TEST_NO_TAG("Refer-To", test_refer_to_header), TEST_NO_TAG("Referred-By", test_referred_by_header), TEST_NO_TAG("Replaces", test_replaces_header), TEST_NO_TAG("Replaces (Escaped)", test_replaces_escaped_header), TEST_NO_TAG("Route", test_route_header), TEST_NO_TAG("Service-Route", test_service_route_header), TEST_NO_TAG("Subscription-State", test_subscription_state_header), TEST_NO_TAG("To", test_to_header), TEST_NO_TAG("To (Param-less address spec)", test_to_header_with_paramless_address_spec), TEST_NO_TAG("User-Agent", test_user_agent_header), TEST_NO_TAG("Via", test_via_header), TEST_NO_TAG("WWW-Authenticate", test_www_authenticate_header), TEST_NO_TAG("Header extension", test_header_extension_1), TEST_NO_TAG("Header extension 2", test_header_extension_2), TEST_NO_TAG("Header event", test_event_header), TEST_NO_TAG("Header Supported", test_supported_header), TEST_NO_TAG("Header Content-Disposition", test_content_disposition_header), TEST_NO_TAG("Header Accept", test_accept_header), TEST_NO_TAG("Header Reason", test_reason_header), TEST_NO_TAG("Header Authentication-Info", test_authentication_info_header) }; test_suite_t headers_test_suite = {"Headers", NULL, NULL, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(headers_tests) / sizeof(headers_tests[0]), headers_tests}; belle-sip-1.6.3/tester/belle_sip_message_tester.c000066400000000000000000001376171313437522400221220ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_tester.h" static void check_uri_and_headers(belle_sip_message_t* message) { if (belle_sip_message_is_request(message)) { BC_ASSERT_TRUE(belle_sip_request_get_uri(BELLE_SIP_REQUEST(message))|| belle_sip_request_get_absolute_uri(BELLE_SIP_REQUEST(message)) ); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Max-Forwards")); BC_ASSERT_PTR_NOT_NULL(BELLE_SIP_HEADER_MAX_FORWARDS(belle_sip_message_get_header(message,"Max-Forwards"))); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"User-Agent")); BC_ASSERT_PTR_NOT_NULL(BELLE_SIP_HEADER_USER_AGENT(belle_sip_message_get_header(message,"User-Agent"))); } BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"From")); BC_ASSERT_PTR_NOT_NULL(BELLE_SIP_HEADER_FROM(belle_sip_message_get_header(message,"From"))); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"To")); BC_ASSERT_PTR_NOT_NULL(BELLE_SIP_HEADER_TO(belle_sip_message_get_header(message,"To"))); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"CSeq")); BC_ASSERT_PTR_NOT_NULL(BELLE_SIP_HEADER_CSEQ(belle_sip_message_get_header(message,"CSeq"))); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Via")); BC_ASSERT_PTR_NOT_NULL(BELLE_SIP_HEADER_VIA(belle_sip_message_get_header(message,"Via"))); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Call-ID")); BC_ASSERT_PTR_NOT_NULL(BELLE_SIP_HEADER_CALL_ID(belle_sip_message_get_header(message,"Call-ID"))); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Content-Length")); BC_ASSERT_PTR_NOT_NULL(BELLE_SIP_HEADER_CONTENT_LENGTH(belle_sip_message_get_header(message,"Content-Length"))); } static void testRegisterMessage(void) { const char* raw_message = "REGISTER sip:192.168.0.20 SIP/2.0\r\n"\ "v: SIP/2.0/UDP 192.168.1.8:5062;rport;branch=z9hG4bK1439638806\r\n"\ "f: ;tag=465687829\r\n"\ "t: \r\n"\ "i: 1053183492\r\n"\ "CSeq: 1 REGISTER\r\n"\ "m: \r\n"\ "Max-Forwards: 70\r\n"\ "User-Agent: Linphone/3.3.99.10 (eXosip2/3.3.0)\r\n"\ "Expires: 3600\r\n"\ "Proxy-Authorization: Digest username=\"8117396\", realm=\"Realm\", nonce=\"MTMwNDAwMjIxMjA4NzVkODY4ZmZhODMzMzU4ZDJkOTA1NzM2NTQ2NDZlNmIz"\ ", uri=\"sip:linphone.net\", response=\"eed376ff7c963441255ec66594e470e7\", algorithm=MD5, cnonce=\"0a4f113b\", qop=auth, nc=00000001\r\n"\ "l: 0\r\n\r\n"; belle_sip_request_t* request; belle_sip_message_t* message = belle_sip_message_parse(raw_message); char* encoded_message = belle_sip_object_to_string(BELLE_SIP_OBJECT(message)); belle_sip_object_unref(BELLE_SIP_OBJECT(message)); message = belle_sip_message_parse(encoded_message); request = BELLE_SIP_REQUEST(message); BC_ASSERT_STRING_EQUAL(belle_sip_request_get_method(request),"REGISTER"); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Expires")); BC_ASSERT_PTR_NOT_NULL(BELLE_SIP_HEADER_EXPIRES(belle_sip_message_get_header(message,"Expires"))); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Proxy-Authorization")); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Contact")); check_uri_and_headers(message); belle_sip_free(encoded_message); belle_sip_object_unref(message); } static void testInviteMessage(void) { const char* raw_message = "INVITE sip:becheong@sip.linphone.org SIP/2.0\r\n"\ "Via: SIP/2.0/UDP 10.23.17.117:22600;branch=z9hG4bK-d8754z-4d7620d2feccbfac-1---d8754z-;rport=4820;received=202.165.193.129\r\n"\ "Max-Forwards: 70\r\n"\ "Contact: \r\n"\ "To: \"becheong\" \r\n"\ "From: \"Benjamin Cheong\" ;tag=7326e5f6\r\n"\ "Call-ID: Y2NlNzg0ODc0ZGIxODU1MWI5MzhkNDVkNDZhOTQ4YWU.\r\n"\ "CSeq: 1 INVITE\r\n"\ "Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO\r\n"\ "c: application/sdp\r\n"\ "Supported: replaces\r\n"\ "Authorization: Digest username=\"003332176\", realm=\"sip.ovh.net\", nonce=\"24212965507cde726e8bc37e04686459\", uri=\"sip:sip.ovh.net\", response=\"896e786e9c0525ca3085322c7f1bce7b\", algorithm=MD5, opaque=\"241b9fb347752f2\"\r\n"\ "User-Agent: X-Lite 4 release 4.0 stamp 58832\r\n"\ "Content-Length: 230\r\n\r\n"; belle_sip_request_t* request; belle_sip_message_t* message = belle_sip_message_parse(raw_message); char* encoded_message = belle_sip_object_to_string(BELLE_SIP_OBJECT(message)); belle_sip_object_unref(BELLE_SIP_OBJECT(message)); message = belle_sip_message_parse(encoded_message); request = BELLE_SIP_REQUEST(message); BC_ASSERT_STRING_EQUAL(belle_sip_request_get_method(request),"INVITE"); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Contact")); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Authorization")); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Content-Type")); check_uri_and_headers(message); belle_sip_object_unref(message); belle_sip_free(encoded_message); } static void testInviteMessageWithTelUri(void) { const char* raw_message = "INVITE tel:11234567888;phone-context=vzims.fr SIP/2.0\r\n"\ "Via: SIP/2.0/UDP 10.23.17.117:22600;branch=z9hG4bK-d8754z-4d7620d2feccbfac-1---d8754z-;rport=4820;received=202.165.193.129\r\n"\ "Max-Forwards: 70\r\n"\ "Contact: \r\n"\ "To: \r\n"\ "From: tel:11234567888;tag=werwrw\r\n"\ "Call-ID: Y2NlNzg0ODc0ZGIxODU1MWI5MzhkNDVkNDZhOTQ4YWU.\r\n"\ "CSeq: 1 INVITE\r\n"\ "Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO\r\n"\ "c: application/sdp\r\n"\ "Supported: replaces\r\n"\ "Authorization: Digest username=\"003332176\", realm=\"sip.ovh.net\", nonce=\"24212965507cde726e8bc37e04686459\", uri=\"sip:sip.ovh.net\", response=\"896e786e9c0525ca3085322c7f1bce7b\", algorithm=MD5, opaque=\"241b9fb347752f2\"\r\n"\ "User-Agent: X-Lite 4 release 4.0 stamp 58832\r\n"\ "Content-Length: 230\r\n\r\n"; belle_sip_request_t* request; belle_sip_message_t* message = belle_sip_message_parse(raw_message); char* encoded_message = belle_sip_object_to_string(BELLE_SIP_OBJECT(message)); belle_sip_object_unref(BELLE_SIP_OBJECT(message)); message = belle_sip_message_parse(encoded_message); request = BELLE_SIP_REQUEST(message); BC_ASSERT_STRING_EQUAL(belle_sip_request_get_method(request),"INVITE"); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Contact")); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Authorization")); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Content-Type")); check_uri_and_headers(message); BC_ASSERT_PTR_NOT_NULL(belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(belle_sip_message_get_header_by_type(message,belle_sip_header_from_t)))); BC_ASSERT_PTR_NOT_NULL(belle_sip_header_address_get_absolute_uri(BELLE_SIP_HEADER_ADDRESS(belle_sip_message_get_header_by_type(message,belle_sip_header_to_t)))); BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_opaque_part(belle_sip_request_get_absolute_uri(request)),"11234567888;phone-context=vzims.fr"); belle_sip_object_unref(message); belle_sip_free(encoded_message); } static void test401Response(void) { const char* raw_message = "SIP/2.0 401 Unauthorized\r\n" "Call-ID: 577586163\r\n" "CSeq: 21 REGISTER\r\n" "From: ;tag=1790643209\r\n" "Server: Cirpack/v4.42x (gw_sip)\r\n" "To: ;tag=00-08075-24212984-22e348d97\r\n" "Via: SIP/2.0/UDP 192.168.0.18:5062;received=81.56.113.2;rport=5062;branch=z9hG4bK1939354046\r\n" "WWW-Authenticate: Digest realm=\"sip.ovh.net\",nonce=\"24212965507cde726e8bc37e04686459\",opaque=\"241b9fb347752f2\",stale=false,algorithm=MD5\r\n" "Content-Length: 0\r\n\r\n"; belle_sip_response_t* response; belle_sip_message_t* message = belle_sip_message_parse(raw_message); char* encoded_message = belle_sip_object_to_string(BELLE_SIP_OBJECT(message)); belle_sip_object_unref(BELLE_SIP_OBJECT(message)); message = belle_sip_message_parse(encoded_message); response = BELLE_SIP_RESPONSE(message); BC_ASSERT_EQUAL(belle_sip_response_get_status_code(response),401,int,"%d"); BC_ASSERT_STRING_EQUAL(belle_sip_response_get_reason_phrase(response),"Unauthorized"); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"WWW-Authenticate")); check_uri_and_headers(message); belle_sip_object_unref(message); belle_sip_free(encoded_message); } static void test401ResponseWithoutResponsePhrase(void) { const char* raw_message = "SIP/2.0 401 \r\n" "Call-ID: 577586163\r\n" "CSeq: 21 REGISTER\r\n" "From: ;tag=1790643209\r\n" "Server: Cirpack/v4.42x (gw_sip)\r\n" "To: ;tag=00-08075-24212984-22e348d97\r\n" "Via: SIP/2.0/UDP 192.168.0.18:5062;received=81.56.113.2;rport=5062;branch=z9hG4bK1939354046\r\n" "WWW-Authenticate: Digest realm=\"sip.ovh.net\",\r\n nonce=\"24212965507cde726e8bc37e04686459\",opaque=\"241b9fb347752f2\",stale=false,algorithm=MD5\r\n" "Content-Length: 0\r\n\r\n"; belle_sip_response_t* response; belle_sip_message_t* message = belle_sip_message_parse(raw_message); char* encoded_message = belle_sip_object_to_string(BELLE_SIP_OBJECT(message)); belle_sip_object_unref(BELLE_SIP_OBJECT(message)); message = belle_sip_message_parse(encoded_message); response = BELLE_SIP_RESPONSE(message); BC_ASSERT_EQUAL(belle_sip_response_get_status_code(response),401,int,"%d"); BC_ASSERT_PTR_NULL(belle_sip_response_get_reason_phrase(response)); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"WWW-Authenticate")); check_uri_and_headers(message); belle_sip_object_unref(message); belle_sip_free(encoded_message); } static void testRegisterRaw(void) { const char* raw_message = "REGISTER sip:192.168.0.20 SIP/2.0\r\n"\ "Via: SIP/2.0/UDP 192.168.1.8:5062;rport;branch=z9hG4bK1439638806\r\n"\ "From: ;tag=465687829\r\n"\ "To: \r\n"\ "Call-ID: 1053183492\r\n"\ "CSeq: 1 REGISTER\r\n"\ "Contact: \r\n"\ "Max-Forwards: 70\r\n"\ "User-Agent: Linphone/3.3.99.10 (eXosip2/3.3.0)\r\n"\ "Expires: 3600\r\n"\ "Content-Length: 0\r\n\r\n123456789"; belle_sip_request_t* request; size_t size=0; size_t raw_message_size= strlen(raw_message); belle_sip_message_t* message = belle_sip_message_parse_raw(raw_message,raw_message_size,&size); BC_ASSERT_EQUAL((unsigned int)raw_message_size,(unsigned int)size+9,unsigned int,"%u"); request = BELLE_SIP_REQUEST(message); BC_ASSERT_STRING_EQUAL(belle_sip_request_get_method(request),"REGISTER"); BC_ASSERT_PTR_NOT_NULL(belle_sip_request_get_uri(request)); BC_ASSERT_STRING_EQUAL(&raw_message[size],"123456789"); belle_sip_object_unref(message); } static void testOptionMessage(void) { const char* raw_message = "REGISTER sip:192.168.0.20 SIP/2.0\r\n"\ "Via: SIP/2.0/UDP 192.168.1.8:5062;rport;branch=z9hG4bK1439638806\r\n"\ "From: ;tag=465687829\r\n"\ "To: \r\n"\ "Call-ID: 1053183492\r\n"\ "CSeq: 1 REGISTER\r\n"\ "Contact: \r\n"\ "Max-Forwards: 70\r\n"\ "User-Agent: Linphone/3.3.99.10 (eXosip2/3.3.0)\r\n"\ "Expires: 3600\r\n"\ "Content-Length: 0\r\n\r\n"; belle_sip_message_t* message = belle_sip_message_parse(raw_message); belle_sip_request_t* request = BELLE_SIP_REQUEST(message); BC_ASSERT_STRING_EQUAL(belle_sip_request_get_method(request),"REGISTER"); BC_ASSERT_PTR_NOT_NULL(belle_sip_request_get_uri(request)); belle_sip_object_unref(message); } static void test_extract_source(void) { const char * invite_1="INVITE sip:jehan@81.56.113.2:50343;transport=tcp;line=f18e0009dd6cc43 SIP/2.0\r\n" "Via: SIP/2.0/TCP 37.59.129.73;branch=z9hG4bK.SKvK9U327e8mU68XUv5rt144pg\r\n" "Via: SIP/2.0/UDP 192.168.1.12:15060;rport=15060;branch=z9hG4bK1596944937;received=81.56.113.2\r\n" "Record-Route: \r\n" "Record-Route: \r\n" "Max-Forwards: 70\r\n" "From: ;tag=711138653\r\n" "To: \r\n" "Call-ID: 977107319\r\n" "CSeq: 21 INVITE\r\n" "Contact: \r\n" "Subject: Phone call\r\n" "User-Agent: Linphone/3.5.2 (eXosip2/3.6.0)\r\n" "Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO\r\n" "Content-Type: application/sdp\r\n"\ "Content-Length: 230\r\n\r\n"; const char * invite_2="INVITE sip:jehan@81.56.113.2:50343;transport=tcp;line=f18e0009dd6cc43 SIP/2.0\r\n" "Via: SIP/2.0/UDP 192.168.1.12:15060;rport=15060;branch=z9hG4bK1596944937;received=81.56.113.2\r\n" "Via: SIP/2.0/TCP 37.59.129.73;branch=z9hG4bK.SKvK9U327e8mU68XUv5rt144pg\r\n" "Record-Route: \r\n" "Record-Route: \r\n" "Max-Forwards: 70\r\n" "From: ;tag=711138653\r\n" "To: \r\n" "Call-ID: 977107319\r\n" "CSeq: 21 INVITE\r\n" "Contact: \r\n" "Subject: Phone call\r\n" "User-Agent: Linphone/3.5.2 (eXosip2/3.6.0)\r\n" "Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO\r\n" "Content-Length: 0\r\n\r\n"; belle_sip_message_t* message = belle_sip_message_parse(invite_1); belle_sip_request_t* request = BELLE_SIP_REQUEST(message); belle_sip_uri_t* source =belle_sip_request_extract_origin(request); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Content-type")); BC_ASSERT_PTR_NOT_NULL(source); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(source),"37.59.129.73"); BC_ASSERT_EQUAL(belle_sip_uri_get_port(source),0,int,"%d"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_transport_param(source),"tcp"); belle_sip_object_unref(message); belle_sip_object_unref(source); message = belle_sip_message_parse(invite_2); request = BELLE_SIP_REQUEST(message); source =belle_sip_request_extract_origin(request); BC_ASSERT_PTR_NOT_NULL(source); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_host(source),"81.56.113.2"); BC_ASSERT_EQUAL(belle_sip_uri_get_port(source),15060,int,"%d"); BC_ASSERT_STRING_EQUAL(belle_sip_uri_get_transport_param(source),"udp"); belle_sip_object_unref(message); belle_sip_object_unref(source); } static void test_sipfrag(void) { const char* raw_message = "SIP/2.0 100 Trying\r\n"; belle_sip_response_t* response; belle_sip_message_t* message = belle_sip_message_parse(raw_message); response = BELLE_SIP_RESPONSE(message); BC_ASSERT_EQUAL(belle_sip_response_get_status_code(response),100,int,"%d"); BC_ASSERT_STRING_EQUAL(belle_sip_response_get_reason_phrase(response),"Trying"); belle_sip_object_unref(message); } /*static void test_fix_contact_with_received_rport() { }*/ static void testMalformedMessage(void) { const char * raw_message= "INVITE sip:jehan@81.56.113.2:50343;transport=tcp;line=f18e0009dd6cc43 SIP/2.0\r\n" "Via: SIP/2.0/UDP 192.168.1.12:15060;rport=15060;branch=z9hG4bK1596944937;received=81.56.113.2\r\n" "Via: SIP/2.0/TCP 37.59.129.73;branch=z9hG4bK.SKvK9U327e8mU68XUv5rt144pg\r\n" "Record-Route: \r\n" "Record-Route: \r\n" "Max-Forwards: 70\r\n" "From: ;tag=711138653\r\n" "To: \r\n" "Call-ID: 977107319\r\n" "CSeq: 21 INVITE\r\n" "Contact: ;pn-tok=/throttledthirdparty\r\n" /*Slash is not allowed for contact params*/\ "Subject: Phone call\r\n" "User-Agent: Linphone/3.5.2 (eXosip2/3.6.0)\r\n" "Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO\r\n" "Content-Length: 0\r\n\r\n"; belle_sip_message_t* message = belle_sip_message_parse(raw_message); BC_ASSERT_FALSE(belle_sip_message_check_headers(message)); belle_sip_object_unref(message); } static void testMalformedOptionnalHeaderInMessage(void) { const char* raw_message = "REGISTER sip:192.168.0.20 SIP/2.0\r\n"\ "Via: SIP/2.0/UDP 192.168.1.8:5062;rport;branch=z9hG4bK1439638806\r\n"\ "From: ;tag=465687829\r\n"\ "To: \r\n"\ "Call-ID: 1053183492\r\n"\ "CSeq: 1 REGISTER\r\n"\ "Contact: ;pn-tok=/throttledthirdparty\r\n" /*Slash is not allowed for contact params*/\ "Max-Forwards: 70\r\n"\ "User-Agent: Linphone/3.3.99.10 (eXosip2/3.3.0)\r\n"\ "Expires: 3600\r\n"\ "Proxy-Authorization: Digest username=\"8117396\", realm=\"Realm\", nonce=\"MTMwNDAwMjIxMjA4NzVkODY4ZmZhODMzMzU4ZDJkOTA1NzM2NTQ2NDZlNmIz"\ ", uri=\"sip:linphone.net\", response=\"eed376ff7c963441255ec66594e470e7\", algorithm=MD5, cnonce=\"0a4f113b\", qop=auth, nc=00000001\r\n"\ "Content-Length: 0\r\n\r\n"; belle_sip_request_t* request; belle_sip_message_t* message = belle_sip_message_parse(raw_message); char* encoded_message = belle_sip_object_to_string(BELLE_SIP_OBJECT(message)); belle_sip_object_unref(BELLE_SIP_OBJECT(message)); message = belle_sip_message_parse(encoded_message); request = BELLE_SIP_REQUEST(message); BC_ASSERT_STRING_EQUAL(belle_sip_request_get_method(request),"REGISTER"); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Expires")); BC_ASSERT_PTR_NOT_NULL(BELLE_SIP_HEADER_EXPIRES(belle_sip_message_get_header(message,"Expires"))); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Proxy-Authorization")); BC_ASSERT_PTR_NULL(belle_sip_message_get_header(message,"Contact")); /*contact is optionnal in register*/ check_uri_and_headers(message); belle_sip_free(encoded_message); belle_sip_object_unref(message); } static void testMalformedMessageWithWrongStart(void) { const char * raw_message= "\r\n" "INVITE sip:jehan@81.56.113.2:50343;transport=tcp;line=f18e0009dd6cc43 SIP/2.0\r\n" "Via: SIP/2.0/UDP 192.168.1.12:15060;rport=15060;branch=z9hG4bK1596944937;received=81.56.113.2\r\n" "Via: SIP/2.0/TCP 37.59.129.73;branch=z9hG4bK.SKvK9U327e8mU68XUv5rt144pg\r\n" "Record-Route: \r\n" "Record-Route: \r\n" "Max-Forwards: 70\r\n" "From: ;tag=711138653\r\n" "To: \r\n" "Call-ID: 977107319\r\n" "CSeq: 21 INVITE\r\n" "Contact: \r\n" "Subject: Phone call\r\n" "User-Agent: Linphone/3.5.2 (eXosip2/3.6.0)\r\n" "Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO\r\n" "Content-Length: 0\r\n\r\n"; belle_sip_message_t* message = belle_sip_message_parse(raw_message); BC_ASSERT_PTR_NULL(message); } #include "belle_sip_internal.h" void channel_parser_tester_recovery_from_error_base (const char* prelude,const char* raw_message) { belle_sip_stack_t* stack = belle_sip_stack_new(NULL); belle_sip_channel_t* channel = belle_sip_stream_channel_new_client(stack , NULL , 45421 , NULL , "127.0.0.1" , 45421); belle_sip_request_t* request; belle_sip_message_t* message; if (prelude) { channel->input_stream.write_ptr = strcpy(channel->input_stream.write_ptr,prelude); channel->input_stream.write_ptr+=strlen(prelude); belle_sip_channel_parse_stream(channel,FALSE); } channel->input_stream.write_ptr = strcpy(channel->input_stream.write_ptr,raw_message); channel->input_stream.write_ptr+=strlen(raw_message); belle_sip_channel_parse_stream(channel,FALSE); BC_ASSERT_PTR_NOT_NULL(channel->incoming_messages); BC_ASSERT_PTR_NOT_NULL(channel->incoming_messages->data); message=BELLE_SIP_MESSAGE(channel->incoming_messages->data); BC_ASSERT_TRUE(BELLE_SIP_OBJECT_IS_INSTANCE_OF(message,belle_sip_request_t)); request = BELLE_SIP_REQUEST(message); BC_ASSERT_STRING_EQUAL(belle_sip_request_get_method(request),"REGISTER"); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Expires")); BC_ASSERT_PTR_NOT_NULL(BELLE_SIP_HEADER_EXPIRES(belle_sip_message_get_header(message,"Expires"))); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Proxy-Authorization")); check_uri_and_headers(message); belle_sip_object_unref(channel); belle_sip_object_unref(stack); } void channel_parser_tester_recovery_from_error(void) { const char * raw_message= "debut de stream tout pourri\r\n" "INVITE je_suis_une_fausse _request_uri_hihihi SIP/2.0\r\n" "Via: SIP/2.0/UDP 192.168.1.12:15060;rport=15060;branch=z9hG4bK1596944937;received=81.56.113.2\r\n" "Via: SIP/2.0/TCP 37.59.129.73;branch=z9hG4bK.SKvK9U327e8mU68XUv5rt144pg\r\n" "Record-Route: \r\n" "Record-Route: \r\n" "Max-Forwards: 70\r\n" "From: ;tag=711138653\r\n" "To: \r\n" "Call-ID: 977107319\r\n" "CSeq: 21 INVITE\r\n" "Contact: \r\n" "Subject: Phone call\r\n" "User-Agent: Linphone/3.5.2 (eXosip2/3.6.0)\r\n" "Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO\r\n" "Content-Length: 0\r\n" "\r\n" "REGISTER sip:192.168.0.20 SIP/2.0\r\n"\ "Via: SIP/2.0/UDP 192.168.1.8:5062;rport;branch=z9hG4bK1439638806\r\n"\ "From: ;tag=465687829\r\n"\ "To: \r\n"\ "Call-ID: 1053183492\r\n"\ "CSeq: 1 REGISTER\r\n"\ "Contact: \r\n" \ "Max-Forwards: 70\r\n"\ "User-Agent: Linphone/3.3.99.10 (eXosip2/3.3.0)\r\n"\ "Expires: 3600\r\n"\ "Proxy-Authorization: Digest username=\"8117396\", realm=\"Realm\", nonce=\"MTMwNDAwMjIxMjA4NzVkODY4ZmZhODMzMzU4ZDJkOTA1NzM2NTQ2NDZlNmIz"\ ", uri=\"sip:linphone.net\", response=\"eed376ff7c963441255ec66594e470e7\", algorithm=MD5, cnonce=\"0a4f113b\", qop=auth, nc=00000001\r\n"\ "Content-Length: 0\r\n" "\r\n"; channel_parser_tester_recovery_from_error_base (NULL, raw_message); } void channel_parser_malformed_start(void) { const char * raw_message= "debut de stream tout pourri\r\n" "REGISTER sip:192.168.0.20 SIP/2.0\r\n" "Via: SIP/2.0/UDP 192.168.1.8:5062;rport;branch=z9hG4bK1439638806\r\n" "From: ;tag=465687829\r\n" "To: \r\n" "Call-ID: 1053183492\r\n" "CSeq: 1 REGISTER\r\n" "Contact: \r\n" "Max-Forwards: 70\r\n" "User-Agent: Linphone/3.3.99.10 (eXosip2/3.3.0)\r\n" "Expires: 3600\r\n" "Proxy-Authorization: Digest username=\"8117396\", realm=\"Realm\", nonce=\"MTMwNDAwMjIxMjA4NzVkODY4ZmZhODMzMzU4ZDJkOTA1NzM2NTQ2NDZlNmIz" ", uri=\"sip:linphone.net\", response=\"eed376ff7c963441255ec66594e470e7\", algorithm=MD5, cnonce=\"0a4f113b\", qop=auth, nc=00000001\r\n" "Content-Length: 0\r\n" "\r\n"; channel_parser_tester_recovery_from_error_base (NULL, raw_message); } void channel_parser_truncated_start(void) { const char * prelude= "R"; const char * raw_message= "EGISTER sip:192.168.0.20 SIP/2.0\r\n" "Via: SIP/2.0/UDP 192.168.1.8:5062;rport;branch=z9hG4bK1439638806\r\n" "From: ;tag=465687829\r\n" "To: \r\n" "Call-ID: 1053183492\r\n" "CSeq: 1 REGISTER\r\n" "Contact: \r\n" "Max-Forwards: 70\r\n" "User-Agent: Linphone/3.3.99.10 (eXosip2/3.3.0)\r\n" "Expires: 3600\r\n" "Proxy-Authorization: Digest username=\"8117396\", realm=\"Realm\", nonce=\"MTMwNDAwMjIxMjA4NzVkODY4ZmZhODMzMzU4ZDJkOTA1NzM2NTQ2NDZlNmIz" ", uri=\"sip:linphone.net\", response=\"eed376ff7c963441255ec66594e470e7\", algorithm=MD5, cnonce=\"0a4f113b\", qop=auth, nc=00000001\r\n" "Content-Length: 0\r\n" "\r\n"; channel_parser_tester_recovery_from_error_base (prelude, raw_message); } void channel_parser_truncated_start_with_garbage(void) { const char * prelude= "truc tout pourit R"; const char * raw_message= "EGISTER sip:192.168.0.20 SIP/2.0\r\n" "Via: SIP/2.0/UDP 192.168.1.8:5062;rport;branch=z9hG4bK1439638806\r\n" "From: ;tag=465687829\r\n" "To: \r\n" "Call-ID: 1053183492\r\n" "CSeq: 1 REGISTER\r\n" "Contact: \r\n" "Max-Forwards: 70\r\n" "User-Agent: Linphone/3.3.99.10 (eXosip2/3.3.0)\r\n" "Expires: 3600\r\n" "Proxy-Authorization: Digest username=\"8117396\", realm=\"Realm\", nonce=\"MTMwNDAwMjIxMjA4NzVkODY4ZmZhODMzMzU4ZDJkOTA1NzM2NTQ2NDZlNmIz" ", uri=\"sip:linphone.net\", response=\"eed376ff7c963441255ec66594e470e7\", algorithm=MD5, cnonce=\"0a4f113b\", qop=auth, nc=00000001\r\n" "Content-Length: 0\r\n" "\r\n"; channel_parser_tester_recovery_from_error_base (prelude, raw_message); } static void testMalformedFrom_process_response_cb(void *user_ctx, const belle_sip_response_event_t *event){ int status = belle_sip_response_get_status_code(belle_sip_response_event_get_response(event)); belle_sip_message("testMalformedFrom_process_response_cb [%i]",status); (*(int*)user_ctx) += 1; // increment the call counter BC_ASSERT( status == 400 ); } #define LISTENING_POINT_PORT 45421 #define LISTENING_POINT_HOSTPORT "127.0.0.1:45421" /* need the same port as above */ static void testMalformedFrom(void){ belle_sip_stack_t* stack = belle_sip_stack_new(NULL); belle_sip_listening_point_t* lp = belle_sip_stack_create_listening_point(stack, "127.0.0.1", LISTENING_POINT_PORT, "tcp"); belle_sip_provider_t* provider = belle_sip_provider_new(stack,lp); belle_sip_listener_callbacks_t listener_cbs = {0}; const char* raw_message = "INVITE sip:us2@172.1.1.1 SIP/2.0\r\n" "Via: SIP/2.0/TCP " LISTENING_POINT_HOSTPORT ";branch=z9hG4bK-edx-U_1zoIkaq72GJPqpSmDpJQ-ouBelFuLODzf9oS5J9MeFUA;rport\r\n" "From: c\x8e test ;tag=klsk+kwDc\r\n" /** 'c test' should be enclosed in double quotes */ "To: \r\n" "Contact: \r\n" "Call-ID: 2b6fb0320-1384-179494-426025-23b6b0-2e3303331@172.16.42.1\r\n" "Content-Type: application/sdp\r\n" "Content-Length: 389\r\n" "CSeq: 1 INVITE\r\n" "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, INFO, UPDATE, REGISTER, MESSAGE, REFER, SUBSCRIBE, PRACK\r\n" "Accept: application/sdp, application/dtmf-relay\r\n" "Max-Forwards: 69\r\n" "\r\n" "v=0\r\n" "o=- 1826 1826 IN IP4 172.16.42.1\r\n" "s=VeriCall Edge\r\n" "c=IN IP4 172.16.42.1\r\n" "t=0 0\r\n" "m=audio 20506 RTP/AVP 0 8 13 101\r\n" "a=rtpmap:0 PCMU/8000\r\n" "a=rtpmap:8 PCMA/8000\r\n" "a=rtpmap:13 CN/8000\r\n" "a=rtpmap:101 telephone-event/8000\r\n" "a=fmtp:101 0-15\r\n" "m=video 24194 RTP/AVP 105 104\r\n" "a=sendonly\r\n" "a=rtpmap:105 H264/90000\r\n" "a=fmtp:105 packetization-mode=0\r\n" "a=rtpmap:104 H263-1998/90000\r\n" "a=fmtp:104 CIF=1;J=1\r\n"; belle_sip_message_t* message = belle_sip_message_parse(raw_message); belle_sip_listener_t* listener = NULL; int called_times = 0; listener_cbs.process_response_event = testMalformedFrom_process_response_cb; listener = belle_sip_listener_create_from_callbacks(&listener_cbs, &called_times); belle_sip_provider_add_sip_listener(provider, listener); belle_sip_object_ref(message); belle_sip_object_ref(message); /* double ref: originally the message is created with 0 refcount, and dispatch_message will unref() it.*/ belle_sip_provider_dispatch_message(provider, message); // we expect the stack to send a 400 error belle_sip_stack_sleep(stack,1000); BC_ASSERT_EQUAL(called_times,1,int,"%d"); belle_sip_provider_remove_sip_listener(provider,listener); belle_sip_object_unref(listener); belle_sip_object_unref(provider); belle_sip_object_unref(stack); belle_sip_object_unref(message); } static void testMalformedMandatoryField(void){ belle_sip_stack_t* stack = belle_sip_stack_new(NULL); belle_sip_listening_point_t* lp = belle_sip_stack_create_listening_point(stack, "127.0.0.1", LISTENING_POINT_PORT, "tcp"); belle_sip_provider_t* provider = belle_sip_provider_new(stack,lp); belle_sip_listener_callbacks_t listener_cbs = {0}; /* the MESSAGE message has no definition on which fields are required, which means we'll go into * * * */ const char* raw_message = "MESSAGE sip:lollol.iphone@22.22.222.222:5861;transport=tcp SIP/2.0\r\n" "Via: SIP/2.0/TCP " LISTENING_POINT_HOSTPORT ";branch=z9hG4bK5eca096a;rport\r\n" "Max-Forwards: 70\r\n" "From: \"MS TFT\" ;tag=as2413a381\r\n" "To: ;tag=\r\n" "Call-ID: 4070383971a9674201f463af2de1f012@11.11.111.111:5060\r\n" "CSeq: 103 MESSAGE\r\n" "User-Agent: Sip Server On Host (20130523_12h10)\r\n" "Content-Type: text/plain;charset=UTF-8\r\n" "Content-Length: 276\r\n" "\r\n" "\r\n" "\r\n" "\r\n"; belle_sip_message_t* message = belle_sip_message_parse(raw_message); belle_sip_listener_t* listener = NULL; int called_times = 0; listener_cbs.process_response_event = testMalformedFrom_process_response_cb; listener = belle_sip_listener_create_from_callbacks(&listener_cbs, &called_times); belle_sip_provider_add_sip_listener(provider, listener); belle_sip_object_ref(message); belle_sip_object_ref(message); /* double ref: originally the message is created with 0 refcount, and dispatch_message will unref() it.*/ belle_sip_provider_dispatch_message(provider, message); // we expect the stack to send a 400 error belle_sip_stack_sleep(stack,1000); BC_ASSERT_EQUAL(called_times,1,int,"%d"); belle_sip_provider_remove_sip_listener(provider,listener); belle_sip_object_unref(listener); belle_sip_object_unref(provider); belle_sip_object_unref(stack); belle_sip_object_unref(message); } static void testRFC2543_base(char* branch) { belle_sip_server_transaction_t *tr; const char* raw_message_base = "INVITE sip:me@127.0.0.1 SIP/2.0\r\n" "Via: SIP/2.0/UDP 192.168.1.12:15060;%srport=15060;received=81.56.113.2\r\n" "Record-Route: \r\n" "Record-Route: \r\n" "Max-Forwards: 70\r\n" "From: ;tag=711138653\r\n" "To: \r\n" "Call-ID: 977107319\r\n" "CSeq: 21 INVITE\r\n" "Contact: \r\n" "Subject: Phone call\r\n" "User-Agent: Linphone/3.5.2 (eXosip2/3.6.0)\r\n" "Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO\r\n" "Content-Length: 0\r\n" "Extended: \r\n" /*fixme lexer*/ "\r\n"; char raw_message[2048]; belle_sip_request_t* request; belle_sip_stack_t *stack=belle_sip_stack_new(NULL); belle_sip_provider_t *prov=belle_sip_provider_new(stack,NULL); belle_sip_message_t* message; snprintf(raw_message,sizeof(raw_message),raw_message_base,branch); message = belle_sip_message_parse(raw_message); belle_sip_object_ref(message); belle_sip_object_ref(message); /*yes double ref: originally the message is created with 0 refcount, and dispatch_message will unref() it.*/ belle_sip_provider_dispatch_message(prov,message); request = BELLE_SIP_REQUEST(message); BC_ASSERT_PTR_NOT_NULL(request); tr=belle_sip_provider_create_server_transaction(prov,request); BC_ASSERT_PTR_NOT_NULL(belle_sip_provider_find_matching_server_transaction(prov,request)); /*make sure branch id is properly set*/ BC_ASSERT_PTR_NOT_NULL(tr); belle_sip_object_unref(prov); belle_sip_object_unref(stack); belle_sip_object_unref(message); } static void testRFC2543Compat(void) { testRFC2543_base(""); } static void testRFC2543CompatWithBranch(void) { testRFC2543_base("branch=blablabla;"); } static void testUriHeadersInInvite(void) { belle_sip_request_t* request; belle_sip_stack_t *stack=belle_sip_stack_new(NULL); belle_sip_provider_t *prov=belle_sip_provider_new(stack,NULL); const char* raw_uri="sip:toto@titi.com" "?header1=blabla" "&header2=blue%3Bweftutu%3Dbla" "&From=toto" "&To=sip%3Atoto%40titi.com" "&Call-ID=asdads" "&CSeq=asdasd" "&Via=asdasd" "&Accept=adsad" "&Accept-Encoding=adsad" "&Accept-Language=adsad" "&Allow=adsad" "&Record-Route=adsad" "&Contact=adsad" "&Organization=adsad" "&Supported=adsad" "&User-Agent=adsad"; belle_sip_header_t* raw_header; request=belle_sip_request_create( belle_sip_uri_parse(raw_uri) ,"INVITE" ,belle_sip_provider_create_call_id(prov) ,belle_sip_header_cseq_create(20,"INVITE") ,belle_sip_header_from_create2("sip:toto@titi.com","4654") ,NULL ,belle_sip_header_via_new() ,70); BC_ASSERT_PTR_NOT_NULL(request); BC_ASSERT_PTR_NOT_NULL(raw_header=belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"header1")); if (raw_header) { BC_ASSERT_STRING_EQUAL(belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(raw_header)),"blabla"); } BC_ASSERT_PTR_NOT_NULL(raw_header=belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"header2")); if (raw_header) { BC_ASSERT_STRING_EQUAL(belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(raw_header)),"blue;weftutu=bla"); } BC_ASSERT_PTR_NOT_NULL(raw_header=belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"To")); if (raw_header) { BC_ASSERT_STRING_EQUAL(belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(raw_header)),"sip:toto@titi.com"); } BC_ASSERT_STRING_NOT_EQUAL(belle_sip_header_get_unparsed_value(belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"From")),"toto"); BC_ASSERT_PTR_NULL(belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"Record-Route")); BC_ASSERT_PTR_NULL(belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"Accept")); BC_ASSERT_PTR_NULL(belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"Accept-Encoding")); BC_ASSERT_PTR_NULL(belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"Accept-Language")); BC_ASSERT_PTR_NULL(belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"Allow")); BC_ASSERT_PTR_NULL(belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"Contact")); BC_ASSERT_PTR_NULL(belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"Organization")); BC_ASSERT_PTR_NULL(belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"Supported")); BC_ASSERT_PTR_NULL(belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),"User-Agent")); belle_sip_object_unref(request); belle_sip_object_unref(prov); belle_sip_object_unref(stack); } static void testUrisComponentsForRequest(void) { belle_sip_request_t* request; belle_sip_stack_t *stack=belle_sip_stack_new(NULL); belle_sip_provider_t *prov=belle_sip_provider_new(stack,NULL); belle_sip_client_transaction_t* t; const char* raw_uri="sip:toto@titi.com?header1=blabla"; request=belle_sip_request_create( belle_sip_uri_parse(raw_uri) ,"INVITE" ,belle_sip_provider_create_call_id(prov) ,belle_sip_header_cseq_create(20,"INVITE") ,belle_sip_header_from_create2("sip:toto@titi.com","4654") ,belle_sip_header_to_parse("To: sip:titi@titi.com:5061") ,belle_sip_header_via_new() ,70); BC_ASSERT_PTR_NOT_NULL(request); t=belle_sip_provider_create_client_transaction(prov,request); BC_ASSERT_NOT_EQUAL(belle_sip_client_transaction_send_request(t),0,int,"%d"); belle_sip_object_unref(prov); belle_sip_object_unref(stack); } static void testGenericMessage(void) { const char* raw_message = "SIP/2.0 180 Ringing\r\n" "Via: SIP/2.0/UDP 192.168.1.73:5060;branch=z9hG4bK.hhdJx4~kD;rport\r\n" "Record-Route: \r\n" "Record-Route: \r\n" "From: ;tag=5DuaoDRru\r\n" "To: ;tag=PelIhu0\r\n" "Call-ID: e-2Q~fxwNs\r\n" "CSeq: 21 INVITE\r\n" "user-agent: Linphone/3.6.99 (belle-sip/1.2.4)\r\n" "supported: replaces\r\n" "supported: outbound\r\n" "Content-Length: 0\r\n" "\r\n"; belle_sip_response_t* response; belle_sip_message_t* message = belle_sip_message_parse(raw_message); char* encoded_message = belle_sip_object_to_string(BELLE_SIP_OBJECT(message)); belle_sip_object_unref(BELLE_SIP_OBJECT(message)); message = belle_sip_message_parse(encoded_message); response = BELLE_SIP_RESPONSE(message); BC_ASSERT_EQUAL(belle_sip_response_get_status_code(response),180,int,"%d"); /* BC_ASSERT_STRING_EQUAL(belle_sip_response_get_reason_phrase(response),"Unauthorized"); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"WWW-Authenticate")); check_uri_and_headers(message);*/ belle_sip_object_unref(message); belle_sip_free(encoded_message); } static void testHttpGet(void) { const char* raw_message = "GET /index.php HTTP/1.1\r\n" "User-Agent: Wget/1.14 (darwin11.4.2)\r\n" "Accept: */*\r\n" "Host: www.linphone.org\r\n" "Connection: Keep-Alive\r\n" "\r\n"; char* marshaled_msg; belle_sip_message_t* msg = belle_sip_message_parse(raw_message); belle_http_request_t* http_request; belle_generic_uri_t* uri; belle_sip_header_extension_t* host_header; belle_sip_object_t* tmp; if (!BC_ASSERT_PTR_NOT_NULL(msg)) return; marshaled_msg=belle_sip_object_to_string(BELLE_SIP_OBJECT(msg)); belle_sip_object_unref(msg); msg = belle_sip_message_parse(marshaled_msg); belle_sip_free(marshaled_msg); tmp=belle_sip_object_clone(BELLE_SIP_OBJECT(msg)); belle_sip_object_unref(msg); msg=BELLE_SIP_MESSAGE(tmp); BC_ASSERT_TRUE(BELLE_SIP_IS_INSTANCE_OF(msg,belle_http_request_t)); http_request=BELLE_HTTP_REQUEST(msg); if (!BC_ASSERT_PTR_NOT_NULL(uri=belle_http_request_get_uri(http_request))) return; BC_ASSERT_STRING_EQUAL(belle_generic_uri_get_path(uri),"/index.php"); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(msg,"User-Agent")); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(msg,"Accept")); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(msg,"Connection")); BC_ASSERT_PTR_NOT_NULL(host_header=BELLE_SIP_HEADER_EXTENSION(belle_sip_message_get_header(msg,"Host"))); BC_ASSERT_STRING_EQUAL(belle_sip_header_extension_get_value(host_header),"www.linphone.org"); belle_sip_object_unref(msg); } static void testHttp200Ok(void) { const char* raw_message = "HTTP/1.1 200 OK\r\n" "Date: Tue, 07 Jan 2014 09:28:43 GMT\r\n" "Server: Apache\r\n" "Last-Modified: Tue, 18 Aug 1998 20:19:11 GMT\r\n" "ETag: \"8982a60-14a17-335b3dcdcadc0\"\r\n" "Accept-Ranges: bytes\r\n" "Vary: Accept-Encoding\r\n" "Content-Encoding: gzip\r\n" "Content-Length: 6\r\n" "Keep-Alive: timeout=15, max=100\r\n" "Connection: Keep-Alive\r\n" "Content-Type: text/plain\r\n" "\r\n" "blabla"; char* marshaled_msg; belle_sip_message_t* msg = belle_sip_message_parse(raw_message); belle_http_response_t* http_response; belle_sip_header_extension_t* host_header; belle_sip_object_t* tmp; if (!BC_ASSERT_PTR_NOT_NULL(msg)) return; marshaled_msg=belle_sip_object_to_string(BELLE_SIP_OBJECT(msg)); belle_sip_object_unref(msg); msg = belle_sip_message_parse(marshaled_msg); belle_sip_free(marshaled_msg); tmp=belle_sip_object_clone(BELLE_SIP_OBJECT(msg)); belle_sip_object_unref(msg); msg=BELLE_SIP_MESSAGE(tmp); BC_ASSERT_TRUE(BELLE_SIP_IS_INSTANCE_OF(msg,belle_http_response_t)); http_response=BELLE_HTTP_RESPONSE(msg); BC_ASSERT_EQUAL(belle_http_response_get_status_code(http_response),200,int,"%d"); BC_ASSERT_STRING_EQUAL(belle_http_response_get_reason_phrase(http_response),"OK"); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(msg,"Date")); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(msg,"ETag")); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(msg,"Connection")); BC_ASSERT_PTR_NOT_NULL(host_header=BELLE_SIP_HEADER_EXTENSION(belle_sip_message_get_header(msg,"Server"))); BC_ASSERT_STRING_EQUAL(belle_sip_header_extension_get_value(host_header),"Apache"); belle_sip_object_unref(msg); } void channel_parser_http_response(void) { belle_sip_stack_t* stack = belle_sip_stack_new(NULL); belle_sip_channel_t* channel = belle_sip_stream_channel_new_client(stack , NULL , 45421 , NULL , "127.0.0.1" , 45421); const char * raw_message= "HTTP/1.1 200 OK\r\n" "Cache-Control: private\r\n" "Date: Tue, 07 Jan 2014 13:51:57 GMT\r\n" "Content-Type: text/html; charset=utf-8\r\n" "Server: Microsoft-IIS/6.0\r\n" "X-Powered-By: ASP.NET\r\n" "Content-Encoding: gzip\r\n" "Vary: Accept-Encoding\r\n" "Transfer-Encoding: chunked\r\n" "\r\n" "\r\n\r\n"; belle_http_response_t* response; belle_sip_message_t* message; channel->input_stream.write_ptr = strcpy(channel->input_stream.write_ptr,raw_message); channel->input_stream.write_ptr+=strlen(raw_message); belle_sip_channel_parse_stream(channel,TRUE); BC_ASSERT_PTR_NOT_NULL(channel->incoming_messages); BC_ASSERT_PTR_NOT_NULL(channel->incoming_messages->data); message=BELLE_SIP_MESSAGE(channel->incoming_messages->data); BC_ASSERT_TRUE(BELLE_SIP_OBJECT_IS_INSTANCE_OF(message,belle_http_response_t)); response = BELLE_HTTP_RESPONSE(message); BC_ASSERT_STRING_EQUAL(belle_http_response_get_reason_phrase(response),"OK"); BC_ASSERT_EQUAL(belle_http_response_get_status_code(response),200,int,"%d"); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Cache-Control")); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header(message,"Vary")); belle_sip_object_unref(channel); belle_sip_object_unref(stack); } void testGetBody(void) { const char* raw_message = "INVITE sip:us2@172.1.1.1 SIP/2.0\r\n" "Via: SIP/2.0/TCP " LISTENING_POINT_HOSTPORT ";branch=z9hG4bK-edx-U_1zoIkaq72GJPqpSmDpJQ-ouBelFuLODzf9oS5J9MeFUA;rport\r\n" "From: test ;tag=klsk+kwDc\r\n" "To: \r\n" "Contact: \r\n" "Call-ID: 2b6fb0320-1384-179494-426025-23b6b0-2e3303331@172.16.42.1\r\n" "Content-Type: application/sdp\r\n" "Content-Length: 389\r\n" "CSeq: 1 INVITE\r\n" "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, INFO, UPDATE, REGISTER, MESSAGE, REFER, SUBSCRIBE, PRACK\r\n" "Accept: application/sdp, application/dtmf-relay\r\n" "Max-Forwards: 69\r\n" "\r\n" "v=0\r\n" "o=- 1826 1826 IN IP4 172.16.42.1\r\n" "s=VeriCall Edge\r\n" "c=IN IP4 172.16.42.1\r\n" "t=0 0\r\n" "m=audio 20506 RTP/AVP 0 8 13 101\r\n" "a=rtpmap:0 PCMU/8000\r\n" "a=rtpmap:8 PCMA/8000\r\n" "a=rtpmap:13 CN/8000\r\n" "a=rtpmap:101 telephone-event/8000\r\n" "a=fmtp:101 0-15\r\n" "m=video 24194 RTP/AVP 105 104\r\n" "a=sendonly\r\n" "a=rtpmap:105 H264/90000\r\n" "a=fmtp:105 packetization-mode=0\r\n" "a=rtpmap:104 H263-1998/90000\r\n" "a=fmtp:104 CIF=1;J=1\r\n" "nimportequoi a la fin"; belle_sip_stack_t* stack = belle_sip_stack_new(NULL); belle_sip_channel_t* channel = belle_sip_stream_channel_new_client(stack , NULL , LISTENING_POINT_PORT , NULL , "127.0.0.1" , LISTENING_POINT_PORT); belle_sip_message_t* message; belle_sip_header_content_length_t *ctlt; channel->input_stream.write_ptr = strcpy(channel->input_stream.write_ptr,raw_message); channel->input_stream.write_ptr+=strlen(raw_message); belle_sip_channel_parse_stream(channel,FALSE); BC_ASSERT_PTR_NOT_NULL(channel->incoming_messages); BC_ASSERT_PTR_NOT_NULL(channel->incoming_messages->data); message=BELLE_SIP_MESSAGE(channel->incoming_messages->data); ctlt = belle_sip_message_get_header_by_type(message,belle_sip_header_content_length_t); BC_ASSERT_PTR_NOT_NULL(ctlt); BC_ASSERT_EQUAL((unsigned int)belle_sip_header_content_length_get_content_length(ctlt),(unsigned int)strlen(belle_sip_message_get_body(message)),unsigned int,"%u"); BC_ASSERT_EQUAL((unsigned int)belle_sip_header_content_length_get_content_length(ctlt),(unsigned int)belle_sip_message_get_body_size(message),unsigned int,"%u"); belle_sip_object_unref(channel); belle_sip_object_unref(stack); } static void testHop(void){ belle_sip_uri_t * uri = belle_sip_uri_parse("sip:sip.linphone.org;maddr=[2001:41d0:8:6e48::]"); belle_sip_hop_t *hop = belle_sip_hop_new_from_uri(uri); BC_ASSERT_STRING_EQUAL(hop->host, "2001:41d0:8:6e48::"); belle_sip_object_unref(uri); belle_sip_object_unref(hop); } /* NOTE - ORDER IS IMPORTANT - MUST TEST fread() AFTER fprintf() */ test_t message_tests[] = { TEST_NO_TAG("REGISTER", testRegisterMessage), TEST_NO_TAG("INVITE", testInviteMessage), TEST_NO_TAG("INVITE with tel uri", testInviteMessageWithTelUri), TEST_NO_TAG("Option message", testOptionMessage), TEST_NO_TAG("REGISTER (Raw)", testRegisterRaw), TEST_NO_TAG("401 Response", test401Response), TEST_NO_TAG("Response without response phrase",test401ResponseWithoutResponsePhrase), TEST_NO_TAG("Origin extraction", test_extract_source), TEST_NO_TAG("SIP frag", test_sipfrag), TEST_NO_TAG("Malformed invite", testMalformedMessage), TEST_NO_TAG("Malformed from", testMalformedFrom), TEST_NO_TAG("Malformed mandatory field", testMalformedMandatoryField), TEST_NO_TAG("Malformed invite with bad begin", testMalformedMessageWithWrongStart), TEST_NO_TAG("Malformed register", testMalformedOptionnalHeaderInMessage), TEST_NO_TAG("Channel parser error recovery", channel_parser_tester_recovery_from_error), TEST_NO_TAG("Channel parser malformed start", channel_parser_malformed_start), TEST_NO_TAG("Channel parser truncated start", channel_parser_truncated_start), TEST_NO_TAG("Channel parser truncated start with garbage",channel_parser_truncated_start_with_garbage), TEST_ONE_TAG("RFC2543 compatibility", testRFC2543Compat, "LeaksMemory"), TEST_ONE_TAG("RFC2543 compatibility with branch id",testRFC2543CompatWithBranch, "LeaksMemory"), TEST_NO_TAG("Uri headers in sip INVITE",testUriHeadersInInvite), TEST_NO_TAG("Uris components in request",testUrisComponentsForRequest), TEST_NO_TAG("Generic message test",testGenericMessage), TEST_NO_TAG("HTTP get",testHttpGet), TEST_NO_TAG("HTTP 200 Ok",testHttp200Ok), TEST_NO_TAG("Channel parser for HTTP reponse",channel_parser_http_response), TEST_NO_TAG("Get body size",testGetBody), TEST_NO_TAG("Create hop from uri", testHop) }; test_suite_t message_test_suite = {"Message", NULL, NULL, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(message_tests) / sizeof(message_tests[0]), message_tests}; belle-sip-1.6.3/tester/belle_sip_refresher_tester.c000066400000000000000000001123301313437522400224440ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_internal.h" #include "belle_sip_tester.h" #ifndef _MSC_VER #include #endif #define USERNAME "toto" #define SIPDOMAIN "sip.linphone.org" #define PASSWD "secret" static char publish_body[]= "\n" "\n" " \n" " \n" " open\n" " \n" " tel:+09012345678\n" " \n" "\n"; typedef enum auth_mode { none ,digest ,digest_auth }auth_mode_t; typedef struct _status { int twoHundredOk; int fourHundredOne; int fourHundredEightyOne; int refreshOk; int refreshKo; int dialogTerminated; int fiveHundredTree; }status_t; typedef struct endpoint { belle_sip_stack_t* stack; belle_sip_listener_callbacks_t* listener_callbacks; belle_sip_provider_t* provider; belle_sip_listening_point_t *lp; belle_sip_listener_t *listener; auth_mode_t auth; status_t stat; unsigned char expire_in_contact; char nonce[32]; unsigned int nonce_count; const char* received; int rport; unsigned char unreconizable_contact; int connection_family; int register_count; int transiant_network_failure; belle_sip_refresher_t* refresher; int early_refresher; int number_of_body_found; } endpoint_t; static unsigned int wait_for(belle_sip_stack_t*s1, belle_sip_stack_t*s2,int* counter,int value,int timeout) { int retry=0; #define ITER 20 while (*counterprovider,belle_sip_request_event_get_request(event)); belle_sip_request_t* req = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction)); belle_sip_response_t* resp; belle_sip_header_contact_t* contact; belle_sip_header_expires_t* expires; belle_sip_header_authorization_t* authorization; belle_sip_header_via_t* via; const char* raw_authenticate_digest = "WWW-Authenticate: Digest " "algorithm=MD5, realm=\"" SIPDOMAIN "\", opaque=\"1bc7f9097684320\""; belle_sip_header_www_authenticate_t* www_authenticate=NULL; const char* auth_uri; const char* qop; unsigned char auth_ok=0; char local_resp[33]; belle_sip_message("caller_process_request_event received [%s] message",belle_sip_request_get_method(belle_sip_request_event_get_request(event))); switch (endpoint->auth) { case none: { auth_ok=1; break; } case digest_auth: case digest: { if ((authorization=belle_sip_message_get_header_by_type(req,belle_sip_header_authorization_t)) != NULL){ qop=belle_sip_header_authorization_get_qop(authorization); if (qop && strcmp(qop,"auth")==0) { compute_response_auth_qop( belle_sip_header_authorization_get_username(authorization) ,belle_sip_header_authorization_get_realm(authorization) ,PASSWD ,endpoint->nonce ,endpoint->nonce_count ,belle_sip_header_authorization_get_cnonce(authorization) ,belle_sip_header_authorization_get_qop(authorization) ,belle_sip_request_get_method(req) ,auth_uri=belle_sip_uri_to_string(belle_sip_header_authorization_get_uri(authorization)) ,local_resp); } else { /*digest*/ compute_response(belle_sip_header_authorization_get_username(authorization) ,belle_sip_header_authorization_get_realm(authorization) ,PASSWD ,endpoint->nonce ,belle_sip_request_get_method(req) ,auth_uri=belle_sip_uri_to_string(belle_sip_header_authorization_get_uri(authorization)) ,local_resp); } belle_sip_free((void*)auth_uri); auth_ok=strcmp(belle_sip_header_authorization_get_response(authorization),local_resp)==0; } if (auth_ok && endpoint->nonce_countauth == digest ) { sprintf(endpoint->nonce,"%p",authorization); //*change the nonce for next auth*/ } else { endpoint->nonce_count++; } } else { auth_ok=0; www_authenticate=belle_sip_header_www_authenticate_parse(raw_authenticate_digest); sprintf(endpoint->nonce,"%p",authorization); //*change the nonce for next auth*/ belle_sip_header_www_authenticate_set_nonce(www_authenticate,endpoint->nonce); if (endpoint->auth == digest_auth) { belle_sip_header_www_authenticate_add_qop(www_authenticate,"auth"); if (endpoint->nonce_count>=MAX_NC_COUNT) { belle_sip_header_www_authenticate_set_stale(www_authenticate,1); } endpoint->nonce_count=1; } } } break; default: break; } if (auth_ok) { resp=belle_sip_response_create_from_request(belle_sip_request_event_get_request(event),200); if (!endpoint->expire_in_contact) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),BELLE_SIP_HEADER(expires=belle_sip_message_get_header_by_type(req,belle_sip_header_expires_t))); } if (strcmp(belle_sip_request_get_method(req),"REGISTER")==0) { contact=belle_sip_message_get_header_by_type(req,belle_sip_header_contact_t); } else { contact=belle_sip_header_contact_new(); } if(endpoint->unreconizable_contact) { /*put an unexpected address*/ belle_sip_uri_set_host(belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(contact)),"nimportequoi.com"); } belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),BELLE_SIP_HEADER(contact)); if (strcmp(belle_sip_request_get_method(req),"PUBLISH")==0) { belle_sip_header_t* sip_if_match=belle_sip_message_get_header(BELLE_SIP_MESSAGE(resp),"SIP-If-Match"); if (sip_if_match) { BC_ASSERT_STRING_EQUAL(belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(sip_if_match)),"blablietag"); } /*check for body*/ BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_body(BELLE_SIP_MESSAGE(req))); if (belle_sip_message_get_body(BELLE_SIP_MESSAGE(req))) { BC_ASSERT_STRING_EQUAL(belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)),publish_body); } BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header_by_type(req,belle_sip_header_content_type_t)); BC_ASSERT_PTR_NOT_NULL(belle_sip_message_get_header_by_type(req,belle_sip_header_content_length_t)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),belle_sip_header_create("SIP-ETag","blablietag")); } if (strcmp(belle_sip_request_get_method(req),"SUBSCRIBE")==0){ if (belle_sip_request_event_get_dialog(event) == NULL){ belle_sip_message("creating dialog for incoming SUBSCRIBE"); belle_sip_provider_create_dialog(endpoint->provider, BELLE_SIP_TRANSACTION(server_transaction)); } } } else { resp=belle_sip_response_create_from_request(belle_sip_request_event_get_request(event),401); if (www_authenticate) belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),BELLE_SIP_HEADER(www_authenticate)); } if (endpoint->received) { via=belle_sip_message_get_header_by_type(req,belle_sip_header_via_t); belle_sip_header_via_set_received(via,endpoint->received); } if (belle_sip_message_get_body(BELLE_SIP_MESSAGE(req))) { endpoint->number_of_body_found++; } belle_sip_server_transaction_send_response(server_transaction,resp); } static void client_process_dialog_terminated(void *obj, const belle_sip_dialog_terminated_event_t *event){ endpoint_t* endpoint = (endpoint_t*)obj; endpoint->stat.dialogTerminated++; } static void server_process_dialog_terminated(void *obj, const belle_sip_dialog_terminated_event_t *event){ endpoint_t* endpoint = (endpoint_t*)obj; endpoint->stat.dialogTerminated++; } static void client_process_response_event(void *obj, const belle_sip_response_event_t *event){ //belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event); endpoint_t* endpoint = (endpoint_t*)obj; int status = belle_sip_response_get_status_code(belle_sip_response_event_get_response(event)); belle_sip_message("caller_process_response_event [%i]",status); switch (status) { case 200: endpoint->stat.twoHundredOk++; if (endpoint->connection_family!=AF_UNSPEC){ const char *host; int family_found; belle_sip_header_contact_t *ct=belle_sip_message_get_header_by_type( (belle_sip_message_t*)belle_sip_response_event_get_response(event),belle_sip_header_contact_t); if (BC_ASSERT_PTR_NOT_NULL(ct)) { host=belle_sip_uri_get_host(belle_sip_header_address_get_uri((belle_sip_header_address_t*)ct)); if (strchr(host,':')) family_found=AF_INET6; else family_found=AF_INET; BC_ASSERT_EQUAL(family_found,endpoint->connection_family,int,"%d"); } } break; case 401:endpoint->stat.fourHundredOne++; break; default: break; } } //static void process_timeout(void *obj, const belle_sip_timeout_event_t *event){ // belle_sip_message("process_timeout"); //} //static void process_transaction_terminated(void *obj, const belle_sip_transaction_terminated_event_t *event){ // belle_sip_message("process_transaction_terminated"); //} static void client_process_auth_requested(void *obj, belle_sip_auth_event_t *event){ BELLESIP_UNUSED(obj); belle_sip_message("process_auth_requested requested for [%s@%s]" ,belle_sip_auth_event_get_username(event) ,belle_sip_auth_event_get_realm(event)); belle_sip_auth_event_set_passwd(event,PASSWD); } static void belle_sip_refresher_listener (belle_sip_refresher_t* refresher ,void* user_pointer ,unsigned int status_code ,const char* reason_phrase, int will_retry) { endpoint_t* endpoint = (endpoint_t*)user_pointer; BELLESIP_UNUSED(refresher); belle_sip_message("belle_sip_refresher_listener [%i] reason [%s]",status_code,reason_phrase); if (status_code >=300) endpoint->stat.refreshKo++; switch (status_code) { case 200:endpoint->stat.refreshOk++; break; case 481:endpoint->stat.fourHundredEightyOne++;break; case 503:endpoint->stat.fiveHundredTree++;break; default: /*nop*/ break; } if (endpoint->stat.refreshKo==1 && endpoint->transiant_network_failure) { belle_sip_stack_set_send_error(endpoint->stack,0); } else if (endpoint->stat.refreshOk==1 && endpoint->stat.refreshKo==0 && endpoint->transiant_network_failure) { /*generate a network failure*/ belle_sip_refresher_set_retry_after(endpoint->refresher,100); belle_sip_stack_set_send_error(endpoint->stack,-1); } } static endpoint_t* create_endpoint(const char *ip, int port,const char* transport,belle_sip_listener_callbacks_t* listener_callbacks) { endpoint_t* endpoint = belle_sip_new0(endpoint_t); endpoint->stack=belle_sip_stack_new(NULL); endpoint->listener_callbacks=listener_callbacks; endpoint->lp=belle_sip_stack_create_listening_point(endpoint->stack,ip,port,transport); endpoint->connection_family=AF_INET; if (endpoint->lp) belle_sip_object_ref(endpoint->lp); endpoint->provider=belle_sip_stack_create_provider(endpoint->stack,endpoint->lp); belle_sip_provider_add_sip_listener(endpoint->provider,(endpoint->listener=belle_sip_listener_create_from_callbacks(endpoint->listener_callbacks,endpoint))); sprintf(endpoint->nonce,"%p",endpoint); /*initial nonce*/ endpoint->nonce_count=1; endpoint->register_count=3; return endpoint; } static void destroy_endpoint(endpoint_t* endpoint) { belle_sip_object_unref(endpoint->lp); belle_sip_object_unref(endpoint->provider); belle_sip_object_unref(endpoint->stack); belle_sip_object_unref(endpoint->listener); belle_sip_free(endpoint); } static endpoint_t* create_udp_endpoint(int port,belle_sip_listener_callbacks_t* listener_callbacks) { endpoint_t *endpoint=create_endpoint("0.0.0.0",port,"udp",listener_callbacks); BC_ASSERT_PTR_NOT_NULL(endpoint->lp); return endpoint; } static void refresher_base_with_body(endpoint_t* client ,endpoint_t *server , const char* method , belle_sip_header_content_type_t* content_type ,const char* body) { belle_sip_request_t* req; belle_sip_client_transaction_t* trans; belle_sip_header_route_t* destination_route; belle_sip_refresher_t* refresher; const char* identity = "sip:" USERNAME "@" SIPDOMAIN ; const char* domain="sip:" SIPDOMAIN ; belle_sip_header_contact_t* contact=belle_sip_header_contact_new(); belle_sip_uri_t *dest_uri; uint64_t begin; uint64_t end; if (client->expire_in_contact) belle_sip_header_contact_set_expires(contact,1); dest_uri=(belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)belle_sip_listening_point_get_uri(server->lp)); if (client->connection_family==AF_INET6) belle_sip_uri_set_host(dest_uri,"::1"); else belle_sip_uri_set_host(dest_uri,"127.0.0.1"); destination_route=belle_sip_header_route_create(belle_sip_header_address_create(NULL,dest_uri)); req=belle_sip_request_create( belle_sip_uri_parse(domain), method, belle_sip_provider_create_call_id(client->provider), belle_sip_header_cseq_create(20,method), belle_sip_header_from_create2(identity,BELLE_SIP_RANDOM_TAG), belle_sip_header_to_create2(identity,NULL), belle_sip_header_via_new(), 70); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(contact)); if (!client->expire_in_contact) belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(1))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(destination_route)); if (content_type && body) { size_t body_lenth=strlen(body); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(content_type)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_length_create(body_lenth))); belle_sip_message_set_body(BELLE_SIP_MESSAGE(req),body,body_lenth); } trans=belle_sip_provider_create_client_transaction(client->provider,req); belle_sip_object_ref(trans);/*to avoid trans from being deleted before refresher can use it*/ belle_sip_client_transaction_send_request(trans); if (client->early_refresher) { client->refresher= refresher = belle_sip_client_transaction_create_refresher(trans); } else { if (server->auth == none) { BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.twoHundredOk,1,1000)); } else { BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.fourHundredOne,1,1000)); /*update cseq*/ req=belle_sip_client_transaction_create_authenticated_request(trans,NULL,NULL); belle_sip_object_unref(trans); trans=belle_sip_provider_create_client_transaction(client->provider,req); belle_sip_object_ref(trans); belle_sip_client_transaction_send_request(trans); BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.twoHundredOk,1,1000)); } client->refresher= refresher = belle_sip_client_transaction_create_refresher(trans); } if (BC_ASSERT_PTR_NOT_NULL(refresher)) { belle_sip_object_unref(trans); belle_sip_refresher_set_listener(refresher,belle_sip_refresher_listener,client); begin = belle_sip_time_ms(); BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.refreshOk,client->register_count+(client->early_refresher?1:0),client->register_count*1000 + 1000)); end = belle_sip_time_ms(); BC_ASSERT_GREATER((long double)(end-begin),client->register_count*1000*.9,long double,"%Lf"); /*because refresh is at 90% of expire*/ BC_ASSERT_LOWER_STRICT(end-begin,(client->register_count*1000 + 2000),unsigned long long,"%llu"); /*unregister twice to make sure refresh operation can be safely cascaded*/ belle_sip_refresher_refresh(refresher,0); belle_sip_refresher_refresh(refresher,0); BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.refreshOk,client->register_count+1,1000)); BC_ASSERT_EQUAL(client->stat.refreshOk,client->register_count+1,int,"%d"); belle_sip_refresher_stop(refresher); belle_sip_object_unref(refresher); } } static void refresher_base(endpoint_t* client,endpoint_t *server, const char* method) { refresher_base_with_body(client,server,method,NULL,NULL); } static void register_base(endpoint_t* client,endpoint_t *server) { refresher_base(client,server,"REGISTER"); } static void refresher_base_with_param_and_body(const char* method , unsigned char expire_in_contact , auth_mode_t auth_mode , int early_refresher , belle_sip_header_content_type_t* content_type ,const char* body){ belle_sip_listener_callbacks_t client_callbacks; belle_sip_listener_callbacks_t server_callbacks; endpoint_t* client,*server; memset(&client_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); memset(&server_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); client_callbacks.process_response_event=client_process_response_event; client_callbacks.process_auth_requested=client_process_auth_requested; server_callbacks.process_request_event=server_process_request_event; client = create_udp_endpoint(3452,&client_callbacks); server = create_udp_endpoint(6788,&server_callbacks); server->expire_in_contact=client->expire_in_contact=expire_in_contact; server->auth=auth_mode; client->early_refresher=early_refresher; refresher_base_with_body(client,server,method,content_type,body); destroy_endpoint(client); destroy_endpoint(server); } static void refresher_base_with_param(const char* method, unsigned char expire_in_contact,auth_mode_t auth_mode) { refresher_base_with_param_and_body(method,expire_in_contact,auth_mode,FALSE,NULL,NULL); } static void register_test_with_param(unsigned char expire_in_contact,auth_mode_t auth_mode) { refresher_base_with_param("REGISTER",expire_in_contact,auth_mode); } static char *list = "\n" "\n" "\n" "\t\n" "\t\n" "\n" "\n"; static void subscribe_base(int with_resource_lists) { belle_sip_listener_callbacks_t client_callbacks; belle_sip_listener_callbacks_t server_callbacks; belle_sip_request_t* req; belle_sip_client_transaction_t* trans; belle_sip_header_route_t* destination_route; const char* identity = "sip:" USERNAME "@" SIPDOMAIN ; const char* domain="sip:" SIPDOMAIN ; endpoint_t* client,*server; belle_sip_uri_t *dest_uri; belle_sip_refresher_t* refresher; belle_sip_header_contact_t* contact=belle_sip_header_contact_new(); belle_sip_dialog_t * client_dialog; char *call_id = NULL; int dummy = 0; uint64_t begin; uint64_t end; memset(&client_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); memset(&server_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); client_callbacks.process_dialog_terminated=client_process_dialog_terminated; client_callbacks.process_response_event=client_process_response_event; client_callbacks.process_auth_requested=client_process_auth_requested; server_callbacks.process_request_event=server_process_request_event; server_callbacks.process_dialog_terminated=server_process_dialog_terminated; client = create_udp_endpoint(3452,&client_callbacks); server = create_udp_endpoint(6788,&server_callbacks); server->expire_in_contact=0; server->auth=digest_auth; dest_uri=(belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)belle_sip_listening_point_get_uri(server->lp)); belle_sip_uri_set_host(dest_uri,"127.0.0.1"); destination_route=belle_sip_header_route_create(belle_sip_header_address_create(NULL,dest_uri)); req=belle_sip_request_create( belle_sip_uri_parse(domain), "SUBSCRIBE", belle_sip_provider_create_call_id(client->provider), belle_sip_header_cseq_create(20,"SUBSCRIBE"), belle_sip_header_from_create2(identity,BELLE_SIP_RANDOM_TAG), belle_sip_header_to_create2(identity,NULL), belle_sip_header_via_new(), 70); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(contact)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(1))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_create("Event","Presence"))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(destination_route)); if (with_resource_lists) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_type_create("application","resource-lists+xml"))); belle_sip_message_set_body(BELLE_SIP_MESSAGE(req), list, strlen(list)); } trans=belle_sip_provider_create_client_transaction(client->provider,req); belle_sip_object_ref(trans);/*to avoid trans from being deleted before refresher can use it*/ belle_sip_client_transaction_send_request(trans); BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.fourHundredOne,1,1000)); req=belle_sip_client_transaction_create_authenticated_request(trans,NULL,NULL); belle_sip_object_unref(trans); trans=belle_sip_provider_create_client_transaction(client->provider,req); belle_sip_object_ref(trans); belle_sip_client_transaction_send_request(trans); BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.twoHundredOk,1,1000)); /*maybe dialog should be automatically created*/ BC_ASSERT_PTR_NOT_NULL(client_dialog = belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(trans))); call_id = belle_sip_strdup(belle_sip_header_call_id_get_call_id(belle_sip_dialog_get_call_id(client_dialog))); belle_sip_object_ref(client_dialog); refresher = belle_sip_client_transaction_create_refresher(trans); belle_sip_object_unref(trans); belle_sip_refresher_set_listener(refresher,belle_sip_refresher_listener,client); begin = belle_sip_time_ms(); BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.refreshOk,3,4000)); end = belle_sip_time_ms(); BC_ASSERT_GREATER((long double)(end-begin),3000*.9,long double,"%Lf"); BC_ASSERT_LOWER_STRICT(end-begin,5000,unsigned long long,"%llu"); belle_sip_message("simulating dialog error and recovery"); belle_sip_stack_set_send_error(client->stack, 1); BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.fourHundredEightyOne,1,4000)); /*let the transaction timeout*/ wait_for(server->stack,client->stack, &dummy, 1, 32000); belle_sip_stack_set_send_error(client->stack, 0); BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.refreshOk,4,4000)); BC_ASSERT_EQUAL(client->stat.dialogTerminated, 0, int, "%i"); /*make sure dialog has changed*/ BC_ASSERT_PTR_NOT_EQUAL(client_dialog, belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher)))); BC_ASSERT_STRING_NOT_EQUAL(call_id, belle_sip_header_call_id_get_call_id(belle_sip_dialog_get_call_id(belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher)))))); belle_sip_free(call_id); belle_sip_object_unref(client_dialog); belle_sip_message("simulating dialog terminated server side and recovery"); client_dialog = belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher))); call_id = belle_sip_strdup(belle_sip_header_call_id_get_call_id(belle_sip_dialog_get_call_id(client_dialog))); belle_sip_object_ref(client_dialog); belle_sip_provider_enable_unconditional_answer(server->provider,TRUE); belle_sip_provider_set_unconditional_answer(server->provider,481); belle_sip_refresher_refresh(refresher, 10); BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.fourHundredEightyOne,2,4000)); belle_sip_provider_enable_unconditional_answer(server->provider,FALSE); BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.refreshOk,5,4000)); BC_ASSERT_EQUAL(client->stat.dialogTerminated, 0, int, "%i"); /*make sure dialog has changed*/ BC_ASSERT_PTR_NOT_EQUAL(client_dialog, belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher)))); BC_ASSERT_STRING_NOT_EQUAL(call_id, belle_sip_header_call_id_get_call_id(belle_sip_dialog_get_call_id(belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher)))))); belle_sip_free(call_id); belle_sip_object_unref(client_dialog); belle_sip_refresher_refresh(refresher, 0); belle_sip_refresher_refresh(refresher, 0); belle_sip_refresher_stop(refresher); BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&server->stat.dialogTerminated,3,4000)); belle_sip_object_unref(refresher); if (with_resource_lists) { BC_ASSERT_EQUAL(server->number_of_body_found, (server->auth == none ?1:2), int, "%i"); } destroy_endpoint(client); destroy_endpoint(server); } static void subscribe_test(void) { subscribe_base(FALSE); } static void subscribe_list_test(void) { subscribe_base(TRUE); } static void register_expires_header(void) { register_test_with_param(0,none); } static void register_expires_in_contact(void) { register_test_with_param(1,none); } static void register_expires_header_digest(void) { register_test_with_param(0,digest); } static void register_expires_in_contact_header_digest_auth(void) { register_test_with_param(1,digest_auth); } static void register_with_failure(void) { belle_sip_listener_callbacks_t client_callbacks; belle_sip_listener_callbacks_t server_callbacks; endpoint_t* client,*server; memset(&client_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); memset(&server_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); client_callbacks.process_response_event=client_process_response_event; client_callbacks.process_auth_requested=client_process_auth_requested; server_callbacks.process_request_event=server_process_request_event; client = create_udp_endpoint(3452,&client_callbacks); server = create_udp_endpoint(6788,&server_callbacks); server->expire_in_contact=1; server->auth=digest; client->transiant_network_failure=1; register_base(client,server); BC_ASSERT_EQUAL(client->stat.refreshKo,1,int,"%d"); destroy_endpoint(client); destroy_endpoint(server); } static void register_with_unrecognizable_contact(void) { belle_sip_listener_callbacks_t client_callbacks; belle_sip_listener_callbacks_t server_callbacks; endpoint_t* client,*server; memset(&client_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); memset(&server_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); client_callbacks.process_response_event=client_process_response_event; client_callbacks.process_auth_requested=client_process_auth_requested; server_callbacks.process_request_event=server_process_request_event; client = create_udp_endpoint(3452,&client_callbacks); server = create_udp_endpoint(6788,&server_callbacks); server->expire_in_contact=1; server->unreconizable_contact=1; server->auth=digest; register_base(client,server); destroy_endpoint(client); destroy_endpoint(server); } static void register_early_refresher(void) { belle_sip_listener_callbacks_t client_callbacks; belle_sip_listener_callbacks_t server_callbacks; endpoint_t* client,*server; memset(&client_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); memset(&server_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); client_callbacks.process_response_event=client_process_response_event; client_callbacks.process_auth_requested=client_process_auth_requested; server_callbacks.process_request_event=server_process_request_event; client = create_udp_endpoint(3452,&client_callbacks); server = create_udp_endpoint(6788,&server_callbacks); server->expire_in_contact=1; server->auth=digest; client->early_refresher=1; register_base(client,server); destroy_endpoint(client); destroy_endpoint(server); } static int register_test_with_interfaces(const char *transport, const char *client_ip, const char *server_ip, int connection_family) { int ret=0; belle_sip_listener_callbacks_t client_callbacks; belle_sip_listener_callbacks_t server_callbacks; endpoint_t* client,*server; memset(&client_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); memset(&server_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); client_callbacks.process_response_event=client_process_response_event; client_callbacks.process_auth_requested=client_process_auth_requested; server_callbacks.process_request_event=server_process_request_event; client = create_endpoint(client_ip,3452,transport,&client_callbacks); client->connection_family=connection_family; client->register_count=1; server = create_endpoint(server_ip,6788,transport,&server_callbacks); server->expire_in_contact=client->expire_in_contact=0; server->auth=none; if (client->lp==NULL || server->lp==NULL){ belle_sip_warning("Cannot check ipv6 because host has no ipv6 support."); ret=-1; }else register_base(client,server); destroy_endpoint(client); destroy_endpoint(server); return ret; } static int register_test_with_random_port(const char *transport, const char *client_ip, const char *server_ip, int connection_family) { int ret=0; belle_sip_listener_callbacks_t client_callbacks; belle_sip_listener_callbacks_t server_callbacks; endpoint_t* client,*server; memset(&client_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); memset(&server_callbacks,0,sizeof(belle_sip_listener_callbacks_t)); client_callbacks.process_response_event=client_process_response_event; client_callbacks.process_auth_requested=client_process_auth_requested; server_callbacks.process_request_event=server_process_request_event; client = create_endpoint(client_ip,-1,transport,&client_callbacks); client->connection_family=connection_family; client->register_count=1; server = create_endpoint(server_ip,6788,transport,&server_callbacks); server->expire_in_contact=client->expire_in_contact=0; server->auth=none; if (client->lp==NULL || server->lp==NULL){ belle_sip_warning("Cannot check ipv6 because host has no ipv6 support."); ret=-1; }else register_base(client,server); destroy_endpoint(client); destroy_endpoint(server); return ret; } static void register_test_ipv6_to_ipv4(void){ if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } register_test_with_interfaces("udp","::0","0.0.0.0",AF_INET); } static void register_test_ipv4_to_ipv6(void){ if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } register_test_with_interfaces("udp","0.0.0.0","::0",AF_INET); } static void register_test_ipv6_to_ipv6_with_ipv4(void){ if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } register_test_with_interfaces("udp","::0","::0",AF_INET); } static void register_test_ipv6_to_ipv6_with_ipv6(void){ if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } register_test_with_interfaces("udp","::0","::0",AF_INET6); } static void register_tcp_test_ipv6_to_ipv4(void){ if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } register_test_with_interfaces("tcp","::0","0.0.0.0",AF_INET); } static void register_tcp_test_ipv4_to_ipv6(void){ if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } register_test_with_interfaces("tcp","0.0.0.0","::0",AF_INET); } static void register_tcp_test_ipv6_to_ipv6_with_ipv4(void){ if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } register_test_with_interfaces("tcp","::0","::0",AF_INET); } static void register_tcp_test_ipv6_to_ipv6_with_ipv6(void){ if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } register_test_with_interfaces("tcp","::0","::0",AF_INET6); } static void register_udp_test_ipv4_random_port(void){ register_test_with_random_port("udp","0.0.0.0","0.0.0.0",AF_INET); } static void register_udp_test_ipv6_random_port(void){ if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } register_test_with_random_port("udp","::0","0.0.0.0",AF_INET); } static void register_tcp_test_ipv4_random_port(void){ register_test_with_random_port("tcp","0.0.0.0","0.0.0.0",AF_INET); } static void register_tcp_test_ipv6_random_port(void){ if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } register_test_with_random_port("tcp","::0","0.0.0.0",AF_INET); } static void simple_publish(void) { belle_sip_header_content_type_t* content_type=belle_sip_header_content_type_create("application","pidf+xml"); refresher_base_with_param_and_body("PUBLISH",FALSE,TRUE,FALSE, content_type,publish_body); } static void simple_publish_with_early_refresher(void) { belle_sip_header_content_type_t* content_type=belle_sip_header_content_type_create("application","pidf+xml"); refresher_base_with_param_and_body("PUBLISH",FALSE,TRUE,TRUE, content_type,publish_body); } test_t refresher_tests[] = { TEST_NO_TAG("REGISTER Expires header", register_expires_header), TEST_NO_TAG("REGISTER Expires in Contact", register_expires_in_contact), TEST_NO_TAG("REGISTER Expires header digest", register_expires_header_digest), TEST_NO_TAG("REGISTER Expires in Contact digest auth", register_expires_in_contact_header_digest_auth), TEST_NO_TAG("REGISTER with failure", register_with_failure), TEST_NO_TAG("REGISTER with early refresher",register_early_refresher), TEST_NO_TAG("SUBSCRIBE", subscribe_test), TEST_NO_TAG("SUBSCRIBE of list" , subscribe_list_test), TEST_NO_TAG("PUBLISH", simple_publish), TEST_NO_TAG("PUBLISH with early refresher", simple_publish_with_early_refresher), TEST_NO_TAG("REGISTER with unrecognizable Contact", register_with_unrecognizable_contact), TEST_NO_TAG("REGISTER UDP from ipv6 to ipv4", register_test_ipv6_to_ipv4), TEST_NO_TAG("REGISTER UDP from ipv4 to ipv6", register_test_ipv4_to_ipv6), TEST_NO_TAG("REGISTER UDP from ipv6 to ipv6 with ipv4", register_test_ipv6_to_ipv6_with_ipv4), TEST_NO_TAG("REGISTER UDP from ipv6 to ipv6 with ipv6", register_test_ipv6_to_ipv6_with_ipv6), TEST_NO_TAG("REGISTER TCP from ipv6 to ipv4", register_tcp_test_ipv6_to_ipv4), TEST_NO_TAG("REGISTER TCP from ipv4 to ipv6", register_tcp_test_ipv4_to_ipv6), TEST_NO_TAG("REGISTER TCP from ipv6 to ipv6 with ipv4", register_tcp_test_ipv6_to_ipv6_with_ipv4), TEST_NO_TAG("REGISTER TCP from ipv6 to ipv6 with ipv6", register_tcp_test_ipv6_to_ipv6_with_ipv6), TEST_NO_TAG("REGISTER UDP from random port using AF_INET", register_udp_test_ipv4_random_port), TEST_NO_TAG("REGISTER UDP from random port using AF_INET6", register_udp_test_ipv6_random_port), TEST_NO_TAG("REGISTER TCP from random port using AF_INET", register_tcp_test_ipv4_random_port), TEST_NO_TAG("REGISTER TCP from random port using AF_INET6", register_tcp_test_ipv6_random_port), }; test_suite_t refresher_test_suite = {"Refresher", NULL, NULL, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(refresher_tests) / sizeof(refresher_tests[0]), refresher_tests}; belle-sip-1.6.3/tester/belle_sip_register_tester.c000066400000000000000000001264041313437522400223120ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_internal.h" #include "belle_sip_tester.h" #include "register_tester.h" const char *test_domain="sip2.linphone.org"; const char *auth_domain="sip.linphone.org"; const char *client_auth_domain="client.example.org"; const char *client_auth_outbound_proxy="sips:sip2.linphone.org:5063"; const char *no_server_running_here="sip:test.linphone.org:3;transport=tcp"; const char *no_response_here="sip:78.220.48.77:3;transport=%s"; const char *test_domain_tls_to_tcp="sip:sip2.linphone.org:5060;transport=tls"; const char *test_http_proxy_addr="sip.linphone.org"; const char *test_with_wrong_cname="sips:rototo.com;maddr=91.121.209.194"; int test_http_proxy_port = 3128 ; static int is_register_ok; static int number_of_challenge; static int using_transaction; static int io_error_count=0; belle_sip_stack_t * stack; belle_sip_provider_t *prov; static belle_sip_listener_t* l; belle_sip_request_t* authorized_request; belle_sip_listener_callbacks_t listener_callbacks; belle_sip_listener_t *listener; static void process_dialog_terminated(void *user_ctx, const belle_sip_dialog_terminated_event_t *event){ BELLESIP_UNUSED(user_ctx); BELLESIP_UNUSED(event); belle_sip_message("process_dialog_terminated called"); } static void process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){ BELLESIP_UNUSED(user_ctx); BELLESIP_UNUSED(event); belle_sip_message("process_io_error, exiting main loop"); belle_sip_main_loop_quit(belle_sip_stack_get_main_loop(stack)); io_error_count++; /*BC_ASSERT(CU_FALSE);*/ } static void process_request_event(void *user_ctx, const belle_sip_request_event_t *event){ BELLESIP_UNUSED(user_ctx); BELLESIP_UNUSED(event); belle_sip_message("process_request_event"); } static void process_response_event(void *user_ctx, const belle_sip_response_event_t *event){ int status; belle_sip_request_t* request; BELLESIP_UNUSED(user_ctx); if (!BC_ASSERT_PTR_NOT_NULL(belle_sip_response_event_get_response(event))) { return; } belle_sip_message("process_response_event [%i] [%s]" ,status=belle_sip_response_get_status_code(belle_sip_response_event_get_response(event)) ,belle_sip_response_get_reason_phrase(belle_sip_response_event_get_response(event))); if (status==401){ belle_sip_header_cseq_t* cseq; belle_sip_client_transaction_t *t; belle_sip_uri_t *dest; // BC_ASSERT_NOT_EQUAL(number_of_challenge,2); BC_ASSERT_PTR_NOT_NULL(belle_sip_response_event_get_client_transaction(event)); /*require transaction mode*/ dest=belle_sip_client_transaction_get_route(belle_sip_response_event_get_client_transaction(event)); request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(belle_sip_response_event_get_client_transaction(event))); cseq=(belle_sip_header_cseq_t*)belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CSEQ); belle_sip_header_cseq_set_seq_number(cseq,belle_sip_header_cseq_get_seq_number(cseq)+1); belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_AUTHORIZATION); belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_PROXY_AUTHORIZATION); BC_ASSERT_TRUE(belle_sip_provider_add_authorization(prov,request,belle_sip_response_event_get_response(event),NULL,NULL,auth_domain)); t=belle_sip_provider_create_client_transaction(prov,request); belle_sip_client_transaction_send_request_to(t,dest); number_of_challenge++; authorized_request=request; belle_sip_object_ref(authorized_request); } else { BC_ASSERT_EQUAL(status,200,int,"%d"); is_register_ok=1; using_transaction=belle_sip_response_event_get_client_transaction(event)!=NULL; belle_sip_main_loop_quit(belle_sip_stack_get_main_loop(stack)); } } static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event){ BELLESIP_UNUSED(user_ctx); BELLESIP_UNUSED(event); belle_sip_message("process_timeout"); } static void process_transaction_terminated(void *user_ctx, const belle_sip_transaction_terminated_event_t *event){ BELLESIP_UNUSED(user_ctx); BELLESIP_UNUSED(event); belle_sip_message("process_transaction_terminated"); } const char* belle_sip_tester_client_cert = /*for URI:sip:tester@client.example.org*/ "-----BEGIN CERTIFICATE-----\n" "MIIDYzCCAsygAwIBAgIBCDANBgkqhkiG9w0BAQUFADCBuzELMAkGA1UEBhMCRlIx\n" "EzARBgNVBAgMClNvbWUtU3RhdGUxETAPBgNVBAcMCEdyZW5vYmxlMSIwIAYDVQQK\n" "DBlCZWxsZWRvbm5lIENvbW11bmljYXRpb25zMQwwCgYDVQQLDANMQUIxFjAUBgNV\n" "BAMMDUplaGFuIE1vbm5pZXIxOjA4BgkqhkiG9w0BCQEWK2plaGFuLm1vbm5pZXJA\n" "YmVsbGVkb25uZS1jb21tdW5pY2F0aW9ucy5jb20wHhcNMTMxMDAzMTQ0MTEwWhcN\n" "MjMxMDAxMTQ0MTEwWjCBtTELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZyYW5jZTER\n" "MA8GA1UEBwwIR3Jlbm9ibGUxIjAgBgNVBAoMGUJlbGxlZG9ubmUgQ29tbXVuaWNh\n" "dGlvbnMxDDAKBgNVBAsMA0xBQjEUMBIGA1UEAwwLY2xpZW50IGNlcnQxOjA4Bgkq\n" "hkiG9w0BCQEWK2plaGFuLm1vbm5pZXJAYmVsbGVkb25uZS1jb21tdW5pY2F0aW9u\n" "cy5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALZxC/qBi/zB/4lgI7V7\n" "I5TsmMmOp0+R/TCyVnYvKQuaJXh9i+CobVM7wj/pQg8RgsY1x+4mVwH1QbhOdIN0\n" "ExYHKgLTPlo9FaN6oHPOcHxU/wt552aZhCHC+ushwUUyjy8+T09UOP+xK9V7y5uD\n" "ZY+vIOvi6QNwc5cqyy8TREwNAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4\n" "QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTL\n" "eWEg7jewRbQbKXSdBvsyygGIFzAfBgNVHSMEGDAWgBQGX13HFq9i+C1ucQOIoNYd\n" "KwR/ujANBgkqhkiG9w0BAQUFAAOBgQBTbEoi94pVfevbK22Oj8PJFsuy+el1pJG+\n" "h0XGM9SQGfbcS7PsV/MhFXtmpnmj3vQB3u5QtMGtWcLA2uxXi79i82gw+oEpBKHR\n" "sLqsNCzWNCL9n1pjpNSdqqBFGUdB9pSpnYalujAbuzkqq1ZLyzsElvK7pCaLQcSs\n" "oEncRDdPOA==\n" "-----END CERTIFICATE-----"; /* fingerprint of certificate generated using openssl x509 -fingerprint */ const char* belle_sip_tester_client_cert_fingerprint = "SHA-1 79:2F:9E:8B:28:CC:38:53:90:1D:71:DC:8F:70:66:75:E5:34:CE:C4"; const char* belle_sip_tester_private_key = "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" "MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIbEHnQwhgRwoCAggA\n" "MBQGCCqGSIb3DQMHBAgmrtCEBCP9kASCAoCq9EKInROalaBSLWY44U4RVAC+CKdx\n" "Q8ooT7Bz/grgZuCiaGf0UKINJeV4LYHoP+AWjCH8EeebIA8dldNy5rGcBTt7sXd1\n" "QOGmnkBplXTW/NTsb9maYRK56kNJhLE4DR5X5keziV1Tdy2KBmTlpllsCXWsSOBq\n" "iI63PTaakIvZxA0TEmie5QQWpH777e/LmW3vVHdH8hhp2zeDDjfSW2E290+ce4Yj\n" "SDW9oFXvauzhzhSYRkUdfoJSbpu5MYwyzhjAXQpmBJDauu7+jAU/rQw6TLmYjDNZ\n" "3PYHzyD4N7tCG9u4mPBo33dhUirP+8E1BftHB+i/VIn6pI3ypMyiFZ1ZCHqi4vhW\n" "z7aChRrUY/8XWCpln3azcfj4SW+Mz62sAChY8rn+yyxFgIno8d9rrx67jyAnYJ6Q\n" "sfIMwKp3Sz5oI7IDk8If5SuBVkpqlRV+eZFT6zRRFk65beYpq70BN2mYaKzSV8A7\n" "rnciho/dfa9wvyWmkqXciBgWh18UTACOM9HPLmQef3FGaUDLiTAGS1osyypGUEPt\n" "Ox3u51qpYkibwyQZo1+ujQkh9PiKfevIAXmty0nTFWMEED15G2SJKjunw5N1rEAh\n" "M9jlYpLnATcfigPfGo19QrIPQ1c0LB4BqdwAWN3ZLe0QqYdgwzdcwIoLQRp9iDcw\n" "Omc31+38cTc2yGQ2Y2XHZkL8GY/rkqkbhVt9Rnh+VJxFeB6FlsL66EycApe07ngx\n" "QimGP57yp4aBzpJyW+6GPf8A/Ogsv3ay1QBLUiGEJtUglRHnl9F6nm5Nxm7wubVx\n" "WEuSefVM4xgB+mfQauAJu2N9yKhzXOytslZflpa06qJedlLYFk9njvcv\n" "-----END ENCRYPTED PRIVATE KEY-----\n"; const char* belle_sip_tester_private_key_passwd="secret"; const char *belle_sip_tester_root_ca = "-----BEGIN CERTIFICATE-----\n" "MIIDRjCCAq+gAwIBAgIJAJ3nFcA7qFrOMA0GCSqGSIb3DQEBBQUAMIG7MQswCQYD\n" "VQQGEwJGUjETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIR3Jlbm9ibGUx\n" "IjAgBgNVBAoMGUJlbGxlZG9ubmUgQ29tbXVuaWNhdGlvbnMxDDAKBgNVBAsMA0xB\n" "QjEWMBQGA1UEAwwNSmVoYW4gTW9ubmllcjE6MDgGCSqGSIb3DQEJARYramVoYW4u\n" "bW9ubmllckBiZWxsZWRvbm5lLWNvbW11bmljYXRpb25zLmNvbTAeFw0xMzA0MzAx\n" "MzMwMThaFw0yMzA0MjgxMzMwMThaMIG7MQswCQYDVQQGEwJGUjETMBEGA1UECAwK\n" "U29tZS1TdGF0ZTERMA8GA1UEBwwIR3Jlbm9ibGUxIjAgBgNVBAoMGUJlbGxlZG9u\n" "bmUgQ29tbXVuaWNhdGlvbnMxDDAKBgNVBAsMA0xBQjEWMBQGA1UEAwwNSmVoYW4g\n" "TW9ubmllcjE6MDgGCSqGSIb3DQEJARYramVoYW4ubW9ubmllckBiZWxsZWRvbm5l\n" "LWNvbW11bmljYXRpb25zLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n" "z5F8mMh3SUr6NUd7tq2uW2Kdn22Zn3kNpLYb78AQK4IoQMOLGXbBdyoXvz1fublg\n" "bxtLYsiGhICd7Ul9zLGc3edn85LbD3Skb7ERx6MakRnYep3FzagZJhn14QEaZCx6\n" "3Qs0Ir4rSP7hmlpYt8VO/zqqNR3tsA59O0D9c7bpQ7UCAwEAAaNQME4wHQYDVR0O\n" "BBYEFAZfXccWr2L4LW5xA4ig1h0rBH+6MB8GA1UdIwQYMBaAFAZfXccWr2L4LW5x\n" "A4ig1h0rBH+6MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAKvmt2m1o\n" "axGKc0DjiJPypU/NsAf4Yu0nOnY8pHqJJCB0AWVoAPM7vGYPWpeH7LSdGZLuT9eK\n" "FUWGJhPnkrnklmBdVB0l7qXYjR5uf766HDkoDxuLhNifow3IYvsS+L2Y6puRQb9w\n" "HLMDE29mBDl0WyoX3h0yR0EiAO15V9A7I10=\n" "-----END CERTIFICATE-----\n" "\n" "AddTrust External Root used for *.linphone.org\n" "======================\n" "-----BEGIN CERTIFICATE-----\n" "MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML\n" "QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD\n" "VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw\n" "NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU\n" "cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg\n" "Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821\n" "+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw\n" "Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo\n" "aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy\n" "2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7\n" "7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P\n" "BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL\n" "VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk\n" "VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB\n" "IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl\n" "j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5\n" "6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355\n" "e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u\n" "G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=\n" "-----END CERTIFICATE-----\n"; static void process_auth_requested(void *user_ctx, belle_sip_auth_event_t *event){ BELLESIP_UNUSED(user_ctx); if (belle_sip_auth_event_get_mode(event) == BELLE_SIP_AUTH_MODE_HTTP_DIGEST) { const char *username = belle_sip_auth_event_get_username(event); const char *realm = belle_sip_auth_event_get_realm(event); belle_sip_message("process_auth_requested requested for [%s@%s]" ,username?username:"" ,realm?realm:""); belle_sip_auth_event_set_passwd(event,"secret"); } else if (belle_sip_auth_event_get_mode(event) == BELLE_SIP_AUTH_MODE_TLS) { const char *distinguished_name = NULL; belle_sip_certificates_chain_t* cert = belle_sip_certificates_chain_parse(belle_sip_tester_client_cert,strlen(belle_sip_tester_client_cert),BELLE_SIP_CERTIFICATE_RAW_FORMAT_PEM); belle_sip_signing_key_t* key = belle_sip_signing_key_parse(belle_sip_tester_private_key,strlen(belle_sip_tester_private_key),belle_sip_tester_private_key_passwd); belle_sip_auth_event_set_client_certificates_chain(event,cert); belle_sip_auth_event_set_signing_key(event,key); distinguished_name = belle_sip_auth_event_get_distinguished_name(event); belle_sip_message("process_auth_requested requested for DN[%s]",distinguished_name?distinguished_name:""); } else { belle_sip_error("Unexpected auth mode"); } } int register_before_all(void) { belle_sip_listening_point_t *lp; stack=belle_sip_stack_new(NULL); lp=belle_sip_stack_create_listening_point(stack,"0.0.0.0",7060,"UDP"); prov=belle_sip_stack_create_provider(stack,lp); lp=belle_sip_stack_create_listening_point(stack,"0.0.0.0",7060,"TCP"); belle_sip_provider_add_listening_point(prov,lp); lp=belle_sip_stack_create_listening_point(stack,"0.0.0.0",7061,"TLS"); if (lp) { belle_tls_crypto_config_t *crypto_config=belle_tls_crypto_config_new(); belle_tls_crypto_config_set_root_ca_data(crypto_config, belle_sip_tester_root_ca); belle_sip_tls_listening_point_set_crypto_config(BELLE_SIP_TLS_LISTENING_POINT(lp), crypto_config); belle_sip_provider_add_listening_point(prov,lp); belle_sip_object_unref(crypto_config); } listener_callbacks.process_dialog_terminated=process_dialog_terminated; listener_callbacks.process_io_error=process_io_error; listener_callbacks.process_request_event=process_request_event; listener_callbacks.process_response_event=process_response_event; listener_callbacks.process_timeout=process_timeout; listener_callbacks.process_transaction_terminated=process_transaction_terminated; listener_callbacks.process_auth_requested=process_auth_requested; listener_callbacks.listener_destroyed=NULL; listener=belle_sip_listener_create_from_callbacks(&listener_callbacks,NULL); return 0; } int register_after_all(void) { belle_sip_object_unref(prov); belle_sip_object_unref(stack); belle_sip_object_unref(listener); return 0; } void unregister_user(belle_sip_stack_t * stack ,belle_sip_provider_t *prov ,belle_sip_request_t* initial_request ,int use_transaction) { belle_sip_request_t *req; belle_sip_header_cseq_t* cseq; belle_sip_header_expires_t* expires_header; int i; belle_sip_provider_add_sip_listener(prov,l); is_register_ok=0; using_transaction=0; req=(belle_sip_request_t*)belle_sip_object_clone((belle_sip_object_t*)initial_request); belle_sip_object_ref(req); cseq=(belle_sip_header_cseq_t*)belle_sip_message_get_header((belle_sip_message_t*)req,BELLE_SIP_CSEQ); belle_sip_header_cseq_set_seq_number(cseq,belle_sip_header_cseq_get_seq_number(cseq)+2); /*+2 if initial reg was challenged*/ expires_header=(belle_sip_header_expires_t*)belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_EXPIRES); belle_sip_header_expires_set_expires(expires_header,0); if (use_transaction){ belle_sip_client_transaction_t *t; belle_sip_provider_add_authorization(prov,req,NULL,NULL,NULL,NULL); /*just in case*/ t=belle_sip_provider_create_client_transaction(prov,req); belle_sip_client_transaction_send_request(t); }else belle_sip_provider_send_request(prov,req); for(i=0;!is_register_ok && i<20 ;i++) { belle_sip_stack_sleep(stack,500); if (!use_transaction && !is_register_ok) { belle_sip_object_ref(req); belle_sip_provider_send_request(prov,req); /*manage retransmitions*/ } } BC_ASSERT_EQUAL(is_register_ok,1,int,"%d"); BC_ASSERT_EQUAL(using_transaction,use_transaction,int,"%d"); belle_sip_object_unref(req); belle_sip_provider_remove_sip_listener(prov,l); } belle_sip_request_t* try_register_user_at_domain(belle_sip_stack_t * stack ,belle_sip_provider_t *prov ,const char *transport ,int use_transaction ,const char* username ,const char* domain ,const char* outbound_proxy ,int success_expected) { belle_sip_request_t *req,*copy; char identity[256]; char uri[256]; int i; char *outbound=NULL; int do_manual_retransmissions = FALSE; number_of_challenge=0; if (transport) snprintf(uri,sizeof(uri),"sip:%s;transport=%s",domain,transport); else snprintf(uri,sizeof(uri),"sip:%s",domain); if (transport && strcasecmp("tls",transport)==0 && belle_sip_provider_get_listening_point(prov,"tls")==NULL){ belle_sip_error("No TLS support, test skipped."); return NULL; } if (outbound_proxy){ if (strstr(outbound_proxy,"sip:")==NULL && strstr(outbound_proxy,"sips:")==NULL){ outbound=belle_sip_strdup_printf("sip:%s",outbound_proxy); }else outbound=belle_sip_strdup(outbound_proxy); } snprintf(identity,sizeof(identity),"Tester ",username,domain); req=belle_sip_request_create( belle_sip_uri_parse(uri), "REGISTER", belle_sip_provider_create_call_id(prov), belle_sip_header_cseq_create(20,"REGISTER"), belle_sip_header_from_create2(identity,BELLE_SIP_RANDOM_TAG), belle_sip_header_to_create2(identity,NULL), belle_sip_header_via_new(), 70); belle_sip_object_ref(req); is_register_ok=0; io_error_count=0; using_transaction=0; belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(600))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_contact_new())); copy=(belle_sip_request_t*)belle_sip_object_ref(belle_sip_object_clone((belle_sip_object_t*)req)); belle_sip_provider_add_sip_listener(prov,l=BELLE_SIP_LISTENER(listener)); if (use_transaction){ belle_sip_client_transaction_t *t=belle_sip_provider_create_client_transaction(prov,req); belle_sip_client_transaction_send_request_to(t,outbound?belle_sip_uri_parse(outbound):NULL); }else{ belle_sip_provider_send_request(prov,req); do_manual_retransmissions = (transport == NULL) || (strcasecmp(transport,"udp") == 0); } for(i=0;!is_register_ok && i<20 && io_error_count==0;i++) { belle_sip_stack_sleep(stack,500); if (do_manual_retransmissions && !is_register_ok) { belle_sip_object_ref(req); belle_sip_provider_send_request(prov,req); /*manage retransmitions*/ } } BC_ASSERT_EQUAL(is_register_ok,success_expected,int,"%d"); if (success_expected) BC_ASSERT_EQUAL(using_transaction,use_transaction,int,"%d"); belle_sip_object_unref(req); belle_sip_provider_remove_sip_listener(prov,l); if (outbound) belle_sip_free(outbound); return copy; } belle_sip_request_t* register_user_at_domain(belle_sip_stack_t * stack ,belle_sip_provider_t *prov ,const char *transport ,int use_transaction ,const char* username ,const char* domain ,const char* outbound) { return try_register_user_at_domain(stack,prov,transport,use_transaction,username,domain,outbound,1); } belle_sip_request_t* register_user(belle_sip_stack_t * stack ,belle_sip_provider_t *prov ,const char *transport ,int use_transaction ,const char* username ,const char* outbound) { return register_user_at_domain(stack,prov,transport,use_transaction,username,test_domain,outbound); } static void register_with_outbound(const char *transport, int use_transaction,const char* outbound ) { belle_sip_request_t *req; req=register_user(stack, prov, transport,use_transaction,"tester",outbound); if (req) { unregister_user(stack,prov,req,use_transaction); belle_sip_object_unref(req); } } static void register_test(const char *transport, int use_transaction) { register_with_outbound(transport,use_transaction,NULL); } static void stateless_register_udp(void){ register_test(NULL,0); } static void stateless_register_tls(void){ register_test("tls",0); } static void stateless_register_tcp(void){ register_test("tcp",0); } static void stateful_register_udp(void){ register_test(NULL,1); } static void stateful_register_udp_with_keep_alive(void) { belle_sip_listening_point_set_keep_alive(belle_sip_provider_get_listening_point(prov,"udp"),200); register_test(NULL,1); belle_sip_main_loop_sleep(belle_sip_stack_get_main_loop(stack),500); belle_sip_listening_point_set_keep_alive(belle_sip_provider_get_listening_point(prov,"udp"),-1); } static void stateful_register_udp_with_outbound_proxy(void){ register_with_outbound("udp",1,test_domain); } static void stateful_register_udp_delayed(void){ belle_sip_stack_set_tx_delay(stack,3000); register_test(NULL,1); belle_sip_stack_set_tx_delay(stack,0); } static void stateful_register_udp_with_send_error(void){ belle_sip_request_t *req; belle_sip_stack_set_send_error(stack,-1); req=try_register_user_at_domain(stack, prov, NULL,1,"tester",test_domain,NULL,0); belle_sip_stack_set_send_error(stack,0); if (req) belle_sip_object_unref(req); } static void stateful_register_tcp(void){ register_test("tcp",1); } static void stateful_register_tls(void){ register_test("tls",1); } static void stateful_register_tls_with_wrong_cname(void){ belle_sip_request_t *req; req = try_register_user_at_domain(stack, prov, "tls", 1, "tester",test_domain, test_with_wrong_cname, 0); if (req) belle_sip_object_unref(req); } static void stateful_register_tls_with_http_proxy(void) { belle_sip_tls_listening_point_t * lp = (belle_sip_tls_listening_point_t*)belle_sip_provider_get_listening_point(prov, "tls"); if (!lp) { belle_sip_error("No TLS support, test skipped."); return; } belle_sip_provider_clean_channels(prov); belle_sip_stack_set_http_proxy_host(stack, test_http_proxy_addr); belle_sip_stack_set_http_proxy_port(stack, test_http_proxy_port); register_test("tls",1); belle_sip_stack_set_http_proxy_host(stack, NULL); belle_sip_stack_set_http_proxy_port(stack, 0); } static void stateful_register_tls_with_wrong_http_proxy(void){ belle_sip_tls_listening_point_t * lp = (belle_sip_tls_listening_point_t*)belle_sip_provider_get_listening_point(prov, "tls"); if (!lp) { belle_sip_error("No TLS support, test skipped."); return; } belle_sip_provider_clean_channels(prov); belle_sip_stack_set_http_proxy_host(stack, "mauvaisproxy.linphone.org"); belle_sip_stack_set_http_proxy_port(stack, test_http_proxy_port); try_register_user_at_domain(stack,prov,"tls",1,"tester",test_domain,NULL,0); belle_sip_stack_set_http_proxy_host(stack, NULL); belle_sip_stack_set_http_proxy_port(stack, 0); } static void bad_req_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){ BELLESIP_UNUSED(user_ctx); BELLESIP_UNUSED(event); belle_sip_message("bad_req_process_io_error not implemented yet"); } static void bad_req_process_response_event(void *user_ctx, const belle_sip_response_event_t *event){ belle_sip_response_t *resp=belle_sip_response_event_get_response(event); int *bad_request_response_received=(int*)user_ctx; if (belle_sip_response_event_get_client_transaction(event) != NULL) { BC_ASSERT_PTR_NOT_NULL(resp); BC_ASSERT_EQUAL(belle_sip_response_get_status_code(resp),400,int,"%d"); *bad_request_response_received=1; belle_sip_main_loop_quit(belle_sip_stack_get_main_loop(stack)); } } static void test_bad_request(void) { belle_sip_request_t *req; belle_sip_listener_t *bad_req_listener; belle_sip_client_transaction_t *t; belle_sip_header_address_t* route_address=belle_sip_header_address_create(NULL,belle_sip_uri_create(NULL,test_domain)); belle_sip_header_route_t* route; belle_sip_header_to_t* to = belle_sip_header_to_create2("sip:toto@titi.com",NULL); belle_sip_listener_callbacks_t cbs; belle_sip_listening_point_t *lp=belle_sip_provider_get_listening_point(prov,"TCP"); int bad_request_response_received=0; memset(&cbs,0,sizeof(cbs)); cbs.process_io_error=bad_req_process_io_error; cbs.process_response_event=bad_req_process_response_event; bad_req_listener = belle_sip_listener_create_from_callbacks(&cbs,&bad_request_response_received); req=belle_sip_request_create( BELLE_SIP_URI(belle_sip_object_clone(BELLE_SIP_OBJECT(belle_sip_header_address_get_uri(route_address)))), "REGISTER", belle_sip_provider_create_call_id(prov), belle_sip_header_cseq_create(20,"REGISTER"), belle_sip_header_from_create2("sip:toto@titi.com",BELLE_SIP_RANDOM_TAG), to, belle_sip_header_via_new(), 70); belle_sip_uri_set_transport_param(belle_sip_header_address_get_uri(route_address),"tcp"); route = belle_sip_header_route_create(route_address); belle_sip_header_set_name(BELLE_SIP_HEADER(to),"BrokenHeader"); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_expires_create(600))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(route)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_contact_new())); belle_sip_provider_add_sip_listener(prov,bad_req_listener); t=belle_sip_provider_create_client_transaction(prov,req); belle_sip_client_transaction_send_request(t); belle_sip_stack_sleep(stack,3000); BC_ASSERT_EQUAL(bad_request_response_received,1,int,"%d"); belle_sip_provider_remove_sip_listener(prov,bad_req_listener); belle_sip_object_unref(bad_req_listener); belle_sip_listening_point_clean_channels(lp); } static void test_register_authenticate(void) { belle_sip_request_t *reg; number_of_challenge=0; authorized_request=NULL; reg=register_user_at_domain(stack, prov, "udp",1,"bellesip",auth_domain,NULL); if (authorized_request) { unregister_user(stack,prov,authorized_request,1); belle_sip_object_unref(authorized_request); } belle_sip_object_unref(reg); } static void test_register_channel_inactive(void){ belle_sip_listening_point_t *lp=belle_sip_provider_get_listening_point(prov,"TCP"); BC_ASSERT_PTR_NOT_NULL(lp); if (lp) { belle_sip_stack_set_inactive_transport_timeout(stack,5); belle_sip_listening_point_clean_channels(lp); BC_ASSERT_EQUAL(belle_sip_listening_point_get_channel_count(lp),0,int,"%d"); register_test("tcp",1); BC_ASSERT_EQUAL(belle_sip_listening_point_get_channel_count(lp),1,int,"%d"); belle_sip_stack_sleep(stack, 3000); BC_ASSERT_EQUAL(belle_sip_listening_point_get_channel_count(lp),1,int,"%d"); register_test("tcp",1); belle_sip_stack_sleep(stack, 3000); BC_ASSERT_EQUAL(belle_sip_listening_point_get_channel_count(lp),1,int,"%d"); belle_sip_stack_sleep(stack,3000); BC_ASSERT_EQUAL(belle_sip_listening_point_get_channel_count(lp),0,int,"%d"); belle_sip_stack_set_inactive_transport_timeout(stack,3600); } } static void test_channel_moving_to_error_and_cleaned(void){ belle_sip_listening_point_t *lp=belle_sip_provider_get_listening_point(prov,"UDP"); BC_ASSERT_PTR_NOT_NULL(lp); if (lp) { belle_sip_request_t *req; belle_sip_client_transaction_t *tr; char identity[128]; char uri[128]; belle_sip_listening_point_clean_channels(lp); BC_ASSERT_EQUAL(belle_sip_listening_point_get_channel_count(lp),0,int,"%d"); snprintf(identity,sizeof(identity),"Tester ","bellesip",test_domain); snprintf(uri,sizeof(uri),"sip:%s",test_domain); req = belle_sip_request_create( belle_sip_uri_parse(uri), "REGISTER", belle_sip_provider_create_call_id(prov), belle_sip_header_cseq_create(20,"REGISTER"), belle_sip_header_from_create2(identity,BELLE_SIP_RANDOM_TAG), belle_sip_header_to_create2(identity,NULL), belle_sip_header_via_new(), 70); tr = belle_sip_provider_create_client_transaction(prov, req); belle_sip_client_transaction_send_request(tr); belle_sip_object_ref(tr); BC_ASSERT_EQUAL(belle_sip_listening_point_get_channel_count(lp),1,int,"%d"); /*calling notify_server_error() will make the channel enter the error state, which is what we want to test*/ belle_sip_channel_notify_server_error((belle_sip_channel_t*)lp->channels->data); /*immediately after, we clean the channel from the listening point*/ belle_sip_listening_point_clean_channels(lp); /*we just want to verify that it doesn't crash*/ belle_sip_stack_sleep(stack, 1000); belle_sip_object_unref(tr); } } static void test_register_client_authenticated(void) { belle_sip_request_t *reg; authorized_request=NULL; reg=register_user_at_domain(stack, prov, "tls",1,"tester",client_auth_domain,client_auth_outbound_proxy); if (authorized_request) { unregister_user(stack,prov,authorized_request,1); belle_sip_object_unref(authorized_request); } if (reg) belle_sip_object_unref(reg); } static void test_connection_failure(void){ belle_sip_request_t *req; io_error_count=0; req=try_register_user_at_domain(stack, prov, "TCP",1,"tester","sip.linphone.org",no_server_running_here,0); BC_ASSERT_GREATER(io_error_count,1,int,"%d"); if (req) belle_sip_object_unref(req); } static void test_connection_too_long(const char *transport){ belle_sip_request_t *req; int orig=belle_sip_stack_get_transport_timeout(stack); char *no_response_here_with_transport = belle_sip_strdup_printf(no_response_here, transport); io_error_count=0; if (transport && strcasecmp("tls",transport)==0 && belle_sip_provider_get_listening_point(prov,"tls")==NULL){ belle_sip_error("No TLS support, test skipped."); return; } belle_sip_stack_set_transport_timeout(stack,2000); req=try_register_user_at_domain(stack, prov, transport,1,"tester","sip.linphone.org",no_response_here_with_transport,0); BC_ASSERT_GREATER(io_error_count, 1, int, "%d"); belle_sip_stack_set_transport_timeout(stack,orig); belle_sip_free(no_response_here_with_transport); if (req) belle_sip_object_unref(req); } static void test_connection_too_long_tcp(void){ test_connection_too_long("tcp"); } static void test_connection_too_long_tls(void){ test_connection_too_long("tls"); } static void test_tls_to_tcp(void){ belle_sip_request_t *req; int orig=belle_sip_stack_get_transport_timeout(stack); io_error_count=0; belle_sip_stack_set_transport_timeout(stack,2000); req=try_register_user_at_domain(stack, prov, "TLS",1,"tester",test_domain,test_domain_tls_to_tcp,0); if (req){ BC_ASSERT_GREATER(io_error_count,1,int,"%d"); belle_sip_object_unref(req); } belle_sip_stack_set_transport_timeout(stack,orig); } static void register_dns_srv_tcp(void){ belle_sip_request_t *req; io_error_count=0; req=try_register_user_at_domain(stack, prov, "TCP",1,"tester",client_auth_domain,"sip:linphone.net;transport=tcp",1); BC_ASSERT_EQUAL(io_error_count,0,int,"%d"); if (req) belle_sip_object_unref(req); } static void enable_cn_mismatch(int enable){ belle_sip_listening_point_t *lp = belle_sip_provider_get_listening_point(prov, "TLS"); belle_sip_provider_clean_channels(prov); if (lp) { belle_tls_crypto_config_t *cfg = belle_sip_tls_listening_point_get_crypto_config(BELLE_SIP_TLS_LISTENING_POINT(lp)); belle_tls_crypto_config_set_verify_exceptions(cfg, enable ? BELLE_TLS_VERIFY_CN_MISMATCH : 0); } } static void register_dns_srv_tls(void){ belle_sip_request_t *req; io_error_count=0; enable_cn_mismatch(TRUE); req=try_register_user_at_domain(stack, prov, "TLS",1,"tester",client_auth_domain,"sip:linphone.net;transport=tls",1); BC_ASSERT_EQUAL(io_error_count, 0, int, "%d"); if (req) belle_sip_object_unref(req); enable_cn_mismatch(FALSE); } static void register_dns_srv_tls_with_http_proxy(void){ belle_sip_request_t *req; belle_sip_tls_listening_point_t * lp = (belle_sip_tls_listening_point_t*)belle_sip_provider_get_listening_point(prov, "tls"); if (!lp) { belle_sip_error("No TLS support, test skipped."); return; } io_error_count=0; enable_cn_mismatch(TRUE); belle_sip_stack_set_http_proxy_host(stack, test_http_proxy_addr); belle_sip_stack_set_http_proxy_port(stack, test_http_proxy_port); req=try_register_user_at_domain(stack, prov, "TLS",1,"tester",client_auth_domain,"sip:linphone.net;transport=tls",1); belle_sip_stack_set_http_proxy_host(stack, NULL); belle_sip_stack_set_http_proxy_port(stack, 0); BC_ASSERT_EQUAL(io_error_count, 0, int, "%d"); if (req) belle_sip_object_unref(req); enable_cn_mismatch(FALSE); } static void register_dns_load_balancing(void) { belle_sip_request_t *req; io_error_count = 0; req = try_register_user_at_domain(stack, prov, "TCP", 1, "tester", client_auth_domain, "sip:belle-sip.net;transport=tcp", 1); BC_ASSERT_EQUAL(io_error_count, 0, int, "%d"); if (req) belle_sip_object_unref(req); } static void process_message_response_event(void *user_ctx, const belle_sip_response_event_t *event){ int status; BELLESIP_UNUSED(user_ctx); if (BC_ASSERT_PTR_NOT_NULL(belle_sip_response_event_get_response(event))) { belle_sip_message("process_response_event [%i] [%s]" ,status=belle_sip_response_get_status_code(belle_sip_response_event_get_response(event)) ,belle_sip_response_get_reason_phrase(belle_sip_response_event_get_response(event))); if (status >= 200){ is_register_ok=status; belle_sip_main_loop_quit(belle_sip_stack_get_main_loop(stack)); } } } static belle_sip_request_t* send_message_to(belle_sip_request_t *initial_request, const char* realm, belle_sip_uri_t *outbound){ int i; int io_error_count=0; belle_sip_request_t *message_request=NULL; belle_sip_request_t *clone_request=NULL; // belle_sip_header_authorization_t * h=NULL; is_register_ok = 0; message_request=belle_sip_request_create( belle_sip_uri_parse("sip:sip.linphone.org;transport=tcp") ,"MESSAGE" ,belle_sip_provider_create_call_id(prov) ,belle_sip_header_cseq_create(22,"MESSAGE") ,belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(initial_request), belle_sip_header_from_t) ,belle_sip_header_to_parse("To: sip:marie@sip.linphone.org") ,belle_sip_header_via_new() ,70); belle_sip_message_add_header(BELLE_SIP_MESSAGE(message_request),BELLE_SIP_HEADER(belle_sip_header_expires_create(600))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(message_request),BELLE_SIP_HEADER(belle_sip_header_contact_new())); belle_sip_provider_add_authorization(prov,message_request,NULL,NULL,NULL,realm); // h = belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(message_request), belle_sip_header_authorization_t); /*if a matching authorization was found, use it as a proxy authorization*/ // if (h != NULL){ // belle_sip_header_set_name(BELLE_SIP_HEADER(h), BELLE_SIP_PROXY_AUTHORIZATION); // } clone_request = (belle_sip_request_t*)belle_sip_object_ref(belle_sip_object_clone((belle_sip_object_t*)message_request)); belle_sip_client_transaction_send_request_to(belle_sip_provider_create_client_transaction(prov,message_request),outbound); for(i=0; i<2 && io_error_count==0 &&is_register_ok==0;i++) belle_sip_stack_sleep(stack,5000); return clone_request; } static void reuse_nonce_base(const char* outbound) { belle_sip_request_t *register_request; size_t initial_auth_context_count=0;/*belle_sip_list_size(prov->auth_contexts);*/ char outbound_uri[256]; /*reset auth context*/ prov->auth_contexts = belle_sip_list_free_with_data(prov->auth_contexts,(void(*)(void*))belle_sip_authorization_destroy); if (outbound) snprintf(outbound_uri, sizeof(outbound_uri),"sip:%s",outbound); register_request=register_user_at_domain(stack, prov, "tcp",1,"marie",outbound,NULL); if (register_request) { belle_sip_header_authorization_t * h = NULL; belle_sip_request_t *message_request; belle_sip_listener_callbacks_t cbs; belle_sip_listener_t *reuse_nonce_listener; cbs.process_dialog_terminated=process_dialog_terminated; cbs.process_io_error=process_io_error; cbs.process_request_event=process_request_event; cbs.process_response_event=process_message_response_event; cbs.process_timeout=process_timeout; cbs.process_transaction_terminated=process_transaction_terminated; cbs.process_auth_requested=process_auth_requested; cbs.listener_destroyed=NULL; reuse_nonce_listener=belle_sip_listener_create_from_callbacks(&cbs,NULL); belle_sip_provider_add_sip_listener(prov,BELLE_SIP_LISTENER(reuse_nonce_listener)); /*currently only one nonce should have been used (the one for the REGISTER)*/ BC_ASSERT_EQUAL((unsigned int)belle_sip_list_size(prov->auth_contexts), (unsigned int)initial_auth_context_count+1,unsigned int,"%u"); /*this should reuse previous nonce*/ message_request=send_message_to(register_request, auth_domain,outbound?belle_sip_uri_parse(outbound_uri):NULL); BC_ASSERT_EQUAL(is_register_ok, 404,int,"%d"); h = BELLE_SIP_HEADER_AUTHORIZATION(belle_sip_message_get_header_by_type( BELLE_SIP_MESSAGE(message_request), belle_sip_header_proxy_authorization_t )); if (BC_ASSERT_PTR_NOT_NULL(h) && belle_sip_header_authorization_get_qop(h) && strcmp(belle_sip_header_authorization_get_qop(h),"auth")==0) { char * first_nonce_used; BC_ASSERT_EQUAL(2, belle_sip_header_authorization_get_nonce_count(h),int,"%d"); first_nonce_used = belle_sip_strdup(belle_sip_header_authorization_get_nonce(h)); belle_sip_free(first_nonce_used); } belle_sip_object_unref(message_request); /*new nonce should be created when not using outbound proxy realm*/ message_request=send_message_to(register_request, NULL,outbound?belle_sip_uri_parse(outbound_uri):NULL); BC_ASSERT_EQUAL(is_register_ok, 407,int,"%d"); h = BELLE_SIP_HEADER_AUTHORIZATION(belle_sip_message_get_header_by_type( BELLE_SIP_MESSAGE(message_request), belle_sip_header_proxy_authorization_t )); BC_ASSERT_PTR_NULL(h); belle_sip_object_unref(message_request); /*new nonce should be created here too*/ message_request=send_message_to(register_request, "wrongrealm",outbound?belle_sip_uri_parse(outbound_uri):NULL); BC_ASSERT_EQUAL(is_register_ok, 407,int,"%d"); h = BELLE_SIP_HEADER_AUTHORIZATION(belle_sip_message_get_header_by_type( BELLE_SIP_MESSAGE(message_request), belle_sip_header_proxy_authorization_t )); BC_ASSERT_PTR_NULL(h); belle_sip_object_unref(message_request); /*first nonce created should be reused. this test is only for qop = auth*/ message_request=send_message_to(register_request, auth_domain,outbound?belle_sip_uri_parse(outbound_uri):NULL); h = BELLE_SIP_HEADER_AUTHORIZATION(belle_sip_message_get_header_by_type( BELLE_SIP_MESSAGE(message_request), belle_sip_header_proxy_authorization_t )); if (BC_ASSERT_PTR_NOT_NULL(h) && belle_sip_header_authorization_get_qop(h) && strcmp(belle_sip_header_authorization_get_qop(h),"auth")==0) { BC_ASSERT_EQUAL(is_register_ok, 404,int,"%d"); BC_ASSERT_EQUAL(3, belle_sip_header_authorization_get_nonce_count(h),int,"%d"); } belle_sip_object_unref(message_request); belle_sip_provider_remove_sip_listener(prov,BELLE_SIP_LISTENER(reuse_nonce_listener)); unregister_user(stack,prov,register_request,1); belle_sip_object_unref(register_request); } } static void reuse_nonce(void) { reuse_nonce_base("sip.linphone.org"); } #define NONCE_SIZE 32 void register_process_request_event(char *nonce, const belle_sip_request_event_t *event) { belle_sip_request_t *req = belle_sip_request_event_get_request(event); belle_sip_header_authorization_t *authorization; int response_code = 407; char *uri_as_string= belle_sip_uri_to_string(belle_sip_request_get_uri(req)); belle_sip_response_t * response_msg; belle_sip_server_transaction_t *trans=belle_sip_provider_create_server_transaction(prov, req); if (strcasecmp(belle_sip_request_get_method(req), "REGISTER") == 0) { response_code=401; } if ( (authorization = belle_sip_message_get_header_by_type(req,belle_sip_header_authorization_t)) || (authorization = BELLE_SIP_HEADER_AUTHORIZATION(belle_sip_message_get_header_by_type(req,belle_sip_header_proxy_authorization_t)))) { char ha1[33], ha2[33], response[33]; belle_sip_auth_helper_compute_ha1(belle_sip_header_authorization_get_username(authorization) , belle_sip_header_authorization_get_realm(authorization) ,"secret", ha1); belle_sip_auth_helper_compute_ha2(belle_sip_request_get_method(req) , uri_as_string, ha2); belle_sip_auth_helper_compute_response(ha1,nonce, ha2, response); if (strcmp(response, belle_sip_header_authorization_get_response(authorization)) == 0) { belle_sip_message("Auth sucessfull"); if (strcasecmp(belle_sip_request_get_method(req), "MESSAGE") == 0) { response_code = 404; } else { response_code = 200; } } } belle_sip_random_token((nonce), NONCE_SIZE); response_msg = belle_sip_response_create_from_request(req, response_code); if (response_code == 407 || response_code == 401 ) { belle_sip_header_www_authenticate_t *www_authenticate = 401?belle_sip_header_www_authenticate_new():BELLE_SIP_HEADER_WWW_AUTHENTICATE(belle_sip_header_proxy_authenticate_new()); belle_sip_header_www_authenticate_set_realm(www_authenticate, "sip.linphone.org"); belle_sip_header_www_authenticate_set_domain(www_authenticate, "sip:sip.linphone.org"); belle_sip_header_www_authenticate_set_scheme(www_authenticate, "Digest"); belle_sip_header_www_authenticate_set_nonce(www_authenticate,nonce); belle_sip_message_add_header(BELLE_SIP_MESSAGE(response_msg), BELLE_SIP_HEADER(www_authenticate)); } else { belle_sip_header_authentication_info_t* authentication_info = belle_sip_header_authentication_info_new(); belle_sip_header_authentication_info_set_next_nonce(authentication_info, nonce); belle_sip_message_add_header(BELLE_SIP_MESSAGE(response_msg), BELLE_SIP_HEADER(authentication_info)); } belle_sip_server_transaction_send_response(trans,response_msg); } static void test_register_with_next_nonce(void) { belle_sip_listening_point_t *server_lp = belle_sip_stack_create_listening_point(stack,"0.0.0.0",bctbx_random()%20000 +1024,"TCP"); char nonce [NONCE_SIZE]; belle_sip_listener_t *server_listener; char listening_uri[256]; belle_sip_listener_callbacks_t cbs; belle_sip_random_token((nonce), sizeof(nonce)); cbs.process_dialog_terminated=NULL; cbs.process_io_error=NULL; cbs.process_request_event=(void (*)(void *user_ctx, const belle_sip_request_event_t *event))register_process_request_event; cbs.process_response_event=NULL; cbs.process_timeout=NULL; cbs.process_transaction_terminated=NULL; cbs.process_auth_requested=NULL; cbs.listener_destroyed=NULL; server_listener=belle_sip_listener_create_from_callbacks(&cbs,nonce); belle_sip_provider_add_sip_listener(prov,server_listener); belle_sip_provider_add_listening_point(prov,server_lp); snprintf(listening_uri,sizeof(listening_uri), "127.0.0.1:%i;transport=tcp",belle_sip_listening_point_get_port(server_lp)); reuse_nonce_base(listening_uri); belle_sip_provider_remove_sip_listener(prov, server_listener); belle_sip_provider_remove_listening_point(prov, server_lp); } test_t register_tests[] = { TEST_NO_TAG("Stateful UDP", stateful_register_udp), TEST_NO_TAG("Stateful UDP with keep-alive", stateful_register_udp_with_keep_alive), TEST_NO_TAG("Stateful UDP with network delay", stateful_register_udp_delayed), TEST_NO_TAG("Stateful UDP with send error", stateful_register_udp_with_send_error), TEST_NO_TAG("Stateful UDP with outbound proxy", stateful_register_udp_with_outbound_proxy), TEST_NO_TAG("Stateful TCP", stateful_register_tcp), TEST_NO_TAG("Stateful TLS", stateful_register_tls), TEST_NO_TAG("Stateful TLS with wrong cname", stateful_register_tls_with_wrong_cname), TEST_NO_TAG("Stateful TLS with http proxy", stateful_register_tls_with_http_proxy), TEST_NO_TAG("Stateful TLS with wrong http proxy", stateful_register_tls_with_wrong_http_proxy), TEST_NO_TAG("Stateless UDP", stateless_register_udp), TEST_NO_TAG("Stateless TCP", stateless_register_tcp), TEST_NO_TAG("Stateless TLS", stateless_register_tls), TEST_NO_TAG("Bad TCP request", test_bad_request), TEST_NO_TAG("Authenticate", test_register_authenticate), TEST_NO_TAG("TLS client cert authentication", test_register_client_authenticated), TEST_NO_TAG("Channel inactive", test_register_channel_inactive), TEST_NO_TAG("Channel moving to error test and cleaned", test_channel_moving_to_error_and_cleaned), TEST_NO_TAG("TCP connection failure", test_connection_failure), TEST_NO_TAG("TCP connection too long", test_connection_too_long_tcp), TEST_NO_TAG("TLS connection too long", test_connection_too_long_tls), TEST_NO_TAG("TLS connection to TCP server", test_tls_to_tcp), TEST_NO_TAG("Register with DNS SRV failover TCP", register_dns_srv_tcp), TEST_NO_TAG("Register with DNS SRV failover TLS", register_dns_srv_tls), TEST_NO_TAG("Register with DNS SRV failover TLS with http proxy", register_dns_srv_tls_with_http_proxy), TEST_NO_TAG("Register with DNS load-balancing", register_dns_load_balancing), TEST_NO_TAG("Nonce reutilization", reuse_nonce), TEST_NO_TAG("Next Nonce", test_register_with_next_nonce) }; test_suite_t register_test_suite = {"Register", register_before_all, register_after_all, NULL, NULL, sizeof(register_tests) / sizeof(register_tests[0]), register_tests}; belle-sip-1.6.3/tester/belle_sip_resolver_tester.c000066400000000000000000000522171313437522400223270ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010-2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_internal.h" #include "belle_sip_tester.h" #define IPV4_SIP_DOMAIN "sip.linphone.org" #define IPV4_SIP_IP "91.121.209.194" #define IPV4_CNAME "stun.linphone.org" #define IPV4_CNAME_IP "37.59.51.72" #define IPV4_SIP_BAD_DOMAIN "dummy.linphone.org" #define IPV4_MULTIRES_DOMAIN "yahoo.fr" /* sip2.linphone.org has an IPv6 and an IPv4 IP*/ #define IPV6_SIP_DOMAIN "sip2.linphone.org" #define IPV6_SIP_IP "2001:41d0:2:14b0::1" #define IPV6_SIP_IPV4 "88.191.250.2" #define SRV_DOMAIN "linphone.org" #define SIP_PORT 5060 typedef struct endpoint { belle_sip_stack_t* stack; belle_sip_resolver_context_t *resolver_ctx; int resolve_done; int resolve_ko; struct addrinfo *ai_list; belle_sip_list_t *srv_list; /**< List of struct dns_srv pointers */ } endpoint_t; static unsigned int wait_for(belle_sip_stack_t *stack, int *current_value, int expected_value, int timeout) { #define ITER 100 uint64_t begin, end; begin = belle_sip_time_ms(); end = begin + timeout; while ((*current_value != expected_value) && (belle_sip_time_ms() < end)) { if (stack) belle_sip_stack_sleep(stack, ITER); } if (*current_value != expected_value) return FALSE; else return TRUE; } static endpoint_t* create_endpoint(void) { endpoint_t* endpoint; endpoint = belle_sip_new0(endpoint_t); endpoint->stack = belle_sip_stack_new(NULL); return endpoint; } static void reset_endpoint(endpoint_t *endpoint) { endpoint->resolver_ctx = 0; endpoint->resolve_done = 0; endpoint->resolve_ko = 0; if (endpoint->ai_list != NULL) { bctbx_freeaddrinfo(endpoint->ai_list); endpoint->ai_list = NULL; } if (endpoint->srv_list != NULL) { belle_sip_list_free_with_data(endpoint->srv_list, belle_sip_object_unref); endpoint->srv_list = NULL; } } static void destroy_endpoint(endpoint_t *endpoint) { reset_endpoint(endpoint); belle_sip_object_unref(endpoint->stack); belle_sip_free(endpoint); belle_sip_uninit_sockets(); } static void a_resolve_done(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl) { endpoint_t *client = (endpoint_t *)data; BELLESIP_UNUSED(name); client->resolve_done = 1; if (ai_list) { client->ai_list = ai_list; client->resolve_done = 1; } else client->resolve_ko = 1; } static void srv_resolve_done(void *data, const char *name, belle_sip_list_t *srv_list, uint32_t ttl) { endpoint_t *client = (endpoint_t *)data; BELLESIP_UNUSED(name); client->resolve_done = 1; if (srv_list) { client->srv_list = srv_list; client->resolve_done = 1; } else client->resolve_ko = 1; } /* Successful IPv4 A query */ static void ipv4_a_query(void) { struct addrinfo *ai; int timeout; endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_SIP_DOMAIN, SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_NULL(client->ai_list); if (client->ai_list) { struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->ai_addr; int ntohsi = (int)ntohs(sock_in->sin_port); BC_ASSERT_EQUAL(ntohsi, SIP_PORT, int, "%d"); ai = bctbx_ip_address_to_addrinfo(AF_INET, SOCK_STREAM, IPV4_SIP_IP, SIP_PORT); if (ai) { BC_ASSERT_EQUAL(sock_in->sin_addr.s_addr, ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr, int, "%d"); bctbx_freeaddrinfo(ai); } } destroy_endpoint(client); } /* Successful IPv4 A query to a CNAME*/ /*This tests the recursion of dns.c*/ static void ipv4_cname_a_query(void) { struct addrinfo *ai; int timeout; endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_CNAME, SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL); if (client->ai_list) { struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->ai_addr; int ntohsi = (int)ntohs(sock_in->sin_port); BC_ASSERT_EQUAL(ntohsi, SIP_PORT, int, "%d"); ai = bctbx_ip_address_to_addrinfo(AF_INET, SOCK_STREAM, IPV4_CNAME_IP, SIP_PORT); if (ai) { BC_ASSERT_EQUAL(sock_in->sin_addr.s_addr, ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr, int, "%d"); bctbx_freeaddrinfo(ai); } } destroy_endpoint(client); } static void local_query(void) { int timeout; endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, "localhost", SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL); if (client->ai_list) { struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->ai_addr; int ntohsi = (int)ntohs(sock_in->sin_port); BC_ASSERT_EQUAL(ntohsi, SIP_PORT, int, "%d"); } destroy_endpoint(client); } /* Successful IPv4 A query with no result */ static void ipv4_a_query_no_result(void) { int timeout; endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_SIP_BAD_DOMAIN, SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_EQUAL(client->ai_list, NULL); destroy_endpoint(client); } /* IPv4 A query send failure */ static void ipv4_a_query_send_failure(void) { endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; belle_sip_stack_set_resolver_send_error(client->stack, -1); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_SIP_DOMAIN, SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NULL(client->resolver_ctx); belle_sip_stack_set_resolver_send_error(client->stack, 0); destroy_endpoint(client); } /* IPv4 A query timeout */ static void ipv4_a_query_timeout(void) { endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; belle_sip_stack_set_dns_timeout(client->stack, 0); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, "toto.com", SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, 2000)); BC_ASSERT_PTR_EQUAL(client->ai_list, NULL); BC_ASSERT_EQUAL(client->resolve_ko,1, int, "%d"); destroy_endpoint(client); } /* Successful IPv4 A query with multiple results */ static void ipv4_a_query_multiple_results(void) { int timeout; endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_MULTIRES_DOMAIN, SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL); if (client->ai_list) { BC_ASSERT_PTR_NOT_NULL(client->ai_list->ai_next); } destroy_endpoint(client); } static void ipv4_a_query_with_v4mapped_results(void) { int timeout; endpoint_t *client; if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_SIP_DOMAIN, SIP_PORT, AF_INET6, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_NULL(client->ai_list); destroy_endpoint(client); } /* Successful IPv6 AAAA query */ static void ipv6_aaaa_query(void) { struct addrinfo *ai; int timeout; endpoint_t *client; if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV6_SIP_DOMAIN, SIP_PORT, AF_INET6, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL); if (client->ai_list) { struct addrinfo *next; struct sockaddr_in6 *sock_in6 = (struct sockaddr_in6 *)client->ai_list->ai_addr; int ntohsi = ntohs(sock_in6->sin6_port); BC_ASSERT_EQUAL(ntohsi, SIP_PORT, int, "%d"); /*the IPv6 address shall return first, and must be a real ipv6 address*/ BC_ASSERT_EQUAL(client->ai_list->ai_family,AF_INET6,int,"%d"); BC_ASSERT_FALSE(IN6_IS_ADDR_V4MAPPED(&sock_in6->sin6_addr)); ai = bctbx_ip_address_to_addrinfo(AF_INET6, SOCK_STREAM, IPV6_SIP_IP, SIP_PORT); BC_ASSERT_PTR_NOT_NULL(ai); if (ai) { struct in6_addr *ipv6_address = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; int i; for (i = 0; i < 8; i++) { BC_ASSERT_EQUAL(sock_in6->sin6_addr.s6_addr[i], ipv6_address->s6_addr[i], int, "%d"); } bctbx_freeaddrinfo(ai); } next=client->ai_list->ai_next; BC_ASSERT_PTR_NOT_NULL(next); if (next){ int ntohsi = ntohs(sock_in6->sin6_port); sock_in6 = (struct sockaddr_in6 *)next->ai_addr; BC_ASSERT_EQUAL(next->ai_family,AF_INET6,int,"%d"); BC_ASSERT_TRUE(IN6_IS_ADDR_V4MAPPED(&sock_in6->sin6_addr)); BC_ASSERT_EQUAL(ntohsi, SIP_PORT, int, "%d"); ai = bctbx_ip_address_to_addrinfo(AF_INET6, SOCK_STREAM, IPV6_SIP_IPV4, SIP_PORT); BC_ASSERT_PTR_NOT_NULL(ai); if (ai) { struct in6_addr *ipv6_address = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; int i; for (i = 0; i < 8; i++) { BC_ASSERT_EQUAL(sock_in6->sin6_addr.s6_addr[i], ipv6_address->s6_addr[i], int, "%d"); } bctbx_freeaddrinfo(ai); } } } destroy_endpoint(client); } /* Successful SRV query */ static void srv_query(void) { int timeout; endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); client->resolver_ctx = belle_sip_stack_resolve_srv(client->stack, "sip", "udp", SRV_DOMAIN, srv_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_NULL(client->srv_list); BC_ASSERT_NOT_EQUAL((unsigned int)belle_sip_list_size(client->srv_list), 0,unsigned int,"%u"); if (client->srv_list && (belle_sip_list_size(client->srv_list) > 0)) { belle_sip_dns_srv_t *result_srv = belle_sip_list_nth_data(client->srv_list, 0); BC_ASSERT_EQUAL(belle_sip_dns_srv_get_port(result_srv), SIP_PORT, int, "%d"); } destroy_endpoint(client); } /* Successful SRV + A or AAAA queries */ static void srv_a_query(void) { int timeout; endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); client->resolver_ctx = belle_sip_stack_resolve(client->stack, "sip", "udp", SRV_DOMAIN, SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL); destroy_endpoint(client); } /* Successful SRV query with no result + A query */ static void srv_a_query_no_srv_result(void) { struct addrinfo *ai; int timeout; endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); client->resolver_ctx = belle_sip_stack_resolve(client->stack, "sip", "udp", IPV4_CNAME, SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_NULL(client->ai_list); if (client->ai_list) { struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->ai_addr; int ntohsi = (int)ntohs(sock_in->sin_port); BC_ASSERT_EQUAL(ntohsi, SIP_PORT, int, "%d"); ai = bctbx_ip_address_to_addrinfo(AF_INET, SOCK_STREAM, IPV4_CNAME_IP, SIP_PORT); if (ai) { BC_ASSERT_EQUAL(sock_in->sin_addr.s_addr, ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr, int, "%d"); bctbx_freeaddrinfo(ai); } } destroy_endpoint(client); } static void local_full_query(void) { int timeout; endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); client->resolver_ctx = belle_sip_stack_resolve(client->stack, "sip", "tcp", "localhost", SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL); if (client->ai_list) { struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->ai_addr; int ntohsi = (int)ntohs(sock_in->sin_port); BC_ASSERT_EQUAL(ntohsi, SIP_PORT, int, "%d"); } destroy_endpoint(client); } /* No query needed because already resolved */ static void no_query_needed(void) { struct addrinfo *ai; endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; client->resolver_ctx = belle_sip_stack_resolve(client->stack, "sip", "udp", IPV4_SIP_IP, SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NULL(client->resolver_ctx); BC_ASSERT_TRUE(client->resolve_done); BC_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL); if (client->ai_list) { struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->ai_addr; int ntohsi = (int)ntohs(sock_in->sin_port); BC_ASSERT_EQUAL(ntohsi, SIP_PORT, int, "%d"); ai = bctbx_ip_address_to_addrinfo(AF_INET, SOCK_STREAM, IPV4_SIP_IP, SIP_PORT); if (ai) { BC_ASSERT_EQUAL(sock_in->sin_addr.s_addr, ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr, int, "%d"); bctbx_freeaddrinfo(ai); } } destroy_endpoint(client); } static void set_custom_resolv_conf(belle_sip_stack_t *stack, const char *ns[3]){ char *resolv_file = bc_tester_file("tmp_resolv.conf"); FILE *f=fopen(resolv_file,"w"); BC_ASSERT_PTR_NOT_NULL(f); if (f){ int i; for (i=0; i<3; ++i){ if (ns[i]!=NULL){ fprintf(f,"nameserver %s\n",ns[i]); }else break; } fclose(f); } belle_sip_stack_set_dns_resolv_conf_file(stack, resolv_file); free(resolv_file); } static void _dns_fallback(const char *nameservers[]) { struct addrinfo *ai; int timeout; endpoint_t *client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); set_custom_resolv_conf(client->stack,nameservers); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_SIP_DOMAIN, SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL); if (client->ai_list) { struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->ai_addr; int ntohsi = (int)ntohs(sock_in->sin_port); BC_ASSERT_EQUAL(ntohsi, SIP_PORT, int, "%d"); ai = bctbx_ip_address_to_addrinfo(AF_INET, SOCK_STREAM, IPV4_SIP_IP, SIP_PORT); if (ai) { BC_ASSERT_EQUAL(sock_in->sin_addr.s_addr, ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr, int, "%d"); bctbx_freeaddrinfo(ai); } } destroy_endpoint(client); } static void dns_fallback(void) { const char *nameservers[]={ "94.23.19.176", /*linphone.org ; this is not a name server, it will not respond*/ "8.8.8.8", /* public nameserver, should work*/ NULL }; _dns_fallback(nameservers); } static void dns_fallback_because_of_scope_link_ipv6(void) { const char *nameservers[]={ "fe80::fdc5:99ef:ac05:5c55%enp0s25", /* Scope link IPv6 name server that will not respond */ "8.8.8.8", /* public nameserver, should work*/ NULL }; _dns_fallback(nameservers); } static void dns_fallback_because_of_invalid_ipv6(void) { const char *nameservers[]={ "fe80::ba26:6cff:feb9:145c", /* Invalid IPv6 name server */ "8.8.8.8", /* public nameserver, should work*/ NULL }; _dns_fallback(nameservers); } static void ipv6_dns_server(void) { struct addrinfo *ai; int timeout; endpoint_t *client; const char *nameservers[]={ "2001:4860:4860::8888", NULL }; if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); set_custom_resolv_conf(client->stack,nameservers); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_SIP_DOMAIN, SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_NULL(client->ai_list); if (client->ai_list) { struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->ai_addr; int ntohsi = (int)ntohs(sock_in->sin_port); BC_ASSERT_EQUAL(ntohsi, SIP_PORT, int, "%d"); ai = bctbx_ip_address_to_addrinfo(AF_INET, SOCK_STREAM, IPV4_SIP_IP, SIP_PORT); if (ai) { BC_ASSERT_EQUAL(sock_in->sin_addr.s_addr, ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr, int, "%d"); bctbx_freeaddrinfo(ai); } } destroy_endpoint(client); } static void ipv4_and_ipv6_dns_server(void) { struct addrinfo *ai; int timeout; endpoint_t *client; const char *nameservers[]={ "8.8.8.8", "2a01:e00::2", NULL }; if (!belle_sip_tester_ipv6_available()){ belle_sip_warning("Test skipped, IPv6 connectivity not available."); return; } client = create_endpoint(); if (!BC_ASSERT_PTR_NOT_NULL(client)) return; timeout = belle_sip_stack_get_dns_timeout(client->stack); set_custom_resolv_conf(client->stack,nameservers); client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_SIP_DOMAIN, SIP_PORT, AF_INET, a_resolve_done, client); BC_ASSERT_PTR_NOT_NULL(client->resolver_ctx); BC_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout)); BC_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL); if (client->ai_list) { struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->ai_addr; int ntohsi = (int)ntohs(sock_in->sin_port); BC_ASSERT_EQUAL(ntohsi, SIP_PORT, int, "%d"); ai = bctbx_ip_address_to_addrinfo(AF_INET, SOCK_STREAM, IPV4_SIP_IP, SIP_PORT); if (ai) { BC_ASSERT_EQUAL(sock_in->sin_addr.s_addr, ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr, int, "%d"); bctbx_freeaddrinfo(ai); } } destroy_endpoint(client); } test_t resolver_tests[] = { TEST_NO_TAG("A query (IPv4)", ipv4_a_query), TEST_NO_TAG("A query (IPv4) with CNAME", ipv4_cname_a_query), TEST_NO_TAG("A query (IPv4) with no result", ipv4_a_query_no_result), TEST_NO_TAG("A query (IPv4) with send failure", ipv4_a_query_send_failure), TEST_NO_TAG("A query (IPv4) with timeout", ipv4_a_query_timeout), TEST_NO_TAG("A query (IPv4) with multiple results", ipv4_a_query_multiple_results), TEST_NO_TAG("A query (IPv4) with AI_V4MAPPED results", ipv4_a_query_with_v4mapped_results), TEST_NO_TAG("Local query", local_query), TEST_NO_TAG("AAAA query (IPv6)", ipv6_aaaa_query), TEST_NO_TAG("SRV query", srv_query), TEST_NO_TAG("SRV + A query", srv_a_query), TEST_NO_TAG("SRV + A query with no SRV result", srv_a_query_no_srv_result), TEST_NO_TAG("Local SRV+A query", local_full_query), TEST_NO_TAG("No query needed", no_query_needed), TEST_NO_TAG("DNS fallback", dns_fallback), TEST_NO_TAG("DNS fallback because of scope link IPv6", dns_fallback_because_of_scope_link_ipv6), TEST_NO_TAG("DNS fallback because of invalid IPv6", dns_fallback_because_of_invalid_ipv6), TEST_NO_TAG("IPv6 DNS server", ipv6_dns_server), TEST_NO_TAG("IPv4 and v6 DNS servers", ipv4_and_ipv6_dns_server) }; test_suite_t resolver_test_suite = {"Resolver", NULL, NULL, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(resolver_tests) / sizeof(resolver_tests[0]), resolver_tests}; belle-sip-1.6.3/tester/belle_sip_tester.c000066400000000000000000000202751313437522400204050ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "belle_sip_tester.h" #include #include #include "port.h" extern const char *test_domain; extern const char *auth_domain; static char* all_leaks_buffer = NULL; static const char *belle_sip_tester_root_ca_path = NULL; static FILE * log_file = NULL; static belle_sip_object_pool_t *pool; static int leaked_objects_count; static int _belle_sip_tester_ipv6_available(void){ struct addrinfo *ai=bctbx_ip_address_to_addrinfo(AF_INET6,SOCK_STREAM,"2a01:e00::2",53); if (ai){ struct sockaddr_storage ss; struct addrinfo src; socklen_t slen=sizeof(ss); char localip[128]; int port=0; belle_sip_get_src_addr_for(ai->ai_addr,(socklen_t)ai->ai_addrlen,(struct sockaddr*) &ss,&slen,4444); src.ai_addr=(struct sockaddr*) &ss; src.ai_addrlen=slen; bctbx_addrinfo_to_ip_address(&src,localip, sizeof(localip),&port); bctbx_freeaddrinfo(ai); return strcmp(localip,"::1")!=0; } return FALSE; } static int ipv6_available=0; int belle_sip_tester_ipv6_available(void){ return ipv6_available; } const char * belle_sip_tester_get_root_ca_path(void) { return belle_sip_tester_root_ca_path; } void belle_sip_tester_set_root_ca_path(const char *root_ca_path) { belle_sip_tester_root_ca_path = root_ca_path; } static void log_handler(int lev, const char *fmt, va_list args) { #ifdef _WIN32 /* We must use stdio to avoid log formatting (for autocompletion etc.) */ vfprintf(lev == BELLE_SIP_LOG_ERROR ? stderr : stdout, fmt, args); fprintf(lev == BELLE_SIP_LOG_ERROR ? stderr : stdout, "\n"); #else va_list cap; va_copy(cap,args); vfprintf(lev == BELLE_SIP_LOG_ERROR ? stderr : stdout, fmt, cap); fprintf(lev == BELLE_SIP_LOG_ERROR ? stderr : stdout, "\n"); va_end(cap); #endif if (log_file){ belle_sip_logv(BELLE_SIP_LOG_DOMAIN,lev, fmt, args); } } void belle_sip_tester_init(void(*ftester_printf)(int level, const char *fmt, va_list args)) { if (ftester_printf == NULL) ftester_printf = log_handler; bc_tester_init(ftester_printf, BELLE_SIP_LOG_MESSAGE, BELLE_SIP_LOG_ERROR, NULL); belle_sip_init_sockets(); belle_sip_object_enable_marshal_check(TRUE); ipv6_available=_belle_sip_tester_ipv6_available(); bc_tester_add_suite(&cast_test_suite); bc_tester_add_suite(&sip_uri_test_suite); bc_tester_add_suite(&fast_sip_uri_test_suite); bc_tester_add_suite(&perf_sip_uri_test_suite); bc_tester_add_suite(&generic_uri_test_suite); bc_tester_add_suite(&headers_test_suite); bc_tester_add_suite(&core_test_suite); bc_tester_add_suite(&sdp_test_suite); bc_tester_add_suite(&resolver_test_suite); bc_tester_add_suite(&message_test_suite); bc_tester_add_suite(&authentication_helper_test_suite); bc_tester_add_suite(®ister_test_suite); bc_tester_add_suite(&dialog_test_suite); bc_tester_add_suite(&refresher_test_suite); bc_tester_add_suite(&http_test_suite); } void belle_sip_tester_uninit(void) { belle_sip_object_unref(pool); belle_sip_uninit_sockets(); // show all leaks that happened during the test if (all_leaks_buffer) { bc_tester_printf(BELLE_SIP_LOG_MESSAGE, all_leaks_buffer); belle_sip_free(all_leaks_buffer); } bc_tester_uninit(); } void belle_sip_tester_before_each(void) { belle_sip_object_enable_leak_detector(TRUE); leaked_objects_count = belle_sip_object_get_object_count(); } void belle_sip_tester_after_each(void) { int leaked_objects = belle_sip_object_get_object_count() - leaked_objects_count; if (leaked_objects > 0) { char* format = belle_sip_strdup_printf("%d object%s leaked in suite [%s] test [%s], please fix that!", leaked_objects, leaked_objects>1?"s were":"was", bc_tester_current_suite_name(), bc_tester_current_test_name()); belle_sip_object_dump_active_objects(); belle_sip_object_flush_active_objects(); bc_tester_printf(BELLE_SIP_LOG_MESSAGE, format); belle_sip_error("%s", format); all_leaks_buffer = all_leaks_buffer ? belle_sip_strcat_printf(all_leaks_buffer, "\n%s", format) : belle_sip_strdup_printf("\n%s", format); } // prevent any future leaks { const char **tags = bc_tester_current_test_tags(); int leaks_expected = (tags && ((tags[0] && !strcmp(tags[0], "LeaksMemory")) || (tags[1] && !strcmp(tags[1], "LeaksMemory")))); // if the test is NOT marked as leaking memory and it actually is, we should make it fail if (!leaks_expected && leaked_objects > 0) { BC_FAIL("This test is leaking memory!"); // and reciprocally } else if (leaks_expected && leaked_objects == 0) { BC_FAIL("This test is not leaking anymore, please remove LeaksMemory tag!"); } } } int belle_sip_tester_set_log_file(const char *filename) { bctbx_log_handler_t *filehandler; char* dir; char* base; if (log_file) { fclose(log_file); } log_file = fopen(filename, "w"); if (!log_file) { belle_sip_error("Cannot open file [%s] for writing logs because [%s]", filename, strerror(errno)); return -1; } dir = bctbx_dirname(filename); base = bctbx_basename(filename); belle_sip_message("Redirecting traces to file [%s]", filename); filehandler = bctbx_create_file_log_handler(0, dir, base, log_file); bctbx_add_log_handler(filehandler); bctbx_add_log_handler(filehandler); if (dir) bctbx_free(dir); if (base) bctbx_free(base); return 0; } #if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE) && !(defined(BELLE_SIP_WINDOWS_PHONE) || defined(BELLE_SIP_WINDOWS_UNIVERSAL)) static const char* belle_sip_helper = "\t\t\t--verbose\n" "\t\t\t--silent\n" "\t\t\t--log-file \n" "\t\t\t--domain \n" "\t\t\t--auth-domain \n" "\t\t\t--root-ca \n"; int main (int argc, char *argv[]) { int i; int ret; const char *root_ca_path = NULL; const char *env_domain=getenv("TEST_DOMAIN"); belle_sip_tester_init(NULL); #ifndef _WIN32 /*this hack doesn't work for argv[0]="c:\blablab\"*/ // this allows to launch liblinphone_tester from outside of tester directory if (strstr(argv[0], ".libs")) { int prefix_length = strstr(argv[0], ".libs") - argv[0] + 1; char *prefix = belle_sip_strdup_printf("%s%.*s", argv[0][0] == '/' ? "" : "./", prefix_length, argv[0]); // printf("Resource prefix set to %s\n", prefix); bc_tester_set_resource_dir_prefix(prefix); bc_tester_set_writable_dir_prefix(prefix); belle_sip_free(prefix); } #endif if (env_domain) { test_domain=env_domain; } bctbx_init_logger(TRUE); for(i=1;i0) { i += ret - 1; continue; } else if (ret<0) { bc_tester_helper(argv[0], belle_sip_helper); } return ret; } } belle_sip_tester_set_root_ca_path(root_ca_path); pool=belle_sip_object_pool_push(); ret = bc_tester_start(argv[0]); belle_sip_tester_uninit(); bctbx_uninit_logger(); return ret; } #endif belle-sip-1.6.3/tester/belle_sip_tester.h000066400000000000000000000043361313437522400204120ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010-2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _BELLE_SIP_TESTER_H #define _BELLE_SIP_TESTER_H #include #define MULTIPART_BOUNDARY "---------------------------14737809831466499882746641449" #ifdef __cplusplus extern "C" { #endif extern test_suite_t cast_test_suite; extern test_suite_t generic_uri_test_suite; extern test_suite_t sip_uri_test_suite; extern test_suite_t fast_sip_uri_test_suite; extern test_suite_t perf_sip_uri_test_suite; extern test_suite_t headers_test_suite; extern test_suite_t core_test_suite; extern test_suite_t sdp_test_suite; extern test_suite_t resolver_test_suite; extern test_suite_t message_test_suite; extern test_suite_t authentication_helper_test_suite; extern test_suite_t register_test_suite; extern test_suite_t dialog_test_suite; extern test_suite_t refresher_test_suite; extern test_suite_t http_test_suite; extern const char* belle_sip_tester_client_cert; extern const char* belle_sip_tester_client_cert_fingerprint; extern const char* belle_sip_tester_private_key; extern const char* belle_sip_tester_private_key_passwd; int belle_sip_tester_ipv6_available(void); const char * belle_sip_tester_get_root_ca_path(void); void belle_sip_tester_set_root_ca_path(const char *root_ca_path); void belle_sip_tester_init(void(*ftester_printf)(int level, const char *fmt, va_list args)); void belle_sip_tester_uninit(void); void belle_sip_tester_before_each(void); void belle_sip_tester_after_each(void); int belle_sip_tester_set_log_file(const char *filename); #ifdef __cplusplus }; #endif #endif /* _BELLE_SIP_TESTER_H */ belle-sip-1.6.3/tester/belle_sip_tester_ios.m000066400000000000000000000040461313437522400212670ustar00rootroot00000000000000/* linphone library - modular sound and video processing and streaming Copyright (C) 2006-2014 Belledonne Communications, Grenoble This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if TARGET_OS_IPHONE #import #import #include #include "belle_sip_tester.h" int g_argc; char** g_argv; void stop_handler(int sig) { return; } static void* _apple_main(void* data) { NSString *bundlePath = [[[NSBundle mainBundle] bundlePath] retain]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentPath = [[paths objectAtIndex:0] retain]; NSLog(@"Bundle path: %@", bundlePath); NSLog(@"Document path: %@", documentPath); bc_tester_set_resource_dir_prefix(bundlePath.UTF8String); bc_tester_set_writable_dir_prefix(documentPath.UTF8String); belle_sip_tester_init(NULL); bc_tester_start(NULL); belle_sip_tester_uninit(); [bundlePath release]; [documentPath release]; return NULL; } int main(int argc, char * argv[]) { pthread_t main_thread; g_argc=argc; g_argv=argv; pthread_create(&main_thread,NULL,_apple_main,NULL); NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; int value = UIApplicationMain(0, nil, nil, nil); [pool release]; return value; pthread_join(main_thread,NULL); return 0; } #endif // target IPHONE belle-sip-1.6.3/tester/belle_sip_tester_windows.cpp000066400000000000000000000106461313437522400225200ustar00rootroot00000000000000#include #include "belle_sip_tester_windows.h" using namespace BelledonneCommunications::BelleSip::Tester; using namespace Platform; using namespace Windows::Foundation; using namespace Windows::Storage; using namespace Windows::System::Threading; #define MAX_TRACE_SIZE 2048 #define MAX_SUITE_NAME_SIZE 128 #define MAX_WRITABLE_DIR_SIZE 1024 static OutputTraceListener^ sTraceListener; static belle_sip_object_pool_t *pool; NativeTester^ NativeTester::_instance = ref new NativeTester(); static void nativeOutputTraceHandler(int lev, const char *fmt, va_list args) { if (sTraceListener) { wchar_t wstr[MAX_TRACE_SIZE]; std::string str; str.resize(MAX_TRACE_SIZE); vsnprintf((char *)str.c_str(), MAX_TRACE_SIZE, fmt, args); mbstowcs(wstr, str.c_str(), MAX_TRACE_SIZE - 1); String^ msg = ref new String(wstr); String^ l; switch (lev) { case BELLE_SIP_LOG_FATAL: case BELLE_SIP_LOG_ERROR: l = ref new String(L"Error"); break; case BELLE_SIP_LOG_WARNING: l = ref new String(L"Warning"); break; case BELLE_SIP_LOG_MESSAGE: l = ref new String(L"Message"); break; default: l = ref new String(L"Debug"); break; } sTraceListener->outputTrace(l, msg); } } static void belleSipNativeOutputTraceHandler(void *info, const char *domain, BctbxLogLevel lev, const char *fmt, va_list args) { nativeOutputTraceHandler((int)lev, fmt, args); } NativeTester::NativeTester() { } NativeTester::~NativeTester() { belle_sip_tester_uninit(); } void NativeTester::setOutputTraceListener(OutputTraceListener^ traceListener) { sTraceListener = traceListener; } void NativeTester::initialize(StorageFolder^ writableDirectory, Platform::Boolean ui) { if (ui) { belle_sip_tester_init(nativeOutputTraceHandler); } else { belle_sip_tester_init(NULL); belle_sip_set_log_level(BELLE_SIP_LOG_DEBUG); } belle_sip_tester_set_root_ca_path("Assets/rootca.pem"); pool = belle_sip_object_pool_push(); char writable_dir[MAX_WRITABLE_DIR_SIZE] = { 0 }; const wchar_t *wwritable_dir = writableDirectory->Path->Data(); wcstombs(writable_dir, wwritable_dir, sizeof(writable_dir)); bc_tester_set_writable_dir_prefix(writable_dir); bc_tester_set_resource_dir_prefix("Assets"); if (!ui) { char *xmlFile = bc_tester_file("BelleSipWindows10.xml"); char *args[] = { "--xml-file", xmlFile }; bc_tester_parse_args(2, args, 0); char *logFile = bc_tester_file("BelleSipWindows10.log"); belle_sip_tester_set_log_file(logFile); free(logFile); } } bool NativeTester::run(Platform::String^ suiteName, Platform::String^ caseName, Platform::Boolean verbose) { std::wstring all(L"ALL"); std::wstring wssuitename = suiteName->Data(); std::wstring wscasename = caseName->Data(); char csuitename[MAX_SUITE_NAME_SIZE] = { 0 }; char ccasename[MAX_SUITE_NAME_SIZE] = { 0 }; bctbx_log_handler_t *log_handler = bctbx_create_log_handler(belleSipNativeOutputTraceHandler, NULL, NULL); wcstombs(csuitename, wssuitename.c_str(), sizeof(csuitename)); wcstombs(ccasename, wscasename.c_str(), sizeof(ccasename)); if (verbose) { belle_sip_set_log_level(BELLE_SIP_LOG_DEBUG); } else { belle_sip_set_log_level(BELLE_SIP_LOG_ERROR); } bctbx_add_log_handler(log_handler); return bc_tester_run_tests(wssuitename == all ? 0 : csuitename, wscasename == all ? 0 : ccasename, NULL) != 0; } void NativeTester::runAllToXml() { auto workItem = ref new WorkItemHandler([this](IAsyncAction ^workItem) { bc_tester_start(NULL); bc_tester_uninit(); }); _asyncAction = ThreadPool::RunAsync(workItem); } unsigned int NativeTester::nbTestSuites() { return bc_tester_nb_suites(); } unsigned int NativeTester::nbTests(Platform::String^ suiteName) { std::wstring suitename = suiteName->Data(); char cname[MAX_SUITE_NAME_SIZE] = { 0 }; wcstombs(cname, suitename.c_str(), sizeof(cname)); return bc_tester_nb_tests(cname); } Platform::String^ NativeTester::testSuiteName(int index) { const char *cname = bc_tester_suite_name(index); wchar_t wcname[MAX_SUITE_NAME_SIZE]; mbstowcs(wcname, cname, sizeof(wcname)); return ref new String(wcname); } Platform::String^ NativeTester::testName(Platform::String^ suiteName, int testIndex) { std::wstring suitename = suiteName->Data(); char csuitename[MAX_SUITE_NAME_SIZE] = { 0 }; wcstombs(csuitename, suitename.c_str(), sizeof(csuitename)); const char *cname = bc_tester_test_name(csuitename, testIndex); wchar_t wcname[MAX_SUITE_NAME_SIZE]; mbstowcs(wcname, cname, sizeof(wcname)); return ref new String(wcname); } belle-sip-1.6.3/tester/belle_sip_tester_windows.h000066400000000000000000000023521313437522400221600ustar00rootroot00000000000000#pragma once #include "belle-sip/belle-sip.h" #include "belle_sip_tester.h" namespace BelledonneCommunications { namespace BelleSip { namespace Tester { public interface class OutputTraceListener { public: void outputTrace(Platform::String^ lev, Platform::String^ msg); }; public ref class NativeTester sealed { public: void setOutputTraceListener(OutputTraceListener^ traceListener); unsigned int nbTestSuites(); unsigned int nbTests(Platform::String^ suiteName); Platform::String^ testSuiteName(int index); Platform::String^ testName(Platform::String^ suiteName, int testIndex); void initialize(Windows::Storage::StorageFolder^ writableDirectory, Platform::Boolean ui); bool run(Platform::String^ suiteName, Platform::String^ caseName, Platform::Boolean verbose); void runAllToXml(); static property NativeTester^ Instance { NativeTester^ get() { return _instance; } } property Windows::Foundation::IAsyncAction^ AsyncAction { Windows::Foundation::IAsyncAction^ get() { return _asyncAction; } } private: NativeTester(); ~NativeTester(); static NativeTester^ _instance; Windows::Foundation::IAsyncAction^ _asyncAction; }; } } }belle-sip-1.6.3/tester/belle_sip_uri_tester.c000066400000000000000000000020311313437522400212520ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2017 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_tester.h" #include "belle_sip_internal.h" /*test body*/ #include "belle_sip_base_uri_tester.c" test_suite_t sip_uri_test_suite = {"SIP URI", NULL, NULL, belle_sip_tester_before_each, belle_sip_tester_after_each, sizeof(uri_tests) / sizeof(uri_tests[0]), uri_tests}; belle-sip-1.6.3/tester/cast_test.c000066400000000000000000000043211313437522400170440ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "belle-sip/belle-sip.h" #include "belle_sip_tester.h" static void cast_test(void){ belle_sip_stack_t *stack=belle_sip_stack_new(NULL); belle_sip_listening_point_t *lp=belle_sip_stack_create_listening_point(stack,"0.0.0.0",7060,"UDP"); belle_sip_provider_t *provider; belle_sip_request_t *req=belle_sip_request_new(); belle_sip_response_t *resp=belle_sip_response_new(); belle_sip_message_t *msg; int tmp; BC_ASSERT_PTR_NOT_NULL(stack); BC_ASSERT_PTR_NOT_NULL(lp); provider=belle_sip_stack_create_provider(stack,lp); BC_ASSERT_PTR_NOT_NULL(provider); BC_ASSERT_PTR_NOT_NULL(req); BC_ASSERT_PTR_NOT_NULL(resp); belle_sip_message("Casting belle_sip_request_t to belle_sip_message_t"); msg=BELLE_SIP_MESSAGE(req); BC_ASSERT_PTR_NOT_NULL(msg); belle_sip_message("Ok."); belle_sip_message("Casting belle_sip_response_t to belle_sip_message_t"); msg=BELLE_SIP_MESSAGE(resp); BC_ASSERT_PTR_NOT_NULL(msg); belle_sip_message("Ok."); tmp=BELLE_SIP_IS_INSTANCE_OF(req,belle_sip_response_t); belle_sip_message("Casting belle_sip_request_t to belle_sip_response_t: %s",tmp ? "yes" : "no"); BC_ASSERT_EQUAL(tmp,0,int,"%d"); belle_sip_object_unref(req); belle_sip_object_unref(resp); belle_sip_object_unref(provider); belle_sip_object_unref(stack); } test_t cast_tests[] = { TEST_NO_TAG("Casting requests and responses", cast_test) }; test_suite_t cast_test_suite = {"Object inheritance", NULL, NULL, NULL, NULL, sizeof(cast_tests) / sizeof(cast_tests[0]), cast_tests}; belle-sip-1.6.3/tester/certificates/000077500000000000000000000000001313437522400173545ustar00rootroot00000000000000belle-sip-1.6.3/tester/certificates/openssl-client-cert.cnf000066400000000000000000000255271313437522400237510ustar00rootroot00000000000000# # OpenSSL example configuration file. # This is mostly being used for generation of certificate requests. # # This definition stops the following lines choking if HOME isn't # defined. HOME = . RANDFILE = $ENV::HOME/.rnd # Extra OBJECT IDENTIFIER info: #oid_file = $ENV::HOME/.oid oid_section = new_oids # To use this configuration file with the "-extfile" option of the # "openssl x509" utility, name here the section containing the # X.509v3 extensions to use: # extensions = # (Alternatively, use a configuration file that has only # X.509v3 extensions in its main [= default] section.) [ new_oids ] # We can add new OIDs in here for use by 'ca', 'req' and 'ts'. # Add a simple OID like this: # testoid1=1.2.3.4 # Or use config file substitution like this: # testoid2=${testoid1}.5.6 # Policies used by the TSA examples. tsa_policy1 = 1.2.3.4.1 tsa_policy2 = 1.2.3.4.5.6 tsa_policy3 = 1.2.3.4.5.7 #################################################################### [ ca ] default_ca = CA_default # The default ca section #################################################################### [ CA_default ] dir = ./root-ca # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. #unique_subject = no # Set to 'no' to allow creation of # several ctificates with same subject. new_certs_dir = $dir/newcerts # default place for new certs. certificate = $dir/cacert.pem # The CA certificate serial = $dir/serial # The current serial number crlnumber = $dir/crlnumber # the current crl number # must be commented out to leave a V1 CRL crl = $dir/crl.pem # The current CRL private_key = $dir/private/cakey.pem# The private key RANDFILE = $dir/private/.rand # private random number file x509_extensions = usr_cert # The extentions to add to the cert # Comment out the following two lines for the "traditional" # (and highly broken) format. name_opt = ca_default # Subject Name options cert_opt = ca_default # Certificate field options # Extension copying option: use with caution. # copy_extensions = copy # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs # so this is commented out by default to leave a V1 CRL. # crlnumber must also be commented out to leave a V1 CRL. # crl_extensions = crl_ext default_days = 365 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = default # use public key default MD preserve = no # keep passed DN ordering # A few difference way of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) policy = policy_match # For the CA policy [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional # For the 'anything' policy # At this point in time, you must list all acceptable 'object' # types. [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### [ req ] default_bits = 1024 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca # The extentions to add to the self signed cert # Passwords for private keys if not present they will be prompted for # input_password = secret # output_password = secret # This sets a mask for permitted string types. There are several options. # default: PrintableString, T61String, BMPString. # pkix : PrintableString, BMPString (PKIX recommendation before 2004) # utf8only: only UTF8Strings (PKIX recommendation after 2004). # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). # MASK:XXXX a literal mask value. # WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. string_mask = utf8only req_extensions = v3_req # The extensions to add to a certificate request [ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = FR countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = France localityName = Locality Name (eg, city) localityName_default = Grenoble 0.organizationName = Organization Name (eg, company) 0.organizationName_default = Belledonne Communications # we can do this but it is not needed normally :-) #1.organizationName = Second Organization Name (eg, company) #1.organizationName_default = World Wide Web Pty Ltd organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_default = LAB #organizationalUnitName_default = commonName = Common Name (e.g. server FQDN or YOUR name) commonName_max = 64 commonName_default = See altname for DNS name emailAddress = Email Address emailAddress_max = 64 emailAddress_default = jehan.monnier@belledonne-communications.com # SET-ex3 = SET extension number 3 [ req_attributes ] challengePassword = A challenge password challengePassword_min = 4 challengePassword_max = 20 unstructuredName = An optional company name [ usr_cert ] # These extensions are added when 'ca' signs a request. # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName # This is required for TSA certificates. # extendedKeyUsage = critical,timeStamping [ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @alt_names [alt_names] URI = sip:tester@client.example.org [ v3_ca ] # Extensions for a typical CA # PKIX recommendation. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer # This is what PKIX recommends but some broken software chokes on critical # extensions. #basicConstraints = critical,CA:true # So we do this instead. basicConstraints = CA:true # Key usage: this is typical for a CA certificate. However since it will # prevent it being used as an test self-signed certificate it is best # left out by default. # keyUsage = cRLSign, keyCertSign # Some might want this also # nsCertType = sslCA, emailCA # Include email address in subject alt name: another PKIX recommendation # subjectAltName=email:copy # Copy issuer details # issuerAltName=issuer:copy # DER hex encoding of an extension: beware experts only! # obj=DER:02:03 # Where 'obj' is a standard or added object # You can even override a supported extension: # basicConstraints= critical, DER:30:03:01:01:FF [ crl_ext ] # CRL extensions. # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. # issuerAltName=issuer:copy authorityKeyIdentifier=keyid:always [ proxy_cert_ext ] # These extensions should be added when creating a proxy certificate # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName # This really needs to be in place for it to be a proxy certificate. proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo #################################################################### [ tsa ] default_tsa = tsa_config1 # the default TSA section [ tsa_config1 ] # These are used by the TSA reply generation only. dir = ./root-ca # TSA root directory serial = $dir/tsaserial # The current serial number (mandatory) crypto_device = builtin # OpenSSL engine to use for signing signer_cert = $dir/tsacert.pem # The TSA signing certificate # (optional) certs = $dir/cacert.pem # Certificate chain to include in reply # (optional) signer_key = $dir/private/tsakey.pem # The TSA private key (optional) default_policy = tsa_policy1 # Policy if request did not specify it # (optional) other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) digests = md5, sha1 # Acceptable message digests (mandatory) accuracy = secs:1, millisecs:500, microsecs:100 # (optional) clock_precision_digits = 0 # number of digits after dot. (optional) ordering = yes # Is ordering defined for timestamps? # (optional, default: no) tsa_name = yes # Must the TSA name be included in the reply? # (optional, default: no) ess_cert_id_chain = no # Must the ESS cert id chain be included? # (optional, default: no) belle-sip-1.6.3/tester/describe.c000066400000000000000000000021541313437522400166350ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "belle-sip/belle-sip.h" int main(int argc, char *argv[]){ char *str; if (argc!=2){ fprintf(stderr,"Usage:\n%s \n",argv[0]); return -1; } str=belle_sip_object_describe_type_from_name(argv[1]); if (str){ printf("%s\n",str); belle_sip_free(str); }else{ fprintf(stderr,"Unknown type %s\n",argv[1]); return -1; } return 0; } belle-sip-1.6.3/tester/get.c000066400000000000000000000063721313437522400156420ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "belle-sip/belle-sip.h" static belle_sip_stack_t *stack=NULL; static void process_response(void *data, const belle_http_response_event_t *event){ belle_http_response_t *resp=event->response; const char *body=belle_sip_message_get_body(BELLE_SIP_MESSAGE(resp)); fprintf(stdout,"Got response:\n"); if (body){ fprintf(stdout,"%s",body); } belle_sip_main_loop_quit(belle_sip_stack_get_main_loop(stack)); } static void process_io_error(void *data, const belle_sip_io_error_event_t *event){ fprintf(stderr,"IO error\n"); belle_sip_main_loop_quit(belle_sip_stack_get_main_loop(stack)); } static void process_timeout(void *data, const belle_sip_timeout_event_t *event){ fprintf(stderr,"Timeout\n"); belle_sip_main_loop_quit(belle_sip_stack_get_main_loop(stack)); } static void usage(const char *argv0){ fprintf(stderr,"Usage:\n%s [--ca-path ] [--debug] [--no-tls-check]\n",argv0); exit(-1); } int main(int argc, char *argv[]){ belle_http_provider_t *prov; belle_http_request_t *req; belle_generic_uri_t *uri; belle_http_request_listener_callbacks_t cbs={0}; const char *ca_path = NULL; belle_tls_crypto_config_t *cfg; int i; int check_tls = 1; if (argc<2){ usage(argv[0]); } bctbx_init_logger(1); for (i = 2; i < argc; ++i){ if (strcmp(argv[i], "--ca-path") == 0){ i++; ca_path = argv[i]; }else if (strcmp(argv[i], "--debug")==0){ fprintf(stderr, "Logs are enabled.\n"); bctbx_set_log_level(BCTBX_LOG_DOMAIN,BCTBX_LOG_DEBUG); }else if (strcmp(argv[i], "--no-tls-check")==0){ check_tls = 0; }else{ usage(argv[0]); } } stack=belle_sip_stack_new(NULL); prov=belle_sip_stack_create_http_provider(stack,"::0"); cfg = belle_tls_crypto_config_new(); if (ca_path){ belle_tls_crypto_config_set_root_ca(cfg, ca_path); } if (!check_tls) belle_tls_crypto_config_set_verify_exceptions(cfg, BELLE_TLS_VERIFY_ANY_REASON); belle_http_provider_set_tls_crypto_config(prov, cfg); uri=belle_generic_uri_parse(argv[1]); if (!uri){ fprintf(stderr,"Bad uri %s\n",argv[1]); return -1; } cbs.process_response=process_response; cbs.process_io_error=process_io_error; cbs.process_timeout=process_timeout; req=belle_http_request_create("GET",uri,NULL); belle_http_provider_send_request(prov,req,belle_http_request_listener_create_from_callbacks(&cbs,NULL)); belle_sip_stack_main(stack); belle_sip_object_unref(prov); belle_sip_object_unref(stack); return 0; } belle-sip-1.6.3/tester/parse.c000066400000000000000000000056151313437522400161740ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #ifdef _WIN32 #define strcasecmp _stricmp #else #include #endif #include #include "belle-sip/belle-sip.h" #include "belle-sip/belle-sdp.h" #include int main(int argc, char *argv[]){ char *str; struct stat st; int fd; int i; const char *filename=NULL; const char *protocol="sip"; if (argc<2){ fprintf(stderr,"Usage:\n%s [--protocol sip|http|sdp] \n",argv[0]); return -1; } for(i=1;i. */ extern belle_sip_stack_t * stack; extern belle_sip_provider_t *prov; extern const char *test_domain; int call_endeed; extern int register_before_all(void); extern int register_after_all(void); extern belle_sip_request_t* register_user(belle_sip_stack_t * stack ,belle_sip_provider_t *prov ,const char *transport ,int use_transaction ,const char* username,const char* outbound) ; extern void unregister_user(belle_sip_stack_t * stack ,belle_sip_provider_t *prov ,belle_sip_request_t* initial_request ,int use_transaction); belle-sip-1.6.3/tester/resolve.c000066400000000000000000000042151313437522400165340ustar00rootroot00000000000000/* belle-sip - SIP (RFC3261) library. Copyright (C) 2013 Belledonne Communications SARL This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "belle-sip/belle-sip.h" static belle_sip_stack_t *stack; static void resolver_callback(void *data, const char *queried_name, struct addrinfo *ai_list){ int err; struct addrinfo *ai_it; char name[NI_MAXHOST]; char port[NI_MAXSERV]; for(ai_it=ai_list;ai_it!=NULL;ai_it=ai_it->ai_next){ err=bctbx_getnameinfo(ai_it->ai_addr,ai_list->ai_addrlen,name,sizeof(name),port,sizeof(port),NI_NUMERICSERV|NI_NUMERICHOST); if (err!=0){ fprintf(stderr,"getnameinfo error: %s",gai_strerror(err)); }else{ printf("\t%s %s\n",name,port); } } belle_sip_main_loop_quit(belle_sip_stack_get_main_loop(stack)); } int main(int argc, char *argv[]){ int i; const char *domain=NULL; const char *transport="udp"; if (argc<2){ fprintf(stderr,"Usage:\n%s [transport] [--debug]\n",argv[0]); return -1; } domain=argv[1]; for (i=2;i. */ #include #include #include "belle-sip/belle-sip.h" #include "belle-sip/object.h" #include "belle-sip/types.h" #include "belle-sip/sipstack.h" #include "belle-sip/http-listener.h" #include "belle-sip/http-provider.h" #include "belle-sip/http-message.h" typedef struct _CardDavRequest { const char *url; const char *method; const char *body; const char *depth; const char *digest_auth_username; const char *digest_auth_password; int request_in_progress; } CardDavRequest; static void process_response_from_post_xml_rpc_request(void *data, const belle_http_response_event_t *event) { CardDavRequest *request = (CardDavRequest *)data; if (event->response) { int code = belle_http_response_get_status_code(event->response); belle_sip_message("HTTP code: %i", code); if (code == 207 || code == 200) { /*const char *body = belle_sip_message_get_body((belle_sip_message_t *)event->response); belle_sip_message("%s", body);*/ request->request_in_progress = 0; } } } static void process_io_error_from_post_xml_rpc_request(void *data, const belle_sip_io_error_event_t *event) { CardDavRequest *request = (CardDavRequest *)data; belle_sip_error("I/O Error during request sending"); request->request_in_progress = 0; } static void process_auth_requested_from_post_xml_rpc_request(void *data, belle_sip_auth_event_t *event) { CardDavRequest *request = (CardDavRequest *)data; if (request->digest_auth_username && request->digest_auth_password) { belle_sip_auth_event_set_username(event, request->digest_auth_username); belle_sip_auth_event_set_passwd(event, request->digest_auth_password); } else { belle_sip_error("Authentication error during request sending"); request->request_in_progress = 0; } } static void prepare_query(CardDavRequest *request) { belle_http_request_listener_callbacks_t cbs = { 0 }; belle_http_request_listener_t *l = NULL; belle_generic_uri_t *uri = NULL; belle_http_request_t *req = NULL; belle_sip_memory_body_handler_t *bh = NULL; belle_sip_stack_t *stack = NULL; belle_http_provider_t *http_provider = NULL; belle_sip_set_log_level(BELLE_SIP_LOG_MESSAGE); uri = belle_generic_uri_parse(request->url); if (!uri) { belle_sip_error("Could not send request, URL %s is invalid", request->url); return; } req = belle_http_request_create(request->method, uri, belle_sip_header_content_type_create("application", "xml; charset=utf-8"), belle_sip_header_create("Depth", request->depth), NULL); if (!req) { belle_sip_object_unref(uri); belle_sip_error("Could not create request"); return; } bh = belle_sip_memory_body_handler_new_copy_from_buffer(request->body, strlen(request->body), NULL, NULL); belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(bh)); cbs.process_response = process_response_from_post_xml_rpc_request; cbs.process_io_error = process_io_error_from_post_xml_rpc_request; cbs.process_auth_requested = process_auth_requested_from_post_xml_rpc_request; l = belle_http_request_listener_create_from_callbacks(&cbs, request); stack = belle_sip_stack_new(NULL); http_provider = belle_sip_stack_create_http_provider(stack, "0.0.0.0"); request->request_in_progress = 1; belle_http_provider_send_request(http_provider, req, l); while (request->request_in_progress) { belle_sip_stack_sleep(stack, 0); } } int main(int argc, char *argv[]) { CardDavRequest *request = (CardDavRequest *)malloc(sizeof(CardDavRequest)); if (argc >= 5) { request->url = argv[1]; request->method = argv[2]; request->body = argv[3]; request->depth = argv[4]; if (argc >= 7) { request->digest_auth_username = argv[5]; request->digest_auth_password = argv[6]; } } else { belle_sip_error("Usage: carddav_http_query [username password]"); return 0; } /* Examples: "http://192.168.0.230/sabredav/addressbookserver.php/addressbooks/sylvain/default"; request->method = "PROPFIND"; request->body = ""; request->depth = "0"; request->url = "http://192.168.0.230/sabredav/addressbookserver.php/addressbooks/sylvain/default"; request->method = "REPORT"; request->body = ""; request->depth = "1"; request->url = "http://192.168.0.230/sabredav/addressbookserver.php/addressbooks/sylvain/default"; request->method = "REPORT"; request->body = "/sabredav/addressbookserver.php/addressbooks/sylvain/default/me.vcf" request->depth = "1"; */ prepare_query(request); }belle-sip-1.6.3/tools/digest-response.py000077500000000000000000000061461313437522400202400ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################ # digest-response.py # Copyright (C) 2015 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ import hashlib import sys import argparse def int16(x): return int(x, 16) def main(argv=None): parser = argparse.ArgumentParser(description='Compute response parameter for a digest authetication. ex %(prog)s --qop-auth --cnonce f1b8598a --nonce-count 2b toto sip.linphone.org secret REGISTER sip:sip.linphone.org QPUC2gAAAABufvQmAABDq5AKuv4AAAAA') parser.add_argument('userid',help='User identifier') parser.add_argument('realm',help='realm as defined by the server in 401/407') parser.add_argument('password',help='clear text password') parser.add_argument('method',help='sip method of the challanged request line') parser.add_argument('uri',help='sip uri of the challanged request uri') parser.add_argument('nonce',help='Nonce param as defined by the server in 401/407') parser.add_argument('--qop-auth', help='Indicate if auth mode has to reuse nonce (I.E qop=auth',action='store_true') parser.add_argument('--cnonce', help='client nonce') parser.add_argument('--nonce-count',type=int16, help='nonce count in hexa: ex 2b' ) args = parser.parse_args(argv) #HA1=MD5(username:realm:password) ha1 = hashlib.md5() ha1.update((args.userid+":"+args.realm+":"+args.password).encode()) #HA2=MD5(method:digestURI) ha2 = hashlib.md5() ha2.update((args.method+":"+args.uri).encode()) print ("ha1 = "+ha1.hexdigest()); print ("ha2 = "+ha2.hexdigest()); if args.qop_auth : if not args.cnonce and not args.nonce_count : print ("--qop-auth requires both --cnonce and --nonce-count") sys.exit(-1) #response=MD5(HA1:nonce:nonceCount:clientNonce:qop:HA2) response = hashlib.md5() response.update( (ha1.hexdigest() +":"+args.nonce +":" + '{:08x}'.format(args.nonce_count) +":" + args.cnonce +":auth" +":" + ha2.hexdigest()).encode()) print ("responce = "+response.hexdigest()); else: #response=MD5(HA1:nonce:HA2) response = hashlib.md5() response.update((ha1.hexdigest()+":"+args.nonce+":"+ha2.hexdigest()).encode()) print ("responce = "+response.hexdigest()); if __name__ == "__main__": sys.exit(main())