[
  {
    "path": ".clangd",
    "content": "If:\n  PathMatch: [include/.*\\.h]\nCompileFlags:\n  Add: [-Wall, -DHAVE_INTTYPES_H, -include re.h]\n  Remove: [-xobjective-c++-header]\n---\nDiagnostics:\n  Suppress: \"-Wgnu-zero-variadic-macro-arguments\"\n"
  },
  {
    "path": ".github/workflows/abi.yml",
    "content": "name: ABI Checks\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  abicheck:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v5\n      with:\n          ref: 'v4.6.0'\n          path: old\n\n    - uses: actions/checkout@v5\n      with:\n          path: current\n\n    - name: fix flaky azure mirrors\n      if: ${{ runner.os == 'Linux' }}\n      run: |\n        sudo sed -i 's/azure\\./de\\./' /etc/apt/sources.list\n\n    - name: install abidiff\n      run: sudo apt-get update && sudo apt-get install -y abigail-tools\n\n    - name: make shared lib\n      run: |\n        cmake -S old -B old/build && cmake --build old/build\n\n    - name: make current shared lib\n      run: |\n        cmake -S current -B current/build && cmake --build current/build\n\n    - name: abidiff compare\n      id: abidiff\n      run: abidiff old/build/libre.so current/build/libre.so\n      continue-on-error: true\n\n    - name: display warning\n      if: steps.abidiff.outcome != 'success'\n      run: echo \"::warning::ABI Check failed - bump ABI version\"\n"
  },
  {
    "path": ".github/workflows/android.yml",
    "content": "name: Android\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\nenv:\n  openssl: 3.2.1\n  toolchain: toolchains/llvm/prebuilt/linux-x86_64\n  api: 26\n  abi: x86_64\n\njobs:\n  android:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - name: Enable KVM\n      run: |\n        echo 'KERNEL==\"kvm\", GROUP=\"kvm\", MODE=\"0666\", OPTIONS+=\"static_node=kvm\"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules\n        sudo udevadm control --reload-rules\n        sudo udevadm trigger --name-match=kvm\n\n    - uses: actions/cache@v4\n      id: openssl\n      with:\n        path: openssl\n        key: ${{ runner.os }}-android-${{ env.abi }}-openssl-${{ env.openssl }}\n\n    - name: \"build openssl\"\n      if: steps.openssl.outputs.cache-hit != 'true'\n      run: |\n        wget -q https://www.openssl.org/source/openssl-$openssl.tar.gz\n        tar -xzf openssl-$openssl.tar.gz\n        mv openssl-$openssl openssl\n        cd openssl && ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME PATH=$ANDROID_NDK_LATEST_HOME/$toolchain/bin:$PATH ./Configure android-$abi no-shared no-tests -U__ANDROID_API__ -D__ANDROID_API__=$api && PATH=$ANDROID_NDK_LATEST_HOME/$toolchain/bin:$PATH make build_libs && cd ..\n\n    - name: build\n      # unixsock is not currently supported on Android as only abstract (not pathname) addresses are allowed\n      run: |\n        cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_LATEST_HOME/build/cmake/android.toolchain.cmake -DANDROID_ABI=\"$abi\" -DANDROID_PLATFORM=android-$api -DOPENSSL_ROOT_DIR=openssl -DOPENSSL_INCLUDE_DIR=openssl/include -DOPENSSL_CRYPTO_LIBRARY=openssl/libcrypto.a -DOPENSSL_SSL_LIBRARY=openssl/libssl.a -DUSE_UNIXSOCK=OFF .\n        cmake --build . -j 4 -t retest\n\n    - name: run\n      uses: reactivecircus/android-emulator-runner@v2\n      with:\n        api-level: ${{ env.api }}\n        arch: ${{ env.abi }}\n        target: google_apis\n        # Use test data directory as a writeable directory for I/O tests as the emulator file system is not generally writeable\n        script: |\n          adb push test/retest /data/local/tmp/retest\n          adb shell chmod 775 /data/local/tmp/retest\n          adb push test/data /data/local/tmp/retest-data\n          adb shell chmod 775 /data/local/tmp/retest-data\n          adb push $ANDROID_NDK_LATEST_HOME/$toolchain/sysroot/usr/lib/$abi-linux-android/libc++_shared.so /data/local/tmp/libc++_shared.so\n          adb shell HOME=/data/local/tmp LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/retest -r -v -d /data/local/tmp/retest-data\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  build:\n    runs-on: ${{ matrix.os }}\n\n    strategy:\n      matrix:\n        build_type: [Release, Debug]\n        compiler: [gcc, clang, gcc-14, clang-21]\n        os: [ubuntu-22.04, ubuntu-24.04, macos-latest]\n        exclude:\n          - os: macos-latest\n            compiler: gcc\n          - os: macos-latest\n            compiler: gcc-14\n          - os: macos-latest\n            compiler: clang-21\n          - os: ubuntu-22.04\n            compiler: gcc-14\n          - os: ubuntu-22.04\n            compiler: clang-21\n    env:\n      CC: ${{ matrix.compiler }}\n      CMAKE_GENERATOR: Ninja\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - name: Set Xcode version\n      if: ${{ runner.os == 'macOS' }}\n      run: sudo xcode-select -s /Applications/Xcode_16.2.app \n\n    - name: openssl path macos\n      if: ${{ runner.os == 'macOS' }}\n      run: |\n        echo \"OPENSSL_ROOT_DIR=$(brew --prefix openssl)\" >> $GITHUB_ENV\n\n    - name: fix flaky azure mirrors\n      if: ${{ runner.os == 'Linux' }}\n      run: |\n        sudo sed -i 's/azure\\./de\\./' /etc/apt/sources.list\n\n    - name: install packages\n      if: ${{ runner.os == 'Linux' }}\n      run: |\n        sudo apt-get update && sudo apt-get install -y ninja-build\n\n    - name: Install clang\n      if: ${{ matrix.compiler == 'clang-21' }}\n      run: |\n        wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -\n        sudo add-apt-repository \"deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main\"\n        sudo apt-get update && sudo apt-get install -y clang-21\n\n    - name: make info\n      run: |\n        echo \"OS: ${{ matrix.os }}\"\n        echo \"--- ${{ matrix.compiler }} DEBUG VERSION ---\"\n        ${{ matrix.compiler }} - --version\n\n    - name: cmake\n      run: |\n        cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_C_FLAGS=\"-Werror\" -DCMAKE_CXX_FLAGS=\"-Werror\"\n        cmake --build build -t retest\n    \n    - name: retest\n      run: |\n        ./build/test/retest -r -v\n"
  },
  {
    "path": ".github/workflows/clang-analyze.yml",
    "content": "name: clang analyze\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  clang-analyze:\n    runs-on: ubuntu-24.04\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - name: fix flaky azure mirrors\n      if: ${{ runner.os == 'Linux' }}\n      run: |\n        sudo sed -i 's/azure\\./de\\./' /etc/apt/sources.list\n\n    - name: Install clang-tools\n      run: |\n        wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -\n        sudo add-apt-repository \"deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main\"\n        sudo apt-get update && sudo apt-get install -y clang-tools-21\n\n    - name: analyze\n      run: | \n        cmake -B build -DCMAKE_C_COMPILER=clang-21\n        analyze-build-21 --cdb build/compile_commands.json --status-bugs -v\n"
  },
  {
    "path": ".github/workflows/cmake_win.yml",
    "content": "name: Windows\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  build:\n    name: ${{ matrix.config.name }}\n    runs-on: ${{ matrix.config.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        config:\n        - {\n            name: \"Windows Debug\",\n            os: windows-2022,\n            environment_script: \"C:\\\\Program Files\\\\Microsoft Visual Studio\\\\2022\\\\Enterprise\\\\VC\\\\Auxiliary\\\\Build\\\\vcvars64.bat\",\n            generators: \"Ninja\",\n            build: \"Debug\",\n            openssl: true,\n            disable_openssl: \"OFF\",\n            testing: true,\n            c11_threads: \"ON\"\n          }\n        - {\n            name: \"Windows Release\",\n            os: windows-2022,\n            environment_script: \"C:\\\\Program Files\\\\Microsoft Visual Studio\\\\2022\\\\Enterprise\\\\VC\\\\Auxiliary\\\\Build\\\\vcvars64.bat\",\n            generators: \"Ninja\",\n            build: \"Release\",\n            openssl: true,\n            disable_openssl: \"OFF\",\n            testing: true,\n            c11_threads: \"ON\"\n          }\n        - {\n            name: \"Windows Debug 32-bit\",\n            os: windows-2022,\n            environment_script: \"C:\\\\Program Files\\\\Microsoft Visual Studio\\\\2022\\\\Enterprise\\\\VC\\\\Auxiliary\\\\Build\\\\vcvarsamd64_x86.bat\",\n            generators: \"Ninja\",\n            build: \"Debug\",\n            openssl: false,\n            disable_openssl: \"ON\",\n            choco: \"--x86\",\n            testing: true,\n            c11_threads: \"OFF\" # missing vcruntime library\n          }\n        - {\n            name: \"Windows Debug ARM64\",\n            os: windows-2022,\n            environment_script: \"C:\\\\Program Files\\\\Microsoft Visual Studio\\\\2022\\\\Enterprise\\\\VC\\\\Auxiliary\\\\Build\\\\vcvarsamd64_arm64.bat\",\n            generators: \"Ninja\",\n            build: \"Debug\",\n            openssl: false,\n            disable_openssl: \"ON\",\n            testing: false,\n            c11_threads: \"ON\"\n          }\n\n    steps:\n      - uses: actions/checkout@v5\n\n      - name: Install OpenSSL\n        if: ${{ matrix.config.openssl }}\n        run: |\n          choco install --no-progress ${{ matrix.config.choco }} openssl --version 3.6.2\n\n      - name: Build\n        shell: cmd\n        run: |\n          call \"${{ matrix.config.environment_script }}\"\n          cmake --version\n          ninja --version\n          cmake -S . -B build -G \"${{ matrix.config.generators }}\" -DCMAKE_C_FLAGS=\"/WX\" -DHAVE_THREADS=\"${{ matrix.c11_threads }}\" -DCMAKE_BUILD_TYPE=\"${{ matrix.config.build }}\" -DCMAKE_DISABLE_FIND_PACKAGE_OpenSSL=\"${{ matrix.config.disable_openssl }}\"\n          cmake --build build --parallel -t retest\n\n      - name: retest\n        if: ${{ matrix.config.testing }}\n        shell: cmd\n        run: |\n          build\\test\\retest.exe -a -v\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  analyze:\n    name: CodeQL Analyze\n    runs-on: ubuntu-latest\n\n    env:\n      CMAKE_GENERATOR: Ninja\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v5\n\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v3\n      with:\n        languages: cpp\n        queries: security-extended\n\n    - name: install packages\n      run: |\n        sudo apt-get update && sudo apt-get install -y ninja-build\n\n    - run: |\n        cmake -B build && cmake --build build\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v3\n\n"
  },
  {
    "path": ".github/workflows/coverage.yml",
    "content": "name: Coverage\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  coverage:\n    runs-on: ubuntu-22.04\n\n    env:\n      CMAKE_GENERATOR: Ninja\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - name: fix flaky azure mirrors\n      if: ${{ runner.os == 'Linux' }}\n      run: |\n        sudo sed -i 's/azure\\./de\\./' /etc/apt/sources.list\n\n    - name: install packages\n      run: |\n        sudo apt-get update && sudo apt-get install -y ninja-build\n\n    - name: make\n      run: | \n        cmake -B build -DCMAKE_C_FLAGS=\"--coverage\" -DCMAKE_EXE_LINKER_FLAGS=\"--coverage\" -DUSE_TRACE=ON\n        cmake --build build -j -t retest\n\n    - name: retest\n      run: |\n        ./build/test/retest -a -v\n        ./build/test/retest -r -m select -v\n\n    - name: gcov\n      run: | \n        gcov build/**/*.o\n\n    - name: install gcovr\n      run: |\n        pip install gcovr==5.0\n\n    - name: coverage check\n      run: |\n        min_cov=\"70.0\"\n        mkdir html\n        cov=$(~/.local/bin/gcovr -r . --html-details html/index.html --json-summary | jq .line_percent)\n        echo \"Coverage: ${cov}% (min $min_cov%)\"\n        exit $(echo \"$cov < $min_cov\" | bc -l)\n\n    - name: coverage zip\n      run: |\n        zip -r coverage.zip html\n\n    - uses: actions/upload-artifact@v4\n      with:\n        name: coverage\n        path: coverage.zip\n        retention-days: 7\n"
  },
  {
    "path": ".github/workflows/coverity.yml",
    "content": "name: Coverity Check\n\non:\n  push:\n    tags:\n      - \"*\"\n    branches:\n      - coverity\n    workflow_dispatch:\n\njobs:\n  coverity:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v5\n    - name: Prepare\n      run: cmake -B ${{github.workspace}}/build\n    - uses: vapier/coverity-scan-action@v1\n      with:\n        project: 'baresip%2Fre'\n        token: ${{ secrets.COVERITY_SCAN_TOKEN }}\n        command: make -C ${{github.workspace}}/build\n        email: 'hallo@studio-link.de'\n"
  },
  {
    "path": ".github/workflows/fedora.yml",
    "content": "name: Fedora\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  build:\n    runs-on: ${{ matrix.os }}\n    container: fedora\n\n    strategy:\n      matrix:\n        compiler: [clang]\n        os: [ubuntu-latest]\n\n    env:\n      CC: ${{ matrix.compiler }}\n      CMAKE_GENERATOR: Ninja\n\n    steps:\n    - uses: actions/checkout@v5\n    - name: install devel tools\n      run: |\n        yum -y install gcc clang cmake make openssl-devel zlib-devel ninja-build\n\n    - name: make info\n      run: |\n        echo \"OS: ${{ matrix.os }}\"\n        echo \"--- ${{ matrix.compiler }} DEBUG VERSION ---\"\n        ${{ matrix.compiler }} - --version\n        cmake --version\n\n    - name: make\n      run: |\n        cmake -B build -DCMAKE_C_FLAGS=\"-Werror\" && cmake --build build -j 16 -t retest\n\n    - name: retest\n      run: |\n        ./build/test/retest -r -v\n"
  },
  {
    "path": ".github/workflows/freebsd.yml",
    "content": "name: FreeBSD\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    timeout-minutes: 20\n\n    env:\n      CMAKE_GENERATOR: Ninja\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - name: Test in FreeBSD\n      id: test\n      uses: vmactions/freebsd-vm@v1\n      with:\n        release: \"14.4\"\n        usesh: true\n        prepare: |\n\n        run: |\n          pkg update && pkg install -y ninja cmake\n          freebsd-version\n          cmake -B build && cmake --build build -t retest\n          ./build/test/retest -r -v\n"
  },
  {
    "path": ".github/workflows/ios.yml",
    "content": "name: iOS\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  ios:\n    runs-on: macos-latest\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - name: build Xcode\n      run: |\n        cmake -B build_xcode -G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 -DCMAKE_DISABLE_FIND_PACKAGE_OpenSSL=ON -DUSE_OPENSSL=OFF -DCMAKE_C_FLAGS=\"-Werror\"\n        cmake --build build_xcode -- CODE_SIGNING_ALLOWED=NO\n\n    - name: normal build\n      run: |\n        cmake -B build -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 -DCMAKE_DISABLE_FIND_PACKAGE_OpenSSL=ON -DUSE_OPENSSL=OFF -DCMAKE_C_FLAGS=\"-Werror\"\n        cmake --build build -j\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: lint\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  lint:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v5\n    - name: ccheck\n      run: |\n        wget \"https://raw.githubusercontent.com/baresip/baresip/main/test/ccheck.py\"\n        python3 ccheck.py\n    - name: CMakeLint\n      run: |\n        pip install cmakelint\n        find . -name \"CMakeLists.txt\" -exec ~/.local/bin/cmakelint {} +\n"
  },
  {
    "path": ".github/workflows/mingw.yml",
    "content": "name: MinGW-w64 Test\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  MinGW-w64-build:\n    runs-on: ubuntu-22.04\n\n    env:\n      CMAKE_GENERATOR: Ninja\n\n    steps:\n\n    - name: fix flaky azure mirrors\n      if: ${{ runner.os == 'Linux' }}\n      run: |\n        sudo sed -i 's/azure\\./de\\./' /etc/apt/sources.list\n\n    - name: \"install packages\"\n      run: |\n        sudo apt-get update && sudo apt-get install -y mingw-w64 ninja-build\n    \n    - uses: actions/checkout@v5 # needed for pr checkout\n\n    - uses: sreimers/pr-dependency-action@v0.6\n      with:\n        name: baresip-win32\n        repo: https://github.com/baresip/baresip-win32\n        secret: ${{ secrets.GITHUB_TOKEN }}\n\n    - uses: actions/checkout@v5\n      with:\n        path: baresip-win32/re\n\n    - uses: actions/cache@v4\n      id: openssl\n      with:\n        path: baresip-win32/openssl\n        key: ${{ runner.os }}-mingw-openssl-3.5.0\n\n    - name: \"build openssl\"\n      if: steps.openssl.outputs.cache-hit != 'true'\n      run: |\n        wget https://www.openssl.org/source/openssl-3.5.0.tar.gz\n        tar -xzf openssl-3.5.0.tar.gz\n        mv openssl-3.5.0 baresip-win32/openssl\n        make -j$(nproc) -C baresip-win32 openssl\n\n    - name: \"build\"\n      run: |\n        cd baresip-win32 && make retest\n    \n    - uses: actions/upload-artifact@v4\n      with:\n        name: retest-exe\n        path: baresip-win32/re/build/test/retest.exe\n        retention-days: 1\n\n  wintest:\n    runs-on: windows-latest\n    needs: MinGW-w64-build\n\n    steps:\n    - uses: actions/checkout@v5\n    - uses: actions/download-artifact@v4\n    - uses: sreimers/pr-dependency-action@v0.6\n      with:\n        name: re\n        repo: https://github.com/baresip/re\n        secret: ${{ secrets.GITHUB_TOKEN }}\n\n    - name: \"cv2pdb\"\n      run: |\n        curl -L https://github.com/rainers/cv2pdb/releases/download/v0.52/cv2pdb-0.52.zip --output cv2pdb.zip\n        unzip -o cv2pdb.zip\n      shell: bash\n\n    - name: \"prepare retest.exe\"\n      run: mv retest-exe/retest.exe re/ && cd re && ../cv2pdb.exe ./retest.exe\n      shell: bash\n\n    - name: \"run retest.exe\"\n      run: cd re && ./retest.exe -v -ri\n      shell: bash\n"
  },
  {
    "path": ".github/workflows/musl.yml",
    "content": "name: Alpine (musl)\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    container: alpine\n\n    env:\n      CMAKE_GENERATOR: Ninja\n\n    steps:\n    - uses: actions/checkout@v5\n    - name: install devel tools\n      run: |\n        apk add musl-dev git cmake gcc g++ make binutils openssl-dev linux-headers zlib-dev ninja\n\n    - name: make\n      run: |\n        cmake -B build -DCMAKE_C_FLAGS=\"-Werror\"\n        cmake --build build -j\n"
  },
  {
    "path": ".github/workflows/run-on-arch.yml",
    "content": "on:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  build_job:\n    # The host should always be linux\n    runs-on: ubuntu-22.04\n    name: Build on ${{ matrix.distro }} ${{ matrix.arch }}\n\n    strategy:\n      matrix:\n        include:\n          - arch: aarch64\n            distro: bookworm\n          - arch: armv7\n            distro: ubuntu22.04\n\n    steps:\n      - uses: actions/checkout@v5\n\n      - uses: uraimo/run-on-arch-action@v3\n        name: Build artifact\n        id: build\n        with:\n          arch: ${{ matrix.arch }}\n          distro: ${{ matrix.distro }}\n\n          # Not required, but speeds up builds\n          githubToken: ${{ github.token }}\n\n          install: |\n            case \"${{ matrix.distro }}\" in\n              ubuntu*|jessie|stretch|buster|bullseye|bookworm)\n                apt-get update -q -y\n                apt-get install -q -y cmake gcc g++ libssl-dev ninja-build\n                ;;\n            esac\n\n          run: |\n            cmake -G Ninja -B build -DCMAKE_C_FLAGS=\"-Werror\"\n            cmake --build build -j --target retest\n            ./build/test/retest -r -v\n"
  },
  {
    "path": ".github/workflows/sanitizers.yml",
    "content": "name: Sanitizers\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  sanitizers:\n    runs-on: ${{ matrix.os }}\n\n    strategy:\n      matrix:\n        os: [ubuntu-24.04]\n        sanitizer: [thread, address, undefined]\n    env:\n      CC: clang-20\n      CXX: clang++-20\n      CMAKE_GENERATOR: Ninja\n      CFLAGS: \"-fsanitize=${{ matrix.sanitizer }} -fno-sanitize-recover=all -fno-sanitize=function\"\n      CXXFLAGS: \"-fsanitize=${{ matrix.sanitizer }} -fno-sanitize-recover=all -fno-sanitize=function\"\n      ASAN_OPTIONS: fast_unwind_on_malloc=0\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - name: fix flaky azure mirrors\n      if: ${{ runner.os == 'Linux' }}\n      run: |\n        sudo sed -i 's/azure\\./de\\./' /etc/apt/sources.list\n\n    - name: install packages\n      run: |\n        sudo apt-get update && sudo apt-get install -y ninja-build\n\n    - name: Install clang-tools\n      run: |\n        wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -\n        sudo add-apt-repository \"deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main\"\n        sudo apt-get update && sudo apt-get install -y clang-tools-20\n\n    - name: make info\n      run: |\n        echo \"OS: ${{ matrix.os }}\"\n        clang - --version\n\n    - name: cmake\n      run: |\n        cmake -B build -DHAVE_THREADS= && cmake --build build -j -t retest\n\n    - name: retest\n      run: |\n        ./build/test/retest -av\n"
  },
  {
    "path": ".github/workflows/sonar.yml",
    "content": "name: Sonarcloud\non:\n  push:\n    branches:\n      - main\n  workflow_dispatch:\n\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n    env:\n      SONAR_SCANNER_VERSION: 5.0.1.3006\n      SONAR_SERVER_URL: \"https://sonarcloud.io\"\n      BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis\n      - name: Set up JDK 17\n        uses: actions/setup-java@v3\n        with:\n          java-version: 17\n          distribution: 'oracle'\n      - name: Download and set up sonar-scanner\n        env:\n          SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip\n        run: |\n          mkdir -p $HOME/.sonar\n          curl -sSLo $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }} \n          unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/\n          echo \"$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin\" >> $GITHUB_PATH\n      - name: Download and set up build-wrapper\n        env:\n          BUILD_WRAPPER_DOWNLOAD_URL: ${{ env.SONAR_SERVER_URL }}/static/cpp/build-wrapper-linux-x86.zip\n        run: |\n          curl -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip ${{ env.BUILD_WRAPPER_DOWNLOAD_URL }}\n          unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/\n          echo \"$HOME/.sonar/build-wrapper-linux-x86\" >> $GITHUB_PATH\n      - name: Run build-wrapper\n        run: |\n          cmake -S . -B build\n          build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/\n      - name: Run sonar-scanner\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}\n        run: |\n          sonar-scanner --define sonar.host.url=\"${{ env.SONAR_SERVER_URL }}\" --define sonar.cfamily.build-wrapper-output=\"${{ env.BUILD_WRAPPER_OUT_DIR }}\"\n\n"
  },
  {
    "path": ".github/workflows/ssl.yml",
    "content": "name: OpenSSL no-deprecated and LibreSSL\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  ssl:\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        ssl: [libressl, openssl]\n\n    env:\n      CMAKE_GENERATOR: Ninja\n      OPENSSL_ROOT_DIR: \"assets/${{ matrix.ssl }}\"\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - name: fix flaky azure mirrors\n      if: ${{ runner.os == 'Linux' }}\n      run: |\n        sudo sed -i 's/azure\\./de\\./' /etc/apt/sources.list\n\n    - name: install packages\n      run: |\n        sudo apt-get update && sudo apt-get install -y ninja-build\n\n    - name: Download pre-compiled OpenSSL/LibreSSL\n      run: |\n        wget \"https://github.com/baresip/tools/releases/download/v2026.05.0/assets.tar.gz\"\n        tar -xf assets.tar.gz\n\n    - name: make\n      run: |\n        cmake -B build -DCMAKE_C_FLAGS=\"-Werror\"\n        cmake --build build -j\n        cmake --build build -t retest\n\n    - name: retest\n      run: |\n        ./build/test/retest -r -v\n"
  },
  {
    "path": ".github/workflows/strict-c.yml",
    "content": "name: Strict C checks\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  strict-c:\n    runs-on: ubuntu-latest\n\n    env:\n      CMAKE_GENERATOR: Ninja\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - name: fix flaky azure mirrors\n      if: ${{ runner.os == 'Linux' }}\n      run: |\n        sudo sed -i 's/azure\\./de\\./' /etc/apt/sources.list\n\n    - name: install packages\n      run: |\n        sudo apt-get update && sudo apt-get install -y ninja-build\n\n    - name: make strict C99\n      run: | \n        cmake -DCMAKE_C_STANDARD=99 -DCMAKE_C_EXTENSIONS=OFF  -DCMAKE_C_FLAGS=\"-Werror\" -B build \n        cmake --build build\n        rm -Rf build\n\n    - name: make strict C11\n      run: | \n        cmake -DCMAKE_C_STANDARD=11 -DCMAKE_C_EXTENSIONS=OFF -DCMAKE_C_FLAGS=\"-Werror\" -B build\n        cmake --build build\n        rm -Rf build\n"
  },
  {
    "path": ".github/workflows/valgrind.yml",
    "content": "name: valgrind leak check\n\non:\n  push:\n    branches:\n    - main\n  pull_request:\n    branches:\n    - main\n\njobs:\n  valgrind:\n    runs-on: ubuntu-latest\n\n    env:\n      CMAKE_GENERATOR: Ninja\n\n    steps:\n    - uses: actions/checkout@v5\n\n    - name: fix flaky azure mirrors\n      if: ${{ runner.os == 'Linux' }}\n      run: |\n        sudo sed -i 's/azure\\./de\\./' /etc/apt/sources.list\n\n    - name: install packages\n      run: |\n        sudo apt-get update && sudo apt-get install -y libssl-dev valgrind ninja-build\n\n    - name: make\n      run: |\n        cmake -B build && cmake --build build -j -t retest\n\n    - name: retest\n      run: |\n        valgrind --leak-check=full --show-reachable=yes --error-exitcode=42 ./build/test/retest -r -v\n"
  },
  {
    "path": ".gitignore",
    "content": "/build*\n/dist\ntest.d\ntest.o\n*stamp\n*.previous\nlibre.a\nlibre.*dylib\nlibre.pc\nlibre.so*\n*.gcov\n\n# Windows build folder\nWin32/*\n\n# Visual studio config\nmk/win32/baresip.vcxproj.user\n\n# ctags\ntags\n\n# Vim swp files\n*.swp\n\n# clangd\n.cache\ncompile_commands.json\n\n# Visual studio config\nmk/win32/*.vcxproj.user\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# libre Changelog\n\nAll notable changes to libre will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## v4.8.0 - 2026-05-13\n\n## What's Changed\n* ci/cmake_win: fix choco openssl by @sreimers in https://github.com/baresip/re/pull/1563\n* test,sipreg: enable outbound testing by @alfredh in https://github.com/baresip/re/pull/1553\n* ci/windows: roll back choco openssl version 3.6.2 by @alfredh in https://github.com/baresip/re/pull/1566\n* hash: safety check for string by @cspiel1 in https://github.com/baresip/re/pull/1562\n* ci/freebsd: use freebsd 14.4 and pkg update fixes by @sreimers in https://github.com/baresip/re/pull/1567\n* tls: upgrade usage of legacy functions by @alfredh in https://github.com/baresip/re/pull/1565\n* ci/coverage: increase minimum coverage to 70% by @alfredh in https://github.com/baresip/re/pull/1564\n* tls: use SSL_set1_dnsname() for OpenSSL 4.0.0 and later by @alfredh in https://github.com/baresip/re/pull/1569\n* tls/openssl/sni: fix OpenSSL 4 deprecation and refactor by @sreimers in https://github.com/baresip/re/pull/1570\n* http: Send HTTP request with explicit server address by @fAuernigg in https://github.com/baresip/re/pull/1571\n* tls/openssl: fix OpenSSL 4.0.0 consts by @sreimers in https://github.com/baresip/re/pull/1572\n* ci/ssl: bump no-deprecated OpenSSL and LibreSSL by @sreimers in https://github.com/baresip/re/pull/1573\n* readme: update supported OpenSSL and LibreSSL versions by @sreimers in https://github.com/baresip/re/pull/1574\n* mem: update mem_debug_tail() doxygen comment by @alfredh in https://github.com/baresip/re/pull/1576\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v4.7.0...v4.8.0\n\n\n## v4.7.0 - 2026-04-07\n\n### What's Changed\n* test: use dns_rrlist_apply() in mock DNS-server by @alfredh in https://github.com/baresip/re/pull/1532\n* sip: fix missing auth algorithm for known realm by @cspiel1 in https://github.com/baresip/re/pull/1535\n* cmake,unixsock: always add unixsock header and functions by @sreimers in https://github.com/baresip/re/pull/1536\n* test/dns: save error code from async work in DNS-test by @alfredh in https://github.com/baresip/re/pull/1538\n* docs: update list of modules in README.md by @alfredh in https://github.com/baresip/re/pull/1537\n* sipsess/modify: fix calling of offer handler by @maximilianfridrich in https://github.com/baresip/re/pull/1539\n* http/server: cleanup  of http_verify_msg_d by @cspiel1 in https://github.com/baresip/re/pull/1542\n* test: add permission handler to TURN test by @alfredh in https://github.com/baresip/re/pull/1541\n* sip/dialog: fix OOM scenarios is sip_dialog_update by @maximilianfridrich in https://github.com/baresip/re/pull/1543\n* test/turnsrv: add LIFETIME attribute to allocation response by @alfredh in https://github.com/baresip/re/pull/1545\n* test: add testing of websock_tcp() in websock testcases by @alfredh in https://github.com/baresip/re/pull/1544\n* test: split turn-test into permissions/channels by @alfredh in https://github.com/baresip/re/pull/1547\n* test: add negative testcases in unixsock by @alfredh in https://github.com/baresip/re/pull/1546\n* trace: flush operations must not run concurrently by @sreimers in https://github.com/baresip/re/pull/1550\n* test,turn: add forced error code and test more combinations by @alfredh in https://github.com/baresip/re/pull/1551\n* test: use TEST_ERR() to check async errors in RTCP-test by @alfredh in https://github.com/baresip/re/pull/1549\n* test,odict: add testing of more api functions by @alfredh in https://github.com/baresip/re/pull/1552\n* ci/abi: bump old version by @sreimers in https://github.com/baresip/re/pull/1560\n\n**Full Changelog**: https://github.com/baresip/re/compare/v4.6.0...v4.7.0\n\n\n## v4.6.0 - 2026-03-04\n\n### What's Changed\n* ci: bump OpenSSL version for Windows job by @alfredh in https://github.com/baresip/re/pull/1509\n* async: print number of workers by @alfredh in https://github.com/baresip/re/pull/1511\n* dns: remove usage of domain parameter from dns_srv_get() by @alfredh in https://github.com/baresip/re/pull/1510\n* thread: with c23 once_flag is mandatory by @sreimers in https://github.com/baresip/re/pull/1516\n* cmake: fix some cmakelint warnings by @alfredh in https://github.com/baresip/re/pull/1517\n* test/dns: add AAAA tests by @cspiel1 in https://github.com/baresip/re/pull/1515\n* test: add testcase for DNS over TCP by @alfredh in https://github.com/baresip/re/pull/1508\n* mem: add mem_debug_tail by @sreimers in https://github.com/baresip/re/pull/1524\n* rtp: rtp_listen_single() - try multiple ports by @cspiel1 in https://github.com/baresip/re/pull/1526\n* test: add testcode for stun_server_discover() by @alfredh in https://github.com/baresip/re/pull/1522\n* test: add testing of dns_rr_print() by @alfredh in https://github.com/baresip/re/pull/1518\n* test: add testing of DNS query from handler by @alfredh in https://github.com/baresip/re/pull/1528\n* dns: add dns_rr_dup and test_dns_rr_dup by @maximilianfridrich in https://github.com/baresip/re/pull/1512\n* sip/request: duplicate RR entries from DNS by @maximilianfridrich in https://github.com/baresip/re/pull/1529\n* test: add testing of DNS header code names by @alfredh in https://github.com/baresip/re/pull/1530\n\n**Full Changelog**: https://github.com/baresip/re/compare/v4.5.0...v4.6.0\n\n\n## v4.5.0 - 2026-01-28\n\n### What's Changed\n* net: remove net_if_getaddr4() -- deprecated by @alfredh in https://github.com/baresip/re/pull/1494\n* test: add testing of dtls_set_handlers() api by @alfredh in https://github.com/baresip/re/pull/1495\n* h265: use h264_find_startcode() -- duplicated code by @alfredh in https://github.com/baresip/re/pull/1493\n* fmt: str_bool: reuse similar logic in pl_bool() by @alfredh in https://github.com/baresip/re/pull/1496\n* net: cleanup fallback return by @sreimers in https://github.com/baresip/re/pull/1497\n* fmt/print: add backtrace for incompatible format arguments by @sreimers in https://github.com/baresip/re/pull/1499\n* btrace: fix for linux addr2line by @cspiel1 in https://github.com/baresip/re/pull/1500\n* rtp: add RTP listen on single port by @cspiel1 in https://github.com/baresip/re/pull/1498\n* copyright: update for new year by @Clusters in https://github.com/baresip/re/pull/1502\n* async: do not hold lock during cb call by @cspiel1 in https://github.com/baresip/re/pull/1503\n* docs: fix README.md document include by @sreimers in https://github.com/baresip/re/pull/1501\n* tmr: improve thread list lock handling by @sreimers in https://github.com/baresip/re/pull/1504\n* aumix: add aumix_source_put_auframe and deprecate aumix_source_put by @sreimers in https://github.com/baresip/re/pull/1505\n* rtp/sess: fix ts_arrive calculation by @sreimers in https://github.com/baresip/re/pull/1506\n\n## New Contributors\n* @Clusters made their first contribution in https://github.com/baresip/re/pull/1502\n\n**Full Changelog**: https://github.com/baresip/re/compare/v4.4.0...v4.5.0\n\n\n## v4.4.0 - 2025-12-22\n\n### What's Changed\n* fmt: add pl_alloc_dup() by @cspiel1 in https://github.com/baresip/re/pull/1469\n* tools: add genfir python script from librem by @alfredh in https://github.com/baresip/re/pull/1471\n* test: av1 obu print by @alfredh in https://github.com/baresip/re/pull/1473\n* genfir: upgrade to python 3 by @alfredh in https://github.com/baresip/re/pull/1472\n* test: add negative conf tests by @alfredh in https://github.com/baresip/re/pull/1474\n* turn: add channel peer mutex locking by @sreimers in https://github.com/baresip/re/pull/1478\n* test: remove h265 fragment handling by @alfredh in https://github.com/baresip/re/pull/1477\n* test: add testing of re_text2pcap_trace() by @alfredh in https://github.com/baresip/re/pull/1476\n* dd: change dd_print() to struct re_printf *pf and add test by @alfredh in https://github.com/baresip/re/pull/1475\n* rtp, stun/msg: doxygen fixes by @sreimers in https://github.com/baresip/re/pull/1479\n* trace: add trace line handler by @sreimers in https://github.com/baresip/re/pull/1460\n* trace: deref id after new trace handler by @sreimers in https://github.com/baresip/re/pull/1481\n* test: add testing of dbg module by @alfredh in https://github.com/baresip/re/pull/1480\n* aumix: implement aumix_source_set_id by @sreimers in https://github.com/baresip/re/pull/1482\n* dns: check memory allocation in get_resolv_dns() by @alfredh in https://github.com/baresip/re/pull/1484\n* test: check errors in turn_thread() test by @alfredh in https://github.com/baresip/re/pull/1485\n* test: remove rotate from mock DNS-server by @alfredh in https://github.com/baresip/re/pull/1487\n* test: check re_main_timeout() return value in test_rtp_listen_priv() by @alfredh in https://github.com/baresip/re/pull/1488\n* h265,test: improve testing and usage of h265_nal_print() by @alfredh in https://github.com/baresip/re/pull/1486\n* test: more testing of IPv6 protocol by @alfredh in https://github.com/baresip/re/pull/1490\n* stun: remove PADDING attribute by @alfredh in https://github.com/baresip/re/pull/1491\n* stun: remove natbd strings (deprecated) by @alfredh in https://github.com/baresip/re/pull/1489\n\n**Full Changelog**: https://github.com/baresip/re/compare/v4.3.0...v4.4.0\n\n\n## v4.3.0 - 2025-11-19\n\n### What's Changed\n* cmake: remove macOS include path by @mohd-akram in https://github.com/baresip/re/pull/1449\n* test: sort testcases in alphabetical order by @alfredh in https://github.com/baresip/re/pull/1447\n* test: increase coverage of websock test with protocol on/off by @alfredh in https://github.com/baresip/re/pull/1446\n* sdp/media: fix sdp_media_align_formats pt handling by @sreimers in https://github.com/baresip/re/pull/1450\n* dns: fix AAAA address comparison in getaddr_dup() by @alfredh in https://github.com/baresip/re/pull/1452\n* test: add support for IPv6 DNS testing by @alfredh in https://github.com/baresip/re/pull/1454\n* ci: add clang-21 by @sreimers in https://github.com/baresip/re/pull/1453\n* sys/fs: improve fs_fread error handling by @sreimers in https://github.com/baresip/re/pull/1455\n* test: compare DNS RR records data in order to increase test-coverage by @alfredh in https://github.com/baresip/re/pull/1458\n* dns: correct comment in dnsc_query_srv() by @alfredh in https://github.com/baresip/re/pull/1457\n* h265: Fix NAL Decode nuh_layer_id by @xiaokuang95 in https://github.com/baresip/re/pull/1456\n* auframe: avoid auframe_bytes_to_ms division by zero by @sreimers in https://github.com/baresip/re/pull/1459\n* aumix: add aumix_latency and new defaults by @sreimers in https://github.com/baresip/re/pull/1461\n* dns: remove get_android_dns() by @alfredh in https://github.com/baresip/re/pull/1464\n* test: add testing of DNS nameservers by @alfredh in https://github.com/baresip/re/pull/1462\n* cmake/re-config: fix HAVE_THREADS discovery by @sreimers in https://github.com/baresip/re/pull/1466\n\n### New Contributors\n* @mohd-akram made their first contribution in https://github.com/baresip/re/pull/1449\n* @xiaokuang95 made their first contribution in https://github.com/baresip/re/pull/1456\n\n**Full Changelog**: https://github.com/baresip/re/compare/v4.2.0...v4.3.0\n\n\n## v4.2.0 - 2025-10-15\n\n### What's Changed\n* test: add testcode for btrace module by @alfredh in https://github.com/baresip/re/pull/1414\n* types: add ETIME fallback by @sreimers in https://github.com/baresip/re/pull/1420\n* test: add testing of conf_get_bool() by @alfredh in https://github.com/baresip/re/pull/1419\n* test/btrace: skip thread test by @sreimers in https://github.com/baresip/re/pull/1422\n* Revert \"dtls: remove dtls_set_handlers() -- unused\" by @sreimers in https://github.com/baresip/re/pull/1421\n* ice/icem: add icem_rcand_ready helper by @sreimers in https://github.com/baresip/re/pull/1424\n* ice/sdp: remove mDNS AI_V4MAPPED and log late candidate by @sreimers in https://github.com/baresip/re/pull/1423\n* tls: minor improvements to SNI and Common-name comparison by @alfredh in https://github.com/baresip/re/pull/1425\n* tls: revert wrong match-checking in SNI function by @alfredh in https://github.com/baresip/re/pull/1427\n* ci-windows: bump choco openssl version to 3.5.3 by @alfredh in https://github.com/baresip/re/pull/1426\n* tls: sni - a null pointer check by @cspiel1 in https://github.com/baresip/re/pull/1430\n* test: fix some minor typos by @alfredh in https://github.com/baresip/re/pull/1429\n* dbg: remove dbg_close() -- unused by @alfredh in https://github.com/baresip/re/pull/1428\n* ci,windows: bump choco openssl to 3.5.4 by @alfredh in https://github.com/baresip/re/pull/1433\n* misc: fix some minor typos by @alfredh in https://github.com/baresip/re/pull/1432\n* test: test both fragmented and non-fragmented H.265 packets by @alfredh in https://github.com/baresip/re/pull/1434\n* test: add negative AES testcases by @alfredh in https://github.com/baresip/re/pull/1435\n* test: add test for conf_apply() by @alfredh in https://github.com/baresip/re/pull/1436\n* ci/android: Upgrade to API-level 29 (Android 10.0) by @alfredh in https://github.com/baresip/re/pull/1439\n* ci/android: remove AVD cache by @sreimers in https://github.com/baresip/re/pull/1442\n* ci/android: revert to android api level 26 by @sreimers in https://github.com/baresip/re/pull/1443\n* bump version number to 4.2.0 by @alfredh in https://github.com/baresip/re/pull/1440\n\n**Full Changelog**: https://github.com/baresip/re/compare/v4.1.0...v4.2.0\n\n\n## v4.1.0 - 2025-09-10\n\n### What's Changed\n* ci: temporary workaround for choco openssl failure by @alfredh in https://github.com/baresip/re/pull/1395\n* test: add support for IPv6 on UDP-test by @alfredh in https://github.com/baresip/re/pull/1390\n* ci: enable Windows testing when OpenSSL is disabled by @alfredh in https://github.com/baresip/re/pull/1392\n* websock: remove unused peer member by @alfredh in https://github.com/baresip/re/pull/1396\n* test: add testing of udp_rxsz_set() and udp_sockbuf_set() by @alfredh in https://github.com/baresip/re/pull/1397\n* ci/build: select xcode version 16.2 by @sreimers in https://github.com/baresip/re/pull/1400\n* udp: combine udp_recv_helper() and udp_recv_packet() by @alfredh in https://github.com/baresip/re/pull/1398\n* test: add support for UDP multicast test by @alfredh in https://github.com/baresip/re/pull/1402\n* ci: update actions/checkout@v5 by @sreimers in https://github.com/baresip/re/pull/1403\n* uri: remove uri_escape_user() by @alfredh in https://github.com/baresip/re/pull/1401\n* uri: remove some unused escape functions by @alfredh in https://github.com/baresip/re/pull/1404\n* test: add support for IPv6 and TURN by @alfredh in https://github.com/baresip/re/pull/1405\n* test: add support for testing more DTLS-SRTP suites by @alfredh in https://github.com/baresip/re/pull/1408\n* dtls: remove dtls_set_handlers() -- unused by @alfredh in https://github.com/baresip/re/pull/1407\n* tls: remove tls_set_certificate_der() -- unused by @alfredh in https://github.com/baresip/re/pull/1410\n* test: set low MTU in DTLS-test by @alfredh in https://github.com/baresip/re/pull/1411\n* test: add support for TURN mock-server authentication by @alfredh in https://github.com/baresip/re/pull/1409\n* tls: tls_set_resumption() -- change const enum to enum by @alfredh in https://github.com/baresip/re/pull/1412\n* ci/abi: bump old abi by @sreimers in https://github.com/baresip/re/pull/1417\n* ci/coverage: bump min coverage by @sreimers in https://github.com/baresip/re/pull/1416\n\n**Full Changelog**: https://github.com/baresip/re/compare/v4.0.0...v4.1.0\n\n\n## v4.0.0 - 2025-08-06\n\n### What's Changed\n\nThis major release drops obsolete API functions, OpenSSL 1.1.1 support and support for old OS versions.\nThe breaking changes are discussed here: https://github.com/baresip/re/discussions/1372\n\n* rem: remove backwards wrapper for au_calc_nsamp() by @alfredh in https://github.com/baresip/re/pull/1366\n* rem: remove local macros, include stdint.h instead by @alfredh in https://github.com/baresip/re/pull/1369\n* mod: remove unused MOD_PRE macro by @alfredh in https://github.com/baresip/re/pull/1370\n* tcp: remove special case for mingw32/wine by @alfredh in https://github.com/baresip/re/pull/1367\n* dd: update AV1 and DD docs by @alfredh in https://github.com/baresip/re/pull/1376\n* test: fix formatted string arguments in URI testcode by @alfredh in https://github.com/baresip/re/pull/1375\n* tls: drop OpenSSL 1.1.1 support by @sreimers in https://github.com/baresip/re/pull/1371\n* Update supported OS versions by @sreimers in https://github.com/baresip/re/pull/1373\n* readme: update supported compilers by @sreimers in https://github.com/baresip/re/pull/1374\n* tls: disable tls_conn_change_cert for LibreSSL by @sreimers in https://github.com/baresip/re/pull/1377\n* ci/ssl: bump ssl tools assets by @sreimers in https://github.com/baresip/re/pull/1127\n* aubuf: remove unused struct auframe in ajb.c by @alfredh in https://github.com/baresip/re/pull/1378\n* aubuf: remove unused private function plot_underrun() by @alfredh in https://github.com/baresip/re/pull/1380\n* readme: bump supported GNU C library (glibc) 2.31 by @sreimers in https://github.com/baresip/re/pull/1379\n* misc: remove deprecated functions/params by @sreimers in https://github.com/baresip/re/pull/1384\n* uri: remove password field by @alfredh in https://github.com/baresip/re/pull/1382\n* websock: increase test coverage by @alfredh in https://github.com/baresip/re/pull/1381\n* udp: remove obsolete todo in udp_local_get() by @alfredh in https://github.com/baresip/re/pull/1386\n* av1: remove av1_packetize_new() -- backwards compat wrapper by @alfredh in https://github.com/baresip/re/pull/1385\n* tls: remove tls_set_selfsigned_rsa() -- use Elliptic Curve instead by @alfredh in https://github.com/baresip/re/pull/1388\n* test: enable dtls_set_single() for DTLS client test by @alfredh in https://github.com/baresip/re/pull/1387\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.24.0...v4.0.0\n\n\n## v3.24.0 - 2025-07-09\n\n### What's Changed\n* list: add LIST_FOREACH_SAFE helper macro by @sreimers in https://github.com/baresip/re/pull/1343\n* test: SDP interop testing by @alfredh in https://github.com/baresip/re/pull/1341\n* ci/abi: bump old ref version by @sreimers in https://github.com/baresip/re/pull/1344\n* list: improve already linked warning by @sreimers in https://github.com/baresip/re/pull/1345\n* ci/mingw: use windows-latest by @sreimers in https://github.com/baresip/re/pull/1346\n* ci/clang: use clang-20 by @sreimers in https://github.com/baresip/re/pull/1348\n* av1: rename av1_packetize_new() and add wrapper by @alfredh in https://github.com/baresip/re/pull/1349\n* trice: update header to match filename by @alfredh in https://github.com/baresip/re/pull/1350\n* rtp/rr: fix fraction left shift promotion by @sreimers in https://github.com/baresip/re/pull/1353\n* test/sipsess: cast rel100_mode by @sreimers in https://github.com/baresip/re/pull/1354\n* test: print trice_debug to buffer to test debug functions by @alfredh in https://github.com/baresip/re/pull/1352\n* trice: update doxygen documentation by @alfredh in https://github.com/baresip/re/pull/1351\n* rtp: add rtp_seq_less inline function helper by @sreimers in https://github.com/baresip/re/pull/1355\n* trice: remove trice_set_software() by @alfredh in https://github.com/baresip/re/pull/1359\n* trice: always enable PRFLX candidates by @alfredh in https://github.com/baresip/re/pull/1360\n* rtp: add TWCC packet definition helpers by @sreimers in https://github.com/baresip/re/pull/1357\n* test: add support check for SRTP GCM test cases by @alfredh in https://github.com/baresip/re/pull/1361\n* trice: add more doxygen comments by @alfredh in https://github.com/baresip/re/pull/1362\n* prepare for release -- bump version to 3.24.0 by @alfredh in https://github.com/baresip/re/pull/1365\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.23.0...v3.24.0\n\n\n## v3.23.0 - 2025-06-04\n\n### What's Changed\n* fmt/pl: optimize pl_bool return early by @sreimers in https://github.com/baresip/re/pull/1311\n* ci/coverage: bump min_cov by @sreimers in https://github.com/baresip/re/pull/1312\n* av1: allow OBU type 0 (RESERVED) by @alfredh in https://github.com/baresip/re/pull/1313\n* ci: remove step to install ninja on macOS by @alfredh in https://github.com/baresip/re/pull/1315\n* rtp: remove unused function ntp2unix() by @alfredh in https://github.com/baresip/re/pull/1314\n* tls: fix compiler warning with libressl 4.1.0 by @alfredh in https://github.com/baresip/re/pull/1318\n* vhprintf: change from char to int to store error number from %m by @alfredh in https://github.com/baresip/re/pull/1319\n* http: cancel verify_cert_tmr and skip init by @alfredh in https://github.com/baresip/re/pull/1317\n* mem: update doxygen comments in mem_pool.c by @alfredh in https://github.com/baresip/re/pull/1320\n* tls: fix LibreSSL SSL_verify_cb by @sreimers in https://github.com/baresip/re/pull/1322\n* cmake,http: add HAVE_TLS1_3_POST_HANDSHAKE_AUTH by @sreimers in https://github.com/baresip/re/pull/1323\n* av1: remove deprecated av1_packetize_high() by @alfredh in https://github.com/baresip/re/pull/1321\n* tls: fix SSL_SESSION_is_resumable LibreSSL by @sreimers in https://github.com/baresip/re/pull/1324\n* sip: exposed ltag and rtag in sip dialog by @gordongrech in https://github.com/baresip/re/pull/1326\n* sipsess: add cancel reason to close handler by @KillingSpark in https://github.com/baresip/re/pull/1325\n* h265: add H265_NAL_RSV_IRAP_VCL{22,23} by @alfredh in https://github.com/baresip/re/pull/1327\n* test/httpauth: fix debug warning for digest response by @cspiel1 in https://github.com/baresip/re/pull/1332\n* sha: use PROV_RSA_AES for CryptAcquireContext by @alfredh in https://github.com/baresip/re/pull/1331\n* test: Add test for thread tss by @weili-jiang in https://github.com/baresip/re/pull/1334\n* 100rel improvements by @maximilianfridrich in https://github.com/baresip/re/pull/1333\n* test/async: Remove unsupported AI_V4MAPPED on Android by @weili-jiang in https://github.com/baresip/re/pull/1336\n* test/sys: Use writeable datapath for fs_open test by @weili-jiang in https://github.com/baresip/re/pull/1335\n* net: Support ifaddrs on Android API level >= 24 by @weili-jiang in https://github.com/baresip/re/pull/1338\n* ci: Enable testing on Android by @weili-jiang in https://github.com/baresip/re/pull/1337\n* test/sipsess: fix test_sipsess_100rel_answer_not_allowed by @maximilianfridrich in https://github.com/baresip/re/pull/1340\n\n### New Contributors\n* @gordongrech made their first contribution in https://github.com/baresip/re/pull/1326\n* @KillingSpark made their first contribution in https://github.com/baresip/re/pull/1325\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.22.0...v3.23.0\n\n\n## v3.22.0 - 2025-04-30\n\n### What's Changed\n* rtp: remove unused int proto by @alfredh in https://github.com/baresip/re/pull/1296\n* mbuf: null pointer checks for inline functions by @cspiel1 in https://github.com/baresip/re/pull/1294\n* test: more coverage in rtcp_loop by @alfredh in https://github.com/baresip/re/pull/1295\n* ci: remove version from choco install openssl by @alfredh in https://github.com/baresip/re/pull/1297\n* net/linux/addrs: use malloc for buffer by @sreimers in https://github.com/baresip/re/pull/1298\n* cmake: update cmake_minimum_required 3.18...4.0 by @sreimers in https://github.com/baresip/re/pull/1291\n* ci: upgrade mingw to openssl 3.5.0 by @alfredh in https://github.com/baresip/re/pull/1299\n* ci/abi: bump ref version by @sreimers in https://github.com/baresip/re/pull/1300\n* rtp: RTCP Extended report by @shrim27 in https://github.com/baresip/re/pull/1302\n* test: move test_rtcp_xr_rrtr to rtcp.c by @alfredh in https://github.com/baresip/re/pull/1306\n* Handle \"w\" properly in the AV1 packetizer by @npcook in https://github.com/baresip/re/pull/1305\n* test: add coverage of RTCP-XR DLRR by @alfredh in https://github.com/baresip/re/pull/1307\n* av1: remove deprecated av1_packetize_one_w() by @alfredh in https://github.com/baresip/re/pull/1308\n\n## New Contributors\n* @shrim27 made their first contribution in https://github.com/baresip/re/pull/1302\n* @npcook made their first contribution in https://github.com/baresip/re/pull/1305\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.21.1...v3.22.0\n\n## v3.21.1 - 2025-04-04\n\n### What's Changed\n* vidconv: fix vidconv_center underflows by @sreimers in https://github.com/baresip/re/pull/1287\n* mem: fix buffer overflow in mem_realloc by @maximilianfridrich in https://github.com/baresip/re/pull/1289\n* mem/mem_pool: use pointer-pointer to prevent heap-use-after-free by @sreimers in https://github.com/baresip/re/pull/1290\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.21.0...v3.21.1\n\n\n## v3.21.0 - 2025-03-26\n\n### What's Changed\n* fmt/pl: add pl_strncasecmp and tests by @sreimers in https://github.com/baresip/re/pull/1277\n* ci: upgrade run-on-arch-action to fix random segfaults by @alfredh in https://github.com/baresip/re/pull/1280\n* test: add testing of RTCP FIR-RFC5104 by @alfredh in https://github.com/baresip/re/pull/1281\n* rtpext: add rtpext_find by @sreimers in https://github.com/baresip/re/pull/1282\n* rtp: add rtp_ prefix to member functions by @alfredh in https://github.com/baresip/re/pull/1283\n* rtp/rtcp: add rtcp_send_twcc and rtcp_rtpfb_twcc_encode by @sreimers in https://github.com/baresip/re/pull/1285\n* list: optimize list_count by @sreimers in https://github.com/baresip/re/pull/1284\n* bump version to 3.21.0 by @alfredh in https://github.com/baresip/re/pull/1286\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.20.0...v3.21.0\n\n\n## v3.20.0 - 2025-02-18\n\n### What's Changed\n* http/server: increase BUFSIZE_MAX to 1 MB and add http_set_max_body_size by @sreimers in https://github.com/baresip/re/pull/1262\n* test: init err to zero (fixes cppcheck warning) by @alfredh in https://github.com/baresip/re/pull/1265\n* test: add RTCP_APP to RTCP test by @alfredh in https://github.com/baresip/re/pull/1266\n* mem,aubuf: add pre-allocated memory pool management by @sreimers in https://github.com/baresip/re/pull/1255\n* test: increase test_oom levels and oom fixes by @sreimers in https://github.com/baresip/re/pull/1260\n* mem/mem_pool: fix mem_pool_extend new member destructor by @sreimers in https://github.com/baresip/re/pull/1267\n* ci: bump version and min_cov by @sreimers in https://github.com/baresip/re/pull/1268\n* av1: remove duplicate/unused getbit.c by @alfredh in https://github.com/baresip/re/pull/1272\n* test/cmake: link C++ lib by @sreimers in https://github.com/baresip/re/pull/1269\n* http: restart timer for each chunk by @fAuernigg in https://github.com/baresip/re/pull/1273\n* ci/valgrind: use ubuntu-latest by @sreimers in https://github.com/baresip/re/pull/1274\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.19.0...v3.20.0\n\n\n## v3.19.0 - 2025-01-15\n\n### What's Changed\n* fmt: fix pl trim methods and add tests by @maximilianfridrich in https://github.com/baresip/re/pull/1226\n* sipsess: add sipsess_msg getter function by @cspiel1 in https://github.com/baresip/re/pull/1225\n* rtp/sess: fix missing srate_tx locking by @sreimers in https://github.com/baresip/re/pull/1231\n* rtcp: use rtcp_rtpfb_gnack_encode() function by @alfredh in https://github.com/baresip/re/pull/1233\n* net/linux: add net_netlink_addrs by @sreimers in https://github.com/baresip/re/pull/1232\n* tcp,udp: set TOS (TCLASS) for IPv6 sockets by @maximilianfridrich in https://github.com/baresip/re/pull/1218\n* sys/fs: fix fs_fopen return null check by @sreimers in https://github.com/baresip/re/pull/1237\n* test: remove mock tcp-server (unused) by @alfredh in https://github.com/baresip/re/pull/1235\n* rtp: remove rtcp_psfb_sli_encode() (unused) by @alfredh in https://github.com/baresip/re/pull/1234\n* ci/clang: bump clang-18 and use ubuntu 24.04 by @sreimers in https://github.com/baresip/re/pull/1236\n* net/linux/addrs: fix point-to-point peer address bug by @sreimers in https://github.com/baresip/re/pull/1239\n* ci/coverage: bump min_cov by @sreimers in https://github.com/baresip/re/pull/1241\n* ci/sanitizers: bump clang and ubuntu by @sreimers in https://github.com/baresip/re/pull/1242\n* net/linux/addrs: fix netlink kernel warnings by @sreimers in https://github.com/baresip/re/pull/1243\n* rem: add au_ prefix to calc_nsamp() by @alfredh in https://github.com/baresip/re/pull/1244\n* rem/vidconv: add vidconv_center and x and y source offsets by @sreimers in https://github.com/baresip/re/pull/1240\n* test: add testcode for rem au-module by @alfredh in https://github.com/baresip/re/pull/1245\n* mem: remove peak from memstat by @alfredh in https://github.com/baresip/re/pull/1238\n* debian: replace with CPack DEB Generator by @sreimers in https://github.com/baresip/re/pull/1247\n* copyright: happy new year 2025 by @sreimers in https://github.com/baresip/re/pull/1246\n* test/vidconv: remove static struct test by @sreimers in https://github.com/baresip/re/pull/1248\n* net/linux/addrs: use list instead of fixed array for interface up by @sreimers in https://github.com/baresip/re/pull/1251\n* test: optional IPv6 for tcp/udp tos test by @alfredh in https://github.com/baresip/re/pull/1252\n* cmake: update min requirement and use range by @sreimers in https://github.com/baresip/re/pull/1253\n* rem/vid/frame: fix vidframe init by @sreimers in https://github.com/baresip/re/pull/1257\n* atomic: fix compilation for C++ and Windows-ARM64 by @alfredh in https://github.com/baresip/re/pull/1259\n* test: add test for C++ applications by @alfredh in https://github.com/baresip/re/pull/1254\n* ci: use ubuntu-22.04 were needed by @sreimers in https://github.com/baresip/re/pull/1261\n* cmake: enable compiler warnings for C only by @alfredh in https://github.com/baresip/re/pull/1263\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.18.0...v3.19.0\n\n\n## v3.18.0 - 2024-12-11\n\n### What's Changed\n* odict: add odict_pl_add() by @cspiel1 in https://github.com/baresip/re/pull/1208\n* ci/build: remove Ubuntu 20.04, add 24.04, use GCC 14 on 24.04 by @robert-scheck in https://github.com/baresip/re/pull/1210\n* test: vertical alignment of integration test names by @alfredh in https://github.com/baresip/re/pull/1212\n* sip: update doxygen comment by @alfredh in https://github.com/baresip/re/pull/1215\n* test/http: decrease test runs from 20 to 3 to decrease test time by @fAuernigg in https://github.com/baresip/re/pull/1216\n* sip/transp: allow requests w/o Max-Forwards header by @cspiel1 in https://github.com/baresip/re/pull/1217\n* test: remove unused fuzz mock by @alfredh in https://github.com/baresip/re/pull/1220\n* rtp: use rtp_pt_is_rtcp() for RTCP demultiplexing by @alfredh in https://github.com/baresip/re/pull/1221\n* aes: remove 192-bits CTR-mode (looks unused) by @alfredh in https://github.com/baresip/re/pull/1219\n* rtp: send all RTCP packets as compound packets by @maximilianfridrich in https://github.com/baresip/re/pull/1222\n* rtp/sess.c: lock rtcp_sess in rtcp_set_srate_tx to fix data race by @maximilianfridrich in https://github.com/baresip/re/pull/1223\n* Update Doxyfile by @alfredh in https://github.com/baresip/re/pull/1224\n* test: remove unused packet-filter mock by @alfredh in https://github.com/baresip/re/pull/1227\n* bump version to 3.18.0 by @alfredh in https://github.com/baresip/re/pull/1230\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.17.0...v3.18.0\n\n\n## v3.17.0 - 2024-11-06\n\n### What's Changed\n* types: remove old BREAKPOINT macro by @alfredh in https://github.com/baresip/re/pull/1194\n* dnsc: Fallback to getaddrinfo without any DNS servers by @weili-jiang in https://github.com/baresip/re/pull/1195\n* dns/client: return ENOTSUP if no server or not getaddrinfo by @sreimers in https://github.com/baresip/re/pull/1196\n* conf: add conf_get_float by @juha-h in https://github.com/baresip/re/pull/1198\n* ci/run-on-arch: use ubuntu 22.04 by @sreimers in https://github.com/baresip/re/pull/1204\n* thread: fix thrd_equal win32 handle by @sreimers in https://github.com/baresip/re/pull/1203\n* test: add pktsize to test_h264_packet_base() by @alfredh in https://github.com/baresip/re/pull/1205\n* tls: make tls_verify_handler() static by @alfredh in https://github.com/baresip/re/pull/1201\n* types: fix clang-tidy warning (gcc bit fields workaround) by @sreimers in https://github.com/baresip/re/pull/1206\n\n### New Contributors\n* @weili-jiang made their first contribution in https://github.com/baresip/re/pull/1195\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.16.0...v3.17.0\n\n\n## v3.16.0 - 2024-10-02\n\n### What's Changed\n* thread: fix pthread_setname_np NetBSD by @leleliu008 in https://github.com/baresip/re/pull/1182\n* ice: AI_V4MAPPED macro is missing on some BSD systems by @leleliu008 in https://github.com/baresip/re/pull/1181\n* rtp/rtcp: add RTCP Generic NACK packet send (RFC 4585 6.2.1) by @sreimers in https://github.com/baresip/re/pull/1186\n* main/fd_listen: return EMFILE if maxfds is reached by @sreimers in https://github.com/baresip/re/pull/1185\n* ci: build retest for android by @alfredh in https://github.com/baresip/re/pull/1187\n* test: minor cmake cleanup by @alfredh in https://github.com/baresip/re/pull/1188\n* test: fix re_printf format string for multithread test by @alfredh in https://github.com/baresip/re/pull/1190\n* ci: run retest on Fedora by @alfredh in https://github.com/baresip/re/pull/1191\n\n### New Contributors\n* @leleliu008 made their first contribution in https://github.com/baresip/re/pull/1182\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.15.0...v3.16.0\n\n\n## v3.15.0 - 2024-08-28\n\n### What's Changed\n* misc: remove HAVE_INET6 by @sreimers in https://github.com/baresip/re/pull/1159\n* dns/rr: fix dns_rr_print underflow by @sreimers in https://github.com/baresip/re/pull/1162\n* test/async: remove AI_ADDRCONFIG by @sreimers in https://github.com/baresip/re/pull/1165\n* retest: update usage message by @robert-scheck in https://github.com/baresip/re/pull/1166\n* add filter_registrar option by @maximilianfridrich in https://github.com/baresip/re/pull/1160\n* sa: add utility function to check if address is multicast by @cmfitch1 in https://github.com/baresip/re/pull/1168\n* tls/sni: skip SNI check if we are client or server_name absent by @maximilianfridrich in https://github.com/baresip/re/pull/1169\n* tls/sni: do not enable client verification when SNI matching is done by @maximilianfridrich in https://github.com/baresip/re/pull/1172\n* dd: Dependency Descriptor RTP header extension by @alfredh in https://github.com/baresip/re/pull/1170\n* aubuf: add AUBUF_TRACE mode with id by @sreimers in https://github.com/baresip/re/pull/1174\n* sip/transp: add client certificate to all TLS transports by @maximilianfridrich in https://github.com/baresip/re/pull/1173\n* tmr: add TMR_INIT by @sreimers in https://github.com/baresip/re/pull/1177\n* sipsess/reply: fix heap-use-after-free bug by @sreimers in https://github.com/baresip/re/pull/1179\n* version 3.15.0 by @alfredh in https://github.com/baresip/re/pull/1180\n\n### New Contributors\n* @cmfitch1 made their first contribution in https://github.com/baresip/re/pull/1168\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.14.0...v3.15.0\n\n\n## [v3.14.0] - 2024-07-23\n\n### What's Changed\n* aumix: use mutex_alloc() by @alfredh in https://github.com/baresip/re/pull/1142\n* sipreg/reg.c: stop retrying registers early after 401/407 by @maximilianfridrich in https://github.com/baresip/re/pull/1143\n* aumix: add locking in aumix_source_count() by @alfredh in https://github.com/baresip/re/pull/1145\n* test: init err in test_sip_auth_encode() by @alfredh in https://github.com/baresip/re/pull/1146\n* sipreg: refactor response_handler else optimization by @sreimers in https://github.com/baresip/re/pull/1147\n* vidmix: improve mutex usage by @alfredh in https://github.com/baresip/re/pull/1148\n* udp/mcast: use group scopeid as interface for IPv6 by @maximilianfridrich in https://github.com/baresip/re/pull/1149\n* .clangd: suppress -Wgnu-zero-variadic-macro-arguments by @maximilianfridrich in https://github.com/baresip/re/pull/1150\n* ci/build: use only macos-latest by @sreimers in https://github.com/baresip/re/pull/1153\n* cmake: fix resolv on FreeBSD by @sreimers in https://github.com/baresip/re/pull/1152\n* test: use h264_stap_decode_annexb() by @alfredh in https://github.com/baresip/re/pull/1151\n* sipsess/reply: terminate session if no (PR)ACK received after 64*T1 by @maximilianfridrich in https://github.com/baresip/re/pull/1155\n* rtcp: send BYE manually by @alfredh in https://github.com/baresip/re/pull/1154\n* cmake: check accept4 only on linux by @sreimers in https://github.com/baresip/re/pull/1157\n* cmake: fix iOS HAVE_ROUTE_LIST and darwin dns by @sreimers in https://github.com/baresip/re/pull/1158\n* test: check if header and payload is set by @alfredh in https://github.com/baresip/re/pull/1161\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.13.0...v3.14.0\n\n\n## [v3.13.0] - 2024-06-19\n\n### What's Changed\n* http/client: use dynamically sized buffers for PEM setters by @maximilianfridrich in https://github.com/baresip/re/pull/1117\n* tls: allow secure TLS renegotiation by @maximilianfridrich in https://github.com/baresip/re/pull/1121\n* tls: always enable USE_OPENSSL_SRTP by @alfredh in https://github.com/baresip/re/pull/1122\n* main: remove call to openssl init by @alfredh in https://github.com/baresip/re/pull/1120\n* sip/transp: Allow ACK w/o Max-Forwards header by @juha-h in https://github.com/baresip/re/pull/1124\n* net: remove NET_ADDRSTRLEN by @alfredh in https://github.com/baresip/re/pull/1123\n* ci/ios: increase min deployment target by @sreimers in https://github.com/baresip/re/pull/1126\n* tls/http: add certificate chain setters by @maximilianfridrich in https://github.com/baresip/re/pull/1125\n* sipsess/connect: set sess->established immediately on 200 receival by @maximilianfridrich in https://github.com/baresip/re/pull/1128\n* test/cmake: add crypt32 linking for WIN32 by @sreimers in https://github.com/baresip/re/pull/1130\n* ci/sanitizers: use clang-17 by @sreimers in https://github.com/baresip/re/pull/1131\n* ci/sanitizer: add undefined behavior sanitizer by @sreimers in https://github.com/baresip/re/pull/1132\n* sip: verify call-id, to-tag, cseq of INVITE response by @maximilianfridrich in https://github.com/baresip/re/pull/1129\n* ci: remove one unneeded directory change by @alfredh in https://github.com/baresip/re/pull/1134\n* test: change GENERATOR_SSRC from define to type by @alfredh in https://github.com/baresip/re/pull/1133\n* tls: refactoring SNI ctx usage for libressl support by @sreimers in https://github.com/baresip/re/pull/1136\n* test: add test_rtcp_loop() by @alfredh in https://github.com/baresip/re/pull/1137\n* ci/coverage: increase min coverage by @sreimers in https://github.com/baresip/re/pull/1138\n* ci/coverage: use json summary and upload html details by @sreimers in https://github.com/baresip/re/pull/1139\n* sip: add host param to sip_send_conn by @sreimers in https://github.com/baresip/re/pull/1141\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.12.0...v3.13.0\n\n## [v3.12.0] - 2024-05-15\n\n### What's Changed\n* cmake: fix static library build (vcpkg) by @alfredh in https://github.com/baresip/re/pull/1096\n* h264: add STAP-A decode with long startcodes by @alfredh in https://github.com/baresip/re/pull/1101\n* sess,request: deref request and ctrans immediately by @maximilianfridrich in https://github.com/baresip/re/pull/1099\n* ua: enforce magic cookie in Via branch by @maximilianfridrich in https://github.com/baresip/re/pull/1102\n* sip/auth: SHA-256 digest algorithm support by @sreimers in https://github.com/baresip/re/pull/1103\n* ci/coverage: increase min. coverage by @sreimers in https://github.com/baresip/re/pull/1106\n* rtp: fix correct logging text by @alfredh in https://github.com/baresip/re/pull/1109\n* types: fix RE_ARG_SIZE gcc bit fields by @sreimers in https://github.com/baresip/re/pull/1110\n* fmt: use re_fprintf instead of DEBUG_WARNING to avoid deadlock by @alfredh in https://github.com/baresip/re/pull/1112\n* dbg: remove support for logfile by @alfredh in https://github.com/baresip/re/pull/1111\n* test: add usage of rtcp_msg_print() by @alfredh in https://github.com/baresip/re/pull/1105\n* http/client: add setter to disable tls server verification by @maximilianfridrich in https://github.com/baresip/re/pull/1114\n* dbg: mutex should be unlocked while calling print handler by @alfredh in https://github.com/baresip/re/pull/1113\n* Update README.md by @alfredh in https://github.com/baresip/re/pull/1115\n* http/request: reset body mbuf pos on re-sending by @maximilianfridrich in https://github.com/baresip/re/pull/1116\n* bump version by @alfredh in https://github.com/baresip/re/pull/1118\n* cmake: bump soversion by @alfredh in https://github.com/baresip/re/pull/1119\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.11.0...v3.12.0\n\n\n## [v3.11.0] - 2024-04-09\n\n### What's Changed\n* ci/clang-analyze: bump clang version and fix status-bugs by @sreimers in https://github.com/baresip/re/pull/1079\n* main: Flush list of deleted fhs on `fd_poll` errors by @Lastique in https://github.com/baresip/re/pull/1081\n* main: Use slist for fhs delete list. by @Lastique in https://github.com/baresip/re/pull/1082\n* http/server: fix wrong sizeof in verify_msg by @akscf in https://github.com/baresip/re/pull/1083\n* ci/sanitizers: add mmap rnd_bits workaround by @sreimers in https://github.com/baresip/re/pull/1086\n* rtcp: add printing of TWCC packet by @alfredh in https://github.com/baresip/re/pull/1084\n* include: add re_h264.h to re.h by @alfredh in https://github.com/baresip/re/pull/1087\n* sdp: add sdp media lattr apply function the same way as for rattr by @cHuberCoffee in https://github.com/baresip/re/pull/1089\n* av1: improve packetizer by @alfredh in https://github.com/baresip/re/pull/1088\n* test: minor H.264 improvements by @alfredh in https://github.com/baresip/re/pull/1090\n* tls: add session resumption setter by @maximilianfridrich in https://github.com/baresip/re/pull/1091\n* thread/posix: optimize handler and fix gcc arm32 warning by @sreimers in https://github.com/baresip/re/pull/1093\n* h264: fix for Annex-B bitstreams with 4-byte startcode by @alfredh in https://github.com/baresip/re/pull/1092\n* ci/arch: add armv7 check by @sreimers in https://github.com/baresip/re/pull/1085\n* main,httpauth: fix different from the declaration by @jobo-zt in https://github.com/baresip/re/pull/1095\n* httpauth: fix doxygen comment by @alfredh in https://github.com/baresip/re/pull/1097\n\n### New Contributors\n* @akscf made their first contribution in https://github.com/baresip/re/pull/1083\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.10.0...v3.11.0\n\n\n## [v3.10.0] - 2024-03-06\n\n## What's Changed\n* transp: deref qent only if qentp is not set by @maximilianfridrich in https://github.com/baresip/re/pull/1061\n* sipsess: fix doxygen comments by @alfredh in https://github.com/baresip/re/pull/1062\n* aufile: fix doxygen comment by @alfredh in https://github.com/baresip/re/pull/1063\n* ci/codeql: bump action v3 by @sreimers in https://github.com/baresip/re/pull/1064\n* misc: text2pcap helpers (RTP/RTCP capturing) by @sreimers in https://github.com/baresip/re/pull/1065\n* ci/mingw: bump upload/download-artifact and cache versions by @sreimers in https://github.com/baresip/re/pull/1066\n* transp,tls: add TLS client verification by @maximilianfridrich in https://github.com/baresip/re/pull/1059\n* fmt/text2pcap: cleanup by @sreimers in https://github.com/baresip/re/pull/1067\n* ci/android: cache openssl build by @sreimers in https://github.com/baresip/re/pull/1068\n* ci/misc: fix double push/pull runs by @sreimers in https://github.com/baresip/re/pull/1069\n* fmt/text2pcap: fix coverity return value warning by @sreimers in https://github.com/baresip/re/pull/1070\n* sipsess/listen: improve glare handling by @maximilianfridrich in https://github.com/baresip/re/pull/1071\n* conf: add conf_get_i32 by @sreimers in https://github.com/baresip/re/pull/1072\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.9.0...v3.10.0\n\n\n## [v3.9.0] - 2024-01-31\n\n## What's Changed\n* http: fix doxygen by @cspiel1 in https://github.com/baresip/re/pull/1033\n* types: remove old ARRAY_SIZE macro by @alfredh in https://github.com/baresip/re/pull/1034\n* cmake: bump minimum to version 3.14 by @alfredh in https://github.com/baresip/re/pull/1030\n* test: use re_is_aligned() by @alfredh in https://github.com/baresip/re/pull/1035\n* sipsess: refactor and simplify SDP negotiation state by @maximilianfridrich in https://github.com/baresip/re/pull/1016\n* bump year by @sreimers in https://github.com/baresip/re/pull/1038\n* cmake,pc: fix static library build by @alfredh in https://github.com/baresip/re/pull/1036\n* rx thread activate by @cspiel1 in https://github.com/baresip/re/pull/1037\n* test: fix cppcheck warnings by @alfredh in https://github.com/baresip/re/pull/1040\n* test: move test_rtcp_decode_badmsg() to separate testcase by @alfredh in https://github.com/baresip/re/pull/1041\n* rtp: lock more fields from rtcp_sess by @cspiel1 in https://github.com/baresip/re/pull/1039\n* rtp: lock rtcp_set_srate() by @cspiel1 in https://github.com/baresip/re/pull/1043\n* test: HAVE_INET6 is always defined by @alfredh in https://github.com/baresip/re/pull/1046\n* ci: add run-on-arch for ARM64 linux by @alfredh in https://github.com/baresip/re/pull/1045\n* httpauth: digest verification rfc 7616 by @cHuberCoffee in https://github.com/baresip/re/pull/1044\n* tmr: prevent race condition on cancel by @sreimers in https://github.com/baresip/re/pull/1048\n* aubuf: fix coverity defect by @alfredh in https://github.com/baresip/re/pull/1051\n* btrace: fix coverity warning by @alfredh in https://github.com/baresip/re/pull/1049\n* ci/win: downgrade openssl by @sreimers in https://github.com/baresip/re/pull/1054\n* docs: update README by @alfredh in https://github.com/baresip/re/pull/1053\n* http: client - set scopeid fixes HTTP requests for IPv6ll by @cspiel1 in https://github.com/baresip/re/pull/1055\n* rtp: add rtp_source_ prefix to RTP source api by @alfredh in https://github.com/baresip/re/pull/1052\n* rtp: make struct rtp_source public by @alfredh in https://github.com/baresip/re/pull/1057\n* rtp: sess - fix coverity warning by @cspiel1 in https://github.com/baresip/re/pull/1058\n* mk: bump version to 3.9.0 by @alfredh in https://github.com/baresip/re/pull/1060\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.8.0...v3.9.0\n\n\n## [v3.8.0] - 2023-12-27\n\n## What's Changed\n* Update README.md by @alfredh in https://github.com/baresip/re/pull/1013\n* rem/aufile: aufile_get_length use aufmt_sample_size by @larsimmisch in https://github.com/baresip/re/pull/1011\n* rem/aufile: test and fix aufile_set_position nread by @larsimmisch in https://github.com/baresip/re/pull/1010\n* ci/ssl: bump assets release by @sreimers in https://github.com/baresip/re/pull/1014\n* readme: update supported openssl versions by @sreimers in https://github.com/baresip/re/pull/1015\n* ci: upgrade android to openssl 3.2.0 by @alfredh in https://github.com/baresip/re/pull/1017\n* sipsess/connect: don't create a dialog for 100 responses by @maximilianfridrich in https://github.com/baresip/re/pull/1018\n* aubuf: fix build with re_trace_event by @cspiel1 in https://github.com/baresip/re/pull/1019\n* trace: fix coverity warnings by @alfredh in https://github.com/baresip/re/pull/1024\n* aumix: fix coverity defect in destructor by @alfredh in https://github.com/baresip/re/pull/1025\n* main: fix doxygen comment by @alfredh in https://github.com/baresip/re/pull/1026\n* connect: do not enforce Contact header in 1XX responses with To tag by @maximilianfridrich in https://github.com/baresip/re/pull/1028\n* test/sipsess: test re-INVITE with wait for ACK by @cspiel1 in https://github.com/baresip/re/pull/1027\n* dialog: fix rtags of forking INVITE by @maximilianfridrich in https://github.com/baresip/re/pull/1023\n* cmake: add RE_LIBS config and add atomic check by @sreimers in https://github.com/baresip/re/pull/1029\n* ci: use actions/checkout@v4 by @robert-scheck in https://github.com/baresip/re/pull/1031\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.7.0...v3.8.0\n\n\n## [v3.7.0] - 2023-11-06\n\n## What's Changed\n* trace: add id handling by @sreimers in https://github.com/baresip/re/pull/981\n* fmt/pl: add pl_alloc_str by @sreimers in https://github.com/baresip/re/pull/983\n* ci/freebsd: limit runtime to 20 mins by @sreimers in https://github.com/baresip/re/pull/985\n* Httpauth digest response by @cHuberCoffee in https://github.com/baresip/re/pull/944\n* dialog: REVERT fix rtags of forking INVITE with 100rel (#947) by @juha-h in https://github.com/baresip/re/pull/986\n* ice: AI_V4MAPPED doesn't exist on OpenBSD by @landryb in https://github.com/baresip/re/pull/989\n* test: call - add call on-hold/resume test by @cspiel1 in https://github.com/baresip/re/pull/990\n* async: fix re_async_cancel mqueue handling by @sreimers in https://github.com/baresip/re/pull/995\n* async: clear callback function pointer after use (#992) by @cspiel1 in https://github.com/baresip/re/pull/993\n* Update README.md: Fix link in section Examples. by @Wolf-SO in https://github.com/baresip/re/pull/991\n* ci/abi: bump version by @sreimers in https://github.com/baresip/re/pull/1000\n* rtp: make flag rtcp_mux atomic by @cspiel1 in https://github.com/baresip/re/pull/997\n* cmake,udp: improve QOS_FLOWID and PQOS_FLOWID detection by @sreimers in https://github.com/baresip/re/pull/1002\n* types: extend RE_ARG to 32 by @sreimers in https://github.com/baresip/re/pull/1003\n* sip/transp: add win32 local transport addr fallback by @sreimers in https://github.com/baresip/re/pull/1001\n* cmake/config: set HAVE_THREADS only if threads.h by @sreimers in https://github.com/baresip/re/pull/1005\n* ci/freebsd: update vmactions/freebsd-vm@v1 by @sreimers in https://github.com/baresip/re/pull/1006\n* Coverity httpauth fixes by @sreimers in https://github.com/baresip/re/pull/1007\n* rem/aufile: fix aufile_get_length calculations by @larsimmisch in https://github.com/baresip/re/pull/1008\n\n## New Contributors\n* @Wolf-SO made their first contribution in https://github.com/baresip/re/pull/991\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.6.0...v3.7.0\n\n## [v3.6.2] - 2023-11-06\n\n## What's Changed\nsip/transp: add win32 local transport addr fallback (fixes TCP/TLS register)\n\n\n## [v3.6.1] - 2023-11-03\n\n## What's Changed\nice: AI_V4MAPPED doesn't exist on OpenBSD #989\ndialog: REVERT fix rtags of forking INVITE with 100rel (#947) #986\ndebian: fix version number\n\n\n## [v3.6.0] - 2023-10-17\n\n## What's Changed\n* ci/coverage: increase min. coverage by @sreimers in https://github.com/baresip/re/pull/958\n* Implement aufile_set_position by @larsimmisch in https://github.com/baresip/re/pull/943\n* dialog: fix rtags of forking INVITE with 100rel by @maximilianfridrich in https://github.com/baresip/re/pull/947\n* tls/alloc: set default min proto TLS 1.2 by @sreimers in https://github.com/baresip/re/pull/948\n* test: init err to 0 in sdp test (cppcheck) by @alfredh in https://github.com/baresip/re/pull/959\n* main: fd_listen fhs alloc rewrite by @sreimers in https://github.com/baresip/re/pull/805\n* Expand RE_BREAKPOINT macro on ARM64 by @larsimmisch in https://github.com/baresip/re/pull/961\n* jbuf: trace data for plot by @cspiel1 in https://github.com/baresip/re/pull/964\n* trace: use global trace log by @sreimers in https://github.com/baresip/re/pull/965\n* main: use ifdef for RE_TRACE_ENABLED by @sreimers in https://github.com/baresip/re/pull/966\n* test/hexdump: hide output by @sreimers in https://github.com/baresip/re/pull/968\n* trace: remove global default trace json by @sreimers in https://github.com/baresip/re/pull/969\n* ci/ssl: use tools repo and new assets by @sreimers in https://github.com/baresip/re/pull/972\n* fmt: doxygen correction in print.c by @cspiel1 in https://github.com/baresip/re/pull/973\n* trace: use only explicit RE_TRACE_ENABLED by cmake by @sreimers in https://github.com/baresip/re/pull/974\n* cmake: enable C11 for Windows (not MINGW) by @alfredh in https://github.com/baresip/re/pull/970\n* ci/coverage: lower min. coverage by @sreimers in https://github.com/baresip/re/pull/975\n* jbuf: move jbuf to baresip by @cspiel1 in https://github.com/baresip/re/pull/971\n* ci/coverage: improve coverage (enable trace) by @sreimers in https://github.com/baresip/re/pull/976\n* ci: bump pr-dependency-action@v0.6 by @sreimers in https://github.com/baresip/re/pull/977\n* ice: mDNS refactoring by @sreimers in https://github.com/baresip/re/pull/934\n* trace: add flush worker and optimize memory usage by @sreimers in https://github.com/baresip/re/pull/967\n* rtp: fix video jitter calculation and add arrival time rtp header by @sreimers in https://github.com/baresip/re/pull/978\n* ci: remove DARWIN compile flag from iOS build by @alfredh in https://github.com/baresip/re/pull/979\n* thread: add trace thread name logging by @sreimers in https://github.com/baresip/re/pull/980\n* ci/coverage: reduce min. coverage by @sreimers in https://github.com/baresip/re/pull/982\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.5.1...v3.6.0\n\n## [v3.5.1] - 2023-09-12\n\n## What's Changed\n* cmake: fix RELEASE definition for older cmake releases by @sreimers in https://github.com/baresip/re/pull/953\n* ci/build: add release build check by @sreimers in https://github.com/baresip/re/pull/954\n* cmake: fix definitions for older cmake by @sreimers in https://github.com/baresip/re/pull/955\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.5.0...v3.5.1\n\n## [v3.5.0] - 2023-09-12\n\n## What's Changed\n* ci/sonar: update scanner and java version by @sreimers in https://github.com/baresip/re/pull/895\n* ci/sonar: fix java distribution by @sreimers in https://github.com/baresip/re/pull/897\n* udp: add doxygen comments by @alfredh in https://github.com/baresip/re/pull/896\n* tls: fix some doxygen warnings by @alfredh in https://github.com/baresip/re/pull/894\n* mk: add release target by @sreimers in https://github.com/baresip/re/pull/901\n* types: add re_assert and re_assert_se definition by @sreimers in https://github.com/baresip/re/pull/900\n* btrace improvements by @sreimers in https://github.com/baresip/re/pull/902\n* Safe RE_VA_ARG helpers by @sreimers in https://github.com/baresip/re/pull/758\n* mbuf: add safe mbuf_printf by @sreimers in https://github.com/baresip/re/pull/899\n* auth: cast time_t timestamp by @sreimers in https://github.com/baresip/re/pull/903\n* mbuf: add mbuf_write_ptr and mbuf_read_ptr by @sreimers in https://github.com/baresip/re/pull/898\n* ci/mingw: remove cmake workaround by @sreimers in https://github.com/baresip/re/pull/906\n* tls: assume OpenSSL version 1.1.1 or later by @alfredh in https://github.com/baresip/re/pull/907\n* cmake: cleanup, remove unused define USE_OPENSSL_DTLS by @alfredh in https://github.com/baresip/re/pull/908\n* test/turn: use mutex instead atomic  by @sreimers in https://github.com/baresip/re/pull/909\n* stun: remove unused struct members by @alfredh in https://github.com/baresip/re/pull/910\n* stun: complete doxygen for struct by @alfredh in https://github.com/baresip/re/pull/912\n* tcp,udp: full IPv6 dual-stack socket support by @sreimers in https://github.com/baresip/re/pull/911\n* aufile: add methods to get size in bytes/length in ms by @larsimmisch in https://github.com/baresip/re/pull/913\n* async: signal ESHUTDOWN to all open worker callbacks by @sreimers in https://github.com/baresip/re/pull/915\n* dns/client: fix async getaddr query abort (not thread safe) by @sreimers in https://github.com/baresip/re/pull/914\n* async,dns/client: replace ESHUTDOWN with ECANCELED and optimize err handling by @sreimers in https://github.com/baresip/re/pull/918\n* sip: remove unused local variable by @cspiel1 in https://github.com/baresip/re/pull/920\n* dns/client: optimize udp timeout by @sreimers in https://github.com/baresip/re/pull/916\n* ice: add candidate sdp mdns support by @sreimers in https://github.com/baresip/re/pull/917\n* ice/icesdp: fix freeaddrinfo by @sreimers in https://github.com/baresip/re/pull/923\n* retest: fix format string in test_listcases for size_t argument by @cHuberCoffee in https://github.com/baresip/re/pull/922\n* httpauth: http digest challenge request using RFC 7616 by @cHuberCoffee in https://github.com/baresip/re/pull/919\n* types: fix RE_ARG_SIZE default argument promotions by @sreimers in https://github.com/baresip/re/pull/924\n* sip: fix TCP source port by @cspiel1 in https://github.com/baresip/re/pull/921\n* fmt/print: add 64-bit length modifier %Li, %Ld and %Lu by @sreimers in https://github.com/baresip/re/pull/905\n* fmt/print: improve print RE_VA_ARG debugging by @sreimers in https://github.com/baresip/re/pull/925\n* sip/request: fix check return code (found by coverity) by @sreimers in https://github.com/baresip/re/pull/926\n* httpauth/digest: use %L instead of PRI*64 macros by @sreimers in https://github.com/baresip/re/pull/927\n* types: add RE_ARG_SIZE struct pl (avoids wrong print fmt %r usage) by @sreimers in https://github.com/baresip/re/pull/928\n* dns/client: fix getaddrinfo err handling and mem_ref dnsc by @sreimers in https://github.com/baresip/re/pull/929\n* rtp/rtp_debug: fix printf size format by @sreimers in https://github.com/baresip/re/pull/933\n* main: optimize re_lock and re_unlock by @alfredh in https://github.com/baresip/re/pull/935\n* hexdump: fix format and add test by @alfredh in https://github.com/baresip/re/pull/936\n* test: fix bug in performance test format by @alfredh in https://github.com/baresip/re/pull/937\n* types: remove some duplicated error codes by @alfredh in https://github.com/baresip/re/pull/939\n* test: minor improvements in remain test by @alfredh in https://github.com/baresip/re/pull/931\n* dbg: remove unused functions by @sreimers in https://github.com/baresip/re/pull/941\n* cmake/re-config: add default CMAKE_BUILD_TYPE and fix RELEASE definition by @sreimers in https://github.com/baresip/re/pull/945\n\n## New Contributors\n* @larsimmisch made their first contribution in https://github.com/baresip/re/pull/913\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.4.0...v3.5.0\n\n## [v3.4.0] - 2023-08-09\n\n## What's Changed\n* rtpext: uniform parameter name fixes doxygen warning by @cspiel1 in https://github.com/baresip/re/pull/868\n* mk: add rem to doxygen inputs by @cspiel1 in https://github.com/baresip/re/pull/869\n* vidmix: allow different pixel format by @sreimers in https://github.com/baresip/re/pull/864\n* ajb doxygen by @cspiel1 in https://github.com/baresip/re/pull/870\n* aes: correct parameters for stub by @cspiel1 in https://github.com/baresip/re/pull/872\n* ci/build: fail on cmake and compile warnings by @sreimers in https://github.com/baresip/re/pull/873\n* fmt: fix format string in fmt_timestamp() by @alfredh in https://github.com/baresip/re/pull/874\n* hmac,md5,sha: add mbedtls backend by @cspiel1 in https://github.com/baresip/re/pull/871\n* test: no need to rewind freshly allocated mbuf by @alfredh in https://github.com/baresip/re/pull/876\n* httpauth: basic challenge creation and verification functions by @cHuberCoffee in https://github.com/baresip/re/pull/875\n* Fix include of re_thread.h in re_tmr.h by @nielsavonds in https://github.com/baresip/re/pull/879\n* btrace: fix WIN32_LEAN_AND_MEAN macro redefine by @jobo-zt in https://github.com/baresip/re/pull/880\n* aumix: add record sum handler by @sreimers in https://github.com/baresip/re/pull/877\n* ci/win: disable x86 testing by @sreimers in https://github.com/baresip/re/pull/883\n* sipsess: allow UPDATE and INFO in early dialog by @maximilianfridrich in https://github.com/baresip/re/pull/878\n* prefix macro VERSION by @cspiel1 in https://github.com/baresip/re/pull/882\n* main: use HAVE_SIGNAL in init.c by @cspiel1 in https://github.com/baresip/re/pull/881\n* test: change to ASSERT_XXX macros, remove EXPECT_XXX macros by @alfredh in https://github.com/baresip/re/pull/885\n* fmt: handy functions for pointer-length objects by @cHuberCoffee in https://github.com/baresip/re/pull/884\n* test: add TWCC test from Chrome 114 packet by @alfredh in https://github.com/baresip/re/pull/886\n* sipsess/listen: Fix target_refresh_handler by @maximilianfridrich in https://github.com/baresip/re/pull/888\n* ci/mingw: downgrade cmake by @sreimers in https://github.com/baresip/re/pull/890\n* cmake: fix target include path for subdir projects by @sreimers in https://github.com/baresip/re/pull/891\n\n## New Contributors\n* @nielsavonds made their first contribution in https://github.com/baresip/re/pull/879\n* @jobo-zt made their first contribution in https://github.com/baresip/re/pull/880\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.3.0...v3.4.0\n\n## [v3.3.0] - 2023-07-05\n\n## What's Changed\n* jbuf: use float ratio by @sreimers in https://github.com/baresip/re/pull/817\n* src: fix some typos by @alfredh in https://github.com/baresip/re/pull/828\n* jbuf: constant jbuf_put() by @cspiel1 in https://github.com/baresip/re/pull/821\n* ci/coverity: split up prepare and make steps by @sreimers in https://github.com/baresip/re/pull/832\n* vidmix: coverity fix by @alfredh in https://github.com/baresip/re/pull/830\n* sys/fs: fix fs_stdio_hide resource leak by @sreimers in https://github.com/baresip/re/pull/833\n* http: coverity fix by @alfredh in https://github.com/baresip/re/pull/834\n* avc: fix coverity by @alfredh in https://github.com/baresip/re/pull/835\n* sys: fix return value by @alfredh in https://github.com/baresip/re/pull/836\n* Do not automatically make new call when 3xx response is received by @juha-h in https://github.com/baresip/re/pull/829\n* sipreg: supports PNS custom contact URI by @codyit in https://github.com/baresip/re/pull/837\n* ci: add iOS platform by @alfredh in https://github.com/baresip/re/pull/838\n* ci/mingw: use cv2pdb for debug info conversion by @sreimers in https://github.com/baresip/re/pull/839\n* main: fix warning on Windows by @alfredh in https://github.com/baresip/re/pull/842\n* http: add compile-time check for USE_TLS by @alfredh in https://github.com/baresip/re/pull/841\n* test: check if USE_TLS is defined by @alfredh in https://github.com/baresip/re/pull/843\n* thread: fix WIN32 mingw warning by @sreimers in https://github.com/baresip/re/pull/844\n* src: fix doxygen warnings by @alfredh in https://github.com/baresip/re/pull/847\n* rtpext: add doxygen comments by @alfredh in https://github.com/baresip/re/pull/846\n* jbuf: enable old frame drop warnings by @sreimers in https://github.com/baresip/re/pull/848\n* md5: add support for native Windows Wincrypt API by @alfredh in https://github.com/baresip/re/pull/850\n* rtpext: add support for Two-Byte headers by @alfredh in https://github.com/baresip/re/pull/849\n* sha: add support for Windows native API by @alfredh in https://github.com/baresip/re/pull/851\n* cmake: change '-lm' to 'm' in LINKLIBS by @alfredh in https://github.com/baresip/re/pull/854\n* hmac: add support for native Windows API by @alfredh in https://github.com/baresip/re/pull/853\n* CI: Add support for building Windows ARM by @alfredh in https://github.com/baresip/re/pull/855\n* async: add work mutex handling by @sreimers in https://github.com/baresip/re/pull/857\n* SIP/TCP use source port for Via header by @cspiel1 in https://github.com/baresip/re/pull/824\n* sip: use TCP source port for Contact header by @cspiel1 in https://github.com/baresip/re/pull/858\n* cmake: remove obsolete $<INSTALL_INTERFACE:include for re-objs by @alfredh in https://github.com/baresip/re/pull/860\n* vidmix: always clear frames (avoid artifacts) by @sreimers in https://github.com/baresip/re/pull/861\n* http: use correct formatting %zu for size_t by @alfredh in https://github.com/baresip/re/pull/866\n\n## New Contributors\n* @codyit made their first contribution in https://github.com/baresip/re/pull/837\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.2.0...v3.3.0\n\n\n## [v3.2.0] - 2023-05-31\n\n## What's Changed\n* btrace: add win32 support by @sreimers in https://github.com/baresip/re/pull/767\n* cmake,thread: OpenBSD support by @landryb in https://github.com/baresip/re/pull/773\n* main/init: add exception signal handlers by @sreimers in https://github.com/baresip/re/pull/765\n* ua: unescape incoming Refer-To header by @maximilianfridrich in https://github.com/baresip/re/pull/770\n* main: add debug boolean to re\\_thread\\_check() by @sreimers in https://github.com/baresip/re/pull/775\n* btrace: add addr2line handling by @sreimers in https://github.com/baresip/re/pull/764\n* cmake: add optional static and shared build options by @sreimers in https://github.com/baresip/re/pull/778\n* main/init: enable win32 signal handler by @sreimers in https://github.com/baresip/re/pull/779\n* uric/contact: fix display name and contact header uri escaping by @maximilianfridrich in https://github.com/baresip/re/pull/762\n* ci/analyze: update clang and use analyze-build by @sreimers in https://github.com/baresip/re/pull/781\n* main: use mtx\\_recursive by @sreimers in https://github.com/baresip/re/pull/782\n* test: fix printf formating by @alfredh in https://github.com/baresip/re/pull/783\n* main: add re\\_thread\\_enter/leave polling check by @sreimers in https://github.com/baresip/re/pull/784\n* main/init: remove ContextRecord-\\>Rip (not available on all platforms) by @sreimers in https://github.com/baresip/re/pull/790\n* sipsess: fix RSeq header and rel\\_seq numbering by @maximilianfridrich in https://github.com/baresip/re/pull/796\n* websock: add proto support by @sreimers in https://github.com/baresip/re/pull/798\n* sip/transp: fix websock\\_accept proto by @sreimers in https://github.com/baresip/re/pull/800\n* sip/transp: remove unneeded websocket tcp tmr by @sreimers in https://github.com/baresip/re/pull/801\n* aubuf: activate overrun/underrun statistics by @cspiel1 in https://github.com/baresip/re/pull/803\n* cmake: add libre namespace export by @sreimers in https://github.com/baresip/re/pull/786\n* revert uri escaping commits by @maximilianfridrich in https://github.com/baresip/re/pull/802\n* jbuf: refactor frame calculation by @sreimers in https://github.com/baresip/re/pull/788\n* jbuf: frame fixes by @sreimers in https://github.com/baresip/re/pull/806\n* sip: add missing WS and WSS transport decoder for VIA headers by @pitti98 in https://github.com/baresip/re/pull/809\n* ice: Fix conncheck callback called multiple times by @pitti98 in https://github.com/baresip/re/pull/807\n* jbuf: JBUF\\_FIXED should also keep min wish size by @sreimers in https://github.com/baresip/re/pull/813\n* ci: compile choco openssl with --x86 for 32-bits by @alfredh in https://github.com/baresip/re/pull/814\n* jbuf: fix possible division by zero by @sreimers in https://github.com/baresip/re/pull/815\n* fix some cppcheck warnings by @alfredh in https://github.com/baresip/re/pull/816\n* test/ice: fix cppcheck by @sreimers in https://github.com/baresip/re/pull/818\n* tls/openssl: fix cppcheck warnings by @sreimers in https://github.com/baresip/re/pull/820\n* base64: fix cppcheck warnings by @sreimers in https://github.com/baresip/re/pull/819\n* main/init: fix upper signal handling by @sreimers in https://github.com/baresip/re/pull/822\n* include: fix some typos by @alfredh in https://github.com/baresip/re/pull/825\n\n## New Contributors\n* @landryb made their first contribution in https://github.com/baresip/re/pull/773\n* @pitti98 made their first contribution in https://github.com/baresip/re/pull/809\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.1.0...v3.2.0\n\n## [v3.1.0] - 2023-04-27\n\n## What's Changed\n* ci: bump mingw openssl to 3.1.0 by @alfredh in https://github.com/baresip/re/pull/738\n* thread: add cnd_timedwait() by @sreimers in https://github.com/baresip/re/pull/736\n* Add tls and http apis for post handshake by @fAuernigg in https://github.com/baresip/re/pull/713\n* ci/sanitizers: add multi thread testing by @sreimers in https://github.com/baresip/re/pull/741\n* ci/win: use separate retest step by @sreimers in https://github.com/baresip/re/pull/742\n* thread: fix pthread_setname_np thread pointer deref by @sreimers in https://github.com/baresip/re/pull/744\n* ci: add FreeBSD test by @sreimers in https://github.com/baresip/re/pull/745\n* cmake: bump minimum version of OpenSSL to 1.1.1 by @alfredh in https://github.com/baresip/re/pull/746\n* ci: avoid hardcoded OpenSSL path on macOS by @robert-scheck in https://github.com/baresip/re/pull/747\n* sip,uri,test: Escape SIP URIs by @maximilianfridrich in https://github.com/baresip/re/pull/740\n* udp: add a lock for the helpers list by @cspiel1 in https://github.com/baresip/re/pull/732\n* rem/vidmix: add position index handling by @sreimers in https://github.com/baresip/re/pull/749\n* aubuf: set auframe fields correct in read_auframe loop by @cspiel1 in https://github.com/baresip/re/pull/750\n* list: refactor/optimize list_insert_sorted by @sreimers in https://github.com/baresip/re/pull/748\n* ci/freebsd: remove openssl-devel by @sreimers in https://github.com/baresip/re/pull/755\n* tmr: add tmr_continue() by @vanrein in https://github.com/baresip/re/pull/754\n* ci,cmake: replace C99 check by strict C99 and C11 checks by @sreimers in https://github.com/baresip/re/pull/759\n* atomic: Fix missing memory order arguments in MSVC atomic functions by @Lastique in https://github.com/baresip/re/pull/766\n* thread: remove win32 SetThreadDescription  by @sreimers in https://github.com/baresip/re/pull/768\n\n**Full Changelog**: https://github.com/baresip/re/compare/v3.0.0...v3.1.0\n\n---\n\n## [v3.0.0] - 2023-03-20\n\n## What's Changed\n* main: allow poll_method change only before setup by @sreimers in https://github.com/baresip/re/pull/681\n* main: add more details to re_debug() by @alfredh in https://github.com/baresip/re/pull/689\n* merge rem into re by @alfredh in https://github.com/baresip/re/pull/683\n* tmr,main: thread safe tmr handling by @sreimers in https://github.com/baresip/re/pull/690\n* tmr,main: add tmrl_count by @sreimers in https://github.com/baresip/re/pull/694\n* main: add re_thread_async_main_id and re_thread_async_main_cancel by @sreimers in https://github.com/baresip/re/pull/697\n* merge retest into re by @alfredh in https://github.com/baresip/re/pull/695\n* tls: add doxygen comment to dtls_recv_packet() by @alfredh in https://github.com/baresip/re/pull/699\n* test: use TEST_ERR by @sreimers in https://github.com/baresip/re/pull/700\n* test: use TEST_ERR by @sreimers in https://github.com/baresip/re/pull/701\n* hmac: remove unused SHA_BLOCKSIZE by @alfredh in https://github.com/baresip/re/pull/703\n* async: fix cancel memory leaks by @sreimers in https://github.com/baresip/re/pull/705\n* ci: windows Debug/Release by @alfredh in https://github.com/baresip/re/pull/704\n* cmake: add extra source files for aes and hmac by @alfredh in https://github.com/baresip/re/pull/708\n* test: remove libpthread from LINKLIBS by @alfredh in https://github.com/baresip/re/pull/710\n* hmac: add stateless HMAC-SHA256 wrapper by @alfredh in https://github.com/baresip/re/pull/706\n* thread: add thread name handling by @sreimers in https://github.com/baresip/re/pull/709\n* ci: add support for Android by @alfredh in https://github.com/baresip/re/pull/707\n* test: fix convert C99 by @sreimers in https://github.com/baresip/re/pull/717\n* dbg: remove pre-C99 fallbacks by @sreimers in https://github.com/baresip/re/pull/718\n* test: remove CMAKE_C_STANDARD by @alfredh in https://github.com/baresip/re/pull/714\n* sdp/media: fix ccheck list_unlink warning by @sreimers in https://github.com/baresip/re/pull/715\n* jbuf: allocate mutex and lock also jbuf_debug() by @cspiel1 in https://github.com/baresip/re/pull/693\n* sys/fs: fix fs_fopen read only mode (should not create file) by @sreimers in https://github.com/baresip/re/pull/719\n* ci/ssl: update OpenSSL/LibreSSL by @sreimers in https://github.com/baresip/re/pull/720\n* http: fix read_file on win32 (wrong filesize) and use mbuf by @fAuernigg in https://github.com/baresip/re/pull/711\n* sys: add sys_getenv() by @sreimers in https://github.com/baresip/re/pull/721\n* rtp: Don't check RTCP socket if rtcp-mux is enabled by @Lastique in https://github.com/baresip/re/pull/723\n* tls: remove return statement that is not needed by @alfredh in https://github.com/baresip/re/pull/724\n* sha: add sha256_printf() by @alfredh in https://github.com/baresip/re/pull/725\n* cmake: add rem headers to install by @sreimers in https://github.com/baresip/re/pull/727\n* cmake: merge REM_HEADERS by @sreimers in https://github.com/baresip/re/pull/728\n* tls: set mbuf pos and end at the same time by @alfredh in https://github.com/baresip/re/pull/729\n* misc: add Makefile helpers and exclude retest from all target by @sreimers in https://github.com/baresip/re/pull/726\n* sa: add sa_struct_get_size() to check size by @alfredh in https://github.com/baresip/re/pull/730\n* rtcp: make rtcp_calc_rtt() public by @alfredh in https://github.com/baresip/re/pull/731\n* test: add HAVE_UNIXSOCK=0 support by @sreimers in https://github.com/baresip/re/pull/734\n* aubuf: set sample format when frame is read by @cspiel1 in https://github.com/baresip/re/pull/737\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v2.12.0...v3.0.0\n\n---\n\n## [v2.12.0] - 2023-02-15\n\n## What's Changed\n* tls: remove ifdef DTLS_CTRL_HANDLE_TIMEOUT by @alfredh in https://github.com/baresip/re/pull/634\n* cmake: increment required version by @cspiel1 in https://github.com/baresip/re/pull/642\n* dtls: add logging of DTLS packet content-type by @alfredh in https://github.com/baresip/re/pull/641\n* dtls: add single connection mode by @alfredh in https://github.com/baresip/re/pull/643\n* ice: reduce conncheck start timer by @alfredh in https://github.com/baresip/re/pull/640\n* async,main: make re_thread_async itself thread safe by @sreimers in https://github.com/baresip/re/pull/644\n* av1: remove old packetizer by @alfredh in https://github.com/baresip/re/pull/645\n* av1: fix chrome interop by @alfredh in https://github.com/baresip/re/pull/646\n* av1: minor cleanups by @alfredh in https://github.com/baresip/re/pull/649\n* trace: fix new json start by @sreimers in https://github.com/baresip/re/pull/648\n* make rtcp interval configureable by @sreimers in https://github.com/baresip/re/pull/650\n* sa: proposal to always enable struct sockaddr_in6 by @alfredh in https://github.com/baresip/re/pull/651\n* ci: rename ccheck to lint by @alfredh in https://github.com/baresip/re/pull/653\n* ci: extend coverage test with retest+select by @alfredh in https://github.com/baresip/re/pull/652\n* main: remove poll support by @sreimers in https://github.com/baresip/re/pull/654\n* ci: use Ninja as CMake generator by @alfredh in https://github.com/baresip/re/pull/656\n* ci/abi: fix abidiff paths by @sreimers in https://github.com/baresip/re/pull/657\n* PRACK refactoring by @maximilianfridrich in https://github.com/baresip/re/pull/630\n* types: add RE_ prefix to ARRAY_SIZE() by @alfredh in https://github.com/baresip/re/pull/658\n* cmake: add USE_TRACE option (default OFF) by @sreimers in https://github.com/baresip/re/pull/660\n* add re prefix by @alfredh in https://github.com/baresip/re/pull/659\n* tcp: add RE_TCP_BACKLOG by @sreimers in https://github.com/baresip/re/pull/661\n* Fix doxygen warnings by @alfredh in https://github.com/baresip/re/pull/662\n* mbuf: docs and setters/getters by @alfredh in https://github.com/baresip/re/pull/663\n* tcp,cmake: use accept4 if supported by @sreimers in https://github.com/baresip/re/pull/665\n* tcp: remove SO_LINGER socket option by @sreimers in https://github.com/baresip/re/pull/664\n* rtcp: update documentation by @alfredh in https://github.com/baresip/re/pull/666\n* tcp: check SO_ERROR only for active connections by @sreimers in https://github.com/baresip/re/pull/667\n* cmake: add HAVE_RESOLV by @sreimers in https://github.com/baresip/re/pull/668\n* hash: add hash_debug by @sreimers in https://github.com/baresip/re/pull/670\n* list: improve list_apply performance by @sreimers in https://github.com/baresip/re/pull/669\n* rtp: add doxygen comments by @alfredh in https://github.com/baresip/re/pull/671\n* rtp: extra dox for rtcp_encode by @alfredh in https://github.com/baresip/re/pull/672\n* ci: add thread and address sanitizer by @sreimers in https://github.com/baresip/re/pull/673\n* Do not change glibc feature selection macros in unsupported ways by @fweimer-rh in https://github.com/baresip/re/pull/674\n* auth: replace ETIME with ETIMEDOUT by @sreimers in https://github.com/baresip/re/pull/675\n* cmake: add min. OpenSSL 1.1.0 version requirement by @sreimers in https://github.com/baresip/re/pull/680\n* ci: fix flaky azure mirrors by @sreimers in https://github.com/baresip/re/pull/682\n* tls: remove obsolete openssl version check and fix libressl build by @cspiel1 in https://github.com/baresip/re/pull/679\n* ci/ssl: fix openssl root dir by @sreimers in https://github.com/baresip/re/pull/677\n* main: add re_thread_async_main for re_global only by @sreimers in https://github.com/baresip/re/pull/685\n* atomic: fix win32 atomic load const warnings by @sreimers in https://github.com/baresip/re/pull/688\n* atomic: fix __iso_volatile_load64 deref by @sreimers in https://github.com/baresip/re/pull/691\n* bump version numbers to 2.12.0 by @alfredh in https://github.com/baresip/re/pull/692\n\n## New Contributors\n* @fweimer-rh made their first contribution in https://github.com/baresip/re/pull/674\n\n**Full Changelog**: https://github.com/baresip/re/compare/v2.11.0...v2.12.0\n\n---\n\n## [v2.11.0] - 2023-01-11\n\n## What's Changed\n* net/types: move socket helpers and rename RE_ERRNO_SOCK and RE_BAD_SOCK by @sreimers in https://github.com/baresip/re/pull/608\n* sys: fix fileno warning by @alfredh in https://github.com/baresip/re/pull/612\n* tls: clear session callbacks in destructor by @cspiel1 in https://github.com/baresip/re/pull/611\n* tls: use long SSL state strings for logging by @cspiel1 in https://github.com/baresip/re/pull/613\n* tls: Set session only once before Client Hello by @cspiel1 in https://github.com/baresip/re/pull/607\n* udp: add optional send/recv handler by @alfredh in https://github.com/baresip/re/pull/602\n* tls: remove deprecated tls_set_selfsigned() by @alfredh in https://github.com/baresip/re/pull/614\n* main: allow for init twice by @alfredh in https://github.com/baresip/re/pull/615\n* cmake: add check_c_compiler_flag for atomic-implicit-seq-cst warning by @sreimers in https://github.com/baresip/re/pull/617\n* http,tcp: add http_listen_fd and tcp_sock_alloc_fd by @sreimers in https://github.com/baresip/re/pull/618\n* tcp_sock_alloc_fd: fix fdc initializing by @sreimers in https://github.com/baresip/re/pull/619\n* sa,unixsock: add unix domain socket support by @sreimers in https://github.com/baresip/re/pull/600\n* mk: remove makefiles by @sreimers in https://github.com/baresip/re/pull/620\n* RTP Resend by @sreimers in https://github.com/baresip/re/pull/626\n* TLS server support SNI based certificate selection by @cspiel1 in https://github.com/baresip/re/pull/596\n* sipsess/request.c: return error code in sipsess_request_alloc by @maximilianfridrich in https://github.com/baresip/re/pull/631\n* ice: add ANSI output with Green and Red colors by @alfredh in https://github.com/baresip/re/pull/632\n* docs: update reference to TLS 1.2 by @alfredh in https://github.com/baresip/re/pull/633\n* cmake, sa: enable unix sockets, if HAVE_UNIXSOCK is undefined by @fAuernigg in https://github.com/baresip/re/pull/636\n* trice: refresh doxygen comments by @alfredh in https://github.com/baresip/re/pull/635\n* tls: add error handling for BIO_reset by @cspiel1 in https://github.com/baresip/re/pull/638\n* dns/client: fix rrlv reference cache handling by @sreimers in https://github.com/baresip/re/pull/637\n\n\n**Full Changelog**: https://github.com/baresip/re/compare/v2.10.0...v2.11.0\n\n---\n\n## [v2.10.0] - 2022-12-06\n\n## What's Changed\n* h264: add STAP-A by @alfredh in https://github.com/baresip/re/pull/584\n* tls: SSL_get_peer_certificate is deprecated by @sreimers in https://github.com/baresip/re/pull/585\n* sipreg fix contact handler `expires` evaluation by @cspiel1 in https://github.com/baresip/re/pull/581\n* ice: local candidate policy config by @sreimers in https://github.com/baresip/re/pull/589\n* h265: add missing NAL types by @alfredh in https://github.com/baresip/re/pull/590\n* rtpext: move from baresip to re by @alfredh in https://github.com/baresip/re/pull/591\n* mk: add rtpext to Makefile build by @cspiel1 in https://github.com/baresip/re/pull/594\n* mk: add makefile deprecation warning by @sreimers in https://github.com/baresip/re/pull/595\n* fs: use dup/dup2 for stdio hide and restore by @sreimers in https://github.com/baresip/re/pull/597\n* dns: fix dnsc_conf_set memory leak by @alfredh in https://github.com/baresip/re/pull/598\n* cmake: add TRACE_SSL compile definition by @cspiel1 in https://github.com/baresip/re/pull/599\n* cmake: add ZLIB_INCLUDE_DIRS by @sreimers in https://github.com/baresip/re/pull/601\n* cmake/pkgconfig: fix prefix variable by @cspiel1 in https://github.com/baresip/re/pull/603\n* ci/valgrind: use ubuntu-20.04 by @sreimers in https://github.com/baresip/re/pull/606\n\n**Full Changelog**: https://github.com/baresip/re/compare/v2.9.0...v2.10.0\n\n---\n\n## [v2.9.0] - 2022-11-01\n\n## What's Changed\n* cmake,make: bump version and set dev identifier by @cspiel1 in https://github.com/baresip/re/pull/553\n* udp: remove udp_send_anon() by @alfredh in https://github.com/baresip/re/pull/550\n* cmake: enable export symbols for backtrace by @sreimers in https://github.com/baresip/re/pull/554\n* README.md: Update build instructions for cmake by @robert-scheck in https://github.com/baresip/re/pull/556\n* cmake: improve kqueue and epoll detection by @sreimers in https://github.com/baresip/re/pull/558\n* fs: add fs_stdio_hide() and fs_stdio_restore() helpers by @sreimers in https://github.com/baresip/re/pull/559\n* json: remove unknown type warning by @alfredh in https://github.com/baresip/re/pull/560\n* http: fix warning arguments by @alfredh in https://github.com/baresip/re/pull/561\n* net_if_getlinklocal: use AF from input parameter by @alfredh in https://github.com/baresip/re/pull/565\n* fmt: add str_itoa by @sreimers in https://github.com/baresip/re/pull/569\n* SDP support for <proto> udp by @vanrein in https://github.com/baresip/re/pull/538\n* tls: remove some warnings by @alfredh in https://github.com/baresip/re/pull/567\n* fmt: add pl_trim functions by @cspiel1 in https://github.com/baresip/re/pull/557\n* aes/openssl: remove obsolete version check by @alfredh in https://github.com/baresip/re/pull/572\n* http: use str_dup() instead of unsafe strcpy() by @alfredh in https://github.com/baresip/re/pull/574\n* doxygen: update comments by @alfredh in https://github.com/baresip/re/pull/577\n* reg: remove obsolete void cast by @cspiel1 in https://github.com/baresip/re/pull/576\n* Tls connect debug by @alfredh in https://github.com/baresip/re/pull/573\n* mk: update doxygen file by @alfredh in https://github.com/baresip/re/pull/578\n* ci: use actions/checkout@v3 by @sreimers in https://github.com/baresip/re/pull/579\n* tls: remove ifdef from public API by @alfredh in https://github.com/baresip/re/pull/580\n* sip: sip_conncfg_set pass by reference by @alfredh in https://github.com/baresip/re/pull/582\n* dnsc get conf and skip hash alloc without hash size changes by @fAuernigg in https://github.com/baresip/re/pull/575\n* sdp/media: fix reorder codecs (restore old behavior) by @juha-h in https://github.com/baresip/re/pull/583\n* list: fix list_flush head and tail by @sreimers in https://github.com/baresip/re/pull/586\n* prepare 2.9.0 by @alfredh in https://github.com/baresip/re/pull/587\n\n## New Contributors\n* @vanrein made their first contribution in https://github.com/baresip/re/pull/538\n\n**Full Changelog**: https://github.com/baresip/re/compare/v2.8.0...v2.9.0\n\n---\n\n## [v2.8.0] - 2022-10-01\n\n* Update README.md by @alfredh in https://github.com/baresip/re/pull/503\n* thread: fix win32 thrd\\_create return values by @sreimers in https://github.com/baresip/re/pull/506\n* cmake: bump min. version 3.10 by @sreimers in https://github.com/baresip/re/pull/504\n* cmake: add USE\\_JBUF option by @alfredh in https://github.com/baresip/re/pull/507\n* http/https requests with large body by @fAuernigg in https://github.com/baresip/re/pull/485\n* http/client: fix possible null pointer dereference by @sreimers in https://github.com/baresip/re/pull/509\n* ci: test choco install no-progress by @alfredh in https://github.com/baresip/re/pull/510\n* bitv: remove deprecated module by @alfredh in https://github.com/baresip/re/pull/513\n* types,fmt: use re\\_restrict by @sreimers in https://github.com/baresip/re/pull/514\n* refer out of dialog by @cspiel1 in https://github.com/baresip/re/pull/508\n* UPDATE bugfix by @maximilianfridrich in https://github.com/baresip/re/pull/516\n* sip/auth: fix mem\\_zalloc return check by @sreimers in https://github.com/baresip/re/pull/518\n* Update media fixes by @cspiel1 in https://github.com/baresip/re/pull/515\n* dns, http: add dnsc\\_getaddrinfo\\_enabled. prevent reset of getaddrinfo enabled by @fAuernigg in https://github.com/baresip/re/pull/519\n* rtp: Improve media synchronization by @Lastique in https://github.com/baresip/re/pull/418\n* conf: check if returned size is larger than buffer by @alfredh in https://github.com/baresip/re/pull/523\n* udp: remove very old iOS hack by @alfredh in https://github.com/baresip/re/pull/524\n* tcp: remove very old iOS hack by @alfredh in https://github.com/baresip/re/pull/525\n* Use CMake for debian packages by @sreimers in https://github.com/baresip/re/pull/522\n* crc32: add re wrapper by @alfredh in https://github.com/baresip/re/pull/526\n* ci: convert valgrind to cmake by @alfredh in https://github.com/baresip/re/pull/529\n* ci: convert ssl build to cmake by @alfredh in https://github.com/baresip/re/pull/530\n* ci: convert fedora to cmake by @alfredh in https://github.com/baresip/re/pull/531\n* ci: convert coverage to cmake by @alfredh in https://github.com/baresip/re/pull/532\n* ci: migrate to cmake by @alfredh in https://github.com/baresip/re/pull/533\n* cmake: add LINKLIBS and make backtrace and zlib optional by @sreimers in https://github.com/baresip/re/pull/534\n* C99 compatibility by @sreimers in https://github.com/baresip/re/pull/536\n* pcp: fix cppcheck warning by @alfredh in https://github.com/baresip/re/pull/540\n* fmt/print: fix cppcheck overflow warning by @sreimers in https://github.com/baresip/re/pull/542\n* tls: remove SHA1 fingerprint (deprecated) by @alfredh in https://github.com/baresip/re/pull/527\n* send DTMF via hidden call by @cspiel1 in https://github.com/baresip/re/pull/537\n* sipreg: avoid sending un-REGISTER periodically by @cspiel1 in https://github.com/baresip/re/pull/543\n* cmake,mk: bump the tentative next release with pre-release identifier by @sreimers in https://github.com/baresip/re/pull/546\n* sipsess/update: Add Contact header to UPDATE by @maximilianfridrich in https://github.com/baresip/re/pull/545\n* cmake: fix shared API soversion (aligned with make) by @sreimers in https://github.com/baresip/re/pull/549\n\n---\n\n## [v2.7.0] - 2022-09-01\n\n* async: add re_thread_async by @sreimers in https://github.com/baresip/re/pull/462\n* atomic: Add support for gcc __sync intrinsics by @Lastique in https://github.com/baresip/re/pull/467\n* btrace: fix gcc 4.3.5 warnings by @cspiel1 in https://github.com/baresip/re/pull/468\n* h264: fix gcc 4.3.5 warnings by @cspiel1 in https://github.com/baresip/re/pull/469\n* async: add guard by @sreimers in https://github.com/baresip/re/pull/474\n* dns/client: add async getaddrinfo usage by @sreimers in https://github.com/baresip/re/pull/470\n* async: make work handler and callback optional by @sreimers in https://github.com/baresip/re/pull/481\n* BareSip. Add a state update action to the main loop to unblock pollin… by @viordash in https://github.com/baresip/re/pull/480\n* dns,net: fix build of asyn_getaddrinfo on gcc 4.3.5 (#482) by @cspiel1 in https://github.com/baresip/re/pull/483\n* dns/client: fix getaddrinfo duplicates by @sreimers in https://github.com/baresip/re/pull/486\n* http/client: fix dnsc_conf initialization by @sreimers in https://github.com/baresip/re/pull/487\n* tmr: tmr_start_dbg use const char for file arg by @sreimers in https://github.com/baresip/re/pull/488\n* base64: Encoding/Decoding with URL and Filename Safe Alphabet by @sreimers in https://github.com/baresip/re/pull/471\n* misc: fix c11 err handling by @sreimers in https://github.com/baresip/re/pull/476\n* cmake: move definitions to re-config.cmake by @sreimers in https://github.com/baresip/re/pull/491\n* ci/mingw: fix make retest by @sreimers in https://github.com/baresip/re/pull/492\n* cmake: add pkgconfig by @sreimers in https://github.com/baresip/re/pull/493\n* Fix error: ‘NI_MAXSERV’ undeclared by @widgetii in https://github.com/baresip/re/pull/495\n* Fix error: storage size of ‘ifrr’ isn’t known by @widgetii in https://github.com/baresip/re/pull/496\n* ci/musl: add alpine/musl build by @sreimers in https://github.com/baresip/re/pull/499\n* Correctly update local media format ids to match those in the offer by @juha-h in https://github.com/baresip/re/pull/498\n* debian: fix prefix by @juha-h in https://github.com/baresip/re/pull/501\n\n---\n\n## [v2.6.0] - 2022-08-01\n\n* ice: change one warning to notice by @alfredh in https://github.com/baresip/re/pull/421\n* Fix compilation error on musl: __GNUC_PREREQ macro defined only for libc library by @widgetii in https://github.com/baresip/re/pull/422\n* sip: add RFC 3262 support by @maximilianfridrich in https://github.com/baresip/re/pull/419\n* bfcp: Add support for TCP transport for BFCP by @Lastique in https://github.com/baresip/re/pull/411\n* strans/accept: fix cancel/rejection by @maximilianfridrich in https://github.com/baresip/re/pull/423\n* hash: add hash_list_idx() by @sreimers in https://github.com/baresip/re/pull/427\n* tls: Add a method to set OpenSSL certificate by @Lastique in https://github.com/baresip/re/pull/426\n* sipsess: fix PRACK offer/answer behavior by @maximilianfridrich in https://github.com/baresip/re/pull/430\n* thread: thrd_error fixes by @sreimers in https://github.com/baresip/re/pull/431\n* sipsess: fix coverity warnings by @maximilianfridrich in https://github.com/baresip/re/pull/433\n* main: add re_nfds() and poll_method_get() getters by @sreimers in https://github.com/baresip/re/pull/435\n* fmt/print: fix local_itoa casting by @sreimers in https://github.com/baresip/re/pull/437\n* leb128: switch to uint64_t by @alfredh in https://github.com/baresip/re/pull/436\n* types,mk: remove HAVE_STDBOOL_H by @sreimers in https://github.com/baresip/re/pull/439\n* fmt/print: snprintf restrict declarations by @sreimers in https://github.com/baresip/re/pull/438\n* net: minor cleanup in linux route code by @alfredh in https://github.com/baresip/re/pull/440\n* sip: add RFC 3311 support by @maximilianfridrich in https://github.com/baresip/re/pull/425\n* rtmp: check upper bound for amf array by @alfredh in https://github.com/baresip/re/pull/441\n* rtcp: check TWCC count range (Coverity fix) by @alfredh in https://github.com/baresip/re/pull/442\n* mem: Align data to natural alignment by @Lastique in https://github.com/baresip/re/pull/416\n* ci/misc: bump pr-dependency-action@v0.5 by @sreimers in https://github.com/baresip/re/pull/444\n* net: linux/rt: init gw to correct af by @alfredh in https://github.com/baresip/re/pull/447\n* rtp: Add `rtcp_send` declaration to the public header by @Lastique in https://github.com/baresip/re/pull/448\n* Main method best by @alfredh in https://github.com/baresip/re/pull/449\n* cmake: add explicit /volatile:ms (required for arm) by @sreimers in https://github.com/baresip/re/pull/451\n* mem: Make nrefs atomic by @Lastique in https://github.com/baresip/re/pull/446\n* atomic: add some short atomic alias helpers by @sreimers in https://github.com/baresip/re/pull/452\n* ci/build: replace deprecated macos-10.15 by @sreimers in https://github.com/baresip/re/pull/454\n* Improve RFC 3262 by @maximilianfridrich in https://github.com/baresip/re/pull/450\n* atomic: rename helpers by @sreimers in https://github.com/baresip/re/pull/455\n* cmake,make: add clang atomic-implicit-seq-cst warning by @sreimers in https://github.com/baresip/re/pull/453\n* cmake: add missing includes to install by @paresy in https://github.com/baresip/re/pull/456\n* Fix prack handling by @maximilianfridrich in https://github.com/baresip/re/pull/457\n* mem: Correct memory clobbering size by @Lastique in https://github.com/baresip/re/pull/458\n* mem: Correct calculation of total mem size in mem_status by @Lastique in https://github.com/baresip/re/pull/459\n* tls: Securely clear memory from private key material by @Lastique in https://github.com/baresip/re/pull/460\n* fmt/str_error: always print error number by @sreimers in https://github.com/baresip/re/pull/461\n* thread: add cnd_broadcast posix/win32 fallbacks by @sreimers in https://github.com/baresip/re/pull/463\n* list: add list_move() helper by @sreimers in https://github.com/baresip/re/pull/464\n* thread: fix thread_create_name ENOMEM by @sreimers in https://github.com/baresip/re/pull/465\n\n---\n\n## [v2.5.0] - 2022-07-01\n\n* av1: add doxygen comments by @alfredh in https://github.com/baresip/re/pull/384\n* rtp: add function to calc sequence number diff by @alfredh in https://github.com/baresip/re/pull/385\n* CI fixes by @sreimers in https://github.com/baresip/re/pull/387\n* trace: C11 mutex by @alfredh in https://github.com/baresip/re/pull/390\n* trace: init refactor by @sreimers in https://github.com/baresip/re/pull/391\n* jbuf: use C11 mutex by @alfredh in https://github.com/baresip/re/pull/392\n* av1: define and make AV1_AGGR_HDR_SIZE public by @alfredh in https://github.com/baresip/re/pull/393\n* main: add re_thread_check() for NON-RE thread calls by @sreimers in https://github.com/baresip/re/pull/389\n* cmake: add HAVE_SIGNAL on UNIX by @sreimers in https://github.com/baresip/re/pull/394\n* av1: add av1_obu_count() by @alfredh in https://github.com/baresip/re/pull/395\n* thread: add mtx_alloc by @sreimers in https://github.com/baresip/re/pull/396\n* rtp: C11 mutex by @alfredh in https://github.com/baresip/re/pull/397\n* lock: remove deprecated module by @alfredh in https://github.com/baresip/re/pull/398\n* Added sippreg_unregister API function by @juha-h in https://github.com/baresip/re/pull/400\n* av1 work by @alfredh in https://github.com/baresip/re/pull/402\n* rtp: add rtp_is_rtcp_packet() by @alfredh in https://github.com/baresip/re/pull/405\n* Fix mutex alloc destroy by @sreimers in https://github.com/baresip/re/pull/406\n* av1: minor fixes and doxygen comments by @alfredh in https://github.com/baresip/re/pull/407\n* rtp: Add support for RFC5104 PSFB FIR by @Lastique in https://github.com/baresip/re/pull/408\n* jbuf: Add drain method by @Lastique in https://github.com/baresip/re/pull/409\n* uag: add timestamps to SIP trace by @cspiel1 in https://github.com/baresip/re/pull/412\n* fmt/fmt_timestamp: some cleanup by @sreimers in https://github.com/baresip/re/pull/413\n* main: refactor libre_init and re_global handling by @sreimers in https://github.com/baresip/re/pull/404\n* main: Add support for external threads attaching/detaching re context by @Lastique in https://github.com/baresip/re/pull/414\n* mem: Fix formatting for nrefs and size. by @Lastique in https://github.com/baresip/re/pull/415\n\n---\n\n## [v2.4.0] - 2022-06-01\n\n## What's Changed\n* ci: test centos -> fedora by @alfredh in https://github.com/baresip/re/pull/340\n* Tls bio opaque by @alfredh in https://github.com/baresip/re/pull/341\n* main: remove usage of crypto_set_id_callback() by @alfredh in https://github.com/baresip/re/pull/342\n* jbuf: in adaptive mode do not manipulate min buffer size by @cspiel1 in https://github.com/baresip/re/pull/343\n* av1 obu by @alfredh in https://github.com/baresip/re/pull/345\n* jbuf: improve adaptive mode by @cspiel1 in https://github.com/baresip/re/pull/344\n* av1 packetizer by @alfredh in https://github.com/baresip/re/pull/346\n* av1: depacketizer by @alfredh in https://github.com/baresip/re/pull/347\n* h265: move from rem to re by @alfredh in https://github.com/baresip/re/pull/348\n* jbuf: avoid reducing of wish size too early by @cspiel1 in https://github.com/baresip/re/pull/349\n* ci/build: add ubuntu 22.04 (beta) by @sreimers in https://github.com/baresip/re/pull/351\n* h264: move from rem to re by @alfredh in https://github.com/baresip/re/pull/350\n* add C11 thread, mutex and condition API by @sreimers in https://github.com/baresip/re/pull/249\n* thread: use pthread as default fallback by @sreimers in https://github.com/baresip/re/pull/354\n* mem: use new C11 mutex locking by @sreimers in https://github.com/baresip/re/pull/352\n* dbg: use C11 thread mutex by @sreimers in https://github.com/baresip/re/pull/356\n* thread: add thread-local storage functions by @sreimers in https://github.com/baresip/re/pull/355\n* main/openssl: cleanup by @sreimers in https://github.com/baresip/re/pull/358\n* cmake: sort warning flags by @alfredh in https://github.com/baresip/re/pull/359\n* doxygen: update comments by @alfredh in https://github.com/baresip/re/pull/360\n* main: use C11 thread mutex by @sreimers in https://github.com/baresip/re/pull/357\n* make: disable warning flag -Wdeclaration-after-statement by @alfredh in https://github.com/baresip/re/pull/363\n* cleanup pthread by @sreimers in https://github.com/baresip/re/pull/362\n* update doxygen comments by @alfredh in https://github.com/baresip/re/pull/366\n* ci/coverage: downgrade gcovr by @sreimers in https://github.com/baresip/re/pull/365\n* tls: print openssl error queue if accept failed by @alfredh in https://github.com/baresip/re/pull/367\n* main: fd_setsize -1 for RLIMIT_NOFILE value by @sreimers in https://github.com/baresip/re/pull/368\n* jbuf: flush on RTP timeout by @cspiel1 in https://github.com/baresip/re/pull/370\n* thread: add mtx_destroy by @sreimers in https://github.com/baresip/re/pull/371\n* dns: add query cache by @sreimers in https://github.com/baresip/re/pull/369\n* mem,btrace: fix struct alignment by @sreimers in https://github.com/baresip/re/pull/372\n* av1: change start flag to continuation flag (inverse) by @alfredh in https://github.com/baresip/re/pull/375\n* tmr: add tmr_start_dbg by @sreimers in https://github.com/baresip/re/pull/373\n* ice: rename to local pref by @alfredh in https://github.com/baresip/re/pull/376\n* tls: Switch from EVP_sha1() to EVP_sha256() when using it for X509_sign() by @robert-scheck in https://github.com/baresip/re/pull/377\n\n---\n\n## [v2.3.0] - 2022-05-01\n\n* cmake: use static build as default target (improves subdirectory usage) by @sreimers in https://github.com/baresip/re/pull/311\n* jbuf: fix RELEASE build with DEBUG_LEVEL 6 by @cspiel1 in https://github.com/baresip/re/pull/313\n* fmt/pl: use unsigned type before negation by @sreimers in https://github.com/baresip/re/pull/312\n* fmt/pl: rewrite negative handling (avoid undefined behavior) by @sreimers in https://github.com/baresip/re/pull/314\n* http/request: fix possbile null pointer dereference by @sreimers in https://github.com/baresip/re/pull/316\n* sdp: check sdp_bandwidth lower bound by @sreimers in https://github.com/baresip/re/pull/317\n* main: use re_sock_t by @sreimers in https://github.com/baresip/re/pull/315\n* ccheck: check all CMakeLists.txt files by @sreimers in https://github.com/baresip/re/pull/320\n* list: O(1) sorted insert if we expect append in most cases by @cspiel1 in https://github.com/baresip/re/pull/318\n* add pcp protocol by @alfredh in https://github.com/baresip/re/pull/321\n* cmake: define RELEASE for release builds by @alfredh in https://github.com/baresip/re/pull/323\n* Mem lock win32 by @alfredh in https://github.com/baresip/re/pull/324\n* pcp: fix win32 warning by @alfredh in https://github.com/baresip/re/pull/325\n* ci/msvc: treat all compiler warnings as errors by @sreimers in https://github.com/baresip/re/pull/326\n* cmake: add MSVC /W3 compile option by @sreimers in https://github.com/baresip/re/pull/327\n* cmake: add FreeBSD and OpenBSD by @sreimers in https://github.com/baresip/re/pull/329\n* md5: remove fallback implementation by @sreimers in https://github.com/baresip/re/pull/328\n* cmake: add runtime and development install components by @sreimers in https://github.com/baresip/re/pull/330\n* mem: remove low/high block size stats by @alfredh in https://github.com/baresip/re/pull/331\n* mem: add error about missing locking by @alfredh in https://github.com/baresip/re/pull/332\n* set TCP source port in Via and Contact header by @cspiel1 in https://github.com/baresip/re/pull/334\n* remove sys_rel_get and epoll_check by @alfredh in https://github.com/baresip/re/pull/335\n* support tls session reuse   by @fAuernigg in https://github.com/baresip/re/pull/333\n* rand: init only needed for libc rand by @alfredh in https://github.com/baresip/re/pull/336\n* tls: fix crash in debug warn msg by @fAuernigg in https://github.com/baresip/re/pull/337\n* mem: init g_memLock directly by @alfredh in https://github.com/baresip/re/pull/339\n* prepare for version 2.3.0 by @alfredh in https://github.com/baresip/re/pull/338\n\n---\n\n## [v2.2.2] - 2022-04-09\n\n* sha256: add wrapper by @alfredh in https://github.com/baresip/re/pull/306\n* workflow: upgrade to openssl 3.0.2 by @alfredh in https://github.com/baresip/re/pull/305\n* aubuf adaptive jitter buffer by @cspiel1 in https://github.com/baresip/re/pull/303\n* Improve WIN32 UDP socket handling by @sreimers in https://github.com/baresip/re/pull/296\n* tcp: remove tcp_conn_fd by @alfredh in https://github.com/baresip/re/pull/308\n* tcp: improve win32 socket and error handling by @sreimers in https://github.com/baresip/re/pull/309\n\n---\n\n## [v2.2.1] - 2022-04-01\n\n* cmake: add packaging by @sreimers in https://github.com/baresip/re/pull/299\n* sha: add sha 256 and 512 digest length OpenSSL compats by @sreimers in https://github.com/baresip/re/pull/300\n* main: use Winsock2.h by @sreimers in https://github.com/baresip/re/pull/302\n* cmake: for Android platform dont enable ifaddrs/getifaddrs by @alfredh in https://github.com/baresip/re/pull/304\n* sa/sa_is_loopback: check full IPv4 loopback range (127.0.0.0/8) by @sreimers in https://github.com/baresip/re/pull/301\n\n---\n\n## [v2.2.0] - 2022-03-28\n\n* tls: fix coverity defect by @alfredh in https://github.com/baresip/re/pull/270\n* http/client: read_file check ftell return value by @sreimers in https://github.com/baresip/re/pull/272\n* udp: fix coverity defect by @alfredh in https://github.com/baresip/re/pull/271\n* cmake: add detection of HAVE_ARC4RANDOM by @alfredh in https://github.com/baresip/re/pull/269\n* Fix coverity issues by @sreimers in https://github.com/baresip/re/pull/273\n* Support adding CRLs by @fAuernigg in https://github.com/baresip/re/pull/274\n* json/decode: fix possible out of bound access, if code changes by @sreimers in https://github.com/baresip/re/pull/275\n* tls/tls_add_crlpem: use const by @sreimers in https://github.com/baresip/re/pull/276\n* udp: fix coverity defect by @alfredh in https://github.com/baresip/re/pull/279\n* dns: fix Coverity Defect by @alfredh in https://github.com/baresip/re/pull/278\n* tls: use const pointer for tls_add_capem() by @cspiel1 in https://github.com/baresip/re/pull/277\n* srtp/srtcp: add sanity check for rtcp->tag_len by @sreimers in https://github.com/baresip/re/pull/280\n* shim: new module from rew by @alfredh in https://github.com/baresip/re/pull/282\n* Trice module by @alfredh in https://github.com/baresip/re/pull/283\n* retest trice by @alfredh in https://github.com/baresip/re/pull/284\n* Add try_into conversion helper and drop gcc 4.8 support by @sreimers in https://github.com/baresip/re/pull/286\n* rtp: fix signed/unsigned warning on WIN32 by @alfredh in https://github.com/baresip/re/pull/287\n* fix build error on openbsd arm64 (raspberry pi) by @jimying in https://github.com/baresip/re/pull/290\n* cmake: disable C extensions (like make) by @sreimers in https://github.com/baresip/re/pull/292\n* fmt: add bool decode from struct pl by @cspiel1 in https://github.com/baresip/re/pull/293\n* sdp: a utility function for decoding SDP direction by @cspiel1 in https://github.com/baresip/re/pull/294\n* sa/sa_ntop: check inet_ntop() return value by @sreimers in https://github.com/baresip/re/pull/295\n* sa_pton: use sa_addrinfo for interface suffix by @alfredh in https://github.com/baresip/re/pull/297\n\n### New Contributors\n* @jimying made their first contribution in https://github.com/baresip/re/pull/290\n\n---\n\n## [v2.1.1] - 2022-03-12\n\n### Fixes\n\n* mk: fix ABI versioning [#268](https://github.com/baresip/re/issues/268)\n\n---\n\n## [v2.1.0] - 2022-03-11\n\n### What's Changed\n* Tls sipcert per acc by @cHuberCoffee in https://github.com/baresip/re/pull/96\n* ToS for video and sip by @cspiel1 in https://github.com/baresip/re/pull/98\n* sdp: in media_decode() reset rdir if port is zero by @cspiel1 in https://github.com/baresip/re/pull/99\n* mk/re: add variable length array (-Wvla) compiler warning by @sreimers in https://github.com/baresip/re/pull/100\n* Macos openssl by @sreimers in https://github.com/baresip/re/pull/105\n* pkg-config version check by @sreimers in https://github.com/baresip/re/pull/107\n* sa: add setter and getter for scope id by @cspiel1 in https://github.com/baresip/re/pull/108\n* net: in net_dst_source_addr_get() make parameter dst const by @cspiel1 in https://github.com/baresip/re/pull/109\n* Avoid 'ISO C90 forbids mixed declarations and code' warnings by @juha-h in https://github.com/baresip/re/pull/112\n* SIP redirect callbackfunction by @cHuberCoffee in https://github.com/baresip/re/pull/111\n* add secure websocket tls context by @sreimers in https://github.com/baresip/re/pull/113\n* fmt: add string to bool function by @cspiel1 in https://github.com/baresip/re/pull/115\n* fix clang analyze warnings by @sreimers in https://github.com/baresip/re/pull/114\n* fmt: support different separators for parameter parsing by @cspiel1 in https://github.com/baresip/re/pull/117\n* Refactor inet_ntop and inet_pton by @sreimers in https://github.com/baresip/re/pull/118\n* add essential fields check by @I-mpossible in https://github.com/baresip/re/pull/119\n* sa: add support for interface suffix for IPv6ll by @cspiel1 in https://github.com/baresip/re/pull/116\n* net: fix net_if_getname IPv6 support by @sreimers in https://github.com/baresip/re/pull/120\n* udp: add udp_recv_helper by @alfredh in https://github.com/baresip/re/pull/122\n* sa: fix build for old systems by @cspiel1 in https://github.com/baresip/re/pull/121\n* sa/addrinfo: fix openbsd (drop AI_V4MAPPED flag) by @sreimers in https://github.com/baresip/re/pull/125\n* ci/codeql: add scan-build by @sreimers in https://github.com/baresip/re/pull/128\n* Fixed debian changelog version by @juha-h in https://github.com/baresip/re/pull/129\n* IPv6 link local support by @cspiel1 in https://github.com/baresip/re/pull/106\n* sip: add fallback transport for transp_find() by @cspiel1 in https://github.com/baresip/re/pull/132\n* SIP default protocol by @cspiel1 in https://github.com/baresip/re/pull/131\n* remove orphaned files by @viordash in https://github.com/baresip/re/pull/136\n* outgoing calls early callid by @cspiel1 in https://github.com/baresip/re/pull/135\n* sip: fix possible \"???\" dns srv queries by skipping lines without srvid by @cHuberCoffee in https://github.com/baresip/re/pull/133\n* odict: hide struct odict_entry by @sreimers in https://github.com/baresip/re/pull/130\n* tls: add keylogger callback function by @cHuberCoffee in https://github.com/baresip/re/pull/140\n* http/client: support other auth token types besides bearer by @fAuernigg in https://github.com/baresip/re/pull/142\n* tls: fix client certificate replacement by @cHuberCoffee in https://github.com/baresip/re/pull/145\n* http/client: support dns ipv6 by @fAuernigg in https://github.com/baresip/re/pull/141\n* rtp: add payload-type helper by @alfredh in https://github.com/baresip/re/pull/148\n* sip: check consistency between CSeq method and that of request line by @I-mpossible in https://github.com/baresip/re/pull/146\n* Fix win32 by @viordash in https://github.com/baresip/re/pull/149\n* fix warnings from PVS-Studio C++ static analyzer by @viordash in https://github.com/baresip/re/pull/150\n* RTP inbound telephone events should not lead to packet loss by @cspiel1 in https://github.com/baresip/re/pull/151\n* support inet6 by default in Win32 project by @viordash in https://github.com/baresip/re/pull/154\n* sdp: differentiate between media line disabled or rejected by @cHuberCoffee in https://github.com/baresip/re/pull/134\n* move network check to module by @cspiel1 in https://github.com/baresip/re/pull/152\n* odict: move odict_compare from retest to re by @fAuernigg in https://github.com/baresip/re/pull/153\n* sip: reuse transport protocol of first request in dialog (#143) by @cspiel1 in https://github.com/baresip/re/pull/144\n* json: fix parsing json containing only single value by @fAuernigg in https://github.com/baresip/re/pull/155\n* ice: fix checklist by @alfredh in https://github.com/baresip/re/pull/156\n* mk: add compile_commands.json (clang only) by @sreimers in https://github.com/baresip/re/pull/157\n* sdp: debug print session and media direction by @cspiel1 in https://github.com/baresip/re/pull/158\n* add btrace module (linux/unix only) by @sreimers in https://github.com/baresip/re/pull/160\n* mk: add CC_TEST header check by @sreimers in https://github.com/baresip/re/pull/162\n* init dst address by @cspiel1 in https://github.com/baresip/re/pull/164\n* ice: check if candpair exist before adding by @alfredh in https://github.com/baresip/re/pull/165\n* mk: add CC_TEST cache by @sreimers in https://github.com/baresip/re/pull/163\n* btrace: use HAVE_EXECINFO by @sreimers in https://github.com/baresip/re/pull/166\n* Coverity by @sreimers in https://github.com/baresip/re/pull/170\n* icem: remove dead code (found by coverity 240639) by @sreimers in https://github.com/baresip/re/pull/171\n* hash: switch to simpler \"fast algorithm\" by @ydroneaud in https://github.com/baresip/re/pull/173\n* dns: fix dnsc_alloc with IPv6 disabled by @sreimers in https://github.com/baresip/re/pull/174\n* mk: deprecate HAVE_INET6 by @sreimers in https://github.com/baresip/re/pull/175\n* Fix for btrace print for memory leaks by @cspiel1 in https://github.com/baresip/re/pull/177\n* set sdp laddr to SIP src address by @cspiel1 in https://github.com/baresip/re/pull/172\n* sdp: include all media formats in SDP offer by @cHuberCoffee in https://github.com/baresip/re/pull/176\n* ci: add centos 7 build test by @sreimers in https://github.com/baresip/re/pull/179\n* sip: move sip_auth_encode to public api for easier testing by @sreimers in https://github.com/baresip/re/pull/181\n* sipsess: do not call desc handler on shutdown by @cspiel1 in https://github.com/baresip/re/pull/182\n* stream flush rtp socket by @cspiel1 in https://github.com/baresip/re/pull/185\n* ci: fix macos openssl build by @sreimers in https://github.com/baresip/re/pull/188\n* http: HTTP Host header conform to RFC for IPv6 addresses by @cspiel1 in https://github.com/baresip/re/pull/189\n* Increased debian compatibility level from 9 to 10 by @juha-h in https://github.com/baresip/re/pull/192\n* mk: move darwin dns LFLAGS to re.mk (fixes static builds) by @sreimers in https://github.com/baresip/re/pull/193\n* build infrastructure: silent and verbose modes by @abrodkin in https://github.com/baresip/re/pull/194\n* mk: use posix regex for sed CC major version detection by @sreimers in https://github.com/baresip/re/pull/195\n* dns: fix parse_resolv_conf for OpenBSD by @sreimers in https://github.com/baresip/re/pull/196\n* sip: add optional TCP source port by @cspiel1 in https://github.com/baresip/re/pull/198\n* ci: add mingw build and test by @sreimers in https://github.com/baresip/re/pull/199\n* net: remove net_hostaddr by @sreimers in https://github.com/baresip/re/pull/200\n* ci/centos7: add openssl by @sreimers in https://github.com/baresip/re/pull/203\n* hmac: use HMAC() api (fixes OpenSSL 3.0 deprecations) by @sreimers in https://github.com/baresip/re/pull/202\n* md5: use EVP_Digest for newer openssl versions by @sreimers in https://github.com/baresip/re/pull/204\n* sha: add new sha1() api by @sreimers in https://github.com/baresip/re/pull/205\n* OpenSSL 3.0 by @sreimers in https://github.com/baresip/re/pull/206\n* udp: add win32 qos support by @sreimers in https://github.com/baresip/re/pull/186\n* ci/mingw: fix dependency checkout by @sreimers in https://github.com/baresip/re/pull/207\n* ice: remove ice_mode by @alfredh in https://github.com/baresip/re/pull/147\n* Codeql security by @sreimers in https://github.com/baresip/re/pull/208\n* aubuf insert auframes sorted by @cspiel1 in https://github.com/baresip/re/pull/209\n* ci: add valgrind by @sreimers in https://github.com/baresip/re/pull/214\n* tls: remove code for openssl 0.9.5 by @alfredh in https://github.com/baresip/re/pull/215\n* ice: remove unused file by @alfredh in https://github.com/baresip/re/pull/217\n* main: remove obsolete OPENWRT epoll check by @alfredh in https://github.com/baresip/re/pull/218\n* dns,http,sa: fix HAVE_INET6 off warnings by @sreimers in https://github.com/baresip/re/pull/219\n* preliminary support for cmake by @alfredh in https://github.com/baresip/re/pull/220\n* make,cmake: set SOVERSION to major version by @sreimers in https://github.com/baresip/re/pull/221\n* mk: remove MSVC project files, use cmake instead by @alfredh in https://github.com/baresip/re/pull/223\n* natbd: remove module (deprecated) by @alfredh in https://github.com/baresip/re/pull/225\n* sha: remove backup implementation by @alfredh in https://github.com/baresip/re/pull/224\n* sha,hmac: use Apple CommonCrypto if defined by @alfredh in https://github.com/baresip/re/pull/226\n* stun: add stun_generate_tid by @alfredh in https://github.com/baresip/re/pull/227\n* add cmakelint by @sreimers in https://github.com/baresip/re/pull/228\n* Cmake version by @alfredh in https://github.com/baresip/re/pull/229\n* cmake: add option to enable/disable rtmp module by @alfredh in https://github.com/baresip/re/pull/230\n* lock: use rwlock by default by @sreimers in https://github.com/baresip/re/pull/232\n* cmake: fixes for MSVC 16 by @alfredh in https://github.com/baresip/re/pull/233\n* json: fix win32 warnings by @alfredh in https://github.com/baresip/re/pull/234\n* ci: add cmake build by @sreimers in https://github.com/baresip/re/pull/222\n* mqueue: fix win32 warnings by @alfredh in https://github.com/baresip/re/pull/235\n* tcp: fix win32 warnings by @alfredh in https://github.com/baresip/re/pull/236\n* cmake: fix target_link_libraries for win32 by @alfredh in https://github.com/baresip/re/pull/238\n* stun: fix win32 warnings by @alfredh in https://github.com/baresip/re/pull/237\n* udp: fix win32 warnings by @alfredh in https://github.com/baresip/re/pull/239\n* tls: fix win32 warnings by @alfredh in https://github.com/baresip/re/pull/241\n* remove HAVE_INTTYPES_H by @alfredh in https://github.com/baresip/re/pull/231\n* udp: fix win32 warnings by @alfredh in https://github.com/baresip/re/pull/242\n* cmake: minor fixes by @alfredh in https://github.com/baresip/re/pull/244\n* cmake: fix MSVC ninja by @sreimers in https://github.com/baresip/re/pull/243\n* tcp: fix win32 warnings by @alfredh in https://github.com/baresip/re/pull/245\n* udp: fix win32 msvc warnings by @sreimers in https://github.com/baresip/re/pull/246\n* rtmp: fix win32 warning by @sreimers in https://github.com/baresip/re/pull/247\n* bfcp: fix win32 warning by @sreimers in https://github.com/baresip/re/pull/248\n* tls: fix libressl 3.5 by @sreimers in https://github.com/baresip/re/pull/250\n* fix coverity scan warnings by @sreimers in https://github.com/baresip/re/pull/251\n* Allow hanging up call that has not been ACKed yet by @juha-h in https://github.com/baresip/re/pull/252\n* mk,cmake: add backtrace support and fix linking on OpenBSD by @sreimers in https://github.com/baresip/re/pull/254\n* github: add CMake and Windows workflow by @alfredh in https://github.com/baresip/re/pull/255\n* Windows (VS 2022/Ninja) by @sreimers in https://github.com/baresip/re/pull/257\n* cmake: fixes for Android by @alfredh in https://github.com/baresip/re/pull/258\n* tmr: reuse tmr_jiffies_usec by @alfredh in https://github.com/baresip/re/pull/259\n* trace: use gettid as thread_id on linux by @sreimers in https://github.com/baresip/re/pull/213\n* tmr: use CLOCK_MONOTONIC_RAW if defined by @alfredh in https://github.com/baresip/re/pull/260\n* add atomic support by @sreimers in https://github.com/baresip/re/pull/261\n* Sonarcloud by @sreimers in https://github.com/baresip/re/pull/262\n* sip: fix gcc 6.3.0 warning for logical expression (#256) by @cspiel1 in https://github.com/baresip/re/pull/263\n* add transport-cc rtcp feedback support by @fippo in https://github.com/baresip/re/pull/264\n\n### New Contributors\n* @I-mpossible made their first contribution in https://github.com/baresip/re/pull/119\n* @viordash made their first contribution in https://github.com/baresip/re/pull/136\n* @ydroneaud made their first contribution in https://github.com/baresip/re/pull/173\n* @abrodkin made their first contribution in https://github.com/baresip/re/pull/194\n\n---\n\n## [v2.0.1] - 2021-04-22\n\n### Fixed\n\n- tmr: fix FreeBSD and OpenBSD [#97]\n- mk: fix clang analyze CFLAGS\n\n### Changed\n\n- tls: different return values for tls_get_ca_chain_field() [#94]\n\n---\n\n## [v2.0.0] - 2021-04-10\n\n### Added\n\n- .gitignore: add ctags and vim swp files to gitignore [#31]\n- tls: add tls_add_capem() for adding CA cert as PEM string [#33]\n- httpauth: Add digest support for http clients [#33]\n- httpauth: Add basic authentication for HTTP clients [#33]\n- dns: add set function for DNS config [#33]\n- http/client: support IPv6 [#33]\n- http/client: use const parameter for set laddr(6) functions [#33]\n- http/client: add set function for timeout [#33]\n- http/client: add http_client_add_capem() [#33]\n- http/client: add set functions for client certificate and private key [#33]\n- http: add HTTP request connection with authorization [#33]\n- http: setting of timeouts for http client [#35]\n- http: set default path for http requests [#35]\n- tls: set selfsigned Elliptic Curve (EC) function [#17]\n- tls: extend server verification by host name check (SNI) [#45]\n- jbuf: adapative jitter buffer [#41]\n- tmr: add tmr_jiffies_usec() - get accurate microseconds [#52]\n- fmt: add pl_i32() that converts pl to int32_t [#60]\n- fmt: add pl_i64() that converts pl to int64_t [#60]\n- mk/re: add C11 and Atomic detection [#61]\n- ci: add abi check [#39]\n- trace: add re_trace api [#48]\n- Add function that resets the timeout timer for a connection of the HTTP server. [#88]\n- add error trace helpers [#87]\n- sip/auth: add algorithm=MD5 [#86]\n- sys: filesystem isdir function\n- tls: use ENOENT in tls_add_cafile_path as error code\n- tls: more generic function to set cafile and capath\n- mk: add .so name versioning, resolves #32\n- mk/re: add clang shorten-64-to-32 warning\n- mk/re: document new library/header prioritised order with custom SYSROOT\n- mk/re: info double colon rule (#64) [#64]\n- udp: Add function udp_open for socket without bind\n- rtp: Add rtp_open which creates an RTP object only for sending. [#77]\n- sip: add decode function for SIP transport\n- sip: SIP/TLS Server Name Indication (#67) [#67]\n- transp: add flag to disable SIP TLS server verification [#76]\n\n### Removed\n\n- openssl: remove obsolete function tls_set_hostname() [#33]\n- mk/re: remove gcc 2.x/3.x support [#58]\n- ci: drop ubuntu 16.04 support - end of life\n\n### Changed\n\n- http/client: cleanup doxygen [#33]\n- http/client: use host of http_req for the host name validation [#37]\n- main: disable MAIN_DEBUG, TMR_DEBUG and increase MAX_BLOCKING to 500ms [#43]\n- sipreg: dont't force digest challenge for register [#49]\n- mk/re: do not override LIBRE_INC, LIBRE_SO and LIBRE_PATH [#62]\n- readme: update supported systems and add tiers [#81]\n- tls: use ENOTDIR in tls_add_cafile_path if capath is not a dir [#84]\n- tls: check capath is directory\n- net: get default source addr from udp local test socket [#66]\n- Update chklist.c [#70]\n- Update icesdp.c [#69]\n- mk: cross build changes (#63) [#63]\n- sip: use sip_transp_decode() [#71]\n- tls: tls_get_issuer/subject return the info of the first loaded ca [#80]\n\n### Fixed\n\n- dns/client: fix HAVE_INET6 and win32/vcxproj: updates [#28]\n- http: fix segfault in response.c [#35]\n- http/request: parameter NULL check for http_reqconn_send() [#37]\n- http/client: fix conn_idle [#46]\n- http/httpreq: mem leak fix [#47]\n- sip/request: fix msg->scode null pointer dereference\n- rtmp/conn: initialize err\n- mk/re: fix LIBRE_SO static detection\n- dns/res: Properly process IPV4 and IPV6 addresses (DARWIN) [#56]\n- sip/keepalive: fix codeql cpp/integer-multiplication-cast-to-long\n- fmt/time: fix codeql gmtime warning\n- mk/re: fix gcc 4.x and newer compiler warnings\n- sys: add _BSD_SOURCE 1 for compatibility reasons [#92]\n- fix weak self-signed certificates [#68]\n- net/tls: fixing shorten-64-to-32 warnings [#65]\n- http: add missing newline to warning [#78]\n- http: fix file read for client certificates\n- mk/re: do not override LIBRE_INC, LIBRE_SO and LIBRE_PATH [#62]\n- tls: safety NULL pointer check in tls_add_ca() [#79]\n\n### Contributors (many thanks)\n\n- [sreimers](https://github.com/sreimers)\n- [cHuberCoffee](https://github.com/cHuberCoffee)\n- [RobertMi21](https://github.com/RobertMi21)\n- [cspiel1](https://github.com/cspiel1)\n- [alfredh](https://github.com/alfredh)\n- [fippo](https://github.com/fippo)\n- [jurjen-van-dijk](https://github.com/jurjen-van-dijk)\n- [rolizo](https://github.com/rolizo)\n\n\n## [v1.1.0] - 2020-10-04\n\n### Added\n\n- tls: functions to get the certificate issuer and subject [#18]\n- uri: Added path field to struct uri and its decode to uri_decode [#22]\n- tcp: add tcp_connect_bind [#24]\n- http: support bind to laddr in http_request [#24]\n- sipreg: support Cisco REGISTER keep-alives [#19]\n- sip: websocket support [#26]\n\n### Fixed\n\n- tls/openssl: fix X509_NAME win32/wincrypt.h conflict\n- dns: listen on IPv4 and IPv6 socket [#27]\n- main: fix/optimize windows file descriptors [#25]\n\n### Contributors (many thanks)\n\n- Alfred E. Heggestad\n- Christian Spielberger\n- Christoph Huber\n- Franz Auernigg\n- Juha Heinanen\n- johnjuuljensen\n- Sebastian Reimers\n\n\n## [v1.0.0] - 2020-09-08\n\n### Added\n\n- sip: add trace\n- sdp: sdp_media_disabled API function [#2]\n- tls: add tls_set_selfsigned_rsa [#6]\n- tls: add functions to verify server cert, purpose and hostname [#10]\n- http: client should set SNI [#10]\n- http: client should use tls functions to verify server certs, purpose\n  and hostname [#10]\n- sipreg: add proxy expires field and get function [#13]\n- sipreg: make re-register interval configurable [#13]\n\n### Changed\n\n- debian: Automatic cleanup after building debian package\n\n### Fixed\n\n- Set SDK path (SYSROOT) using xcrun (fix building on macOS 10.14)\n- tcp: close socket on windows if connection is aborted or reset [#1]\n- rtmp: Fix URL path parsing (creytiv#245)\n- ice: various fixes [baresip/baresip#925]\n- openssl/tls: replace deprecated openssl 1.1.0 functions [#5]\n\n### Contributors (many thanks)\n\n- Alfred E. Heggestad\n- Christian Spielberger\n- Christoph Huber\n- Franz Auernigg\n- juha-h\n- Juha Heinanen\n- Richard Aas\n- Sebastian Reimers\n\n[#97]: https://github.com/baresip/re/pull/97\n[#94]: https://github.com/baresip/re/pull/94\n[#81]: https://github.com/baresip/re/pull/81\n[#48]: https://github.com/baresip/re/pull/48\n[#92]: https://github.com/baresip/re/pull/92\n[#88]: https://github.com/baresip/re/pull/88\n[#87]: https://github.com/baresip/re/pull/87\n[#86]: https://github.com/baresip/re/pull/86\n[#84]: https://github.com/baresip/re/pull/84\n[#83]: https://github.com/baresip/re/pull/83\n[#82]: https://github.com/baresip/re/pull/82\n[#80]: https://github.com/baresip/re/pull/80\n[#79]: https://github.com/baresip/re/pull/79\n[#78]: https://github.com/baresip/re/pull/78\n[#77]: https://github.com/baresip/re/pull/77\n[#76]: https://github.com/baresip/re/pull/76\n[#39]: https://github.com/baresip/re/pull/39\n[#66]: https://github.com/baresip/re/pull/66\n[#74]: https://github.com/baresip/re/pull/74\n[#67]: https://github.com/baresip/re/pull/67\n[#71]: https://github.com/baresip/re/pull/71\n[#70]: https://github.com/baresip/re/pull/70\n[#69]: https://github.com/baresip/re/pull/69\n[#68]: https://github.com/baresip/re/pull/68\n[#65]: https://github.com/baresip/re/pull/65\n[#63]: https://github.com/baresip/re/pull/63\n[#64]: https://github.com/baresip/re/pull/64\n[#62]: https://github.com/baresip/re/pull/62\n[#61]: https://github.com/baresip/re/pull/61\n[#60]: https://github.com/baresip/re/pull/60\n[#58]: https://github.com/baresip/re/pull/58\n[#56]: https://github.com/baresip/re/pull/56\n[#52]: https://github.com/baresip/re/pull/52\n[#49]: https://github.com/baresip/re/pull/49\n[#47]: https://github.com/baresip/re/pull/47\n[#46]: https://github.com/baresip/re/pull/46\n[#45]: https://github.com/baresip/re/pull/45\n[#43]: https://github.com/baresip/re/pull/43\n[#41]: https://github.com/baresip/re/pull/41\n[#37]: https://github.com/baresip/re/pull/37\n[#35]: https://github.com/baresip/re/pull/35\n[#33]: https://github.com/baresip/re/pull/33\n[#31]: https://github.com/baresip/re/pull/31\n[#28]: https://github.com/baresip/re/pull/28\n[#27]: https://github.com/baresip/re/pull/27\n[#26]: https://github.com/baresip/re/pull/26\n[#25]: https://github.com/baresip/re/pull/25\n[#19]: https://github.com/baresip/re/pull/19\n[#24]: https://github.com/baresip/re/pull/24\n[#22]: https://github.com/baresip/re/pull/22\n[#18]: https://github.com/baresip/re/pull/18\n[#17]: https://github.com/baresip/re/pull/17\n[#13]: https://github.com/baresip/re/pull/13\n[#10]: https://github.com/baresip/re/pull/10\n[#6]: https://github.com/baresip/re/pull/6\n[#5]: https://github.com/baresip/re/pull/5\n[#2]: https://github.com/baresip/re/pull/2\n[#1]: https://github.com/baresip/re/pull/1\n\n[Unreleased]: https://github.com/baresip/re/compare/v2.7.0...HEAD\n[v2.7.0]: https://github.com/baresip/re/compare/v2.6.0...v2.7.0\n[v2.6.0]: https://github.com/baresip/re/compare/v2.5.0...v2.6.0\n[v2.5.0]: https://github.com/baresip/re/compare/v2.4.0...v2.5.0\n[v2.4.0]: https://github.com/baresip/re/compare/v2.3.0...v2.4.0\n[v2.3.0]: https://github.com/baresip/re/compare/v2.2.2...v2.3.0\n[v2.2.2]: https://github.com/baresip/re/compare/v2.2.1...v2.2.2\n[v2.2.1]: https://github.com/baresip/re/compare/v2.2.0...v2.2.1\n[v2.2.0]: https://github.com/baresip/re/compare/v2.1.1...v2.2.0\n[v2.1.1]: https://github.com/baresip/re/compare/v2.1.0...v2.1.1\n[v2.1.0]: https://github.com/baresip/re/compare/v2.0.1...v2.1.0\n[v2.0.1]: https://github.com/baresip/re/compare/v2.0.0...v2.0.1\n[v2.0.0]: https://github.com/baresip/re/compare/v1.1.0...v2.0.0\n[v1.1.0]: https://github.com/baresip/re/compare/v1.0.0...v1.1.0\n[v1.0.0]: https://github.com/baresip/re/compare/v0.6.1...v1.0.0\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "#\n# CMakeLists.txt\n#\n# Copyright (C) 2010 - 2025 Alfred E. Heggestad\n# Copyright (C) 2022 - 2025 Sebastian Reimers\n# Copyright (C) 2023 - 2025 Christian Spielberger\n#\n\n##############################################################################\n#\n# Versioning\n#\n\ncmake_minimum_required(VERSION 3.18...4.0)\n\nproject(re\n  VERSION 4.8.0\n  LANGUAGES C\n  HOMEPAGE_URL https://github.com/baresip/re\n  DESCRIPTION \"Generic library for real-time communications\"\n)\n\nset(PROJECT_SOVERSION 42) # bump if ABI breaks\n\n# Pre-release identifier, comment out on a release\n# Increment for breaking changes (dev2, dev3...)\n#set(PROJECT_VERSION_PRE dev)\n\nif(PROJECT_VERSION_PRE)\n  set(PROJECT_VERSION_FULL ${PROJECT_VERSION}-${PROJECT_VERSION_PRE})\nelse()\n  set(PROJECT_VERSION_FULL ${PROJECT_VERSION})\nendif()\n\nif(WIN32 AND NOT MINGW)\n  set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -std:c11\")\nendif()\n\nlist(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)\n\n##############################################################################\n#\n# Module/Package Includes\n#\ninclude(GNUInstallDirs)\ninclude(CheckCCompilerFlag)\n\n##############################################################################\n#\n# Options\n#\n\noption(USE_REM \"Enable Librem\" ON)\noption(USE_BFCP \"Enable BFCP\" ON)\noption(USE_PCP \"Enable PCP\" ON)\noption(USE_RTMP \"Enable RTMP\" ON)\noption(USE_SIP \"Enable SIP\" ON)\noption(LIBRE_BUILD_SHARED \"Build shared library\" ON)\noption(LIBRE_BUILD_STATIC \"Build static library\" ON)\n\nset(CMAKE_EXPORT_COMPILE_COMMANDS ON)\n\nif(MSVC)\n  add_compile_options(\"/W3\")\nelse()\n\n  set(c_flags\n    -pedantic\n    -Wall\n    -Wbad-function-cast\n    -Wcast-align\n    -Wextra\n    -Wmissing-declarations\n    -Wmissing-prototypes\n    -Wnested-externs\n    -Wno-strict-aliasing\n    -Wold-style-definition\n    -Wshadow\n    -Wstrict-prototypes\n    -Wuninitialized\n    -Wvla\n  )\n\n  add_compile_options(\n    \"$<$<COMPILE_LANGUAGE:C>:${c_flags}>\"\n  )\nendif()\n\nif(CMAKE_C_COMPILER_ID MATCHES \"Clang\")\n    add_compile_options(\n      -Wno-gnu-zero-variadic-macro-arguments\n      -Wno-c2x-extensions\n    )\n    add_compile_options(\"$<$<COMPILE_LANGUAGE:C>:-Wshorten-64-to-32>\")\nendif()\n\ncheck_c_compiler_flag(\"-Watomic-implicit-seq-cst\" COMPILER_SUPPORTS_WATOMIC)\nif(COMPILER_SUPPORTS_WATOMIC)\n  add_compile_options(\"$<$<COMPILE_LANGUAGE:C>:-Watomic-implicit-seq-cst>\")\nendif()\n\nif(CMAKE_C_COMPILER_ID MATCHES \"Clang\")\n  # Ensure struct mem is aligned (used as fat pointer)\n  set_source_files_properties(src/mem/mem.c PROPERTIES COMPILE_FLAGS -Wpadded)\nendif()\n\nset(re_DIR ${CMAKE_CURRENT_LIST_DIR}/cmake)\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/cmake/re-config.cmake\")\n\nlist(APPEND RE_DEFINITIONS\n  -DRE_VERSION=\"${PROJECT_VERSION_FULL}\"\n  -DVER_MAJOR=${PROJECT_VERSION_MAJOR}\n  -DVER_MINOR=${PROJECT_VERSION_MINOR}\n  -DVER_PATCH=${PROJECT_VERSION_PATCH}\n  -D_GNU_SOURCE\n)\n\nif(DEFINED TRACE_SSL)\n  list(APPEND RE_DEFINITIONS\n    -DTRACE_SSL=\"${TRACE_SSL}\")\nendif()\n\n##############################################################################\n#\n# Source/Header section\n#\n\nset(HEADERS\n  include/re.h\n  include/re_aes.h\n  include/re_async.h\n  include/re_atomic.h\n  include/re_av1.h\n  include/re_base64.h\n  include/re_bfcp.h\n  include/re_btrace.h\n  include/re_conf.h\n  include/re_convert.h\n  include/re_crc32.h\n  include/re_dbg.h\n  include/re_dd.h\n  include/re_dns.h\n  include/re_fmt.h\n  include/re_h264.h\n  include/re_h265.h\n  include/re_hash.h\n  include/re_hmac.h\n  include/re_http.h\n  include/re_httpauth.h\n  include/re_ice.h\n  include/re_json.h\n  include/re_list.h\n  include/re_main.h\n  include/re_mbuf.h\n  include/re_md5.h\n  include/re_mem.h\n  include/re_mod.h\n  include/re_mqueue.h\n  include/re_msg.h\n  include/re_net.h\n  include/re_odict.h\n  include/re_pcp.h\n  include/re_rtmp.h\n  include/re_rtp.h\n  include/re_rtpext.h\n  include/re_sa.h\n  include/re_sdp.h\n  include/re_sha.h\n  include/re_shim.h\n  include/re_sip.h\n  include/re_sipevent.h\n  include/re_sipreg.h\n  include/re_sipsess.h\n  include/re_srtp.h\n  include/re_stun.h\n  include/re_sys.h\n  include/re_tcp.h\n  include/re_telev.h\n  include/re_thread.h\n  include/re_tls.h\n  include/re_tmr.h\n  include/re_trace.h\n  include/re_trice.h\n  include/re_turn.h\n  include/re_types.h\n  include/re_udp.h\n  include/re_unixsock.h\n  include/re_uri.h\n  include/re_websock.h\n  include/rem_aac.h\n  include/rem_aubuf.h\n  include/rem_auconv.h\n  include/rem_audio.h\n  include/rem_aufile.h\n  include/rem_auframe.h\n  include/rem_au.h\n  include/rem_aulevel.h\n  include/rem_aumix.h\n  include/rem_auresamp.h\n  include/rem_autone.h\n  include/rem_avc.h\n  include/rem_dsp.h\n  include/rem_dtmf.h\n  include/rem_fir.h\n  include/rem_flv.h\n  include/rem_g711.h\n  include/rem_goertzel.h\n  include/rem.h\n  include/rem_vidconv.h\n  include/rem_video.h\n  include/rem_vid.h\n  include/rem_vidmix.h\n)\n\n\nset(SRCS\n\n  src/av1/depack.c\n  src/av1/obu.c\n  src/av1/pkt.c\n\n  src/async/async.c\n\n  src/base64/b64.c\n\n  src/btrace/btrace.c\n\n  src/conf/conf.c\n\n  src/dbg/dbg.c\n\n  src/dd/dd.c\n  src/dd/dd_enc.c\n  src/dd/putbit.c\n\n  src/dns/client.c\n  src/dns/cstr.c\n  src/dns/dname.c\n  src/dns/hdr.c\n  src/dns/ns.c\n  src/dns/rr.c\n  src/dns/rrlist.c\n\n  src/fmt/ch.c\n  src/fmt/hexdump.c\n  src/fmt/pl.c\n  src/fmt/print.c\n  src/fmt/prm.c\n  src/fmt/regex.c\n  src/fmt/str.c\n  src/fmt/str_error.c\n  src/fmt/text2pcap.c\n  src/fmt/time.c\n  src/fmt/unicode.c\n\n  src/h264/getbit.c\n  src/h264/nal.c\n  src/h264/sps.c\n\n  src/h265/nal.c\n\n  src/hash/func.c\n  src/hash/hash.c\n\n  src/hmac/hmac_sha1.c\n\n  src/http/auth.c\n  src/http/chunk.c\n  src/http/client.c\n  src/http/msg.c\n  src/http/request.c\n  src/http/server.c\n\n  src/httpauth/basic.c\n  src/httpauth/digest.c\n\n  src/ice/cand.c\n  src/ice/candpair.c\n  src/ice/chklist.c\n  src/ice/comp.c\n  src/ice/connchk.c\n  src/ice/icem.c\n  src/ice/icesdp.c\n  src/ice/icestr.c\n  src/ice/stunsrv.c\n  src/ice/util.c\n\n  src/json/decode.c\n  src/json/decode_odict.c\n  src/json/encode.c\n\n  src/list/list.c\n\n  src/main/init.c\n  src/main/main.c\n  src/main/method.c\n\n  src/mbuf/mbuf.c\n\n  src/md5/wrap.c\n\n  src/mem/mem.c\n  src/mem/mem_pool.c\n  src/mem/secure.c\n\n  src/mod/mod.c\n\n  src/mqueue/mqueue.c\n\n  src/msg/ctype.c\n  src/msg/param.c\n\n  src/net/if.c\n  src/net/net.c\n  src/net/netstr.c\n  src/net/rt.c\n  src/net/sock.c\n  src/net/sockopt.c\n\n  src/odict/entry.c\n  src/odict/get.c\n  src/odict/odict.c\n  src/odict/type.c\n\n  src/rtp/fb.c\n  src/rtp/member.c\n  src/rtp/ntp.c\n  src/rtp/pkt.c\n  src/rtp/rr.c\n  src/rtp/rtcp.c\n  src/rtp/rtp.c\n  src/rtp/sdes.c\n  src/rtp/sess.c\n  src/rtp/source.c\n\n  src/rtpext/rtpext.c\n\n  src/sa/printaddr.c\n  src/sa/sa.c\n\n  src/sdp/attr.c\n  src/sdp/format.c\n  src/sdp/media.c\n  src/sdp/msg.c\n  src/sdp/session.c\n  src/sdp/str.c\n  src/sdp/util.c\n\n  src/sha/wrap.c\n\n  src/shim/shim.c\n\n  src/srtp/misc.c\n  src/srtp/replay.c\n  src/srtp/srtcp.c\n  src/srtp/srtp.c\n  src/srtp/stream.c\n\n  src/stun/addr.c\n  src/stun/attr.c\n  src/stun/ctrans.c\n  src/stun/dnsdisc.c\n  src/stun/hdr.c\n  src/stun/ind.c\n  src/stun/keepalive.c\n  src/stun/msg.c\n  src/stun/rep.c\n  src/stun/req.c\n  src/stun/stun.c\n  src/stun/stunstr.c\n\n  src/sys/daemon.c\n  src/sys/endian.c\n  src/sys/fs.c\n  src/sys/rand.c\n  src/sys/sleep.c\n  src/sys/sys.c\n\n  src/tcp/tcp.c\n  src/tcp/tcp_high.c\n\n  src/telev/telev.c\n\n  src/thread/thread.c\n\n  src/tmr/tmr.c\n\n  src/trace/trace.c\n\n  src/trice/cand.c\n  src/trice/candpair.c\n  src/trice/chklist.c\n  src/trice/connchk.c\n  src/trice/lcand.c\n  src/trice/rcand.c\n  src/trice/stunsrv.c\n  src/trice/tcpconn.c\n  src/trice/trice.c\n\n  src/turn/chan.c\n  src/turn/perm.c\n  src/turn/turnc.c\n\n  src/udp/mcast.c\n  src/udp/udp.c\n  src/unixsock/unixsock.c\n\n  src/uri/uri.c\n  src/uri/uric.c\n\n  src/websock/websock.c\n)\n\nset(REM_SRCS\n  rem/aac/aac.c\n  rem/au/fmt.c\n  rem/au/util.c\n  rem/aubuf/aubuf.c\n  rem/aubuf/ajb.c\n  rem/auconv/auconv.c\n  rem/aufile/aufile.c\n  rem/aufile/wave.c\n  rem/auframe/auframe.c\n  rem/aulevel/aulevel.c\n  rem/aumix/aumix.c\n  rem/auresamp/resamp.c\n  rem/autone/tone.c\n  rem/avc/config.c\n  rem/dtmf/dec.c\n  rem/fir/fir.c\n  rem/g711/g711.c\n  rem/goertzel/goertzel.c\n  rem/vid/draw.c\n  rem/vid/fmt.c\n  rem/vid/frame.c\n  rem/vidconv/vconv.c\n  rem/vidmix/vidmix.c\n)\n\n\nif(USE_BFCP)\n  list(APPEND SRCS\n    src/bfcp/attr.c\n    src/bfcp/conn.c\n    src/bfcp/msg.c\n    src/bfcp/reply.c\n    src/bfcp/request.c\n  )\nendif()\n\n\nif(USE_PCP)\n  list(APPEND SRCS\n    src/pcp/msg.c\n    src/pcp/option.c\n    src/pcp/payload.c\n    src/pcp/pcp.c\n    src/pcp/reply.c\n    src/pcp/request.c\n  )\nendif()\n\n\nif(USE_RTMP)\n  list(APPEND SRCS\n    src/rtmp/amf.c\n    src/rtmp/amf_dec.c\n    src/rtmp/amf_enc.c\n    src/rtmp/chunk.c\n    src/rtmp/conn.c\n    src/rtmp/control.c\n    src/rtmp/ctrans.c\n    src/rtmp/dechunk.c\n    src/rtmp/hdr.c\n    src/rtmp/stream.c\n  )\nendif()\n\n\nif(USE_SIP)\n  list(APPEND SRCS\n    src/sip/addr.c\n    src/sip/auth.c\n    src/sip/contact.c\n    src/sip/cseq.c\n    src/sip/ctrans.c\n    src/sip/dialog.c\n    src/sip/keepalive.c\n    src/sip/keepalive_udp.c\n    src/sip/msg.c\n    src/sip/rack.c\n    src/sip/reply.c\n    src/sip/request.c\n    src/sip/sip.c\n    src/sip/strans.c\n    src/sip/transp.c\n    src/sip/via.c\n\n    src/sipevent/listen.c\n    src/sipevent/msg.c\n    src/sipevent/notify.c\n    src/sipevent/subscribe.c\n\n    src/sipreg/reg.c\n\n    src/sipsess/accept.c\n    src/sipsess/ack.c\n    src/sipsess/close.c\n    src/sipsess/connect.c\n    src/sipsess/info.c\n    src/sipsess/listen.c\n    src/sipsess/modify.c\n    src/sipsess/prack.c\n    src/sipsess/reply.c\n    src/sipsess/request.c\n    src/sipsess/sess.c\n    src/sipsess/update.c\n  )\nendif()\n\n\nif(USE_OPENSSL)\n  list(APPEND SRCS\n    src/main/openssl.c\n    src/aes/openssl/aes.c\n    src/tls/openssl/tls_tcp.c\n    src/tls/openssl/tls_udp.c\n    src/tls/openssl/tls.c\n    src/tls/openssl/sni.c\n    src/hmac/openssl/hmac.c\n  )\nelseif(APPLE)\n  list(APPEND SRCS\n    src/aes/apple/aes.c\n    src/hmac/apple/hmac.c\n  )\nelse()\n  list(APPEND SRCS\n    src/aes/stub.c\n    src/hmac/hmac.c\n    src/tls/stub.c\n  )\nendif()\n\n\nif(WIN32)\n  list(APPEND SRCS\n    src/dns/win32/srv.c\n    src/mod/win32/dll.c\n    src/mqueue/win32/pipe.c\n    src/net/win32/wif.c\n  )\nelseif(UNIX)\n  list(APPEND SRCS\n    src/mod/dl.c\n    src/net/posix/pif.c\n  )\n  if(HAVE_GETIFADDRS)\n    list(APPEND SRCS\n      src/net/ifaddrs.c\n    )\n  endif()\nendif()\n\nlist(APPEND SRCS\n  src/crc32/crc32.c\n)\n\nif(HAVE_THREADS)\n  #Do nothing\nelseif(CMAKE_USE_WIN32_THREADS_INIT)\n  list(APPEND SRCS\n    src/thread/win32.c\n  )\nelse()\n  list(APPEND SRCS\n    src/thread/posix.c\n  )\nendif()\n\nif(HAVE_RESOLV)\n  list(APPEND SRCS\n    src/dns/res.c\n  )\nendif()\n\nif(${CMAKE_SYSTEM_NAME} MATCHES \"Darwin\")\n  list(APPEND SRCS\n    src/dns/darwin/srv.c\n    src/net/bsd/brt.c\n  )\nelseif(${CMAKE_SYSTEM_NAME} MATCHES \"iOS\")\n  list(APPEND SRCS\n    src/dns/darwin/srv.c\n  )\nelseif(${CMAKE_SYSTEM_NAME} MATCHES \"FreeBSD\")\n  list(APPEND SRCS\n    src/net/bsd/brt.c\n  )\nelseif(${CMAKE_SYSTEM_NAME} MATCHES \"OpenBSD\")\n  list(APPEND SRCS\n    src/net/bsd/brt.c\n  )\nelseif(${CMAKE_SYSTEM_NAME} MATCHES \"Linux\")\n  list(APPEND SRCS\n    src/net/linux/rt.c\n    src/net/linux/addrs.c\n  )\nelseif(${CMAKE_SYSTEM_NAME} MATCHES \"Android\")\n  list(APPEND SRCS\n    src/net/linux/rt.c\n  )\nendif()\n\nif(USE_REM)\n  list(APPEND SRCS ${REM_SRCS})\nendif()\n\n\n##############################################################################\n#\n# Main target object\n#\n\nadd_library(re-objs OBJECT ${SRCS} ${HEADERS})\n\nset_target_properties(re-objs PROPERTIES POSITION_INDEPENDENT_CODE ON)\n\ntarget_compile_definitions(re-objs PRIVATE ${RE_DEFINITIONS})\n\ntarget_include_directories(re-objs PRIVATE include)\ntarget_include_directories(re-objs PRIVATE\n  ${OPENSSL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIRS})\n\n\n##############################################################################\n#\n# Shared target libre.[so|dll|dylib]\n#\n\nif(LIBRE_BUILD_SHARED)\n  list(APPEND RE_INSTALL_TARGETS re-shared)\n  add_library(re-shared SHARED $<TARGET_OBJECTS:re-objs>)\n  target_link_libraries(re-shared PRIVATE ${RE_LIBS})\n  set_target_properties(re-shared PROPERTIES VERSION\n    ${PROJECT_SOVERSION}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})\n  set_target_properties(re-shared PROPERTIES SOVERSION ${PROJECT_SOVERSION})\n  set_target_properties(re-shared PROPERTIES OUTPUT_NAME \"re\")\n  add_library(libre::re-shared ALIAS re-shared)\nendif()\n\n\n##############################################################################\n#\n# Static target libre.a\n#\n\nif(LIBRE_BUILD_STATIC)\n  list(APPEND RE_INSTALL_TARGETS re)\n  add_library(re STATIC $<TARGET_OBJECTS:re-objs>)\n  target_link_libraries(re PRIVATE ${RE_LIBS})\n  target_include_directories(re PUBLIC\n    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>\n  )\n  add_library(libre::re ALIAS re)\n\n  if(MSVC)\n    set_target_properties(re PROPERTIES OUTPUT_NAME \"re-static\")\n    if(NOT LIBRE_BUILD_SHARED)\n      set(PC_LIBNAME \"re-static\")\n    endif()\n  endif()\nendif()\n\n\n##############################################################################\n#\n# PKGCONF section\n#\n\nif(NOT PC_LIBNAME)\n  set(PC_LIBNAME \"re\")\nendif()\nset(PC_REQUIRES \"\")\nset(PC_LINKLIBS \"\")\nforeach(item IN LISTS RE_LIBS)\n  if(item STREQUAL \"Threads::Threads\")\n    list(APPEND PC_LINKLIBS ${CMAKE_THREADS_LIBS_INIT})\n  elseif(item STREQUAL \"OpenSSL::Crypto\")\n    list(APPEND PC_REQUIRES \"libcrypto\")\n  elseif(item STREQUAL \"OpenSSL::SSL\")\n    list(APPEND PC_REQUIRES \"libssl\")\n  elseif(item STREQUAL \"ZLIB::ZLIB\")\n    list(APPEND PC_REQUIRES \"zlib\")\n  elseif(item MATCHES \"^-|/\")\n    list(APPEND PC_LINKLIBS \"${item}\")\n  else()\n    list(APPEND PC_LINKLIBS \"-l${item}\")\n  endif()\nendforeach()\nlist(JOIN PC_LINKLIBS \" \" PC_LINKLIBS)\nlist(JOIN PC_REQUIRES \" \" PC_REQUIRES)\nconfigure_file(packaging/libre.pc.in libre.pc @ONLY)\n\n\n##############################################################################\n#\n# Install section\n#\n\ninstall(TARGETS ${RE_INSTALL_TARGETS}\n  EXPORT libre\n  RUNTIME\n    DESTINATION ${CMAKE_INSTALL_BINDIR}\n    COMPONENT Libraries\n  LIBRARY\n    DESTINATION ${CMAKE_INSTALL_LIBDIR}\n    COMPONENT Libraries\n    NAMELINK_SKIP\n  ARCHIVE\n    DESTINATION ${CMAKE_INSTALL_LIBDIR}\n    COMPONENT Development\n  INCLUDES\n    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/re\n)\n\ninstall(FILES ${HEADERS}\n  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/re\n  COMPONENT Development\n)\n\ninstall(EXPORT libre\n  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libre\n  FILE libre-targets.cmake\n  NAMESPACE libre::\n  COMPONENT Development\n)\n\nif(LIBRE_BUILD_SHARED)\n  install(TARGETS re-shared\n    LIBRARY\n      DESTINATION ${CMAKE_INSTALL_LIBDIR}\n      NAMELINK_ONLY\n      COMPONENT Development\n  )\nendif()\n\ninstall(FILES cmake/re-config.cmake\n  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/re\n  COMPONENT Development\n)\n\nconfigure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/libre-config.cmake\"\n  \"${CMAKE_CURRENT_BINARY_DIR}/cmake/libre-config.cmake\" @ONLY)\ninstall(FILES \"${CMAKE_CURRENT_BINARY_DIR}/cmake/libre-config.cmake\"\n  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libre\n  COMPONENT Development\n)\n\ninstall(FILES ${CMAKE_CURRENT_BINARY_DIR}/libre.pc\n  DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig\n  COMPONENT Development\n)\n\n\n##############################################################################\n#\n# Packaging section\n#\n\nif(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)\n  add_subdirectory(packaging)\nendif()\n\n\n##############################################################################\n# Test\n#\n\nadd_subdirectory(test EXCLUDE_FROM_ALL)\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (C) 2020 - 2026, Baresip Foundation (https://github.com/baresip)\nCopyright (c) 2010 - 2024, Alfred E. Heggestad\nCopyright (c) 2010 - 2020, Richard Aas\nCopyright (c) 2010 - 2020, Creytiv.com\nAll rights reserved.\n\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n1. Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in the\n   documentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors\n   may be used to endorse or promote products derived from this software\n   without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\nGOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\nHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\nOF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: build\nbuild:\n\t[ -d build ] || cmake -B build\n\tcmake --build build --parallel\n\n.PHONY: ninja\nninja:\n\t[ -d build ] || cmake -B build -G Ninja\n\tmake build\n\n.PHONY: release\nrelease:\n\t[ -d build ] || cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo\n\tcmake --build build --parallel\n\n.PHONY: dist\ndist: build\n\tcmake --install build --prefix dist\n\n.PHONY: deb\ndeb: release\n\tcd build && cpack -G DEB\n\n.PHONY: test\ntest: build\n\tcmake --build build --parallel -t retest\n\tbuild/test/retest -rv\n\n.PHONY: clean\nclean:\n\t@rm -Rf build dist CMakeCache.txt CMakeFiles\n\n\n###############################################################################\n#\n# Documentation section\n#\nDOX_DIR=../re-dox\n\n$(DOX_DIR):\n\t@mkdir $@\n\n$(DOX_DIR)/Doxyfile: mk/Doxyfile Makefile\n\t@cp $< $@\n\t@perl -pi -e 's/PROJECT_NUMBER\\s*=.*/PROJECT_NUMBER = $(VERSION)/' \\\n\t$(DOX_DIR)/Doxyfile\n\n.PHONY:\ndox:\t$(DOX_DIR) $(DOX_DIR)/Doxyfile\n\t@doxygen $(DOX_DIR)/Doxyfile 2>&1 | grep -v DEBUG_ ; true\n\techo \"Doxygen docs in $(DOX_DIR)\"\n"
  },
  {
    "path": "README.md",
    "content": "libre README\n============\n\n\nlibre is a Generic library for real-time communications with async IO support.\n\n- Copyright (C) 2010 - 2020 Creytiv.com\n- Copyright (C) 2020 - 2026 Baresip Foundation (https://github.com/baresip)\n\n![Build](https://github.com/baresip/re/workflows/Build/badge.svg)\n![ccheck](https://github.com/baresip/re/workflows/ccheck/badge.svg)\n![OpenSSL no-deprecated and LibreSSL](https://github.com/baresip/re/workflows/OpenSSL%20no-deprecated%20and%20LibreSSL/badge.svg)\n\n\n## Features\n\n* SIP Stack ([RFC 3261](https://tools.ietf.org/html/rfc3261))\n* SDP\n* RTP and RTCP\n* SRTP and SRTCP (Secure RTP)\n* DNS-Client\n* STUN/TURN/ICE stack\n* BFCP\n* HTTP-stack with client/server\n* Websockets\n* Async I/O (select, epoll, kqueue)\n* UDP/TCP/TLS/DTLS transport\n* JSON parser\n* Real Time Messaging Protocol (RTMP)\n\n\n## Building\n\nlibre is using CMake. CMake and OpenSSL development headers must be\ninstalled before building.\n\n\n### Build with debug enabled\n\n```\n$ cmake -B build\n$ cmake --build build -j\n$ sudo cmake --install build\n$ sudo ldconfig\n```\n\n### Build/run tests\n\n```\ncmake -B build && cmake --build build -t retest -j\nbuild/test/retest -rv\n```\n\nOn some distributions, /usr/local/lib may not be included in ld.so.conf. \nYou can check with `grep \"/usr/local/lib\" /etc/ld.so.conf.d/*.conf` \nand add if necessary:\n\n```\n$ echo \"/usr/local/lib\" | sudo tee /etc/ld.so.conf.d/libc.conf\n$ sudo ldconfig\n```\n\n\n### Build with release\n\n```\n$ cmake -B build -DCMAKE_BUILD_TYPE=Release \n$ cmake --build build -j\n$ sudo cmake --install build\n$ sudo ldconfig\n```\n\n### Build with clang compiler\n\n```\n$ cmake -B build -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++\n$ cmake --build build -j\n$ sudo cmake --install build\n$ sudo ldconfig\n```\n\n\n### Examples\n\nCoding examples are available from the\n[redemo](https://github.com/creytiv/redemo \"creytiv/redemo: Demo example applications using libre\") project.\n\n\n## License\n\nThe libre project is using the BSD license.\n\n\n## Contributing\n\nPatches can sent via Github\n[Pull-Requests](https://github.com/baresip/re/pulls)\n\n\n## Design goals\n\n* Portable POSIX source code (ISO C99 and C11 standard)\n* Robust, fast, low memory footprint\n* RFC compliance\n* IPv4 and IPv6 support\n\n\n## Modules\n\n| Name     | Status   | Description                                    |\n|----------|----------|------------------------------------------------|\n| aes      | stable   | AES (Advanced Encryption Standard)             |\n| async    | testing  | Async module                                   |\n| av1      | testing  | AV1 Packetizer                                 |\n| base64   | stable   | Base-64 encoding/decoding functions            |\n| bfcp     | stable   | The Binary Floor Control Protocol (BFCP)       |\n| btrace   | testing  | Backtrace module                               |\n| conf     | stable   | Configuration file parser                      |\n| crc32    | stable   | 32-bit CRC defined in ITU V.42                 |\n| dbg      | stable   | Debug printing                                 |\n| dd       | testing  | Dependency Descriptor                          |\n| dns      | stable   | DNS resolving (NAPTR, SRV, A)                  |\n| fmt      | stable   | Formatted printing and regular expression      |\n| h264     | testing  | H.264 packetizer                               |\n| h265     | testing  | H.265 packetizer                               |\n| hash     | stable   | Hashmap table                                  |\n| hmac     | stable   | HMAC: Keyed-Hashing for Message Authentication |\n| http     | stable   | HTTP parser (RFC 2616)                         |\n| httpauth | stable   | HTTP-based Authentication (RFC 2617)           |\n| ice      | stable   | Interactive Connectivity Establishment (ICE)   |\n| json     | stable   | JavaScript Object Notation (JSON)              |\n| list     | stable   | Sortable doubly-linked list handling           |\n| main     | stable   | Main poll loop                                 |\n| mbuf     | stable   | Linear memory buffers                          |\n| md5      | stable   | The MD5 Message-Digest Algorithm (RFC 1321)    |\n| mem      | stable   | Memory referencing                             |\n| mod      | stable   | Run-time module loading                        |\n| mqueue   | stable   | Thread-safe message queue                      |\n| msg      | stable   | Generic message component library              |\n| net      | stable   | Networking routines                            |\n| odict    | stable   | Ordered Dictionary                             |\n| pcp      | testing  | Port Control Protocol                          |\n| rtmp     | stable   | Real Time Messaging Protocol                   |\n| rtp      | stable   | Real-time Transport Protocol                   |\n| rtpext   | testing  | RTP extensions                                 |\n| sa       | stable   | Socket Address functions                       |\n| sdp      | stable   | Session Description Protocol                   |\n| sha      | stable   | Secure Hash Standard, NIST, FIPS PUB 180-1     |\n| sip      | stable   | Core SIP library                               |\n| sipevent | stable   | SIP Event framework                            |\n| sipreg   | stable   | SIP register client                            |\n| sipsess  | stable   | SIP Sessions                                   |\n| srtp     | stable   | Secure Real-time Transport Protocol (SRTP)     |\n| stun     | stable   | Session Traversal Utilities for NAT (STUN)     |\n| sys      | stable   | System information                             |\n| tcp      | stable   | TCP transport                                  |\n| telev    | stable   | Telephony Events (RFC 4733)                    |\n| thread   | testing  | C11 threads (with pthread and win32 emulation) |\n| tls      | stable   | Transport Layer Security                       |\n| tmr      | stable   | Timer handling                                 |\n| turn     | stable   | Obtaining Relay Addresses from STUN (TURN)     |\n| trace    | testing  | Trace Helpers JSON traces (chrome://tracing)   |\n| trice    | testing  | Trickle ICE                                    |\n| udp      | stable   | UDP transport                                  |\n| unixsock | testing  | Unix domain sockets                            |\n| uri      | stable   | Generic URI library                            |\n| websock  | stable   | WebSocket Client and Server                    |\n\nlegend:\n* *stable* - code complete; stable code and stable API\n* *testing* - code complete, but API might change\n* *unstable* - code complete but not completely tested\n* *development* - code is under development\n\n\n## Features\n\n* [RFC 1321](https://tools.ietf.org/html/rfc1321) - The MD5 Message-Digest Algorithm\n* [RFC 1886](https://tools.ietf.org/html/rfc1886) - DNS Extensions to support IP version 6\n* [RFC 2616](https://tools.ietf.org/html/rfc2616) - Hypertext Transfer Protocol -- HTTP/1.1\n* [RFC 2617](https://tools.ietf.org/html/rfc2617) - HTTP Authentication: Basic and Digest Access Authentication\n* [RFC 2782](https://tools.ietf.org/html/rfc2782) - A DNS RR for Specifying the Location of Services (DNS SRV)\n* [RFC 2915](https://tools.ietf.org/html/rfc2915) - The Naming Authority Pointer (NAPTR) DNS Resource Record\n* [RFC 3261](https://tools.ietf.org/html/rfc3261) - SIP: Session Initiation Protocol\n* [RFC 3262](https://tools.ietf.org/html/rfc3262) - SIP Reliability of Provisional Responses\n* [RFC 3263](https://tools.ietf.org/html/rfc3263) - Locating SIP Servers\n* [RFC 3264](https://tools.ietf.org/html/rfc3264) - An Offer/Answer Model with SDP\n* [RFC 3265](https://tools.ietf.org/html/rfc3265) - SIP-Specific Event Notification\n* [RFC 3311](https://tools.ietf.org/html/rfc3311) - The SIP UPDATE Method\n* [RFC 3327](https://tools.ietf.org/html/rfc3327) - SIP Extension Header Field for Registering Non-Adjacent Contacts\n* [RFC 3428](https://tools.ietf.org/html/rfc3428) - SIP Extension for Instant Messaging\n* [RFC 3489](https://tools.ietf.org/html/rfc3489) - STUN - Simple Traversal of UDP Through NATs\n* [RFC 3515](https://tools.ietf.org/html/rfc3515) - The SIP Refer Method\n* [RFC 3550](https://tools.ietf.org/html/rfc3550) - RTP: A Transport Protocol for Real-Time Applications\n* [RFC 3551](https://tools.ietf.org/html/rfc3551) - RTP Profile for Audio and Video Conferences with Minimal Control\n* [RFC 3555](https://tools.ietf.org/html/rfc3555) - MIME Type Registration of RTP Payload Formats\n* [RFC 3556](https://tools.ietf.org/html/rfc3556) - SDP Bandwidth Modifiers for RTCP Bandwidth\n* [RFC 3581](https://tools.ietf.org/html/rfc3581) - An Extension to SIP for Symmetric Response Routing\n* [RFC 3605](https://tools.ietf.org/html/rfc3605) - RTCP attribute in SDP\n* [RFC 3611](https://tools.ietf.org/html/rfc3611) - RTCP Extended Reports\n* [RFC 3711](https://tools.ietf.org/html/rfc3711) - The Secure Real-time Transport Protocol (SRTP)\n* [RFC 3969](https://tools.ietf.org/html/rfc3969) - The IANA URI Parameter Registry for SIP\n* [RFC 3994](https://tools.ietf.org/html/rfc3994) - Indication of Message Composition for Instant Messaging\n* [RFC 4566](https://tools.ietf.org/html/rfc4566) - SDP: Session Description Protocol\n* [RFC 4582](https://tools.ietf.org/html/rfc4582) - The Binary Floor Control Protocol (BFCP)\n* [RFC 4582bis](https://tools.ietf.org/html/draft-ietf-bfcpbis-rfc4582bis-08) - The Binary Floor Control Protocol (BFCP)\n* [RFC 4585](https://tools.ietf.org/html/rfc4585) - Extended RTP Profile for RTCP-Based Feedback\n* [RFC 4733](https://tools.ietf.org/html/rfc4733) - RTP Payload for DTMF Digits, Telephony Tones, and Teleph. Signals\n* [RFC 4961](https://tools.ietf.org/html/rfc4961) - Symmetric RTP / RTP Control Protocol (RTCP)\n* [RFC 5104](https://tools.ietf.org/html/rfc5104) - Codec Control Messages in AVPF\n* [RFC 5118](https://tools.ietf.org/html/rfc5118) - SIP Torture Test Messages for IPv6\n* [RFC 5245](https://tools.ietf.org/html/rfc5245) - Interactive Connectivity Establishment (ICE)\n* [RFC 5246](https://tools.ietf.org/html/rfc5246) - The TLS Protocol Version 1.2\n* [RFC 5389](https://tools.ietf.org/html/rfc5389) - Session Traversal Utilities for NAT (STUN)\n* [RFC 5626](https://tools.ietf.org/html/rfc5626) - Managing Client-Initiated Connections in SIP\n* [RFC 5761](https://tools.ietf.org/html/rfc5761) - Multiplexing RTP Data and Control Packets on a Single Port\n* [RFC 5766](https://tools.ietf.org/html/rfc5766) - Traversal Using Relays around NAT (TURN)\n* [RFC 5768](https://tools.ietf.org/html/rfc5768) - Indicating Support for ICE in SIP\n* [RFC 5769](https://tools.ietf.org/html/rfc5769) - Test vectors for STUN\n* [RFC 6026](https://tools.ietf.org/html/rfc6026) - Correct Transaction Handling for 2xx Resp. to SIP INVITE Requests\n* [RFC 6156](https://tools.ietf.org/html/rfc6156) - TURN Extension for IPv6\n* [RFC 6188](https://tools.ietf.org/html/rfc6188) - The Use of AES-192 and AES-256 in Secure RTP\n* [RFC 6455](https://tools.ietf.org/html/rfc6455) - The WebSocket Protocol\n* [RFC 7159](https://tools.ietf.org/html/rfc7159) - JavaScript Object Notation (JSON)\n* [RFC 7350](https://tools.ietf.org/html/rfc7350) - DTLS as Transport for STUN\n* [RFC 7616](https://tools.ietf.org/html/rfc7616) - HTTP Digest Access Authentication\n* [RFC 7714](https://tools.ietf.org/html/rfc7714) - AES-GCM Authenticated Encryption in SRTP\n* [AV1-RTP](https://aomediacodec.github.io/av1-rtp-spec/) - RTP Payload Format For AV1\n\n\n## Supported platforms\n\n|  System | Support type | Supported versions | Notes |\n|---|---|---|---|\n| Linux | Tier 1 | glibc >= 2.31 | |\n| Linux | Tier 1 | musl >= 1.2 | |\n| macOS | Tier 1 | macOS >= 10.10 | |\n| Windows | Tier 1 | >= Windows 10 | MinGW-w64, >= VS 2022 |\n| Android | Tier 2 | Android 8 (API Level 26)| |\n| iOS | Tier 2 | | |\n| FreeBSD | Tier 2 | >= 12 | |\n| OpenBSD | Tier 2 | >= 7.4 | |\n| Linux | Tier 2 | uClibc | |\n\n\n### Known bugs\n\nmacOS clang-1600.0.26.3 (Xcode 16.0) and clang-1600.0.26.4 (Xcode 16.1) have a optimization bug:\n\n- https://github.com/baresip/re/pull/1399\n- https://github.com/baresip/baresip/issues/3240\n\n\n### Support types\n\n* **Tier 1**: Officially supported and tested with CI. Any contributed patch\n  MUST NOT break such systems.\n\n* **Tier 2**: Officially supported, but not necessarily tested with CI. These\n  systems are maintained to the best of collaborators ability, without being\n  a top priority.\n\n* **Tier 3**: Community maintained. These systems may inadvertently break and the\n  community and interested parties are expected to help with the maintenance.\n\n\n### Supported versions of C Standard library\n\n* Android bionic\n* BSD libc\n* GNU C Library (glibc)\n* Windows C Run-Time Libraries (CRT)\n* uClibc\n* musl\n\n\n### Supported compilers:\n\n* gcc 9 or later\n* MSVC 2022\n* clang 9.x or later\n\n\n### Supported versions of OpenSSL\n\n* OpenSSL version 3.x.x and 4.x.x\n* LibreSSL version 4.x\n\n\n## Coding guidelines\n\n* Use enum for constants where appropriate\n* Use const as much as possible (where appropriate)\n* Use C99 data types (intN_t, uintN_t, bool)\n* Hide data-types in .c files where possible (use struct foo)\n* Avoid malloc/free, use mem_alloc/mem_deref instead\n* CVS/svn/git tags are NOT allowed in the code!\n* Avoid bit-fields in structs which are not portable\n* Use dummy handlers for timing-critical callbacks\n* return err, return alloced objects as pointer-pointers\n* in allocating functions, first arg is always double pointer\n* Use POSIX error-codes; EINVAL for invalid args, EBADMSG for\n  parse errors and EPROTO for protocol errors\n\n\n## Transport protocols\n\n\n|         | TCP | UDP | TLS | DTLS|\n|:--------|:---:|:---:|:---:|:---:|\n| BFCP    | -   | yes | -   | -   |\n| DNS     | yes | yes | -   | -   |\n| HTTP    | yes | n/a | yes | n/a |\n| ICE     | -   | yes | -   | -   |\n| RTP     | -   | yes | -   | -   |\n| RTCP    | -   | yes | -   | -   |\n| RTMP    | yes | -   | yes | -   |\n| SIP     | yes | yes | yes | -   |\n| STUN    | yes | yes | yes | yes |\n| TURN    | yes | yes | yes | yes |\n| WEBSOCK | yes | n/a | yes | n/a |\n\n\n## Related projects\n\n* [librem](https://github.com/baresip/rem)\n* [retest](https://github.com/baresip/retest)\n* [baresip](https://github.com/baresip/baresip)\n\n\n## References\n\nhttps://github.com/creytiv/re\n"
  },
  {
    "path": "cmake/FindMBEDTLS.cmake",
    "content": "find_path(MBEDTLS_INCLUDE_DIR\n    NAMES mbedtls/ssl.h mbedtls/md.h mbedtls/md5.h mbedtls/error.h\n          mbedtls/sha1.h mbedtls/sha256.h\n    HINTS\n      \"${MBEDTLS_INCLUDE_DIRS}\"\n      \"${MBEDTLS_HINTS}/include\"\n    PATHS /usr/local/include /usr/include\n)\n\nfind_library(MBEDTLS_LIBRARY\n  NAMES mbedtls mbedx509 mbedcrypto\n  HINTS\n    \"${MBEDTLS_LIBRARY_DIRS}\"\n    \"${MBEDTLS_HINTS}/lib\"\n  PATHS /usr/local/lib /usr/lib\n)\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(MBEDTLS DEFAULT_MSG\n    MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARY)\n\nif(MBEDTLS_FOUND)\n  set( MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR} )\n  set( MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} )\nelse()\n  set( MBEDTLS_INCLUDE_DIRS )\n  set( MBEDTLS_LIBRARIES )\nendif()\n\nmark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARIES)\n"
  },
  {
    "path": "cmake/libre-config.cmake",
    "content": "if(\"@LIBRE_BUILD_STATIC@\")\n    include(CMakeFindDependencyMacro)\n    find_dependency(Threads)\n    if(\"@USE_OPENSSL@\")\n        find_dependency(OpenSSL)\n    endif()\n    if(\"@ZLIB_FOUND@\")\n        find_dependency(ZLIB)\n    endif()\nendif()\n\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/libre-targets.cmake\")\n\n# convenience target libre::libre for uniform usage\nif(NOT TARGET libre::libre)\n    if(TARGET libre::re_shared AND (BUILD_SHARED_LIBS OR NOT TARGET libre::re))\n        add_library(libre::libre INTERFACE IMPORTED)\n        set_target_properties(libre::libre PROPERTIES INTERFACE_LINK_LIBRARIES libre::re_shared)\n    elseif(TARGET libre::re AND (NOT BUILD_SHARED_LIBS OR NOT TARGET libre::re_shared))\n        add_library(libre::libre INTERFACE IMPORTED)\n        set_target_properties(libre::libre PROPERTIES INTERFACE_LINK_LIBRARIES libre::re)\n    endif()\nendif()\n"
  },
  {
    "path": "cmake/re-config.cmake",
    "content": "#\n# re-config.cmake\n#\ninclude(CheckIncludeFile)\ninclude(CheckFunctionExists)\ninclude(CheckSymbolExists)\ninclude(CheckTypeSize)\ninclude(CheckCXXSourceCompiles)\n\noption(USE_MBEDTLS \"Enable MbedTLS\" OFF)\noption(USE_TLS1_3_PHA \"Enable TLS 1.3 Post-Handshake Auth\" ON)\n\nfind_package(Backtrace)\nfind_package(Threads REQUIRED)\nfind_package(ZLIB)\n\nif(USE_MBEDTLS)\n  find_package(MBEDTLS)\nelse()\n  find_package(OpenSSL \"1.1.1\")\nendif()\n\noption(USE_OPENSSL \"Enable OpenSSL\" ${OPENSSL_FOUND})\noption(USE_UNIXSOCK \"Enable Unix Domain Sockets\" ON)\noption(USE_TRACE \"Enable Tracing helpers\" OFF)\n\nif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n    message(STATUS \"Setting build type to 'Debug' as none was specified.\")\n    set(CMAKE_BUILD_TYPE \"Debug\" CACHE STRING \"Choose the type of build.\"\n        FORCE)\nendif()\n\ncheck_symbol_exists(LIBRESSL_VERSION_NUMBER \"${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h\" HAVE_LIBRESSL)\nif(USE_TLS1_3_PHA AND NOT HAVE_LIBRESSL AND NOT USE_MBEDTLS AND OPENSSL_FOUND)\n  list(APPEND RE_DEFINITIONS HAVE_TLS1_3_POST_HANDSHAKE_AUTH)\nendif()\n\ncheck_symbol_exists(\"arc4random\" \"stdlib.h\" HAVE_ARC4RANDOM)\nif(HAVE_ARC4RANDOM)\n  list(APPEND RE_DEFINITIONS HAVE_ARC4RANDOM)\nendif()\n\nif(ZLIB_FOUND)\n  list(APPEND RE_DEFINITIONS USE_ZLIB)\nendif()\n\ncheck_include_file(syslog.h HAVE_SYSLOG_H)\nif(HAVE_SYSLOG_H)\n  list(APPEND RE_DEFINITIONS HAVE_SYSLOG)\nendif()\n\ncheck_include_file(getopt.h HAVE_GETOPT_H)\nif(HAVE_GETOPT_H)\n  list(APPEND RE_DEFINITIONS HAVE_GETOPT)\nendif()\n\ncheck_include_file(unistd.h HAVE_UNISTD_H)\nif(HAVE_UNISTD_H)\n  list(APPEND RE_DEFINITIONS HAVE_UNISTD_H)\nendif()\n\nif(${CMAKE_SYSTEM_NAME} MATCHES \"OpenBSD\")\n  check_symbol_exists(res_init resolv.h HAVE_RESOLV)\nelse()\n  check_symbol_exists(res_ninit resolv.h HAVE_RESOLV)\nendif()\nif(HAVE_RESOLV AND ${CMAKE_SYSTEM_NAME} MATCHES \"FreeBSD\")\n  list(APPEND RE_DEFINITIONS HAVE_RESOLV)\n  set(RESOLV_LIBRARY) # Provided by libc\nelseif(HAVE_RESOLV)\n  set(RESOLV_LIBRARY resolv)\n  list(APPEND RE_DEFINITIONS HAVE_RESOLV)\nelse()\n  set(RESOLV_LIBRARY)\nendif()\n\nif(Backtrace_FOUND)\n  list(APPEND RE_DEFINITIONS HAVE_EXECINFO)\nelse()\n  set(Backtrace_LIBRARIES)\nendif()\n\ncheck_function_exists(thrd_create HAVE_THREADS_FUN)\ncheck_include_file(threads.h HAVE_THREADS_H)\nif(HAVE_THREADS_FUN AND HAVE_THREADS_H)\n  set(HAVE_THREADS ON CACHE BOOL \"HAVE C11 Threads\")\nendif()\nif(HAVE_THREADS)\n  list(APPEND RE_DEFINITIONS HAVE_THREADS)\nendif()\n\nif(${CMAKE_SYSTEM_NAME} MATCHES \"Linux\")\n  check_function_exists(accept4 HAVE_ACCEPT4)\n  if(HAVE_ACCEPT4)\n    list(APPEND RE_DEFINITIONS HAVE_ACCEPT4)\n  endif()\nendif()\n\nif(CMAKE_USE_PTHREADS_INIT)\n  list(APPEND RE_DEFINITIONS HAVE_PTHREAD)\n  set(HAVE_PTHREAD ON)\nendif()\n\nif(UNIX)\n  check_symbol_exists(epoll_create \"sys/epoll.h\" HAVE_EPOLL)\n  if(HAVE_EPOLL)\n    list(APPEND RE_DEFINITIONS HAVE_EPOLL)\n  endif()\n  check_symbol_exists(kqueue \"sys/types.h;sys/event.h\" HAVE_KQUEUE)\n  if(HAVE_KQUEUE)\n    list(APPEND RE_DEFINITIONS HAVE_KQUEUE)\n  endif()\nendif()\n\ncheck_include_file(sys/prctl.h HAVE_PRCTL)\nif(HAVE_PRCTL)\n  list(APPEND RE_DEFINITIONS HAVE_PRCTL)\nendif()\n\n\nlist(APPEND RE_DEFINITIONS\n  HAVE_ATOMIC\n  HAVE_SELECT\n  )\n\nif(UNIX)\n  if(ANDROID)\n    string(REPLACE \"android-\" \"\" ANDROID_API_LEVEL ${ANDROID_PLATFORM})\n    if(ANDROID_API_LEVEL GREATER_EQUAL 24)\n      set(HAVE_GETIFADDRS ON CACHE BOOL \"\" FORCE)\n    endif()\n  else()\n    check_include_file(ifaddrs.h HAVE_GETIFADDRS)\n  endif()\n  if(HAVE_GETIFADDRS)\n    list(APPEND RE_DEFINITIONS HAVE_GETIFADDRS)\n  endif()\nendif()\n\nif(UNIX)\n  list(APPEND RE_DEFINITIONS\n    HAVE_PWD_H\n    HAVE_SETRLIMIT\n    HAVE_STRERROR_R\n    HAVE_STRINGS_H\n    HAVE_SYS_TIME_H\n    HAVE_UNAME\n    HAVE_SELECT_H\n    HAVE_SIGNAL\n    HAVE_FORK\n    )\n  if(NOT IOS)\n    list(APPEND RE_DEFINITIONS HAVE_ROUTE_LIST)\n  endif()\nendif()\n\n\nif(MSVC)\n  list(APPEND RE_DEFINITIONS\n    HAVE_IO_H\n    _CRT_SECURE_NO_WARNINGS\n  )\nendif()\n\nif(WIN32)\n  list(APPEND RE_DEFINITIONS\n    WIN32\n    _WIN32_WINNT=0x0A00\n  )\n\n  unset(CMAKE_EXTRA_INCLUDE_FILES)\n  set(CMAKE_EXTRA_INCLUDE_FILES \"winsock2.h;qos2.h\")\n  check_type_size(\"QOS_FLOWID\" HAVE_QOS_FLOWID BUILTIN_TYPES_ONLY)\n  check_type_size(\"PQOS_FLOWID\" HAVE_PQOS_FLOWID BUILTIN_TYPES_ONLY)\n  unset(CMAKE_EXTRA_INCLUDE_FILES)\n\n  if(HAVE_QOS_FLOWID)\n    list(APPEND RE_DEFINITIONS HAVE_QOS_FLOWID)\n  endif()\n\n  if(HAVE_PQOS_FLOWID)\n    list(APPEND RE_DEFINITIONS HAVE_PQOS_FLOWID)\n  endif()\nendif()\n\nif(USE_OPENSSL)\n  list(APPEND RE_DEFINITIONS\n    USE_DTLS\n    USE_OPENSSL\n    USE_OPENSSL_AES\n    USE_OPENSSL_HMAC\n    USE_TLS\n  )\nendif()\n\nif(USE_MBEDTLS)\n  list(APPEND RE_DEFINITIONS\n    USE_MBEDTLS\n  )\nendif()\n\nif(USE_UNIXSOCK)\n  list(APPEND RE_DEFINITIONS\n    HAVE_UNIXSOCK=1\n  )\nelse()\n  list(APPEND RE_DEFINITIONS\n    HAVE_UNIXSOCK=0\n  )\nendif()\n\nif(USE_TRACE)\n  list(APPEND RE_DEFINITIONS\n    RE_TRACE_ENABLED\n  )\nendif()\n\nif(${CMAKE_SYSTEM_NAME} MATCHES \"Darwin\")\n  list(APPEND RE_DEFINITIONS DARWIN)\nelseif(${CMAKE_SYSTEM_NAME} MATCHES \"iOS\")\n  list(APPEND RE_DEFINITIONS DARWIN)\nelseif(${CMAKE_SYSTEM_NAME} MATCHES \"FreeBSD\")\n  list(APPEND RE_DEFINITIONS FREEBSD)\nelseif(${CMAKE_SYSTEM_NAME} MATCHES \"OpenBSD\")\n  list(APPEND RE_DEFINITIONS OPENBSD)\nelseif(${CMAKE_SYSTEM_NAME} MATCHES \"Linux\")\n  list(APPEND RE_DEFINITIONS LINUX)\nendif()\n\n\nlist(APPEND RE_DEFINITIONS\n  ARCH=\"${CMAKE_SYSTEM_PROCESSOR}\"\n  OS=\"${CMAKE_SYSTEM_NAME}\"\n  $<$<NOT:$<CONFIG:DEBUG>>:RELEASE>\n)\n\nif(NOT ${CMAKE_BUILD_TYPE} MATCHES \"[Rr]el\")\n  if(Backtrace_FOUND)\n    set(CMAKE_ENABLE_EXPORTS ON)\n  endif()\nendif()\n\n\n##############################################################################\n#\n# Linking LIBS\n#\n\nset(RE_LIBS Threads::Threads ${RESOLV_LIBRARY})\n\nif(BACKTRACE_FOUND)\n  list(APPEND RE_LIBS ${Backtrace_LIBRARIES})\nendif()\n\nif(ZLIB_FOUND)\n  list(APPEND RE_LIBS ZLIB::ZLIB)\nendif()\n\nif(USE_OPENSSL)\n  list(APPEND RE_LIBS OpenSSL::SSL OpenSSL::Crypto)\nendif()\n\nif(${CMAKE_SYSTEM_NAME} MATCHES \"Darwin\")\n  list(APPEND RE_LIBS\n    \"-framework SystemConfiguration\" \"-framework CoreFoundation\"\n  )\nendif()\n\nif(WIN32)\n  list(APPEND RE_LIBS\n    qwave\n    iphlpapi\n    wsock32\n    ws2_32\n    dbghelp\n  )\nelse()\n  list(APPEND RE_LIBS m)\nendif()\n\nif(UNIX)\n  list(APPEND RE_LIBS\n    ${CMAKE_DL_LIBS}\n  )\nendif()\n\n\n##############################################################################\n#\n# Testing Atomic\n#\n\nenable_language(CXX)\n\nset(ATOMIC_TEST_CODE \"\n    #include <atomic>\n    #include <cstdint>\n    std::atomic<uint8_t> n8 (0); // riscv64\n    std::atomic<uint64_t> n64 (0); // armel, mipsel, powerpc\n    int main() {\n      ++n8;\n      ++n64;\n      return 0;\n  }\")\n\ncheck_cxx_source_compiles(\"${ATOMIC_TEST_CODE}\" atomic_test)\n\nif(NOT atomic_test)\n  set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} atomic)\n  check_cxx_source_compiles(\"${ATOMIC_TEST_CODE}\" atomic_test_lib)\n  if(NOT atomic_test_lib)\n    message(FATAL_ERROR \"No builtin or libatomic support\")\n  else()\n    list(APPEND RE_LIBS atomic)\n  endif()\nendif()\n"
  },
  {
    "path": "cmake/sanitizer.cmake",
    "content": "if(USE_SANITIZER STREQUAL \"address\")\n  set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -fsanitize=address\")\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fsanitize=address\")\nelseif(USE_SANITIZER STREQUAL \"thread\")\n  set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -fsanitize=thread\")\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fsanitize=thread\")\nelseif(USE_SANITIZER STREQUAL \"undefined\")\n  set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -fsanitize=undefined\")\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fsanitize=undefined\")\nelseif(USE_SANITIZER STREQUAL \"memory\")\n  set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -fsanitize=memory\")\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -fsanitize=memory\")\nendif()\n"
  },
  {
    "path": "docs/ChangeLog",
    "content": "2019-09-07 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.6.1\n\nAleksei (1):\n      Update MSVS project (Remove uri_cmp) (#209)\n\nAlfred E. Heggestad (10):\n      rtmp: update timestamp after complete packet (#172)\n      rtmp: add rtmp_meta() to send metadata on stream (#173)\n      remove gai_strerror stub (#174)\n      rtmp: add function to set handlers (#180)\n      rtmp: proper handling of extended timestamps (#184)\n      docs: update copyright year (#186)\n      debian: fix some warnings and add version to SONAME (#192)\n      uri: remove uri_cmp() (#199)\n      rtmp: add rtmps (tls) support (#195)\n      Update README.md (#205)\n\nAndrey Semashev (1):\n      Fix libdir in pkgconfig (#185)\n\nLennart Grahl (1):\n      Raise FD_EXCEPT on EPOLLHUP (fixes closed pipes) (#159)\n\nRichard Aas (9):\n      debian release 0.6.0-1\n      debian release 0.6.0-2\n      debian release 0.6.0-3\n      dns: fix dname decode buffer checks (#197)\n      regex: fix character buffer check (#198)\n      Debian release 0.6.0-4\n      tls: don't close connection on SSL_ERROR_ZERO_RETURN error (#217)\n      DNS TXT resource record support (#219)\n      debian release 0.6.0-6\n\nSteffen Vogel (1):\n      rpm: add missing file to %install section (#178)\n\n\n2018-11-24 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.6.0\n\n\t* Project URL: https://github.com/creytiv/re\n\n\t* build: add major,minor,patch versions to CFLAGS\n\n\t* odict: add high-level odict helper functions\n\n\t* rtmp: new module for Real Time Messaging Protocol (RTMP)\n\n\t* uri: add uri_decode_hostport\n\n\n2018-09-01 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.5.9\n\n\t* Project URL: https://github.com/creytiv/re\n\n\t* build: Added support for 64-bit MINGW (#131)\n\t\t (thanks Alexander Ushakov)\n\n\t\t fixed inline issue when compiling VS as C++ (#143)\n\t\t (thanks TheSil)\n\n\t* jbuf: zero out jbuf_stat on jbuf flush (#147)\n\t\t(thanks Christian Spielberger)\n\n\t* net: remove net_conn api (old and unused) (#145)\n\t       fix bug in net_if_getname (#144)\n\n\t* sip: get local TCP address in establish handler (#146)\n\n\t* tls: add AES-GCM to DTLS-SRTP (#141)\n\n\n2018-04-20 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.5.8\n\n\t* Project URL: https://github.com/creytiv/re\n\n\t* build: update win32 files (thanks Encamy)\n\n\t* aes: add support for AES-GCM (Galois Counter Mode)\n\n\t* fmt: json/utf8: fix unescaping of unicode code points (#127)\n\t       add utf8_byteseq\n\n\t* mqueue: set non-blocking mode for read/write file descriptors (#122)\n\n\t* srtp: add support for AES-GCM cipher suite (RFC 7714)\n\n\n2018-01-12 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.5.7\n\n\t* Project URL: https://github.com/creytiv/re\n\n\t* build: remove support for Cygwin (#95)\n\t\t remove support for splint (#96)\n\n\t* mem: add secure memory functions (#102)\n\n\t* net: larger buffer for net_if_list (#100)\n\n\t* sipreg: add from_name (Display Name) (#104)\n\n\t* tls: use per connection bio_method (fixes issue #92) (#93)\n\n\n2017-11-06 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.5.6\n\n\t* Project URL: https://github.com/creytiv/re\n\n\t* build: Update ar flags; use deterministic mode (#79)\n\n\t* http: added support for chunked transfer encoding (#90)\n\n\t* ice: Added functions to get selected candidates. (#72)\n\t       (thanks Joachim Bauch)\n\n\t* json: improved performance for mypower10 (#88)\n\t\t(thanks Chris Owen)\n\n\t* mqueue: Pack struct of mqueue messages. (#62)\n\t\t  (thanks Joachim Bauch)\n\n\t* odict: use int instead of enum to avoid vararg promotion (#81)\n\n\t* tls: add dtls_recv_packet() (#89)\n\n\n2017-09-05 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.5.5\n\n\t* Project URL: https://github.com/creytiv/re\n\n\t* ice: move gathering to application\n\n\t* mod: add accessor function to module list\n\n\t* sipreg: Added function sipreg_laddr()\n\n\t* sys: optimize rand_str() and rand_char()\n\n\n2017-06-24 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.5.4\n\n\t* Project URL: https://github.com/creytiv/re\n\n\t* rtp: add extension bit to the api\n\n\n2017-05-13 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.5.3\n\n\t* Project URL: https://github.com/creytiv/re\n\n\t* build: upgrade windows project to VS2015 (thanks Mikhail Barg)\n\t\t makefile improvements (thanks Lennart Grahl)\n\n\t* ice: remove session object \"struct ice\"\n\n\t* telev: add telev_set_srate (thanks Jan Hoffmann)\n\n\n2017-04-08 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.5.2\n\n\t* Project URL: https://github.com/creytiv/re\n\n\t* build: add Debian/kFreeBSD+Hurd (thanks Vasudev Kamath)\n\n\t* ice: make enum ice_role type public\n\n\t* main: fix build for Solaris 11\n\n\t* srtcp: use unsigned for encrypted bit\n\n\t* tls: add tls_openssl_context() accessor function\n\n\n2017-02-04 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.5.1\n\n\t* Project URL: https://github.com/creytiv/re\n\n\t* fmt: print directly to stream using handler (#38)\n\n\t* http: HTTP client improvements (#36)\n\t  - http client connection reuse\n\t  - retry failed requests using fresh connections\n\t  - Handle Connection: close response header\n\n\n2016-11-25 Alfred E. Heggestad <alfred.heggestad@gmail.com>\n\n\t* Version 0.5.0\n\n\t* Project URL: https://github.com/creytiv/re\n\n\t* build: add Dragonfly BSD (thanks Dmitrij D. Czarkoff)\n\t\t remove support for Symbian OS\n\n\t* aes: add support for OpenSSL version 1.1.0\n\n\t* dns: dns/resolv cleanup (#11)\n\t       (thanks Dmitrij D. Czarkoff)\n\n\t* hmac: add support for OpenSSL version 1.1.0\n\n\t* main: remove support for ActiveScheduler (SymbianOS)\n\n\t* tls: add support for OpenSSL version 1.1.0\n\t       add tls_set_certificate_pem()\n\t       add tls_set_certificate_der()\n\t       add dtls_peer()\n\t       add dtls_set_peer()\n\t       add tls_flush_error to dump openssl errors\n\t       (thanks to Lennart Grahl)\n\n\t* udp: add udp_helper_find()\n\n\n2016-06-24 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.17\n\n\t* build: add USE_OPENSSL_AES and USE_OPENSSL_HMAC\n\n\t* dns: add key to dns_rrlist_sort()\n\t       add dns_rrlist_sort_addr\n\n\t* tls: add tls_set_ciphers()\n\t       add tls_set_servername()\n\n\t* sip: fix for stateless SIP requests\n\t       sort DNS RR entries by a fixed key\n\n\t* stun: fix bug with 8-bit and 16-bit attributes on certain\n\t\tplatforms, such as MIPS\n\n\n2016-04-27 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.16\n\n\t* build: fix warnings about DEFAULT_SOURCE with new glibc\n\n\t* lock: fix debian build without HAVE_PTHREAD_RWLOCK\n\n\t* rand: add arc4random (based on patch from Dmitrij D. Czarkoff)\n\n\t* rtcp: adjust mbuf positions for RTCP_PSFB_AFB decoding\n\n\t* tls: add tls_cipher_name()\n\t       add dtls_set_mtu()\n\n\n2016-02-06 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.15\n\n\t* build: fix warnings about DEFAULT_SOURCE with new glibc\n\t\t fix compile error for mingw32 (thanks Dmitrij D. Czarkoff)\n\n\t* aes: handle buffers with zero length\n\n\t* dns: add support for multiple DNS name-servers on Android\n\t\n\t* hmac: add support for HMAC-SHA256\n\n\t* rtp: fix packet-loss calc when first packet has seq=0\n\n\t* srtp: split error code in ENOSR and ENOMEM\n\n\t* stun: keepalive: handle method BINDING only\n\n\t* telev: add a maximum queue size\n\n\t* uri: fix a potential read buffer overflow\n\n\n2015-10-24 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.14\n\n\t* New modules: json and odict\n\n\t* build: add pkg-config file (thanks to William King)\n\t\n\t* re_types: added a portable __REFUNC__\n\n\t* fmt: add utf8_encode/decode, used by JSON module\n\n\t* hash: add hash_fast() function\n\n\t* json: new JavaScript Object Notation (JSON) module\n\n\t* main: fix order of kqueue setting events (WRITE,READ)\n\n\t* odict: new Ordered Dictionary module\n\n\t* sip: reverse order of transport enumeration for SRV-records\n\n\t* udp,tcp,net: add __USE_XOPEN2K (thanks Dmitrij D. Czarkoff)\n\n\n2015-07-01 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.13\n\n\t* aes: added support for CommonCrypto API\n\n\t* fmt: pl_float() handles negative numbers now\n\n\t* hmac: added support for CommonCrypto API\n\n\t* main: added support for async I/O method `kqueue'\n\t\tthis is now the default on platforms like OSX/iOS,\n\t\tFreeBSD, NetBSD and OpenBSD.\n\n\t* mem: added mem_reallocarray(), inspired by OpenBSD\n\n\t* net: added net_default_gateway_get()\n\n\t* tls: use RSA_generate_key_ex() instead of deprecated functions\n\n\n2015-03-16 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.12\n\n\t* ice: added ice_ prefix to some functions and types\n               fix bug in priority calculations (thanks to Daniel Ma)\n\n\t* mqueue: fix bug with leaking sockets on Windows-32\n\n\t* rtp: fix bug with RTCP timestamp calculation\n\n\t* sip: export sip_transp_laddr()\n\n\t* tls: added more TLS methods\n\n\n2014-12-09 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.11\n\n\t* build: export USE_TLS and USE_DTLS flags in re.mk makefile\n\t\t detect sysctl.h and epoll.h for multi-arch platforms\n\t\t dont use libresolv for openbsd\n\n\t* main: check that maxfds is less than FD_SETSIZE (for select method)\n\n\t* dtls: added udp-socket accessor and function to set handlers\n\n\t* stun: added support for DTLS-transport\n\t\tadded doxygen comments\n\n\t* tls: added function to set certificate from a string\n\n\t* turn: added support for DTLS-transport\n\n\n2014-10-19 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.10\n\n\t* dns: added support for using multi-threaded libresolv\n               (thanks to Thomas Klausner)\n\t       (thanks to Dmitrij D. Czarkoff for testing on OpenBSD)\n\t\n\t* dtls: added support for sending DTLS over e.g. TURN\n\t\t(this is done by adding 4 bytes of headroom in the packet)\n\n\t* ice: added ice_set_conf()\n\t       continue checklist if send fails (thanks to SnakE)\n\t\n\t* mbuf: added mbuf_shift()\n\n\t* sdp: added sdp_media_session_rattr()\n\t       added extmap decoding RFC 5285 (thanks to Jose Carlos Pujol)\n\n\t* sip: added struct sip_contact and related functions\n\n\t* sipevent: added support for URI in contact-user, used for GRUU\n\t\t    (thanks to Juha Heinanen)\n\t\n\t* sipreg: added \"gruu\" to list of Supported extensions\n\t\n\t* sipsess: added support for URI in contact-user, used for GRUU\n\t\t   (thanks to Juha Heinanen)\n\n\n2014-06-18 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.9\n\n\t* aes: clear openssl error queue in error cases\n\n\t* bfcp: disabled support for DTLS-transport\n\n\t* hmac: clear openssl error queue in error cases\n\n\t* http: make response parsing a bit more robust\n\n\t* main: make use of openssl's multi-threading API\n\n\t* rtcp: added Round-Trip Time (RTT) field to struct rtcp_stats\n                fix some rounding errors\n\n\t* sa: added padding buffer to struct sa union\n\n\t* sdp: improved handling of unsupported transport protocols\n\t       and sub-sequence offer/answer exchanges\n\n\t* srtp: added support for Secure Real-time Transport Protocol (SRTP)\n                (RFC 3711 and RFC 6188)\n\n\t* sys: rand -- clear openssl error queue in error cases\n\n\t* tls: added support for generating self-signed certificates\n\t       added support for the SRTP-extension using openssl\n\t       clear openssl error queue in error cases\n\t       improved DTLS api\n\n\n2014-04-11 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.8\n\n\t* build: added support for Apple in cmake\n\n\t* debian: update package\n\n\t* aes: added AES (Advanced Encryption Standard) wrapper\n\n\t* hmac: added a stateful HMAC wrapper\n\n\t* http: added a HTTP client\n\n\t* ice: minor API improvements\n\n\t* msg: added a Generic message component library\n\t       (shared by SIP module and HTTP module)\n\n\t* sdp: added sdp_media_laddr() to get local transport address\n\n\t* sip: change struct sip_msg to use new struct msg_ctype\n\t       fixed a bug in parsing of Via headers\n\n\t* sipsess: added sipsess_set_close_headers() to set any additional\n\t\t   SIP headers for BYE or BYE-response\n\n\t* net: fixed a bug in net_rt_list() for darwin\n\n\t* websock: added WebSocket Protocol (RFC 6455)\n\n\n2014-01-05 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.7\n\n\t* build: added support for LLVM clang compiler\n\n\t* dns: added support for getting Android nameserver address\n\n\t* ice: minor debug tuning\n\n\t* sipsess: only send INFO when dialog is established\n\t\n\n2013-11-12 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.6\n\n\t* bfcp: fix bitwise operator for bool (thanks Tomasz Ostrowski)\n\n\t* dns: do not connect the UDP socket\n\n\t* ice: fix deref of NULL-pointer (thanks Tomasz Ostrowski)\n\n\t* rtp: add support for RTCP AFB (Application-layer Feedback)\n\t       make RTCP decoding more robust\n\n\t* udp: udp_connect() -- add peer address\n\t       add udp_error_handler_set()\n\n\n2013-10-03 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.5\n\n\t* udp: add functions for joining/leaving multicast groups\n\n\t* fmt: re_regex() fix va_end robustness\n\n\t* rtp: set sequence number in range 0-32767\n\n\t* sa: sa_print_addr() fix build when HAVE_INET6 is not set\n\n\n2013-08-27 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.4\n\n\t* base64: added base64_print()\n\n\t* http: added HTTP (Hypertext Transfer Protocol) parser\n\n\t* ice: cleanup and minor bug fixes\n\n\t* main: added external mutex for re_main() loop\n\n\t* sdp: added sdp_media_set_alt_protos()\n\t       added sdp_media_proto()\n\n\t* stun: make API compatible with C++\n\t\tfix endianess-bug in STUN attributes\n\n\t* sys: sys_rel_get() detect new kernels\n\n\t* tls: fingerprint: add SHA-256\n\t       proper error handling, call ERR_clear_error()\n\t\n\n2013-05-05 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.3\n\n\t* bfcp: added udp\n\n\t* dns: added doxygen comments\n\n\t* fmt: added str_cmp()\n\n\t* mbuf: make mbuf_get_left() and mbuf_get_space() more robust\n\t        added mbuf_fill()\n\n\t* mod: fixed a bug in mod_find()\n\n\t* mqueue: move handler to mqueue_alloc()\n\n\t* sa: fix building on some Windows platforms\n\n\t* sdp: added sdp_session_lbandwidth()\n\t       added sdp_media_set_fmt_ignore()\n\n\t* stun: make stun_msg_vencode() public\n\t\tunlink element from attribute-list in destructor\n\n\t* tcp: make fd handling more robust\n\n\t* tls: added tls_get_remote_fingerprint()\n\n\n2012-08-10 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.2\n\n\t* added debian build\n\n\t* build: fix building for Ubuntu 12.04\n\n\t* re_types: increase ERRNO values\n\n\t* fmt: re_printf() add support for %m to print errno description\n\t       added str_error()\n\t\n\t* hash: added hash_clear()\n\n\t* list: added list_clear()\n\n\t* net: added net_if_getlinklocal()\n\n\t* rtp: added rtcp_set_srate_tx/rx()\n\t       rtcp_msg_print(): add all types\n\n\t* sa: added sa_print_addr()\n\n\t* sdp: added media encode handler\n\t       sdp_format_add(): added fmtp encode handler (breaks API)\n\n\t* sip: handle merged SIP requests (482 Loop Detected)\n\t       added doxygen comments\n\n\t* sipevent: fix bug in handler argument\n\t\n\t* sys: added sys_username()\n\t       added fs_mkdir() and fs_gethome()\n\n\t* tcp: added tcp_conn_txqsz()\n\t       fix enqueue buffer size\n\t       handle scopeid for IPv6 linklocal\n\n\t* tmr: added tmr_status %H handler\n\t\n\t* udp: handle scopeid for IPv6 linklocal\n\t\n\t\n2012-04-21 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.1\n\n\t* updated doxygen comments for sdp and tls\n\n\t* dns: dnsc_srv_set: copy DNS servers to fixed-size array\n\n\t* fmt: added str_isset()\n\t       added fmt_param_exists()\n\n\t* rtp: fix lock protection of RTCP txstat during read\n\n\t* sdp: sdp_media_align_formats: move unsupported codecs to end of list\n\n\t* sip: limit startline to max 8192 bytes\n\t       limit tcp buffersize to max 65536 bytes\n\n\t* tcp: limit the size of the tcp send queue\n\n\n2011-12-25 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.4.0\n\n\t* updated doxygen comments\n\n\t* build: add support for CMake (thanks to Stefan Radomski)\n\t\t clean up OS and ARCH detection\n\n\t* dns: fix potential infinite loop in dname decode\n\n\t* sip: change struct sip_via transp to enum sip_transp (breaks API)\n\t       added sip_transp_isladdr() and sip_transp_port()\n\t       added sip_dialog_fork(), sip_dialog_lseq(),\n\t       sip_dialog_established(), sip_dialog_cmp_half()\n\n\t* sipevent: new module for SIP Event framework (RFC 3265, RFC 3515)\n\n\t* sys: add portable sys_usleep() and sys_msleep()\n\t\n\t* tcp: add tcp_send_helper()\n\n\t* tls: add support for DTLSv1 (Datagram TLS)\n\t       tls_alloc: add tls_method and layer (breaks API)\n\t       tls_tcp: use custom BIO to send data\n\n\t* tmr: optimize tmr_start() where delay == 0\n\n\t* turn: add stun_msg to turnc handler (breaks API)\n\n\t* udp: add udp_send_helper()\n\n\n2011-09-07 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.3.0\n\n\t* build support for native mingw32 (thanks to Michael Erskine)\n\n\t* bfcp: new module for The Binary Floor Control Protocol (RFC 4582)\n\n\t* g711: module moved to librem\n\n\t* sipreg: fix a bug in failwait() calculation\n\n\t* stun: add support for STUNS (secure STUN)\n\n\t* tcp: added tcp_set_handlers()\n\n\t* turn: added send/recv functions\n\n\n2011-05-20 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.2.0\n\n\t* updated doxygen comments\n\n\t* conf: added conf_get_bool()\n\n\t* dns: fixed a bug in get_resolv_dns()\n\n\t* fmt: added pl_x64() pl_float() fmt_gmtime()\n\t\n\t* hash: added hash_valid_size()\n\n\t* httpauth: clean up API\n\n\t* ice: many improvements and bugfixes\n\n\t* main: fix a bug if re_main() fails\n\n\t* mbuf: added mbuf_debug()\n\n\t* natbd: fixed some race conditions and memory leaks\n\n\t* rtp: added rtcp_enable_mux() (RFC 5761; RTP and RTCP multiplexing)\n\n\t* sdp: fixed setting RTCP port if RTP port is zero\n\n\t* sip: added support for SIP Outbound (RFC 5626)\n\t       added sip_msg_hdr_count() sip_msg_xhdr_count()\n\t       added sip_msg_hdr_has_value() sip_msg_xhdr_has_value()\n\t       added sip_auth_reset()\n\t       handle multiple authenticate headers with equal realm value\n\t       fixed a bug with loose-routing in Route header\n\t       fixed decoding of Via header\n\t\n\t* sipreg: added support for SIP Outbound (breaks API compatibility)\n\n\t* sipsess: fix a bug in sipsess_reject() if fmt is NULL\n\t\n\t* tcp: update tcp_register_helper() (breaks API)\n\n\t* tmr: removed tmrl from 'struct tmr' (breaks ABI)\n\t       added tmr_isrunning()\n\n\t* udp: update udp_register_helper() (breaks API)\n\n\t* uri: fix optional username in uri_decode()\n\n\n2010-11-05 Alfred E. Heggestad <aeh@db.org>\n\n\t* Version 0.1.0\n\t\n\t* Initial Release\n"
  },
  {
    "path": "docs/TODO",
    "content": "TODO\n\n-------------------------------------------------------------------------------\nVersion v0.x.y\n\n  tmr: scaling using binary heap or hash\n\n-------------------------------------------------------------------------------\n"
  },
  {
    "path": "docs/main.dox",
    "content": "/**\n * \\mainpage libre Development Documentation\n *\n * Development documentation for libre\n *\n * \n * \\include{doc} README.md\n *\n *\n * \\section modules modules\n */\n"
  },
  {
    "path": "include/re.h",
    "content": "/**\n * @file re.h  Wrapper for all header files\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#ifndef RE_H__\n#define RE_H__\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Basic types */\n#include \"re_types.h\"\n#include \"re_fmt.h\"\n#include \"re_mbuf.h\"\n#include \"re_msg.h\"\n#include \"re_list.h\"\n#include \"re_sa.h\"\n\n/* Library modules */\n#include \"re_aes.h\"\n#include \"re_async.h\"\n#include \"re_base64.h\"\n#include \"re_bfcp.h\"\n#include \"re_btrace.h\"\n#include \"re_conf.h\"\n#include \"re_convert.h\"\n#include \"re_crc32.h\"\n#include \"re_dns.h\"\n#include \"re_h264.h\"\n#include \"re_hash.h\"\n#include \"re_hmac.h\"\n#include \"re_http.h\"\n#include \"re_httpauth.h\"\n#include \"re_ice.h\"\n#include \"re_net.h\"\n#include \"re_main.h\"\n#include \"re_md5.h\"\n#include \"re_mem.h\"\n#include \"re_mod.h\"\n#include \"re_mqueue.h\"\n#include \"re_odict.h\"\n#include \"re_json.h\"\n#include \"re_rtmp.h\"\n#include \"re_rtp.h\"\n#include \"re_rtpext.h\"\n#include \"re_sdp.h\"\n#include \"re_uri.h\"\n#include \"re_sip.h\"\n#include \"re_sipevent.h\"\n#include \"re_sipreg.h\"\n#include \"re_sipsess.h\"\n#include \"re_stun.h\"\n#include \"re_srtp.h\"\n#include \"re_sys.h\"\n#include \"re_tcp.h\"\n#include \"re_telev.h\"\n#include \"re_thread.h\"\n#include \"re_tmr.h\"\n#include \"re_trace.h\"\n#include \"re_tls.h\"\n#include \"re_turn.h\"\n#include \"re_udp.h\"\n#include \"re_unixsock.h\"\n#include \"re_websock.h\"\n#include \"re_shim.h\"\n#include \"re_trice.h\"\n#include \"re_pcp.h\"\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/re_aes.h",
    "content": "/**\n * @file re_aes.h Interface to AES (Advanced Encryption Standard)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#ifndef AES_BLOCK_SIZE\n#define AES_BLOCK_SIZE 16\n#endif\n\n/** AES mode */\nenum aes_mode {\n\tAES_MODE_CTR,  /**< AES Counter mode (CTR) */\n\tAES_MODE_GCM,  /**< AES Galois Counter Mode (GCM) */\n};\n\nstruct aes;\n\nint  aes_alloc(struct aes **stp, enum aes_mode mode,\n\t       const uint8_t *key, size_t key_bits,\n\t       const uint8_t *iv);\nvoid aes_set_iv(struct aes *aes, const uint8_t *iv);\nint  aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len);\nint  aes_decr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len);\nint  aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen);\nint  aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen);\n"
  },
  {
    "path": "include/re_async.h",
    "content": "/**\n * @file re_async.h async\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n\n#ifndef RE_H_ASYNC__\n#define RE_H_ASYNC__\nstruct re_async;\n\ntypedef int(re_async_work_h)(void *arg);\ntypedef void(re_async_h)(int err, void *arg);\n\nint re_async_alloc(struct re_async **asyncp, uint16_t workers);\nint re_async(struct re_async *a, intptr_t id, re_async_work_h *workh,\n\t     re_async_h *cb, void *arg);\nvoid re_async_cancel(struct re_async *async, intptr_t id);\n\n#endif\n"
  },
  {
    "path": "include/re_atomic.h",
    "content": "/**\n * @file re_atomic.h  Atomic support\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n\n#ifndef RE_H_ATOMIC__\n#define RE_H_ATOMIC__\n\n/* C11 */\n#if defined(HAVE_ATOMIC) && __STDC_VERSION__ >= 201112L &&                    \\\n\t!defined(__STDC_NO_ATOMICS__)\n\n#include <stdatomic.h>\n\n#define RE_ATOMIC _Atomic\n\n#define RE_ATOMIC_BOOL_LOCK_FREE ATOMIC_BOOL_LOCK_FREE\n#define RE_ATOMIC_CHAR_LOCK_FREE ATOMIC_CHAR_LOCK_FREE\n#define RE_ATOMIC_WCHAR_T_LOCK_FREE ATOMIC_WCHAR_T_LOCK_FREE\n#define RE_ATOMIC_SHORT_LOCK_FREE ATOMIC_SHORT_LOCK_FREE\n#define RE_ATOMIC_INT_LOCK_FREE ATOMIC_INT_LOCK_FREE\n#define RE_ATOMIC_LONG_LOCK_FREE ATOMIC_LONG_LOCK_FREE\n#define RE_ATOMIC_LLONG_LOCK_FREE ATOMIC_LLONG_LOCK_FREE\n#define RE_ATOMIC_POINTER_LOCK_FREE ATOMIC_POINTER_LOCK_FREE\n\n#define re_memory_order_relaxed memory_order_relaxed\n#define re_memory_order_acquire memory_order_acquire\n#define re_memory_order_release memory_order_release\n#define re_memory_order_acq_rel memory_order_acq_rel\n#define re_memory_order_seq_cst memory_order_seq_cst\n\n#define re_atomic_store(_a, _v, _mo) \\\n\tatomic_store_explicit(_a, _v, _mo)\n\n#define re_atomic_load(_a, _mo) \\\n\tatomic_load_explicit(_a, _mo)\n\n#define re_atomic_exchange(_a, _v, _mo) \\\n\tatomic_exchange_explicit(_a, _v, _mo)\n\n#define re_atomic_compare_exchange_strong(\\\n\t_a, _expected, _desired, _success_mo, _fail_mo) \\\n\tatomic_compare_exchange_strong_explicit(\\\n\t\t_a, _expected, _desired, _success_mo, _fail_mo)\n\n#define re_atomic_compare_exchange_weak(\\\n\t_a, _expected, _desired, _success_mo, _fail_mo) \\\n\tatomic_compare_exchange_weak_explicit(\\\n\t\t_a, _expected, _desired, _success_mo, _fail_mo)\n\n#define re_atomic_fetch_add(_a, _v, _mo) \\\n\tatomic_fetch_add_explicit(_a, _v, _mo)\n\n#define re_atomic_fetch_sub(_a, _v, _mo) \\\n\tatomic_fetch_sub_explicit(_a, _v, _mo)\n\n#define re_atomic_fetch_or(_a, _v, _mo) \\\n\tatomic_fetch_or_explicit(_a, _v, _mo)\n\n#define re_atomic_fetch_xor(_a, _v, _mo) \\\n\tatomic_fetch_xor_explicit(_a, _v, _mo)\n\n#define re_atomic_fetch_and(_a, _v, _mo) \\\n\tatomic_fetch_and_explicit(_a, _v, _mo)\n\n/* gcc-style __atomic* intrinsics.\n * Note: clang-cl also supports these, even though it impersonates MSVC. */\n#elif (defined(__GNUC__) || defined(__clang__)) && \\\n\tdefined(__GCC_ATOMIC_BOOL_LOCK_FREE) && \\\n\tdefined(__GCC_ATOMIC_CHAR_LOCK_FREE) && \\\n\tdefined(__GCC_ATOMIC_WCHAR_T_LOCK_FREE) && \\\n\tdefined(__GCC_ATOMIC_SHORT_LOCK_FREE) && \\\n\tdefined(__GCC_ATOMIC_INT_LOCK_FREE) && \\\n\tdefined(__GCC_ATOMIC_LONG_LOCK_FREE) && \\\n\tdefined(__GCC_ATOMIC_LLONG_LOCK_FREE) && \\\n\tdefined(__GCC_ATOMIC_POINTER_LOCK_FREE) && \\\n\tdefined(__ATOMIC_RELAXED) && defined(__ATOMIC_ACQUIRE) && \\\n\tdefined(__ATOMIC_RELEASE) && defined(__ATOMIC_ACQ_REL) && \\\n\tdefined(__ATOMIC_SEQ_CST)\n\n#define RE_ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE\n#define RE_ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE\n#define RE_ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE\n#define RE_ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE\n#define RE_ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE\n#define RE_ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE\n#define RE_ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE\n#define RE_ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE\n\n#define re_memory_order_relaxed __ATOMIC_RELAXED\n#define re_memory_order_acquire __ATOMIC_ACQUIRE\n#define re_memory_order_release __ATOMIC_RELEASE\n#define re_memory_order_acq_rel __ATOMIC_ACQ_REL\n#define re_memory_order_seq_cst __ATOMIC_SEQ_CST\n\n#define re_atomic_store(_a, _v, _mo) \\\n\t__atomic_store_n(_a, _v, _mo)\n\n#define re_atomic_load(_a, _mo) \\\n\t__atomic_load_n(_a, _mo)\n\n#define re_atomic_exchange(_a, _v, _mo) \\\n\t__atomic_exchange_n(_a, _v, _mo)\n\n#define re_atomic_compare_exchange_strong(\\\n\t_a, _expected, _desired, _success_mo, _fail_mo) \\\n\t__atomic_compare_exchange_n(\\\n\t\t_a, _expected, _desired, 0, _success_mo, _fail_mo)\n\n#define re_atomic_compare_exchange_weak(\\\n\t_a, _expected, _desired, _success_mo, _fail_mo) \\\n\t__atomic_compare_exchange_n(\\\n\t\t_a, _expected, _desired, 1, _success_mo, _fail_mo)\n\n#define re_atomic_fetch_add(_a, _v, _mo) \\\n\t__atomic_fetch_add(_a, _v, _mo)\n\n#define re_atomic_fetch_sub(_a, _v, _mo) \\\n\t__atomic_fetch_sub(_a, _v, _mo)\n\n#define re_atomic_fetch_or(_a, _v, _mo) \\\n\t__atomic_fetch_or(_a, _v, _mo)\n\n#define re_atomic_fetch_xor(_a, _v, _mo) \\\n\t__atomic_fetch_xor(_a, _v, _mo)\n\n#define re_atomic_fetch_and(_a, _v, _mo) \\\n\t__atomic_fetch_and(_a, _v, _mo)\n\n/* gcc-style __sync* intrinsics. */\n#elif defined(__GNUC__) && \\\n\t(defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) || \\\n\tdefined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || \\\n\tdefined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || \\\n\tdefined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8))\n\n#if !defined(__SIZEOF_SHORT__) || !defined(__SIZEOF_INT__) || \\\n\t!defined(__SIZEOF_LONG__) || !defined(__SIZEOF_LONG_LONG__)\n#include <limits.h>\n#endif\n#if !defined(__SIZEOF_POINTER__)\n#include <stdint.h>\n#endif\n#if !defined(__SIZEOF_WCHAR_T__)\n#include <wchar.h>\n#endif\n\n#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1)\n#define RE_ATOMIC_CHAR_LOCK_FREE 2\n#endif\n\n#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2)\n#if (defined(__SIZEOF_SHORT__) && __SIZEOF_SHORT__ == 2) || \\\n\t(defined(USHRT_MAX) && USHRT_MAX == 0xffffu)\n#define RE_ATOMIC_SHORT_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_INT__) && __SIZEOF_INT__ == 2) || \\\n\t(defined(UINT_MAX) && UINT_MAX == 0xffffu)\n#define RE_ATOMIC_INT_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 2) || \\\n\t(defined(ULONG_MAX) && ULONG_MAX == 0xffffu)\n#define RE_ATOMIC_LONG_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ == 2) || \\\n\t(defined(ULLONG_MAX) && ULLONG_MAX == 0xffffu)\n#define RE_ATOMIC_LLONG_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 2) || \\\n\t(defined(UINTPTR_MAX) && UINTPTR_MAX == 0xffffu)\n#define RE_ATOMIC_POINTER_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2) || \\\n\t(defined(WCHAR_MAX) && (WCHAR_MAX == 0xffff || WCHAR_MAX == 0x7fff))\n#define RE_ATOMIC_WCHAR_T_LOCK_FREE 2\n#endif\n#endif\n\n#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)\n#if (defined(__SIZEOF_SHORT__) && __SIZEOF_SHORT__ == 4) || \\\n\t(defined(USHRT_MAX) && USHRT_MAX == 0xffffffffu)\n#define RE_ATOMIC_SHORT_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_INT__) && __SIZEOF_INT__ == 4) || \\\n\t(defined(UINT_MAX) && UINT_MAX == 0xffffffffu)\n#define RE_ATOMIC_INT_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) || \\\n\t(defined(ULONG_MAX) && ULONG_MAX == 0xffffffffu)\n#define RE_ATOMIC_LONG_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ == 4) || \\\n\t(defined(ULLONG_MAX) && ULLONG_MAX == 0xffffffffu)\n#define RE_ATOMIC_LLONG_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 4) || \\\n\t(defined(UINTPTR_MAX) && UINTPTR_MAX == 0xffffffffu)\n#define RE_ATOMIC_POINTER_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 4) || \\\n\t(defined(WCHAR_MAX) && (WCHAR_MAX == 0xffffffff || \\\n\t\tWCHAR_MAX == 0x7fffffff))\n#define RE_ATOMIC_WCHAR_T_LOCK_FREE 2\n#endif\n#endif\n\n#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)\n#if (defined(__SIZEOF_SHORT__) && __SIZEOF_SHORT__ == 8) || \\\n\t(defined(USHRT_MAX) && USHRT_MAX == 0xffffffffffffffffu)\n#define RE_ATOMIC_SHORT_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_INT__) && __SIZEOF_INT__ == 8) || \\\n\t(defined(UINT_MAX) && UINT_MAX == 0xffffffffffffffffu)\n#define RE_ATOMIC_INT_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8) || \\\n\t(defined(ULONG_MAX) && ULONG_MAX == 0xffffffffffffffffu)\n#define RE_ATOMIC_LONG_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ == 8) || \\\n\t(defined(ULLONG_MAX) && ULLONG_MAX == 0xffffffffffffffffu)\n#define RE_ATOMIC_LLONG_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8) || \\\n\t(defined(UINTPTR_MAX) && UINTPTR_MAX == 0xffffffffffffffffu)\n#define RE_ATOMIC_POINTER_LOCK_FREE 2\n#endif\n#if (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 8) || \\\n\t(defined(WCHAR_MAX) && (WCHAR_MAX == 0xffffffffffffffff || \\\n\t\tWCHAR_MAX == 0x7fffffffffffffff))\n#define RE_ATOMIC_WCHAR_T_LOCK_FREE 2\n#endif\n#endif\n\n#if !defined(RE_ATOMIC_CHAR_LOCK_FREE)\n#define RE_ATOMIC_CHAR_LOCK_FREE 0\n#endif\n#if !defined(RE_ATOMIC_SHORT_LOCK_FREE)\n#define RE_ATOMIC_SHORT_LOCK_FREE 0\n#endif\n#if !defined(RE_ATOMIC_INT_LOCK_FREE)\n#define RE_ATOMIC_INT_LOCK_FREE 0\n#endif\n#if !defined(RE_ATOMIC_LONG_LOCK_FREE)\n#define RE_ATOMIC_LONG_LOCK_FREE 0\n#endif\n#if !defined(RE_ATOMIC_LLONG_LOCK_FREE)\n#define RE_ATOMIC_LLONG_LOCK_FREE 0\n#endif\n#if !defined(RE_ATOMIC_POINTER_LOCK_FREE)\n#define RE_ATOMIC_POINTER_LOCK_FREE 0\n#endif\n#if !defined(RE_ATOMIC_WCHAR_T_LOCK_FREE)\n#define RE_ATOMIC_WCHAR_T_LOCK_FREE 0\n#endif\n\n/* Assume bool is always 1 byte. Add platform-specific exceptions,\n * if needed. */\n#define RE_ATOMIC_BOOL_LOCK_FREE RE_ATOMIC_CHAR_LOCK_FREE\n\n/* These constants match __ATOMIC_* predefined macros on\n * gcc versions that support __atomic intrinsics. */\n#define re_memory_order_relaxed 0\n#define re_memory_order_acquire 2\n#define re_memory_order_release 3\n#define re_memory_order_acq_rel 4\n#define re_memory_order_seq_cst 5\n\n#if defined(__x86_64__)\n\n#define re_atomic_store(_a, _v, _mo) \\\n\t__extension__\\\n\t({\\\n\t\t__typeof__(*(_a)) _val = (_v);\\\n\t\tif ((_mo) != re_memory_order_seq_cst) {\\\n\t\t\t__asm__ __volatile__ (\"mov %1, %0\"\\\n\t\t\t\t: \"=m\" (*(_a))\\\n\t\t\t\t: \"q\" (_val)\\\n\t\t\t\t: \"memory\");\\\n\t\t}\\\n\t\telse {\\\n\t\t\t__asm__ __volatile__ (\"xchg %1, %0\"\\\n\t\t\t\t: \"=m\" (*(_a)), \"+q\" (_val)\\\n\t\t\t\t: \\\n\t\t\t\t: \"memory\");\\\n\t\t}\\\n\t})\n\n#define re_atomic_load(_a, _mo) \\\n\t__extension__\\\n\t({\\\n\t\t__typeof__(*(_a)) _val;\\\n\t\t__asm__ __volatile__ (\"mov %1, %0\"\\\n\t\t\t: \"=q\" (_val)\\\n\t\t\t: \"m\" (*(_a))\\\n\t\t\t: \"memory\");\\\n\t\t_val;\\\n\t})\n\n#define re_atomic_exchange(_a, _v, _mo) \\\n\t__extension__\\\n\t({\\\n\t\t__typeof__(*(_a)) _val = (_v);\\\n\t\t__asm__ __volatile__ (\"xchg %1, %0\"\\\n\t\t\t: \"+m\" (*(_a)), \"+q\" (_val)\\\n\t\t\t: \\\n\t\t\t: \"memory\");\\\n\t\t_val;\\\n\t})\n\n#elif defined(__i386__)\n\n#define re_atomic_store(_a, _v, _mo) \\\n\t__extension__\\\n\t({\\\n\t\t__typeof__(*(_a)) _val = (_v);\\\n\t\tif (sizeof(_val) < 8) {\\\n\t\t\tif ((_mo) != re_memory_order_seq_cst) {\\\n\t\t\t\t__asm__ __volatile__ (\"mov %1, %0\"\\\n\t\t\t\t\t: \"=m\" (*(_a))\\\n\t\t\t\t\t: \"q\" (_val)\\\n\t\t\t\t\t: \"memory\");\\\n\t\t\t}\\\n\t\t\telse {\\\n\t\t\t\t__asm__ __volatile__ (\"xchg %1, %0\"\\\n\t\t\t\t\t: \"=m\" (*(_a)), \"+q\" (_val)\\\n\t\t\t\t\t: \\\n\t\t\t\t\t: \"memory\");\\\n\t\t\t}\\\n\t\t}\\\n\t\telse {\\\n\t\t\t__typeof__(*(_a)) _expected = *(_a);\\\n\t\t\twhile (1) {\\\n\t\t\t\t__typeof__(*(_a)) _prev_val =\\\n\t\t\t\t\t__sync_val_compare_and_swap(\\\n\t\t\t\t\t\t_a, _expected, _val);\\\n\t\t\t\tif (_prev_val == _expected)\\\n\t\t\t\t\tbreak;\\\n\t\t\t\t_expected = _prev_val;\\\n\t\t\t}\\\n\t\t}\\\n\t})\n\n#define re_atomic_load(_a, _mo) \\\n\t__extension__\\\n\t({\\\n\t\t__typeof__(*(_a)) _val;\\\n\t\tif (sizeof(_val) < 8) {\\\n\t\t\t__asm__ __volatile__ (\"mov %1, %0\"\\\n\t\t\t\t: \"=q\" (_val)\\\n\t\t\t\t: \"m\" (*(_a))\\\n\t\t\t\t: \"memory\");\\\n\t\t}\\\n\t\telse {\\\n\t\t\t_val = __sync_val_compare_and_swap(\\\n\t\t\t\t_a,\\\n\t\t\t\t(__typeof__(*(_a)))0,\\\n\t\t\t\t(__typeof__(*(_a)))0);\\\n\t\t}\\\n\t\t_val;\\\n\t})\n\n#define re_atomic_exchange(_a, _v, _mo) \\\n\t__extension__\\\n\t({\\\n\t\t__typeof__(*(_a)) _val = (_v);\\\n\t\tif (sizeof(_val) < 8) {\\\n\t\t\t__asm__ __volatile__ (\"xchg %1, %0\"\\\n\t\t\t\t: \"+m\" (*(_a)), \"+q\" (_val)\\\n\t\t\t\t: \\\n\t\t\t\t: \"memory\");\\\n\t\t}\\\n\t\telse {\\\n\t\t\t__typeof__(*(_a)) _expected = *(_a);\\\n\t\t\twhile (1) {\\\n\t\t\t\t__typeof__(*(_a)) _prev_val =\\\n\t\t\t\t\t__sync_val_compare_and_swap(\\\n\t\t\t\t\t\t_a, _expected, _val);\\\n\t\t\t\tif (_prev_val == _expected)\\\n\t\t\t\t\tbreak;\\\n\t\t\t\t_expected = _prev_val;\\\n\t\t\t}\\\n\t\t\t_val = _expected;\\\n\t\t}\\\n\t\t_val;\\\n\t})\n\n#else\n\n#define re_atomic_store(_a, _v, _mo) \\\n\t(void)re_atomic_exchange(_a, _v, _mo)\n\n#define re_atomic_load(_a, _mo) \\\n\t__sync_val_compare_and_swap(\\\n\t\t_a, (__typeof__(*(_a)))0, (__typeof__(*(_a)))0)\n\n#define re_atomic_exchange(_a, _v, _mo) \\\n\t__extension__\\\n\t({\\\n\t\t__typeof__(*(_a)) _val = (_v);\\\n\t\t__typeof__(*(_a)) _expected = *(_a);\\\n\t\twhile (1) {\\\n\t\t\t__typeof__(*(_a)) _prev_val =\\\n\t\t\t\t__sync_val_compare_and_swap(\\\n\t\t\t\t\t_a, _expected, _val);\\\n\t\t\tif (_prev_val == _expected)\\\n\t\t\t\tbreak;\\\n\t\t\t_expected = _prev_val;\\\n\t\t}\\\n\t\t_expected;\\\n\t})\n\n#endif\n\n#define re_atomic_compare_exchange_strong(\\\n\t_a, _expected, _desired, _success_mo, _fail_mo) \\\n\t__extension__\\\n\t({\\\n\t\t__typeof__(*(_a)) _exp_val = *(_expected);\\\n\t\t__typeof__(*(_a)) _prev_val =\\\n\t\t\t__sync_val_compare_and_swap(_a, _exp_val,\\\n\t\t\t\t(__typeof__(*(_a)))(_desired));\\\n\t\t*(_expected) = _prev_val;\\\n\t\t_prev_val == _exp_val;\\\n\t})\n\n#define re_atomic_compare_exchange_weak(\\\n\t_a, _expected, _desired, _success_mo, _fail_mo) \\\n\tre_atomic_compare_exchange_strong(\\\n\t\t_a, _expected, _desired, _success_mo, _fail_mo)\n\n#define re_atomic_fetch_add(_a, _v, _mo) \\\n\t__sync_fetch_and_add(_a, (__typeof__(*(_a)))(_v))\n\n#define re_atomic_fetch_sub(_a, _v, _mo) \\\n\t__sync_fetch_and_sub(_a, (__typeof__(*(_a)))(_v))\n\n#define re_atomic_fetch_or(_a, _v, _mo) \\\n\t__sync_fetch_and_or(_a, (__typeof__(*(_a)))(_v))\n\n#define re_atomic_fetch_xor(_a, _v, _mo) \\\n\t__sync_fetch_and_xor(_a, (__typeof__(*(_a)))(_v))\n\n#define re_atomic_fetch_and(_a, _v, _mo) \\\n\t__sync_fetch_and_and(_a, (__typeof__(*(_a)))(_v))\n\n/* MSVC Interlocked* intrinsics. This needs to go after clang to let clang-cl\n * get handled above. */\n#elif defined(_MSC_VER)\n\n#include <assert.h>\n#include <intrin.h>\n#include \"re_types.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define RE_ATOMIC_BOOL_LOCK_FREE 2\n#define RE_ATOMIC_CHAR_LOCK_FREE 2\n#define RE_ATOMIC_WCHAR_T_LOCK_FREE 2\n#define RE_ATOMIC_SHORT_LOCK_FREE 2\n#define RE_ATOMIC_INT_LOCK_FREE 2\n#define RE_ATOMIC_LONG_LOCK_FREE 2\n#define RE_ATOMIC_LLONG_LOCK_FREE 2\n#define RE_ATOMIC_POINTER_LOCK_FREE 2\n\n/* These constants don't matter but for consistency they match\n * values in std::memory_order from <atomic> in C++.\n * There are specialized intrinsics for ARM and ARM64\n * for different memory ordering types, but they are not used (yet) below. */\n#define re_memory_order_relaxed 0\n#define re_memory_order_acquire 2\n#define re_memory_order_release 3\n#define re_memory_order_acq_rel 4\n#define re_memory_order_seq_cst 5\n\nstatic unsigned __int64 _re_atomic_exchange(\n\tsize_t size, void *a, unsigned __int64 v);\n\n#if defined(_M_IX86) || defined(_M_AMD64)\n\nstatic __forceinline void _re_atomic_store(\n\tsize_t size, void *a, unsigned __int64 v, unsigned int mo)\n{\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\tif (mo != re_memory_order_seq_cst) {\n\t\t_ReadWriteBarrier();\n\t\tswitch (size) {\n\t\tcase 1u:\n\t\t\t*(volatile unsigned __int8*)a = (unsigned __int8)v;\n\t\t\tbreak;\n\t\tcase 2u:\n\t\t\t*(volatile unsigned __int16*)a = (unsigned __int16)v;\n\t\t\tbreak;\n\t\tcase 4u:\n\t\t\t*(volatile unsigned __int32*)a = (unsigned __int32)v;\n\t\t\tbreak;\n\t\tdefault:\n#if defined(_M_IX86)\n\t\t\t{\n\t\t\t\t__int64 prev_val =\n\t\t\t\t\t*(const volatile __int64*)(a);\n\t\t\t\twhile (1) {\n\t\t\t\t\t__int64 prev_val2 =\n\t\t\t\t\t\t_InterlockedCompareExchange64(\n\t\t\t\t\t\t\t(__int64*)a,\n\t\t\t\t\t\t\t(__int64)v,\n\t\t\t\t\t\t\tprev_val);\n\t\t\t\t\tif (prev_val2 == prev_val)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tprev_val = prev_val2;\n\t\t\t\t}\n\t\t\t}\n#else\n\t\t\t*(volatile unsigned __int64*)a = v;\n#endif\n\t\t\tbreak;\n\t\t}\n\t\t_ReadWriteBarrier();\n\t}\n\telse {\n\t\t_re_atomic_exchange(size, a, v);\n\t}\n}\n\nstatic __forceinline unsigned __int64 _re_atomic_load(\n\tsize_t size, const void *a, unsigned int mo)\n{\n\tunsigned __int64 v;\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\t_ReadWriteBarrier();\n\tswitch (size) {\n\tcase 1u:\n\t\tv = *(const volatile unsigned __int8*)a;\n\t\tbreak;\n\tcase 2u:\n\t\tv = *(const volatile unsigned __int16*)a;\n\t\tbreak;\n\tcase 4u:\n\t\tv = *(const volatile unsigned __int32*)a;\n\t\tbreak;\n\tdefault:\n#if defined(_M_IX86)\n\t\tv = _InterlockedCompareExchange64((__int64*)a, 0, 0);\n#else\n\t\tv = *(const volatile unsigned __int64*)a;\n#endif\n\t\tbreak;\n\t}\n\t_ReadWriteBarrier();\n\n\treturn v;\n}\n\n#elif defined(_M_ARM) || defined(_M_ARM64)\n\nstatic __forceinline void _re_atomic_store(\n\tsize_t size, void *a, unsigned __int64 v, unsigned int mo)\n{\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\t_ReadWriteBarrier();\n\n\tif (mo >= re_memory_order_release)\n\t\t__dmb(0x0b); /* dmb ish */\n\n\t_ReadWriteBarrier();\n\n\tswitch (size) {\n\tcase 1u:\n\t\t__iso_volatile_store8((__int8*)a, (__int8)v);\n\t\tbreak;\n\tcase 2u:\n\t\t__iso_volatile_store16((__int16*)a, (__int16)v);\n\t\tbreak;\n\tcase 4u:\n\t\t__iso_volatile_store32((__int32*)a, (__int32)v);\n\t\tbreak;\n\tdefault:\n\t\t__iso_volatile_store64((__int64*)a, (__int64)v);\n\t\tbreak;\n\t}\n\n\t_ReadWriteBarrier();\n\n\tif (mo == re_memory_order_seq_cst)\n\t\t__dmb(0x0b); /* dmb ish */\n\n\t_ReadWriteBarrier();\n}\n\nstatic __forceinline unsigned __int64 _re_atomic_load(\n\tsize_t size, const void *a, unsigned int mo)\n{\n\tunsigned __int64 v;\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\t_ReadWriteBarrier();\n\n\tswitch (size) {\n\tcase 1u:\n\t\tv = __iso_volatile_load8((const volatile __int8*)a);\n\t\tbreak;\n\tcase 2u:\n\t\tv = __iso_volatile_load16((const volatile __int16*)a);\n\t\tbreak;\n\tcase 4u:\n\t\tv = __iso_volatile_load32((const volatile __int32*)a);\n\t\tbreak;\n\tdefault:\n\t\tv = __iso_volatile_load64((const volatile __int64*)a);\n\t\tbreak;\n\t}\n\n\t_ReadWriteBarrier();\n\n\tif (mo != re_memory_order_relaxed && mo <= re_memory_order_acquire)\n\t\t__dmb(0x0b); /* dmb ish */\n\n\t_ReadWriteBarrier();\n\n\treturn v;\n}\n\n#else\n\nstatic __forceinline void _re_atomic_store(\n\tsize_t size, void *a, unsigned __int64 v, unsigned int mo)\n{\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\t_ReadWriteBarrier();\n\tswitch (size) {\n\tcase 1u:\n\t\t{\n\t\t\tchar prev_val = *(const volatile char*)(a);\n\t\t\twhile (1) {\n\t\t\t\tchar prev_val2 =\n\t\t\t\t\t_InterlockedCompareExchange8(\n\t\t\t\t\t\t(char*)a,\n\t\t\t\t\t\t(char)v,\n\t\t\t\t\t\tprev_val);\n\t\t\t\tif (prev_val2 == prev_val)\n\t\t\t\t\tbreak;\n\t\t\t\tprev_val = prev_val2;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase 2u:\n\t\t{\n\t\t\tshort prev_val = *(const volatile short*)(a);\n\t\t\twhile (1) {\n\t\t\t\tshort prev_val2 =\n\t\t\t\t\t_InterlockedCompareExchange16(\n\t\t\t\t\t\t(short*)a,\n\t\t\t\t\t\t(short)v,\n\t\t\t\t\t\tprev_val);\n\t\t\t\tif (prev_val2 == prev_val)\n\t\t\t\t\tbreak;\n\t\t\t\tprev_val = prev_val2;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase 4u:\n\t\t{\n\t\t\tlong prev_val = *(const volatile long*)(a);\n\t\t\twhile (1) {\n\t\t\t\tlong prev_val2 =\n\t\t\t\t\t_InterlockedCompareExchange(\n\t\t\t\t\t\t(long*)a,\n\t\t\t\t\t\t(long)v,\n\t\t\t\t\t\tprev_val);\n\t\t\t\tif (prev_val2 == prev_val)\n\t\t\t\t\tbreak;\n\t\t\t\tprev_val = prev_val2;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t{\n\t\t\t__int64 prev_val = *(const volatile __int64*)(a);\n\t\t\twhile (1) {\n\t\t\t\t__int64 prev_val2 =\n\t\t\t\t\t_InterlockedCompareExchange64(\n\t\t\t\t\t\t(__int64*)a,\n\t\t\t\t\t\t(__int64)v,\n\t\t\t\t\t\tprev_val);\n\t\t\t\tif (prev_val2 == prev_val)\n\t\t\t\t\tbreak;\n\t\t\t\tprev_val = prev_val2;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\t_ReadWriteBarrier();\n}\n\nstatic __forceinline unsigned __int64 _re_atomic_load(\n\tsize_t size, const void *a, unsigned int mo)\n{\n\tunsigned __int64 v;\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\tswitch (size) {\n\tcase 1u:\n\t\tv = _InterlockedCompareExchange8((char*)a, 0, 0);\n\t\tbreak;\n\tcase 2u:\n\t\tv = _InterlockedCompareExchange16((short*)a, 0, 0);\n\t\tbreak;\n\tcase 4u:\n\t\tv = _InterlockedCompareExchange((long*)a, 0, 0);\n\t\tbreak;\n\tdefault:\n\t\tv = _InterlockedCompareExchange64((__int64*)a, 0, 0);\n\t\tbreak;\n\t}\n\n\treturn v;\n}\n\n#endif\n\n#define re_atomic_store(_a, _v, _mo) \\\n\t_re_atomic_store(sizeof(*(_a)), _a, _v, _mo);\n\n#define re_atomic_load(_a, _mo) \\\n\t_re_atomic_load(sizeof(*(_a)), _a, _mo)\n\nstatic __forceinline unsigned __int64 _re_atomic_exchange(\n\tsize_t size, void *a, unsigned __int64 v)\n{\n\tunsigned __int64 prev_val;\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\tswitch (size) {\n\tcase 1u:\n\t\tprev_val = _InterlockedExchange8((char*)a, (char)v);\n\t\tbreak;\n\tcase 2u:\n\t\tprev_val = _InterlockedExchange16((short*)a, (short)v);\n\t\tbreak;\n\tcase 4u:\n\t\tprev_val = _InterlockedExchange((long*)a, (long)v);\n\t\tbreak;\n\tdefault:\n#if defined(_M_IX86)\n\t\t{\n\t\t\t_ReadWriteBarrier();\n\t\t\tprev_val = *(const volatile __int64*)(a);\n\t\t\twhile (1) {\n\t\t\t\t__int64 prev_val2 =\n\t\t\t\t\t_InterlockedCompareExchange64(\n\t\t\t\t\t\t(__int64*)a,\n\t\t\t\t\t\t(__int64)v,\n\t\t\t\t\t\t(__int64)prev_val);\n\t\t\t\tif (prev_val2 == prev_val)\n\t\t\t\t\tbreak;\n\t\t\t\tprev_val = prev_val2;\n\t\t\t}\n\t\t\t_ReadWriteBarrier();\n\t\t}\n#else\n\t\tprev_val = _InterlockedExchange64((__int64*)a, (__int64)v);\n#endif\n\t\tbreak;\n\t}\n\n\treturn prev_val;\n}\n\n#define re_atomic_exchange(_a, _v, _mo) \\\n\t_re_atomic_exchange(sizeof(*(_a)), _a, _v)\n\nstatic __forceinline bool _re_atomic_compare_exchange_strong(\n\tsize_t size, void *a, void *expected, unsigned __int64 desired)\n{\n\tbool res;\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\tswitch (size) {\n\tcase 1u:\n\t\t{\n\t\t\tchar expected_val = *(char*)expected;\n\t\t\tchar prev_val =\n\t\t\t\t _InterlockedCompareExchange8(\n\t\t\t\t\t(char*)a,\n\t\t\t\t\t(char)desired,\n\t\t\t\t\texpected_val);\n\t\t\t*(char*)expected = prev_val;\n\t\t\tres = prev_val == expected_val;\n\t\t}\n\t\tbreak;\n\tcase 2u:\n\t\t{\n\t\t\tshort expected_val = *(short*)expected;\n\t\t\tshort prev_val =\n\t\t\t\t_InterlockedCompareExchange16(\n\t\t\t\t\t(short*)a,\n\t\t\t\t\t(short)desired,\n\t\t\t\t\texpected_val);\n\t\t\t*(short*)expected = prev_val;\n\t\t\tres = prev_val == expected_val;\n\t\t}\n\t\tbreak;\n\tcase 4u:\n\t\t{\n\t\t\tlong expected_val = *(long*)expected;\n\t\t\tlong prev_val =\n\t\t\t\t _InterlockedCompareExchange(\n\t\t\t\t\t(long*)a,\n\t\t\t\t\t(long)desired,\n\t\t\t\t\texpected_val);\n\t\t\t*(long*)expected = prev_val;\n\t\t\tres = prev_val == expected_val;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\t{\n\t\t\t__int64 expected_val = *(__int64*)expected;\n\t\t\t__int64 prev_val =\n\t\t\t\t_InterlockedCompareExchange64(\n\t\t\t\t\t(__int64*)a,\n\t\t\t\t\t(__int64)desired,\n\t\t\t\t\texpected_val);\n\t\t\t*(__int64*)expected = prev_val;\n\t\t\tres = prev_val == expected_val;\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn res;\n}\n\n#define re_atomic_compare_exchange_strong(\\\n\t_a, _expected, _desired, _success_mo, _fail_mo) \\\n\t_re_atomic_compare_exchange_strong(\\\n\t\tsizeof(*(_a)), _a, _expected, _desired)\n\n#define re_atomic_compare_exchange_weak(\\\n\t_a, _expected, _desired, _success_mo, _fail_mo) \\\n\tre_atomic_compare_exchange_strong(\\\n\t\t_a, _expected, _desired, _success_mo, _fail_mo)\n\nstatic __forceinline unsigned __int64 _re_atomic_fetch_add(\n\tsize_t size, void *a, unsigned __int64 v)\n{\n\tunsigned __int64 prev_val;\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\tswitch (size) {\n\tcase 1u:\n\t\tprev_val = _InterlockedExchangeAdd8((char*)a, (char)v);\n\t\tbreak;\n\tcase 2u:\n\t\tprev_val = _InterlockedExchangeAdd16((short*)a, (short)v);\n\t\tbreak;\n\tcase 4u:\n\t\tprev_val = _InterlockedExchangeAdd((long*)a, (long)v);\n\t\tbreak;\n\tdefault:\n#if defined(_M_IX86)\n\t\t{\n\t\t\t_ReadWriteBarrier();\n\t\t\tprev_val = *(const volatile __int64*)(a);\n\t\t\twhile (1) {\n\t\t\t\t__int64 new_val = prev_val + v;\n\t\t\t\t__int64 prev_val2 =\n\t\t\t\t\t _InterlockedCompareExchange64(\n\t\t\t\t\t\t(__int64*)a,\n\t\t\t\t\t\t(__int64)new_val,\n\t\t\t\t\t\t(__int64)prev_val);\n\t\t\t\tif (prev_val2 == prev_val)\n\t\t\t\t\tbreak;\n\t\t\t\tprev_val = prev_val2;\n\t\t\t}\n\t\t\t_ReadWriteBarrier();\n\t\t}\n#else\n\t\tprev_val = _InterlockedExchangeAdd64((__int64*)a, (__int64)v);\n#endif\n\t\tbreak;\n\t}\n\n\treturn prev_val;\n}\n\n#define re_atomic_fetch_add(_a, _v, _mo) \\\n\t_re_atomic_fetch_add(sizeof(*(_a)), _a, _v)\n\n#define re_atomic_fetch_sub(_a, _v, _mo) \\\n\tre_atomic_fetch_add(_a, -(__int64)(_v), _mo)\n\nstatic __forceinline unsigned __int64 _re_atomic_fetch_or(\n\tsize_t size, void *a, unsigned __int64 v)\n{\n\tunsigned __int64 prev_val;\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\tswitch (size) {\n\tcase 1u:\n\t\tprev_val = _InterlockedOr8((char*)a, (char)v);\n\t\tbreak;\n\tcase 2u:\n\t\tprev_val = _InterlockedOr16((short*)a, (short)v);\n\t\tbreak;\n\tcase 4u:\n\t\tprev_val = _InterlockedOr((long*)a, (long)v);\n\t\tbreak;\n\tdefault:\n#if defined(_M_IX86)\n\t\t{\n\t\t\t_ReadWriteBarrier();\n\t\t\tprev_val = *(const volatile __int64*)(a);\n\t\t\twhile (1) {\n\t\t\t\t__int64 new_val = prev_val | v;\n\t\t\t\t__int64 prev_val2 =\n\t\t\t\t\t_InterlockedCompareExchange64(\n\t\t\t\t\t\t(__int64*)a,\n\t\t\t\t\t\t(__int64)new_val,\n\t\t\t\t\t\t(__int64)prev_val);\n\t\t\t\tif (prev_val2 == prev_val)\n\t\t\t\t\tbreak;\n\t\t\t\tprev_val = prev_val2;\n\t\t\t}\n\t\t\t_ReadWriteBarrier();\n\t\t}\n#else\n\t\tprev_val = _InterlockedOr64((__int64*)a, (__int64)v);\n#endif\n\t\tbreak;\n\t}\n\n\treturn prev_val;\n}\n\n#define re_atomic_fetch_or(_a, _v, _mo) \\\n\t_re_atomic_fetch_or(sizeof(*(_a)), _a, _v)\n\nstatic __forceinline unsigned __int64 _re_atomic_fetch_xor(\n\tsize_t size, void *a, unsigned __int64 v)\n{\n\tunsigned __int64 prev_val;\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\tswitch (size) {\n\tcase 1u:\n\t\tprev_val = _InterlockedXor8((char*)a, (char)v);\n\t\tbreak;\n\tcase 2u:\n\t\tprev_val = _InterlockedXor16((short*)a, (short)v);\n\t\tbreak;\n\tcase 4u:\n\t\tprev_val = _InterlockedXor((long*)a, (long)v);\n\t\tbreak;\n\tdefault:\n#if defined(_M_IX86)\n\t\t{\n\t\t\t_ReadWriteBarrier();\n\t\t\tprev_val = *(const volatile __int64*)(a);\n\t\t\twhile (1) {\n\t\t\t\t__int64 new_val = prev_val ^ v;\n\t\t\t\t__int64 prev_val2 =\n\t\t\t\t\t_InterlockedCompareExchange64(\n\t\t\t\t\t\t(__int64*)a,\n\t\t\t\t\t\t(__int64)new_val,\n\t\t\t\t\t\t(__int64)prev_val);\n\t\t\t\tif (prev_val2 == prev_val)\n\t\t\t\t\tbreak;\n\t\t\t\tprev_val = prev_val2;\n\t\t\t}\n\t\t\t_ReadWriteBarrier();\n\t\t}\n#else\n\t\tprev_val = _InterlockedXor64((__int64*)a, (__int64)v);\n#endif\n\t\tbreak;\n\t}\n\n\treturn prev_val;\n}\n\n#define re_atomic_fetch_xor(_a, _v, _mo) \\\n\t_re_atomic_fetch_xor(sizeof(*(_a)), _a, _v)\n\nstatic __forceinline unsigned __int64 _re_atomic_fetch_and(\n\tsize_t size, void *a, unsigned __int64 v)\n{\n\tunsigned __int64 prev_val;\n\tassert(size == 1u || size == 2u || size == 4u || size == 8u);\n\tswitch (size) {\n\tcase 1u:\n\t\tprev_val = _InterlockedAnd8((char*)a, (char)v);\n\t\tbreak;\n\tcase 2u:\n\t\tprev_val = _InterlockedAnd16((short*)a, (short)v);\n\t\tbreak;\n\tcase 4u:\n\t\tprev_val = _InterlockedAnd((long*)a, (long)v);\n\t\tbreak;\n\tdefault:\n#if defined(_M_IX86)\n\t\t{\n\t\t\t_ReadWriteBarrier();\n\t\t\tprev_val = *(const volatile __int64*)(a);\n\t\t\twhile (1) {\n\t\t\t\t__int64 new_val = prev_val & v;\n\t\t\t\t__int64 prev_val2 =\n\t\t\t\t\t_InterlockedCompareExchange64(\n\t\t\t\t\t\t(__int64*)a,\n\t\t\t\t\t\t(__int64)new_val,\n\t\t\t\t\t\t(__int64)prev_val);\n\t\t\t\tif (prev_val2 == prev_val)\n\t\t\t\t\tbreak;\n\t\t\t\tprev_val = prev_val2;\n\t\t\t}\n\t\t\t_ReadWriteBarrier();\n\t\t}\n#else\n\t\tprev_val = _InterlockedAnd64((__int64*)a, (__int64)v);\n#endif\n\t\tbreak;\n\t}\n\n\treturn prev_val;\n}\n\n#define re_atomic_fetch_and(_a, _v, _mo) \\\n\t_re_atomic_fetch_and(sizeof(*(_a)), _a, _v)\n\n#ifdef __cplusplus\n} /* extern \"C\" */\n#endif\n\n#else\n#error \"Compiler does not support atomics\"\n#endif /* HAVE_ATOMIC */\n\n#ifndef RE_ATOMIC\n#define RE_ATOMIC\n#endif\n\n\n/* --- Some short alias helpers --- */\n\n/**\n * @def re_atomic_rlx(_a)\n *\n * Load value from an atomic object with relaxed order\n *\n * @param _a  pointer to the atomic object\n *\n * @return value of the atomic variable\n */\n#define re_atomic_rlx(_a) re_atomic_load(_a, re_memory_order_relaxed)\n\n\n/**\n * @def re_atomic_rlx_set(_a, _v)\n *\n * Store value in an atomic object with relaxed order\n *\n * @param _a  pointer to the atomic object\n * @param _v  new value\n */\n#define re_atomic_rlx_set(_a, _v)                                            \\\n\tre_atomic_store(_a, _v, re_memory_order_relaxed)\n\n\n/**\n * @def re_atomic_rlx_add(_a, _v)\n *\n * Replace value from an atomic object with addition and relaxed order\n *\n * @param _a  pointer to the atomic object\n * @param _v  value to add\n *\n * @return value held previously by the atomic variable\n */\n#define re_atomic_rlx_add(_a, _v)                                            \\\n\tre_atomic_fetch_add(_a, _v, re_memory_order_relaxed)\n\n\n/**\n * @def re_atomic_rlx_sub(_a, _v)\n *\n * Replace value from an atomic object with subtraction and relaxed order\n *\n * @param _a  pointer to the atomic object\n * @param _v  value to subtract\n *\n * @return value held previously by the atomic variable\n */\n#define re_atomic_rlx_sub(_a, _v)                                            \\\n\tre_atomic_fetch_sub(_a, _v, re_memory_order_relaxed)\n\n\n/**\n * @def re_atomic_acq(_a)\n *\n * Load value from an atomic object with acquire order\n *\n * @param _a  pointer to the atomic object\n *\n * @return value of the atomic variable\n */\n#define re_atomic_acq(_a) re_atomic_load(_a, re_memory_order_acquire)\n\n\n/**\n * @def re_atomic_rls_set(_a, _v)\n *\n * Store value in an atomic object with release order\n *\n * @param _a  pointer to the atomic object\n * @param _v  new value\n */\n#define re_atomic_rls_set(_a, _v)                                          \\\n\tre_atomic_store(_a, _v, re_memory_order_release)\n\n\n/**\n * @def re_atomic_acq_add(_a, _v)\n *\n * Replace value from an atomic object with addition and acquire-release order\n *\n * @param _a  pointer to the atomic object\n * @param _v  value to add\n *\n * @return value held previously by the atomic variable\n */\n#define re_atomic_acq_add(_a, _v)                                          \\\n\tre_atomic_fetch_add(_a, _v, re_memory_order_acq_rel)\n\n\n/**\n * @def re_atomic_acq_sub(_a, _v)\n *\n * Replace value from an atomic object with subtraction and acquire-release\n * order\n *\n * @param _a  pointer to the atomic object\n * @param _v  value to subtract\n *\n * @return value held previously by the atomic variable\n */\n#define re_atomic_acq_sub(_a, _v)                                          \\\n\tre_atomic_fetch_sub(_a, _v, re_memory_order_acq_rel)\n\n\n/**\n * @def re_atomic_seq(_a)\n *\n * Load value from an atomic object with sequentially-consistent order\n *\n * @param _a  pointer to the atomic object\n *\n * @return value of the atomic variable\n */\n#define re_atomic_seq(_a) re_atomic_load(_a, re_memory_order_seq_cst)\n\n\n/**\n * @def re_atomic_seq_set(_a, _v)\n *\n * Store value in an atomic object with sequentially-consistent order\n *\n * @param _a  pointer to the atomic object\n * @param _v  new value\n */\n#define re_atomic_seq_set(_a, _v)                                            \\\n\tre_atomic_store(_a, _v, re_memory_order_seq_cst)\n\n\n/**\n * @def re_atomic_seq_add(_a, _v)\n *\n * Replace value from an atomic object with addition and\n * sequentially-consistent order\n *\n * @param _a  pointer to the atomic object\n * @param _v  value to add\n *\n * @return value held previously by the atomic variable\n */\n#define re_atomic_seq_add(_a, _v)                                            \\\n\tre_atomic_fetch_add(_a, _v, re_memory_order_seq_cst)\n\n\n/**\n * @def re_atomic_seq_sub(_a, _v)\n *\n * Replace value from an atomic object with subtraction and\n * sequentially-consistent order\n *\n * @param _a  pointer to the atomic object\n * @param _v  value to subtract\n *\n * @return value held previously by the atomic variable\n */\n#define re_atomic_seq_sub(_a, _v)                                            \\\n\tre_atomic_fetch_sub(_a, _v, re_memory_order_seq_cst)\n\n\n#endif /* RE_H_ATOMIC__ */\n"
  },
  {
    "path": "include/re_av1.h",
    "content": "/**\n * @file re_av1.h AV1 Open Bitstream Unit (OBU)\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n\n/* OBU (Open Bitstream Units) */\n\n\n/** Defines the OBU type */\nenum obu_type {\n\tAV1_OBU_SEQUENCE_HEADER        =  1,\n\tAV1_OBU_TEMPORAL_DELIMITER     =  2,\n\tAV1_OBU_FRAME_HEADER           =  3,\n\tAV1_OBU_TILE_GROUP             =  4,\n\tAV1_OBU_METADATA               =  5,\n\tAV1_OBU_FRAME                  =  6,\n\tAV1_OBU_REDUNDANT_FRAME_HEADER =  7,\n\tAV1_OBU_TILE_LIST              =  8,\n\tAV1_OBU_PADDING                = 15,\n};\n\n/**\n * AV1 OBU Header\n *\n *     0 1 2 3 4 5 6 7\n *    +-+-+-+-+-+-+-+-+\n *    |F| type  |X|S|-| (REQUIRED)\n *    +-+-+-+-+-+-+-+-+\n */\nstruct av1_obu_hdr {\n\tenum obu_type type;  /**< OBU type       */\n\tbool x;              /**< Extension flag */\n\tbool s;              /**< Has size field */\n\tsize_t size;         /**< Payload size   */\n};\n\nint av1_leb128_encode(struct mbuf *mb, uint64_t value);\nint av1_leb128_decode(struct mbuf *mb, uint64_t *value);\nint av1_obu_encode(struct mbuf *mb, uint8_t type, bool has_size,\n\t\t   size_t len, const uint8_t *payload);\nint av1_obu_decode(struct av1_obu_hdr *hdr, struct mbuf *mb);\nint av1_obu_print(struct re_printf *pf, const struct av1_obu_hdr *hdr);\nunsigned av1_obu_count(const uint8_t *buf, size_t size);\nunsigned av1_obu_count_rtp(const uint8_t *buf, size_t size);\nconst char *av1_obu_name(enum obu_type type);\nbool obu_allowed_rtp(enum obu_type type);\n\n\n/*\n * Packetizer\n */\n\ntypedef int (av1_packet_h)(bool marker, uint64_t rtp_ts,\n\t\t\t    const uint8_t *hdr, size_t hdr_len,\n\t\t\t    const uint8_t *pld, size_t pld_len,\n\t\t\t    void *arg);\n\nint av1_packetize(bool *newp, bool marker, uint64_t rtp_ts,\n\t\t  const uint8_t *buf, size_t len, size_t maxlen,\n\t\t  av1_packet_h *pkth, void *arg);\n\n\nenum {\n\tAV1_AGGR_HDR_SIZE = 1,\n};\n\n/** AV1 Aggregation Header */\nstruct av1_aggr_hdr {\n\tunsigned z:1;  /**< Continuation of OBU fragment from prev packet */\n\tunsigned y:1;  /**< Last OBU element will continue in next packet */\n\tunsigned w:2;  /**< Number of OBU elements in the packet          */\n\tunsigned n:1;  /**< First packet of a coded video sequence        */\n};\n\nint av1_aggr_hdr_decode(struct av1_aggr_hdr *hdr, struct mbuf *mb);\n"
  },
  {
    "path": "include/re_base64.h",
    "content": "/**\n * @file re_base64.h  Interface to Base64 encoding/decoding functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nint base64_encode(const uint8_t *in, size_t ilen, char *out, size_t *olen);\nint base64url_encode(const uint8_t *in, size_t ilen, char *out, size_t *olen);\nint base64_print(struct re_printf *pf, const uint8_t *ptr, size_t len);\nint base64_decode(const char *in, size_t ilen, uint8_t *out, size_t *olen);\n"
  },
  {
    "path": "include/re_bfcp.h",
    "content": "/**\n * @file re_bfcp.h Interface to Binary Floor Control Protocol (BFCP)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** BFCP Versions */\nenum {\n\tBFCP_VER1 = 1,\n\tBFCP_VER2 = 2,\n};\n\n/** BFCP Primitives */\nenum bfcp_prim {\n\tBFCP_FLOOR_REQUEST        =  1,\n\tBFCP_FLOOR_RELEASE        =  2,\n\tBFCP_FLOOR_REQUEST_QUERY  =  3,\n\tBFCP_FLOOR_REQUEST_STATUS =  4,\n\tBFCP_USER_QUERY           =  5,\n\tBFCP_USER_STATUS          =  6,\n\tBFCP_FLOOR_QUERY          =  7,\n\tBFCP_FLOOR_STATUS         =  8,\n\tBFCP_CHAIR_ACTION         =  9,\n\tBFCP_CHAIR_ACTION_ACK     = 10,\n\tBFCP_HELLO                = 11,\n\tBFCP_HELLO_ACK            = 12,\n\tBFCP_ERROR                = 13,\n\tBFCP_FLOOR_REQ_STATUS_ACK = 14,\n\tBFCP_FLOOR_STATUS_ACK     = 15,\n\tBFCP_GOODBYE              = 16,\n\tBFCP_GOODBYE_ACK          = 17,\n};\n\n/** BFCP Attributes */\nenum bfcp_attrib {\n\tBFCP_BENEFICIARY_ID     =  1,\n\tBFCP_FLOOR_ID           =  2,\n\tBFCP_FLOOR_REQUEST_ID   =  3,\n\tBFCP_PRIORITY           =  4,\n\tBFCP_REQUEST_STATUS     =  5,\n\tBFCP_ERROR_CODE         =  6,\n\tBFCP_ERROR_INFO         =  7,\n\tBFCP_PART_PROV_INFO     =  8,\n\tBFCP_STATUS_INFO        =  9,\n\tBFCP_SUPPORTED_ATTRS    = 10,\n\tBFCP_SUPPORTED_PRIMS    = 11,\n\tBFCP_USER_DISP_NAME     = 12,\n\tBFCP_USER_URI           = 13,\n\t/* grouped: */\n\tBFCP_BENEFICIARY_INFO   = 14,\n\tBFCP_FLOOR_REQ_INFO     = 15,\n\tBFCP_REQUESTED_BY_INFO  = 16,\n\tBFCP_FLOOR_REQ_STATUS   = 17,\n\tBFCP_OVERALL_REQ_STATUS = 18,\n\n\t/** Mandatory Attribute */\n\tBFCP_MANDATORY          = 1<<7,\n\t/** Encode Handler */\n\tBFCP_ENCODE_HANDLER     = 1<<8,\n};\n\n/** BFCP Request Status */\nenum bfcp_reqstat {\n\tBFCP_PENDING   = 1,\n\tBFCP_ACCEPTED  = 2,\n\tBFCP_GRANTED   = 3,\n\tBFCP_DENIED    = 4,\n\tBFCP_CANCELLED = 5,\n\tBFCP_RELEASED  = 6,\n\tBFCP_REVOKED   = 7\n};\n\n/** BFCP Error Codes */\nenum bfcp_err {\n\tBFCP_CONF_NOT_EXIST         = 1,\n\tBFCP_USER_NOT_EXIST         = 2,\n\tBFCP_UNKNOWN_PRIM           = 3,\n\tBFCP_UNKNOWN_MAND_ATTR      = 4,\n\tBFCP_UNAUTH_OPERATION       = 5,\n\tBFCP_INVALID_FLOOR_ID       = 6,\n\tBFCP_FLOOR_REQ_ID_NOT_EXIST = 7,\n\tBFCP_MAX_FLOOR_REQ_REACHED  = 8,\n\tBFCP_USE_TLS                = 9,\n\tBFCP_PARSE_ERROR            = 10,\n\tBFCP_USE_DTLS               = 11,\n\tBFCP_UNSUPPORTED_VERSION    = 12,\n\tBFCP_BAD_LENGTH             = 13,\n\tBFCP_GENERIC_ERROR          = 14,\n};\n\n/** BFCP Priority */\nenum bfcp_priority {\n\tBFCP_PRIO_LOWEST  = 0,\n\tBFCP_PRIO_LOW     = 1,\n\tBFCP_PRIO_NORMAL  = 2,\n\tBFCP_PRIO_HIGH    = 3,\n\tBFCP_PRIO_HIGHEST = 4\n};\n\n/** BFCP Transport */\nenum bfcp_transp {\n\tBFCP_UDP,\n\tBFCP_DTLS,\n\tBFCP_TCP\n};\n\n/** BFCP Request Status */\nstruct bfcp_reqstatus {\n\tenum bfcp_reqstat status;\n\tuint8_t qpos;\n};\n\n/** BFCP Error Code */\nstruct bfcp_errcode {\n\tenum bfcp_err code;\n\tuint8_t *details;  /* optional */\n\tsize_t len;\n};\n\n/** BFCP Supported Attributes */\nstruct bfcp_supattr {\n\tenum bfcp_attrib *attrv;\n\tsize_t attrc;\n};\n\n/** BFCP Supported Primitives */\nstruct bfcp_supprim {\n\tenum bfcp_prim *primv;\n\tsize_t primc;\n};\n\n/** BFCP Attribute */\nstruct bfcp_attr {\n\tstruct le le;\n\tstruct list attrl;\n\tenum bfcp_attrib type;\n\tbool mand;\n\tunion bfcp_union {\n\t\t/* generic types */\n\t\tchar *str;\n\t\tuint16_t u16;\n\n\t\t/* actual attributes */\n\t\tuint16_t beneficiaryid;\n\t\tuint16_t floorid;\n\t\tuint16_t floorreqid;\n\t\tenum bfcp_priority priority;\n\t\tstruct bfcp_reqstatus reqstatus;\n\t\tstruct bfcp_errcode errcode;\n\t\tchar *errinfo;\n\t\tchar *partprovinfo;\n\t\tchar *statusinfo;\n\t\tstruct bfcp_supattr supattr;\n\t\tstruct bfcp_supprim supprim;\n\t\tchar *userdname;\n\t\tchar *useruri;\n\t\tuint16_t reqbyid;\n\t} v;\n};\n\n/** BFCP unknown attributes */\nstruct bfcp_unknown_attr {\n\tuint8_t typev[16];\n\tsize_t typec;\n};\n\n/** BFCP Message */\nstruct bfcp_msg {\n\tstruct bfcp_unknown_attr uma;\n\tstruct sa src;\n\tuint8_t ver;\n\tunsigned r:1;\n\tunsigned f:1;\n\tenum bfcp_prim prim;\n\tuint16_t len;\n\tuint32_t confid;\n\tuint16_t tid;\n\tuint16_t userid;\n\tstruct list attrl;\n};\n\nstruct tls;\nstruct bfcp_conn;\n\n\n/**\n * Defines the BFCP encode handler\n *\n * @param mb  Mbuf to encode into\n * @param arg Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\ntypedef int (bfcp_encode_h)(struct mbuf *mb, void *arg);\n\n/** BFCP Encode */\nstruct bfcp_encode {\n\tbfcp_encode_h *ench;\n\tvoid *arg;\n};\n\n\n/**\n * Defines the BFCP attribute handler\n *\n * @param attr BFCP attribute\n * @param arg  Handler argument\n *\n * @return True to stop processing, false to continue\n */\ntypedef bool (bfcp_attr_h)(const struct bfcp_attr *attr, void *arg);\n\n\n/**\n * Defines the BFCP receive handler\n *\n * @param msg BFCP message\n * @param arg Handler argument\n */\ntypedef void (bfcp_recv_h)(const struct bfcp_msg *msg, void *arg);\n\n\n/**\n * Defines the BFCP response handler\n *\n * @param err Error code\n * @param msg BFCP message\n * @param arg Handler argument\n */\ntypedef void (bfcp_resp_h)(int err, const struct bfcp_msg *msg, void *arg);\n\n\n/* attr */\nint bfcp_attrs_vencode(struct mbuf *mb, unsigned attrc, va_list *ap);\nint bfcp_attrs_encode(struct mbuf *mb, unsigned attrc, ...);\nstruct bfcp_attr *bfcp_attr_subattr(const struct bfcp_attr *attr,\n\t\t\t\t    enum bfcp_attrib type);\nstruct bfcp_attr *bfcp_attr_subattr_apply(const struct bfcp_attr *attr,\n\t\t\t\t\t  bfcp_attr_h *h, void *arg);\nint bfcp_attr_print(struct re_printf *pf, const struct bfcp_attr *attr);\nconst char *bfcp_attr_name(enum bfcp_attrib type);\nconst char *bfcp_reqstatus_name(enum bfcp_reqstat status);\nconst char *bfcp_errcode_name(enum bfcp_err code);\n\n\n/* msg */\nint bfcp_msg_vencode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim,\n\t\t     uint32_t confid, uint16_t tid, uint16_t userid,\n\t\t     unsigned attrc, va_list *ap);\nint bfcp_msg_encode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim,\n\t\t    uint32_t confid, uint16_t tid, uint16_t userid,\n\t\t    unsigned attrc, ...);\nint bfcp_msg_decode(struct bfcp_msg **msgp, struct mbuf *mb);\nstruct bfcp_attr *bfcp_msg_attr(const struct bfcp_msg *msg,\n\t\t\t\tenum bfcp_attrib type);\nstruct bfcp_attr *bfcp_msg_attr_apply(const struct bfcp_msg *msg,\n\t\t\t\t      bfcp_attr_h *h, void *arg);\nint bfcp_msg_print(struct re_printf *pf, const struct bfcp_msg *msg);\nconst char *bfcp_prim_name(enum bfcp_prim prim);\n\n\n/* conn */\n\n/**\n * Defines the BFCP incoming connection handler\n *\n * @param peer Remote peer address\n * @param arg Handler argument\n */\ntypedef void (bfcp_conn_h)(const struct sa *peer, void *arg);\n\n/**\n * Defines the BFCP connection established handler\n *\n * @param arg Handler argument\n */\ntypedef void (bfcp_estab_h)(void *arg);\n\n/**\n * Defines the BFCP close handler for example for TCP connection\n *\n * @param err Error code\n * @param arg Handler argument\n */\ntypedef void (bfcp_close_h)(int err, void *arg);\n\nint bfcp_listen(struct bfcp_conn **bcp, enum bfcp_transp tp,\n\t\tstruct sa *laddr, struct tls *tls, bfcp_conn_h *connh,\n\t\tbfcp_estab_h *estabh, bfcp_recv_h *recvh, bfcp_close_h *closeh,\n\t\tvoid *arg);\nint bfcp_connect(struct bfcp_conn **bcp, enum bfcp_transp tp,\n\t\t struct sa *laddr, const struct sa *peer, bfcp_estab_h *estabh,\n\t\t bfcp_recv_h *recvh, bfcp_close_h *closeh, void *arg);\nint bfcp_accept(struct bfcp_conn *bc);\nvoid bfcp_reject(struct bfcp_conn *bc);\nvoid *bfcp_sock(const struct bfcp_conn *bc);\n\n/* request */\nint bfcp_request(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,\n\t\t enum bfcp_prim prim, uint32_t confid, uint16_t userid,\n\t\t bfcp_resp_h *resph, void *arg, unsigned attrc, ...);\n\n\n/* notify */\nint bfcp_notify(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,\n\t\tenum bfcp_prim prim, uint32_t confid, uint16_t userid,\n\t\tunsigned attrc, ...);\n\n\n/* reply */\nint bfcp_reply(struct bfcp_conn *bc, const struct bfcp_msg *req,\n\t       enum bfcp_prim prim, unsigned attrc, ...);\nint bfcp_edreply(struct bfcp_conn *bc, const struct bfcp_msg *req,\n\t\t enum bfcp_err code, const uint8_t *details, size_t len);\nint bfcp_ereply(struct bfcp_conn *bc, const struct bfcp_msg *req,\n\t\tenum bfcp_err code);\n"
  },
  {
    "path": "include/re_btrace.h",
    "content": "/**\n * @file re_btrace.h  Backtrace API\n *\n */\n#define BTRACE_SZ 16\n\nstruct btrace {\n\tvoid *stack[BTRACE_SZ];\n\tsize_t len;\n};\n\nint btrace_print(struct re_printf *pf, struct btrace *bt);\nint btrace_println(struct re_printf *pf, struct btrace *bt);\nint btrace_print_json(struct re_printf *pf, struct btrace *bt);\n\n#if defined(HAVE_EXECINFO) && !defined(RELEASE)\n#include <execinfo.h>\nstatic inline int btrace(struct btrace *bt)\n{\n\tif (!bt)\n\t\treturn EINVAL;\n\n\tbt->len = backtrace(bt->stack, BTRACE_SZ);\n\n\treturn 0;\n}\n#elif defined(WIN32)\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\nstatic inline int btrace(struct btrace *bt)\n{\n\tif (!bt)\n\t\treturn EINVAL;\n\n\tbt->len = CaptureStackBackTrace(0, BTRACE_SZ, bt->stack, NULL);\n\n\treturn 0;\n}\n#else\nstatic inline int btrace(struct btrace *bt)\n{\n\t(void)bt;\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "include/re_conf.h",
    "content": "/**\n * @file re_conf.h  Interface to configuration\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nstruct conf;\n\ntypedef int (conf_h)(const struct pl *val, void *arg);\n\nint conf_alloc(struct conf **confp, const char *filename);\nint conf_alloc_buf(struct conf **confp, const uint8_t *buf, size_t sz);\nint conf_get(const struct conf *conf, const char *name, struct pl *pl);\nint conf_get_str(const struct conf *conf, const char *name, char *str,\n\t\t size_t size);\nint conf_get_u32(const struct conf *conf, const char *name, uint32_t *num);\nint conf_get_i32(const struct conf *conf, const char *name, int32_t *num);\nint conf_get_float(const struct conf *conf, const char *name, double *num);\nint conf_get_bool(const struct conf *conf, const char *name, bool *val);\nint conf_apply(const struct conf *conf, const char *name,\n\t       conf_h *ch, void *arg);\n"
  },
  {
    "path": "include/re_convert.h",
    "content": "/**\n * @file re_convert.h  Conversion helpers\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n#include <limits.h>\n\nstatic inline int try_into_u16_from_size(uint16_t *dest, const size_t src)\n{\n\t*dest = 0;\n\n\tif (src > UINT16_MAX)\n\t\treturn ERANGE;\n\n\t*dest = (uint16_t)src;\n\n\treturn 0;\n}\n\n\nstatic inline int try_into_u16_from_int(uint16_t *dest, const int src)\n{\n\t*dest = 0;\n\n\tif (src > UINT16_MAX)\n\t\treturn ERANGE;\n\n\tif (src < 0)\n\t\treturn ERANGE;\n\n\t*dest = (uint16_t)src;\n\n\treturn 0;\n}\n\n\nstatic inline int try_into_int_from_size(int *dest, const size_t src)\n{\n\t*dest = 0;\n\n\tif (src > INT_MAX)\n\t\treturn ERANGE;\n\n\t*dest = (int)src;\n\n\treturn 0;\n}\n\n\nstatic inline int try_into_err(void *dest, ...)\n{\n\t(void)dest;\n\n\treturn ENOTSUP;\n}\n\n\n#if __STDC_VERSION__ >= 201112L /* Needs C11 support */\n/**\n * Try to convert safely from one type (src) into another (dest).\n * Types are auto detected.\n *\n * @param dest Destination\n * @param src  Source value\n *\n * @return 0 if success, ERANGE if value overflow and ENOTSUP if not supported\n */\n#define try_into(dest, src)\t\t\t\t\t\t      \\\n\t_Generic((dest), \t\t\t\t\t\t      \\\n\t\tuint16_t: _Generic((src),\t\t\t\t      \\\n\t\t\t\tsize_t: try_into_u16_from_size, \t      \\\n\t\t\t\tint: try_into_u16_from_int,\t\t      \\\n\t\t\t\tdefault: try_into_err\t\t\t      \\\n\t\t\t),\t\t\t\t\t\t      \\\n\t\tint: _Generic((src),\t\t\t\t\t      \\\n\t\t\t\tsize_t: try_into_int_from_size,\t\t      \\\n\t\t\t\tdefault: try_into_err\t\t\t      \\\n\t\t\t)\t\t\t\t\t\t      \\\n\t\t)\t\t\t\t\t\t\t      \\\n\t(&(dest), (src))\n#endif\n"
  },
  {
    "path": "include/re_crc32.h",
    "content": "/**\n * @file re_crc32.h  Interface to CRC-32 functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nuint32_t re_crc32(uint32_t crc, const void *buf, uint32_t size);\n"
  },
  {
    "path": "include/re_dbg.h",
    "content": "/**\n * @file re_dbg.h  Interface to debugging module\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Debug levels */\nenum {\n\tDBG_EMERG       = 0,       /**< System is unusable               */\n\tDBG_ALERT       = 1,       /**< Action must be taken immediately */\n\tDBG_CRIT        = 2,       /**< Critical conditions              */\n\tDBG_ERR         = 3,       /**< Error conditions                 */\n\tDBG_WARNING     = 4,       /**< Warning conditions               */\n\tDBG_NOTICE      = 5,       /**< Normal but significant condition */\n\tDBG_INFO        = 6,       /**< Informational                    */\n\tDBG_DEBUG       = 7        /**< Debug-level messages             */\n};\n\n\n/**\n * @def DEBUG_MODULE\n *\n * Module name defined by application\n */\n\n/**\n * @def DEBUG_LEVEL\n *\n * Debug level defined by application\n */\n\n#ifndef DEBUG_MODULE\n# warning \"DEBUG_MODULE is not defined\"\n#define DEBUG_MODULE \"?\"\n#endif\n\n#ifndef DEBUG_LEVEL\n# warning \"DEBUG_LEVEL is not defined\"\n#define DEBUG_LEVEL 7\n#endif\n\n\n/**\n * @def DEBUG_WARNING(...)\n *\n * Print warning message\n */\n#if (DEBUG_LEVEL >= 4)\n#define DEBUG_WARNING(...) \\\n\tdbg_printf(DBG_WARNING, DEBUG_MODULE \": \" __VA_ARGS__)\n#else\n#define DEBUG_WARNING(...)\n#endif\n\n/**\n * @def DEBUG_NOTICE(...)\n *\n * Print notice message\n */\n#if (DEBUG_LEVEL >= 5)\n#define DEBUG_NOTICE(...) \\\n\tdbg_printf(DBG_NOTICE, DEBUG_MODULE \": \" __VA_ARGS__)\n#else\n#define DEBUG_NOTICE(...)\n#endif\n\n/**\n * @def DEBUG_INFO(...)\n *\n * Print info message\n */\n#if (DEBUG_LEVEL >= 6)\n#define DEBUG_INFO(...) \\\n\tdbg_printf(DBG_INFO, DEBUG_MODULE \": \" __VA_ARGS__)\n#else\n#define DEBUG_INFO(...)\n#endif\n\n/**\n * @def DEBUG_PRINTF(...)\n *\n * Print debug message\n */\n#if (DEBUG_LEVEL >= 7)\n#define DEBUG_PRINTF(...) \\\n\tdbg_printf(DBG_DEBUG, DEBUG_MODULE \": \" __VA_ARGS__)\n#else\n#define DEBUG_PRINTF(...)\n#endif\n\n\n/** Debug flags */\nenum dbg_flags {\n\tDBG_NONE = 0,                 /**< No debug flags         */\n\tDBG_TIME = 1<<0,              /**< Print timestamp flag   */\n\tDBG_ANSI = 1<<1,              /**< Print ANSI color codes */\n\tDBG_ALL  = DBG_TIME|DBG_ANSI  /**< All flags enabled      */\n};\n\n\n/**\n * Defines the debug print handler\n *\n * @param level Debug level\n * @param p     Debug string\n * @param len   String length\n * @param arg   Handler argument\n */\ntypedef void (dbg_print_h)(int level, const char *p, size_t len, void *arg);\n\nvoid dbg_init(int level, enum dbg_flags flags);\nvoid dbg_handler_set(dbg_print_h *ph, void *arg);\nvoid dbg_printf(int level, const char *fmt, ...);\nconst char *dbg_level_str(int level);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "include/re_dd.h",
    "content": "/**\n * @file re_dd.h Dependency Descriptor (DD)\n *\n * Copyright (C) 2010 - 2023 Alfred E. Heggestad\n */\n\n\n/*\n * Put bits wrapper (XXX: move to common place)\n */\n\nstruct putbit {\n\tstruct mbuf *mb;\n\tsize_t bit_pos;\n};\n\nvoid putbit_init(struct putbit *pb, struct mbuf *mb);\nint  putbit_one(struct putbit *pb, unsigned bit);\nint  putbit_write(struct putbit *pb, unsigned count, unsigned val);\nint  putbit_write_ns(struct putbit *pb, unsigned n, unsigned v);\n\n\n/*\n * Dependency Descriptor (DD)\n *\n * DT:  Decode Target\n * DTI: Decode Target Indication\n * SID: Spatial ID\n * TID: Temporal ID\n */\n\n/* Constants. */\nenum {\n\tDD_MAX_SPATIAL_IDS   =   4u,\n\tDD_MAX_TEMPORAL_IDS  =   4u,\n\tDD_MAX_TEMPLATES     =   8u,\n\tDD_MAX_FDIFFS        =  16u,\n\tDD_MAX_DECODE_TARGETS=  16u,\n\tDD_MAX_CHAINS        =  32u,\n};\n\n\n/* Decode Target Indication (DTI) */\nenum dd_dti {\n\tDD_DTI_NOT_PRESENT = 0,\n\tDD_DTI_DISCARDABLE = 1,\n\tDD_DTI_SWITCH      = 2,\n\tDD_DTI_REQUIRED    = 3,\n};\n\nenum dd_next_layer_idc {\n\tDD_SAME_LAYER          = 0,\n\tDD_NEXT_TEMPORAL_LAYER = 1,\n\tDD_NEXT_SPATIAL_LAYER  = 2,\n\tDD_NO_MORE_TEMPLATES   = 3,\n};\n\n/*\n * https://aomediacodec.github.io/av1-rtp-spec/\n *     #dependency-descriptor-rtp-header-extension\n */\nstruct dd {\n\n\t/* Mandatory Descriptor Fields */\n\tunsigned start_of_frame:1;\n\tunsigned end_of_frame:1;\n\tunsigned frame_dependency_template_id:6;\n\tuint16_t frame_number;\n\n\tbool ext;\n\n\tunsigned template_dependency_structure_present_flag:1;\n\tunsigned active_decode_targets_present_flag:1;\n\tunsigned custom_dtis_flag:1;\n\tunsigned custom_fdiffs_flag:1;\n\tunsigned custom_chains_flag:1;\n\n\tunsigned active_decode_targets_bitmask;\n\tunsigned template_id_offset:6;\n\tuint8_t dt_cnt;\n\tuint8_t template_cnt;\n\tuint8_t max_spatial_id;\n\n\tuint8_t template_spatial_id[DD_MAX_TEMPLATES];\n\tuint8_t template_temporal_id[DD_MAX_TEMPLATES];\n\n\t/* render_resolutions */\n\tbool resolutions_present_flag;\n\tuint16_t max_render_width_minus_1[DD_MAX_SPATIAL_IDS];\n\tuint16_t max_render_height_minus_1[DD_MAX_SPATIAL_IDS];\n\tuint8_t render_count;\n\n\t/* type: enum dd_dti */\n\tuint8_t template_dti[DD_MAX_TEMPLATES][DD_MAX_DECODE_TARGETS];\n\n\t/* template fdiffs */\n\tuint8_t template_fdiff[DD_MAX_TEMPLATES][DD_MAX_FDIFFS];\n\tuint8_t template_fdiff_cnt[DD_MAX_TEMPLATES];\n\n\t/* template chains */\n\tuint8_t decode_target_protected_by[DD_MAX_DECODE_TARGETS];\n\tuint8_t template_chain_fdiff[DD_MAX_TEMPLATES][DD_MAX_CHAINS];\n\tuint8_t chain_cnt;\n};\n\nint  dd_encode(struct mbuf *mb, const struct dd *dd);\nint  dd_decode(struct dd *dd, const uint8_t *buf, size_t sz);\nint  dd_print(struct re_printf *pf, const struct dd *dd);\n"
  },
  {
    "path": "include/re_dns.h",
    "content": "/**\n * @file re_dns.h  Interface to DNS module\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nenum {\n\tDNS_PORT = 53,\n\tDNS_HEADER_SIZE = 12\n};\n\n\n/** DNS Opcodes */\nenum {\n\tDNS_OPCODE_QUERY  = 0,\n\tDNS_OPCODE_IQUERY = 1,\n\tDNS_OPCODE_STATUS = 2,\n\tDNS_OPCODE_NOTIFY = 4\n};\n\n\n/** DNS Response codes */\nenum {\n\tDNS_RCODE_OK       = 0,\n\tDNS_RCODE_FMT_ERR  = 1,\n\tDNS_RCODE_SRV_FAIL = 2,\n\tDNS_RCODE_NAME_ERR = 3,\n\tDNS_RCODE_NOT_IMPL = 4,\n\tDNS_RCODE_REFUSED  = 5,\n\tDNS_RCODE_NOT_AUTH = 9\n};\n\n\n/** DNS Resource Record types */\nenum {\n\tDNS_TYPE_A     = 0x0001,\n\tDNS_TYPE_NS    = 0x0002,\n\tDNS_TYPE_CNAME = 0x0005,\n\tDNS_TYPE_SOA   = 0x0006,\n\tDNS_TYPE_PTR   = 0x000c,\n\tDNS_TYPE_MX    = 0x000f,\n\tDNS_TYPE_TXT   = 0x0010,\n\tDNS_TYPE_AAAA  = 0x001c,\n\tDNS_TYPE_SRV   = 0x0021,\n\tDNS_TYPE_NAPTR = 0x0023,\n\tDNS_QTYPE_IXFR = 0x00fb,\n\tDNS_QTYPE_AXFR = 0x00fc,\n\tDNS_QTYPE_ANY  = 0x00ff\n};\n\n\n/** DNS Classes */\nenum {\n\tDNS_CLASS_IN   = 0x0001,\n\tDNS_QCLASS_ANY = 0x00ff\n};\n\n\n/** Defines a DNS Header */\nstruct dnshdr {\n\tuint16_t id;\n\tbool qr;\n\tuint8_t opcode;\n\tbool aa;\n\tbool tc;\n\tbool rd;\n\tbool ra;\n\tuint8_t z;\n\tuint8_t rcode;\n\tuint16_t nq;\n\tuint16_t nans;\n\tuint16_t nauth;\n\tuint16_t nadd;\n};\n\n\n/** Defines a DNS Resource Record (RR) */\nstruct dnsrr {\n\tstruct le le;\n\tstruct le le_priv;\n\tchar *name;\n\tuint16_t type;\n\tuint16_t dnsclass;\n\tint64_t ttl;\n\tuint16_t rdlen;\n\tunion {\n\t\tstruct {\n\t\t\tuint32_t addr;\n\t\t} a;\n\t\tstruct {\n\t\t\tchar *nsdname;\n\t\t} ns;\n\t\tstruct {\n\t\t\tchar *cname;\n\t\t} cname;\n\t\tstruct {\n\t\t\tchar *mname;\n\t\t\tchar *rname;\n\t\t\tuint32_t serial;\n\t\t\tuint32_t refresh;\n\t\t\tuint32_t retry;\n\t\t\tuint32_t expire;\n\t\t\tuint32_t ttlmin;\n\t\t} soa;\n\t\tstruct {\n\t\t\tchar *ptrdname;\n\t\t} ptr;\n\t\tstruct {\n\t\t\tuint16_t pref;\n\t\t\tchar *exchange;\n\t\t} mx;\n\t\tstruct {\n\t\t\tchar *data;\n\t\t} txt;\n\t\tstruct {\n\t\t\tuint8_t addr[16];\n\t\t} aaaa;\n\t\tstruct {\n\t\t\tuint16_t pri;\n\t\t\tuint16_t weight;\n\t\t\tuint16_t port;\n\t\t\tchar *target;\n\t\t} srv;\n\t\tstruct {\n\t\t\tuint16_t order;\n\t\t\tuint16_t pref;\n\t\t\tchar *flags;\n\t\t\tchar *services;\n\t\t\tchar *regexp;\n\t\t\tchar *replace;\n\t\t} naptr;\n\t} rdata;\n};\n\nstruct hash;\n\n/**\n * Defines the DNS Query handler\n *\n * @param err   0 if success, otherwise errorcode\n * @param hdr   DNS Header\n * @param ansl  List of Answer records\n * @param authl List of Authoritative records\n * @param addl  List of Additional records\n * @param arg   Handler argument\n */\ntypedef void(dns_query_h)(int err, const struct dnshdr *hdr,\n\t\t\t  struct list *ansl, struct list *authl,\n\t\t\t  struct list *addl, void *arg);\n\n/**\n * Defines the DNS Resource Record list handler\n *\n * @param rr  DNS Resource Record\n * @param arg Handler argument\n *\n * @return True to stop traversing, False to continue\n */\ntypedef bool(dns_rrlist_h)(struct dnsrr *rr, void *arg);\n\nint  dns_hdr_encode(struct mbuf *mb, const struct dnshdr *hdr);\nint  dns_hdr_decode(struct mbuf *mb, struct dnshdr *hdr);\nconst char *dns_hdr_opcodename(uint8_t opcode);\nconst char *dns_hdr_rcodename(uint8_t rcode);\nstruct dnsrr *dns_rr_alloc(void);\nint  dns_rr_dup(struct dnsrr **rrp, const struct dnsrr *rr);\nint  dns_rr_encode(struct mbuf *mb, const struct dnsrr *rr, int64_t ttl_offs,\n\t\t   struct hash *ht_dname, size_t start);\nint  dns_rr_decode(struct mbuf *mb, struct dnsrr **rr, size_t start);\nbool dns_rr_cmp(const struct dnsrr *rr1, const struct dnsrr *rr2, bool rdata);\nconst char *dns_rr_typename(uint16_t type);\nconst char *dns_rr_classname(uint16_t dnsclass);\nint  dns_rr_print(struct re_printf *pf, const struct dnsrr *rr);\nint  dns_dname_encode(struct mbuf *mb, const char *name,\n\t\t      struct hash *ht_dname, size_t start, bool comp);\nint  dns_dname_decode(struct mbuf *mb, char **name, size_t start);\nint  dns_cstr_encode(struct mbuf *mb, const char *str);\nint  dns_cstr_decode(struct mbuf *mb, char **str);\nvoid dns_rrlist_sort(struct list *rrl, uint16_t type, size_t key);\nvoid dns_rrlist_sort_addr(struct list *rrl, size_t key);\nstruct dnsrr *dns_rrlist_apply(struct list *rrl, const char *name,\n\t\t\t       uint16_t type, uint16_t dnsclass,\n\t\t\t       bool recurse, dns_rrlist_h *rrlh, void *arg);\nstruct dnsrr *dns_rrlist_apply2(struct list *rrl, const char *name,\n\t\t\t\tuint16_t type1, uint16_t type2,\n\t\t\t\tuint16_t dnsclass, bool recurse,\n\t\t\t\tdns_rrlist_h *rrlh, void *arg);\nstruct dnsrr *dns_rrlist_find(struct list *rrl, const char *name,\n\t\t\t      uint16_t type, uint16_t dnsclass, bool recurse);\n\n\n/* DNS Client */\nstruct sa;\nstruct dnsc;\nstruct dns_query;\n\n/** DNS Client configuration */\nstruct dnsc_conf {\n\tuint32_t query_hash_size;\n\tuint32_t tcp_hash_size;\n\tuint32_t conn_timeout;  /* in [ms] */\n\tuint32_t idle_timeout;  /* in [ms] */\n\tuint32_t cache_ttl_max; /* in [s] 0 for disabled */\n\tbool     getaddrinfo;   /* use getaddrinfo (by default disabled) */\n};\n\nint  dnsc_alloc(struct dnsc **dcpp, const struct dnsc_conf *conf,\n\t\tconst struct sa *srvv, uint32_t srvc);\nint  dnsc_conf_set(struct dnsc *dnsc, const struct dnsc_conf *conf);\nvoid dnsc_conf_set_timeout(struct dnsc *dnsc, uint32_t connect, uint32_t idle);\nint  dnsc_srv_set(struct dnsc *dnsc, const struct sa *srvv, uint32_t srvc);\nint  dnsc_query(struct dns_query **qp, struct dnsc *dnsc, const char *name,\n\t\tuint16_t type, uint16_t dnsclass,\n\t\tbool rd, dns_query_h *qh, void *arg);\nint  dnsc_query_srv(struct dns_query **qp, struct dnsc *dnsc, const char *name,\n\t\t    uint16_t type, uint16_t dnsclass, int proto,\n\t\t    const struct sa *srvv, const uint32_t *srvc,\n\t\t    bool rd, dns_query_h *qh, void *arg);\nint  dnsc_notify(struct dns_query **qp, struct dnsc *dnsc, const char *name,\n\t\t uint16_t type, uint16_t dnsclass, const struct dnsrr *ans_rr,\n\t\t int proto, const struct sa *srvv, const uint32_t *srvc,\n\t\t dns_query_h *qh, void *arg);\nvoid dnsc_cache_flush(struct dnsc *dnsc);\nvoid dnsc_cache_max(struct dnsc *dnsc, uint32_t max);\nvoid dnsc_getaddrinfo(struct dnsc *dnsc, bool active);\nbool dnsc_getaddrinfo_enabled(struct dnsc *dnsc);\nbool dnsc_getaddrinfo_only(const struct dnsc *dnsc);\n\n\n/* DNS System functions */\nint dns_srv_get(char *domain, size_t dsize, struct sa *srvv, uint32_t *n);\n"
  },
  {
    "path": "include/re_fmt.h",
    "content": "/**\n * @file re_fmt.h  Interface to formatted text functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#include <stdarg.h>\n#include <stdio.h>\n\nenum {\n\tITOA_BUFSZ = 34,\n};\n\nstruct mbuf;\n\n\n/** Defines a pointer-length string type */\nstruct pl {\n\tconst char *p;  /**< Pointer to string */\n\tsize_t l;       /**< Length of string  */\n};\n\n/** Initialise a pointer-length object from a constant string */\n#define PL(s) {(s), sizeof((s))-1}\n\n/** Pointer-length Initializer */\n#define PL_INIT {NULL, 0}\n\nextern const struct pl pl_null;\n\nstruct pl *pl_alloc_str(const char *str);\nstruct pl *pl_alloc_dup(const struct pl *src);\nvoid     pl_set_str(struct pl *pl, const char *str);\nvoid     pl_set_mbuf(struct pl *pl, const struct mbuf *mb);\nint32_t  pl_i32(const struct pl *pl);\nint64_t  pl_i64(const struct pl *pl);\nuint32_t pl_u32(const struct pl *pl);\nuint32_t pl_x32(const struct pl *pl);\nuint64_t pl_u64(const struct pl *pl);\nuint64_t pl_x64(const struct pl *pl);\ndouble   pl_float(const struct pl *pl);\nint      pl_bool(bool *val, const struct pl *pl);\nint      pl_hex(const struct pl *pl, uint8_t *hex, size_t len);\nbool     pl_isset(const struct pl *pl);\nint      pl_strcpy(const struct pl *pl, char *str, size_t size);\nint      pl_strdup(char **dst, const struct pl *src);\nint      pl_dup(struct pl *dst, const struct pl *src);\nint      pl_strcmp(const struct pl *pl, const char *str);\nint      pl_strncmp(const struct pl *pl, const char *str, size_t n);\nint      pl_strncasecmp(const struct pl *pl, const char *str, size_t n);\nint      pl_strcasecmp(const struct pl *pl, const char *str);\nint      pl_cmp(const struct pl *pl1, const struct pl *pl2);\nint      pl_casecmp(const struct pl *pl1, const struct pl *pl2);\nconst char *pl_strchr(const struct pl *pl, char c);\nconst char *pl_strrchr(const struct pl *pl, char c);\nconst char *pl_strstr(const struct pl *pl, const char *str);\nint      pl_trim(struct pl *pl);\nint      pl_ltrim(struct pl *pl);\nint      pl_rtrim(struct pl *pl);\nvoid     pl_strip_html(struct pl *pl);\n\n/** Advance pl position/length by +/- N bytes */\nstatic inline void pl_advance(struct pl *pl, ssize_t n)\n{\n\tpl->p += n;\n\tpl->l -= n;\n}\n\n\n/* Formatted printing */\n\n/**\n * Defines the re_vhprintf() print handler\n *\n * @param p    String to print\n * @param size Size of string to print\n * @param arg  Handler argument\n *\n * @return 0 for success, otherwise errorcode\n */\ntypedef int(re_vprintf_h)(const char *p, size_t size, void *arg);\n\n/** Defines a print backend */\nstruct re_printf {\n\tre_vprintf_h *vph; /**< Print handler   */\n\tvoid *arg;         /**< Handler argument */\n};\n\n/**\n * Defines the %H print handler\n *\n * @param pf  Print backend\n * @param arg Handler argument\n *\n * @return 0 for success, otherwise errorcode\n */\ntypedef int(re_printf_h)(struct re_printf *pf, void *arg);\n\nint re_vhprintf(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg);\nint re_vfprintf(FILE *stream, const char *fmt, va_list ap);\nint re_vprintf(const char *fmt, va_list ap);\nint re_vsnprintf(char *re_restrict str, size_t size,\n\t\t const char *re_restrict fmt, va_list ap);\nint re_vsdprintf(char **strp, const char *fmt, va_list ap);\n\n/* Secure va_list print */\nint re_vhprintf_s(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg);\nint re_vfprintf_s(FILE *stream, const char *fmt, va_list ap);\nint re_vprintf_s(const char *fmt, va_list ap);\nint re_vsnprintf_s(char *re_restrict str, size_t size,\n\t\t const char *re_restrict fmt, va_list ap);\nint re_vsdprintf_s(char **strp, const char *fmt, va_list ap);\n\n#ifdef HAVE_RE_ARG\n#define re_printf(fmt, ...) _re_printf_s((fmt), RE_VA_ARGS(__VA_ARGS__))\n#define re_hprintf(pf, fmt, ...)                                              \\\n\t_re_hprintf_s((pf), (fmt), RE_VA_ARGS(__VA_ARGS__))\n#define re_fprintf(stream, fmt, ...)                                          \\\n\t_re_fprintf_s((stream), (fmt), RE_VA_ARGS(__VA_ARGS__))\n#define re_snprintf(str, size, fmt, ...)                                      \\\n\t_re_snprintf_s((str), (size), (fmt), RE_VA_ARGS(__VA_ARGS__))\n#define re_sdprintf(strp, fmt, ...)                                           \\\n\t_re_sdprintf_s((strp), (fmt), RE_VA_ARGS(__VA_ARGS__))\n#else\n#define re_printf(...) _re_printf(__VA_ARGS__)\n#define re_hprintf _re_hprintf\n#define re_fprintf _re_fprintf\n#define re_snprintf _re_snprintf\n#define re_sdprintf _re_sdprintf\n#endif\n\nint _re_printf(const char *fmt, ...);\nint _re_hprintf(struct re_printf *pf, const char *fmt, ...);\nint _re_fprintf(FILE *stream, const char *fmt, ...);\nint _re_snprintf(char *re_restrict str, size_t size,\n\t\t const char *re_restrict fmt, ...);\nint _re_sdprintf(char **strp, const char *fmt, ...);\n\nint _re_printf_s(const char *fmt, ...);\nint _re_hprintf_s(struct re_printf *pf, const char *fmt, ...);\nint _re_fprintf_s(FILE *stream, const char *fmt, ...);\nint _re_snprintf_s(char *re_restrict str, size_t size,\n\t\t   const char *re_restrict fmt, ...);\nint _re_sdprintf_s(char **strp, const char *fmt, ...);\n\n\n/* Regular expressions */\nint re_regex(const char *ptr, size_t len, const char *expr, ...);\n\n\n/* Character functions */\nuint8_t ch_hex(char ch);\n\n\n/* String functions */\nint  str_bool(bool *val, const char *str);\nint  str_hex(uint8_t *hex, size_t len, const char *str);\nvoid str_ncpy(char *dst, const char *src, size_t n);\nint  str_dup(char **dst, const char *src);\nint  str_x64dup(char **dst, uint64_t val);\nint  str_cmp(const char *s1, const char *s2);\nint  str_ncmp(const char *s1, const char *s2, size_t n);\nconst char *str_str(const char *s1, const char *s2);\nint  str_casecmp(const char *s1, const char *s2);\nsize_t str_len(const char *s);\nconst char *str_error(int errnum, char *buf, size_t sz);\nchar *str_itoa(uint32_t val, char *buf, int base);\nwchar_t *str_wchar(const char *c);\n\n\n/**\n * Check if string is set\n *\n * @param s Zero-terminated string\n *\n * @return true if set, false if not set\n */\nstatic inline bool str_isset(const char *s)\n{\n\treturn s && s[0] != '\\0';\n}\n\n\n/* time */\nint fmt_gmtime(struct re_printf *pf, void *ts);\nint fmt_timestamp(struct re_printf *pf, void *ts);\nint fmt_timestamp_us(struct re_printf *pf, void *arg);\nint fmt_human_time(struct re_printf *pf, const uint32_t *seconds);\n\n\nvoid hexdump(FILE *f, const void *p, size_t len);\n\n\n/* param */\ntypedef void (fmt_param_h)(const struct pl *name, const struct pl *val,\n\t\t\t   void *arg);\n\nbool fmt_param_exists(const struct pl *pl, const char *pname);\nbool fmt_param_sep_get(const struct pl *pl, const char *pname, char sep,\n\t\tstruct pl *val);\nbool fmt_param_get(const struct pl *pl, const char *pname, struct pl *val);\nvoid fmt_param_apply(const struct pl *pl, fmt_param_h *ph, void *arg);\n\n\n/* unicode */\nint utf8_encode(struct re_printf *pf, const char *str);\nint utf8_decode(struct re_printf *pf, const struct pl *pl);\nsize_t utf8_byteseq(char u[4], unsigned cp);\n\n\n/* text2pcap */\nstruct re_text2pcap {\n\tbool in;\n\tconst struct mbuf *mb;\n\tconst char *id;\n};\n\nint re_text2pcap(struct re_printf *pf, struct re_text2pcap *pcap);\nvoid re_text2pcap_trace(const char *name, const char *id, bool in,\n\t\t\tconst struct mbuf *mb);\n"
  },
  {
    "path": "include/re_h264.h",
    "content": "/**\n * @file re_h264.h Interface to H.264 header parser\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** NAL unit types */\nenum h264_nalu {\n\tH264_NALU_SLICE        = 1,\n\tH264_NALU_DPA          = 2,\n\tH264_NALU_DPB          = 3,\n\tH264_NALU_DPC          = 4,\n\tH264_NALU_IDR_SLICE    = 5,\n\tH264_NALU_SEI          = 6,\n\tH264_NALU_SPS          = 7,\n\tH264_NALU_PPS          = 8,\n\tH264_NALU_AUD          = 9,\n\tH264_NALU_END_SEQUENCE = 10,\n\tH264_NALU_END_STREAM   = 11,\n\tH264_NALU_FILLER_DATA  = 12,\n\tH264_NALU_SPS_EXT      = 13,\n\tH264_NALU_AUX_SLICE    = 19,\n\n\tH264_NALU_STAP_A       = 24,\n\tH264_NALU_STAP_B       = 25,\n\tH264_NALU_MTAP16       = 26,\n\tH264_NALU_MTAP24       = 27,\n\tH264_NALU_FU_A         = 28,\n\tH264_NALU_FU_B         = 29,\n};\n\n\n/**\n * H.264 NAL Header\n */\nstruct h264_nal_header {\n\tunsigned f:1;      /**< Forbidden zero bit (must be 0) */\n\tunsigned nri:2;    /**< nal_ref_idc                    */\n\tunsigned type:5;   /**< NAL unit type                  */\n};\n\n\nint h264_nal_header_encode(struct mbuf *mb, const struct h264_nal_header *hdr);\nint h264_nal_header_decode(struct h264_nal_header *hdr, struct mbuf *mb);\nvoid h264_nal_header_decode_buf(struct h264_nal_header *hdr,\n\t\t\t\tconst uint8_t *buf);\nconst char *h264_nal_unit_name(enum h264_nalu nal_type);\n\n\n/**\n * H.264 Sequence Parameter Set (SPS)\n */\nstruct h264_sps {\n\tuint8_t profile_idc;\n\tuint8_t level_idc;\n\tuint8_t seq_parameter_set_id;               /* 0-31 */\n\tuint8_t chroma_format_idc;                  /* 0-3 */\n\n\tunsigned log2_max_frame_num;\n\tunsigned pic_order_cnt_type;\n\n\tunsigned max_num_ref_frames;\n\tunsigned pic_width_in_mbs;\n\tunsigned pic_height_in_map_units;\n\n\tunsigned frame_crop_left_offset;            /* pixels */\n\tunsigned frame_crop_right_offset;           /* pixels */\n\tunsigned frame_crop_top_offset;             /* pixels */\n\tunsigned frame_crop_bottom_offset;          /* pixels */\n};\n\nint  h264_sps_decode(struct h264_sps *sps, const uint8_t *p, size_t len);\nvoid h264_sps_resolution(const struct h264_sps *sps,\n\t\tunsigned *width, unsigned *height);\nconst char *h264_sps_chroma_format_name(uint8_t chroma_format_idc);\n\n\ntypedef int (h264_packet_h)(bool marker, uint64_t rtp_ts,\n\t\t\t    const uint8_t *hdr, size_t hdr_len,\n\t\t\t    const uint8_t *pld, size_t pld_len,\n\t\t\t    void *arg);\n\n/** Fragmentation Unit header */\nstruct h264_fu {\n\tunsigned s:1;      /**< Start bit                               */\n\tunsigned e:1;      /**< End bit                                 */\n\tunsigned r:1;      /**< The Reserved bit MUST be equal to 0     */\n\tunsigned type:5;   /**< The NAL unit payload type               */\n};\n\nint h264_fu_hdr_encode(const struct h264_fu *fu, struct mbuf *mb);\nint h264_fu_hdr_decode(struct h264_fu *fu, struct mbuf *mb);\n\nconst uint8_t *h264_find_startcode(const uint8_t *p, const uint8_t *end);\n\nint h264_packetize(uint64_t rtp_ts, const uint8_t *buf, size_t len,\n\t\t   size_t pktsize, h264_packet_h *pkth, void *arg);\nint h264_nal_send(bool first, bool last,\n\t\t  bool marker, uint32_t ihdr, uint64_t rtp_ts,\n\t\t  const uint8_t *buf, size_t size, size_t maxsz,\n\t\t  h264_packet_h *pkth, void *arg);\nbool h264_is_keyframe(int type);\nint  h264_stap_encode(struct mbuf *mb, const uint8_t *frame,\n\t\t      size_t frame_sz);\nint  h264_stap_decode_annexb(struct mbuf *mb_frame, struct mbuf *mb_pkt);\nint  h264_stap_decode_annexb_long(struct mbuf *mb_frame, struct mbuf *mb_pkt);\n\n\n/*\n * Get bits wrapper\n */\n\nstruct getbit {\n\tconst uint8_t *buf;\n\tsize_t pos;\n\tsize_t end;\n};\n\nvoid     getbit_init(struct getbit *gb, const uint8_t *buf, size_t size);\nsize_t   getbit_get_left(const struct getbit *gb);\nunsigned get_bit(struct getbit *gb);\nint      get_ue_golomb(struct getbit *gb, unsigned *valp);\nunsigned get_bits(struct getbit *gb, unsigned n);\nunsigned getbit_read_ns(struct getbit *gb, unsigned n);\n"
  },
  {
    "path": "include/re_h265.h",
    "content": "/**\n * @file re_h265.h Interface to H.265 header parser\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n\nenum {\n\tH265_HDR_SIZE = 2\n};\n\n\nenum h265_naltype {\n\t/* VCL class */\n\tH265_NAL_TRAIL_N         = 0,\n\tH265_NAL_TRAIL_R         = 1,\n\n\tH265_NAL_TSA_N           = 2,\n\tH265_NAL_TSA_R           = 3,\n\n\tH265_NAL_STSA_N          = 4,\n\tH265_NAL_STSA_R          = 5,\n\tH265_NAL_RADL_N          = 6,\n\tH265_NAL_RADL_R          = 7,\n\n\tH265_NAL_RASL_N          = 8,\n\tH265_NAL_RASL_R          = 9,\n\n\tH265_NAL_BLA_W_LP        = 16,\n\tH265_NAL_BLA_W_RADL      = 17,\n\tH265_NAL_BLA_N_LP        = 18,\n\tH265_NAL_IDR_W_RADL      = 19,\n\tH265_NAL_IDR_N_LP        = 20,\n\tH265_NAL_CRA_NUT         = 21,\n\tH265_NAL_RSV_IRAP_VCL22  = 22,\n\tH265_NAL_RSV_IRAP_VCL23  = 23,\n\n\t/* non-VCL class */\n\tH265_NAL_VPS_NUT         = 32,\n\tH265_NAL_SPS_NUT         = 33,\n\tH265_NAL_PPS_NUT         = 34,\n\tH265_NAL_AUD             = 35,\n\tH265_NAL_EOS_NUT         = 36,    /* End of sequence */\n\tH265_NAL_EOB_NUT         = 37,    /* End of bitstream */\n\tH265_NAL_FD_NUT          = 38,    /* Filler data */\n\tH265_NAL_PREFIX_SEI_NUT  = 39,\n\tH265_NAL_SUFFIX_SEI_NUT  = 40,\n\n\t/* RFC 7798 */\n\tH265_NAL_AP              = 48,    /* Aggregation Packets */\n\tH265_NAL_FU              = 49,\n};\n\nstruct h265_nal {\n\tunsigned nal_unit_type:6;          /* NAL unit type (0-40)       */\n\tunsigned nuh_layer_id:6;           /* layer identifier           */\n\tunsigned nuh_temporal_id_plus1:3;  /* temporal identifier plus 1 */\n};\n\nvoid h265_nal_encode(uint8_t buf[2], unsigned nal_unit_type,\n\t\t     unsigned nuh_temporal_id_plus1);\nint  h265_nal_encode_mbuf(struct mbuf *mb, const struct h265_nal *nal);\nint  h265_nal_decode(struct h265_nal *nal, const uint8_t *p);\nint  h265_nal_print(struct re_printf *pf, const struct h265_nal *nal);\nconst char *h265_nalunit_name(enum h265_naltype type);\nbool h265_is_keyframe(enum h265_naltype type);\n\n\ntypedef int (h265_packet_h)(bool marker, uint64_t rtp_ts,\n\t\t\t    const uint8_t *hdr, size_t hdr_len,\n\t\t\t    const uint8_t *pld, size_t pld_len,\n\t\t\t    void *arg);\n\nint h265_packetize(uint64_t rtp_ts, const uint8_t *buf, size_t len,\n\t\t   size_t pktsize, h265_packet_h *pkth, void *arg);\n"
  },
  {
    "path": "include/re_hash.h",
    "content": "/**\n * @file re_hash.h  Interface to hashmap table\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nstruct hash;\nstruct pl;\n\n\nint  hash_alloc(struct hash **hp, uint32_t bsize);\nvoid hash_append(struct hash *h, uint32_t key, struct le *le, void *data);\nvoid hash_unlink(struct le *le);\nstruct le *hash_lookup(const struct hash *h, uint32_t key, list_apply_h *ah,\n\t\t       void *arg);\nstruct le *hash_apply(const struct hash *h, list_apply_h *ah, void *arg);\nstruct list *hash_list_idx(const struct hash *h, uint32_t i);\nstruct list *hash_list(const struct hash *h, uint32_t key);\nuint32_t hash_bsize(const struct hash *h);\nvoid hash_flush(struct hash *h);\nvoid hash_clear(struct hash *h);\nuint32_t hash_valid_size(uint32_t size);\nint hash_debug(struct re_printf *pf, struct hash *h);\n\n\n/* Hash functions */\nuint32_t hash_joaat(const uint8_t *key, size_t len);\nuint32_t hash_joaat_ci(const char *str, size_t len);\nuint32_t hash_joaat_str(const char *str);\nuint32_t hash_joaat_str_ci(const char *str);\nuint32_t hash_joaat_pl(const struct pl *pl);\nuint32_t hash_joaat_pl_ci(const struct pl *pl);\nuint32_t hash_fast(const char *k, size_t len);\nuint32_t hash_fast_str(const char *str);\n"
  },
  {
    "path": "include/re_hmac.h",
    "content": "/**\n * @file re_hmac.h  Interface to HMAC functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nvoid hmac_sha1(const uint8_t *k,   /* secret key */\n\t       size_t         lk,  /* length of the key in bytes */\n\t       const uint8_t *d,   /* data */\n\t       size_t         ld,  /* length of data in bytes */\n\t       uint8_t*       out, /* output buffer, at least \"t\" bytes */\n\t       size_t         t);\n\nvoid hmac_sha256(const uint8_t *key,\n\t\t size_t         key_len,\n\t\t const uint8_t *data,\n\t\t size_t         data_len,\n\t\t uint8_t       *out,\n\t\t size_t         out_len);\n\n\nenum hmac_hash {\n\tHMAC_HASH_SHA1,\n\tHMAC_HASH_SHA256\n};\n\nstruct hmac;\n\nint  hmac_create(struct hmac **hmacp, enum hmac_hash hash,\n\t\t const uint8_t *key, size_t key_len);\nint  hmac_digest(struct hmac *hmac, uint8_t *md, size_t md_len,\n\t\t const uint8_t *data, size_t data_len);\n"
  },
  {
    "path": "include/re_http.h",
    "content": "/**\n * @file re_http.h  Hypertext Transfer Protocol\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n/* forward declarations */\nstruct tls;\n\n/** HTTP Header ID (perfect hash value) */\nenum http_hdrid {\n\tHTTP_HDR_ACCEPT                   = 3186,\n\tHTTP_HDR_ACCEPT_CHARSET           =   24,\n\tHTTP_HDR_ACCEPT_ENCODING          =  708,\n\tHTTP_HDR_ACCEPT_LANGUAGE          = 2867,\n\tHTTP_HDR_ACCEPT_RANGES            = 3027,\n\tHTTP_HDR_AGE                      =  742,\n\tHTTP_HDR_ALLOW                    = 2429,\n\tHTTP_HDR_AUTHORIZATION            = 2503,\n\tHTTP_HDR_CACHE_CONTROL            = 2530,\n\tHTTP_HDR_CONNECTION               =  865,\n\tHTTP_HDR_CONTENT_ENCODING         =  580,\n\tHTTP_HDR_CONTENT_LANGUAGE         = 3371,\n\tHTTP_HDR_CONTENT_LENGTH           = 3861,\n\tHTTP_HDR_CONTENT_LOCATION         = 3927,\n\tHTTP_HDR_CONTENT_MD5              =  406,\n\tHTTP_HDR_CONTENT_RANGE            = 2846,\n\tHTTP_HDR_CONTENT_TYPE             =  809,\n\tHTTP_HDR_DATE                     = 1027,\n\tHTTP_HDR_ETAG                     = 2392,\n\tHTTP_HDR_EXPECT                   = 1550,\n\tHTTP_HDR_EXPIRES                  = 1983,\n\tHTTP_HDR_FROM                     = 1963,\n\tHTTP_HDR_HOST                     = 3191,\n\tHTTP_HDR_IF_MATCH                 = 2684,\n\tHTTP_HDR_IF_MODIFIED_SINCE        = 2187,\n\tHTTP_HDR_IF_NONE_MATCH            = 4030,\n\tHTTP_HDR_IF_RANGE                 = 2220,\n\tHTTP_HDR_IF_UNMODIFIED_SINCE      =  962,\n\tHTTP_HDR_LAST_MODIFIED            = 2946,\n\tHTTP_HDR_LOCATION                 = 2514,\n\tHTTP_HDR_MAX_FORWARDS             = 3549,\n\tHTTP_HDR_PRAGMA                   = 1673,\n\tHTTP_HDR_PROXY_AUTHENTICATE       =  116,\n\tHTTP_HDR_PROXY_AUTHORIZATION      = 2363,\n\tHTTP_HDR_RANGE                    = 4004,\n\tHTTP_HDR_REFERER                  = 2991,\n\tHTTP_HDR_RETRY_AFTER              =  409,\n\tHTTP_HDR_SEC_WEBSOCKET_ACCEPT     = 2959,\n\tHTTP_HDR_SEC_WEBSOCKET_EXTENSIONS = 2937,\n\tHTTP_HDR_SEC_WEBSOCKET_KEY        =  746,\n\tHTTP_HDR_SEC_WEBSOCKET_PROTOCOL   = 2076,\n\tHTTP_HDR_SEC_WEBSOCKET_VERSION    = 3158,\n\tHTTP_HDR_SERVER                   =  973,\n\tHTTP_HDR_TE                       = 2035,\n\tHTTP_HDR_TRAILER                  = 2577,\n\tHTTP_HDR_TRANSFER_ENCODING        = 2115,\n\tHTTP_HDR_UPGRADE                  =  717,\n\tHTTP_HDR_USER_AGENT               = 4064,\n\tHTTP_HDR_VARY                     = 3076,\n\tHTTP_HDR_VIA                      = 3961,\n\tHTTP_HDR_WARNING                  = 2108,\n\tHTTP_HDR_WWW_AUTHENTICATE         = 2763,\n\n\tHTTP_HDR_NONE = -1\n};\n\n\n/** HTTP Header */\nstruct http_hdr {\n\tstruct le le;          /**< Linked-list element     */\n\tstruct pl name;        /**< HTTP Header name        */\n\tstruct pl val;         /**< HTTP Header value       */\n\tenum http_hdrid id;    /**< HTTP Header id (unique) */\n};\n\n/** HTTP Message */\nstruct http_msg {\n\tstruct pl ver;         /**< HTTP Version number                    */\n\tstruct pl met;         /**< Request Method                         */\n\tstruct pl path;        /**< Request path/resource                  */\n\tstruct pl prm;         /**< Request parameters                     */\n\tuint16_t scode;        /**< Response Status code                   */\n\tstruct pl reason;      /**< Response Reason phrase                 */\n\tstruct list hdrl;      /**< List of HTTP headers (struct http_hdr) */\n\tstruct msg_ctype ctyp; /**< Content-type                           */\n\tstruct mbuf *_mb;      /**< Buffer containing the HTTP message     */\n\tstruct mbuf *mb;       /**< Buffer containing the HTTP body        */\n\tuint32_t clen;         /**< Content length                         */\n};\n\n\nstruct http_uri {\n\tstruct pl scheme;\n\tstruct pl host;\n\tstruct pl port;\n\tstruct pl path;\n};\n\nint http_uri_decode(struct http_uri *hu, const struct pl *uri);\n\n\n/** Http Client configuration */\nstruct http_conf {\n\tuint32_t conn_timeout;  /* in [ms] */\n\tuint32_t recv_timeout;  /* in [ms] */\n\tuint32_t idle_timeout;  /* in [ms] */\n};\n\n\ntypedef bool(http_hdr_h)(const struct http_hdr *hdr, void *arg);\n\nint  http_msg_decode(struct http_msg **msgp, struct mbuf *mb, bool req);\n\n\nconst struct http_hdr *http_msg_hdr(const struct http_msg *msg,\n\t\t\t\t    enum http_hdrid id);\nconst struct http_hdr *http_msg_hdr_apply(const struct http_msg *msg,\n\t\t\t\t\t  bool fwd, enum http_hdrid id,\n\t\t\t\t\t  http_hdr_h *h, void *arg);\nconst struct http_hdr *http_msg_xhdr(const struct http_msg *msg,\n\t\t\t\t     const char *name);\nconst struct http_hdr *http_msg_xhdr_apply(const struct http_msg *msg,\n\t\t\t\t\t   bool fwd, const char *name,\n\t\t\t\t\t   http_hdr_h *h, void *arg);\nuint32_t http_msg_hdr_count(const struct http_msg *msg, enum http_hdrid id);\nuint32_t http_msg_xhdr_count(const struct http_msg *msg, const char *name);\nbool http_msg_hdr_has_value(const struct http_msg *msg, enum http_hdrid id,\n\t\t\t    const char *value);\nbool http_msg_xhdr_has_value(const struct http_msg *msg, const char *name,\n\t\t\t     const char *value);\nint  http_msg_print(struct re_printf *pf, const struct http_msg *msg);\n\n\n/* Client */\nstruct http_cli;\nstruct http_req;\nstruct dnsc;\nstruct tcp_conn;\nstruct tls_conn;\n\ntypedef void (http_resp_h)(int err, const struct http_msg *msg, void *arg);\ntypedef int  (http_data_h)(const uint8_t *buf, size_t size,\n\t\t\t   const struct http_msg *msg, void *arg);\ntypedef void (http_conn_h)(struct tcp_conn *tc, struct tls_conn *sc,\n\t\t\t   void *arg);\ntypedef size_t (http_bodyh)(struct mbuf *mb, void *arg);\n\nint http_client_alloc(struct http_cli **clip, struct dnsc *dnsc);\nint http_client_set_config(struct http_cli *cli, struct http_conf *conf);\nint http_request(struct http_req **reqp, struct http_cli *cli, const char *met,\n\t\t const char *uri, http_resp_h *resph, http_data_h *datah,\n\t\t http_bodyh *bodyh, void *arg, const char *fmt, ...);\nint http_request_addr(struct http_req **reqp, struct http_cli *cli,\n\t\t      const char *met, const char *uri, const struct sa *addr,\n\t\t      http_resp_h *resph, http_data_h *datah,\n\t\t      http_bodyh *bodyh, void *arg, const char *fmt, ...);\nvoid http_req_set_conn_handler(struct http_req *req, http_conn_h *connh);\nvoid http_client_set_laddr(struct http_cli *cli, const struct sa *addr);\nvoid http_client_set_laddr6(struct http_cli *cli, const struct sa *addr);\nvoid http_client_set_bufsize_max(struct http_cli *cli, size_t max_size);\nsize_t http_client_get_bufsize_max(struct http_cli *cli);\n\n#ifdef USE_TLS\nint http_client_set_tls(struct http_cli *cli, struct tls *tls);\nint http_client_get_tls(struct http_cli *cli, struct tls **tls);\nint http_client_add_ca(struct http_cli *cli, const char *tls_ca);\nint http_client_add_capem(struct http_cli *cli, const char *capem);\nint http_client_add_crlpem(struct http_cli *cli, const char *pem);\nint http_client_set_tls_hostname(struct http_cli *cli,\n\t\t\t\t const struct pl *hostname);\nint http_client_set_cert(struct http_cli *cli, const char *path);\nint http_client_set_certpem(struct http_cli *cli, const char *pem);\nint http_client_set_key(struct http_cli *cli, const char *path);\nint http_client_set_keypem(struct http_cli *cli, const char *pem);\nint http_client_use_chain(struct http_cli *cli, const char *path);\nint http_client_use_chainpem(struct http_cli *cli, const char *chain,\n\t\t\t     int len_chain);\n\nint http_client_set_session_reuse(struct http_cli *cli, bool enabled);\nint http_client_set_tls_min_version(struct http_cli *cli, int version);\nint http_client_set_tls_max_version(struct http_cli *cli, int version);\nint http_client_disable_verify_server(struct http_cli *cli);\n#endif\n\n/* Server */\nstruct http_sock;\nstruct http_conn;\n\nenum re_https_verify_msg {\n\tHTTPS_MSG_OK = 0,\n\tHTTPS_MSG_REQUEST_CERT = 1,\n\tHTTPS_MSG_IGNORE = 2,\n};\n\ntypedef void (http_req_h)(struct http_conn *conn, const struct http_msg *msg,\n\t\t\t  void *arg);\ntypedef enum re_https_verify_msg (https_verify_msg_h)(struct http_conn *conn,\n\tconst struct http_msg *msg, void *arg);\n\nint http_listen_fd(struct http_sock **sockp, re_sock_t fd, http_req_h *reqh,\n\t\t   void *arg);\nint  http_listen(struct http_sock **sockp, const struct sa *laddr,\n\t\t http_req_h *reqh, void *arg);\nint  https_listen(struct http_sock **sockp, const struct sa *laddr,\n\t\t  const char *cert, http_req_h *reqh, void *arg);\nint  https_set_verify_msgh(struct http_sock *sock,\n\t\t\t   https_verify_msg_h *verifyh);\nvoid http_set_max_body_size(struct http_sock *sock, size_t limit);\nstruct tcp_sock *http_sock_tcp(struct http_sock *sock);\nstruct tls *http_sock_tls(const struct http_sock *conn);\nconst struct sa *http_conn_peer(const struct http_conn *conn);\nstruct tcp_conn *http_conn_tcp(struct http_conn *conn);\nstruct tls_conn *http_conn_tls(struct http_conn *conn);\n\nvoid http_conn_reset_timeout(struct http_conn *conn);\nvoid http_conn_close(struct http_conn *conn);\nint  http_reply(struct http_conn *conn, uint16_t scode, const char *reason,\n\t\tconst char *fmt, ...);\nint  http_creply(struct http_conn *conn, uint16_t scode, const char *reason,\n\t\t const char *ctype, const char *fmt, ...);\nint  http_ereply(struct http_conn *conn, uint16_t scode, const char *reason);\n\n\n/* Authentication */\nstruct http_auth {\n\tconst char *realm;\n\tbool stale;\n};\n\ntypedef int (http_auth_h)(const struct pl *username, uint8_t *ha1, void *arg);\n\nint  http_auth_print_challenge(struct re_printf *pf,\n\t\t\t       const struct http_auth *auth);\nbool http_auth_check(const struct pl *hval, const struct pl *method,\n\t\t     struct http_auth *auth, http_auth_h *authh, void *arg);\nbool http_auth_check_request(const struct http_msg *msg,\n\t\t\t     struct http_auth *auth,\n\t\t\t     http_auth_h *authh, void *arg);\n\n/* http_reqconn - HTTP request connection */\nstruct http_reqconn;\nint http_reqconn_alloc(struct http_reqconn **pconn,\n\t\tstruct http_cli *client,\n\t\thttp_resp_h *resph, http_data_h *datah, void* arg);\nint http_reqconn_set_auth(struct http_reqconn *conn, const struct pl *user,\n\t\tconst struct pl *pass);\nint http_reqconn_set_bearer(struct http_reqconn *conn,\n\t\tconst struct pl *bearer);\nint http_reqconn_set_authtoken(struct http_reqconn *conn,\n\t\tconst struct pl *token);\nint http_reqconn_set_tokentype(struct http_reqconn *conn,\n\t\tconst struct pl *tokentype);\nint http_reqconn_set_method(struct http_reqconn *conn, const struct pl *met);\nint http_reqconn_set_body(struct http_reqconn *conn, struct mbuf *body);\nint http_reqconn_set_ctype(struct http_reqconn *conn, const struct pl *ctype);\nint http_reqconn_add_header(struct http_reqconn *conn,\n\t\tconst struct pl *header);\nint http_reqconn_clr_header(struct http_reqconn *conn);\nint http_reqconn_send(struct http_reqconn *conn, const struct pl *uri);\nint http_reqconn_set_peer(struct http_reqconn *conn,\n\t\t\t  const struct sa *peer);\n#ifdef USE_TLS\nint http_reqconn_set_tls_hostname(struct http_reqconn *conn,\n\t\tconst struct pl *hostname);\n#endif\n\nint http_reqconn_set_req_bodyh(struct http_reqconn *conn,\n\t\thttp_bodyh cb, uint64_t len);\n"
  },
  {
    "path": "include/re_httpauth.h",
    "content": "/**\n * @file re_httpauth.h  Interface to HTTP Authentication\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** HTTP digest request challenge */\nstruct httpauth_digest_chall_req {\n\tchar *realm;\n\tchar *domain;\n\tchar *nonce;\n\tchar *opaque;\n\tbool stale;\n\tchar *algorithm;\n\tchar *qop;\n\n\t/* optional */\n\tchar *charset;\n\tbool userhash;\n};\n\n/** HTTP Digest Challenge */\nstruct httpauth_digest_chall {\n\tstruct pl realm;\n\tstruct pl nonce;\n\n\t/* optional */\n\tstruct pl opaque;\n\tstruct pl stale;\n\tstruct pl algorithm;\n\tstruct pl qop;\n\tstruct pl domain;\n\tstruct pl charset;\n\tstruct pl userhash;\n};\n\nstruct httpauth_digest_enc_resp {\n\tchar *realm;\n\tchar *nonce;\n\tchar *opaque;\n\tchar *algorithm;\n\tchar *qop;\n\n\t/* response specific */\n\tchar *response;\n\tchar *username;\n\tchar *username_star;\n\tchar *uri;\n\tuint32_t cnonce;\n\tuint32_t nc;\n\n\t/* optional */\n\tchar *charset;\n\tbool userhash;\n\tvoid (*hashh)(const uint8_t *, size_t, uint8_t *);\n\tsize_t hash_length;\n};\n\n/** HTTP Digest response */\nstruct httpauth_digest_resp {\n\tstruct pl realm;\n\tstruct pl nonce;\n\tstruct pl response;\n\tstruct pl username;\n\tstruct pl uri;\n\n\t/* optional */\n\tstruct pl nc;\n\tstruct pl cnonce;\n\tstruct pl qop;\n\n\tstruct pl algorithm;\n\tstruct pl charset;\n\tstruct pl userhash;\n\tvoid (*hashh)(const uint8_t *, size_t, uint8_t *);\n\tsize_t hash_length;\n\n\tstruct mbuf *mb;\n};\n\n\n/** HTTP Basic */\nstruct httpauth_basic {\n\tstruct mbuf *mb;\n\tstruct pl realm;\n\tstruct pl auth;\n};\n\nstruct httpauth_basic_req {\n\tchar *realm;\n\n\t/* optional */\n\tchar *charset;\n};\n\n\nint httpauth_digest_challenge_decode(struct httpauth_digest_chall *chall,\n\t\t\t\t     const struct pl *hval);\nint httpauth_digest_response_decode(struct httpauth_digest_resp *resp,\n\t\t\t\t    const struct pl *hval);\nint httpauth_digest_response_auth(const struct httpauth_digest_resp *resp,\n\t\t\t\t  const struct pl *method, const uint8_t *ha1);\nint httpauth_digest_make_response(struct httpauth_digest_resp **resp,\n\t\tconst struct httpauth_digest_chall *chall,\n\t\tconst char *path, const char *method, const char *user,\n\t\tconst char *pwd, struct mbuf *body);\nint httpauth_digest_response_encode(const struct httpauth_digest_resp *resp,\n\t\t\t\t  struct mbuf *mb);\n\n\nint httpauth_digest_response_print(struct re_printf *pf,\n\tconst struct httpauth_digest_enc_resp *resp);\nint httpauth_digest_response_set_cnonce(struct httpauth_digest_enc_resp *resp,\n\tconst struct httpauth_digest_chall *chall, const struct pl *method,\n\tconst char *user, const char *passwd, const char *entitybody,\n\tuint32_t cnonce, uint32_t nonce_cnt);\nint httpauth_digest_response(struct httpauth_digest_enc_resp **presp,\n\tconst struct httpauth_digest_chall *chall, const struct pl *method,\n\tconst char *uri, const char *user, const char *passwd, const char *qop,\n\tconst char *entitybody);\nint httpauth_digest_response_full(struct httpauth_digest_enc_resp **presp,\n\tconst struct httpauth_digest_chall *chall, const struct pl *method,\n\tconst char *uri, const char *user, const char *passwd, const char *qop,\n\tconst char *entitybody, const char *charset, const bool userhash);\nint httpauth_digest_verify(struct httpauth_digest_chall_req *req,\n\tconst struct pl *hval, const struct pl *method, const char *etag,\n\tconst char *user, const char *passwd, const char *entitybody);\n\nint httpauth_digest_chall_req_print(struct re_printf *pf,\n\tconst struct httpauth_digest_chall_req *req);\nint httpauth_digest_chall_request(struct httpauth_digest_chall_req **preq,\n\tconst char *realm, const char *etag, const char *qop);\nint httpauth_digest_chall_request_full(struct httpauth_digest_chall_req **preq,\n\tconst char *real, const char *domain, const char *etag,\n\tconst char *opaque, const bool stale, const char *algo,\n\tconst char *qop, const char *charset, const bool userhash);\n\nstruct httpauth_basic *httpauth_basic_alloc(void);\nint httpauth_basic_decode(struct httpauth_basic *basic,\n\t\tconst struct pl *hval);\nint httpauth_basic_make_response(struct httpauth_basic *basic,\n\t\tconst char *user, const char *pwd);\nint httpauth_basic_encode(const struct httpauth_basic *basic, struct mbuf *mb);\n\n\nint httpauth_basic_request_print(struct re_printf *pf,\n\tconst struct httpauth_basic_req *req);\nint httpauth_basic_verify(const struct pl *hval, const char *user,\n\tconst char *passwd);\nint httpauth_basic_request(struct httpauth_basic_req **preq,\n\tconst char *realm, const char *charset);\n"
  },
  {
    "path": "include/re_ice.h",
    "content": "/**\n * @file re_ice.h  Interface to Interactive Connectivity Establishment (ICE)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** ICE Role */\nenum ice_role {\n\tICE_ROLE_UNKNOWN = 0,\n\tICE_ROLE_CONTROLLING,\n\tICE_ROLE_CONTROLLED\n};\n\n/** ICE Transport **/\nenum ice_transp {\n\tICE_TRANSP_NONE = -1,\n\tICE_TRANSP_UDP  = IPPROTO_UDP\n};\n\n/** ICE Component ID */\nenum ice_compid {\n\tICE_COMPID_RTP  = 1,\n\tICE_COMPID_RTCP = 2\n};\n\n/** ICE Candidate type */\nenum ice_cand_type {\n\tICE_CAND_TYPE_HOST,   /**< Host candidate             */\n\tICE_CAND_TYPE_SRFLX,  /**< Server Reflexive candidate */\n\tICE_CAND_TYPE_PRFLX,  /**< Peer Reflexive candidate   */\n\tICE_CAND_TYPE_RELAY   /**< Relayed candidate          */\n};\n\n/** ICE TCP protocol type */\nenum ice_tcptype {\n\tICE_TCP_ACTIVE,   /**< Active TCP client                   */\n\tICE_TCP_PASSIVE,  /**< Passive TCP server                  */\n\tICE_TCP_SO        /**< Simultaneous-open TCP client/server */\n};\n\n/** Candidate pair states */\nenum ice_candpair_state {\n\tICE_CANDPAIR_FROZEN = 0, /**< Frozen state (default)                 */\n\tICE_CANDPAIR_WAITING,    /**< Waiting to become highest on list      */\n\tICE_CANDPAIR_INPROGRESS, /**< In-Progress state;transac. in progress */\n\tICE_CANDPAIR_SUCCEEDED,  /**< Succeeded state; successful result     */\n\tICE_CANDPAIR_FAILED      /**< Failed state; check failed             */\n};\n\n/** ICE local candidate policy */\nenum ice_policy {\n\tICE_POLICY_ALL,\t  /**< Allow all local candidates */\n\tICE_POLICY_RELAY, /**< Use only RELAY candidates  */\n};\n\nstruct ice;\nstruct ice_cand;\nstruct icem;\nstruct turnc;\n\n/** ICE Configuration */\nstruct ice_conf {\n\tuint32_t rto;             /**< STUN Retransmission TimeOut */\n\tuint32_t rc;              /**< STUN Retransmission Count   */\n\tenum ice_policy policy;   /**< ICE Local Candidate Policy  */\n\tbool debug;               /**< Enable ICE debugging        */\n};\n\ntypedef void (ice_connchk_h)(int err, bool update, void *arg);\n\n\n/* ICE Media */\nint  icem_alloc(struct icem **icemp, enum ice_role role, int proto, int layer,\n\t\tuint64_t tiebrk, const char *lufrag, const char *lpwd,\n\t\tice_connchk_h *chkh, void *arg);\nstruct ice_conf *icem_conf(struct icem *icem);\nenum ice_role icem_local_role(const struct icem *icem);\nvoid icem_set_conf(struct icem *icem, const struct ice_conf *conf);\nvoid icem_set_role(struct icem *icem, enum ice_role role);\nvoid icem_set_name(struct icem *icem, const char *name);\nint  icem_comp_add(struct icem *icem, unsigned compid, void *sock);\n\nbool icem_verify_support(struct icem *icem, unsigned compid,\n\t\t\t const struct sa *raddr);\nint  icem_conncheck_start(struct icem *icem);\nvoid icem_conncheck_stop(struct icem *icem, int err);\nint  icem_add_chan(struct icem *icem, unsigned compid, const struct sa *raddr);\nbool icem_mismatch(const struct icem *icem);\nvoid icem_update(struct icem *icem);\nint  ice_sdp_decode(struct icem *ice, const char *name, const char *value);\nint  icem_sdp_decode(struct icem *icem, const char *name, const char *value);\nint  icem_debug(struct re_printf *pf, const struct icem *icem);\nstruct list *icem_lcandl(const struct icem *icem);\nstruct list *icem_rcandl(const struct icem *icem);\nstruct list *icem_checkl(const struct icem *icem);\nstruct list *icem_validl(const struct icem *icem);\nbool icem_rcand_ready(struct icem *icem);\nconst struct sa *icem_cand_default(struct icem *icem, unsigned compid);\nconst struct sa *icem_selected_laddr(const struct icem *icem, unsigned compid);\nconst struct ice_cand *icem_selected_lcand(const struct icem *icem,\n\t\t\t\tunsigned compid);\nconst struct ice_cand *icem_selected_rcand(const struct icem *icem,\n\t\t\t\tunsigned compid);\nvoid ice_candpair_set_states(struct icem *icem);\nvoid icem_cand_redund_elim(struct icem *icem);\nint  icem_comps_set_default_cand(struct icem *icem);\nstruct stun *icem_stun(struct icem *icem);\nint icem_set_turn_client(struct icem *icem, unsigned compid,\n\t\t\t struct turnc *turnc);\n\n\nbool ice_remotecands_avail(const struct icem *icem);\nint  ice_cand_encode(struct re_printf *pf, const struct ice_cand *cand);\nint  ice_remotecands_encode(struct re_printf *pf, const struct icem *icem);\nstruct ice_cand *icem_cand_find(const struct list *lst, unsigned compid,\n\t\t\t\tconst struct sa *addr);\nint icem_lcand_add_base(struct icem *icem, enum ice_cand_type type,\n\t\t\tunsigned compid, uint16_t lprio, const char *ifname,\n\t\t\tenum ice_transp transp, const struct sa *addr);\nint icem_lcand_add(struct icem *icem, struct ice_cand *base,\n\t\t   enum ice_cand_type type,\n\t\t   const struct sa *addr);\nstruct ice_cand *icem_lcand_base(struct ice_cand *lcand);\nconst struct sa *icem_lcand_addr(const struct ice_cand *cand);\nenum ice_cand_type icem_cand_type(const struct ice_cand *cand);\n\n\nextern const char ice_attr_cand[];\nextern const char ice_attr_lite[];\nextern const char ice_attr_mismatch[];\nextern const char ice_attr_pwd[];\nextern const char ice_attr_remote_cand[];\nextern const char ice_attr_ufrag[];\n\n\nconst char        *ice_cand_type2name(enum ice_cand_type type);\nenum ice_cand_type ice_cand_name2type(const char *name);\nconst char    *ice_role2name(enum ice_role role);\nconst char    *ice_candpair_state2name(enum ice_candpair_state st);\n\n\nuint32_t ice_cand_calc_prio(enum ice_cand_type type, uint16_t lpref,\n\t\t\t    unsigned compid);\n\n\n/** Defines an SDP candidate attribute */\nstruct ice_cand_attr {\n\tchar foundation[32];      /**< Foundation string                    */\n\tunsigned compid;          /**< Component ID (1-256)                 */\n\tint proto;                /**< Transport protocol                   */\n\tuint32_t prio;            /**< Priority of this candidate           */\n\tstruct sa addr;           /**< Transport address                    */\n\tenum ice_cand_type type;  /**< Candidate type                       */\n\tstruct sa rel_addr;       /**< Related transport address (optional) */\n\tenum ice_tcptype tcptype; /**< TCP candidate type (TCP-only)        */\n};\n\nint ice_cand_attr_encode(struct re_printf *pf,\n\t\t\t const struct ice_cand_attr *cand);\nint ice_cand_attr_decode(struct ice_cand_attr *cand, const char *val);\n"
  },
  {
    "path": "include/re_json.h",
    "content": "/**\n * @file re_json.h  Interface to JavaScript Object Notation (JSON) -- RFC 7159\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n\nenum json_typ {\n\tJSON_STRING,\n\tJSON_INT,\n\tJSON_DOUBLE,\n\tJSON_BOOL,\n\tJSON_NULL,\n};\n\nstruct json_value {\n\tunion {\n\t\tchar *str;\n\t\tint64_t integer;\n\t\tdouble dbl;\n\t\tbool boolean;\n\t} v;\n\tenum json_typ type;\n};\n\nstruct json_handlers;\n\ntypedef int (json_object_entry_h)(const char *name,\n\t\t\t\t  const struct json_value *value, void *arg);\ntypedef int (json_array_entry_h)(unsigned idx,\n\t\t\t\t const struct json_value *value, void *arg);\ntypedef int (json_object_h)(const char *name, unsigned idx,\n\t\t\t    struct json_handlers *h);\ntypedef int (json_array_h)(const char *name, unsigned idx,\n\t\t\t   struct json_handlers *h);\n\nstruct json_handlers {\n\tjson_object_h *oh;\n\tjson_array_h *ah;\n\tjson_object_entry_h *oeh;\n\tjson_array_entry_h *aeh;\n\tvoid *arg;\n};\n\nint json_decode(const char *str, size_t len, unsigned maxdepth,\n\t\tjson_object_h *oh, json_array_h *ah,\n\t\tjson_object_entry_h *oeh, json_array_entry_h *aeh, void *arg);\n\nint json_decode_odict(struct odict **op, uint32_t hash_size, const char *str,\n\t\t      size_t len, unsigned maxdepth);\nint json_encode_odict(struct re_printf *pf, const struct odict *o);\n"
  },
  {
    "path": "include/re_list.h",
    "content": "/**\n * @file re_list.h  Interface to Linked List\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** Linked-list element */\nstruct le {\n\tstruct le *prev;    /**< Previous element                    */\n\tstruct le *next;    /**< Next element                        */\n\tstruct list *list;  /**< Parent list (NULL if not linked-in) */\n\tvoid *data;         /**< User-data                           */\n};\n\n/** List Element Initializer */\n#define LE_INIT {NULL, NULL, NULL, NULL}\n\n\n/** Defines a linked list */\nstruct list {\n\tstruct le *head;  /**< First list element */\n\tstruct le *tail;  /**< Last list element  */\n\tsize_t cnt;       /**< Number of elements */\n};\n\n/** Linked list Initializer */\n#define LIST_INIT {NULL, NULL, 0}\n\n\n/**\n * Defines the list apply handler\n *\n * @param le  List element\n * @param arg Handler argument\n *\n * @return true to stop traversing, false to continue\n */\ntypedef bool (list_apply_h)(struct le *le, void *arg);\n\n/**\n * Defines the list sort handler\n *\n * @param le1  Current list element\n * @param le2  Next list element\n * @param arg  Handler argument\n *\n * @return true if sorted, otherwise false\n */\ntypedef bool (list_sort_h)(struct le *le1, struct le *le2, void *arg);\n\n\nvoid list_init(struct list *list);\nvoid list_flush(struct list *list);\nvoid list_clear(struct list *list);\nvoid list_append(struct list *list, struct le *le, void *data);\nvoid list_prepend(struct list *list, struct le *le, void *data);\nvoid list_insert_before(struct list *list, struct le *le, struct le *ile,\n\t\t\tvoid *data);\nvoid list_insert_after(struct list *list, struct le *le, struct le *ile,\n\t\t       void *data);\nvoid list_insert_sorted(struct list *list, list_sort_h *sh, void *arg,\n\t\t\tstruct le *ile, void *data);\nvoid list_unlink(struct le *le);\nvoid list_sort(struct list *list, list_sort_h *sh, void *arg);\nstruct le *list_apply(const struct list *list, bool fwd, list_apply_h *ah,\n\t\t      void *arg);\nstruct le *list_head(const struct list *list);\nstruct le *list_tail(const struct list *list);\nuint32_t list_count(const struct list *list);\n\n\n/**\n * Get the user-data from a list element\n *\n * @param le List element\n *\n * @return Pointer to user-data\n */\nstatic inline void *list_ledata(const struct le *le)\n{\n\treturn le ? le->data : NULL;\n}\n\n\nstatic inline bool list_contains(const struct list *list, const struct le *le)\n{\n\treturn le ? le->list == list : false;\n}\n\n\nstatic inline bool list_isempty(const struct list *list)\n{\n\treturn list ? list->head == NULL : true;\n}\n\n\n/**\n * @def LIST_FOREACH\n * @brief Iterates over each element in a list\n *\n * @param list The list to iterate\n * @param le   Iterator variable\n */\n#define LIST_FOREACH(list, le)\t\t\t\t\t\\\n\tfor ((le) = list_head((list)); (le); (le) = (le)->next)\n\n\n/**\n * @def LIST_FOREACH_SAFE\n * @brief Safe list iteration allowing element removal\n *\n * @param list   The list to iterate\n * @param le     Iterator variable\n * @param le_tmp Temporary variable for next element\n */\n#define LIST_FOREACH_SAFE(list, le, le_tmp)                                   \\\n\tfor ((le) = list_head((list)); (le) && ((le_tmp = le->next) || 1);    \\\n\t\t\t(le) = le_tmp)\n\n\n/**\n * Move element to another linked list\n *\n * @param le    List element to move\n * @param list  Destination list\n */\nstatic inline void list_move(struct le *le, struct list *list)\n{\n\tlist_unlink(le);\n\tlist_append(list, le, le->data);\n}\n"
  },
  {
    "path": "include/re_main.h",
    "content": "/**\n * @file re_main.h  Interface to main polling routine\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include \"re_async.h\"\n\nstruct re;\nstruct re_fhs;\n\nenum {\n#ifndef FD_READ\n\tFD_READ   = 1<<0,\n#endif\n#ifndef FD_WRITE\n\tFD_WRITE  = 1<<1,\n#endif\n\tFD_EXCEPT = 1<<2\n};\n\n\n/**\n * File descriptor event handler\n *\n * @param flags  Event flags\n * @param arg    Handler argument\n */\ntypedef void (fd_h)(int flags, void *arg);\n\n/**\n * Thread-safe signal handler\n *\n * @param sig Signal number\n */\ntypedef void (re_signal_h)(int sig);\n\n\nint   fd_listen(struct re_fhs **fhsp, re_sock_t fd, int flags, fd_h *fh,\n\tvoid *arg);\nstruct re_fhs *fd_close(struct re_fhs *fhs);\nint   fd_setsize(int maxfds);\n\nint   libre_init(void);\nvoid  libre_close(void);\nvoid  libre_exception_btrace(bool enable);\n\nint   re_main(re_signal_h *signalh);\nvoid  re_cancel(void);\nint   re_debug(struct re_printf *pf, void *unused);\nint   re_nfds(void);\n\nint  re_alloc(struct re **rep);\nint  re_thread_attach(struct re *re);\nvoid re_thread_detach(void);\n\nint  re_thread_init(void);\nvoid re_thread_close(void);\nvoid re_thread_enter(void);\nvoid re_thread_leave(void);\nint  re_thread_check(bool debug);\nint  re_thread_async_init(uint16_t workers);\nvoid re_thread_async_close(void);\nint  re_thread_async(re_async_work_h *work, re_async_h *cb, void *arg);\nint  re_thread_async_main(re_async_work_h *work, re_async_h *cb, void *arg);\nint  re_thread_async_id(intptr_t id, re_async_work_h *work, re_async_h *cb,\n\t\t       void *arg);\nint re_thread_async_main_id(intptr_t id, re_async_work_h *work, re_async_h *cb,\n\t\t\t    void *arg);\nvoid re_thread_async_cancel(intptr_t id);\nvoid re_thread_async_main_cancel(intptr_t id);\n\nvoid re_set_mutex(void *mutexp);\nvoid re_fhs_flush(void);\n\nstruct tmrl *re_tmrl_get(void);\n\n/** Polling methods */\nenum poll_method {\n\tMETHOD_NULL = 0,\n\tMETHOD_SELECT,\n\tMETHOD_EPOLL,\n\tMETHOD_KQUEUE,\n\t/* sep */\n\tMETHOD_MAX\n};\n\nint              poll_method_set(enum poll_method method);\nenum poll_method poll_method_get(void);\nenum poll_method poll_method_best(void);\nconst char      *poll_method_name(enum poll_method method);\nint poll_method_type(enum poll_method *method, const struct pl *name);\n"
  },
  {
    "path": "include/re_mbuf.h",
    "content": "/**\n * @file re_mbuf.h  Interface to memory buffers\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#include <stdarg.h>\n\n\n#ifndef RELEASE\n#define MBUF_DEBUG 1  /**< Mbuf debugging (0 or 1) */\n#endif\n\n#if MBUF_DEBUG\n/** Check that mbuf position does not exceed end */\n#define MBUF_CHECK_POS(mb)\t\t\t\t\t\t\\\n\tif ((mb) && (mb)->pos > (mb)->end) {\t\t\t\t\\\n\t\tRE_BREAKPOINT;\t\t\t\t\t\t\\\n\t}\n/** Check that mbuf end does not exceed size */\n#define MBUF_CHECK_END(mb)\t\t\t\t\t\t\\\n\tif ((mb) && (mb)->end > (mb)->size) {\t\t\t\t\\\n\t\tRE_BREAKPOINT;\t\t\t\t\t\t\\\n\t}\n#else\n#define MBUF_CHECK_POS(mb)\n#define MBUF_CHECK_END(mb)\n#endif\n\n/**\n * Defines a memory buffer.\n *\n * This is a dynamic and linear buffer for storing raw bytes.\n * It is designed for network protocols, and supports automatic\n * resizing of the buffer.\n *\n * - Writing to the buffer\n * - Reading from the buffer\n * - Automatic growing of buffer size\n * - Print function for formatting printing\n */\nstruct mbuf {\n\tuint8_t *buf;   /**< Buffer memory      */\n\tsize_t size;    /**< Size of buffer     */\n\tsize_t pos;     /**< Position in buffer */\n\tsize_t end;     /**< End of buffer      */\n};\n\n\nstruct pl;\nstruct re_printf;\n\nstruct mbuf *mbuf_alloc(size_t size);\nstruct mbuf *mbuf_dup(struct mbuf *mbd);\nstruct mbuf *mbuf_alloc_ref(struct mbuf *mbr);\nvoid     mbuf_init(struct mbuf *mb);\nvoid     mbuf_reset(struct mbuf *mb);\nint      mbuf_resize(struct mbuf *mb, size_t size);\nvoid     mbuf_trim(struct mbuf *mb);\nint      mbuf_shift(struct mbuf *mb, ssize_t shift);\nint      mbuf_write_mem(struct mbuf *mb, const uint8_t *buf, size_t size);\nint      mbuf_write_ptr(struct mbuf *mb, intptr_t v);\nint      mbuf_write_u8(struct mbuf *mb, uint8_t v);\nint      mbuf_write_u16(struct mbuf *mb, uint16_t v);\nint      mbuf_write_u32(struct mbuf *mb, uint32_t v);\nint      mbuf_write_u64(struct mbuf *mb, uint64_t v);\nint      mbuf_write_str(struct mbuf *mb, const char *str);\nint      mbuf_write_pl(struct mbuf *mb, const struct pl *pl);\nint      mbuf_read_mem(struct mbuf *mb, uint8_t *buf, size_t size);\nintptr_t mbuf_read_ptr(struct mbuf *mb);\nuint8_t  mbuf_read_u8(struct mbuf *mb);\nuint16_t mbuf_read_u16(struct mbuf *mb);\nuint32_t mbuf_read_u32(struct mbuf *mb);\nuint64_t mbuf_read_u64(struct mbuf *mb);\nint      mbuf_read_str(struct mbuf *mb, char *str, size_t size);\nint      mbuf_strdup(struct mbuf *mb, char **strp, size_t len);\nint      mbuf_vprintf(struct mbuf *mb, const char *fmt, va_list ap);\n\n#ifdef HAVE_RE_ARG\n#define mbuf_printf(mb, fmt, ...)                                             \\\n\t_mbuf_printf_s((mb), (fmt), RE_VA_ARGS(__VA_ARGS__))\n#else\n#define mbuf_printf _mbuf_printf\n#endif\n\nint      _mbuf_printf(struct mbuf *mb, const char *fmt, ...);\nint      _mbuf_printf_s(struct mbuf *mb, const char *fmt, ...);\n\nint      mbuf_write_pl_skip(struct mbuf *mb, const struct pl *pl,\n\t\t\t    const struct pl *skip);\nint      mbuf_fill(struct mbuf *mb, uint8_t c, size_t n);\nvoid     mbuf_set_posend(struct mbuf *mb, size_t pos, size_t end);\nint      mbuf_debug(struct re_printf *pf, const struct mbuf *mb);\n\n\n/**\n * Get the buffer from the current position\n *\n * @param mb Memory buffer\n *\n * @return Current buffer\n */\nstatic inline uint8_t *mbuf_buf(const struct mbuf *mb)\n{\n\treturn mb ? mb->buf + mb->pos : (uint8_t *)NULL;\n}\n\n\n/**\n * Get number of bytes left in a memory buffer, from current position to end\n *\n * @param mb Memory buffer\n *\n * @return Number of bytes left\n */\nstatic inline size_t mbuf_get_left(const struct mbuf *mb)\n{\n\treturn (mb && (mb->end > mb->pos)) ? (mb->end - mb->pos) : 0;\n}\n\n\n/**\n * Get available space in buffer (size - pos)\n *\n * @param mb Memory buffer\n *\n * @return Number of bytes available in buffer\n */\nstatic inline size_t mbuf_get_space(const struct mbuf *mb)\n{\n\treturn (mb && (mb->size > mb->pos)) ? (mb->size - mb->pos) : 0;\n}\n\n\n/**\n * Set absolute position\n *\n * @param mb  Memory buffer\n * @param pos Position\n */\nstatic inline void mbuf_set_pos(struct mbuf *mb, size_t pos)\n{\n\tif (!mb)\n\t\treturn;\n\n\tmb->pos = pos;\n\tMBUF_CHECK_POS(mb);\n}\n\n\n/**\n * Set absolute end\n *\n * @param mb  Memory buffer\n * @param end End position\n */\nstatic inline void mbuf_set_end(struct mbuf *mb, size_t end)\n{\n\tif (!mb)\n\t\treturn;\n\n\tmb->end = end;\n\tMBUF_CHECK_END(mb);\n}\n\n\n/**\n * Advance position +/- N bytes\n *\n * @param mb  Memory buffer\n * @param n   Number of bytes to advance\n */\nstatic inline void mbuf_advance(struct mbuf *mb, ssize_t n)\n{\n\tif (!mb)\n\t\treturn;\n\n\tmb->pos += n;\n\tMBUF_CHECK_POS(mb);\n}\n\n\n/**\n * Rewind position and end to the beginning of buffer\n *\n * @param mb  Memory buffer\n */\nstatic inline void mbuf_rewind(struct mbuf *mb)\n{\n\tif (!mb)\n\t\treturn;\n\n\tmb->pos = mb->end = 0;\n}\n\n\n/**\n * Set position to the end of the buffer\n *\n * @param mb  Memory buffer\n */\nstatic inline void mbuf_skip_to_end(struct mbuf *mb)\n{\n\tif (!mb)\n\t\treturn;\n\n\tmb->pos = mb->end;\n}\n\n\n/**\n * Get the current MBUF position\n *\n * @param mb Memory buffer\n *\n * @return Current position\n */\nstatic inline size_t mbuf_pos(const struct mbuf *mb)\n{\n\treturn mb ? mb->pos : 0;\n}\n\n\n/**\n * Get the current MBUF end position\n *\n * @param mb Memory buffer\n *\n * @return Current end position\n */\nstatic inline size_t mbuf_end(const struct mbuf *mb)\n{\n\treturn mb ? mb->end : 0;\n}\n"
  },
  {
    "path": "include/re_md5.h",
    "content": "/**\n * @file re_md5.h  Interface to MD5 functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** MD5 values */\nenum {\n\tMD5_SIZE     = 16,             /**< Number of bytes in MD5 hash   */\n\tMD5_STR_SIZE = 2*MD5_SIZE + 1  /**< Number of bytes in MD5 string */\n};\n\nvoid md5(const uint8_t *d, size_t n, uint8_t *md);\nint  md5_printf(uint8_t *md, const char *fmt, ...);\n"
  },
  {
    "path": "include/re_mem.h",
    "content": "/**\n * @file re_mem.h  Interface to Memory management with reference counting\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/**\n * Defines the memory destructor handler, which is called when the reference\n * of a memory object goes down to zero\n *\n * @param data Pointer to memory object\n */\ntypedef void (mem_destroy_h)(void *data);\n\n/** Memory Statistics */\nstruct memstat {\n\tsize_t bytes_cur;    /**< Current bytes allocated      */\n\tsize_t blocks_cur;   /**< Current blocks allocated     */\n};\n\nvoid    *mem_alloc(size_t size, mem_destroy_h *dh);\nvoid    *mem_zalloc(size_t size, mem_destroy_h *dh);\nvoid    *mem_realloc(void *data, size_t size);\nvoid    *mem_reallocarray(void *ptr, size_t nmemb,\n\t\t\t  size_t membsize, mem_destroy_h *dh);\nvoid     mem_destructor(void *data, mem_destroy_h *dh);\nvoid    *mem_ref(void *data);\nvoid    *mem_deref(void *data);\nuint32_t mem_nrefs(const void *data);\n\nvoid     mem_debug(void);\nvoid     mem_debug_tail(uint32_t last_n);\nvoid     mem_threshold_set(ssize_t n);\nstruct re_printf;\nint      mem_status(struct re_printf *pf, void *unused);\nint      mem_get_stat(struct memstat *mstat);\n\n\n/* Secure memory functions */\nint mem_seccmp(const uint8_t *s1, const uint8_t *s2, size_t n);\nvoid mem_secclean(void *data, size_t size);\n\n\n/* Mem Pool */\nstruct mem_pool;\nstruct mem_pool_entry;\nint mem_pool_alloc(struct mem_pool **poolp, size_t nmemb, size_t membsize,\n\t\t   mem_destroy_h *dh);\nint mem_pool_extend(struct mem_pool *pool, size_t num);\nstruct mem_pool_entry *mem_pool_borrow(struct mem_pool *pool);\nstruct mem_pool_entry *mem_pool_borrow_extend(struct mem_pool *pool);\nvoid *mem_pool_release(struct mem_pool *pool, struct mem_pool_entry *e);\nvoid *mem_pool_member(const struct mem_pool_entry *entry);\nvoid mem_pool_flush(struct mem_pool *pool);\n"
  },
  {
    "path": "include/re_mod.h",
    "content": "/**\n * @file re_mod.h  Interface to loadable modules\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/**\n * @def MOD_EXT\n *\n * Module Extension\n */\n#if defined (WIN32)\n#define MOD_EXT \".dll\"\n#else\n#define MOD_EXT \".so\"\n#endif\n\n\n/** Symbol to enable exporting of functions from a module */\n#ifdef WIN32\n#define EXPORT_SYM __declspec(dllexport)\n#else\n#define EXPORT_SYM\n#endif\n\n\n/* ----- Module API ----- */\n\n\n/**\n * Defines the module initialisation handler\n *\n * @return 0 for success, otherwise errorcode\n */\ntypedef int (mod_init_h)(void);\n\n/**\n * Defines the module close handler\n *\n * @return 0 for success, otherwise errorcode\n */\ntypedef int (mod_close_h)(void);\n\n\nstruct mod;\nstruct re_printf;\n\n\n/** Defines the module export */\nstruct mod_export {\n\tconst char *name;    /**< Module name             */\n\tconst char *type;    /**< Module type             */\n\tmod_init_h *init;    /**< Module init handler     */\n\tmod_close_h *close;  /**< Module close handler    */\n};\n\n\n/* ----- Application API ----- */\n\nvoid        mod_init(void);\nvoid        mod_close(void);\n\nint         mod_load(struct mod **mp, const char *name);\nint         mod_add(struct mod **mp, const struct mod_export *me);\nstruct mod *mod_find(const char *name);\nconst struct mod_export *mod_export(const struct mod *m);\nstruct list *mod_list(void);\nint         mod_debug(struct re_printf *pf, void *unused);\n"
  },
  {
    "path": "include/re_mqueue.h",
    "content": "/**\n * @file re_mqueue.h Thread Safe Message Queue\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\nstruct mqueue;\n\ntypedef void (mqueue_h)(int id, void *data, void *arg);\n\nint mqueue_alloc(struct mqueue **mqp, mqueue_h *h, void *arg);\nint mqueue_push(struct mqueue *mq, int id, void *data);\n"
  },
  {
    "path": "include/re_msg.h",
    "content": "/**\n * @file re_msg.h  Interface to generic message components\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** Content-Type */\nstruct msg_ctype {\n\tstruct pl type;\n\tstruct pl subtype;\n\tstruct pl params;\n};\n\n\nint  msg_ctype_decode(struct msg_ctype *ctype, const struct pl *pl);\nbool msg_ctype_cmp(const struct msg_ctype *ctype,\n\t\t   const char *type, const char *subtype);\n\nint msg_param_decode(const struct pl *pl, const char *name, struct pl *val);\nint msg_param_exists(const struct pl *pl, const char *name, struct pl *end);\n"
  },
  {
    "path": "include/re_net.h",
    "content": "/**\n * @file re_net.h  Interface to Networking module.\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#if defined(WIN32)\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#else\n#include <sys/types.h>\n#ifndef _BSD_SOCKLEN_T_\n#define _BSD_SOCKLEN_T_ int  /**< Defines the BSD socket length type */\n#endif\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#endif\n\n/** Length of IPv4 address string */\n#ifndef INET_ADDRSTRLEN\n#define INET_ADDRSTRLEN 16\n#endif\n\n/** Length of IPv6 address string */\n#ifndef INET6_ADDRSTRLEN\n#define INET6_ADDRSTRLEN 46\n#endif\n\n\n/* forward declarations */\nstruct sa;\n\n\n/* Net generic */\nint  net_dst_source_addr_get(const struct sa *dst, struct sa *ip);\nint  net_default_source_addr_get(int af, struct sa *ip);\nint  net_default_gateway_get(int af, struct sa *gw);\n\n\n/* Net sockets */\nint  net_sock_init(void);\nvoid net_sock_close(void);\n\n\n/* Net socket options */\nint net_sockopt_blocking_set(re_sock_t fd, bool blocking);\nint net_sockopt_reuse_set(re_sock_t fd, bool reuse);\nint net_sockopt_v6only(re_sock_t fd, bool only);\n\n\n/* Net interface (if.c) */\n\n/**\n * Defines the interface address handler - called once per interface\n *\n * @param ifname Name of the interface\n * @param sa     IP address of the interface\n * @param arg    Handler argument\n *\n * @return true to stop traversing, false to continue\n */\ntypedef bool (net_ifaddr_h)(const char *ifname, const struct sa *sa,\n\t\t\t    void *arg);\n\nint net_if_getname(char *ifname, size_t sz, int af, const struct sa *ip);\nint net_if_getaddr(const char *ifname, int af, struct sa *ip);\nint net_if_list(net_ifaddr_h *ifh, void *arg);\nint net_if_apply(net_ifaddr_h *ifh, void *arg);\nint net_if_debug(struct re_printf *pf, void *unused);\nint net_if_getlinklocal(const char *ifname, int af, struct sa *ip);\n\n\n/* Net interface (ifaddrs.c) */\nint net_getifaddrs(net_ifaddr_h *ifh, void *arg);\nint net_netlink_addrs(net_ifaddr_h *ifh, void *arg);\n\n\n/* Net route */\n\n/**\n * Defines the routing table handler - called once per route entry\n *\n * @param ifname Interface name\n * @param dst    Destination IP address/network\n * @param dstlen Prefix length of destination\n * @param gw     Gateway IP address\n * @param arg    Handler argument\n *\n * @return true to stop traversing, false to continue\n */\ntypedef bool (net_rt_h)(const char *ifname, const struct sa *dst,\n\t\t\tint dstlen, const struct sa *gw, void *arg);\n\nint net_rt_list(net_rt_h *rth, void *arg);\nint net_rt_default_get(int af, char *ifname, size_t size);\nint net_rt_debug(struct re_printf *pf, void *unused);\n\n\n/* Net strings */\nconst char *net_proto2name(int proto);\nconst char *net_af2name(int af);\n\n"
  },
  {
    "path": "include/re_odict.h",
    "content": "/**\n * @file re_odict.h  Interface to Ordered Dictionary\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n\nenum odict_type {\n\tODICT_ERR = -1,\n\tODICT_OBJECT,\n\tODICT_ARRAY,\n\tODICT_STRING,\n\tODICT_INT,\n\tODICT_DOUBLE,\n\tODICT_BOOL,\n\tODICT_NULL\n};\n\nstruct odict {\n\tstruct list lst;\n\tstruct hash *ht;\n};\n\nstruct odict_entry;\n\nint odict_alloc(struct odict **op, uint32_t hash_size);\nconst struct odict_entry *odict_lookup(const struct odict *o, const char *key);\nsize_t odict_count(const struct odict *o, bool nested);\nint odict_debug(struct re_printf *pf, const struct odict *o);\n\nint odict_entry_add(struct odict *o, const char *key,\n\t\t    int type, ...);\nint odict_pl_add(struct odict *od, const char *key,\n\t\t const struct pl *val);\nvoid odict_entry_del(struct odict *o, const char *key);\nint odict_entry_debug(struct re_printf *pf, const struct odict_entry *e);\nbool odict_compare(const struct odict *dict1, const struct odict *dict2,\n\tbool ignore_order);\n\nbool odict_type_iscontainer(enum odict_type type);\nbool odict_type_isreal(enum odict_type type);\nconst char *odict_type_name(enum odict_type type);\n\n\n/* Odict Helpers */\n\nconst struct odict_entry *odict_get_type(const struct odict *o,\n\t\t\t\t\tenum odict_type type, const char *key);\nconst char *odict_string(const struct odict *o, const char *key);\nbool odict_get_number(const struct odict *o, uint64_t *num, const char *key);\nbool odict_get_boolean(const struct odict *o, bool *value, const char *key);\nstruct odict *odict_get_object(const struct odict *o, const char *key);\nstruct odict *odict_get_array(const struct odict *o, const char *key);\n\n\n/* Entry Helpers */\n\nenum odict_type odict_entry_type(const struct odict_entry *e);\nconst char *odict_entry_key(const struct odict_entry *e);\nstruct odict *odict_entry_object(const struct odict_entry *e);\nstruct odict *odict_entry_array(const struct odict_entry *e);\nchar *odict_entry_str(const struct odict_entry *e);\nint64_t odict_entry_int(const struct odict_entry *e);\ndouble odict_entry_dbl(const struct odict_entry *e);\nbool odict_entry_boolean(const struct odict_entry *e);\nbool odict_value_compare(const struct odict_entry *e1,\n\tconst struct odict_entry *e2, bool ignore_order);\n"
  },
  {
    "path": "include/re_pcp.h",
    "content": "/**\n * @file re_pcp.h  PCP - Port Control Protocol (RFC 6887)\n *\n * Copyright (C) 2010 - 2014 Alfred E. Heggestad\n */\n\n\n/*\n * The following specifications are implemented:\n *\n *   RFC 6887\n *   draft-ietf-pcp-description-option-02\n *   draft-cheshire-pcp-unsupp-family\n *\n */\n\n\n/** PCP Version numbers */\nenum {\n\tPCP_VERSION = 2,\n};\n\n/* PCP port numbers */\nenum {\n\tPCP_PORT_CLI   = 5350,  /* for ANNOUNCE notifications */\n\tPCP_PORT_SRV   = 5351,\n};\n\n/** PCP Protocol sizes */\nenum {\n\tPCP_HDR_SZ   = 24,\n\tPCP_NONCE_SZ = 12,\n\tPCP_MAP_SZ   = 36,\n\tPCP_PEER_SZ  = 56,\n\n\tPCP_MIN_PACKET =   24,\n\tPCP_MAX_PACKET = 1100\n};\n\nenum pcp_opcode {\n\tPCP_ANNOUNCE = 0,\n\tPCP_MAP      = 1,\n\tPCP_PEER     = 2,\n};\n\nenum pcp_result {\n\tPCP_SUCCESS                 =  0,\n\tPCP_UNSUPP_VERSION          =  1,\n\tPCP_NOT_AUTHORIZED          =  2,\n\tPCP_MALFORMED_REQUEST       =  3,\n\tPCP_UNSUPP_OPCODE           =  4,\n\tPCP_UNSUPP_OPTION           =  5,\n\tPCP_MALFORMED_OPTION        =  6,\n\tPCP_NETWORK_FAILURE         =  7,\n\tPCP_NO_RESOURCES            =  8,\n\tPCP_UNSUPP_PROTOCOL         =  9,\n\tPCP_USER_EX_QUOTA           = 10,\n\tPCP_CANNOT_PROVIDE_EXTERNAL = 11,\n\tPCP_ADDRESS_MISMATCH        = 12,\n\tPCP_EXCESSIVE_REMOTE_PEERS  = 13,\n};\n\nenum pcp_option_code {\n\tPCP_OPTION_THIRD_PARTY    =   1,\n\tPCP_OPTION_PREFER_FAILURE =   2,\n\tPCP_OPTION_FILTER         =   3,\n\tPCP_OPTION_DESCRIPTION    = 128,  /* RFC 7220 */\n};\n\n/* forward declarations */\nstruct udp_sock;\n\n/** Defines a PCP option */\nstruct pcp_option {\n\tstruct le le;\n\tenum pcp_option_code code;\n\tunion {\n\t\tstruct sa third_party;          /* Internal IP-address */\n\t\tstruct pcp_option_filter {\n\t\t\tuint8_t prefix_length;\n\t\t\tstruct sa remote_peer;\n\t\t} filter;\n\t\tchar *description;\n\t} u;\n};\n\n/**\n * Defines a complete and decoded PCP request/response.\n *\n * A PCP message consist of a header, and optional payload and options:\n *\n *     [      Header    ]\n *     ( Opcode Payload )\n *     (   PCP Options  )\n *\n */\nstruct pcp_msg {\n\n\t/** PCP Common Header */\n\tstruct pcp_hdr {\n\t\tuint8_t version;        /**< PCP Protocol version 2        */\n\t\tunsigned resp:1;        /**< R-bit; 0=Request, 1=Response  */\n\t\tuint8_t opcode;         /**< A 7-bit opcode                */\n\t\tuint32_t lifetime;      /**< Lifetime in [seconds]         */\n\n\t\t/* request: */\n\t\tstruct sa cli_addr;     /**< Client's IP Address (SA_ADDR) */\n\n\t\t/* response: */\n\t\tenum pcp_result result; /**< Result code for this response */\n\t\tuint32_t epoch;         /**< Server's Epoch Time [seconds] */\n\t} hdr;\n\n\t/** PCP Opcode-specific payload */\n\tunion pcp_payload {\n\t\tstruct pcp_map {\n\t\t\tuint8_t nonce[PCP_NONCE_SZ]; /**< Mapping Nonce    */\n\t\t\tuint8_t proto;               /**< IANA protocol    */\n\t\t\tuint16_t int_port;           /**< Internal Port    */\n\t\t\tstruct sa ext_addr;          /**< External Address */\n\t\t} map;\n\t\tstruct pcp_peer {\n\t\t\tstruct pcp_map map;          /**< Common with MAP  */\n\t\t\tstruct sa remote_addr;       /**< Remote address   */\n\t\t} peer;\n\t} pld;\n\n\t/** List of PCP Options (struct pcp_option) */\n\tstruct list optionl;\n};\n\n/** PCP request configuration */\nstruct pcp_conf {\n\tuint32_t irt;  /**< Initial retransmission time [seconds]     */\n\tuint32_t mrc;  /**< Maximum retransmission count              */\n\tuint32_t mrt;  /**< Maximum retransmission time [seconds]     */\n\tuint32_t mrd;  /**< Maximum retransmission duration [seconds] */\n};\n\n\n/* request */\n\nstruct pcp_request;\n\ntypedef void (pcp_resp_h)(int err, struct pcp_msg *msg, void *arg);\n\nint pcp_request(struct pcp_request **reqp, const struct pcp_conf *conf,\n\t\tconst struct sa *pcp_server, enum pcp_opcode opcode,\n\t\tuint32_t lifetime, const void *payload,\n\t\tpcp_resp_h *resph, void *arg, uint32_t optionc, ...);\nvoid pcp_force_refresh(struct pcp_request *req);\n\n\n/* reply */\n\nint pcp_reply(struct udp_sock *us, const struct sa *dst, struct mbuf *req,\n\t      enum pcp_opcode opcode, enum pcp_result result,\n\t      uint32_t lifetime, uint32_t epoch_time, const void *payload);\n\n\n/* msg */\n\ntypedef bool (pcp_option_h)(const struct pcp_option *opt, void *arg);\n\nint pcp_msg_decode(struct pcp_msg **msgp, struct mbuf *mb);\nint pcp_msg_printhdr(struct re_printf *pf, const struct pcp_msg *msg);\nint pcp_msg_print(struct re_printf *pf, const struct pcp_msg *msg);\nstruct pcp_option *pcp_msg_option(const struct pcp_msg *msg,\n\t\t\t\t  enum pcp_option_code code);\nstruct pcp_option *pcp_msg_option_apply(const struct pcp_msg *msg,\n\t\t\t\t\tpcp_option_h *h, void *arg);\nconst void *pcp_msg_payload(const struct pcp_msg *msg);\n\n\n/* option */\n\nint pcp_option_encode(struct mbuf *mb, enum pcp_option_code code,\n\t\t      const void *v);\nint pcp_option_decode(struct pcp_option **optp, struct mbuf *mb);\nint pcp_option_print(struct re_printf *pf, const struct pcp_option *opt);\n\n\n/* encode */\n\nint pcp_msg_req_vencode(struct mbuf *mb, enum pcp_opcode opcode,\n\t\t\tuint32_t lifetime, const struct sa *cli_addr,\n\t\t\tconst void *payload, uint32_t optionc, va_list ap);\nint pcp_msg_req_encode(struct mbuf *mb, enum pcp_opcode opcode,\n\t\t       uint32_t lifetime, const struct sa *cli_addr,\n\t\t       const void *payload, uint32_t optionc, ...);\n\n\n/* pcp */\n\nint pcp_ipaddr_encode(struct mbuf *mb, const struct sa *sa);\nint pcp_ipaddr_decode(struct mbuf *mb, struct sa *sa);\nconst char *pcp_result_name(enum pcp_result result);\nconst char *pcp_opcode_name(enum pcp_opcode opcode);\nconst char *pcp_proto_name(int proto);\n"
  },
  {
    "path": "include/re_rtmp.h",
    "content": "/**\n * @file re_rtmp.h  Interface to Real Time Messaging Protocol (RTMP)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** RTMP Protocol values */\nenum {\n\tRTMP_PORT = 1935,\n};\n\n/** RTMP Stream IDs */\nenum {\n\n\t/* User Control messages SHOULD use message stream ID 0\n\t   (known as the control stream) */\n\tRTMP_CONTROL_STREAM_ID = 0\n};\n\n/** RTMP Packet types */\nenum rtmp_packet_type {\n\tRTMP_TYPE_SET_CHUNK_SIZE     = 1,  /**< Set Chunk Size               */\n\tRTMP_TYPE_ACKNOWLEDGEMENT    = 3,  /**< Acknowledgement              */\n\tRTMP_TYPE_USER_CONTROL_MSG   = 4,  /**< User Control Messages        */\n\tRTMP_TYPE_WINDOW_ACK_SIZE    = 5,  /**< Window Acknowledgement Size  */\n\tRTMP_TYPE_SET_PEER_BANDWIDTH = 6,  /**< Set Peer Bandwidth           */\n\tRTMP_TYPE_AUDIO              = 8,  /**< Audio Message                */\n\tRTMP_TYPE_VIDEO              = 9,  /**< Video Message                */\n\tRTMP_TYPE_DATA               = 18, /**< Data Message                 */\n\tRTMP_TYPE_AMF0               = 20, /**< Action Message Format (AMF)  */\n};\n\n/** RTMP AMF types */\nenum rtmp_amf_type {\n\tRTMP_AMF_TYPE_ROOT         = -1,    /**< Special internal type      */\n\tRTMP_AMF_TYPE_NUMBER       = 0x00,  /**< Number Type                */\n\tRTMP_AMF_TYPE_BOOLEAN      = 0x01,  /**< Boolean Type               */\n\tRTMP_AMF_TYPE_STRING       = 0x02,  /**< String Type                */\n\tRTMP_AMF_TYPE_OBJECT       = 0x03,  /**< Object Type                */\n\tRTMP_AMF_TYPE_NULL         = 0x05,  /**< Null type                  */\n\tRTMP_AMF_TYPE_ECMA_ARRAY   = 0x08,  /**< ECMA 'associative' Array   */\n\tRTMP_AMF_TYPE_OBJECT_END   = 0x09,  /**< Object End Type            */\n\tRTMP_AMF_TYPE_STRICT_ARRAY = 0x0a,  /**< Array with ordinal indices */\n};\n\n/** RTMP Event types */\nenum rtmp_event_type {\n\tRTMP_EVENT_STREAM_BEGIN       = 0,  /**< Stream begin               */\n\tRTMP_EVENT_STREAM_EOF         = 1,  /**< Stream End-Of-File         */\n\tRTMP_EVENT_STREAM_DRY         = 2,  /**< No more data on the stream */\n\tRTMP_EVENT_SET_BUFFER_LENGTH  = 3,  /**< Set buffer size in [ms]    */\n\tRTMP_EVENT_STREAM_IS_RECORDED = 4,  /**< Stream is recorded         */\n\tRTMP_EVENT_PING_REQUEST       = 6,  /**< Ping Request from server   */\n\tRTMP_EVENT_PING_RESPONSE      = 7,  /**< Ping Response to server    */\n};\n\n\n/* forward declarations */\nstruct tls;\nstruct dnsc;\nstruct odict;\nstruct tcp_sock;\n\n\n/*\n * RTMP High-level API (connection, stream)\n */\n\n\n/* conn */\nstruct rtmp_conn;\n\ntypedef void (rtmp_estab_h)(void *arg);\ntypedef void (rtmp_command_h)(const struct odict *msg, void *arg);\ntypedef void (rtmp_close_h)(int err, void *arg);\n\nint rtmp_connect(struct rtmp_conn **connp, struct dnsc *dnsc, const char *uri,\n\t\t struct tls *tls,\n\t\t rtmp_estab_h *estabh, rtmp_command_h *cmdh,\n\t\t rtmp_close_h *closeh, void *arg);\nint rtmp_accept(struct rtmp_conn **connp, struct tcp_sock *ts,\n\t\tstruct tls *tls,\n\t\trtmp_command_h *cmdh, rtmp_close_h *closeh, void *arg);\nint rtmp_control(const struct rtmp_conn *conn,\n\t\t enum rtmp_packet_type type, ...);\nvoid rtmp_set_handlers(struct rtmp_conn *conn, rtmp_command_h *cmdh,\n\t\t       rtmp_close_h *closeh, void *arg);\nstruct tcp_conn *rtmp_conn_tcpconn(const struct rtmp_conn *conn);\nconst char *rtmp_conn_stream(const struct rtmp_conn *conn);\nint  rtmp_conn_debug(struct re_printf *pf, const struct rtmp_conn *conn);\n\n\ntypedef void (rtmp_resp_h)(bool success, const struct odict *msg,\n\t\t\t   void *arg);\n\n/* amf */\nint rtmp_amf_command(const struct rtmp_conn *conn, uint32_t stream_id,\n\t\t     const char *command,\n\t\t     unsigned body_propc, ...);\nint rtmp_amf_request(struct rtmp_conn *conn, uint32_t stream_id,\n\t\t     const char *command,\n\t\t     rtmp_resp_h *resph, void *arg, unsigned body_propc, ...);\nint rtmp_amf_reply(struct rtmp_conn *conn, uint32_t stream_id, bool success,\n\t\t   const struct odict *req,\n\t\t   unsigned body_propc, ...);\nint rtmp_amf_data(const struct rtmp_conn *conn, uint32_t stream_id,\n\t\t  const char *command, unsigned body_propc, ...);\n\n\n/* stream */\nstruct rtmp_stream;\n\ntypedef void (rtmp_control_h)(enum rtmp_event_type event, struct mbuf *mb,\n\t\t\t      void *arg);\ntypedef void (rtmp_audio_h)(uint32_t timestamp,\n\t\t\t    const uint8_t *pld, size_t len, void *arg);\ntypedef void (rtmp_video_h)(uint32_t timestamp,\n\t\t\t    const uint8_t *pld, size_t len, void *arg);\n\nint rtmp_stream_alloc(struct rtmp_stream **strmp, struct rtmp_conn *conn,\n\t\t      uint32_t stream_id, rtmp_command_h *cmdh,\n\t\t      rtmp_control_h *ctrlh, rtmp_audio_h *auh,\n\t\t      rtmp_video_h *vidh, rtmp_command_h *datah,\n\t\t      void *arg);\nint rtmp_stream_create(struct rtmp_stream **strmp, struct rtmp_conn *conn,\n\t\t       rtmp_resp_h *resph, rtmp_command_h *cmdh,\n\t\t       rtmp_control_h *ctrlh, rtmp_audio_h *auh,\n\t\t       rtmp_video_h *vidh, rtmp_command_h *datah,\n\t\t       void *arg);\nint rtmp_play(struct rtmp_stream *strm, const char *name);\nint rtmp_publish(struct rtmp_stream *strm, const char *name);\nint rtmp_meta(struct rtmp_stream *strm);\nint rtmp_send_audio(struct rtmp_stream *strm, uint32_t timestamp,\n\t\t    const uint8_t *pld, size_t len);\nint rtmp_send_video(struct rtmp_stream *strm, uint32_t timestamp,\n\t\t    const uint8_t *pld, size_t len);\nstruct rtmp_stream *rtmp_stream_find(const struct rtmp_conn *conn,\n\t\t\t\t     uint32_t stream_id);\n\n\nconst char *rtmp_event_name(enum rtmp_event_type event);\n"
  },
  {
    "path": "include/re_rtp.h",
    "content": "/**\n * @file re_rtp.h  Interface to Real-time Transport Protocol and RTCP\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** RTP protocol values */\nenum {\n\tRTP_VERSION     =  2,  /**< Defines the RTP version we support */\n\tRTCP_VERSION    =  2,  /**< Supported RTCP Version             */\n\tRTP_HEADER_SIZE = 12   /**< Number of bytes in RTP Header      */\n};\n\n\n/** Defines the RTP header */\nstruct rtp_header {\n\tuint8_t  ver;       /**< RTP version number     */\n\tbool     pad;       /**< Padding bit            */\n\tbool     ext;       /**< Extension bit          */\n\tuint8_t  cc;        /**< CSRC count             */\n\tbool     m;         /**< Marker bit             */\n\tuint8_t  pt;        /**< Payload type           */\n\tuint16_t seq;       /**< Sequence number        */\n\tuint32_t ts;        /**< Timestamp              */\n\tuint64_t ts_arrive; /**< Arrival Timestamp      */\n\tuint32_t ssrc;      /**< Synchronization source */\n\tuint32_t csrc[16];  /**< Contributing sources   */\n\tstruct {\n\t\tuint16_t type;  /**< Defined by profile     */\n\t\tuint16_t len;   /**< Number of 32-bit words */\n\t} x;\n};\n\n/** RTCP Packet Types */\nenum rtcp_type {\n\tRTCP_FIR   = 192,  /**< Full INTRA-frame Request (RFC 2032)    */\n\tRTCP_NACK  = 193,  /**< Negative Acknowledgement (RFC 2032)    */\n\tRTCP_SR    = 200,  /**< Sender Report                          */\n\tRTCP_RR    = 201,  /**< Receiver Report                        */\n\tRTCP_SDES  = 202,  /**< Source Description                     */\n\tRTCP_BYE   = 203,  /**< Goodbye                                */\n\tRTCP_APP   = 204,  /**< Application-defined                    */\n\tRTCP_RTPFB = 205,  /**< Transport layer FB message (RFC 4585)  */\n\tRTCP_PSFB  = 206,  /**< Payload-specific FB message (RFC 4585) */\n\tRTCP_XR    = 207,  /**< Extended Report (RFC 3611)             */\n\tRTCP_AVB   = 208,  /**< AVB RTCP Packet (IEEE1733)             */\n};\n\n/** SDES Types */\nenum rtcp_sdes_type {\n\tRTCP_SDES_END   = 0,  /**< End of SDES list               */\n\tRTCP_SDES_CNAME = 1,  /**< Canonical name                 */\n\tRTCP_SDES_NAME  = 2,  /**< User name                      */\n\tRTCP_SDES_EMAIL = 3,  /**< User's electronic mail address */\n\tRTCP_SDES_PHONE = 4,  /**< User's phone number            */\n\tRTCP_SDES_LOC   = 5,  /**< Geographic user location       */\n\tRTCP_SDES_TOOL  = 6,  /**< Name of application or tool    */\n\tRTCP_SDES_NOTE  = 7,  /**< Notice about the source        */\n\tRTCP_SDES_PRIV  = 8   /**< Private extension              */\n};\n\n/** Transport Layer Feedback Messages */\nenum rtcp_rtpfb {\n\tRTCP_RTPFB_GNACK = 1,  /**< Generic NACK */\n\tRTCP_RTPFB_TWCC  = 15  /**< transport-wide-cc-extensions-01 */\n};\n\n/** Payload-Specific Feedback Messages */\nenum rtcp_psfb {\n\tRTCP_PSFB_PLI  = 1,   /**< Picture Loss Indication (PLI) */\n\tRTCP_PSFB_SLI  = 2,   /**< Slice Loss Indication (SLI)   */\n\tRTCP_PSFB_FIR  = 4,   /**< Full INTRA-frame Request (FIR) (RFC 5104) */\n\tRTCP_PSFB_AFB  = 15,  /**< Application layer Feedback Messages */\n};\n\n/** Extended Report Block */\nenum rtcp_xr {\n\tRTCP_XR_LRRB  = 1,  /**< Loss RLE Report Block */\n\tRTCP_XR_DULRR = 2,  /**< Duplicate RLE Report Block   */\n\tRTCP_XR_PRTR  = 3,  /**< Packet Receipt Times Report Block */\n\tRTCP_XR_RRTR  = 4,  /**< Receiver Reference Time Report Block */\n\tRTCP_XR_DLRR  = 5,  /**< DLRR Report Block */\n\tRTCP_XR_SSR   = 6,  /**< Statistics Summary Report Block */\n\tRTCP_XR_VMR   = 7,  /**< VoIP Metrics Report Block */\n};\n\n/** Reception report block */\nstruct rtcp_rr {\n\tuint32_t ssrc;            /**< Data source being reported      */\n\tunsigned int fraction:8;  /**< Fraction lost since last SR/RR  */\n\tsigned int lost:24;       /**< Cumul. no. pkts lost (signed!)  */\n\tuint32_t last_seq;        /**< Extended last seq. no. received */\n\tuint32_t jitter;          /**< Interarrival jitter             */\n\tuint32_t lsr;             /**< Last SR packet from this source */\n\tuint32_t dlsr;            /**< Delay since last SR packet      */\n};\n\n/** SDES item */\nstruct rtcp_sdes_item {\n\tenum rtcp_sdes_type type; /**< Type of item (enum rtcp_sdes_type) */\n\tuint8_t length;           /**< Length of item (in octets)         */\n\tchar *data;               /**< Text, not null-terminated          */\n};\n\n/** One RTCP Message */\nstruct rtcp_msg {\n\t/** RTCP Header */\n\tstruct rtcp_hdr {\n\t\tunsigned int version:2;  /**< Protocol version       */\n\t\tunsigned int p:1;        /**< Padding flag           */\n\t\tunsigned int count:5;    /**< Varies by packet type  */\n\t\tunsigned int pt:8;       /**< RTCP packet type       */\n\t\tuint16_t length;         /**< Packet length in words */\n\t} hdr;\n\tunion {\n\t\t/** Sender report (SR) */\n\t\tstruct {\n\t\t\tuint32_t ssrc;        /**< Sender generating report  */\n\t\t\tuint32_t ntp_sec;     /**< NTP timestamp - seconds   */\n\t\t\tuint32_t ntp_frac;    /**< NTP timestamp - fractions */\n\t\t\tuint32_t rtp_ts;      /**< RTP timestamp             */\n\t\t\tuint32_t psent;       /**< RTP packets sent          */\n\t\t\tuint32_t osent;       /**< RTP octets sent           */\n\t\t\tstruct rtcp_rr *rrv;  /**< Reception report blocks   */\n\t\t} sr;\n\n\t\t/** Reception report (RR) */\n\t\tstruct {\n\t\t\tuint32_t ssrc;        /**< Receiver generating report*/\n\t\t\tstruct rtcp_rr *rrv;  /**< Reception report blocks   */\n\t\t} rr;\n\n\t\t/** Source Description (SDES) */\n\t\tstruct rtcp_sdes {\n\t\t\tuint32_t src;         /**< First SSRC/CSRC           */\n\t\t\tstruct rtcp_sdes_item *itemv;  /**< SDES items       */\n\t\t\tuint32_t n;           /**< Number of SDES items      */\n\t\t} *sdesv;\n\n\t\t/** BYE */\n\t\tstruct {\n\t\t\tuint32_t *srcv;    /**< List of sources              */\n\t\t\tchar *reason;      /**< Reason for leaving (opt.)    */\n\t\t} bye;\n\n\t\t/** Application-defined (APP) */\n\t\tstruct {\n\t\t\tuint32_t src;      /**< SSRC/CSRC                  */\n\t\t\tchar name[4];      /**< Name (ASCII)               */\n\t\t\tuint8_t *data;     /**< Application data (32 bits) */\n\t\t\tsize_t data_len;   /**< Number of data bytes       */\n\t\t} app;\n\n\t\t/** Full INTRA-frame Request (FIR) packet */\n\t\tstruct {\n\t\t\tuint32_t ssrc;  /**< SSRC for sender of this packet */\n\t\t} fir;\n\n\t\t/** Negative ACKnowledgements (NACK) packet */\n\t\tstruct {\n\t\t\tuint32_t ssrc;  /**< SSRC for sender of this packet */\n\t\t\tuint16_t fsn;   /**< First Sequence Number lost     */\n\t\t\tuint16_t blp;   /**< Bitmask of lost packets        */\n\t\t} nack;\n\n\t\t/** Feedback (RTPFB or PSFB) packet */\n\t\tstruct {\n\t\t\tuint32_t ssrc_packet;\n\t\t\tuint32_t ssrc_media;\n\t\t\tuint32_t n;\n\t\t\t/** Feedback Control Information (FCI) */\n\t\t\tunion {\n\t\t\t\tstruct gnack {\n\t\t\t\t\tuint16_t pid;\n\t\t\t\t\tuint16_t blp;\n\t\t\t\t} *gnackv;\n\t\t\t\tstruct sli {\n\t\t\t\t\tuint16_t first;\n\t\t\t\t\tuint16_t number;\n\t\t\t\t\tuint8_t picid;\n\t\t\t\t} *sliv;\n\t\t\t\tstruct fir_rfc5104 {\n\t\t\t\t\tuint32_t ssrc;\n\t\t\t\t\tuint8_t seq_n;\n\t\t\t\t} *firv;\n\t\t\t\tstruct twcc {\n\t\t\t\t\tuint16_t seq;\n\t\t\t\t\tuint16_t count;\n\t\t\t\t\tuint32_t reftime;\n\t\t\t\t\tuint8_t fbcount;\n\t\t\t\t\tstruct mbuf *chunks;\n\t\t\t\t\tstruct mbuf *deltas;\n\t\t\t\t} *twccv;\n\t\t\t\tstruct mbuf *afb;\n\t\t\t\tvoid *p;\n\t\t\t} fci;\n\t\t} fb;\n\n\t\t/** Extended Report (XR) packet */\n\t\t/** https://datatracker.ietf.org/doc/html/rfc3611#section-4 */\n\t\tstruct {\n\t\t\tuint32_t ssrc;\n\t\t\tuint8_t bt;         /**< Block type */\n\t\t\tuint16_t block_len; /**< Number of 32-bit words */\n\t\t\t/** Report blocks (RB) */\n\t\t\tunion {\n\t\t\t\tstruct {\n\t\t\t\t\tuint32_t ntp_msw;\n\t\t\t\t\tuint32_t ntp_lsw;\n\t\t\t\t} rrtrb;\n\t\t\t\tstruct {\n\t\t\t\t\tuint32_t ssrc;\n\t\t\t\t\tuint32_t lrr;\n\t\t\t\t\tuint32_t dlrr;\n\t\t\t\t} dlrrb;\n\t\t\t} rb;\n\t\t} xr;\n\t} r;\n};\n\n/** RTCP Statistics */\nstruct rtcp_stats {\n\tstruct {\n\t\tuint32_t sent;  /**< Tx RTP Packets                  */\n\t\tint lost;       /**< Tx RTP Packets Lost             */\n\t\tuint32_t jit;   /**< Tx Inter-arrival Jitter in [us] */\n\t} tx;\n\tstruct {\n\t\tuint32_t sent;  /**< Rx RTP Packets                  */\n\t\tint lost;       /**< Rx RTP Packets Lost             */\n\t\tuint32_t jit;   /**< Rx Inter-Arrival Jitter in [us] */\n\t} rx;\n\tuint32_t rtt;           /**< Current Round-Trip Time in [us] */\n};\n\nstruct sa;\nstruct re_printf;\nstruct rtp_sock;\n\n/**\n * Defines the callback handler for received RTP packets\n *\n * @param src  Source network address\n * @param hdr  RTP header\n * @param mb   RTP payload\n * @param arg  Handler argument\n */\ntypedef void (rtp_recv_h)(const struct sa *src, const struct rtp_header *hdr,\n\t\t\t  struct mbuf *mb, void *arg);\n\n\n/**\n * Defines the callback handler for received RTCP packets\n *\n * @param src  Source network address\n * @param msg  RTCP packet\n * @param arg  Handler argument\n */\ntypedef void (rtcp_recv_h)(const struct sa *src, struct rtcp_msg *msg,\n\t\t\t   void *arg);\n\n/* RTP api */\nint   rtp_alloc(struct rtp_sock **rsp);\nint   rtp_listen(struct rtp_sock **rsp, int proto, const struct sa *ip,\n\t\t uint16_t min_port, uint16_t max_port, bool enable_rtcp,\n\t\t rtp_recv_h *recvh, rtcp_recv_h *rtcph, void *arg);\nint   rtp_listen_single(struct rtp_sock **rsp, const struct sa *ip,\n\t\t\tuint16_t port, rtp_recv_h *recvh, void *arg);\nint   rtp_open(struct rtp_sock **rsp, int af);\nint   rtp_hdr_encode(struct mbuf *mb, const struct rtp_header *hdr);\nint   rtp_hdr_decode(struct rtp_header *hdr, struct mbuf *mb);\nint   rtp_encode(struct rtp_sock *rs, bool ext, bool marker, uint8_t pt,\n\t\t uint32_t ts, struct mbuf *mb);\nint   rtp_encode_seq(struct rtp_sock *rs, uint16_t seq, bool ext, bool marker,\n\t\t   uint8_t pt, uint32_t ts, struct mbuf *mb);\nint   rtp_decode(struct rtp_sock *rs, struct mbuf *mb, struct rtp_header *hdr);\nint   rtp_send(struct rtp_sock *rs, const struct sa *dst, bool ext,\n\t       bool marker, uint8_t pt, uint32_t ts, uint64_t jfs_rt,\n\t       struct mbuf *mb);\nint   rtp_resend(struct rtp_sock *rs, uint16_t seq, const struct sa *dst,\n\t       bool ext, bool marker, uint8_t pt, uint32_t ts,\n\t       struct mbuf *mb);\nint   rtp_debug(struct re_printf *pf, const struct rtp_sock *rs);\nvoid *rtp_sock(const struct rtp_sock *rs);\nuint32_t rtp_sess_ssrc(const struct rtp_sock *rs);\nuint16_t rtp_sess_seq(const struct rtp_sock *rs);\nconst struct sa *rtp_local(const struct rtp_sock *rs);\nint rtp_clear(struct rtp_sock *rs);\n\n/* RTCP session api */\nvoid  rtcp_start(struct rtp_sock *rs, const char *cname,\n\t\t const struct sa *peer);\nvoid  rtcp_enable_mux(struct rtp_sock *rs, bool enabled);\nvoid  rtcp_set_interval(struct rtp_sock *rs, uint32_t n);\nvoid  rtcp_set_srate(struct rtp_sock *rs, uint32_t sr_tx, uint32_t sr_rx);\nvoid  rtcp_set_srate_tx(struct rtp_sock *rs, uint32_t srate_tx);\nvoid  rtcp_set_srate_rx(struct rtp_sock *rs, uint32_t srate_rx);\nint   rtcp_send(struct rtp_sock *rs, struct mbuf *mb);\nint   rtcp_send_app(struct rtp_sock *rs, const char name[4],\n\t\t    const uint8_t *data, size_t len);\nint   rtcp_send_fir(struct rtp_sock *rs, uint32_t ssrc);\nint   rtcp_send_nack(struct rtp_sock *rs, uint16_t fsn, uint16_t blp);\nint   rtcp_send_gnack(struct rtp_sock *rs, uint32_t ssrc, uint16_t fsn,\n\t\t    uint16_t blp);\nint   rtcp_send_twcc(struct rtp_sock *rs, uint32_t ssrc, struct twcc *twcc);\nint   rtcp_send_pli(struct rtp_sock *rs, uint32_t fb_ssrc);\nint   rtcp_send_fir_rfc5104(struct rtp_sock *rs, uint32_t ssrc,\n\t\t\t    uint8_t fir_seqn);\nint   rtcp_debug(struct re_printf *pf, const struct rtp_sock *rs);\nvoid *rtcp_sock(const struct rtp_sock *rs);\nint   rtcp_stats(struct rtp_sock *rs, uint32_t ssrc, struct rtcp_stats *stats);\nint   rtcp_send_bye_packet(struct rtp_sock *rs);\n\n/* RTCP utils */\nint   rtcp_encode(struct mbuf *mb, enum rtcp_type type, uint32_t count, ...);\nint   rtcp_decode(struct rtcp_msg **msgp, struct mbuf *mb);\nint   rtcp_msg_print(struct re_printf *pf, const struct rtcp_msg *msg);\nint   rtcp_sdes_encode(struct mbuf *mb, uint32_t src, uint32_t itemc, ...);\nconst char *rtcp_type_name(enum rtcp_type type);\nconst char *rtcp_sdes_name(enum rtcp_sdes_type sdes);\nbool rtp_is_rtcp_packet(const struct mbuf *mb);\nvoid rtcp_calc_rtt(uint32_t *rtt, uint32_t lsr, uint32_t dlsr);\n\n\n/**\n * Check if a payload type is RTCP\n *\n * @param pt Payload type\n *\n * @return True if RTCP, otherwise false\n */\nstatic inline bool rtp_pt_is_rtcp(uint8_t pt)\n{\n\treturn 64 <= pt && pt <= 95;\n}\n\n\n/**\n * Calculate difference between two sequence numbers\n *\n * @param x First sequence number\n * @param y Second sequence number\n *\n * @return Difference between the two sequence numbers\n */\nstatic inline int16_t rtp_seq_diff(uint16_t x, uint16_t y)\n{\n\treturn (int16_t)(y - x);\n}\n\n\n/**\n * Compare two RTP sequence numbers\n *\n * @param x First sequence number\n * @param y Second sequence number\n *\n * @return true if x is less than y; false otherwise\n */\nstatic inline bool rtp_seq_less(uint16_t x, uint16_t y)\n{\n\treturn ((int16_t)(x - y)) < 0;\n}\n\n\n/** NTP Time */\nstruct rtp_ntp_time {\n\tuint32_t hi;  /**< Seconds since 0h UTC on 1 January 1900 */\n\tuint32_t lo;  /**< Fraction of seconds                    */\n};\n\n/** Per-source state information */\nstruct rtp_source {\n\tstruct sa rtp_peer;       /**< IP-address of the RTP source        */\n\tuint16_t max_seq;         /**< Highest seq. number seen            */\n\tuint32_t cycles;          /**< Shifted count of seq. number cycles */\n\tuint32_t base_seq;        /**< Base seq number                     */\n\tuint32_t bad_seq;         /**< Last 'bad' seq number + 1           */\n\tuint32_t probation;       /**< Sequ. packets till source is valid  */\n\tuint32_t received;        /**< Packets received                    */\n\tuint32_t expected_prior;  /**< Packet expected at last interval    */\n\tuint32_t received_prior;  /**< Packet received at last interval    */\n\tint transit;              /**< Relative trans time for prev pkt    */\n\tuint32_t jitter;          /**< Estimated jitter                    */\n\tsize_t rtp_rx_bytes;      /**< Number of RTP bytes received        */\n\tuint64_t sr_recv;         /**< When the last SR was received       */\n\tstruct rtp_ntp_time last_sr;/**< NTP Timestamp from last SR recvd  */\n\tuint32_t rtp_ts;          /**< RTP timestamp                       */\n\tuint32_t last_rtp_ts;     /**< Last RTP timestamp                  */\n\tuint32_t psent;           /**< RTP packets sent                    */\n\tuint32_t osent;           /**< RTP octets sent                     */\n};\n\n/* Source */\nvoid rtp_source_init_seq(struct rtp_source *s, uint16_t seq);\nint  rtp_source_update_seq(struct rtp_source *s, uint16_t seq);\nvoid rtp_source_calc_jitter(struct rtp_source *s, uint32_t rtp_ts,\n\t\t\tuint32_t arrival);\nint  rtp_source_calc_lost(const struct rtp_source *s);\nuint8_t rtp_source_calc_fraction_lost(struct rtp_source *s);\n\n\n/** RTP Extensions for Transport-wide Congestion Control */\nenum twcc_packet_state {\n\tTWCC_PK_NOT_RECEIVED = 0,/**< Packet not received                   */\n\tTWCC_PK_RECEIVED,        /**< Packet received, small delta          */\n\tTWCC_PK_LARGE_DELTA      /**< Packet received, large or neg. delta  */\n};\n\nstruct rtcp_twcc_packet {\n\tstruct le le;\n\tuint64_t ts;\n\tint32_t delta;\n\tenum twcc_packet_state state;\n\tuint16_t tseq;\n};\n"
  },
  {
    "path": "include/re_rtpext.h",
    "content": "/**\n * @file re_rtpext.h  Interface to RTP Header Extensions\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n\n/*\n * RTP Header Extensions\n */\n\n#define RTPEXT_HDR_SIZE        4\n#define RTPEXT_TYPE_MAGIC      0xbede  /* One-Byte header */\n#define RTPEXT_TYPE_MAGIC_LONG 0x1000  /* Two-Byte header */\n\nenum {\n\tRTPEXT_ID_MIN  =  1,\n\tRTPEXT_ID_MAX  = 14,\n};\n\nenum {\n\tRTPEXT_LEN_MIN =  1,\n\tRTPEXT_LEN_MAX = 16,\n\tRTPEXT_LEN_MAX_LONG = 256,\n};\n\n\n/** Defines an RTP header extension */\nstruct rtpext {\n\tuint8_t id;                        /**< Identifier             */\n\tuint8_t len;                       /**< Length of data [bytes] */\n\tuint8_t data[RTPEXT_LEN_MAX_LONG]; /**< Data field             */\n};\n\n\nint rtpext_hdr_encode(struct mbuf *mb, size_t num_bytes);\nint rtpext_hdr_encode_long(struct mbuf *mb, size_t num_bytes);\nint rtpext_encode(struct mbuf *mb, uint8_t id, size_t len,\n\t\t  const uint8_t *data);\nint rtpext_encode_long(struct mbuf *mb, uint8_t id, uint8_t len,\n\t\t       const uint8_t *data);\nint rtpext_decode(struct rtpext *ext, struct mbuf *mb);\nint rtpext_decode_long(struct rtpext *ext, struct mbuf *mb);\nconst struct rtpext *rtpext_find(const struct rtpext *extv, size_t extc,\n\t\t\t\t uint8_t id);\n"
  },
  {
    "path": "include/re_sa.h",
    "content": "/**\n * @file re_sa.h  Interface to Socket Address\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#if defined(WIN32)\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#if !defined(UNIX_PATH_MAX)\n#define UNIX_PATH_MAX 108\ntypedef struct sockaddr_un {\n\tADDRESS_FAMILY sun_family;\n\tchar sun_path[UNIX_PATH_MAX];\n} SOCKADDR_UN, *PSOCKADDR_UN;\n#endif\n#else\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <sys/un.h>\n#endif\n\n\nstruct pl;\n\n/** Socket Address flags */\nenum sa_flag {\n\tSA_ADDR      = 1<<0,\n\tSA_PORT      = 1<<1,\n\tSA_ALL       = SA_ADDR | SA_PORT\n};\n\n/** Defines a Socket Address */\nstruct sa {\n\tunion {\n\t\tstruct sockaddr sa;\n\t\tstruct sockaddr_in in;\n\t\tstruct sockaddr_in6 in6;\n#if !defined(HAVE_UNIXSOCK) || HAVE_UNIXSOCK == 1\n\t\tstruct sockaddr_un un;\n#endif\n\t} u;\n\tsocklen_t len;\n};\n\nvoid     sa_init(struct sa *sa, int af);\nint      sa_set(struct sa *sa, const struct pl *addr, uint16_t port);\nint      sa_set_str(struct sa *sa, const char *addr, uint16_t port);\nvoid     sa_set_in(struct sa *sa, uint32_t addr, uint16_t port);\nvoid     sa_set_in6(struct sa *sa, const uint8_t *addr, uint16_t port);\nint      sa_set_sa(struct sa *sa, const struct sockaddr *s);\nvoid     sa_set_port(struct sa *sa, uint16_t port);\nint      sa_decode(struct sa *sa, const char *str, size_t len);\n\nint      sa_af(const struct sa *sa);\nuint32_t sa_in(const struct sa *sa);\nvoid     sa_in6(const struct sa *sa, uint8_t *addr);\nint      sa_addrinfo(const char *addr, struct sa *sa);\n\nint      sa_ntop(const struct sa *sa, char *buf, int size);\nint      sa_pton(const char *addr, struct sa *sa);\nuint16_t sa_port(const struct sa *sa);\nbool     sa_isset(const struct sa *sa, int flag);\nuint32_t sa_hash(const struct sa *sa, int flag);\n\nvoid     sa_cpy(struct sa *dst, const struct sa *src);\nbool     sa_cmp(const struct sa *l, const struct sa *r, int flag);\n\nbool     sa_is_linklocal(const struct sa *sa);\nbool     sa_is_loopback(const struct sa *sa);\nbool     sa_is_multicast(const struct sa *sa);\nbool     sa_is_any(const struct sa *sa);\n\nvoid     sa_set_scopeid(struct sa *sa, uint32_t scopeid);\nuint32_t sa_scopeid(const struct sa *sa);\nsize_t   sa_struct_get_size(void);\n\nstruct re_printf;\nint      sa_print_addr(struct re_printf *pf, const struct sa *sa);\n"
  },
  {
    "path": "include/re_sdp.h",
    "content": "/**\n * @file re_sdp.h  Interface to Session Description Protocol (SDP)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nenum {\n\tSDP_VERSION = 0\n};\n\n/** SDP Direction */\nenum sdp_dir {\n\tSDP_INACTIVE = 0,\n\tSDP_RECVONLY = 1,\n\tSDP_SENDONLY = 2,\n\tSDP_SENDRECV = 3,\n};\n\n/** SDP Bandwidth type */\nenum sdp_bandwidth {\n\tSDP_BANDWIDTH_MIN = 0,\n\tSDP_BANDWIDTH_CT = 0,  /**< [kbit/s] Conference Total         */\n\tSDP_BANDWIDTH_AS,      /**< [kbit/s] Application Specific     */\n\tSDP_BANDWIDTH_RS,      /**< [bit/s] RTCP Senders (RFC 3556)   */\n\tSDP_BANDWIDTH_RR,      /**< [bit/s] RTCP Receivers (RFC 3556) */\n\tSDP_BANDWIDTH_TIAS,    /**< [bit/s] Transport Independent Application\n\t\t\t\t  Specific Maximum (RFC 3890) */\n\tSDP_BANDWIDTH_MAX,\n};\n\n\nstruct sdp_format;\n\ntypedef int(sdp_media_enc_h)(struct mbuf *mb, bool offer, void *arg);\ntypedef int(sdp_fmtp_enc_h)(struct mbuf *mb, const struct sdp_format *fmt,\n\t\t\t    bool offer, void *data);\ntypedef bool(sdp_fmtp_cmp_h)(const char *params1, const char *params2,\n\t\t\t     void *data);\ntypedef bool(sdp_format_h)(struct sdp_format *fmt, void *arg);\ntypedef bool(sdp_attr_h)(const char *name, const char *value, void *arg);\n\n/** SDP Format */\nstruct sdp_format {\n\tstruct le le;\n\tchar *id;\n\tchar *params;\n\tchar *rparams;\n\tchar *name;\n\tsdp_fmtp_enc_h *ench;\n\tsdp_fmtp_cmp_h *cmph;\n\tvoid *data;\n\tbool ref;\n\tbool sup;\n\tint pt;\n\tuint32_t srate;\n\tuint8_t ch;\n};\n\n\n/* session */\nstruct sdp_session;\n\nint  sdp_session_alloc(struct sdp_session **sessp, const struct sa *laddr);\nvoid sdp_session_set_laddr(struct sdp_session *sess, const struct sa *laddr);\nconst struct sa *sdp_session_laddr(struct sdp_session *sess);\nvoid sdp_session_set_lbandwidth(struct sdp_session *sess,\n\t\t\t\tenum sdp_bandwidth type, int32_t bw);\nint  sdp_session_set_lattr(struct sdp_session *sess, bool replace,\n\t\t\t   const char *name, const char *value, ...);\nvoid sdp_session_del_lattr(struct sdp_session *sess, const char *name);\nint32_t sdp_session_lbandwidth(const struct sdp_session *sess,\n\t\t\t       enum sdp_bandwidth type);\nint32_t sdp_session_rbandwidth(const struct sdp_session *sess,\n\t\t\t       enum sdp_bandwidth type);\nconst char *sdp_session_rattr(const struct sdp_session *sess,\n\t\t\t      const char *name);\nconst char *sdp_session_rattr_apply(const struct sdp_session *sess,\n\t\t\t\t    const char *name,\n\t\t\t\t    sdp_attr_h *attrh, void *arg);\nconst struct list *sdp_session_medial(const struct sdp_session *sess,\n\t\t\t\t      bool local);\nint  sdp_session_debug(struct re_printf *pf, const struct sdp_session *sess);\n\n\n/* media */\nstruct sdp_media;\n\nint  sdp_media_add(struct sdp_media **mp, struct sdp_session *sess,\n\t\t   const char *name, uint16_t port, const char *proto);\nint  sdp_media_set_alt_protos(struct sdp_media *m, unsigned protoc, ...);\nvoid sdp_media_set_encode_handler(struct sdp_media *m, sdp_media_enc_h *ench,\n\t\t\t\t  void *arg);\nvoid sdp_media_set_fmt_ignore(struct sdp_media *m, bool fmt_ignore);\nbool sdp_media_disabled(struct sdp_media *m);\nvoid sdp_media_set_disabled(struct sdp_media *m, bool disabled);\nvoid sdp_media_set_lport(struct sdp_media *m, uint16_t port);\nvoid sdp_media_set_laddr(struct sdp_media *m, const struct sa *laddr);\nvoid sdp_media_set_lbandwidth(struct sdp_media *m, enum sdp_bandwidth type,\n\t\t\t      int32_t bw);\nvoid sdp_media_set_lport_rtcp(struct sdp_media *m, uint16_t port);\nvoid sdp_media_set_laddr_rtcp(struct sdp_media *m, const struct sa *laddr);\nvoid sdp_media_set_ldir(struct sdp_media *m, enum sdp_dir dir);\nint  sdp_media_set_lattr(struct sdp_media *m, bool replace,\n\t\t\t const char *name, const char *value, ...);\nvoid sdp_media_del_lattr(struct sdp_media *m, const char *name);\nconst char *sdp_media_proto(const struct sdp_media *m);\nuint16_t sdp_media_rport(const struct sdp_media *m);\nconst struct sa *sdp_media_raddr(const struct sdp_media *m);\nconst struct sa *sdp_media_laddr(const struct sdp_media *m);\nvoid sdp_media_raddr_rtcp(const struct sdp_media *m, struct sa *raddr);\nint32_t sdp_media_rbandwidth(const struct sdp_media *m,\n\t\t\t     enum sdp_bandwidth type);\nenum sdp_dir sdp_media_ldir(const struct sdp_media *m);\nenum sdp_dir sdp_media_rdir(const struct sdp_media *m);\nenum sdp_dir sdp_media_dir(const struct sdp_media *m);\nconst struct sdp_format *sdp_media_lformat(const struct sdp_media *m, int pt);\nconst struct sdp_format *sdp_media_rformat(const struct sdp_media *m,\n\t\t\t\t\t   const char *name);\nstruct sdp_format *sdp_media_format(const struct sdp_media *m,\n\t\t\t\t    bool local, const char *id,\n\t\t\t\t    int pt, const char *name,\n\t\t\t\t    int32_t srate, int8_t ch);\nstruct sdp_format *sdp_media_format_apply(const struct sdp_media *m,\n\t\t\t\t\t  bool local, const char *id,\n\t\t\t\t\t  int pt, const char *name,\n\t\t\t\t\t  int32_t srate, int8_t ch,\n\t\t\t\t\t  sdp_format_h *fmth, void *arg);\nconst struct list *sdp_media_format_lst(const struct sdp_media *m, bool local);\nconst char *sdp_media_rattr(const struct sdp_media *m, const char *name);\nconst char *sdp_media_session_rattr(const struct sdp_media *m,\n\t\t\t\t    const struct sdp_session *sess,\n\t\t\t\t    const char *name);\nconst char *sdp_media_lattr_apply(const struct sdp_media *m, const char *name,\n\t\t\t\t  sdp_attr_h *attrh, void *arg);\nconst char *sdp_media_rattr_apply(const struct sdp_media *m, const char *name,\n\t\t\t\t  sdp_attr_h *attrh, void *arg);\nconst char *sdp_media_name(const struct sdp_media *m);\nint  sdp_media_debug(struct re_printf *pf, const struct sdp_media *m);\n\n\n/* format */\nint  sdp_format_add(struct sdp_format **fmtp, struct sdp_media *m,\n\t\t    bool prepend, const char *id, const char *name,\n\t\t    uint32_t srate, uint8_t ch, sdp_fmtp_enc_h *ench,\n\t\t    sdp_fmtp_cmp_h *cmph, void *data, bool ref,\n\t\t    const char *params, ...);\nint  sdp_format_set_params(struct sdp_format *fmt, const char *params, ...);\nbool sdp_format_cmp(const struct sdp_format *fmt1,\n\t\t    const struct sdp_format *fmt2);\nint  sdp_format_debug(struct re_printf *pf, const struct sdp_format *fmt);\n\n\n/* encode/decode */\nint sdp_encode(struct mbuf **mbp, struct sdp_session *sess, bool offer);\nint sdp_decode(struct sdp_session *sess, struct mbuf *mb, bool offer);\n\n\n/* strings */\nconst char *sdp_dir_name(enum sdp_dir dir);\nconst char *sdp_bandwidth_name(enum sdp_bandwidth type);\n\n\nextern const char sdp_attr_fmtp[];\nextern const char sdp_attr_maxptime[];\nextern const char sdp_attr_ptime[];\nextern const char sdp_attr_rtcp[];\nextern const char sdp_attr_rtpmap[];\n\nextern const char sdp_media_audio[];\nextern const char sdp_media_video[];\nextern const char sdp_media_text[];\n\nextern const char sdp_proto_rtpavp[];\nextern const char sdp_proto_rtpsavp[];\n\n\n/* utility functions */\n\nenum sdp_dir sdp_dir_decode(const struct pl *pl);\n\n/** RTP Header Extensions, as defined in RFC 5285 */\nstruct sdp_extmap {\n\tstruct pl name;\n\tstruct pl attrs;\n\tenum sdp_dir dir;\n\tbool dir_set;\n\tuint32_t id;\n};\n\nint sdp_extmap_decode(struct sdp_extmap *ext, const char *val);\n"
  },
  {
    "path": "include/re_sha.h",
    "content": "/**\n * @file re_sha.h  Interface to SHA (Secure Hash Standard) functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n/** SHA-1 Digest size in bytes */\n#define SHA1_DIGEST_SIZE 20\n#define SHA256_DIGEST_SIZE 32\n#define SHA512_DIGEST_SIZE 64\n\n#ifndef SHA_DIGEST_LENGTH\n/** SHA-1 Digest size in bytes (OpenSSL compat) */\n#define SHA_DIGEST_LENGTH SHA1_DIGEST_SIZE\n#endif\n\n#ifndef SHA256_DIGEST_LENGTH\n/** SHA-256 Digest size in bytes (OpenSSL compat) */\n#define SHA256_DIGEST_LENGTH SHA256_DIGEST_SIZE\n#endif\n\n#ifndef SHA512_DIGEST_LENGTH\n/** SHA-512 Digest size in bytes (OpenSSL compat) */\n#define SHA512_DIGEST_LENGTH SHA512_DIGEST_SIZE\n#endif\n\nvoid sha1(const uint8_t *d, size_t n, uint8_t *md);\nvoid sha256(const uint8_t *d, size_t n, uint8_t *md);\nint  sha256_printf(uint8_t md[32], const char *fmt, ...);\n"
  },
  {
    "path": "include/re_shim.h",
    "content": "/**\n * @file re_shim.h  Interface to SHIM layer\n *\n * Copyright (C) 2015 - 2022 Alfred E. Heggestad\n */\n\n\n/* RFC 4571 */\n\n\nenum { SHIM_HDR_SIZE = 2 };\n\nstruct shim;\n\ntypedef bool (shim_frame_h)(struct mbuf *mb, void *arg);\n\n\nint shim_insert(struct shim **shimp, struct tcp_conn *tc, int layer,\n\t\tshim_frame_h *frameh, void *arg);\nint shim_debug(struct re_printf *pf, const struct shim *shim);\n"
  },
  {
    "path": "include/re_sip.h",
    "content": "/**\n * @file re_sip.h  Session Initiation Protocol\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n/* forward declarations */\nstruct tls;\n\nenum {\n\tSIP_PORT     = 5060,\n\tSIP_PORT_TLS = 5061,\n};\n\n/** SIP Transport */\nenum sip_transp {\n\tSIP_TRANSP_NONE = -1,\n\tSIP_TRANSP_UDP = 0,\n\tSIP_TRANSP_TCP,\n\tSIP_TRANSP_TLS,\n\tSIP_TRANSP_WS,\n\tSIP_TRANSP_WSS,\n\n\tSIP_TRANSPC,\n};\n\n\n/** SIP Header ID (perfect hash value) */\nenum sip_hdrid {\n\tSIP_HDR_ACCEPT                        = 3186,\n\tSIP_HDR_ACCEPT_CONTACT                =  232,\n\tSIP_HDR_ACCEPT_ENCODING               =  708,\n\tSIP_HDR_ACCEPT_LANGUAGE               = 2867,\n\tSIP_HDR_ACCEPT_RESOURCE_PRIORITY      = 1848,\n\tSIP_HDR_ALERT_INFO                    =  274,\n\tSIP_HDR_ALLOW                         = 2429,\n\tSIP_HDR_ALLOW_EVENTS                  =   66,\n\tSIP_HDR_ANSWER_MODE                   = 2905,\n\tSIP_HDR_AUTHENTICATION_INFO           = 3144,\n\tSIP_HDR_AUTHORIZATION                 = 2503,\n\tSIP_HDR_CALL_ID                       = 3095,\n\tSIP_HDR_CALL_INFO                     =  586,\n\tSIP_HDR_CONTACT                       =  229,\n\tSIP_HDR_CONTENT_DISPOSITION           = 1425,\n\tSIP_HDR_CONTENT_ENCODING              =  580,\n\tSIP_HDR_CONTENT_LANGUAGE              = 3371,\n\tSIP_HDR_CONTENT_LENGTH                = 3861,\n\tSIP_HDR_CONTENT_TYPE                  =  809,\n\tSIP_HDR_CSEQ                          =  746,\n\tSIP_HDR_DATE                          = 1027,\n\tSIP_HDR_ENCRYPTION                    = 3125,\n\tSIP_HDR_ERROR_INFO                    =   21,\n\tSIP_HDR_EVENT                         = 3286,\n\tSIP_HDR_EXPIRES                       = 1983,\n\tSIP_HDR_FLOW_TIMER                    =  584,\n\tSIP_HDR_FROM                          = 1963,\n\tSIP_HDR_HIDE                          =  283,\n\tSIP_HDR_HISTORY_INFO                  = 2582,\n\tSIP_HDR_IDENTITY                      = 2362,\n\tSIP_HDR_IDENTITY_INFO                 =  980,\n\tSIP_HDR_IN_REPLY_TO                   = 1577,\n\tSIP_HDR_JOIN                          = 3479,\n\tSIP_HDR_MAX_BREADTH                   = 3701,\n\tSIP_HDR_MAX_FORWARDS                  = 3549,\n\tSIP_HDR_MIME_VERSION                  = 3659,\n\tSIP_HDR_MIN_EXPIRES                   = 1121,\n\tSIP_HDR_MIN_SE                        = 2847,\n\tSIP_HDR_ORGANIZATION                  = 3247,\n\tSIP_HDR_P_ACCESS_NETWORK_INFO         = 1662,\n\tSIP_HDR_P_ANSWER_STATE                =   42,\n\tSIP_HDR_P_ASSERTED_IDENTITY           = 1233,\n\tSIP_HDR_P_ASSOCIATED_URI              =  900,\n\tSIP_HDR_P_CALLED_PARTY_ID             = 3347,\n\tSIP_HDR_P_CHARGING_FUNCTION_ADDRESSES = 2171,\n\tSIP_HDR_P_CHARGING_VECTOR             =   25,\n\tSIP_HDR_P_DCS_TRACE_PARTY_ID          = 3027,\n\tSIP_HDR_P_DCS_OSPS                    = 1788,\n\tSIP_HDR_P_DCS_BILLING_INFO            = 2017,\n\tSIP_HDR_P_DCS_LAES                    =  693,\n\tSIP_HDR_P_DCS_REDIRECT                = 1872,\n\tSIP_HDR_P_EARLY_MEDIA                 = 2622,\n\tSIP_HDR_P_MEDIA_AUTHORIZATION         = 1035,\n\tSIP_HDR_P_PREFERRED_IDENTITY          = 1263,\n\tSIP_HDR_P_PROFILE_KEY                 = 1904,\n\tSIP_HDR_P_REFUSED_URI_LIST            = 1047,\n\tSIP_HDR_P_SERVED_USER                 = 1588,\n\tSIP_HDR_P_USER_DATABASE               = 2827,\n\tSIP_HDR_P_VISITED_NETWORK_ID          = 3867,\n\tSIP_HDR_PATH                          = 2741,\n\tSIP_HDR_PERMISSION_MISSING            = 1409,\n\tSIP_HDR_PRIORITY                      = 3520,\n\tSIP_HDR_PRIV_ANSWER_MODE              = 2476,\n\tSIP_HDR_PRIVACY                       = 3150,\n\tSIP_HDR_PROXY_AUTHENTICATE            =  116,\n\tSIP_HDR_PROXY_AUTHORIZATION           = 2363,\n\tSIP_HDR_PROXY_REQUIRE                 = 3562,\n\tSIP_HDR_RACK                          = 2523,\n\tSIP_HDR_REASON                        = 3732,\n\tSIP_HDR_RECORD_ROUTE                  =  278,\n\tSIP_HDR_REFER_SUB                     = 2458,\n\tSIP_HDR_REFER_TO                      = 1521,\n\tSIP_HDR_REFERRED_BY                   = 3456,\n\tSIP_HDR_REJECT_CONTACT                =  285,\n\tSIP_HDR_REPLACES                      = 2534,\n\tSIP_HDR_REPLY_TO                      = 2404,\n\tSIP_HDR_REQUEST_DISPOSITION           = 3715,\n\tSIP_HDR_REQUIRE                       = 3905,\n\tSIP_HDR_RESOURCE_PRIORITY             = 1643,\n\tSIP_HDR_RESPONSE_KEY                  = 1548,\n\tSIP_HDR_RETRY_AFTER                   =  409,\n\tSIP_HDR_ROUTE                         =  661,\n\tSIP_HDR_RSEQ                          =  445,\n\tSIP_HDR_SECURITY_CLIENT               = 1358,\n\tSIP_HDR_SECURITY_SERVER               =  811,\n\tSIP_HDR_SECURITY_VERIFY               =  519,\n\tSIP_HDR_SERVER                        =  973,\n\tSIP_HDR_SERVICE_ROUTE                 = 1655,\n\tSIP_HDR_SESSION_EXPIRES               = 1979,\n\tSIP_HDR_SIP_ETAG                      = 1997,\n\tSIP_HDR_SIP_IF_MATCH                  = 3056,\n\tSIP_HDR_SUBJECT                       = 1043,\n\tSIP_HDR_SUBSCRIPTION_STATE            = 2884,\n\tSIP_HDR_SUPPORTED                     =  119,\n\tSIP_HDR_TARGET_DIALOG                 = 3450,\n\tSIP_HDR_TIMESTAMP                     =  938,\n\tSIP_HDR_TO                            = 1449,\n\tSIP_HDR_TRIGGER_CONSENT               = 3180,\n\tSIP_HDR_UNSUPPORTED                   =  982,\n\tSIP_HDR_USER_AGENT                    = 4064,\n\tSIP_HDR_VIA                           = 3961,\n\tSIP_HDR_WARNING                       = 2108,\n\tSIP_HDR_WWW_AUTHENTICATE              = 2763,\n\n\tSIP_HDR_NONE = -1\n};\n\n\nenum rel100_mode {\n\tREL100_DISABLED = 0,\n\tREL100_ENABLED = 1,\n\tREL100_REQUIRED = 2,\n};\n\n\nenum {\n\tSIP_T1 =  500,\n\tSIP_T2 = 4000,\n\tSIP_T4 = 5000,\n};\n\n\n/** SIP Via header */\n#define RE_RFC3261_BRANCH_ID \"z9hG4bK\"\nstruct sip_via {\n\tstruct pl sentby;\n\tstruct sa addr;\n\tstruct pl params;\n\tstruct pl branch;\n\tstruct pl val;\n\tenum sip_transp tp;\n};\n\n/** SIP Address */\nstruct sip_addr {\n\tstruct pl dname;\n\tstruct pl auri;\n\tstruct uri uri;\n\tstruct pl params;\n};\n\n/** SIP Tag address */\nstruct sip_taddr {\n\tstruct pl dname;\n\tstruct pl auri;\n\tstruct uri uri;\n\tstruct pl params;\n\tstruct pl tag;\n\tstruct pl val;\n};\n\n/** SIP CSeq header */\nstruct sip_cseq {\n\tstruct pl met;\n\tuint32_t num;\n};\n\n/** SIP RAck header (RFC 3262) */\nstruct sip_rack {\n\tstruct pl met;\n\tuint32_t rel_seq;\n\tuint32_t cseq;\n};\n\n/** SIP Header */\nstruct sip_hdr {\n\tstruct le le;          /**< Linked-list element    */\n\tstruct le he;          /**< Hash-table element     */\n\tstruct pl name;        /**< SIP Header name        */\n\tstruct pl val;         /**< SIP Header value       */\n\tenum sip_hdrid id;     /**< SIP Header id (unique) */\n};\n\n/** SIP Message */\nstruct sip_msg {\n\tstruct sa src;         /**< Source network address               */\n\tstruct sa dst;         /**< Destination network address          */\n\tstruct pl ver;         /**< SIP Version number                   */\n\tstruct pl met;         /**< Request method                       */\n\tstruct pl ruri;        /**< Raw request URI                      */\n\tstruct uri uri;        /**< Parsed request URI                   */\n\tuint16_t scode;        /**< Response status code                 */\n\tstruct pl reason;      /**< Response reason phrase               */\n\tstruct list hdrl;      /**< List of SIP Headers (struct sip_hdr) */\n\tstruct sip_via via;    /**< Parsed first Via header              */\n\tstruct sip_taddr to;   /**< Parsed To header                     */\n\tstruct sip_taddr from; /**< Parsed From header                   */\n\tstruct sip_cseq cseq;  /**< Parsed CSeq header                   */\n\tstruct sip_rack rack;  /**< Parsed RAck header (RFC 3262)        */\n\tuint32_t rel_seq;      /**< RSeq number (RFC 3262)               */\n\tstruct msg_ctype ctyp; /**< Content Type                         */\n\tstruct pl callid;      /**< Cached Call-ID header                */\n\tstruct pl maxfwd;      /**< Cached Max-Forwards header           */\n\tstruct pl expires;     /**< Cached Expires header                */\n\tstruct pl clen;        /**< Cached Content-Length header         */\n\tstruct hash *hdrht;    /**< Hash-table with all SIP headers      */\n\tstruct mbuf *mb;       /**< Buffer containing the SIP message    */\n\tvoid *sock;            /**< Transport socket                     */\n\tuint64_t tag;          /**< Opaque tag                           */\n\tenum sip_transp tp;    /**< SIP Transport                        */\n\tbool req;              /**< True if Request, False if Response  */\n};\n\n/** SIP Loop-state */\nstruct sip_loopstate {\n\tuint32_t failc;\n\tuint16_t last_scode;\n};\n\n/** SIP Contact */\nstruct sip_contact {\n\tconst char *uri;\n\tconst struct sa *addr;\n\tenum sip_transp tp;\n};\n\n/** SIP connection config */\nstruct sip_conncfg {\n\tstruct le he;\n\tstruct sa paddr;\n\n\tuint16_t srcport;\n};\n\n/** SIP UAS Authentication */\nstruct sip_uas_auth {\n\tconst char *realm;\n\tchar *nonce;\n\tbool stale;\n};\n\nstruct sip;\nstruct sip_lsnr;\nstruct sip_request;\nstruct sip_strans;\nstruct sip_auth;\nstruct sip_dialog;\nstruct sip_keepalive;\nstruct sip_uas_auth;\nstruct dnsc;\n\ntypedef bool(sip_msg_h)(const struct sip_msg *msg, void *arg);\ntypedef int(sip_send_h)(enum sip_transp tp, struct sa *src,\n\t\t\tconst struct sa *dst, struct mbuf *mb,\n\t\t\tstruct mbuf **contp, void *arg);\ntypedef int(sip_conn_h)(struct sa *src, const struct sa *dst, struct mbuf *mb,\n\t\t\tvoid *arg);\ntypedef void(sip_resp_h)(int err, const struct sip_msg *msg, void *arg);\ntypedef void(sip_cancel_h)(void *arg);\ntypedef void(sip_exit_h)(void *arg);\ntypedef int(sip_auth_h)(char **username, char **password, const char *realm,\n\t\t\tvoid *arg);\ntypedef bool(sip_hdr_h)(const struct sip_hdr *hdr, const struct sip_msg *msg,\n\t\t\tvoid *arg);\ntypedef void(sip_keepalive_h)(int err, void *arg);\ntypedef int(digest_printf_h)(uint8_t *md, const char *fmt, ...);\n\n#define LIBRE_HAVE_SIPTRACE 1\ntypedef void(sip_trace_h)(bool tx, enum sip_transp tp,\n\t\t\t  const struct sa *src, const struct sa *dst,\n\t\t\t  const uint8_t *pkt, size_t len, void *arg);\ntypedef int (sip_uas_auth_h)(uint8_t *ha1, const struct pl *user,\n\t\t\t     const char *realm, void *arg);\n\n\n/* sip */\nint  sip_alloc(struct sip **sipp, struct dnsc *dnsc, uint32_t ctsz,\n\t       uint32_t stsz, uint32_t tcsz, const char *software,\n\t       sip_exit_h *exith, void *arg);\nvoid sip_close(struct sip *sip, bool force);\nint  sip_listen(struct sip_lsnr **lsnrp, struct sip *sip, bool req,\n\t\tsip_msg_h *msgh, void *arg);\nint  sip_debug(struct re_printf *pf, const struct sip *sip);\nint  sip_send(struct sip *sip, void *sock, enum sip_transp tp,\n\t      const struct sa *dst, struct mbuf *mb);\nint  sip_send_conn(struct sip *sip, void *sock, enum sip_transp tp,\n\t\t   const struct sa *dst, char *host, struct mbuf *mb,\n\t\t   sip_conn_h *connh, void *arg);\nvoid sip_set_trace_handler(struct sip *sip, sip_trace_h *traceh);\n\n\n/* transport */\nint  sip_transp_add(struct sip *sip, enum sip_transp tp,\n\t\t    const struct sa *laddr, ...);\nint  sip_transp_add_sock(struct sip *sip, enum sip_transp tp,\n\t\t\t bool listen, const struct sa *laddr, ...);\nint  sip_transp_add_websock(struct sip *sip, enum sip_transp tp,\n\t\t\t    const struct sa *laddr,\n\t\t\t    bool server, const char *cert, struct tls *tls);\nint  sip_transp_add_ccert(struct sip *sip, const struct uri *uri,\n\t\t\t  const char *ccertfile);\nvoid sip_transp_flush(struct sip *sip);\nbool sip_transp_isladdr(const struct sip *sip, enum sip_transp tp,\n\t\t\tconst struct sa *laddr);\nconst char *sip_transp_name(enum sip_transp tp);\nconst char *sip_transp_param(enum sip_transp tp);\nenum sip_transp sip_transp_decode(const struct pl *pl);\nuint16_t sip_transp_port(enum sip_transp tp, uint16_t port);\nint  sip_transp_laddr(struct sip *sip, struct sa *laddr, enum sip_transp tp,\n\t\t      const struct sa *dst);\nint  sip_transp_set_default(struct sip *sip, enum sip_transp tp);\nvoid sip_transp_rmladdr(struct sip *sip, const struct sa *laddr);\nint  sip_settos(struct sip *sip, uint8_t tos);\n\n\n/* request */\nint sip_request(struct sip_request **reqp, struct sip *sip, bool stateful,\n\t\tconst char *met, int metl, const char *uri, int uril,\n\t\tconst struct uri *route, struct mbuf *mb, size_t sortkey,\n\t\tsip_send_h *sendh, sip_resp_h *resph, void *arg);\nint sip_requestf(struct sip_request **reqp, struct sip *sip, bool stateful,\n\t\t const char *met, const char *uri, const struct uri *route,\n\t\t struct sip_auth *auth, sip_send_h *sendh, sip_resp_h *resph,\n\t\t void *arg, const char *fmt, ...);\nint sip_drequestf(struct sip_request **reqp, struct sip *sip, bool stateful,\n\t\t  const char *met, struct sip_dialog *dlg, uint32_t cseq,\n\t\t  struct sip_auth *auth, sip_send_h *sendh, sip_resp_h *resph,\n\t\t  void *arg, const char *fmt, ...);\nvoid sip_request_cancel(struct sip_request *req);\nbool sip_request_loops(struct sip_loopstate *ls, uint16_t scode);\nvoid sip_loopstate_reset(struct sip_loopstate *ls);\nbool sip_request_provrecv(const struct sip_request *req);\n\n\n/* reply */\nint  sip_strans_alloc(struct sip_strans **stp, struct sip *sip,\n\t\t      const struct sip_msg *msg, sip_cancel_h *cancelh,\n\t\t      void *arg);\nint  sip_strans_reply(struct sip_strans **stp, struct sip *sip,\n\t\t      const struct sip_msg *msg, const struct sa *dst,\n\t\t      uint16_t scode, struct mbuf *mb);\nint  sip_treplyf(struct sip_strans **stp, struct mbuf **mbp, struct sip *sip,\n\t\t const struct sip_msg *msg, bool rec_route, uint16_t scode,\n\t\t const char *reason, const char *fmt, ...);\nint  sip_treply(struct sip_strans **stp, struct sip *sip,\n\t\tconst struct sip_msg *msg, uint16_t scode, const char *reason);\nint  sip_replyf(struct sip *sip, const struct sip_msg *msg, uint16_t scode,\n\t\tconst char *reason, const char *fmt, ...);\nint  sip_reply(struct sip *sip, const struct sip_msg *msg, uint16_t scode,\n\t       const char *reason);\nvoid sip_reply_addr(struct sa *addr, const struct sip_msg *msg, bool rport);\nconst struct sip_msg *sip_strans_cancel_msg(struct sip_strans *st);\n\n/* auth */\nint  sip_auth_authenticate(struct sip_auth *auth, const struct sip_msg *msg);\nint  sip_auth_alloc(struct sip_auth **authp, sip_auth_h *authh,\n\t\t    void *arg, bool ref);\n\nvoid sip_auth_reset(struct sip_auth *auth);\nint  sip_auth_encode(struct mbuf *mb, struct sip_auth *auth, const char *met,\n\t\t     const char *uri);\n\n\n/* contact */\nvoid sip_contact_set(struct sip_contact *contact, const char *uri,\n\t\t     const struct sa *addr, enum sip_transp tp);\nint  sip_contact_print(struct re_printf *pf,\n\t\t       const struct sip_contact *contact);\n\n\n/* dialog */\nint  sip_dialog_alloc(struct sip_dialog **dlgp,\n\t\t      const char *uri, const char *to_uri,\n\t\t      const char *from_name, const char *from_uri,\n\t\t      const char *routev[], uint32_t routec);\nint  sip_dialog_accept(struct sip_dialog **dlgp, const struct sip_msg *msg);\nint  sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg);\nint  sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg,\n\t\t     const struct sip_msg *msg);\nint  sip_dialog_update(struct sip_dialog *dlg, const struct sip_msg *msg);\nbool sip_dialog_rseq_valid(struct sip_dialog *dlg, const struct sip_msg *msg);\nconst char *sip_dialog_callid(const struct sip_dialog *dlg);\nint  sip_dialog_set_callid(struct sip_dialog *dlg, const char *callid);\nvoid sip_dialog_set_srcport(struct sip_dialog *dlg, uint16_t srcport);\nuint16_t sip_dialog_srcport(struct sip_dialog *dlg);\nconst char *sip_dialog_uri(const struct sip_dialog *dlg);\nconst char *sip_dialog_ltag(const struct sip_dialog *dlg);\nconst char *sip_dialog_rtag(const struct sip_dialog *dlg);\nuint32_t sip_dialog_lseq(const struct sip_dialog *dlg);\nuint32_t sip_dialog_lseqinv(const struct sip_dialog *dlg);\nenum sip_transp sip_dialog_tp(const struct sip_dialog *dlg);\nbool sip_dialog_established(const struct sip_dialog *dlg);\nbool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg);\nbool sip_dialog_cmp_half(const struct sip_dialog *dlg,\n\t\t\t const struct sip_msg *msg);\n\n\n/* msg */\nint sip_msg_decode(struct sip_msg **msgp, struct mbuf *mb);\nconst struct sip_hdr *sip_msg_hdr(const struct sip_msg *msg,\n\t\t\t\t  enum sip_hdrid id);\nconst struct sip_hdr *sip_msg_hdr_apply(const struct sip_msg *msg,\n\t\t\t\t\tbool fwd, enum sip_hdrid id,\n\t\t\t\t\tsip_hdr_h *h, void *arg);\nconst struct sip_hdr *sip_msg_xhdr(const struct sip_msg *msg,\n\t\t\t\t   const char *name);\nconst struct sip_hdr *sip_msg_xhdr_apply(const struct sip_msg *msg,\n\t\t\t\t\t bool fwd, const char *name,\n\t\t\t\t\t sip_hdr_h *h, void *arg);\nuint32_t sip_msg_hdr_count(const struct sip_msg *msg, enum sip_hdrid id);\nuint32_t sip_msg_xhdr_count(const struct sip_msg *msg, const char *name);\nbool sip_msg_hdr_has_value(const struct sip_msg *msg, enum sip_hdrid id,\n\t\t\t   const char *value);\nbool sip_msg_xhdr_has_value(const struct sip_msg *msg, const char *name,\n\t\t\t    const char *value);\nstruct tcp_conn *sip_msg_tcpconn(const struct sip_msg *msg);\nvoid sip_msg_dump(const struct sip_msg *msg);\n\nint sip_addr_decode(struct sip_addr *addr, const struct pl *pl);\nint sip_via_decode(struct sip_via *via, const struct pl *pl);\nint sip_cseq_decode(struct sip_cseq *cseq, const struct pl *pl);\nint sip_rack_decode(struct sip_rack *rack, const struct pl *pl);\n\n\n/* keepalive */\nint sip_keepalive_start(struct sip_keepalive **kap, struct sip *sip,\n\t\t\tconst struct sip_msg *msg, uint32_t interval,\n\t\t\tsip_keepalive_h *kah, void *arg);\n\n/* sip_conncfg */\nint sip_conncfg_set(struct sip *sip, const struct sa *paddr,\n\t\t    const struct sip_conncfg *conncfg);\n\n\n/* sip_uas_auth */\nint sip_uas_auth_gen(struct sip_uas_auth **authp, const struct sip_msg *msg,\n\t\t     const char *realm);\nint sip_uas_auth_print(struct re_printf *pf,\n\t\t       const struct sip_uas_auth *auth);\nint sip_uas_auth_check(struct sip_uas_auth *auth, const struct sip_msg *msg,\n\t\t       sip_uas_auth_h *authh, void *arg);\n"
  },
  {
    "path": "include/re_sipevent.h",
    "content": "/**\n * @file re_sipevent.h  SIP Event Framework\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/* Message Components */\n\nstruct sipevent_event {\n\tstruct pl event;\n\tstruct pl params;\n\tstruct pl id;\n};\n\nenum sipevent_subst {\n\tSIPEVENT_ACTIVE = 0,\n\tSIPEVENT_PENDING,\n\tSIPEVENT_TERMINATED,\n};\n\nenum sipevent_reason {\n\tSIPEVENT_DEACTIVATED = 0,\n\tSIPEVENT_PROBATION,\n\tSIPEVENT_REJECTED,\n\tSIPEVENT_TIMEOUT,\n\tSIPEVENT_GIVEUP,\n\tSIPEVENT_NORESOURCE,\n};\n\nstruct sipevent_substate {\n\tenum sipevent_subst state;\n\tenum sipevent_reason reason;\n\tstruct pl expires;\n\tstruct pl retry_after;\n\tstruct pl params;\n};\n\nint sipevent_event_decode(struct sipevent_event *se, const struct pl *pl);\nint sipevent_substate_decode(struct sipevent_substate *ss,\n\t\t\t     const struct pl *pl);\nconst char *sipevent_substate_name(enum sipevent_subst state);\nconst char *sipevent_reason_name(enum sipevent_reason reason);\n\n\n/* Listener Socket */\n\nstruct sipevent_sock;\n\nint sipevent_listen(struct sipevent_sock **sockp, struct sip *sip,\n\t\t    uint32_t htsize_not, uint32_t htsize_sub,\n\t\t    sip_msg_h *subh, void *arg);\n\n\n/* Notifier */\n\nstruct sipnot;\n\ntypedef void (sipnot_close_h)(int err, const struct sip_msg *msg, void *arg);\n\nint sipevent_accept(struct sipnot **notp, struct sipevent_sock *sock,\n\t\t    const struct sip_msg *msg, struct sip_dialog *dlg,\n\t\t    const struct sipevent_event *event,\n\t\t    uint16_t scode, const char *reason, uint32_t expires_min,\n\t\t    uint32_t expires_dfl, uint32_t expires_max,\n\t\t    const char *cuser, const char *ctype,\n\t\t    sip_auth_h *authh, void *aarg, bool aref,\n\t\t    sipnot_close_h *closeh, void *arg, const char *fmt, ...);\nint sipevent_notify(struct sipnot *sipnot, struct mbuf *mb,\n\t\t    enum sipevent_subst state, enum sipevent_reason reason,\n\t\t    uint32_t retry_after);\nint sipevent_notifyf(struct sipnot *sipnot, struct mbuf **mbp,\n\t\t     enum sipevent_subst state, enum sipevent_reason reason,\n\t\t     uint32_t retry_after, const char *fmt, ...);\n\n\n/* Subscriber */\n\nstruct sipsub;\n\ntypedef int  (sipsub_fork_h)(struct sipsub **subp, struct sipsub *osub,\n\t\t\t     const struct sip_msg *msg, void *arg);\ntypedef void (sipsub_notify_h)(struct sip *sip, const struct sip_msg *msg,\n\t\t\t       void *arg);\ntypedef void (sipsub_close_h)(int err, const struct sip_msg *msg,\n\t\t\t      const struct sipevent_substate *substate,\n\t\t\t      void *arg);\n\nint sipevent_subscribe(struct sipsub **subp, struct sipevent_sock *sock,\n\t\t       const char *uri, const char *from_name,\n\t\t       const char *from_uri, const char *event, const char *id,\n\t\t       uint32_t expires, const char *cuser,\n\t\t       const char *routev[], uint32_t routec,\n\t\t       sip_auth_h *authh, void *aarg, bool aref,\n\t\t       sipsub_fork_h *forkh, sipsub_notify_h *notifyh,\n\t\t       sipsub_close_h *closeh, void *arg,\n\t\t       const char *fmt, ...);\nint sipevent_dsubscribe(struct sipsub **subp, struct sipevent_sock *sock,\n\t\t\tstruct sip_dialog *dlg, const char *event,\n\t\t\tconst char *id, uint32_t expires, const char *cuser,\n\t\t\tsip_auth_h *authh, void *aarg, bool aref,\n\t\t\tsipsub_notify_h *notifyh, sipsub_close_h *closeh,\n\t\t\tvoid *arg, const char *fmt, ...);\n\nint sipevent_refer(struct sipsub **subp, struct sipevent_sock *sock,\n\t\t   const char *uri, const char *from_name,\n\t\t   const char *from_uri, const char *cuser,\n\t\t   const char *routev[], uint32_t routec,\n\t\t   sip_auth_h *authh, void *aarg, bool aref,\n\t\t   sipsub_fork_h *forkh, sipsub_notify_h *notifyh,\n\t\t   sipsub_close_h *closeh, void *arg,\n\t\t   const char *fmt, ...);\nint sipevent_drefer(struct sipsub **subp, struct sipevent_sock *sock,\n\t\t    struct sip_dialog *dlg, const char *cuser,\n\t\t    sip_auth_h *authh, void *aarg, bool aref,\n\t\t    sipsub_notify_h *notifyh, sipsub_close_h *closeh,\n\t\t    void *arg, const char *fmt, ...);\n\nint sipevent_fork(struct sipsub **subp, struct sipsub *osub,\n\t\t  const struct sip_msg *msg,\n\t\t  sip_auth_h *authh, void *aarg, bool aref,\n\t\t  sipsub_notify_h *notifyh, sipsub_close_h *closeh,\n\t\t  void *arg);\n"
  },
  {
    "path": "include/re_sipreg.h",
    "content": "/**\n * @file re_sipreg.h  SIP Registration\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\nstruct sipreg;\n\n\nvoid sipreg_unregister(struct sipreg *reg);\nint sipreg_alloc(struct sipreg **regp, struct sip *sip, const char *reg_uri,\n\t\t    const char *to_uri, const char *from_name,\n\t\t    const char *from_uri, uint32_t expires,\n\t\t    const char *cuser, const char *routev[], uint32_t routec,\n\t\t    int regid, sip_auth_h *authh, void *aarg, bool aref,\n\t\t    sip_resp_h *resph, void *arg,\n\t\t    const char *params, const char *fmt, ...);\nint sipreg_send(struct sipreg *reg);\n\nint sipreg_set_rwait(struct sipreg *reg, uint32_t rwait);\n\nconst struct sa *sipreg_laddr(const struct sipreg *reg);\n\nuint32_t sipreg_proxy_expires(const struct sipreg *reg);\nbool sipreg_registered(const struct sipreg *reg);\nbool sipreg_failed(const struct sipreg *reg);\nvoid sipreg_incfailc(struct sipreg *reg);\n\nint sipreg_set_fbregint(struct sipreg *reg, uint32_t fbregint);\nvoid sipreg_set_srcport(struct sipreg *reg, uint16_t srcport);\nint sipreg_set_contact_params(struct sipreg *reg, const char *cparams);\n"
  },
  {
    "path": "include/re_sipsess.h",
    "content": "/**\n * @file re_sipsess.h  SIP Session\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\nstruct sipsess_sock;\nstruct sipsess;\n\n/** SDP Negotiation state */\nenum sdp_neg_state {\n\tSDP_NEG_NONE = 0,\n\tSDP_NEG_LOCAL_OFFER,\t\t/**< SDP offer sent */\n\tSDP_NEG_REMOTE_OFFER,\t\t/**< SDP offer received */\n\tSDP_NEG_PREVIEW_ANSWER,\t\t/**< SDP preview answer sent */\n\tSDP_NEG_DONE\t\t\t/**< SDP negotiation done */\n};\n\n\ntypedef void (sipsess_conn_h)(const struct sip_msg *msg, void *arg);\ntypedef int  (sipsess_desc_h)(struct mbuf **descp, const struct sa *src,\n\t\t\t      const struct sa *dst, void *arg);\ntypedef int  (sipsess_offer_h)(struct mbuf **descp, const struct sip_msg *msg,\n\t\t\t       void *arg);\ntypedef int  (sipsess_answer_h)(const struct sip_msg *msg, void *arg);\ntypedef void (sipsess_progr_h)(const struct sip_msg *msg, void *arg);\ntypedef void (sipsess_estab_h)(const struct sip_msg *msg, void *arg);\ntypedef void (sipsess_info_h)(struct sip *sip, const struct sip_msg *msg,\n\t\t\t      void *arg);\ntypedef void (sipsess_refer_h)(struct sip *sip, const struct sip_msg *msg,\n\t\t\t       void *arg);\ntypedef void (sipsess_close_h)(int err, const struct sip_msg *msg, void *arg);\n\ntypedef void (sipsess_redirect_h)(const struct sip_msg *msg,\n\t\t\t\t  const char *uri, void *arg);\ntypedef void (sipsess_prack_h)(const struct sip_msg *msg, void *arg);\n\nint  sipsess_listen(struct sipsess_sock **sockp, struct sip *sip,\n\t\t    int htsize, sipsess_conn_h *connh, void *arg);\n\nint  sipsess_connect(struct sipsess **sessp, struct sipsess_sock *sock,\n\t\t     const char *to_uri, const char *from_name,\n\t\t     const char *from_uri, const char *cuser,\n\t\t     const char *routev[], uint32_t routec,\n\t\t     const char *ctype,\n\t\t     sip_auth_h *authh, void *aarg, bool aref,\n\t\t     const char *callid,\n\t\t     sipsess_desc_h *desch,\n\t\t     sipsess_offer_h *offerh, sipsess_answer_h *answerh,\n\t\t     sipsess_progr_h *progrh, sipsess_estab_h *estabh,\n\t\t     sipsess_info_h *infoh, sipsess_refer_h *referh,\n\t\t     sipsess_close_h *closeh, void *arg, const char *fmt, ...);\n\nint  sipsess_accept(struct sipsess **sessp, struct sipsess_sock *sock,\n\t\t    const struct sip_msg *msg, uint16_t scode,\n\t\t    const char *reason, enum rel100_mode rel100,\n\t\t    const char *cuser, const char *ctype,\n\t\t    struct mbuf *desc, sip_auth_h *authh, void *aarg,\n\t\t    bool aref, sipsess_offer_h *offerh,\n\t\t    sipsess_answer_h *answerh, sipsess_estab_h *estabh,\n\t\t    sipsess_info_h *infoh, sipsess_refer_h *referh,\n\t\t    sipsess_close_h *closeh, void *arg,\n\t\t    const char *fmt, ...);\n\nint  sipsess_set_redirect_handler(struct sipsess *sess,\n\t\t\t\t  sipsess_redirect_h *redirecth);\nint  sipsess_set_prack_handler(struct sipsess *sess, sipsess_prack_h *prackh);\n\nint  sipsess_progress(struct sipsess *sess, uint16_t scode,\n\t\t      const char *reason, enum rel100_mode rel100,\n\t\t      struct mbuf *desc, const char *fmt, ...);\nint  sipsess_answer(struct sipsess *sess, uint16_t scode, const char *reason,\n\t\t    struct mbuf *desc, const char *fmt, ...);\nint  sipsess_reject(struct sipsess *sess, uint16_t scode, const char *reason,\n\t\t    const char *fmt, ...);\nint  sipsess_modify(struct sipsess *sess, struct mbuf *desc);\nint  sipsess_info(struct sipsess *sess, const char *ctype, struct mbuf *body,\n\t\t  sip_resp_h *resph, void *arg);\nint  sipsess_set_close_headers(struct sipsess *sess, const char *hdrs, ...);\nbool sipsess_awaiting_prack(const struct sipsess *sess);\nbool sipsess_refresh_allowed(const struct sipsess *sess);\nvoid sipsess_close_all(struct sipsess_sock *sock);\nstruct sip_dialog *sipsess_dialog(const struct sipsess *sess);\nvoid sipsess_abort(struct sipsess *sess);\nbool sipsess_ack_pending(const struct sipsess *sess);\nconst struct sip_msg *sipsess_msg(const struct sipsess *sess);\nenum sdp_neg_state sipsess_sdp_neg_state(const struct sipsess *sess);\n"
  },
  {
    "path": "include/re_srtp.h",
    "content": "/**\n * @file re_srtp.h Secure Real-time Transport Protocol (SRTP)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nenum srtp_suite {\n\tSRTP_AES_CM_128_HMAC_SHA1_32,\n\tSRTP_AES_CM_128_HMAC_SHA1_80,\n\tSRTP_AES_256_CM_HMAC_SHA1_32,\n\tSRTP_AES_256_CM_HMAC_SHA1_80,\n\tSRTP_AES_128_GCM,\n\tSRTP_AES_256_GCM,\n};\n\nenum srtp_flags {\n\tSRTP_UNENCRYPTED_SRTCP = 1<<1,\n};\n\nstruct srtp;\n\nint srtp_alloc(struct srtp **srtpp, enum srtp_suite suite,\n\t       const uint8_t *key, size_t key_bytes, int flags);\nint srtp_encrypt(struct srtp *srtp, struct mbuf *mb);\nint srtp_decrypt(struct srtp *srtp, struct mbuf *mb);\nint srtcp_encrypt(struct srtp *srtp, struct mbuf *mb);\nint srtcp_decrypt(struct srtp *srtp, struct mbuf *mb);\n\nconst char *srtp_suite_name(enum srtp_suite suite);\n"
  },
  {
    "path": "include/re_stun.h",
    "content": "/**\n * @file re_stun.h  Session Traversal Utilities for (NAT) (STUN)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** STUN Protocol values */\nenum {\n\tSTUN_PORT        = 3478,   /**< STUN Port number */\n\tSTUNS_PORT       = 5349,   /**< STUNS Port number                    */\n\tSTUN_HEADER_SIZE = 20,     /**< Number of bytes in header            */\n\tSTUN_ATTR_HEADER_SIZE = 4, /**< Size of attribute header             */\n\tSTUN_TID_SIZE    = 12,     /**< Number of bytes in transaction ID    */\n\tSTUN_DEFAULT_RTO = 500,    /**< Default Retrans Timeout in [ms]      */\n\tSTUN_DEFAULT_RC  = 7,      /**< Default number of retransmits        */\n\tSTUN_DEFAULT_RM  = 16,     /**< Wait time after last request is sent */\n\tSTUN_DEFAULT_TI  = 39500   /**< Reliable timeout */\n};\n\n/** STUN Address Family */\nenum stun_af {\n\tSTUN_AF_IPv4 = 0x01,  /**< IPv4 Address Family */\n\tSTUN_AF_IPv6 = 0x02   /**< IPv6 Address Family */\n};\n\n/** STUN Transport */\nenum stun_transp {\n\tSTUN_TRANSP_UDP = IPPROTO_UDP, /**< UDP-transport (struct udp_sock)  */\n\tSTUN_TRANSP_TCP = IPPROTO_TCP, /**< TCP-transport (struct tcp_conn)  */\n\tSTUN_TRANSP_DTLS,              /**< DTLS-transport (struct tls_conn) */\n};\n\n/** STUN Methods */\nenum stun_method {\n\tSTUN_METHOD_BINDING    = 0x001,\n\tSTUN_METHOD_ALLOCATE   = 0x003,\n\tSTUN_METHOD_REFRESH    = 0x004,\n\tSTUN_METHOD_SEND       = 0x006,\n\tSTUN_METHOD_DATA       = 0x007,\n\tSTUN_METHOD_CREATEPERM = 0x008,\n\tSTUN_METHOD_CHANBIND   = 0x009,\n};\n\n/** STUN Message class */\nenum stun_msg_class {\n\tSTUN_CLASS_REQUEST      = 0x0,  /**< STUN Request          */\n\tSTUN_CLASS_INDICATION   = 0x1,  /**< STUN Indication       */\n\tSTUN_CLASS_SUCCESS_RESP = 0x2,  /**< STUN Success Response */\n\tSTUN_CLASS_ERROR_RESP   = 0x3   /**< STUN Error Response   */\n};\n\n/** STUN Attributes */\nenum stun_attrib {\n\t/* Comprehension-required range (0x0000-0x7FFF) */\n\tSTUN_ATTR_MAPPED_ADDR        = 0x0001,\n\tSTUN_ATTR_CHANGE_REQ         = 0x0003,\n\tSTUN_ATTR_USERNAME           = 0x0006,\n\tSTUN_ATTR_MSG_INTEGRITY      = 0x0008,\n\tSTUN_ATTR_ERR_CODE           = 0x0009,\n\tSTUN_ATTR_UNKNOWN_ATTR       = 0x000a,\n\tSTUN_ATTR_CHANNEL_NUMBER     = 0x000c,\n\tSTUN_ATTR_LIFETIME           = 0x000d,\n\tSTUN_ATTR_XOR_PEER_ADDR      = 0x0012,\n\tSTUN_ATTR_DATA               = 0x0013,\n\tSTUN_ATTR_REALM              = 0x0014,\n\tSTUN_ATTR_NONCE              = 0x0015,\n\tSTUN_ATTR_XOR_RELAY_ADDR     = 0x0016,\n\tSTUN_ATTR_REQ_ADDR_FAMILY    = 0x0017,\n\tSTUN_ATTR_EVEN_PORT          = 0x0018,\n\tSTUN_ATTR_REQ_TRANSPORT      = 0x0019,\n\tSTUN_ATTR_DONT_FRAGMENT      = 0x001a,\n\tSTUN_ATTR_XOR_MAPPED_ADDR    = 0x0020,\n\tSTUN_ATTR_RSV_TOKEN          = 0x0022,\n\tSTUN_ATTR_PRIORITY           = 0x0024,\n\tSTUN_ATTR_USE_CAND           = 0x0025,\n\tSTUN_ATTR_RESP_PORT          = 0x0027,\n\n\t/* Comprehension-optional range (0x8000-0xFFFF) */\n\tSTUN_ATTR_SOFTWARE           = 0x8022,\n\tSTUN_ATTR_ALT_SERVER         = 0x8023,\n\tSTUN_ATTR_FINGERPRINT        = 0x8028,\n\tSTUN_ATTR_CONTROLLED         = 0x8029,\n\tSTUN_ATTR_CONTROLLING        = 0x802a,\n\tSTUN_ATTR_RESP_ORIGIN        = 0x802b,\n\tSTUN_ATTR_OTHER_ADDR         = 0x802c,\n};\n\n\nstruct stun_change_req {\n\tbool ip;\n\tbool port;\n};\n\nstruct stun_errcode {\n\tuint16_t code;\n\tchar *reason;\n};\n\nstruct stun_unknown_attr {\n\tuint16_t typev[8];\n\tuint32_t typec;\n};\n\nstruct stun_even_port {\n\tbool r;\n};\n\n/** Defines a STUN attribute */\nstruct stun_attr {\n\tstruct le le;\n\tuint16_t type;\n\tunion {\n\t\t/* generic types */\n\t\tstruct sa sa;\n\t\tchar *str;\n\t\tuint64_t uint64;\n\t\tuint32_t uint32;\n\t\tuint16_t uint16;\n\t\tuint8_t uint8;\n\t\tstruct mbuf mb;\n\n\t\t/* actual attributes */\n\t\tstruct sa mapped_addr;\n\t\tstruct stun_change_req change_req;\n\t\tchar *username;\n\t\tuint8_t msg_integrity[20];\n\t\tstruct stun_errcode err_code;\n\t\tstruct stun_unknown_attr unknown_attr;\n\t\tuint16_t channel_number;\n\t\tuint32_t lifetime;\n\t\tstruct sa xor_peer_addr;\n\t\tstruct mbuf data;\n\t\tchar *realm;\n\t\tchar *nonce;\n\t\tstruct sa xor_relay_addr;\n\t\tuint8_t req_addr_family;\n\t\tstruct stun_even_port even_port;\n\t\tuint8_t req_transport;\n\t\tstruct sa xor_mapped_addr;\n\t\tuint64_t rsv_token;\n\t\tuint32_t priority;\n\t\tuint16_t resp_port;\n\t\tchar *software;\n\t\tstruct sa alt_server;\n\t\tuint32_t fingerprint;\n\t\tuint64_t controlled;\n\t\tuint64_t controlling;\n\t\tstruct sa resp_origin;\n\t\tstruct sa other_addr;\n\t} v;\n};\n\n\n/** STUN Configuration */\nstruct stun_conf {\n\tuint32_t rto;  /**< RTO Retransmission TimeOut [ms]        */\n\tuint32_t rc;   /**< Rc Retransmission count (default 7)    */\n\tuint32_t rm;   /**< Rm Max retransmissions (default 16)    */\n\tuint32_t ti;   /**< Ti Timeout for reliable transport [ms] */\n\tuint8_t tos;   /**< Type-of-service field                  */\n};\n\n\nextern const char *stun_software;\nstruct stun;\nstruct stun_msg;\nstruct stun_ctrans;\n\ntypedef void(stun_resp_h)(int err, uint16_t scode, const char *reason,\n\t\t\t  const struct stun_msg *msg, void *arg);\ntypedef void(stun_ind_h)(struct stun_msg *msg, void *arg);\ntypedef bool(stun_attr_h)(const struct stun_attr *attr, void *arg);\n\nint  stun_alloc(struct stun **stunp, const struct stun_conf *conf,\n\t\tstun_ind_h *indh, void *arg);\nstruct stun_conf *stun_conf(struct stun *stun);\nint  stun_send(int proto, void *sock, const struct sa *dst, struct mbuf *mb);\nint  stun_recv(struct stun *stun, struct mbuf *mb);\nint  stun_ctrans_recv(struct stun *stun, const struct stun_msg *msg,\n\t\t      const struct stun_unknown_attr *ua);\nstruct re_printf;\nint  stun_debug(struct re_printf *pf, const struct stun *stun);\n\nint  stun_request(struct stun_ctrans **ctp, struct stun *stun, int proto,\n\t\t  void *sock, const struct sa *dst, size_t presz,\n\t\t  uint16_t method, const uint8_t *key, size_t keylen, bool fp,\n\t\t  stun_resp_h *resph, void *arg, uint32_t attrc, ...);\nint  stun_reply(int proto, void *sock, const struct sa *dst, size_t presz,\n\t\tconst struct stun_msg *req, const uint8_t *key,\n\t\tsize_t keylen, bool fp, uint32_t attrc, ...);\nint  stun_ereply(int proto, void *sock, const struct sa *dst, size_t presz,\n\t\t const struct stun_msg *req, uint16_t scode,\n\t\t const char *reason, const uint8_t *key, size_t keylen,\n\t\t bool fp, uint32_t attrc, ...);\nint  stun_indication(int proto, void *sock, const struct sa *dst, size_t presz,\n\t\t     uint16_t method, const uint8_t *key, size_t keylen,\n\t\t     bool fp, uint32_t attrc, ...);\n\nint  stun_msg_vencode(struct mbuf *mb, uint16_t method, uint8_t cls,\n\t\t      const uint8_t *tid, const struct stun_errcode *ec,\n\t\t      const uint8_t *key, size_t keylen, bool fp,\n\t\t      uint8_t padding, uint32_t attrc, va_list ap);\nint  stun_msg_encode(struct mbuf *mb, uint16_t method, uint8_t cls,\n\t\t     const uint8_t *tid, const struct stun_errcode *ec,\n\t\t     const uint8_t *key, size_t keylen, bool fp,\n\t\t     uint8_t padding, uint32_t attrc, ...);\nint  stun_msg_decode(struct stun_msg **msgpp, struct mbuf *mb,\n\t\t     struct stun_unknown_attr *ua);\nuint16_t stun_msg_type(const struct stun_msg *msg);\nuint16_t stun_msg_class(const struct stun_msg *msg);\nuint16_t stun_msg_method(const struct stun_msg *msg);\nbool stun_msg_mcookie(const struct stun_msg *msg);\nconst uint8_t *stun_msg_tid(const struct stun_msg *msg);\nstruct stun_attr *stun_msg_attr(const struct stun_msg *msg, uint16_t type);\nstruct stun_attr *stun_msg_attr_apply(const struct stun_msg *msg,\n\t\t\t\t      stun_attr_h *h, void *arg);\nint  stun_msg_chk_mi(const struct stun_msg *msg, const uint8_t *key,\n\t\t     size_t keylen);\nint  stun_msg_chk_fingerprint(const struct stun_msg *msg);\nvoid stun_msg_dump(const struct stun_msg *msg);\n\nconst char *stun_class_name(uint16_t cls);\nconst char *stun_method_name(uint16_t method);\nconst char *stun_attr_name(uint16_t type);\nconst char *stun_transp_name(enum stun_transp tp);\nvoid stun_generate_tid(uint8_t tid[STUN_TID_SIZE]);\n\n\n/* DNS Discovery of a STUN Server */\nextern const char *stun_proto_udp;\nextern const char *stun_proto_tcp;\n\nextern const char *stun_usage_binding;\nextern const char *stuns_usage_binding;\nextern const char *stun_usage_relay;\nextern const char *stuns_usage_relay;\n\n\n/**\n * Defines the STUN Server Discovery handler\n *\n * @param err Errorcode\n * @param srv IP Address and port of STUN Server\n * @param arg Handler argument\n */\ntypedef void (stun_dns_h)(int err, const struct sa *srv, void *arg);\n\nstruct stun_dns;\nstruct dnsc;\nint  stun_server_discover(struct stun_dns **dnsp, struct dnsc *dnsc,\n\t\t\t  const char *service, const char *proto,\n\t\t\t  int af, const char *domain, uint16_t port,\n\t\t\t  stun_dns_h *dnsh, void *arg);\n\n\n/* NAT Keepalives */\nstruct stun_keepalive;\n\n/**\n * Defines the STUN Keepalive Mapped-Address handler\n *\n * @param err Errorcode\n * @param map Mapped Address\n * @param arg Handler argument\n */\ntypedef void (stun_mapped_addr_h)(int err, const struct sa *map, void *arg);\n\n\nint  stun_keepalive_alloc(struct stun_keepalive **skap,\n\t\t\t  int proto, void *sock, int layer,\n\t\t\t  const struct sa *dst, const struct stun_conf *conf,\n\t\t\t  stun_mapped_addr_h *mah, void *arg);\nvoid stun_keepalive_enable(struct stun_keepalive *ska, uint32_t interval);\n\n\n/* STUN Reason Phrase */\nextern const char *stun_reason_300;\nextern const char *stun_reason_400;\nextern const char *stun_reason_401;\nextern const char *stun_reason_403;\nextern const char *stun_reason_420;\nextern const char *stun_reason_437;\nextern const char *stun_reason_438;\nextern const char *stun_reason_440;\nextern const char *stun_reason_441;\nextern const char *stun_reason_442;\nextern const char *stun_reason_443;\nextern const char *stun_reason_486;\nextern const char *stun_reason_500;\nextern const char *stun_reason_508;\n"
  },
  {
    "path": "include/re_sys.h",
    "content": "/**\n * @file re_sys.h  Interface to system module\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdio.h>\n\n#ifndef RE_VERSION\n#define RE_VERSION \"?\"\n#endif\n\n/**\n * @def ARCH\n *\n * Architecture\n */\n#ifndef ARCH\n#define ARCH \"?\"\n#endif\n\n/**\n * @def OS\n *\n * Operating System\n */\n#ifndef OS\n#ifdef WIN32\n#define OS \"win32\"\n#else\n#define OS \"?\"\n#endif\n#endif\n\nstruct re_printf;\nstruct mbuf;\n\nint  sys_kernel_get(struct re_printf *pf, void *unused);\nint  sys_build_get(struct re_printf *pf, void *unused);\nconst char *sys_arch_get(void);\nconst char *sys_os_get(void);\nconst char *sys_libre_version_get(void);\nconst char *sys_username(void);\nint sys_getenv(char **env, const char *name);\nint sys_coredump_set(bool enable);\nint sys_daemon(void);\nvoid sys_usleep(unsigned int us);\n\nstatic inline void sys_msleep(unsigned int ms)\n{\n\tsys_usleep(ms * 1000);\n}\n\n\nuint16_t sys_htols(uint16_t v);\nuint32_t sys_htoll(uint32_t v);\nuint16_t sys_ltohs(uint16_t v);\nuint32_t sys_ltohl(uint32_t v);\nuint64_t sys_htonll(uint64_t v);\nuint64_t sys_ntohll(uint64_t v);\n\n\n/* Random */\nuint16_t rand_u16(void);\nuint32_t rand_u32(void);\nuint64_t rand_u64(void);\nchar     rand_char(void);\nvoid     rand_str(char *str, size_t size);\nvoid     rand_bytes(uint8_t *p, size_t size);\n\n\n/* File-System */\nint  fs_mkdir(const char *path, uint16_t mode);\nint  fs_gethome(char *path, size_t sz);\nbool fs_isdir(const char *path);\nbool fs_isfile(const char *file);\nint  fs_fopen(FILE **fp, const char *file, const char *mode);\nint  fs_fread(struct mbuf **mbp, const char *path);\n\nvoid fs_stdio_hide(void);\nvoid fs_stdio_restore(void);\n"
  },
  {
    "path": "include/re_tcp.h",
    "content": "/**\n * @file re_tcp.h  Interface to Transport Control Protocol\n *\n * Copyright (C) 2010 Creytiv.com\n */\nstruct sa;\nstruct tcp_sock;\nstruct tcp_conn;\n\n\n/**\n * Defines the incoming TCP connection handler\n *\n * @param peer Network address of peer\n * @param arg  Handler argument\n */\ntypedef void (tcp_conn_h)(const struct sa *peer, void *arg);\n\n/**\n * Defines the TCP connection established handler\n *\n * @param arg Handler argument\n */\ntypedef void (tcp_estab_h)(void *arg);\n\n/**\n * Defines the TCP connection data send handler\n *\n * @param arg Handler argument\n */\ntypedef void (tcp_send_h)(void *arg);\n\n/**\n * Defines the TCP connection data receive handler\n *\n * @param mb  Buffer with data\n * @param arg Handler argument\n */\ntypedef void (tcp_recv_h)(struct mbuf *mb, void *arg);\n\n/**\n * Defines the TCP connection close handler\n *\n * @param err Error code\n * @param arg Handler argument\n */\ntypedef void (tcp_close_h)(int err, void *arg);\n\n\n/* TCP Socket */\nint  tcp_sock_alloc(struct tcp_sock **tsp, const struct sa *local,\n\t\t    tcp_conn_h *ch, void *arg);\nstruct tcp_sock *tcp_sock_dup(struct tcp_sock *tso);\nint  tcp_sock_bind(struct tcp_sock *ts, const struct sa *local);\nint  tcp_sock_listen(struct tcp_sock *ts, int backlog);\nint  tcp_accept(struct tcp_conn **tcp, struct tcp_sock *ts, tcp_estab_h *eh,\n\t\ttcp_recv_h *rh, tcp_close_h *ch, void *arg);\nvoid tcp_reject(struct tcp_sock *ts);\nint  tcp_sock_local_get(const struct tcp_sock *ts, struct sa *local);\nint  tcp_settos(struct tcp_sock *ts, uint32_t tos);\nint  tcp_conn_settos(struct tcp_conn *tc, uint32_t tos);\n\n\n/* TCP Connection */\nint tcp_sock_alloc_fd(struct tcp_sock **tsp, re_sock_t fd, tcp_conn_h *ch,\n\t\t      void *arg);\nint  tcp_conn_alloc(struct tcp_conn **tcp, const struct sa *peer,\n\t\t    tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch,\n\t\t    void *arg);\nint  tcp_conn_bind(struct tcp_conn *tc, const struct sa *local);\nint  tcp_conn_connect(struct tcp_conn *tc, const struct sa *peer);\nint  tcp_send(struct tcp_conn *tc, struct mbuf *mb);\nint  tcp_set_send(struct tcp_conn *tc, tcp_send_h *sendh);\nvoid tcp_set_handlers(struct tcp_conn *tc, tcp_estab_h *eh, tcp_recv_h *rh,\n\t\t      tcp_close_h *ch, void *arg);\nvoid tcp_conn_rxsz_set(struct tcp_conn *tc, size_t rxsz);\nvoid tcp_conn_txqsz_set(struct tcp_conn *tc, size_t txqsz);\nint  tcp_conn_local_get(const struct tcp_conn *tc, struct sa *local);\nint  tcp_conn_peer_get(const struct tcp_conn *tc, struct sa *peer);\nsize_t tcp_conn_txqsz(const struct tcp_conn *tc);\n\n\n/* High-level API */\nint  tcp_listen(struct tcp_sock **tsp, const struct sa *local,\n\t\ttcp_conn_h *ch, void *arg);\nint  tcp_connect(struct tcp_conn **tcp, const struct sa *peer,\n\t\t tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg);\nint  tcp_connect_bind(struct tcp_conn **tcp, const struct sa *peer,\n\t\ttcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch,\n\t\tconst struct sa *local, void *arg);\nint  tcp_local_get(const struct tcp_sock *ts, struct sa *local);\n\n\n/* Helper API */\ntypedef bool (tcp_helper_estab_h)(int *err, bool active, void *arg);\ntypedef bool (tcp_helper_send_h)(int *err, struct mbuf *mb, void *arg);\ntypedef bool (tcp_helper_recv_h)(int *err, struct mbuf *mb, bool *estab,\n\t\t\t\t void *arg);\n\nstruct tcp_helper;\n\n\nint tcp_register_helper(struct tcp_helper **thp, struct tcp_conn *tc,\n\t\t\tint layer,\n\t\t\ttcp_helper_estab_h *eh, tcp_helper_send_h *sh,\n\t\t\ttcp_helper_recv_h *rh, void *arg);\nint tcp_send_helper(struct tcp_conn *tc, struct mbuf *mb,\n\t\t    struct tcp_helper *th);\nbool tcp_sendq_used(struct tcp_conn *tc);\n"
  },
  {
    "path": "include/re_telev.h",
    "content": "/**\n * @file re_telev.h  Interface to Telephony Events (RFC 4733)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\nenum {\n\tTELEV_PTIME = 50,\n\tTELEV_SRATE = 8000\n};\n\nstruct telev;\n\nextern const char telev_rtpfmt[];\n\nint telev_alloc(struct telev **tp, uint32_t ptime);\nint telev_set_srate(struct telev *tel, uint32_t srate);\nint telev_send(struct telev *tel, int event, bool end);\nint telev_recv(struct telev *tel, struct mbuf *mb, int *event, bool *end);\nint telev_poll(struct telev *tel, bool *marker, struct mbuf *mb);\nbool telev_is_empty(const struct telev *tel);\n\nint telev_digit2code(int digit);\nint telev_code2digit(int code);\n"
  },
  {
    "path": "include/re_thread.h",
    "content": "/**\n * @file re_thread.h  Thread support\n *\n * Inspired by C11 thread support this provides a cross platform interfaces to\n * thread, mutex and condition handling (C11, POSIX and Windows Threads).\n *\n * Preferred order:\n *\n * - C11 threads (glibc>=2.28, musl, FreeBSD>=10)\n * - Windows Thread API\n * - POSIX PTHREAD (Linux/UNIX)\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n\n#ifndef RE_H_THREAD__\n#define RE_H_THREAD__\n\n#if defined(HAVE_THREADS)\n#include <threads.h>\n\n#else\n\n#if defined(WIN32)\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\n\nstruct thrd_win32 {\n\tHANDLE hdl;\n\tDWORD id;\n};\n#define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT\ntypedef INIT_ONCE once_flag;\ntypedef struct thrd_win32 thrd_t;\ntypedef CONDITION_VARIABLE cnd_t;\ntypedef CRITICAL_SECTION mtx_t;\ntypedef DWORD tss_t;\n\n#else\n\n#include <pthread.h>\n#include <time.h>\n#ifndef ONCE_FLAG_INIT\n#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT\ntypedef pthread_once_t once_flag;\n#endif\ntypedef pthread_t thrd_t;\ntypedef pthread_cond_t cnd_t;\ntypedef pthread_mutex_t mtx_t;\ntypedef pthread_key_t tss_t;\n\n#endif\n\nenum { mtx_plain = 0, mtx_try = 1, mtx_timed = 2, mtx_recursive = 4 };\n\n/* Exit and error codes.  */\nenum {\n\tthrd_success  = 0,\n\tthrd_busy     = 1,\n\tthrd_error    = 2,\n\tthrd_nomem    = 3,\n\tthrd_timedout = 4\n};\n\ntypedef void (*tss_dtor_t)(void *);\ntypedef int (*thrd_start_t)(void *);\n\n\n/******************************************************************************\n * Thread functions\n *****************************************************************************/\n\n/**\n * Creates a new thread\n *\n * @param thr   Pointer to new thread\n * @param func  Function to execute\n * @param arg   Argument to pass to the function\n *\n * @return thrd_success on success, otherwise thrd_error\n */\nint thrd_create(thrd_t *thr, thrd_start_t func, void *arg);\n\n\n/**\n * Checks whether `lhs` and `rhs` refer to the same thread.\n *\n * @param lhs  Left hand side thread\n * @param rhs  Right hand side thread\n *\n * @return Non-zero value if lhs and rhs refer to the same value, 0 otherwise.\n */\nint thrd_equal(thrd_t lhs, thrd_t rhs);\n\n\n/**\n * Return the identifier of the calling thread.\n *\n * @return Current thread\n */\nthrd_t thrd_current(void);\n\n\n/**\n * Detaches the thread identified by `thr` from the current environment.\n *\n * @param thr  Thread\n *\n * @return thrd_success on success, otherwise thrd_error\n */\nint thrd_detach(thrd_t thr);\n\n\n/**\n * Blocks the current thread until the thread identified by `thr` finishes\n * execution\n *\n * @param thr  Thread\n * @param res  Result code location\n *\n * @return thrd_success on success, otherwise thrd_error\n */\nint thrd_join(thrd_t thr, int *res);\n\n\n/**\n * Calls a function exactly once\n *\n * @param flag  Pointer to object initialized by ONCE_FLAG_INIT\n * @param func  The function to execute only once\n */\nvoid call_once(once_flag *flag, void (*func)(void));\n\n\n/**\n * Terminates the calling thread\n *\n * @param res  The result value to return\n */\nvoid thrd_exit(int res);\n\n\n/******************************************************************************\n * Condition functions\n *****************************************************************************/\n\n/**\n * Initializes new condition variable\n *\n * @param cnd  Pointer to a variable to store condition variable\n *\n * @return thrd_success on success, otherwise thrd_error\n */\nint cnd_init(cnd_t *cnd);\n\n\n/**\n * Unblocks one thread blocked on a condition variable\n *\n * @param cnd  Pointer to condition variable\n *\n * @return thrd_success on success, otherwise thrd_error\n */\nint cnd_signal(cnd_t *cnd);\n\n\n/**\n * Unblocks all thrds blocked on a condition variable\n *\n * @param cnd  Pointer to condition variable\n *\n * @return thrd_success on success, otherwise thrd_error\n */\nint cnd_broadcast(cnd_t *cnd);\n\n\n/**\n * Blocks on a condition variable\n *\n * @param cnd   Pointer to condition variable\n * @param mtx   Lock mutex pointer\n *\n * @return thrd_success on success, otherwise thrd_error\n */\nint cnd_wait(cnd_t *cnd, mtx_t *mtx);\n\n\n/**\n * Blocks on a condition variable with timeout (TIME_UTC based)\n *\n * @param cnd     Pointer to condition variable\n * @param mtx     Lock mutex pointer\n * @param abstime Pointer to timeout time\n *\n * @return thrd_success on success, thrd_timedout if the timeout time\n * has been reached before the mutex is locked, otherwise thrd_error\n */\nint cnd_timedwait(cnd_t *cnd, mtx_t *mtx, const struct timespec *abstime);\n\n\n/**\n * Destroys the condition variable pointed to by cnd.\n * If there are thrds waiting on cnd, the behavior is undefined.\n *\n * @param cnd  pointer to the condition variable to destroy\n */\nvoid cnd_destroy(cnd_t *cnd);\n\n\n/******************************************************************************\n * Mutex functions\n *****************************************************************************/\n\n/**\n * Creates a new mutex object with type. The object pointed to by mutex is set\n * to an identifier of the newly created mutex.\n *\n * @param mtx   Pointer to the mutex to initialize\n * @param type  The type of the mutex\n *\n * @return thrd_success on success, otherwise thrd_error\n */\nint mtx_init(mtx_t *mtx, int type);\n\n\n/**\n * Blocks the current thread until the mutex pointed to by mutex is locked.\n * The behavior is undefined if the current thread has already locked the\n * mutex and the mutex is not recursive.\n *\n * @param mtx   Pointer to the mutex\n *\n * @return thrd_success on success, otherwise thrd_error\n */\nint mtx_lock(mtx_t *mtx);\n\n\n/**\n * Tries to lock the mutex pointed to by mutex without blocking.\n * Returns immediately if the mutex is already locked.\n *\n * @param mtx   Pointer to the mutex\n *\n * @return thrd_success on success, thrd_busy if already locked,\n * otherwise thrd_error\n */\nint mtx_trylock(mtx_t *mtx);\n\n\n/**\n * Unlocks the mutex pointed to by mutex.\n *\n * @param mtx   Pointer to the mutex\n *\n * @return thrd_success on success, otherwise thrd_error\n */\nint mtx_unlock(mtx_t *mtx);\n\n\n/**\n * Destroys the mutex pointed to by mutex.\n * If there are threads waiting on mutex, the behavior is undefined.\n *\n * @param mtx   Pointer to the mutex\n */\nvoid mtx_destroy(mtx_t *mtx);\n\n\n/******************************************************************************\n * Thread-local storage functions\n *****************************************************************************/\nint tss_create(tss_t *key, tss_dtor_t destructor);\nvoid *tss_get(tss_t key);\nint tss_set(tss_t key, void *val);\nvoid tss_delete(tss_t key);\n\n#endif /* C11 threads fallback */\n\n\n/******************************************************************************\n * Extra - non C11 helpers\n * (We avoid tss_ mtx_ cnd_ prefixes since these reserved for functions with\n * different return values)\n *****************************************************************************/\n\n/* Ideas: */\n/* int thread_prio(enum thrd_prio prio) */\n/* void thread_print(struct re_printf *pf, void *unused); */\n\n/**\n * Allocates and initializes a new mutex\n *\n * @param mtx   Pointer to new mutex\n *\n * @return 0 if success, otherwise errorcode\n */\nint mutex_alloc(mtx_t **mtx);\nint mutex_alloc_tp(mtx_t **mtx, int type);\n\n\n/**\n * Creates a new thread with name\n *\n * @param thr   Pointer to new thread\n * @param name  Unique name for a thread\n * @param func  Function to execute\n * @param arg   Argument to pass to the function\n *\n * @return 0 if success, otherwise errorcode\n */\nint thread_create_name(thrd_t *thr, const char *name, thrd_start_t func,\n\t\t     void *arg);\n\n#endif /* RE_H_THREAD__ */\n"
  },
  {
    "path": "include/re_tls.h",
    "content": "/**\n * @file re_tls.h  Interface to Transport Layer Security\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\nstruct tls;\nstruct tls_conn;\nstruct tcp_conn;\nstruct udp_sock;\n\n\n/** Defines the TLS method */\nenum tls_method {\n\tTLS_METHOD_TLS,\n\tTLS_METHOD_SSLV23,    /* deprecated - fallback to TLS_METHOD_TLS */\n\tTLS_METHOD_DTLS,      /* DTLS 1.0 and 1.2 */\n\tTLS_METHOD_DTLSV1,    /* deprecated - fallback to TLS_METHOD_DTLS */\n\tTLS_METHOD_DTLSV1_2,  /* deprecated - fallback to TLS_METHOD_DTLS */\n};\n\nenum tls_fingerprint {\n\tTLS_FINGERPRINT_SHA256,\n};\n\nenum tls_keytype {\n\tTLS_KEYTYPE_RSA,\n\tTLS_KEYTYPE_EC,\n};\n\nenum tls_resume_mode {\n\tTLS_RESUMPTION_NONE\t= 0,\n\tTLS_RESUMPTION_IDS\t= (1 << 0),\n\tTLS_RESUMPTION_TICKETS\t= (1 << 1),\n\tTLS_RESUMPTION_ALL\t= TLS_RESUMPTION_IDS | TLS_RESUMPTION_TICKETS,\n};\n\nstruct tls_conn_d {\n\tint (*verifyh) (int ok, void *arg);\n\tvoid *arg;\n};\n\nint tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile,\n\t      const char *pwd);\nint tls_add_ca(struct tls *tls, const char *cafile);\nint tls_add_cafile_path(struct tls *tls, const char *cafile,\n\tconst char *capath);\nint tls_add_capem(const struct tls *tls, const char *capem);\nint tls_add_crlpem(const struct tls *tls, const char *pem);\nint tls_set_selfsigned_ec(struct tls *tls, const char *cn,\n\tconst char *curve_n);\nint tls_set_certificate_pem(struct tls *tls, const char *cert, size_t len_cert,\n\t\t\t    const char *key, size_t len_key);\nint tls_set_certificate(struct tls *tls, const char *cert, size_t len);\nint tls_set_certificate_chain_pem(struct tls *tls, const char *chain,\n\t\t\t\t  size_t len_chain);\nint tls_set_certificate_chain(struct tls *tls, const char *path);\nvoid tls_set_verify_client_trust_all(struct tls *tls);\nint tls_set_verify_client_handler(struct tls_conn *tc, int depth,\n\tint (*verifyh) (int ok, void *arg), void *arg);\n\nint tls_set_srtp(struct tls *tls, const char *suites);\nint tls_fingerprint(const struct tls *tls, enum tls_fingerprint type,\n\t\t    uint8_t *md, size_t size);\n\nint tls_peer_fingerprint(const struct tls_conn *tc, enum tls_fingerprint type,\n\t\t\t uint8_t *md, size_t size);\nint tls_peer_common_name(const struct tls_conn *tc, char *cn, size_t size);\nint tls_set_verify_purpose(struct tls *tls, const char *purpose);\nint tls_peer_verify(const struct tls_conn *tc);\nint tls_srtp_keyinfo(const struct tls_conn *tc, enum srtp_suite *suite,\n\t\t     uint8_t *cli_key, size_t cli_key_size,\n\t\t     uint8_t *srv_key, size_t srv_key_size);\nconst char *tls_cipher_name(const struct tls_conn *tc);\nint tls_set_ciphers(struct tls *tls, const char *cipherv[], size_t count);\nint tls_set_verify_server(struct tls_conn *tc, const char *host);\nint tls_verify_client(struct tls_conn *tc);\n\nint tls_get_issuer(struct tls *tls, struct mbuf *mb);\nint tls_get_subject(struct tls *tls, struct mbuf *mb);\nvoid tls_disable_verify_server(struct tls *tls);\nvoid tls_enable_verify_client(struct tls *tls, bool enable);\nint tls_set_resumption(struct tls *tls, enum tls_resume_mode mode);\n\nint tls_set_min_proto_version(struct tls *tls, int version);\nint tls_set_max_proto_version(struct tls *tls, int version);\n\nint tls_set_session_reuse(struct tls *tls, int enabled);\nbool tls_get_session_reuse(const struct tls_conn *tc);\nint tls_reuse_session(const struct tls_conn *tc);\nbool tls_session_reused(const struct tls_conn *tc);\nint tls_update_sessions(const struct tls_conn *tc);\nvoid tls_set_posthandshake_auth(struct tls *tls, int value);\n\n/* TCP */\n\nint tls_conn_change_cert(struct tls_conn *tc, const char *file);\nint tls_start_tcp(struct tls_conn **ptc, struct tls *tls,\n\t\t  struct tcp_conn *tcp, int layer);\n\nint tls_verify_client_post_handshake(struct tls_conn *tc);\n\nconst struct tcp_conn *tls_get_tcp_conn(const struct tls_conn *tc);\n\n\n/* UDP (DTLS) */\n\ntypedef void (dtls_conn_h)(const struct sa *peer, void *arg);\ntypedef void (dtls_estab_h)(void *arg);\ntypedef void (dtls_recv_h)(struct mbuf *mb, void *arg);\ntypedef void (dtls_close_h)(int err, void *arg);\n\nstruct dtls_sock;\n\nint dtls_listen(struct dtls_sock **sockp, const struct sa *laddr,\n\t\tstruct udp_sock *us, uint32_t htsize, int layer,\n\t\tdtls_conn_h *connh, void *arg);\nstruct udp_sock *dtls_udp_sock(struct dtls_sock *sock);\nvoid dtls_set_mtu(struct dtls_sock *sock, size_t mtu);\nint dtls_connect(struct tls_conn **ptc, struct tls *tls,\n\t\t struct dtls_sock *sock, const struct sa *peer,\n\t\t dtls_estab_h *estabh, dtls_recv_h *recvh,\n\t\t dtls_close_h *closeh, void *arg);\nint dtls_accept(struct tls_conn **ptc, struct tls *tls,\n\t\tstruct dtls_sock *sock,\n\t\tdtls_estab_h *estabh, dtls_recv_h *recvh,\n\t\tdtls_close_h *closeh, void *arg);\nint dtls_send(struct tls_conn *tc, struct mbuf *mb);\nvoid dtls_set_handlers(struct tls_conn *tc, dtls_estab_h *estabh,\n\t\t       dtls_recv_h *recvh, dtls_close_h *closeh, void *arg);\nconst struct sa *dtls_peer(const struct tls_conn *tc);\nvoid dtls_set_peer(struct tls_conn *tc, const struct sa *peer);\nvoid dtls_recv_packet(struct dtls_sock *sock, const struct sa *src,\n\t\t      struct mbuf *mb);\nvoid dtls_set_single(struct dtls_sock *sock, bool single);\n\n\nstruct x509_st;\nstruct evp_pkey_st;\n\nint tls_set_certificate_openssl(struct tls *tls, struct x509_st *cert,\n\t\t\t\tstruct evp_pkey_st *pkey, bool up_ref);\nint tls_add_certf(struct tls *tls, const char *certf, const char *host);\n"
  },
  {
    "path": "include/re_tmr.h",
    "content": "/**\n * @file re_tmr.h  Interface to timer implementation\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#include \"re_thread.h\"\n#include \"re_atomic.h\"\n\n/**\n * Defines the timeout handler\n *\n * @param arg Handler argument\n */\ntypedef void (tmr_h)(void *arg);\n\nstruct tmrl;\n\n/** Defines a timer */\nstruct tmr {\n\tstruct le le;\t\t   /**< Linked list element */\n\tRE_ATOMIC uintptr_t llock; /**< List Mutex lock     */\n\ttmr_h *th;\t\t   /**< Timeout handler     */\n\tvoid *arg;\t\t   /**< Handler argument    */\n\tuint64_t jfs;\t\t   /**< Jiffies for timeout */\n\tconst char *file;\n\tint line;\n};\n\n#define TMR_INIT {.le = LE_INIT}\n\nint      tmrl_alloc(struct tmrl **tmrl);\nvoid     tmr_poll(struct tmrl *tmrl);\nuint64_t tmr_jiffies_usec(void);\nuint64_t tmr_jiffies(void);\nuint64_t tmr_jiffies_rt_usec(void);\nint      tmr_timespec_get(struct timespec *tp, uint64_t offset);\nuint64_t tmr_next_timeout(struct tmrl *tmrl);\nvoid     tmr_debug(void);\nint      tmr_status(struct re_printf *pf, void *unused);\n\nvoid     tmr_init(struct tmr *tmr);\nvoid     tmr_start_dbg(struct tmr *tmr, uint64_t delay, tmr_h *th, void *arg,\n\t\t   const char *file, int line);\nvoid     tmr_continue_dbg(struct tmr *tmr, uint64_t delay,\n                   tmr_h *th, void *arg,\n\t\t   const char *file, int line);\nuint32_t tmrl_count(struct tmrl *tmrl);\n\n\n/**\n * @def tmr_start(tmr, delay, th, arg)\n *\n * Start a timer\n *\n * @param tmr   Timer to start\n * @param delay Timer delay in [ms]\n * @param th    Timeout handler\n * @param arg   Handler argument\n */\n#define tmr_start(tmr, delay, th, arg)                                        \\\n\ttmr_start_dbg(tmr, delay, th, arg, __FILE__, __LINE__)\n\n/**\n * @def tmr_continue(tmr, delay, th, arg)\n *\n * Continue a previously started timer with exactly added delay\n *\n * @param tmr   Timer to start\n * @param delay Timer delay in [ms]\n * @param th    Timeout handler\n * @param arg   Handler argument\n */\n#define tmr_continue(tmr, delay, th, arg)                                     \\\n\ttmr_continue_dbg(tmr, delay, th, arg, __FILE__, __LINE__)\n\nvoid     tmr_cancel(struct tmr *tmr);\nuint64_t tmr_get_expire(const struct tmr *tmr);\n\n\n/**\n * Check if the timer is running\n *\n * @param tmr Timer to check\n *\n * @return true if running, false if not running\n */\nstatic inline bool tmr_isrunning(const struct tmr *tmr)\n{\n\treturn tmr ? NULL != tmr->th : false;\n}\n"
  },
  {
    "path": "include/re_trace.h",
    "content": "/**\n * @file re_trace.h RE_TRACE helpers\n * JSON traces (chrome://tracing)\n */\n\nstruct pl;\nstruct mbuf;\n\ntypedef enum {\n\tRE_TRACE_ARG_NONE,\n\tRE_TRACE_ARG_INT,\n\tRE_TRACE_ARG_STRING_CONST,\n\tRE_TRACE_ARG_STRING_COPY,\n} re_trace_arg_type;\n\nstruct re_trace_event_s {\n\tconst char *name;\n\tconst char *cat;\n\tstruct pl *id;\n\tuint64_t ts;\n\tint pid;\n\tunsigned long tid;\n\tchar ph;\n\tre_trace_arg_type arg_type;\n\tconst char *arg_name;\n\tunion {\n\t\tconst char *a_str;\n\t\tint a_int;\n\t} arg;\n};\n\ntypedef void(re_trace_line_h)(const struct re_trace_event_s *e,\n\t\t\t      struct mbuf *json);\n\n\nint re_trace_init(const char *json_file);\nint re_trace_close(void);\nint re_trace_flush(void);\nvoid re_trace_event(const char *cat, const char *name, char ph, struct pl *id,\n\t\t    re_trace_arg_type arg_type, const char *arg_name,\n\t\t    void *arg_value);\nvoid re_set_trace_line_h(re_trace_line_h *trace_h);\n\n#ifdef RE_TRACE_ENABLED\n\n#define RE_TRACE_BEGIN(c, n) \\\n\tre_trace_event(c, n, 'B', NULL, RE_TRACE_ARG_NONE, NULL, NULL)\n#define RE_TRACE_END(c, n) \\\n\tre_trace_event(c, n, 'E', NULL, RE_TRACE_ARG_NONE, NULL, NULL)\n\n#define RE_TRACE_ID_BEGIN(c, n, id) \\\n\tre_trace_event(c, n, 'B', id, RE_TRACE_ARG_NONE, NULL, NULL)\n#define RE_TRACE_ID_END(c, n, id) \\\n\tre_trace_event(c, n, 'E', id, RE_TRACE_ARG_NONE, NULL, NULL)\n\n#define RE_TRACE_INSTANT(c, n) \\\n\tre_trace_event(c, n, 'I', NULL, RE_TRACE_ARG_NONE, NULL, NULL)\n#define RE_TRACE_INSTANT_C(c, n, vname, str) \\\n\tre_trace_event(c, n, 'I', NULL, RE_TRACE_ARG_STRING_CONST, \\\n\tvname, (void *)(str))\n#define RE_TRACE_INSTANT_I(c, n, i) \\\n\tre_trace_event(c, n, 'I', NULL, RE_TRACE_ARG_INT, \\\n\tn, (void *)(intptr_t)i)\n\n#define RE_TRACE_ID_INSTANT(c, n, id) \\\n\tre_trace_event(c, n, 'I', id, RE_TRACE_ARG_NONE, NULL, NULL)\n#define RE_TRACE_ID_INSTANT_C(c, n, vname, str, id) \\\n\tre_trace_event(c, n, 'I', id, RE_TRACE_ARG_STRING_CONST, \\\n\tvname, (void *)(str))\n#define RE_TRACE_ID_INSTANT_I(c, n, i, id) \\\n\tre_trace_event(c, n, 'I', id, RE_TRACE_ARG_INT, \\\n\tn, (void *)(intptr_t)i)\n\n#define RE_TRACE_PROCESS_NAME(n) \\\n\tre_trace_event(\"\", \"process_name\", 'M', NULL, \\\n\tRE_TRACE_ARG_STRING_COPY, \\\n\t\"name\", (void *)(n))\n#define RE_TRACE_THREAD_NAME(n) \\\n\tre_trace_event(\"\", \"thread_name\", 'M', NULL, \\\n\tRE_TRACE_ARG_STRING_COPY, \\\n\t\"name\", (void *)(n))\n\n#else\n\n#define RE_TRACE_BEGIN(c, n)\n#define RE_TRACE_END(c, n)\n#define RE_TRACE_ID_BEGIN(c, n, id)\n#define RE_TRACE_ID_END(c, n, id)\n#define RE_TRACE_INSTANT(c, n)\n#define RE_TRACE_INSTANT_C(c, n, str)\n#define RE_TRACE_INSTANT_I(c, n, i)\n#define RE_TRACE_ID_INSTANT(c, n, id)\n#define RE_TRACE_ID_INSTANT_C(c, n, str, id)\n#define RE_TRACE_ID_INSTANT_I(c, n, i, id)\n#define RE_TRACE_PROCESS_NAME(n)\n#define RE_TRACE_THREAD_NAME(n)\n\n#endif\n\n#define RE_TRACE_BEGIN_FUNC() RE_TRACE_BEGIN(__FILE__, __func__)\n#define RE_TRACE_END_FUNC() RE_TRACE_END(__FILE__, __func__)\n"
  },
  {
    "path": "include/re_trice.h",
    "content": "/**\n * @file re_trice.h  Interface to Interactive Connectivity Establishment (ICE)\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n\n\n/** ICE Configuration */\nstruct trice_conf {\n\tbool debug;                    /**< Enable ICE debugging             */\n\tbool trace;                    /**< Enable tracing of Connectivity\n\t\t\t\t\t    checks                           */\n\tbool ansi;                     /**< Enable ANSI colors for debug\n\t\t\t\t\t   output                            */\n\tbool optimize_loopback_pairing;/**< Reduce candidate pairs when\n\t\t\t\t\t    using loopback addresses         */\n};\n\nstruct trice;\nstruct ice_lcand;\nstruct ice_candpair;\nstruct stun_conf;\n\n\n/**\n * Handler for receiving packets on candidate\n *\n * @param lcand Local candidate\n * @param proto Network protocol (UDP or TCP)\n * @param sock  Local socket (struct udp_sock or struct tcp_conn)\n * @param src   Source address\n * @param mb    Data packet\n * @param arg   Handler argument\n *\n * @return True if handled, False if not\n */\ntypedef bool (ice_cand_recv_h)(struct ice_lcand *lcand,\n\t\t\t       int proto, void *sock, const struct sa *src,\n\t\t\t       struct mbuf *mb, void *arg);\n\n\n/** Local candidate */\nstruct ice_lcand {\n\tstruct ice_cand_attr attr; /**< Base class (inheritance)           */\n\tstruct le le;              /**< List element                       */\n\tstruct sa base_addr;       /**< IP-address of \"base\" candidate     */\n\tstruct udp_sock *us;       /**< UDP socket                         */\n\tstruct udp_helper *uh;     /**< UDP helper to intercept packets    */\n\tstruct tcp_sock *ts;       /**< TCP for simultaneous-open/passive  */\n\tchar ifname[32];           /**< Network interface, for diagnostics */\n\tint layer;                 /**< Protocol layer                     */\n\tice_cand_recv_h *recvh;    /**< Handler for receiving packets      */\n\tvoid *arg;                 /**< Handler argument                   */\n\tstruct trice *icem;        /**< Pointer to parent                  */\n\n\t/** Packet statistics */\n\tstruct {\n\t\tsize_t n_tx;  /**< Number of packets sent */\n\t\tsize_t n_rx;  /**< Number of packets received */\n\t} stats;\n};\n\n/** Remote candidate */\nstruct ice_rcand {\n\tstruct ice_cand_attr attr;   /**< Base class (inheritance)           */\n\tstruct le le;                /**< List element                       */\n};\n\n\n/** Defines a candidate pair */\nstruct ice_candpair {\n\tstruct le le;                  /**< List element                   */\n\tstruct ice_lcand *lcand;       /**< Local candidate                */\n\tstruct ice_rcand *rcand;       /**< Remote candidate               */\n\tenum ice_candpair_state state; /**< Candidate pair state           */\n\tuint64_t pprio;                /**< Pair priority                  */\n\tbool valid;                    /**< Valid flag                     */\n\tbool nominated;                /**< Nominated flag                 */\n\tbool estab;                    /**< Pair is established            */\n\tbool trigged;                  /**< Pair was triggered             */\n\tint err;                       /**< Saved error code, if failed    */\n\tuint16_t scode;                /**< Saved STUN code, if failed     */\n\tstruct tcp_conn *tc;           /**< TCP-connection used            */\n\tstruct ice_tcpconn *conn;      /**< the ICE-TCP-connection used    */\n};\n\n\n/**\n * Handler for established candidate pair\n *\n * @param pair Which candidate pair was established\n * @param msg  STUN message\n * @param arg  Handler argument\n */\ntypedef void (trice_estab_h)(struct ice_candpair *pair,\n\t\t\t     const struct stun_msg *msg, void *arg);\n\n\n/**\n * Handler for failed candidate pair\n *\n * @param err   Posix error code\n * @param scode STUN status code\n * @param pair  Candidate pair\n * @param arg   Handler argument\n */\ntypedef void (trice_failed_h)(int err, uint16_t scode,\n\t\t\t    struct ice_candpair *pair, void *arg);\n\n\nint  trice_alloc(struct trice **icemp, const struct trice_conf *conf,\n\t\t enum ice_role role, const char *lufrag, const char *lpwd);\nint  trice_set_remote_ufrag(struct trice *icem, const char *rufrag);\nint  trice_set_remote_pwd(struct trice *icem, const char *rpwd);\nint  trice_set_role(struct trice *trice, enum ice_role role);\nenum ice_role trice_local_role(const struct trice *icem);\nint  trice_debug(struct re_printf *pf, const struct trice *icem);\nstruct trice_conf *trice_conf(struct trice *icem);\n\n\n/* Candidates (common) */\nint  trice_cand_print(struct re_printf *pf, const struct ice_cand_attr *cand);\nenum ice_tcptype   ice_tcptype_reverse(enum ice_tcptype type);\nconst char        *ice_tcptype_name(enum ice_tcptype tcptype);\nenum ice_cand_type ice_cand_type_base(enum ice_cand_type type);\n\n\n/* Local candidates */\nint trice_lcand_add(struct ice_lcand **lcandp, struct trice *icem,\n\t\t    unsigned compid, int proto, uint32_t prio,\n\t\t    const struct sa *addr, const struct sa *base_addr,\n\t\t    enum ice_cand_type type, const struct sa *rel_addr,\n\t\t    enum ice_tcptype tcptype,\n\t\t    void *sock, int layer);\nstruct list      *trice_lcandl(const struct trice *icem);\nstruct ice_lcand *trice_lcand_find(struct trice *icem,\n\t\t\t\t   enum ice_cand_type type,\n\t\t\t\t   unsigned compid, int proto,\n\t\t\t\t   const struct sa *addr);\nstruct ice_lcand *trice_lcand_find2(const struct trice *icem,\n\t\t\t\t    enum ice_cand_type type, int af);\nvoid *trice_lcand_sock(struct trice *icem, const struct ice_lcand *lcand);\nvoid trice_lcand_recv_packet(struct ice_lcand *lcand,\n\t\t\t     const struct sa *src, struct mbuf *mb);\n\n\n/* Remote candidate */\nstruct list *trice_rcandl(const struct trice *icem);\nint trice_rcand_add(struct ice_rcand **rcandp, struct trice *icem,\n\t\t    unsigned compid, const char *foundation, int proto,\n\t\t    uint32_t prio, const struct sa *addr,\n\t\t    enum ice_cand_type type, enum ice_tcptype tcptype);\nstruct ice_rcand *trice_rcand_find(struct trice *icem, unsigned compid,\n\t\t\t\t   int proto, const struct sa *addr);\n\n\n/* ICE Candidate pairs */\nstruct list *trice_checkl(const struct trice *icem);\nstruct list *trice_validl(const struct trice *icem);\nstruct ice_candpair *trice_candpair_find_state(const struct list *lst,\n\t\t\t\t\t   enum ice_candpair_state state);\nint  trice_candpair_debug(struct re_printf *pf, const struct ice_candpair *cp);\nint  trice_candpairs_debug(struct re_printf *pf, bool ansi_output,\n\t\t\t   const struct list *list);\n\n\n/* ICE checklist */\nvoid trice_checklist_set_waiting(struct trice *icem);\nint  trice_checklist_start(struct trice *icem, struct stun *stun,\n\t\t\t   uint32_t interval,\n\t\t\t   trice_estab_h *estabh, trice_failed_h *failh,\n\t\t\t   void *arg);\nvoid trice_checklist_stop(struct trice *icem);\nbool trice_checklist_isrunning(const struct trice *icem);\nbool trice_checklist_iscompleted(const struct trice *icem);\n\n\n/* ICE Conncheck */\nint trice_conncheck_send(struct trice *icem, struct ice_candpair *pair,\n\t\t\tbool use_cand);\n\n/* Port range */\nint trice_set_port_range(struct trice *trice,\n\t\t\t uint16_t min_port, uint16_t max_port);\n"
  },
  {
    "path": "include/re_turn.h",
    "content": "/**\n * @file re_turn.h  Interface to TURN implementation\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** TURN Protocol values */\nenum {\n\tTURN_DEFAULT_LIFETIME = 600,  /**< Default lifetime is 10 minutes */\n\tTURN_MAX_LIFETIME     = 3600  /**< Maximum lifetime is 1 hour     */\n};\n\ntypedef void(turnc_h)(int err, uint16_t scode, const char *reason,\n\t\t      const struct sa *relay_addr,\n\t\t      const struct sa *mapped_addr,\n\t\t      const struct stun_msg *msg,\n\t\t      void *arg);\ntypedef void(turnc_perm_h)(void *arg);\ntypedef void(turnc_chan_h)(void *arg);\n\nstruct turnc;\n\nint turnc_alloc(struct turnc **turncp, const struct stun_conf *conf, int proto,\n\t\tvoid *sock, int layer, const struct sa *srv,\n\t\tconst char *username, const char *password,\n\t\tuint32_t lifetime, turnc_h *th, void *arg);\nint turnc_send(struct turnc *turnc, const struct sa *dst, struct mbuf *mb);\nint turnc_recv(struct turnc *turnc, struct sa *src, struct mbuf *mb);\nint turnc_add_perm(struct turnc *turnc, const struct sa *peer,\n\t\t   turnc_perm_h *ph, void *arg);\nint turnc_add_chan(struct turnc *turnc, const struct sa *peer,\n\t\t   turnc_chan_h *ch, void *arg);\n"
  },
  {
    "path": "include/re_types.h",
    "content": "/**\n * @file re_types.h  Defines basic types\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <assert.h>\n#include <stddef.h>\n#include <sys/types.h>\n\n#ifdef __cplusplus\n#define restrict\n#endif\n\n#ifdef _MSC_VER\n#include <stdlib.h>\n\n#include <BaseTsd.h>\ntypedef SSIZE_T ssize_t;\n\n#endif\n\n/*\n * Basic integral types and boolean from C99\n */\n#include <inttypes.h>\n#include <stdbool.h>\n\n\n/* Needed for MS compiler */\n#ifdef _MSC_VER\n#ifndef __cplusplus\n#define inline _inline\n#endif\n#endif\n\n\n/*\n * Misc macros\n */\n\n/** Defines the NULL pointer */\n#ifndef NULL\n#define NULL ((void *)0)\n#endif\n\n/** Get number of elements in an array */\n#define RE_ARRAY_SIZE(a) ((sizeof(a))/(sizeof((a)[0])))\n\n\n/** Align a value to the boundary of mask */\n#define RE_ALIGN_MASK(x, mask)    (((x)+(mask))&~(mask))\n\n/** Check alignment of pointer (p) and byte count (c) **/\n#define re_is_aligned(p, c) (((uintptr_t)(const void *)(p)) % (c) == 0)\n\n/** Get the minimal value */\n#undef MIN\n#define MIN(a,b) (((a)<(b)) ? (a) : (b))\n\n/** Get the maximal value */\n#undef MAX\n#define MAX(a,b) (((a)>(b)) ? (a) : (b))\n\n#ifndef __cplusplus\n\n/** Get the minimal value */\n#undef min\n#define min(x,y) MIN(x, y)\n\n/** Get the maximal value */\n#undef max\n#define max(x,y) MAX(x, y)\n\n#endif\n\n/** Defines a soft breakpoint */\n#if (defined(__i386__) || defined(__x86_64__))\n#define RE_BREAKPOINT __asm__(\"int $0x03\")\n#elif defined(__has_builtin)\n#if __has_builtin(__builtin_debugtrap)\n#define RE_BREAKPOINT __builtin_debugtrap()\n#endif\n#endif\n\n#ifndef RE_BREAKPOINT\n#define RE_BREAKPOINT\n#endif\n\n\n/* Error return/goto debug helpers */\n#ifdef TRACE_ERR\n#define PRINT_TRACE_ERR(err)\t\t\t\t\t\t\\\n\t\t(void)re_fprintf(stderr, \"TRACE_ERR: %s:%u: %s():\"\t\\\n\t\t\t      \" %m (%d)\\n\",\t\t\t\t\\\n\t\t\t      __FILE__, __LINE__, __func__,\t\t\\\n\t\t\t      (err), (err));\n#else\n#define PRINT_TRACE_ERR(err)\n#endif\n\n#define IF_ERR_GOTO_OUT(err)\t\t\\\n\tif ((err)) {\t\t\t\\\n\t\tPRINT_TRACE_ERR((err))\t\\\n\t\tgoto out;\t\t\\\n\t}\n\n#define IF_ERR_GOTO_OUT1(err)\t\t\\\n\tif ((err)) {\t\t\t\\\n\t\tPRINT_TRACE_ERR((err))\t\\\n\t\tgoto out1;\t\t\\\n\t}\n\n#define IF_ERR_GOTO_OUT2(err)\t\t\\\n\tif ((err)) {\t\t\t\\\n\t\tPRINT_TRACE_ERR((err))\t\\\n\t\tgoto out2;\t\t\\\n\t}\n\n#define IF_ERR_RETURN(err)\t\t\\\n\tif ((err)) {\t\t\t\\\n\t\tPRINT_TRACE_ERR((err))\t\\\n\t\treturn (err);\t\t\\\n\t}\n\n#define IF_RETURN_EINVAL(exp)\t\t\\\n\tif ((exp)) {\t\t\t\\\n\t\tPRINT_TRACE_ERR(EINVAL)\t\\\n\t\treturn (EINVAL);\t\\\n\t}\n\n#define RETURN_ERR(err)\t\t\t\\\n\tif ((err)) {\t\t\t\\\n\t\tPRINT_TRACE_ERR((err))\t\\\n\t}\t\t\t\t\\\n\treturn (err);\n\n\n/* Error codes */\n#include <errno.h>\n\n/* Duplication of error codes. Values are from linux asm-generic/errno.h */\n\n/** No data available */\n#ifndef ENODATA\n#define ENODATA 200\n#endif\n\n/** Accessing a corrupted shared library */\n#ifndef ELIBBAD\n#define ELIBBAD 204\n#endif\n\n/** Destination address required */\n#ifndef EDESTADDRREQ\n#define EDESTADDRREQ 205\n#endif\n\n/** Protocol not supported */\n#ifndef EPROTONOSUPPORT\n#define EPROTONOSUPPORT 206\n#endif\n\n/** Operation not supported */\n#ifndef ENOTSUP\n#define ENOTSUP 207\n#endif\n\n/** Address family not supported by protocol */\n#ifndef EAFNOSUPPORT\n#define EAFNOSUPPORT 208\n#endif\n\n/** Cannot assign requested address */\n#ifndef EADDRNOTAVAIL\n#define EADDRNOTAVAIL 209\n#endif\n\n/** Software caused connection abort */\n#ifndef ECONNABORTED\n#define ECONNABORTED 210\n#endif\n\n/** Connection reset by peer */\n#ifndef ECONNRESET\n#define ECONNRESET 211\n#endif\n\n/** Transport endpoint is not connected */\n#ifndef ENOTCONN\n#define ENOTCONN 212\n#endif\n\n/** Connection timed out */\n#ifndef ETIMEDOUT\n#define ETIMEDOUT 213\n#endif\n\n/** Connection refused */\n#ifndef ECONNREFUSED\n#define ECONNREFUSED 214\n#endif\n\n/** Operation already in progress */\n#ifndef EALREADY\n#define EALREADY 215\n#endif\n\n/** Operation now in progress */\n#ifndef EINPROGRESS\n#define EINPROGRESS 216\n#endif\n\n/** Authentication error */\n#ifndef EAUTH\n#define EAUTH 217\n#endif\n\n/** No STREAM resources */\n#ifndef ENOSR\n#define ENOSR 218\n#endif\n\n/** Timer expired */\n#ifndef ETIME\n#define ETIME 219\n#endif\n\n/** Key was rejected by service */\n#ifndef EKEYREJECTED\n#define EKEYREJECTED 129\n#endif\n\n/* Cannot send after transport endpoint shutdown */\n#ifndef ESHUTDOWN\n#define ESHUTDOWN 108\n#endif\n\n/*\n * Give the compiler a hint which branch is \"likely\" or \"unlikely\" (inspired\n * by linux kernel and C++20/C2X)\n */\n#ifdef __GNUC__\n#define likely(x)       __builtin_expect(!!(x), 1)\n#define unlikely(x)     __builtin_expect(!!(x), 0)\n#else\n#define likely(x) x\n#define unlikely(x) x\n#endif\n\n/*\n * https://clang.llvm.org/docs/AttributeReference.html#nonstring\n */\n#define re_nonstring\n#ifdef __GNUC__\n#if __has_attribute(nonstring)\n#undef re_nonstring\n#define re_nonstring __attribute__((nonstring))\n#endif\n#endif\n\n#ifdef WIN32\n#define re_restrict __restrict\n#else\n#define re_restrict restrict\n#endif\n\n/* Socket helpers */\n#ifdef WIN32\n#define RE_ERRNO_SOCK WSAGetLastError()\n#define RE_BAD_SOCK INVALID_SOCKET\ntypedef size_t re_sock_t;\n#else\n#define RE_ERRNO_SOCK errno\n#define RE_BAD_SOCK -1\ntypedef int re_sock_t;\n#endif\n\n\n/* re_assert helpers */\n\n/**\n * @def re_assert(expr)\n *\n * If expression is false, prints error and calls abort() (not in\n * RELEASE/NDEBUG builds)\n *\n * @param expr   expression\n */\n\n\n/**\n * @def re_assert_se(expr)\n *\n * If expression is false, prints error and calls abort(),\n * in RELEASE/NDEBUG builds expression is always executed and keeps side effect\n *\n * @param expr   expression\n */\n\n#if defined(RELEASE) || defined(NDEBUG)\n#define re_assert(expr) (void)0\n#define re_assert_se(expr) do{(void)(expr);} while(false)\n#else\n#define re_assert(expr) assert(expr)\n#define re_assert_se(expr) assert(expr)\n#endif\n\n\n/* RE_VA_ARG SIZE helpers */\n#if !defined(DISABLE_RE_ARG) &&                                               \\\n\t!defined(__STRICT_ANSI__) && /* Needs ## trailing comma fix, with C23 \\\n\t\t\t\t\twe can use __VA_OPT__ */              \\\n\t__STDC_VERSION__ >= 201112L  /* _Generic C11 support required */\n\n#define HAVE_RE_ARG 1\n\n#if defined(__clang__)\n#define RE_ARG_SIZE(type)                                                     \\\n\t_Generic((type),                                                      \\\n\tbool:\t\t\tsizeof(int),                                  \\\n\tchar:\t\t\tsizeof(int),                                  \\\n\tunsigned char:\t\tsizeof(unsigned int),                         \\\n\tshort:\t\t\tsizeof(int),                                  \\\n\tunsigned short:\t\tsizeof(unsigned int),\t                      \\\n\tint:\t\t\tsizeof(int),                                  \\\n\tunsigned int:\t\tsizeof(unsigned int),                         \\\n\tlong:\t\t\tsizeof(long),                                 \\\n\tunsigned long:\t\tsizeof(unsigned long),                        \\\n\tlong long:\t\tsizeof(long long),                            \\\n\tunsigned long long:\tsizeof(unsigned long long),                   \\\n\tfloat:\t\t\tsizeof(double),                               \\\n\tdouble:\t\t\tsizeof(double),                               \\\n\tchar const*:\t\tsizeof(char const *),                         \\\n\tchar*:\t\t\tsizeof(char *),                               \\\n\tvoid const*:\t\tsizeof(void const *),                         \\\n\tvoid*:\t\t\tsizeof(void *),                               \\\n\tstruct pl:\t\tsizeof(struct pl),                            \\\n\tdefault: sizeof(void*)                                                \\\n)\n#else /* GCC bit fields workaround */\n#define RE_ARG_SIZE(type)                                                     \\\n\t_Generic((0)?(type):(type),                                           \\\n\tbool:\t\t\tsizeof(int),                                  \\\n\tchar:\t\t\tsizeof(int),                                  \\\n\tunsigned char:\t\tsizeof(unsigned int),                         \\\n\tshort:\t\t\tsizeof(int),                                  \\\n\tunsigned short:\t\tsizeof(unsigned int),\t                      \\\n\tint:\t\t\tsizeof(int),                                  \\\n\tunsigned int:\t\tsizeof(unsigned int),                         \\\n\tlong:\t\t\tsizeof(long),                                 \\\n\tunsigned long:\t\tsizeof(unsigned long),                        \\\n\tlong long:\t\tsizeof(long long),                            \\\n\tunsigned long long:\tsizeof(unsigned long long),                   \\\n\tfloat:\t\t\tsizeof(double),                               \\\n\tdouble:\t\t\tsizeof(double),                               \\\n\tchar const*:\t\tsizeof(char const *),                         \\\n\tchar*:\t\t\tsizeof(char *),                               \\\n\tvoid const*:\t\tsizeof(void const *),                         \\\n\tvoid*:\t\t\tsizeof(void *),                               \\\n\tstruct pl:\t\tsizeof(struct pl),                            \\\n\tdefault: sizeof(void*)                                                \\\n)\n#endif\n\n#define RE_ARG_0() 0\n#define RE_ARG_1(expr) RE_ARG_SIZE(expr), (expr), 0\n#define RE_ARG_2(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_1(__VA_ARGS__)\n#define RE_ARG_3(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_2(__VA_ARGS__)\n#define RE_ARG_4(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_3(__VA_ARGS__)\n#define RE_ARG_5(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_4(__VA_ARGS__)\n#define RE_ARG_6(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_5(__VA_ARGS__)\n#define RE_ARG_7(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_6(__VA_ARGS__)\n#define RE_ARG_8(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_7(__VA_ARGS__)\n#define RE_ARG_9(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_8(__VA_ARGS__)\n#define RE_ARG_10(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_9(__VA_ARGS__)\n#define RE_ARG_11(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_10(__VA_ARGS__)\n#define RE_ARG_12(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_11(__VA_ARGS__)\n#define RE_ARG_13(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_12(__VA_ARGS__)\n#define RE_ARG_14(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_13(__VA_ARGS__)\n#define RE_ARG_15(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_14(__VA_ARGS__)\n#define RE_ARG_16(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_15(__VA_ARGS__)\n#define RE_ARG_17(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_16(__VA_ARGS__)\n#define RE_ARG_18(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_17(__VA_ARGS__)\n#define RE_ARG_19(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_18(__VA_ARGS__)\n#define RE_ARG_20(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_19(__VA_ARGS__)\n#define RE_ARG_21(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_20(__VA_ARGS__)\n#define RE_ARG_22(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_21(__VA_ARGS__)\n#define RE_ARG_23(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_22(__VA_ARGS__)\n#define RE_ARG_24(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_23(__VA_ARGS__)\n#define RE_ARG_25(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_24(__VA_ARGS__)\n#define RE_ARG_26(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_25(__VA_ARGS__)\n#define RE_ARG_27(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_26(__VA_ARGS__)\n#define RE_ARG_28(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_27(__VA_ARGS__)\n#define RE_ARG_29(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_28(__VA_ARGS__)\n#define RE_ARG_30(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_29(__VA_ARGS__)\n#define RE_ARG_31(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_30(__VA_ARGS__)\n#define RE_ARG_32(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_31(__VA_ARGS__)\n\n#define RE_ARG_VA_NUM_2(X, X32, X31, X30, X29, X28, X27, X26, X25, X24, X23,  \\\n\t\t\tX22, X21, X20, X19, X18, X17, X16, X15, X14, X13,     \\\n\t\t\tX12, X11, X10, X9, X8, X7, X6, X5, X4, X3, X2, X1, N, \\\n\t\t\t...)                                                  \\\n\tN\n#define RE_ARG_VA_NUM(...)                                                    \\\n\tRE_ARG_VA_NUM_2(0, ##__VA_ARGS__, 32, 31, 30, 29, 28, 27, 26, 25, 24, \\\n\t\t\t23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11,   \\\n\t\t\t10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)\n\n#define RE_ARG_N3(N, ...) RE_ARG_##N(__VA_ARGS__)\n#define RE_ARG_N2(N, ...) RE_ARG_N3(N, __VA_ARGS__)\n#define RE_VA_ARGS(...) RE_ARG_N2(RE_ARG_VA_NUM(__VA_ARGS__), __VA_ARGS__)\n#endif /* End RE_VA_ARG SIZE helpers */\n\n#define RE_VA_ARG(ap, val, type, safe)                                        \\\n\tif (likely((safe))) {                                                 \\\n\t\tsize_t sz = va_arg((ap), size_t);                             \\\n\t\tif (unlikely(!sz)) {                                          \\\n\t\t\terr = ENODATA;                                        \\\n\t\t\tgoto out;                                             \\\n\t\t}                                                             \\\n\t\tif (unlikely(sz != sizeof(type))) {                           \\\n\t\t\terr = EOVERFLOW;                                      \\\n\t\t\tgoto out;                                             \\\n\t\t}                                                             \\\n\t}                                                                     \\\n\t(val) = va_arg((ap), type)\n"
  },
  {
    "path": "include/re_udp.h",
    "content": "/**\n * @file re_udp.h  Interface to User Datagram Protocol\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nstruct sa;\nstruct udp_sock;\n\ntypedef int (udp_send_h)(const struct sa *dst,\n\t\t\t struct mbuf *mb, void *arg);\n\n/**\n * Defines the UDP Receive handler\n *\n * @param src Source address\n * @param mb  Datagram buffer\n * @param arg Handler argument\n */\ntypedef void (udp_recv_h)(const struct sa *src, struct mbuf *mb, void *arg);\ntypedef void (udp_error_h)(int err, void *arg);\n\n\nint  udp_listen(struct udp_sock **usp, const struct sa *local,\n\t\tudp_recv_h *rh, void *arg);\nint  udp_alloc_sockless(struct udp_sock **usp,\n\t\t\tudp_send_h *sendh, udp_recv_h *recvh, void *arg);\nint  udp_alloc_fd(struct udp_sock **usp, re_sock_t fd,\n\t\t   udp_recv_h *recvh, void *arg);\nint  udp_connect(struct udp_sock *us, const struct sa *peer);\nint  udp_open(struct udp_sock **usp, int af);\nint  udp_send(struct udp_sock *us, const struct sa *dst, struct mbuf *mb);\nint  udp_local_get(const struct udp_sock *us, struct sa *local);\nint  udp_setsockopt(struct udp_sock *us, int level, int optname,\n\t\t    const void *optval, uint32_t optlen);\nint  udp_sockbuf_set(struct udp_sock *us, int size);\nvoid udp_rxsz_set(struct udp_sock *us, size_t rxsz);\nvoid udp_rxbuf_presz_set(struct udp_sock *us, size_t rx_presz);\nvoid udp_handler_set(struct udp_sock *us, udp_recv_h *rh, void *arg);\nvoid udp_error_handler_set(struct udp_sock *us, udp_error_h *eh);\nint  udp_thread_attach(struct udp_sock *us);\nvoid udp_thread_detach(struct udp_sock *us);\nre_sock_t udp_sock_fd(const struct udp_sock *us);\n\nint  udp_multicast_join(struct udp_sock *us, const struct sa *group);\nint  udp_multicast_leave(struct udp_sock *us, const struct sa *group);\nint  udp_settos(struct udp_sock *us, uint8_t tos);\nvoid udp_flush(const struct udp_sock *us);\nvoid udp_recv_packet(struct udp_sock *us, const struct sa *src,\n\t\tstruct mbuf *mb);\n\n\n/* Helper API */\ntypedef bool (udp_helper_send_h)(int *err, struct sa *dst,\n\t\t\t\t struct mbuf *mb, void *arg);\ntypedef bool (udp_helper_recv_h)(struct sa *src,\n\t\t\t\t struct mbuf *mb, void *arg);\n\nstruct udp_helper;\n\n\nint udp_register_helper(struct udp_helper **uhp, struct udp_sock *us,\n\t\t\tint layer,\n\t\t\tudp_helper_send_h *sh, udp_helper_recv_h *rh,\n\t\t\tvoid *arg);\nint udp_send_helper(struct udp_sock *us, const struct sa *dst,\n\t\t    struct mbuf *mb, struct udp_helper *uh);\nvoid udp_recv_helper(struct udp_sock *us, const struct sa *src,\n\t\t     struct mbuf *mb, struct udp_helper *uh);\nstruct udp_helper *udp_helper_find(const struct udp_sock *us, int layer);\n"
  },
  {
    "path": "include/re_unixsock.h",
    "content": "\nint unixsock_listen_fd(re_sock_t *fdp, const struct sa *sock);\n"
  },
  {
    "path": "include/re_uri.h",
    "content": "/**\n * @file re_uri.h  Interface to URI module\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** Defines a URI - Uniform Resource Identifier */\nstruct uri {\n\tstruct pl scheme;    /**< URI scheme e.g. \"sip:\" \"sips:\"    */\n\tstruct pl user;      /**< Username                          */\n\tstruct pl host;      /**< Hostname or IP-address            */\n\tint af;              /**< Address family of host IP-address */\n\tuint16_t port;       /**< Port number                       */\n\tstruct pl path;      /**< Optional URI-path                 */\n\tstruct pl params;    /**< Optional URI-parameters           */\n\tstruct pl headers;   /**< Optional URI-headers              */\n};\n\ntypedef int (uri_apply_h)(const struct pl *name, const struct pl *val,\n\t\t\t  void *arg);\n\nstruct re_printf;\nint  uri_encode(struct re_printf *pf, const struct uri *uri);\nint  uri_decode(struct uri *uri, const struct pl *pl);\nint  uri_decode_hostport(const struct pl *hostport, struct pl *host,\n\t\t\t struct pl *port);\nint  uri_param_get(const struct pl *pl, const struct pl *pname,\n\t\t   struct pl *pvalue);\nint  uri_params_apply(const struct pl *pl, uri_apply_h *ah, void *arg);\nint  uri_header_get(const struct pl *pl, const struct pl *hname,\n\t\t    struct pl *hvalue);\nint  uri_headers_apply(const struct pl *pl, uri_apply_h *ah, void *arg);\n\n\n/* Special URI escaping/unescaping */\nint uri_user_escape(struct re_printf *pf, const struct pl *pl);\nint uri_user_unescape(struct re_printf *pf, const struct pl *pl);\nint uri_param_escape(struct re_printf *pf, const struct pl *pl);\nint uri_param_unescape(struct re_printf *pf, const struct pl *pl);\nint uri_header_escape(struct re_printf *pf, const struct pl *pl);\nint uri_header_unescape(struct re_printf *pf, const struct pl *pl);\n"
  },
  {
    "path": "include/re_websock.h",
    "content": "/**\n * @file re_websock.h  The WebSocket Protocol\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nenum {\n\tWEBSOCK_VERSION = 13,\n};\n\nenum websock_opcode {\n\t/* Data frames */\n\tWEBSOCK_CONT  = 0x0,\n\tWEBSOCK_TEXT  = 0x1,\n\tWEBSOCK_BIN   = 0x2,\n\t/* Control frames */\n\tWEBSOCK_CLOSE = 0x8,\n\tWEBSOCK_PING  = 0x9,\n\tWEBSOCK_PONG  = 0xa,\n};\n\nenum websock_scode {\n\tWEBSOCK_NORMAL_CLOSURE   = 1000,\n\tWEBSOCK_GOING_AWAY       = 1001,\n\tWEBSOCK_PROTOCOL_ERROR   = 1002,\n\tWEBSOCK_UNSUPPORTED_DATA = 1003,\n\tWEBSOCK_INVALID_PAYLOAD  = 1007,\n\tWEBSOCK_POLICY_VIOLATION = 1008,\n\tWEBSOCK_MESSAGE_TOO_BIG  = 1009,\n\tWEBSOCK_EXTENSION_ERROR  = 1010,\n\tWEBSOCK_INTERNAL_ERROR   = 1011,\n};\n\nstruct websock_hdr {\n\tunsigned fin:1;\n\tunsigned rsv1:1;\n\tunsigned rsv2:1;\n\tunsigned rsv3:1;\n\tunsigned opcode:4;\n\tunsigned mask:1;\n\tuint64_t len;\n\tuint8_t mkey[4];\n};\n\nstruct websock;\nstruct websock_conn;\n\ntypedef void (websock_estab_h)(void *arg);\ntypedef void (websock_recv_h)(const struct websock_hdr *hdr, struct mbuf *mb,\n\t\t\t      void *arg);\ntypedef void (websock_close_h)(int err, void *arg);\n\n\nint websock_connect(struct websock_conn **connp, struct websock *sock,\n\t\t    struct http_cli *cli, const char *uri, unsigned kaint,\n\t\t    websock_estab_h *estabh, websock_recv_h *recvh,\n\t\t    websock_close_h *closeh, void *arg,\n\t\t    const char *fmt, ...);\nint websock_connect_proto(struct websock_conn **connp, const char *proto,\n\t\t\t  struct websock *sock, struct http_cli *cli,\n\t\t\t  const char *uri, unsigned kaint,\n\t\t\t  websock_estab_h *estabh, websock_recv_h *recvh,\n\t\t\t  websock_close_h *closeh, void *arg, const char *fmt,\n\t\t\t  ...);\nint websock_accept(struct websock_conn **connp, struct websock *sock,\n\t\t   struct http_conn *htconn, const struct http_msg *msg,\n\t\t   unsigned kaint, websock_recv_h *recvh,\n\t\t   websock_close_h *closeh, void *arg);\nint websock_accept_proto(struct websock_conn **connp, const char *proto,\n\t\t\t struct websock *sock, struct http_conn *htconn,\n\t\t\t const struct http_msg *msg, unsigned kaint,\n\t\t\t websock_recv_h *recvh, websock_close_h *closeh,\n\t\t\t void *arg);\nint websock_send(struct websock_conn *conn, enum websock_opcode opcode,\n\t\t const char *fmt, ...);\nint websock_close(struct websock_conn *conn, enum websock_scode scode,\n\t\t  const char *fmt, ...);\nstruct tcp_conn *websock_tcp(const struct websock_conn *conn);\n\ntypedef void (websock_shutdown_h)(void *arg);\n\nint  websock_alloc(struct websock **sockp, websock_shutdown_h *shuth,\n\t\t   void *arg);\nvoid websock_shutdown(struct websock *sock);\n"
  },
  {
    "path": "include/rem.h",
    "content": "/**\n * @file rem.h  Wrapper for librem headers\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#ifndef REM_H__\n#define REM_H__\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n\n#include \"rem_audio.h\"\n#include \"rem_video.h\"\n#include \"rem_dsp.h\"\n#include \"rem_flv.h\"\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "include/rem_aac.h",
    "content": "/**\n * @file rem_aac.h Advanced Audio Coding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** Defines the AAC header */\nstruct aac_header {\n\tunsigned sample_rate;  /**< Audio sample rate in [Hz]    */\n\tunsigned channels;     /**< Number of audio channels     */\n\tunsigned frame_size;   /**< Frame size, 960 or 1024 bits */\n};\n\nint aac_header_decode(struct aac_header *hdr, const uint8_t *p, size_t len);\n"
  },
  {
    "path": "include/rem_au.h",
    "content": "/**\n * @file rem_au.h Basic audio types\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** Audio formats */\nenum aufmt {\n\tAUFMT_S16LE,  /**< Signed 16-bit PCM */\n\tAUFMT_S32LE,  /**< Signed 32-bit PCM */\n\tAUFMT_PCMA,   /**< G.711 A-law       */\n\tAUFMT_PCMU,   /**< G.711 U-law       */\n\tAUFMT_FLOAT,  /**< Float 32 bit (CPU endian)                   */\n\tAUFMT_S24_3LE,/**< Signed 24bit Little Endian in 3bytes format */\n\tAUFMT_RAW,    /**< RAW PCM                                     */\n};\n\nsize_t      aufmt_sample_size(enum aufmt fmt);\nconst char *aufmt_name(enum aufmt fmt);\n\nuint32_t au_calc_nsamp(uint32_t srate, uint8_t channels, uint16_t ptime);\n"
  },
  {
    "path": "include/rem_aubuf.h",
    "content": "/**\n * @file rem_aubuf.h Audio Buffer\n *\n * Copyright (C) 2010 Creytiv.com\n */\nstruct aubuf;\n\nenum aubuf_mode {\n\tAUBUF_FIXED,\n\tAUBUF_ADAPTIVE\n};\n\nint  aubuf_alloc(struct aubuf **abp, size_t min_sz, size_t max_sz);\nvoid aubuf_set_id(struct aubuf *ab, struct pl *id);\nvoid aubuf_set_live(struct aubuf *ab, bool live);\nvoid aubuf_set_mode(struct aubuf *ab, enum aubuf_mode mode);\nvoid aubuf_set_silence(struct aubuf *ab, double silence);\nint  aubuf_resize(struct aubuf *ab, size_t min_sz, size_t max_sz);\nint  aubuf_write_auframe(struct aubuf *ab, const struct auframe *af);\nint  aubuf_append_auframe(struct aubuf *ab, struct mbuf *mb,\n\t\t\t  const struct auframe *af);\nvoid aubuf_read_auframe(struct aubuf *ab, struct auframe *af);\nvoid aubuf_sort_auframe(struct aubuf *ab);\nint  aubuf_get(struct aubuf *ab, uint32_t ptime, uint8_t *p, size_t sz);\nvoid aubuf_flush(struct aubuf *ab);\nint  aubuf_debug(struct re_printf *pf, const struct aubuf *ab);\nsize_t aubuf_cur_size(const struct aubuf *ab);\nsize_t aubuf_maxsz(const struct aubuf *ab);\nbool aubuf_started(const struct aubuf *ab);\nvoid aubuf_drop_auframe(struct aubuf *ab, const struct auframe *af);\n\n\nstatic inline int aubuf_append(struct aubuf *ab, struct mbuf *mb)\n{\n\treturn aubuf_append_auframe(ab, mb, NULL);\n}\n\n\nstatic inline int aubuf_get_samp(struct aubuf *ab, uint32_t ptime,\n\t\t\t\t int16_t *sampv, size_t sampc)\n{\n\treturn aubuf_get(ab, ptime, (uint8_t *)sampv, sampc * 2);\n}\n\n\n#ifndef __cplusplus\nstatic inline int aubuf_write(struct aubuf *ab, const uint8_t *p, size_t sz)\n{\n\tstruct auframe af = {\n\t\t.fmt = AUFMT_RAW,\n\t\t.srate = 0,\n\t\t.sampv = (uint8_t *)p,\n\t\t.sampc = sz,\n\t\t.timestamp = 0,\n\t\t.level = AULEVEL_UNDEF\n\t};\n\n\treturn aubuf_write_auframe(ab, &af);\n}\n\n\nstatic inline int aubuf_write_samp(struct aubuf *ab, const int16_t *sampv,\n\t\t\t\t   size_t sampc)\n{\n\tstruct auframe af = {\n\t\t.fmt = AUFMT_S16LE,\n\t\t.srate = 0,\n\t\t.sampv = (uint8_t *)sampv,\n\t\t.sampc = sampc,\n\t\t.timestamp = 0,\n\t\t.level = AULEVEL_UNDEF\n\t};\n\n\treturn aubuf_write_auframe(ab, &af);\n}\n\n\nstatic inline void aubuf_read(struct aubuf *ab, uint8_t *p, size_t sz)\n{\n\tstruct auframe af = {\n\t\t.fmt = AUFMT_RAW,\n\t\t.srate = 0,\n\t\t.sampv = p,\n\t\t.sampc = sz,\n\t\t.timestamp = 0,\n\t\t.level = AULEVEL_UNDEF\n\t};\n\n\taubuf_read_auframe(ab, &af);\n}\n\n\nstatic inline void aubuf_read_samp(struct aubuf *ab, int16_t *sampv,\n\t\t\t\t   size_t sampc)\n{\n\tstruct auframe af = {\n\t\t.fmt = AUFMT_S16LE,\n\t\t.srate = 0,\n\t\t.sampv = (uint8_t *)sampv,\n\t\t.sampc = sampc,\n\t\t.timestamp = 0,\n\t\t.level = AULEVEL_UNDEF\n\t};\n\n\taubuf_read_auframe(ab, &af);\n}\n#endif\n"
  },
  {
    "path": "include/rem_auconv.h",
    "content": "/**\n * @file rem_auconv.h Audio sample format conversion\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nvoid auconv_from_s16(enum aufmt dst_fmt, void *dst_sampv,\n\t\t     const int16_t *src_sampv, size_t sampc);\nvoid auconv_to_s16(int16_t *dst_sampv, enum aufmt src_fmt,\n\t\t   void *src_sampv, size_t sampc);\nvoid auconv_to_float(float *dst_sampv, enum aufmt src_fmt,\n\t\t     const void *src_sampv, size_t sampc);\n"
  },
  {
    "path": "include/rem_audio.h",
    "content": "/**\n * @file rem_audio.h  Wrapper for all Audio header files\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#include \"rem_au.h\"\n#include \"rem_aulevel.h\"\n#include \"rem_auframe.h\"\n#include \"rem_aubuf.h\"\n#include \"rem_auconv.h\"\n#include \"rem_aufile.h\"\n#include \"rem_autone.h\"\n#include \"rem_aumix.h\"\n#include \"rem_dtmf.h\"\n#include \"rem_fir.h\"\n#include \"rem_goertzel.h\"\n#include \"rem_auresamp.h\"\n#include \"rem_g711.h\"\n#include \"rem_aac.h\"\n"
  },
  {
    "path": "include/rem_aufile.h",
    "content": "/**\n * @file rem_aufile.h Audio File interface\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** Audio file mode */\nenum aufile_mode {\n\tAUFILE_READ,\n\tAUFILE_WRITE,\n};\n\n/** Audio file parameters */\nstruct aufile_prm {\n\tuint32_t srate;\n\tuint8_t channels;\n\tenum aufmt fmt;\n};\n\nstruct aufile;\n\nint aufile_open(struct aufile **afp, struct aufile_prm *prm,\n\t\tconst char *filename, enum aufile_mode mode);\nint aufile_read(struct aufile *af, uint8_t *p, size_t *sz);\nint aufile_write(struct aufile *af, const uint8_t *p, size_t sz);\nsize_t aufile_get_size(struct aufile *af);\nsize_t aufile_get_length(struct aufile *af, const struct aufile_prm *prm);\nint aufile_set_position(struct aufile *af, const struct aufile_prm *prm,\n\t\tsize_t pos_ms);\n"
  },
  {
    "path": "include/rem_auframe.h",
    "content": "/*\n * Audio frame\n */\n\n#define AUDIO_TIMEBASE 1000000U\n\n/**\n * Defines a frame of audio samples\n */\nstruct auframe {\n\tenum aufmt fmt;      /**< Sample format (enum aufmt)        */\n\tuint32_t srate;      /**< Samplerate                        */\n\tvoid *sampv;         /**< Audio samples (must be mem_ref'd) */\n\tsize_t sampc;        /**< Total number of audio samples     */\n\tuint64_t timestamp;  /**< Timestamp in AUDIO_TIMEBASE units */\n\tdouble level;        /**< Audio level in dBov               */\n\tuint16_t id;         /**< Frame/Channel identifier          */\n\tuint8_t ch;          /**< Channels                          */\n\tuint8_t padding[5];\n};\n\nvoid auframe_init(struct auframe *af, enum aufmt fmt, void *sampv,\n\t\t  size_t sampc, uint32_t srate, uint8_t ch);\n\n/**\n * Update an audio frame\n *\n * @param af        Audio frame\n * @param sampv     Audio samples\n * @param sampc     Total number of audio samples\n * @param timestamp Timestamp in AUDIO_TIMEBASE units\n */\nstatic inline void auframe_update(struct auframe *af, void *sampv,\n\t\t\t\t  size_t sampc, uint64_t timestamp)\n{\n\tif (!af)\n\t\treturn;\n\n\taf->sampv = sampv;\n\taf->sampc = sampc;\n\taf->timestamp = timestamp;\n\taf->level = AULEVEL_UNDEF;\n}\n\nsize_t auframe_size(const struct auframe *af);\nvoid   auframe_mute(struct auframe *af);\ndouble auframe_level(struct auframe *af);\nuint64_t auframe_bytes_to_timestamp(const struct auframe *af, size_t n);\nuint64_t auframe_bytes_to_ms(const struct auframe *af, size_t n);\nsize_t auframe_ms_to_bytes(const struct auframe *af, uint16_t ms);\n"
  },
  {
    "path": "include/rem_aulevel.h",
    "content": "\n\n/*\n * Audio-level\n */\n\n\n#define AULEVEL_UNDEF (-128.0)\n#define AULEVEL_MIN    (-96.0)\n#define AULEVEL_MAX      (0.0)\n\n\ndouble aulevel_calc_dbov(int fmt, const void *sampv, size_t sampc);\n"
  },
  {
    "path": "include/rem_aumix.h",
    "content": "/**\n * @file rem_aumix.h Audio Mixer\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\nstruct aumix;\nstruct aumix_source;\n\n/**\n * Audio mixer frame handler\n *\n * @param sampv Buffer with audio samples\n * @param sampc Number of samples\n * @param arg   Handler argument\n */\ntypedef void (aumix_frame_h)(const int16_t *sampv, size_t sampc, void *arg);\ntypedef void (aumix_record_h)(struct auframe *af);\ntypedef void (aumix_read_h)(struct auframe *af, void *arg);\n\nint aumix_alloc(struct aumix **mixp, uint32_t srate,\n\t\tuint8_t ch, uint32_t ptime);\nvoid aumix_latency(struct aumix *mix, uint16_t min, uint16_t max);\nvoid aumix_recordh(struct aumix *mix, aumix_record_h *recordh);\nvoid aumix_record_sumh(struct aumix *mix, aumix_record_h *recordh);\nint aumix_playfile(struct aumix *mix, const char *filepath);\nuint32_t aumix_source_count(const struct aumix *mix);\nint aumix_source_alloc(struct aumix_source **srcp, struct aumix *mix,\n\t\t       aumix_frame_h *fh, void *arg);\nvoid aumix_source_set_id(struct aumix_source *src, struct pl *id);\nvoid aumix_source_enable(struct aumix_source *src, bool enable);\nvoid aumix_source_mute(struct aumix_source *src, bool mute);\nint  aumix_source_put(struct aumix_source *src, const int16_t *sampv,\n\t\t      size_t sampc);\nint  aumix_source_put_auframe(struct aumix_source *src, struct auframe *af);\nvoid aumix_source_readh(struct aumix_source *src, aumix_read_h *readh);\nvoid aumix_source_flush(struct aumix_source *src);\nint aumix_debug(struct re_printf *pf, const struct aumix *mix);\n"
  },
  {
    "path": "include/rem_auresamp.h",
    "content": "/**\n * @file rem_auresamp.h Audio Resampling\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n/**\n * Defines the audio resampler handler\n *\n * @param outv  Output samples\n * @param inv   Input samples\n * @param inc   Number of input samples\n * @param ratio Resample ratio\n */\ntypedef void (auresamp_h)(int16_t *outv, const int16_t *inv,\n\t\t\t  size_t inc, unsigned ratio);\n\n/** Defines the resampler state */\nstruct auresamp {\n\tstruct fir fir;        /**< FIR filter state */\n\tauresamp_h *resample;  /**< Resample handler */\n\tconst int16_t *tapv;   /**< FIR filter taps */\n\tsize_t tapc;           /**< FIR filter tap count */\n\tuint32_t orate, irate; /**< Input/output sample rate */\n\tunsigned och, ich;     /**< Input/output channel count */\n\tunsigned ratio;        /**< Resample ratio */\n\tbool up;               /**< Up/down sample flag */\n};\n\nvoid auresamp_init(struct auresamp *rs);\nint  auresamp_setup(struct auresamp *rs, uint32_t irate, unsigned ich,\n\t\t    uint32_t orate, unsigned och);\nint  auresamp(struct auresamp *rs, int16_t *outv, size_t *outc,\n\t      const int16_t *inv, size_t inc);\n"
  },
  {
    "path": "include/rem_autone.h",
    "content": "/**\n * @file rem_autone.h Audio Tones\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nint autone_sine(struct mbuf *mb, uint32_t srate,\n\t\tuint32_t f1, int l1, uint32_t f2, int l2);\nint autone_dtmf(struct mbuf *mb, uint32_t srate, int digit);\n"
  },
  {
    "path": "include/rem_avc.h",
    "content": "/**\n * @file rem_avc.h Advanced Video Coding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nstruct avc_config {\n\tuint8_t profile_ind;\n\tuint8_t profile_compat;\n\tuint8_t level_ind;\n\tuint16_t sps_len;\n\tuint8_t sps[256];\n\tuint16_t pps_len;\n\tuint8_t pps[64];\n};\n\n\nint avc_config_encode(struct mbuf *mb, uint8_t profile_ind,\n\t\t      uint8_t profile_compat, uint8_t level_ind,\n\t\t      uint16_t sps_length, const uint8_t *sps,\n\t\t      uint16_t pps_length, const uint8_t *pps);\nint avc_config_decode(struct avc_config *conf, struct mbuf *mb);\n"
  },
  {
    "path": "include/rem_dsp.h",
    "content": "/**\n * @file rem_dsp.h DSP routines\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#include <stdint.h>\n\n\n#if defined (HAVE_ARMV6) || defined (HAVE_NEON)\n\nstatic inline uint8_t saturate_u8(int32_t a)\n{\n\tuint8_t r;\n\t__asm__ __volatile__ (\"usat %0, #8, %1\" : \"=r\"(r) : \"r\"(a));\n\treturn r;\n}\n\nstatic inline int16_t saturate_s16(int32_t a)\n{\n\t__asm__ __volatile__ (\"ssat %0, #16, %1  \\n\\t\"\n\t\t\t      : \"+r\"(a)\n\t\t\t      : \"r\"(a)\n\t\t\t      );\n\n\treturn a;\n}\n\nstatic inline int16_t saturate_add16(int32_t a, int32_t b)\n{\n\t__asm__ __volatile__ (\"add %0, %1, %2    \\n\\t\"\n\t\t\t      \"ssat %0, #16, %0  \\n\\t\"\n\t\t\t      :\"+r\"(a)\n\t\t\t      :\"r\"(a), \"r\"(b)\n\t\t\t      );\n\treturn a;\n}\n\nstatic inline int16_t saturate_sub16(int32_t a, int32_t b)\n{\n\t__asm__ __volatile__ (\"sub %0, %1, %2     \\n\\t\"\n\t\t\t      \"ssat %0, #16, %0   \\n\\t\"\n\t\t\t      :\"+r\"(a)\n\t\t\t      :\"r\"(a), \"r\"(b)\n\t\t\t      );\n\treturn a;\n}\n\n\n#else\n\n\nstatic inline uint8_t saturate_u8(int32_t a)\n{\n\treturn (a > (int32_t)UINT8_MAX) ? UINT8_MAX : ((a < 0) ? 0 : a);\n}\n\nstatic inline int16_t saturate_s16(int32_t a)\n{\n\tif (a > INT16_MAX)\n\t\treturn INT16_MAX;\n\telse if (a < INT16_MIN)\n\t\treturn INT16_MIN;\n\telse\n\t\treturn a;\n}\n\nstatic inline int16_t saturate_add16(int32_t a, int32_t b)\n{\n\treturn saturate_s16(a + b);\n}\n\n\nstatic inline int16_t saturate_sub16(int32_t a, int32_t b)\n{\n\treturn saturate_s16(a - b);\n}\n\n\n#endif\n\n\n#ifdef HAVE_NEON\nstatic inline int ABS32(int a)\n{\n\tint r;\n\t__asm__ __volatile__ (\"vmov.s32 d0[0], %1 \\t\\n\"\n\t\t\t      \"vabs.s32 d0, d0    \\t\\n\"\n\t\t\t      \"vmov.s32 %0, d0[0] \\t\\n\"\n\t\t\t      : \"=r\"(r)\n\t\t\t      : \"r\"(a)\n\t\t\t      );\n\treturn a;\n}\n#else\nstatic inline int ABS32(int a)\n{\n\treturn a > 0 ? a : -a;\n}\n#endif\n"
  },
  {
    "path": "include/rem_dtmf.h",
    "content": "/**\n * @file rem_dtmf.h  DTMF Decoder\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\nstruct dtmf_dec;\n\n/**\n * Defines the DTMF decode handler\n *\n * @param digit Decoded DTMF digit\n * @param arg   Handler argument\n */\ntypedef void (dtmf_dec_h)(char digit, void *arg);\n\n\nint  dtmf_dec_alloc(struct dtmf_dec **decp, unsigned srate, unsigned ch,\n\t\t    dtmf_dec_h *dech, void *arg);\nvoid dtmf_dec_reset(struct dtmf_dec *dec, unsigned srate, unsigned ch);\nvoid dtmf_dec_probe(struct dtmf_dec *dec, const int16_t *sampv, size_t sampc);\n"
  },
  {
    "path": "include/rem_fir.h",
    "content": "/**\n * @file rem_fir.h  Finite Impulse Response (FIR) functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n/** Defines the fir filter state */\nstruct fir {\n\tint16_t history[256];  /**< Previous samples */\n\tunsigned index;        /**< Sample index */\n};\n\nvoid fir_reset(struct fir *fir);\nvoid fir_filter(struct fir *fir, int16_t *outv, const int16_t *inv, size_t inc,\n\t\tunsigned ch, const int16_t *tapv, size_t tapc);\n"
  },
  {
    "path": "include/rem_flv.h",
    "content": "/**\n * @file rem_flv.h Flash Video File Format\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/*\n * Audio\n */\n\nenum flv_aucodec {\n\tFLV_AUCODEC_PCM       = 0,\n\tFLV_AUCODEC_MP3       = 2,\n\tFLV_AUCODEC_PCM_LE    = 3,\n\tFLV_AUCODEC_ALAW      = 7,\n\tFLV_AUCODEC_ULAW      = 8,\n\tFLV_AUCODEC_AAC       = 10,\n};\n\nenum flv_srate {\n\tFLV_SRATE_5500HZ  = 0,\n\tFLV_SRATE_11000HZ = 1,\n\tFLV_SRATE_22000HZ = 2,\n\tFLV_SRATE_44000HZ = 3,\n};\n\nenum flv_aac_packet_type {\n\tFLV_AAC_SEQUENCE_HEADER = 0,\n\tFLV_AAC_RAW             = 1,\n};\n\n\n/*\n * Video\n */\n\nenum flv_vidframe {\n\tFLV_VIDFRAME_KEY            = 1,\n\tFLV_VIDFRAME_INTER          = 2,\n\tFLV_VIDFRAME_DISP_INTER     = 3,\n\tFLV_VIDFRAME_GENERATED_KEY  = 4,\n\tFLV_VIDFRAME_VIDEO_INFO_CMD = 5,\n};\n\nenum flv_vidcodec {\n\tFLV_VIDCODEC_H263  = 2,\n\tFLV_VIDCODEC_H264  = 7,\n\tFLV_VIDCODEC_MPEG4 = 9,\n};\n\nenum flv_avc_packet_type {\n\tFLV_AVC_SEQUENCE = 0,\n\tFLV_AVC_NALU     = 1,\n\tFLV_AVC_EOS      = 2,\n};\n"
  },
  {
    "path": "include/rem_g711.h",
    "content": "/**\n * @file rem_g711.h  Interface to G.711 codec\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nextern const uint8_t g711_l2u[4096];\nextern const uint8_t g711_l2A[2048];\nextern const int16_t g711_u2l[256];\nextern const int16_t g711_A2l[256];\n\n\n/**\n * Encode one 16-bit PCM sample to U-law format\n *\n * @param l Signed PCM sample\n *\n * @return U-law byte\n */\nstatic inline uint8_t g711_pcm2ulaw(int16_t lx)\n{\n\tint32_t l = lx;\n\tconst uint8_t mask = (l < 0) ? 0x7f : 0xff;\n\tif (l < 0)\n\t\tl = -l;\n\tif (l < 4)\n\t\treturn 0xff & mask;\n\tl -= 4;\n\tl >>= 3;\n\n\treturn g711_l2u[l] & mask;\n}\n\n\n/**\n * Encode one 16-bit PCM sample to A-law format\n *\n * @param l Signed PCM sample\n *\n * @return A-law byte\n */\nstatic inline uint8_t g711_pcm2alaw(int16_t l)\n{\n\tconst uint8_t mask = (l < 0) ? 0x7f : 0xff;\n\tif (l < 0)\n\t\tl = ~l;\n\tl >>= 4;\n\n\treturn g711_l2A[l] & mask;\n}\n\n\n/**\n * Decode one U-law sample to 16-bit PCM sample\n *\n * @param u U-law byte\n *\n * @return Signed PCM sample\n */\nstatic inline int16_t g711_ulaw2pcm(uint8_t u)\n{\n\treturn g711_u2l[u];\n}\n\n\n/**\n * Decode one A-law sample to 16-bit PCM sample\n *\n * @param A A-law byte\n *\n * @return Signed PCM sample\n */\nstatic inline int16_t g711_alaw2pcm(uint8_t a)\n{\n\treturn g711_A2l[a];\n}\n"
  },
  {
    "path": "include/rem_goertzel.h",
    "content": "/**\n * @file rem_goertzel.h  Goertzel algorithm\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n/** Defines the goertzel algorithm state */\nstruct goertzel {\n\tdouble q1;   /**< current state */\n\tdouble q2;   /**< previous state */\n\tdouble coef; /**< coefficient */\n};\n\n\nvoid  goertzel_init(struct goertzel *g, double freq, unsigned srate);\nvoid  goertzel_reset(struct goertzel *g);\ndouble goertzel_result(struct goertzel *g);\n\n\n/**\n * Process sample\n *\n * @param g    Goertzel state\n * @param samp Sample value\n */\nstatic inline void goertzel_update(struct goertzel *g, int16_t samp)\n{\n\tdouble q0 = g->coef*g->q1 - g->q2 + (double)samp;\n\n\tg->q2 = g->q1;\n\tg->q1 = q0;\n}\n"
  },
  {
    "path": "include/rem_vid.h",
    "content": "/**\n * @file rem_vid.h Basic video types\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** Pixel format */\nenum vidfmt {\n\tVID_FMT_YUV420P =  0, /* planar YUV  4:2:0   12bpp                 */\n\tVID_FMT_YUYV422,      /* packed YUV  4:2:2   16bpp                 */\n\tVID_FMT_UYVY422,      /* packed YUV  4:2:2   16bpp                 */\n\tVID_FMT_RGB32,        /* packed RGBA 8:8:8:8 32bpp (native endian) */\n\tVID_FMT_ARGB,         /* packed RGBA 8:8:8:8 32bpp (big endian)    */\n\tVID_FMT_RGB565,       /* packed RGB  5:6:5   16bpp (native endian) */\n\tVID_FMT_NV12,         /* planar YUV  4:2:0   12bpp UV interleaved  */\n\tVID_FMT_NV21,         /* planar YUV  4:2:0   12bpp VU interleaved  */\n\tVID_FMT_YUV444P,      /* planar YUV  4:4:4   24bpp                 */\n\tVID_FMT_YUV422P,      /* planar YUV  4:2:2   16bpp                 */\n\t/* marker */\n\tVID_FMT_N\n};\n\n/** Video pixel format component description */\nstruct vidfmt_compdesc {\n\tunsigned plane_index:2;\n\tunsigned step:3;\n};\n\n/** Video pixel format description */\nstruct vidfmt_desc {\n\tconst char *name;\n\tuint8_t planes;\n\tuint8_t compn;\n\tstruct vidfmt_compdesc compv[4];\n};\n\n/** Video orientation */\nenum vidorient {\n\tVIDORIENT_PORTRAIT,\n\tVIDORIENT_PORTRAIT_UPSIDEDOWN,\n\tVIDORIENT_LANDSCAPE_LEFT,\n\tVIDORIENT_LANDSCAPE_RIGHT,\n};\n\n/** Video size */\nstruct vidsz {\n\tunsigned w;  /**< Width  */\n\tunsigned h;  /**< Height */\n};\n\n/** Video frame */\nstruct vidframe {\n\tuint8_t *data[4];      /**< Video planes        */\n\tuint16_t linesize[4];  /**< Array of line-sizes */\n\tstruct vidsz size;     /**< Frame resolution    */\n\tenum vidfmt fmt;       /**< Video pixel format  */\n\tunsigned xoffs;        /**< x offset            */\n\tunsigned yoffs;        /**< y offset            */\n};\n\n/** Video point */\nstruct vidpt {\n\tunsigned x;  /**< X position */\n\tunsigned y;  /**< Y position */\n};\n\n/** Video rectangle */\nstruct vidrect {\n\tunsigned x;  /**< X position */\n\tunsigned y;  /**< Y position */\n\tunsigned w;  /**< Width      */\n\tunsigned h;  /**< Height     */\n};\n\nstatic inline bool vidsz_cmp(const struct vidsz *a, const struct vidsz *b)\n{\n\tif (!a || !b)\n\t\treturn false;\n\n\tif (a == b)\n\t\treturn true;\n\n\treturn a->w == b->w && a->h == b->h;\n}\n\n\nstatic inline bool vidrect_cmp(const struct vidrect *a,\n\t\t\t       const struct vidrect *b)\n{\n\tif (!a || !b)\n\t\treturn false;\n\n\tif (a == b)\n\t\treturn true;\n\n\treturn a->x == b->x && a->y == b->y && a->w == b->w && a->h == b->h;\n}\n\n\nstatic inline int rgb2y(uint8_t r, uint8_t g, uint8_t b)\n{\n\treturn ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;\n}\n\n\nstatic inline int rgb2u(uint8_t r, uint8_t g, uint8_t b)\n{\n\treturn ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;\n}\n\n\nstatic inline int rgb2v(uint8_t r, uint8_t g, uint8_t b)\n{\n\treturn ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;\n}\n\n\nsize_t vidframe_size(enum vidfmt fmt, const struct vidsz *sz);\nvoid vidframe_init(struct vidframe *vf, enum vidfmt fmt,\n\t\t   const struct vidsz *sz, void *data[4],\n\t\t   unsigned linesize[4]);\nvoid vidframe_init_buf(struct vidframe *vf, enum vidfmt fmt,\n\t\t       const struct vidsz *sz, uint8_t *buf);\nint  vidframe_alloc(struct vidframe **vfp, enum vidfmt fmt,\n\t\t    const struct vidsz *sz);\nvoid vidframe_fill(struct vidframe *vf, uint32_t r, uint32_t g, uint32_t b);\nvoid vidframe_copy(struct vidframe *dst, const struct vidframe *src);\n\n\nconst char *vidfmt_name(enum vidfmt fmt);\n\n\nstatic inline bool vidframe_isvalid(const struct vidframe *f)\n{\n\treturn f ? f->data[0] != NULL : false;\n}\n\n\nextern const struct vidfmt_desc vidfmt_descv[VID_FMT_N];\n\n\n/* draw */\nvoid vidframe_draw_point(struct vidframe *f, unsigned x, unsigned y,\n\t\t\t uint8_t r, uint8_t g, uint8_t b);\nvoid vidframe_draw_hline(struct vidframe *f,\n\t\t\t unsigned x0, unsigned y0, unsigned w,\n\t\t\t uint8_t r, uint8_t g, uint8_t b);\nvoid vidframe_draw_vline(struct vidframe *f,\n\t\t\t unsigned x0, unsigned y0, unsigned h,\n\t\t\t uint8_t r, uint8_t g, uint8_t b);\nvoid vidframe_draw_rect(struct vidframe *f,\n\t\t\tunsigned x0, unsigned y0, unsigned w, unsigned h,\n\t\t\tuint8_t r, uint8_t g, uint8_t b);\n"
  },
  {
    "path": "include/rem_vidconv.h",
    "content": "/**\n * @file rem_vidconv.h  Video colorspace conversion\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nvoid vidconv(struct vidframe *dst, const struct vidframe *src,\n\t     struct vidrect *r);\nvoid vidconv_aspect(struct vidframe *dst, const struct vidframe *src,\n\t\t    struct vidrect *r);\nvoid vidconv_center(struct vidframe *dst, const struct vidframe *src,\n\t\t    struct vidrect *r);\n"
  },
  {
    "path": "include/rem_video.h",
    "content": "/**\n * @file rem_video.h  Wrapper for all Video header files\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#include \"rem_vid.h\"\n#include \"rem_vidmix.h\"\n#include \"rem_vidconv.h\"\n#include \"rem_avc.h\"\n"
  },
  {
    "path": "include/rem_vidmix.h",
    "content": "/**\n * @file rem_vidmix.h  Video Mixer\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nstruct vidmix;\nstruct vidmix_source;\n\n/**\n * Video mixer frame handler\n *\n * @param ts    Timestamp\n * @param frame Video frame\n * @param arg   Handler argument\n */\ntypedef void (vidmix_frame_h)(uint64_t ts, const struct vidframe *frame,\n\t\t\t      void *arg);\n\nint  vidmix_alloc(struct vidmix **mixp);\nvoid vidmix_set_fmt(struct vidmix *mix, enum vidfmt fmt);\nint  vidmix_source_alloc(struct vidmix_source **srcp, struct vidmix *mix,\n\t\t\t const struct vidsz *sz, unsigned fps, bool content,\n\t\t\t vidmix_frame_h *fh, void *arg);\nbool vidmix_source_isenabled(const struct vidmix_source *src);\nbool vidmix_source_isrunning(const struct vidmix_source *src);\nuint32_t vidmix_source_get_pidx(const struct vidmix_source *src);\nvoid *vidmix_source_get_focus(const struct vidmix_source *src);\nvoid vidmix_source_enable(struct vidmix_source *src, bool enable);\nint  vidmix_source_start(struct vidmix_source *src);\nvoid vidmix_source_stop(struct vidmix_source *src);\nint  vidmix_source_set_size(struct vidmix_source *src, const struct vidsz *sz);\nvoid vidmix_source_set_rate(struct vidmix_source *src, unsigned fps);\nvoid vidmix_source_set_content_hide(struct vidmix_source *src, bool hide);\nvoid vidmix_source_toggle_selfview(struct vidmix_source *src);\nvoid vidmix_source_set_focus(struct vidmix_source *src,\n\t\t\t     const struct vidmix_source *focus_src,\n\t\t\t     bool focus_full);\nvoid vidmix_source_set_focus_idx(struct vidmix_source *src, uint32_t pidx);\nvoid vidmix_source_put(struct vidmix_source *src,\n\t\t       const struct vidframe *frame);\n"
  },
  {
    "path": "mk/Doxyfile",
    "content": "# Doxyfile 1.4.7\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\nPROJECT_NAME           = libre\nPROJECT_NUMBER         = 4.8.0\nOUTPUT_DIRECTORY       = ../re-dox\nCREATE_SUBDIRS         = NO\nOUTPUT_LANGUAGE        = English\n#USE_WINDOWS_ENCODING   = NO\nBRIEF_MEMBER_DESC      = YES\nREPEAT_BRIEF           = YES\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\nALWAYS_DETAILED_SEC    = NO\nINLINE_INHERITED_MEMB  = NO\nFULL_PATH_NAMES        = NO\nSTRIP_FROM_PATH        = \nSTRIP_FROM_INC_PATH    = \nSHORT_NAMES            = NO\nJAVADOC_AUTOBRIEF      = YES\nMULTILINE_CPP_IS_BRIEF = NO\n#DETAILS_AT_TOP         = NO\nINHERIT_DOCS           = YES\nSEPARATE_MEMBER_PAGES  = NO\nTAB_SIZE               = 8\nALIASES                = \nOPTIMIZE_OUTPUT_FOR_C  = YES\nOPTIMIZE_OUTPUT_JAVA   = NO\n#BUILTIN_STL_SUPPORT    = NO\nDISTRIBUTE_GROUP_DOC   = NO\nSUBGROUPING            = YES\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\nEXTRACT_ALL            = NO\nEXTRACT_PRIVATE        = NO\nEXTRACT_STATIC         = NO\nEXTRACT_LOCAL_CLASSES  = YES\nEXTRACT_LOCAL_METHODS  = NO\nHIDE_UNDOC_MEMBERS     = YES\nHIDE_UNDOC_CLASSES     = YES\nHIDE_FRIEND_COMPOUNDS  = NO\nHIDE_IN_BODY_DOCS      = NO\nINTERNAL_DOCS          = NO\nCASE_SENSE_NAMES       = YES\nHIDE_SCOPE_NAMES       = NO\nSHOW_INCLUDE_FILES     = YES\nINLINE_INFO            = YES\nSORT_MEMBER_DOCS       = YES\nSORT_BRIEF_DOCS        = NO\nSORT_BY_SCOPE_NAME     = NO\nGENERATE_TODOLIST      = YES\nGENERATE_TESTLIST      = YES\nGENERATE_BUGLIST       = YES\nGENERATE_DEPRECATEDLIST= YES\nENABLED_SECTIONS       = \nMAX_INITIALIZER_LINES  = 30\nSHOW_USED_FILES        = YES\n#SHOW_DIRECTORIES       = NO\nFILE_VERSION_FILTER    = \n#---------------------------------------------------------------------------\n# configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\nQUIET                  = YES\nWARNINGS               = YES\nWARN_IF_UNDOCUMENTED   = YES\nWARN_IF_DOC_ERROR      = YES\nWARN_NO_PARAMDOC       = YES\nWARN_FORMAT            = \"$file:$line: $text\"\nWARN_LOGFILE           = \n#---------------------------------------------------------------------------\n# configuration options related to the input files\n#---------------------------------------------------------------------------\nINPUT                  = include src docs rem\nFILE_PATTERNS          = *.c \\\n                         *.h \\\n                         *.dox\nRECURSIVE              = YES\nEXCLUDE                = test.c \\\n\t\t\t src/md5/md5.h src/md5/md5.c\n\nEXCLUDE_SYMLINKS       = NO\nEXCLUDE_PATTERNS       = */.svn/*\nEXAMPLE_PATH           = .\nEXAMPLE_PATTERNS       = *\nEXAMPLE_RECURSIVE      = NO\nIMAGE_PATH             = \nINPUT_FILTER           = \nFILTER_PATTERNS        = \nFILTER_SOURCE_FILES    = NO\n#---------------------------------------------------------------------------\n# configuration options related to source browsing\n#---------------------------------------------------------------------------\nSOURCE_BROWSER         = YES\nINLINE_SOURCES         = NO\nSTRIP_CODE_COMMENTS    = YES\nREFERENCED_BY_RELATION = YES\nREFERENCES_RELATION    = YES\n#REFERENCES_LINK_SOURCE = YES\n#USE_HTAGS              = NO\nVERBATIM_HEADERS       = YES\n#---------------------------------------------------------------------------\n# configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\nALPHABETICAL_INDEX     = YES\n#COLS_IN_ALPHA_INDEX    = 5\nIGNORE_PREFIX          = \n#---------------------------------------------------------------------------\n# configuration options related to the HTML output\n#---------------------------------------------------------------------------\nGENERATE_HTML          = YES\nHTML_OUTPUT            = html\nHTML_FILE_EXTENSION    = .html\nHTML_HEADER            = \nHTML_FOOTER            = \nHTML_STYLESHEET        = \n#HTML_ALIGN_MEMBERS     = YES\nGENERATE_HTMLHELP      = NO\nCHM_FILE               = \nHHC_LOCATION           = \nGENERATE_CHI           = NO\nBINARY_TOC             = NO\nTOC_EXPAND             = NO\nDISABLE_INDEX          = NO\nENUM_VALUES_PER_LINE   = 4\nGENERATE_TREEVIEW      = NO\nTREEVIEW_WIDTH         = 250\n#---------------------------------------------------------------------------\n# configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\nGENERATE_LATEX         = NO\nLATEX_OUTPUT           = latex\nLATEX_CMD_NAME         = latex\nMAKEINDEX_CMD_NAME     = makeindex\nCOMPACT_LATEX          = NO\n#PAPER_TYPE             = a4wide\nEXTRA_PACKAGES         = \nLATEX_HEADER           = \nPDF_HYPERLINKS         = NO\nUSE_PDFLATEX           = NO\nLATEX_BATCHMODE        = NO\nLATEX_HIDE_INDICES     = NO\n#---------------------------------------------------------------------------\n# configuration options related to the RTF output\n#---------------------------------------------------------------------------\nGENERATE_RTF           = NO\nRTF_OUTPUT             = rtf\nCOMPACT_RTF            = NO\nRTF_HYPERLINKS         = NO\nRTF_STYLESHEET_FILE    = \nRTF_EXTENSIONS_FILE    = \n#---------------------------------------------------------------------------\n# configuration options related to the man page output\n#---------------------------------------------------------------------------\nGENERATE_MAN           = NO\nMAN_OUTPUT             = man\nMAN_EXTENSION          = .3\nMAN_LINKS              = NO\n#---------------------------------------------------------------------------\n# configuration options related to the XML output\n#---------------------------------------------------------------------------\nGENERATE_XML           = NO\nXML_OUTPUT             = xml\nXML_PROGRAMLISTING     = YES\n#---------------------------------------------------------------------------\n# configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\nGENERATE_AUTOGEN_DEF   = NO\n#---------------------------------------------------------------------------\n# configuration options related to the Perl module output\n#---------------------------------------------------------------------------\nGENERATE_PERLMOD       = NO\nPERLMOD_LATEX          = NO\nPERLMOD_PRETTY         = YES\nPERLMOD_MAKEVAR_PREFIX = \n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor   \n#---------------------------------------------------------------------------\nENABLE_PREPROCESSING   = YES\nMACRO_EXPANSION        = YES\nEXPAND_ONLY_PREDEF     = YES\nSEARCH_INCLUDES        = YES\nINCLUDE_PATH           = include\nINCLUDE_FILE_PATTERNS  = \nPREDEFINED             =\nEXPAND_AS_DEFINED      = \nSKIP_FUNCTION_MACROS   = YES\n#---------------------------------------------------------------------------\n# Configuration::additions related to external references   \n#---------------------------------------------------------------------------\nTAGFILES               = \nGENERATE_TAGFILE       = \nALLEXTERNALS           = NO\nEXTERNAL_GROUPS        = YES\n#PERL_PATH              = /usr/bin/perl\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool   \n#---------------------------------------------------------------------------\n#CLASS_DIAGRAMS         = YES\nHIDE_UNDOC_RELATIONS   = YES\nHAVE_DOT               = YES\nCLASS_GRAPH            = YES\nCOLLABORATION_GRAPH    = YES\nGROUP_GRAPHS           = YES\nUML_LOOK               = NO\nTEMPLATE_RELATIONS     = NO\nINCLUDE_GRAPH          = YES\nINCLUDED_BY_GRAPH      = YES\n#CALL_GRAPH             = YES\ttodo: disabled to run faster\n#CALLER_GRAPH           = YES\nGRAPHICAL_HIERARCHY    = YES\nDIRECTORY_GRAPH        = YES\nDOT_IMAGE_FORMAT       = png\nDOT_PATH               = \nDOTFILE_DIRS           = \nDOT_GRAPH_MAX_NODES    = 256\n#MAX_DOT_GRAPH_WIDTH    = 1024\n#MAX_DOT_GRAPH_HEIGHT   = 1024\n#MAX_DOT_GRAPH_DEPTH    = 1000\n#DOT_TRANSPARENT        = NO\nDOT_MULTI_TARGETS      = NO\nGENERATE_LEGEND        = YES\nDOT_CLEANUP            = YES\n#---------------------------------------------------------------------------\n# Configuration::additions related to the search engine   \n#---------------------------------------------------------------------------\nSEARCHENGINE           = NO\n"
  },
  {
    "path": "packaging/CMakeLists.txt",
    "content": "set(CPACK_PACKAGE_NAME libre)\nset(CPACK_PACKAGE_CONTACT \"sreimers\")\nset(CPACK_PACKAGE_VENDOR baresip)\nset(CPACK_PACKAGE_DESCRIPTION_SUMMARY \"Library for Real-Time Communications\")\nset(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})\nset(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})\nset(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})\nset(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})\nset(CPACK_VERBATIM_VARIABLES YES)\nset(CPACK_RESOURCE_FILE_LICENSE \"${CMAKE_SOURCE_DIR}/LICENSE\")\nset(CPACK_RESOURCE_FILE_README \"${CMAKE_SOURCE_DIR}/README.md\")\n\n# Debian\nset(CPACK_DEB_COMPONENT_INSTALL ON)\nset(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)\nset(CPACK_DEBIAN_LIBRARIES_PACKAGE_NAME \"libre\")\nset(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_NAME \"libre-dev\")\nset(CPACK_DEBIAN_PACKAGE_DEPENDS \"libssl3, zlib1g, libc6\")\n\ninclude(CPack)\n"
  },
  {
    "path": "packaging/libre.pc.in",
    "content": "prefix=\"@CMAKE_INSTALL_PREFIX@\"\nexec_prefix=${prefix}\nlibdir=${prefix}/lib\nincludedir=${prefix}/include/re\n\nName: libre\nDescription: @CMAKE_PROJECT_DESCRIPTION@\nVersion: @PROJECT_VERSION@\nURL: @CMAKE_PROJECT_HOMEPAGE_URL@\nLibs: -L${libdir} -l@PC_LIBNAME@\nLibs.private: @PC_LINKLIBS@\nRequires.private: @PC_REQUIRES@\nCflags: -I${includedir}\n"
  },
  {
    "path": "rem/aac/aac.c",
    "content": "/**\n * @file aac.c Advanced Audio Coding\n *\n * Copyright (C) 2018 Creytiv.com\n */\n\n#include <re_types.h>\n#include <rem_aac.h>\n\n\n/*\n * Ref https://wiki.multimedia.cx/index.php/MPEG-4_Audio\n */\n\nenum {\n\tOBJECT_TYPE_AAC_LC = 2\n};\n\n\nstatic const unsigned aac_sample_rates[13] = {\n\t96000, 88200, 64000, 48000, 44100, 32000,\n\t24000, 22050, 16000, 12000, 11025, 8000, 7350\n};\n\n\nstatic const unsigned aac_channels[8] = {\n\t0, 1, 2, 3, 4, 5, 6, 8\n};\n\n\n/**\n * Decode an AAC header\n *\n * @param hdr Decoded AAC header\n * @param p   Packet to decode\n * @param len Packet length\n *\n * @return 0 if success, otherwise errorcode\n */\nint aac_header_decode(struct aac_header *hdr, const uint8_t *p, size_t len)\n{\n\tuint8_t object_type;\n\tuint8_t srate_index;\n\tuint8_t channel_index;\n\n\tif (!hdr || !p || len<2)\n\t\treturn EINVAL;\n\n\tobject_type = (p[0] >> 3) & 0x1f;\n\n\tif (object_type != OBJECT_TYPE_AAC_LC)\n\t\treturn EBADMSG;\n\n\tsrate_index  = (p[0] & 0x07) << 1;\n\tsrate_index |= (p[1] & 0x80) >> 7;\n\n\tchannel_index = (p[1] >> 3) & 0xf;\n\n\tif (srate_index >= RE_ARRAY_SIZE(aac_sample_rates))\n\t\treturn ENOTSUP;\n\tif (channel_index >= RE_ARRAY_SIZE(aac_channels))\n\t\treturn ENOTSUP;\n\n\thdr->sample_rate = aac_sample_rates[srate_index];\n\thdr->channels    = aac_channels[channel_index];\n\thdr->frame_size  = ((p[1] >> 2) & 1) ? 960 : 1024;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "rem/au/fmt.c",
    "content": "/**\n * @file au/fmt.c  Audio formats\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re.h>\n#include <rem_au.h>\n\n\n/* Number of bytes per sample */\nsize_t aufmt_sample_size(enum aufmt fmt)\n{\n\tswitch (fmt) {\n\n\tcase AUFMT_S16LE:   return sizeof(int16_t);\n\tcase AUFMT_S32LE:   return sizeof(int32_t);\n\tcase AUFMT_RAW:     return 1;\n\tcase AUFMT_PCMA:    return 1;\n\tcase AUFMT_PCMU:    return 1;\n\tcase AUFMT_FLOAT:   return sizeof(float);\n\tcase AUFMT_S24_3LE: return 3;\n\tdefault:            return 0;\n\t}\n}\n\n\nconst char *aufmt_name(enum aufmt fmt)\n{\n\tswitch (fmt) {\n\n\tcase AUFMT_S16LE:   return \"S16LE\";\n\tcase AUFMT_S32LE:   return \"S32LE\";\n\tcase AUFMT_PCMA:    return \"PCMA\";\n\tcase AUFMT_PCMU:    return \"PCMU\";\n\tcase AUFMT_FLOAT:   return \"FLOAT\";\n\tcase AUFMT_S24_3LE: return \"S24_3LE\";\n\tcase AUFMT_RAW:     return \"RAW\";\n\tdefault:            return \"???\";\n\t}\n}\n"
  },
  {
    "path": "rem/au/util.c",
    "content": "/**\n * @file util.c  Audio utility functions\n *\n * Copyright (C) 2022 Commend.com - c.spielberger@commend.com\n */\n\n#include <re.h>\n#include <rem_au.h>\n\n\n/**\n * Calculate number of samples from sample rate, channels and packet time\n *\n * @param srate    Sample rate in [Hz]\n * @param channels Number of channels\n * @param ptime    Packet time in [ms]\n *\n * @return Number of samples\n */\nuint32_t au_calc_nsamp(uint32_t srate, uint8_t channels, uint16_t ptime)\n{\n\treturn srate * channels * ptime / 1000;\n}\n"
  },
  {
    "path": "rem/aubuf/ajb.c",
    "content": "/**\n * @file ajb.c  Adaptive Jitter Buffer algorithm\n *\n * Copyright (C) 2022 Commend.com - c.spielberger@commend.com\n *\n * The adaptive jitter buffer algorithm (ajb) for audio buffer can be activated\n * by invoking `aubuf_set_mode(ab, AUBUF_ADAPTIVE)`. The ajb algorithm\n * increases the number of packets in the audio buffer during periods of high\n * network jitter. It reduces the number of packets if the network condition\n * improves.\n *\n * @section jitter Computing the jitter\n *\n * The jitter \\f$j\\f$ is the moving mean absolute deviation (MAD) of the\n * time period buffered in `aubuf` \\f$D\\f$. It is estimated by the iterative\n * formula\n *\n * \\f$j_n = j_{n-1} + (|D - \\overline{D}| - j_{n-1})\\kappa\\f$\n *\n * where \\f$\\kappa\\f$ is the weight that influences how fast the jitter value\n * changes.\n *\n * We choose a higher value for the weight \\f$\\kappa\\f$ if\n * \\f$|D - \\overline{D}| > j_{n-1}\\f$. Thus the jitter rises fast if e.g.\n * suddenly a network jitter appears. In contrast when the network condition\n * improves the jitter value slowly shrinks. The reason for different\n * rising and falling speed is that we have to react fast to avoid buffer\n * under-runs, whereas reducing of the latency is not so time-critical.\n *\n * In the following sections we will describe how the computed jitter is used\n * to detect situations where the buffered packets should be increased due to\n * a high jitter. We call this situation **Low** situation. When the jitter\n * shrinks below some specific value it is a good idea to reduce the buffer in\n * order to reduce the audio latency. We call this situation **High**\n * situation. Surely, the Low/High situations have to be decided somehow.\n *\n * @section reduce_increase Reduce/Increase buffered packets\n *\n * When a Low situation is detected we increase the number of packets in\n * `aubuf` by holding back a packet during one call to function\n * `aubuf_read_auframe()`. While when a High situation is detected we reduce\n * the number of packets by reading another audio frame. This overwrites one\n * frame. By means of a silence detection `aubuf` is able to drop frames that\n * are not important for the speech quality. This reduces the audio latency\n * down to the value before the High situation.\n *\n * @section smooth_latency Computing a smooth latency\n *\n * The audio frames that are buffered at a concrete point in time in `aubuf`\n * lead to a temporary latency value \\f$D\\f$. Let \\f$f_0, ..., f_m\\f$ be the\n * audio frames currently stored in `aubuf`. Then\n *\n * \\f$D = t_m - t_0 + D_p\\f$\n *\n * where \\f$t_i\\f$ is the timestamp of frame \\f$f_i\\f$ and \\f$D_p\\f$ is the\n * packet time `ptime`. The packet time is a constant that is specified at the\n * beginning of a SIP call.\n *\n * The temporary latency \\f$D\\f$ is discontinuous over time and not adequate\n * for deciding or detecting Low or High situations. Therefore we again use an\n * exponential moving average (EMA) to smooth \\f$D\\f$. Let \\f$\\kappa\\f$ be an\n * adequate moving average speed factor, then the smoothed latency\n *\n * \\f$l_n = l_{n-1} + (D - l_{n-1})\\kappa\\f$.\n *\n * We use \\f$l_n\\f$ to estimate the average buffer time \\f$\\overline{D}\\f$.\n * Low/High situations are decided when the smoothed latency \\f$l_n\\f$ runs out\n * of some boundaries that are computed from the jitter.\n *\n * @section low_high Deciding Low/High situations\n *\n * During each iteration (each call to `aubuf_write_frame()`) the jitter and\n * the latency are computed. Additionally we compute the bottom boundary\n * \\f$D_b\\f$ and the top boundary \\f$D_t\\f$ with\n *\n * \\f$D_b = max(m . l_n, \\frac{2}{3} D_p)\\f$ and\n *\n * \\f$D_t = max(M . l_n, D_b + D_p)\\f$,\n *\n * where \\f$1 < m < M\\f$.\n *\n * Finally we have everything for deciding Low and High situations. That is if\n * \\f$l_n\\f$ moves out of the boundaries\n *\n * \\f$D_b < l_n < D_t\\f$,\n *\n * then we fire a Low/High.\n *\n * @section early_adjustment Early adjustment of the latency\n *\n * If we detect a Low/High situation we increase/reduce the number of packets.\n * Now we immediately increment/decrement the smoothed latency \\f$l_n\\f$ by\n * \\f$D_p\\f$.\n * Thus early adjustment for a Low situation is\n *\n * \\f$l_{n+1} = l_n + D_p\\f$\n *\n * and for a High situation\n *\n * \\f$l_{n+1} = l_n - D_p\\f$.\n *\n * This avoids multiple Low/High detections in a row.\n *\n * @section silence Silence detection\n *\n * It is preferable to drop an audio frame only if it contains nearly silence.\n *\n * @section symbols Math symbols vs. C-variables\n *\n * In order to avoid float computation we use micro seconds to measure the time\n * differences, the jitter and buffer time. Symbols used in this document are\n * mapped to the C-variables in `src/aubuf/ajb.c` like this table shows:\n *\n * Symbol  |Variable\n * --------|--------\n *  \\f$j_n\\f$   | `jitter`\n *  \\f$D\\f$     | `buftime`\n *  \\f$l_n\\f$   | `avbuftime`\n *  \\f$D_b\\f$   | `bufmin`\n *  \\f$D_t\\f$   | `bufmax`\n *  \\f$D_p\\f$   | `ptime`\n */\n\n#include <stdlib.h>\n#include <re.h>\n#include <rem_au.h>\n#include <rem_aulevel.h>\n#include <rem_auframe.h>\n#include <rem_aubuf.h>\n#include \"ajb.h\"\n\n#define DEBUG_LEVEL 5\n\n/**\n * @defgroup The adaptive jitter computation is done by means of an exponential\n * moving average (EMA).\n *￼j_i = j_{i-1} + a (c - j_{i-1})\n *\n * Where $a$ ist the EMA coefficient and $c$ is the current value.\n */\n\nenum {\n\tJITTER_EMA_COEFF   = 512,  /* Divisor for jitter EMA coefficient */\n\tJITTER_UP_SPEED    = 64,   /* 64 times faster up than down       */\n\tBUFTIME_EMA_COEFF  = 128,  /* Divisor for Buftime EMA coeff.     */\n\tBUFTIME_LO         = 125,  /* 125% of jitter                     */\n\tBUFTIME_HI         = 175,  /* 175% of jitter                     */\n\tSKEW_MAX           = 10,   /* Max skew in [ms]                   */\n};\n\n\n/** Adaptive jitter buffer statistics */\nstruct ajb {\n\tint32_t jitter;      /**< Jitter in [us]                  */\n\tmtx_t *lock;\n\n\tuint64_t ts;         /**< previous timestamp              */\n\tuint64_t ts0;        /**< reference timestamp             */\n\tuint64_t tr0;        /**< reference time of arrival       */\n\tuint64_t tr00;       /**< arrival of first packet         */\n#ifdef RE_AUBUF_TRACE\n\tstruct {\n\t\tint32_t d;\n\t\tuint32_t buftime;\n\t\tuint32_t bufmin;\n\t\tuint32_t bufmax;\n\t\tenum ajb_state as;\n\t} plot;\n\n\tchar buf[136];       /**< Buffer for trace                */\n#endif\n\n\tenum ajb_state as;   /**< computed jitter buffer state    */\n\n\tint32_t avbuftime;   /**< average buffered time [us]      */\n\tbool started;        /**< Started flag                    */\n\tsize_t wish_sz;      /**< Wish size of buffer [Bytes]     */\n\tuint32_t dropped;    /**< Dropped audio frames counter    */\n\tdouble silence;      /**< Silence audio level             */\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct ajb *ajb = arg;\n\n\tmem_deref(ajb->lock);\n}\n\n\n#ifdef RE_AUBUF_TRACE\nstatic void plot_ajb(struct ajb *ajb, uint64_t tr)\n{\n\tuint32_t treal;\n\n\tif (!ajb->tr00)\n\t\tajb->tr00 = tr;\n\n\ttreal = (uint32_t) (tr - ajb->tr00);\n\tre_snprintf(ajb->buf, sizeof(ajb->buf),\n\t\t    \"%s, 0x%p, %u, %i, %u, %u, %u, %i, %i, %u\",\n\t\t\t__func__,               /* row 1  - grep */\n\t\t\tajb,                    /* row 2  - grep optional */\n\t\t\ttreal,                  /* row 3  - plot x-axis */\n\t\t\tajb->plot.d,            /* row 4  - plot */\n\t\t\tajb->jitter,            /* row 5  - plot */\n\t\t\tajb->plot.buftime,      /* row 6  - plot */\n\t\t\tajb->avbuftime,         /* row 7  - plot */\n\t\t\tajb->plot.bufmin,       /* row 8  - plot */\n\t\t\tajb->plot.bufmax,       /* row 9  - plot */\n\t\t\tajb->plot.as);          /* row 10 - plot */\n\tre_trace_event(\"ajb\", \"plot\", 'P', NULL, RE_TRACE_ARG_STRING_COPY,\n\t\t       \"line\", ajb->buf);\n}\n#endif\n\n\n/**\n * Initializes the adaptive jitter buffer statistics\n *\n * @param silence Silence audio level\n * @param wish_sz Wish size of buffer [Bytes]\n *\n * @return ajb    Adaptive jitter buffer statistics\n */\nstruct ajb *ajb_alloc(double silence, size_t wish_sz)\n{\n\tstruct ajb *ajb;\n\tint err;\n\n\tajb = mem_zalloc(sizeof(*ajb), destructor);\n\tif (!ajb)\n\t\treturn NULL;\n\n\terr = mutex_alloc(&ajb->lock);\n\tif (err)\n\t\tgoto out;\n\n\tajb->ts0 = 0;\n\tajb->tr0 = 0;\n\tajb->as = AJB_GOOD;\n\tajb->silence = silence;\n\tajb->wish_sz = wish_sz;\n\nout:\n\tif (err)\n\t\tajb = mem_deref(ajb);\n\n\treturn ajb;\n}\n\n\nvoid ajb_reset(struct ajb *ajb)\n{\n\tif (!ajb)\n\t\treturn;\n\n\tmtx_lock(ajb->lock);\n\tajb->ts  = 0;\n\tajb->ts0 = 0;\n\tajb->tr0 = 0;\n\n\t/* We start with wish size. */\n\tajb->started = false;\n\tajb->as = AJB_GOOD;\n\tmtx_unlock(ajb->lock);\n}\n\n\n/**\n * Computes the jitter for audio frame arrival.\n *\n * @param ajb     Adaptive jitter buffer statistics\n * @param af      Audio frame\n * @param cur_sz  Current aubuf size\n */\nvoid ajb_calc(struct ajb *ajb, const struct auframe *af, size_t cur_sz)\n{\n\tuint64_t tr;                       /**< Real time in [us]            */\n\tuint32_t buftime, bufmax, bufmin;  /**< Buffer time in [us]          */\n\tuint32_t bufwish;                  /**< Buffer wish time in [us]     */\n\tint32_t d;                         /**< Time shift in [us]           */\n\tint32_t da;                        /**< Absolut time shift in [us]   */\n\tint32_t s;                         /**< EMA coefficient              */\n\tuint64_t ts;                       /**< Time stamp                   */\n\tuint64_t ds;                       /**< Time stamp duration          */\n\tuint32_t ptime;                    /**< Packet time [us]             */\n\tsize_t szdiv;\n\n\tif (!ajb || !af || !af->srate)\n\t\treturn;\n\n\tmtx_lock(ajb->lock);\n\tts = af->timestamp;\n\ttr = tmr_jiffies_usec();\n\tif (!ajb->ts0)\n\t\tgoto out;\n\n\tds = ts - ajb->ts0;\n\td = (int32_t) (int64_t) ( (tr - ajb->tr0) - ds );\n\tda = abs(d);\n\n\tszdiv = af->srate * af->ch *  aufmt_sample_size(af->fmt) / 1000;\n\tbuftime = (uint32_t) (cur_sz * 1000 / szdiv);\n\tbufwish = (uint32_t) (ajb->wish_sz * 1000 / szdiv);\n\tif (ajb->started) {\n\t\tajb->avbuftime += ((int32_t) buftime - ajb->avbuftime) /\n\t\t\t\t  BUFTIME_EMA_COEFF;\n\t\tif (ajb->avbuftime < 0)\n\t\t\tajb->avbuftime = 0;\n\t}\n\telse {\n\t\t/* Directly after \"filling\" of aubuf compute a good start value\n\t\t * fitting to wish size. */\n\t\tajb->avbuftime = buftime;\n\t\tajb->jitter = ajb->avbuftime * 100 * 2 /\n\t\t\t(BUFTIME_LO + BUFTIME_HI);\n\t\tajb->started = true;\n\t}\n\n\ts = da > ajb->jitter ? JITTER_UP_SPEED : 1;\n\n\tajb->jitter += (da - ajb->jitter) * s / JITTER_EMA_COEFF;\n\tif (ajb->jitter < 0)\n\t\tajb->jitter = 0;\n\n\tbufmin = (uint32_t) ajb->jitter * BUFTIME_LO / 100;\n\tbufmax = (uint32_t) ajb->jitter * BUFTIME_HI / 100;\n\n\tptime = (uint32_t) (af->sampc * AUDIO_TIMEBASE / (af->srate * af->ch));\n\tbufmin = MAX(bufmin, ptime * 2 / 3);\n\tif (bufwish >= ptime)\n\t\tbufmin = MAX(bufmin, bufwish - ptime / 3);\n\n\tbufmax = MAX(bufmax, bufmin + 7 * ptime / 6);\n\n\t/* reset time base if a frame is missing or skew is too high */\n\tif (ts - ajb->ts > ptime || da > SKEW_MAX * 1000)\n\t\tajb->ts0 = 0;\n\n\tif ((uint32_t) ajb->avbuftime < bufmin)\n\t\tajb->as = AJB_LOW;\n\telse if ((uint32_t) ajb->avbuftime > bufmax)\n\t\tajb->as = AJB_HIGH;\n\telse\n\t\tajb->as = AJB_GOOD;\n\n#ifdef RE_AUBUF_TRACE\n\tajb->plot.d = d;\n\tajb->plot.buftime = buftime;\n\tajb->plot.bufmin  = bufmin;\n\tajb->plot.bufmax  = bufmax;\n\tplot_ajb(ajb, tr / 1000);\n#endif\nout:\n\tajb->ts = ts;\n\tif (!ajb->ts0) {\n\t\tajb->ts0 = ts;\n\t\tajb->tr0 = tr;\n\t}\n\tmtx_unlock(ajb->lock);\n}\n\n\nvoid ajb_set_ts0(struct ajb *ajb, uint64_t timestamp)\n{\n\tif (!ajb)\n\t\treturn;\n\n\tmtx_lock(ajb->lock);\n\tajb->ts  = timestamp;\n\tajb->ts0 = timestamp;\n\tajb->tr0 = tmr_jiffies_usec();\n\tmtx_unlock(ajb->lock);\n}\n\n\n/**\n * Get the state of the Adaptive Jitter Buffer\n *\n * @param ajb Adaptive Jitter Buffer state\n * @param af  Audio frame\n *\n * @return Computed jitter buffer state\n */\nenum ajb_state ajb_get(struct ajb *ajb, struct auframe *af)\n{\n\tenum ajb_state as = AJB_GOOD;\n\tuint32_t ptime;      /**< Packet time [us]                */\n\n\tif (!ajb || !af || !af->srate || !af->sampc)\n\t\treturn AJB_GOOD;\n\n\tmtx_lock(ajb->lock);\n\n\t/* ptime in [us] */\n\tptime = (uint32_t) (af->sampc * AUDIO_TIMEBASE / (af->srate * af->ch));\n\tif (!ajb->avbuftime)\n\t\tgoto out;\n\n\tif (ajb->as == AJB_GOOD ||\n\t    (ajb->silence < 0. && auframe_level(af) > ajb->silence))\n\t\tgoto out;\n\n\tas = ajb->as;\n\tif (as == AJB_HIGH) {\n\t\t/* early adjustment of avbuftime */\n\t\tajb->avbuftime -= ptime;\n\t\tajb->as = AJB_GOOD;\n#ifdef RE_AUBUF_TRACE\n\t\tajb->plot.as = AJB_HIGH;\n\t\tplot_ajb(ajb, tmr_jiffies());\n\t\tajb->plot.as = AJB_GOOD;\n#endif\n\t}\n\telse if (as == AJB_LOW) {\n\t\t/* early adjustment */\n\t\tajb->avbuftime += ptime;\n\t\tajb->as = AJB_GOOD;\n#ifdef RE_AUBUF_TRACE\n\t\tajb->plot.as = AJB_LOW;\n\t\tplot_ajb(ajb, tmr_jiffies());\n\t\tajb->plot.as = AJB_GOOD;\n#endif\n\t}\n\nout:\n\tmtx_unlock(ajb->lock);\n\treturn as;\n}\n\n\nint32_t ajb_debug(const struct ajb *ajb)\n{\n\tint32_t jitter;\n\n\tif (!ajb)\n\t\treturn 0;\n\n\tmtx_lock(ajb->lock);\n\tjitter = ajb->jitter;\n\tmtx_unlock(ajb->lock);\n\tre_printf(\"  ajb jitter: %d, ajb avbuftime: %d\\n\", jitter / 1000,\n\t\t  ajb->avbuftime);\n\n\treturn jitter;\n}\n"
  },
  {
    "path": "rem/aubuf/ajb.h",
    "content": "/**\n * @file ajb.h  Adaptive Jitter Buffer interface\n *\n * Copyright (C) 2022 Commend.com - c.spielberger@commend.com\n */\n\nenum ajb_state {\n\tAJB_GOOD = 0,\n\tAJB_LOW,\n\tAJB_HIGH,\n};\n\nstruct ajb;\n\nstruct ajb *ajb_alloc(double silence, size_t wish_sz);\nvoid ajb_reset(struct ajb *ajb);\nvoid ajb_calc(struct ajb *ajb, const struct auframe *af, size_t sampc);\nenum ajb_state ajb_get(struct ajb *ajb, struct auframe *af);\nint32_t ajb_debug(const struct ajb *ajb);\nvoid ajb_set_ts0(struct ajb *ajb, uint64_t timestamp);\n"
  },
  {
    "path": "rem/aubuf/aubuf.c",
    "content": "/**\n * @file aubuf.c  Audio Buffer\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#undef RE_TRACE_ENABLED\n#if AUBUF_TRACE\n#define RE_TRACE_ENABLED 1\n#endif\n#include <string.h>\n#include <re.h>\n#include <rem_au.h>\n#include <rem_aulevel.h>\n#include <rem_auframe.h>\n#include <rem_aubuf.h>\n#include \"ajb.h\"\n\n\n#define AUBUF_DEBUG 0\n\nenum { POOL_FRAMES = 25 };\n\n/** Locked audio-buffer with almost zero-copy */\nstruct aubuf {\n\tstruct list afl;\n\tstruct mem_pool *pool;\n\tstruct pl *id;          /**< Audio buffer Identifier                 */\n\tmtx_t *lock;\n\tsize_t wish_sz;\n\tsize_t cur_sz;\n\tsize_t max_sz;\n\tsize_t fill_sz;         /**< To fill size                            */\n\tsize_t pkt_sz;          /**< Packet size                             */\n\tsize_t wr_sz;           /**< Written size                            */\n\tbool started;\n\tuint64_t ts;\n\n\tstruct {\n\t\tsize_t or;\n\t\tsize_t ur;\n\t} stats;\n\n\tenum aubuf_mode mode;\n\tstruct ajb *ajb;         /**< Adaptive jitter buffer statistics      */\n\tdouble silence;          /**< Silence volume in negative [dB]        */\n\tbool live;               /**< Live stream switch                     */\n};\n\n\nstruct frame {\n\tstruct le le;\n\tstruct mbuf *mb;\n\tstruct auframe af;\n\tstruct mem_pool_entry *e;\n};\n\n\nstatic void frame_destructor(void *data)\n{\n\tstruct frame *f = data;\n\tlist_unlink(&f->le);\n\tmem_deref(f->mb);\n}\n\n\nstatic void aubuf_destructor(void *arg)\n{\n\tstruct aubuf *ab = arg;\n\n\tmem_deref(ab->lock);\n\tmem_deref(ab->ajb);\n\tmem_deref(ab->id);\n\tmem_deref(ab->pool);\n}\n\n\nstatic void read_auframe(struct aubuf *ab, struct auframe *af)\n{\n\tstruct le *le = ab->afl.head;\n\tsize_t sample_size = aufmt_sample_size(af->fmt);\n\tsize_t sz = auframe_size(af);\n\tuint8_t *p = af->sampv;\n\tbool first = true;\n\n\twhile (le) {\n\t\tstruct frame *f = le->data;\n\t\tsize_t n;\n\n\t\tle = le->next;\n\n\t\tn = min(mbuf_get_left(f->mb), sz);\n\n\t\t(void)mbuf_read_mem(f->mb, p, n);\n\t\tab->cur_sz -= n;\n\n\t\tif (first) {\n\t\t\taf->id\t      = f->af.id;\n\t\t\taf->srate     = f->af.srate;\n\t\t\taf->ch\t      = f->af.ch;\n\t\t\taf->timestamp = f->af.timestamp;\n\t\t\taf->fmt       = f->af.fmt;\n\t\t}\n\n\t\tif (!mbuf_get_left(f->mb)) {\n\t\t\tmem_pool_release(ab->pool, f->e);\n\t\t}\n\t\telse if (af->srate && af->ch && sample_size) {\n\n\t\t\tf->af.timestamp +=\n\t\t\t\tauframe_bytes_to_timestamp(&f->af, n);\n\t\t}\n\n\t\tif (n == sz)\n\t\t\tbreak;\n\n\t\tp  += n;\n\t\tsz -= n;\n\t\tfirst = false;\n\t}\n}\n\n\n/**\n * Allocate a new audio buffer\n *\n * @param abp    Pointer to allocated audio buffer\n * @param min_sz Minimum buffer size\n * @param max_sz Maximum buffer size (0 for no max size)\n *\n * @return 0 for success, otherwise error code\n */\nint aubuf_alloc(struct aubuf **abp, size_t min_sz, size_t max_sz)\n{\n\tstruct aubuf *ab;\n\tint err;\n\n\tif (!abp)\n\t\treturn EINVAL;\n\n\tab = mem_zalloc(sizeof(*ab), aubuf_destructor);\n\tif (!ab)\n\t\treturn ENOMEM;\n\n\terr = mem_pool_alloc(&ab->pool, POOL_FRAMES, sizeof(struct frame),\n\t\t\t     frame_destructor);\n\tif (err)\n\t\tgoto out;\n\n\terr = mutex_alloc(&ab->lock);\n\tif (err)\n\t\tgoto out;\n\n\tab->wish_sz = min_sz;\n\tab->max_sz  = max_sz;\n\tab->fill_sz = min_sz;\n\tab->live    = true;\n\n out:\n\tif (err)\n\t\tmem_deref(ab);\n\telse\n\t\t*abp = ab;\n\n\treturn err;\n}\n\n\n/**\n * Set buffer id.\n *\n * @param ab  Audio buffer.\n * @param id  Identifier.\n */\nvoid aubuf_set_id(struct aubuf *ab, struct pl *id)\n{\n\tif (!ab)\n\t\treturn;\n\n\tmtx_lock(ab->lock);\n\tab->id = mem_ref(id);\n\tmtx_unlock(ab->lock);\n}\n\n\n/**\n * Sets the live stream flag on/off. If activated the audio buffer drops old\n * frames on first read to keep the latency under `min_sz` bytes on startup.\n * Default: `live` is true.\n *\n * @param ab   Audio buffer\n * @param live Live flag\n */\nvoid aubuf_set_live(struct aubuf *ab, bool live)\n{\n\tif (!ab)\n\t\treturn;\n\n\tab->live = live;\n}\n\n\nvoid aubuf_set_mode(struct aubuf *ab, enum aubuf_mode mode)\n{\n\tif (!ab)\n\t\treturn;\n\n\tab->mode = mode;\n}\n\n\n/**\n * Sets the volume level for silence\n *\n * @param ab       Audio buffer\n * @param silence  Volume level in negative [dB]\n */\nvoid aubuf_set_silence(struct aubuf *ab, double silence)\n{\n\tif (!ab)\n\t\treturn;\n\n\tab->silence = silence;\n}\n\n\n/**\n * Resize audio buffer (flushes aubuf)\n *\n * @param ab     Audio buffer\n * @param min_sz Minimum buffer size\n * @param max_sz Maximum buffer size (0 for no max size)\n *\n * @return 0 for success, otherwise error code\n */\nint aubuf_resize(struct aubuf *ab, size_t min_sz, size_t max_sz)\n{\n\tif (!ab)\n\t\treturn EINVAL;\n\n\tmtx_lock(ab->lock);\n\tab->wish_sz = min_sz;\n\tab->max_sz  = max_sz;\n\tmtx_unlock(ab->lock);\n\n\taubuf_flush(ab);\n\n\treturn 0;\n}\n\n\nstatic bool frame_less_equal(struct le *le1, struct le *le2, void *arg)\n{\n\tstruct frame *frame1 = le1->data;\n\tstruct frame *frame2 = le2->data;\n\t(void)arg;\n\n\treturn frame1->af.timestamp <= frame2->af.timestamp;\n}\n\n\n/**\n * Append a PCM-buffer to the end of the audio buffer\n *\n * @param ab Audio buffer\n * @param mb Mbuffer with PCM samples\n * @param af Audio frame (optional)\n *\n * @return 0 for success, otherwise error code\n */\nint aubuf_append_auframe(struct aubuf *ab, struct mbuf *mb,\n\t\t\t const struct auframe *af)\n{\n\tstruct frame *f;\n\tsize_t sz;\n\n\tif (!ab || !mb)\n\t\treturn EINVAL;\n\n\tstruct mem_pool_entry *e = mem_pool_borrow_extend(ab->pool);\n\tif (!e)\n\t\treturn ENOMEM;\n\n\tf    = mem_pool_member(e);\n\tf->e = e;\n\n\tf->mb = mem_ref(mb);\n\tif (af)\n\t\tf->af = *af;\n\n\tsz = mbuf_get_left(mb);\n\n\tmtx_lock(ab->lock);\n\tab->pkt_sz = sz;\n\tif (ab->fill_sz >= ab->pkt_sz)\n\t\tab->fill_sz -= ab->pkt_sz;\n\n\tif (!f->af.timestamp && f->af.srate && f->af.ch) {\n\t\tf->af.timestamp =\n\t\t\tauframe_bytes_to_timestamp(&f->af, ab->wr_sz);\n\t}\n\n\tlist_insert_sorted(&ab->afl, frame_less_equal, NULL, &f->le, f);\n\tab->cur_sz += sz;\n\tab->wr_sz += sz;\n\n\tif (ab->max_sz && ab->cur_sz > ab->max_sz) {\n\t\t++ab->stats.or;\n\t\tRE_TRACE_ID_INSTANT(\"aubuf\", \"overrun\", ab->id);\n\t\tf = list_ledata(ab->afl.head);\n\t\tif (f) {\n\t\t\tab->cur_sz -= mbuf_get_left(f->mb);\n\t\t\tmem_pool_release(ab->pool, f->e);\n\t\t}\n\t}\n\n\tmtx_unlock(ab->lock);\n\treturn 0;\n}\n\n\n/**\n * Write PCM samples to the audio buffer\n *\n * @param ab Audio buffer\n * @param af Audio frame\n *\n * @return 0 for success, otherwise error code\n */\nint aubuf_write_auframe(struct aubuf *ab, const struct auframe *af)\n{\n\tstruct mbuf *mb;\n\tsize_t sz;\n\tsize_t sample_size;\n\tbool ajb;\n\tint err;\n\n\tif (!ab || !af)\n\t\treturn EINVAL;\n\tsample_size = aufmt_sample_size(af->fmt);\n\tif (sample_size)\n\t\tsz = af->sampc * aufmt_sample_size(af->fmt);\n\telse\n\t\tsz = af->sampc;\n\n\tmb = mbuf_alloc(sz);\n\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\t(void)mbuf_write_mem(mb, af->sampv, sz);\n\tmb->pos = 0;\n\n\terr = aubuf_append_auframe(ab, mb, af);\n\n\tmtx_lock(ab->lock);\n\tmem_deref(mb);\n\tajb = !ab->fill_sz && ab->ajb;\n\tmtx_unlock(ab->lock);\n\n\tif (ajb)\n\t\tajb_calc(ab->ajb, af, ab->cur_sz);\n\n\treturn err;\n}\n\n\n/**\n * Read PCM samples from the audio buffer. If there is not enough data\n * in the audio buffer, silence will be read.\n *\n * @param ab Audio buffer\n * @param af Audio frame (af.sampv, af.sampc and af.fmt needed)\n */\nvoid aubuf_read_auframe(struct aubuf *ab, struct auframe *af)\n{\n\tsize_t sz;\n\tbool filling;\n\tenum ajb_state as;\n\tbool drop;\n\n\tif (!ab || !af)\n\t\treturn;\n\n\tsz = auframe_size(af);\n\n\tmtx_lock(ab->lock);\n\n\tif (!ab->ajb && ab->mode == AUBUF_ADAPTIVE)\n\t\tab->ajb = ajb_alloc(ab->silence, ab->wish_sz);\n\n\tas = ajb_get(ab->ajb, af);\n\tif (as == AJB_LOW) {\n#if AUBUF_DEBUG\n\t\t(void)re_printf(\"aubuf: inc buffer due to high jitter\\n\");\n\t\tajb_debug(ab->ajb);\n#endif\n\t\tgoto out;\n\t}\n\n\tRE_TRACE_ID_INSTANT_I(\"aubuf\", \"cur_sz_ms\",\n\t\t\t      auframe_bytes_to_ms(af, ab->cur_sz), ab->id);\n\n\tif (ab->fill_sz || ab->cur_sz < sz) {\n\t\tif (!ab->fill_sz) {\n\t\t\t++ab->stats.ur;\n\t\t\tRE_TRACE_ID_INSTANT(\"aubuf\", \"underrun\", ab->id);\n\t\t}\n\n\t\tif (!ab->fill_sz)\n\t\t\tajb_set_ts0(ab->ajb, 0);\n\n\t\tfilling = ab->fill_sz > 0;\n\t\tmemset(af->sampv, 0, sz);\n\t\tif (filling) {\n\t\t\tRE_TRACE_ID_INSTANT(\"aubuf\", \"filling\", ab->id);\n\t\t\tgoto out;\n\t\t}\n\t\telse\n\t\t\tab->fill_sz = ab->wish_sz;\n\t}\n\n\t/* on first read drop old frames */\n\tdrop = ab->live && !ab->started && ab->wish_sz;\n\twhile (drop && ab->cur_sz > ab->wish_sz) {\n\t\tstruct frame *f = list_ledata(ab->afl.head);\n\t\tif (f) {\n\t\t\tab->cur_sz -= mbuf_get_left(f->mb);\n\t\t\tmem_pool_release(ab->pool, f->e);\n\t\t}\n\t}\n\n\tab->started = true;\n\tread_auframe(ab, af);\n\tif (as == AJB_HIGH) {\n#if AUBUF_DEBUG\n\t\t(void)re_printf(\"aubuf: drop a frame to reduce latency\\n\");\n\t\tajb_debug(ab->ajb);\n#endif\n\t\tread_auframe(ab, af);\n\t}\n\n out:\n\n\tif (ab->fill_sz && ab->fill_sz < ab->pkt_sz) {\n\t\tif (ab->fill_sz >= sz)\n\t\t\tab->fill_sz -= sz;\n\t\telse\n\t\t\tab->fill_sz = 0;\n\t}\n\n\tmtx_unlock(ab->lock);\n}\n\n\n/**\n * Timed read PCM samples from the audio buffer. If there is not enough data\n * in the audio buffer, silence will be read.\n *\n * @param ab    Audio buffer\n * @param ptime Packet time in [ms]\n * @param p     Buffer where PCM samples are read into\n * @param sz    Number of bytes to read\n *\n * @note This does the same as aubuf_read() except that it also takes\n *       timing into consideration.\n *\n * @return 0 if valid PCM was read, ETIMEDOUT if no PCM is ready yet\n */\nint aubuf_get(struct aubuf *ab, uint32_t ptime, uint8_t *p, size_t sz)\n{\n\tuint64_t now;\n\tint err = 0;\n\n\tif (!ab || !ptime)\n\t\treturn EINVAL;\n\n\tmtx_lock(ab->lock);\n\n\tnow = tmr_jiffies();\n\tif (!ab->ts)\n\t\tab->ts = now;\n\n\tif (now < ab->ts) {\n\t\terr = ETIMEDOUT;\n\t\tgoto out;\n\t}\n\n\tab->ts += ptime;\n\n out:\n\tmtx_unlock(ab->lock);\n\n\tif (!err)\n\t\taubuf_read(ab, p, sz);\n\n\treturn err;\n}\n\n\n/**\n * Flush the audio buffer\n *\n * @param ab Audio buffer\n */\nvoid aubuf_flush(struct aubuf *ab)\n{\n\tif (!ab)\n\t\treturn;\n\n\tmtx_lock(ab->lock);\n\n\tlist_clear(&ab->afl);\n\tmem_pool_flush(ab->pool);\n\tab->fill_sz = ab->wish_sz;\n\tab->cur_sz  = 0;\n\tab->wr_sz   = 0;\n\tab->ts      = 0;\n\n\tmtx_unlock(ab->lock);\n\tajb_reset(ab->ajb);\n}\n\n\n/**\n * Audio buffer debug handler, use with fmt %H\n *\n * @param pf Print function\n * @param ab Audio buffer\n *\n * @return 0 if success, otherwise errorcode\n */\nint aubuf_debug(struct re_printf *pf, const struct aubuf *ab)\n{\n\tint err;\n\n\tif (!ab)\n\t\treturn 0;\n\n\tmtx_lock(ab->lock);\n\terr  = re_hprintf(pf, \"wish_sz=%zu cur_sz=%zu fill_sz=%zu\",\n\t\t\t ab->wish_sz, ab->cur_sz, ab->fill_sz);\n\terr |= re_hprintf(pf, \" [overrun=%zu underrun=%zu]\",\n\t\t\t  ab->stats.or, ab->stats.ur);\n\n\tmtx_unlock(ab->lock);\n\n\treturn err;\n}\n\n\n/**\n * Get the current number of bytes in the audio buffer\n *\n * @param ab Audio buffer\n *\n * @return Number of bytes in the audio buffer\n */\nsize_t aubuf_cur_size(const struct aubuf *ab)\n{\n\tsize_t sz;\n\n\tif (!ab)\n\t\treturn 0;\n\n\tmtx_lock(ab->lock);\n\tsz = ab->cur_sz;\n\tmtx_unlock(ab->lock);\n\n\treturn sz;\n}\n\n\n/**\n * Get the maximum number of bytes of the audio buffer\n *\n * @param ab Audio buffer\n *\n * @return Maximum number of bytes\n */\nsize_t aubuf_maxsz(const struct aubuf *ab)\n{\n\tsize_t sz;\n\n\tif (!ab)\n\t\treturn 0;\n\n\tmtx_lock(ab->lock);\n\tsz = ab->max_sz;\n\tmtx_unlock(ab->lock);\n\n\treturn sz;\n}\n\n\n/**\n * Returns true if the minimum size was reached and the read function returned\n * already the first real data\n *\n * @param ab Audio buffer\n *\n * @return True if reading was started\n */\nbool aubuf_started(const struct aubuf *ab)\n{\n\tbool started;\n\n\tif (!ab)\n\t\treturn false;\n\n\tmtx_lock(ab->lock);\n\tstarted = ab->started;\n\tmtx_unlock(ab->lock);\n\n\treturn started;\n}\n\n\n/**\n * Reorder aubuf by auframe->timestamp\n *\n * @param ab Audio buffer\n */\nvoid aubuf_sort_auframe(struct aubuf *ab)\n{\n\tif (!ab)\n\t\treturn;\n\n\tlist_sort(&ab->afl, frame_less_equal, NULL);\n}\n\n\n/**\n * This function is for reporting that the given audio frame was dropped. Its\n * timestamp is used to reset the ajb structure to avoid a jump of the computed\n * jitter value\n *\n * @param ab Audio buffer\n * @param af Audio frame\n */\nvoid aubuf_drop_auframe(struct aubuf *ab, const struct auframe *af)\n{\n\tif (!ab)\n\t\treturn;\n\n\tajb_set_ts0(ab->ajb, af->timestamp);\n}\n"
  },
  {
    "path": "rem/auconv/auconv.c",
    "content": "/**\n * @file auconv.c  Audio sample format converter\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <math.h>\n#include <re.h>\n#include <rem_au.h>\n#include <rem_auconv.h>\n\n\nstatic inline float ausamp_short2float(int16_t in)\n{\n\tfloat out;\n\n\tout = (float) (in / (1.0 * 0x8000));\n\n\treturn out;\n}\n\n\nstatic inline int16_t ausamp_float2short(float in)\n{\n\tdouble value;\n\tint16_t out;\n\n\tvalue = in * (8.0 * 0x10000000);\n\n\tif (value >= (1.0 * 0x7fffffff)) {\n\t\tout = 32767;\n\t}\n\telse if (value <= (-8.0 * 0x10000000)) {\n\t\tout = -32768;\n\t}\n\telse\n\t\tout = (short) (lrint (value) >> 16);\n\n\treturn out;\n}\n\n\nvoid auconv_from_s16(enum aufmt dst_fmt, void *dst_sampv,\n\t\t     const int16_t *src_sampv, size_t sampc)\n{\n\tfloat *f;\n\tuint8_t *b;\n\tsize_t i;\n\n\tif (!dst_sampv || !src_sampv || !sampc)\n\t\treturn;\n\n\tswitch (dst_fmt) {\n\n\tcase AUFMT_FLOAT:\n\t\tf = dst_sampv;\n\t\tfor (i=0; i<sampc; i++) {\n\t\t\tf[i] = ausamp_short2float(src_sampv[i]);\n\t\t}\n\t\tbreak;\n\n\tcase AUFMT_S24_3LE:\n\t\tb = dst_sampv;\n\t\tfor (i=0; i<sampc; i++) {\n\t\t\tint16_t s = src_sampv[i];\n\t\t\tb[3*i+2] = s >> 8;\n\t\t\tb[3*i+1] = s & 0xff;\n\t\t\tb[3*i+0] = 0;\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\t(void)re_fprintf(stderr, \"auconv: sample format %d (%s)\"\n\t\t\t\t \" not supported\\n\",\n\t\t\t\t dst_fmt, aufmt_name(dst_fmt));\n\t\treturn;\n\t}\n}\n\n\nvoid auconv_to_s16(int16_t *dst_sampv, enum aufmt src_fmt,\n\t\t   void *src_sampv, size_t sampc)\n{\n\tfloat *f;\n\tuint8_t *b;\n\tsize_t i;\n\n\tif (!dst_sampv || !src_sampv || !sampc)\n\t\treturn;\n\n\tswitch (src_fmt) {\n\n\tcase AUFMT_FLOAT:\n\t\tf = src_sampv;\n\t\tfor (i=0; i<sampc; i++) {\n\t\t\tdst_sampv[i] = ausamp_float2short(f[i]);\n\t\t}\n\t\tbreak;\n\n\tcase AUFMT_S24_3LE:\n\t\tb = src_sampv;\n\t\tfor (i=0; i<sampc; i++) {\n\t\t\tint16_t s;\n\t\t\ts = b[3*i+1] | b[3*i+2] << 8;\n\t\t\tdst_sampv[i] = s;\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\t(void)re_fprintf(stderr, \"auconv: sample format %d (%s)\"\n\t\t\t\t \" not supported\\n\",\n\t\t\t\t src_fmt, aufmt_name(src_fmt));\n\t\treturn;\n\t}\n}\n\n\nvoid auconv_to_float(float *dst_sampv, enum aufmt src_fmt,\n\t\t     const void *src_sampv, size_t sampc)\n{\n\tconst int16_t *s16;\n\n\tif (!dst_sampv || !src_sampv || !sampc)\n\t\treturn;\n\n\tswitch (src_fmt) {\n\n\tcase AUFMT_S16LE:\n\t\ts16 = src_sampv;\n\t\tfor (size_t i=0; i<sampc; i++) {\n\t\t\tdst_sampv[i] = ausamp_short2float(s16[i]);\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\tre_fprintf(stderr, \"auconv: sample format %d (%s)\"\n\t\t\t   \" not supported\\n\",\n\t\t\t   src_fmt, aufmt_name(src_fmt));\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "rem/aufile/aufile.c",
    "content": "/**\n * @file aufile.c  Audio File interface\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include <rem_au.h>\n#include <rem_aufile.h>\n#include \"aufile.h\"\n\n\n/** Audio file state */\nstruct aufile {\n\tstruct aufile_prm prm;\n\tenum aufile_mode mode;\n\tsize_t datasize;\n\tsize_t nread;\n\tsize_t nwritten;\n\tFILE *f;\n};\n\n\nstatic int wavfmt_to_aufmt(enum wavfmt fmt, uint16_t bps)\n{\n\tswitch (fmt) {\n\n\tcase WAVE_FMT_PCM:\n\t\tif (bps != 16)\n\t\t\treturn -1;\n\n\t\treturn AUFMT_S16LE;\n\n\tcase WAVE_FMT_ALAW:\n\t\tif (bps != 8)\n\t\t\treturn -1;\n\n\t\treturn AUFMT_PCMA;\n\n\tcase WAVE_FMT_ULAW:\n\t\tif (bps != 8)\n\t\t\treturn -1;\n\n\t\treturn AUFMT_PCMU;\n\n\tdefault:\n\t\treturn -1;\n\t}\n}\n\n\nstatic enum wavfmt aufmt_to_wavfmt(enum aufmt fmt)\n{\n\tswitch (fmt) {\n\n\tcase AUFMT_S16LE:  return WAVE_FMT_PCM;\n\tcase AUFMT_PCMA:   return WAVE_FMT_ALAW;\n\tcase AUFMT_PCMU:   return WAVE_FMT_ULAW;\n\tdefault:           return -1;\n\t}\n}\n\n\nstatic uint16_t aufmt_to_bps(enum aufmt fmt)\n{\n\tswitch (fmt) {\n\n\tcase AUFMT_S16LE: return 16;\n\tcase AUFMT_PCMA:  return 8;\n\tcase AUFMT_PCMU:  return 8;\n\tdefault:          return 0;\n\t}\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct aufile *af = arg;\n\n\tif (!af->f)\n\t\treturn;\n\n\t/* Update WAV header in write-mode */\n\tif (af->mode == AUFILE_WRITE && af->nwritten > 0) {\n\n\t\trewind(af->f);\n\n\t\t(void)wav_header_encode(af->f, aufmt_to_wavfmt(af->prm.fmt),\n\t\t\t\t\taf->prm.channels, af->prm.srate,\n\t\t\t\t\taufmt_to_bps(af->prm.fmt),\n\t\t\t\t\taf->nwritten);\n\t}\n\n\t(void)fclose(af->f);\n}\n\n\n/**\n * Open a WAVE file for reading or writing\n *\n * Supported formats:  16-bit PCM, A-law, U-law\n *\n * @param afp       Pointer to allocated Audio file\n * @param prm       Audio format of the file\n * @param filename  Filename of the WAV-file to load\n * @param mode      Read or write mode\n *\n * @return 0 if success, otherwise errorcode\n */\nint aufile_open(struct aufile **afp, struct aufile_prm *prm,\n\t\tconst char *filename, enum aufile_mode mode)\n{\n\tstruct wav_fmt fmt;\n\tstruct aufile *af;\n\tint aufmt;\n\tint err;\n\n\tif (!afp || !filename || (mode == AUFILE_WRITE && !prm))\n\t\treturn EINVAL;\n\n\taf = mem_zalloc(sizeof(*af), destructor);\n\tif (!af)\n\t\treturn ENOMEM;\n\n\taf->mode = mode;\n\n\taf->f = fopen(filename, mode == AUFILE_READ ? \"rb\" : \"wb\");\n\tif (!af->f) {\n\t\terr = errno;\n\t\tgoto out;\n\t}\n\n\tswitch (mode) {\n\n\tcase AUFILE_READ:\n\t\terr = wav_header_decode(&fmt, &af->datasize, af->f);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\taufmt = wavfmt_to_aufmt(fmt.format, fmt.bps);\n\t\tif (aufmt < 0) {\n\t\t\terr = ENOSYS;\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (prm) {\n\t\t\tprm->srate    = fmt.srate;\n\t\t\tprm->channels = (uint8_t)fmt.channels;\n\t\t\tprm->fmt      = aufmt;\n\t\t}\n\t\tbreak;\n\n\tcase AUFILE_WRITE:\n\t\taf->prm = *prm;\n\n\t\terr = wav_header_encode(af->f, aufmt_to_wavfmt(prm->fmt),\n\t\t\t\t\tprm->channels, prm->srate,\n\t\t\t\t\taufmt_to_bps(prm->fmt), 0);\n\t\tbreak;\n\n\tdefault:\n\t\terr = ENOSYS;\n\t\tbreak;\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(af);\n\telse\n\t\t*afp = af;\n\n\treturn err;\n}\n\n\n/**\n * Read PCM-samples from a WAV file\n *\n * @param af  Audio-file\n * @param p   Read buffer\n * @param sz  Size of buffer, on return contains actual read\n *\n * @return 0 if success, otherwise errorcode\n */\nint aufile_read(struct aufile *af, uint8_t *p, size_t *sz)\n{\n\tsize_t n;\n\n\tif (!af || !p || !sz || af->mode != AUFILE_READ)\n\t\treturn EINVAL;\n\n\tif (af->nread >= af->datasize) {\n\t\t*sz = 0;\n\t\treturn 0;\n\t}\n\n\tn = min(*sz, af->datasize - af->nread);\n\n\tn = fread(p, 1, n, af->f);\n\tif (ferror(af->f))\n\t\treturn errno;\n\n\t*sz = n;\n\taf->nread += n;\n\n\treturn 0;\n}\n\n\n/**\n * Write PCM-samples to a WAV file\n *\n * @param af  Audio-file\n * @param p   Write buffer\n * @param sz  Size of buffer\n *\n * @return 0 if success, otherwise errorcode\n */\nint aufile_write(struct aufile *af, const uint8_t *p, size_t sz)\n{\n\tif (!af || !p || !sz || af->mode != AUFILE_WRITE)\n\t\treturn EINVAL;\n\n\tif (1 != fwrite(p, sz, 1, af->f))\n\t\treturn ferror(af->f);\n\n\taf->nwritten += sz;\n\n\treturn 0;\n}\n\n/**\n * Get size of a WAV file in bytes\n *\n * @param af  Audio-file\n *\n * @return size in bytes if success, otherwise 0.\n */\nsize_t aufile_get_size(struct aufile *af)\n{\n\tif (!af)\n\t\treturn 0;\n\n\treturn af->datasize;\n}\n\n/**\n * Get length of a WAV file in ms\n *\n * @param af  Audio-file\n * @param prm Audio file parameters from aufile_open\n *\n * @return length in ms if success, otherwise 0.\n */\nsize_t aufile_get_length(struct aufile *af, const struct aufile_prm *prm)\n{\n\tif (!af || !prm)\n\t\treturn 0;\n\n\tsize_t sample_size = aufmt_sample_size(prm->fmt);\n\n\tif (sample_size == 0)\n\t\treturn 0;\n\n\treturn af->datasize * 1000 / (sample_size *\n\t\tprm->channels * prm->srate);\n}\n\n/**\n * Set initial playing position of a WAV file in ms\n *\n * @param af  Audio-file\n * @param prm Audio file parameters from aufile_open\n * @param pos_ms Playing position in milliseconds\n *\n * @return 0 if success, otherwise errorcode\n */\nint aufile_set_position(struct aufile *af, const struct aufile_prm *prm,\n\t\t\t\t\t\t   size_t pos_ms)\n{\n\tif (!af || !prm)\n\t\treturn EINVAL;\n\n\tif (fseek(af->f, 0, SEEK_SET) < 0)\n\t\treturn errno;\n\n\t/* this is only used for the side effect of moving the file ptr to the\n\t   first data block. */\n\tstruct wav_fmt fmt;\n\tsize_t datasize;\n\tint err = wav_header_decode(&fmt, &datasize, af->f);\n\tif (err)\n\t\treturn err;\n\n\toff_t pos = (off_t)(prm->srate * aufmt_sample_size(prm->fmt)\n\t\t* prm->channels * pos_ms / 1000);\n\n\tpos = min((off_t)datasize, pos);\n\n\tif (fseek(af->f, pos, SEEK_CUR) < 0)\n\t\treturn errno;\n\n\taf->nread = pos;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "rem/aufile/aufile.h",
    "content": "/**\n * @file aufile.h  Audio File -- internal API\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nenum wavfmt {\n\tWAVE_FMT_PCM    = 0x0001,\n\tWAVE_FMT_ALAW   = 0x0006,\n\tWAVE_FMT_ULAW   = 0x0007,\n};\n\n/** WAVE format sub-chunk */\nstruct wav_fmt {\n\tuint16_t format;\n\tuint16_t channels;\n\tuint32_t srate;\n\tuint32_t byterate;\n\tuint16_t block_align;\n\tuint16_t bps;\n\tuint16_t extra;\n};\n\nint wav_header_encode(FILE *f, uint16_t format, uint16_t channels,\n\t\t      uint32_t srate, uint16_t bps, size_t bytes);\nint wav_header_decode(struct wav_fmt *fmt, size_t *datasize, FILE *f);\n"
  },
  {
    "path": "rem/aufile/wave.c",
    "content": "/**\n * @file wave.c  WAVE format encoding and decoding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include <rem_au.h>\n#include <rem_aufile.h>\n#include \"aufile.h\"\n\n\nenum {\n\tWAVE_FMT_SIZE = 16\n};\n\n\n/** WAV-file chunk */\nstruct wav_chunk {\n\tuint8_t id[4];\n\tuint32_t size;\n};\n\n\nstatic int write_u16(FILE *f, uint16_t v)\n{\n\tv = sys_htols(v);\n\n\tif (1 != fwrite(&v, sizeof(v), 1, f))\n\t\treturn ferror(f);\n\n\treturn 0;\n}\n\n\nstatic int write_u32(FILE *f, uint32_t v)\n{\n\tv = sys_htoll(v);\n\n\tif (1 != fwrite(&v, sizeof(v), 1, f))\n\t\treturn ferror(f);\n\n\treturn 0;\n}\n\n\nstatic int read_u16(FILE *f, uint16_t *v)\n{\n\tuint16_t vle;\n\n\tif (1 != fread(&vle, sizeof(vle), 1, f))\n\t\treturn ferror(f);\n\n\t*v = sys_ltohs(vle);\n\n\treturn 0;\n}\n\n\nstatic int read_u32(FILE *f, uint32_t *v)\n{\n\tuint32_t vle;\n\n\tif (1 != fread(&vle, sizeof(vle), 1, f))\n\t\treturn ferror(f);\n\n\t*v = sys_ltohl(vle);\n\n\treturn 0;\n\n}\n\n\nstatic int chunk_encode(FILE *f, const char *id, size_t sz)\n{\n\tif (1 != fwrite(id, 4, 1, f))\n\t\treturn ferror(f);\n\n\treturn write_u32(f, (uint32_t)sz);\n}\n\n\nstatic int chunk_decode(struct wav_chunk *chunk, FILE *f)\n{\n\tif (1 != fread(chunk->id, sizeof(chunk->id), 1, f))\n\t\treturn ferror(f);\n\n\treturn read_u32(f, &chunk->size);\n}\n\n\nint wav_header_encode(FILE *f, uint16_t format, uint16_t channels,\n\t\t      uint32_t srate, uint16_t bps, size_t bytes)\n{\n\tint err;\n\n\terr = chunk_encode(f, \"RIFF\", 36 + bytes);\n\tif (err)\n\t\treturn err;\n\n\tif (1 != fwrite(\"WAVE\", 4, 1, f))\n\t\treturn ferror(f);\n\n\terr = chunk_encode(f, \"fmt \", WAVE_FMT_SIZE);\n\tif (err)\n\t\treturn err;\n\n\terr  = write_u16(f, format);\n\terr |= write_u16(f, channels);\n\terr |= write_u32(f, srate);\n\terr |= write_u32(f, srate * channels * bps / 8);\n\terr |= write_u16(f, channels * bps / 8);\n\terr |= write_u16(f, bps);\n\tif (err)\n\t\treturn err;\n\n\treturn chunk_encode(f, \"data\", bytes);\n}\n\n\nint wav_header_decode(struct wav_fmt *fmt, size_t *datasize, FILE *f)\n{\n\tstruct wav_chunk header, format, chunk;\n\tuint8_t rifftype[4];        /* \"WAVE\" */\n\tint err = 0;\n\n\terr = chunk_decode(&header, f);\n\tif (err)\n\t\treturn err;\n\n\tif (memcmp(header.id, \"RIFF\", 4)) {\n\t\t(void)re_fprintf(stderr, \"aufile: expected RIFF (%b)\\n\",\n\t\t\t\t header.id, sizeof(header.id));\n\t\treturn EBADMSG;\n\t}\n\n\tif (1 != fread(rifftype, sizeof(rifftype), 1, f))\n\t\treturn ferror(f);\n\n\tif (memcmp(rifftype, \"WAVE\", 4)) {\n\t\t(void)re_fprintf(stderr, \"aufile: expected WAVE (%b)\\n\",\n\t\t\t\t rifftype, sizeof(rifftype));\n\t\treturn EBADMSG;\n\t}\n\n\terr = chunk_decode(&format, f);\n\tif (err)\n\t\treturn err;\n\n\tif (memcmp(format.id, \"fmt \", 4)) {\n\t\t(void)re_fprintf(stderr, \"aufile: expected fmt (%b)\\n\",\n\t\t\t\t format.id, sizeof(format.id));\n\t\treturn EBADMSG;\n\t}\n\n\tif (format.size < WAVE_FMT_SIZE)\n\t\treturn EBADMSG;\n\n\terr  = read_u16(f, &fmt->format);\n\terr |= read_u16(f, &fmt->channels);\n\terr |= read_u32(f, &fmt->srate);\n\terr |= read_u32(f, &fmt->byterate);\n\terr |= read_u16(f, &fmt->block_align);\n\terr |= read_u16(f, &fmt->bps);\n\tif (err)\n\t\treturn err;\n\n\t/* skip any extra bytes */\n\tif (format.size >= (WAVE_FMT_SIZE + 2)) {\n\n\t\terr = read_u16(f, &fmt->extra);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (fmt->extra > 0) {\n\t\t\tif (fseek(f, fmt->extra, SEEK_CUR))\n\t\t\t\treturn errno;\n\t\t}\n\t}\n\n\t/* fast forward to \"data\" chunk */\n\tfor (;;) {\n\n\t\terr = chunk_decode(&chunk, f);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (chunk.size > header.size) {\n\t\t\t(void)re_fprintf(stderr, \"chunk size too large\"\n\t\t\t\t\t \" (%u > %u)\\n\",\n\t\t\t\t\t chunk.size, header.size);\n\t\t\treturn EBADMSG;\n\t\t}\n\n\t\tif (0 == memcmp(chunk.id, \"data\", 4)) {\n\t\t\t*datasize = chunk.size;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (fseek(f, chunk.size, SEEK_CUR) < 0)\n\t\t\treturn errno;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "rem/auframe/auframe.c",
    "content": "/**\n * @file auframe.c  Audio frame\n *\n * Copyright (C) 2010 - 2020 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re.h>\n#include <rem_au.h>\n#include <rem_aulevel.h>\n#include <rem_auframe.h>\n\n\n/**\n * Initialize an audio frame\n *\n * @param af       Audio frame\n * @param fmt      Sample format (enum aufmt)\n * @param sampv    Audio samples\n * @param sampc    Total number of audio samples\n * @param srate    Samplerate\n * @param ch       Channels\n */\nvoid auframe_init(struct auframe *af, enum aufmt fmt, void *sampv,\n\t\t  size_t sampc, uint32_t srate, uint8_t ch)\n{\n\tif (!af)\n\t\treturn;\n\n\tif (0 == aufmt_sample_size(fmt)) {\n\t\tre_printf(\"auframe: init: unsupported sample format %d (%s)\\n\",\n\t\t\tfmt, aufmt_name(fmt));\n\t}\n\n\tmemset(af, 0, sizeof(*af));\n\n\taf->fmt = fmt;\n\taf->sampv = sampv;\n\taf->sampc = sampc;\n\taf->srate = srate;\n\taf->level = AULEVEL_UNDEF;\n\taf->ch = ch;\n\taf->id = 0;\n}\n\n\n/**\n * Get the size of an audio frame\n *\n * @param af Audio frame\n *\n * @return Number of bytes\n */\nsize_t auframe_size(const struct auframe *af)\n{\n\tsize_t sz;\n\n\tif (!af)\n\t\treturn 0;\n\n\tsz = aufmt_sample_size(af->fmt);\n\tif (sz == 0) {\n\t\tre_printf(\"auframe: size: illegal format %d (%s)\\n\",\n\t\t\taf->fmt, aufmt_name(af->fmt));\n\t\tsz = 1;\n\t}\n\n\treturn af->sampc * sz;\n}\n\n\n/**\n * Silence all samples in an audio frame\n *\n * @param af Audio frame\n */\nvoid auframe_mute(struct auframe *af)\n{\n\tif (!af)\n\t\treturn;\n\n\tmemset(af->sampv, 0, auframe_size(af));\n}\n\n\n/**\n * Get audio level (only calculated once)\n *\n * @note Set af->level = AULEVEL_UNDEF to force re-calculation\n *\n * @param af  Audio frame\n *\n * @return Audio level expressed in dBov on success and AULEVEL_UNDEF on error\n */\ndouble auframe_level(struct auframe *af)\n{\n\tif (!af)\n\t\treturn AULEVEL_UNDEF;\n\n\tif (af->fmt == AUFMT_RAW)\n\t\treturn AULEVEL_UNDEF;\n\n\tif (af->level == AULEVEL_UNDEF)\n\t\taf->level = aulevel_calc_dbov(af->fmt, af->sampv, af->sampc);\n\n\treturn af->level;\n}\n\n\nuint64_t auframe_bytes_to_timestamp(const struct auframe *af, size_t n)\n{\n\tsize_t sample_size = aufmt_sample_size(af->fmt);\n\n\treturn ((uint64_t) n) * AUDIO_TIMEBASE /\n\t\t(af->srate * af->ch * sample_size);\n}\n\n\nuint64_t auframe_bytes_to_ms(const struct auframe *af, size_t n)\n{\n\tsize_t sample_size = aufmt_sample_size(af->fmt);\n\n\tif (!af->srate || !af->ch || !sample_size)\n\t\treturn 0;\n\n\treturn ((uint64_t)n * 1000) / (af->srate * af->ch * sample_size);\n}\n\n\nsize_t auframe_ms_to_bytes(const struct auframe *af, uint16_t ms)\n{\n\treturn aufmt_sample_size(af->fmt) *\n\t       au_calc_nsamp(af->srate, af->ch, ms);\n}\n"
  },
  {
    "path": "rem/aulevel/aulevel.c",
    "content": "/**\n * @file aulevel/aulevel.c  Audio level\n *\n * Copyright (C) 2017 Creytiv.com\n */\n\n#include <math.h>\n#include <re.h>\n#include <rem.h>\n\n\n/**\n * Generic routine to calculate RMS (Root-Mean-Square) from\n * a set of signed 16-bit values\n *\n * \\verbatim\n\n\t     .---------------\n\t     |   N-1\n\t     |  ----.\n\t     |  \\\n\t     |   \\        2\n\t     |    |   s[n]\n\t     |   /\n\t     |  /\n\t _   |  ----'\n\t  \\  |   n=0\n\t   \\ |  ------------\n\t    \\|       N\n\n   \\endverbatim\n *\n * @param data Array of signed 16-bit values\n * @param len  Number of values\n *\n * @return RMS value from 0 to 32768\n */\nstatic double calc_rms_s16(const int16_t *data, size_t len)\n{\n\tint64_t sum = 0;\n\tsize_t i;\n\n\tif (!data || !len)\n\t\treturn .0;\n\n\tfor (i = 0; i < len; i++) {\n\t\tsum += data[i] * data[i];\n\t}\n\n\treturn sqrt(sum / (double)len);\n}\n\n\nstatic double calc_rms_s32(const int32_t *data, size_t len)\n{\n\tdouble sum = 0;\n\tsize_t i;\n\n\tif (!data || !len)\n\t\treturn .0;\n\n\tfor (i = 0; i < len; i++) {\n\t\tconst double sample = data[i];\n\n\t\tsum += sample * sample;\n\t}\n\n\treturn sqrt(sum / (double)len);\n}\n\n\nstatic double calc_rms_float(const float *data, size_t len)\n{\n\tdouble sum = 0;\n\tsize_t i;\n\n\tif (!data || !len)\n\t\treturn .0;\n\n\tfor (i = 0; i < len; i++) {\n\t\tconst double sample = data[i];\n\n\t\tsum += sample * sample;\n\t}\n\n\treturn sqrt(sum / (double)len);\n}\n\n\n/**\n * Calculate the audio level in dBov from a set of audio samples.\n * dBov is the level, in decibels, relative to the overload point\n * of the system\n *\n * @param fmt   Sample format (enum aufmt)\n * @param sampv Audio samples\n * @param sampc Number of audio samples\n *\n * @return Audio level expressed in dBov on success and AULEVEL_UNDEF on error\n */\ndouble aulevel_calc_dbov(int fmt, const void *sampv, size_t sampc)\n{\n\tstatic const double peak_s16 = 32767.0;\n\tstatic const double peak_s32 = 2147483647.0;\n\tdouble rms, dbov;\n\n\tif (!sampv || !sampc)\n\t\treturn AULEVEL_UNDEF;\n\n\tswitch (fmt) {\n\n\tcase AUFMT_S16LE:\n\t\trms = calc_rms_s16(sampv, sampc) / peak_s16;\n\t\tbreak;\n\n\tcase AUFMT_S32LE:\n\t\trms = calc_rms_s32(sampv, sampc) / peak_s32;\n\t\tbreak;\n\n\tcase AUFMT_FLOAT:\n\t\trms = calc_rms_float(sampv, sampc) / 1.0;\n\t\tbreak;\n\n\tdefault:\n\t\tre_printf(\"aulevel: sample format not supported (%s)\\n\",\n\t\t\taufmt_name(fmt));\n\t\treturn AULEVEL_UNDEF;\n\t}\n\n\tdbov = 20 * log10(rms);\n\n\tif (dbov < AULEVEL_MIN)\n\t\tdbov = AULEVEL_MIN;\n\telse if (dbov > AULEVEL_MAX)\n\t\tdbov = AULEVEL_MAX;\n\n\treturn dbov;\n}\n"
  },
  {
    "path": "rem/aumix/aumix.c",
    "content": "/**\n * @file aumix.c Audio Mixer\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#define _BSD_SOURCE 1\n#define _DEFAULT_SOURCE 1\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include <string.h>\n#include <re.h>\n#include <rem_au.h>\n#include <rem_aulevel.h>\n#include <rem_auframe.h>\n#include <rem_aubuf.h>\n#include <rem_aufile.h>\n#include <rem_aumix.h>\n\n\n/** Defines an Audio mixer */\nstruct aumix {\n\tmtx_t *mutex;\n\tcnd_t cond;\n\tstruct list srcl;\n\tthrd_t thread;\n\tstruct aufile *af;\n\tuint32_t ptime;\n\tuint32_t frame_size;\n\tuint32_t srate;\n\tuint8_t ch;\n\tstruct {\n\t\tuint16_t min;\n\t\tuint16_t max;\n\t} latency;\n\taumix_record_h *recordh;\n\taumix_record_h *record_sumh;\n\tstruct auframe rec_sum;\n\tbool run;\n};\n\n/** Defines an Audio mixer source */\nstruct aumix_source {\n\tstruct le le;\n\tstruct pl *id;\n\tstruct auframe af;\n\tint16_t *frame;\n\tstruct aubuf *aubuf;\n\tstruct aumix *mix;\n\taumix_frame_h *fh;\n\taumix_read_h *readh;\n\tvoid *arg;\n\tbool muted;\n};\n\n\nstatic void dummy_frame_handler(const int16_t *sampv, size_t sampc, void *arg)\n{\n\t(void)sampv;\n\t(void)sampc;\n\t(void)arg;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct aumix *mix = arg;\n\n\tmtx_lock(mix->mutex);\n\tbool run = mix->run;\n\tmtx_unlock(mix->mutex);\n\n\tif (run) {\n\n\t\tmtx_lock(mix->mutex);\n\t\tmix->run = false;\n\t\tcnd_signal(&mix->cond);\n\t\tmtx_unlock(mix->mutex);\n\n\t\tthrd_join(mix->thread, NULL);\n\t}\n\n\tmem_deref(mix->af);\n\tmem_deref(mix->mutex);\n}\n\n\nstatic void source_destructor(void *arg)\n{\n\tstruct aumix_source *src = arg;\n\n\tif (src->le.list) {\n\t\tmtx_lock(src->mix->mutex);\n\t\tlist_unlink(&src->le);\n\t\tmtx_unlock(src->mix->mutex);\n\t}\n\n\tmem_deref(src->aubuf);\n\tmem_deref(src->frame);\n\tmem_deref(src->mix);\n\tmem_deref(src->id);\n}\n\n\nstatic int aumix_thread(void *arg)\n{\n\tuint8_t *silence, *frame, *base_frame;\n\tstruct aumix *mix = arg;\n\tint16_t *mix_frame;\n\tuint64_t ts = 0;\n\n\tsilence   = mem_zalloc(mix->frame_size*2, NULL);\n\tframe     = mem_alloc(mix->frame_size*2, NULL);\n\tmix_frame = mem_alloc(mix->frame_size*2, NULL);\n\n\tif (!silence || !frame || !mix_frame)\n\t\tgoto out;\n\n\tmtx_lock(mix->mutex);\n\n\twhile (mix->run) {\n\n\t\tstruct le *le;\n\t\tuint64_t now;\n\n\t\tif (!mix->srcl.head) {\n\t\t\tmix->af = mem_deref(mix->af);\n\t\t\tcnd_wait(&mix->cond, mix->mutex);\n\t\t\tts = 0;\n\t\t}\n\t\telse {\n\t\t\tmtx_unlock(mix->mutex);\n\t\t\tsys_usleep(4000);\n\t\t\tmtx_lock(mix->mutex);\n\t\t}\n\n\t\tnow = tmr_jiffies();\n\t\tif (!ts)\n\t\t\tts = now;\n\n\t\tif (ts > now)\n\t\t\tcontinue;\n\n\t\tif (mix->af) {\n\n\t\t\tsize_t n = mix->frame_size*2;\n\n\t\t\tif (aufile_read(mix->af, frame, &n) || n == 0) {\n\t\t\t\tmix->af = mem_deref(mix->af);\n\t\t\t\tbase_frame = silence;\n\t\t\t}\n\t\t\telse if (n < mix->frame_size*2) {\n\t\t\t\tmemset(frame + n, 0, mix->frame_size*2 - n);\n\t\t\t\tmix->af = mem_deref(mix->af);\n\t\t\t\tbase_frame = frame;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbase_frame = frame;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tbase_frame = silence;\n\t\t}\n\n\t\tfor (le = mix->srcl.head; le; le = le->next) {\n\n\t\t\tstruct aumix_source *src = le->data;\n\n\t\t\tif (src->muted)\n\t\t\t\tcontinue;\n\n\t\t\tif (src->readh)\n\t\t\t\tsrc->readh(&src->af, src->arg);\n\t\t\telse\n\t\t\t\taubuf_read_auframe(src->aubuf, &src->af);\n\n\t\t\tif (mix->recordh)\n\t\t\t\tmix->recordh(&src->af);\n\t\t}\n\n\t\tfor (le = mix->srcl.head; le; le = le->next) {\n\n\t\t\tstruct aumix_source *src = le->data;\n\t\t\tstruct le *cle;\n\n\t\t\tmemcpy(mix_frame, base_frame, mix->frame_size * 2);\n\n\t\t\tLIST_FOREACH(&mix->srcl, cle)\n\t\t\t{\n\n\t\t\t\tstruct aumix_source *csrc = cle->data;\n\t\t\t\tint32_t sample;\n\n\t\t\t\t/* skip self */\n\t\t\t\tif (csrc == src)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (csrc->muted)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tfor (size_t i = 0; i < mix->frame_size; i++) {\n\t\t\t\t\tsample = mix_frame[i] + csrc->frame[i];\n\n\t\t\t\t\t/* hard clipping */\n\t\t\t\t\tif (sample >= 32767)\n\t\t\t\t\t\tsample = 32767;\n\t\t\t\t\tif (sample <= -32767)\n\t\t\t\t\t\tsample = -32767;\n\n\t\t\t\t\tmix_frame[i] = (int16_t)sample;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsrc->fh(mix_frame, mix->frame_size, src->arg);\n\t\t}\n\n\t\tif (mix->record_sumh) {\n\t\t\tstruct le *cle;\n\n\t\t\tmemcpy(mix_frame, base_frame, mix->frame_size * 2);\n\n\t\t\tLIST_FOREACH(&mix->srcl, cle)\n\t\t\t{\n\t\t\t\tstruct aumix_source *csrc = cle->data;\n\t\t\t\tint32_t sample;\n\n\t\t\t\tif (csrc->muted)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tfor (size_t i = 0; i < mix->frame_size; i++) {\n\t\t\t\t\tsample = mix_frame[i] + csrc->frame[i];\n\n\t\t\t\t\t/* hard clipping */\n\t\t\t\t\tif (sample >= 32767)\n\t\t\t\t\t\tsample = 32767;\n\t\t\t\t\tif (sample <= -32767)\n\t\t\t\t\t\tsample = -32767;\n\n\t\t\t\t\tmix_frame[i] = (int16_t)sample;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmix->rec_sum.timestamp = now;\n\t\t\tmix->rec_sum.sampv     = mix_frame;\n\n\t\t\tmix->record_sumh(&mix->rec_sum);\n\t\t}\n\n\t\tts += mix->ptime;\n\t}\n\n\tmtx_unlock(mix->mutex);\n\n out:\n\tmem_deref(mix_frame);\n\tmem_deref(silence);\n\tmem_deref(frame);\n\n\treturn 0;\n}\n\n\n/**\n * Allocate a new Audio mixer\n *\n * @param mixp  Pointer to allocated audio mixer\n * @param srate Sample rate in [Hz]\n * @param ch    Number of channels\n * @param ptime Packet time in [ms]\n *\n * @return 0 for success, otherwise error code\n */\nint aumix_alloc(struct aumix **mixp, uint32_t srate,\n\t\tuint8_t ch, uint32_t ptime)\n{\n\tstruct aumix *mix;\n\tint err;\n\n\tif (!mixp || !srate || !ch || !ptime)\n\t\treturn EINVAL;\n\n\tmix = mem_zalloc(sizeof(*mix), destructor);\n\tif (!mix)\n\t\treturn ENOMEM;\n\n\tmix->ptime\t = ptime;\n\tmix->frame_size\t = srate * ch * ptime / 1000;\n\tmix->srate\t = srate;\n\tmix->ch\t\t = ch;\n\tmix->recordh\t = NULL;\n\tmix->latency.min = 60;\t/* ms */\n\tmix->latency.max = 200; /* ms */\n\n\tmix->rec_sum.ch\t  = ch;\n\tmix->rec_sum.srate = srate;\n\tmix->rec_sum.sampc = mix->frame_size;\n\n\terr = mutex_alloc(&mix->mutex);\n\tif (err) {\n\t\tgoto out;\n\t}\n\n\terr = cnd_init(&mix->cond) != thrd_success;\n\tif (err) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tmix->run = true;\n\n\terr = thread_create_name(&mix->thread, \"aumix\", aumix_thread, mix);\n\tif (err) {\n\t\tmix->run = false;\n\t\tgoto out;\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(mix);\n\telse\n\t\t*mixp = mix;\n\n\treturn err;\n}\n\n\n/**\n * Set aumix aubuf default latency\n *\n * @param mix  Audio mixer\n * @param min  Minimum value in [ms]\n * @param max  Maximum value in [ms]\n */\nvoid aumix_latency(struct aumix *mix, uint16_t min, uint16_t max)\n{\n\tif (!mix)\n\t\treturn;\n\n\tmtx_lock(mix->mutex);\n\tmix->latency.min = min;\n\tmix->latency.max = max;\n\tmtx_unlock(mix->mutex);\n}\n\n\n/**\n * Add multitrack record handler (each source can be identified by auframe->id)\n *\n * @param mix      Audio mixer\n * @param recordh  Record Handler\n */\nvoid aumix_recordh(struct aumix *mix, aumix_record_h *recordh)\n{\n\tif (!mix)\n\t\treturn;\n\n\tmtx_lock(mix->mutex);\n\tmix->recordh = recordh;\n\tmtx_unlock(mix->mutex);\n}\n\n\n/**\n * Add sum record handler\n *\n * @param mix      Audio mixer\n * @param recordh  Record Handler\n */\nvoid aumix_record_sumh(struct aumix *mix, aumix_record_h *recordh)\n{\n\tif (!mix)\n\t\treturn;\n\n\tmtx_lock(mix->mutex);\n\tmix->record_sumh = recordh;\n\tmtx_unlock(mix->mutex);\n}\n\n\n/**\n * Load audio file for mixer announcements\n *\n * @param mix      Audio mixer\n * @param filepath Filename of audio file with complete path\n *\n * @return 0 for success, otherwise error code\n */\nint aumix_playfile(struct aumix *mix, const char *filepath)\n{\n\tstruct aufile_prm prm;\n\tstruct aufile *af;\n\tint err;\n\n\tif (!mix || !filepath)\n\t\treturn EINVAL;\n\n\terr = aufile_open(&af, &prm, filepath, AUFILE_READ);\n\tif (err)\n\t\treturn err;\n\n\tif (prm.fmt != AUFMT_S16LE || prm.srate != mix->srate ||\n\t    prm.channels != mix->ch) {\n\t\tmem_deref(af);\n\t\treturn EINVAL;\n\t}\n\n\tmtx_lock(mix->mutex);\n\tmem_deref(mix->af);\n\tmix->af = af;\n\tmtx_unlock(mix->mutex);\n\n\treturn 0;\n}\n\n\n/**\n * Count number of audio sources in the audio mixer\n *\n * @param mix Audio mixer\n *\n * @return Number of audio sources\n */\nuint32_t aumix_source_count(const struct aumix *mix)\n{\n\tif (!mix)\n\t\treturn 0;\n\n\tmtx_lock(mix->mutex);\n\tuint32_t count = list_count(&mix->srcl);\n\tmtx_unlock(mix->mutex);\n\n\treturn count;\n}\n\n\n/**\n * Allocate an audio mixer source\n *\n * @param srcp Pointer to allocated audio source\n * @param mix  Audio mixer\n * @param fh   Mixer frame handler\n * @param arg  Handler argument\n *\n * @return 0 for success, otherwise error code\n */\nint aumix_source_alloc(struct aumix_source **srcp, struct aumix *mix,\n\t\t       aumix_frame_h *fh, void *arg)\n{\n\tstruct aumix_source *src;\n\tsize_t sz;\n\tint err;\n\n\tif (!srcp || !mix)\n\t\treturn EINVAL;\n\n\tsrc = mem_zalloc(sizeof(*src), source_destructor);\n\tif (!src)\n\t\treturn ENOMEM;\n\n\tsrc->mix = mem_ref(mix);\n\tsrc->fh  = fh ? fh : dummy_frame_handler;\n\tsrc->arg = arg;\n\tsrc->muted = false;\n\n\tsz = mix->frame_size*2;\n\n\tsrc->frame = mem_alloc(sz, NULL);\n\tif (!src->frame) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tauframe_init(&src->af, AUFMT_S16LE, src->frame, mix->frame_size,\n\t\t     mix->srate, mix->ch);\n\n\terr = aubuf_alloc(&src->aubuf,\n\t\t\t  auframe_ms_to_bytes(&src->af, mix->latency.min),\n\t\t\t  auframe_ms_to_bytes(&src->af, mix->latency.max));\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(src);\n\telse\n\t\t*srcp = src;\n\n\treturn err;\n}\n\n\n/**\n * Set source id\n *\n * @param src  Audio mixer source\n * @param id   Source identifier\n */\nvoid aumix_source_set_id(struct aumix_source *src, struct pl *id)\n{\n\tif (!src || !id)\n\t\treturn;\n\n\tmtx_lock(src->mix->mutex);\n\tsrc->id = mem_ref(id);\n\taubuf_set_id(src->aubuf, id);\n\tmtx_unlock(src->mix->mutex);\n}\n\n\n/**\n * Add source read handler (alternative to aumix_source_put)\n *\n * @param src    Audio mixer source\n * @param readh  Read Handler\n */\nvoid aumix_source_readh(struct aumix_source *src, aumix_read_h *readh)\n{\n\tif (!src || !src->mix)\n\t\treturn;\n\n\tmtx_lock(src->mix->mutex);\n\tsrc->readh = readh;\n\tmtx_unlock(src->mix->mutex);\n}\n\n\n/**\n * Mute/unmute aumix source\n *\n * @param src    Audio mixer source\n * @param mute   True to mute, false to unmute\n */\nvoid aumix_source_mute(struct aumix_source *src, bool mute)\n{\n\tif (!src)\n\t\treturn;\n\n\tsrc->muted = mute;\n}\n\n\n/**\n * Enable/disable aumix source\n *\n * @param src    Audio mixer source\n * @param enable True to enable, false to disable\n */\nvoid aumix_source_enable(struct aumix_source *src, bool enable)\n{\n\tstruct aumix *mix;\n\n\tif (!src)\n\t\treturn;\n\n\tif (src->le.list && enable)\n\t\treturn;\n\n\tif (!src->le.list && !enable)\n\t\treturn;\n\n\tmix = src->mix;\n\n\tmtx_lock(mix->mutex);\n\n\tif (enable) {\n\t\tlist_append(&mix->srcl, &src->le, src);\n\t\tcnd_signal(&mix->cond);\n\t}\n\telse {\n\t\tlist_unlink(&src->le);\n\t}\n\n\tmtx_unlock(mix->mutex);\n}\n\n\n/**\n * Write PCM samples for a given source to the audio mixer\n *\n * @deprecated use aumix_source_readh or aumix_source_put_auframe\n *\n * @param src   Audio mixer source\n * @param sampv PCM samples\n * @param sampc Number of samples\n *\n * @return 0 for success, otherwise error code\n */\nint aumix_source_put(struct aumix_source *src, const int16_t *sampv,\n\t\t     size_t sampc)\n{\n\tif (!src || !sampv)\n\t\treturn EINVAL;\n\n\treturn aubuf_write_samp(src->aubuf, sampv, sampc);\n}\n\n\n/**\n * Put a audio frame for a given source to the audio mixer\n *\n * @param src Audio mixer source\n * @param af  Audio frame\n *\n * @return 0 for success, otherwise error code\n */\nint aumix_source_put_auframe(struct aumix_source *src, struct auframe *af)\n{\n\tif (!src)\n\t\treturn EINVAL;\n\n\treturn aubuf_write_auframe(src->aubuf, af);\n}\n\n\n/**\n * Flush the audio buffer of a given audio mixer source\n *\n * @param src Audio mixer source\n */\nvoid aumix_source_flush(struct aumix_source *src)\n{\n\tif (!src)\n\t\treturn;\n\n\taubuf_flush(src->aubuf);\n}\n\n\n/**\n * Audio mixer debug handler\n *\n * @param pf  Print function\n * @param mix Audio mixer\n *\n * @return 0 if success, otherwise errorcode\n */\nint aumix_debug(struct re_printf *pf, const struct aumix *mix)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tif (!pf || !mix)\n\t\treturn EINVAL;\n\n\tre_hprintf(pf, \"aumix debug:\\n\");\n\tmtx_lock(mix->mutex);\n\tLIST_FOREACH(&mix->srcl, le)\n\t{\n\t\tstruct aumix_source *src = le->data;\n\t\tre_hprintf(pf, \"\\tsource: %p muted=%d \", src, src->muted);\n\t\terr = aubuf_debug(pf, src->aubuf);\n\t\tif (err)\n\t\t\tgoto out;\n\t\tre_hprintf(pf, \"\\n\");\n\t}\n\nout:\n\tmtx_unlock(mix->mutex);\n\treturn err;\n}\n"
  },
  {
    "path": "rem/auresamp/resamp.c",
    "content": "/**\n * @file resamp.c Audio Resampler\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re.h>\n#include <rem_fir.h>\n#include <rem_auresamp.h>\n\n\n/* 48kHz sample-rate, 4kHz cutoff (pass 0-3kHz, stop 5-24kHz) */\nstatic const int16_t fir_48_4[] = {\n\t 62,   -176,   -329,   -556,   -802,  -1005,  -1090,   -985,\n       -636,    -23,    826,   1837,   2894,   3859,   4595,   4994,\n       4994,   4595,   3859,   2894,   1837,    826,    -23,   -636,\n       -985,  -1090,  -1005,   -802,   -556,   -329,   -176,     62\n};\n\n/* 48kHz sample-rate, 8kHz cutoff (pass 0-7kHz, stop 9-24kHz) */\nstatic const int16_t fir_48_8[] = {\n\t238,    198,   -123,   -738,  -1268,  -1204,   -380,    714,\n       1164,    376,  -1220,  -2206,  -1105,   2395,   6909,  10069,\n      10069,   6909,   2395,  -1105,  -2206,  -1220,    376,   1164,\n\t714,   -380,  -1204,  -1268,   -738,   -123,    198,    238\n};\n\n/* 16kHz sample-rate, 4kHz cutoff and 32kHz sample-rate, 8 kHz cutoff */\nstatic const int16_t fir_16_4[] = {\n\t\t22, 60, -41, -157, -9, 322, 195, -490, -613, 539, 1362, -229,\n\t\t-2657, -1101, 6031, 13167, 13167, 6031, -1101, -2657, -229,\n\t\t1362, 539, -613, -490, 195, 322, -9, -157, -41, 60, 22\n};\n\nstatic void upsample_mono2mono(int16_t *outv, const int16_t *inv,\n\t\t\t       size_t inc, unsigned ratio)\n{\n\tunsigned i;\n\n\twhile (inc >= 1) {\n\n\t\tfor (i=0; i<ratio; i++)\n\t\t\t*outv++ = *inv;\n\n\t\t++inv;\n\t\t--inc;\n\t}\n}\n\n\nstatic void upsample_mono2stereo(int16_t *outv, const int16_t *inv,\n\t\t\t\t size_t inc, unsigned ratio)\n{\n\tunsigned i;\n\n\tratio *= 2;\n\n\twhile (inc >= 1) {\n\n\t\tfor (i=0; i<ratio; i++)\n\t\t\t*outv++ = *inv;\n\n\t\t++inv;\n\t\t--inc;\n\t}\n}\n\n\nstatic void upsample_stereo2mono(int16_t *outv, const int16_t *inv,\n\t\t\t\t size_t inc, unsigned ratio)\n{\n\tunsigned i;\n\n\twhile (inc >= 2) {\n\n\t\tconst int16_t s = inv[0]/2 + inv[1]/2;\n\n\t\tfor (i=0; i<ratio; i++)\n\t\t\t*outv++ = s;\n\n\t\tinv += 2;\n\t\tinc -= 2;\n\t}\n}\n\n\nstatic void upsample_stereo2stereo(int16_t *outv, const int16_t *inv,\n\t\t\t\t   size_t inc, unsigned ratio)\n{\n\tunsigned i;\n\n\twhile (inc >= 2) {\n\n\t\tfor (i=0; i<ratio; i++) {\n\t\t\t*outv++ = inv[0];\n\t\t\t*outv++ = inv[1];\n\t\t}\n\n\t\tinv += 2;\n\t\tinc -= 2;\n\t}\n}\n\n\nstatic void downsample_mono2mono(int16_t *outv, const int16_t *inv,\n\t\t\t\t size_t inc, unsigned ratio)\n{\n\twhile (inc >= ratio) {\n\n\t\t*outv++ = *inv;\n\n\t\tinv += ratio;\n\t\tinc -= ratio;\n\t}\n}\n\n\nstatic void downsample_mono2stereo(int16_t *outv, const int16_t *inv,\n\t\t\t\t   size_t inc, unsigned ratio)\n{\n\twhile (inc >= ratio) {\n\n\t\t*outv++ = *inv;\n\t\t*outv++ = *inv;\n\n\t\tinv += ratio;\n\t\tinc -= ratio;\n\t}\n}\n\n\nstatic void downsample_stereo2mono(int16_t *outv, const int16_t *inv,\n\t\t\t\t   size_t inc, unsigned ratio)\n{\n\tratio *= 2;\n\n\twhile (inc >= ratio) {\n\n\t\t*outv++ = inv[0]/2 + inv[1]/2;\n\n\t\tinv += ratio;\n\t\tinc -= ratio;\n\t}\n}\n\n\nstatic void downsample_stereo2stereo(int16_t *outv, const int16_t *inv,\n\t\t\t\t     size_t inc, unsigned ratio)\n{\n\tratio *= 2;\n\n\twhile (inc >= ratio) {\n\n\t\t*outv++ = inv[0];\n\t\t*outv++ = inv[1];\n\n\t\tinv += ratio;\n\t\tinc -= ratio;\n\t}\n}\n\n\n/**\n * Initialize a resampler object\n *\n * @param rs Resampler to initialize\n */\nvoid auresamp_init(struct auresamp *rs)\n{\n\tif (!rs)\n\t\treturn;\n\n\tmemset(rs, 0, sizeof(*rs));\n\tfir_reset(&rs->fir);\n}\n\n\n/**\n * Configure a resampler object\n *\n * @note The sample rate ratio must be an integer\n *\n * @param rs    Resampler\n * @param irate Input sample rate\n * @param ich   Input channel count\n * @param orate Output sample rate\n * @param och   Output channel count\n *\n * @return 0 if success, otherwise error code\n */\nint auresamp_setup(struct auresamp *rs, uint32_t irate, unsigned ich,\n\t\t   uint32_t orate, unsigned och)\n{\n\tif (!rs || !irate || !ich || !orate || !och)\n\t\treturn EINVAL;\n\n\tif (orate == irate && och == ich) {\n\t\tauresamp_init(rs);\n\t\treturn 0;\n\t}\n\n\tif (orate >= irate) {\n\n\t\tif (orate % irate)\n\t\t\treturn ENOTSUP;\n\n\t\tif (ich == 1 && och == 1)\n\t\t\trs->resample = upsample_mono2mono;\n\t\telse if (ich == 1 && och == 2)\n\t\t\trs->resample = upsample_mono2stereo;\n\t\telse if (ich == 2 && och == 1)\n\t\t\trs->resample = upsample_stereo2mono;\n\t\telse if (ich == 2 && och == 2)\n\t\t\trs->resample = upsample_stereo2stereo;\n\t\telse\n\t\t\treturn ENOTSUP;\n\n\t\tif (!rs->up || orate != rs->orate || och != rs->och)\n\t\t\tfir_reset(&rs->fir);\n\n\t\trs->ratio = orate / irate;\n\t\trs->up    = true;\n\n\t\tif (orate == irate) {\n\t\t\trs->tapv = NULL;\n\t\t\trs->tapc = 0;\n\t\t}\n\t\telse if (orate == 48000 && irate == 16000) {\n\t\t\trs->tapv = fir_48_8;\n\t\t\trs->tapc = RE_ARRAY_SIZE(fir_48_8);\n\t\t}\n\t\telse if ((orate == 16000 && irate == 8000) ||\n                         (orate == 32000 && irate == 16000)) {\n\t\t\trs->tapv = fir_16_4;\n\t\t\trs->tapc = RE_ARRAY_SIZE(fir_16_4);\n\t\t}\n\t\telse {\n\t\t\trs->tapv = fir_48_4;\n\t\t\trs->tapc = RE_ARRAY_SIZE(fir_48_4);\n\t\t}\n\t}\n\telse {\n\t\tif (irate % orate)\n\t\t\treturn ENOTSUP;\n\n\t\tif (ich == 1 && och == 1)\n\t\t\trs->resample = downsample_mono2mono;\n\t\telse if (ich == 1 && och == 2)\n\t\t\trs->resample = downsample_mono2stereo;\n\t\telse if (ich == 2 && och == 1)\n\t\t\trs->resample = downsample_stereo2mono;\n\t\telse if (ich == 2 && och == 2)\n\t\t\trs->resample = downsample_stereo2stereo;\n\t\telse\n\t\t\treturn ENOTSUP;\n\n\t\tif (rs->up || irate != rs->irate || ich != rs->ich)\n\t\t\tfir_reset(&rs->fir);\n\n\t\trs->ratio = irate / orate;\n\t\trs->up    = false;\n\n\t\tif (irate == 48000 && orate == 16000) {\n\t\t\trs->tapv = fir_48_8;\n\t\t\trs->tapc = RE_ARRAY_SIZE(fir_48_8);\n\t\t}\n\t\telse if ((irate == 16000 && orate == 8000) ||\n                         (irate == 32000 && orate == 16000)) {\n\t\t\trs->tapv = fir_16_4;\n\t\t\trs->tapc = RE_ARRAY_SIZE(fir_16_4);\n\t\t}\n\t\telse {\n\t\t\trs->tapv = fir_48_4;\n\t\t\trs->tapc = RE_ARRAY_SIZE(fir_48_4);\n\t\t}\n\t}\n\n\trs->orate = orate;\n\trs->och   = och;\n\trs->irate = irate;\n\trs->ich   = ich;\n\n\treturn 0;\n}\n\n\n/**\n * Resample\n *\n * @note When downsampling, the input count must be divisible by rate ratio\n *\n * @param rs   Resampler\n * @param outv Output samples\n * @param outc Output sample count (in/out)\n * @param inv  Input samples\n * @param inc  Input sample count\n *\n * @return 0 if success, otherwise error code\n */\nint auresamp(struct auresamp *rs, int16_t *outv, size_t *outc,\n\t     const int16_t *inv, size_t inc)\n{\n\tsize_t incc, outcc;\n\n\tif (!rs || !rs->resample || !outv || !outc || !inv)\n\t\treturn EINVAL;\n\n\tincc = inc / rs->ich;\n\n\tif (rs->up) {\n\t\toutcc = incc * rs->ratio;\n\n\t\tif (*outc < outcc * rs->och)\n\t\t\treturn ENOMEM;\n\n\t\trs->resample(outv, inv, inc, rs->ratio);\n\n\t\t*outc = outcc * rs->och;\n\n\t\tif (rs->tapv)\n\t\t\tfir_filter(&rs->fir, outv, outv, *outc, rs->och,\n\t\t\t\t   rs->tapv, rs->tapc);\n\t}\n\telse {\n\t\toutcc = incc / rs->ratio;\n\n\t\tif (*outc < outcc * rs->och || *outc < inc)\n\t\t\treturn ENOMEM;\n\n\t\tfir_filter(&rs->fir, outv, inv, inc, rs->ich,\n\t\t\t   rs->tapv, rs->tapc);\n\n\t\trs->resample(outv, outv, inc, rs->ratio);\n\n\t\t*outc = outcc * rs->och;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "rem/autone/tone.c",
    "content": "/**\n * @file tone.c  Audio Tones\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <math.h>\n#include <re.h>\n#include <rem_autone.h>\n#include <rem_dsp.h>\n\n\n#define SCALE (32767)\n#define DTMF_AMP  (5)\n\n#if !defined (M_PI)\n#define M_PI 3.14159265358979323846264338327\n#endif\n\n\nstatic inline uint32_t digit2lo(int digit)\n{\n\tswitch (digit) {\n\n\tcase '1': case '2': case '3': case 'A': return 697;\n\tcase '4': case '5': case '6': case 'B': return 770;\n\tcase '7': case '8': case '9': case 'C': return 852;\n\tcase '*': case '0': case '#': case 'D': return 941;\n\tdefault: return 0;\n\t}\n}\n\n\nstatic inline uint32_t digit2hi(int digit)\n{\n\tswitch (digit) {\n\n\tcase '1': case '4': case '7': case '*': return 1209;\n\tcase '2': case '5': case '8': case '0': return 1336;\n\tcase '3': case '6': case '9': case '#': return 1477;\n\tcase 'A': case 'B': case 'C': case 'D': return 1633;\n\tdefault: return 0;\n\t}\n}\n\n\n/**\n * Generate a dual-tone sine wave into a PCM buffer\n *\n * @param mb    Buffer for PCM samples\n * @param srate Sample rate in [Hz]\n * @param f1    Frequency number one\n * @param l1    Level of f1 from 0-100\n * @param f2    Frequency number two\n * @param l2    Level of f2 from 0-100\n *\n * @return 0 for success, otherwise error code\n */\nint autone_sine(struct mbuf *mb, uint32_t srate,\n\t\tuint32_t f1, int l1, uint32_t f2, int l2)\n{\n\tdouble d1, d2;\n\tuint32_t i;\n\tint err = 0;\n\n\tif (!mb || !srate)\n\t\treturn EINVAL;\n\n\td1 = 1.0f * f1 / srate;\n\td2 = 1.0f * f2 / srate;\n\n\tfor (i=0; i<srate; i++) {\n\t\tint16_t s1, s2;\n\n\t\ts1 = (int16_t)(SCALE * l1 / 100.0f * sin(2 * M_PI * d1 * i));\n\t\ts2 = (int16_t)(SCALE * l2 / 100.0f * sin(2 * M_PI * d2 * i));\n\n\t\terr |= mbuf_write_u16(mb, saturate_add16(s1, s2));\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Generate a DTMF tone into a PCM buffer\n *\n * @param mb    Buffer for PCM samples\n * @param srate Sample rate in [Hz]\n * @param digit DTMF digit to generate (0-9, *, #, A-D)\n *\n * @return 0 for success, otherwise error code\n */\nint autone_dtmf(struct mbuf *mb, uint32_t srate, int digit)\n{\n\treturn autone_sine(mb, srate,\n\t\t\t   digit2lo(digit), DTMF_AMP,\n\t\t\t   digit2hi(digit), DTMF_AMP);\n}\n"
  },
  {
    "path": "rem/avc/config.c",
    "content": "/**\n * @file avc/config.c Advanced Video Coding -- Configuration record\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_net.h>\n#include <rem_avc.h>\n\n\n#define AVC_CONFIG_VERSION 1\n#define SPS_MASK 0xe0\n\n\nint avc_config_encode(struct mbuf *mb, uint8_t profile_ind,\n\t\t      uint8_t profile_compat, uint8_t level_ind,\n\t\t      uint16_t sps_length, const uint8_t *sps,\n\t\t      uint16_t pps_length, const uint8_t *pps)\n{\n\tint err;\n\n\tif (!mb || !sps || !pps)\n\t\treturn EINVAL;\n\n\terr  = mbuf_write_u8(mb, AVC_CONFIG_VERSION);\n\n\terr |= mbuf_write_u8(mb, profile_ind);\n\terr |= mbuf_write_u8(mb, profile_compat);\n\terr |= mbuf_write_u8(mb, level_ind);\n\n\terr |= mbuf_write_u8(mb, 0xfc | (4-1));\n\n\t/* SPS */\n\terr |= mbuf_write_u8(mb, SPS_MASK | 1);\n\terr |= mbuf_write_u16(mb, htons(sps_length));\n\terr |= mbuf_write_mem(mb, sps, sps_length);\n\n\t/* PPS */\n\terr |= mbuf_write_u8(mb, 1);\n\terr |= mbuf_write_u16(mb, htons(pps_length));\n\terr |= mbuf_write_mem(mb, pps, pps_length);\n\n\treturn err;\n}\n\n\nint avc_config_decode(struct avc_config *conf, struct mbuf *mb)\n{\n\tuint8_t version, length_size, count;\n\n\tif (!conf || !mb)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < 5)\n\t\treturn EBADMSG;\n\n\tversion              = mbuf_read_u8(mb);\n\tconf->profile_ind    = mbuf_read_u8(mb);\n\tconf->profile_compat = mbuf_read_u8(mb);\n\tconf->level_ind      = mbuf_read_u8(mb);\n\tlength_size          = mbuf_read_u8(mb) & 0x03;\n\n\tif (version != AVC_CONFIG_VERSION || length_size != 3)\n\t\treturn EPROTO;\n\n\t/* SPS */\n\tif (mbuf_get_left(mb) < 3)\n\t\treturn EBADMSG;\n\n\tcount = mbuf_read_u8(mb) & 0x1f;\n\tconf->sps_len = ntohs(mbuf_read_u16(mb));\n\n\tif (count != 1 || conf->sps_len > sizeof(conf->sps))\n\t\treturn EOVERFLOW;\n\n\tif (mbuf_get_left(mb) < conf->sps_len)\n\t\treturn EBADMSG;\n\n\tint err = mbuf_read_mem(mb, conf->sps, conf->sps_len);\n\tif (err)\n\t\treturn err;\n\n\t/* PPS */\n\tif (mbuf_get_left(mb) < 3)\n\t\treturn EBADMSG;\n\n\tcount = mbuf_read_u8(mb);\n\tconf->pps_len = ntohs(mbuf_read_u16(mb));\n\n\tif (count != 1 || conf->pps_len > sizeof(conf->pps))\n\t\treturn EOVERFLOW;\n\n\tif (mbuf_get_left(mb) < conf->pps_len)\n\t\treturn EBADMSG;\n\n\terr = mbuf_read_mem(mb, conf->pps, conf->pps_len);\n\tif (err)\n\t\treturn err;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "rem/dtmf/dec.c",
    "content": "/**\n * @file dtmf/dec.c  DTMF Decoder\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <re.h>\n#include <rem_goertzel.h>\n#include <rem_dtmf.h>\n\n\n#define BLOCK_SIZE    102         /* At 8kHz sample rate */\n#define THRESHOLD     16439.10631 /* -42dBm0 / bsize^2 */\n#define NORMAL_TWIST  6.309573    /*   8dB   */\n#define REVERSE_TWIST 2.511886    /*   4dB   */\n#define RELATIVE_KEY  6.309573    /*   8dB   */\n#define RELATIVE_SUM  0.822243    /* -0.85dB */\n\n\nstatic const double fx[4] = { 1209.0, 1336.0, 1477.0, 1633.0 };\nstatic const double fy[4] = {  697.0,  770.0,  852.0,  941.0 };\n\nstatic const char keyv[4][4] = {{'1', '2', '3', 'A'},\n\t\t\t\t{'4', '5', '6', 'B'},\n\t\t\t\t{'7', '8', '9', 'C'},\n\t\t\t\t{'*', '0', '#', 'D'}};\n\n\nstruct dtmf_dec {\n\tstruct goertzel gx[4], gy[4];\n\tdtmf_dec_h *dech;\n\tvoid *arg;\n\tdouble threshold;\n\tdouble energy;\n\tdouble efac;\n\tunsigned bsize;\n\tunsigned bidx;\n\tchar digit, digit1;\n};\n\n\nstatic char decode_digit(struct dtmf_dec *dec)\n{\n\tunsigned i, x = 0, y = 0;\n\tdouble ex[4], ey[4];\n\n\tfor (i=0; i<4; i++) {\n\n\t\tex[i] = goertzel_result(&dec->gx[i]);\n\t\tey[i] = goertzel_result(&dec->gy[i]);\n\n\t\tif (ex[i] > ex[x])\n\t\t\tx = i;\n\n\t\tif (ey[i] > ey[y])\n\t\t\ty = i;\n\t}\n\n\tif (ex[x] < dec->threshold ||\n\t    ey[y] < dec->threshold)\n\t\treturn 0;\n\n\tif (ex[x] > ey[y] * NORMAL_TWIST ||\n\t    ey[y] > ex[x] * REVERSE_TWIST)\n\t\treturn 0;\n\n\tfor (i=0; i<4; i++) {\n\n\t\tif ((i != x && ex[i] * RELATIVE_KEY > ex[x]) ||\n\t\t    (i != y && ey[i] * RELATIVE_KEY > ey[y]))\n\t\t\treturn 0;\n\t}\n\n\tif ((ex[x] + ey[y]) < dec->efac * dec->energy)\n\t\treturn 0;\n\n\treturn keyv[y][x];\n}\n\n\n/**\n * Allocate a DTMF decoder instance\n *\n * @param decp  Pointer to allocated decoder\n * @param srate Sample rate\n * @param ch    Number of channels\n * @param dech  Decode handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint dtmf_dec_alloc(struct dtmf_dec **decp, unsigned srate, unsigned ch,\n\t\t   dtmf_dec_h *dech, void *arg)\n{\n\tstruct dtmf_dec *dec;\n\n\tif (!decp || !dech || !srate || !ch)\n\t\treturn EINVAL;\n\n\tdec = mem_zalloc(sizeof(*dec), NULL);\n\tif (!dec)\n\t\treturn ENOMEM;\n\n\tdtmf_dec_reset(dec, srate, ch);\n\n\tdec->dech = dech;\n\tdec->arg  = arg;\n\n\t*decp = dec;\n\n\treturn 0;\n}\n\n\n/**\n * Reset and configure DTMF decoder state\n *\n * @param dec   DTMF decoder\n * @param srate Sample rate\n * @param ch    Number of channels\n */\nvoid dtmf_dec_reset(struct dtmf_dec *dec, unsigned srate, unsigned ch)\n{\n\tunsigned i;\n\n\tif (!dec || !srate || !ch)\n\t\treturn;\n\n\tsrate *= ch;\n\n\tfor (i=0; i<4; i++) {\n\t\tgoertzel_init(&dec->gx[i], fx[i], srate);\n\t\tgoertzel_init(&dec->gy[i], fy[i], srate);\n\t}\n\n\tdec->bsize     = (BLOCK_SIZE * srate) / 8000;\n\tdec->threshold = THRESHOLD * dec->bsize * dec->bsize;\n\tdec->efac      = RELATIVE_SUM * dec->bsize;\n\n\tdec->energy = 0.0;\n\tdec->bidx   = 0;\n\tdec->digit  = 0;\n\tdec->digit1 = 0;\n}\n\n\n/**\n * Decode DTMF from input audio samples\n *\n * @param dec   DTMF decoder\n * @param sampv Buffer with audio samples\n * @param sampc Number of samples\n */\nvoid dtmf_dec_probe(struct dtmf_dec *dec, const int16_t *sampv, size_t sampc)\n{\n\tsize_t i;\n\n\tif (!dec || !sampv)\n\t\treturn;\n\n\tfor (i=0; i<sampc; i++) {\n\n\t\tchar digit0;\n\t\tunsigned j;\n\n\t\tfor (j=0; j<4; j++) {\n\t\t\tgoertzel_update(&dec->gx[j], sampv[i]);\n\t\t\tgoertzel_update(&dec->gy[j], sampv[i]);\n\t\t}\n\n\t\tdec->energy += sampv[i] * sampv[i];\n\n\t\tif (++dec->bidx < dec->bsize)\n\t\t\tcontinue;\n\n\t\tdigit0 = decode_digit(dec);\n\n\t\tif (digit0 != dec->digit && dec->digit1 != dec->digit) {\n\n\t\t\tdec->digit = digit0;\n\n\t\t\tif (digit0 != dec->digit1)\n\t\t\t\tdec->digit = 0;\n\n\t\t\tif (dec->digit)\n\t\t\t\tdec->dech(dec->digit, dec->arg);\n\t\t}\n\n\t\tdec->digit1 = digit0;\n\t\tdec->energy = 0.0;\n\t\tdec->bidx   = 0;\n\t}\n}\n"
  },
  {
    "path": "rem/fir/fir.c",
    "content": "/**\n * @file fir.c FIR -- Finite Impulse Response\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re.h>\n#include <rem_fir.h>\n\n\n/**\n * Reset the FIR-filter\n *\n * @param fir FIR-filter state\n */\nvoid fir_reset(struct fir *fir)\n{\n\tif (!fir)\n\t\treturn;\n\n\tmemset(fir, 0, sizeof(*fir));\n}\n\n\n/**\n * Process samples with the FIR filter\n *\n * @note product of channel and tap-count must be power of two\n *\n * @param fir  FIR filter\n * @param outv Output samples\n * @param inv  Input samples\n * @param inc  Number of samples\n * @param ch   Number of channels\n * @param tapv Filter taps\n * @param tapc Number of taps\n */\nvoid fir_filter(struct fir *fir, int16_t *outv, const int16_t *inv, size_t inc,\n\t\tunsigned ch, const int16_t *tapv, size_t tapc)\n{\n\tconst unsigned hmask = (ch * (unsigned)tapc) - 1;\n\n\tif (!fir || !outv || !inv || !ch || !tapv || !tapc)\n\t\treturn;\n\n\tif (hmask >= RE_ARRAY_SIZE(fir->history) || hmask & (hmask+1))\n\t\treturn;\n\n\twhile (inc--) {\n\n\t\tint64_t acc = 0;\n\t\tunsigned i, j;\n\n\t\tfir->history[fir->index & hmask] = *inv++;\n\n\t\tfor (i=0, j=fir->index++; i<tapc; ++i, j-=ch)\n\t\t\tacc += (int64_t)fir->history[j & hmask] * tapv[i];\n\n\t\tif (acc > 0x3fffffff)\n\t\t\tacc = 0x3fffffff;\n\t\telse if (acc < -0x40000000)\n\t\t\tacc = -0x40000000;\n\n\t\t*outv++ = (int16_t)(acc>>15);\n\t}\n}\n"
  },
  {
    "path": "rem/g711/g711.c",
    "content": "/**\n * @file g711.c G.711 codec\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <re_types.h>\n#include <rem_g711.h>\n\n\nconst uint8_t g711_l2u[4096] = {\n\t0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7,\n\t0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0xef,\n\t0xef, 0xee, 0xee, 0xed, 0xed, 0xec, 0xec, 0xeb,\n\t0xeb, 0xea, 0xea, 0xe9, 0xe9, 0xe8, 0xe8, 0xe7,\n\t0xe7, 0xe6, 0xe6, 0xe5, 0xe5, 0xe4, 0xe4, 0xe3,\n\t0xe3, 0xe2, 0xe2, 0xe1, 0xe1, 0xe0, 0xe0, 0xdf,\n\t0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xdd,\n\t0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdb,\n\t0xdb, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xda, 0xd9,\n\t0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7,\n\t0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd5,\n\t0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3,\n\t0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1,\n\t0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xcf,\n\t0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce,\n\t0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd,\n\t0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcc,\n\t0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb,\n\t0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca,\n\t0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xc9,\n\t0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8,\n\t0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7,\n\t0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc6,\n\t0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5,\n\t0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4,\n\t0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc3,\n\t0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2,\n\t0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1,\n\t0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc0,\n\t0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf,\n\t0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\n\t0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbe,\n\t0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\n\t0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd,\n\t0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\n\t0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc,\n\t0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\n\t0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbb,\n\t0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\n\t0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba,\n\t0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\n\t0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9,\n\t0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\n\t0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb8,\n\t0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\n\t0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7,\n\t0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\n\t0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6,\n\t0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\n\t0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb5,\n\t0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\n\t0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4,\n\t0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\n\t0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3,\n\t0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\n\t0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb2,\n\t0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\n\t0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1,\n\t0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\n\t0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0,\n\t0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\n\t0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xaf,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xac,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa9,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa6,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa3,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa0,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f,\n\t0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\n\t0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\n\t0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\n\t0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\n\t0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\n\t0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\n\t0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\n\t0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9e,\n\t0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\n\t0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\n\t0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\n\t0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\n\t0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\n\t0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\n\t0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\n\t0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d,\n\t0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\n\t0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\n\t0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\n\t0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\n\t0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\n\t0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\n\t0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\n\t0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c,\n\t0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\n\t0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\n\t0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\n\t0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\n\t0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\n\t0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\n\t0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\n\t0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9b,\n\t0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\n\t0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\n\t0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\n\t0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\n\t0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\n\t0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\n\t0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\n\t0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a,\n\t0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\n\t0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\n\t0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\n\t0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\n\t0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\n\t0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\n\t0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\n\t0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99,\n\t0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\n\t0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\n\t0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\n\t0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\n\t0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\n\t0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\n\t0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\n\t0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98,\n\t0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\n\t0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\n\t0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\n\t0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\n\t0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\n\t0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\n\t0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\n\t0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97,\n\t0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\n\t0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\n\t0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\n\t0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\n\t0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\n\t0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\n\t0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\n\t0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96,\n\t0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\n\t0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\n\t0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\n\t0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\n\t0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\n\t0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\n\t0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\n\t0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x95,\n\t0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\n\t0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\n\t0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\n\t0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\n\t0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\n\t0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\n\t0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\n\t0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94,\n\t0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\n\t0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\n\t0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\n\t0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\n\t0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\n\t0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\n\t0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\n\t0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93,\n\t0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\n\t0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\n\t0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\n\t0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\n\t0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\n\t0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\n\t0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\n\t0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x92,\n\t0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\n\t0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\n\t0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\n\t0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\n\t0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\n\t0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\n\t0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\n\t0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91,\n\t0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\n\t0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\n\t0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\n\t0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\n\t0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\n\t0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\n\t0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\n\t0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90,\n\t0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\n\t0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\n\t0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\n\t0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\n\t0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\n\t0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\n\t0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\n\t0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n};\n\nconst uint8_t g711_l2A[2048] = {\n\t0xd5, 0xd4, 0xd7, 0xd6, 0xd1, 0xd0, 0xd3, 0xd2,\n\t0xdd, 0xdc, 0xdf, 0xde, 0xd9, 0xd8, 0xdb, 0xda,\n\t0xc5, 0xc4, 0xc7, 0xc6, 0xc1, 0xc0, 0xc3, 0xc2,\n\t0xcd, 0xcc, 0xcf, 0xce, 0xc9, 0xc8, 0xcb, 0xca,\n\t0xf5, 0xf5, 0xf4, 0xf4, 0xf7, 0xf7, 0xf6, 0xf6,\n\t0xf1, 0xf1, 0xf0, 0xf0, 0xf3, 0xf3, 0xf2, 0xf2,\n\t0xfd, 0xfd, 0xfc, 0xfc, 0xff, 0xff, 0xfe, 0xfe,\n\t0xf9, 0xf9, 0xf8, 0xf8, 0xfb, 0xfb, 0xfa, 0xfa,\n\t0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4,\n\t0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6,\n\t0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0,\n\t0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2,\n\t0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec,\n\t0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee,\n\t0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8,\n\t0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea,\n\t0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,\n\t0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,\n\t0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,\n\t0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96,\n\t0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91,\n\t0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,\n\t0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93,\n\t0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92,\n\t0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d,\n\t0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,\n\t0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,\n\t0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,\n\t0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,\n\t0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,\n\t0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,\n\t0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a,\n\t0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\n\t0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\n\t0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\n\t0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,\n\t0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\n\t0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\n\t0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\n\t0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,\n\t0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\n\t0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\n\t0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\n\t0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,\n\t0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\n\t0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\n\t0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\n\t0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6,\n\t0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\n\t0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\n\t0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\n\t0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1,\n\t0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\n\t0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\n\t0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\n\t0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0,\n\t0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\n\t0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\n\t0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\n\t0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,\n\t0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\n\t0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\n\t0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\n\t0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,\n\t0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\n\t0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\n\t0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\n\t0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,\n\t0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\n\t0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\n\t0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\n\t0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,\n\t0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\n\t0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\n\t0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\n\t0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,\n\t0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\n\t0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\n\t0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\n\t0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,\n\t0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\n\t0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\n\t0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\n\t0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9,\n\t0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\n\t0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\n\t0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\n\t0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,\n\t0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\n\t0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\n\t0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\n\t0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,\n\t0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\n\t0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\n\t0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\n\t0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\n\t0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\n\t0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\n\t0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\n\t0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\n\t0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\n\t0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\n\t0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\n\t0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\n\t0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\n\t0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\n\t0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\n\t0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\n\t0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\n\t0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\n\t0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n\t0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,\n};\n\nconst int16_t g711_u2l[256] = {\n\t-32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,\n\t-23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,\n\t-15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,\n\t-11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,\n\t -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,\n\t -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,\n\t -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,\n\t -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,\n\t -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,\n\t -1372, -1308, -1244, -1180, -1116, -1052,  -988,  -924,\n\t  -876,  -844,  -812,  -780,  -748,  -716,  -684,  -652,\n\t  -620,  -588,  -556,  -524,  -492,  -460,  -428,  -396,\n\t  -372,  -356,  -340,  -324,  -308,  -292,  -276,  -260,\n\t  -244,  -228,  -212,  -196,  -180,  -164,  -148,  -132,\n\t  -120,  -112,  -104,   -96,   -88,   -80,   -72,   -64,\n\t   -56,   -48,   -40,   -32,   -24,   -16,    -8,    -2,\n\t 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,\n\t 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,\n\t 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,\n\t 11900, 11388, 10876, 10364,  9852,  9340,  8828,  8316,\n\t  7932,  7676,  7420,  7164,  6908,  6652,  6396,  6140,\n\t  5884,  5628,  5372,  5116,  4860,  4604,  4348,  4092,\n\t  3900,  3772,  3644,  3516,  3388,  3260,  3132,  3004,\n\t  2876,  2748,  2620,  2492,  2364,  2236,  2108,  1980,\n\t  1884,  1820,  1756,  1692,  1628,  1564,  1500,  1436,\n\t  1372,  1308,  1244,  1180,  1116,  1052,   988,   924,\n\t   876,   844,   812,   780,   748,   716,   684,   652,\n\t   620,   588,   556,   524,   492,   460,   428,   396,\n\t   372,   356,   340,   324,   308,   292,   276,   260,\n\t   244,   228,   212,   196,   180,   164,   148,   132,\n\t   120,   112,   104,    96,    88,    80,    72,    64,\n\t    56,    48,    40,    32,    24,    16,     8,     2,\n};\n\nconst int16_t g711_A2l[256] = {\n\t -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,\n\t -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,\n\t -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,\n\t -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,\n\t-22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,\n\t-30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,\n\t-11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472,\n\t-15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,\n\t  -344,  -328,  -376,  -360,  -280,  -264,  -312,  -296,\n\t  -472,  -456,  -504,  -488,  -408,  -392,  -440,  -424,\n\t   -88,   -72,  -120,  -104,   -24,    -8,   -56,   -40,\n\t  -216,  -200,  -248,  -232,  -152,  -136,  -184,  -168,\n\t -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,\n\t -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,\n\t  -688,  -656,  -752,  -720,  -560,  -528,  -624,  -592,\n\t  -944,  -912, -1008,  -976,  -816,  -784,  -880,  -848,\n\t  5504,  5248,  6016,  5760,  4480,  4224,  4992,  4736,\n\t  7552,  7296,  8064,  7808,  6528,  6272,  7040,  6784,\n\t  2752,  2624,  3008,  2880,  2240,  2112,  2496,  2368,\n\t  3776,  3648,  4032,  3904,  3264,  3136,  3520,  3392,\n\t 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,\n\t 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,\n\t 11008, 10496, 12032, 11520,  8960,  8448,  9984,  9472,\n\t 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,\n\t   344,   328,   376,   360,   280,   264,   312,   296,\n\t   472,   456,   504,   488,   408,   392,   440,   424,\n\t    88,    72,   120,   104,    24,     8,    56,    40,\n\t   216,   200,   248,   232,   152,   136,   184,   168,\n\t  1376,  1312,  1504,  1440,  1120,  1056,  1248,  1184,\n\t  1888,  1824,  2016,  1952,  1632,  1568,  1760,  1696,\n\t   688,   656,   752,   720,   560,   528,   624,   592,\n\t   944,   912,  1008,   976,   816,   784,   880,   848,\n};\n"
  },
  {
    "path": "rem/goertzel/goertzel.c",
    "content": "/**\n * @file goertzel.c  Goertzel algorithm\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <math.h>\n#include <re.h>\n#include <rem_goertzel.h>\n\n\n#define PI 3.14159265358979323846264338327\n\n\n/**\n * Initialize goertzel state\n *\n * @param g     Goertzel state\n * @param freq  Target frequency\n * @param srate Sample rate\n */\nvoid goertzel_init(struct goertzel *g, double freq, unsigned srate)\n{\n\tg->q1   = 0.0;\n\tg->q2   = 0.0;\n\tg->coef = 2.0 * cos(2.0 * PI * (freq/(double)srate));\n}\n\n\n/**\n * Reset goertzel state\n *\n * @param g Goertzel state\n */\nvoid goertzel_reset(struct goertzel *g)\n{\n\tg->q1 = 0.0;\n\tg->q2 = 0.0;\n}\n\n\n/**\n * Calculate result and reset state\n *\n * @param g Goertzel state\n *\n * @return Result value\n */\ndouble goertzel_result(struct goertzel *g)\n{\n\tdouble res;\n\n\tgoertzel_update(g, 0);\n\n\tres = g->q1*g->q1 + g->q2*g->q2 - g->q1*g->q2*g->coef;\n\n\tgoertzel_reset(g);\n\n\treturn res * 2.0;\n}\n"
  },
  {
    "path": "rem/vid/draw.c",
    "content": "/**\n * @file draw.c Video Frame primitive drawing routines\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re.h>\n#include <rem_vid.h>\n\n\n/**\n * Draw a pixel to a video frame\n *\n * @param f   Video frame\n * @param x   Pixel X-position\n * @param y   Pixel Y-position\n * @param r   Red color component\n * @param g   Green color component\n * @param b   Blue color component\n */\nvoid vidframe_draw_point(struct vidframe *f, unsigned x, unsigned y,\n\t\t\t uint8_t r, uint8_t g, uint8_t b)\n{\n\tuint8_t *yp, *up, *vp;\n\tuint32_t *p;\n\tsize_t uv_offset;\n\n\tif (!f)\n\t\treturn;\n\n\tif (x >= f->size.w || y >= f->size.h)\n\t\treturn;\n\n\tswitch (f->fmt) {\n\n\tcase VID_FMT_YUV420P:\n\t\typ = f->data[0] + f->linesize[0] * y     + x;\n\t\tup = f->data[1] + f->linesize[1] * (y/2) + x/2;\n\t\tvp = f->data[2] + f->linesize[2] * (y/2) + x/2;\n\n\t\typ[0] = rgb2y(r, g, b);\n\t\tup[0] = rgb2u(r, g, b);\n\t\tvp[0] = rgb2v(r, g, b);\n\t\tbreak;\n\n\tcase VID_FMT_YUYV422:\n\t\tuv_offset = (f->linesize[0] * y + x * 2) & ~3;\n\n\t\typ = f->data[0] + uv_offset;\n\n\t\typ[0] = rgb2y(r, g, b);\n\t\typ[1] = rgb2u(r, g, b);\n\t\typ[2] = rgb2y(r, g, b);\n\t\typ[3] = rgb2v(r, g, b);\n\t\tbreak;\n\n\tcase VID_FMT_YUV444P:\n\t\typ = f->data[0] + f->linesize[0] * y + x;\n\t\tup = f->data[1] + f->linesize[1] * y + x;\n\t\tvp = f->data[2] + f->linesize[2] * y + x;\n\n\t\typ[0] = rgb2y(r, g, b);\n\t\tup[0] = rgb2u(r, g, b);\n\t\tvp[0] = rgb2v(r, g, b);\n\t\tbreak;\n\n\tcase VID_FMT_RGB32:\n\t\tp = (void *)(f->data[0] + f->linesize[0] * y + x*4);\n\n\t\t*p = (uint32_t)r << 16 | (uint32_t)g << 8 | b;\n\t\tbreak;\n\n\tcase VID_FMT_NV12:\n\t\tuv_offset = (f->linesize[1] * (y/2) + x) & ~1;\n\n\t\typ = f->data[0] + f->linesize[0] * y + x;\n\t\tup = f->data[1] + uv_offset;\n\t\tvp = f->data[1] + uv_offset + 1;\n\n\t\typ[0] = rgb2y(r, g, b);\n\t\tup[0] = rgb2u(r, g, b);\n\t\tvp[0] = rgb2v(r, g, b);\n\t\tbreak;\n\n\tcase VID_FMT_NV21:\n\t\tuv_offset = (f->linesize[1] * (y/2) + x) & ~1;\n\n\t\typ = f->data[0] + f->linesize[0] * y + x;\n\t\tup = f->data[1] + uv_offset + 1;\n\t\tvp = f->data[1] + uv_offset;\n\n\t\typ[0] = rgb2y(r, g, b);\n\t\tup[0] = rgb2u(r, g, b);\n\t\tvp[0] = rgb2v(r, g, b);\n\t\tbreak;\n\n\tcase VID_FMT_YUV422P:\n\t\typ = f->data[0] + f->linesize[0] * y + x;\n\t\tup = f->data[1] + f->linesize[1] * y + x/2;\n\t\tvp = f->data[2] + f->linesize[2] * y + x/2;\n\n\t\typ[0] = rgb2y(r, g, b);\n\t\tup[0] = rgb2u(r, g, b);\n\t\tvp[0] = rgb2v(r, g, b);\n\t\tbreak;\n\n\tdefault:\n\t\t(void)re_fprintf(stderr, \"vidframe_draw_point:\"\n\t\t\t\t \" unsupported format %s\\n\",\n\t\t\t\t vidfmt_name(f->fmt));\n\t\tbreak;\n\t}\n}\n\n\n/**\n * Draw a horizontal line\n *\n * @param f   Video frame\n * @param x0  Origin X-position\n * @param y0  Origin Y-position\n * @param w   Line width\n * @param r   Red color component\n * @param g   Green color component\n * @param b   Blue color component\n */\nvoid vidframe_draw_hline(struct vidframe *f,\n\t\t\t unsigned x0, unsigned y0, unsigned w,\n\t\t\t uint8_t r, uint8_t g, uint8_t b)\n{\n\tuint8_t y, u, v;\n\tuint8_t *p;\n\tsize_t offset;\n\n\tif (!f)\n\t\treturn;\n\n\tif (x0 >= f->size.w || y0 >= f->size.h)\n\t\treturn;\n\n\tw = min(w, f->size.w-x0);\n\n\ty = rgb2y(r, g, b);\n\tu = rgb2u(r, g, b);\n\tv = rgb2v(r, g, b);\n\n\tswitch (f->fmt) {\n\n\tcase VID_FMT_YUV420P:\n\t\tmemset(f->data[0] +  y0   *f->linesize[0] + x0,   y, w);\n\t\tmemset(f->data[1] + (y0/2)*f->linesize[1] + x0/2, u, w/2);\n\t\tmemset(f->data[2] + (y0/2)*f->linesize[2] + x0/2, v, w/2);\n\t\tbreak;\n\n\tcase VID_FMT_YUV444P:\n\t\tmemset(f->data[0] + y0*f->linesize[0] + x0, y, w);\n\t\tmemset(f->data[1] + y0*f->linesize[1] + x0, u, w);\n\t\tmemset(f->data[2] + y0*f->linesize[2] + x0, v, w);\n\t\tbreak;\n\n\tcase VID_FMT_YUYV422:\n\t\toffset = (y0*f->linesize[0] + x0) & ~3;\n\t\tp = f->data[0] + offset;\n\n\t\tfor (unsigned x=0; x<w; x++) {\n\n\t\t\tp[x*4  ] = y;\n\t\t\tp[x*4+1] = u;\n\t\t\tp[x*4+2] = y;\n\t\t\tp[x*4+3] = v;\n\t\t}\n\t\tbreak;\n\n\tcase VID_FMT_NV12:\n\t\toffset = (f->linesize[1] * (y0/2) + x0) & ~1;\n\t\tp = f->data[1] + offset;\n\n\t\tmemset(f->data[0] +  y0   *f->linesize[0] + x0,   y, w);\n\n\t\tfor (unsigned x=0; x<w; x+=2) {\n\n\t\t\tp[x  ] = u;\n\t\t\tp[x+1] = v;\n\t\t}\n\t\tbreak;\n\n\tcase VID_FMT_YUV422P:\n\t\tmemset(f->data[0] + y0*f->linesize[0] + x0, y, w);\n\t\tmemset(f->data[1] + y0*f->linesize[1] + x0, u, w);\n\t\tmemset(f->data[2] + y0*f->linesize[2] + x0, v, w);\n\t\tbreak;\n\n\tdefault:\n\t\t(void)re_fprintf(stderr, \"vidframe_draw_hline:\"\n\t\t\t\t \" unsupported format %s\\n\",\n\t\t\t\t vidfmt_name(f->fmt));\n\t\tbreak;\n\t}\n}\n\n\n/**\n * Draw a vertical line\n *\n * @param f   Video frame\n * @param x0  Origin X-position\n * @param y0  Origin Y-position\n * @param h   Line height\n * @param r   Red color component\n * @param g   Green color component\n * @param b   Blue color component\n */\nvoid vidframe_draw_vline(struct vidframe *f,\n\t\t\t unsigned x0, unsigned y0, unsigned h,\n\t\t\t uint8_t r, uint8_t g, uint8_t b)\n{\n\tif (!f)\n\t\treturn;\n\n\twhile (h--) {\n\t\tvidframe_draw_point(f, x0, y0++, r, g, b);\n\t}\n}\n\n\n/**\n * Draw a rectangle\n *\n * @param f   Video frame\n * @param x0  Origin X-position\n * @param y0  Origin Y-position\n * @param w   Rectangle width\n * @param h   Rectangle height\n * @param r   Red color component\n * @param g   Green color component\n * @param b   Blue color component\n */\nvoid vidframe_draw_rect(struct vidframe *f, unsigned x0, unsigned y0,\n\t\t\tunsigned w, unsigned h,\n\t\t\tuint8_t r, uint8_t g, uint8_t b)\n{\n\tif (!f)\n\t\treturn;\n\n\tvidframe_draw_hline(f, x0,     y0,     w, r, g, b);\n\tvidframe_draw_hline(f, x0,     y0+h-1, w, r, g, b);\n\tvidframe_draw_vline(f, x0,     y0,     h, r, g, b);\n\tvidframe_draw_vline(f, x0+w-1, y0,     h, r, g, b);\n}\n"
  },
  {
    "path": "rem/vid/fmt.c",
    "content": "/**\n * @file vid/fmt.c Video Formats\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <re.h>\n#include <rem_vid.h>\n\n\n/** Video format description table */\nconst struct vidfmt_desc vidfmt_descv[VID_FMT_N] = {\n\n\t{\"yuv420p\", 3, 3, { {0, 1}, {1, 1}, {2, 1}, {0, 0} } },\n\t{\"yuyv422\", 1, 3, { {0, 2}, {0, 4}, {0, 4}, {0, 0} } },\n\t{\"uyvy422\", 1, 3, { {0, 2}, {0, 4}, {0, 4}, {0, 0} } },\n\t{\"rgb32\",   1, 4, { {0, 4}, {0, 4}, {0, 4}, {0, 4} } },\n\t{\"argb\",    1, 4, { {0, 4}, {0, 4}, {0, 4}, {0, 4} } },\n\t{\"rgb565\",  1, 3, { {0, 2}, {0, 2}, {0, 2}, {0, 0} } },\n\t{\"nv12\",    3, 2, { {0, 1}, {1, 2}, {1, 2}, {0, 0} } },\n\t{\"nv21\",    3, 2, { {0, 1}, {1, 2}, {1, 2}, {0, 0} } },\n\t{\"yuv444p\", 3, 3, { {0, 1}, {1, 1}, {2, 1}, {0, 0} } },\n\t{\"yuv422p\", 3, 3, { {0, 1}, {1, 1}, {2, 1}, {0, 0} } },\n};\n\n\n/**\n * Get the name of a video format\n *\n * @param fmt Video format\n *\n * @return Name of the video format\n */\nconst char *vidfmt_name(enum vidfmt fmt)\n{\n\tif (fmt >= VID_FMT_N)\n\t\treturn \"???\";\n\n\treturn vidfmt_descv[fmt].name;\n}\n"
  },
  {
    "path": "rem/vid/frame.c",
    "content": "/**\n * @file frame.c Video Frame\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re.h>\n#include <rem_vid.h>\n\n\n/**\n * Get video frame buffer size\n *\n * @param fmt Video pixel format\n * @param sz  Size of video frame\n *\n * @return Number of bytes\n */\nsize_t vidframe_size(enum vidfmt fmt, const struct vidsz *sz)\n{\n\tif (!sz)\n\t\treturn 0;\n\n\tswitch (fmt) {\n\n\tcase VID_FMT_YUV420P: return (size_t)sz->w * sz->h * 3 / 2;\n\tcase VID_FMT_YUYV422: return (size_t)sz->w * sz->h * 2;\n\tcase VID_FMT_UYVY422: return (size_t)sz->w * sz->h * 2;\n\tcase VID_FMT_RGB32:   return (size_t)sz->w * sz->h * 4;\n\tcase VID_FMT_ARGB:    return (size_t)sz->w * sz->h * 4;\n\tcase VID_FMT_RGB565:  return (size_t)sz->w * sz->h * 2;\n\tcase VID_FMT_NV12:    return (size_t)sz->w * sz->h * 3 / 2;\n\tcase VID_FMT_NV21:    return (size_t)sz->w * sz->h * 3 / 2;\n\tcase VID_FMT_YUV444P: return (size_t)sz->w * sz->h * 3;\n\tcase VID_FMT_YUV422P: return (size_t)sz->w * sz->h * 2;\n\tdefault:\n\t\treturn 0;\n\t}\n}\n\n\n/**\n * Initialize a video frame\n *\n * @param vf       Video frame\n * @param fmt      Video pixel format\n * @param sz       Size of video frame\n * @param data     Pointer to video planes\n * @param linesize Pointer to linesizes\n */\nvoid vidframe_init(struct vidframe *vf, enum vidfmt fmt,\n\t\t   const struct vidsz *sz, void *data[4], unsigned linesize[4])\n{\n\tint i;\n\n\tif (!vf || !sz || !data || !linesize)\n\t\treturn;\n\n\tfor (i=0; i<4; i++) {\n\t\tvf->data[i]     = data[i];\n\t\tvf->linesize[i] = linesize[i];\n\t}\n\n\tvf->size = *sz;\n\tvf->fmt = fmt;\n\tvf->xoffs = 0;\n\tvf->yoffs = 0;\n}\n\n\n/**\n * Initialize a video frame from a buffer\n *\n * @param vf  Video frame\n * @param fmt Video pixel format\n * @param sz  Size of video frame\n * @param buf Frame buffer\n */\nvoid vidframe_init_buf(struct vidframe *vf, enum vidfmt fmt,\n\t\t       const struct vidsz *sz, uint8_t *buf)\n{\n\tunsigned w, h;\n\n\tif (!vf || !sz || !buf)\n\t\treturn;\n\n\tw = (sz->w + 1) >> 1;\n\th = (sz->h + 1) >> 1;\n\n\tunsigned w2 = (sz->w + 1) >> 1;\n\n\tmemset(vf->linesize, 0, sizeof(vf->linesize));\n\tmemset(vf->data, 0, sizeof(vf->data));\n\n\tswitch (fmt) {\n\n\tcase VID_FMT_YUV420P:\n\t\tvf->linesize[0] = sz->w;\n\t\tvf->linesize[1] = w;\n\t\tvf->linesize[2] = w;\n\n\t\tvf->data[0] = buf;\n\t\tvf->data[1] = vf->data[0] + vf->linesize[0] * sz->h;\n\t\tvf->data[2] = vf->data[1] + vf->linesize[1] * h;\n\t\tbreak;\n\n\tcase VID_FMT_YUYV422:\n\tcase VID_FMT_UYVY422:\n\t\tvf->linesize[0] = sz->w * 2;\n\t\tvf->data[0] = buf;\n\t\tbreak;\n\n\tcase VID_FMT_RGB32:\n\tcase VID_FMT_ARGB:\n\t\tvf->linesize[0] = sz->w * 4;\n\t\tvf->data[0] = buf;\n\t\tbreak;\n\n\tcase VID_FMT_RGB565:\n\t\tvf->linesize[0] = sz->w * 2;\n\t\tvf->data[0] = buf;\n\t\tbreak;\n\n\tcase VID_FMT_NV12:\n\tcase VID_FMT_NV21:\n\t\tvf->linesize[0] = sz->w;\n\t\tvf->linesize[1] = w*2;\n\n\t\tvf->data[0] = buf;\n\t\tvf->data[1] = vf->data[0] + vf->linesize[0] * sz->h;\n\t\tbreak;\n\n\tcase VID_FMT_YUV444P:\n\t\tvf->linesize[0] = sz->w;\n\t\tvf->linesize[1] = sz->w;\n\t\tvf->linesize[2] = sz->w;\n\n\t\tvf->data[0] = buf;\n\t\tvf->data[1] = vf->data[0] + vf->linesize[0] * sz->h;\n\t\tvf->data[2] = vf->data[1] + vf->linesize[1] * sz->h;\n\t\tbreak;\n\n\tcase VID_FMT_YUV422P:\n\t\tvf->linesize[0] = sz->w;\n\t\tvf->linesize[1] = w2;\n\t\tvf->linesize[2] = w2;\n\n\t\tvf->data[0] = buf;\n\t\tvf->data[1] = vf->data[0] + vf->linesize[0] * sz->h;\n\t\tvf->data[2] = vf->data[1] + vf->linesize[1] * sz->h;\n\t\tbreak;\n\n\tdefault:\n\t\t(void)re_printf(\"vidframe: no fmt %s\\n\", vidfmt_name(fmt));\n\t\treturn;\n\t}\n\n\tvf->size = *sz;\n\tvf->fmt = fmt;\n\tvf->xoffs = 0;\n\tvf->yoffs = 0;\n}\n\n\n/**\n * Allocate an empty video frame\n *\n * @param vfp Pointer to allocated video frame\n * @param fmt Video pixel format\n * @param sz  Size of video frame\n *\n * @return 0 for success, otherwise error code\n */\nint vidframe_alloc(struct vidframe **vfp, enum vidfmt fmt,\n\t\t   const struct vidsz *sz)\n{\n\tstruct vidframe *vf;\n\n\tif (!sz || !sz->w || !sz->h)\n\t\treturn EINVAL;\n\n\tvf = mem_zalloc(sizeof(*vf) + vidframe_size(fmt, sz), NULL);\n\tif (!vf)\n\t\treturn ENOMEM;\n\n\tvidframe_init_buf(vf, fmt, sz, (uint8_t *)(vf + 1));\n\n\t*vfp = vf;\n\n\treturn 0;\n}\n\n\n/**\n * Fill a video frame with a nice color\n *\n * @param vf Video frame\n * @param r  Red color component\n * @param g  Green color component\n * @param b  Blue color component\n */\nvoid vidframe_fill(struct vidframe *vf, uint32_t r, uint32_t g, uint32_t b)\n{\n\tuint8_t *p;\n\tsize_t h;\n\tunsigned i, x;\n\tint u, v;\n\n\tif (!vf)\n\t\treturn;\n\n\tswitch (vf->fmt) {\n\n\tcase VID_FMT_YUV420P:\n\t\th = vf->size.h;\n\n\t\tmemset(vf->data[0], rgb2y(r, g, b), h * vf->linesize[0]);\n\t\tmemset(vf->data[1], rgb2u(r, g, b), h/2 * vf->linesize[1]);\n\t\tmemset(vf->data[2], rgb2v(r, g, b), h/2 * vf->linesize[2]);\n\t\tbreak;\n\n\tcase VID_FMT_YUV444P:\n\t\th = vf->size.h;\n\n\t\tmemset(vf->data[0], rgb2y(r, g, b), h * vf->linesize[0]);\n\t\tmemset(vf->data[1], rgb2u(r, g, b), h * vf->linesize[1]);\n\t\tmemset(vf->data[2], rgb2v(r, g, b), h * vf->linesize[2]);\n\t\tbreak;\n\n\tcase VID_FMT_RGB32:\n\t\tp = vf->data[0];\n\t\tfor (i=0; i<vf->linesize[0] * vf->size.h; i+=4) {\n\t\t\t*p++ = b;\n\t\t\t*p++ = g;\n\t\t\t*p++ = r;\n\t\t\t*p++ = 0;\n\t\t}\n\t\tbreak;\n\n\tcase VID_FMT_NV12:\n\tcase VID_FMT_NV21:\n\t\th = vf->size.h;\n\n\t\tif (vf->fmt == VID_FMT_NV12) {\n\t\t\tu = rgb2u(r, g, b);\n\t\t\tv = rgb2v(r, g, b);\n\t\t}\n\t\telse {\n\t\t\tv = rgb2u(r, g, b);\n\t\t\tu = rgb2v(r, g, b);\n\t\t}\n\n\t\tmemset(vf->data[0], rgb2y(r, g, b), h * vf->linesize[0]);\n\n\t\tp = vf->data[1];\n\n\t\tfor (h=0; h<vf->size.h; h+=2) {\n\n\t\t\tfor (x=0; x<vf->size.w; x+=2) {\n\t\t\t\tp[x  ] = u;\n\t\t\t\tp[x+1] = v;\n\t\t\t}\n\n\t\t\tp += vf->linesize[1];\n\t\t}\n\t\tbreak;\n\n\tcase VID_FMT_YUV422P:\n\t\th = vf->size.h;\n\n\t\tmemset(vf->data[0], rgb2y(r, g, b), h * vf->linesize[0]);\n\t\tmemset(vf->data[1], rgb2u(r, g, b), h * vf->linesize[1]);\n\t\tmemset(vf->data[2], rgb2v(r, g, b), h * vf->linesize[2]);\n\t\tbreak;\n\n\tdefault:\n\t\t(void)re_printf(\"vidfill: no fmt %s\\n\", vidfmt_name(vf->fmt));\n\t\tbreak;\n\t}\n}\n\n\n/**\n * Copy content between to equally sized video frames of same pixel format\n *\n * @param dst Destination frame\n * @param src Source frame\n */\nvoid vidframe_copy(struct vidframe *dst, const struct vidframe *src)\n{\n\tconst uint8_t *ds0, *ds1, *ds2;\n\tunsigned lsd, lss, w, h, y;\n\tunsigned lsd1, lss1;\n\tunsigned lsd2, lss2;\n\tuint8_t *dd0, *dd1, *dd2;\n\n\tif (!dst || !src)\n\t\treturn;\n\n\tif (!vidsz_cmp(&dst->size, &src->size))\n\t\treturn;\n\n\tif (dst->fmt != src->fmt)\n\t\treturn;\n\n\tswitch (dst->fmt) {\n\n\tcase VID_FMT_YUV420P:\n\t\tlsd = dst->linesize[0];\n\t\tlss = src->linesize[0];\n\n\t\tdd0 = dst->data[0];\n\t\tdd1 = dst->data[1];\n\t\tdd2 = dst->data[2];\n\n\t\tds0 = src->data[0];\n\t\tds1 = src->data[1];\n\t\tds2 = src->data[2];\n\n\t\tw  = dst->size.w & ~1;\n\t\th  = dst->size.h & ~1;\n\n\t\tfor (y=0; y<h; y+=2) {\n\n\t\t\tmemcpy(dd0, ds0, w);\n\t\t\tdd0 += lsd;\n\t\t\tds0 += lss;\n\n\t\t\tmemcpy(dd0, ds0, w);\n\t\t\tdd0 += lsd;\n\t\t\tds0 += lss;\n\n\t\t\tmemcpy(dd1, ds1, w/2);\n\t\t\tdd1 += lsd/2;\n\t\t\tds1 += lss/2;\n\n\t\t\tmemcpy(dd2, ds2, w/2);\n\t\t\tdd2 += lsd/2;\n\t\t\tds2 += lss/2;\n\t\t}\n\t\tbreak;\n\n\tcase VID_FMT_YUV444P:\n\t\tlsd = dst->linesize[0];\n\t\tlss = src->linesize[0];\n\n\t\tdd0 = dst->data[0];\n\t\tdd1 = dst->data[1];\n\t\tdd2 = dst->data[2];\n\n\t\tds0 = src->data[0];\n\t\tds1 = src->data[1];\n\t\tds2 = src->data[2];\n\n\t\tw  = dst->size.w;\n\t\th  = dst->size.h;\n\n\t\tfor (y=0; y<h; y++) {\n\n\t\t\t/* Y */\n\t\t\tmemcpy(dd0, ds0, w);\n\t\t\tdd0 += lsd;\n\t\t\tds0 += lss;\n\n\t\t\t/* U */\n\t\t\tmemcpy(dd1, ds1, w);\n\t\t\tdd1 += lsd;\n\t\t\tds1 += lss;\n\n\t\t\t/* V */\n\t\t\tmemcpy(dd2, ds2, w);\n\t\t\tdd2 += lsd;\n\t\t\tds2 += lss;\n\t\t}\n\t\tbreak;\n\n\tcase VID_FMT_NV12:\n\tcase VID_FMT_NV21:\n\t\tlsd = dst->linesize[0];\n\t\tlss = src->linesize[0];\n\n\t\tdd0 = dst->data[0];\n\t\tdd1 = dst->data[1];\n\n\t\tds0 = src->data[0];\n\t\tds1 = src->data[1];\n\n\t\tw  = dst->size.w & ~1;\n\t\th  = dst->size.h & ~1;\n\n\t\tfor (y=0; y<h; y+=2) {\n\n\t\t\tmemcpy(dd0, ds0, w);\n\t\t\tdd0 += lsd;\n\t\t\tds0 += lss;\n\n\t\t\tmemcpy(dd0, ds0, w);\n\t\t\tdd0 += lsd;\n\t\t\tds0 += lss;\n\n\t\t\tmemcpy(dd1, ds1, w);\n\t\t\tdd1 += lsd;\n\t\t\tds1 += lss;\n\t\t}\n\t\tbreak;\n\n\tcase VID_FMT_YUV422P:\n\t\tlsd  = dst->linesize[0];\n\t\tlss  = src->linesize[0];\n\t\tlsd1 = dst->linesize[1];\n\t\tlss1 = src->linesize[1];\n\t\tlsd2 = dst->linesize[2];\n\t\tlss2 = src->linesize[2];\n\n\t\tdd0 = dst->data[0];\n\t\tdd1 = dst->data[1];\n\t\tdd2 = dst->data[2];\n\n\t\tds0 = src->data[0];\n\t\tds1 = src->data[1];\n\t\tds2 = src->data[2];\n\n\t\tw  = dst->size.w & ~1;\n\t\th  = dst->size.h & ~1;\n\n\t\tfor (y=0; y<h; y+=1) {\n\n\t\t\tmemcpy(dd0, ds0, w);\n\t\t\tdd0 += lsd;\n\t\t\tds0 += lss;\n\n\t\t\tmemcpy(dd1, ds1, w/2);\n\t\t\tdd1 += lsd1;\n\t\t\tds1 += lss1;\n\n\t\t\tmemcpy(dd2, ds2, w/2);\n\t\t\tdd2 += lsd2;\n\t\t\tds2 += lss2;\n\t\t}\n\t\tbreak;\n\n\tcase VID_FMT_YUYV422:\n\t\tlsd = dst->linesize[0];\n\t\tlss = src->linesize[0];\n\n\t\tdd0 = dst->data[0];\n\n\t\tds0 = src->data[0];\n\n\t\tw  = dst->size.w & ~1;\n\t\th  = dst->size.h & ~1;\n\n\t\tfor (y=0; y<h; y+=2) {\n\n\t\t\tmemcpy(dd0, ds0, w*2);\n\t\t\tdd0 += lsd;\n\t\t\tds0 += lss;\n\n\t\t\tmemcpy(dd0, ds0, w*2);\n\t\t\tdd0 += lsd;\n\t\t\tds0 += lss;\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\t(void)re_printf(\"vidframe_copy(): unsupported format:\"\n\t\t\t\t\" %s\\n\", vidfmt_name(dst->fmt));\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "rem/vidconv/vconv.c",
    "content": "/**\n * @file vconv.c Video Conversion\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re.h>\n#include <rem_vid.h>\n#include <rem_dsp.h>\n#include <rem_vidconv.h>\n\n\n#if 0\n\n/*\n * The lookup tables are generated with the following code:\n */\n\n#define P 14\n\n#define COEF_RV ((int32_t) (1.370705f * (float)(1 << P)))\n#define COEF_GU ((int32_t) (-0.337633f * (float)(1 << P)))\n#define COEF_GV ((int32_t) (-0.698001f * (float)(1 << P)))\n#define COEF_BU ((int32_t) (1.732446f * (float)(1 << P)))\n\n#define ERV(a) (COEF_RV * ((a) - 128))\n#define EGU(a) (COEF_GU * ((a) - 128))\n#define EGV(a) (COEF_GV * ((a) - 128))\n#define EBU(a) (COEF_BU * ((a) - 128))\n\n\nint16_t CRV[256];\nint16_t CGU[256];\nint16_t CGV[256];\nint16_t CBU[256];\n\n\nstatic void init_table(void)\n{\n\tint i;\n\n\tfor (i = 0; i < 256; ++i) {\n\t\tCRV[i] = ERV(i) >> P;\n\t\tCGU[i] = EGU(i) >> P;\n\t\tCGV[i] = EGV(i) >> P;\n\t\tCBU[i] = EBU(i) >> P;\n\t}\n}\n#endif\n\n\nstatic const int16_t CRV[256] = {\n\t-176,-175,-173,-172,-170,-169,-168,-166,-165,-164,-162,-161,\n\t-159,-158,-157,-155,-154,-153,-151,-150,-149,-147,-146,-144,\n\t-143,-142,-140,-139,-138,-136,-135,-133,-132,-131,-129,-128,\n\t-127,-125,-124,-122,-121,-120,-118,-117,-116,-114,-113,-112,\n\t-110,-109,-107,-106,-105,-103,-102,-101, -99, -98, -96, -95,\n\t -94, -92, -91, -90, -88, -87, -85, -84, -83, -81, -80, -79,\n\t -77, -76, -75, -73, -72, -70, -69, -68, -66, -65, -64, -62,\n\t -61, -59, -58, -57, -55, -54, -53, -51, -50, -48, -47, -46,\n\t -44, -43, -42, -40, -39, -38, -36, -35, -33, -32, -31, -29,\n\t -28, -27, -25, -24, -22, -21, -20, -18, -17, -16, -14, -13,\n\t -11, -10,  -9,  -7,  -6,  -5,  -3,  -2,   0,   1,   2,   4,\n\t   5,   6,   8,   9,  10,  12,  13,  15,  16,  17,  19,  20,\n\t  21,  23,  24,  26,  27,  28,  30,  31,  32,  34,  35,  37,\n\t  38,  39,  41,  42,  43,  45,  46,  47,  49,  50,  52,  53,\n\t  54,  56,  57,  58,  60,  61,  63,  64,  65,  67,  68,  69,\n\t  71,  72,  74,  75,  76,  78,  79,  80,  82,  83,  84,  86,\n\t  87,  89,  90,  91,  93,  94,  95,  97,  98, 100, 101, 102,\n\t 104, 105, 106, 108, 109, 111, 112, 113, 115, 116, 117, 119,\n\t 120, 121, 123, 124, 126, 127, 128, 130, 131, 132, 134, 135,\n\t 137, 138, 139, 141, 142, 143, 145, 146, 148, 149, 150, 152,\n\t 153, 154, 156, 157, 158, 160, 161, 163, 164, 165, 167, 168,\n\t 169, 171, 172, 174};\n\nstatic const int16_t CGU[256] = {\n\t  43,  42,  42,  42,  41,  41,  41,  40,  40,  40,  39,  39,\n\t  39,  38,  38,  38,  37,  37,  37,  36,  36,  36,  35,  35,\n\t  35,  34,  34,  34,  33,  33,  33,  32,  32,  32,  31,  31,\n\t  31,  30,  30,  30,  29,  29,  29,  28,  28,  28,  27,  27,\n\t  27,  26,  26,  25,  25,  25,  24,  24,  24,  23,  23,  23,\n\t  22,  22,  22,  21,  21,  21,  20,  20,  20,  19,  19,  19,\n\t  18,  18,  18,  17,  17,  17,  16,  16,  16,  15,  15,  15,\n\t  14,  14,  14,  13,  13,  13,  12,  12,  12,  11,  11,  11,\n\t  10,  10,  10,   9,   9,   9,   8,   8,   8,   7,   7,   7,\n\t   6,   6,   6,   5,   5,   5,   4,   4,   4,   3,   3,   3,\n\t   2,   2,   2,   1,   1,   1,   0,   0,   0,  -1,  -1,  -2,\n\t  -2,  -2,  -3,  -3,  -3,  -4,  -4,  -4,  -5,  -5,  -5,  -6,\n\t  -6,  -6,  -7,  -7,  -7,  -8,  -8,  -8,  -9,  -9,  -9, -10,\n\t -10, -10, -11, -11, -11, -12, -12, -12, -13, -13, -13, -14,\n\t -14, -14, -15, -15, -15, -16, -16, -16, -17, -17, -17, -18,\n\t -18, -18, -19, -19, -19, -20, -20, -20, -21, -21, -21, -22,\n\t -22, -22, -23, -23, -23, -24, -24, -24, -25, -25, -25, -26,\n\t -26, -26, -27, -27, -28, -28, -28, -29, -29, -29, -30, -30,\n\t -30, -31, -31, -31, -32, -32, -32, -33, -33, -33, -34, -34,\n\t -34, -35, -35, -35, -36, -36, -36, -37, -37, -37, -38, -38,\n\t -38, -39, -39, -39, -40, -40, -40, -41, -41, -41, -42, -42,\n\t -42, -43, -43, -43};\n\nstatic const int16_t CGV[256] = {\n\t  89,  88,  87,  87,  86,  85,  85,  84,  83,  83,  82,  81,\n\t  80,  80,  79,  78,  78,  77,  76,  76,  75,  74,  73,  73,\n\t  72,  71,  71,  70,  69,  69,  68,  67,  67,  66,  65,  64,\n\t  64,  63,  62,  62,  61,  60,  60,  59,  58,  57,  57,  56,\n\t  55,  55,  54,  53,  53,  52,  51,  50,  50,  49,  48,  48,\n\t  47,  46,  46,  45,  44,  43,  43,  42,  41,  41,  40,  39,\n\t  39,  38,  37,  36,  36,  35,  34,  34,  33,  32,  32,  31,\n\t  30,  30,  29,  28,  27,  27,  26,  25,  25,  24,  23,  23,\n\t  22,  21,  20,  20,  19,  18,  18,  17,  16,  16,  15,  14,\n\t  13,  13,  12,  11,  11,  10,   9,   9,   8,   7,   6,   6,\n\t   5,   4,   4,   3,   2,   2,   1,   0,   0,  -1,  -2,  -3,\n\t  -3,  -4,  -5,  -5,  -6,  -7,  -7,  -8,  -9, -10, -10, -11,\n\t -12, -12, -13, -14, -14, -15, -16, -17, -17, -18, -19, -19,\n\t -20, -21, -21, -22, -23, -24, -24, -25, -26, -26, -27, -28,\n\t -28, -29, -30, -31, -31, -32, -33, -33, -34, -35, -35, -36,\n\t -37, -37, -38, -39, -40, -40, -41, -42, -42, -43, -44, -44,\n\t -45, -46, -47, -47, -48, -49, -49, -50, -51, -51, -52, -53,\n\t -54, -54, -55, -56, -56, -57, -58, -58, -59, -60, -61, -61,\n\t -62, -63, -63, -64, -65, -65, -66, -67, -68, -68, -69, -70,\n\t -70, -71, -72, -72, -73, -74, -74, -75, -76, -77, -77, -78,\n\t -79, -79, -80, -81, -81, -82, -83, -84, -84, -85, -86, -86,\n\t -87, -88, -88, -89};\n\nstatic const int16_t CBU[256] = {\n\t-222,-221,-219,-217,-215,-214,-212,-210,-208,-207,-205,-203,\n\t-201,-200,-198,-196,-195,-193,-191,-189,-188,-186,-184,-182,\n\t-181,-179,-177,-175,-174,-172,-170,-169,-167,-165,-163,-162,\n\t-160,-158,-156,-155,-153,-151,-149,-148,-146,-144,-143,-141,\n\t-139,-137,-136,-134,-132,-130,-129,-127,-125,-124,-122,-120,\n\t-118,-117,-115,-113,-111,-110,-108,-106,-104,-103,-101, -99,\n\t -98, -96, -94, -92, -91, -89, -87, -85, -84, -82, -80, -78,\n\t -77, -75, -73, -72, -70, -68, -66, -65, -63, -61, -59, -58,\n\t -56, -54, -52, -51, -49, -47, -46, -44, -42, -40, -39, -37,\n\t -35, -33, -32, -30, -28, -26, -25, -23, -21, -20, -18, -16,\n\t -14, -13, -11,  -9,  -7,  -6,  -4,  -2,   0,   1,   3,   5,\n\t   6,   8,  10,  12,  13,  15,  17,  19,  20,  22,  24,  25,\n\t  27,  29,  31,  32,  34,  36,  38,  39,  41,  43,  45,  46,\n\t  48,  50,  51,  53,  55,  57,  58,  60,  62,  64,  65,  67,\n\t  69,  71,  72,  74,  76,  77,  79,  81,  83,  84,  86,  88,\n\t  90,  91,  93,  95,  97,  98, 100, 102, 103, 105, 107, 109,\n\t 110, 112, 114, 116, 117, 119, 121, 123, 124, 126, 128, 129,\n\t 131, 133, 135, 136, 138, 140, 142, 143, 145, 147, 148, 150,\n\t 152, 154, 155, 157, 159, 161, 162, 164, 166, 168, 169, 171,\n\t 173, 174, 176, 178, 180, 181, 183, 185, 187, 188, 190, 192,\n\t 194, 195, 197, 199, 200, 202, 204, 206, 207, 209, 211, 213,\n\t 214, 216, 218, 220};\n\n\nstatic inline void yuv2rgb(uint8_t *rgb, uint8_t y, int ruv, int guv, int buv)\n{\n\t*rgb++ = saturate_u8(y + buv);\n\t*rgb++ = saturate_u8(y + guv);\n\t*rgb++ = saturate_u8(y + ruv);\n\t*rgb   = 0;\n}\n\n\nstatic inline void yuv2rgb565(uint8_t *rgb, uint8_t y,\n\t\t\t      int ruv, int guv, int buv)\n{\n\tint r = saturate_u8(y + ruv) >> 3;\n\tint g = saturate_u8(y + guv) >> 2;\n\tint b = saturate_u8(y + buv) >> 3;\n\n\trgb[1] = r << 3 | g >> 3;\n\trgb[0] = g << 5 | b;\n}\n\n\nstatic inline void _yuv2rgb(uint8_t *rgb, uint8_t y, uint8_t u, uint8_t v)\n{\n\tint ruv, guv, buv;\n\n\truv = CRV[v];\n\tguv = CGV[v] + CGU[u];\n\tbuv = CBU[u];\n\n\tyuv2rgb(rgb, y, ruv, guv, buv);\n}\n\n\ntypedef void(line_h)(unsigned xsoffs, unsigned xdoffs, unsigned width,\n\t\t     double rw, unsigned yd, unsigned ys, unsigned ys2,\n\t\t     uint8_t *dd0, uint8_t *dd1, uint8_t *dd2, unsigned lsd,\n\t\t     const uint8_t *sd0, const uint8_t *sd1,\n\t\t     const uint8_t *sd2, unsigned lss);\n\n\nstatic void yuv420p_to_yuv420p(unsigned xsoffs, unsigned xdoffs,\n\t\t\t       unsigned width, double rw, unsigned yd,\n\t\t\t       unsigned ys, unsigned ys2, uint8_t *dd0,\n\t\t\t       uint8_t *dd1, uint8_t *dd2, unsigned lsd,\n\t\t\t       const uint8_t *ds0, const uint8_t *ds1,\n\t\t\t       const uint8_t *ds2, unsigned lss)\n{\n\tunsigned x, xd, xs, xs2;\n\tunsigned id, is;\n\n\tfor (x=0; x<width; x+=2) {\n\n\t\txd  = x + xdoffs;\n\n\t\txs  = (unsigned)((x + xsoffs) * rw);\n\t\txs2 = (unsigned)((x + xsoffs + 1) * rw);\n\n\t\tid = xd + yd*lsd;\n\n\t\tdd0[id]         = ds0[xs  + ys*lss];\n\t\tdd0[id+1]       = ds0[xs2 + ys*lss];\n\t\tdd0[id + lsd]   = ds0[xs  + ys2*lss];\n\t\tdd0[id+1 + lsd] = ds0[xs2 + ys2*lss];\n\n\t\tid = xd/2    + yd*lsd/4;\n\t\tis = (xs>>1) + (ys>>1)*lss/2;\n\n\t\tdd1[id] = ds1[is];\n\t\tdd2[id] = ds2[is];\n\t}\n}\n\n\nstatic void yuyv422_to_yuv420p(unsigned xsoffs, unsigned xdoffs,\n\t\t\t       unsigned width, double rw, unsigned yd,\n\t\t\t       unsigned ys, unsigned ys2, uint8_t *dd0,\n\t\t\t       uint8_t *dd1, uint8_t *dd2, unsigned lsd,\n\t\t\t       const uint8_t *sd0, const uint8_t *sd1,\n\t\t\t       const uint8_t *sd2, unsigned lss)\n{\n\tunsigned x, xd, xs;\n\tunsigned id, is, is2;\n\n\t(void)sd1;\n\t(void)sd2;\n\n\tfor (x=0; x<width; x+=2) {\n\n\t\txd  = x + xdoffs;\n\n\t\txs  = ((unsigned)((x + xsoffs) * rw * 2)) & ~3;\n\n\t\tid  = xd + yd*lsd;\n\t\tis  = xs + ys*lss;\n\t\tis2 = xs + ys2*lss;\n\n\t\tdd0[id]         = sd0[is];\n\t\tdd0[id+1]       = sd0[is + 2];\n\t\tdd0[id + lsd]   = sd0[is2];\n\t\tdd0[id+1 + lsd] = sd0[is2 + 2];\n\n\t\tid = xd/2 + yd*lsd/4;\n\n\t\tdd1[id] = sd0[is + 1];\n\t\tdd2[id] = sd0[is + 3];\n\t}\n}\n\n\nstatic void uyvy422_to_yuv420p(unsigned xsoffs, unsigned xdoffs,\n\t\t\t       unsigned width, double rw, unsigned yd,\n\t\t\t       unsigned ys, unsigned ys2, uint8_t *dd0,\n\t\t\t       uint8_t *dd1, uint8_t *dd2, unsigned lsd,\n\t\t\t       const uint8_t *sd0, const uint8_t *sd1,\n\t\t\t       const uint8_t *sd2, unsigned lss)\n{\n\tunsigned x, xd, xs;\n\tunsigned id, is, is2;\n\n\t(void)sd1;\n\t(void)sd2;\n\n\tfor (x=0; x<width; x+=2) {\n\n\t\txd  = x + xdoffs;\n\n\t\txs = ((unsigned)((x + xsoffs) * rw * 2)) & ~3;\n\n\t\tid  = xd + yd*lsd;\n\t\tis  = xs + ys*lss;\n\t\tis2 = xs + ys2*lss;\n\n\t\tdd0[id]         = sd0[is + 1];\n\t\tdd0[id+1]       = sd0[is + 3];\n\t\tdd0[id + lsd]   = sd0[is2 + 1];\n\t\tdd0[id+1 + lsd] = sd0[is2 + 3];\n\n\t\tid = xd/2 + yd*lsd/4;\n\n\t\tdd1[id] = sd0[is + 0];\n\t\tdd2[id] = sd0[is + 2];\n\t}\n}\n\n\nstatic void rgb32_to_yuv420p(unsigned xsoffs, unsigned xdoffs, unsigned width,\n\t\t\t     double rw, unsigned yd, unsigned ys, unsigned ys2,\n\t\t\t     uint8_t *dd0, uint8_t *dd1, uint8_t *dd2,\n\t\t\t     unsigned lsd, const uint8_t *ds0,\n\t\t\t     const uint8_t *ds1, const uint8_t *ds2,\n\t\t\t     unsigned lss)\n{\n\tunsigned x, xd, xs, xs2;\n\tunsigned id;\n\n\t(void)ds1;\n\t(void)ds2;\n\n\tfor (x=0; x<width; x+=2) {\n\n\t\tuint32_t x0;\n\t\tuint32_t x1;\n\t\tuint32_t x2;\n\t\tuint32_t x3;\n\n\t\txd  = x + xdoffs;\n\n\t\txs  = 4 * ((unsigned)((x + xsoffs) * rw));\n\t\txs2 = 4 * ((unsigned)((x + xsoffs + 1) * rw));\n\n\t\tid = xd + yd*lsd;\n\n\t\tx0 = *(uint32_t *)(void *)&ds0[xs  + ys*lss];\n\t\tx1 = *(uint32_t *)(void *)&ds0[xs2 + ys*lss];\n\t\tx2 = *(uint32_t *)(void *)&ds0[xs  + ys2*lss];\n\t\tx3 = *(uint32_t *)(void *)&ds0[xs2 + ys2*lss];\n\n\t\tdd0[id]         = rgb2y(x0 >> 16, x0 >> 8, x0);\n\t\tdd0[id+1]       = rgb2y(x1 >> 16, x1 >> 8, x1);\n\t\tdd0[id + lsd]   = rgb2y(x2 >> 16, x2 >> 8, x2);\n\t\tdd0[id+1 + lsd] = rgb2y(x3 >> 16, x3 >> 8, x3);\n\n\t\tid = xd/2 + yd*lsd/4;\n\n\t\tdd1[id] = rgb2u(x0 >> 16, x0 >> 8, x0);\n\t\tdd2[id] = rgb2v(x0 >> 16, x0 >> 8, x0);\n\t}\n}\n\n\nstatic void rgb32_to_yuv444p(unsigned xsoffs, unsigned xdoffs, unsigned width,\n\t\t\t     double rw, unsigned yd, unsigned ys, unsigned ys2,\n\t\t\t     uint8_t *dd0, uint8_t *dd1, uint8_t *dd2,\n\t\t\t     unsigned lsd, const uint8_t *ds0,\n\t\t\t     const uint8_t *ds1, const uint8_t *ds2,\n\t\t\t     unsigned lss)\n{\n\tunsigned x, xd, xs;\n\tunsigned id;\n\n\t(void)ds1;\n\t(void)ds2;\n\n\tfor (x=0; x<width; x++) {\n\n\t\tuint32_t x0;\n\t\tuint32_t x1;\n\n\t\txd = x + xdoffs;\n\n\t\txs = 4 * ((unsigned)((x + xsoffs) * rw));\n\n\t\tid = xd + yd*lsd;\n\n\t\tx0 = *(uint32_t *)(void *)&ds0[xs + ys *lss];\n\t\tx1 = *(uint32_t *)(void *)&ds0[xs + ys2*lss];\n\n\t\tdd0[id]       = rgb2y(x0 >> 16, x0 >> 8, x0);\n\t\tdd0[id + lsd] = rgb2y(x1 >> 16, x1 >> 8, x1);\n\n\t\tdd1[id]       = rgb2u(x0 >> 16, x0 >> 8, x0);\n\t\tdd1[id + lsd] = rgb2u(x1 >> 16, x1 >> 8, x1);\n\n\t\tdd2[id]       = rgb2v(x0 >> 16, x0 >> 8, x0);\n\t\tdd2[id + lsd] = rgb2v(x1 >> 16, x1 >> 8, x1);\n\t}\n}\n\n\nstatic void yuv420p_to_rgb32(unsigned xsoffs, unsigned xdoffs, unsigned width,\n\t\t\t     double rw, unsigned yd, unsigned ys, unsigned ys2,\n\t\t\t     uint8_t *dd0, uint8_t *dd1, uint8_t *dd2,\n\t\t\t     unsigned lsd, const uint8_t *ds0,\n\t\t\t     const uint8_t *ds1, const uint8_t *ds2,\n\t\t\t     unsigned lss)\n{\n\tunsigned x, xd, xs, xs2;\n\tunsigned id, is;\n\n\t(void)dd1;\n\t(void)dd2;\n\n\tfor (x=0; x<width; x+=2) {\n\n\t\tint ruv, guv, buv;\n\t\tuint8_t u, v;\n\n\t\txd  = (x + xdoffs) * 4;\n\n\t\txs  = (unsigned)((x + xsoffs) * rw);\n\t\txs2 = (unsigned)((x + xsoffs + 1) * rw);\n\n\t\tid = (xd + yd*lsd);\n\t\tis  = (xs>>1) + (ys>>1)*lss/2;\n\n\t\tu = ds1[is];\n\t\tv = ds2[is];\n\n\t\truv = CRV[v];\n\t\tguv = CGV[v] + CGU[u];\n\t\tbuv = CBU[u];\n\n\t\tyuv2rgb(&dd0[id],         ds0[xs  + ys*lss],  ruv, guv, buv);\n\t\tyuv2rgb(&dd0[id+4],       ds0[xs2 + ys*lss],  ruv, guv, buv);\n\t\tyuv2rgb(&dd0[id   + lsd], ds0[xs  + ys2*lss], ruv, guv, buv);\n\t\tyuv2rgb(&dd0[id+4 + lsd], ds0[xs2 + ys2*lss], ruv, guv, buv);\n\t}\n}\n\n\nstatic void yuv420p_to_rgb565(unsigned xsoffs, unsigned xdoffs, unsigned width,\n\t\t\t      double rw, unsigned yd, unsigned ys,\n\t\t\t      unsigned ys2, uint8_t *dd0, uint8_t *dd1,\n\t\t\t      uint8_t *dd2, unsigned lsd, const uint8_t *ds0,\n\t\t\t      const uint8_t *ds1, const uint8_t *ds2,\n\t\t\t      unsigned lss)\n{\n\tunsigned x, xd, xs, xs2;\n\tunsigned id, is;\n\n\t(void)dd1;\n\t(void)dd2;\n\n\tfor (x=0; x<width; x+=2) {\n\n\t\tint ruv, guv, buv;\n\t\tuint8_t u, v;\n\n\t\txd  = (x + xdoffs) * 2;\n\n\t\txs  = (unsigned)((x + xsoffs) * rw);\n\t\txs2 = (unsigned)((x + xsoffs + 1) * rw);\n\n\t\tid = (xd + yd*lsd);\n\t\tis  = (xs>>1) + (ys>>1)*lss/2;\n\n\t\tu = ds1[is];\n\t\tv = ds2[is];\n\n\t\truv = CRV[v];\n\t\tguv = CGV[v] + CGU[u];\n\t\tbuv = CBU[u];\n\n\t\tyuv2rgb565(&dd0[id],         ds0[xs  + ys*lss],  ruv, guv,buv);\n\t\tyuv2rgb565(&dd0[id+2],       ds0[xs2 + ys*lss],  ruv, guv,buv);\n\t\tyuv2rgb565(&dd0[id   + lsd], ds0[xs  + ys2*lss], ruv, guv,buv);\n\t\tyuv2rgb565(&dd0[id+2 + lsd], ds0[xs2 + ys2*lss], ruv, guv,buv);\n\t}\n}\n\n\nstatic void nv12_to_yuv420p(unsigned xsoffs, unsigned xdoffs, unsigned width,\n\t\t\t    double rw, unsigned yd, unsigned ys, unsigned ys2,\n\t\t\t    uint8_t *dd0, uint8_t *dd1, uint8_t *dd2,\n\t\t\t    unsigned lsd, const uint8_t *ds0,\n\t\t\t    const uint8_t *ds1, const uint8_t *ds2,\n\t\t\t    unsigned lss)\n{\n\tunsigned x, xd, xs, xs2;\n\tunsigned id, is;\n\n\t(void)ds2;\n\n\tfor (x=0; x<width; x+=2) {\n\n\t\txd  = x + xdoffs;\n\n\t\txs  = (unsigned)((x + xsoffs) * rw);\n\t\txs2 = (unsigned)((x + xsoffs + 1) * rw);\n\n\t\tid = xd + yd*lsd;\n\n\t\tdd0[id]         = ds0[xs  + ys*lss];\n\t\tdd0[id+1]       = ds0[xs2 + ys*lss];\n\t\tdd0[id + lsd]   = ds0[xs  + ys2*lss];\n\t\tdd0[id+1 + lsd] = ds0[xs2 + ys2*lss];\n\n\t\tid = (xd>>1) + (yd>>1)*lsd/2;\n\t\tis = xs/2    + ys*lss/4;\n\n\t\tdd1[id] = ds1[2*is];\n\t\tdd2[id] = ds1[2*is+1];\n\t}\n}\n\n\nstatic void yuv420p_to_nv12(unsigned xsoffs, unsigned xdoffs, unsigned width,\n\t\t\t    double rw, unsigned yd, unsigned ys, unsigned ys2,\n\t\t\t    uint8_t *dd0, uint8_t *dd1, uint8_t *dd2,\n\t\t\t    unsigned lsd, const uint8_t *ds0,\n\t\t\t    const uint8_t *ds1, const uint8_t *ds2,\n\t\t\t    unsigned lss)\n{\n\tunsigned x, xd, xs, xs2;\n\tunsigned id, is;\n\n\t(void)dd2;\n\n\tfor (x=0; x<width; x+=2) {\n\n\t\txd  = x + xdoffs;\n\n\t\txs  = (unsigned)((x + xsoffs) * rw);\n\t\txs2 = (unsigned)((x + xsoffs + 1) * rw);\n\n\t\tid = xd + yd*lsd;\n\n\t\tdd0[id]         = ds0[xs  + ys*lss];\n\t\tdd0[id+1]       = ds0[xs2 + ys*lss];\n\t\tdd0[id + lsd]   = ds0[xs  + ys2*lss];\n\t\tdd0[id+1 + lsd] = ds0[xs2 + ys2*lss];\n\n\t\tid = xd/2    + yd*lsd/4;\n\t\tis = (xs>>1) + (ys>>1)*lss/2;\n\n\t\tdd1[2*id]   = ds1[is];\n\t\tdd1[2*id+1] = ds2[is];\n\t}\n}\n\n\nstatic void nv21_to_yuv420p(unsigned xsoffs, unsigned xdoffs, unsigned width,\n\t\t\t    double rw, unsigned yd, unsigned ys, unsigned ys2,\n\t\t\t    uint8_t *dd0, uint8_t *dd1, uint8_t *dd2,\n\t\t\t    unsigned lsd, const uint8_t *ds0,\n\t\t\t    const uint8_t *ds1, const uint8_t *ds2,\n\t\t\t    unsigned lss)\n{\n\tunsigned x, xd, xs, xs2;\n\tunsigned id, is;\n\n\t(void)ds2;\n\n\tfor (x=0; x<width; x+=2) {\n\n\t\txd  = x + xdoffs;\n\n\t\txs  = (unsigned)((x + xsoffs) * rw);\n\t\txs2 = (unsigned)((x + xsoffs + 1) * rw);\n\n\t\tid = xd + yd*lsd;\n\n\t\tdd0[id]         = ds0[xs  + ys*lss];\n\t\tdd0[id+1]       = ds0[xs2 + ys*lss];\n\t\tdd0[id + lsd]   = ds0[xs  + ys2*lss];\n\t\tdd0[id+1 + lsd] = ds0[xs2 + ys2*lss];\n\n\t\tid = xd/2    + yd*lsd/4;\n\t\tis = ((xs>>1) + (ys>>1)*lss/2) & ~1;\n\n\t\tdd2[id] = ds1[2*is];\n\t\tdd1[id] = ds1[2*is+1];\n\t}\n}\n\n\nstatic void yuv444p_to_rgb32(unsigned xsoffs, unsigned xdoffs, unsigned width,\n\t\t\t     double rw, unsigned yd, unsigned ys, unsigned ys2,\n\t\t\t     uint8_t *dd0, uint8_t *dd1, uint8_t *dd2,\n\t\t\t     unsigned lsd, const uint8_t *ds0,\n\t\t\t     const uint8_t *ds1, const uint8_t *ds2,\n\t\t\t     unsigned lss)\n{\n\tunsigned x, xd, xs;\n\tunsigned id;\n\tunsigned is1, is2;\n\n\t(void)dd1;\n\t(void)dd2;\n\n\tfor (x=0; x<width; x++) {\n\n\t\txd = (x + xdoffs) * 4;\n\n\t\txs = (unsigned)((x + xsoffs) * rw);\n\n\t\tid = xd + yd*lsd;\n\n\t\tis1 = xs + ys *lss;\n\t\tis2 = xs + ys2*lss;\n\n\t\t_yuv2rgb(&dd0[id],       ds0[is1], ds1[is1], ds2[is1]);\n\t\t_yuv2rgb(&dd0[id + lsd], ds0[is2], ds1[is2], ds2[is2]);\n\t}\n}\n\n\nstatic void nv12_to_rgb32(unsigned xsoffs, unsigned xdoffs, unsigned width,\n\t\t\t  double rw, unsigned yd, unsigned ys, unsigned ys2,\n\t\t\t  uint8_t *dd0, uint8_t *dd1, uint8_t *dd2,\n\t\t\t  unsigned lsd, const uint8_t *ds0, const uint8_t *ds1,\n\t\t\t  const uint8_t *ds2, unsigned lss)\n{\n       unsigned x, xd, xs, xs2;\n       unsigned id, is;\n\n       (void)ds2;\n       (void)dd1;\n       (void)dd2;\n\n       for (x=0; x<width; x+=2) {\n               int ruv, guv, buv;\n               uint8_t u, v;\n\n               xd  = (x + xdoffs) * 4;\n\n\t       xs  = (unsigned)((x + xsoffs) * rw);\n\t       xs2 = (unsigned)((x + xsoffs + 1) * rw);\n\n\t       id = (xd + yd*lsd);\n               is = xs/2 + ys*lss/4;\n\n               u = ds1[2*is];\n               v = ds1[2*is+1];\n               ruv = CRV[v];\n               guv = CGV[v] + CGU[u];\n               buv = CBU[u];\n\n               yuv2rgb(&dd0[id],         ds0[xs  + ys*lss],  ruv, guv, buv);\n               yuv2rgb(&dd0[id+4],       ds0[xs2 + ys*lss],  ruv, guv, buv);\n               yuv2rgb(&dd0[id   + lsd], ds0[xs  + ys2*lss], ruv, guv, buv);\n               yuv2rgb(&dd0[id+4 + lsd], ds0[xs2 + ys2*lss], ruv, guv, buv);\n       }\n}\n\n\nstatic void nv21_to_rgb32(unsigned xsoffs, unsigned xdoffs, unsigned width,\n\t\t\t  double rw, unsigned yd, unsigned ys, unsigned ys2,\n\t\t\t  uint8_t *dd0, uint8_t *dd1, uint8_t *dd2,\n\t\t\t  unsigned lsd, const uint8_t *ds0, const uint8_t *ds1,\n\t\t\t  const uint8_t *ds2, unsigned lss)\n{\n\tunsigned x, xd, xs, xs2;\n\tunsigned id, is;\n\n\t(void)ds2;\n\t(void)dd1;\n\t(void)dd2;\n\n\tfor (x = 0; x < width; x += 2) {\n\t\tint ruv, guv, buv;\n\t\tuint8_t u, v;\n\n\t\txd = (x + xdoffs) * 4;\n\n\t\txs  = (unsigned)((x + xsoffs) * rw);\n\t\txs2 = (unsigned)((x + xsoffs + 1) * rw);\n\n\t\tid = (xd + yd * lsd);\n\t\tis = xs / 2 + ys * lss / 4;\n\n\t\tv   = ds1[2 * is];\n\t\tu   = ds1[2 * is + 1];\n\t\truv = CRV[v];\n\t\tguv = CGV[v] + CGU[u];\n\t\tbuv = CBU[u];\n\n\t\tyuv2rgb(&dd0[id], ds0[xs + ys * lss], ruv, guv, buv);\n\t\tyuv2rgb(&dd0[id + 4], ds0[xs2 + ys * lss], ruv, guv, buv);\n\t\tyuv2rgb(&dd0[id + lsd], ds0[xs + ys2 * lss], ruv, guv, buv);\n\t\tyuv2rgb(&dd0[id + 4 + lsd], ds0[xs2 + ys2 * lss], ruv, guv,\n\t\t\tbuv);\n\t}\n}\n\n\n#define MAX_SRC 10\n#define MAX_DST 10\n\n\n/**\n * Pixel conversion table:  [src][dst]\n *\n * @note Index must be aligned to values in enum vidfmt\n */\nstatic line_h *conv_table[MAX_SRC][MAX_DST] = {\n\n/*\n * Dst:  YUV420P              YUYV422   UYVY422   RGB32\n */\n\t{yuv420p_to_yuv420p,  NULL,     NULL,     yuv420p_to_rgb32, NULL,\n\t yuv420p_to_rgb565, yuv420p_to_nv12},\n\t{yuyv422_to_yuv420p,  NULL,     NULL,     NULL, NULL, NULL, NULL},\n\t{uyvy422_to_yuv420p,  NULL,     NULL,     NULL, NULL, NULL, NULL},\n\t{rgb32_to_yuv420p,    NULL,     NULL,     NULL, NULL, NULL, NULL,\n\t NULL, rgb32_to_yuv444p},\n\t{rgb32_to_yuv420p,    NULL,     NULL,     NULL, NULL, NULL, NULL},\n\t{NULL,                NULL,     NULL,     NULL, NULL, NULL, NULL},\n\t{nv12_to_yuv420p,     NULL,     NULL,     nv12_to_rgb32,\n\t NULL, NULL, NULL},\n\t{nv21_to_yuv420p,     NULL,     NULL,     nv21_to_rgb32,\n\t NULL, NULL, NULL},\n\t{NULL,                NULL,     NULL,     yuv444p_to_rgb32}\n};\n\n\n/**\n * Convert a video frame from one pixel format to another pixel format\n *\n * Speed matches swscale: SWS_BILINEAR\n *\n * todo: optimize (check out SWS_FAST_BILINEAR)\n *\n * @param dst  Destination video frame\n * @param src  Source video frame\n * @param r    Drawing area in destination frame, NULL means whole frame\n */\nvoid vidconv(struct vidframe *dst, const struct vidframe *src,\n\t     struct vidrect *r)\n{\n\tstruct vidrect rdst;\n\tunsigned yd, ys, ys2, lsd, lss, y;\n\tconst uint8_t *ds0, *ds1, *ds2;\n\tuint8_t *dd0, *dd1, *dd2;\n\tdouble rw, rh;\n\tline_h *lineh = NULL;\n\n\tif (!vidframe_isvalid(dst) || !vidframe_isvalid(src))\n\t\treturn;\n\n\tif (src->fmt < MAX_SRC && dst->fmt < MAX_DST) {\n\n\t\t/* Lookup conversion function */\n\t\tlineh = conv_table[src->fmt][dst->fmt];\n\t}\n\tif (!lineh) {\n\t\t(void)re_printf(\"vidconv: no pixel converter found for\"\n\t\t\t\t\" %s -> %s\\n\", vidfmt_name(src->fmt),\n\t\t\t\tvidfmt_name(dst->fmt));\n\t\treturn;\n\t}\n\n\tif (r) {\n\t\tr->x &= ~1;\n\t\tr->y &= ~1;\n\t\tr->w &= ~1;\n\t\tr->h &= ~1;\n\n\t\tif ((r->x + r->w) > dst->size.w ||\n\t\t    (r->y + r->h) > dst->size.h) {\n\t\t\t(void)re_printf(\"vidconv: out of bounds (%u x %u)\\n\",\n\t\t\t\t\tdst->size.w, dst->size.h);\n\t\t\treturn;\n\t\t}\n\t}\n\telse {\n\t\trdst.x = rdst.y = 0;\n\t\trdst.w = dst->size.w & ~1;\n\t\trdst.h = dst->size.h & ~1;\n\t\tr = &rdst;\n\t}\n\n\trw = (double)src->size.w / (double)r->w;\n\trh = (double)src->size.h / (double)r->h;\n\n\tlsd = dst->linesize[0];\n\tlss = src->linesize[0];\n\n\tdd0 = dst->data[0];\n\tdd1 = dst->data[1];\n\tdd2 = dst->data[2];\n\n\tds0 = src->data[0];\n\tds1 = src->data[1];\n\tds2 = src->data[2];\n\n\tfor (y=0; y<r->h; y+=2) {\n\n\t\tyd  = y + r->y;\n\n\t\tys  = (unsigned)((y + src->yoffs) * rh);\n\t\tys2 = (unsigned)((y + src->yoffs + 1) * rh);\n\n\t\tlineh(src->xoffs, r->x, r->w, rw, yd, ys, ys2,\n\t\t      dd0, dd1, dd2, lsd,\n\t\t      ds0, ds1, ds2, lss);\n\t}\n}\n\n\n/**\n * Same as vidconv(), but maintain source aspect ratio within bounds of r\n *\n * @param dst  Destination video frame\n * @param src  Source video frame\n * @param r    Drawing area in destination frame\n */\nvoid vidconv_aspect(struct vidframe *dst, const struct vidframe *src,\n\t\t    struct vidrect *r)\n{\n\tstruct vidsz asz;\n\tdouble ar;\n\n\tar = (double)src->size.w / (double)src->size.h;\n\n\tasz.w = r->w;\n\tasz.h = r->h;\n\n\tr->w = (unsigned)min((double)asz.w, (double)asz.h * ar);\n\tr->h = (unsigned)min((double)asz.h, (double)asz.w / ar);\n\tr->x = r->x + (asz.w - r->w) / 2;\n\tr->y = r->y + (asz.h - r->h) / 2;\n\n\tvidconv(dst, src, r);\n}\n\n\n/**\n * Same as vidconv(), but maintain source min. center within bounds of r\n *\n * @param dst  Destination video frame\n * @param src  Source video frame\n * @param r    Drawing area in destination frame\n */\nvoid vidconv_center(struct vidframe *dst, const struct vidframe *src,\n\t\t    struct vidrect *r)\n{\n\tstruct vidframe sc = *src;\n\n\tif (src->size.w >= src->size.h) {\n\t\tdouble rh = (double)src->size.h / r->h;\n\t\tsc.size.w =\n\t\t\t(unsigned)min((double)src->size.w, (double)r->w * rh);\n\t\tsc.xoffs = ((unsigned)(src->size.w / rh) - r->w) / 2;\n\t\tif (sc.xoffs >= src->size.w)\n\t\t\tsc.xoffs = 0;\n\t}\n\telse {\n\t\tdouble rw = (double)src->size.w / r->w;\n\t\tsc.size.h =\n\t\t\t(unsigned)min((double)src->size.h, (double)r->h * rw);\n\t\tsc.yoffs = ((unsigned)(src->size.h / rw) - r->h) / 2;\n\t\tif (sc.yoffs >= src->size.h)\n\t\t\tsc.yoffs = 0;\n\t}\n\n\tvidconv(dst, &sc, r);\n}\n"
  },
  {
    "path": "rem/vidmix/vidmix.c",
    "content": "/**\n * @file vidmix.c Video Mixer\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#define _BSD_SOURCE 1\n#define _DEFAULT_SOURCE 1\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#define __USE_UNIX98 1\n#include <string.h>\n#include <re.h>\n#include <rem_vid.h>\n#include <rem_vidconv.h>\n#include <rem_vidmix.h>\n\n/*\n * Clock-rate for video timestamp\n */\n#define VIDEO_TIMEBASE 1000000U\n\n\nstruct vidmix {\n\tmtx_t rwlock;\n\tstruct list srcl;\n\tbool initialized;\n\tuint32_t next_pidx;\n\tenum vidfmt fmt;\n};\n\nstruct vidmix_source {\n\tstruct le le;\n\tuint32_t pidx;\n\tthrd_t thread;\n\tmtx_t *mutex;\n\tstruct vidframe *frame_tx;\n\tstruct vidframe *frame_rx;\n\tstruct vidmix *mix;\n\tvidmix_frame_h *fh;\n\tvoid *arg;\n\tvoid *focus;\n\tbool content_hide;\n\tbool focus_full;\n\tunsigned fint;\n\tbool selfview;\n\tbool content;\n\tbool run;\n};\n\n\nstatic inline void source_mix_full(struct vidframe *mframe,\n\t\t\t\t   const struct vidframe *frame_src);\n\n\nstatic inline void clear_frame(struct vidframe *vf)\n{\n\tvidframe_fill(vf, 0, 0, 0);\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct vidmix *mix = arg;\n\n\tif (mix->initialized)\n\t\tmtx_destroy(&mix->rwlock);\n}\n\n\nstatic void source_destructor(void *arg)\n{\n\tstruct vidmix_source *src = arg;\n\n\tvidmix_source_stop(src);\n\n\tif (src->le.list) {\n\t\tmtx_lock(&src->mix->rwlock);\n\t\tlist_unlink(&src->le);\n\t\tmtx_unlock(&src->mix->rwlock);\n\t}\n\n\tmem_deref(src->frame_tx);\n\tmem_deref(src->frame_rx);\n\tmem_deref(src->mix);\n\tmem_deref(src->mutex);\n}\n\n\nstatic inline void source_mix(struct vidframe *mframe,\n\t\t\t      const struct vidframe *frame_src,\n\t\t\t      unsigned n, unsigned rows, unsigned idx,\n\t\t\t      bool focus, bool focus_this, bool focus_full)\n{\n\tstruct vidrect rect;\n\n\tif (!frame_src)\n\t\treturn;\n\n\tif (focus) {\n\n\t\tconst unsigned nmin = focus_full ? 12 : 6;\n\n\t\tn = max((n+1), nmin)/2;\n\n\t\tif (focus_this) {\n\t\t\trect.w = mframe->size.w * (n-1) / n;\n\t\t\trect.h = mframe->size.h * (n-1) / n;\n\t\t\trect.x = 0;\n\t\t\trect.y = 0;\n\t\t}\n\t\telse {\n\t\t\trect.w = mframe->size.w / n;\n\t\t\trect.h = mframe->size.h / n;\n\n\t\t\tif (idx < n) {\n\t\t\t\trect.x = mframe->size.w - rect.w;\n\t\t\t\trect.y = rect.h * idx;\n\t\t\t}\n\t\t\telse if (idx < (n*2 - 1)) {\n\t\t\t\trect.x = rect.w * (n*2 - 2 - idx);\n\t\t\t\trect.y = mframe->size.h - rect.h;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse if (rows == 1) {\n\n\t\tsource_mix_full(mframe, frame_src);\n\t\treturn;\n\t}\n\telse if (n <= 3) {\n\t\trect.w = mframe->size.w / n;\n\t\trect.h = mframe->size.h;\n\t\trect.x = (rect.w) * (idx % n);\n\t\trect.y = 0;\n\t\tvidconv_center(mframe, frame_src, &rect);\n\t\treturn;\n\t}\n\telse {\n\t\trect.w = mframe->size.w / rows;\n\t\trect.h = mframe->size.h / rows;\n\t\trect.x = rect.w * (idx % rows);\n\t\trect.y = rect.h * (idx / rows);\n\t}\n\n\tvidconv_aspect(mframe, frame_src, &rect);\n}\n\n\nstatic inline void source_mix_full(struct vidframe *mframe,\n\t\t\t\t   const struct vidframe *frame_src)\n{\n\tif (!frame_src)\n\t\treturn;\n\n\tif (vidsz_cmp(&mframe->size, &frame_src->size)) {\n\n\t\tvidframe_copy(mframe, frame_src);\n\t}\n\telse {\n\t\tstruct vidrect rect;\n\n\t\trect.w = mframe->size.w;\n\t\trect.h = mframe->size.h;\n\t\trect.x = 0;\n\t\trect.y = 0;\n\n\t\tvidconv_aspect(mframe, frame_src, &rect);\n\t}\n}\n\n\nstatic inline unsigned calc_rows(unsigned n)\n{\n\tunsigned rows;\n\n\tfor (rows=1;; rows++)\n\t\tif (n <= (rows * rows))\n\t\t\treturn rows;\n}\n\n\nstatic int vidmix_thread(void *arg)\n{\n\tstruct vidmix_source *src = arg;\n\tstruct vidmix *mix = src->mix;\n\tuint64_t ts = tmr_jiffies_usec();\n\n\tmtx_lock(src->mutex);\n\n\twhile (src->run) {\n\n\t\tunsigned n, rows, idx;\n\t\tstruct le *le;\n\t\tuint64_t now;\n\n\t\tmtx_unlock(src->mutex);\n\t\tsys_usleep(4000);\n\t\tmtx_lock(src->mutex);\n\n\t\tnow = tmr_jiffies_usec();\n\n\t\tif (ts > now)\n\t\t\tcontinue;\n\n\t\tif (!src->frame_tx) {\n\t\t\tts += src->fint;\n\t\t\tcontinue;\n\t\t}\n\n\t\tmtx_lock(&mix->rwlock);\n\n\t\tclear_frame(src->frame_tx);\n\n\t\tfor (le=mix->srcl.head, n=0; le; le=le->next) {\n\n\t\t\tconst struct vidmix_source *lsrc = le->data;\n\n\t\t\tif (lsrc == src && !src->selfview)\n\t\t\t\tcontinue;\n\n\t\t\tif (lsrc->content && src->content_hide)\n\t\t\t\tcontinue;\n\n\t\t\tif (lsrc == src->focus && src->focus_full)\n\t\t\t\tsource_mix_full(src->frame_tx, lsrc->frame_rx);\n\n\t\t\t++n;\n\t\t}\n\n\t\trows = calc_rows(n);\n\n\t\tfor (le=mix->srcl.head, idx=0; le; le=le->next) {\n\n\t\t\tconst struct vidmix_source *lsrc = le->data;\n\n\t\t\tif (lsrc == src && !src->selfview)\n\t\t\t\tcontinue;\n\n\t\t\tif (lsrc->content && src->content_hide)\n\t\t\t\tcontinue;\n\n\t\t\tif (lsrc == src->focus && src->focus_full)\n\t\t\t\tcontinue;\n\n\t\t\tsource_mix(src->frame_tx, lsrc->frame_rx, n, rows, idx,\n\t\t\t\t   src->focus != NULL, src->focus == lsrc,\n\t\t\t\t   src->focus_full);\n\n\t\t\tif (src->focus != lsrc)\n\t\t\t\t++idx;\n\t\t}\n\n\t\tmtx_unlock(&mix->rwlock);\n\n\t\tsrc->fh(ts, src->frame_tx, src->arg);\n\n\t\tts += src->fint;\n\t}\n\n\tmtx_unlock(src->mutex);\n\n\treturn 0;\n}\n\n\nstatic int content_thread(void *arg)\n{\n\tstruct vidmix_source *src = arg;\n\tstruct vidmix *mix = src->mix;\n\tuint64_t ts = tmr_jiffies_usec();\n\n\tmtx_lock(src->mutex);\n\n\twhile (src->run) {\n\n\t\tstruct le *le;\n\t\tuint64_t now;\n\n\t\tmtx_unlock(src->mutex);\n\t\tsys_usleep(4000);\n\t\tmtx_lock(src->mutex);\n\n\t\tnow = tmr_jiffies_usec();\n\n\t\tif (ts > now)\n\t\t\tcontinue;\n\n\t\tmtx_lock(&mix->rwlock);\n\n\t\tfor (le=mix->srcl.head; le; le=le->next) {\n\n\t\t\tconst struct vidmix_source *lsrc = le->data;\n\n\t\t\tif (!lsrc->content || !lsrc->frame_rx || lsrc == src)\n\t\t\t\tcontinue;\n\n\t\t\tsrc->fh(ts, lsrc->frame_rx, src->arg);\n\t\t\tbreak;\n\t\t}\n\n\t\tmtx_unlock(&mix->rwlock);\n\n\t\tts += src->fint;\n\t}\n\n\tmtx_unlock(src->mutex);\n\n\treturn 0;\n}\n\n\n/**\n * Allocate a new Video mixer\n *\n * @param mixp Pointer to allocated video mixer\n *\n * @return 0 for success, otherwise error code\n */\nint vidmix_alloc(struct vidmix **mixp)\n{\n\tstruct vidmix *mix;\n\tint err;\n\n\tif (!mixp)\n\t\treturn EINVAL;\n\n\tmix = mem_zalloc(sizeof(*mix), destructor);\n\tif (!mix)\n\t\treturn ENOMEM;\n\n\terr = mtx_init(&mix->rwlock, mtx_plain) != thrd_success;\n\tif (err) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tmix->fmt\t = VID_FMT_YUV420P;\n\tmix->initialized = true;\n\n out:\n\tif (err)\n\t\tmem_deref(mix);\n\telse\n\t\t*mixp = mix;\n\n\treturn err;\n}\n\n\n/**\n * Set video mixer pixel format\n *\n * @param mix Video mixer\n * @param fmt Pixel format\n */\nvoid vidmix_set_fmt(struct vidmix *mix, enum vidfmt fmt)\n{\n\tif (!mix)\n\t\treturn;\n\n\tmix->fmt = fmt;\n}\n\n\n/**\n * Allocate a video mixer source\n *\n * @param srcp    Pointer to allocated video source\n * @param mix     Video mixer\n * @param sz      Size of output video frame (optional)\n * @param fps     Output frame rate (frames per second)\n * @param content True if source is of type content\n * @param fh      Mixer frame handler\n * @param arg     Handler argument\n *\n * @return 0 for success, otherwise error code\n */\nint vidmix_source_alloc(struct vidmix_source **srcp, struct vidmix *mix,\n\t\t\tconst struct vidsz *sz, unsigned fps, bool content,\n\t\t\tvidmix_frame_h *fh, void *arg)\n{\n\tstruct vidmix_source *src;\n\tint err;\n\n\tif (!srcp || !mix || !fps || !fh)\n\t\treturn EINVAL;\n\n\tsrc = mem_zalloc(sizeof(*src), source_destructor);\n\tif (!src)\n\t\treturn ENOMEM;\n\n\tsrc->mix     = mem_ref(mix);\n\tsrc->fint    = VIDEO_TIMEBASE/fps;\n\tsrc->content = content;\n\tsrc->fh      = fh;\n\tsrc->arg     = arg;\n\tsrc->pidx    = ++mix->next_pidx;\n\n\terr = mutex_alloc(&src->mutex);\n\tif (err)\n\t\tgoto out;\n\n\tif (sz) {\n\t\terr = vidframe_alloc(&src->frame_tx, mix->fmt, sz);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tclear_frame(src->frame_tx);\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(src);\n\telse\n\t\t*srcp = src;\n\n\treturn err;\n}\n\n\n/**\n * Check if vidmix source is enabled\n *\n * @param src Video mixer source\n *\n * @return true if enabled, otherwise false\n */\nbool vidmix_source_isenabled(const struct vidmix_source *src)\n{\n\treturn src ? (src->le.list != NULL) : false;\n}\n\n\n/**\n * Check if vidmix source is running\n *\n * @param src Video mixer source\n *\n * @return true if running, otherwise false\n */\nbool vidmix_source_isrunning(const struct vidmix_source *src)\n{\n\tif (!src)\n\t\treturn false;\n\n\tmtx_lock(src->mutex);\n\tbool run = src->run;\n\tmtx_unlock(src->mutex);\n\n\treturn run;\n}\n\n\n/**\n * Get vidmix source position index\n *\n * @param src Video mixer source\n *\n * @return position index\n */\nuint32_t vidmix_source_get_pidx(const struct vidmix_source *src)\n{\n\treturn src ? src->pidx : 0;\n}\n\n\n/**\n * Get focus source\n *\n * @param src Video mixer source\n *\n * @return pointer of focused source or NULL if focus is not set\n */\nvoid *vidmix_source_get_focus(const struct vidmix_source *src)\n{\n\treturn src ? src->focus : NULL;\n}\n\n\nstatic bool sort_src_handler(struct le *le1, struct le *le2, void *arg)\n{\n\tstruct vidmix_source *src1 = le1->data;\n\tstruct vidmix_source *src2 = le2->data;\n\t(void)arg;\n\n\t/* NOTE: important to use less than OR equal to, otherwise\n\t   the list_sort function may be stuck in a loop */\n\treturn src1->pidx <= src2->pidx;\n}\n\n\n/**\n * Enable/disable vidmix source\n *\n * @param src    Video mixer source\n * @param enable True to enable, false to disable\n */\nvoid vidmix_source_enable(struct vidmix_source *src, bool enable)\n{\n\tif (!src)\n\t\treturn;\n\n\tif (src->le.list && enable)\n\t\treturn;\n\n\tif (!src->le.list && !enable)\n\t\treturn;\n\n\tmtx_lock(&src->mix->rwlock);\n\n\tif (enable) {\n\t\tif (src->frame_rx)\n\t\t\tclear_frame(src->frame_rx);\n\n\t\tlist_insert_sorted(&src->mix->srcl, sort_src_handler, NULL,\n\t\t\t\t   &src->le, src);\n\t}\n\telse {\n\t\tlist_unlink(&src->le);\n\t}\n\n\tmtx_unlock(&src->mix->rwlock);\n}\n\n\n/**\n * Start vidmix source thread\n *\n * @param src    Video mixer source\n *\n * @return 0 for success, otherwise error code\n */\nint vidmix_source_start(struct vidmix_source *src)\n{\n\tint err;\n\n\tif (!src)\n\t\treturn EINVAL;\n\n\tmtx_lock(src->mutex);\n\tbool run = src->run;\n\tmtx_unlock(src->mutex);\n\n\tif (run)\n\t\treturn EALREADY;\n\n\tmtx_lock(src->mutex);\n\tsrc->run = true;\n\tmtx_unlock(src->mutex);\n\n\terr = thread_create_name(&src->thread, \"vidmix\",\n\t\t\t\t src->content ? content_thread : vidmix_thread,\n\t\t\t\t src);\n\tif (err) {\n\t\tmtx_lock(src->mutex);\n\t\tsrc->run = false;\n\t\tmtx_unlock(src->mutex);\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Stop vidmix source thread\n *\n * @param src    Video mixer source\n */\nvoid vidmix_source_stop(struct vidmix_source *src)\n{\n\tif (!src)\n\t\treturn;\n\n\tmtx_lock(src->mutex);\n\tbool run = src->run;\n\tmtx_unlock(src->mutex);\n\n\tif (run) {\n\t\tmtx_lock(src->mutex);\n\t\tsrc->run = false;\n\t\tmtx_unlock(src->mutex);\n\t\tthrd_join(src->thread, NULL);\n\t}\n}\n\n\n/**\n * Set video mixer output frame size\n *\n * @param src  Video mixer source\n * @param sz   Size of output video frame\n *\n * @return 0 for success, otherwise error code\n */\nint vidmix_source_set_size(struct vidmix_source *src, const struct vidsz *sz)\n{\n\tstruct vidframe *frame;\n\tint err;\n\n\tif (!src || !sz)\n\t\treturn EINVAL;\n\n\tif (src->frame_tx && vidsz_cmp(&src->frame_tx->size, sz))\n\t\treturn 0;\n\n\terr = vidframe_alloc(&frame, src->mix->fmt, sz);\n\tif (err)\n\t\treturn err;\n\n\tclear_frame(frame);\n\n\tmtx_lock(src->mutex);\n\tmem_deref(src->frame_tx);\n\tsrc->frame_tx = frame;\n\tmtx_unlock(src->mutex);\n\n\treturn 0;\n}\n\n\n/**\n * Set video mixer output frame rate\n *\n * @param src  Video mixer source\n * @param fps  Output frame rate (frames per second)\n */\nvoid vidmix_source_set_rate(struct vidmix_source *src, unsigned fps)\n{\n\tif (!src || !fps)\n\t\treturn;\n\n\tmtx_lock(src->mutex);\n\tsrc->fint = VIDEO_TIMEBASE/fps;\n\tmtx_unlock(src->mutex);\n}\n\n\n/**\n * Set video mixer content hide\n *\n * @param src    Video mixer source\n * @param hide   True to hide content, false to show\n */\nvoid vidmix_source_set_content_hide(struct vidmix_source *src, bool hide)\n{\n\tif (!src)\n\t\treturn;\n\n\tmtx_lock(src->mutex);\n\tsrc->content_hide = hide;\n\tmtx_unlock(src->mutex);\n}\n\n\n/**\n * Toggle vidmix source selfview\n *\n * @param src    Video mixer source\n */\nvoid vidmix_source_toggle_selfview(struct vidmix_source *src)\n{\n\tif (!src)\n\t\treturn;\n\n\tmtx_lock(src->mutex);\n\tsrc->selfview = !src->selfview;\n\tmtx_unlock(src->mutex);\n}\n\n\n/**\n * Set focus on selected participant source\n *\n * @param src        Video mixer source\n * @param focus_src  Video mixer source to focus, NULL to clear focus state\n * @param focus_full Full focus\n */\nvoid vidmix_source_set_focus(struct vidmix_source *src,\n\t\t\t     const struct vidmix_source *focus_src,\n\t\t\t     bool focus_full)\n{\n\tif (!src)\n\t\treturn;\n\n\tmtx_lock(src->mutex);\n\tsrc->focus_full = focus_full;\n\tsrc->focus = (void *)focus_src;\n\tmtx_unlock(src->mutex);\n}\n\n\n/**\n * Set focus on selected participant\n *\n * @param src    Video mixer source\n * @param pidx   Participant to focus, 0 to disable\n */\nvoid vidmix_source_set_focus_idx(struct vidmix_source *src, uint32_t pidx)\n{\n\tbool focus_full = false;\n\tvoid *focus = NULL;\n\n\tif (!src)\n\t\treturn;\n\n\tif (pidx > 0) {\n\n\t\tstruct le *le;\n\n\t\tmtx_lock(&src->mix->rwlock);\n\n\t\tLIST_FOREACH(&src->mix->srcl, le) {\n\t\t\tconst struct vidmix_source *lsrc = le->data;\n\n\t\t\tif (lsrc == src && !src->selfview)\n\t\t\t\tcontinue;\n\n\t\t\tif (lsrc->content && src->content_hide)\n\t\t\t\tcontinue;\n\n\t\t\tif (lsrc->pidx == pidx) {\n\t\t\t\tfocus = (void *)lsrc;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tmtx_unlock(&src->mix->rwlock);\n\t}\n\n\tif (focus && focus == src->focus)\n\t\tfocus_full = !src->focus_full;\n\n\tmtx_lock(src->mutex);\n\tsrc->focus_full = focus_full;\n\tsrc->focus = focus;\n\tmtx_unlock(src->mutex);\n}\n\n\n/**\n * Put a video frame into the video mixer\n *\n * @param src   Video source\n * @param frame Video frame\n */\nvoid vidmix_source_put(struct vidmix_source *src, const struct vidframe *frame)\n{\n\tif (!src || !frame || frame->fmt != src->mix->fmt)\n\t\treturn;\n\n\tif (!src->frame_rx || !vidsz_cmp(&src->frame_rx->size, &frame->size)) {\n\n\t\tstruct vidframe *frm;\n\t\tint err;\n\n\t\terr = vidframe_alloc(&frm, src->mix->fmt, &frame->size);\n\t\tif (err)\n\t\t\treturn;\n\n\t\tmtx_lock(&src->mix->rwlock);\n\n\t\tmem_deref(src->frame_rx);\n\t\tsrc->frame_rx = frm;\n\n\t\tmtx_unlock(&src->mix->rwlock);\n\t}\n\n\tmtx_lock(&src->mix->rwlock);\n\tvidframe_copy(src->frame_rx, frame);\n\tmtx_unlock(&src->mix->rwlock);\n}\n"
  },
  {
    "path": "sonar-project.properties",
    "content": "sonar.projectKey=baresip_re\nsonar.organization=baresip\nsonar.cfamily.threads=2\n"
  },
  {
    "path": "src/aes/apple/aes.c",
    "content": "/**\n * @file apple/aes.c  AES using Apple CommonCrypto API\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_fmt.h>\n#include <re_aes.h>\n#include <CommonCrypto/CommonCryptor.h>\n\n\nstruct aes {\n\tCCCryptorRef cryptor;\n\tuint8_t key[64];\n\tsize_t key_bytes;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct aes *st = arg;\n\n\tif (st->cryptor)\n\t\tCCCryptorRelease(st->cryptor);\n}\n\n\nint aes_alloc(struct aes **stp, enum aes_mode mode,\n\t      const uint8_t *key, size_t key_bits,\n\t      const uint8_t *iv)\n{\n\tstruct aes *st;\n\tsize_t key_bytes = key_bits / 8;\n\tCCCryptorStatus status;\n\tint err = 0;\n\n\tif (!stp || !key)\n\t\treturn EINVAL;\n\n\tif (mode != AES_MODE_CTR)\n\t\treturn ENOTSUP;\n\n\tst = mem_zalloc(sizeof(*st), destructor);\n\tif (!st)\n\t\treturn ENOMEM;\n\n\tif (key_bytes > sizeof(st->key)) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\tst->key_bytes = key_bytes;\n\tmemcpy(st->key, key, st->key_bytes);\n\n\t/* used for both encryption and decryption because CTR is symmetric */\n\tstatus = CCCryptorCreateWithMode(kCCEncrypt, kCCModeCTR,\n\t\t\t\t\t kCCAlgorithmAES, ccNoPadding,\n\t\t\t\t\t iv, key, key_bytes, NULL, 0, 0,\n\t\t\t\t\t kCCModeOptionCTR_BE, &st->cryptor);\n\tif (status != kCCSuccess) {\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(st);\n\telse\n\t\t*stp = st;\n\n\treturn err;\n}\n\n\nvoid aes_set_iv(struct aes *st, const uint8_t *iv)\n{\n\tCCCryptorStatus status;\n\n\tif (!st)\n\t\treturn;\n\n\t/* we must reset the state when updating IV */\n\tif (st->cryptor) {\n\t\tCCCryptorRelease(st->cryptor);\n\t\tst->cryptor = NULL;\n\t}\n\n\tstatus = CCCryptorCreateWithMode(kCCEncrypt, kCCModeCTR,\n\t\t\t\t\t kCCAlgorithmAES, ccNoPadding,\n\t\t\t\t\t iv, st->key, st->key_bytes,\n\t\t\t\t\t NULL, 0, 0, kCCModeOptionCTR_BE,\n\t\t\t\t\t &st->cryptor);\n\tif (status != kCCSuccess) {\n\t\tre_fprintf(stderr, \"aes: CCCryptorCreateWithMode error (%d)\\n\",\n\t\t\t   status);\n\t}\n}\n\n\nint aes_encr(struct aes *st, uint8_t *out, const uint8_t *in, size_t len)\n{\n\tCCCryptorStatus status;\n\tsize_t moved;\n\n\tif (!st || !out || !in)\n\t\treturn EINVAL;\n\n\tstatus = CCCryptorUpdate(st->cryptor, in, len, out, len, &moved);\n\tif (status != kCCSuccess) {\n\t\tre_fprintf(stderr, \"aes: CCCryptorUpdate error (%d)\\n\",\n\t\t\t   status);\n\t\treturn EPROTO;\n\t}\n\n\treturn 0;\n}\n\n\nint aes_decr(struct aes *st, uint8_t *out, const uint8_t *in, size_t len)\n{\n\treturn aes_encr(st, out, in, len);\n}\n\n\nint aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen)\n{\n\t(void)aes;\n\t(void)tag;\n\t(void)taglen;\n\n\treturn ENOSYS;\n}\n\n\nint aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen)\n{\n\t(void)aes;\n\t(void)tag;\n\t(void)taglen;\n\n\treturn ENOSYS;\n}\n"
  },
  {
    "path": "src/aes/openssl/aes.c",
    "content": "/**\n * @file openssl/aes.c  AES (Advanced Encryption Standard) using OpenSSL\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <openssl/aes.h>\n#include <openssl/evp.h>\n#include <openssl/err.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_aes.h>\n\n\nstruct aes {\n\tEVP_CIPHER_CTX *ctx;\n\tenum aes_mode mode;\n\tbool encr;\n};\n\n\nstatic const EVP_CIPHER *aes_cipher(enum aes_mode mode, size_t key_bits)\n{\n\tif (mode == AES_MODE_CTR) {\n\n\t\tswitch (key_bits) {\n\n\t\tcase 128: return EVP_aes_128_ctr();\n\t\tcase 256: return EVP_aes_256_ctr();\n\t\tdefault:\n\t\t\treturn NULL;\n\t\t}\n\t}\n\telse if (mode == AES_MODE_GCM) {\n\n\t\tswitch (key_bits) {\n\n\t\tcase 128: return EVP_aes_128_gcm();\n\t\tcase 256: return EVP_aes_256_gcm();\n\t\tdefault:\n\t\t\treturn NULL;\n\t\t}\n\t}\n\telse {\n\t\treturn NULL;\n\t}\n}\n\n\nstatic inline bool set_crypt_dir(struct aes *aes, bool encr)\n{\n\tif (aes->encr != encr) {\n\n\t\t/* update the encrypt/decrypt direction */\n\t\tif (!EVP_CipherInit_ex(aes->ctx, NULL, NULL,\n\t\t\t\t       NULL, NULL, encr)) {\n\t\t\tERR_clear_error();\n\t\t\treturn false;\n\t\t}\n\n\t\taes->encr = encr;\n\t}\n\n\treturn true;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct aes *st = arg;\n\n\tif (st->ctx)\n\t\tEVP_CIPHER_CTX_free(st->ctx);\n}\n\n\nint aes_alloc(struct aes **aesp, enum aes_mode mode,\n\t      const uint8_t *key, size_t key_bits,\n\t      const uint8_t *iv)\n{\n\tconst EVP_CIPHER *cipher;\n\tstruct aes *st;\n\tint err = 0, r;\n\n\tif (!aesp || !key)\n\t\treturn EINVAL;\n\n\tcipher = aes_cipher(mode, key_bits);\n\tif (!cipher)\n\t\treturn ENOTSUP;\n\n\tst = mem_zalloc(sizeof(*st), destructor);\n\tif (!st)\n\t\treturn ENOMEM;\n\n\tst->mode = mode;\n\tst->encr = true;\n\n\tst->ctx = EVP_CIPHER_CTX_new();\n\tif (!st->ctx) {\n\t\tERR_clear_error();\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tr = EVP_EncryptInit_ex(st->ctx, cipher, NULL, key, iv);\n\tif (!r) {\n\t\tERR_clear_error();\n\t\terr = EPROTO;\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(st);\n\telse\n\t\t*aesp = st;\n\n\treturn err;\n}\n\n\nvoid aes_set_iv(struct aes *aes, const uint8_t *iv)\n{\n\tint r;\n\n\tif (!aes || !iv)\n\t\treturn;\n\n\tr = EVP_CipherInit_ex(aes->ctx, NULL, NULL, NULL, iv, -1);\n\tif (!r)\n\t\tERR_clear_error();\n}\n\n\nint aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)\n{\n\tint c_len = (int)len;\n\n\tif (!aes || !in)\n\t\treturn EINVAL;\n\n\tif (!set_crypt_dir(aes, true))\n\t\treturn EPROTO;\n\n\tif (!EVP_EncryptUpdate(aes->ctx, out, &c_len, in, (int)len)) {\n\t\tERR_clear_error();\n\t\treturn EPROTO;\n\t}\n\n\treturn 0;\n}\n\n\nint aes_decr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)\n{\n\tint c_len = (int)len;\n\n\tif (!aes || !in)\n\t\treturn EINVAL;\n\n\tif (!set_crypt_dir(aes, false))\n\t\treturn EPROTO;\n\n\tif (!EVP_DecryptUpdate(aes->ctx, out, &c_len, in, (int)len)) {\n\t\tERR_clear_error();\n\t\treturn EPROTO;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Get the authentication tag for an AEAD cipher (e.g. GCM)\n *\n * @param aes    AES Context\n * @param tag    Authentication tag\n * @param taglen Length of Authentication tag\n *\n * @return 0 if success, otherwise errorcode\n */\nint aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen)\n{\n\tint tmplen;\n\n\tif (!aes || !tag || !taglen)\n\t\treturn EINVAL;\n\n\tswitch (aes->mode) {\n\n\tcase AES_MODE_GCM:\n\t\tif (!EVP_EncryptFinal_ex(aes->ctx, NULL, &tmplen)) {\n\t\t\tERR_clear_error();\n\t\t\treturn EPROTO;\n\t\t}\n\n\t\tif (!EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_GET_TAG,\n\t\t\t\t\t (int)taglen, tag)) {\n\t\t\tERR_clear_error();\n\t\t\treturn EPROTO;\n\t\t}\n\n\t\treturn 0;\n\n\tdefault:\n\t\treturn ENOTSUP;\n\t}\n}\n\n\n/**\n * Authenticate a decryption tag for an AEAD cipher (e.g. GCM)\n *\n * @param aes    AES Context\n * @param tag    Authentication tag\n * @param taglen Length of Authentication tag\n *\n * @return 0 if success, otherwise errorcode\n *\n * @retval EAUTH if authentication failed\n */\nint aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen)\n{\n\tint tmplen;\n\n\tif (!aes || !tag || !taglen)\n\t\treturn EINVAL;\n\n\tswitch (aes->mode) {\n\n\tcase AES_MODE_GCM:\n\t\tif (!EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_SET_TAG,\n\t\t\t\t\t (int)taglen, (void *)tag)) {\n\t\t\tERR_clear_error();\n\t\t\treturn EPROTO;\n\t\t}\n\n\t\tif (EVP_DecryptFinal_ex(aes->ctx, NULL, &tmplen) <= 0) {\n\t\t\tERR_clear_error();\n\t\t\treturn EAUTH;\n\t\t}\n\n\t\treturn 0;\n\n\tdefault:\n\t\treturn ENOTSUP;\n\t}\n}\n"
  },
  {
    "path": "src/aes/stub.c",
    "content": "/**\n * @file aes/stub.c  AES stub\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_aes.h>\n\n\nint aes_alloc(struct aes **stp, enum aes_mode mode,\n\t      const uint8_t *key, size_t key_bits,\n\t      const uint8_t *iv)\n{\n\t(void)stp;\n\t(void)mode;\n\t(void)key;\n\t(void)key_bits;\n\t(void)iv;\n\treturn ENOSYS;\n}\n\n\nvoid aes_set_iv(struct aes *aes, const uint8_t *iv)\n{\n\t(void)aes;\n\t(void)iv;\n}\n\n\nint aes_encr(struct aes *st, uint8_t *out, const uint8_t *in, size_t len)\n{\n\t(void)st;\n\t(void)out;\n\t(void)in;\n\t(void)len;\n\treturn ENOSYS;\n}\n\n\nint aes_decr(struct aes *st, uint8_t *out, const uint8_t *in, size_t len)\n{\n\t(void)st;\n\t(void)out;\n\t(void)in;\n\t(void)len;\n\treturn ENOSYS;\n}\n\n\nint aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen)\n{\n\t(void)aes;\n\t(void)tag;\n\t(void)taglen;\n\n\treturn ENOSYS;\n}\n\n\nint aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen)\n{\n\t(void)aes;\n\t(void)tag;\n\t(void)taglen;\n\n\treturn ENOSYS;\n}\n"
  },
  {
    "path": "src/async/async.c",
    "content": "/**\n * @file async.c Async API\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_list.h>\n#include <re_thread.h>\n#include <re_async.h>\n#include <re_tmr.h>\n#include <re_mqueue.h>\n\n#define DEBUG_MODULE \"async\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nstruct async_work {\n\tstruct le le;\n\tmtx_t *mtx;\n\tre_async_work_h *workh;\n\tre_async_h *cb;\n\tvoid *arg;\n\tint err;\n\tintptr_t id;\n};\n\nstruct re_async {\n\tthrd_t *thrd;\n\tuint16_t workers;\n\tvolatile bool run;\n\tcnd_t wait;\n\tmtx_t mtx;\n\tstruct list freel;\n\tstruct list workl;\n\tstruct list curl;\n\tstruct tmr tmr;\n\tstruct mqueue *mqueue;\n};\n\n\nstatic int worker_thread(void *arg)\n{\n\tstruct re_async *a = arg;\n\tstruct le *le;\n\tstruct async_work *work;\n\n\tfor (;;) {\n\t\tmtx_lock(&a->mtx);\n\t\tif (!a->run) {\n\t\t\tmtx_unlock(&a->mtx);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (list_isempty(&a->workl)) {\n\t\t\tcnd_wait(&a->wait, &a->mtx);\n\n\t\t\tif (list_isempty(&a->workl) || !a->run) {\n\t\t\t\tmtx_unlock(&a->mtx);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tle = list_head(&a->workl);\n\t\tlist_move(le, &a->curl);\n\t\tmtx_unlock(&a->mtx);\n\n\t\twork = le->data;\n\t\tmtx_lock(work->mtx);\n\t\tif (work->workh) {\n\t\t\twork->err = work->workh(work->arg);\n\t\t\twork->workh = NULL;\n\t\t}\n\t\tmtx_unlock(work->mtx);\n\n\t\tmtx_lock(&a->mtx);\n\t\tmqueue_push(a->mqueue, 0, work);\n\t\tmtx_unlock(&a->mtx);\n\t}\n\n\treturn 0;\n}\n\n\nstatic void async_destructor(void *data)\n{\n\tstruct re_async *async = data;\n\n\ttmr_cancel(&async->tmr);\n\n\tmtx_lock(&async->mtx);\n\tasync->run = false;\n\tcnd_broadcast(&async->wait);\n\tmtx_unlock(&async->mtx);\n\n\tfor (int i = 0; i < async->workers; i++) {\n\t\tthrd_join(async->thrd[i], NULL);\n\t}\n\n\t/* Notify worker callbacks (so they can call destructors) */\n\tstruct le *le;\n\tLIST_FOREACH(&async->workl, le)\n\t{\n\t\tstruct async_work *work = le->data;\n\t\tif (work->cb) {\n\t\t\twork->cb(ECANCELED, work->arg);\n\t\t\twork->cb = NULL;\n\t\t}\n\t}\n\tLIST_FOREACH(&async->curl, le)\n\t{\n\t\tstruct async_work *work = le->data;\n\t\tif (work->cb) {\n\t\t\twork->cb(ECANCELED, work->arg);\n\t\t\twork->cb = NULL;\n\t\t}\n\t}\n\n\tlist_flush(&async->workl);\n\tlist_flush(&async->curl);\n\tlist_flush(&async->freel);\n\tcnd_destroy(&async->wait);\n\tmtx_destroy(&async->mtx);\n\tmem_deref(async->mqueue);\n\tmem_deref(async->thrd);\n}\n\n\nstatic void worker_check(void *arg)\n{\n\tstruct re_async *async = arg;\n\n\tmtx_lock(&async->mtx);\n\tif (!list_isempty(&async->workl)) {\n\t\tif (async->workers == list_count(&async->curl))\n\t\t\tDEBUG_WARNING(\"all async workers are busy (%u)\\n\",\n\t\t\t\t      async->workers);\n\t\telse\n\t\t\tcnd_broadcast(&async->wait);\n\t}\n\tmtx_unlock(&async->mtx);\n\n\ttmr_start(&async->tmr, 100, worker_check, async);\n}\n\n\n/* called by re main event loop */\nstatic void queueh(int id, void *data, void *arg)\n{\n\tstruct async_work *work = data;\n\tstruct re_async *async\t= arg;\n\t(void)id;\n\n\tmtx_lock(work->mtx);\n\tre_async_h *cb = work->cb;\n\tvoid *cb_arg   = work->arg;\n\tint err        = work->err;\n\twork->cb = NULL;\n\tmtx_unlock(work->mtx);\n\n\tif (cb)\n\t\tcb(err, cb_arg);\n\n\tmtx_lock(&async->mtx);\n\tlist_move(&work->le, &async->freel);\n\tmtx_unlock(&async->mtx);\n}\n\n\nstatic void work_destruct(void *arg)\n{\n\tstruct async_work *work = arg;\n\tmem_deref(work->mtx);\n}\n\n\nstatic int work_alloc(struct async_work **workp)\n{\n\tint err;\n\tstruct async_work *work;\n\n\twork = mem_zalloc(sizeof(struct async_work), NULL);\n\tif (!work) {\n\t\terr = ENOMEM;\n\t\treturn err;\n\t}\n\n\terr = mutex_alloc(&work->mtx);\n\tif (err) {\n\t\tmem_deref(work);\n\t\treturn err;\n\t}\n\n\tmem_destructor(work, work_destruct);\n\n\t*workp = work;\n\n\treturn 0;\n}\n\n\n/**\n * Allocate a new async object\n *\n * @param asyncp  Pointer to allocated async object\n * @param workers Number of worker threads\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_async_alloc(struct re_async **asyncp, uint16_t workers)\n{\n\tint err;\n\tstruct re_async *async;\n\tstruct async_work *work;\n\n\tif (!asyncp || !workers)\n\t\treturn EINVAL;\n\n\tasync = mem_zalloc(sizeof(struct re_async), NULL);\n\tif (!async)\n\t\treturn ENOMEM;\n\n\terr = mqueue_alloc(&async->mqueue, queueh, async);\n\tif (err)\n\t\tgoto err;\n\n\tasync->thrd = mem_zalloc(sizeof(thrd_t) * workers, NULL);\n\tif (!async->thrd) {\n\t\terr = ENOMEM;\n\t\tmem_deref(async->mqueue);\n\t\tgoto err;\n\t}\n\n\tmtx_init(&async->mtx, mtx_plain);\n\tcnd_init(&async->wait);\n\ttmr_init(&async->tmr);\n\n\tmem_destructor(async, async_destructor);\n\n\tasync->run = true;\n\n\tfor (int i = 0; i < workers; i++) {\n\t\terr = thread_create_name(&async->thrd[i],\n\t\t\t\t\t \"async worker thread\", worker_thread,\n\t\t\t\t\t async);\n\t\tif (err)\n\t\t\tgoto err;\n\n\t\tasync->workers++;\n\n\t\t/* preallocate */\n\t\terr = work_alloc(&work);\n\t\tif (err)\n\t\t\tgoto err;\n\n\t\tlist_append(&async->freel, &work->le, work);\n\t}\n\n\ttmr_start(&async->tmr, 10, worker_check, async);\n\n\t*asyncp = async;\n\n\treturn 0;\n\nerr:\n\tmem_deref(async);\n\treturn err;\n}\n\n\n/**\n * Execute work handler async and get a callback from re main thread\n *\n * @param async Pointer to async object\n * @param id    Work identifier\n * @param workh Work handler\n * @param cb    Callback handler (called by re main thread)\n * @param arg   Handler argument (has to be thread-safe)\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_async(struct re_async *async, intptr_t id, re_async_work_h *workh,\n\t     re_async_h *cb, void *arg)\n{\n\tint err = 0;\n\tstruct async_work *work;\n\n\tif (unlikely(!async))\n\t\treturn EINVAL;\n\n\tmtx_lock(&async->mtx);\n\tif (unlikely(list_isempty(&async->freel))) {\n\n\t\terr = work_alloc(&work);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse {\n\t\twork = list_head(&async->freel)->data;\n\t\tlist_unlink(&work->le);\n\t}\n\n\twork->workh = workh;\n\twork->cb    = cb;\n\twork->arg   = arg;\n\twork->id    = id;\n\n\tlist_append(&async->workl, &work->le, work);\n\tcnd_signal(&async->wait);\n\nout:\n\tmtx_unlock(&async->mtx);\n\n\treturn err;\n}\n\n\n/**\n * Cancel pending async work and callback\n *\n * @param async Pointer to async object\n * @param id    Work identifier\n */\nvoid re_async_cancel(struct re_async *async, intptr_t id)\n{\n\tstruct le *le;\n\n\tif (unlikely(!async))\n\t\treturn;\n\n\tmtx_lock(&async->mtx);\n\n\tle = list_head(&async->workl);\n\twhile (le) {\n\t\tstruct async_work *w = le->data;\n\n\t\tle = le->next;\n\n\t\tif (w->id != id)\n\t\t\tcontinue;\n\n\t\tmtx_lock(w->mtx);\n\t\tw->id\t = 0;\n\t\tw->workh = NULL;\n\t\tw->cb\t = NULL;\n\t\tw->arg\t = mem_deref(w->arg);\n\t\tlist_move(&w->le, &async->freel);\n\t\tmtx_unlock(w->mtx);\n\t}\n\n\tle = list_head(&async->curl);\n\twhile (le) {\n\t\tstruct async_work *w = le->data;\n\n\t\tle = le->next;\n\n\t\tif (w->id != id)\n\t\t\tcontinue;\n\n\t\tmtx_lock(w->mtx);\n\t\tw->id\t = 0;\n\t\tw->workh = NULL;\n\t\tw->cb\t = NULL;\n\t\tw->arg\t = mem_deref(w->arg);\n\t\t/* No move to free list since queueh must always handled if\n\t\t * mqueue_push is called */\n\t\tmtx_unlock(w->mtx);\n\t}\n\n\tmtx_unlock(&async->mtx);\n}\n"
  },
  {
    "path": "src/av1/depack.c",
    "content": "/**\n * @file av1/depack.c AV1 De-packetizer\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_av1.h>\n\n\n/**\n * Decode an AV1 Aggregation header from mbuffer\n *\n * @param hdr Decoded aggregation header\n * @param mb  Mbuffer to decode from\n *\n * @return 0 if success, otherwise errorcode\n */\nint av1_aggr_hdr_decode(struct av1_aggr_hdr *hdr, struct mbuf *mb)\n{\n\tuint8_t v;\n\n\tif (!hdr || !mb)\n\t\treturn EINVAL;\n\n\tmemset(hdr, 0, sizeof(*hdr));\n\n\tif (mbuf_get_left(mb) < 1)\n\t\treturn EBADMSG;\n\n\tv = mbuf_read_u8(mb);\n\n\thdr->z = v>>7 & 0x1;\n\thdr->y = v>>6 & 0x1;\n\thdr->w = v>>4 & 0x3;\n\thdr->n = v>>3 & 0x1;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/av1/obu.c",
    "content": "/**\n * @file av1/obu.c AV1 Open Bitstream Unit (OBU)\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_av1.h>\n\n\n#define DEBUG_MODULE \"av1\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nbool obu_allowed_rtp(enum obu_type type)\n{\n\tswitch (type) {\n\n\tcase AV1_OBU_SEQUENCE_HEADER:\n\tcase AV1_OBU_FRAME_HEADER:\n\tcase AV1_OBU_METADATA:\n\tcase AV1_OBU_FRAME:\n\tcase AV1_OBU_REDUNDANT_FRAME_HEADER:\n\tcase AV1_OBU_TILE_GROUP:\n\t\treturn true;\n\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\n/**\n * Encode a number into LEB128 format, which is an unsigned integer\n * represented by a variable number of little-endian bytes.\n *\n * @param mb    Mbuffer to encode into\n * @param value Value to encode\n *\n * @return 0 if success, otherwise errorcode\n */\nint av1_leb128_encode(struct mbuf *mb, uint64_t value)\n{\n\tint err = 0;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\twhile (value >= 0x80) {\n\n\t\tuint8_t u8 = 0x80 | (value & 0x7f);\n\n\t\terr |= mbuf_write_u8(mb, u8);\n\n\t\tvalue >>= 7;\n\t}\n\n\terr |= mbuf_write_u8(mb, (uint8_t)value);\n\n\treturn err;\n}\n\n\n/**\n * Decode a number in LEB128 format, which is an unsigned integer\n * represented by a variable number of little-endian bytes.\n *\n * @param mb    Mbuffer to decode from\n * @param value Decoded value, set on return\n *\n * @return 0 if success, otherwise errorcode\n */\nint av1_leb128_decode(struct mbuf *mb, uint64_t *value)\n{\n\tuint64_t ret = 0;\n\n\tif (!mb || !value)\n\t\treturn EINVAL;\n\n\tfor (unsigned i = 0; i < 8; i++) {\n\n\t\tsize_t byte;\n\n\t\tif (mbuf_get_left(mb) < 1)\n\t\t\treturn EBADMSG;\n\n\t\tbyte = mbuf_read_u8(mb);\n\n\t\tret |= (uint64_t)(byte & 0x7f) << (i * 7);\n\n\t\tif (!(byte & 0x80))\n\t\t\tbreak;\n\t}\n\n\t*value = ret;\n\n\treturn 0;\n}\n\n\n/**\n * Encode an OBU into an mbuffer\n *\n * @param mb       Mbuffer to encode into\n * @param type     OBU type\n * @param has_size True to use the 'has_size' field\n * @param len      Number of bytes\n * @param payload  Optional OBU payload\n *\n * @return 0 if success, otherwise errorcode\n */\nint av1_obu_encode(struct mbuf *mb, uint8_t type, bool has_size,\n\t\t   size_t len, const uint8_t *payload)\n{\n\tuint8_t val;\n\tint err;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\tval  = (type&0xf) << 3;\n\tval |= (unsigned)has_size << 1;\n\n\terr  = mbuf_write_u8(mb, val);\n\n\tif (has_size)\n\t\terr |= av1_leb128_encode(mb, len);\n\n\tif (payload && len)\n\t\terr |= mbuf_write_mem(mb, payload, len);\n\n\treturn err;\n}\n\n\n/**\n * Decode an OBU header from mbuffer\n *\n * @param hdr Decoded OBU header\n * @param mb  Mbuffer to decode from\n *\n * @return 0 if success, otherwise errorcode\n */\nint av1_obu_decode(struct av1_obu_hdr *hdr, struct mbuf *mb)\n{\n\tuint8_t val;\n\tbool f;\n\tint err;\n\n\tif (!hdr || !mb)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < 1)\n\t\treturn EBADMSG;\n\n\tmemset(hdr, 0, sizeof(*hdr));\n\n\tval = mbuf_read_u8(mb);\n\n\tf         = (val >> 7) & 0x1;\n\thdr->type = (val >> 3) & 0xf;\n\thdr->x    = (val >> 2) & 0x1;\n\thdr->s    = (val >> 1) & 0x1;\n\n\tif (f) {\n\t\tDEBUG_WARNING(\"av1: header: obu forbidden bit!\"\n\t\t\t\" [type=%u, x=%d, s=%d, left=%zu bytes]\\n\",\n\t\t\thdr->type, hdr->x, hdr->s, mbuf_get_left(mb));\n\t\treturn EBADMSG;\n\t}\n\n\tif (hdr->x) {\n\t\tDEBUG_WARNING(\"av1: header: extension not supported (%u)\\n\",\n\t\t\t      hdr->type);\n\t\treturn ENOTSUP;\n\t}\n\n\tif (hdr->s) {\n\t\tuint64_t size;\n\n\t\terr = av1_leb128_decode(mb, &size);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (size > mbuf_get_left(mb)) {\n\t\t\tDEBUG_WARNING(\"av1: obu decode: short packet:\"\n\t\t\t\t      \" %llu > %zu\\n\",\n\t\t\t\t      size, mbuf_get_left(mb));\n\t\t\treturn EBADMSG;\n\t\t}\n\n\t\thdr->size = (size_t)size;\n\t}\n\telse {\n\t\thdr->size = mbuf_get_left(mb);\n\t}\n\n\treturn 0;\n}\n\n\nint av1_obu_print(struct re_printf *pf, const struct av1_obu_hdr *hdr)\n{\n\tif (!hdr)\n\t\treturn 0;\n\n\treturn re_hprintf(pf, \"type=%u,%-24s x=%d s=%d size=%zu\",\n\t\t\t  hdr->type, av1_obu_name(hdr->type),\n\t\t\t  hdr->x, hdr->s, hdr->size);\n}\n\n\n/**\n * Count number of OBUs in the bitstream\n *\n * @param buf  Bitstream buffer\n * @param size Number of bytes in buffer\n *\n * @return Number of OBUs\n */\nunsigned av1_obu_count(const uint8_t *buf, size_t size)\n{\n\tstruct mbuf wrap = {\n\t\t.buf = (uint8_t *)buf,\n\t\t.size = size,\n\t\t.pos = 0,\n\t\t.end = size\n\t};\n\tunsigned count = 0;\n\n\twhile (mbuf_get_left(&wrap) > 1) {\n\n\t\tstruct av1_obu_hdr hdr;\n\n\t\tint err = av1_obu_decode(&hdr, &wrap);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"count: could not decode OBU\"\n\t\t\t\t      \" [%zu bytes]: %m\\n\", size, err);\n\t\t\treturn 0;\n\t\t}\n\n\t\tmbuf_advance(&wrap, hdr.size);\n\n\t\t++count;\n\t}\n\n\treturn count;\n}\n\n\n/**\n * Count number of OBUs in the bitstream allowed for RTP transmission\n *\n * @param buf  Bitstream buffer\n * @param size Number of bytes in buffer\n *\n * @return Number of OBUs\n */\nunsigned av1_obu_count_rtp(const uint8_t *buf, size_t size)\n{\n\tstruct mbuf wrap = {\n\t\t.buf = (uint8_t *)buf,\n\t\t.size = size,\n\t\t.pos = 0,\n\t\t.end = size\n\t};\n\tunsigned count = 0;\n\n\twhile (mbuf_get_left(&wrap) > 1) {\n\n\t\tstruct av1_obu_hdr hdr;\n\n\t\tint err = av1_obu_decode(&hdr, &wrap);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"count: could not decode OBU\"\n\t\t\t\t      \" [%zu bytes]: %m\\n\", size, err);\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (obu_allowed_rtp(hdr.type))\n\t\t\t++count;\n\n\t\tmbuf_advance(&wrap, hdr.size);\n\t}\n\n\treturn count;\n}\n\n\nconst char *av1_obu_name(enum obu_type type)\n{\n\tswitch (type) {\n\n\tcase AV1_OBU_SEQUENCE_HEADER:        return \"OBU_SEQUENCE_HEADER\";\n\tcase AV1_OBU_TEMPORAL_DELIMITER:     return \"OBU_TEMPORAL_DELIMITER\";\n\tcase AV1_OBU_FRAME_HEADER:           return \"OBU_FRAME_HEADER\";\n\tcase AV1_OBU_REDUNDANT_FRAME_HEADER:\n\t\treturn \"OBU_REDUNDANT_FRAME_HEADER\";\n\tcase AV1_OBU_FRAME:                  return \"OBU_FRAME\";\n\tcase AV1_OBU_TILE_GROUP:             return \"OBU_TILE_GROUP\";\n\tcase AV1_OBU_METADATA:               return \"OBU_METADATA\";\n\tcase AV1_OBU_TILE_LIST:              return \"OBU_TILE_LIST\";\n\tcase AV1_OBU_PADDING:                return \"OBU_PADDING\";\n\tdefault: return \"???\";\n\t}\n}\n"
  },
  {
    "path": "src/av1/pkt.c",
    "content": "/**\n * @file av1/pkt.c AV1 Packetizer\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_av1.h>\n\n\n#define DEBUG_MODULE \"av1\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tMAX_OBUS = 3,   /* Maximum number of OBUs for W field */\n\tAV1_OBU_HEADER_SIZE = 1,\n};\n\nstruct av1_context {\n\t/* The current RTP packet being created */\n\tstruct mbuf *mb_pkt;\n\t/* The current OBU being packetized. This only contains the OBU\n\t * payload, not the header or size */\n\tstruct mbuf *curr_obu;\n\t/* The input buffer provided by the application.\n\t * The position always points to immediately after curr_obu */\n\tstruct mbuf *mb_buf;\n\t/* The OBU header for the current OBU being packetized */\n\tstruct av1_obu_hdr curr_hdr;\n\t/* The number of bytes which still need to be written from the\n\t * current OBU. This can be greater than the OBU size if the OBU\n\t * header hasn't been written yet.\n\t * If this is zero at the end of a packet, packetization is done */\n\tsize_t curr_remaining;\n\t/* The max length of each RTP packet */\n\tsize_t maxlen;\n};\n\n\n/*\n * Calculate length of LEB128 field\n *\n * Add high 1 bits on all but last (most significant) group to form bytes\n */\nstatic size_t leb128_calc_size(uint64_t value)\n{\n\tsize_t bytes = 1;\n\n\t/* Bit7: 1=more bytes coming, 0=complete */\n\twhile (value >= 0x80) {\n\n\t\t++bytes;\n\n\t\tvalue >>= 7;\n\t}\n\n\treturn bytes;\n}\n\n\n/*\n * Serialize the AV1 RTP aggregation header\n *\n * Z: MUST be set to 1 if the first OBU element is an OBU fragment that is a\n *    continuation of an OBU fragment from the previous packet, and MUST be\n *    set to 0 otherwise.\n *\n * Y: MUST be set to 1 if the last OBU element is an OBU fragment that will\n *    continue in the next packet, and MUST be set to 0 otherwise.\n */\nstatic void aggr_hdr_encode(uint8_t hdr[AV1_AGGR_HDR_SIZE],\n\t\t       bool z, bool y, uint8_t w, bool n)\n{\n\thdr[0] = z<<7 | y<<6 | w<<4 | n<<3;\n}\n\n\n/**\n * @returns the size the given OBU will be once we packetize it.\n * We force has_size to false in the OBU header, so this is the size of the OBU\n * and the one-byte OBU header.\n */\nstatic size_t packetized_obu_size(const struct av1_obu_hdr* hdr) {\n\treturn AV1_OBU_HEADER_SIZE + hdr->size;\n}\n\n\n/**\n * Searches through mb_buf until it finds an OBU which should be packetized,\n * and updates the current OBU when one is found.\n * If there are no more OBUs, curr_remaining will be set to 0\n *\n * @return 0 if success, otherwise errorcode\n */\nstatic int update_curr_obu(struct av1_context* context) {\n\tint err = 0;\n\tdo {\n\t\tsize_t remaining = mbuf_get_left(context->mb_buf);\n\t\t/* OBUs must be at least 2 bytes */\n\t\tif (remaining < 2) {\n\t\t\tif (remaining > 0) {\n\t\t\t\tDEBUG_WARNING(\n\t\t\t\t\t\"av1: encode: leftover data \"\n\t\t\t\t\t\"(%zu bytes)\\n\",\n\t\t\t\t\tremaining);\n\t\t\t\tmbuf_advance(context->mb_buf, remaining);\n\t\t\t}\n\t\t\tcontext->curr_obu->pos = context->curr_obu->end;\n\t\t\tcontext->curr_remaining = 0;\n\t\t\tbreak;\n\t\t}\n\t\terr = av1_obu_decode(&context->curr_hdr, context->mb_buf);\n\t\tif (err) {\n\t\t\tbreak;\n\t\t}\n\t\tcontext->curr_obu->buf = mbuf_buf(context->mb_buf);\n\t\tcontext->curr_obu->size = context->curr_hdr.size;\n\t\tcontext->curr_obu->pos = 0;\n\t\tcontext->curr_obu->end = context->curr_hdr.size;\n\t\tcontext->curr_remaining =\n\t\t\tpacketized_obu_size(&context->curr_hdr);\n\t\tmbuf_advance(context->mb_buf, context->curr_hdr.size);\n\t} while (!obu_allowed_rtp(context->curr_hdr.type));\n\n\treturn err;\n}\n\n\n/**\n * Copies len_to_copy of the current OBU to the RTP packet, taking the OBU\n * header into account. The caller is responsible for ensuring there is\n * enough space left in the packet for this many bytes:\n *     len_to_copy +\n *         include_prefix ? leb128_calc_size(len_to_copy) : 0\n *\n * @param context        Packetization context\n * @param include_prefix Whether to include the size prefix before the fragment\n * @param len_to_copy    The length of the OBU fragment to write. This is the\n *                       number of bytes to copy from the OBU\n *\n * @return 0 if success, otherwise errorcode\n */\nstatic int copy_fragment(struct av1_context* context, bool include_prefix,\n\t\tsize_t len_to_copy) {\n\tint err = 0;\n\tif (include_prefix) {\n\t\terr = av1_leb128_encode(context->mb_pkt, len_to_copy);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\t}\n\t/*\n\t * If this is the first time we're writing this OBU, we need to take\n\t * the OBU header into account. The header can't be copied normally\n\t * because we might need to modify it to remove the size information.\n\t */\n\tif (context->curr_remaining == packetized_obu_size(&context->curr_hdr))\n\t{\n\t\tuint8_t obu_hdr = (context->curr_hdr.type & 0xf) << 3;\n\t\terr |= mbuf_write_u8(context->mb_pkt, obu_hdr);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\t\t--len_to_copy;\n\t\t--context->curr_remaining;\n\t}\n\n\terr = mbuf_write_mem(context->mb_pkt,\n\t\tmbuf_buf(context->curr_obu), len_to_copy);\n\tif (err) {\n\t\tgoto out;\n\t}\n\tmbuf_advance(context->curr_obu, len_to_copy);\n\tcontext->curr_remaining -= len_to_copy;\nout:\n\treturn err;\n}\n\n\n/**\n * Calculates the length of a fragment and the length of the leb128-encoded\n * size prefix for it.\n *\n * @param context         Packetization context\n * @param remaining_pkt   The number of bytes remaining in the current packet.\n *                        Must be greater than zero\n * @param len_to_copy_out Pointer to receive the length of the fragment to be\n *                        written\n * @param prefix_len_out  Pointer to receive the length of the size prefix to\n *                        be written\n */\nstatic void calc_fragment_len_with_prefix(\n\t\tstruct av1_context* context,\n\t\tsize_t remaining_pkt,\n\t\tsize_t* len_to_copy_out,\n\t\tsize_t* prefix_len_out) {\n\t/* The size always uses at least 1 byte, so subtract one from the\n\t * remaining packet space */\n\tsize_t len_to_copy = min(context->curr_remaining, remaining_pkt - 1);\n\tsize_t prefix_len = leb128_calc_size(len_to_copy);\n\n\tif (len_to_copy + prefix_len > remaining_pkt) {\n\t\t/* If there's not enough room in the packet for the initial\n\t\t * estimate, reserve space in the packet for the length of the\n\t\t * prefix and try again. Note that prefix_len can change here\n\t\t * if len_to_copy changes from, say, 128 to 127, but the new\n\t\t * prefix_len will always be <= the old one. */\n\t\tlen_to_copy = remaining_pkt - prefix_len;\n\t\tprefix_len = leb128_calc_size(len_to_copy);\n\t}\n\n\t*len_to_copy_out = len_to_copy;\n\t*prefix_len_out = prefix_len;\n}\n\n\n/**\n * Calculates the length of the next fragment to write to the RTP packet and\n * whether it needs a size prefix.\n *\n * @param context            Packetization context\n * @param count              The number of fragments already written to the RTP\n *                           packet\n * @param include_prefix_out Pointer to receive whether the size prefix should\n *                           be written before this fragment\n * @param len_to_copy_out    Pointer to receive the length of the fragment to\n *                           be written. If set to zero, there was not enough\n *                           room in the packet for another fragment\n *\n * @returns 0 on success and non-zero otherwise. On success, check\n *          len_to_copy_out to determine if there was room in the packet for\n *          another fragment\n */\nstatic int calc_fragment_len(\n\t\tstruct av1_context* context,\n\t\tsize_t count,\n\t\tbool* include_prefix_out,\n\t\tsize_t* len_to_copy_out) {\n\t/* Note: This checks for space left in the entire buffer,\n\t * not the current OBU */\n\tbool is_last_obu = mbuf_get_left(context->mb_buf) == 0;\n\tsize_t pkt_len = mbuf_pos(context->mb_pkt);\n\tsize_t remaining_pkt = 0;\n\tsize_t len_to_copy = 0;\n\tsize_t fragment_size_len = 0;\n\tbool include_prefix = false;\n\n\t*include_prefix_out = 0;\n\t*len_to_copy_out = 0;\n\n\tif (pkt_len > context->maxlen) {\n\t\tDEBUG_WARNING(\"av1: encode: packet too large (%zu > %zu)\\n\",\n\t\t\t\tpkt_len, context->maxlen);\n\t\treturn ERANGE;\n\t}\n\tremaining_pkt = context->maxlen - pkt_len;\n\tif (remaining_pkt < 1) {\n\t\treturn 0;\n\t}\n\n\t/* The size prefix can be elided for the last fragment only when there\n\t * are 3 or fewer fragments in the packet. */\n\tif (count > MAX_OBUS) {\n\t\tinclude_prefix = true;\n\t}\n\telse if (!is_last_obu && remaining_pkt > context->curr_remaining) {\n\t\t/* If the next fragment would need a prefix, we need a minimum\n\t\t * of 2 bytes for it. */\n\t\tsize_t next_fragment = count > 2 ? 2 : 1;\n\t\tcalc_fragment_len_with_prefix(context,\n\t\t\tremaining_pkt,\n\t\t\t&len_to_copy,\n\t\t\t&fragment_size_len);\n\n\t\t/* Only include a prefix if there\n\t\t * is room for another fragment. */\n\t\tinclude_prefix = len_to_copy + fragment_size_len <=\n\t\t\t\tremaining_pkt - next_fragment;\n\t}\n\n\tif (include_prefix) {\n\t\t/* A prefixed fragment needs a minimum of 2 bytes:\n\t\t * 1 for the prefix itself and at least 1 for the data. */\n\t\tif (remaining_pkt < 2) {\n\t\t\treturn 0;\n\t\t}\n\t\t/* Only calculate this if it wasn't calculated above when\n\t\t * count <= 3 */\n\t\tif (len_to_copy == 0) {\n\t\t\tcalc_fragment_len_with_prefix(context,\n\t\t\t\tremaining_pkt,\n\t\t\t\t&len_to_copy,\n\t\t\t\t&fragment_size_len);\n\t\t}\n\t}\n\telse {\n\t\t/* No prefix is needed, fill as much\n\t\t * of the packet as possible. */\n\t\tlen_to_copy = min(context->curr_remaining, remaining_pkt);\n\t}\n\n\t*include_prefix_out = include_prefix;\n\t*len_to_copy_out = len_to_copy;\n\treturn 0;\n}\n\n\n/**\n * Copy as many OBU fragments as possible to the current RTP packet.\n *\n * @param context Packetization context\n * @param w       The value of w for this packet (whether each fragment is\n *                prefixed or the last one is elided)\n * @param y       The value of y for this packet (whether the last fragment\n *                will continue to the next packet)\n */\nstatic int copy_obus_to_packet(struct av1_context* context, uint8_t* w,\n\t\tbool *y) {\n\tunsigned count = 0;\n\tint err = 0;\n\tbool include_prefix = true;\n\n\t/* Stop copying OBUs when:\n\t * 1. There are no more OBUs left (curr_remaining == 0), or\n\t * 2. The last fragment didn't include a prefix. We aren't allowed to\n\t *    copy another fragment even if there's space in the packet, or\n\t * 3. calc_fragment_len determines there isn't enough room for another\n\t *    fragment */\n\twhile (context->curr_remaining > 0 && include_prefix) {\n\t\tsize_t len_to_copy = 0;\n\t\terr = calc_fragment_len(context, count + 1, &include_prefix,\n\t\t\t&len_to_copy);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\t\t/* Not enough room for another fragment */\n\t\tif (len_to_copy == 0) {\n\t\t\tbreak;\n\t\t}\n\t\terr = copy_fragment(context, include_prefix, len_to_copy);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\t\t++count;\n\n\t\tif (context->curr_remaining == 0) {\n\t\t\t/* We finished packetizing the current OBU,\n\t\t\t * move onto the next one. */\n\t\t\t*y = false;\n\t\t\terr = update_curr_obu(context);\n\t\t\tif (err) {\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t/* The current OBU still has data left to packetize. */\n\t\t\t*y = true;\n\t\t}\n\t}\n\t/* It's possible for copy_obu_to_packet to accidentally include the\n\t * size prefix before the last OBU in the packet if the last OBU in\n\t * the buffer is skipped. We need to set w = 0 in that case even if\n\t * there are <= 3 OBU fragments */\n\t*w = count > MAX_OBUS || include_prefix ? 0 : count;\n\nout:\n\treturn err;\n}\n\n\n/**\n * Packetize an AV1 bitstream with one or more OBUs\n *\n * @param newp    Pointer to new stream flag\n * @param marker  Set marker bit\n * @param rtp_ts  RTP timestamp\n * @param buf     Input buffer\n * @param len     Buffer length\n * @param maxlen  Maximum RTP packet size\n * @param pkth    Packet handler\n * @param arg     Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint av1_packetize(bool *newp, bool marker, uint64_t rtp_ts,\n\t\t  const uint8_t *buf, size_t len, size_t maxlen,\n\t\t  av1_packet_h *pkth, void *arg)\n{\n\tstruct mbuf *mb_pkt;\n\tuint8_t w;\n\tint err;\n\tbool continuing_to_next_packet = false;\n\tbool continued_from_previous_packet = false;\n\tstruct mbuf mb_buf = {\n\t\t.buf  = (uint8_t *)buf,\n\t\t.size = len,\n\t\t.pos  = 0,\n\t\t.end  = len\n\t};\n\tstruct mbuf curr_obu;\n\tuint8_t aggr_hdr[AV1_AGGR_HDR_SIZE];\n\n\tif (!newp || !buf || !len || maxlen < (AV1_AGGR_HDR_SIZE + 1) || !pkth)\n\t\treturn EINVAL;\n\n\tmaxlen -= sizeof(aggr_hdr);\n\tmb_pkt = mbuf_alloc(maxlen);\n\tif (!mb_pkt)\n\t\treturn ENOMEM;\n\tmbuf_init(&curr_obu);\n\n\tstruct av1_context context = {\n\t\t.mb_pkt = mb_pkt,\n\t\t.mb_buf = &mb_buf,\n\t\t.curr_obu = &curr_obu,\n\t\t.curr_remaining = 0,\n\t\t.maxlen = maxlen\n\t};\n\terr = update_curr_obu(&context);\n\tif (err) {\n\t\tgoto out;\n\t}\n\n\twhile (context.curr_remaining > 0) {\n\t\tcontinued_from_previous_packet = continuing_to_next_packet;\n\t\terr = copy_obus_to_packet(&context, &w,\n\t\t\t&continuing_to_next_packet);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\t\taggr_hdr_encode(aggr_hdr, continued_from_previous_packet,\n\t\t\tcontinuing_to_next_packet, w, *newp);\n\t\t*newp = false;\n\n\t\tmbuf_set_pos(context.mb_pkt, 0);\n\t\terr = pkth(marker && context.curr_remaining == 0,\n\t\t\trtp_ts,\n\t\t\taggr_hdr,\n\t\t\tsizeof(aggr_hdr),\n\t\t\tmbuf_buf(context.mb_pkt),\n\t\t\tmbuf_get_left(context.mb_pkt),\n\t\t\targ);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\t\tmbuf_rewind(context.mb_pkt);\n\t}\n\n out:\n\tmem_deref(mb_pkt);\n\treturn err;\n}\n"
  },
  {
    "path": "src/base64/b64.c",
    "content": "/**\n * @file b64.c  Base64 encoding/decoding functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_base64.h>\n\n\nstatic const char b64_table[65] =\n\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\t\"abcdefghijklmnopqrstuvwxyz\"\n\t\"0123456789+/\";\n\n/* Base 64 Encoding with URL and Filename Safe Alphabet, RFC 4648 section 5 */\nstatic const char b64url_table[65] =\n\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\t\"abcdefghijklmnopqrstuvwxyz\"\n\t\"0123456789-_\";\n\n\n/**\n * Base-64 encode a buffer\n *\n * @param in   Input buffer\n * @param ilen Length of input buffer\n * @param out  Output buffer\n * @param olen Size of output buffer, actual written on return\n *\n * @return 0 if success, otherwise errorcode\n */\nint base64_encode(const uint8_t *in, size_t ilen, char *out, size_t *olen)\n{\n\tconst uint8_t *in_end = in + ilen;\n\tconst char *o = out;\n\n\tif (!in || !out || !olen)\n\t\treturn EINVAL;\n\n\tif (*olen < 4 * ((ilen+2)/3))\n\t\treturn EOVERFLOW;\n\n\tfor (; in < in_end; ) {\n\t\tuint32_t v;\n\t\tint pad = 0;\n\n\t\tv  = *in++ << 16;\n\t\tif (in < in_end) {\n\t\t\tv |= *in++ << 8;\n\t\t}\n\t\telse {\n\t\t\t++pad;\n\t\t}\n\t\tif (in < in_end) {\n\t\t\tv |= *in++ << 0;\n\t\t}\n\t\telse {\n\t\t\t++pad;\n\t\t}\n\n\t\t*out++ = b64_table[v >> 18 & 0x3f];\n\t\t*out++ = b64_table[v >> 12 & 0x3f];\n\t\t*out++ = (pad >= 2) ? '=' : b64_table[v >> 6 & 0x3f];\n\t\t*out++ = (pad >= 1) ? '=' : b64_table[v >> 0 & 0x3f];\n\t}\n\n\t*olen = out - o;\n\n\treturn 0;\n}\n\n\n/**\n * Base-64 url encode a buffer (without padding)\n *\n * @param in   Input buffer\n * @param ilen Length of input buffer\n * @param out  Output buffer\n * @param olen Size of output buffer, actual written on return\n *\n * @return 0 if success, otherwise errorcode\n */\nint base64url_encode(const uint8_t *in, size_t ilen, char *out, size_t *olen)\n{\n\tif (!in || !out || !olen)\n\t\treturn EINVAL;\n\n\tconst uint8_t *in_end = in + ilen;\n\tconst char *o = out;\n\n\tif (*olen < 4 * ((ilen+2)/3))\n\t\treturn EOVERFLOW;\n\n\tfor (; in < in_end; ) {\n\t\tuint32_t v;\n\t\tint pad = 0;\n\n\t\tv  = *in++ << 16;\n\t\tif (in < in_end) {\n\t\t\tv |= *in++ << 8;\n\t\t}\n\t\telse {\n\t\t\t++pad;\n\t\t}\n\t\tif (in < in_end) {\n\t\t\tv |= *in++ << 0;\n\t\t}\n\t\telse {\n\t\t\t++pad;\n\t\t}\n\n\t\t*out++ = b64url_table[v >> 18 & 0x3f];\n\t\t*out++ = b64url_table[v >> 12 & 0x3f];\n\t\tif (pad < 2)\n\t\t\t*out++ = b64url_table[v >> 6 & 0x3f];\n\t\tif (pad < 1)\n\t\t\t*out++ = b64url_table[v >> 0 & 0x3f];\n\t}\n\n\t*olen = out - o;\n\n\treturn 0;\n}\n\n\nint base64_print(struct re_printf *pf, const uint8_t *ptr, size_t len)\n{\n\tchar buf[256];\n\n\tif (!pf || !ptr)\n\t\treturn EINVAL;\n\n\twhile (len > 0) {\n\t\tsize_t l, sz = sizeof(buf);\n\t\tint err;\n\n\t\tl = min(len, 3 * (sizeof(buf)/4));\n\n\t\terr = base64_encode(ptr, l, buf, &sz);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = pf->vph(buf, sz, pf->arg);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tptr += l;\n\t\tlen -= l;\n\t}\n\n\treturn 0;\n}\n\n\n/* convert char -> 6-bit value */\nstatic inline uint32_t b64val(char c)\n{\n\tif ('A' <= c && c <= 'Z')\n\t\treturn c - 'A' + 0;\n\telse if ('a' <= c && c <= 'z')\n\t\treturn c - 'a' + 26;\n\telse if ('0' <= c && c <= '9')\n\t\treturn c - '0' + 52;\n\telse if ('+' == c || '-' == c)\n\t\treturn 62;\n\telse if ('/' == c || '_' == c)\n\t\treturn 63;\n\telse if ('=' == c)\n\t\treturn 1<<24; /* special trick */\n\telse\n\t\treturn 0;\n}\n\n\n/**\n * Decode a Base-64 encoded string\n *\n * @param in   Input buffer\n * @param ilen Length of input buffer\n * @param out  Output buffer\n * @param olen Size of output buffer, actual written on return\n *\n * @return 0 if success, otherwise errorcode\n */\nint base64_decode(const char *in, size_t ilen, uint8_t *out, size_t *olen)\n{\n\tif (!in || !out || !olen)\n\t\treturn EINVAL;\n\n\tconst char *in_end = in + ilen;\n\tconst uint8_t *o = out;\n\n\tif (*olen < 3 * (ilen/4))\n\t\treturn EOVERFLOW;\n\n\tfor (;in+1 < in_end; ) {\n\t\tuint32_t v;\n\n\t\tv  = b64val(*in++) << 18;\n\t\tv |= b64val(*in++) << 12;\n\n\t\tif (in < in_end)\n\t\t\tv |= b64val(*in++) << 6;\n\t\telse\n\t\t\tv |= (1<<24) << 6; /* padding fallback */\n\n\t\tif (in < in_end)\n\t\t\tv |= b64val(*in++) << 0;\n\t\telse\n\t\t\tv |= (1<<24) << 0; /* padding fallback */\n\n\t\t*out++ = v>>16;\n\t\tif (!(v & (1<<30)))\n\t\t\t*out++ = (v>>8) & 0xff;\n\t\tif (!(v & (1<<24)))\n\t\t\t*out++ = (v>>0) & 0xff;\n\t}\n\n\t*olen = out - o;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/bfcp/attr.c",
    "content": "/**\n * @file bfcp/attr.c BFCP Attributes\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_bfcp.h>\n#include \"bfcp.h\"\n\n\nenum {\n\tBFCP_ATTR_HDR_SIZE = 2,\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct bfcp_attr *attr = arg;\n\n\tswitch (attr->type) {\n\n\tcase BFCP_ERROR_CODE:\n\t\tmem_deref(attr->v.errcode.details);\n\t\tbreak;\n\n\tcase BFCP_ERROR_INFO:\n\tcase BFCP_PART_PROV_INFO:\n\tcase BFCP_STATUS_INFO:\n\tcase BFCP_USER_DISP_NAME:\n\tcase BFCP_USER_URI:\n\t\tmem_deref(attr->v.str);\n\t\tbreak;\n\n\tcase BFCP_SUPPORTED_ATTRS:\n\t\tmem_deref(attr->v.supattr.attrv);\n\t\tbreak;\n\n\tcase BFCP_SUPPORTED_PRIMS:\n\t\tmem_deref(attr->v.supprim.primv);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\tlist_flush(&attr->attrl);\n\tlist_unlink(&attr->le);\n}\n\n\nstatic int attr_encode(struct mbuf *mb, bool mand, enum bfcp_attrib type,\n\t\t       const void *v)\n{\n\tconst struct bfcp_reqstatus *reqstatus = v;\n\tconst struct bfcp_errcode *errcode = v;\n\tconst struct bfcp_supattr *supattr = v;\n\tconst struct bfcp_supprim *supprim = v;\n\tconst enum bfcp_priority *priority = v;\n\tconst uint16_t *u16 = v;\n\tsize_t start, i;\n\tuint8_t len;\n\tint err;\n\n\tstart = mb->pos;\n\tmb->pos += BFCP_ATTR_HDR_SIZE;\n\n\tswitch (type) {\n\n\tcase BFCP_BENEFICIARY_ID:\n\tcase BFCP_FLOOR_ID:\n\tcase BFCP_FLOOR_REQUEST_ID:\n\tcase BFCP_BENEFICIARY_INFO:\n\tcase BFCP_FLOOR_REQ_INFO:\n\tcase BFCP_REQUESTED_BY_INFO:\n\tcase BFCP_FLOOR_REQ_STATUS:\n\tcase BFCP_OVERALL_REQ_STATUS:\n\t\terr = mbuf_write_u16(mb, htons(*u16));\n\t\tbreak;\n\n\tcase BFCP_PRIORITY:\n\t\terr  = mbuf_write_u8(mb, *priority << 5);\n\t\terr |= mbuf_write_u8(mb, 0x00);\n\t\tbreak;\n\n\tcase BFCP_REQUEST_STATUS:\n\t\terr  = mbuf_write_u8(mb, reqstatus->status);\n\t\terr |= mbuf_write_u8(mb, reqstatus->qpos);\n\t\tbreak;\n\n\tcase BFCP_ERROR_CODE:\n\t\terr = mbuf_write_u8(mb, errcode->code);\n\t\tif (errcode->details && errcode->len)\n\t\t\terr |= mbuf_write_mem(mb, errcode->details,\n\t\t\t\t\t      errcode->len);\n\t\tbreak;\n\n\tcase BFCP_ERROR_INFO:\n\tcase BFCP_PART_PROV_INFO:\n\tcase BFCP_STATUS_INFO:\n\tcase BFCP_USER_DISP_NAME:\n\tcase BFCP_USER_URI:\n\t\terr = mbuf_write_str(mb, v);\n\t\tbreak;\n\n\tcase BFCP_SUPPORTED_ATTRS:\n\t\tfor (i=0, err=0; i<supattr->attrc; i++)\n\t\t\terr |= mbuf_write_u8(mb, supattr->attrv[i] << 1);\n\t\tbreak;\n\n\tcase BFCP_SUPPORTED_PRIMS:\n\t\tfor (i=0, err=0; i<supprim->primc; i++)\n\t\t\terr |= mbuf_write_u8(mb, supprim->primv[i]);\n\t\tbreak;\n\n\tdefault:\n\t\terr = EINVAL;\n\t\tbreak;\n\t}\n\n\t/* header */\n\tlen = (uint8_t)(mb->pos - start);\n\n\tmb->pos = start;\n\terr |= mbuf_write_u8(mb, (type<<1) | (mand ? 1 : 0));\n\terr |= mbuf_write_u8(mb, len);\n\tmb->pos += (size_t)(len - BFCP_ATTR_HDR_SIZE);\n\n\t/* padding */\n\twhile ((mb->pos - start) & 0x03)\n\t\terr |= mbuf_write_u8(mb, 0x00);\n\n\treturn err;\n}\n\n\n/**\n * Encode BFCP Attributes with variable arguments\n *\n * @param mb    Mbuf to encode into\n * @param attrc Number of attributes\n * @param ap    Variable argument of attributes\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_attrs_vencode(struct mbuf *mb, unsigned attrc, va_list *ap)\n{\n\tunsigned i;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\tfor (i=0; i<attrc; i++) {\n\n\t\tint  type     = va_arg(*ap, int);\n\t\tunsigned subc = va_arg(*ap, unsigned);\n\t\tconst void *v = va_arg(*ap, const void *);\n\t\tsize_t start, len;\n\t\tint err;\n\n\t\tif (!v)\n\t\t\tcontinue;\n\n\t\tstart = mb->pos;\n\n\t\tif (type == BFCP_ENCODE_HANDLER) {\n\n\t\t\tconst struct bfcp_encode *enc = v;\n\n\t\t\tif (enc->ench) {\n\t\t\t\terr = enc->ench(mb, enc->arg);\n\t\t\t\tif (err)\n\t\t\t\t\treturn err;\n\t\t\t}\n\n\t\t\tcontinue;\n\t\t}\n\n\t\terr = attr_encode(mb, type>>7, type & 0x7f, v);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (subc == 0)\n\t\t\tcontinue;\n\n\t\terr = bfcp_attrs_vencode(mb, subc, ap);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\t/* update total length for grouped attributes */\n\t\tlen = mb->pos - start;\n\n\t\tmb->pos = start + 1;\n\t\terr = mbuf_write_u8(mb, (uint8_t)len);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos += (len - BFCP_ATTR_HDR_SIZE);\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Encode BFCP Attributes\n *\n * @param mb      Mbuf to encode into\n * @param attrc   Number of attributes\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_attrs_encode(struct mbuf *mb, unsigned attrc, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, attrc);\n\terr = bfcp_attrs_vencode(mb, attrc, &ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\nstatic int attr_decode(struct bfcp_attr **attrp, struct mbuf *mb,\n\t\t       struct bfcp_unknown_attr *uma)\n{\n\tstruct bfcp_attr *attr;\n\tunion bfcp_union *v;\n\tsize_t i, start, len;\n\tint err = 0;\n\tuint8_t b;\n\n\tif (mbuf_get_left(mb) < BFCP_ATTR_HDR_SIZE)\n\t\treturn EBADMSG;\n\n\tattr = mem_zalloc(sizeof(*attr), destructor);\n\tif (!attr)\n\t\treturn ENOMEM;\n\n\tstart = mb->pos;\n\n\tb = mbuf_read_u8(mb);\n\tattr->type = b >> 1;\n\tattr->mand = b & 1;\n\tlen = mbuf_read_u8(mb);\n\n\tif (len < BFCP_ATTR_HDR_SIZE)\n\t\tgoto badmsg;\n\n\tlen -= BFCP_ATTR_HDR_SIZE;\n\n\tif (mbuf_get_left(mb) < len)\n\t\tgoto badmsg;\n\n\tv = &attr->v;\n\n\tswitch (attr->type) {\n\n\tcase BFCP_BENEFICIARY_ID:\n\tcase BFCP_FLOOR_ID:\n\tcase BFCP_FLOOR_REQUEST_ID:\n\t\tif (len < 2)\n\t\t\tgoto badmsg;\n\n\t\tv->u16 = ntohs(mbuf_read_u16(mb));\n\t\tbreak;\n\n\tcase BFCP_PRIORITY:\n\t\tif (len < 2)\n\t\t\tgoto badmsg;\n\n\t\tv->priority = mbuf_read_u8(mb) >> 5;\n\t\t(void)mbuf_read_u8(mb);\n\t\tbreak;\n\n\tcase BFCP_REQUEST_STATUS:\n\t\tif (len < 2)\n\t\t\tgoto badmsg;\n\n\t\tv->reqstatus.status = mbuf_read_u8(mb);\n\t\tv->reqstatus.qpos   = mbuf_read_u8(mb);\n\t\tbreak;\n\n\tcase BFCP_ERROR_CODE:\n\t\tif (len < 1)\n\t\t\tgoto badmsg;\n\n\t\tv->errcode.code = mbuf_read_u8(mb);\n\t\tv->errcode.len  = len - 1;\n\n\t\tif (v->errcode.len == 0)\n\t\t\tbreak;\n\n\t\tv->errcode.details = mem_alloc(v->errcode.len, NULL);\n\t\tif (!v->errcode.details) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto error;\n\t\t}\n\n\t\t(void)mbuf_read_mem(mb, v->errcode.details,\n\t\t\t\t    v->errcode.len);\n\t\tbreak;\n\n\tcase BFCP_ERROR_INFO:\n\tcase BFCP_PART_PROV_INFO:\n\tcase BFCP_STATUS_INFO:\n\tcase BFCP_USER_DISP_NAME:\n\tcase BFCP_USER_URI:\n\t\terr = mbuf_strdup(mb, &v->str, len);\n\t\tbreak;\n\n\tcase BFCP_SUPPORTED_ATTRS:\n\t\tv->supattr.attrc = len;\n\t\tv->supattr.attrv = mem_alloc(len*sizeof(*v->supattr.attrv),\n\t\t\t\t\t     NULL);\n\t\tif (!v->supattr.attrv) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto error;\n\t\t}\n\n\t\tfor (i=0; i<len; i++)\n\t\t\tv->supattr.attrv[i] = mbuf_read_u8(mb) >> 1;\n\t\tbreak;\n\n\tcase BFCP_SUPPORTED_PRIMS:\n\t\tv->supprim.primc = len;\n\t\tv->supprim.primv = mem_alloc(len * sizeof(*v->supprim.primv),\n\t\t\t\t\t     NULL);\n\t\tif (!v->supprim.primv) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto error;\n\t\t}\n\n\t\tfor (i=0; i<len; i++)\n\t\t\tv->supprim.primv[i] = mbuf_read_u8(mb);\n\t\tbreak;\n\n\t\t/* grouped attributes */\n\n\tcase BFCP_BENEFICIARY_INFO:\n\tcase BFCP_FLOOR_REQ_INFO:\n\tcase BFCP_REQUESTED_BY_INFO:\n\tcase BFCP_FLOOR_REQ_STATUS:\n\tcase BFCP_OVERALL_REQ_STATUS:\n\t\tif (len < 2)\n\t\t\tgoto badmsg;\n\n\t\tv->u16 = ntohs(mbuf_read_u16(mb));\n\t\terr = bfcp_attrs_decode(&attr->attrl, mb, len - 2, uma);\n\t\tbreak;\n\n\tdefault:\n\t\tmb->pos += len;\n\n\t\tif (!attr->mand)\n\t\t\tbreak;\n\n\t\tif (uma && uma->typec < RE_ARRAY_SIZE(uma->typev))\n\t\t\tuma->typev[uma->typec++] = attr->type<<1;\n\t\tbreak;\n\t}\n\n\tif (err)\n\t\tgoto error;\n\n\t/* padding */\n\twhile (((mb->pos - start) & 0x03) && mbuf_get_left(mb))\n\t\t++mb->pos;\n\n\t*attrp = attr;\n\n\treturn 0;\n\n badmsg:\n\terr = EBADMSG;\n error:\n\tmem_deref(attr);\n\n\treturn err;\n}\n\n\nint bfcp_attrs_decode(struct list *attrl, struct mbuf *mb, size_t len,\n\t\t      struct bfcp_unknown_attr *uma)\n{\n\tint err = 0;\n\tsize_t end;\n\n\tif (!attrl || !mb || mbuf_get_left(mb) < len)\n\t\treturn EINVAL;\n\n\tend     = mb->end;\n\tmb->end = mb->pos + len;\n\n\twhile (mbuf_get_left(mb) >= BFCP_ATTR_HDR_SIZE) {\n\n\t\tstruct bfcp_attr *attr;\n\n\t\terr = attr_decode(&attr, mb, uma);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tlist_append(attrl, &attr->le, attr);\n\t}\n\n\tmb->end = end;\n\n\treturn err;\n}\n\n\nstruct bfcp_attr *bfcp_attrs_find(const struct list *attrl,\n\t\t\t\t  enum bfcp_attrib type)\n{\n\tstruct le *le = list_head(attrl);\n\n\twhile (le) {\n\t\tstruct bfcp_attr *attr = le->data;\n\n\t\tle = le->next;\n\n\t\tif (attr->type == type)\n\t\t\treturn attr;\n\t}\n\n\treturn NULL;\n}\n\n\nstruct bfcp_attr *bfcp_attrs_apply(const struct list *attrl,\n\t\t\t\t   bfcp_attr_h *h, void *arg)\n{\n\tstruct le *le = list_head(attrl);\n\n\twhile (le) {\n\t\tstruct bfcp_attr *attr = le->data;\n\n\t\tle = le->next;\n\n\t\tif (h && h(attr, arg))\n\t\t\treturn attr;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Get a BFCP sub-attribute from a BFCP attribute\n *\n * @param attr BFCP attribute\n * @param type Attribute type\n *\n * @return Matching BFCP attribute if found, otherwise NULL\n */\nstruct bfcp_attr *bfcp_attr_subattr(const struct bfcp_attr *attr,\n\t\t\t\t    enum bfcp_attrib type)\n{\n\tif (!attr)\n\t\treturn NULL;\n\n\treturn bfcp_attrs_find(&attr->attrl, type);\n}\n\n\n/**\n * Apply a function handler to all sub-attributes in a BFCP attribute\n *\n * @param attr BFCP attribute\n * @param h    Handler\n * @param arg  Handler argument\n *\n * @return BFCP attribute returned by handler, or NULL\n */\nstruct bfcp_attr *bfcp_attr_subattr_apply(const struct bfcp_attr *attr,\n\t\t\t\t\t  bfcp_attr_h *h, void *arg)\n{\n\tif (!attr)\n\t\treturn NULL;\n\n\treturn bfcp_attrs_apply(&attr->attrl, h, arg);\n}\n\n\n/**\n * Print a BFCP attribute\n *\n * @param pf   Print function\n * @param attr BFCP attribute\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_attr_print(struct re_printf *pf, const struct bfcp_attr *attr)\n{\n\tconst union bfcp_union *v;\n\tsize_t i;\n\tint err;\n\n\tif (!attr)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"%c%-28s\", attr->mand ? '*' : ' ',\n\t\t\t bfcp_attr_name(attr->type));\n\n\tv = &attr->v;\n\n\tswitch (attr->type) {\n\n\tcase BFCP_BENEFICIARY_ID:\n\tcase BFCP_FLOOR_ID:\n\tcase BFCP_FLOOR_REQUEST_ID:\n\t\terr |= re_hprintf(pf, \"%u\", v->u16);\n\t\tbreak;\n\n\tcase BFCP_PRIORITY:\n\t\terr |= re_hprintf(pf, \"%d\", v->priority);\n\t\tbreak;\n\n\tcase BFCP_REQUEST_STATUS:\n\t\terr |= re_hprintf(pf, \"%s (%d), qpos=%u\",\n\t\t\t\t  bfcp_reqstatus_name(v->reqstatus.status),\n\t\t\t\t  v->reqstatus.status,\n\t\t\t\t  v->reqstatus.qpos);\n\t\tbreak;\n\n\tcase BFCP_ERROR_CODE:\n\t\terr |= re_hprintf(pf, \"%d (%s)\", v->errcode.code,\n\t\t\t\t  bfcp_errcode_name(v->errcode.code));\n\n\t\tif (v->errcode.code == BFCP_UNKNOWN_MAND_ATTR) {\n\n\t\t\tfor (i=0; i<v->errcode.len; i++) {\n\n\t\t\t\tuint8_t type = v->errcode.details[i] >> 1;\n\n\t\t\t\terr |= re_hprintf(pf, \" %s\",\n\t\t\t\t\t\t  bfcp_attr_name(type));\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase BFCP_ERROR_INFO:\n\tcase BFCP_PART_PROV_INFO:\n\tcase BFCP_STATUS_INFO:\n\tcase BFCP_USER_DISP_NAME:\n\tcase BFCP_USER_URI:\n\t\terr |= re_hprintf(pf, \"\\\"%s\\\"\", v->str);\n\t\tbreak;\n\n\tcase BFCP_SUPPORTED_ATTRS:\n\t\terr |= re_hprintf(pf, \"%zu:\", v->supattr.attrc);\n\n\t\tfor (i=0; i<v->supattr.attrc; i++) {\n\n\t\t\tconst enum bfcp_attrib type = v->supattr.attrv[i];\n\n\t\t\terr |= re_hprintf(pf, \" %s\", bfcp_attr_name(type));\n\t\t}\n\t\tbreak;\n\n\tcase BFCP_SUPPORTED_PRIMS:\n\t\terr |= re_hprintf(pf, \"%zu:\", v->supprim.primc);\n\n\t\tfor (i=0; i<v->supprim.primc; i++) {\n\n\t\t\tconst enum bfcp_prim prim = v->supprim.primv[i];\n\n\t\t\terr |= re_hprintf(pf, \" %s\", bfcp_prim_name(prim));\n\t\t}\n\t\tbreak;\n\n\t\t/* Grouped Attributes */\n\n\tcase BFCP_BENEFICIARY_INFO:\n\t\terr |= re_hprintf(pf, \"beneficiary-id=%u\", v->beneficiaryid);\n\t\tbreak;\n\n\tcase BFCP_FLOOR_REQ_INFO:\n\t\terr |= re_hprintf(pf, \"floor-request-id=%u\", v->floorreqid);\n\t\tbreak;\n\n\tcase BFCP_REQUESTED_BY_INFO:\n\t\terr |= re_hprintf(pf, \"requested-by-id=%u\", v->reqbyid);\n\t\tbreak;\n\n\tcase BFCP_FLOOR_REQ_STATUS:\n\t\terr |= re_hprintf(pf, \"floor-id=%u\", v->floorid);\n\t\tbreak;\n\n\tcase BFCP_OVERALL_REQ_STATUS:\n\t\terr |= re_hprintf(pf, \"floor-request-id=%u\", v->floorreqid);\n\t\tbreak;\n\n\tdefault:\n\t\terr |= re_hprintf(pf, \"???\");\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nint bfcp_attrs_print(struct re_printf *pf, const struct list *attrl,\n\t\t     unsigned level)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tfor (le=list_head(attrl); le; le=le->next) {\n\n\t\tconst struct bfcp_attr *attr = le->data;\n\t\tunsigned i;\n\n\t\tfor (i=0; i<level; i++)\n\t\t\terr |= re_hprintf(pf, \"    \");\n\n\t\terr |= re_hprintf(pf, \"%H\\n\", bfcp_attr_print, attr);\n\t\terr |= bfcp_attrs_print(pf, &attr->attrl, level + 1);\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Get the BFCP attribute name\n *\n * @param type BFCP attribute type\n *\n * @return String with BFCP attribute name\n */\nconst char *bfcp_attr_name(enum bfcp_attrib type)\n{\n\tswitch (type) {\n\n\tcase BFCP_BENEFICIARY_ID:     return \"BENEFICIARY-ID\";\n\tcase BFCP_FLOOR_ID:           return \"FLOOR-ID\";\n\tcase BFCP_FLOOR_REQUEST_ID:   return \"FLOOR-REQUEST-ID\";\n\tcase BFCP_PRIORITY:           return \"PRIORITY\";\n\tcase BFCP_REQUEST_STATUS:     return \"REQUEST-STATUS\";\n\tcase BFCP_ERROR_CODE:         return \"ERROR-CODE\";\n\tcase BFCP_ERROR_INFO:         return \"ERROR-INFO\";\n\tcase BFCP_PART_PROV_INFO:     return \"PARTICIPANT-PROVIDED-INFO\";\n\tcase BFCP_STATUS_INFO:        return \"STATUS-INFO\";\n\tcase BFCP_SUPPORTED_ATTRS:    return \"SUPPORTED-ATTRIBUTES\";\n\tcase BFCP_SUPPORTED_PRIMS:    return \"SUPPORTED-PRIMITIVES\";\n\tcase BFCP_USER_DISP_NAME:     return \"USER-DISPLAY-NAME\";\n\tcase BFCP_USER_URI:           return \"USER-URI\";\n\tcase BFCP_BENEFICIARY_INFO:   return \"BENEFICIARY-INFORMATION\";\n\tcase BFCP_FLOOR_REQ_INFO:     return \"FLOOR-REQUEST-INFORMATION\";\n\tcase BFCP_REQUESTED_BY_INFO:  return \"REQUESTED-BY-INFORMATION\";\n\tcase BFCP_FLOOR_REQ_STATUS:   return \"FLOOR-REQUEST-STATUS\";\n\tcase BFCP_OVERALL_REQ_STATUS: return \"OVERALL-REQUEST-STATUS\";\n\tdefault:                      return \"???\";\n\t}\n}\n\n\n/**\n * Get the BFCP Request status name\n *\n * @param status Request status\n *\n * @return String with BFCP Request status name\n */\nconst char *bfcp_reqstatus_name(enum bfcp_reqstat status)\n{\n\tswitch (status) {\n\n\tcase BFCP_PENDING:   return \"Pending\";\n\tcase BFCP_ACCEPTED:  return \"Accepted\";\n\tcase BFCP_GRANTED:   return \"Granted\";\n\tcase BFCP_DENIED:    return \"Denied\";\n\tcase BFCP_CANCELLED: return \"Cancelled\";\n\tcase BFCP_RELEASED:  return \"Released\";\n\tcase BFCP_REVOKED:   return \"Revoked\";\n\tdefault:             return \"???\";\n\t}\n}\n\n\n/**\n * Get the BFCP Error code name\n *\n * @param code BFCP Error code\n *\n * @return String with error code\n */\nconst char *bfcp_errcode_name(enum bfcp_err code)\n{\n\tswitch (code) {\n\n\tcase BFCP_CONF_NOT_EXIST:\n\t\treturn \"Conference does not Exist\";\n\n\tcase BFCP_USER_NOT_EXIST:\n\t\treturn \"User does not Exist\";\n\n\tcase BFCP_UNKNOWN_PRIM:\n\t\treturn \"Unknown Primitive\";\n\n\tcase BFCP_UNKNOWN_MAND_ATTR:\n\t\treturn \"Unknown Mandatory Attribute\";\n\n\tcase BFCP_UNAUTH_OPERATION:\n\t\treturn \"Unauthorized Operation\";\n\n\tcase BFCP_INVALID_FLOOR_ID:\n\t\treturn \"Invalid Floor ID\";\n\n\tcase BFCP_FLOOR_REQ_ID_NOT_EXIST:\n\t\treturn \"Floor Request ID Does Not Exist\";\n\n\tcase BFCP_MAX_FLOOR_REQ_REACHED:\n\t\treturn \"You have Already Reached the Maximum Number \"\n\t\t       \"of Ongoing Floor Requests for this Floor\";\n\n\tcase BFCP_USE_TLS:\n\t\treturn \"Use TLS\";\n\n\tcase BFCP_PARSE_ERROR:\n\t\treturn \"Unable to Parse Message\";\n\n\tcase BFCP_USE_DTLS:\n\t\treturn \"Use DTLS\";\n\n\tcase BFCP_UNSUPPORTED_VERSION:\n\t\treturn \"Unsupported Version\";\n\n\tcase BFCP_BAD_LENGTH:\n\t\treturn \"Incorrect Message Length\";\n\n\tcase BFCP_GENERIC_ERROR:\n\t\treturn \"Generic Error\";\n\n\tdefault:\n\t\treturn \"???\";\n\t}\n}\n"
  },
  {
    "path": "src/bfcp/bfcp.h",
    "content": "/**\n * @file bfcp.h Internal interface to Binary Floor Control Protocol (BFCP)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\nstruct bfcp_strans {\n\tenum bfcp_prim prim;\n\tuint32_t confid;\n\tuint16_t tid;\n\tuint16_t userid;\n};\n\nstruct bfcp_conn {\n\tstruct bfcp_strans st;\n\tstruct list ctransl;\n\tstruct tmr tmr1;\n\tstruct tmr tmr2;\n\tstruct udp_sock *us;\n\tstruct tcp_sock *ts;\n\tstruct tcp_conn *tc;\n\tstruct sa sa_peer;\n\tstruct mbuf *mb;\n\tbfcp_conn_h *connh;\n\tbfcp_estab_h *estabh;\n\tbfcp_recv_h *recvh;\n\tbfcp_close_h *closeh;\n\tvoid *arg;\n\tenum bfcp_transp tp;\n\tunsigned txc;\n\tuint16_t tid;\n};\n\n\n/* attributes */\nint bfcp_attrs_decode(struct list *attrl, struct mbuf *mb, size_t len,\n\t\t      struct bfcp_unknown_attr *uma);\nstruct bfcp_attr *bfcp_attrs_find(const struct list *attrl,\n\t\t\t\t  enum bfcp_attrib type);\nstruct bfcp_attr *bfcp_attrs_apply(const struct list *attrl,\n\t\t\t\t   bfcp_attr_h *h, void *arg);\nint bfcp_attrs_print(struct re_printf *pf, const struct list *attrl,\n\t\t     unsigned level);\n\n\n/* connection */\nint bfcp_send(struct bfcp_conn *bc, const struct sa *dst, struct mbuf *mb);\n\n\n/* request */\nbool bfcp_handle_response(struct bfcp_conn *bc, const struct bfcp_msg *msg);\nint  bfcp_vrequest(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,\n\t\t   enum bfcp_prim prim, uint32_t confid, uint16_t userid,\n\t\t   bfcp_resp_h *resph, void *arg, unsigned attrc, va_list *ap);\n"
  },
  {
    "path": "src/bfcp/conn.c",
    "content": "/**\n * @file bfcp/conn.c BFCP Connection\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_udp.h>\n#include <re_tcp.h>\n#include <re_tmr.h>\n#include <re_bfcp.h>\n#include \"bfcp.h\"\n\n\nstatic void destructor(void *arg)\n{\n\tstruct bfcp_conn *bc = arg;\n\n\tlist_flush(&bc->ctransl);\n\ttmr_cancel(&bc->tmr1);\n\ttmr_cancel(&bc->tmr2);\n\tmem_deref(bc->us);\n\tmem_deref(bc->tc);\n\tmem_deref(bc->ts);\n\tmem_deref(bc->mb);\n}\n\n\nstatic bool strans_cmp(const struct bfcp_strans *st,\n\t\t       const struct bfcp_msg *msg)\n{\n\tif (st->tid != msg->tid)\n\t\treturn false;\n\n\tif (st->prim != msg->prim)\n\t\treturn false;\n\n\tif (st->confid != msg->confid)\n\t\treturn false;\n\n\tif (st->userid != msg->userid)\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstatic void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct bfcp_conn *bc = arg;\n\tstruct bfcp_msg *msg;\n\tint err;\n\n\terr = bfcp_msg_decode(&msg, mb);\n\tif (err)\n\t\treturn;\n\n\tmsg->src = *src;\n\n\tif (bfcp_handle_response(bc, msg))\n\t\tgoto out;\n\n\tif (bc->mb && strans_cmp(&bc->st, msg)) {\n\t\t(void)bfcp_send(bc, &msg->src, bc->mb);\n\t\tgoto out;\n\t}\n\n\tif (bc->recvh)\n\t\tbc->recvh(msg, bc->arg);\n\nout:\n\tmem_deref(msg);\n}\n\n\nstatic void tcp_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct bfcp_conn *bc = arg;\n\tstruct bfcp_msg *msg;\n\tint err;\n\n\twhile (mb->pos < mb->end) {\n\t\terr = bfcp_msg_decode(&msg, mb);\n\t\tif (err)\n\t\t\treturn;\n\n\t\tmsg->src = bc->sa_peer;\n\n\t\tif (bfcp_handle_response(bc, msg))\n\t\t\tgoto out;\n\n\t\tif (bc->mb && strans_cmp(&bc->st, msg)) {\n\t\t\t(void)bfcp_send(bc, &msg->src, bc->mb);\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (bc->recvh)\n\t\t\tbc->recvh(msg, bc->arg);\n\n\tout:\n\t\tmem_deref(msg);\n\t}\n}\n\n\nstatic void tcp_estab_handler(void *arg)\n{\n\tstruct bfcp_conn *bc = arg;\n\tif (bc->estabh)\n\t\tbc->estabh(bc->arg);\n}\n\n\nstatic void tcp_close_handler(int err, void *arg)\n{\n\tstruct bfcp_conn *bc = arg;\n\n\tbc->tc = NULL;\n\n\tif (bc->closeh)\n\t\tbc->closeh(err, bc->arg);\n}\n\n\nstatic void tcp_conn_handler(const struct sa *peer, void *arg)\n{\n\tstruct bfcp_conn *bc = arg;\n\n\tif (bc->connh) {\n\t\tbc->connh(peer, bc->arg);\n\t}\n\telse {\n\t\tint err;\n\t\tif (bc->tc) {\n\t\t\ttcp_reject(bc->ts);\n\t\t\treturn;\n\t\t}\n\n\t\terr = tcp_accept(&bc->tc, bc->ts, tcp_estab_handler,\n\t\t\t\t tcp_recv_handler, tcp_close_handler, bc);\n\n\t\tif (err == 0)\n\t\t\tbc->sa_peer = *peer;\n\t}\n}\n\n\n/**\n * Create BFCP connection. For TCP, creates a listening socket for incoming\n * connections.\n *\n * @param bcp   Pointer to BFCP connection\n * @param tp    BFCP Transport type\n * @param laddr Optional listening address/port\n * @param tls   TLS Context (optional)\n * @param connh Incoming connection handler (optional)\n * @param estabh Connection established handler (optional)\n * @param recvh Receive handler\n * @param closeh Connection closed handler (optional)\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_listen(struct bfcp_conn **bcp, enum bfcp_transp tp, struct sa *laddr,\n\t\tstruct tls *tls, bfcp_conn_h *connh, bfcp_estab_h *estabh,\n\t\tbfcp_recv_h *recvh, bfcp_close_h *closeh, void *arg)\n{\n\tstruct bfcp_conn *bc;\n\tint err;\n\t(void)tls;\n\n\tif (!bcp)\n\t\treturn EINVAL;\n\n\tbc = mem_zalloc(sizeof(*bc), destructor);\n\tif (!bc)\n\t\treturn ENOMEM;\n\n\tbc->tp    = tp;\n\tbc->connh = connh;\n\tbc->estabh = estabh;\n\tbc->recvh = recvh;\n\tbc->closeh = closeh;\n\tbc->arg   = arg;\n\n\tswitch (bc->tp) {\n\n\tcase BFCP_UDP:\n\t\terr = udp_listen(&bc->us, laddr, udp_recv_handler, bc);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (laddr) {\n\t\t\terr = udp_local_get(bc->us, laddr);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t\tbreak;\n\n\tcase BFCP_TCP:\n\t\terr = tcp_listen(&bc->ts, laddr, tcp_conn_handler, bc);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (laddr) {\n\t\t\terr = tcp_local_get(bc->ts, laddr);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\terr = ENOSYS;\n\t\tgoto out;\n\t}\n\nout:\n\tif (err)\n\t\tmem_deref(bc);\n\telse\n\t\t*bcp = bc;\n\n\treturn err;\n}\n\n\n/**\n * Create BFCP connection. For TCP, creates an outgoing connection.\n *\n * @param bcp   Pointer to BFCP connection\n * @param tp    BFCP Transport type\n * @param laddr Optional local address/port\n * @param peer  Remote address/port\n * @param estabh Connection established handler (optional)\n * @param recvh Receive handler\n * @param closeh Connection closed handler (optional)\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_connect(struct bfcp_conn **bcp, enum bfcp_transp tp,\n\t\t struct sa *laddr, const struct sa *peer, bfcp_estab_h *estabh,\n\t\t bfcp_recv_h *recvh, bfcp_close_h *closeh, void *arg)\n{\n\tstruct bfcp_conn *bc;\n\tint err;\n\n\tif (!bcp)\n\t\treturn EINVAL;\n\n\tbc = mem_zalloc(sizeof(*bc), destructor);\n\tif (!bc)\n\t\treturn ENOMEM;\n\n\tbc->tp    = tp;\n\tbc->estabh = estabh;\n\tbc->recvh = recvh;\n\tbc->closeh = closeh;\n\tbc->arg   = arg;\n\n\tswitch (bc->tp) {\n\n\tcase BFCP_UDP:\n\t\terr = udp_open(&bc->us, laddr ? sa_af(laddr) : AF_UNSPEC);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tudp_handler_set(bc->us, udp_recv_handler, bc);\n\n\t\tif (peer) {\n\t\t\terr = udp_connect(bc->us, peer);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n\t\t\tbc->sa_peer = *peer;\n\t\t}\n\n\t\terr = udp_thread_attach(bc->us);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (laddr) {\n\t\t\terr = udp_local_get(bc->us, laddr);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t\tbreak;\n\n\tcase BFCP_TCP:\n\t\terr = tcp_connect(&bc->tc, peer, tcp_estab_handler,\n\t\t\t   tcp_recv_handler, tcp_close_handler, bc);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tbc->sa_peer = *peer;\n\n\t\tif (laddr) {\n\t\t\terr = tcp_conn_local_get(bc->tc, laddr);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\terr = ENOSYS;\n\t\tgoto out;\n\t}\n\nout:\n\tif (err)\n\t\tmem_deref(bc);\n\telse\n\t\t*bcp = bc;\n\n\treturn err;\n}\n\n\n/**\n * Accept pending inbound TCP connection for the BFCP connection.\n * Only one TCP connection is supported.\n *\n * @param bc    Pointer to BFCP connection\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_accept(struct bfcp_conn *bc)\n{\n\tif (!bc)\n\t\treturn EINVAL;\n\n\tif (bc->tp != BFCP_TCP)\n\t\treturn ENOSYS;\n\n\tif (bc->tc)\n\t\treturn EALREADY;\n\n\treturn tcp_accept(&bc->tc, bc->ts, tcp_estab_handler,\n\t\ttcp_recv_handler, tcp_close_handler, bc);\n}\n\n\n/**\n * Reject pending inbound TCP connection for the BFCP connection.\n *\n * @param bc    Pointer to BFCP connection\n */\nvoid bfcp_reject(struct bfcp_conn *bc)\n{\n\tif (!bc || bc->tp != BFCP_TCP)\n\t\treturn;\n\n\ttcp_reject(bc->ts);\n}\n\n\nint bfcp_send(struct bfcp_conn *bc, const struct sa *dst, struct mbuf *mb)\n{\n\tif (!bc || !mb)\n\t\treturn EINVAL;\n\n\tswitch (bc->tp) {\n\n\tcase BFCP_UDP:\n\t\tif (!dst)\n\t\t\treturn EINVAL;\n\t\treturn udp_send(bc->us, dst, mb);\n\n\tcase BFCP_TCP:\n\t\treturn tcp_send(bc->tc, mb);\n\n\tdefault:\n\t\treturn ENOSYS;\n\t}\n}\n\n\n/**\n * Returns socket used to send messages over BFCP connection. For TCP,\n * TCP connection socket is returned.\n *\n * @param bc    Pointer to BFCP connection\n *\n * @return Pointer to socket/connection or NULL.\n */\nvoid *bfcp_sock(const struct bfcp_conn *bc)\n{\n\tif (!bc)\n\t\treturn NULL;\n\n\tswitch (bc->tp) {\n\n\tcase BFCP_UDP:\n\t\treturn bc->us;\n\n\tcase BFCP_TCP:\n\t\treturn bc->tc;\n\n\tdefault:\n\t\treturn NULL;\n\t}\n}\n"
  },
  {
    "path": "src/bfcp/msg.c",
    "content": "/**\n * @file bfcp/msg.c BFCP Message\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_tmr.h>\n#include <re_bfcp.h>\n#include \"bfcp.h\"\n\n\nenum {\n\tBFCP_HDR_SIZE = 12,\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct bfcp_msg *msg = arg;\n\n\tlist_flush(&msg->attrl);\n}\n\n\nstatic int hdr_encode(struct mbuf *mb, uint8_t ver, bool r,\n\t\t      enum bfcp_prim prim, uint16_t len, uint32_t confid,\n\t\t      uint16_t tid, uint16_t userid)\n{\n\tint err;\n\n\terr  = mbuf_write_u8(mb, (ver << 5) | ((r ? 1 : 0) << 4));\n\terr |= mbuf_write_u8(mb, prim);\n\terr |= mbuf_write_u16(mb, htons(len));\n\terr |= mbuf_write_u32(mb, htonl(confid));\n\terr |= mbuf_write_u16(mb, htons(tid));\n\terr |= mbuf_write_u16(mb, htons(userid));\n\n\treturn err;\n}\n\n\nstatic int hdr_decode(struct bfcp_msg *msg, struct mbuf *mb)\n{\n\tuint8_t b;\n\n\tif (mbuf_get_left(mb) < BFCP_HDR_SIZE)\n\t\treturn ENODATA;\n\n\tb = mbuf_read_u8(mb);\n\n\tmsg->ver    = b >> 5;\n\tmsg->r      = (b >> 4) & 1;\n\tmsg->f      = (b >> 3) & 1;\n\tmsg->prim   = mbuf_read_u8(mb);\n\tmsg->len    = ntohs(mbuf_read_u16(mb));\n\tmsg->confid = ntohl(mbuf_read_u32(mb));\n\tmsg->tid    = ntohs(mbuf_read_u16(mb));\n\tmsg->userid = ntohs(mbuf_read_u16(mb));\n\n\tif (msg->ver != BFCP_VER1 && msg->ver != BFCP_VER2)\n\t\treturn EBADMSG;\n\n\t/* fragmentation not supported */\n\tif (msg->f)\n\t\treturn ENOSYS;\n\n\tif (mbuf_get_left(mb) < (size_t)(4*msg->len))\n\t\treturn ENODATA;\n\n\treturn 0;\n}\n\n\n/**\n * Encode a BFCP message with variable arguments\n *\n * @param mb      Mbuf to encode into\n * @param ver     Protocol version\n * @param r       Transaction responder flag\n * @param prim    BFCP Primitive\n * @param confid  Conference ID\n * @param tid     Transaction ID\n * @param userid  User ID\n * @param attrc   Number of attributes\n * @param ap      Variable argument of attributes\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_msg_vencode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim,\n\t\t     uint32_t confid, uint16_t tid, uint16_t userid,\n\t\t     unsigned attrc, va_list *ap)\n{\n\tsize_t start, len;\n\tint err;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\tstart = mb->pos;\n\tmb->pos += BFCP_HDR_SIZE;\n\n\terr = bfcp_attrs_vencode(mb, attrc, ap);\n\tif (err)\n\t\treturn err;\n\n\t/* header */\n\tlen = mb->pos - start - BFCP_HDR_SIZE;\n\tmb->pos = start;\n\terr = hdr_encode(mb, ver, r, prim, (uint16_t)(len/4), confid, tid,\n\t\t\t userid);\n\tmb->pos += len;\n\n\treturn err;\n}\n\n\n/**\n * Encode a BFCP message\n *\n * @param mb      Mbuf to encode into\n * @param ver     Protocol version\n * @param r       Transaction responder flag\n * @param prim    BFCP Primitive\n * @param confid  Conference ID\n * @param tid     Transaction ID\n * @param userid  User ID\n * @param attrc   Number of attributes\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_msg_encode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim,\n\t\t    uint32_t confid, uint16_t tid, uint16_t userid,\n\t\t    unsigned attrc, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, attrc);\n\terr = bfcp_msg_vencode(mb, ver, r, prim, confid, tid, userid,\n\t\t\t       attrc, &ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Decode a BFCP message from a buffer\n *\n * @param msgp Pointer to allocated and decoded BFCP message\n * @param mb   Mbuf to decode from\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_msg_decode(struct bfcp_msg **msgp, struct mbuf *mb)\n{\n\tstruct bfcp_msg *msg;\n\tsize_t start;\n\tint err;\n\n\tif (!msgp || !mb)\n\t\treturn EINVAL;\n\n\tmsg = mem_zalloc(sizeof(*msg), destructor);\n\tif (!msg)\n\t\treturn ENOMEM;\n\n\tstart = mb->pos;\n\n\terr = hdr_decode(msg, mb);\n\tif (err) {\n\t\tmb->pos = start;\n\t\tgoto out;\n\t}\n\n\terr = bfcp_attrs_decode(&msg->attrl, mb, 4*msg->len, &msg->uma);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(msg);\n\telse\n\t\t*msgp = msg;\n\n\treturn err;\n}\n\n\n/**\n * Get a BFCP attribute from a BFCP message\n *\n * @param msg  BFCP message\n * @param type Attribute type\n *\n * @return Matching BFCP attribute if found, otherwise NULL\n */\nstruct bfcp_attr *bfcp_msg_attr(const struct bfcp_msg *msg,\n\t\t\t\tenum bfcp_attrib type)\n{\n\tif (!msg)\n\t\treturn NULL;\n\n\treturn bfcp_attrs_find(&msg->attrl, type);\n}\n\n\n/**\n * Apply a function handler to all attributes in a BFCP message\n *\n * @param msg  BFCP message\n * @param h    Handler\n * @param arg  Handler argument\n *\n * @return BFCP attribute returned by handler, or NULL\n */\nstruct bfcp_attr *bfcp_msg_attr_apply(const struct bfcp_msg *msg,\n\t\t\t\t      bfcp_attr_h *h, void *arg)\n{\n\tif (!msg)\n\t\treturn NULL;\n\n\treturn bfcp_attrs_apply(&msg->attrl, h, arg);\n}\n\n\n/**\n * Print a BFCP message\n *\n * @param pf  Print function\n * @param msg BFCP message\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_msg_print(struct re_printf *pf, const struct bfcp_msg *msg)\n{\n\tint err;\n\n\tif (!msg)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"%s (confid=%u tid=%u userid=%u)\\n\",\n\t\t\t bfcp_prim_name(msg->prim), msg->confid,\n\t\t\t msg->tid, msg->userid);\n\n\terr |= bfcp_attrs_print(pf, &msg->attrl, 0);\n\n\treturn err;\n}\n\n\n/**\n * Get the BFCP primitive name\n *\n * @param prim BFCP primitive\n *\n * @return String with BFCP primitive name\n */\nconst char *bfcp_prim_name(enum bfcp_prim prim)\n{\n\tswitch (prim) {\n\n\tcase BFCP_FLOOR_REQUEST:        return \"FloorRequest\";\n\tcase BFCP_FLOOR_RELEASE:        return \"FloorRelease\";\n\tcase BFCP_FLOOR_REQUEST_QUERY:  return \"FloorRequestQuery\";\n\tcase BFCP_FLOOR_REQUEST_STATUS: return \"FloorRequestStatus\";\n\tcase BFCP_USER_QUERY:           return \"UserQuery\";\n\tcase BFCP_USER_STATUS:          return \"UserStatus\";\n\tcase BFCP_FLOOR_QUERY:          return \"FloorQuery\";\n\tcase BFCP_FLOOR_STATUS:         return \"FloorStatus\";\n\tcase BFCP_CHAIR_ACTION:         return \"ChairAction\";\n\tcase BFCP_CHAIR_ACTION_ACK:     return \"ChairActionAck\";\n\tcase BFCP_HELLO:                return \"Hello\";\n\tcase BFCP_HELLO_ACK:            return \"HelloAck\";\n\tcase BFCP_ERROR:                return \"Error\";\n\tcase BFCP_FLOOR_REQ_STATUS_ACK: return \"FloorRequestStatusAck\";\n\tcase BFCP_FLOOR_STATUS_ACK:     return \"FloorStatusAck\";\n\tcase BFCP_GOODBYE:              return \"Goodbye\";\n\tcase BFCP_GOODBYE_ACK:          return \"GoodbyeAck\";\n\tdefault:                        return \"???\";\n\t}\n}\n"
  },
  {
    "path": "src/bfcp/reply.c",
    "content": "/**\n * @file bfcp/reply.c BFCP Reply\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_tmr.h>\n#include <re_bfcp.h>\n#include \"bfcp.h\"\n\n\nenum {\n\tBFCP_T2  = 10000,\n};\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct bfcp_conn *bc = arg;\n\n\tbc->mb = mem_deref(bc->mb);\n}\n\n\n/**\n * Send a BFCP response\n *\n * @param bc      BFCP connection\n * @param req     BFCP request message\n * @param prim    BFCP Primitive\n * @param attrc   Number of attributes\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_reply(struct bfcp_conn *bc, const struct bfcp_msg *req,\n\t       enum bfcp_prim prim, unsigned attrc, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tif (!bc || !req)\n\t\treturn EINVAL;\n\n\tbc->mb = mem_deref(bc->mb);\n\ttmr_cancel(&bc->tmr2);\n\n\tbc->mb = mbuf_alloc(64);\n\tif (!bc->mb)\n\t\treturn ENOMEM;\n\n\tva_start(ap, attrc);\n\terr = bfcp_msg_vencode(bc->mb, req->ver, true, prim, req->confid,\n\t\t\t       req->tid, req->userid, attrc, &ap);\n\tva_end(ap);\n\n\tif (err)\n\t\tgoto out;\n\n\tbc->mb->pos = 0;\n\n\terr = bfcp_send(bc, &req->src, bc->mb);\n\tif (err)\n\t\tgoto out;\n\n\tbc->st.prim   = req->prim;\n\tbc->st.confid = req->confid;\n\tbc->st.tid    = req->tid;\n\tbc->st.userid = req->userid;\n\n\ttmr_start(&bc->tmr2, BFCP_T2, tmr_handler, bc);\n\n out:\n\tif (err)\n\t\tbc->mb = mem_deref(bc->mb);\n\n\treturn err;\n}\n\n\n/**\n * Send a BFCP error response with details\n *\n * @param bc      BFCP connection\n * @param req     BFCP request message\n * @param code    Error code\n * @param details Error details\n * @param len     Details length\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_edreply(struct bfcp_conn *bc, const struct bfcp_msg *req,\n\t\t enum bfcp_err code, const uint8_t *details, size_t len)\n{\n\tstruct bfcp_errcode errcode;\n\n\terrcode.code    = code;\n\terrcode.details = (uint8_t *)details;\n\terrcode.len     = len;\n\n\treturn bfcp_reply(bc, req, BFCP_ERROR, 1,\n\t\t\t  BFCP_ERROR_CODE, 0, &errcode);\n}\n\n\n/**\n * Send a BFCP error response\n *\n * @param bc      BFCP connection\n * @param req     BFCP request message\n * @param code    Error code\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_ereply(struct bfcp_conn *bc, const struct bfcp_msg *req,\n\t\tenum bfcp_err code)\n{\n\treturn bfcp_edreply(bc, req, code, NULL, 0);\n}\n"
  },
  {
    "path": "src/bfcp/request.c",
    "content": "/**\n * @file bfcp/request.c BFCP Request\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_tmr.h>\n#include <re_bfcp.h>\n#include \"bfcp.h\"\n\n\nenum {\n\tBFCP_T1  = 500,\n\tBFCP_TXC = 4,\n};\n\n\nstruct bfcp_ctrans {\n\tstruct le le;\n\tstruct sa dst;\n\tstruct mbuf *mb;\n\tbfcp_resp_h *resph;\n\tvoid *arg;\n\tuint32_t confid;\n\tuint16_t userid;\n\tuint16_t tid;\n};\n\n\nstatic void tmr_handler(void *arg);\n\n\nstatic void dummy_resp_handler(int err, const struct bfcp_msg *msg, void *arg)\n{\n\t(void)err;\n\t(void)msg;\n\t(void)arg;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct bfcp_ctrans *ct = arg;\n\n\tlist_unlink(&ct->le);\n\tmem_deref(ct->mb);\n}\n\n\nstatic void dispatch(struct bfcp_conn *bc)\n{\n\tstruct le *le = bc->ctransl.head;\n\n\twhile (le) {\n\t\tstruct bfcp_ctrans *ct = le->data;\n\t\tint err;\n\n\t\tle = le->next;\n\n\t\terr = bfcp_send(bc, &ct->dst, ct->mb);\n\t\tif (err) {\n\t\t\tct->resph(err, NULL, ct->arg);\n\t\t\tmem_deref(ct);\n\t\t\tcontinue;\n\t\t}\n\n\t\ttmr_start(&bc->tmr1, BFCP_T1, tmr_handler, bc);\n\t\tbc->txc = 1;\n\t\tbreak;\n\t}\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct bfcp_conn *bc = arg;\n\tstruct bfcp_ctrans *ct;\n\tuint32_t timeout;\n\tint err;\n\n\tct = list_ledata(bc->ctransl.head);\n\tif (!ct)\n\t\treturn;\n\n\ttimeout = BFCP_T1<<bc->txc;\n\n\tif (++bc->txc > BFCP_TXC) {\n\t\terr = ETIMEDOUT;\n\t\tgoto out;\n\t}\n\n\terr = bfcp_send(bc, &ct->dst, ct->mb);\n\tif (err)\n\t\tgoto out;\n\n\ttmr_start(&bc->tmr1, timeout, tmr_handler, bc);\n\treturn;\n\n out:\n\tct->resph(err, NULL, ct->arg);\n\tmem_deref(ct);\n\tdispatch(bc);\n}\n\n\nbool bfcp_handle_response(struct bfcp_conn *bc, const struct bfcp_msg *msg)\n{\n\tstruct bfcp_ctrans *ct;\n\n\tif (!bc || !msg)\n\t\treturn false;\n\n\tct = list_ledata(bc->ctransl.head);\n\tif (!ct)\n\t\treturn false;\n\n\tif (msg->tid != ct->tid)\n\t\treturn false;\n\n\tif (msg->confid != ct->confid)\n\t\treturn false;\n\n\tif (msg->userid != ct->userid)\n\t\treturn false;\n\n\ttmr_cancel(&bc->tmr1);\n\n\tct->resph(0, msg, ct->arg);\n\tmem_deref(ct);\n\n\tdispatch(bc);\n\n\treturn true;\n}\n\n\nint bfcp_vrequest(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,\n\t\t  enum bfcp_prim prim, uint32_t confid, uint16_t userid,\n\t\t  bfcp_resp_h *resph, void *arg, unsigned attrc, va_list *ap)\n{\n\tstruct bfcp_ctrans *ct;\n\tint err;\n\n\tif (!bc || !dst)\n\t\treturn EINVAL;\n\n\tct = mem_zalloc(sizeof(*ct), destructor);\n\tif (!ct)\n\t\treturn ENOMEM;\n\n\tif (bc->tid == 0)\n\t\tbc->tid = 1;\n\n\tct->dst    = *dst;\n\tct->confid = confid;\n\tct->userid = userid;\n\tct->tid    = bc->tid++;\n\tct->resph  = resph ? resph : dummy_resp_handler;\n\tct->arg    = arg;\n\n\tct->mb = mbuf_alloc(128);\n\tif (!ct->mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = bfcp_msg_vencode(ct->mb, ver, false, prim, confid, ct->tid,\n\t\t\t       userid, attrc, ap);\n\tif (err)\n\t\tgoto out;\n\n\tct->mb->pos = 0;\n\n\tif (!bc->ctransl.head) {\n\n\t\terr = bfcp_send(bc, &ct->dst, ct->mb);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\ttmr_start(&bc->tmr1, BFCP_T1, tmr_handler, bc);\n\t\tbc->txc = 1;\n\t}\n\n\tlist_append(&bc->ctransl, &ct->le, ct);\n\n out:\n\tif (err)\n\t\tmem_deref(ct);\n\n\treturn err;\n}\n\n\n/**\n * Send a BFCP request\n *\n * @param bc      BFCP connection\n * @param dst     Destination address\n * @param ver     BFCP Version\n * @param prim    BFCP Primitive\n * @param confid  Conference ID\n * @param userid  User ID\n * @param resph   Response handler\n * @param arg     Response handler argument\n * @param attrc   Number of attributes\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_request(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,\n\t\t enum bfcp_prim prim, uint32_t confid, uint16_t userid,\n\t\t bfcp_resp_h *resph, void *arg, unsigned attrc, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, attrc);\n\terr = bfcp_vrequest(bc, dst, ver, prim, confid, userid, resph, arg,\n\t\t\t    attrc, &ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Send a BFCP notification/subsequent response\n *\n * @param bc      BFCP connection\n * @param dst     Destination address\n * @param ver     BFCP Version\n * @param prim    BFCP Primitive\n * @param confid  Conference ID\n * @param userid  User ID\n * @param attrc   Number of attributes\n *\n * @return 0 if success, otherwise errorcode\n */\nint bfcp_notify(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver,\n\t\tenum bfcp_prim prim, uint32_t confid, uint16_t userid,\n\t\tunsigned attrc, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, attrc);\n\terr = bfcp_vrequest(bc, dst, ver, prim, confid, userid, NULL, NULL,\n\t\t\t    attrc, &ap);\n\tva_end(ap);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/btrace/btrace.c",
    "content": "/**\n * @file btrace.c Backtrace API\n *\n * Copyright (C) 2023 Sebastian Reimers\n */\n\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef WIN32\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\n#include <dbghelp.h>\n#endif\n#include <stdlib.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_btrace.h>\n\n#define DEBUG_MODULE \"btrace\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nenum print_type { BTRACE_CSV, BTRACE_NEWLINE, BTRACE_JSON };\n\nstatic int print_debug(struct re_printf *pf, struct btrace *bt,\n\t\t       enum print_type type)\n{\n#if (!defined(HAVE_EXECINFO) && !defined(WIN32)) || defined(RELEASE)\n\t(void)pf;\n\t(void)bt;\n\t(void)type;\n\n\treturn 0;\n#elif defined(WIN32)\n\tSYMBOL_INFO *symbol;\n\tDWORD displacement = 0;\n\tIMAGEHLP_LINE line;\n\tHANDLE hProcess = GetCurrentProcess();\n\t(void)type;\n\n\t/* Initialize the symbol buffer. */\n\tsymbol = mem_zalloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), NULL);\n\tif (!symbol)\n\t\treturn ENOMEM;\n\n\tSymInitialize(hProcess, NULL, TRUE);\n\n\tsymbol->MaxNameLen   = 255;\n\tsymbol->SizeOfStruct = sizeof(SYMBOL_INFO);\n\n\t/* Initialize the line buffer. */\n\tZeroMemory(&line, sizeof(line));\n\tline.SizeOfStruct = sizeof(line);\n\n\tfor (size_t i = 0; i < bt->len; i++) {\n\t\tSymFromAddr(hProcess, (DWORD64)(bt->stack[i]), NULL, symbol);\n\t\tSymGetLineFromAddr(hProcess, (DWORD64)(bt->stack[i]),\n\t\t\t\t   &displacement, &line);\n\t\tre_hprintf(pf, \"%zu: %s (%s:%lu)\\n\", i, symbol->Name,\n\t\t\t   line.FileName, line.LineNumber);\n\t}\n\n\tmem_deref(symbol);\n\tSymCleanup(hProcess);\n\n\treturn 0;\n#else\n\tchar **symbols;\n\n\tif (!pf || !bt)\n\t\treturn EINVAL;\n\n\tif (!bt->len)\n\t\treturn 0;\n\n#if defined(FREEBSD) || defined(OPENBSD)\n\tsymbols = backtrace_symbols(bt->stack, bt->len);\n#else\n\tsymbols = backtrace_symbols(bt->stack, (int)bt->len);\n#endif\n\n\tif (!symbols)\n\t\treturn 0;\n\n\tswitch (type) {\n\tcase BTRACE_CSV:\n\t\tfor (size_t j = 0; j < bt->len; j++) {\n\t\t\tre_hprintf(pf, \"%s%s\", symbols[j],\n\t\t\t\t   ((j + 1) < bt->len) ? \", \" : \"\");\n\t\t}\n\t\tbreak;\n\tcase BTRACE_NEWLINE:\n\t\tfor (size_t j = 0; j < bt->len; j++) {\n\t\t\tre_hprintf(pf, \"%s\\n\", symbols[j]);\n#ifdef LINUX\n\t\t\tstruct pl file\t     = PL_INIT;\n\t\t\tstruct pl addr\t     = PL_INIT;\n\t\t\tchar addr2l[512]     = {0};\n\t\t\tchar addr2l_out[256] = {0};\n\t\t\tFILE *pipe;\n\n\t\t\tre_regex(symbols[j], str_len(symbols[j]),\n\t\t\t\t \"[^(]+([^)]+\", &file, &addr);\n\n\t\t\t(void)re_snprintf(addr2l, sizeof(addr2l),\n\t\t\t\t    \"addr2line -p -f -e %r %r\", &file, &addr);\n\n\t\t\tpipe = popen(addr2l, \"r\");\n\t\t\tif (!pipe)\n\t\t\t\tcontinue;\n\n\t\t\twhile (fgets(addr2l_out, sizeof(addr2l_out), pipe)) {\n\t\t\t\tre_hprintf(pf, \"\\t%s\", addr2l_out);\n\t\t\t}\n\n\t\t\tpclose(pipe);\n#endif\n\t\t}\n\t\tbreak;\n\tcase BTRACE_JSON:\n\t\tre_hprintf(pf, \"[\");\n\t\tfor (size_t j = 0; j < bt->len; j++) {\n\t\t\tre_hprintf(pf, \"\\\"%s\\\"%s\", symbols[j],\n\t\t\t\t   ((j + 1) < bt->len) ? \", \" : \"\");\n\t\t}\n\t\tre_hprintf(pf, \"]\");\n\t\tbreak;\n\t}\n\n\tfree(symbols);\n\n\treturn 0;\n#endif\n}\n\n\n/**\n * Print debug backtrace (comma separated)\n *\n * @param pf Print function for debug output\n * @param bt Backtrace object\n *\n * @return 0 if success, otherwise errorcode\n */\nint btrace_print(struct re_printf *pf, struct btrace *bt)\n{\n\treturn print_debug(pf, bt, BTRACE_CSV);\n}\n\n\n/**\n * Print debug backtrace with newlines\n *\n * @param pf Print function for debug output\n * @param bt Backtrace object\n *\n * @return 0 if success, otherwise errorcode\n */\nint btrace_println(struct re_printf *pf, struct btrace *bt)\n{\n\treturn print_debug(pf, bt, BTRACE_NEWLINE);\n}\n\n\n/**\n * Print debug backtrace as json array\n *\n * @param pf Print function for debug output\n * @param bt Backtrace object\n *\n * @return 0 if success, otherwise errorcode\n */\nint btrace_print_json(struct re_printf *pf, struct btrace *bt)\n{\n\treturn print_debug(pf, bt, BTRACE_JSON);\n}\n"
  },
  {
    "path": "src/conf/conf.c",
    "content": "/**\n * @file conf.c  Configuration file parser\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef HAVE_IO_H\n#include <io.h>\n#endif\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_conf.h>\n\n\n#ifdef WIN32\n#define open _open\n#define read _read\n#define close _close\n#endif\n\n\n/**\n * Defines a Configuration state. The configuration data is stored in a\n * linear buffer which can be used for reading key-value pairs of\n * configuration data. The config data can be strings or numeric values.\n */\nstruct conf {\n\tstruct mbuf *mb;\n};\n\n\nstatic int load_file(struct mbuf *mb, const char *filename)\n{\n\tint err = 0, fd = open(filename, O_RDONLY);\n\tif (fd < 0)\n\t\treturn errno;\n\n\tfor (;;) {\n\t\tuint8_t buf[1024];\n\n\t\tconst ssize_t n = read(fd, (void *)buf, sizeof(buf));\n\t\tif (n < 0) {\n\t\t\terr = errno;\n\t\t\tbreak;\n\t\t}\n\t\telse if (n == 0)\n\t\t\tbreak;\n\n\t\tif ((size_t)n > sizeof(buf)) {\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\terr |= mbuf_write_mem(mb, buf, n);\n\t}\n\n\t(void)close(fd);\n\n\treturn err;\n}\n\n\nstatic void conf_destructor(void *data)\n{\n\tstruct conf *conf = data;\n\n\tmem_deref(conf->mb);\n}\n\n\n/**\n * Load configuration from file\n *\n * @param confp    Configuration object to be allocated\n * @param filename Name of configuration file\n *\n * @return 0 if success, otherwise errorcode\n */\nint conf_alloc(struct conf **confp, const char *filename)\n{\n\tstruct conf *conf;\n\tint err = 0;\n\n\tif (!confp)\n\t\treturn EINVAL;\n\n\tconf = mem_zalloc(sizeof(*conf), conf_destructor);\n\tif (!conf)\n\t\treturn ENOMEM;\n\n\tconf->mb = mbuf_alloc(1024);\n\tif (!conf->mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr |= mbuf_write_u8(conf->mb, '\\n');\n\tif (filename)\n\t\terr |= load_file(conf->mb, filename);\n\n out:\n\tif (err)\n\t\tmem_deref(conf);\n\telse\n\t\t*confp = conf;\n\n\treturn err;\n}\n\n\n/**\n * Allocate configuration from a buffer\n *\n * @param confp    Configuration object to be allocated\n * @param buf      Buffer containing configuration\n * @param sz       Size of configuration buffer\n *\n * @return 0 if success, otherwise errorcode\n */\nint conf_alloc_buf(struct conf **confp, const uint8_t *buf, size_t sz)\n{\n\tstruct conf *conf;\n\tint err;\n\n\terr = conf_alloc(&conf, NULL);\n\tif (err)\n\t\treturn err;\n\n\terr = mbuf_write_mem(conf->mb, buf, sz);\n\n\tif (err)\n\t\tmem_deref(conf);\n\telse\n\t\t*confp = conf;\n\n\treturn err;\n}\n\n\n/**\n * Get the value of a configuration item PL string\n *\n * @param conf Configuration object\n * @param name Name of config item key\n * @param pl   Value of config item, if present\n *\n * @return 0 if success, otherwise errorcode\n */\nint conf_get(const struct conf *conf, const char *name, struct pl *pl)\n{\n\tchar expr[512];\n\tstruct pl spl;\n\n\tif (!conf || !name || !pl)\n\t\treturn EINVAL;\n\n\tspl.p = (const char *)conf->mb->buf;\n\tspl.l = conf->mb->end;\n\n\t(void)re_snprintf(expr, sizeof(expr),\n\t\t\t  \"[\\r\\n]+[ \\t]*%s[ \\t]+[~ \\t\\r\\n]+\", name);\n\n\treturn re_regex(spl.p, spl.l, expr, NULL, NULL, NULL, pl);\n}\n\n\n/**\n * Get the value of a configuration item string\n *\n * @param conf Configuration object\n * @param name Name of config item key\n * @param str  Value of config item, if present\n * @param size Size of string to store value\n *\n * @return 0 if success, otherwise errorcode\n */\nint conf_get_str(const struct conf *conf, const char *name, char *str,\n\t\t size_t size)\n{\n\tstruct pl pl;\n\tint err;\n\n\tif (!conf || !name || !str || !size)\n\t\treturn EINVAL;\n\n\terr = conf_get(conf, name, &pl);\n\tif (err)\n\t\treturn err;\n\n\treturn pl_strcpy(&pl, str, size);\n}\n\n\n/**\n * Get the numeric value of a configuration item\n *\n * @param conf Configuration object\n * @param name Name of config item key\n * @param num  Returned numeric value of config item, if present\n *\n * @return 0 if success, otherwise errorcode\n */\nint conf_get_u32(const struct conf *conf, const char *name, uint32_t *num)\n{\n\tstruct pl pl;\n\tint err;\n\n\tif (!conf || !name || !num)\n\t\treturn EINVAL;\n\n\terr = conf_get(conf, name, &pl);\n\tif (err)\n\t\treturn err;\n\n\t*num = pl_u32(&pl);\n\n\treturn 0;\n}\n\n\n/**\n * Get the numeric signed value of a configuration item\n *\n * @param conf Configuration object\n * @param name Name of config item key\n * @param num  Returned numeric value of config item, if present\n *\n * @return 0 if success, otherwise errorcode\n */\nint conf_get_i32(const struct conf *conf, const char *name, int32_t *num)\n{\n\tstruct pl pl;\n\tint err;\n\n\tif (!conf || !name || !num)\n\t\treturn EINVAL;\n\n\terr = conf_get(conf, name, &pl);\n\tif (err)\n\t\treturn err;\n\n\t*num = pl_i32(&pl);\n\n\treturn 0;\n}\n\n\n/**\n * Get the numeric floating point value of a configuration item\n *\n * @param conf Configuration object\n * @param name Name of config item key\n * @param num  Returned numeric value of config item, if present\n *\n * @return 0 if success, otherwise errorcode\n */\nint conf_get_float(const struct conf *conf, const char *name, double *num)\n{\n\tstruct pl opt;\n\tint err;\n\n\tif (!conf || !name || !num)\n\t\treturn EINVAL;\n\n\terr = conf_get(conf, name, &opt);\n\tif (err)\n\t\treturn err;\n\n\t*num = pl_float(&opt);\n\n\treturn 0;\n}\n\n\n/**\n * Get the boolean value of a configuration item\n *\n * @param conf Configuration object\n * @param name Name of config item key\n * @param val  Returned boolean value of config item, if present\n *\n * @return 0 if success, otherwise errorcode\n */\nint conf_get_bool(const struct conf *conf, const char *name, bool *val)\n{\n\tstruct pl pl;\n\tint err;\n\n\tif (!conf || !name || !val)\n\t\treturn EINVAL;\n\n\terr = conf_get(conf, name, &pl);\n\tif (err)\n\t\treturn err;\n\n\tif (!pl_strcasecmp(&pl, \"true\"))\n\t\t*val = true;\n\telse if (!pl_strcasecmp(&pl, \"yes\"))\n\t\t*val = true;\n\telse if (!pl_strcasecmp(&pl, \"1\"))\n\t\t*val = true;\n\telse\n\t\t*val = false;\n\n\treturn 0;\n}\n\n\n/**\n * Apply a function handler to all config items of a certain key\n *\n * @param conf Configuration object\n * @param name Name of config item key\n * @param ch   Config item handler\n * @param arg  Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint conf_apply(const struct conf *conf, const char *name,\n\t       conf_h *ch, void *arg)\n{\n\tchar expr[512];\n\tstruct pl pl, val;\n\tint err = 0;\n\n\tif (!conf || !name || !ch)\n\t\treturn EINVAL;\n\n\tpl.p = (const char *)conf->mb->buf;\n\tpl.l = conf->mb->end;\n\n\t(void)re_snprintf(expr, sizeof(expr),\n\t\t\t  \"[\\r\\n]+[ \\t]*%s[ \\t]+[~ \\t\\r\\n]+\", name);\n\n\twhile (!re_regex(pl.p, pl.l, expr, NULL, NULL, NULL, &val)) {\n\n\t\terr = ch(&val, arg);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tpl.l -= val.p + val.l - pl.p;\n\t\tpl.p  = val.p + val.l;\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/crc32/crc32.c",
    "content": "/**\n * @file crc32.c CRC32 Implementation\n *\n *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or\n *  code or tables extracted from it, as desired without restriction.\n */\n\n/*\n *  First, the polynomial itself and its table of feedback terms.  The\n *  polynomial is\n *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0\n *\n *  Note that we take it \"backwards\" and put the highest-order term in\n *  the lowest-order bit.  The X^32 term is \"implied\"; the LSB is the\n *  X^31 term, etc.  The X^0 term (usually shown as \"+1\") results in\n *  the MSB being 1\n *\n *  Note that the usual hardware shift register implementation, which\n *  is what we're using (we're merely optimizing it by doing eight-bit\n *  chunks at a time) shifts bits into the lowest-order term.  In our\n *  implementation, that means shifting towards the right.  Why do we\n *  do it this way?  Because the calculated CRC must be transmitted in\n *  order from highest-order term to lowest-order term.  UARTs transmit\n *  characters in order from LSB to MSB.  By storing the CRC this way\n *  we hand it to the UART in the order low-byte to high-byte; the UART\n *  sends each low-bit to high-bit; and the result is transmission bit\n *  by bit from highest- to lowest-order term without requiring any bit\n *  shuffling on our part.  Reception works similarly\n *\n *  The feedback terms table consists of 256, 32-bit entries.  Notes\n *\n *      The table can be generated at runtime if desired; code to do so\n *      is shown later.  It might not be obvious, but the feedback\n *      terms simply represent the results of eight shift/xor opera\n *      tions for all combinations of data and CRC register values\n *\n *      The values must be right-shifted by eight bits by the \"updcrc\n *      logic; the shift must be unsigned (bring in zeroes).  On some\n *      hardware you could probably optimize the shift in assembler by\n *      using byte-swap instructions\n *      polynomial $edb88320\n *\n *\n * CRC32 code derived from work by Gary S. Brown.\n */\n#include <re_types.h>\n#include <re_crc32.h>\n\n\n#ifdef USE_ZLIB\n#include <zlib.h>\n#else\n\n\nstatic const uint32_t crc32_tab[] = {\n\t0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,\n\t0xe963a535, 0x9e6495a3,\t0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,\n\t0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,\n\t0xf3b97148, 0x84be41de,\t0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,\n\t0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,\t0x14015c4f, 0x63066cd9,\n\t0xfa0f3d63, 0x8d080df5,\t0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,\n\t0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,\t0x35b5a8fa, 0x42b2986c,\n\t0xdbbbc9d6, 0xacbcf940,\t0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,\n\t0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,\n\t0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,\n\t0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,\t0x76dc4190, 0x01db7106,\n\t0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,\n\t0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,\n\t0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,\n\t0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,\n\t0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,\n\t0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,\n\t0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,\n\t0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,\n\t0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,\n\t0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,\n\t0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,\n\t0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,\n\t0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,\n\t0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,\n\t0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,\n\t0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,\n\t0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,\n\t0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,\n\t0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,\n\t0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,\n\t0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,\n\t0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,\n\t0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,\n\t0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,\n\t0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,\n\t0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,\n\t0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,\n\t0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,\n\t0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,\n\t0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,\n\t0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,\n\t0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d\n};\n\n\n#endif\n\n\n/**\n * A function that calculates the CRC-32 based on the table above is\n * given below for documentation purposes. An equivalent implementation\n * of this function that's actually used in the kernel can be found\n * in sys/libkern.h, where it can be inlined.\n *\n * @param crc  Initial CRC value\n * @param buf  Buffer to generate CRC from\n * @param size Number of bytes in buffer\n *\n * @return CRC value\n */\nuint32_t re_crc32(uint32_t crc, const void *buf, uint32_t size)\n{\n\n#ifdef USE_ZLIB\n\treturn (uint32_t)crc32(crc, buf, size);\n#else\n\tconst uint8_t *p = buf;\n\n\tcrc = ~crc;\n\twhile (size--)\n\t\tcrc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);\n\treturn crc ^ ~0U;\n#endif\n}\n"
  },
  {
    "path": "src/dbg/dbg.c",
    "content": "/**\n * @file dbg.c  Debug printing\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdio.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include <time.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sys.h>\n#include <re_thread.h>\n\n\n#define DEBUG_MODULE \"dbg\"\n#define DEBUG_LEVEL 0\n#include <re_dbg.h>\n\n\n/** Debug configuration */\nstatic struct {\n\tuint64_t tick;         /**< Init ticks             */\n\tint level;             /**< Current debug level    */\n\tenum dbg_flags flags;  /**< Debug flags            */\n\tdbg_print_h *ph;       /**< Optional print handler */\n\tvoid *arg;             /**< Handler argument       */\n} dbg = {\n\t0,\n\tDBG_INFO,\n\tDBG_ANSI,\n\tNULL,\n\tNULL,\n};\n\nstatic once_flag flag = ONCE_FLAG_INIT;\nstatic mtx_t mtx;\n\n\nstatic void mem_lock_init(void)\n{\n\tmtx_init(&mtx, mtx_plain);\n}\n\n\nstatic inline void dbg_lock(void)\n{\n\tcall_once(&flag, mem_lock_init);\n\tmtx_lock(&mtx);\n}\n\n\nstatic inline void dbg_unlock(void)\n{\n\tmtx_unlock(&mtx);\n}\n\n\n/**\n * Initialise debug printing\n *\n * @param level Debug level\n * @param flags Debug flags\n */\nvoid dbg_init(int level, enum dbg_flags flags)\n{\n\tdbg_lock();\n\tdbg.tick  = tmr_jiffies();\n\tdbg.level = level;\n\tdbg.flags = flags;\n\tdbg_unlock();\n}\n\n\n/**\n * Set optional debug print handler\n *\n * @param ph  Print handler\n * @param arg Handler argument\n */\nvoid dbg_handler_set(dbg_print_h *ph, void *arg)\n{\n\tdbg_lock();\n\tdbg.ph  = ph;\n\tdbg.arg = arg;\n\tdbg_unlock();\n}\n\n\n/* NOTE: This function should not allocate memory */\nstatic void dbg_vprintf(int level, const char *fmt, va_list ap)\n{\n\tdbg_lock();\n\n\tif (level > dbg.level)\n\t\tgoto out;\n\n\t/* Print handler? */\n\tif (dbg.ph)\n\t\tgoto out;\n\n\tif (dbg.flags & DBG_ANSI) {\n\n\t\tswitch (level) {\n\n\t\tcase DBG_WARNING:\n\t\t\t(void)re_fprintf(stderr, \"\\x1b[31m\"); /* Red */\n\t\t\tbreak;\n\n\t\tcase DBG_NOTICE:\n\t\t\t(void)re_fprintf(stderr, \"\\x1b[33m\"); /* Yellow */\n\t\t\tbreak;\n\n\t\tcase DBG_INFO:\n\t\t\t(void)re_fprintf(stderr, \"\\x1b[32m\"); /* Green */\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (dbg.flags & DBG_TIME) {\n\t\tconst uint64_t ticks = tmr_jiffies();\n\n\t\tif (0 == dbg.tick)\n\t\t\tdbg.tick = tmr_jiffies();\n\n\t\t(void)re_fprintf(stderr, \"[%09llu] \", ticks - dbg.tick);\n\t}\n\n\t(void)re_vfprintf(stderr, fmt, ap);\n\n\tif (dbg.flags & DBG_ANSI && level < DBG_DEBUG)\n\t\t(void)re_fprintf(stderr, \"\\x1b[;m\");\nout:\n\tdbg_unlock();\n}\n\n\n/* Formatted output to print handler */\nstatic void dbg_fmt_vprintf(int level, const char *fmt, va_list ap)\n{\n\tchar buf[256];\n\n\tdbg_lock();\n\tint dbg_level   = dbg.level;\n\tdbg_print_h *ph = dbg.ph;\n\tvoid *arg       = dbg.arg;\n\tdbg_unlock();\n\n\tif (level > dbg_level)\n\t\treturn;\n\n\t/* Print handler? */\n\tif (ph) {\n\t\tint len = re_vsnprintf(buf, sizeof(buf), fmt, ap);\n\t\tif (len <= 0)\n\t\t\treturn;\n\n\t\tph(level, buf, len, arg);\n\t}\n}\n\n\n/**\n * Print a formatted debug message\n *\n * @param level Debug level\n * @param fmt   Formatted string\n */\nvoid dbg_printf(int level, const char *fmt, ...)\n{\n\tva_list ap;\n\n\tva_start(ap, fmt);\n\tdbg_vprintf(level, fmt, ap);\n\tva_end(ap);\n\n\tva_start(ap, fmt);\n\tdbg_fmt_vprintf(level, fmt, ap);\n\tva_end(ap);\n}\n\n\n/**\n * Get the name of the debug level\n *\n * @param level Debug level\n *\n * @return String with debug level name\n */\nconst char *dbg_level_str(int level)\n{\n\tswitch (level) {\n\n\tcase DBG_EMERG:   return \"EMERGENCY\";\n\tcase DBG_ALERT:   return \"ALERT\";\n\tcase DBG_CRIT:    return \"CRITICAL\";\n\tcase DBG_ERR:     return \"ERROR\";\n\tcase DBG_WARNING: return \"WARNING\";\n\tcase DBG_NOTICE:  return \"NOTICE\";\n\tcase DBG_INFO:    return \"INFO\";\n\tcase DBG_DEBUG:   return \"DEBUG\";\n\tdefault:          return \"???\";\n\t}\n}\n"
  },
  {
    "path": "src/dd/dd.c",
    "content": "/**\n * @file dd.c Dependency Descriptor (DD) -- decoder\n *\n * Copyright (C) 2023 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re.h>\n#include <re_dd.h>\n\n\n#define dd_f(n) get_bits(gb, (n))\n\n\nstatic const char *dti_name(enum dd_dti dti)\n{\n\tswitch (dti) {\n\n\tcase DD_DTI_NOT_PRESENT:  return \"NOT_PRESENT\";\n\tcase DD_DTI_DISCARDABLE:  return \"DISCARDABLE\";\n\tcase DD_DTI_SWITCH:       return \"SWITCH\";\n\tcase DD_DTI_REQUIRED:     return \"REQUIRED\";\n\t}\n\n\treturn \"???\";\n}\n\n\n#if 0\nstatic const char *next_layer_name(enum dd_next_layer_idc idc)\n{\n\tswitch (idc) {\n\n\tcase DD_SAME_LAYER:          return \"Same\";\n\tcase DD_NEXT_TEMPORAL_LAYER: return \"Temporal\";\n\tcase DD_NEXT_SPATIAL_LAYER:  return \"Spatial\";\n\tcase DD_NO_MORE_TEMPLATES:   return \"None\";\n\t}\n\n\treturn \"???\";\n}\n#endif\n\n\nstatic int mandatory_descriptor_fields(struct dd *dd, struct getbit *gb)\n{\n\tif (getbit_get_left(gb) < 24)\n\t\treturn EBADMSG;\n\n\tdd->start_of_frame               = dd_f(1);\n\tdd->end_of_frame                 = dd_f(1);\n\tdd->frame_dependency_template_id = dd_f(6);\n\tdd->frame_number                 = dd_f(16);\n\n\treturn 0;\n}\n\n\n/*\n * 0  next template has the same spatial ID and temporal ID as current template\n *\n * 1  next template has the same spatial ID and temporal ID plus 1 compared\n *    with the current Frame dependency template.\n *\n * 2  next Frame dependency template has temporal ID equal to 0 and\n *    spatial ID plus 1 compared with the current Frame dependency template.\n *\n * 3  No more Frame dependency templates are present in the\n *    Frame dependency structure.\n */\nstatic int template_layers(struct dd *dd, struct getbit *gb)\n{\n\tuint8_t temporalId = 0;\n\tuint8_t spatialId = 0;\n\tuint8_t TemplateCnt = 0;\n\tuint8_t MaxTemporalId = 0;\n\tuint8_t next_layer_idc = DD_SAME_LAYER;\n\n\tdo {\n\t\tif (TemplateCnt >= DD_MAX_TEMPLATES)\n\t\t\treturn EOVERFLOW;\n\n\t\tdd->template_spatial_id[TemplateCnt] = spatialId;\n\t\tdd->template_temporal_id[TemplateCnt] = temporalId;\n\n\t\t++TemplateCnt;\n\n\t\tif (getbit_get_left(gb) < 2)\n\t\t\treturn EBADMSG;\n\n\t\tnext_layer_idc = dd_f(2);\n\n\t\t/* next_layer_idc == 0 - same sid and tid */\n\t\tif (next_layer_idc == DD_NEXT_TEMPORAL_LAYER) {\n\t\t\t++temporalId;\n\t\t\tif (temporalId > MaxTemporalId) {\n\t\t\t\tMaxTemporalId = temporalId;\n\t\t\t}\n\t\t}\n\t\telse if (next_layer_idc == DD_NEXT_SPATIAL_LAYER) {\n\t\t\ttemporalId = 0;\n\t\t\t++spatialId;\n\t\t}\n\t}\n\twhile (next_layer_idc != DD_NO_MORE_TEMPLATES);\n\n\tdd->max_spatial_id = spatialId;\n\tdd->template_cnt = TemplateCnt;\n\n\treturn 0;\n}\n\n\nstatic int template_dtis(struct dd *dd, struct getbit *gb)\n{\n\tfor (uint8_t templateIndex = 0;\n\t     templateIndex < dd->template_cnt;\n\t     templateIndex++) {\n\n\t\tif (templateIndex >= DD_MAX_TEMPLATES)\n\t\t\treturn EOVERFLOW;\n\n\t\tfor (uint8_t dtIndex = 0; dtIndex < dd->dt_cnt; dtIndex++) {\n\n\t\t\tif (getbit_get_left(gb) < 2)\n\t\t\t\treturn EBADMSG;\n\n\t\t\t/* See table A.1 below for meaning of DTI values. */\n\t\t\tdd->template_dti[templateIndex][dtIndex] = dd_f(2);\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\nstatic int template_fdiffs(struct dd *dd, struct getbit *gb)\n{\n\tfor (uint8_t templateIndex = 0;\n\t     templateIndex < dd->template_cnt;\n\t     templateIndex++) {\n\n\t\tuint8_t fdiffCnt = 0;\n\n\t\tif (getbit_get_left(gb) < 1)\n\t\t\treturn EBADMSG;\n\n\t\tbool fdiff_follows_flag = dd_f(1);\n\n\t\twhile (fdiff_follows_flag) {\n\n\t\t\tif (getbit_get_left(gb) < 5)\n\t\t\t\treturn EBADMSG;\n\n\t\t\tuint8_t fdiff_minus_one = dd_f(4);\n\n\t\t\tuint8_t fdiff = fdiff_minus_one + 1;\n\n\t\t\tdd->template_fdiff[templateIndex][fdiffCnt] = fdiff;\n\n\t\t\t++fdiffCnt;\n\t\t\tfdiff_follows_flag = dd_f(1);\n\t\t}\n\n\t\tdd->template_fdiff_cnt[templateIndex] = fdiffCnt;\n\t}\n\n\treturn 0;\n}\n\n\nstatic int template_chains(struct dd *dd, struct getbit *gb)\n{\n\t/* todo: check bits left */\n\tdd->chain_cnt = getbit_read_ns(gb, dd->dt_cnt + 1);\n\n\tif (dd->chain_cnt == 0)\n\t\treturn 0;\n\n\tfor (uint8_t dtIndex = 0; dtIndex < dd->dt_cnt; dtIndex++) {\n\t\tuint8_t v = getbit_read_ns(gb, dd->chain_cnt);\n\t\tdd->decode_target_protected_by[dtIndex] = v;\n\t}\n\n\tfor (uint8_t templateIndex = 0;\n\t     templateIndex < dd->template_cnt;\n\t     templateIndex++) {\n\n\t\tfor (uint8_t chainIndex = 0;\n\t\t     chainIndex < dd->chain_cnt;\n\t\t     chainIndex++) {\n\n\t\t\tif (getbit_get_left(gb) < 4)\n\t\t\t\treturn EBADMSG;\n\n\t\t\tdd->template_chain_fdiff[templateIndex][chainIndex] =\n\t\t\t\tdd_f(4);\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\nstatic int render_resolutions(struct dd *dd, struct getbit *gb)\n{\n\tfor (uint8_t spatial_id = 0;\n\t     spatial_id <= dd->max_spatial_id;\n\t     spatial_id++) {\n\n\t\tif (getbit_get_left(gb) < 32)\n\t\t\treturn EBADMSG;\n\n\t\tdd->max_render_width_minus_1[spatial_id]  = dd_f(16);\n\t\tdd->max_render_height_minus_1[spatial_id] = dd_f(16);\n\n\t\t++dd->render_count;\n\t}\n\n\treturn 0;\n}\n\n\nstatic int template_dependency_structure(struct dd *dd, struct getbit *gb)\n{\n\tif (getbit_get_left(gb) < 11)\n\t\treturn EBADMSG;\n\n\tdd->template_id_offset = dd_f(6);\n\tuint8_t dt_cnt_minus_one = dd_f(5);\n\n\tdd->dt_cnt = dt_cnt_minus_one + 1;\n\n\tint err = template_layers(dd, gb);\n\tif (err)\n\t\treturn err;\n\n\terr = template_dtis(dd, gb);\n\tif (err)\n\t\treturn err;\n\n\terr = template_fdiffs(dd, gb);\n\tif (err)\n\t\treturn err;\n\n\ttemplate_chains(dd, gb);\n\n\t/* note:  decode_target_layers() */\n\n\tif (getbit_get_left(gb) < 1)\n\t\treturn EBADMSG;\n\n\tdd->resolutions_present_flag = dd_f(1);\n\tif (dd->resolutions_present_flag) {\n\t\terr = render_resolutions(dd, gb);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\nstatic int extended_descriptor_fields(struct dd *dd, struct getbit *gb)\n{\n\tif (getbit_get_left(gb) < 5)\n\t\treturn EBADMSG;\n\n\tdd->template_dependency_structure_present_flag = dd_f(1);\n\tdd->active_decode_targets_present_flag = dd_f(1);\n\tdd->custom_dtis_flag   = dd_f(1);\n\tdd->custom_fdiffs_flag = dd_f(1);\n\tdd->custom_chains_flag = dd_f(1);\n\n\tif (dd->template_dependency_structure_present_flag) {\n\n\t\tint err = template_dependency_structure(dd, gb);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tdd->active_decode_targets_bitmask = (1u << dd->dt_cnt) - 1;\n\t}\n\n\tif (dd->active_decode_targets_present_flag) {\n\n\t\tdd->active_decode_targets_bitmask = dd_f(dd->dt_cnt);\n\t}\n\n\treturn 0;\n}\n\n\nstatic void no_extended_descriptor_fields(struct dd *dd)\n{\n\tdd->custom_dtis_flag   = 0;\n\tdd->custom_fdiffs_flag = 0;\n\tdd->custom_chains_flag = 0;\n}\n\n\nint dd_decode(struct dd *dd, const uint8_t *buf, size_t sz)\n{\n\tif (!dd || !buf)\n\t\treturn EINVAL;\n\n\tmemset(dd, 0, sizeof(*dd));\n\n\tstruct getbit gb;\n\n\tgetbit_init(&gb, buf, sz*8);\n\n\tint err = mandatory_descriptor_fields(dd, &gb);\n\tif (err)\n\t\treturn err;\n\n\tif (sz > 3) {\n\t\terr = extended_descriptor_fields(dd, &gb);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tdd->ext = true;\n\t}\n\telse {\n\t\tno_extended_descriptor_fields(dd);\n\t}\n\n\treturn 0;\n}\n\n\nint dd_print(struct re_printf *pf, const struct dd *dd)\n{\n\tif (!dd)\n\t\treturn 0;\n\n\tint err = re_hprintf(pf, \"~~~~ DD: ~~~~\\n\");\n\n\terr |= re_hprintf(pf, \".... start=%d, end=%d,\"\n\t\t\t  \" frame_dependency_template_id=%u,\"\n\t\t\t  \" frame_number=%u\\n\",\n\t\t\t  dd->start_of_frame,\n\t\t\t  dd->end_of_frame,\n\t\t\t  dd->frame_dependency_template_id,\n\t\t\t  dd->frame_number);\n\terr |= re_hprintf(pf, \".... ext: %d\\n\", dd->ext);\n\tif (err)\n\t\treturn err;\n\n\tif (dd->ext) {\n\n\t\terr = re_hprintf(pf,\n\t\t\t \".... template_dependency_structure_present:   %u\\n\",\n\t\t\t dd->template_dependency_structure_present_flag);\n\t\terr |= re_hprintf(pf,\n\t\t\t  \".... active_decode_targets_present_flag:      %u\\n\",\n\t\t\t  dd->active_decode_targets_present_flag);\n\t\terr |= re_hprintf(pf,\n\t\t\t  \".... custom_dtis_flag:                        %u\\n\",\n\t\t\t  dd->custom_dtis_flag);\n\t\terr |= re_hprintf(pf,\n\t\t\t  \".... custom_fdiffs_flag:                      %u\\n\",\n\t\t\t  dd->custom_fdiffs_flag);\n\t\terr |= re_hprintf(pf,\n\t\t\t  \".... custom_chains_flag:                      %u\\n\",\n\t\t\t  dd->custom_chains_flag);\n\t\terr |= re_hprintf(pf, \"\\n\");\n\n\t\terr |= re_hprintf(pf,\n\t\t\t  \".... active_decode_targets_bitmask: 0x%x\\n\",\n\t\t\t  dd->active_decode_targets_bitmask);\n\t\terr |= re_hprintf(pf,\n\t\t\t  \".... template_id_offset:            %u\\n\",\n\t\t\t  dd->template_id_offset);\n\t\terr |= re_hprintf(pf,\n\t\t\t\t  \".... dt_cnt:                        %u\\n\",\n\t\t\t\t  dd->dt_cnt);\n\t\terr |= re_hprintf(pf,\n\t\t\t\t  \".... template_cnt:                  %u\\n\",\n\t\t\t\t  dd->template_cnt);\n\t\terr |= re_hprintf(pf,\n\t\t\t\t  \".... max_spatial_id:                %u\\n\",\n\t\t\t\t  dd->max_spatial_id);\n\t\terr |= re_hprintf(pf, \"\\n\");\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = re_hprintf(pf, \".... template spatial/temporal ids:\\n\");\n\t\tfor (uint8_t i=0; i<dd->template_cnt; i++) {\n\n\t\t\terr |= re_hprintf(pf,\n\t\t\t\t  \".... [%u] spatial=%u temporal=%u\\n\",\n\t\t\t\t  i,\n\t\t\t\t  dd->template_spatial_id[i],\n\t\t\t\t  dd->template_temporal_id[i]);\n\t\t}\n\t\terr |= re_hprintf(pf, \"\\n\");\n\n\t\terr |= re_hprintf(pf, \".... resolutions_present_flag: %u\\n\",\n\t\t\t  dd->resolutions_present_flag);\n\t\terr |= re_hprintf(pf, \".... render_count: %u\\n\",\n\t\t\t\t  dd->render_count);\n\t\tfor (uint8_t i = 0; i < dd->render_count; i++) {\n\n\t\t\terr |= re_hprintf(pf,\n\t\t\t\t  \".... max_render %u:        %u x %u\\n\",\n\t\t\t\t  i,\n\t\t\t\t  dd->max_render_width_minus_1[i] + 1,\n\t\t\t\t  dd->max_render_height_minus_1[i] + 1);\n\t\t}\n\t\terr |= re_hprintf(pf, \"\\n\");\n\n\t\tfor (uint8_t i = 0; i < dd->template_cnt; i++) {\n\n\t\t\tuint8_t fdiffCnt = dd->template_fdiff_cnt[i];\n\n\t\t\terr |= re_hprintf(pf,\n\t\t\t\t  \".... [%u] template_fdiff_cnt: %u\",\n\t\t\t\t  i, fdiffCnt);\n\n\t\t\tfor (uint8_t j = 0; j < fdiffCnt; j++) {\n\n\t\t\t\tuint8_t fdiff;\n\n\t\t\t\tfdiff = dd->template_fdiff[i][j];\n\n\t\t\t\terr |= re_hprintf(pf, \"  <fdiff=%u>\", fdiff);\n\t\t\t}\n\n\t\t\terr |= re_hprintf(pf, \"\\n\");\n\t\t}\n\t\terr |= re_hprintf(pf, \"\\n\");\n\n\t\terr |= re_hprintf(pf, \".... chain_cnt:             %u\\n\",\n\t\t\t\t  dd->chain_cnt);\n\t\terr |= re_hprintf(pf, \"\\n\");\n\n\t\terr |= re_hprintf(pf, \".... template_dti: 2D\\n\");\n\t\tfor (uint8_t tix = 0; tix < dd->template_cnt; tix++) {\n\n\t\t\tfor (uint8_t dtix = 0; dtix < dd->dt_cnt; dtix++) {\n\n\t\t\t\tuint8_t val = dd->template_dti[tix][dtix];\n\n\t\t\t\terr |= re_hprintf(pf,\n\t\t\t\t\t  \".... DTI:  [%u][%u] %u %s\\n\",\n\t\t\t\t\t  tix, dtix, val, dti_name(val));\n\t\t\t}\n\t\t}\n\t}\n\n\terr |= re_hprintf(pf, \"~~~~~~~~~~~~\\n\");\n\terr |= re_hprintf(pf, \"\\n\");\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/dd/dd_enc.c",
    "content": "/**\n * @file dd_enc.c Dependency Descriptor (DD) -- encoder\n *\n * Copyright (C) 2023 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re.h>\n#include <re_dd.h>\n\n\n#define DEBUG_MODULE \"dd\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int mandatory_descriptor_fields(struct putbit *pb, const struct dd *dd)\n{\n\tint err = 0;\n\n\terr |= putbit_one(pb, dd->start_of_frame);\n\terr |= putbit_one(pb, dd->end_of_frame);\n\terr |= putbit_write(pb, 6, dd->frame_dependency_template_id);\n\terr |= putbit_write(pb, 16, dd->frame_number);\n\n\treturn err;\n}\n\n\nstatic uint8_t next_layer(const struct dd *dd, unsigned prev, unsigned next)\n{\n\tif (dd->template_spatial_id[next] == dd->template_spatial_id[prev] &&\n\t    dd->template_temporal_id[next] == dd->template_temporal_id[prev]) {\n\n\t\treturn DD_SAME_LAYER;\n\t}\n\telse if (dd->template_spatial_id[next] ==\n\t\t dd->template_spatial_id[prev] &&\n\t\t dd->template_temporal_id[next] ==\n\t\t dd->template_temporal_id[prev] + 1) {\n\n\t\treturn DD_NEXT_TEMPORAL_LAYER;\n\t}\n\telse if (dd->template_spatial_id[next] ==\n\t\t dd->template_spatial_id[prev] + 1 &&\n\t\t dd->template_temporal_id[next] == 0) {\n\n\t\treturn DD_NEXT_SPATIAL_LAYER;\n\t}\n\n\treturn DD_NO_MORE_TEMPLATES;\n}\n\n\nstatic int template_layers(struct putbit *pb, const struct dd *dd)\n{\n\tint err = 0;\n\n\tfor (unsigned i = 1; i < dd->template_cnt; ++i) {\n\n\t\tuint8_t next_layer_idc = next_layer(dd, i - 1, i);\n\t\tif (next_layer_idc == DD_NO_MORE_TEMPLATES)\n\t\t\treturn EBADMSG;\n\n\t\terr |= putbit_write(pb, 2, next_layer_idc);\n\t}\n\n\t/* end of layers */\n\terr |= putbit_write(pb, 2, DD_NO_MORE_TEMPLATES);\n\n\treturn err;\n}\n\n\nstatic int template_dtis(struct putbit *pb, const struct dd *dd)\n{\n\tfor (uint8_t templateIndex = 0;\n\t     templateIndex < dd->template_cnt;\n\t     templateIndex++) {\n\n\t\tfor (uint8_t dtIndex = 0; dtIndex < dd->dt_cnt; dtIndex++) {\n\n\t\t\t/* See table A.1 below for meaning of DTI values. */\n\n\t\t\tuint8_t v = dd->template_dti[templateIndex][dtIndex];\n\n\t\t\tint err = putbit_write(pb, 2, v);\n\t\t\tif (err)\n\t\t\t\treturn err;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\nstatic int template_fdiffs(struct putbit *pb, const struct dd *dd)\n{\n\tint err;\n\n\tfor (uint8_t templateIndex = 0;\n\t     templateIndex < dd->template_cnt;\n\t     templateIndex++) {\n\n\t\tuint8_t fdiffCnt = dd->template_fdiff_cnt[templateIndex];\n\n\t\tfor (uint8_t j = 0; j < fdiffCnt; j++) {\n\n\t\t\tuint8_t fdiff;\n\n\t\t\tfdiff = dd->template_fdiff[templateIndex][j];\n\n\t\t\t/* fdiff_follows_flag */\n\t\t\terr = putbit_write(pb, 1, true);\n\t\t\tif (err)\n\t\t\t\treturn err;\n\n\t\t\terr = putbit_write(pb, 4, fdiff - 1);\n\t\t\tif (err)\n\t\t\t\treturn err;\n\t\t}\n\n\t\t/* fdiff_follows_flag */\n\t\terr = putbit_write(pb, 1, false);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\nstatic int template_chains(struct putbit *pb, const struct dd *dd)\n{\n\tint err = putbit_write_ns(pb, dd->dt_cnt + 1, dd->chain_cnt);\n\tif (err)\n\t\treturn err;\n\n\tif (dd->chain_cnt == 0)\n\t\treturn 0;\n\n\tfor (uint8_t dtIndex = 0; dtIndex < dd->dt_cnt; dtIndex++) {\n\n\t\tuint8_t val = dd->decode_target_protected_by[dtIndex];\n\n\t\terr = putbit_write_ns(pb, dd->chain_cnt, val);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tfor (uint8_t templateIndex = 0;\n\t     templateIndex < dd->template_cnt;\n\t     templateIndex++) {\n\n\t\tfor (uint8_t chainIndex = 0;\n\t\t     chainIndex < dd->chain_cnt;\n\t\t     chainIndex++) {\n\n\t\t\tuint8_t val;\n\n\t\t       val=dd->template_chain_fdiff[templateIndex][chainIndex];\n\n\t\t\terr = putbit_write(pb, 4, val);\n\t\t\tif (err)\n\t\t\t\treturn err;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\nstatic int render_resolutions(struct putbit *pb, const struct dd *dd)\n{\n\tfor (uint8_t i=0; i<dd->render_count; i++) {\n\n\t\tputbit_write(pb, 16, dd->max_render_width_minus_1[i]);\n\t\tputbit_write(pb, 16, dd->max_render_height_minus_1[i]);\n\t}\n\n\treturn 0;\n}\n\n\nstatic int template_dependency_structure(struct putbit *pb,\n\t\t\t\t\t const struct dd *dd)\n{\n\tint err;\n\n\tuint8_t dt_cnt_minus_one = dd->dt_cnt - 1;\n\n\terr  = putbit_write(pb, 6, dd->template_id_offset);\n\terr |= putbit_write(pb, 5, dt_cnt_minus_one);\n\tif (err)\n\t\treturn err;\n\n\terr = template_layers(pb, dd);\n\tif (err)\n\t\treturn err;\n\n\terr = template_dtis(pb, dd);\n\tif (err)\n\t\treturn err;\n\n\terr = template_fdiffs(pb, dd);\n\tif (err)\n\t\treturn err;\n\n\terr = template_chains(pb, dd);\n\tif (err)\n\t\treturn err;\n\n\terr = putbit_one(pb, dd->resolutions_present_flag);\n\tif (err)\n\t\treturn err;\n\n\tif (dd->resolutions_present_flag) {\n\t\trender_resolutions(pb, dd);\n\t}\n\n\t/* XXX  decode_target_layers() */\n\n\treturn 0;\n}\n\n\nstatic int extended_descriptor_fields(struct putbit *pb, const struct dd *dd)\n{\n\tint err = 0;\n\n\terr |= putbit_one(pb, dd->template_dependency_structure_present_flag);\n\terr |= putbit_one(pb, dd->active_decode_targets_present_flag);\n\terr |= putbit_one(pb, dd->custom_dtis_flag);\n\terr |= putbit_one(pb, dd->custom_fdiffs_flag);\n\terr |= putbit_one(pb, dd->custom_chains_flag);\n\tif (err)\n\t\treturn err;\n\n\tif (dd->template_dependency_structure_present_flag) {\n\n\t\terr = template_dependency_structure(pb, dd);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (dd->active_decode_targets_present_flag) {\n\t\tDEBUG_WARNING(\"no active_decode_targets_present_flag\\n\");\n\t\treturn ENOTSUP;\n\t}\n\n\treturn 0;\n}\n\n\nint dd_encode(struct mbuf *mb, const struct dd *dd)\n{\n\tstruct putbit pb;\n\n\tif (!mb || !dd)\n\t\treturn EINVAL;\n\n\tputbit_init(&pb, mb);\n\n\tint err = mandatory_descriptor_fields(&pb, dd);\n\tif (err)\n\t\treturn err;\n\n\tif (dd->ext) {\n\t\terr = extended_descriptor_fields(&pb, dd);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/dd/putbit.c",
    "content": "/**\n * @file putbit.c Put bits helper\n *\n * Copyright (C) 2023 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re.h>\n#include <re_dd.h>\n\n\nvoid putbit_init(struct putbit *pb, struct mbuf *mb)\n{\n\tif (!pb || !mb)\n\t\treturn;\n\n\tpb->mb      = mb;\n\tpb->bit_pos = 0;\n\n\tmemset(pb->mb->buf, 0, pb->mb->size);\n}\n\n\nint putbit_one(struct putbit *pb, unsigned bit)\n{\n\tif (!pb)\n\t\treturn EINVAL;\n\n\tsize_t byte_pos = pb->bit_pos >> 0x3;\n\n\t/* resize mbuf */\n\tif (byte_pos >= pb->mb->size) {\n\n\t\tint err = mbuf_resize(pb->mb, pb->mb->size * 2);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tuint8_t *p = pb->mb->buf;\n\tsize_t bit_pos = (size_t)(1u << (0x7 - (pb->bit_pos & 0x7)));\n\n\tif (bit) {\n\t\tp[byte_pos] |= bit_pos;\n\t}\n\telse {\n\t\tp[byte_pos] &= ~bit_pos;\n\t}\n\n\t++pb->bit_pos;\n\n\t/* NOTE: mb->pos not used */\n\tmbuf_set_end(pb->mb, (pb->bit_pos + 7) >> 0x3);\n\n\treturn 0;\n}\n\n\nint putbit_write(struct putbit *pb, unsigned count, unsigned val)\n{\n\tif (!pb)\n\t\treturn EINVAL;\n\n\tif (count > 32)\n\t\treturn EINVAL;\n\n\tfor (unsigned i=0; i<count; i++) {\n\n\t\tunsigned shift = count-i-1;\n\t\tunsigned bit = (val >> shift) & 0x1;\n\n\t\tint err = putbit_one(pb, bit);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\nint putbit_write_ns(struct putbit *pb, unsigned n, unsigned v)\n{\n\tif (!pb)\n\t\treturn EINVAL;\n\n\tint err;\n\n#if 0\n\t/* TODO: check this */\n\tif (n == 1)\n\t\treturn EINVAL;\n#endif\n\n\tunsigned w = 0;\n\tunsigned x = n;\n\n\twhile (x != 0) {\n\t\tx = x >> 1;\n\t\t++w;\n\t}\n\n\tunsigned m = (1 << w) - n;\n\tif (v < m)\n\t\terr = putbit_write(pb, w - 1, v);\n\telse\n\t\terr = putbit_write(pb, w, v + m);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/dns/client.c",
    "content": "/**\n * @file dns/client.c  DNS Client\n *\n * Copyright (C) 2010 Creytiv.com\n * Copyright (C) 2022 Sebastian Reimers\n */\n#ifndef WIN32\n#include <arpa/inet.h>\n#include <netdb.h>\n#endif\n\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_udp.h>\n#include <re_tcp.h>\n#include <re_sys.h>\n#include <re_dns.h>\n#include <re_net.h>\n#include <re_main.h>\n\n\n#define DEBUG_MODULE \"dnsc\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tNTX_MAX = 4,\n\tQUERY_HASH_SIZE = 16,\n\tTCP_HASH_SIZE = 2,\n\tCONN_TIMEOUT = 10 * 1000,\n\tIDLE_TIMEOUT = 30 * 1000,\n\tSRVC_MAX = 32,\n\tRR_MAX = 32,\n\tCACHE_TTL_MAX = 1800,\n\tGETADDRINFO_TTL = 60,\n\tRRLV_MAX = 3\n};\n\n\nstruct tcpconn {\n\tstruct le le;\n\tstruct list ql;\n\tstruct tmr tmr;\n\tstruct sa srv;\n\tstruct tcp_conn *conn;\n\tstruct mbuf *mb;\n\tbool connected;\n\tuint16_t flen;\n\tstruct dnsc *dnsc; /* parent */\n};\n\n\nstruct dns_query {\n\tstruct le le;\n\tstruct le le_hdl;\n\tstruct le le_tc;\n\tstruct dnshdr hdr;\n\tstruct tmr tmr;\n\tstruct tmr tmr_ttl;\n\tstruct mbuf mb;\n\tstruct list *rrlv[RRLV_MAX];\n\tchar *name;\n\tconst struct sa *srvv;\n\tconst uint32_t *srvc;\n\tstruct tcpconn *tc;\n\tstruct dnsc *dnsc;     /* parent  */\n\tstruct dns_query **qp; /* app ref */\n\tuint32_t ntx;\n\tuint16_t id;\n\tuint16_t type;\n\tuint16_t dnsclass;\n\tuint8_t opcode;\n\tdns_query_h *qh;\n\tvoid *arg;\n};\n\n\nstruct dnsquery {\n\tstruct dnshdr hdr;\n\tchar *name;\n\tuint16_t type;\n\tuint16_t dnsclass;\n\tstruct list *rrlv;\n\tbool cache;\n\tstruct dnsc *dnsc; /* parent */\n};\n\n\nstruct dnsc {\n\tstruct dnsc_conf conf;\n\tstruct tmr hdl_tmr;\n\tstruct list hdl_cache;\n\tstruct hash *ht_query;\n\tstruct hash *ht_query_cache;\n\tstruct hash *ht_tcpconn;\n\tstruct udp_sock *us;\n\tstruct udp_sock *us6;\n\tstruct sa srvv[SRVC_MAX];\n\tuint32_t srvc;\n};\n\n\nstatic const struct dnsc_conf default_conf = {\n\tQUERY_HASH_SIZE,\n\tTCP_HASH_SIZE,\n\tCONN_TIMEOUT,\n\tIDLE_TIMEOUT,\n\tCACHE_TTL_MAX,\n\tfalse\n};\n\n\nstatic void tcpconn_close(struct tcpconn *tc, int err);\nstatic int  send_tcp(struct dns_query *q);\nstatic void udp_timeout_handler(void *arg);\n\n\nstatic bool rr_unlink_handler(struct le *le, void *arg)\n{\n\tstruct dnsrr *rr = le->data;\n\t(void)arg;\n\n\tif (mem_nrefs(rr) < 2)\n\t\tlist_unlink(&rr->le_priv);\n\tmem_deref(rr);\n\n\treturn false;\n}\n\n\nstatic void query_abort(struct dns_query *q)\n{\n\tif (q->tc) {\n\t\tlist_unlink(&q->le_tc);\n\t\tq->tc = mem_deref(q->tc);\n\t}\n\n\ttmr_cancel(&q->tmr);\n\thash_unlink(&q->le);\n}\n\n\nstatic void query_destructor(void *data)\n{\n\tstruct dns_query *q = data;\n\n\tquery_abort(q);\n\ttmr_cancel(&q->tmr_ttl);\n\tmbuf_reset(&q->mb);\n\tmem_deref(q->name);\n\tlist_unlink(&q->le_hdl);\n\n\tfor (int i = 0; i < RRLV_MAX; i++) {\n\t\t(void)list_apply(q->rrlv[i], true, rr_unlink_handler, NULL);\n\t\tmem_deref(q->rrlv[i]);\n\t}\n}\n\n\nstatic void query_handler(struct dns_query *q, int err, struct list *ansl,\n\t\t\t  struct list *authl, struct list *addl)\n{\n\t/* deref here - before calling handler */\n\tif (q->qp)\n\t\t*q->qp = NULL;\n\n\t/* The handler must only be called _once_ */\n\tif (q->qh) {\n\t\tq->qh(err, &q->hdr, ansl, authl, addl, q->arg);\n\t\tq->qh = NULL;\n\t}\n\n\t/* in case we have more (than one) q refs */\n\tquery_abort(q);\n}\n\n\nstatic bool query_close_handler(struct le *le, void *arg)\n{\n\tstruct dns_query *q = le->data;\n\t(void)arg;\n\n\tquery_handler(q, ECONNABORTED, NULL, NULL, NULL);\n\tmem_deref(q);\n\n\treturn false;\n}\n\n\nstatic bool query_cmp_handler(struct le *le, void *arg)\n{\n\tstruct dns_query *q = le->data;\n\tstruct dnsquery *dq = arg;\n\n\tif (!dq->cache && q->id != dq->hdr.id)\n\t\treturn false;\n\n\tif (q->opcode != dq->hdr.opcode)\n\t\treturn false;\n\n\tif (q->type != dq->type)\n\t\treturn false;\n\n\tif (q->dnsclass != dq->dnsclass)\n\t\treturn false;\n\n\tif (str_casecmp(q->name, dq->name))\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstatic void ttl_timeout_handler(void *arg)\n{\n\tstruct dns_query *q = arg;\n\n\tDEBUG_INFO(\"ttl cache delete (id: %d): %s.\\t%s\\t%s\\n\", q->id, q->name,\n\t\t   dns_rr_classname(q->dnsclass), dns_rr_typename(q->type));\n\n\tmem_deref(q);\n}\n\n\nstatic int reply_recv(struct dnsc *dnsc, struct mbuf *mb)\n{\n\tstruct dns_query *q = NULL;\n\tuint32_t nv[3];\n\tstruct dnsquery dq;\n\tint err = 0;\n\tint64_t ttl;\n\n\tif (!dnsc || !mb)\n\t\treturn EINVAL;\n\n\tttl = dnsc->conf.cache_ttl_max;\n\tdq.name = NULL;\n\tdq.cache = false;\n\n\tif (dns_hdr_decode(mb, &dq.hdr) || !dq.hdr.qr) {\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\terr = dns_dname_decode(mb, &dq.name, 0);\n\tif (err)\n\t\tgoto out;\n\n\tif (mbuf_get_left(mb) < 4) {\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\tdq.type     = ntohs(mbuf_read_u16(mb));\n\tdq.dnsclass = ntohs(mbuf_read_u16(mb));\n\n\tq = list_ledata(hash_lookup(dnsc->ht_query, hash_joaat_str_ci(dq.name),\n\t\t\t\t    query_cmp_handler, &dq));\n\tif (!q) {\n\t\terr = ENOENT;\n\t\tgoto out;\n\t}\n\n\t/* try next server */\n\tif (dq.hdr.rcode == DNS_RCODE_SRV_FAIL && q->ntx < *q->srvc) {\n\n\t\tif (!q->tc) /* try next UDP server immediately */\n\t\t\ttmr_start(&q->tmr, 0, udp_timeout_handler, q);\n\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\n\tnv[0] = dq.hdr.nans;\n\tnv[1] = dq.hdr.nauth;\n\tnv[2] = dq.hdr.nadd;\n\n\tDEBUG_INFO(\"--- ANSWER SECTION id: %d ---\\n\", q->id);\n\tfor (uint32_t i = 0; i < RE_ARRAY_SIZE(nv); i++) {\n\t\tuint32_t l = nv[i];\n\n\t\tif (l > RR_MAX) {\n\t\t\tl = RR_MAX;\n\t\t\tDEBUG_WARNING(\"limit rr records %d\\n\", l);\n\t\t}\n\n\t\tfor (uint32_t j = 0; j < l; j++) {\n\n\t\t\tstruct dnsrr *rr = NULL;\n\n\t\t\terr = dns_rr_decode(mb, &rr, 0);\n\t\t\tif (err) {\n\t\t\t\tquery_handler(q, err, NULL, NULL, NULL);\n\t\t\t\tmem_deref(q);\n\t\t\t\tgoto out;\n\t\t\t}\n\n\t\t\tDEBUG_INFO(\"%H\\n\", dns_rr_print, rr);\n\n\t\t\tlist_append(q->rrlv[i], &rr->le_priv, rr);\n\t\t\tif (rr->ttl < ttl)\n\t\t\t\tttl = rr->ttl;\n\t\t}\n\t}\n\n\tif (q->type == DNS_QTYPE_AXFR) {\n\n\t\tstruct dnsrr *rrh, *rrt;\n\n\t\trrh = list_ledata(list_head(q->rrlv[0]));\n\t\trrt = list_ledata(list_tail(q->rrlv[0]));\n\n\t\t/* Wait for last AXFR reply with terminating SOA record */\n\t\tif (dq.hdr.rcode == DNS_RCODE_OK && dq.hdr.nans > 0 &&\n\t\t    (!rrt || rrt->type != DNS_TYPE_SOA || rrh == rrt)) {\n\t\t\tDEBUG_INFO(\"waiting for last SOA record in reply\\n\");\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\tq->hdr = dq.hdr;\n\tquery_handler(q, 0, q->rrlv[0], q->rrlv[1], q->rrlv[2]);\n\n\n\tif (!dnsc->conf.cache_ttl_max || q->type == DNS_QTYPE_AXFR) {\n\t\tmem_deref(q);\n\t\tgoto out;\n\t}\n\n\t/* Don't cache empty RR answer if authority is also empty. */\n\tif (!dq.hdr.nans && !dq.hdr.nauth) {\n\t\tmem_deref(q);\n\t\tgoto out;\n\t}\n\n\t/* Cache negative answer with SOA minimum value (RFC 2308) */\n\tif (!dq.hdr.nans && dq.hdr.nauth) {\n\t\tconst struct dnsrr *rr = list_ledata(list_head(q->rrlv[1]));\n\n\t\tif (!rr || rr->type != DNS_TYPE_SOA) {\n\t\t\tmem_deref(q);\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (rr->rdata.soa.ttlmin < dnsc->conf.cache_ttl_max)\n\t\t\tttl = rr->rdata.soa.ttlmin;\n\t}\n\n\t/* Cache DNS query with TTL timeout */\n\thash_append(dnsc->ht_query_cache, hash_joaat_str_ci(q->name), &q->le,\n\t\t    q);\n\tDEBUG_INFO(\"cache %s. (id: %d) %d secs\\n\", q->name, q->id, ttl);\n\t/* Fallback to 100ms for faster unit tests */\n\ttmr_start(&q->tmr_ttl, ttl > 1 ? ttl * 1000 : 100,\n\t\t  ttl_timeout_handler, q);\n\n out:\n\tmem_deref(dq.name);\n\n\treturn err;\n}\n\n\nstatic void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\t(void)src;\n\t(void)reply_recv(arg, mb);\n}\n\n\nstatic void tcp_recv_handler(struct mbuf *mbrx, void *arg)\n{\n\tstruct tcpconn *tc = arg;\n\tstruct mbuf *mb = tc->mb;\n\tint err = 0;\n\tsize_t n;\n\n next:\n\t/* frame length */\n\tif (!tc->flen) {\n\n\t\tn = min(2 - mb->end, mbuf_get_left(mbrx));\n\n\t\terr = mbuf_write_mem(mb, mbuf_buf(mbrx), n);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\tmbrx->pos += n;\n\n\t\tif (mb->end < 2)\n\t\t\treturn;\n\n\t\tmb->pos = 0;\n\t\ttc->flen = ntohs(mbuf_read_u16(mb));\n\t\tmb->pos = 0;\n\t\tmb->end = 0;\n\t}\n\n\t/* content */\n\tn = min(tc->flen - mb->end, mbuf_get_left(mbrx));\n\n\terr = mbuf_write_mem(mb, mbuf_buf(mbrx), n);\n\tif (err)\n\t\tgoto error;\n\n\tmbrx->pos += n;\n\n\tif (mb->end < tc->flen)\n\t\treturn;\n\n\tmb->pos = 0;\n\n\terr = reply_recv(tc->dnsc, mb);\n\tif (err)\n\t\tgoto error;\n\n\t/* reset tcp buffer */\n\ttc->flen = 0;\n\tmb->pos = 0;\n\tmb->end = 0;\n\n\t/* more data ? */\n\tif (mbuf_get_left(mbrx) > 0) {\n\t\tDEBUG_INFO(\"%u bytes of tcp data left\\n\", mbuf_get_left(mbrx));\n\t\tgoto next;\n\t}\n\n\treturn;\n\n error:\n\ttcpconn_close(tc, err);\n}\n\n\nstatic void tcpconn_timeout_handler(void *arg)\n{\n\tstruct tcpconn *tc = arg;\n\n\tDEBUG_NOTICE(\"tcp (%J) %s timeout \\n\", &tc->srv,\n\t\t     tc->connected ? \"idle\" : \"connect\");\n\n\ttcpconn_close(tc, ETIMEDOUT);\n}\n\n\nstatic void tcp_estab_handler(void *arg)\n{\n\tstruct tcpconn *tc = arg;\n\tstruct le *le = list_head(&tc->ql);\n\tint err = 0;\n\n\tDEBUG_INFO(\"connection (%J) established\\n\", &tc->srv);\n\n\twhile (le) {\n\t\tstruct dns_query *q = le->data;\n\n\t\tle = le->next;\n\n\t\tq->mb.pos = 0;\n\t\terr = tcp_send(tc->conn, &q->mb);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tDEBUG_INFO(\"tcp send %J\\n\", &tc->srv);\n\t}\n\n\tif (err) {\n\t\ttcpconn_close(tc, err);\n\t\treturn;\n\t}\n\n\ttmr_start(&tc->tmr, tc->dnsc->conf.idle_timeout,\n\t\t  tcpconn_timeout_handler, tc);\n\ttc->connected = true;\n}\n\n\nstatic void tcp_close_handler(int err, void *arg)\n{\n\tstruct tcpconn *tc = arg;\n\n\tDEBUG_NOTICE(\"connection (%J) closed: %m\\n\", &tc->srv, err);\n\ttcpconn_close(tc, err);\n}\n\n\nstatic bool tcpconn_cmp_handler(struct le *le, void *arg)\n{\n\tconst struct tcpconn *tc = le->data;\n\n\t/* avoid trying this connection if dead */\n\tif (!tc->conn)\n\t\treturn false;\n\n\treturn sa_cmp(&tc->srv, arg, SA_ALL);\n}\n\n\nstatic bool tcpconn_fail_handler(struct le *le, void *arg)\n{\n\tstruct dns_query *q = le->data;\n\tint err = *((int *)arg);\n\n\tlist_unlink(&q->le_tc);\n\tq->tc = mem_deref(q->tc);\n\n\tif (q->ntx >= *q->srvc) {\n\t\tDEBUG_WARNING(\"all servers failed, giving up!!\\n\");\n\t\terr = err ? err : ECONNREFUSED;\n\t\tgoto out;\n\t}\n\n\t/* try next server(s) */\n\terr = send_tcp(q);\n\tif (err) {\n\t\tDEBUG_WARNING(\"all servers failed, giving up\\n\");\n\t\tgoto out;\n\t}\n\n out:\n\tif (err) {\n\t\tquery_handler(q, err, NULL, NULL, NULL);\n\t\tmem_deref(q);\n\t}\n\n\treturn false;\n}\n\n\nstatic void tcpconn_close(struct tcpconn *tc, int err)\n{\n\tif (!tc)\n\t\treturn;\n\n\t/* avoid trying this connection again (e.g. same address) */\n\ttc->conn = mem_deref(tc->conn);\n\t(void)list_apply(&tc->ql, true, tcpconn_fail_handler, &err);\n\tmem_deref(tc);\n}\n\n\nstatic void tcpconn_destructor(void *arg)\n{\n\tstruct tcpconn *tc = arg;\n\n\thash_unlink(&tc->le);\n\ttmr_cancel(&tc->tmr);\n\tmem_deref(tc->conn);\n\tmem_deref(tc->mb);\n}\n\n\nstatic int tcpconn_alloc(struct tcpconn **tcpp, struct dnsc *dnsc,\n\t\t\t const struct sa *srv)\n{\n\tstruct tcpconn *tc;\n\tint err = ENOMEM;\n\n\tif (!tcpp || !dnsc || !srv)\n\t\treturn EINVAL;\n\n\ttc = mem_zalloc(sizeof(struct tcpconn), tcpconn_destructor);\n\tif (!tc)\n\t\tgoto out;\n\n\thash_append(dnsc->ht_tcpconn, sa_hash(srv, SA_ALL), &tc->le, tc);\n\ttc->srv = *srv;\n\ttc->dnsc = dnsc;\n\n\ttc->mb = mbuf_alloc(1500);\n\tif (!tc->mb)\n\t\tgoto out;\n\n\terr = tcp_connect(&tc->conn, srv, tcp_estab_handler,\n\t\t\t  tcp_recv_handler, tcp_close_handler, tc);\n\tif (err)\n\t\tgoto out;\n\n\ttmr_start(&tc->tmr, tc->dnsc->conf.conn_timeout,\n\t\t  tcpconn_timeout_handler, tc);\n out:\n\tif (err)\n\t\tmem_deref(tc);\n\telse\n\t\t*tcpp = tc;\n\n\treturn err;\n}\n\n\nstatic int send_tcp(struct dns_query *q)\n{\n\tconst struct sa *srv;\n\tstruct tcpconn *tc;\n\tint err = 0;\n\n\tif (!q)\n\t\treturn EINVAL;\n\n\twhile (q->ntx < *q->srvc) {\n\n\t\tsrv = &q->srvv[q->ntx++];\n\n\t\tDEBUG_NOTICE(\"trying tcp server#%u: %J\\n\", q->ntx-1, srv);\n\n\t\ttc = list_ledata(hash_lookup(q->dnsc->ht_tcpconn,\n\t\t\t\t\t     sa_hash(srv, SA_ALL),\n\t\t\t\t\t     tcpconn_cmp_handler,\n\t\t\t\t\t     (void *)srv));\n\t\tif (!tc) {\n\t\t\terr = tcpconn_alloc(&tc, q->dnsc, srv);\n\t\t\tif (err)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (tc->connected) {\n\t\t\tq->mb.pos = 0;\n\t\t\terr = tcp_send(tc->conn, &q->mb);\n\t\t\tif (err) {\n\t\t\t\ttcpconn_close(tc, err);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttmr_start(&tc->tmr, tc->dnsc->conf.idle_timeout,\n\t\t\t\t  tcpconn_timeout_handler, tc);\n\t\t\tDEBUG_NOTICE(\"tcp send %J\\n\", srv);\n\t\t}\n\n\t\tlist_append(&tc->ql, &q->le_tc, q);\n\t\tq->tc = mem_ref(tc);\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nstatic void tcp_timeout_handler(void *arg)\n{\n\tstruct dns_query *q = arg;\n\n\tquery_handler(q, ETIMEDOUT, NULL, NULL, NULL);\n\tmem_deref(q);\n}\n\n\nstatic int send_udp(struct dns_query *q)\n{\n\tconst struct sa *srv;\n\tint err = ETIMEDOUT;\n\tuint32_t i;\n\n\tif (!q)\n\t\treturn EINVAL;\n\n\tfor (i=0; i<*q->srvc; i++) {\n\n\t\tstruct udp_sock *us;\n\n\t\tsrv = &q->srvv[q->ntx++%*q->srvc];\n\n\t\tDEBUG_INFO(\"trying udp server#%u: %J\\n\", i, srv);\n\n\t\tswitch (sa_af(srv)) {\n\n\t\tcase AF_INET:\n\t\t\tus = q->dnsc->us;\n\t\t\tbreak;\n\n\t\tcase AF_INET6:\n\t\t\tus = q->dnsc->us6;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tcontinue;\n\t\t}\n\n\t\tq->mb.pos = 0;\n\t\terr = udp_send(us, srv, &q->mb);\n\t\tif (!err)\n\t\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nstatic void udp_timeout_handler(void *arg)\n{\n\tstruct dns_query *q = arg;\n\tint err = ETIMEDOUT;\n\n\tif (q->ntx >= NTX_MAX * *q->srvc)\n\t\tgoto out;\n\n\terr = send_udp(q);\n\tif (err)\n\t\tgoto out;\n\n\tint timeout = 500 << MIN(2, (q->ntx - 1) / *q->srvc);\n\n\tDEBUG_INFO(\"waiting udp timeout: %dms\\n\", timeout);\n\ttmr_start(&q->tmr, timeout, udp_timeout_handler, q);\n\n out:\n\tif (err) {\n\t\tquery_handler(q, err, NULL, NULL, NULL);\n\t\tmem_deref(q);\n\t}\n}\n\n\nstatic void hdl_tmr_cache(void *arg)\n{\n\tstruct list *l = arg;\n\tstruct le *le;\n\n\tLIST_FOREACH(l, le) {\n\t\tstruct dns_query *q = le->data;\n#if DEBUG_LEVEL > 5\n\t\tstruct le *re_rr;\n\t\tDEBUG_INFO(\"--- ANSWER SECTION (CACHED) id: %d ---\\n\",\n\t\t\t   q->id);\n\t\tLIST_FOREACH(q->rrlv[0], re_rr) {\n\t\t\tstruct dnsrr *rr = re_rr->data;\n\t\t\tDEBUG_INFO(\"%H\\n\", dns_rr_print, rr);\n\t\t}\n#endif\n\t\tquery_handler(q, 0, q->rrlv[0], q->rrlv[1], q->rrlv[2]);\n\t}\n\tlist_flush(l);\n}\n\n\nstatic bool query_cache_handler(struct dns_query *q)\n{\n\tstruct dnsquery dq;\n\tconst struct dns_query *qc = NULL;\n\tstruct le *le;\n\n\tdq.hdr\t    = q->hdr;\n\tdq.type\t    = q->type;\n\tdq.dnsclass = q->dnsclass;\n\tdq.name\t    = q->name;\n\tdq.cache    = true;\n\n\tqc = list_ledata(hash_lookup(q->dnsc->ht_query_cache,\n\t\t\t\t     hash_joaat_str_ci(q->name),\n\t\t\t\t     query_cmp_handler, &dq));\n\tif (!qc)\n\t\treturn false;\n\n\n\tfor (int i = 0; i < RRLV_MAX; i++) {\n\t\tLIST_FOREACH(qc->rrlv[i], le)\n\t\t{\n\t\t\tstruct dnsrr *rr = le->data;\n\t\t\tmem_ref(rr);\n\t\t}\n\t\tq->rrlv[i] = mem_ref(qc->rrlv[i]);\n\t}\n\n\thash_unlink(&q->le);\n\tlist_append(&q->dnsc->hdl_cache, &q->le_hdl, q);\n\n\ttmr_start(&q->dnsc->hdl_tmr, 0, hdl_tmr_cache, &q->dnsc->hdl_cache);\n\n\treturn true;\n}\n\n\nstatic bool getaddr_dup(struct le *le, void *arg)\n{\n\tstruct dnsrr *r1 = list_ledata(le);\n\tstruct dnsrr *r2 = arg;\n\n\tif (r1->type == DNS_TYPE_A && r2->type == DNS_TYPE_A) {\n\t\tif (r1->rdata.a.addr == r2->rdata.a.addr)\n\t\t\treturn true;\n\t}\n\n\tif (r1->type == DNS_TYPE_AAAA && r2->type == DNS_TYPE_AAAA) {\n\t\tif (0 == memcmp(r1->rdata.aaaa.addr, r2->rdata.aaaa.addr, 16))\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nstatic int async_getaddrinfo(void *arg)\n{\n\tstruct dnsquery *dq = arg;\n\tint err;\n\tstruct addrinfo *res0 = NULL;\n\tstruct addrinfo *res;\n\tstruct addrinfo hints;\n\tstruct sa sa;\n\n\tmemset(&hints, 0, sizeof(hints));\n\n\tif (dq->type == DNS_TYPE_A)\n\t\thints.ai_family = AF_INET;\n\tif (dq->type == DNS_TYPE_AAAA)\n\t\thints.ai_family = AF_INET6;\n\thints.ai_flags = AI_ADDRCONFIG;\n\n\terr = getaddrinfo(dq->name, NULL, &hints, &res0);\n\tif (err)\n\t\treturn EADDRNOTAVAIL;\n\n\tfor (res = res0; res; res = res->ai_next) {\n\t\tstruct dnsrr *rr = dns_rr_alloc();\n\t\tstruct le *le;\n\n\t\tif (!rr) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\tstr_dup(&rr->name, dq->name);\n\n\t\trr->dnsclass = DNS_CLASS_IN;\n\t\trr->ttl\t     = GETADDRINFO_TTL;\n\n\t\terr = sa_set_sa(&sa, res->ai_addr);\n\t\tif (err) {\n\t\t\tmem_deref(rr);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (sa_af(&sa) == AF_INET) {\n\t\t\trr->type\t = DNS_TYPE_A;\n\t\t\trr->rdlen\t = 4;\n\t\t\trr->rdata.a.addr = sa_in(&sa);\n\t\t}\n\n\t\tif (sa_af(&sa) == AF_INET6) {\n\t\t\trr->type  = DNS_TYPE_AAAA;\n\t\t\trr->rdlen = 16;\n\t\t\tsa_in6(&sa, rr->rdata.aaaa.addr);\n\t\t}\n\n\t\tle = list_apply(dq->rrlv, false, getaddr_dup, rr);\n\t\tif (le) {\n\t\t\tmem_deref(rr);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlist_append(dq->rrlv, &rr->le_priv, rr);\n\t}\n\nout:\n\tif (err)\n\t\tlist_flush(dq->rrlv);\n\n\tfreeaddrinfo(res0);\n\n\treturn err;\n}\n\n\nstatic void getaddrinfo_h(int err, void *arg)\n{\n\tstruct dnsquery *dq = arg;\n\tstruct dns_query *q;\n\n\tq = list_ledata(hash_lookup(dq->dnsc->ht_query,\n\t\t\t\t    hash_joaat_str_ci(dq->name),\n\t\t\t\t    query_cmp_handler, dq));\n\tif (!q) {\n\t\tDEBUG_WARNING(\"getaddrinfo_h: no query found\\n\");\n\t\tlist_flush(dq->rrlv);\n\t\tmem_deref(dq->rrlv);\n\t\tgoto out;\n\t}\n\n\tmem_deref(q->rrlv[0]);\n\tq->rrlv[0] = dq->rrlv;\n\n\tconst bool cache = q->dnsc->conf.cache_ttl_max > 0;\n\n\tDEBUG_INFO(\"--- ANSWER SECTION (getaddrinfo) id: %d %s ---\\n\", q->id,\n\t\t   cache ? \"(caching)\" : \"\");\n\n\tif (err) {\n\t\tDEBUG_INFO(\"getaddrinfo_h: err %m\\n\", err);\n\t}\n\telse {\n\t\tstruct le *le;\n\t\tLIST_FOREACH(q->rrlv[0], le)\n\t\t{\n\t\t\tDEBUG_INFO(\"%H%s\\n\", dns_rr_print, le->data);\n\t\t}\n\t}\n\n\tquery_handler(q, err, q->rrlv[0], q->rrlv[1], q->rrlv[2]);\n\n\tif (err || !cache) {\n\t\tmem_deref(q);\n\t\tgoto out;\n\t}\n\n\thash_append(q->dnsc->ht_query_cache, hash_joaat_str_ci(q->name),\n\t\t    &q->le, q);\n\ttmr_start(&q->tmr_ttl, GETADDRINFO_TTL * 1000, ttl_timeout_handler, q);\n\nout:\n\tmem_deref(dq);\n}\n\n\nstatic void dq_deref(void *arg)\n{\n\tstruct dnsquery *dq = arg;\n\n\tmem_deref(dq->dnsc);\n\tmem_deref(dq->name);\n}\n\n\nstatic int query_getaddrinfo(struct dns_query *q)\n{\n\tint err;\n\n\tstruct dnsquery *dq = mem_zalloc(sizeof(struct dnsquery), dq_deref);\n\tif (!dq)\n\t\treturn ENOMEM;\n\n\terr = str_dup(&dq->name, q->name);\n\tif (err)\n\t\tgoto out;\n\n\tdq->type       = q->type;\n\tdq->hdr.id     = q->id;\n\tdq->hdr.opcode = q->opcode;\n\tdq->dnsclass   = q->dnsclass;\n\tdq->dnsc       = mem_ref(q->dnsc);\n\n\tdq->rrlv = mem_alloc(sizeof(struct list), NULL);\n\tif (!dq->rrlv) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tlist_init(dq->rrlv);\n\n\terr = re_thread_async(async_getaddrinfo, getaddrinfo_h, dq);\n\tif (err)\n\t\tDEBUG_WARNING(\"re_thread_async: %m\\n\", err);\n\nout:\n\tif (err)\n\t\tmem_deref(dq);\n\n\treturn err;\n}\n\n\nstatic int query(struct dns_query **qp, struct dnsc *dnsc, uint8_t opcode,\n\t\t const char *name, uint16_t type, uint16_t dnsclass,\n\t\t const struct dnsrr *ans_rr, int proto,\n\t\t const struct sa *srvv, const uint32_t *srvc,\n\t\t bool aa, bool rd, dns_query_h *qh, void *arg)\n{\n\tstruct dns_query *q = NULL;\n\tstruct dnshdr hdr;\n\tint err = 0;\n\tbool use_getaddrinfo = false;\n\tbool srv_available = srvv && srvc && *srvc != 0;\n\n\tif (!dnsc || !name)\n\t\treturn EINVAL;\n\n\tuse_getaddrinfo = dnsc->conf.getaddrinfo &&\n\t\t(type == DNS_TYPE_A || type == DNS_TYPE_AAAA);\n\n\tif (!srv_available && !use_getaddrinfo)\n\t\treturn ENOTSUP;\n\n\tif (DNS_QTYPE_AXFR == type)\n\t\tproto = IPPROTO_TCP;\n\n\tq = mem_zalloc(sizeof(*q), query_destructor);\n\tif (!q)\n\t\tgoto nmerr;\n\n\thash_append(dnsc->ht_query, hash_joaat_str_ci(name), &q->le, q);\n\ttmr_init(&q->tmr);\n\ttmr_init(&q->tmr_ttl);\n\tmbuf_init(&q->mb);\n\n\terr = str_dup(&q->name, name);\n\tif (err)\n\t\tgoto error;\n\n\tq->srvv = srvv;\n\tq->srvc = srvc;\n\tq->id   = rand_u16();\n\tq->type = type;\n\tq->opcode = opcode;\n\tq->dnsclass = dnsclass;\n\tq->dnsc = dnsc;\n\n\tmemset(&hdr, 0, sizeof(hdr));\n\n\thdr.id = q->id;\n\thdr.opcode = q->opcode;\n\thdr.aa = aa;\n\thdr.rd = rd;\n\thdr.nq = 1;\n\thdr.nans = ans_rr ? 1 : 0;\n\n\tq->qh  = qh;\n\tq->arg = arg;\n\tq->hdr = hdr;\n\n\tDEBUG_INFO(\"--- QUESTION SECTION id: %d ---\\n\", q->id);\n\tDEBUG_INFO(\"%s.\\t%s\\t%s\\n\", q->name, dns_rr_classname(q->dnsclass),\n\t\t   dns_rr_typename(q->type));\n\n\tif (query_cache_handler(q))\n\t\tgoto out;\n\n\tfor (int i = 0; i < RRLV_MAX; i++) {\n\t\tq->rrlv[i] = mem_alloc(sizeof(struct list), NULL);\n\t\tif (!q->rrlv[i])\n\t\t\tgoto nmerr;\n\t\tlist_init(q->rrlv[i]);\n\t}\n\n\tif (use_getaddrinfo) {\n\t\terr = query_getaddrinfo(q);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\tgoto out;\n\t}\n\n\tif (proto == IPPROTO_TCP)\n\t\tq->mb.pos += 2;\n\n\terr = dns_hdr_encode(&q->mb, &hdr);\n\tif (err)\n\t\tgoto error;\n\n\terr = dns_dname_encode(&q->mb, name, NULL, 0, false);\n\tif (err)\n\t\tgoto error;\n\n\terr |= mbuf_write_u16(&q->mb, htons(type));\n\terr |= mbuf_write_u16(&q->mb, htons(dnsclass));\n\tif (err)\n\t\tgoto error;\n\n\tif (ans_rr) {\n\t\terr = dns_rr_encode(&q->mb, ans_rr, 0, NULL, 0);\n\t\tif (err)\n\t\t\tgoto error;\n\t}\n\n\tswitch (proto) {\n\n\tcase IPPROTO_TCP:\n\t\tq->mb.pos = 0;\n\t\t(void)mbuf_write_u16(&q->mb, htons((uint16_t)q->mb.end - 2));\n\n\t\terr = send_tcp(q);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\ttmr_start(&q->tmr, 60 * 1000, tcp_timeout_handler, q);\n\t\tbreak;\n\n\tcase IPPROTO_UDP:\n\t\terr = send_udp(q);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\ttmr_start(&q->tmr, 500, udp_timeout_handler, q);\n\t\tbreak;\n\n\tdefault:\n\t\terr = EPROTONOSUPPORT;\n\t\tgoto error;\n\t}\n\nout:\n\tif (qp) {\n\t\tq->qp = qp;\n\t\t*qp = q;\n\t}\n\n\treturn 0;\n\n nmerr:\n\terr = ENOMEM;\n error:\n\tmem_deref(q);\n\n\treturn err;\n}\n\n\n/**\n * Query a DNS name\n *\n * @param qp       Pointer to allocated DNS query\n * @param dnsc     DNS Client\n * @param name     DNS name\n * @param type     DNS Resource Record type\n * @param dnsclass DNS Class\n * @param rd       Recursion Desired (RD) flag\n * @param qh       Query handler\n * @param arg      Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint dnsc_query(struct dns_query **qp, struct dnsc *dnsc, const char *name,\n\t       uint16_t type, uint16_t dnsclass,\n\t       bool rd, dns_query_h *qh, void *arg)\n{\n\tif (!dnsc)\n\t\treturn EINVAL;\n\n\treturn query(qp, dnsc, DNS_OPCODE_QUERY, name, type, dnsclass, NULL,\n\t\t     IPPROTO_UDP, dnsc->srvv, &dnsc->srvc, false, rd, qh, arg);\n}\n\n\n/**\n * Query a DNS name using specific nameservers\n *\n * @param qp       Pointer to allocated DNS query\n * @param dnsc     DNS Client\n * @param name     DNS name\n * @param type     DNS Resource Record type\n * @param dnsclass DNS Class\n * @param proto    Protocol\n * @param srvv     DNS Nameservers\n * @param srvc     Number of DNS nameservers\n * @param rd       Recursion Desired (RD) flag\n * @param qh       Query handler\n * @param arg      Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint dnsc_query_srv(struct dns_query **qp, struct dnsc *dnsc, const char *name,\n\t\t   uint16_t type, uint16_t dnsclass, int proto,\n\t\t   const struct sa *srvv, const uint32_t *srvc,\n\t\t   bool rd, dns_query_h *qh, void *arg)\n{\n\treturn query(qp, dnsc, DNS_OPCODE_QUERY, name, type, dnsclass,\n\t\t     NULL, proto, srvv, srvc, false, rd, qh, arg);\n}\n\n\n/**\n * Send a DNS query with NOTIFY opcode\n *\n * @param qp       Pointer to allocated DNS query\n * @param dnsc     DNS Client\n * @param name     DNS name\n * @param type     DNS Resource Record type\n * @param dnsclass DNS Class\n * @param ans_rr   Answer Resource Record\n * @param proto    Protocol\n * @param srvv     DNS Nameservers\n * @param srvc     Number of DNS nameservers\n * @param qh       Query handler\n * @param arg      Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint dnsc_notify(struct dns_query **qp, struct dnsc *dnsc, const char *name,\n\t\tuint16_t type, uint16_t dnsclass, const struct dnsrr *ans_rr,\n\t\tint proto, const struct sa *srvv, const uint32_t *srvc,\n\t\tdns_query_h *qh, void *arg)\n{\n\treturn query(qp, dnsc, DNS_OPCODE_NOTIFY, name, type, dnsclass,\n\t\t     ans_rr, proto, srvv, srvc, true, false, qh, arg);\n}\n\n\nstatic void dnsc_destructor(void *data)\n{\n\tstruct dnsc *dnsc = data;\n\n\tlist_flush(&dnsc->hdl_cache);\n\n\t(void)hash_apply(dnsc->ht_query, query_close_handler, NULL);\n\thash_flush(dnsc->ht_tcpconn);\n\thash_flush(dnsc->ht_query_cache);\n\ttmr_cancel(&dnsc->hdl_tmr);\n\n\tmem_deref(dnsc->ht_tcpconn);\n\tmem_deref(dnsc->ht_query);\n\tmem_deref(dnsc->ht_query_cache);\n\tmem_deref(dnsc->us6);\n\tmem_deref(dnsc->us);\n}\n\n\n/**\n * Allocate a DNS Client\n *\n * @param dcpp Pointer to allocated DNS Client\n * @param conf Optional DNS configuration, NULL for default\n * @param srvv DNS servers\n * @param srvc Number of DNS Servers\n *\n * @return 0 if success, otherwise errorcode\n */\nint dnsc_alloc(struct dnsc **dcpp, const struct dnsc_conf *conf,\n\t       const struct sa *srvv, uint32_t srvc)\n{\n\tstruct dnsc *dnsc;\n\tstruct sa laddr;\n\tstruct sa laddr6;\n\tint err;\n\n\tif (!dcpp)\n\t\treturn EINVAL;\n\n\tdnsc = mem_zalloc(sizeof(*dnsc), dnsc_destructor);\n\tif (!dnsc)\n\t\treturn ENOMEM;\n\n\tif (conf)\n\t\tdnsc->conf = *conf;\n\telse\n\t\tdnsc->conf = default_conf;\n\n\terr = dnsc_srv_set(dnsc, srvv, srvc);\n\tif (err)\n\t\tgoto out;\n\n\tsa_set_str(&laddr, \"0.0.0.0\", 0);\n\terr  = udp_listen(&dnsc->us, &laddr, udp_recv_handler, dnsc);\n\n\tsa_set_str(&laddr6, \"::\", 0);\n\terr &= udp_listen(&dnsc->us6, &laddr6, udp_recv_handler, dnsc);\n\n\tif (err)\n\t\tgoto out;\n\n\terr = hash_alloc(&dnsc->ht_query, dnsc->conf.query_hash_size);\n\tif (err)\n\t\tgoto out;\n\n\terr = hash_alloc(&dnsc->ht_query_cache, dnsc->conf.query_hash_size);\n\tif (err)\n\t\tgoto out;\n\n\terr = hash_alloc(&dnsc->ht_tcpconn, dnsc->conf.tcp_hash_size);\n\tif (err)\n\t\tgoto out;\n\n\ttmr_init(&dnsc->hdl_tmr);\n\tlist_init(&dnsc->hdl_cache);\n\n out:\n\tif (err)\n\t\tmem_deref(dnsc);\n\telse\n\t\t*dcpp = dnsc;\n\n\treturn err;\n}\n\n\nvoid  dnsc_conf_set_timeout(struct dnsc *dnsc, uint32_t connect, uint32_t idle)\n{\n\tif (!dnsc)\n\t\treturn;\n\n\tdnsc->conf.conn_timeout = connect;\n\tdnsc->conf.idle_timeout = idle;\n}\n\n\nint dnsc_conf_set(struct dnsc *dnsc, const struct dnsc_conf *conf)\n{\n\tint err;\n\tif (!dnsc)\n\t\treturn EINVAL;\n\n\tif (conf)\n\t\tdnsc->conf = *conf;\n\telse\n\t\tdnsc->conf = default_conf;\n\n\tlist_flush(&dnsc->hdl_cache);\n\n\thash_flush(dnsc->ht_tcpconn);\n\thash_flush(dnsc->ht_query_cache);\n\n\tdnsc->ht_query = mem_deref(dnsc->ht_query);\n\tdnsc->ht_query_cache = mem_deref(dnsc->ht_query_cache);\n\tdnsc->ht_tcpconn = mem_deref(dnsc->ht_tcpconn);\n\n\terr = hash_alloc(&dnsc->ht_query, dnsc->conf.query_hash_size);\n\tif (err)\n\t\treturn err;\n\n\terr = hash_alloc(&dnsc->ht_query_cache, dnsc->conf.query_hash_size);\n\tif (err)\n\t\treturn err;\n\n\terr = hash_alloc(&dnsc->ht_tcpconn, dnsc->conf.tcp_hash_size);\n\treturn err;\n}\n\n\n/**\n * Set the DNS Servers on a DNS Client\n *\n * @param dnsc DNS Client\n * @param srvv DNS Nameservers\n * @param srvc Number of nameservers\n *\n * @return 0 if success, otherwise errorcode\n */\nint dnsc_srv_set(struct dnsc *dnsc, const struct sa *srvv, uint32_t srvc)\n{\n\tuint32_t i;\n\n\tif (!dnsc)\n\t\treturn EINVAL;\n\n\tdnsc->srvc = min((uint32_t)RE_ARRAY_SIZE(dnsc->srvv), srvc);\n\n\tif (srvv) {\n\t\tfor (i=0; i<dnsc->srvc; i++)\n\t\t\tdnsc->srvv[i] = srvv[i];\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Flush DNS cache\n *\n * @param dnsc DNS Client\n */\nvoid dnsc_cache_flush(struct dnsc *dnsc)\n{\n\tif (!dnsc)\n\t\treturn;\n\n\thash_flush(dnsc->ht_query_cache);\n}\n\n\n/**\n * Set max. Cache TTL\n *\n * @param dnsc  DNS Client\n * @param max   Value in [s] and 0 to disable caching\n */\nvoid dnsc_cache_max(struct dnsc *dnsc, uint32_t max)\n{\n\tif (!dnsc)\n\t\treturn;\n\n\tdnsc->conf.cache_ttl_max = max;\n\n\tif (!max)\n\t\tdnsc_cache_flush(dnsc);\n}\n\n\n/**\n * Enable/Disable getaddrinfo usage\n *\n * @param dnsc   DNS Client\n * @param active true for enabled, otherwise disabled (default)\n */\nvoid dnsc_getaddrinfo(struct dnsc *dnsc, bool active)\n{\n\tif (!dnsc)\n\t\treturn;\n\n\tdnsc->conf.getaddrinfo = active;\n}\n\n\n/**\n * Return if getaddrinfo usage is enabled\n *\n * @param dnsc  DNS Client\n *\n * @return true if getaddrinfo is used, false otherwise\n */\nbool dnsc_getaddrinfo_enabled(struct dnsc *dnsc)\n{\n\tif (!dnsc)\n\t\treturn false;\n\n\treturn dnsc->conf.getaddrinfo;\n}\n\n\n/**\n * Return if getaddrinfo usage is enabled and exclusive,\n * i.e. there are no DNS servers known explicitly\n *\n * @param dnsc  DNS Client\n *\n * @return true if DNS servers are available, false otherwise\n */\nbool dnsc_getaddrinfo_only(const struct dnsc *dnsc)\n{\n\tif (!dnsc)\n\t\treturn false;\n\n\treturn dnsc->conf.getaddrinfo && dnsc->srvc == 0;\n}\n"
  },
  {
    "path": "src/dns/cstr.c",
    "content": "/**\n * @file cstr.c  DNS character strings encoding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_dns.h>\n\n\n/**\n * Encode a DNS character string into a memory buffer\n *\n * @param mb  Memory buffer to encode into\n * @param str Character string\n *\n * @return 0 if success, otherwise errorcode\n */\nint dns_cstr_encode(struct mbuf *mb, const char *str)\n{\n\tuint8_t len;\n\tint err = 0;\n\n\tif (!mb || !str)\n\t\treturn EINVAL;\n\n\tlen = (uint8_t)strlen(str);\n\n\terr |= mbuf_write_u8(mb, len);\n\terr |= mbuf_write_mem(mb, (const uint8_t *)str, len);\n\n\treturn err;\n}\n\n\n/**\n * Decode a DNS character string from a memory buffer\n *\n * @param mb  Memory buffer to decode from\n * @param str Pointer to allocated character string\n *\n * @return 0 if success, otherwise errorcode\n */\nint dns_cstr_decode(struct mbuf *mb, char **str)\n{\n\tuint8_t len;\n\n\tif (!mb || !str || (mbuf_get_left(mb) < 1))\n\t\treturn EINVAL;\n\n\tlen = mbuf_read_u8(mb);\n\n\tif (mbuf_get_left(mb) < len)\n\t\treturn EBADMSG;\n\n\treturn mbuf_strdup(mb, str, len);\n}\n"
  },
  {
    "path": "src/dns/darwin/srv.c",
    "content": "/**\n * @file darwin/srv.c  Get DNS Server IP code for Mac OS X\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_dns.h>\n#include \"../dns.h\"\n#define __CF_USE_FRAMEWORK_INCLUDES__\n#include <SystemConfiguration/SystemConfiguration.h>\n\n\nint get_darwin_dns(struct sa *nsv, uint32_t *n)\n{\n#if TARGET_OS_IPHONE\n\t(void)nsv;\n\t(void)n;\n\treturn ENOSYS;\n#else\n\tSCDynamicStoreContext context = {0, NULL, NULL, NULL, NULL};\n\tCFArrayRef addresses;\n\tSCDynamicStoreRef store;\n\tCFStringRef key;\n\tCFDictionaryRef dict;\n\tuint32_t c, i;\n\tint err = ENOENT;\n\n\tif (!nsv || !n)\n\t\treturn EINVAL;\n\n\tstore = SCDynamicStoreCreate(NULL, CFSTR(\"get_darwin_dns\"),\n\t\t\t\t     NULL, &context);\n\tif (!store)\n\t\treturn ENOENT;\n\n\tkey = CFSTR(\"State:/Network/Global/DNS\");\n\tdict = SCDynamicStoreCopyValue(store, key);\n\tif (!dict)\n\t\tgoto out1;\n\n\taddresses = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses);\n\tif (!addresses)\n\t\tgoto out;\n\n\tc = (uint32_t)CFArrayGetCount(addresses);\n\t*n = min(*n, c);\n\n\tfor (i=0; i<*n; i++) {\n\t\tCFStringRef address = CFArrayGetValueAtIndex(addresses, i);\n\t\tchar str[64];\n\n\t\tCFStringGetCString(address, str, sizeof(str),\n\t\t\t\t   kCFStringEncodingUTF8);\n\n\t\terr = sa_set_str(&nsv[i], str, DNS_PORT);\n\t\tif (err)\n\t\t\tbreak;\n\t}\n\n out:\n\tCFRelease(dict);\n out1:\n\tCFRelease(store);\n\n\treturn err;\n#endif\n}\n"
  },
  {
    "path": "src/dns/dname.c",
    "content": "/**\n * @file dname.c  DNS domain names\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_dns.h>\n\n\n#define COMP_MASK   0xc0\n#define OFFSET_MASK 0x3fff\n#define COMP_LOOP   255\n\n\nstruct dname {\n\tstruct le he;\n\tsize_t pos;\n\tchar *name;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct dname *dn = arg;\n\n\thash_unlink(&dn->he);\n\tmem_deref(dn->name);\n}\n\n\nstatic void dname_append(struct hash *ht_dname, const char *name, size_t pos)\n{\n\tstruct dname *dn;\n\n\tif (!ht_dname || pos > OFFSET_MASK || !*name)\n\t\treturn;\n\n\tdn = mem_zalloc(sizeof(*dn), destructor);\n\tif (!dn)\n\t\treturn;\n\n\tif (str_dup(&dn->name, name)) {\n\t\tmem_deref(dn);\n\t\treturn;\n\t}\n\n\thash_append(ht_dname, hash_joaat_str_ci(name), &dn->he, dn);\n\tdn->pos = pos;\n}\n\n\nstatic bool lookup_handler(struct le *le, void *arg)\n{\n\tstruct dname *dn = le->data;\n\n\treturn 0 == str_casecmp(dn->name, arg);\n}\n\n\nstatic inline struct dname *dname_lookup(struct hash *ht_dname,\n\t\t\t\t\t const char *name)\n{\n\treturn list_ledata(hash_lookup(ht_dname, hash_joaat_str_ci(name),\n\t\t\t\t       lookup_handler, (void *)name));\n}\n\n\nstatic inline int dname_encode_pointer(struct mbuf *mb, size_t pos)\n{\n\treturn mbuf_write_u16(mb, htons((uint16_t)pos | (COMP_MASK<<8)));\n}\n\n\n/**\n * Encode a DNS Domain name into a memory buffer\n *\n * @param mb       Memory buffer\n * @param name     Domain name\n * @param ht_dname Domain name hashtable\n * @param start    Start position\n * @param comp     Enable compression\n *\n * @return 0 if success, otherwise errorcode\n */\nint dns_dname_encode(struct mbuf *mb, const char *name,\n\t\t     struct hash *ht_dname, size_t start, bool comp)\n{\n\tstruct dname *dn;\n\tsize_t pos;\n\tint err;\n\n\tif (!mb || !name)\n\t\treturn EINVAL;\n\n\tdn = dname_lookup(ht_dname, name);\n\tif (dn && comp)\n\t\treturn dname_encode_pointer(mb, dn->pos);\n\n\tpos = mb->pos;\n\tif (!dn)\n\t\tdname_append(ht_dname, name, pos - start);\n\terr = mbuf_write_u8(mb, 0);\n\n\tif ('.' == name[0] && '\\0' == name[1])\n\t\treturn err;\n\n\twhile (err == 0) {\n\n\t\tconst size_t lablen = mb->pos - pos - 1;\n\n\t\tif ('\\0' == *name) {\n\t\t\tif (!lablen)\n\t\t\t\tbreak;\n\n\t\t\tmb->buf[pos] = (uint8_t)lablen;\n\t\t\terr |= mbuf_write_u8(mb, 0);\n\t\t\tbreak;\n\t\t}\n\t\telse if ('.' == *name) {\n\t\t\tif (!lablen)\n\t\t\t\treturn EINVAL;\n\n\t\t\tmb->buf[pos] = (uint8_t)lablen;\n\n\t\t\tdn = dname_lookup(ht_dname, name + 1);\n\t\t\tif (dn && comp) {\n\t\t\t\terr |= dname_encode_pointer(mb, dn->pos);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tpos = mb->pos;\n\t\t\tif (!dn)\n\t\t\t\tdname_append(ht_dname, name + 1, pos - start);\n\t\t\terr |= mbuf_write_u8(mb, 0);\n\t\t}\n\t\telse {\n\t\t\terr |= mbuf_write_u8(mb, *name);\n\t\t}\n\n\t\t++name;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Decode a DNS domain name from a memory buffer\n *\n * @param mb    Memory buffer to decode from\n * @param name  Pointer to allocated string with domain name\n * @param start Start position\n *\n * @return 0 if success, otherwise errorcode\n */\nint dns_dname_decode(struct mbuf *mb, char **name, size_t start)\n{\n\tuint32_t i = 0, loopc = 0;\n\tbool comp = false;\n\tsize_t pos = 0;\n\tchar buf[256];\n\n\tif (!mb || !name)\n\t\treturn EINVAL;\n\n\twhile (mb->pos < mb->end) {\n\n\t\tuint8_t len = mb->buf[mb->pos++];\n\t\tif (!len) {\n\t\t\tif (comp)\n\t\t\t\tmb->pos = pos;\n\n\t\t\tbuf[i++] = '\\0';\n\n\t\t\t*name = mem_alloc(i, NULL);\n\t\t\tif (!*name)\n\t\t\t\treturn ENOMEM;\n\n\t\t\tstr_ncpy(*name, buf, i);\n\n\t\t\treturn 0;\n\t\t}\n\t\telse if ((len & COMP_MASK) == COMP_MASK) {\n\t\t\tuint16_t offset;\n\n\t\t\tif (loopc++ > COMP_LOOP)\n\t\t\t\tbreak;\n\n\t\t\t--mb->pos;\n\n\t\t\tif (mbuf_get_left(mb) < 2)\n\t\t\t\tbreak;\n\n\t\t\toffset = ntohs(mbuf_read_u16(mb)) & OFFSET_MASK;\n\t\t\tif (!comp) {\n\t\t\t\tpos  = mb->pos;\n\t\t\t\tcomp = true;\n\t\t\t}\n\n\t\t\tmb->pos = offset + start;\n\t\t\tcontinue;\n\t\t}\n\t\telse if (len > mbuf_get_left(mb))\n\t\t\tbreak;\n\t\telse if (len + i + 2 > sizeof(buf))\n\t\t\tbreak;\n\n\t\tif (i > 0)\n\t\t\tbuf[i++] = '.';\n\n\t\twhile (len--)\n\t\t\tbuf[i++] = mb->buf[mb->pos++];\n\t}\n\n\treturn EINVAL;\n}\n"
  },
  {
    "path": "src/dns/dns.h",
    "content": "/**\n * @file dns.h  Internal DNS header file\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#ifdef HAVE_RESOLV\nint get_resolv_dns(struct sa *nsv, uint32_t *n);\n#endif\n#ifdef WIN32\nint get_windns(struct sa *nav, uint32_t *n);\n#endif\n#ifdef DARWIN\nint get_darwin_dns(struct sa *nsv, uint32_t *n);\n#endif\n"
  },
  {
    "path": "src/dns/hdr.c",
    "content": "/**\n * @file dns/hdr.c  DNS header encoding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_dns.h>\n\n\nenum {\n\tQUERY_RESPONSE      = 15,\n\tOPCODE              = 11,\n\tAUTH_ANSWER         = 10,\n\tTRUNCATED           =  9,\n\tRECURSION_DESIRED   =  8,\n\tRECURSION_AVAILABLE =  7,\n\tZERO                =  4\n};\n\n\n/**\n * Encode a DNS header\n *\n * @param mb  Memory buffer to encode header into\n * @param hdr DNS header\n *\n * @return 0 if success, otherwise errorcode\n */\nint dns_hdr_encode(struct mbuf *mb, const struct dnshdr *hdr)\n{\n\tuint16_t flags = 0;\n\tint err = 0;\n\n\tif (!mb || !hdr)\n\t\treturn EINVAL;\n\n\tflags |= hdr->qr     <<QUERY_RESPONSE;\n\tflags |= hdr->opcode <<OPCODE;\n\tflags |= hdr->aa     <<AUTH_ANSWER;\n\tflags |= hdr->tc     <<TRUNCATED;\n\tflags |= hdr->rd     <<RECURSION_DESIRED;\n\tflags |= hdr->ra     <<RECURSION_AVAILABLE;\n\tflags |= hdr->z      <<ZERO;\n\tflags |= hdr->rcode;\n\n\terr |= mbuf_write_u16(mb, htons(hdr->id));\n\terr |= mbuf_write_u16(mb, htons(flags));\n\terr |= mbuf_write_u16(mb, htons(hdr->nq));\n\terr |= mbuf_write_u16(mb, htons(hdr->nans));\n\terr |= mbuf_write_u16(mb, htons(hdr->nauth));\n\terr |= mbuf_write_u16(mb, htons(hdr->nadd));\n\n\treturn err;\n}\n\n\n/**\n * Decode a DNS header from a memory buffer\n *\n * @param mb  Memory buffer to decode header from\n * @param hdr DNS header (output)\n *\n * @return 0 if success, otherwise errorcode\n */\nint dns_hdr_decode(struct mbuf *mb, struct dnshdr *hdr)\n{\n\tuint16_t flags = 0;\n\n\tif (!mb || !hdr || (mbuf_get_left(mb) < DNS_HEADER_SIZE))\n\t\treturn EINVAL;\n\n\thdr->id = ntohs(mbuf_read_u16(mb));\n\tflags   = ntohs(mbuf_read_u16(mb));\n\n\thdr->qr     = 0x1 & (flags >> QUERY_RESPONSE);\n\thdr->opcode = 0xf & (flags >> OPCODE);\n\thdr->aa     = 0x1 & (flags >> AUTH_ANSWER);\n\thdr->tc     = 0x1 & (flags >> TRUNCATED);\n\thdr->rd     = 0x1 & (flags >> RECURSION_DESIRED);\n\thdr->ra     = 0x1 & (flags >> RECURSION_AVAILABLE);\n\thdr->z      = 0x7 & (flags >> ZERO);\n\thdr->rcode  = 0xf & (flags >> 0);\n\n\thdr->nq    = ntohs(mbuf_read_u16(mb));\n\thdr->nans  = ntohs(mbuf_read_u16(mb));\n\thdr->nauth = ntohs(mbuf_read_u16(mb));\n\thdr->nadd  = ntohs(mbuf_read_u16(mb));\n\n\treturn 0;\n}\n\n\n/**\n * Get the string of a DNS opcode\n *\n * @param opcode DNS opcode\n *\n * @return Opcode string\n */\nconst char *dns_hdr_opcodename(uint8_t opcode)\n{\n\tswitch (opcode) {\n\n\tcase DNS_OPCODE_QUERY:  return \"QUERY\";\n\tcase DNS_OPCODE_IQUERY: return \"IQUERY\";\n\tcase DNS_OPCODE_STATUS: return \"STATUS\";\n\tcase DNS_OPCODE_NOTIFY: return \"NOTIFY\";\n\tdefault:                return \"??\";\n\t}\n}\n\n\n/**\n * Get the string of a DNS response code\n *\n * @param rcode Response code\n *\n * @return Response code string\n */\nconst char *dns_hdr_rcodename(uint8_t rcode)\n{\n\tswitch (rcode) {\n\n\tcase DNS_RCODE_OK:       return \"OK\";\n\tcase DNS_RCODE_FMT_ERR:  return \"Format Error\";\n\tcase DNS_RCODE_SRV_FAIL: return \"Server Failure\";\n\tcase DNS_RCODE_NAME_ERR: return \"Name Error\";\n\tcase DNS_RCODE_NOT_IMPL: return \"Not Implemented\";\n\tcase DNS_RCODE_REFUSED:  return \"Refused\";\n\tcase DNS_RCODE_NOT_AUTH: return \"Server Not Authoritative for zone\";\n\tdefault:                 return \"??\";\n\t}\n}\n"
  },
  {
    "path": "src/dns/ns.c",
    "content": "/**\n * @file ns.c  DNS Nameserver configuration\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdio.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_dns.h>\n#include \"dns.h\"\n\n\n#define DEBUG_MODULE \"ns\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int parse_resolv_conf(struct sa *srvv, uint32_t *n)\n{\n\tFILE *f;\n\tuint32_t i = 0;\n\tint err = 0;\n\n\tif (!srvv || !n || !*n)\n\t\treturn EINVAL;\n\n\tf = fopen(\"/etc/resolv.conf\", \"r\");\n\tif (!f)\n\t\treturn errno;\n\n\tfor (;;) {\n\t\tchar line[128];\n\t\tstruct pl srv;\n\t\tsize_t len;\n\n\t\tif (1 != fscanf(f, \"%127[^\\n]\\n\", line))\n\t\t\tbreak;\n\n\t\tif ('#' == line[0] || ';' == line[0])\n\t\t\tcontinue;\n\n\t\tlen = str_len(line);\n\n\t\t/* Use the first entry */\n\t\tif (i < *n && 0 == re_regex(line, len,\n\t\t\t\t\t    \"nameserver [0-9a-f.:]+\", &srv)) {\n\t\t\terr = sa_set(&srvv[i], &srv, DNS_PORT);\n\t\t\tif (err) {\n\t\t\t\tDEBUG_WARNING(\"sa_set: %r (%m)\\n\", &srv, err);\n\t\t\t}\n\t\t\t++i;\n\t\t}\n\t}\n\n\t*n = i;\n\n\t(void)fclose(f);\n\n\treturn err;\n}\n\n\n/**\n * Get the DNS domain and nameservers\n *\n * @param domain Unused\n * @param dsize  Unused\n * @param srvv   Returned nameservers\n * @param n      Nameservers capacity, actual on return\n *\n * @return 0 if success, otherwise errorcode\n */\nint dns_srv_get(char *domain, size_t dsize, struct sa *srvv, uint32_t *n)\n{\n\tint err;\n\t(void)domain;\n\t(void)dsize;\n\n\t/* Try them all in prioritized order */\n\n#ifdef HAVE_RESOLV\n\terr = get_resolv_dns(srvv, n);\n\tif (!err)\n\t\treturn 0;\n#endif\n\n#ifdef DARWIN\n\terr = get_darwin_dns(srvv, n);\n\tif (!err)\n\t\treturn 0;\n#endif\n\n\terr = parse_resolv_conf(srvv, n);\n\tif (!err)\n\t\treturn 0;\n\n#ifdef WIN32\n\terr = get_windns(srvv, n);\n#endif\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/dns/res.c",
    "content": "/**\n * @file res.c  Get DNS Server IP using resolv\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <sys/types.h>\n#include <netinet/in.h>\n#include <arpa/nameser.h>\n#include <resolv.h>\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_dns.h>\n#include <re_mem.h>\n#include \"dns.h\"\n\n\nint get_resolv_dns(struct sa *nsv, uint32_t *n)\n{\n\tstruct __res_state state;\n\tuint32_t i;\n\tint ret, err;\n\n#ifdef OPENBSD\n\tret = res_init();\n\tstate = _res;\n#else\n\tmemset(&state, 0, sizeof(state));\n\tret = res_ninit(&state);\n#endif\n\tif (0 != ret)\n\t\treturn ENOENT;\n\n\tif (!state.nscount) {\n\t\terr = ENOENT;\n\t\tgoto out;\n\t}\n\n\terr = 0;\n#ifdef DARWIN\n\tint memsize = state.nscount * sizeof(union res_sockaddr_union);\n\tunion res_sockaddr_union *addr = mem_alloc(memsize, NULL);\n\tif (!addr) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\tint servers = res_getservers(&state, addr,  state.nscount);\n\n\tfor (i = 0; i < min(*n, (uint32_t)servers) && !err; i++) {\n\t\tif (addr[i].sin.sin_family == AF_INET)\n\t\t\terr |= sa_set_sa(&nsv[i],\n\t\t\t\t\t(struct sockaddr *)&addr[i].sin);\n\t\telse if (addr[i].sin6.sin6_family == AF_INET6)\n\t\t\terr |= sa_set_sa(&nsv[i],\n\t\t\t\t\t(struct sockaddr *)&addr[i].sin6);\n\t\telse\n\t\t\t(void)re_fprintf(stderr,\n\t\t\t\t\t\"get_resolv_dns: Undefined family.\\n\");\n\t}\n\tmem_deref(addr);\n#else\n\tfor (i=0; i<min(*n, (uint32_t)state.nscount) && !err; i++) {\n\t\tstruct sockaddr_in *addr = &state.nsaddr_list[i];\n\t\terr |= sa_set_sa(&nsv[i], (struct sockaddr *)addr);\n\t}\n#endif\n\tif (err)\n\t\tgoto out;\n\n\t*n = i;\n\n out:\n#ifdef OPENBSD\n#else\n\tres_nclose(&state);\n#endif\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/dns/rr.c",
    "content": "/**\n * @file dns/rr.c  DNS Resource Records\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_dns.h>\n\n\nstatic void rr_destructor(void *data)\n{\n\tstruct dnsrr *rr = data;\n\n\tmem_deref(rr->name);\n\n\tswitch (rr->type) {\n\n\tcase DNS_TYPE_NS:\n\t\tmem_deref(rr->rdata.ns.nsdname);\n\t\tbreak;\n\n\tcase DNS_TYPE_CNAME:\n\t\tmem_deref(rr->rdata.cname.cname);\n\t\tbreak;\n\n\tcase DNS_TYPE_SOA:\n\t\tmem_deref(rr->rdata.soa.mname);\n\t\tmem_deref(rr->rdata.soa.rname);\n\t\tbreak;\n\n\tcase DNS_TYPE_PTR:\n\t\tmem_deref(rr->rdata.ptr.ptrdname);\n\t\tbreak;\n\n\tcase DNS_TYPE_MX:\n\t\tmem_deref(rr->rdata.mx.exchange);\n\t\tbreak;\n\n\tcase DNS_TYPE_TXT:\n\t\tmem_deref(rr->rdata.txt.data);\n\t\tbreak;\n\n\tcase DNS_TYPE_SRV:\n\t\tmem_deref(rr->rdata.srv.target);\n\t\tbreak;\n\n\tcase DNS_TYPE_NAPTR:\n\t\tmem_deref(rr->rdata.naptr.flags);\n\t\tmem_deref(rr->rdata.naptr.services);\n\t\tmem_deref(rr->rdata.naptr.regexp);\n\t\tmem_deref(rr->rdata.naptr.replace);\n\t\tbreak;\n\t}\n}\n\n\n/**\n * Allocate a new DNS Resource Record (RR)\n *\n * @return Newly allocated Resource Record, or NULL if no memory\n */\nstruct dnsrr *dns_rr_alloc(void)\n{\n\treturn mem_zalloc(sizeof(struct dnsrr), rr_destructor);\n}\n\n\n/**\n * Duplicate a DNS Resource Record (RR)\n *\n * @param rrp Pointer to new Resource Record\n * @param rr  Resource Record to duplicate\n *\n * @return 0 if success, otherwise errorcode\n */\nint dns_rr_dup(struct dnsrr **rrp, const struct dnsrr *rr)\n{\n\tstruct dnsrr *new_rr;\n\tint err = 0;\n\n\tif (!rrp || !rr)\n\t\treturn EINVAL;\n\n\tnew_rr = dns_rr_alloc();\n\tif (!new_rr)\n\t\treturn ENOMEM;\n\n\tnew_rr->type     = rr->type;\n\tnew_rr->dnsclass = rr->dnsclass;\n\tnew_rr->ttl      = rr->ttl;\n\tnew_rr->rdlen    = rr->rdlen;\n\n\terr = str_dup(&new_rr->name, rr->name);\n\tif (err)\n\t\tgoto error;\n\n\tswitch (rr->type) {\n\n\tcase DNS_TYPE_A:\n\t\tnew_rr->rdata.a.addr = rr->rdata.a.addr;\n\t\tbreak;\n\n\tcase DNS_TYPE_NS:\n\t\terr = str_dup(&new_rr->rdata.ns.nsdname, rr->rdata.ns.nsdname);\n\t\tif (err)\n\t\t\tgoto error;\n\t\tbreak;\n\n\tcase DNS_TYPE_CNAME:\n\t\terr = str_dup(&new_rr->rdata.cname.cname,\n\t\t\t      rr->rdata.cname.cname);\n\t\tif (err)\n\t\t\tgoto error;\n\t\tbreak;\n\n\tcase DNS_TYPE_SOA:\n\t\terr = str_dup(&new_rr->rdata.soa.mname, rr->rdata.soa.mname);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\terr = str_dup(&new_rr->rdata.soa.rname, rr->rdata.soa.rname);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\tnew_rr->rdata.soa.serial  = rr->rdata.soa.serial;\n\t\tnew_rr->rdata.soa.refresh = rr->rdata.soa.refresh;\n\t\tnew_rr->rdata.soa.retry   = rr->rdata.soa.retry;\n\t\tnew_rr->rdata.soa.expire  = rr->rdata.soa.expire;\n\t\tnew_rr->rdata.soa.ttlmin  = rr->rdata.soa.ttlmin;\n\t\tbreak;\n\n\tcase DNS_TYPE_PTR:\n\t\terr = str_dup(&new_rr->rdata.ptr.ptrdname,\n\t\t\t      rr->rdata.ptr.ptrdname);\n\t\tif (err)\n\t\t\tgoto error;\n\t\tbreak;\n\n\tcase DNS_TYPE_MX:\n\t\tnew_rr->rdata.mx.pref = rr->rdata.mx.pref;\n\n\t\terr = str_dup(&new_rr->rdata.mx.exchange,\n\t\t\t      rr->rdata.mx.exchange);\n\t\tif (err)\n\t\t\tgoto error;\n\t\tbreak;\n\n\tcase DNS_TYPE_TXT:\n\t\terr = str_dup(&new_rr->rdata.txt.data, rr->rdata.txt.data);\n\t\tif (err)\n\t\t\tgoto error;\n\t\tbreak;\n\n\tcase DNS_TYPE_AAAA:\n\t\tmemcpy(new_rr->rdata.aaaa.addr, rr->rdata.aaaa.addr, 16);\n\t\tbreak;\n\n\tcase DNS_TYPE_SRV:\n\t\tnew_rr->rdata.srv.pri    = rr->rdata.srv.pri;\n\t\tnew_rr->rdata.srv.weight = rr->rdata.srv.weight;\n\t\tnew_rr->rdata.srv.port   = rr->rdata.srv.port;\n\n\t\terr = str_dup(&new_rr->rdata.srv.target,\n\t\t\t      rr->rdata.srv.target);\n\t\tif (err)\n\t\t\tgoto error;\n\t\tbreak;\n\n\tcase DNS_TYPE_NAPTR:\n\t\tnew_rr->rdata.naptr.order = rr->rdata.naptr.order;\n\t\tnew_rr->rdata.naptr.pref  = rr->rdata.naptr.pref;\n\n\t\terr = str_dup(&new_rr->rdata.naptr.flags,\n\t\t\t      rr->rdata.naptr.flags);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\terr = str_dup(&new_rr->rdata.naptr.services,\n\t\t\t      rr->rdata.naptr.services);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\terr = str_dup(&new_rr->rdata.naptr.regexp,\n\t\t\t      rr->rdata.naptr.regexp);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\terr = str_dup(&new_rr->rdata.naptr.replace,\n\t\t\t      rr->rdata.naptr.replace);\n\t\tif (err)\n\t\t\tgoto error;\n\t\tbreak;\n\n\tdefault:\n\t\terr = ENOSYS;\n\t\tgoto error;\n\t}\n\n\t*rrp = new_rr;\n\treturn 0;\n\n error:\n\tmem_deref(new_rr);\n\treturn err;\n}\n\n\n/**\n * Encode a DNS Resource Record\n *\n * @param mb       Memory buffer to encode into\n * @param rr       DNS Resource Record\n * @param ttl_offs TTL Offset\n * @param ht_dname Domain name hash-table\n * @param start    Start position\n *\n * @return 0 if success, otherwise errorcode\n */\nint dns_rr_encode(struct mbuf *mb, const struct dnsrr *rr, int64_t ttl_offs,\n\t\t  struct hash *ht_dname, size_t start)\n{\n\tuint32_t ttl;\n\tsize_t start_rdata, dlen, len;\n\tchar *ptr;\n\tint err = 0;\n\n\tif (!mb || !rr)\n\t\treturn EINVAL;\n\n\tttl = (uint32_t)((rr->ttl > ttl_offs) ? (rr->ttl - ttl_offs) : 0);\n\n\terr |= dns_dname_encode(mb, rr->name, ht_dname, start, true);\n\terr |= mbuf_write_u16(mb, htons(rr->type));\n\terr |= mbuf_write_u16(mb, htons(rr->dnsclass));\n\terr |= mbuf_write_u32(mb, htonl(ttl));\n\terr |= mbuf_write_u16(mb, htons(rr->rdlen));\n\n\tstart_rdata = mb->pos;\n\n\tswitch (rr->type) {\n\n\tcase DNS_TYPE_A:\n\t\terr |= mbuf_write_u32(mb, htonl(rr->rdata.a.addr));\n\t\tbreak;\n\n\tcase DNS_TYPE_NS:\n\t\terr |= dns_dname_encode(mb, rr->rdata.ns.nsdname,\n\t\t\t\t\tht_dname, start, true);\n\t\tbreak;\n\n\tcase DNS_TYPE_CNAME:\n\t\terr |= dns_dname_encode(mb, rr->rdata.cname.cname,\n\t\t\t\t\tht_dname, start, true);\n\t\tbreak;\n\n\tcase DNS_TYPE_SOA:\n\t\terr |= dns_dname_encode(mb, rr->rdata.soa.mname,\n\t\t\t\t\tht_dname, start, true);\n\t\terr |= dns_dname_encode(mb, rr->rdata.soa.rname,\n\t\t\t\t\tht_dname, start, true);\n\t\terr |= mbuf_write_u32(mb, htonl(rr->rdata.soa.serial));\n\t\terr |= mbuf_write_u32(mb, htonl(rr->rdata.soa.refresh));\n\t\terr |= mbuf_write_u32(mb, htonl(rr->rdata.soa.retry));\n\t\terr |= mbuf_write_u32(mb, htonl(rr->rdata.soa.expire));\n\t\terr |= mbuf_write_u32(mb, htonl(rr->rdata.soa.ttlmin));\n\t\tbreak;\n\n\tcase DNS_TYPE_PTR:\n\t\terr |= dns_dname_encode(mb, rr->rdata.ptr.ptrdname,\n\t\t\t\t\tht_dname, start, true);\n\t\tbreak;\n\n\tcase DNS_TYPE_MX:\n\t\terr |= mbuf_write_u16(mb, htons(rr->rdata.mx.pref));\n\t\terr |= dns_dname_encode(mb, rr->rdata.mx.exchange,\n\t\t\t\t\tht_dname, start, true);\n\t\tbreak;\n\n\tcase DNS_TYPE_TXT:\n\t\tptr  = rr->rdata.txt.data;\n\t\tdlen = str_len(rr->rdata.txt.data);\n\n\t\tdo {\n\t\t\tuint8_t slen = min((uint8_t)dlen, 0xff);\n\n\t\t\terr |= mbuf_write_u8(mb, slen);\n\t\t\terr |= mbuf_write_mem(mb, (uint8_t *)ptr, slen);\n\n\t\t\tptr  += slen;\n\t\t\tdlen -= slen;\n\t\t} while (dlen > 0);\n\t\tbreak;\n\n\tcase DNS_TYPE_AAAA:\n\t\terr |= mbuf_write_mem(mb, rr->rdata.aaaa.addr, 16);\n\t\tbreak;\n\n\tcase DNS_TYPE_SRV:\n\t\terr |= mbuf_write_u16(mb, htons(rr->rdata.srv.pri));\n\t\terr |= mbuf_write_u16(mb, htons(rr->rdata.srv.weight));\n\t\terr |= mbuf_write_u16(mb, htons(rr->rdata.srv.port));\n\t\terr |= dns_dname_encode(mb, rr->rdata.srv.target,\n\t\t\t\t\tht_dname, start, false);\n\t\tbreak;\n\n\tcase DNS_TYPE_NAPTR:\n\t\terr |= mbuf_write_u16(mb, htons(rr->rdata.naptr.order));\n\t\terr |= mbuf_write_u16(mb, htons(rr->rdata.naptr.pref));\n\t\terr |= dns_cstr_encode(mb, rr->rdata.naptr.flags);\n\t\terr |= dns_cstr_encode(mb, rr->rdata.naptr.services);\n\t\terr |= dns_cstr_encode(mb, rr->rdata.naptr.regexp);\n\t\terr |= dns_dname_encode(mb, rr->rdata.naptr.replace,\n\t\t\t\t\tht_dname, start, false);\n\t\tbreak;\n\n\tdefault:\n\t\terr = EINVAL;\n\t\tbreak;\n\t}\n\n\tlen = mb->pos - start_rdata;\n\n\tif (len > 0xffff)\n\t\treturn EOVERFLOW;\n\n\tmb->pos = start_rdata - 2;\n\terr |= mbuf_write_u16(mb, htons((uint16_t)len));\n\tmb->pos += len;\n\n\treturn err;\n}\n\n\n/**\n * Decode a DNS Resource Record (RR) from a memory buffer\n *\n * @param mb    Memory buffer to decode from\n * @param rr    Pointer to allocated Resource Record\n * @param start Start position\n *\n * @return 0 if success, otherwise errorcode\n */\nint dns_rr_decode(struct mbuf *mb, struct dnsrr **rr, size_t start)\n{\n\tint err = 0;\n\tstruct dnsrr *lrr;\n\tuint16_t rdlen;\n\tchar *ptr;\n\n\tif (!mb || !rr)\n\t\treturn EINVAL;\n\n\tlrr = dns_rr_alloc();\n\tif (!lrr)\n\t\treturn ENOMEM;\n\n\terr = dns_dname_decode(mb, &lrr->name, start);\n\tif (err)\n\t\tgoto error;\n\n\tif (mbuf_get_left(mb) < 10)\n\t\tgoto fmerr;\n\n\tlrr->type     = ntohs(mbuf_read_u16(mb));\n\tlrr->dnsclass = ntohs(mbuf_read_u16(mb));\n\tlrr->ttl      = ntohl(mbuf_read_u32(mb));\n\tlrr->rdlen    = ntohs(mbuf_read_u16(mb));\n\n\tif (mbuf_get_left(mb) < lrr->rdlen)\n\t\tgoto fmerr;\n\n\tswitch (lrr->type) {\n\n\tcase DNS_TYPE_A:\n\t\tif (lrr->rdlen != 4)\n\t\t\tgoto fmerr;\n\n\t\tlrr->rdata.a.addr = ntohl(mbuf_read_u32(mb));\n\t\tbreak;\n\n\tcase DNS_TYPE_NS:\n\t\terr = dns_dname_decode(mb, &lrr->rdata.ns.nsdname, start);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_CNAME:\n\t\terr = dns_dname_decode(mb, &lrr->rdata.cname.cname, start);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_SOA:\n\t\terr = dns_dname_decode(mb, &lrr->rdata.soa.mname, start);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\terr = dns_dname_decode(mb, &lrr->rdata.soa.rname, start);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\tif (mbuf_get_left(mb) < 20)\n\t\t\tgoto fmerr;\n\n\t\tlrr->rdata.soa.serial  = ntohl(mbuf_read_u32(mb));\n\t\tlrr->rdata.soa.refresh = ntohl(mbuf_read_u32(mb));\n\t\tlrr->rdata.soa.retry   = ntohl(mbuf_read_u32(mb));\n\t\tlrr->rdata.soa.expire  = ntohl(mbuf_read_u32(mb));\n\t\tlrr->rdata.soa.ttlmin  = ntohl(mbuf_read_u32(mb));\n\t\tbreak;\n\n\tcase DNS_TYPE_PTR:\n\t\terr = dns_dname_decode(mb, &lrr->rdata.ptr.ptrdname, start);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_MX:\n\t\tif (mbuf_get_left(mb) < 2)\n\t\t\tgoto fmerr;\n\n\t\tlrr->rdata.mx.pref = ntohs(mbuf_read_u16(mb));\n\n\t\terr = dns_dname_decode(mb, &lrr->rdata.mx.exchange, start);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_TXT:\n\t\tptr = lrr->rdata.txt.data = mem_alloc(lrr->rdlen + 1, NULL);\n\t\tif (!lrr->rdata.txt.data) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto error;\n\t\t}\n\n\t\trdlen = lrr->rdlen;\n\n\t\twhile (rdlen > 0) {\n\n\t\t\tuint8_t len = mbuf_read_u8(mb);\n\n\t\t\tif (len > --rdlen)\n\t\t\t\tgoto fmerr;\n\n\t\t\terr = mbuf_read_mem(mb, (uint8_t *)ptr, len);\n\t\t\tif (err)\n\t\t\t\tgoto error;\n\n\t\t\tptr   += len;\n\t\t\trdlen -= len;\n\t\t}\n\n\t\t*ptr = '\\0';\n\t\tbreak;\n\n\tcase DNS_TYPE_AAAA:\n\t\tif (lrr->rdlen != 16)\n\t\t\tgoto fmerr;\n\n\t\terr = mbuf_read_mem(mb, lrr->rdata.aaaa.addr, 16);\n\t\tif (err)\n\t\t\tgoto error;\n\t\tbreak;\n\n\tcase DNS_TYPE_SRV:\n\t\tif (mbuf_get_left(mb) < 6)\n\t\t\tgoto fmerr;\n\n\t\tlrr->rdata.srv.pri    = ntohs(mbuf_read_u16(mb));\n\t\tlrr->rdata.srv.weight = ntohs(mbuf_read_u16(mb));\n\t\tlrr->rdata.srv.port   = ntohs(mbuf_read_u16(mb));\n\n\t\terr = dns_dname_decode(mb, &lrr->rdata.srv.target, start);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_NAPTR:\n\t\tif (mbuf_get_left(mb) < 4)\n\t\t\tgoto fmerr;\n\n\t\tlrr->rdata.naptr.order = ntohs(mbuf_read_u16(mb));\n\t\tlrr->rdata.naptr.pref  = ntohs(mbuf_read_u16(mb));\n\n\t\terr = dns_cstr_decode(mb, &lrr->rdata.naptr.flags);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\terr = dns_cstr_decode(mb, &lrr->rdata.naptr.services);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\terr = dns_cstr_decode(mb, &lrr->rdata.naptr.regexp);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\terr = dns_dname_decode(mb, &lrr->rdata.naptr.replace, start);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\tbreak;\n\n\tdefault:\n\t\tmb->pos += lrr->rdlen;\n\t\tbreak;\n\t}\n\n\t*rr = lrr;\n\n\treturn 0;\n\n fmerr:\n\terr = EINVAL;\n error:\n\tmem_deref(lrr);\n\n\treturn err;\n}\n\n\n/**\n * Compare two DNS Resource Records\n *\n * @param rr1   First Resource Record\n * @param rr2   Second Resource Record\n * @param rdata If true, also compares Resource Record data\n *\n * @return True if match, false if not match\n */\nbool dns_rr_cmp(const struct dnsrr *rr1, const struct dnsrr *rr2, bool rdata)\n{\n\tif (!rr1 || !rr2)\n\t\treturn false;\n\n\tif (rr1 == rr2)\n\t\treturn true;\n\n\tif (rr1->type != rr2->type)\n\t\treturn false;\n\n\tif (rr1->dnsclass != rr2->dnsclass)\n\t\treturn false;\n\n\tif (str_casecmp(rr1->name, rr2->name))\n\t\treturn false;\n\n\tif (!rdata)\n\t\treturn true;\n\n\tswitch (rr1->type) {\n\n\tcase DNS_TYPE_A:\n\t\tif (rr1->rdata.a.addr != rr2->rdata.a.addr)\n\t\t\treturn false;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_NS:\n\t\tif (str_casecmp(rr1->rdata.ns.nsdname, rr2->rdata.ns.nsdname))\n\t\t\treturn false;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_CNAME:\n\t\tif (str_casecmp(rr1->rdata.cname.cname,\n\t\t\t\trr2->rdata.cname.cname))\n\t\t\treturn false;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_SOA:\n\t\tif (str_casecmp(rr1->rdata.soa.mname, rr2->rdata.soa.mname))\n\t\t\treturn false;\n\n\t\tif (str_casecmp(rr1->rdata.soa.rname, rr2->rdata.soa.rname))\n\t\t\treturn false;\n\n\t\tif (rr1->rdata.soa.serial != rr2->rdata.soa.serial)\n\t\t\treturn false;\n\n\t\tif (rr1->rdata.soa.refresh != rr2->rdata.soa.refresh)\n\t\t\treturn false;\n\n\t\tif (rr1->rdata.soa.retry != rr2->rdata.soa.retry)\n\t\t\treturn false;\n\n\t\tif (rr1->rdata.soa.expire != rr2->rdata.soa.expire)\n\t\t\treturn false;\n\n\t\tif (rr1->rdata.soa.ttlmin != rr2->rdata.soa.ttlmin)\n\t\t\treturn false;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_PTR:\n\t\tif (str_casecmp(rr1->rdata.ptr.ptrdname,\n\t\t\t\trr2->rdata.ptr.ptrdname))\n\t\t\treturn false;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_MX:\n\t\tif (rr1->rdata.mx.pref != rr2->rdata.mx.pref)\n\t\t\treturn false;\n\n\t\tif (str_casecmp(rr1->rdata.mx.exchange,\n\t\t\t\trr2->rdata.mx.exchange))\n\t\t\treturn false;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_TXT:\n\t\tif (str_casecmp(rr1->rdata.txt.data,\n\t\t\t\trr2->rdata.txt.data))\n\t\t\treturn false;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_AAAA:\n\t\tif (memcmp(rr1->rdata.aaaa.addr, rr2->rdata.aaaa.addr, 16))\n\t\t\treturn false;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_SRV:\n\t\tif (rr1->rdata.srv.pri != rr2->rdata.srv.pri)\n\t\t\treturn false;\n\n\t\tif (rr1->rdata.srv.weight != rr2->rdata.srv.weight)\n\t\t\treturn false;\n\n\t\tif (rr1->rdata.srv.port != rr2->rdata.srv.port)\n\t\t\treturn false;\n\n\t\tif (str_casecmp(rr1->rdata.srv.target, rr2->rdata.srv.target))\n\t\t\treturn false;\n\n\t\tbreak;\n\n\tcase DNS_TYPE_NAPTR:\n\t\tif (rr1->rdata.naptr.order != rr2->rdata.naptr.order)\n\t\t\treturn false;\n\n\t\tif (rr1->rdata.naptr.pref != rr2->rdata.naptr.pref)\n\t\t\treturn false;\n\n\t\t/* todo check case sensitiveness */\n\t\tif (str_casecmp(rr1->rdata.naptr.flags,\n\t\t\t\trr2->rdata.naptr.flags))\n\t\t\treturn false;\n\n\t\t/* todo check case sensitiveness */\n\t\tif (str_casecmp(rr1->rdata.naptr.services,\n\t\t\t\trr2->rdata.naptr.services))\n\t\t\treturn false;\n\n\t\t/* todo check case sensitiveness */\n\t\tif (str_casecmp(rr1->rdata.naptr.regexp,\n\t\t\t\trr2->rdata.naptr.regexp))\n\t\t\treturn false;\n\n\t\t/* todo check case sensitiveness */\n\t\tif (str_casecmp(rr1->rdata.naptr.replace,\n\t\t\t\trr2->rdata.naptr.replace))\n\t\t\treturn false;\n\n\t\tbreak;\n\n\tdefault:\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/**\n * Get the DNS Resource Record (RR) name\n *\n * @param type DNS Resource Record type\n *\n * @return DNS Resource Record name\n */\nconst char *dns_rr_typename(uint16_t type)\n{\n\tswitch (type) {\n\n\tcase DNS_TYPE_A:     return \"A\";\n\tcase DNS_TYPE_NS:    return \"NS\";\n\tcase DNS_TYPE_CNAME: return \"CNAME\";\n\tcase DNS_TYPE_SOA:   return \"SOA\";\n\tcase DNS_TYPE_PTR:   return \"PTR\";\n\tcase DNS_TYPE_MX:    return \"MX\";\n\tcase DNS_TYPE_TXT:   return \"TXT\";\n\tcase DNS_TYPE_AAAA:  return \"AAAA\";\n\tcase DNS_TYPE_SRV:   return \"SRV\";\n\tcase DNS_TYPE_NAPTR: return \"NAPTR\";\n\tcase DNS_QTYPE_IXFR: return \"IXFR\";\n\tcase DNS_QTYPE_AXFR: return \"AXFR\";\n\tcase DNS_QTYPE_ANY:  return \"ANY\";\n\tdefault:             return \"??\";\n\t}\n}\n\n\n/**\n * Get the DNS Resource Record (RR) class name\n *\n * @param dnsclass DNS Class\n *\n * @return DNS Class name\n */\nconst char *dns_rr_classname(uint16_t dnsclass)\n{\n\tswitch (dnsclass) {\n\n\tcase DNS_CLASS_IN:   return \"IN\";\n\tcase DNS_QCLASS_ANY: return \"ANY\";\n\tdefault:             return \"??\";\n\t}\n}\n\n\n/**\n * Print a DNS Resource Record\n *\n * @param pf Print function\n * @param rr DNS Resource Record\n *\n * @return 0 if success, otherwise errorcode\n */\nint dns_rr_print(struct re_printf *pf, const struct dnsrr *rr)\n{\n\tstatic const size_t w = 24;\n\tstruct sa sa;\n\tsize_t n, l;\n\tint err;\n\n\tif (!pf || !rr)\n\t\treturn EINVAL;\n\n\tl = str_len(rr->name);\n\tn = (w > l) ? w - l : 0;\n\n\terr = re_hprintf(pf, \"%s.\", rr->name);\n\twhile (n) {\n\t\terr |= pf->vph(\" \", 1, pf->arg);\n\t\t--n;\n\t}\n\n\terr |= re_hprintf(pf, \" %10lld %-4s %-7s \",\n\t\t\t  rr->ttl,\n\t\t\t  dns_rr_classname(rr->dnsclass),\n\t\t\t  dns_rr_typename(rr->type));\n\n\tswitch (rr->type) {\n\n\tcase DNS_TYPE_A:\n\t\tsa_set_in(&sa, rr->rdata.a.addr, 0);\n\t\terr |= re_hprintf(pf, \"%j\", &sa);\n\t\tbreak;\n\n\tcase DNS_TYPE_NS:\n\t\terr |= re_hprintf(pf, \"%s.\", rr->rdata.ns.nsdname);\n\t\tbreak;\n\n\tcase DNS_TYPE_CNAME:\n\t\terr |= re_hprintf(pf, \"%s.\", rr->rdata.cname.cname);\n\t\tbreak;\n\n\tcase DNS_TYPE_SOA:\n\t\terr |= re_hprintf(pf, \"%s. %s. %u %u %u %u %u\",\n\t\t\t\t  rr->rdata.soa.mname,\n\t\t\t\t  rr->rdata.soa.rname,\n\t\t\t\t  rr->rdata.soa.serial,\n\t\t\t\t  rr->rdata.soa.refresh,\n\t\t\t\t  rr->rdata.soa.retry,\n\t\t\t\t  rr->rdata.soa.expire,\n\t\t\t\t  rr->rdata.soa.ttlmin);\n\t\tbreak;\n\n\tcase DNS_TYPE_PTR:\n\t\terr |= re_hprintf(pf, \"%s.\", rr->rdata.ptr.ptrdname);\n\t\tbreak;\n\n\tcase DNS_TYPE_MX:\n\t\terr |= re_hprintf(pf, \"%3u %s.\", rr->rdata.mx.pref,\n\t\t\t\t  rr->rdata.mx.exchange);\n\t\tbreak;\n\n\tcase DNS_TYPE_TXT:\n\t\terr |= re_hprintf(pf, \"\\\"%s\\\"\", rr->rdata.txt.data);\n\t\tbreak;\n\n\tcase DNS_TYPE_AAAA:\n\t\tsa_set_in6(&sa, rr->rdata.aaaa.addr, 0);\n\t\terr |= re_hprintf(pf, \"%j\", &sa);\n\t\tbreak;\n\n\tcase DNS_TYPE_SRV:\n\t\terr |= re_hprintf(pf, \"%3u %3u %u %s.\",\n\t\t\t\t  rr->rdata.srv.pri,\n\t\t\t\t  rr->rdata.srv.weight,\n\t\t\t\t  rr->rdata.srv.port,\n\t\t\t\t  rr->rdata.srv.target);\n\t\tbreak;\n\n\tcase DNS_TYPE_NAPTR:\n\t\terr |= re_hprintf(pf, \"%3u %3u \\\"%s\\\" \\\"%s\\\" \\\"%s\\\" %s.\",\n\t\t\t\t  rr->rdata.naptr.order,\n\t\t\t\t  rr->rdata.naptr.pref,\n\t\t\t\t  rr->rdata.naptr.flags,\n\t\t\t\t  rr->rdata.naptr.services,\n\t\t\t\t  rr->rdata.naptr.regexp,\n\t\t\t\t  rr->rdata.naptr.replace);\n\t\tbreak;\n\n\tdefault:\n\t\terr |= re_hprintf(pf, \"?\");\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/dns/rrlist.c",
    "content": "/**\n * @file rrlist.c  DNS Resource Records list\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_list.h>\n#include <re_fmt.h>\n#include <re_hash.h>\n#include <re_mbuf.h>\n#include <re_dns.h>\n\n\nenum {\n\tCNAME_RECURSE_MAX = 16,\n};\n\n\nstruct sort {\n\tuint16_t type;\n\tuint32_t key;\n};\n\n\nstatic uint32_t sidx(const struct dnsrr *rr, uint32_t key)\n{\n\tuint32_t addr[4];\n\n\tswitch (rr->type) {\n\n\tcase DNS_TYPE_A:\n\t\treturn rr->rdata.a.addr ^ key;\n\n\tcase DNS_TYPE_AAAA:\n\t\tmemcpy(addr, rr->rdata.aaaa.addr, 16);\n\n\t\treturn addr[0] ^ addr[1] ^ addr[2] ^ addr[3] ^ key;\n\n\tcase DNS_TYPE_SRV:\n\t\treturn ((hash_fast_str(rr->rdata.srv.target) & 0xfff) ^ key) +\n\t\t\trr->rdata.srv.weight;\n\n\tdefault:\n\t\treturn 0;\n\t}\n}\n\n\nstatic bool std_sort_handler(struct le *le1, struct le *le2, void *arg)\n{\n\tstruct dnsrr *rr1 = le1->data;\n\tstruct dnsrr *rr2 = le2->data;\n\tstruct sort *sort = arg;\n\n\tif (sort->type != rr1->type)\n\t\treturn sort->type != rr2->type;\n\n\tif (sort->type != rr2->type)\n\t\treturn true;\n\n\tswitch (sort->type) {\n\n\tcase DNS_TYPE_MX:\n\t\treturn rr1->rdata.mx.pref <= rr2->rdata.mx.pref;\n\n\tcase DNS_TYPE_SRV:\n\t\tif (rr1->rdata.srv.pri == rr2->rdata.srv.pri)\n\t\t\treturn sidx(rr1, sort->key) >= sidx(rr2, sort->key);\n\n\t\treturn rr1->rdata.srv.pri < rr2->rdata.srv.pri;\n\n\tcase DNS_TYPE_NAPTR:\n\t\tif (rr1->rdata.naptr.order == rr2->rdata.naptr.order)\n\t\t\treturn rr1->rdata.naptr.pref <= rr2->rdata.naptr.pref;\n\n\t\treturn rr1->rdata.naptr.order < rr2->rdata.naptr.order;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n\n\nstatic bool addr_sort_handler(struct le *le1, struct le *le2, void *arg)\n{\n\tstruct dnsrr *rr1 = le1->data;\n\tstruct dnsrr *rr2 = le2->data;\n\tstruct sort *sort = arg;\n\n\treturn sidx(rr1, sort->key) >= sidx(rr2, sort->key);\n}\n\n\n/**\n * Sort a list of DNS Resource Records\n *\n * @param rrl  DNS Resource Record list\n * @param type DNS Record type\n * @param key  Sort key\n */\nvoid dns_rrlist_sort(struct list *rrl, uint16_t type, size_t key)\n{\n\tstruct sort sort = {type, (uint32_t)key>>5};\n\n\tlist_sort(rrl, std_sort_handler, &sort);\n}\n\n\n/**\n * Sort a list of A/AAAA DNS Resource Records\n *\n * @param rrl  DNS Resource Record list\n * @param key  Sort key\n */\nvoid dns_rrlist_sort_addr(struct list *rrl, size_t key)\n{\n\tstruct sort sort = {0, (uint32_t)key>>5};\n\n\tlist_sort(rrl, addr_sort_handler, &sort);\n}\n\n\nstatic struct dnsrr *rrlist_apply(struct list *rrl, const char *name,\n\t\t\t\t  uint16_t type1, uint16_t type2,\n\t\t\t\t  uint16_t dnsclass,\n\t\t\t\t  bool recurse, uint32_t depth,\n\t\t\t\t  dns_rrlist_h *rrlh, void *arg)\n{\n\tstruct le *le = list_head(rrl);\n\n\tif (depth > CNAME_RECURSE_MAX)\n\t\treturn NULL;\n\n\twhile (le) {\n\n\t\tstruct dnsrr *rr = le->data;\n\n\t\tle = le->next;\n\n\t\tif (name && str_casecmp(name, rr->name))\n\t\t\tcontinue;\n\n\t\tif (type1 != DNS_QTYPE_ANY && type2 != DNS_QTYPE_ANY &&\n\t\t    rr->type != type1 && rr->type != type2 &&\n\t\t    (rr->type != DNS_TYPE_CNAME || !recurse))\n\t\t\tcontinue;\n\n\t\tif (dnsclass != DNS_QCLASS_ANY && rr->dnsclass != dnsclass)\n\t\t\tcontinue;\n\n\t\tif (!rrlh || rrlh(rr, arg))\n\t\t\treturn rr;\n\n\t\tif (recurse &&\n\t\t    DNS_QTYPE_ANY != type1 && DNS_QTYPE_ANY != type2 &&\n\t\t    DNS_TYPE_CNAME != type1 && DNS_TYPE_CNAME != type2 &&\n\t\t    DNS_TYPE_CNAME == rr->type) {\n\t\t\trr = rrlist_apply(rrl, rr->rdata.cname.cname, type1,\n\t\t\t\t\t  type2, dnsclass, recurse, ++depth,\n\t\t\t\t\t  rrlh, arg);\n\t\t\tif (rr)\n\t\t\t\treturn rr;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Apply a function handler to a list of DNS Resource Records\n *\n * @param rrl      DNS Resource Record list\n * @param name     If set, filter on domain name\n * @param type     If not DNS_QTYPE_ANY, filter on record type\n * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class\n * @param recurse  Cname recursion\n * @param rrlh     Resource record handler\n * @param arg      Handler argument\n *\n * @return Matching Resource Record or NULL\n */\nstruct dnsrr *dns_rrlist_apply(struct list *rrl, const char *name,\n\t\t\t       uint16_t type, uint16_t dnsclass,\n\t\t\t       bool recurse, dns_rrlist_h *rrlh, void *arg)\n{\n\treturn rrlist_apply(rrl, name, type, type, dnsclass,\n\t\t\t    recurse, 0, rrlh, arg);\n}\n\n\n/**\n * Apply a function handler to a list of DNS Resource Records (two types)\n *\n * @param rrl      DNS Resource Record list\n * @param name     If set, filter on domain name\n * @param type1    If not DNS_QTYPE_ANY, filter on record type\n * @param type2    If not DNS_QTYPE_ANY, filter on record type\n * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class\n * @param recurse  Cname recursion\n * @param rrlh     Resource record handler\n * @param arg      Handler argument\n *\n * @return Matching Resource Record or NULL\n */\nstruct dnsrr *dns_rrlist_apply2(struct list *rrl, const char *name,\n\t\t\t\tuint16_t type1, uint16_t type2,\n\t\t\t\tuint16_t dnsclass, bool recurse,\n\t\t\t\tdns_rrlist_h *rrlh, void *arg)\n{\n\treturn rrlist_apply(rrl, name, type1, type2, dnsclass,\n\t\t\t    recurse, 0, rrlh, arg);\n}\n\n\nstatic bool find_handler(struct dnsrr *rr, void *arg)\n{\n\tuint16_t type = *(uint16_t *)arg;\n\n\treturn rr->type == type;\n}\n\n\n/**\n * Find a DNS Resource Record in a list\n *\n * @param rrl      Resource Record list\n * @param name     If set, filter on domain name\n * @param type     If not DNS_QTYPE_ANY, filter on record type\n * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class\n * @param recurse  Cname recursion\n *\n * @return Matching Resource Record or NULL\n */\nstruct dnsrr *dns_rrlist_find(struct list *rrl, const char *name,\n\t\t\t      uint16_t type, uint16_t dnsclass, bool recurse)\n{\n\treturn rrlist_apply(rrl, name, type, type, dnsclass,\n\t\t\t    recurse, 0, find_handler, &type);\n}\n"
  },
  {
    "path": "src/dns/win32/srv.c",
    "content": "/**\n * @file win32/srv.c  Get DNS Server IP code for Windows\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <winsock2.h>\n#include <iphlpapi.h>\n#include <io.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_dns.h>\n#include \"../dns.h\"\n\n\n#define DEBUG_MODULE \"win32/srv\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint get_windns(struct sa *srvv, uint32_t *n)\n{\n\tFIXED_INFO *     FixedInfo = NULL;\n\tULONG            ulOutBufLen;\n\tDWORD            dwRetVal;\n\tIP_ADDR_STRING * pIPAddr;\n\tHANDLE           hLib;\n\tunion {\n\t\tFARPROC proc;\n\t\tDWORD (WINAPI *_GetNetworkParams)(FIXED_INFO*, DWORD*);\n\t} u;\n\tuint32_t i;\n\tint err;\n\n\tif (!srvv || !n || !*n)\n\t\treturn EINVAL;\n\n\thLib = LoadLibrary(TEXT(\"iphlpapi.dll\"));\n\tif (!hLib)\n\t\treturn ENOSYS;\n\n\tu.proc = GetProcAddress(hLib, TEXT(\"GetNetworkParams\"));\n\tif (!u.proc) {\n\t\terr = ENOSYS;\n\t\tgoto out;\n\t}\n\n\tFixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof( FIXED_INFO ));\n\tulOutBufLen = sizeof( FIXED_INFO );\n\n\tif (ERROR_BUFFER_OVERFLOW == (*u._GetNetworkParams)(FixedInfo,\n\t\t\t\t\t\t\t    &ulOutBufLen)) {\n\t\tGlobalFree( FixedInfo );\n\t\tFixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, ulOutBufLen);\n\t}\n\n\tif ((dwRetVal = (*u._GetNetworkParams)( FixedInfo, &ulOutBufLen ))) {\n\t\tDEBUG_WARNING(\"couldn't get network params (%d)\\n\", dwRetVal);\n\t\terr = ENOENT;\n\t\tgoto out;\n\t}\n\n#if 0\n\tprintf( \"Host Name: %s\\n\", FixedInfo->HostName);\n\tprintf( \"Domain Name: %s\\n\", FixedInfo->DomainName);\n\tprintf( \"DNS Servers:\\n\" );\n\tprintf( \"\\t%s\\n\", FixedInfo->DnsServerList.IpAddress.String );\n#endif\n\n\ti = 0;\n\tpIPAddr = &FixedInfo->DnsServerList;\n\twhile (pIPAddr && strlen(pIPAddr->IpAddress.String) > 0) {\n\t\terr = sa_set_str(&srvv[i], pIPAddr->IpAddress.String,\n\t\t\t\t DNS_PORT);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"sa_set_str: %s (%m)\\n\",\n\t\t\t\t      pIPAddr->IpAddress.String, err);\n\t\t}\n\t\tDEBUG_INFO(\"dns ip %u: %j\\n\", i, &srvv[i]);\n\t\t++i;\n\t\tpIPAddr = pIPAddr ->Next;\n\n\t\tif (i >= *n)\n\t\t\tbreak;\n\t}\n\n\t*n = i;\n\tDEBUG_INFO(\"got %u nameservers\\n\", i);\n\terr = i>0 ? 0 : ENOENT;\n\n out:\n\tif (FixedInfo)\n\t\tGlobalFree(FixedInfo);\n\tFreeLibrary(hLib);\n\treturn err;\n}\n"
  },
  {
    "path": "src/fmt/ch.c",
    "content": "/**\n * @file ch.c Character format functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n\n\n/**\n * Convert an ASCII hex character to binary format\n *\n * @param ch ASCII hex character\n *\n * @return Binary value\n */\nuint8_t ch_hex(char ch)\n{\n\tif ('0' <= ch && ch <= '9')\n\t\treturn ch - '0';\n\n\telse if ('A' <= ch && ch <= 'F')\n\t\treturn ch - 'A' + 10;\n\n\telse if ('a' <= ch && ch <= 'f')\n\t\treturn ch - 'a' + 10;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/fmt/hexdump.c",
    "content": "/**\n * @file hexdump.c  Hexadecimal dumping\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <ctype.h>\n#include <re_types.h>\n#include <re_fmt.h>\n\n\n/**\n * Hexadecimal dump of binary buffer. Similar output to HEXDUMP(1)\n *\n * @param f   File stream for output (e.g. stderr, stdout)\n * @param p   Pointer to data\n * @param len Number of bytes\n */\nvoid hexdump(FILE *f, const void *p, size_t len)\n{\n\tconst uint8_t *buf = p;\n\tuint32_t j;\n\tsize_t i;\n\n\tif (!f || !buf)\n\t\treturn;\n\n\tfor (i=0; i < len; i += 16) {\n\n\t\t(void)re_fprintf(f, \"%08zx \", i);\n\n\t\tfor (j=0; j<16; j++) {\n\t\t\tconst size_t pos = i+j;\n\t\t\tif (pos < len)\n\t\t\t\t(void)re_fprintf(f, \" %02x\", buf[pos]);\n\t\t\telse\n\t\t\t\t(void)re_fprintf(f, \"   \");\n\n\t\t\tif (j == 7)\n\t\t\t\t(void)re_fprintf(f, \"  \");\n\t\t}\n\n\t\t(void)re_fprintf(f, \"  |\");\n\n\t\tfor (j=0; j<16; j++) {\n\t\t\tconst size_t pos = i+j;\n\t\t\tuint8_t v;\n\t\t\tif (pos >= len)\n\t\t\t\tbreak;\n\t\t\tv = buf[pos];\n\t\t\t(void)re_fprintf(f, \"%c\", isprint(v) ? v : '.');\n\t\t\tif (j == 7)\n\t\t\t\t(void)re_fprintf(f, \" \");\n\t\t}\n\n\t\t(void)re_fprintf(f, \"|\\n\");\n\t}\n}\n"
  },
  {
    "path": "src/fmt/pl.c",
    "content": "/**\n * @file pl.c Pointer-length functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <ctype.h>\n#include <sys/types.h>\n#ifdef HAVE_STRINGS_H\n#define __EXTENSIONS__ 1\n#include <strings.h>\n#endif\n#include <string.h>\n#include <stdlib.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_fmt.h>\n\n\n/** Pointer-length NULL initialiser */\nconst struct pl pl_null = {NULL, 0};\n\n\nstatic void pl_alloc_destruct(void *arg)\n{\n\tstruct pl *pl = arg;\n\n\tmem_deref((void *)pl->p);\n}\n\n\n/**\n * Allocate a pointer-length object from a NULL-terminated string\n *\n * @param str NULL-terminated string\n *\n * @return Allocated Pointer-length object or NULL\n */\nstruct pl *pl_alloc_str(const char *str)\n{\n\tstruct pl *pl;\n\n\tif (!str)\n\t\treturn NULL;\n\n\tsize_t sz = strlen(str);\n\n\tpl = mem_zalloc(sizeof(struct pl), pl_alloc_destruct);\n\tif (!pl)\n\t\treturn NULL;\n\n\tpl->p = mem_alloc(sz, NULL);\n\tif (!pl->p) {\n\t\tmem_deref(pl);\n\t\treturn NULL;\n\t}\n\n\tmemcpy((void *)pl->p, str, sz);\n\n\tpl->l = sz;\n\n\treturn pl;\n}\n\n\n/**\n * Duplicate a pointer-length object\n *\n * @param src Pointer-length object to duplicate\n *\n * @return Allocated Pointer-length object or NULL\n */\nstruct pl *pl_alloc_dup(const struct pl *src)\n{\n\tstruct pl *pl;\n\n\tif (!src)\n\t\treturn NULL;\n\n\tsize_t sz = src->l;\n\n\tpl = mem_zalloc(sizeof(struct pl), pl_alloc_destruct);\n\tif (!pl)\n\t\treturn NULL;\n\n\tif (!pl_isset(src))\n\t\treturn pl;\n\n\tpl->p = mem_alloc(sz, NULL);\n\tif (!pl->p) {\n\t\tmem_deref(pl);\n\t\treturn NULL;\n\t}\n\n\tmemcpy((void *)pl->p, src->p, sz);\n\tpl->l = sz;\n\treturn pl;\n}\n\n\n/**\n * Initialise a pointer-length object from a NULL-terminated string\n *\n * @param pl  Pointer-length object to be initialised\n * @param str NULL-terminated string\n */\nvoid pl_set_str(struct pl *pl, const char *str)\n{\n\tif (!pl || !str)\n\t\treturn;\n\n\tpl->p = str;\n\tpl->l = strlen(str);\n}\n\n\n/**\n * Initialise a pointer-length object from current position and\n * length of a memory buffer\n *\n * @param pl  Pointer-length object to be initialised\n * @param mb  Memory buffer\n */\nvoid pl_set_mbuf(struct pl *pl, const struct mbuf *mb)\n{\n\tif (!pl || !mb)\n\t\treturn;\n\n\tpl->p = (char *)mbuf_buf(mb);\n\tpl->l = mbuf_get_left(mb);\n}\n\n\n/**\n * Convert a pointer-length object to an int32_t.\n *\n * @param pl Pointer-length object\n *\n * @return int value\n */\nint32_t pl_i32(const struct pl *pl)\n{\n\tint32_t v = 0;\n\tuint32_t mul = 1;\n\tconst char *p;\n\tbool neg = false;\n\n\tif (!pl || !pl->p)\n\t\treturn 0;\n\n\tp = &pl->p[pl->l];\n\twhile (p > pl->p) {\n\t\tconst char ch = *--p;\n\n\t\tif ('0' <= ch && ch <= '9') {\n\t\t\tv -= mul * (ch - '0');\n\t\t\tmul *= 10;\n\t\t}\n\t\telse if (ch == '-' && p == pl->p) {\n\t\t\tneg = true;\n\t\t\tbreak;\n\t\t}\n\t\telse if (ch == '+' && p == pl->p) {\n\t\t\tbreak;\n\t\t}\n\t\telse {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tif (!neg && v == INT32_MIN)\n\t\treturn INT32_MIN;\n\n\treturn neg ? v : -v;\n}\n\n\n/**\n * Convert a pointer-length object to an int64_t.\n *\n * @param pl Pointer-length object\n *\n * @return int value\n */\nint64_t pl_i64(const struct pl *pl)\n{\n\tint64_t v = 0;\n\tuint64_t mul = 1;\n\tconst char *p;\n\tbool neg = false;\n\n\tif (!pl || !pl->p)\n\t\treturn 0;\n\n\tp = &pl->p[pl->l];\n\twhile (p > pl->p) {\n\t\tconst char ch = *--p;\n\n\t\tif ('0' <= ch && ch <= '9') {\n\t\t\tv -= mul * (ch - '0');\n\t\t\tmul *= 10;\n\t\t}\n\t\telse if (ch == '-' && p == pl->p) {\n\t\t\tneg = true;\n\t\t\tbreak;\n\t\t}\n\t\telse if (ch == '+' && p == pl->p) {\n\t\t\tbreak;\n\t\t}\n\t\telse {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tif (!neg && v == INT64_MIN)\n\t\treturn INT64_MIN;\n\n\treturn neg ? v : -v;\n}\n\n\n/**\n * Convert a pointer-length object to a numeric 32-bit value\n *\n * @param pl Pointer-length object\n *\n * @return 32-bit value\n */\nuint32_t pl_u32(const struct pl *pl)\n{\n\tuint32_t v=0, mul=1;\n\tconst char *p;\n\n\tif (!pl || !pl->p)\n\t\treturn 0;\n\n\tp = &pl->p[pl->l];\n\twhile (p > pl->p) {\n\t\tconst uint8_t c = *--p - '0';\n\t\tif (c > 9)\n\t\t\treturn 0;\n\t\tv += mul * c;\n\t\tmul *= 10;\n\t}\n\n\treturn v;\n}\n\n\n/**\n * Convert a hex pointer-length object to a numeric 32-bit value\n *\n * @param pl Pointer-length object\n *\n * @return 32-bit value\n */\nuint32_t pl_x32(const struct pl *pl)\n{\n\tuint32_t v=0, mul=1;\n\tconst char *p;\n\n\tif (!pl || !pl->p)\n\t\treturn 0;\n\n\tp = &pl->p[pl->l];\n\twhile (p > pl->p) {\n\n\t\tconst char ch = *--p;\n\t\tuint8_t c;\n\n\t\tif ('0' <= ch && ch <= '9')\n\t\t\tc = ch - '0';\n\t\telse if ('A' <= ch && ch <= 'F')\n\t\t\tc = ch - 'A' + 10;\n\t\telse if ('a' <= ch && ch <= 'f')\n\t\t\tc = ch - 'a' + 10;\n\t\telse\n\t\t\treturn 0;\n\n\t\tv += mul * c;\n\t\tmul *= 16;\n\t}\n\n\treturn v;\n}\n\n\n/**\n * Convert a pointer-length object to a numeric 64-bit value\n *\n * @param pl Pointer-length object\n *\n * @return 64-bit value\n */\nuint64_t pl_u64(const struct pl *pl)\n{\n\tuint64_t v=0, mul=1;\n\tconst char *p;\n\n\tif (!pl || !pl->p)\n\t\treturn 0;\n\n\tp = &pl->p[pl->l];\n\twhile (p > pl->p) {\n\t\tconst uint8_t c = *--p - '0';\n\t\tif (c > 9)\n\t\t\treturn 0;\n\t\tv += mul * c;\n\t\tmul *= 10;\n\t}\n\n\treturn v;\n}\n\n\n/**\n * Convert a hex pointer-length object to a numeric 64-bit value\n *\n * @param pl Pointer-length object\n *\n * @return 64-bit value\n */\nuint64_t pl_x64(const struct pl *pl)\n{\n\tuint64_t v=0, mul=1;\n\tconst char *p;\n\n\tif (!pl || !pl->p)\n\t\treturn 0;\n\n\tp = &pl->p[pl->l];\n\twhile (p > pl->p) {\n\n\t\tconst char ch = *--p;\n\t\tuint8_t c;\n\n\t\tif ('0' <= ch && ch <= '9')\n\t\t\tc = ch - '0';\n\t\telse if ('A' <= ch && ch <= 'F')\n\t\t\tc = ch - 'A' + 10;\n\t\telse if ('a' <= ch && ch <= 'f')\n\t\t\tc = ch - 'a' + 10;\n\t\telse\n\t\t\treturn 0;\n\n\t\tv += mul * c;\n\t\tmul *= 16;\n\t}\n\n\treturn v;\n}\n\n\n/**\n * Convert a pointer-length object to floating point representation.\n * Both positive and negative numbers are supported, a string with a\n * minus sign ('-') is treated as a negative number.\n *\n * @param pl Pointer-length object\n *\n * @return Double value\n */\ndouble pl_float(const struct pl *pl)\n{\n\tdouble v=0, mul=1;\n\tconst char *p;\n\tbool neg = false;\n\n\tif (!pl || !pl->p)\n\t\treturn 0;\n\n\tp = &pl->p[pl->l];\n\n\twhile (p > pl->p) {\n\n\t\tconst char ch = *--p;\n\n\t\tif ('0' <= ch && ch <= '9') {\n\t\t\tv += mul * (ch - '0');\n\t\t\tmul *= 10;\n\t\t}\n\t\telse if (ch == '.') {\n\t\t\tv /= mul;\n\t\t\tmul = 1;\n\t\t}\n\t\telse if (ch == '-' && p == pl->p) {\n\t\t\tneg = true;\n\t\t}\n\t\telse {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\treturn neg ? -v : v;\n}\n\n\n/**\n * Decode a bool from a pointer-length object\n *\n * @param val Pointer to bool for returned value\n * @param pl  Pointer-length object\n *\n * @return int 0 if success, otherwise errorcode\n */\n\nint pl_bool(bool *val, const struct pl *pl)\n{\n\tconst char *tval[] = {\"1\", \"true\",  \"enable\",  \"yes\", \"on\"};\n\tconst char *fval[] = {\"0\", \"false\", \"disable\", \"no\",  \"off\"};\n\tsize_t i;\n\n\tif (!val || !pl)\n\t\treturn EINVAL;\n\n\tfor (i = 0; i < RE_ARRAY_SIZE(tval); ++i) {\n\t\tif (!pl_strcasecmp(pl, tval[i])) {\n\t\t\t*val = true;\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tfor (i = 0; i < RE_ARRAY_SIZE(fval); ++i) {\n\t\tif (!pl_strcasecmp(pl, fval[i])) {\n\t\t\t*val = false;\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\treturn EINVAL;\n}\n\n\n/**\n * Convert an ASCII hex string as a pointer-length object to binary format\n *\n * @param pl  Pointer-length object\n * @param hex Destination binary buffer\n * @param len Length of binary buffer\n *\n * @return 0 if success, otherwise errorcode\n */\nint pl_hex(const struct pl *pl, uint8_t *hex, size_t len)\n{\n\tif (!pl_isset(pl) || !hex || (pl->l != (2 * len)))\n\t\treturn EINVAL;\n\n\tfor (size_t i = 0; i < pl->l; i += 2) {\n\t\thex[i/2]  = ch_hex(*(pl->p + i)) << 4;\n\t\thex[i/2] += ch_hex(*(pl->p + i +1));\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Check if pointer-length object is set\n *\n * @param pl Pointer-length object\n *\n * @return true if set, false if not set\n */\nbool pl_isset(const struct pl *pl)\n{\n\treturn pl ? pl->p && pl->l : false;\n}\n\n\n/**\n * Copy a pointer-length object to a NULL-terminated string\n *\n * @param pl   Pointer-length object\n * @param str  Buffer for NULL-terminated string\n * @param size Size of buffer\n *\n * @return 0 if success, otherwise errorcode\n */\nint pl_strcpy(const struct pl *pl, char *str, size_t size)\n{\n\tsize_t len;\n\n\tif (!pl || !pl->p || !str || !size)\n\t\treturn EINVAL;\n\n\tlen = min(pl->l, size-1);\n\n\tmemcpy(str, pl->p, len);\n\tstr[len] = '\\0';\n\n\treturn 0;\n}\n\n\n/**\n * Duplicate a pointer-length object to a NULL-terminated string\n *\n * @param dst Pointer to destination string (set on return)\n * @param src Source pointer-length object\n *\n * @return 0 if success, otherwise errorcode\n */\nint pl_strdup(char **dst, const struct pl *src)\n{\n\tchar *p;\n\n\tif (!dst || !src || !src->p)\n\t\treturn EINVAL;\n\n\tp = mem_alloc(src->l+1, NULL);\n\tif (!p)\n\t\treturn ENOMEM;\n\n\tmemcpy(p, src->p, src->l);\n\tp[src->l] = '\\0';\n\n\t*dst = p;\n\n\treturn 0;\n}\n\n\n/**\n * Duplicate a pointer-length object to a new pointer-length object\n *\n * @param dst Destination pointer-length object (set on return)\n * @param src Source pointer-length object\n *\n * @return 0 if success, otherwise errorcode\n */\nint pl_dup(struct pl *dst, const struct pl *src)\n{\n\tchar *p;\n\n\tif (!dst || !src || !src->p)\n\t\treturn EINVAL;\n\n\tp = mem_alloc(src->l, NULL);\n\tif (!p)\n\t\treturn ENOMEM;\n\n\tmemcpy(p, src->p, src->l);\n\n\tdst->p = p;\n\tdst->l = src->l;\n\n\treturn 0;\n}\n\n\n/**\n * Compare a pointer-length object with a NULL-terminated string\n * (case-sensitive)\n *\n * @param pl  Pointer-length object\n * @param str NULL-terminated string\n *\n * @return 0 if match, otherwise errorcode\n */\nint pl_strcmp(const struct pl *pl, const char *str)\n{\n\tstruct pl s;\n\n\tif (!pl || !str)\n\t\treturn EINVAL;\n\n\tpl_set_str(&s, str);\n\n\treturn pl_cmp(pl, &s);\n}\n\n\n/**\n * Compare n characters of a pointer-length object with a NULL-terminated\n * string (case-sensitive)\n *\n * @param pl  Pointer-length object\n * @param str NULL-terminated string\n * @param n   number of characters that should be compared\n *\n * @return 0 if match, otherwise errorcode\n */\nint pl_strncmp(const struct pl *pl, const char *str, size_t n)\n{\n\tif (!pl_isset(pl) || !str || !n)\n\t\treturn EINVAL;\n\n\tif (pl->l < n)\n\t\treturn EINVAL;\n\n\treturn strncmp(pl->p, str, n) == 0 ? 0 : EINVAL;\n}\n\n\n/**\n * Compare n characters of a pointer-length object with a NULL-terminated\n * string (case-insensitive)\n *\n * @param pl  Pointer-length object\n * @param str NULL-terminated string\n * @param n   number of characters that should be compared\n *\n * @return 0 if match, otherwise errorcode\n */\nint pl_strncasecmp(const struct pl *pl, const char *str, size_t n)\n{\n\tif (!pl_isset(pl) || !str || !n)\n\t\treturn EINVAL;\n\n\tif (pl->l < n)\n\t\treturn EINVAL;\n#ifdef WIN32\n\treturn _strnicmp(pl->p, str, n) == 0 ? 0 : EINVAL;\n#else\n\treturn strncasecmp(pl->p, str, n) == 0 ? 0 : EINVAL;\n#endif\n}\n\n\n/**\n * Compare a pointer-length object with a NULL-terminated string\n * (case-insensitive)\n *\n * @param pl  Pointer-length object\n * @param str NULL-terminated string\n *\n * @return 0 if match, otherwise errorcode\n */\nint pl_strcasecmp(const struct pl *pl, const char *str)\n{\n\tstruct pl s;\n\n\tif (!pl || !str)\n\t\treturn EINVAL;\n\n\tpl_set_str(&s, str);\n\n\treturn pl_casecmp(pl, &s);\n}\n\n\n/**\n * Compare two pointer-length objects (case-sensitive)\n *\n * @param pl1  First pointer-length object\n * @param pl2  Second pointer-length object\n *\n * @return 0 if match, otherwise errorcode\n */\nint pl_cmp(const struct pl *pl1, const struct pl *pl2)\n{\n\tif (!pl1 || !pl2)\n\t\treturn EINVAL;\n\n\t/* Different length -> no match */\n\tif (pl1->l != pl2->l)\n\t\treturn EINVAL;\n\n\t/* Zero-length strings are always identical */\n\tif (pl1->l == 0)\n\t\treturn 0;\n\n\t/*\n\t * ~35% speed increase for fmt/pl test\n\t */\n\n\t/* The two pl's are the same */\n\tif (pl1 == pl2)\n\t\treturn 0;\n\n\t/* Two different pl's pointing to same string */\n\tif (pl1->p == pl2->p)\n\t\treturn 0;\n\n\treturn 0 == memcmp(pl1->p, pl2->p, pl1->l) ? 0 : EINVAL;\n}\n\n\n#ifndef HAVE_STRINGS_H\nstatic int casecmp(const struct pl *pl, const char *str)\n{\n\tsize_t i = 0;\n\n#define LOWER(d) ((d) | 0x20202020)\n\tconst uint32_t *p1 = (uint32_t *)pl->p;\n\tconst uint32_t *p2 = (uint32_t *)str;\n\tconst size_t len = pl->l & ~0x3;\n\n\t/* Skip any unaligned pointers */\n\tif (((size_t)pl->p) & (sizeof(void *) - 1))\n\t\tgoto next;\n\tif (((size_t)str) & (sizeof(void *) - 1))\n\t\tgoto next;\n\n\t/* Compare word-wise */\n\tfor (; i<len; i+=4) {\n\t\tif (LOWER(*p1++) != LOWER(*p2++))\n\t\t\treturn EINVAL;\n\t}\n\n next:\n\t/* Compare byte-wise */\n\tfor (; i<pl->l; i++) {\n\t\tif (tolower(pl->p[i]) != tolower(str[i]))\n\t\t\treturn EINVAL;\n\t}\n\n\treturn 0;\n}\n#endif\n\n\n/**\n * Compare two pointer-length objects (case-insensitive)\n *\n * @param pl1  First pointer-length object\n * @param pl2  Second pointer-length object\n *\n * @return 0 if match, otherwise errorcode\n */\nint pl_casecmp(const struct pl *pl1, const struct pl *pl2)\n{\n\tif (!pl1 || !pl2)\n\t\treturn EINVAL;\n\n\t/* Different length -> no match */\n\tif (pl1->l != pl2->l)\n\t\treturn EINVAL;\n\n\t/* Zero-length strings are always identical */\n\tif (pl1->l == 0)\n\t\treturn 0;\n\n\t/*\n\t * ~35% speed increase for fmt/pl test\n\t */\n\n\t/* The two pl's are the same */\n\tif (pl1 == pl2)\n\t\treturn 0;\n\n\t/* Two different pl's pointing to same string */\n\tif (pl1->p == pl2->p)\n\t\treturn 0;\n\n#ifdef HAVE_STRINGS_H\n\treturn 0 == strncasecmp(pl1->p, pl2->p, pl1->l) ? 0 : EINVAL;\n#else\n\treturn casecmp(pl1, pl2->p);\n#endif\n}\n\n\n/**\n * Locate character in pointer-length string\n *\n * @param pl  Pointer-length string\n * @param c   Character to locate\n *\n * @return Pointer to first char if found, otherwise NULL\n */\nconst char *pl_strchr(const struct pl *pl, char c)\n{\n\tconst char *p, *end;\n\n\tif (!pl)\n\t\treturn NULL;\n\n\tend = pl->p + pl->l;\n\tfor (p = pl->p; p < end; p++) {\n\t\tif (*p == c)\n\t\t\treturn p;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Locate the last occurrence of character in pointer-length string\n *\n * @param pl  Pointer-length string\n * @param c   Character to locate\n *\n * @return Pointer to last char if found, otherwise NULL\n */\nconst char *pl_strrchr(const struct pl *pl, char c)\n{\n\tconst char *p, *end;\n\n\tif (!pl_isset(pl))\n\t\treturn NULL;\n\n\tend = pl->p + pl->l - 1;\n\tfor (p = end; p >= pl->p; p--) {\n\t\tif (*p == c)\n\t\t\treturn p;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Locate the first substring in a pointer-length string\n *\n * @param pl  Pointer-length string\n * @param str Substring to locate\n *\n * @return Pointer to first char if substring is found, otherwise NULL\n */\nconst char *pl_strstr(const struct pl *pl, const char *str)\n{\n\tsize_t len = str_len(str);\n\n\t/*case pl not set & pl is not long enough*/\n\tif (!pl_isset(pl) || pl->l < len)\n\t\treturn NULL;\n\n\t/*case str is empty or just '\\0'*/\n\tif (!len)\n\t\treturn pl->p;\n\n\tfor (size_t i = 0; i < pl->l; ++i) {\n\t\t/*case rest of pl is not long enough*/\n\t\tif (pl->l - i < len)\n\t\t\treturn NULL;\n\n\t\tif (!memcmp(pl->p + i, str, len))\n\t\t\treturn pl->p + i;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Trim white space characters at start of pointer-length string\n *\n * @param pl Pointer-length string\n *\n * @return int 0 if success, otherwise errorcode\n */\nint pl_ltrim(struct pl *pl)\n{\n\tif (!pl)\n\t\treturn EINVAL;\n\n\tif (!pl_isset(pl))\n\t\treturn 0;\n\n\tsize_t i = 0;\n\twhile (i < pl->l && isspace((unsigned char)pl->p[i])) {\n\t\t++i;\n\t}\n\n\tif (i == pl->l)\n\t\tpl->l = 0;\n\telse\n\t\tpl_advance(pl, i);\n\n\treturn 0;\n}\n\n\n/**\n * Trim white space characters at end of pointer-length string\n *\n * @param pl Pointer-length string\n *\n * @return int 0 if success, otherwise errorcode\n */\nint pl_rtrim(struct pl *pl)\n{\n\tif (!pl)\n\t\treturn EINVAL;\n\n\tif (!pl_isset(pl))\n\t\treturn 0;\n\n\twhile (pl->l > 0 && isspace((unsigned char)pl->p[pl->l - 1])) {\n\t\t--pl->l;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Trim a pointer-length string on both ends\n *\n * @param pl Pointer-length string\n *\n * @return int 0 if success, otherwise errorcode\n */\nint pl_trim(struct pl *pl)\n{\n\tint err;\n\n\terr  = pl_ltrim(pl);\n\terr |= pl_rtrim(pl);\n\n\treturn err;\n}\n\n\n/**\n * Strip HTML tags from a pointer-length string in-place\n *\n * @param pl Pointer-length string\n */\nvoid pl_strip_html(struct pl *pl)\n{\n\tif (!pl)\n\t\treturn;\n\n\tconst char *r = pl->p;\n\tbool in_tag   = false;\n\n\t/* lookup first possible html tag */\n\tr = memchr(r, '<', pl->l);\n\tif (!r)\n\t\treturn;\n\n\tchar *w = (char *)r;\n\n\t/* Reference: https://html.spec.whatwg.org/multipage/parsing.html */\n\tfor (size_t len = pl->l - (size_t)(r - pl->p); len--; r++) {\n\t\tif (in_tag) {\n\t\t\tif (*r == '>')\n\t\t\t\tin_tag = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* 13.2.5.1 Data state */\n\t\tif (*r == '<' && len >= 1) {\n\t\t\t/* 13.2.5.6 Tag open state */\n\t\t\tunsigned char n = *(r + 1);\n\t\t\tif (isalpha(n) || n == '/' || n == '!' || n == '?') {\n\t\t\t\tin_tag = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tunsigned char c = (unsigned char)*r;\n\t\tif (c >= 0x20 || c == '\\n' || c == '\\r' || c == '\\t') {\n\t\t\t*w++ = *r;\n\t\t}\n\t}\n\n\tpl->l = (size_t)(w - pl->p);\n}\n"
  },
  {
    "path": "src/fmt/print.c",
    "content": "/**\n * @file fmt/print.c Formatted printing\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <math.h>\n#include <re_types.h>\n#include <re_sa.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_btrace.h>\n#ifdef _MSC_VER\n#include <float.h>\n#ifndef isinf\n#define isinf(d) (!_finite(d))\n#endif\n#ifndef isnan\n#define isnan(d) _isnan(d)\n#endif\n#endif\n#ifdef SOLARIS\n#include <ieeefp.h>\n#undef isinf\n#define isinf(a) (fpclass((a)) == FP_NINF || fpclass((a)) == FP_PINF)\n#undef isnan\n#define isnan(a) isnand((a))\n#endif\n\n#define DEBUG_MODULE \"print\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nenum length_modifier {\n\tLENMOD_NONE      = 0,\n\tLENMOD_LONG      = 1,\n\tLENMOD_LONG_LONG = 2,\n\tLENMOD_INT64     = 3,\n\tLENMOD_SIZE      = 42,\n};\n\nenum {\n\tDEC_SIZE = 42,\n\tNUM_SIZE = 64\n};\n\nstatic const char prfx_neg[]  = \"-\";\nstatic const char prfx_hex[]  = \"0x\";\nstatic const char str_nil[]  = \"(nil)\";\n\n\nstatic int write_padded(const char *p, size_t sz, size_t pad, char pch,\n\t\t\tbool plr, const char *prfx, re_vprintf_h *vph,\n\t\t\tvoid *arg)\n{\n\tconst size_t prfx_len = str_len(prfx);\n\tint err = 0;\n\n\tpad -= MIN(pad, prfx_len);\n\n\tif (prfx && pch == '0')\n\t\terr |= vph(prfx, prfx_len, arg);\n\n\twhile (!plr && (pad-- > sz))\n\t\terr |= vph(&pch, 1, arg);\n\n\tif (prfx && pch != '0')\n\t\terr |= vph(prfx, prfx_len, arg);\n\n\tif (p && sz)\n\t\terr |= vph(p, sz, arg);\n\n\twhile (plr && pad-- > sz)\n\t\terr |= vph(&pch, 1, arg);\n\n\treturn err;\n}\n\n\nstatic uint32_t local_itoa(char *buf, uint64_t n, uint8_t base, bool uc)\n{\n\tchar c, *p = buf + (NUM_SIZE - 1);\n\tuint32_t len = 1;\n\tconst char a = uc ? 'A' : 'a';\n\n\t*p = '\\0';\n\tdo {\n\t\tconst uint64_t dv  = n / base;\n\t\tconst uint64_t mul = dv * base;\n\n\t\tc = (char)(n - mul);\n\n\t\tif (c < 10)\n\t\t\t*--p = '0' + c;\n\t\telse\n\t\t\t*--p = a + (c - 10);\n\n\t\tn = dv;\n\t\t++len;\n\n\t} while (n != 0);\n\n\tmemmove(buf, p, len);\n\n\treturn len - 1;\n}\n\n\nstatic size_t local_ftoa(char *buf, double n, size_t dp)\n{\n\tchar *p = buf;\n\tlong long a = (long long)n;\n\tdouble b = n - (double)a;\n\n\tb = (b < 0) ? -b : b;\n\n\t/* integral part */\n\tp += local_itoa(p, (a < 0) ? -a : a, 10, false);\n\n\t*p++ = '.';\n\n\t/* decimal digits */\n\twhile (dp--) {\n\t\tchar v;\n\n\t\tb *= 10;\n\t\tv  = (char)b;\n\t\tb -= v;\n\n\t\t*p++ = '0' + (char)v;\n\t}\n\n\t*p = '\\0';\n\n\treturn p - buf;\n}\n\n\nstatic int vhprintf(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg,\n\t\t    bool safe)\n{\n\tuint8_t base, *bptr;\n\tchar pch = 0, ch, num[NUM_SIZE], addr[64], msg[256];\n\tenum length_modifier lenmod = LENMOD_NONE;\n\tstruct re_printf pf;\n\tbool fm = false, plr = false;\n\tconst struct pl *pl;\n\tsize_t pad = 0, fpad = -1, len, i;\n\tconst char *str, *p = fmt, *p0 = fmt;\n\tconst struct sa *sa;\n\tre_printf_h *ph;\n\tvoid *ph_arg;\n\tva_list *apl;\n\tint err = 0;\n\tvoid *ptr;\n\tuint64_t n;\n\tint64_t sn;\n\tbool uc = false;\n\tdouble dbl;\n\tint errnum;\n#ifndef RELEASE\n\tstruct btrace bt;\n#endif\n\n\tif (!fmt || !vph)\n\t\treturn EINVAL;\n\n\tpf.vph = vph;\n\tpf.arg = arg;\n\n\tfor (;*p && !err; p++) {\n\t\tif (!fm) {\n\t\t\tif (*p != '%')\n\t\t\t\tcontinue;\n\n\t\t\tpch = ' ';\n\t\t\tplr = false;\n\t\t\tpad = 0;\n\t\t\tfpad = -1;\n\t\t\tlenmod = LENMOD_NONE;\n\t\t\tuc = false;\n\n\t\t\tif (p > p0)\n\t\t\t\terr |= vph(p0, p - p0, arg);\n\n\t\t\tfm = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tfm = false;\n\t\tbase = 10;\n\n\t\tswitch (*p) {\n\n\t\tcase '-':\n\t\t\tplr = true;\n\t\t\tfm  = true;\n\t\t\tbreak;\n\n\t\tcase '.':\n\t\t\tfpad = pad;\n\t\t\tpad = 0;\n\t\t\tfm = true;\n\t\t\tbreak;\n\n\t\tcase '%':\n\t\t\tch = '%';\n\n\t\t\terr |= vph(&ch, 1, arg);\n\t\t\tbreak;\n\n\t\tcase 'b':\n\t\t\tRE_VA_ARG(ap, str, const char *, safe);\n\t\t\tRE_VA_ARG(ap, len, size_t, safe);\n\n\t\t\terr |= write_padded(str, str ? len : 0, pad, ' ',\n\t\t\t\t\t    plr, NULL, vph, arg);\n\t\t\tbreak;\n\n\t\tcase 'c':\n\t\t\tRE_VA_ARG(ap, ch, int, safe);\n\n\t\t\terr |= write_padded(&ch, 1, pad, ' ', plr, NULL,\n\t\t\t\t\t    vph, arg);\n\t\t\tbreak;\n\n\t\tcase 'd':\n\t\tcase 'i':\n\t\t\tswitch (lenmod) {\n\n\t\t\tcase LENMOD_INT64:\n\t\t\t\tRE_VA_ARG(ap, sn, int64_t, safe);\n\t\t\t\tbreak;\n\n\t\t\tcase LENMOD_SIZE:\n\t\t\t\tRE_VA_ARG(ap, sn, ssize_t, safe);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\tcase LENMOD_LONG_LONG:\n\t\t\t\tRE_VA_ARG(ap, sn, signed long long, safe);\n\t\t\t\tbreak;\n\n\t\t\tcase LENMOD_LONG:\n\t\t\t\tRE_VA_ARG(ap, sn, signed long, safe);\n\t\t\t\tbreak;\n\n\t\t\tcase LENMOD_NONE:\n\t\t\t\tRE_VA_ARG(ap, sn, signed, safe);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tlen = local_itoa(num, (sn < 0) ? -sn : sn, base,\n\t\t\t\t\t false);\n\n\t\t\terr |= write_padded(num, len, pad,\n\t\t\t\t\t    plr ? ' ' : pch, plr,\n\t\t\t\t\t    (sn < 0) ? prfx_neg : NULL,\n\t\t\t\t\t    vph, arg);\n\t\t\tbreak;\n\n\t\tcase 'f':\n\t\tcase 'F':\n\t\t\tRE_VA_ARG(ap, dbl, double, safe);\n\n\t\t\tif (fpad == (size_t)-1) {\n\t\t\t\tfpad = pad;\n\t\t\t\tpad  = 0;\n\t\t\t}\n\n\t\t\tif (isinf(dbl)) {\n\t\t\t\terr |= write_padded(\"inf\", 3, fpad,\n\t\t\t\t\t\t    ' ', plr, NULL, vph, arg);\n\t\t\t}\n\t\t\telse if (isnan(dbl)) {\n\t\t\t\terr |= write_padded(\"nan\", 3, fpad,\n\t\t\t\t\t\t    ' ', plr, NULL, vph, arg);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlen = local_ftoa(num, dbl,\n\t\t\t\t\t\t pad ? min(pad, DEC_SIZE) : 6);\n\n\t\t\t\terr |= write_padded(num, len, fpad,\n\t\t\t\t\t\t    plr ? ' ' : pch, plr,\n\t\t\t\t\t\t    (dbl<0) ? prfx_neg : NULL,\n\t\t\t\t\t\t    vph, arg);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'H':\n\t\t\tRE_VA_ARG(ap, ph, re_printf_h *, safe);\n\t\t\tRE_VA_ARG(ap, ph_arg, void *, safe);\n\n\t\t\tif (ph)\n\t\t\t\terr |= ph(&pf, ph_arg);\n\t\t\tbreak;\n\n\t\tcase 'l':\n\t\t\t++lenmod;\n\t\t\tfm = true;\n\t\t\tbreak;\n\n\t\tcase 'm':\n\t\t\tRE_VA_ARG(ap, errnum, int, safe);\n\t\t\tstr = str_error(errnum, msg, sizeof(msg));\n\t\t\terr |= write_padded(str, str_len(str), pad,\n\t\t\t\t\t    ' ', plr, NULL, vph, arg);\n\t\t\tbreak;\n\n\t\tcase 'p':\n\t\t\tRE_VA_ARG(ap, ptr, void *, safe);\n\n\t\t\tif (ptr) {\n\t\t\t\tlen = local_itoa(num, (size_t)ptr,\n\t\t\t\t\t\t 16, false);\n\t\t\t\terr |= write_padded(num, len, pad,\n\t\t\t\t\t\t    plr ? ' ' : pch, plr,\n\t\t\t\t\t\t    prfx_hex, vph, arg);\n\t\t\t}\n\t\t\telse {\n\t\t\t\terr |= write_padded(str_nil,\n\t\t\t\t\t\t    sizeof(str_nil) - 1,\n\t\t\t\t\t\t    pad, ' ', plr, NULL,\n\t\t\t\t\t\t    vph, arg);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase 'r':\n\t\t\tRE_VA_ARG(ap, pl, const struct pl *, safe);\n\n\t\t\terr |= write_padded(pl ? pl->p : NULL,\n\t\t\t\t\t    (pl && pl->p) ? pl->l : 0,\n\t\t\t\t\t    pad, ' ', plr, NULL, vph, arg);\n\t\t\tbreak;\n\n\t\tcase 's':\n\t\t\tRE_VA_ARG(ap, str, char *, safe);\n\t\t\terr |= write_padded(str, str_len(str), pad,\n\t\t\t\t\t    ' ', plr, NULL, vph, arg);\n\t\t\tbreak;\n\n\t\tcase 'X':\n\t\t\tuc = true;\n\t\t\t/*@fallthrough@*/\n\t\tcase 'x':\n\t\t\tbase = 16;\n\t\t\t/*@fallthrough@*/\n\t\tcase 'u':\n\t\t\tswitch (lenmod) {\n\n\t\t\tcase LENMOD_INT64:\n\t\t\t\tRE_VA_ARG(ap, n, uint64_t, safe);\n\t\t\t\tbreak;\n\n\t\t\tcase LENMOD_SIZE:\n\t\t\t\tRE_VA_ARG(ap, n, size_t, safe);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\tcase LENMOD_LONG_LONG:\n\t\t\t\tRE_VA_ARG(ap, n, unsigned long long, safe);\n\t\t\t\tbreak;\n\n\t\t\tcase LENMOD_LONG:\n\t\t\t\tRE_VA_ARG(ap, n, unsigned long, safe);\n\t\t\t\tbreak;\n\n\t\t\tcase LENMOD_NONE:\n\t\t\t\tRE_VA_ARG(ap, n, unsigned, safe);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tlen = local_itoa(num, n, base, uc);\n\n\t\t\terr |= write_padded(num, len, pad,\n\t\t\t\t\t    plr ? ' ' : pch, plr, NULL,\n\t\t\t\t\t    vph, arg);\n\t\t\tbreak;\n\n\t\tcase 'v':\n\t\t\tRE_VA_ARG(ap, str, char *, safe);\n\t\t\tRE_VA_ARG(ap, apl, void *, safe);\n\n\t\t\tif (!str || !apl)\n\t\t\t\tbreak;\n\n\t\t\terr |= re_vhprintf(str, *apl, vph, arg);\n\t\t\tbreak;\n\n\t\tcase 'W':\n\t\t\tuc = true;\n\t\t\t/*@fallthrough@*/\n\t\tcase 'w':\n\t\t\tRE_VA_ARG(ap, bptr, void *, safe);\n\t\t\tRE_VA_ARG(ap, len, size_t, safe);\n\n\t\t\tlen = bptr ? len : 0;\n\t\t\tpch = plr ? ' ' : pch;\n\n\t\t\twhile (!plr && pad-- > (len * 2))\n\t\t\t\terr |= vph(&pch, 1, arg);\n\n\t\t\tfor (i=0; i<len; i++) {\n\t\t\t\tconst uint8_t v = *bptr++;\n\t\t\t\tuint32_t l = local_itoa(num, v, 16, uc);\n\t\t\t\terr |= write_padded(num, l, 2, '0',\n\t\t\t\t\t\t    false, NULL, vph, arg);\n\t\t\t}\n\n\t\t\twhile (plr && pad-- > (len * 2))\n\t\t\t\terr |= vph(&pch, 1, arg);\n\n\t\t\tbreak;\n\n\t\tcase 'z':\n\t\t\tlenmod = LENMOD_SIZE;\n\t\t\tfm = true;\n\t\t\tbreak;\n\n\t\tcase 'L':\n\t\t\tlenmod = LENMOD_INT64;\n\t\t\tfm = true;\n\t\t\tbreak;\n\n\t\tcase 'j':\n\t\t\tRE_VA_ARG(ap, sa, struct sa *, safe);\n\t\t\tif (!sa)\n\t\t\t\tbreak;\n\t\t\tif (sa_ntop(sa, addr, sizeof(addr))) {\n\t\t\t\terr |= write_padded(\"?\", 1, pad, ' ',\n\t\t\t\t\t\t    plr, NULL, vph, arg);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\terr |= write_padded(addr, strlen(addr), pad, ' ',\n\t\t\t\t\t    plr, NULL, vph, arg);\n\t\t\tbreak;\n\n\n\t\tcase 'J':\n\t\t\tRE_VA_ARG(ap, sa, struct sa *, safe);\n\t\t\tif (!sa)\n\t\t\t\tbreak;\n\t\t\tif (sa_ntop(sa, addr, sizeof(addr))) {\n\t\t\t\terr |= write_padded(\"?\", 1, pad, ' ',\n\t\t\t\t\t\t    plr, NULL, vph, arg);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (AF_INET6 == sa_af(sa)) {\n\t\t\t\tch = '[';\n\t\t\t\terr |= vph(&ch, 1, arg);\n\t\t\t}\n\t\t\terr |= write_padded(addr, strlen(addr), pad, ' ',\n\t\t\t\t\t    plr, NULL, vph, arg);\n\t\t\tif (AF_INET6 == sa_af(sa)) {\n\t\t\t\tch = ']';\n\t\t\t\terr |= vph(&ch, 1, arg);\n\t\t\t}\n\n\t\t\tch = ':';\n\t\t\terr |= vph(&ch, 1, arg);\n\t\t\tlen = local_itoa(num, sa_port(sa), 10, false);\n\t\t\terr |= write_padded(num, len, pad,\n\t\t\t\t\t    plr ? ' ' : pch, plr, NULL,\n\t\t\t\t\t    vph, arg);\n\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tif (('0' <= *p) && (*p <= '9')) {\n\t\t\t\tif (!pad && ('0' == *p)) {\n\t\t\t\t\tpch = '0';\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tpad *= 10;\n\t\t\t\t\tpad += *p - '0';\n\t\t\t\t}\n\t\t\t\tfm = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tch = '?';\n\n\t\t\terr |= vph(&ch, 1, arg);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!fm)\n\t\t\tp0 = p + 1;\n\t}\n\n\tif (!fm && p > p0)\n\t\terr |= vph(p0, p - p0, arg);\n\nout:\n#ifndef RELEASE\n\tif (err == ENODATA) {\n\t\tbtrace(&bt);\n\t\tre_fprintf(stderr, \"Format: \\\"%b<-- NO ARG\\n%H\\n\", fmt,\n\t\t\t   p - fmt + 1, btrace_println, &bt);\n\t\tre_assert(0 && \"RE_VA_ARG: no more arguments\");\n\t}\n\telse if (err == EOVERFLOW) {\n\t\tbtrace(&bt);\n\t\tre_fprintf(stderr, \"Format: \\\"%b<-- SIZE ERROR\\n%H\\n\", fmt,\n\t\t\t   p - fmt + 1, btrace_println, &bt);\n\t\tre_assert(0 && \"RE_VA_ARG: arg is not compatible\");\n\t}\n#endif\n\treturn err;\n}\n\n\n/**\n * Print a formatted string\n *\n * @param fmt Formatted string\n * @param ap  Variable argument\n * @param vph Print handler\n * @param arg Handler argument\n *\n * @return 0 if success, otherwise errorcode\n *\n * Extensions:\n *\n * <pre>\n *   %b  (char *, size_t)        Buffer string with pointer and length\n *   %r  (struct pl *)           Pointer-length object\n *   %w  (uint8_t *, size_t)     Binary buffer to hexadecimal format\n *   %j  (struct sa *)           Socket address - address part only\n *   %J  (struct sa *)           Socket address and port - like 1.2.3.4:1234\n *   %H  (re_printf_h *, void *) Print handler with argument\n *   %v  (char *fmt, va_list *)  Variable argument list\n *   %m  (int)                   Describe an error code\n *   %L  (uint64_t/int64_t)      64-bit length modifier for %i, %d, %x and %u\n * </pre>\n *\n * Reserved for the future:\n *\n *   %k\n *   %y\n *\n */\nint re_vhprintf(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg)\n{\n\treturn vhprintf(fmt, ap, vph, arg, false);\n}\n\n\n/**\n * Print a safe formatted string\n *\n * @param fmt Formatted string\n * @param ap  Variable argument\n * @param vph Print handler\n * @param arg Handler argument\n *\n * @return 0 if success, otherwise errorcode\n *\n * Extensions:\n *\n * <pre>\n *   %b  (char *, size_t)        Buffer string with pointer and length\n *   %r  (struct pl *)           Pointer-length object\n *   %w  (uint8_t *, size_t)     Binary buffer to hexadecimal format\n *   %j  (struct sa *)           Socket address - address part only\n *   %J  (struct sa *)           Socket address and port - like 1.2.3.4:1234\n *   %H  (re_printf_h *, void *) Print handler with argument\n *   %v  (char *fmt, va_list *)  Variable argument list\n *   %m  (int)                   Describe an error code\n * </pre>\n *\n * Reserved for the future:\n *\n *   %k\n *   %y\n *\n */\nint re_vhprintf_s(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg)\n{\n\treturn vhprintf(fmt, ap, vph, arg, true);\n}\n\n\nstatic int print_handler(const char *p, size_t size, void *arg)\n{\n\tstruct pl *pl = arg;\n\n\tif (size > pl->l)\n\t\treturn ENOMEM;\n\n\tmemcpy((void *)pl->p, p, size);\n\n\tpl_advance(pl, size);\n\n\treturn 0;\n}\n\n\nstruct dyn_print {\n\tchar *str;\n\tchar *p;\n\tsize_t l;\n\tsize_t size;\n};\n\n\nstatic int print_handler_dyn(const char *p, size_t size, void *arg)\n{\n\tstruct dyn_print *dp = arg;\n\n\tif (size > dp->l - 1) {\n\t\tconst size_t new_size = MAX(dp->size + size, dp->size * 2);\n\t\tchar *str = mem_realloc(dp->str, new_size);\n\t\tif (!str)\n\t\t\treturn ENOMEM;\n\n\t\tdp->str = str;\n\t\tdp->l += new_size - dp->size;\n\t\tdp->p = dp->str + new_size - dp->l;\n\t\tdp->size = new_size;\n\t}\n\n\tmemcpy(dp->p, p, size);\n\n\tdp->p += size;\n\tdp->l -= size;\n\n\treturn 0;\n}\n\n\nstruct strm_print {\n\tFILE *f;\n\tsize_t n;\n};\n\nstatic int print_handler_stream(const char *p, size_t size, void *arg)\n{\n\tstruct strm_print *sp = arg;\n\n\tif (1 != fwrite(p, size, 1, sp->f))\n\t\treturn ENOMEM;\n\n\tsp->n += size;\n\n\treturn 0;\n}\n\n\n/**\n * Print a formatted string to a file stream, using va_list\n *\n * @param stream File stream for the output\n * @param fmt    Formatted string\n * @param ap     Variable-arguments list\n *\n * @return The number of characters printed, or -1 if error\n */\nint re_vfprintf(FILE *stream, const char *fmt, va_list ap)\n{\n\tstruct strm_print sp;\n\n\tif (!stream)\n\t\treturn -1;\n\n\tsp.f = stream;\n\tsp.n = 0;\n\n\tif (0 != vhprintf(fmt, ap, print_handler_stream, &sp, false))\n\t\treturn -1;\n\n\treturn (int)sp.n;\n}\n\n\n/**\n * Print a safe formatted string to a file stream, using va_list\n *\n * @param stream File stream for the output\n * @param fmt    Formatted string\n * @param ap     Variable-arguments list\n *\n * @return The number of characters printed, or -1 if error\n */\nint re_vfprintf_s(FILE *stream, const char *fmt, va_list ap)\n{\n\tstruct strm_print sp;\n\n\tif (!stream)\n\t\treturn -1;\n\n\tsp.f = stream;\n\tsp.n = 0;\n\n\tif (0 != vhprintf(fmt, ap, print_handler_stream, &sp, true))\n\t\treturn -1;\n\n\treturn (int)sp.n;\n}\n\n\n/**\n * Print a formatted string to stdout, using va_list\n *\n * @param fmt Formatted string\n * @param ap  Variable-arguments list\n *\n * @return The number of characters printed, or -1 if error\n */\nint re_vprintf(const char *fmt, va_list ap)\n{\n\treturn re_vfprintf(stdout, fmt, ap);\n}\n\n\n/**\n * Print a safe formatted string to stdout, using va_list\n *\n * @param fmt Formatted string\n * @param ap  Variable-arguments list\n *\n * @return The number of characters printed, or -1 if error\n */\nint re_vprintf_s(const char *fmt, va_list ap)\n{\n\treturn re_vfprintf_s(stdout, fmt, ap);\n}\n\n\n/**\n * Print a formatted string to a buffer, using va_list\n *\n * @param str  Buffer for output string\n * @param size Size of buffer\n * @param fmt  Formatted string\n * @param ap   Variable-arguments list\n *\n * @return The number of characters printed, or -1 if error\n */\nint re_vsnprintf(char *re_restrict str, size_t size,\n\t\t const char *re_restrict fmt, va_list ap)\n{\n\tstruct pl pl;\n\tint err;\n\n\tif (!str || !size)\n\t\treturn -1;\n\n\tpl.p = str;\n\tpl.l = size - 1;\n\n\terr = vhprintf(fmt, ap, print_handler, &pl, false);\n\n\tstr[size - pl.l - 1] = '\\0';\n\n\treturn err ? -1 : (int)(size - pl.l - 1);\n}\n\n\n/**\n * Print a safe formatted string to a buffer, using va_list\n *\n * @param str  Buffer for output string\n * @param size Size of buffer\n * @param fmt  Formatted string\n * @param ap   Variable-arguments list\n *\n * @return The number of characters printed, or -1 if error\n */\nint re_vsnprintf_s(char *re_restrict str, size_t size,\n\t\t const char *re_restrict fmt, va_list ap)\n{\n\tstruct pl pl;\n\tint err;\n\n\tif (!str || !size)\n\t\treturn -1;\n\n\tpl.p = str;\n\tpl.l = size - 1;\n\n\terr = vhprintf(fmt, ap, print_handler, &pl, true);\n\n\tstr[size - pl.l - 1] = '\\0';\n\n\treturn err ? -1 : (int)(size - pl.l - 1);\n}\n\n\nstatic int vsdprintf(char **strp, const char *fmt, va_list ap, bool safe)\n{\n\tstruct dyn_print dp;\n\tint err;\n\n\tif (!strp)\n\t\treturn EINVAL;\n\n\tdp.size = 16;\n\tdp.str  = mem_alloc(dp.size, NULL);\n\tif (!dp.str)\n\t\treturn ENOMEM;\n\n\tdp.p = dp.str;\n\tdp.l = dp.size;\n\n\terr = vhprintf(fmt, ap, print_handler_dyn, &dp, safe);\n\tif (err)\n\t\tgoto out;\n\n\t*dp.p = '\\0';\n\n out:\n\tif (err)\n\t\tmem_deref(dp.str);\n\telse\n\t\t*strp = dp.str;\n\n\treturn err;\n}\n\n\n/**\n * Print a formatted string to a dynamically allocated buffer, using va_list\n *\n * @param strp Pointer for output string\n * @param fmt  Formatted string\n * @param ap   Variable-arguments list\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_vsdprintf(char **strp, const char *fmt, va_list ap)\n{\n\treturn vsdprintf(strp, fmt, ap, false);\n}\n\n\n/**\n * Print a safe formatted string to a dynamically allocated buffer, using\n * va_list\n *\n * @param strp Pointer for output string\n * @param fmt  Formatted string\n * @param ap   Variable-arguments list\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_vsdprintf_s(char **strp, const char *fmt, va_list ap)\n{\n\treturn vsdprintf(strp, fmt, ap, true);\n}\n\n\n/**\n * Print a formatted string\n *\n * @param pf  Print backend\n * @param fmt Formatted string\n *\n * @return 0 if success, otherwise errorcode\n */\nint _re_hprintf(struct re_printf *pf, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tif (!pf)\n\t\treturn EINVAL;\n\n\tva_start(ap, fmt);\n\terr = re_vhprintf(fmt, ap, pf->vph, pf->arg);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Print a safe formatted string\n *\n * @param pf  Print backend\n * @param fmt Formatted string\n *\n * @return 0 if success, otherwise errorcode\n */\nint _re_hprintf_s(struct re_printf *pf, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tif (!pf)\n\t\treturn EINVAL;\n\n\tva_start(ap, fmt);\n\terr = re_vhprintf_s(fmt, ap, pf->vph, pf->arg);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Print a formatted string to a file stream\n *\n * @param stream File stream for output\n * @param fmt    Formatted string\n *\n * @return The number of characters printed, or -1 if error\n */\nint _re_fprintf(FILE *stream, const char *fmt, ...)\n{\n\tva_list ap;\n\tint n;\n\n\tva_start(ap, fmt);\n\tn = re_vfprintf(stream, fmt, ap);\n\tva_end(ap);\n\n\treturn n;\n}\n\n\n/**\n * Print a safe formatted string to a file stream\n *\n * @param stream File stream for output\n * @param fmt    Formatted string\n *\n * @return The number of characters printed, or -1 if error\n */\nint _re_fprintf_s(FILE *stream, const char *fmt, ...)\n{\n\tva_list ap;\n\tint n;\n\n\tva_start(ap, fmt);\n\tn = re_vfprintf_s(stream, fmt, ap);\n\tva_end(ap);\n\n\treturn n;\n}\n\n\n/**\n * Print a formatted string to stdout\n *\n * @param fmt    Formatted string\n *\n * @return The number of characters printed, or -1 if error\n */\nint _re_printf(const char *fmt, ...)\n{\n\tva_list ap;\n\tint n;\n\n\tva_start(ap, fmt);\n\tn = re_vprintf(fmt, ap);\n\tva_end(ap);\n\n\treturn n;\n}\n\n\n/**\n * Print a safe formatted string to stdout\n *\n * @param fmt    Formatted string\n *\n * @return The number of characters printed, or -1 if error\n */\nint _re_printf_s(const char *fmt, ...)\n{\n\tva_list ap;\n\tint n;\n\n\tva_start(ap, fmt);\n\tn = re_vprintf_s(fmt, ap);\n\tva_end(ap);\n\n\treturn n;\n}\n\n\n/**\n * Print a formatted string to a buffer\n *\n * @param str  Buffer for output string\n * @param size Size of buffer\n * @param fmt  Formatted string\n *\n * @return The number of characters printed, or -1 if error\n */\nint _re_snprintf(char *re_restrict str, size_t size,\n\t\tconst char *re_restrict fmt, ...)\n{\n\tva_list ap;\n\tint n;\n\n\tva_start(ap, fmt);\n\tn = re_vsnprintf(str, size, fmt, ap);\n\tva_end(ap);\n\n\treturn n;\n}\n\n\n/**\n * Print a safe formatted string to a buffer\n *\n * @param str  Buffer for output string\n * @param size Size of buffer\n * @param fmt  Formatted string\n *\n * @return The number of characters printed, or -1 if error\n */\nint _re_snprintf_s(char *re_restrict str, size_t size,\n\t\tconst char *re_restrict fmt, ...)\n{\n\tva_list ap;\n\tint n;\n\n\tva_start(ap, fmt);\n\tn = re_vsnprintf_s(str, size, fmt, ap);\n\tva_end(ap);\n\n\treturn n;\n}\n\n\n/**\n * Print a formatted string to a buffer\n *\n * @param strp Buffer pointer for output string\n * @param fmt  Formatted string\n *\n * @return 0 if success, otherwise errorcode\n */\nint _re_sdprintf(char **strp, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = re_vsdprintf(strp, fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Print a safe formatted string to a buffer\n *\n * @param strp Buffer pointer for output string\n * @param fmt  Formatted string\n *\n * @return 0 if success, otherwise errorcode\n */\nint _re_sdprintf_s(char **strp, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = re_vsdprintf_s(strp, fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/fmt/prm.c",
    "content": "/**\n * @file prm.c Generic parameter decoding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n\n\n/**\n * Check if a semicolon separated parameter is present\n *\n * @param pl    PL string to search\n * @param pname Parameter name\n *\n * @return true if found, false if not found\n */\nbool fmt_param_exists(const struct pl *pl, const char *pname)\n{\n\tstruct pl semi, eop;\n\tchar expr[128];\n\n\tif (!pl || !pname)\n\t\treturn false;\n\n\t(void)re_snprintf(expr, sizeof(expr),\n\t\t\t  \"[;]*[ \\t\\r\\n]*%s[ \\t\\r\\n;=]*\",\n\t\t\t  pname);\n\n\tif (re_regex(pl->p, pl->l, expr, &semi, NULL, &eop))\n\t\treturn false;\n\n\tif (!eop.l && eop.p < pl->p + pl->l)\n\t\treturn false;\n\n\treturn semi.l > 0 || pl->p == semi.p;\n}\n\n\n/**\n * Fetch parameter from a PL string. The separator can be specified\n *\n * @param pl    PL string to search\n * @param pname Parameter name\n * @param sep   Separator\n * @param val   Parameter value, set on return\n *\n * @return true if found, false if not found\n */\nbool fmt_param_sep_get(const struct pl *pl, const char *pname, char sep,\n\t\tstruct pl *val)\n{\n\tstruct pl semi;\n\tchar expr[128];\n\n\tif (!pl || !pname)\n\t\treturn false;\n\n\t(void)re_snprintf(expr, sizeof(expr),\n\t\t  \"[%c]*[ \\t\\r\\n]*%s[ \\t\\r\\n]*=[ \\t\\r\\n]*[~ \\t\\r\\n%c]+\",\n\t\t  sep, pname, sep);\n\n\tif (re_regex(pl->p, pl->l, expr, &semi, NULL, NULL, NULL, val))\n\t\treturn false;\n\n\treturn semi.l > 0 || pl->p == semi.p;\n}\n\n\n/**\n * Fetch a semicolon separated parameter from a PL string\n *\n * @param pl    PL string to search\n * @param pname Parameter name\n * @param val   Parameter value, set on return\n *\n * @return true if found, false if not found\n */\nbool fmt_param_get(const struct pl *pl, const char *pname, struct pl *val)\n{\n\treturn fmt_param_sep_get(pl, pname, ';', val);\n}\n\n\n/**\n * Apply a function handler for each semicolon separated parameter\n *\n * @param pl  PL string to search\n * @param ph  Parameter handler\n * @param arg Handler argument\n */\nvoid fmt_param_apply(const struct pl *pl, fmt_param_h *ph, void *arg)\n{\n\tstruct pl prmv, prm, semi, name, val;\n\n\tif (!pl || !ph)\n\t\treturn;\n\n\tprmv = *pl;\n\n\twhile (!re_regex(prmv.p, prmv.l, \"[ \\t\\r\\n]*[~;]+[;]*\",\n\t\t\t NULL, &prm, &semi)) {\n\n\t\tpl_advance(&prmv, semi.p + semi.l - prmv.p);\n\n\t\tif (re_regex(prm.p, prm.l,\n\t\t\t     \"[^ \\t\\r\\n=]+[ \\t\\r\\n]*[=]*[ \\t\\r\\n]*[~ \\t\\r\\n]*\",\n\t\t\t     &name, NULL, NULL, NULL, &val))\n\t\t\tbreak;\n\n\t\tph(&name, &val, arg);\n\t}\n}\n"
  },
  {
    "path": "src/fmt/regex.c",
    "content": "/**\n * @file regex.c Implements basic regular expressions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <ctype.h>\n#include <re_types.h>\n#include <re_fmt.h>\n\n\n/** Defines a character range */\nstruct chr {\n\tuint8_t min;  /**< Minimum value */\n\tuint8_t max;  /**< Maximum value */\n};\n\n\nstatic bool expr_match(const struct chr *chrv, uint32_t n, uint8_t c,\n\t\t       bool neg)\n{\n\tuint32_t i;\n\n\tfor (i=0; i<n; i++) {\n\n\t\tif (c < chrv[i].min)\n\t\t\tcontinue;\n\n\t\tif (c > chrv[i].max)\n\t\t\tcontinue;\n\n\t\tbreak;\n\t}\n\n\treturn neg ? (i == n) : (i != n);\n}\n\n\n/**\n * Parse a string using basic regular expressions. Any number of matching\n * expressions can be given, and each match will be stored in a \"struct pl\"\n * pointer-length type.\n *\n * @param ptr  String to parse\n * @param len  Length of string\n * @param expr Regular expressions string\n *\n * @return 0 if success, otherwise errorcode\n *\n * Example:\n *\n *   We parse the buffer for any numerical values, to get a match we must have\n *   1 or more occurrences of the digits 0-9. The result is stored in 'num',\n *   which is of pointer-length type and will point to the first location in\n *   the buffer that contains \"42\".\n *\n * <pre>\n const char buf[] = \"foo 42 bar\";\n struct pl num;\n int err = re_regex(buf, strlen(buf), \"[0-9]+\", &num);\n\n here num contains a pointer to '42'\n * </pre>\n */\nint re_regex(const char *ptr, size_t len, const char *expr, ...)\n{\n\tstruct chr chrv[64];\n\tconst char *p, *ep;\n\tbool fm, range = false, ec = false, neg = false, qesc = false;\n\tuint32_t n = 0;\n\tva_list ap;\n\tbool eesc;\n\tsize_t l;\n\n\tif (!ptr || !expr)\n\t\treturn EINVAL;\n\n again:\n\teesc = false;\n\tfm = false;\n\tl  = len--;\n\tp  = ptr++;\n\tep = expr;\n\n\tva_start(ap, expr);\n\n\tif (!l)\n\t\tgoto out;\n\n\tfor (; *ep; ep++) {\n\n\t\tif ('\\\\' == *ep && !eesc) {\n\t\t\teesc = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!fm) {\n\n\t\t\t/* Start of character class */\n\t\t\tif ('[' == *ep && !eesc) {\n\t\t\t\tn     = 0;\n\t\t\t\tfm    = true;\n\t\t\t\tec    = false;\n\t\t\t\tneg   = false;\n\t\t\t\trange = false;\n\t\t\t\tqesc  = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!l)\n\t\t\t\tbreak;\n\n\t\t\tif (tolower(*ep) != tolower(*p)) {\n\t\t\t\tva_end(ap);\n\t\t\t\tgoto again;\n\t\t\t}\n\n\t\t\teesc = false;\n\t\t\t++p;\n\t\t\t--l;\n\t\t\tcontinue;\n\t\t}\n\t\t/* End of character class */\n\t\telse if (ec) {\n\n\t\t\tuint32_t nm, nmin, nmax;\n\t\t\tstruct pl lpl, *pl = va_arg(ap, struct pl *);\n\t\t\tbool quote = false, esc = false;\n\n\t\t\t/* Match 0 or more times */\n\t\t\tif ('*' == *ep) {\n\t\t\t\tnmin = 0;\n\t\t\t\tnmax = -1;\n\t\t\t}\n\t\t\t/* Match 1 or more times */\n\t\t\telse if ('+' == *ep) {\n\t\t\t\tnmin = 1;\n\t\t\t\tnmax = -1;\n\t\t\t}\n\t\t\t/* Match exactly n times */\n\t\t\telse if ('1' <= *ep && *ep <= '9') {\n\t\t\t\tnmin = *ep - '0';\n\t\t\t\tnmax = *ep - '0';\n\t\t\t}\n\t\t\telse\n\t\t\t\tbreak;\n\n\t\t\tfm = false;\n\n\t\t\tlpl.p = p;\n\t\t\tlpl.l = 0;\n\n\t\t\tfor (nm = 0; l && nm < nmax; nm++, p++, l--, lpl.l++) {\n\n\t\t\t\tif (qesc) {\n\n\t\t\t\t\tif (esc) {\n\t\t\t\t\t\tesc = false;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch (*p) {\n\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\t\tesc = true;\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tcase '\"':\n\t\t\t\t\t\tquote = !quote;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (quote)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!expr_match(chrv, n, tolower(*p), neg))\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* Strip quotes */\n\t\t\tif (qesc && lpl.l > 1 &&\n\t\t\t    lpl.p[0] == '\"' && lpl.p[lpl.l - 1] == '\"') {\n\n\t\t\t\tlpl.p += 1;\n\t\t\t\tlpl.l -= 2;\n\t\t\t\tnm    -= 2;\n\t\t\t}\n\n\t\t\tif ((nm < nmin) || (nm > nmax)) {\n\t\t\t\tva_end(ap);\n\t\t\t\tgoto again;\n\t\t\t}\n\n\t\t\tif (pl)\n\t\t\t\t*pl = lpl;\n\n\t\t\teesc = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (eesc) {\n\t\t\teesc = false;\n\t\t\tgoto chr;\n\t\t}\n\n\t\tswitch (*ep) {\n\n\t\t\t/* End of character class */\n\t\tcase ']':\n\t\t\tec = true;\n\t\t\tcontinue;\n\n\t\t\t/* Negate with quote escape */\n\t\tcase '~':\n\t\t\tif (n)\n\t\t\t\tbreak;\n\n\t\t\tqesc = true;\n\t\t\tneg  = true;\n\t\t\tcontinue;\n\n\t\t\t/* Negate */\n\t\tcase '^':\n\t\t\tif (n)\n\t\t\t\tbreak;\n\n\t\t\tneg = true;\n\t\t\tcontinue;\n\n\t\t\t/* Range */\n\t\tcase '-':\n\t\t\tif (!n || range)\n\t\t\t\tbreak;\n\n\t\t\trange = true;\n\t\t\t--n;\n\t\t\tcontinue;\n\t\t}\n\n\tchr:\n\t\tif (n >= RE_ARRAY_SIZE(chrv))\n\t\t\tbreak;\n\n\t\tchrv[n].max = tolower(*ep);\n\n\t\tif (range)\n\t\t\trange = false;\n\t\telse\n\t\t\tchrv[n].min = tolower(*ep);\n\n\t\t++n;\n\t}\n out:\n\tva_end(ap);\n\n\tif (fm)\n\t\treturn EINVAL;\n\n\treturn *ep ? ENOENT : 0;\n}\n"
  },
  {
    "path": "src/fmt/str.c",
    "content": "/**\n * @file fmt/str.c String format functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#undef __STRICT_ANSI__ /* for mingw32 */\n#include <string.h>\n#include <stdlib.h>\n#ifdef HAVE_STRINGS_H\n#include <strings.h>\n#endif\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_fmt.h>\n\nenum {\n\tX64_STRSIZE = 17,\n};\n\n/**\n * Convert a ascii hex string to binary format\n *\n * @param hex Destination binary buffer\n * @param len Length of binary buffer\n * @param str Source ascii string\n *\n * @return 0 if success, otherwise errorcode\n */\nint str_hex(uint8_t *hex, size_t len, const char *str)\n{\n\tsize_t i;\n\n\tif (!hex || !str || (strlen(str) != (2 * len)))\n\t\treturn EINVAL;\n\n\tfor (i=0; i<len*2; i+=2) {\n\t\thex[i/2]  = ch_hex(str[i]) << 4;\n\t\thex[i/2] += ch_hex(str[i+1]);\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Copy a 0-terminated string with maximum length\n *\n * @param dst Destination string\n * @param src Source string\n * @param n   Maximum size of destination, including 0-terminator\n */\nvoid str_ncpy(char *dst, const char *src, size_t n)\n{\n\tif (!dst || !src || !n)\n\t\treturn;\n\n\t(void)strncpy(dst, src, n-1);\n\tdst[n-1] = '\\0'; /* strncpy does not null terminate if overflow */\n}\n\n\n/**\n * Duplicate a 0-terminated string\n *\n * @param dst Pointer to destination string (set on return)\n * @param src Source string\n *\n * @return 0 if success, otherwise errorcode\n */\nint str_dup(char **dst, const char *src)\n{\n\tchar *p;\n\tsize_t sz;\n\n\tif (!dst || !src)\n\t\treturn EINVAL;\n\n\tsz = strlen(src) + 1;\n\n\tp = mem_alloc(sz, NULL);\n\tif (!p)\n\t\treturn ENOMEM;\n\n\tmemcpy(p, src, sz);\n\n\t*dst = p;\n\n\treturn 0;\n}\n\n\n/**\n * Converts an uint64_t to a 0-terminated string\n *\n * @param dst Pointer to destination string (set on return)\n * @param val Value\n *\n * @return 0 if success, otherwise errorcode\n */\nint str_x64dup(char **dst, uint64_t val)\n{\n\tchar *str;\n\n\tstr = mem_alloc(X64_STRSIZE, NULL);\n\tif (!str)\n\t\treturn ENOMEM;\n\n\t(void)re_snprintf(str, X64_STRSIZE, \"%016llx\", val);\n\n\t*dst = str;\n\treturn 0;\n}\n\n\n/**\n * Compare two 0-terminated strings\n *\n * @param s1 First string\n * @param s2 Second string\n *\n * @return an integer less than, equal to, or greater than zero if s1 is found\n *         respectively, to be less than, to match, or be greater than s2\n */\nint str_cmp(const char *s1, const char *s2)\n{\n\tif (!s1 || !s2)\n\t\treturn 1;\n\n\treturn strcmp(s1, s2);\n}\n\n\n/**\n * Compare two 0-terminated strings up to n positions\n *\n * @param s1 First string\n * @param s2 Second string\n * @param n  Number of characters to compare\n *\n * @return an integer less than, equal to, or greater than zero if s1 is found\n *         respectively, to be less than, to match, or be greater than s2 in\n *         the first n positions\n */\nint str_ncmp(const char *s1, const char *s2, size_t n)\n{\n\tif (!s1 || !s2)\n\t\treturn 1;\n\n\treturn strncmp(s1, s2, n);\n}\n\n\n/**\n * Check if one 0-terminated string contains another\n *\n * @param s1 First string\n * @param s2 Second string\n *\n * @return a position in s1 where it contains s2 as a substring, or NULL\n *         if s2 does not occur inside s1\n */\nconst char *str_str(const char *s1, const char *s2)\n{\n\tif (!s1 || !s2)\n\t\treturn NULL;\n\n\treturn strstr(s1, s2);\n}\n\n\n/**\n * Compare two 0-terminated strings, ignoring case\n *\n * @param s1 First string\n * @param s2 Second string\n *\n * @return an integer less than, equal to, or greater than zero if s1 is found\n *         respectively, to be less than, to match, or be greater than s2\n */\nint str_casecmp(const char *s1, const char *s2)\n{\n\t/* Same strings -> equal */\n\tif (s1 == s2)\n\t\treturn 0;\n\n\tif (!s1 || !s2)\n\t\treturn 1;\n\n#ifdef WIN32\n\treturn _stricmp(s1, s2);\n#else\n\treturn strcasecmp(s1, s2);\n#endif\n}\n\n\n/**\n * Calculate the length of a string, safe version.\n *\n * @param s String\n *\n * @return Length of the string\n */\nsize_t str_len(const char *s)\n{\n\treturn s ? strlen(s) : 0;\n}\n\n\n/**\n * Convert various possible boolean strings to a bool\n *\n * @param val  Pointer to bool for returned value\n * @param str  String to be converted\n *\n * @return int 0 if success, otherwise errorcode\n */\nint str_bool(bool *val, const char *str)\n{\n\tif (!val || !str_isset(str))\n\t\treturn EINVAL;\n\n\tstruct pl pl = pl_null;\n\n\tpl_set_str(&pl, str);\n\n\treturn pl_bool(val, &pl);\n}\n\n\n/**\n * Converts unsigned integer to string\n *\n * @param val  Number to be converted\n * @param buf  Buffer[ITOA_BUFSZ] that holds the result of the conversion\n * @param base Base to use for conversion\n *\n * @return Pointer to buffer\n */\nchar *str_itoa(uint32_t val, char *buf, int base)\n{\n\tint i = ITOA_BUFSZ - 2;\n\n\tbuf[ITOA_BUFSZ - 1] = '\\0';\n\n\tif (!val) {\n\t\tbuf[i] = '0';\n\t\treturn &buf[i];\n\t}\n\n\tfor (; val && i; --i, val /= base)\n\t\tbuf[i] = \"0123456789abcdef\"[val % base];\n\n\treturn &buf[i + 1];\n}\n\n\n/**\n * Converts multibyte characters to wide characters\n *\n * @param str  String for conversion\n *\n * @return Pointer to new allocated wide string\n */\nwchar_t *str_wchar(const char *str)\n{\n\twchar_t *w;\n\tsize_t n;\n\n\tif (!str)\n\t\treturn NULL;\n\n\tn = strlen(str) + 1;\n\n\tw = mem_zalloc(n * sizeof(wchar_t), NULL);\n\tif (!w)\n\t\treturn NULL;\n\n\tif (mbstowcs(w, str, n) == (size_t)-1) {\n\t\tmem_deref(w);\n\t\treturn NULL;\n\t}\n\n\treturn w;\n}\n"
  },
  {
    "path": "src/fmt/str_error.c",
    "content": "/**\n * @file str_error.c System error messages\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n\n\n/**\n * Look up an error message string corresponding to an error number.\n *\n * @param errnum Error Code\n * @param buf    Buffer for storing error message\n * @param sz     Buffer size\n *\n * @return Error message string\n */\nconst char *str_error(int errnum, char *buf, size_t sz)\n{\n\tconst char *s;\n\tchar msg[128] = {0};\n\n\tif (!buf || !sz)\n\t\treturn NULL;\n\n#ifdef HAVE_STRERROR_R\n\n#ifdef __GLIBC__\n\ts = strerror_r(errnum, msg, sizeof(msg));\n#else\n\t(void)strerror_r(errnum, msg, sizeof(msg));\n\ts = msg;\n#endif\n\n#elif defined (WIN32)\n\t(void)strerror_s(msg, sizeof(msg), errnum);\n\ts = msg;\n#else\n\t/* fallback */\n\t(void)errnum;\n\ts = \"unknown error\";\n#endif\n\n\tre_snprintf(buf, sz, \"%s [%d]\", s, errnum);\n\n\treturn buf;\n}\n"
  },
  {
    "path": "src/fmt/text2pcap.c",
    "content": "#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_trace.h>\n#include <re_mem.h>\n\n\nint re_text2pcap(struct re_printf *pf, struct re_text2pcap *pcap)\n{\n\tif (!pcap)\n\t\treturn EINVAL;\n\n\tuint8_t *buf = mbuf_buf(pcap->mb);\n\tif (!buf)\n\t\treturn EINVAL;\n\n\tre_hprintf(pf, \"%s %H 000000\", pcap->in ? \"I\" : \"O\", fmt_timestamp_us,\n\t\t   NULL);\n\n\tsize_t sz = mbuf_get_left(pcap->mb);\n\tfor (size_t i = 0; i < sz; i++) {\n\t\tre_hprintf(pf, \" %02x\", buf[i]);\n\t}\n\n\tre_hprintf(pf, \" %s\", pcap->id);\n\n\treturn 0;\n}\n\n\nvoid re_text2pcap_trace(const char *name, const char *id, bool in,\n\t\t\tconst struct mbuf *mb)\n{\n\tstruct re_text2pcap pcap = {.in = in, .mb = mb, .id = id};\n\tsize_t pcap_buf_sz = (mbuf_get_left(mb) * 3) + 64;\n\n\tchar *pcap_buf = mem_alloc(pcap_buf_sz, NULL);\n\tif (!pcap_buf)\n\t\treturn;\n\n\t(void)re_snprintf(pcap_buf, pcap_buf_sz, \"%H\", re_text2pcap, &pcap);\n\n\tre_trace_event(\"pcap\", name, 'I', NULL, RE_TRACE_ARG_STRING_COPY,\n\t\t       \"pcap\", pcap_buf);\n\n\tmem_deref(pcap_buf);\n}\n"
  },
  {
    "path": "src/fmt/time.c",
    "content": "/**\n * @file time.c  Time formatting\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#ifdef __MINGW32__\n#define _POSIX_C_SOURCE 200809L\n#endif\n\n#include <time.h>\n\n#ifdef WIN32\n#include <windows.h>\n#endif\n\n#include <re_types.h>\n#include <re_fmt.h>\n\n\nstatic const char *dayv[] = {\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"};\n\nstatic const char *monv[] = {\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\",\n\t\t\t     \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"};\n\n\n/**\n * Print Greenwich Mean Time\n *\n * @param pf Print function for output\n * @param ts Time in seconds since the Epoch or NULL for current time\n *\n * @return 0 if success, otherwise errorcode\n */\nint fmt_gmtime(struct re_printf *pf, void *ts)\n{\n\tstruct tm tm;\n\ttime_t t;\n\n\tif (!ts) {\n\t\tt  = time(NULL);\n\t\tts = &t;\n\t}\n\n#ifdef WIN32\n\tif (gmtime_s(&tm, ts))\n\t\treturn EINVAL;\n#else\n\tif (!gmtime_r(ts, &tm))\n\t\treturn EINVAL;\n#endif\n\n\treturn re_hprintf(pf, \"%s, %02u %s %u %02u:%02u:%02u GMT\",\n\t\t\tdayv[min((unsigned)tm.tm_wday, RE_ARRAY_SIZE(dayv)-1)],\n\t\t\ttm.tm_mday,\n\t\t\tmonv[min((unsigned)tm.tm_mon, RE_ARRAY_SIZE(monv)-1)],\n\t\t\ttm.tm_year + 1900,\n\t\t\ttm.tm_hour, tm.tm_min, tm.tm_sec);\n}\n\n\n/**\n * Print the human readable time\n *\n * @param pf       Print function for output\n * @param seconds  Pointer to number of seconds\n *\n * @return 0 if success, otherwise errorcode\n */\nint fmt_human_time(struct re_printf *pf, const uint32_t *seconds)\n{\n\t/* max 136 years */\n\tconst uint32_t sec  = *seconds%60;\n\tconst uint32_t min  = *seconds/60%60;\n\tconst uint32_t hrs  = *seconds/60/60%24;\n\tconst uint32_t days = *seconds/60/60/24;\n\tint err = 0;\n\n\tif (days)\n\t\terr |= re_hprintf(pf, \"%u day%s \", days, 1==days?\"\":\"s\");\n\n\tif (hrs) {\n\t\terr |= re_hprintf(pf, \"%u hour%s \", hrs, 1==hrs?\"\":\"s\");\n\t}\n\n\tif (min) {\n\t\terr |= re_hprintf(pf, \"%u min%s \", min, 1==min?\"\":\"s\");\n\t}\n\n\tif (sec) {\n\t\terr |= re_hprintf(pf, \"%u sec%s\", sec, 1==sec?\"\":\"s\");\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Print local time stamp including milli seconds relative to user's timezone\n *\n * @param pf  Print function for output\n * @param arg Not used\n *\n * @return 0 if success, otherwise errorcode\n */\nint fmt_timestamp(struct re_printf *pf, void *arg)\n{\n\tint h, m, s;\n\tuint64_t ms;\n#ifdef WIN32\n\tSYSTEMTIME st;\n\n\tGetSystemTime(&st);\n\n\th  = st.wHour;\n\tm  = st.wMinute;\n\ts  = st.wSecond;\n\tms = st.wMilliseconds;\n#else\n\tstruct timespec tspec;\n\tstruct tm tm;\n\n\t(void)clock_gettime(CLOCK_REALTIME, &tspec);\n\tif (!localtime_r(&tspec.tv_sec, &tm))\n\t\treturn EINVAL;\n\n\th  = tm.tm_hour;\n\tm  = tm.tm_min;\n\ts  = tm.tm_sec;\n\tms = tspec.tv_nsec / 1000000;\n#endif\n\t(void)arg;\n\n\treturn re_hprintf(pf, \"%02u:%02u:%02u.%03llu\", h, m, s, ms);\n}\n\n\n/**\n * Print local time stamp including microseconds relative to user's timezone\n *\n * @param pf  Print function for output\n * @param arg Not used\n *\n * @return 0 if success, otherwise errorcode\n */\nint fmt_timestamp_us(struct re_printf *pf, void *arg)\n{\n\tint h, m, s;\n\tuint64_t us;\n\tstruct timespec tspec;\n\tstruct tm tm = {0};\n\n#if defined(WIN32) && !defined(__MINGW32__)\n\ttimespec_get(&tspec, TIME_UTC);\n\tint err = localtime_s(&tm, &tspec.tv_sec);\n\tif (err)\n\t\treturn err;\n#else\n\t(void)clock_gettime(CLOCK_REALTIME, &tspec);\n\tif (!localtime_r(&tspec.tv_sec, &tm))\n\t\treturn EINVAL;\n#endif\n\n\th  = tm.tm_hour;\n\tm  = tm.tm_min;\n\ts  = tm.tm_sec;\n\tus = tspec.tv_nsec / 1000;\n\t(void)arg;\n\n\treturn re_hprintf(pf, \"%02u:%02u:%02u.%06llu\", h, m, s, us);\n}\n"
  },
  {
    "path": "src/fmt/unicode.c",
    "content": "/**\n * @file unicode.c  Unicode character coding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <ctype.h>\n#include <re_types.h>\n#include <re_fmt.h>\n\n\nstatic const char *hex_chars = \"0123456789ABCDEF\";\n\n\n/**\n * UTF-8 encode\n *\n * @param pf  Print function for output\n * @param str Input string to encode\n *\n * @return 0 if success, otherwise errorcode\n */\nint utf8_encode(struct re_printf *pf, const char *str)\n{\n\tchar ubuf[6] = \"\\\\u00\", ebuf[2] = \"\\\\\";\n\n\tif (!pf)\n\t\treturn EINVAL;\n\n\tif (!str)\n\t\treturn 0;\n\n\twhile (*str) {\n\t\tconst uint8_t c = *str++;  /* NOTE: must be unsigned 8-bit */\n\t\tbool unicode = false;\n\t\tchar ec = 0;\n\t\tint err;\n\n\t\tswitch (c) {\n\n\t\tcase '\"':  ec = '\"'; break;\n\t\tcase '\\\\': ec = '\\\\'; break;\n\t\tcase '/':  ec = '/'; break;\n\t\tcase '\\b': ec = 'b'; break;\n\t\tcase '\\f': ec = 'f'; break;\n\t\tcase '\\n': ec = 'n'; break;\n\t\tcase '\\r': ec = 'r'; break;\n\t\tcase '\\t': ec = 't'; break;\n\t\tdefault:\n\t\t\tif (c < ' ') {\n\t\t\t\tunicode = true;\n\t\t\t}\n\t\t\t/* chars in range 0x80-0xff are not escaped */\n\t\t\tbreak;\n\t\t}\n\n\t\tif (unicode) {\n\t\t\tubuf[4] = hex_chars[(c>>4) & 0xf];\n\t\t\tubuf[5] = hex_chars[c & 0xf];\n\n\t\t\terr = pf->vph(ubuf, sizeof(ubuf), pf->arg);\n\t\t}\n\t\telse if (ec) {\n\t\t\tebuf[1] = ec;\n\n\t\t\terr = pf->vph(ebuf, sizeof(ebuf), pf->arg);\n\t\t}\n\t\telse {\n\t\t\terr = pf->vph((char *)&c, 1, pf->arg);\n\t\t}\n\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * UTF-8 decode\n *\n * @param pf Print function for output\n * @param pl Input buffer to decode\n *\n * @return 0 if success, otherwise errorcode\n */\nint utf8_decode(struct re_printf *pf, const struct pl *pl)\n{\n\tint uhi = -1;\n\tsize_t i;\n\n\tif (!pf)\n\t\treturn EINVAL;\n\n\tif (!pl)\n\t\treturn 0;\n\n\tfor (i=0; i<pl->l; i++) {\n\n\t\tchar ch = pl->p[i];\n\t\tint err;\n\n\t\tif (ch == '\\\\') {\n\n\t\t\tunsigned u = 0;\n\t\t\tchar ubuf[4];\n\t\t\tsize_t ulen;\n\n\t\t\t++i;\n\n\t\t\tif (i >= pl->l)\n\t\t\t\treturn EBADMSG;\n\n\t\t\tch = pl->p[i];\n\n\t\t\tswitch (ch) {\n\n\t\t\tcase 'b':\n\t\t\t\tch = '\\b';\n\t\t\t\tbreak;\n\n\t\t\tcase 'f':\n\t\t\t\tch = '\\f';\n\t\t\t\tbreak;\n\n\t\t\tcase 'n':\n\t\t\t\tch = '\\n';\n\t\t\t\tbreak;\n\n\t\t\tcase 'r':\n\t\t\t\tch = '\\r';\n\t\t\t\tbreak;\n\n\t\t\tcase 't':\n\t\t\t\tch = '\\t';\n\t\t\t\tbreak;\n\n\t\t\tcase 'u':\n\t\t\t\tif (i+4 >= pl->l)\n\t\t\t\t\treturn EBADMSG;\n\n\t\t\t\tif (!isxdigit(pl->p[i+1]) ||\n\t\t\t\t    !isxdigit(pl->p[i+2]) ||\n\t\t\t\t    !isxdigit(pl->p[i+3]) ||\n\t\t\t\t    !isxdigit(pl->p[i+4]))\n\t\t\t\t\treturn EBADMSG;\n\n\t\t\t\tu |= ((uint16_t)ch_hex(pl->p[++i])) << 12;\n\t\t\t\tu |= ((uint16_t)ch_hex(pl->p[++i])) << 8;\n\t\t\t\tu |= ((uint16_t)ch_hex(pl->p[++i])) << 4;\n\t\t\t\tu |= ((uint16_t)ch_hex(pl->p[++i])) << 0;\n\n\t\t\t\t/* UTF-16 surrogate pair */\n\t\t\t\tif (u >= 0xd800 && u <= 0xdbff) {\n\t\t\t\t\tuhi = (u - 0xd800) * 0x400;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\telse if (u >= 0xdc00 && u <= 0xdfff) {\n\t\t\t\t\tif (uhi < 0)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tu = uhi + u - 0xdc00 + 0x10000;\n\t\t\t\t}\n\n\t\t\t\tuhi = -1;\n\n\t\t\t\tulen = utf8_byteseq(ubuf, u);\n\n\t\t\t\terr = pf->vph(ubuf, ulen, pf->arg);\n\t\t\t\tif (err)\n\t\t\t\t\treturn err;\n\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tuhi = -1;\n\n\t\terr = pf->vph(&ch, 1, pf->arg);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Encode Unicode code point into binary UTF-8\n *\n * @param u  Binary UTF-8 buffer\n * @param cp Unicode code point\n *\n * @return length of UTF-8 byte sequence\n */\nsize_t utf8_byteseq(char u[4], unsigned cp)\n{\n\tif (!u)\n\t\treturn 0;\n\n\tif (cp <= 0x7f) {\n\t\tu[0] = cp;\n\t\treturn 1;\n\t}\n\telse if (cp <= 0x7ff) {\n\t\tu[0] = 0xc0 | (cp>>6 & 0x1f);\n\t\tu[1] = 0x80 | (cp    & 0x3f);\n\t\treturn 2;\n\t}\n\telse if (cp <= 0xffff) {\n\t\tu[0] = 0xe0 | (cp>>12 & 0x0f);\n\t\tu[1] = 0x80 | (cp>>6  & 0x3f);\n\t\tu[2] = 0x80 | (cp     & 0x3f);\n\t\treturn 3;\n\t}\n\telse if (cp <= 0x10ffff) {\n\t\tu[0] = 0xf0 | (cp>>18 & 0x07);\n\t\tu[1] = 0x80 | (cp>>12 & 0x3f);\n\t\tu[2] = 0x80 | (cp>>6  & 0x3f);\n\t\tu[3] = 0x80 | (cp     & 0x3f);\n\t\treturn 4;\n\t}\n\telse {\n\t\t/* The replacement character (U+FFFD) */\n\t\tu[0] = (char)0xef;\n\t\tu[1] = (char)0xbf;\n\t\tu[2] = (char)0xbd;\n\t\treturn 3;\n\t}\n}\n"
  },
  {
    "path": "src/h264/getbit.c",
    "content": "/**\n * @file h264/getbit.c Generic bit parser\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_h264.h>\n#include \"h264.h\"\n\n\n#define DEBUG_MODULE \"getbit\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nvoid getbit_init(struct getbit *gb, const uint8_t *buf, size_t size)\n{\n\tif (!gb)\n\t\treturn;\n\n\tgb->buf = buf;\n\tgb->pos = 0;\n\tgb->end = size;\n}\n\n\nsize_t getbit_get_left(const struct getbit *gb)\n{\n\tif (!gb)\n\t\treturn 0;\n\n\tif (gb->end > gb->pos)\n\t\treturn gb->end - gb->pos;\n\telse\n\t\treturn 0;\n}\n\n\nunsigned get_bit(struct getbit *gb)\n{\n\tconst uint8_t *p;\n\tregister unsigned tmp;\n\n\tif (!gb)\n\t\treturn 0;\n\n\tif (gb->pos >= gb->end) {\n\t\tre_fprintf(stderr, \"get_bit: read past end\"\n\t\t\t   \" (%zu >= %zu)\\n\", gb->pos, gb->end);\n\t\treturn 0;\n\t}\n\n\tp = gb->buf;\n\ttmp = ((*(p + (gb->pos >> 0x3))) >> (0x7 - (gb->pos & 0x7))) & 0x1;\n\n\t++gb->pos;\n\n\treturn tmp;\n}\n\n\nint get_ue_golomb(struct getbit *gb, unsigned *valp)\n{\n\tunsigned zeros = 0;\n\tunsigned info;\n\tint i;\n\n\tif (!gb)\n\t\treturn EINVAL;\n\n\twhile (1) {\n\n\t\tif (getbit_get_left(gb) < 1)\n\t\t\treturn EBADMSG;\n\n\t\tif (0 == get_bit(gb))\n\t\t\t++zeros;\n\t\telse\n\t\t\tbreak;\n\t}\n\n\tinfo = 1 << zeros;\n\n\tfor (i = zeros - 1; i >= 0; i--) {\n\n\t\tif (getbit_get_left(gb) < 1)\n\t\t\treturn EBADMSG;\n\n\t\tinfo |= get_bit(gb) << i;\n\t}\n\n\tif (valp)\n\t\t*valp = info - 1;\n\n\treturn 0;\n}\n\n\n/*\n  x = 0\n  for ( i = 0; i < n; i++ ) {\n   x = 2 * x + read_bit()\n  }\n  TotalConsumedBits += n\n  return x\n */\nunsigned get_bits(struct getbit *gb, unsigned n)\n{\n\tunsigned x = 0;\n\n\tif (getbit_get_left(gb) < n) {\n\t\tDEBUG_WARNING(\"get_bits: read past end\"\n\t\t\t\" (n=%zu, left=%zu)\\n\", n, getbit_get_left(gb));\n\t\treturn 0;\n\t}\n\n\tfor (unsigned i=0; i<n; i++) {\n\n\t\tx = 2*x + get_bit(gb);\n\t}\n\n\treturn x;\n}\n\n\n#define dd_f(n) get_bits(gb, (n))\n\n\n/*\n * ns(n) - non-symmetric unsigned encoded integer with maximum\n *         number of values n (i.e., output in range 0..n-1).\n *\n */\nunsigned getbit_read_ns(struct getbit *gb, unsigned n)\n{\n\tunsigned w = 0;\n\tunsigned x = n;\n\n\twhile (x != 0) {\n\n\t\tx = x >> 1;\n\t\t++w;\n\t}\n\n\tunsigned m = (1u << w) - n;\n\tunsigned v = dd_f(w - 1);\n\n\tif (v < m)\n\t\treturn v;\n\n\tunsigned extra_bit = dd_f(1);\n\n\treturn (v << 1) - m + extra_bit;\n}\n"
  },
  {
    "path": "src/h264/h264.h",
    "content": "/**\n * @file h264/h264.h Internal interface\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n"
  },
  {
    "path": "src/h264/nal.c",
    "content": "/**\n * @file h264/nal.c H.264 header parser\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_fmt.h>\n#include <re_h264.h>\n\n\nenum {\n\tH264_HEADER_LENGTH   = 1,\n\tH264_STAP_MIN_LENGTH = sizeof(uint16_t)\n};\n\n\nstatic int nal_header_encode_val(struct mbuf *mb,\n\t\t\t\t uint8_t nri, enum h264_nalu type)\n{\n\tuint8_t v = nri<<5 | type;\n\n\treturn mbuf_write_u8(mb, v);\n}\n\n\nvoid h264_nal_header_decode_buf(struct h264_nal_header *hdr,\n\t\t\t\tconst uint8_t *buf)\n{\n\tif (!hdr || !buf)\n\t\treturn;\n\n\tuint8_t v = buf[0];\n\n\thdr->f    = v>>7 & 0x1;\n\thdr->nri  = v>>5 & 0x3;\n\thdr->type = v    & 0x1f;\n}\n\n\n/**\n * Encode H.264 NAL header\n *\n * @param mb  Buffer to encode into\n * @param hdr H.264 NAL header to encode\n *\n * @return 0 if success, otherwise errorcode\n */\nint h264_nal_header_encode(struct mbuf *mb, const struct h264_nal_header *hdr)\n{\n\tuint8_t v;\n\n\tif (!mb || !hdr)\n\t\treturn EINVAL;\n\n\tv = hdr->f<<7 | hdr->nri<<5 | hdr->type;\n\n\treturn mbuf_write_u8(mb, v);\n}\n\n\n/**\n * Decode H.264 NAL header\n *\n * @param hdr H.264 NAL header to decode into\n * @param mb  Buffer to decode\n *\n * @return 0 if success, otherwise errorcode\n */\nint h264_nal_header_decode(struct h264_nal_header *hdr, struct mbuf *mb)\n{\n\tuint8_t v;\n\n\tif (!hdr || !mb)\n\t\treturn EINVAL;\n\tif (mbuf_get_left(mb) < 1)\n\t\treturn EBADMSG;\n\n\tv = mbuf_read_u8(mb);\n\n\thdr->f    = v>>7 & 0x1;\n\thdr->nri  = v>>5 & 0x3;\n\thdr->type = v    & 0x1f;\n\n\treturn 0;\n}\n\n\n/**\n * Get the name of an H.264 NAL unit\n *\n * @param nal_type NAL unit type\n *\n * @return A string containing the NAL unit name\n */\nconst char *h264_nal_unit_name(enum h264_nalu nal_type)\n{\n\tswitch (nal_type) {\n\n\tcase H264_NALU_SLICE:        return \"SLICE\";\n\tcase H264_NALU_DPA:          return \"DPA\";\n\tcase H264_NALU_DPB:          return \"DPB\";\n\tcase H264_NALU_DPC:          return \"DPC\";\n\tcase H264_NALU_IDR_SLICE:    return \"IDR_SLICE\";\n\tcase H264_NALU_SEI:          return \"SEI\";\n\tcase H264_NALU_SPS:          return \"SPS\";\n\tcase H264_NALU_PPS:          return \"PPS\";\n\tcase H264_NALU_AUD:          return \"AUD\";\n\tcase H264_NALU_END_SEQUENCE: return \"END_SEQUENCE\";\n\tcase H264_NALU_END_STREAM:   return \"END_STREAM\";\n\tcase H264_NALU_FILLER_DATA:  return \"FILLER_DATA\";\n\tcase H264_NALU_SPS_EXT:      return \"SPS_EXT\";\n\tcase H264_NALU_AUX_SLICE:    return \"AUX_SLICE\";\n\tcase H264_NALU_STAP_A:       return \"STAP-A\";\n\tcase H264_NALU_STAP_B:       return \"STAP-B\";\n\tcase H264_NALU_MTAP16:       return \"MTAP16\";\n\tcase H264_NALU_MTAP24:       return \"MTAP24\";\n\tcase H264_NALU_FU_A:         return \"FU-A\";\n\tcase H264_NALU_FU_B:         return \"FU-B\";\n\t}\n\n\treturn \"???\";\n}\n\n\nint h264_fu_hdr_encode(const struct h264_fu *fu, struct mbuf *mb)\n{\n\tuint8_t v = fu->s<<7 | fu->s<<6 | fu->r<<5 | fu->type;\n\treturn mbuf_write_u8(mb, v);\n}\n\n\nint h264_fu_hdr_decode(struct h264_fu *fu, struct mbuf *mb)\n{\n\tuint8_t v;\n\n\tif (mbuf_get_left(mb) < 1)\n\t\treturn ENOENT;\n\n\tv = mbuf_read_u8(mb);\n\n\tfu->s    = v>>7 & 0x1;\n\tfu->e    = v>>6 & 0x1;\n\tfu->r    = v>>5 & 0x1;\n\tfu->type = v>>0 & 0x1f;\n\n\treturn 0;\n}\n\n\n/*\n * Find the NAL start sequence in a H.264 byte stream\n *\n * @note: copied from ffmpeg source\n */\nstatic const uint8_t *h264_find_startcode_int(const uint8_t *p,\n\t\t\t\t\t      const uint8_t *end)\n{\n\tconst uint8_t *a = p + 4 - ((size_t)p & 3);\n\n\tfor (end -= 3; p < a && p < end; p++ ) {\n\t\tif (p[0] == 0 && p[1] == 0 && p[2] == 1)\n\t\t\treturn p;\n\t}\n\n\tfor (end -= 3; p < end; p += 4) {\n\t\tuint32_t x = *(const uint32_t*)(void *)p;\n\t\tif ( (x - 0x01010101) & (~x) & 0x80808080 ) {\n\t\t\tif (p[1] == 0 ) {\n\t\t\t\tif ( p[0] == 0 && p[2] == 1 )\n\t\t\t\t\treturn p;\n\t\t\t\tif ( p[2] == 0 && p[3] == 1 )\n\t\t\t\t\treturn p+1;\n\t\t\t}\n\t\t\tif ( p[3] == 0 ) {\n\t\t\t\tif ( p[2] == 0 && p[4] == 1 )\n\t\t\t\t\treturn p+2;\n\t\t\t\tif ( p[4] == 0 && p[5] == 1 )\n\t\t\t\t\treturn p+3;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (end += 3; p < end; p++) {\n\t\tif (p[0] == 0 && p[1] == 0 && p[2] == 1)\n\t\t\treturn p;\n\t}\n\n\treturn end + 3;\n}\n\n\nconst uint8_t *h264_find_startcode(const uint8_t *p, const uint8_t *end)\n{\n\tconst uint8_t *out = h264_find_startcode_int(p, end);\n\n\t/* check for 4-byte startcode */\n\tif (p<out && out<end && !out[-1])\n\t\t--out;\n\n\treturn out;\n}\n\n\nstatic int rtp_send_data(const uint8_t *hdr, size_t hdr_sz,\n\t\t\t const uint8_t *buf, size_t sz,\n\t\t\t bool eof, uint64_t rtp_ts,\n\t\t\t h264_packet_h *pkth, void *arg)\n{\n\treturn pkth(eof, rtp_ts, hdr, hdr_sz, buf, sz, arg);\n}\n\n\nint h264_nal_send(bool first, bool last,\n\t\t  bool marker, uint32_t ihdr, uint64_t rtp_ts,\n\t\t  const uint8_t *buf, size_t size, size_t maxsz,\n\t\t  h264_packet_h *pkth, void *arg)\n{\n\tuint8_t hdr = (uint8_t)ihdr;\n\tint err = 0;\n\n\tif (first && last && size <= maxsz) {\n\t\terr = rtp_send_data(&hdr, 1, buf, size, marker, rtp_ts,\n\t\t\t\t    pkth, arg);\n\t}\n\telse {\n\t\tuint8_t fu_hdr[2];\n\t\tconst uint8_t type = hdr & 0x1f;\n\t\tconst uint8_t nri  = hdr & 0x60;\n\t\tconst size_t sz = maxsz - 2;\n\n\t\tfu_hdr[0] = nri | H264_NALU_FU_A;\n\t\tfu_hdr[1] = first ? (1<<7 | type) : type;\n\n\t\twhile (size > sz) {\n\t\t\terr |= rtp_send_data(fu_hdr, 2, buf, sz, false,\n\t\t\t\t\t     rtp_ts,\n\t\t\t\t\t     pkth, arg);\n\t\t\tbuf += sz;\n\t\t\tsize -= sz;\n\t\t\tfu_hdr[1] &= ~(1 << 7);\n\t\t}\n\n\t\tif (last)\n\t\t\tfu_hdr[1] |= 1<<6;  /* end bit */\n\n\t\terr |= rtp_send_data(fu_hdr, 2, buf, size, marker && last,\n\t\t\t\t     rtp_ts,\n\t\t\t\t     pkth, arg);\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Packetize an H.264 bitstream with one or more NAL units\n * with Annex-B startcode (3-byte or 4-byte startcode)\n *\n * @param rtp_ts  RTP timestamp\n * @param buf     Input buffer\n * @param len     Buffer length\n * @param pktsize Maximum RTP packet size\n * @param pkth    Packet handler\n * @param arg     Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint h264_packetize(uint64_t rtp_ts, const uint8_t *buf, size_t len,\n\t\t   size_t pktsize, h264_packet_h *pkth, void *arg)\n{\n\tconst uint8_t *start = buf;\n\tconst uint8_t *end   = buf + len;\n\tconst uint8_t *r;\n\tint err = 0;\n\n\tr = h264_find_startcode(start, end);\n\n\twhile (r < end) {\n\t\tconst uint8_t *r1;\n\n\t\t/* skip zeros */\n\t\twhile (!*(r++))\n\t\t\t;\n\n\t\tr1 = h264_find_startcode(r, end);\n\n\t\terr |= h264_nal_send(true, true, (r1 >= end), r[0],\n\t\t\t\t     rtp_ts, r+1, r1-r-1, pktsize,\n\t\t\t\t     pkth, arg);\n\t\tr = r1;\n\t}\n\n\treturn err;\n}\n\n\nbool h264_is_keyframe(int type)\n{\n\treturn type == H264_NALU_IDR_SLICE;\n}\n\n\n/**\n * Encode STAP-A header and payload\n *\n * @param mb       Target mbuffer for STAP-A NAL unit\n * @param frame    Input frame in Annex-B format\n * @param frame_sz Number of bytes in input frame\n *\n * @return 0 if success, otherwise errorcode\n *\n * NOTE: The value of NRI MUST be the maximum of all the\n *       NAL units carried in the aggregation packet.\n *\n * NOTE: The input must be in Annex-B format (3-4 byte Startcode)\n */\nint h264_stap_encode(struct mbuf *mb, const uint8_t *frame,\n\t\t     size_t frame_sz)\n{\n\tuint8_t nri_max = 0;\n\n\tif (!mb || !frame || !frame_sz)\n\t\treturn EINVAL;\n\n\tsize_t start = mb->pos;\n\tconst uint8_t *end = frame + frame_sz;\n\n\tint err = nal_header_encode_val(mb, 0, H264_NALU_STAP_A);\n\tif (err)\n\t\treturn err;\n\n\tconst uint8_t *r = h264_find_startcode(frame, end);\n\n\twhile (r < end) {\n\n\t\tstruct h264_nal_header hdr;\n\n\t\twhile (!*(r++))\n\t\t\t;\n\n\t\tconst uint8_t *r1 = h264_find_startcode(r, end);\n\t\tsize_t len = r1 - r;\n\n\t\tif (len > UINT16_MAX)\n\t\t\treturn ERANGE;\n\n\t\terr  = mbuf_write_u16(mb, htons((uint16_t)len));\n\t\terr |= mbuf_write_mem(mb, r, len);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\th264_nal_header_decode_buf(&hdr, r);\n\t\tnri_max = max(hdr.nri, nri_max);\n\n\t\tr = r1;\n\t}\n\n\t/* update NAL header */\n\tmb->buf[start] |= nri_max<<5;\n\n\treturn 0;\n}\n\n\nstatic int stap_decode_annexb_int(struct mbuf *mb_frame, struct mbuf *mb_pkt,\n\t\t\t\t  bool long_startcode)\n{\n\tif (!mb_frame || !mb_pkt)\n\t\treturn EINVAL;\n\n\twhile (mbuf_get_left(mb_pkt) >= H264_STAP_MIN_LENGTH) {\n\n\t\tuint16_t len = ntohs(mbuf_read_u16(mb_pkt));\n\n\t\tif (len < H264_HEADER_LENGTH || len > mbuf_get_left(mb_pkt))\n\t\t\treturn EBADMSG;\n\n#if 0\n\t\tstruct h264_nal_header hdr;\n\n\t\th264_nal_header_decode_buf(&hdr, mbuf_buf(mb_pkt));\n\n\t\tre_printf(\"STAP-A decode:  len=%u  nri=%u  type=%u(%s)\\n\",\n\t\t\t  len,\n\t\t\t  hdr.nri, hdr.type, h264_nal_unit_name(hdr.type));\n#endif\n\n\t\tstatic const uint8_t sc3[] = {0, 0, 1};\n\t\tstatic const uint8_t sc4[] = {0, 0, 0, 1};\n\n\t\tint err;\n\t\tif (long_startcode)\n\t\t\terr = mbuf_write_mem(mb_frame, sc4, sizeof(sc4));\n\t\telse\n\t\t\terr = mbuf_write_mem(mb_frame, sc3, sizeof(sc3));\n\t\terr |= mbuf_write_mem(mb_frame, mbuf_buf(mb_pkt), len);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmbuf_advance(mb_pkt, len);\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Decode STAP-A payload and convert to Annex-B NAL units\n *\n * @param mb_frame Target mbuffer for frame with multiple NAL units\n * @param mb_pkt   Input packet with STAP-A payload\n *\n * @return 0 if success, otherwise errorcode\n *\n * NOTE: The NAL header must be decoded outside\n */\nint h264_stap_decode_annexb(struct mbuf *mb_frame, struct mbuf *mb_pkt)\n{\n\treturn stap_decode_annexb_int(mb_frame, mb_pkt, false);\n}\n\n\n/**\n * Decode STAP-A payload and convert to Annex-B NAL units\n * with long startcode (0x00 0x00 0x00 0x01).\n *\n * @param mb_frame Target mbuffer for frame with multiple NAL units\n * @param mb_pkt   Input packet with STAP-A payload\n *\n * @return 0 if success, otherwise errorcode\n *\n * NOTE: The NAL header must be decoded outside\n */\nint h264_stap_decode_annexb_long(struct mbuf *mb_frame, struct mbuf *mb_pkt)\n{\n\treturn stap_decode_annexb_int(mb_frame, mb_pkt, true);\n}\n"
  },
  {
    "path": "src/h264/sps.c",
    "content": "/**\n * @file h264/sps.c H.264 SPS parser\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_h264.h>\n#include \"h264.h\"\n\n\nenum {\n\tMAX_SPS_COUNT          = 32,\n\tMAX_LOG2_MAX_FRAME_NUM = 16,\n\tMACROBLOCK_SIZE        = 16,\n};\n\n#define MAX_MACROBLOCKS 1048576u\n\n\nstatic int scaling_list(struct getbit *gb,\n\t\t\tunsigned *scalingl, size_t sizeofscalinglist,\n\t\t\tbool *usedefaultscalingmatrix)\n{\n\tunsigned lastscale = 8;\n\tunsigned nextscale = 8;\n\tsize_t j;\n\tint err;\n\n\tfor (j = 0; j < sizeofscalinglist; j++) {\n\n\t\tif (nextscale != 0) {\n\n\t\t\tunsigned delta_scale;\n\n\t\t\terr = get_ue_golomb(gb, &delta_scale);\n\t\t\tif (err)\n\t\t\t\treturn err;\n\n\t\t\tnextscale = (lastscale + delta_scale + 256) % 256;\n\n\t\t\t*usedefaultscalingmatrix = (j==0 && nextscale==0);\n\t\t}\n\n\t\tscalingl[j] = (nextscale==0) ? lastscale : nextscale;\n\n\t\tlastscale = scalingl[j];\n\t}\n\n\treturn 0;\n}\n\n\nstatic int decode_scaling_matrix(struct getbit *gb, unsigned chroma_format_idc)\n{\n\tunsigned scalinglist4x4[16];\n\tunsigned scalinglist8x8[64];\n\tbool usedefaultscalingmatrix[12];\n\tunsigned i;\n\tint err;\n\n\tfor (i = 0; i < ((chroma_format_idc != 3) ? 8u : 12u); i++) {\n\n\t\tunsigned seq_scaling_list_present_flag;\n\n\t\tif (getbit_get_left(gb) < 1)\n\t\t\treturn EBADMSG;\n\n\t\tseq_scaling_list_present_flag = get_bit(gb);\n\n\t\tif (seq_scaling_list_present_flag) {\n\n\t\t\tif (i < 6) {\n\t\t\t\terr = scaling_list(gb, scalinglist4x4, 16,\n\t\t\t\t\t   &usedefaultscalingmatrix[i]);\n\t\t\t}\n\t\t\telse {\n\t\t\t\terr = scaling_list(gb, scalinglist8x8, 64,\n\t\t\t\t\t   &usedefaultscalingmatrix[i]);\n\t\t\t}\n\n\t\t\tif (err)\n\t\t\t\treturn err;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Decode a Sequence Parameter Set (SPS) bitstream\n *\n * @param sps  Decoded H.264 SPS\n * @param p    SPS bitstream to decode, excluding NAL header\n * @param len  Number of bytes\n *\n * @return 0 if success, otherwise errorcode\n */\nint h264_sps_decode(struct h264_sps *sps, const uint8_t *p, size_t len)\n{\n\tstruct getbit gb;\n\tuint8_t profile_idc;\n\tunsigned seq_parameter_set_id;\n\tunsigned frame_mbs_only_flag;\n\tunsigned chroma_format_idc = 1;\n\tbool frame_cropping_flag;\n\tunsigned mb_w_m1;\n\tunsigned mb_h_m1;\n\tint err;\n\n\tif (!sps || !p || len < 3)\n\t\treturn EINVAL;\n\n\tmemset(sps, 0, sizeof(*sps));\n\n\tprofile_idc    = p[0];\n\tsps->level_idc = p[2];\n\n\tgetbit_init(&gb, p+3, (len-3)*8);\n\n\terr = get_ue_golomb(&gb, &seq_parameter_set_id);\n\tif (err)\n\t\treturn err;\n\n\tif (seq_parameter_set_id >= MAX_SPS_COUNT) {\n\t\tre_fprintf(stderr, \"h264: sps: sps_id %u out of range\\n\",\n\t\t\t   seq_parameter_set_id);\n\t\treturn EBADMSG;\n\t}\n\n\tif (profile_idc == 100 ||\n\t    profile_idc == 110 ||\n\t    profile_idc == 122 ||\n\t    profile_idc == 244 ||\n\t    profile_idc ==  44 ||\n\t    profile_idc ==  83 ||\n\t    profile_idc ==  86 ||\n\t    profile_idc == 118 ||\n\t    profile_idc == 128 ||\n\t    profile_idc == 138 ||\n\t    profile_idc == 144) {\n\n\t\tunsigned seq_scaling_matrix_present_flag;\n\n\t\terr = get_ue_golomb(&gb, &chroma_format_idc);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (chroma_format_idc > 3U) {\n\t\t\treturn EBADMSG;\n\t\t}\n\t\telse if (chroma_format_idc == 3) {\n\n\t\t\tif (getbit_get_left(&gb) < 1)\n\t\t\t\treturn EBADMSG;\n\n\t\t\t/* separate_colour_plane_flag */\n\t\t\t(void)get_bit(&gb);\n\t\t}\n\n\t\t/* bit_depth_luma/chroma */\n\t\terr  = get_ue_golomb(&gb, NULL);\n\t\terr |= get_ue_golomb(&gb, NULL);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (getbit_get_left(&gb) < 2)\n\t\t\treturn EBADMSG;\n\n\t\t/* qpprime_y_zero_transform_bypass_flag */\n\t\tget_bit(&gb);\n\n\t\tseq_scaling_matrix_present_flag = get_bit(&gb);\n\t\tif (seq_scaling_matrix_present_flag) {\n\n\t\t\terr = decode_scaling_matrix(&gb, chroma_format_idc);\n\t\t\tif (err)\n\t\t\t\treturn err;\n\t\t}\n\t}\n\n\terr = get_ue_golomb(&gb, &sps->log2_max_frame_num);\n\tif (err)\n\t\treturn err;\n\n\tsps->log2_max_frame_num += 4;\n\n\tif (sps->log2_max_frame_num > MAX_LOG2_MAX_FRAME_NUM) {\n\t\tre_fprintf(stderr, \"h264: sps: log2_max_frame_num\"\n\t\t\t   \" out of range: %u\\n\", sps->log2_max_frame_num);\n\t\treturn EBADMSG;\n\t}\n\n\terr = get_ue_golomb(&gb, &sps->pic_order_cnt_type);\n\tif (err)\n\t\treturn err;\n\n\tif (sps->pic_order_cnt_type == 0) {\n\n\t\t/* log2_max_pic_order_cnt_lsb */\n\t\terr = get_ue_golomb(&gb, NULL);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\telse if (sps->pic_order_cnt_type == 2) {\n\t}\n\telse {\n\t\tre_fprintf(stderr, \"h264: sps: WARNING:\"\n\t\t\t   \" unknown pic_order_cnt_type (%u)\\n\",\n\t\t\t   sps->pic_order_cnt_type);\n\t\treturn ENOTSUP;\n\t}\n\n\terr = get_ue_golomb(&gb, &sps->max_num_ref_frames);\n\tif (err)\n\t\treturn err;\n\n\tif (getbit_get_left(&gb) < 1)\n\t\treturn EBADMSG;\n\n\t/* gaps_in_frame_num_value_allowed_flag */\n\t(void)get_bit(&gb);\n\n\terr  = get_ue_golomb(&gb, &mb_w_m1);\n\terr |= get_ue_golomb(&gb, &mb_h_m1);\n\tif (err)\n\t\treturn err;\n\n\tif (getbit_get_left(&gb) < 1)\n\t\treturn EBADMSG;\n\tframe_mbs_only_flag = get_bit(&gb);\n\n\tsps->pic_width_in_mbs        = mb_w_m1 + 1;\n\tsps->pic_height_in_map_units = mb_h_m1 + 1;\n\n\tsps->pic_height_in_map_units *= 2 - frame_mbs_only_flag;\n\n\tif (sps->pic_width_in_mbs >= MAX_MACROBLOCKS ||\n\t    sps->pic_height_in_map_units >= MAX_MACROBLOCKS) {\n\t\tre_fprintf(stderr, \"h264: sps: width/height overflow\\n\");\n\t\treturn EBADMSG;\n\t}\n\n\tif (!frame_mbs_only_flag) {\n\n\t\tif (getbit_get_left(&gb) < 1)\n\t\t\treturn EBADMSG;\n\n\t\t/* mb_adaptive_frame_field_flag */\n\t\t(void)get_bit(&gb);\n\t}\n\n\tif (getbit_get_left(&gb) < 1)\n\t\treturn EBADMSG;\n\n\t/* direct_8x8_inference_flag */\n\t(void)get_bit(&gb);\n\n\tif (getbit_get_left(&gb) < 1)\n\t\treturn EBADMSG;\n\n\tframe_cropping_flag = get_bit(&gb);\n\n\tif (frame_cropping_flag) {\n\n\t\tunsigned crop_left;\n\t\tunsigned crop_right;\n\t\tunsigned crop_top;\n\t\tunsigned crop_bottom;\n\n\t\tunsigned vsub   = (chroma_format_idc == 1) ? 1 : 0;\n\t\tunsigned hsub   = (chroma_format_idc == 1 ||\n\t\t\t      chroma_format_idc == 2) ? 1 : 0;\n\t\tunsigned sx = 1u << hsub;\n\t\tunsigned sy = (2u - frame_mbs_only_flag) << vsub;\n\n\t\tunsigned w = MACROBLOCK_SIZE * sps->pic_width_in_mbs;\n\t\tunsigned h = MACROBLOCK_SIZE * sps->pic_height_in_map_units;\n\n\t\terr  = get_ue_golomb(&gb, &crop_left);\n\t\terr |= get_ue_golomb(&gb, &crop_right);\n\t\terr |= get_ue_golomb(&gb, &crop_top);\n\t\terr |= get_ue_golomb(&gb, &crop_bottom);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif ((crop_left + crop_right ) * sx >= w ||\n\t\t    (crop_top  + crop_bottom) * sy >= h) {\n\t\t\tre_fprintf(stderr, \"h264: sps: crop values invalid\"\n\t\t\t\t   \" %u %u %u %u / %u %u\\n\",\n\t\t\t\t   crop_left, crop_right, crop_top,\n\t\t\t\t   crop_bottom, w, h);\n\t\t\treturn EBADMSG;\n\t\t}\n\n\t\tsps->frame_crop_left_offset   = sx * crop_left;\n\t\tsps->frame_crop_right_offset  = sx * crop_right;\n\t\tsps->frame_crop_top_offset    = sy * crop_top;\n\t\tsps->frame_crop_bottom_offset = sy * crop_bottom;\n\t}\n\n\t/* success */\n\tsps->profile_idc = profile_idc;\n\tsps->seq_parameter_set_id = (uint8_t)seq_parameter_set_id;\n\tsps->chroma_format_idc = chroma_format_idc;\n\n\treturn 0;\n}\n\n\nvoid h264_sps_resolution(const struct h264_sps *sps,\n\t\tunsigned *width, unsigned *height)\n{\n\tif (!sps || !width || !height)\n\t\treturn;\n\n\t*width = MACROBLOCK_SIZE * sps->pic_width_in_mbs\n\t\t- sps->frame_crop_left_offset\n\t\t- sps->frame_crop_right_offset;\n\n\t*height = MACROBLOCK_SIZE * sps->pic_height_in_map_units\n\t\t- sps->frame_crop_top_offset\n\t\t- sps->frame_crop_bottom_offset;\n}\n\n\nconst char *h264_sps_chroma_format_name(uint8_t chroma_format_idc)\n{\n\tswitch (chroma_format_idc) {\n\n\tcase 0: return \"monochrome\";\n\tcase 1: return \"YUV420\";\n\tcase 2: return \"YUV422\";\n\tcase 3: return \"YUV444\";\n\tdefault: return \"???\";\n\t}\n}\n"
  },
  {
    "path": "src/h265/nal.c",
    "content": "/**\n * @file h265/nal.c H.265 header parser\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_h265.h>\n#include <re_h264.h>\n\n\nvoid h265_nal_encode(uint8_t buf[2], unsigned nal_unit_type,\n\t\t    unsigned nuh_temporal_id_plus1)\n{\n\tif (!buf)\n\t\treturn;\n\n\tbuf[0] = (nal_unit_type & 0x3f) << 1;\n\tbuf[1] = nuh_temporal_id_plus1 & 0x07;\n}\n\n\nint h265_nal_encode_mbuf(struct mbuf *mb, const struct h265_nal *nal)\n{\n\tuint8_t buf[2];\n\n\th265_nal_encode(buf, nal->nal_unit_type, nal->nuh_temporal_id_plus1);\n\n\treturn mbuf_write_mem(mb, buf, sizeof(buf));\n}\n\n\nint h265_nal_decode(struct h265_nal *nal, const uint8_t *p)\n{\n\tbool forbidden_zero_bit;\n\n\tif (!nal || !p)\n\t\treturn EINVAL;\n\n\tforbidden_zero_bit         = p[0] >> 7;\n\tnal->nal_unit_type         = (p[0] >> 1) & 0x3f;\n\tnal->nuh_layer_id          = (p[0]&1)<<5 | p[1] >> 3;\n\tnal->nuh_temporal_id_plus1 = p[1] & 0x07;\n\n\tif (forbidden_zero_bit) {\n\t\tre_fprintf(stderr, \"h265: nal_decode: FORBIDDEN bit set\\n\");\n\t\treturn EBADMSG;\n\t}\n\n\treturn 0;\n}\n\n\nint h265_nal_print(struct re_printf *pf, const struct h265_nal *nal)\n{\n\tif (!nal)\n\t\treturn 0;\n\n\treturn re_hprintf(pf, \"type=%u(%s), TID=%u\\n\",\n\t\t\t  nal->nal_unit_type,\n\t\t\t  h265_nalunit_name(nal->nal_unit_type),\n\t\t\t  nal->nuh_temporal_id_plus1);\n}\n\n\nconst char *h265_nalunit_name(enum h265_naltype type)\n{\n\tswitch (type) {\n\n\t/* VCL class */\n\tcase H265_NAL_TRAIL_N:         return \"TRAIL_N\";\n\tcase H265_NAL_TRAIL_R:         return \"TRAIL_R\";\n\n\tcase H265_NAL_TSA_N:           return \"TSA_N\";\n\tcase H265_NAL_TSA_R:           return \"TSA_R\";\n\n\tcase H265_NAL_STSA_N:          return \"STSA_N\";\n\tcase H265_NAL_STSA_R:          return \"STSA_R\";\n\tcase H265_NAL_RADL_N:          return \"RADL_N\";\n\tcase H265_NAL_RADL_R:          return \"RADL_R\";\n\n\tcase H265_NAL_RASL_N:          return \"RASL_N\";\n\tcase H265_NAL_RASL_R:          return \"RASL_R\";\n\n\tcase H265_NAL_BLA_W_LP:        return \"BLA_W_LP\";\n\tcase H265_NAL_BLA_W_RADL:      return \"BLA_W_RADL\";\n\tcase H265_NAL_BLA_N_LP:        return \"BLA_N_LP\";\n\tcase H265_NAL_IDR_W_RADL:      return \"IDR_W_RADL\";\n\tcase H265_NAL_IDR_N_LP:        return \"IDR_N_LP\";\n\tcase H265_NAL_CRA_NUT:         return \"CRA_NUT\";\n\tcase H265_NAL_RSV_IRAP_VCL22:  return \"H265_NAL_RSV_IRAP_VCL22\";\n\tcase H265_NAL_RSV_IRAP_VCL23:  return \"H265_NAL_RSV_IRAP_VCL23\";\n\n\t/* non-VCL class */\n\tcase H265_NAL_VPS_NUT:         return \"VPS_NUT\";\n\tcase H265_NAL_SPS_NUT:         return \"SPS_NUT\";\n\tcase H265_NAL_PPS_NUT:         return \"PPS_NUT\";\n\tcase H265_NAL_AUD:             return \"AUD\";\n\tcase H265_NAL_EOS_NUT:         return \"EOS_NUT\";\n\tcase H265_NAL_EOB_NUT:         return \"EOB_NUT\";\n\tcase H265_NAL_FD_NUT:          return \"FD_NUT\";\n\tcase H265_NAL_PREFIX_SEI_NUT:  return \"PREFIX_SEI_NUT\";\n\tcase H265_NAL_SUFFIX_SEI_NUT:  return \"SUFFIX_SEI_NUT\";\n\n\t/* RFC 7798 */\n\tcase H265_NAL_AP:              return \"H265_NAL_AP\";\n\tcase H265_NAL_FU:              return \"H265_NAL_FU\";\n\t}\n\n\treturn \"???\";\n}\n\n\nbool h265_is_keyframe(enum h265_naltype type)\n{\n\t/* between 16 and 23 (inclusive) */\n\tswitch (type) {\n\n\tcase H265_NAL_BLA_W_LP:\n\tcase H265_NAL_BLA_W_RADL:\n\tcase H265_NAL_BLA_N_LP:\n\tcase H265_NAL_IDR_W_RADL:\n\tcase H265_NAL_IDR_N_LP:\n\tcase H265_NAL_CRA_NUT:\n\tcase H265_NAL_RSV_IRAP_VCL22:\n\tcase H265_NAL_RSV_IRAP_VCL23:\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\nstatic inline int packetize(bool marker, const uint8_t *buf, size_t len,\n\t\t\t    size_t maxlen, uint64_t rtp_ts,\n\t\t\t    h265_packet_h *pkth, void *arg)\n{\n\tint err = 0;\n\n\tif (len <= maxlen) {\n\t\terr = pkth(marker, rtp_ts, NULL, 0, buf, len, arg);\n\t}\n\telse {\n\t\tstruct h265_nal nal;\n\t\tuint8_t fu_hdr[3];\n\t\tconst size_t flen = maxlen - sizeof(fu_hdr);\n\n\t\terr = h265_nal_decode(&nal, buf);\n\t\tif (err) {\n\t\t\tre_fprintf(stderr, \"h265: encode: could not decode\"\n\t\t\t\t\" NAL of %zu bytes (%m)\\n\", len, err);\n\t\t\treturn err;\n\t\t}\n\n\t\th265_nal_encode(fu_hdr, H265_NAL_FU,\n\t\t\t\tnal.nuh_temporal_id_plus1);\n\n\t\tfu_hdr[2] = 1<<7 | nal.nal_unit_type;\n\n\t\tbuf+=2;\n\t\tlen-=2;\n\n\t\twhile (len > flen) {\n\t\t\terr |= pkth(false, rtp_ts, fu_hdr, 3, buf, flen,\n\t\t\t\t    arg);\n\n\t\t\tbuf += flen;\n\t\t\tlen -= flen;\n\t\t\tfu_hdr[2] &= ~(1 << 7); /* clear Start bit */\n\t\t}\n\n\t\tfu_hdr[2] |= 1<<6;  /* set END bit */\n\n\t\terr |= pkth(marker, rtp_ts, fu_hdr, 3, buf, len,\n\t\t\t    arg);\n\t}\n\n\treturn err;\n}\n\n\nint h265_packetize(uint64_t rtp_ts, const uint8_t *buf, size_t len,\n\t\t   size_t pktsize, h265_packet_h *pkth, void *arg)\n{\n\tconst uint8_t *start = buf;\n\tconst uint8_t *end   = buf + len;\n\tconst uint8_t *r;\n\tint err = 0;\n\n\tr = h264_find_startcode(start, end);\n\n\twhile (r < end) {\n\t\tconst uint8_t *r1;\n\t\tbool marker;\n\n\t\t/* skip zeros */\n\t\twhile (!*(r++))\n\t\t\t;\n\n\t\tr1 = h264_find_startcode(r, end);\n\n\t\tmarker = (r1 >= end);\n\n\t\terr |= packetize(marker, r, r1-r, pktsize, rtp_ts, pkth, arg);\n\n\t\tr = r1;\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/hash/func.c",
    "content": "/**\n * @file func.c  Hashmap functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <ctype.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_hash.h>\n\n\n#define FNV1_32A_INIT UINT32_C(0x811c9dc5)\n#define FNV_32_PRIME UINT32_C(0x01000193)\n\n\n/**\n * Calculate hash-value using \"Jenkins One-at-a-time\" hash algorithm.\n *\n * @param key  Pointer to key\n * @param len  Key length\n *\n * @return Calculated hash-value\n */\nuint32_t hash_joaat(const uint8_t *key, size_t len)\n{\n\tuint32_t hash = 0;\n\tsize_t i;\n\n\tfor (i = 0; i < len; i++) {\n\t\thash += key[i];\n\t\thash += (hash << 10);\n\t\thash ^= (hash >> 6);\n\t}\n\thash += (hash << 3);\n\thash ^= (hash >> 11);\n\thash += (hash << 15);\n\n\treturn hash;\n}\n\n\n/**\n * Calculate hash-value for a case-insensitive string\n *\n * @param str  String\n * @param len  Length of string\n *\n * @return Calculated hash-value\n */\nuint32_t hash_joaat_ci(const char *str, size_t len)\n{\n\tuint32_t hash = 0;\n\tsize_t i;\n\tif (!str)\n\t\treturn 0;\n\n\tfor (i = 0; i < len; i++) {\n\t\thash += tolower(str[i]);\n\t\thash += (hash << 10);\n\t\thash ^= (hash >> 6);\n\t}\n\thash += (hash << 3);\n\thash ^= (hash >> 11);\n\thash += (hash << 15);\n\n\treturn hash;\n}\n\n\n/**\n * Calculate hash-value for a NULL-terminated string\n *\n * @param str  String\n *\n * @return Calculated hash-value\n */\nuint32_t hash_joaat_str(const char *str)\n{\n\tuint32_t hash = 0;\n\n\twhile (*str) {\n\t\thash += *str++;\n\t\thash += (hash << 10);\n\t\thash ^= (hash >> 6);\n\t}\n\thash += (hash << 3);\n\thash ^= (hash >> 11);\n\thash += (hash << 15);\n\n\treturn hash;\n}\n\n\n/**\n * Calculate hash-value for a case-insensitive NULL-terminated string\n *\n * @param str  String\n *\n * @return Calculated hash-value\n */\nuint32_t hash_joaat_str_ci(const char *str)\n{\n\tuint32_t hash = 0;\n\n\twhile (*str) {\n\t\thash += tolower(*str++);\n\t\thash += (hash << 10);\n\t\thash ^= (hash >> 6);\n\t}\n\thash += (hash << 3);\n\thash ^= (hash >> 11);\n\thash += (hash << 15);\n\n\treturn hash;\n}\n\n\n/**\n * Calculate hash-value for a pointer-length object\n *\n * @param pl Pointer-length object\n *\n * @return Calculated hash-value\n */\nuint32_t hash_joaat_pl(const struct pl *pl)\n{\n\treturn pl ? hash_joaat((const uint8_t *)pl->p, pl->l) : 0;\n}\n\n\n/**\n * Calculate hash-value for a case-insensitive pointer-length object\n *\n * @param pl Pointer-length object\n *\n * @return Calculated hash-value\n */\nuint32_t hash_joaat_pl_ci(const struct pl *pl)\n{\n\treturn pl ? hash_joaat_ci(pl->p, pl->l) : 0;\n}\n\n\n/**\n * Calculate hash-value using fast hash algorithm.\n *\n * @param k    Pointer to key\n * @param len  Key length\n *\n * @return Calculated hash-value\n */\nuint32_t hash_fast(const char *k, size_t len)\n{\n\tuint32_t h = FNV1_32A_INIT;\n\n\tif (!k)\n\t\treturn 0;\n\n\twhile (len--) {\n\t\th ^= (uint32_t)*k++;\n\t\th *= FNV_32_PRIME;\n\t}\n\n\treturn h;\n}\n\n\n/**\n * Calculate hash-value for a NULL-terminated string\n *\n * @param str  String\n *\n * @return Calculated hash-value\n */\nuint32_t hash_fast_str(const char *str)\n{\n\tuint32_t h = FNV1_32A_INIT;\n\n\tif (!str)\n\t\treturn 0;\n\n\twhile (*str) {\n\t\th ^= (uint32_t)*str++;\n\t\th *= FNV_32_PRIME;\n\t}\n\n\treturn h;\n}\n"
  },
  {
    "path": "src/hash/hash.c",
    "content": "/**\n * @file hash.c  Hashmap table\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_hash.h>\n\n\n/** Defines a hashmap table */\nstruct hash {\n\tstruct list *bucket;  /**< Bucket with linked lists */\n\tuint32_t bsize;       /**< Bucket size              */\n};\n\n\nstatic void hash_destructor(void *data)\n{\n\tstruct hash *h = data;\n\n\tmem_deref(h->bucket);\n}\n\n\n/**\n * Allocate a new hashmap table\n *\n * @param hp     Address of hashmap pointer\n * @param bsize  Bucket size\n *\n * @return 0 if success, otherwise errorcode\n */\nint hash_alloc(struct hash **hp, uint32_t bsize)\n{\n\tstruct hash *h;\n\tint err = 0;\n\n\tif (!hp || !bsize)\n\t\treturn EINVAL;\n\n\t/* Validate bucket size */\n\tif (bsize & (bsize-1))\n\t\treturn EINVAL;\n\n\th = mem_zalloc(sizeof(*h), hash_destructor);\n\tif (!h)\n\t\treturn ENOMEM;\n\n\th->bsize = bsize;\n\n\th->bucket = mem_zalloc(bsize*sizeof(*h->bucket), NULL);\n\tif (!h->bucket) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(h);\n\telse\n\t\t*hp = h;\n\n\treturn err;\n}\n\n\n/**\n * Add an element to the hashmap table\n *\n * @param h      Hashmap table\n * @param key    Hash key\n * @param le     List element\n * @param data   Element data\n */\nvoid hash_append(struct hash *h, uint32_t key, struct le *le, void *data)\n{\n\tif (!h || !le)\n\t\treturn;\n\n\tlist_append(&h->bucket[key & (h->bsize-1)], le, data);\n}\n\n\n/**\n * Unlink an element from the hashmap table\n *\n * @param le     List element\n */\nvoid hash_unlink(struct le *le)\n{\n\tlist_unlink(le);\n}\n\n\n/**\n * Apply a handler function to all elements in the hashmap with a matching key\n *\n * @param h   Hashmap table\n * @param key Hash key\n * @param ah  Apply handler\n * @param arg Handler argument\n *\n * @return List element if traversing stopped, otherwise NULL\n */\nstruct le *hash_lookup(const struct hash *h, uint32_t key, list_apply_h *ah,\n\t\t       void *arg)\n{\n\tif (!h || !ah)\n\t\treturn NULL;\n\n\treturn list_apply(&h->bucket[key & (h->bsize-1)], true, ah, arg);\n}\n\n\n/**\n * Apply a handler function to all elements in the hashmap\n *\n * @param h   Hashmap table\n * @param ah  Apply handler\n * @param arg Handler argument\n *\n * @return List element if traversing stopped, otherwise NULL\n */\nstruct le *hash_apply(const struct hash *h, list_apply_h *ah, void *arg)\n{\n\tstruct le *le = NULL;\n\tuint32_t i;\n\n\tif (!h || !ah)\n\t\treturn NULL;\n\n\tfor (i=0; (i<h->bsize) && !le; i++)\n\t\tle = list_apply(&h->bucket[i], true, ah, arg);\n\n\treturn le;\n}\n\n\n/**\n * Return bucket list for a given bucket index\n *\n * @param h  Hashmap table\n * @param i  Bucket index\n *\n * @return Bucket list if valid input, otherwise NULL\n */\nstruct list *hash_list_idx(const struct hash *h, uint32_t i)\n{\n\tif (!h || i >= h->bsize)\n\t\treturn NULL;\n\n\treturn &h->bucket[i];\n}\n\n\n/**\n * Return bucket list for a given key\n *\n * @param h   Hashmap table\n * @param key Hash key\n *\n * @return Bucket list if valid input, otherwise NULL\n */\nstruct list *hash_list(const struct hash *h, uint32_t key)\n{\n\treturn h ? &h->bucket[key & (h->bsize - 1)] : NULL;\n}\n\n\n/**\n * Get hash bucket size\n *\n * @param h Hashmap table\n *\n * @return hash bucket size\n */\nuint32_t hash_bsize(const struct hash *h)\n{\n\treturn h ? h->bsize : 0;\n}\n\n\n/**\n * Flush a hashmap and free all elements\n *\n * @param h Hashmap table\n */\nvoid hash_flush(struct hash *h)\n{\n\tuint32_t i;\n\n\tif (!h)\n\t\treturn;\n\n\tfor (i=0; i<h->bsize; i++)\n\t\tlist_flush(&h->bucket[i]);\n}\n\n\n/**\n * Clear a hashmap without dereferencing the elements\n *\n * @param h Hashmap table\n */\nvoid hash_clear(struct hash *h)\n{\n\tuint32_t i;\n\n\tif (!h)\n\t\treturn;\n\n\tfor (i=0; i<h->bsize; i++)\n\t\tlist_clear(&h->bucket[i]);\n}\n\n\n/**\n * Calculate a valid hash size from a random size\n *\n * @param size Requested size\n *\n * @return Valid hash size\n */\nuint32_t hash_valid_size(uint32_t size)\n{\n\tuint32_t x;\n\n\tfor (x=0; (uint32_t)1<<x < size && x < 31; x++)\n\t\t;\n\n\treturn 1u<<x;\n}\n\n\n/**\n * Debug Hashmap table\n *\n * @param pf  Print handler where debug output is printed to\n * @param h   Hashmap table\n *\n * @return 0 if success, otherwise errorcode\n */\nint hash_debug(struct re_printf *pf, struct hash *h)\n{\n\tint err;\n\n\tif (!h)\n\t\treturn EINVAL;\n\n\terr = re_hprintf(pf, \"hash (bsize %u) list entries:\\n\", h->bsize);\n\tfor (uint32_t i = 0; i < h->bsize; i++) {\n\t\tstruct le *he = hash_list_idx(h, i)->head;\n\n\t\tif (!he)\n\t\t\tcontinue;\n\n\t\tuint32_t c = list_count(he->list);\n\t\tif (!c)\n\t\t\tcontinue;\n\n\t\terr |= re_hprintf(pf, \"  [%u]: %u\\n\", i, c);\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/hmac/apple/hmac.c",
    "content": "/**\n * @file apple/hmac.c  HMAC using Apple API\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n\n#include <string.h>\n#include <CommonCrypto/CommonHMAC.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_hmac.h>\n\n\nenum { KEY_SIZE = 256 };\n\nstruct hmac {\n\tCCHmacContext ctx;\n\tuint8_t key[KEY_SIZE];\n\tsize_t key_len;\n\tCCHmacAlgorithm algo;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct hmac *hmac = arg;\n\n\tmemset(&hmac->ctx, 0, sizeof(hmac->ctx));\n}\n\n\nint hmac_create(struct hmac **hmacp, enum hmac_hash hash,\n\t\tconst uint8_t *key, size_t key_len)\n{\n\tstruct hmac *hmac;\n\tCCHmacAlgorithm algo;\n\n\tif (!hmacp || !key || !key_len || key_len > KEY_SIZE)\n\t\treturn EINVAL;\n\n\tswitch (hash) {\n\n\tcase HMAC_HASH_SHA1:\n\t\talgo = kCCHmacAlgSHA1;\n\t\tbreak;\n\n\tcase HMAC_HASH_SHA256:\n\t\talgo = kCCHmacAlgSHA256;\n\t\tbreak;\n\n\tdefault:\n\t\treturn ENOTSUP;\n\t}\n\n\thmac = mem_zalloc(sizeof(*hmac), destructor);\n\tif (!hmac)\n\t\treturn ENOMEM;\n\n\tmemcpy(hmac->key, key, key_len);\n\thmac->key_len = key_len;\n\thmac->algo = algo;\n\n\t*hmacp = hmac;\n\n\treturn 0;\n}\n\n\nint hmac_digest(struct hmac *hmac, uint8_t *md, size_t md_len,\n\t\tconst uint8_t *data, size_t data_len)\n{\n\tif (!hmac || !md || !md_len || !data || !data_len)\n\t\treturn EINVAL;\n\n\t/* reset state */\n\tCCHmacInit(&hmac->ctx, hmac->algo, hmac->key, hmac->key_len);\n\n\tCCHmacUpdate(&hmac->ctx, data, data_len);\n\tCCHmacFinal(&hmac->ctx, md);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/hmac/hmac.c",
    "content": "/**\n * @file hmac/hmac.c  HMAC-SHA1\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_sha.h>\n#include <re_hmac.h>\n\n\nstruct hmac {\n\tuint8_t key[SHA_DIGEST_LENGTH];\n\tsize_t key_len;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct hmac *hmac = arg;\n\n\tmemset(hmac, 0, sizeof(*hmac));\n}\n\n\nint hmac_create(struct hmac **hmacp, enum hmac_hash hash,\n\t\tconst uint8_t *key, size_t key_len)\n{\n\tstruct hmac *hmac;\n\n\tif (!hmacp || !key || !key_len)\n\t\treturn EINVAL;\n\n\tif (hash != HMAC_HASH_SHA1)\n\t\treturn ENOTSUP;\n\n\tif (key_len > SHA_DIGEST_LENGTH)\n\t\treturn EINVAL;\n\n\thmac = mem_zalloc(sizeof(*hmac), destructor);\n\tif (!hmac)\n\t\treturn ENOMEM;\n\n\tmemcpy(hmac->key, key, key_len);\n\thmac->key_len = key_len;\n\n\t*hmacp = hmac;\n\n\treturn 0;\n}\n\n\nint hmac_digest(struct hmac *hmac, uint8_t *md, size_t md_len,\n\t\tconst uint8_t *data, size_t data_len)\n{\n\tif (!hmac || !md || !md_len || !data || !data_len)\n\t\treturn EINVAL;\n\n\thmac_sha1(hmac->key, hmac->key_len, data, data_len, md, md_len);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/hmac/hmac_sha1.c",
    "content": "/**\n * @file hmac_sha1.c  Implements HMAC-SHA1 as of RFC 2202\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#ifdef USE_OPENSSL\n#include <openssl/sha.h>\n#include <openssl/hmac.h>\n#include <openssl/err.h>\n#elif defined (__APPLE__)\n#include <CommonCrypto/CommonHMAC.h>\n#elif defined (WIN32)\n#include <windows.h>\n#include <wincrypt.h>\n#elif defined (USE_MBEDTLS)\n#include <mbedtls/md.h>\n#include <mbedtls/error.h>\n#endif\n#include <re_hmac.h>\n\n\n#define DEBUG_MODULE \"hmac\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#if !defined (USE_OPENSSL) && defined (WIN32)\nstatic void compute_hash(ALG_ID alg_id, const void* data, size_t dataSize,\n\t\t\t uint8_t hashBuf[64], DWORD hashSize,\n\t\t\t const void *hmacSecret, size_t hmacSecretSize)\n{\n\tDWORD hashSizeSize = sizeof(hashSize);\n\tHCRYPTPROV context;\n\tHCRYPTKEY hmackey = 0;\n\n\tCryptAcquireContext(&context, 0, 0, PROV_RSA_FULL,CRYPT_VERIFYCONTEXT);\n\n\tstruct HmacSecretBlob {\n\t\tBLOBHEADER header;\n\t\tDWORD hmacSecretSize;\n\t\tBYTE hmacSecret[1];\n\t};\n\tsize_t hmacSecretBlobSize =\n\t\tMAX(offsetof(struct HmacSecretBlob, hmacSecret) +\n\t\t    hmacSecretSize, sizeof(struct HmacSecretBlob));\n\tuint8_t blobData[256];\n\tstruct HmacSecretBlob* hmacSecretBlob\n\t\t= (struct HmacSecretBlob*) ( blobData );\n\n\thmacSecretBlob->header.bType = PLAINTEXTKEYBLOB;\n\thmacSecretBlob->header.bVersion = CUR_BLOB_VERSION;\n\thmacSecretBlob->header.reserved = 0;\n\thmacSecretBlob->header.aiKeyAlg = CALG_RC2;\n\thmacSecretBlob->hmacSecretSize = (DWORD)hmacSecretSize;\n\tmemcpy(hmacSecretBlob->hmacSecret, hmacSecret, hmacSecretSize);\n\n\tCryptImportKey(context, blobData, (DWORD)hmacSecretBlobSize, 0,\n\t\t       CRYPT_IPSEC_HMAC_KEY, &hmackey);\n\n\tHCRYPTHASH hash;\n\n\tif (CryptCreateHash(context, CALG_HMAC, hmackey, 0, &hash)) {\n\n\t\tHMAC_INFO info = { 0 };\n\t\tinfo.HashAlgid = alg_id;\n\n\t\tCryptSetHashParam(hash, HP_HMAC_INFO, (BYTE *)&info, 0);\n\n\t\tCryptGetHashParam(hash, HP_HASHSIZE,\n\t\t\t\t  (BYTE *)&hashSize, &hashSizeSize, 0);\n\t\tif (hashSize == 0) {\n\t\t\tDEBUG_WARNING(\"INVALID HASHSIZE\\n\");\n\t\t}\n\n\t\tCryptHashData(hash, (BYTE*)data, (DWORD)dataSize, 0);\n\t\tCryptGetHashParam(hash, HP_HASHVAL, hashBuf, &hashSize, 0);\n\n\t\tCryptDestroyHash(hash);\n\t}\n\n\tCryptDestroyKey(hmackey);\n\tCryptReleaseContext(context, 0);\n}\n#endif\n\n\n/**\n * Function to compute the digest\n *\n * @param k   Secret key\n * @param lk  Length of the key in bytes\n * @param d   Data\n * @param ld  Length of data in bytes\n * @param out Digest output\n * @param t   Size of digest output\n */\nvoid hmac_sha1(const uint8_t *k,  /* secret key */\n\t       size_t   lk,       /* length of the key in bytes */\n\t       const uint8_t *d,  /* data */\n\t       size_t   ld,       /* length of data in bytes */\n\t       uint8_t *out,      /* output buffer, at least \"t\" bytes */\n\t       size_t   t)\n{\n#ifdef USE_OPENSSL\n\t(void)t;\n\n\tif (!HMAC(EVP_sha1(), k, (int)lk, d, ld, out, NULL))\n\t\tERR_clear_error();\n#elif defined (__APPLE__)\n\t(void)t;\n\n\tCCHmac(kCCHmacAlgSHA1, k, lk, d, ld, out);\n\n#elif defined (WIN32)\n\tcompute_hash(CALG_SHA1, d, ld,\n\t\t     out, (DWORD)t, k, lk);\n#elif defined (MBEDTLS_MD_C)\n\tint err;\n\t(void)t;\n\n\terr = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1),\n\t\t\t      k, lk, d, ld, out);\n\tif (err)\n\t\tDEBUG_WARNING(\"mbedtls_md_hmac: %s\\n\",\n\t\t\t      mbedtls_high_level_strerr(err));\n\n#else\n\t(void)k;\n\t(void)lk;\n\t(void)d;\n\t(void)ld;\n\t(void)out;\n\t(void)t;\n\n#error missing HMAC-SHA1 backend\n\n\n#endif\n}\n\n\nvoid hmac_sha256(const uint8_t *key, size_t key_len,\n\t\t const uint8_t *data, size_t data_len,\n\t\t uint8_t *out, size_t out_len)\n{\n#ifdef USE_OPENSSL\n\n\t(void)out_len;\n\n\tif (!HMAC(EVP_sha256(), key, (int)key_len, data, data_len, out, NULL))\n\t\tERR_clear_error();\n\n#elif defined (__APPLE__)\n\t(void)out_len;\n\n\tCCHmac(kCCHmacAlgSHA256, key, key_len, data, data_len, out);\n\n#elif defined (WIN32)\n\tcompute_hash(CALG_SHA_256, data, data_len,\n\t\t     out, (DWORD)out_len, key, key_len);\n#elif defined (MBEDTLS_MD_C)\n\tint err;\n\t(void)out_len;\n\n\terr = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),\n\t\t\t      key, key_len, data, data_len, out);\n\tif (err)\n\t\tDEBUG_WARNING(\"mbedtls_md_hmac: %s\\n\",\n\t\t\t      mbedtls_high_level_strerr(err));\n#else\n\t(void)key;\n\t(void)key_len;\n\t(void)data;\n\t(void)data_len;\n\t(void)out;\n\t(void)out_len;\n\n#error missing HMAC-SHA256 backend\n\n\n#endif\n}\n"
  },
  {
    "path": "src/hmac/openssl/hmac.c",
    "content": "/**\n * @file openssl/hmac.c  HMAC using OpenSSL\n *\n * Copyright (C) 2010 Creytiv.com\n * Copyright (C) 2022 Sebastian Reimers\n */\n\n#include <openssl/hmac.h>\n#include <openssl/err.h>\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_hmac.h>\n\n\nstruct hmac {\n\tconst EVP_MD *evp;\n\tuint8_t *key;\n\tint key_len;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct hmac *hmac = arg;\n\n\tmem_deref(hmac->key);\n}\n\n\nint hmac_create(struct hmac **hmacp, enum hmac_hash hash, const uint8_t *key,\n\t\tsize_t key_len)\n{\n\tstruct hmac *hmac;\n\tint err = 0;\n\n\tif (!hmacp || !key || !key_len)\n\t\treturn EINVAL;\n\n\thmac = mem_zalloc(sizeof(*hmac), destructor);\n\tif (!hmac)\n\t\treturn ENOMEM;\n\n\thmac->key = mem_zalloc(key_len, NULL);\n\tif (!hmac->key) {\n\t\terr = ENOMEM;\n\t\tgoto error;\n\t}\n\n\tmemcpy(hmac->key, key, key_len);\n\thmac->key_len = (int)key_len;\n\n\tswitch (hash) {\n\n\tcase HMAC_HASH_SHA1:\n\t\thmac->evp = EVP_sha1();\n\t\tbreak;\n\n\tcase HMAC_HASH_SHA256:\n\t\thmac->evp = EVP_sha256();\n\t\tbreak;\n\n\tdefault:\n\t\terr = ENOTSUP;\n\t\tgoto error;\n\t}\n\n\t*hmacp = hmac;\n\n\treturn 0;\n\nerror:\n\tmem_deref(hmac);\n\treturn err;\n}\n\n\nint hmac_digest(struct hmac *hmac, uint8_t *md, size_t md_len,\n\t\tconst uint8_t *data, size_t data_len)\n{\n\tunsigned int len = (unsigned int)md_len;\n\tunsigned char *rval;\n\n\tif (!hmac || !md || !md_len || !data || !data_len)\n\t\treturn EINVAL;\n\n\trval = HMAC(hmac->evp, hmac->key, hmac->key_len, data, data_len, md,\n\t\t    &len);\n\tif (!rval) {\n\t\tERR_clear_error();\n\t\treturn EPROTO;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/http/auth.c",
    "content": "/**\n * @file http/auth.c HTTP Authentication\n *\n * Copyright (C) 2011 Creytiv.com\n */\n\n#include <string.h>\n#include <time.h>\n#include <re_types.h>\n#include <re_sys.h>\n#include <re_md5.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_fmt.h>\n#include <re_msg.h>\n#include <re_httpauth.h>\n#include <re_http.h>\n\n\nenum {\n\tNONCE_EXPIRES  = 300,\n\tNONCE_MIN_SIZE = 33,\n};\n\n\nstatic uint64_t secret;\nstatic bool secret_set;\n\n\n/**\n * Print HTTP digest authentication challenge\n *\n * @param pf   Print function for output\n * @param auth Authentication parameters\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_auth_print_challenge(struct re_printf *pf,\n\t\t\t      const struct http_auth *auth)\n{\n\tuint8_t key[MD5_SIZE];\n\tuint64_t nv[2];\n\n\tif (!auth)\n\t\treturn 0;\n\n\tif (!secret_set) {\n\t\tsecret = rand_u64();\n\t\tsecret_set = true;\n\t}\n\n\tnv[0] = time(NULL);\n\tnv[1] = secret;\n\n\tmd5((uint8_t *)nv, sizeof(nv), key);\n\n\treturn re_hprintf(pf,\n\t\t\t  \"Digest realm=\\\"%s\\\", nonce=\\\"%w%llx\\\", \"\n\t\t\t  \"qop=\\\"auth\\\"%s\",\n\t\t\t  auth->realm,\n\t\t\t  key, sizeof(key), nv[0],\n\t\t\t  auth->stale ? \", stale=true\" : \"\");\n}\n\n\nstatic int chk_nonce(const struct pl *nonce, uint32_t expires)\n{\n\tuint8_t nkey[MD5_SIZE], ckey[MD5_SIZE];\n\tuint64_t nv[2];\n\tstruct pl pl;\n\tint64_t age;\n\tunsigned i;\n\n\tif (!nonce || !nonce->p || nonce->l < NONCE_MIN_SIZE)\n\t\treturn EINVAL;\n\n\tpl = *nonce;\n\n\tfor (i=0; i<sizeof(nkey); i++) {\n\t\tnkey[i]  = ch_hex(*pl.p++) << 4;\n\t\tnkey[i] += ch_hex(*pl.p++);\n\t\tpl.l -= 2;\n\t}\n\n\tnv[0] = pl_x64(&pl);\n\tnv[1] = secret;\n\n\tmd5((uint8_t *)nv, sizeof(nv), ckey);\n\n\tif (memcmp(nkey, ckey, MD5_SIZE))\n\t\treturn EAUTH;\n\n\tage = time(NULL) - nv[0];\n\n\tif (age < 0 || age > expires)\n\t\treturn ETIMEDOUT;\n\n\treturn 0;\n}\n\n\n/**\n * Check HTTP digest authorization\n *\n * @param hval   Authorization header value\n * @param method Request method\n * @param auth   Authentication parameters\n * @param authh  Authentication handler\n * @param arg    Authentication handler argument\n *\n * @return true if check is passed, otherwise false\n */\nbool http_auth_check(const struct pl *hval, const struct pl *method,\n\t\t     struct http_auth *auth, http_auth_h *authh, void *arg)\n{\n\tstruct httpauth_digest_resp resp;\n\tuint8_t ha1[MD5_SIZE];\n\n\tif (!hval || !method || !auth || !authh)\n\t\treturn false;\n\n\tif (httpauth_digest_response_decode(&resp, hval))\n\t\treturn false;\n\n\tif (pl_strcasecmp(&resp.realm, auth->realm))\n\t\treturn false;\n\n\tif (chk_nonce(&resp.nonce, NONCE_EXPIRES)) {\n\t\tauth->stale = true;\n\t\treturn false;\n\t}\n\n\tif (authh(&resp.username, ha1, arg))\n\t\treturn false;\n\n\tif (httpauth_digest_response_auth(&resp, method, ha1))\n\t\treturn false;\n\n\treturn true;\n}\n\n\n/**\n * Check HTTP digest authorization of an HTTP request\n *\n * @param msg   HTTP message\n * @param auth  Authentication parameters\n * @param authh Authentication handler\n * @param arg   Authentication handler argument\n *\n * @return true if check is passed, otherwise false\n */\nbool http_auth_check_request(const struct http_msg *msg,\n\t\t\t     struct http_auth *auth,\n\t\t\t     http_auth_h *authh, void *arg)\n{\n\tconst struct http_hdr *hdr;\n\n\tif (!msg)\n\t\treturn false;\n\n\thdr = http_msg_hdr(msg, HTTP_HDR_AUTHORIZATION);\n\tif (!hdr)\n\t\treturn false;\n\n\treturn http_auth_check(&hdr->val, &msg->met, auth, authh, arg);\n}\n"
  },
  {
    "path": "src/http/chunk.c",
    "content": "/**\n * @file http/chunk.c Chunked Transfer Encoding\n *\n * Copyright (C) 2011 Creytiv.com\n */\n\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include \"http.h\"\n\n\nstatic int decode_chunk_size(struct http_chunk *chunk, struct mbuf *mb)\n{\n\twhile (mbuf_get_left(mb)) {\n\n\t\tchar ch = (char)mbuf_read_u8(mb);\n\t\tuint8_t c;\n\n\t\tif (ch == '\\n') {\n\t\t\tif (chunk->digit) {\n\t\t\t\tchunk->digit = false;\n\t\t\t\tchunk->param = false;\n\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (chunk->param)\n\t\t\tcontinue;\n\n\t\tif ('0' <= ch && ch <= '9')\n\t\t\tc = ch - '0';\n\t\telse if ('A' <= ch && ch <= 'F')\n\t\t\tc = ch - 'A' + 10;\n\t\telse if ('a' <= ch && ch <= 'f')\n\t\t\tc = ch - 'a' + 10;\n\t\telse if (ch == '\\r' || ch == ' ' || ch == '\\t')\n\t\t\tcontinue;\n\t\telse if (ch == ';' && chunk->digit) {\n\t\t\tchunk->param = true;\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t\treturn EPROTO;\n\n\t\tchunk->digit = true;\n\n\t\tchunk->size <<= 4;\n\t\tchunk->size += c;\n\t}\n\n\treturn ENODATA;\n}\n\n\nstatic int decode_trailer(struct http_chunk *chunk, struct mbuf *mb)\n{\n\twhile (mbuf_get_left(mb)) {\n\n\t\tchar ch = (char)mbuf_read_u8(mb);\n\n\t\tif (ch == '\\n') {\n\t\t\tif (++chunk->lf >= 2)\n\t\t\t\treturn 0;\n\t\t}\n\t\telse if (ch != '\\r')\n\t\t\tchunk->lf = 0;\n\t}\n\n\treturn ENODATA;\n}\n\n\nint http_chunk_decode(struct http_chunk *chunk, struct mbuf *mb, size_t *size)\n{\n\tint err;\n\n\tif (!chunk || !mb || !size)\n\t\treturn EINVAL;\n\n\tif (chunk->trailer) {\n\t\terr = decode_trailer(chunk, mb);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\t*size = 0;\n\n\t\treturn 0;\n\t}\n\n\terr = decode_chunk_size(chunk, mb);\n\tif (err)\n\t\treturn err;\n\n\tif (chunk->size == 0) {\n\t\tchunk->trailer = true;\n\t\tchunk->lf = 1;\n\n\t\terr = decode_trailer(chunk, mb);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\t*size = chunk->size;\n\tchunk->size = 0;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/http/client.c",
    "content": "/**\n * @file http/client.c HTTP Client\n *\n * Copyright (C) 2011 Creytiv.com\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_tmr.h>\n#include <re_srtp.h>\n#include <re_tcp.h>\n#include <re_tls.h>\n#include <re_dns.h>\n#include <re_msg.h>\n#include <re_http.h>\n#include <re_sys.h>\n#include \"http.h\"\n\n\n#define DEBUG_MODULE \"http_client\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nenum {\n\tCONN_TIMEOUT = 30000,\n\tRECV_TIMEOUT = 60000,\n\tIDLE_TIMEOUT = 900000,\n\tBUFSIZE_MAX  = 524288,\n\tCONN_BSIZE   = 256,\n\tQUERY_HASH_SIZE = 16,\n\tTCP_HASH_SIZE = 2,\n};\n\nstruct http_cli {\n\tstruct http_conf conf;\n\tstruct list reql;\n\tstruct hash *ht_conn;\n\tstruct dnsc *dnsc;\n\tstruct tls *tls;\n\tchar *tlshn;\n\tstruct mbuf *cert;\n\tstruct mbuf *key;\n\tstruct sa laddr;\n\tstruct sa laddr6;\n\tsize_t bufsize_max;\n};\n\nstruct conn;\n\nstruct http_req {\n\tstruct http_chunk chunk;\n\tstruct sa srvv[16];\n\tstruct le le;\n\tstruct http_req **reqp;\n\tstruct http_cli *cli;\n\tstruct http_msg *msg;\n\tstruct dns_query *dq;\n\tstruct dns_query *dq6;\n\tstruct conn *conn;\n\tstruct mbuf *mbreq;\n\tstruct mbuf *mb;\n\tchar *host;\n\thttp_resp_h *resph;\n\thttp_data_h *datah;\n\thttp_conn_h *connh;\n\tvoid *arg;\n\tsize_t rx_len;\n\tunsigned srvc;\n\tuint16_t port;\n\tbool chunked;\n\tbool secure;\n\tbool close;\n\thttp_bodyh *bodyh;\n};\n\n\nstatic const struct http_conf default_conf = {\n\tCONN_TIMEOUT,\n\tRECV_TIMEOUT,\n\tIDLE_TIMEOUT,\n};\n\n\nstruct conn {\n\tstruct tmr tmr;\n\tstruct sa addr;\n\tstruct le he;\n\tstruct http_req *req;\n\tstruct tls_conn *sc;\n\tstruct tcp_conn *tc;\n\tuint64_t usec;\n};\n\n\nstatic void req_close(struct http_req *req, int err,\n\t\t      const struct http_msg *msg);\nstatic int req_connect(struct http_req *req);\nstatic void timeout_handler(void *arg);\n\n\nstatic void cli_destructor(void *arg)\n{\n\tstruct http_cli *cli = arg;\n\tstruct le *le = cli->reql.head;\n\n\twhile (le) {\n\t\tstruct http_req *req = le->data;\n\n\t\tle = le->next;\n\t\treq_close(req, ECONNABORTED, NULL);\n\t}\n\n\thash_flush(cli->ht_conn);\n\tmem_deref(cli->ht_conn);\n\tmem_deref(cli->cert);\n\tmem_deref(cli->key);\n\tmem_deref(cli->dnsc);\n\tmem_deref(cli->tls);\n\tmem_deref(cli->tlshn);\n}\n\n\nstatic void req_destructor(void *arg)\n{\n\tstruct http_req *req = arg;\n\n\tlist_unlink(&req->le);\n\tmem_deref(req->msg);\n\tmem_deref(req->dq);\n\tmem_deref(req->dq6);\n\tmem_deref(req->conn);\n\tmem_deref(req->mbreq);\n\tmem_deref(req->mb);\n\tmem_deref(req->host);\n}\n\n\nstatic void conn_destructor(void *arg)\n{\n\tstruct conn *conn = arg;\n\n\ttmr_cancel(&conn->tmr);\n\thash_unlink(&conn->he);\n\tmem_deref(conn->sc);\n\tmem_deref(conn->tc);\n}\n\n\nstatic void conn_idle(struct conn *conn)\n{\n\tstruct http_req *req;\n\tstruct http_cli *cli;\n\n\tif (!conn)\n\t\treturn;\n\n\treq = conn->req;\n\tif (!req)\n\t\tgoto out;\n\n\tcli = req->cli;\n\tif (!cli)\n\t\tgoto out;\n\n\ttmr_start(&conn->tmr, cli->conf.idle_timeout, timeout_handler, conn);\n\n out:\n\tconn->req = NULL;\n}\n\n\nstatic void req_close(struct http_req *req, int err,\n\t\t      const struct http_msg *msg)\n{\n\tlist_unlink(&req->le);\n\treq->dq = mem_deref(req->dq);\n\treq->dq6 = mem_deref(req->dq6);\n\treq->datah = NULL;\n\n\tif (req->conn) {\n\t\tif (req->connh)\n\t\t\treq->connh(req->conn->tc, req->conn->sc, req->arg);\n\n\t\tif (err || req->close || req->connh)\n\t\t\tmem_deref(req->conn);\n\t\telse\n\t\t\tconn_idle(req->conn);\n\n\t\treq->conn = NULL;\n\t}\n\n\treq->connh = NULL;\n\n\tif (req->reqp) {\n\t\t*req->reqp = NULL;\n\t\treq->reqp = NULL;\n\t}\n\n\tif (req->resph) {\n\t\tif (msg)\n\t\t\tmsg->mb->pos = 0;\n\n\t\treq->resph(err, msg, req->arg);\n\t\treq->resph = NULL;\n\t}\n\n\tmem_deref(req);\n}\n\n\nstatic void try_next(struct conn *conn, int err)\n{\n\tstruct http_req *req = conn->req;\n\tbool retry = conn->usec > 1;\n\n\tmem_deref(conn);\n\n\tif (!req)\n\t\treturn;\n\n\treq->conn = NULL;\n\n\tif (retry)\n\t\t++req->srvc;\n\n\tif (req->srvc > 0 && !req->msg) {\n\n\t\terr = req_connect(req);\n\t\tif (!err)\n\t\t\treturn;\n\t}\n\n\treq_close(req, err, NULL);\n}\n\n\nstatic int write_body_buf(struct http_msg *msg, const uint8_t *buf,\n\tsize_t sz, size_t max_size)\n{\n\tif ((msg->mb->pos + sz) > max_size)\n\t\treturn EOVERFLOW;\n\n\treturn mbuf_write_mem(msg->mb, buf, sz);\n}\n\n\nstatic int write_body(struct http_req *req, struct mbuf *mb)\n{\n\tconst size_t size = min(mbuf_get_left(mb), req->rx_len);\n\tint err;\n\n\tif (size == 0)\n\t\treturn 0;\n\n\tif (req->datah)\n\t\terr = req->datah(mbuf_buf(mb), size, req->msg, req->arg);\n\telse\n\t\terr = write_body_buf(req->msg, mbuf_buf(mb), size,\n\t\t\treq->cli->bufsize_max);\n\n\tif (err)\n\t\treturn err;\n\n\treq->rx_len -= size;\n\tmb->pos     += size;\n\n\treturn 0;\n}\n\n\nstatic int req_recv(struct http_req *req, struct mbuf *mb, bool *last)\n{\n\tint err;\n\n\t*last = false;\n\n\tif (!req->chunked) {\n\n\t\terr = write_body(req, mb);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (req->rx_len == 0)\n\t\t\t*last = true;\n\n\t\treturn 0;\n\t}\n\n\ttmr_start(&req->conn->tmr, req->cli->conf.recv_timeout,\n\t\t  timeout_handler, req->conn);\n\n\twhile (mbuf_get_left(mb)) {\n\n\t\tif (req->rx_len == 0) {\n\n\t\t\terr = http_chunk_decode(&req->chunk, mb, &req->rx_len);\n\t\t\tif (err == ENODATA)\n\t\t\t\treturn 0;\n\t\t\telse if (err)\n\t\t\t\treturn err;\n\t\t\telse if (req->rx_len == 0) {\n\t\t\t\t*last = true;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\n\t\terr = write_body(req, mb);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\nstatic void timeout_handler(void *arg)\n{\n\tstruct conn *conn = arg;\n\n\ttry_next(conn, ETIMEDOUT);\n}\n\n\nstatic int read_req_data(struct http_req *req)\n{\n\tint err = 0;\n\tstruct mbuf *mb;\n\tsize_t rlen = 0;\n\n\tif (!req->bodyh)\n\t\treturn 0;\n\n\tmb = mbuf_alloc(1);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\trlen = req->bodyh(mb, req->arg);\n\tif (!rlen)\n\t\tgoto out;\n\n\terr = mbuf_write_mem(req->mbreq, mb->buf, mb->end);\n\tif (err)\n\t\tgoto out;\n\nout:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstatic int send_buf(struct tcp_conn *tc, struct mbuf *large, size_t max_size)\n{\n\tstruct mbuf *mb = NULL;\n\tsize_t len;\n\tint err = 0;\n\n\tif (!tc || !large)\n\t\treturn EINVAL;\n\n\tif (!mbuf_get_left(large))\n\t\treturn 0;\n\n\tmb = mbuf_alloc_ref(large);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tlen = min(mbuf_get_left(large), max_size);\n\tmb->end = large->pos + len;\n\n\terr = tcp_send(tc, mb);\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_advance(large, len);\n\nout:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic int send_req_buf(struct conn *conn)\n{\n\tint err;\n\tstruct http_req *req;\n\n\tif (!conn)\n\t\treturn EINVAL;\n\n\treq = conn->req;\n\n\tif (!req || !req->cli)\n\t\treturn EINVAL;\n\n\tif (!mbuf_get_left(req->mbreq))\n\t\treturn 0;\n\n\terr = send_buf(conn->tc, req->mbreq, req->cli->bufsize_max);\n\tif (err)\n\t\tgoto out;\n\n\tif (!mbuf_get_left(req->mbreq)) {\n\t\tif (req->mbreq)\n\t\t\tmbuf_rewind(req->mbreq);\n\n\t\terr = read_req_data(req);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (req->mbreq)\n\t\t\tmbuf_set_pos(req->mbreq, 0);\n\n\t\tif (mbuf_get_left(req->mbreq) && !tcp_sendq_used(conn->tc))\n\t\t\t(void) send_req_buf(conn);\n\t}\n\n\ttmr_start(&conn->tmr, req->cli->conf.recv_timeout, timeout_handler,\n\t\t  conn);\n\nout:\n\treturn err;\n}\n\n\nstatic void send_req_handler(void *arg)\n{\n\t(void) send_req_buf((struct conn *)arg);\n}\n\n\nstatic void estab_handler(void *arg)\n{\n\tstruct conn *conn = arg;\n\tstruct http_req *req = conn->req;\n\tint err;\n\tif (!req || !req->cli)\n\t\treturn;\n\n\terr = send_req_buf(conn);\n\tif (err) {\n\t\ttry_next(conn, err);\n\t\treturn;\n\t}\n\n\tif (mbuf_get_left(req->mbreq))\n\t\ttcp_set_send(conn->tc, send_req_handler);\n\n\ttmr_start(&conn->tmr, req->cli->conf.recv_timeout,\n\t\t  timeout_handler, conn);\n}\n\n\nstatic void recv_handler(struct mbuf *mb, void *arg)\n{\n\tconst struct http_hdr *hdr;\n\tstruct conn *conn = arg;\n\tstruct http_req *req = conn->req;\n\tsize_t pos;\n\tbool last;\n\tint err;\n\n\tif (!req)\n\t\treturn;\n\n\tif (req->msg) {\n\t\terr = req_recv(req, mb, &last);\n\t\tif (err || last)\n\t\t\tgoto out;\n\n\t\treturn;\n\t}\n\n\tif (req->mb) {\n\n\t\tconst size_t len = mbuf_get_left(mb);\n\n\t\tif ((mbuf_get_left(req->mb) + len) > req->cli->bufsize_max) {\n\t\t\terr = EOVERFLOW;\n\t\t\tgoto out;\n\t\t}\n\n\t\tpos = req->mb->pos;\n\t\treq->mb->pos = req->mb->end;\n\n\t\terr = mbuf_write_mem(req->mb, mbuf_buf(mb), len);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\treq->mb->pos = pos;\n\t}\n\telse {\n\t\treq->mb = mem_ref(mb);\n\t}\n\n\tpos = req->mb->pos;\n\n\terr = http_msg_decode(&req->msg, req->mb, false);\n\tif (err) {\n\t\tif (err == ENODATA) {\n\t\t\treq->mb->pos = pos;\n\t\t\treturn;\n\t\t}\n\t\tgoto out;\n\t}\n\n\tif (req->datah)\n\t\ttmr_cancel(&conn->tmr);\n\n\thdr = http_msg_hdr(req->msg, HTTP_HDR_CONNECTION);\n\tif (hdr && !pl_strcasecmp(&hdr->val, \"close\"))\n\t\treq->close = true;\n\n\tif (http_msg_hdr_has_value(req->msg, HTTP_HDR_TRANSFER_ENCODING,\n\t\t\t\t   \"chunked\"))\n\t\treq->chunked = true;\n\telse\n\t\treq->rx_len = req->msg->clen;\n\n\terr = req_recv(req, req->mb, &last);\n\tif (err || last)\n\t\tgoto out;\n\n\treturn;\n\n out:\n\treq_close(req, err, req->msg);\n}\n\n\nstatic void close_handler(int err, void *arg)\n{\n\tstruct conn *conn = arg;\n\n\ttry_next(conn, err ? err : ECONNRESET);\n}\n\n\nstatic bool conn_cmp(struct le *le, void *arg)\n{\n\tconst struct conn *conn = le->data;\n\tconst struct http_req *req = arg;\n\n\tif (!sa_cmp(&req->srvv[req->srvc], &conn->addr, SA_ALL))\n\t\treturn false;\n\n\tif (req->secure != !!conn->sc)\n\t\treturn false;\n\n\treturn conn->req == NULL;\n}\n\n\nstatic int conn_connect(struct http_req *req)\n{\n\tstruct sa *laddr = NULL;\n\tint err = 0;\n\n\tif (!req || !req->cli)\n\t\treturn EINVAL;\n\n\tconst struct sa *addr = &req->srvv[req->srvc];\n\tstruct conn *conn = list_ledata(hash_lookup(req->cli->ht_conn,\n\t\t\t\t       sa_hash(addr, SA_ALL), conn_cmp, req));\n\tif (conn) {\n\t\terr = send_req_buf(conn);\n\t\tif (!err) {\n\t\t\ttmr_start(&conn->tmr, req->cli->conf.recv_timeout,\n\t\t\t\t  timeout_handler, conn);\n\n\t\t\treq->conn = conn;\n\t\t\tconn->req = req;\n\n\t\t\t++conn->usec;\n\n\t\t\tif (mbuf_get_left(req->mbreq))\n\t\t\t\ttcp_set_send(conn->tc, send_req_handler);\n\n\t\t\treturn 0;\n\t\t}\n\n\t\tmem_deref(conn);\n\t}\n\n\tconn = mem_zalloc(sizeof(*conn), conn_destructor);\n\tif (!conn)\n\t\treturn ENOMEM;\n\n\thash_append(req->cli->ht_conn, sa_hash(addr, SA_ALL), &conn->he, conn);\n\n\tconn->addr = *addr;\n\tconn->usec = 1;\n\n\tif (sa_af(&conn->addr) == AF_INET)\n\t\tladdr = &req->cli->laddr;\n\telse if (sa_af(&conn->addr) == AF_INET6)\n\t\tladdr = &req->cli->laddr6;\n\n\tif (sa_isset(laddr, SA_ADDR)) {\n\t\tsa_set_scopeid(&conn->addr, sa_scopeid(laddr));\n\t\terr = tcp_connect_bind(&conn->tc, addr, estab_handler,\n\t\t\trecv_handler,close_handler, laddr, conn);\n\t}\n\telse {\n\t\terr = tcp_connect(&conn->tc, addr, estab_handler, recv_handler,\n\t\t\tclose_handler, conn);\n\t}\n\tif (err)\n\t\tgoto out;\n\n#ifdef USE_TLS\n\tif (req->secure) {\n\n\t\terr = tls_start_tcp(&conn->sc, req->cli->tls, conn->tc, 0);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (req->cli->tlshn)\n\t\t\terr  = tls_set_verify_server(conn->sc,\n\t\t\t\t\treq->cli->tlshn);\n\t\telse\n\t\t\terr  = tls_set_verify_server(conn->sc, req->host);\n\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n#endif\n\n\ttmr_start(&conn->tmr, req->cli->conf.conn_timeout, timeout_handler,\n\t\t  conn);\n\n\tif (!err) {\n\t\treq->conn = conn;\n\t\tconn->req = req;\n\t}\n\nout:\n\tif (err)\n\t\tmem_deref(conn);\n\n\treturn err;\n}\n\n\nstatic int req_connect(struct http_req *req)\n{\n\tint err = EINVAL;\n\n\twhile (req->srvc > 0) {\n\n\t\t--req->srvc;\n\n\t\treq->mb = mem_deref(req->mb);\n\n\t\terr = conn_connect(req);\n\t\tif (!err)\n\t\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nstatic bool rr_handler(struct dnsrr *rr, void *arg)\n{\n\tstruct http_req *req = arg;\n\n\tif (req->srvc >= RE_ARRAY_SIZE(req->srvv))\n\t\treturn true;\n\n\tswitch (rr->type) {\n\n\tcase DNS_TYPE_A:\n\t\tsa_set_in(&req->srvv[req->srvc++], rr->rdata.a.addr,\n\t\t\t  req->port);\n\t\tbreak;\n\n\tcase DNS_TYPE_AAAA:\n\t\tsa_set_in6(&req->srvv[req->srvc++], rr->rdata.aaaa.addr,\n\t\t\t   req->port);\n\t\tbreak;\n\t}\n\n\treturn false;\n}\n\n\nstatic void query_handler(int err, const struct dnshdr *hdr, struct list *ansl,\n\t\t\t  struct list *authl, struct list *addl, void *arg)\n{\n\tstruct http_req *req = arg;\n\t(void)hdr;\n\t(void)authl;\n\t(void)addl;\n\n\tdns_rrlist_apply2(ansl, req->host, DNS_TYPE_A, DNS_TYPE_AAAA,\n\t\t\t  DNS_CLASS_IN, true, rr_handler, req);\n\n\t/* wait for other (A/AAAA) query to complete */\n\tif (req->dq || req->dq6)\n\t\treturn;\n\n\tif (req->srvc == 0) {\n\t\terr = err ? err : EDESTADDRREQ;\n\t\tgoto fail;\n\t}\n\n\terr = req_connect(req);\n\tif (err)\n\t\tgoto fail;\n\n\treturn;\n\n fail:\n\treq_close(req, err, NULL);\n}\n\n\nint http_uri_decode(struct http_uri *hu, const struct pl *uri)\n{\n\tint err = 0;\n\tif (!hu)\n\t\treturn EINVAL;\n\n\tmemset(hu, 0, sizeof(*hu));\n\n\t/* Try IPv6 first */\n\terr = re_regex(uri->p, uri->l, \"[a-z]+://\\\\[[^\\\\]]+\\\\][:]*[0-9]*[^]+\",\n\t\t       &hu->scheme, &hu->host, NULL, &hu->port, &hu->path) ||\n\t      hu->scheme.p != uri->p;\n\tif (!err)\n\t\tgoto out;\n\n\t/* Then non-IPv6 host */\n\terr = re_regex(uri->p, uri->l, \"[a-z]+://[^:/]+[:]*[0-9]*[^]+\",\n\t\t       &hu->scheme, &hu->host, NULL, &hu->port, &hu->path) ||\n\t      hu->scheme.p != uri->p;\n\nout:\n\tif (!err && !pl_isset(&hu->path))\n\t\tpl_set_str(&hu->path, \"/\");\n\n\treturn err;\n}\n\n\nstatic int http_request_addr_v(struct http_req **reqp, struct http_cli *cli,\n\t\t\t       const char *met, const char *uri,\n\t\t\t       const struct sa *addr,\n\t\t\t       http_resp_h *resph, http_data_h *datah,\n\t\t\t       http_bodyh *bodyh, void *arg,\n\t\t\t       const char *fmt, va_list ap)\n{\n\tstruct http_uri http_uri;\n\tstruct pl pl;\n\tstruct http_req *req;\n\tuint16_t defport;\n\tstruct sa sa;\n\tbool ipv6;\n\tbool secure;\n\tint err;\n\n\tif (!cli || !met || !uri)\n\t\treturn EINVAL;\n\n\tpl_set_str(&pl, uri);\n\tif (http_uri_decode(&http_uri, &pl))\n\t\treturn EINVAL;\n\n\tif (!pl_strcasecmp(&http_uri.scheme, \"http\") ||\n\t    !pl_strcasecmp(&http_uri.scheme, \"ws\")) {\n\t\tsecure  = false;\n\t\tdefport = 80;\n\t}\n#ifdef USE_TLS\n\telse if (!pl_strcasecmp(&http_uri.scheme, \"https\") ||\n\t\t !pl_strcasecmp(&http_uri.scheme, \"wss\")) {\n\t\tsecure  = true;\n\t\tdefport = 443;\n\t}\n#endif\n\telse\n\t\treturn ENOTSUP;\n\n\treq = mem_zalloc(sizeof(*req), req_destructor);\n\tif (!req)\n\t\treturn ENOMEM;\n\n\tlist_append(&cli->reql, &req->le, req);\n\n\treq->cli    = cli;\n\treq->secure = secure;\n\treq->port   = (addr && sa_port(addr)) ? sa_port(addr) :\n\t\t\t(pl_isset(&http_uri.port) ? pl_u32(&http_uri.port) :\n\t\t\t  defport);\n\treq->resph  = resph;\n\treq->datah  = datah;\n\treq->bodyh  = bodyh;\n\treq->arg    = arg;\n\n\terr = pl_strdup(&req->host, &http_uri.host);\n\tif (err)\n\t\tgoto out;\n\n\treq->mbreq = mbuf_alloc(1024);\n\tif (!req->mbreq) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tipv6 = sa_set(&sa, &http_uri.host, 0) == 0 && sa_af(&sa) == AF_INET6;\n\terr = mbuf_printf(req->mbreq,\n\t\t\t  \"%s %r HTTP/1.1\\r\\n\"\n\t\t\t  \"Host: %s%r%s\\r\\n\",\n\t\t\t  met, &http_uri.path,\n\t\t\t  ipv6 ? \"[\" : \"\", &http_uri.host, ipv6 ? \"]\" : \"\");\n\n\tif (fmt)\n\t\terr |= mbuf_vprintf(req->mbreq, fmt, ap);\n\telse\n\t\terr |= mbuf_write_str(req->mbreq, \"\\r\\n\");\n\n\tif (err)\n\t\tgoto out;\n\n\terr = read_req_data(req);\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(req->mbreq, 0);\n\n#ifdef USE_TLS\n\tif (cli->cert && cli->key) {\n\t\terr = tls_set_certificate_pem(cli->tls,\n\t\t\t\t(char *)cli->cert->buf, cli->cert->end,\n\t\t\t\t(char *)cli->key->buf, cli->key->end);\n\t}\n\telse if (cli->cert) {\n\t\terr = tls_set_certificate(cli->tls,\n\t\t\t\t(char *)cli->cert->buf, cli->cert->end);\n\t}\n\tif (err)\n\t\tgoto out;\n#endif\n\n\tif (addr) {\n\t\t/* Use explicit addr instead of DNS resolution */\n\t\treq->srvv[0] = *addr;\n\t\tsa_set_port(&req->srvv[0], req->port);\n\t\treq->srvc = 1;\n\t\terr = req_connect(req);\n\t}\n\telse if (!sa_set_str(&req->srvv[0], req->host, req->port)) {\n\t\treq->srvc = 1;\n\n\t\terr = req_connect(req);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse {\n\t\tstruct sa tmp;\n\t\terr = dnsc_query(&req->dq, cli->dnsc, req->host,\n\t\t\t\t DNS_TYPE_A, DNS_CLASS_IN, true,\n\t\t\t\t query_handler, req);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (0 == net_default_source_addr_get(AF_INET6, &tmp)) {\n\n\t\t\terr = dnsc_query(&req->dq6, cli->dnsc, req->host,\n\t\t\t\t\t DNS_TYPE_AAAA, DNS_CLASS_IN, true,\n\t\t\t\t\t query_handler, req);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(req);\n\telse if (reqp) {\n\t\treq->reqp = reqp;\n\t\t*reqp = req;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Send an HTTP request\n *\n * @param reqp   Pointer to allocated HTTP request object\n * @param cli    HTTP Client\n * @param met    Request method\n * @param uri    Request URI\n * @param resph  Response handler\n * @param datah  Content handler (optional)\n * @param bodyh  Body handler (optional)\n * @param arg    Handler argument\n * @param fmt    Formatted HTTP headers and body (optional)\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_request(struct http_req **reqp, struct http_cli *cli, const char *met,\n\t\t const char *uri, http_resp_h *resph, http_data_h *datah,\n\t\t http_bodyh *bodyh, void *arg, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = http_request_addr_v(reqp, cli, met, uri, NULL,\n\t\t\t\t  resph, datah, bodyh, arg, fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Send an HTTP request with explicit server address (bypasses DNS)\n *\n * Like http_request but connects to addr instead of resolving the URI\n * host via DNS. The Host header still uses the hostname from uri.\n * If addr is NULL, falls back to DNS resolution like http_request().\n *\n * @param reqp   Pointer to allocated HTTP request object\n * @param cli    HTTP Client\n * @param met    Request method\n * @param uri    Request URI (host used for Host header only)\n * @param addr   Explicit server address for TCP connect, or NULL\n * @param resph  Response handler\n * @param datah  Content handler (optional)\n * @param bodyh  Body handler (optional)\n * @param arg    Handler argument\n * @param fmt    Formatted HTTP headers and body (optional)\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_request_addr(struct http_req **reqp, struct http_cli *cli,\n\t\t      const char *met, const char *uri, const struct sa *addr,\n\t\t      http_resp_h *resph, http_data_h *datah,\n\t\t      http_bodyh *bodyh, void *arg, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = http_request_addr_v(reqp, cli, met, uri, addr,\n\t\t\t\t  resph, datah, bodyh, arg, fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Set HTTP request connection handler\n *\n * @param req   HTTP request object\n * @param connh Connection handler\n */\nvoid http_req_set_conn_handler(struct http_req *req, http_conn_h *connh)\n{\n\tif (!req)\n\t\treturn;\n\n\treq->connh = connh;\n}\n\n\nint http_client_set_config(struct http_cli *cli, struct http_conf *conf)\n{\n\tif (!cli || !conf)\n\t\treturn EINVAL;\n\n\tcli->conf = *conf;\n\tdnsc_conf_set_timeout(cli->dnsc, conf->conn_timeout,\n\t\t\t\t\t\t  conf->idle_timeout);\n\treturn 0;\n}\n\n\n/**\n * Allocate an HTTP Client instance\n *\n * @param clip      Pointer to allocated HTTP Client\n * @param dnsc      DNS Client\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_client_alloc(struct http_cli **clip, struct dnsc *dnsc)\n{\n\tstruct http_cli *cli;\n\tint err;\n\n\tif (!clip || !dnsc)\n\t\treturn EINVAL;\n\n\tcli = mem_zalloc(sizeof(*cli), cli_destructor);\n\tif (!cli)\n\t\treturn ENOMEM;\n\n\terr = hash_alloc(&cli->ht_conn, CONN_BSIZE);\n\tif (err)\n\t\tgoto out;\n\n#ifdef USE_TLS\n\terr = tls_alloc(&cli->tls, TLS_METHOD_SSLV23, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\terr = tls_set_verify_purpose(cli->tls, \"sslserver\");\n\tif (err)\n\t\tgoto out;\n#else\n\terr = 0;\n#endif\n\n\tcli->dnsc = mem_ref(dnsc);\n\tcli->conf = default_conf;\n\tcli->bufsize_max = BUFSIZE_MAX;\n\n out:\n\tif (err)\n\t\tmem_deref(cli);\n\telse\n\t\t*clip = cli;\n\n\treturn err;\n}\n\n\n#ifdef USE_TLS\n/**\n * Replace HTTP Client TLS Context\n *\n * @param cli     HTTP Client\n * @param tls     TLS Context\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_client_set_tls(struct http_cli *cli, struct tls *tls)\n{\n\tif (!cli || !tls)\n\t\treturn EINVAL;\n\n\tmem_deref(cli->tls);\n\tcli->tls = mem_ref(tls);\n\n\treturn 0;\n}\n\n\n/**\n * Get HTTP Client TLS Context\n *\n * @param cli     HTTP Client\n * @param tls     TLS Context\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_client_get_tls(struct http_cli *cli, struct tls **tls)\n{\n\tif (!cli || !tls)\n\t\treturn EINVAL;\n\n\t*tls = cli->tls;\n\n\treturn 0;\n}\n\n\n/**\n * Add trusted CA certificates\n *\n * @param cli     HTTP Client\n * @param capath  Path to CA certificates\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_client_add_ca(struct http_cli *cli, const char *tls_ca)\n{\n\tif (!cli || !tls_ca)\n\t\treturn EINVAL;\n\n\treturn tls_add_ca(cli->tls, tls_ca);\n}\n\n\n/**\n * Add trusted CA certificates given as string\n *\n * @param cli    HTTP Client\n * @param capem  The trusted CA as 0-terminated string given in PEM format\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_client_add_capem(struct http_cli *cli, const char *capem)\n{\n\tif (!cli)\n\t\treturn EINVAL;\n\n\treturn tls_add_capem(cli->tls, capem);\n}\n\n\n/**\n * Add trusted CRL certificates given as string\n *\n * @param cli    HTTP Client\n * @param pem    The trusted CRL as 0-terminated string given in PEM format\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_client_add_crlpem(struct http_cli *cli, const char *pem)\n{\n\tif (!cli)\n\t\treturn EINVAL;\n\n\treturn tls_add_crlpem(cli->tls, pem);\n}\n\n\n/**\n * Set client certificate\n * @param cli   HTTP Client\n * @param path  File path to client certificate\n *\n * @return 0 for success, error code otherwise.\n */\nint http_client_set_cert(struct http_cli *cli, const char *path)\n{\n\tint err = 0;\n\n\tif (!cli || !path)\n\t\treturn EINVAL;\n\n\tcli->cert = mem_deref(cli->cert);\n\terr = fs_fread(&cli->cert, path);\n\tif (err) {\n\t\tcli->cert = mem_deref(cli->cert);\n\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Set client certificate in PEM format\n *\n * @param cli    HTTP Client\n * @param pem    Client certificate as 0-terminated string in PEM format\n *\n * @return 0 for success, error code otherwise.\n */\n/* ------------------------------------------------------------------------- */\nint http_client_set_certpem(struct http_cli *cli, const char *pem)\n{\n\tif (!cli || !str_isset(pem))\n\t\treturn EINVAL;\n\n\tcli->cert = mem_deref(cli->cert);\n\tcli->cert = mbuf_alloc(strlen(pem));\n\treturn mbuf_write_str(cli->cert, pem);\n}\n\n\n/**\n * Set client key\n *\n * @param cli   HTTP Client\n * @param path  File path to client key\n *\n * @return 0 for success, error code otherwise.\n */\nint http_client_set_key(struct http_cli *cli, const char *path)\n{\n\tint err = 0;\n\n\tif (!cli || !path)\n\t\treturn EINVAL;\n\n\tcli->key = mem_deref(cli->key);\n\terr = fs_fread(&cli->key, path);\n\tif (err) {\n\t\tcli->key = mem_deref(cli->key);\n\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Set client key in PEM format\n *\n * @param cli    HTTP Client\n * @param pem    Client key as 0-terminated string in PEM format\n *\n * @return 0 for success, error code otherwise.\n */\nint http_client_set_keypem(struct http_cli *cli, const char *pem)\n{\n\tif (!cli || !str_isset(pem))\n\t\treturn EINVAL;\n\n\tcli->key = mem_deref(cli->key);\n\n\tcli->key = mbuf_alloc(strlen(pem));\n\treturn mbuf_write_str(cli->key, pem);\n}\n\n\n/**\n * Set verify host name\n *\n * @param cli       HTTP Client\n * @param hostname  String for alternative name validation.\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_client_set_tls_hostname(struct http_cli *cli,\n\t\t\t\t const struct pl *hostname)\n{\n\tif (!cli)\n\t\treturn EINVAL;\n\n\tcli->tlshn = mem_deref(cli->tlshn);\n\tif (!hostname)\n\t\treturn 0;\n\n\treturn pl_strdup(&cli->tlshn, hostname);\n}\n\n\n/**\n * Enabled / disable tls session reuse.\n * Note: disabled per default\n *\n * @param cli       HTTP Client\n * @param enabled\tPass 1 for enabled, 0 for disabled\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_client_set_session_reuse(struct http_cli *cli, bool enabled)\n{\n\tif (!cli || !cli->tls)\n\t\treturn EINVAL;\n\n\treturn tls_set_session_reuse(cli->tls, enabled);\n}\n\n\n/**\n * Set minimum TLS version\n *\n * @param cli       HTTP Client\n * @param version   Minimum version, e.g.: TLS1_2_VERSION\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_client_set_tls_min_version(struct http_cli *cli, int version)\n{\n\tif (!cli || !cli->tls)\n\t\treturn EINVAL;\n\n\treturn tls_set_min_proto_version(cli->tls, version);\n}\n\n\n/**\n * Set maximum TLS version\n *\n * @param cli       HTTP Client\n * @param version   Maximum version, e.g.: TLS1_2_VERSION\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_client_set_tls_max_version(struct http_cli *cli, int version)\n{\n\tif (!cli || !cli->tls)\n\t\treturn EINVAL;\n\n\treturn tls_set_max_proto_version(cli->tls, version);\n}\n\n\n/**\n * Disable TLS server certificate verification\n *\n * @param cli\tHTTP Client\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_client_disable_verify_server(struct http_cli *cli)\n{\n\tif (!cli)\n\t\treturn EINVAL;\n\n\ttls_disable_verify_server(cli->tls);\n\n\treturn 0;\n}\n\n\n/**\n * Change used certificate+key of TLS connection\n *\n * @param cli       HTTP Client\n * @param chain     Cert (chain) + Key (PEM format)\n * @param len_chain Length of certificate + key PEM string\n *\n * @return int 0 if success, otherwise errorcode\n */\nint http_client_use_chainpem(struct http_cli *cli, const char *chain,\n\t\t\t     int len_chain)\n{\n\tif (!cli || !cli->tls)\n\t\treturn EINVAL;\n\n\tint err = tls_set_certificate_chain_pem(cli->tls, chain, len_chain);\n\tif (err)\n\t\treturn err;\n\n\tcli->cert = mem_deref(cli->cert);\n\tcli->key = mem_deref(cli->key);\n\n\treturn 0;\n}\n\n\n/**\n * Change used certificate+key of TLS connection\n *\n * @param cli  HTTP Client\n * @param path Path to Cert (chain) + Key file (PEM format)\n *\n * @return int 0 if success, otherwise errorcode\n */\nint http_client_use_chain(struct http_cli *cli, const char *path)\n{\n\tint err;\n\n\tif (!cli || !cli->tls)\n\t\treturn EINVAL;\n\n\terr =  tls_set_certificate_chain(cli->tls, path);\n\tif (err)\n\t\treturn err;\n\n\tcli->cert = mem_deref(cli->cert);\n\tcli->key = mem_deref(cli->key);\n\n\treturn 0;\n}\n#endif /* USE_TLS */\n\n\n/**\n * Send an HTTP request\n *\n * @param cli   HTTP Client\n * @param addr  Bind to local v4 address\n *\n */\nvoid http_client_set_laddr(struct http_cli *cli, const struct sa *addr)\n{\n\tif (cli && addr)\n\t\tsa_cpy(&cli->laddr, addr);\n}\n\n\n/**\n * Send an HTTP request\n *\n * @param cli    HTTP Client\n * @param addr   Bind to local v6 address\n *\n */\nvoid http_client_set_laddr6(struct http_cli *cli, const struct sa *addr)\n{\n\tif (cli && addr)\n\t\tsa_cpy(&cli->laddr6, addr);\n}\n\n\nvoid http_client_set_bufsize_max(struct http_cli *cli, size_t max_size)\n{\n\tif (!cli)\n\t\treturn;\n\n\tcli->bufsize_max = max_size;\n}\n\n\nsize_t http_client_get_bufsize_max(struct http_cli *cli)\n{\n\tif (!cli)\n\t\treturn BUFSIZE_MAX;\n\n\treturn cli->bufsize_max;\n}\n"
  },
  {
    "path": "src/http/http.h",
    "content": "/**\n * @file http.h  HTTP Private Interface\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nstruct http_chunk {\n\tsize_t size;\n\tunsigned lf;\n\tbool trailer;\n\tbool digit;\n\tbool param;\n};\n\n\nint http_chunk_decode(struct http_chunk *chunk, struct mbuf *mb, size_t *size);\n"
  },
  {
    "path": "src/http/msg.c",
    "content": "/**\n * @file http/msg.c  HTTP Message decode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_msg.h>\n#include <re_http.h>\n\n\nenum {\n\tSTARTLINE_MAX = 8192,\n};\n\n\nstatic void hdr_destructor(void *arg)\n{\n\tstruct http_hdr *hdr = arg;\n\n\tlist_unlink(&hdr->le);\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct http_msg *msg = arg;\n\n\tlist_flush(&msg->hdrl);\n\tmem_deref(msg->_mb);\n\tmem_deref(msg->mb);\n}\n\n\nstatic enum http_hdrid hdr_hash(const struct pl *name)\n{\n\tif (!name->l)\n\t\treturn HTTP_HDR_NONE;\n\n\tswitch (name->p[0]) {\n\n\tcase 'x':\n\tcase 'X':\n\t\tif (name->l > 1 && name->p[1] == '-')\n\t\t\treturn HTTP_HDR_NONE;\n\n\t\tbreak;\n\t}\n\n\treturn (enum http_hdrid)(hash_joaat_ci(name->p, name->l) & 0xfff);\n}\n\n\nstatic inline bool hdr_comma_separated(enum http_hdrid id)\n{\n\tswitch (id) {\n\n\tcase HTTP_HDR_ACCEPT:\n\tcase HTTP_HDR_ACCEPT_CHARSET:\n\tcase HTTP_HDR_ACCEPT_ENCODING:\n\tcase HTTP_HDR_ACCEPT_LANGUAGE:\n\tcase HTTP_HDR_ACCEPT_RANGES:\n\tcase HTTP_HDR_ALLOW:\n\tcase HTTP_HDR_CACHE_CONTROL:\n\tcase HTTP_HDR_CONNECTION:\n\tcase HTTP_HDR_CONTENT_ENCODING:\n\tcase HTTP_HDR_CONTENT_LANGUAGE:\n\tcase HTTP_HDR_EXPECT:\n\tcase HTTP_HDR_IF_MATCH:\n\tcase HTTP_HDR_IF_NONE_MATCH:\n\tcase HTTP_HDR_PRAGMA:\n\tcase HTTP_HDR_SEC_WEBSOCKET_EXTENSIONS:\n\tcase HTTP_HDR_SEC_WEBSOCKET_PROTOCOL:\n\tcase HTTP_HDR_SEC_WEBSOCKET_VERSION:\n\tcase HTTP_HDR_TE:\n\tcase HTTP_HDR_TRAILER:\n\tcase HTTP_HDR_TRANSFER_ENCODING:\n\tcase HTTP_HDR_UPGRADE:\n\tcase HTTP_HDR_VARY:\n\tcase HTTP_HDR_VIA:\n\tcase HTTP_HDR_WARNING:\n\t\treturn true;\n\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\nstatic inline int hdr_add(struct http_msg *msg, const struct pl *name,\n\t\t\t  enum http_hdrid id, const char *p, ssize_t l)\n{\n\tstruct http_hdr *hdr;\n\tint err = 0;\n\n\thdr = mem_zalloc(sizeof(*hdr), hdr_destructor);\n\tif (!hdr)\n\t\treturn ENOMEM;\n\n\thdr->name  = *name;\n\thdr->val.p = p;\n\thdr->val.l = MAX(l, 0);\n\thdr->id    = id;\n\n\tlist_append(&msg->hdrl, &hdr->le, hdr);\n\n\t/* parse common headers */\n\tswitch (id) {\n\n\tcase HTTP_HDR_CONTENT_TYPE:\n\t\terr = msg_ctype_decode(&msg->ctyp, &hdr->val);\n\t\tbreak;\n\n\tcase HTTP_HDR_CONTENT_LENGTH:\n\t\tmsg->clen = pl_u32(&hdr->val);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\tif (err)\n\t\tmem_deref(hdr);\n\n\treturn err;\n}\n\n\n/**\n * Decode a HTTP message\n *\n * @param msgp Pointer to allocated HTTP Message\n * @param mb   Buffer containing HTTP Message\n * @param req  True for request, false for response\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_msg_decode(struct http_msg **msgp, struct mbuf *mb, bool req)\n{\n\tstruct pl b, s, e, name, scode;\n\tconst char *p, *cv;\n\tstruct http_msg *msg;\n\tbool comsep, quote;\n\tenum http_hdrid id = HTTP_HDR_NONE;\n\tuint32_t ws, lf;\n\tsize_t l;\n\tint err;\n\n\tif (!msgp || !mb)\n\t\treturn EINVAL;\n\n\tp = (const char *)mbuf_buf(mb);\n\tl = mbuf_get_left(mb);\n\n\tif (re_regex(p, l, \"[\\r\\n]*[^\\r\\n]+[\\r]*[\\n]1\", &b, &s, NULL, &e))\n\t\treturn (l > STARTLINE_MAX) ? EBADMSG : ENODATA;\n\n\tmsg = mem_zalloc(sizeof(*msg), destructor);\n\tif (!msg)\n\t\treturn ENOMEM;\n\n\tmsg->_mb = mem_ref(mb);\n\n\tmsg->mb = mbuf_alloc(8192);\n\tif (!msg->mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tif (req) {\n\t\tif (re_regex(s.p, s.l, \"[a-z]+ [^? ]+[^ ]* HTTP/[0-9.]+\",\n\t\t\t     &msg->met, &msg->path, &msg->prm, &msg->ver) ||\n\t\t    msg->met.p != s.p) {\n\t\t\terr = EBADMSG;\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse {\n\t\tif (re_regex(s.p, s.l, \"HTTP/[0-9.]+ [0-9]+[ ]*[^]*\",\n\t\t\t     &msg->ver, &scode, NULL, &msg->reason) ||\n\t\t    msg->ver.p != s.p + 5) {\n\t\t\terr = EBADMSG;\n\t\t\tgoto out;\n\t\t}\n\n\t\tmsg->scode = pl_u32(&scode);\n\t}\n\n\tl -= e.p + e.l - p;\n\tp = e.p + e.l;\n\n\tname.p = cv = NULL;\n\tname.l = ws = lf = 0;\n\tcomsep = false;\n\tquote = false;\n\n\tfor (; l > 0; p++, l--) {\n\n\t\tswitch (*p) {\n\n\t\tcase ' ':\n\t\tcase '\\t':\n\t\t\tlf = 0; /* folding */\n\t\t\t++ws;\n\t\t\tbreak;\n\n\t\tcase '\\r':\n\t\t\t++ws;\n\t\t\tbreak;\n\n\t\tcase '\\n':\n\t\t\t++ws;\n\n\t\t\tif (!name.p) {\n\t\t\t\t++p; --l; /* no headers */\n\t\t\t\terr = 0;\n\t\t\t\tgoto out;\n\t\t\t}\n\n\t\t\tif (!lf++)\n\t\t\t\tbreak;\n\n\t\t\t++p; --l; /* eoh */\n\n\t\t\t/*@fallthrough@*/\n\n\t\tdefault:\n\t\t\tif (lf || (*p == ',' && comsep && !quote)) {\n\n\t\t\t\tif (!name.l) {\n\t\t\t\t\terr = EBADMSG;\n\t\t\t\t\tgoto out;\n\t\t\t\t}\n\n\t\t\t\terr = hdr_add(msg, &name, id, cv ? cv : p,\n\t\t\t\t\t      cv ? p - cv - ws : 0);\n\t\t\t\tif (err)\n\t\t\t\t\tgoto out;\n\n\t\t\t\tif (!lf) { /* comma separated */\n\t\t\t\t\tcv = NULL;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (lf > 1) { /* eoh */\n\t\t\t\t\terr = 0;\n\t\t\t\t\tgoto out;\n\t\t\t\t}\n\n\t\t\t\tcomsep = false;\n\t\t\t\tname.p = NULL;\n\t\t\t\tcv = NULL;\n\t\t\t\tlf = 0;\n\t\t\t}\n\n\t\t\tif (!name.p) {\n\t\t\t\tname.p = p;\n\t\t\t\tname.l = 0;\n\t\t\t\tws = 0;\n\t\t\t}\n\n\t\t\tif (!name.l) {\n\t\t\t\tif (*p != ':') {\n\t\t\t\t\tws = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tname.l = MAX((int)(p - name.p - ws), 0);\n\t\t\t\tif (!name.l) {\n\t\t\t\t\terr = EBADMSG;\n\t\t\t\t\tgoto out;\n\t\t\t\t}\n\n\t\t\t\tid = hdr_hash(&name);\n\t\t\t\tcomsep = hdr_comma_separated(id);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!cv) {\n\t\t\t\tquote = false;\n\t\t\t\tcv = p;\n\t\t\t}\n\n\t\t\tif (*p == '\"')\n\t\t\t\tquote = !quote;\n\n\t\t\tws = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\terr = ENODATA;\n\n out:\n\tif (err)\n\t\tmem_deref(msg);\n\telse {\n\t\t*msgp = msg;\n\t\tmb->pos = mb->end - l;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Get a HTTP Header from a HTTP Message\n *\n * @param msg HTTP Message\n * @param id  HTTP Header ID\n *\n * @return HTTP Header if found, NULL if not found\n */\nconst struct http_hdr *http_msg_hdr(const struct http_msg *msg,\n\t\t\t\t    enum http_hdrid id)\n{\n\treturn http_msg_hdr_apply(msg, true, id, NULL, NULL);\n}\n\n\n/**\n * Apply a function handler to certain HTTP Headers\n *\n * @param msg HTTP Message\n * @param fwd True to traverse forwards, false to traverse backwards\n * @param id  HTTP Header ID\n * @param h   Function handler\n * @param arg Handler argument\n *\n * @return HTTP Header if handler returns true, otherwise NULL\n */\nconst struct http_hdr *http_msg_hdr_apply(const struct http_msg *msg,\n\t\t\t\t\t  bool fwd, enum http_hdrid id,\n\t\t\t\t\t  http_hdr_h *h, void *arg)\n{\n\tstruct le *le;\n\n\tif (!msg)\n\t\treturn NULL;\n\n\tle = fwd ? msg->hdrl.head : msg->hdrl.tail;\n\n\twhile (le) {\n\t\tconst struct http_hdr *hdr = le->data;\n\n\t\tle = fwd ? le->next : le->prev;\n\n\t\tif (hdr->id != id)\n\t\t\tcontinue;\n\n\t\tif (!h || h(hdr, arg))\n\t\t\treturn hdr;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Get an unknown HTTP Header from a HTTP Message\n *\n * @param msg  HTTP Message\n * @param name Header name\n *\n * @return HTTP Header if found, NULL if not found\n */\nconst struct http_hdr *http_msg_xhdr(const struct http_msg *msg,\n\t\t\t\t     const char *name)\n{\n\treturn http_msg_xhdr_apply(msg, true, name, NULL, NULL);\n}\n\n\n/**\n * Apply a function handler to certain unknown HTTP Headers\n *\n * @param msg  HTTP Message\n * @param fwd  True to traverse forwards, false to traverse backwards\n * @param name HTTP Header name\n * @param h    Function handler\n * @param arg  Handler argument\n *\n * @return HTTP Header if handler returns true, otherwise NULL\n */\nconst struct http_hdr *http_msg_xhdr_apply(const struct http_msg *msg,\n\t\t\t\t\t   bool fwd, const char *name,\n\t\t\t\t\t   http_hdr_h *h, void *arg)\n{\n\tstruct le *le;\n\tstruct pl pl;\n\n\tif (!msg || !name)\n\t\treturn NULL;\n\n\tpl_set_str(&pl, name);\n\n\tle = fwd ? msg->hdrl.head : msg->hdrl.tail;\n\n\twhile (le) {\n\t\tconst struct http_hdr *hdr = le->data;\n\n\t\tle = fwd ? le->next : le->prev;\n\n\t\tif (pl_casecmp(&hdr->name, &pl))\n\t\t\tcontinue;\n\n\t\tif (!h || h(hdr, arg))\n\t\t\treturn hdr;\n\t}\n\n\treturn NULL;\n}\n\n\nstatic bool count_handler(const struct http_hdr *hdr, void *arg)\n{\n\tuint32_t *n = arg;\n\t(void)hdr;\n\n\t++(*n);\n\n\treturn false;\n}\n\n\n/**\n * Count the number of HTTP Headers\n *\n * @param msg HTTP Message\n * @param id  HTTP Header ID\n *\n * @return Number of HTTP Headers\n */\nuint32_t http_msg_hdr_count(const struct http_msg *msg, enum http_hdrid id)\n{\n\tuint32_t n = 0;\n\n\thttp_msg_hdr_apply(msg, true, id, count_handler, &n);\n\n\treturn n;\n}\n\n\n/**\n * Count the number of unknown HTTP Headers\n *\n * @param msg  HTTP Message\n * @param name HTTP Header name\n *\n * @return Number of HTTP Headers\n */\nuint32_t http_msg_xhdr_count(const struct http_msg *msg, const char *name)\n{\n\tuint32_t n = 0;\n\n\thttp_msg_xhdr_apply(msg, true, name, count_handler, &n);\n\n\treturn n;\n}\n\n\nstatic bool value_handler(const struct http_hdr *hdr, void *arg)\n{\n\treturn 0 == pl_strcasecmp(&hdr->val, (const char *)arg);\n}\n\n\n/**\n * Check if a HTTP Header matches a certain value\n *\n * @param msg   HTTP Message\n * @param id    HTTP Header ID\n * @param value Header value to check\n *\n * @return True if value matches, false if not\n */\nbool http_msg_hdr_has_value(const struct http_msg *msg, enum http_hdrid id,\n\t\t\t    const char *value)\n{\n\treturn NULL != http_msg_hdr_apply(msg, true, id, value_handler,\n\t\t\t\t\t (void *)value);\n}\n\n\n/**\n * Check if an unknown HTTP Header matches a certain value\n *\n * @param msg   HTTP Message\n * @param name  HTTP Header name\n * @param value Header value to check\n *\n * @return True if value matches, false if not\n */\nbool http_msg_xhdr_has_value(const struct http_msg *msg, const char *name,\n\t\t\t     const char *value)\n{\n\treturn NULL != http_msg_xhdr_apply(msg, true, name, value_handler,\n\t\t\t\t\t  (void *)value);\n}\n\n\n/**\n * Print a HTTP Message\n *\n * @param pf  Print function for output\n * @param msg HTTP Message\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_msg_print(struct re_printf *pf, const struct http_msg *msg)\n{\n\tstruct le *le;\n\tint err;\n\n\tif (!msg)\n\t\treturn 0;\n\n\tif (pl_isset(&msg->met))\n\t\terr = re_hprintf(pf, \"%r %r%r HTTP/%r\\n\", &msg->met,\n\t\t\t\t &msg->path, &msg->prm, &msg->ver);\n\telse\n\t\terr = re_hprintf(pf, \"HTTP/%r %u %r\\n\", &msg->ver, msg->scode,\n\t\t\t\t &msg->reason);\n\n\tfor (le=msg->hdrl.head; le; le=le->next) {\n\n\t\tconst struct http_hdr *hdr = le->data;\n\n\t\terr |= re_hprintf(pf, \"%r: %r (%i)\\n\", &hdr->name, &hdr->val,\n\t\t\t\t  hdr->id);\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/http/request.c",
    "content": "/**\n * @file http/request.c HTTP request connection\n *\n * Supports:\n * - GET, POST and PUT requests\n * - basic, digest and token authentication (e.g. bearer)\n * - TLS\n *\n * Copyright (C) 2020 Commend.com\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_sys.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_tmr.h>\n#include <re_srtp.h>\n#include <re_tcp.h>\n#include <re_tls.h>\n#include <re_dns.h>\n#include <re_msg.h>\n#include <re_http.h>\n#include <re_httpauth.h>\n#include \"http.h\"\n\n#define DEBUG_MODULE \"reqconn\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tMAX_RETRIES = 3,\n};\n\nstruct http_reqconn {\n\tstruct le le;\n\n\tstruct http_cli *client;        /**< HTTP client                     */\n\n\tstruct sa peer;          /**< Peer address                           */\n\tstruct http_req *req;    /**< Current HTTP request                   */\n\n\tchar *uri;               /**< Request URI                            */\n\tchar *met;               /**< Request Method                         */\n\tchar *path;              /**< Request path/resource                  */\n\tchar *ctype;             /**< Content-type                           */\n\tuint32_t timeout;        /**< Timeout for DNS and HTTP               */\n\tchar *user;              /**< Auth user                              */\n\tchar *pass;              /**< Auth password                          */\n\tstruct mbuf *body;       /**< HTTP body for POST/PUT request         */\n\tchar *token;             /**< Auth token (e.g. bearer token)         */\n\tchar *tokentype;         /**< Auth token type                        */\n\tstruct mbuf *custhdr;    /**< Custom HTTP headers                    */\n\n\tint retries;             /**< Auth retry counter                     */\n\thttp_resp_h *resph;      /**< HTTP response handler                  */\n\thttp_data_h *datah;      /**< HTTP data handler for downloads        */\n\tvoid *arg;               /**< User data pointer for resph and datah  */\n\n\thttp_bodyh *bodyh;       /**< Handler for the request body           */\n\tuint64_t bodyl;          /**< Size of body if request handler used   */\n\n#ifdef USE_TLS\n\tchar *tlshn;             /**< TLS host name                          */\n#endif\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct http_reqconn *conn = arg;\n\n\tmem_deref(conn->req);\n\tmem_deref(conn->uri);\n\tmem_deref(conn->met);\n\tmem_deref(conn->path);\n\tmem_deref(conn->ctype);\n\tmem_deref(conn->user);\n\tmem_deref(conn->pass);\n\tmem_deref(conn->body);\n\tmem_deref(conn->token);\n\tmem_deref(conn->tokentype);\n\tmem_deref(conn->custhdr);\n#ifdef USE_TLS\n\tmem_deref(conn->tlshn);\n#endif\n}\n\n\nstatic int make_digest_mb(struct mbuf *mb,\n\t\tstruct httpauth_digest_chall *digest,\n\t\tstruct http_reqconn *conn)\n{\n\tstruct httpauth_digest_resp *resp = NULL;\n\tint err;\n\n\terr = httpauth_digest_make_response(&resp, digest, conn->path,\n\t\t\tconn->met, conn->user, conn->pass, conn->body);\n\tif (err)\n\t\treturn err;\n\n\terr = httpauth_digest_response_encode(resp, mb);\n\tmem_deref(resp);\n\treturn err;\n}\n\n\nstatic int make_token_mb(struct mbuf *mb, const struct http_reqconn *conn)\n{\n\tint err;\n\tconst char auth[] = \"Authorization: \";\n\tconst char defaulttype[] = \"Bearer\";\n\n\tif (!conn || !mb)\n\t\treturn EINVAL;\n\n\tif (!str_isset(conn->token) || !mb)\n\t\treturn EINVAL;\n\n\terr = mbuf_resize(mb, strlen(conn->token) + sizeof(auth) +\n\t\tstrlen(conn->tokentype ? conn->tokentype : defaulttype) + 1);\n\tif (err)\n\t\treturn err;\n\n\terr  = mbuf_write_str(mb, auth);\n\n\tif (conn->tokentype)\n\t\terr |= mbuf_write_str(mb, conn->tokentype);\n\telse\n\t\terr |= mbuf_write_str(mb, defaulttype);\n\n\terr |= mbuf_write_str(mb, \" \");\n\terr |= mbuf_write_str(mb, conn->token);\n\tmbuf_set_pos(mb, 0);\n\treturn err;\n}\n\n\nstatic int make_basic_mb(struct mbuf *mb, struct http_reqconn *conn)\n{\n\tint err;\n\tstruct httpauth_basic *basic;\n\n\tif (!conn || !mb)\n\t\treturn EINVAL;\n\n\tbasic = httpauth_basic_alloc();\n\tif (!basic)\n\t\treturn ENOMEM;\n\n\terr = httpauth_basic_make_response(basic, conn->user, conn->pass);\n\tif (err)\n\t\tgoto out;\n\n\terr = httpauth_basic_encode(basic, mb);\n\nout:\n\tmem_deref(basic);\n\treturn err;\n}\n\n\nstatic int send_req(struct http_reqconn *conn, const struct pl *auth);\n\n\nstatic void resp_handler(int err, const struct http_msg *msg, void *arg)\n{\n\tstruct http_reqconn *conn = arg;\n\tconst struct http_hdr *hdr;\n\tstruct httpauth_digest_chall digest;\n\tstruct httpauth_basic *basic = NULL;\n\tstruct pl auth;\n\tstruct mbuf *abuf = NULL;\n\n\tif (!conn)\n\t\treturn;\n\n\tif (!msg) {\n\t\tDEBUG_INFO(\"no http_msg (%m)\\n\", err);\n\t\tgoto disconnect;\n\t}\n\telse {\n\t\tDEBUG_INFO(\"scode=%u (%m)\\n\", msg->scode, err);\n\t}\n\n\tif (err || (msg->scode != 401 && msg->scode != 403))\n\t\tgoto disconnect;\n\n\thdr = http_msg_hdr(msg, HTTP_HDR_WWW_AUTHENTICATE);\n\tif (!hdr)\n\t\tgoto disconnect;\n\n\tconn->retries++;\n\tif (conn->retries > MAX_RETRIES) {\n\t\terr = EAUTH;\n\t\tDEBUG_INFO(\"not authorized\\n\");\n\t\tgoto disconnect;\n\t}\n\n\tif (httpauth_digest_challenge_decode(&digest, &hdr->val)) {\n\t\t/* It's not digest. Now try basic. */\n\t\tbasic = httpauth_basic_alloc();\n\t\tif (!basic) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto disconnect;\n\t\t}\n\n\t\tif (httpauth_basic_decode(basic, &hdr->val)) {\n\t\t\terr = EBADMSG;\n\t\t\tgoto disconnect;\n\t\t}\n\t}\n\n\tabuf = mbuf_alloc(1);\n\tif (!abuf) {\n\t\terr = ENOMEM;\n\t\tgoto disconnect;\n\t}\n\n\tif (pl_isset(&digest.nonce))\n\t\terr = make_digest_mb(abuf, &digest, conn);\n\telse if (basic && pl_isset(&basic->realm))\n\t\terr = make_basic_mb(abuf, conn);\n\telse\n\t\terr = EBADMSG;\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"Authentication failed (%m)\\n\", err);\n\t\tgoto disconnect;\n\t}\n\n\tpl_set_mbuf(&auth, abuf);\n\tmbuf_set_pos(conn->body, 0);\n\terr = send_req(conn, &auth);\n\tif (err)\n\t\tgoto disconnect;\n\n\tgoto out;\n\n disconnect:\n\tif (conn && conn->resph)\n\t\tconn->resph(err, msg, conn->arg);\n\n out:\n\tmem_deref(abuf);\n\tmem_deref(basic);\n\tmem_deref(conn);\n}\n\n\nstatic int data_handler(const uint8_t *buf, size_t size,\n\t\tconst struct http_msg *msg, void *arg)\n{\n\tstruct http_reqconn *conn = arg;\n\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tif (!conn->datah)\n\t\treturn 0;\n\n\treturn conn->datah(buf, size, msg, conn->arg);\n}\n\n\nstatic size_t req_body_handler(struct mbuf *mb, void *arg)\n{\n\tstruct http_reqconn *conn = arg;\n\tsize_t len = 0;\n\n\tif (!mb)\n\t\treturn 0;\n\n\tif (conn->bodyh) {\n\t\tlen = conn->bodyh(mb, conn->arg);\n\t}\n\telse if (conn->body) {\n\t\tlen = min(mbuf_get_left(conn->body),\n\t\t\thttp_client_get_bufsize_max(conn->client));\n\t\tif (!len)\n\t\t\treturn len;\n\n\t\tmbuf_write_mem(mb, mbuf_buf(conn->body), len);\n\t\tmbuf_advance(conn->body, len);\n\t}\n\n\treturn len;\n}\n\n\nstatic int send_req(struct http_reqconn *conn, const struct pl *auth)\n{\n\tint err;\n\tstruct mbuf *ctbuf = NULL;\n\tstruct mbuf *clbuf = NULL;\n\tstruct pl ct = PL_INIT;\n\tstruct pl cl = PL_INIT;\n\tstruct pl custh = PL_INIT;\n#if (DEBUG_LEVEL >= 7)\n\tstruct pl dbg;\n#endif\n\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tif (conn->body || conn->bodyh) {\n\t\tclbuf = mbuf_alloc(22);\n\t\tif (!clbuf)\n\t\t\treturn ENOMEM;\n\n\t\tif (conn->bodyh)\n\t\t\tmbuf_printf(clbuf, \"Content-Length: %llu\\r\\n\",\n\t\t\t\t    conn->bodyl);\n\t\telse\n\t\t\tmbuf_printf(clbuf, \"Content-Length: %zu\\r\\n\",\n\t\t\t\t    mbuf_get_left(conn->body));\n\n\t\tmbuf_set_pos(clbuf, 0);\n\t\tpl_set_mbuf(&cl, clbuf);\n\t}\n\n\tif (conn->ctype) {\n\t\tctbuf = mbuf_alloc(17 + strlen(conn->ctype));\n\t\tmbuf_printf(ctbuf, \"Content-Type: %s\\r\\n\", conn->ctype);\n\t\tmbuf_set_pos(ctbuf, 0);\n\t\tpl_set_mbuf(&ct, ctbuf);\n\t}\n\n\tDEBUG_INFO(\"send %s uri=%s path=%s len=%zu %s auth.\\n\",\n\t\t\tconn->met, conn->uri, conn->path,\n\t\t\tmbuf_get_left(conn->body),\n\t\t\tauth ? \"with\" : \"without\");\n\n\tif (auth) {\n\t\tDEBUG_INFO(\"auth=|%r|\\n\", auth);\n\t}\n\n#if (DEBUG_LEVEL >= 7)\n\tif (conn->body) {\n\t\tpl_set_mbuf(&dbg, conn->body);\n\t\tDEBUG_PRINTF(\"postdata:\\n%r\\n\", &dbg);\n\t}\n#endif\n\n\tif (conn->custhdr)\n\t\tpl_set_mbuf(&custh, conn->custhdr);\n\n\terr = http_request_addr(&conn->req, conn->client,\n\t\t\t\tconn->met, conn->uri,\n\t\t\t\tsa_isset(&conn->peer, SA_ADDR) ?\n\t\t\t\t\t &conn->peer : NULL,\n\t\t\t\tresp_handler,\n\t\t\t\tconn->datah ? data_handler : NULL,\n\t\t\t\t(conn->bodyh || conn->body) ?\n\t\t\t\t\treq_body_handler : NULL,\n\t\t\t\tconn,\n\t\t\t\t\"%r%s\"\n\t\t\t\t\"User-Agent: re \" RE_VERSION \"\\r\\n\"\n\t\t\t\t\"%r\"\n\t\t\t\t\"%r\"\n\t\t\t\t\"%r\"\n\t\t\t\t\"\\r\\n\",\n\t\t\t\tauth, auth ? \"\\r\\n\" : \"\",\n\t\t\t\t&ct,\n\t\t\t\t&custh,\n\t\t\t\t&cl);\n\n\tmem_deref(clbuf);\n\tmem_deref(ctbuf);\n\tif (err) {\n\t\tDEBUG_WARNING(\"Could not send %s request. (%m)\\n\",\n\t\t\t      conn->met, err);\n\t\treturn err;\n\t}\n\n\t/* keep internal reference for resp_handler */\n\tmem_ref(conn);\n\treturn 0;\n}\n\n\nstatic int send_auth_token(struct http_reqconn *conn)\n{\n\tstruct pl auth;\n\tint err = 0;\n\tstruct mbuf *mb = mbuf_alloc(1);\n\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = make_token_mb(mb, conn);\n\tif (err)\n\t\tgoto out;\n\n\tpl_set_mbuf(&auth, mb);\n\terr = send_req(conn, &auth);\n\nout:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\n/**\n * Allocates a new http_reqconn instance. Has to be freed after usage with\n * mem_deref().\n *\n * @param pconn   A pointer for returning the new http_reqconn.\n * @param client  The HTTP client. Multiple parallel HTTP request with the same\n *                HTTP client are possible.\n * @param resph   The optional response handler.\n * @param datah   The optional data handler. This is useful for downloading\n *                large files.\n * @param arg     A pointer that will be passed to resph and datah.\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_reqconn_alloc(struct http_reqconn **pconn,\n\t\tstruct http_cli *client,\n\t\thttp_resp_h *resph, http_data_h *datah, void* arg)\n{\n\tstruct http_reqconn *conn = NULL;\n\tint err;\n\tstruct pl pl = PL(\"GET\");\n\n\tif (!pconn || !client)\n\t\treturn EINVAL;\n\n\tconn = mem_zalloc(sizeof(*conn), destructor);\n\tif (!conn)\n\t\treturn ENOMEM;\n\n\tconn->client = client;\n\tconn->resph = resph;\n\tconn->datah = datah;\n\tconn->arg = arg;\n\n\terr = http_reqconn_set_method(conn, &pl);\n\tif (err)\n\t\tconn = mem_deref(conn);\n\n\t*pconn = conn;\n\treturn err;\n}\n\n\nint http_reqconn_set_auth(struct http_reqconn *conn, const struct pl *user,\n\t\tconst struct pl *pass)\n{\n\tint err = 0;\n\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tconn->user = mem_deref(conn->user);\n\tconn->pass = mem_deref(conn->pass);\n\tif (pl_isset(user))\n\t\terr |= pl_strdup(&conn->user, user);\n\n\tif (pl_isset(pass))\n\t\terr |= pl_strdup(&conn->pass, pass);\n\n\treturn err;\n}\n\n\nint http_reqconn_set_bearer(struct http_reqconn *conn, const struct pl *bearer)\n{\n\tconn->tokentype = mem_deref(conn->tokentype);\n\treturn http_reqconn_set_authtoken(conn, bearer);\n}\n\n\nint http_reqconn_set_authtoken(struct http_reqconn *conn,\n\tconst struct pl *token)\n{\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tconn->token = mem_deref(conn->token);\n\tif (!pl_isset(token))\n\t\treturn 0;\n\n\treturn pl_strdup(&conn->token, token);\n}\n\n\nint http_reqconn_set_tokentype(struct http_reqconn *conn,\n\tconst struct pl *tokentype)\n{\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tconn->tokentype = mem_deref(conn->tokentype);\n\tif (!pl_isset(tokentype))\n\t\treturn 0;\n\n\treturn pl_strdup(&conn->tokentype, tokentype);\n}\n\n\nint http_reqconn_set_method(struct http_reqconn *conn, const struct pl *met)\n{\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tconn->met = mem_deref(conn->met);\n\treturn pl_strdup(&conn->met, met);\n}\n\n\nint http_reqconn_set_body(struct http_reqconn *conn, struct mbuf *body)\n{\n\tif (!conn || !body)\n\t\treturn EINVAL;\n\n\tconn->body = mbuf_alloc_ref(body);\n\n\tif (!conn->body)\n\t\treturn ENOMEM;\n\n\tmbuf_set_pos(conn->body, 0);\n\tconn->bodyl = mbuf_get_left(conn->body);\n\n\treturn 0;\n}\n\n\nint http_reqconn_set_ctype(struct http_reqconn *conn, const struct pl *ctype)\n{\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tconn->ctype = mem_deref(conn->ctype);\n\tif (!pl_isset(ctype))\n\t\treturn 0;\n\n\treturn pl_strdup(&conn->ctype, ctype);\n}\n\n\nint http_reqconn_add_header(struct http_reqconn *conn, const struct pl *header)\n{\n\tint err;\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tif (!pl_isset(header))\n\t\treturn 0;\n\n\tif (!conn->custhdr)\n\t\tconn->custhdr = mbuf_alloc(8);\n\n\tif (!conn->custhdr)\n\t\treturn ENOMEM;\n\n\terr = mbuf_write_pl(conn->custhdr, header);\n\terr |= mbuf_write_str(conn->custhdr, \"\\r\\n\");\n\tif (err)\n\t\tconn->custhdr = mem_deref(conn->custhdr);\n\n\treturn err;\n}\n\n\nint http_reqconn_clr_header(struct http_reqconn *conn)\n{\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tconn->custhdr = mem_deref(conn->custhdr);\n\treturn 0;\n}\n\n\n#ifdef USE_TLS\nint http_reqconn_set_tls_hostname(struct http_reqconn *conn,\n\t\tconst struct pl *hostname)\n{\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tconn->tlshn = mem_deref(conn->tlshn);\n\tif (!pl_isset(hostname))\n\t\treturn 0;\n\n\treturn pl_strdup(&conn->tlshn, hostname);\n}\n#endif\n\n\n/**\n * Set explicit peer address for next request (bypasses DNS)\n *\n * If set, the TCP connection goes to this address while\n * the URI's hostname is still used for the Host header and\n * TLS SNI. Useful for routing through a local proxy.\n *\n * @param conn HTTP request connection\n * @param peer Peer address (NULL to clear)\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_reqconn_set_peer(struct http_reqconn *conn,\n\t\t\t  const struct sa *peer)\n{\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tif (peer)\n\t\tconn->peer = *peer;\n\telse\n\t\tmemset(&conn->peer, 0, sizeof(conn->peer));\n\n\treturn 0;\n}\n\n\nint http_reqconn_send(struct http_reqconn *conn, const struct pl *uri)\n{\n\tint err;\n\tstruct http_uri hu;\n\tchar *host = NULL;\n#ifdef USE_TLS\n\tstruct pl tlshn;\n#endif\n\n\tif (!conn || !pl_isset(uri))\n\t\treturn EINVAL;\n\n\terr = http_uri_decode(&hu, uri);\n\tif (err) {\n\t\tDEBUG_WARNING(\"http uri %r decode error (%m)\\n\", uri, err);\n\t\treturn EINVAL;\n\t}\n\n\tconn->uri = mem_deref(conn->uri);\n\tconn->path = mem_deref(conn->path);\n\terr |= pl_strdup(&conn->uri, uri);\n\terr |= pl_strdup(&conn->path, &hu.path);\n\terr |= pl_strdup(&host, &hu.host);\n\tif (err)\n\t\treturn err;\n\n#ifdef USE_TLS\n\tif (conn->tlshn) {\n\t\tpl_set_str(&tlshn, conn->tlshn);\n\t\terr = http_client_set_tls_hostname(conn->client, &tlshn);\n\t}\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"Could not set TLS hostname.\\n\");\n\t\tmem_deref(host);\n\t\treturn err;\n\t}\n#endif\n\n\tmem_deref(host);\n\tif (conn->custhdr)\n\t\tmbuf_set_pos(conn->custhdr, 0);\n\n\tconn->retries = 0;\n\tif (conn->token)\n\t\terr = send_auth_token(conn);\n\telse\n\t\terr = send_req(conn, NULL);\n\n\treturn err;\n}\n\n\nint http_reqconn_set_req_bodyh(struct http_reqconn *conn,\n\t\t\t       http_bodyh cb, uint64_t len)\n{\n\tint err = 0;\n\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tconn->bodyh = cb;\n\tconn->bodyl = len;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/http/server.c",
    "content": "/**\n * @file http/server.c HTTP Server\n *\n * Copyright (C) 2011 Creytiv.com\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_fmt.h>\n#include <re_tmr.h>\n#include <re_srtp.h>\n#include <re_tcp.h>\n#include <re_tls.h>\n#include <re_msg.h>\n#include <re_http.h>\n\n\nenum {\n\tTIMEOUT_IDLE = 600000,\n\tTIMEOUT_INIT = 10000,\n\tBUFSIZE_MAX  = 1024 * 1024 * 1, /* 1 MB */\n};\n\nstruct http_sock {\n\tstruct list connl;\n\tstruct tcp_sock *ts;\n\tstruct tls *tls;\n\thttp_req_h *reqh;\n\thttps_verify_msg_h *verifyh;\n\tsize_t max_body_size;\n\tvoid *arg;\n};\n\nstruct http_conn {\n\tstruct le le;\n\tstruct tmr tmr;\n\tstruct sa peer;\n\tstruct http_sock *sock;\n\tstruct tcp_conn *tc;\n\tstruct tls_conn *sc;\n\tstruct mbuf *mb;\n\tstruct http_verify_msg_d *verify_msg_d;\n};\n\n\nstatic void conn_close(struct http_conn *conn);\n\n\nstatic void sock_destructor(void *arg)\n{\n\tstruct http_sock *sock = arg;\n\tstruct le *le;\n\n\tfor (le=sock->connl.head; le;) {\n\n\t\tstruct http_conn *conn = le->data;\n\n\t\tle = le->next;\n\n\t\tconn_close(conn);\n\t\tmem_deref(conn);\n\t}\n\n\tmem_deref(sock->tls);\n\tmem_deref(sock->ts);\n}\n\n\nstatic void conn_destructor(void *arg)\n{\n\tstruct http_conn *conn = arg;\n\n\tlist_unlink(&conn->le);\n\ttmr_cancel(&conn->tmr);\n\tmem_deref(conn->verify_msg_d);\n\tmem_deref(conn->sc);\n\tmem_deref(conn->tc);\n\tmem_deref(conn->mb);\n}\n\n\nstatic void conn_close(struct http_conn *conn)\n{\n\tlist_unlink(&conn->le);\n\ttmr_cancel(&conn->tmr);\n\tconn->verify_msg_d = mem_deref(conn->verify_msg_d);\n\tconn->sc = mem_deref(conn->sc);\n\tconn->tc = mem_deref(conn->tc);\n\tconn->sock = NULL;\n}\n\n\nstatic void timeout_handler(void *arg)\n{\n\tstruct http_conn *conn = arg;\n\n\tconn_close(conn);\n\tmem_deref(conn);\n}\n\n\n#ifdef HAVE_TLS1_3_POST_HANDSHAKE_AUTH\nstruct http_verify_msg_d {\n\tstruct http_conn *conn;\n\tstruct http_msg *msg;\n\tint err;\n\tint scode;\n\tconst char *reason;\n\tstruct tmr tmr;\n};\n\n\nstatic void verify_msg_destructor(void *arg)\n{\n\tstruct http_verify_msg_d *d = arg;\n\ttmr_cancel(&d->tmr);\n\tmem_deref(d->msg);\n}\n\n\nstatic void verify_cert_done(void *arg)\n{\n\tstruct http_verify_msg_d *d = arg;\n\tstruct http_conn *conn = d->conn;\n\n\tif (d->err)\n\t\thttp_ereply(conn, d->scode, d->reason);\n\telse\n\t\tconn->sock->reqh(conn, d->msg, conn->sock->arg);\n\n\tconn->verify_msg_d = mem_deref(d);\n}\n\n\nstatic int http_verify_handler(int ok, void *arg)\n{\n\tstruct http_verify_msg_d *d = arg;\n\n\tif (ok) {\n\t\td->err = 0;\n\t}\n\telse {\n\t\td->err = EACCES;\n\t\td->scode = 403;\n\t\td->reason = \"Forbidden\";\n\t}\n\n\ttmr_start(&d->tmr, 1, verify_cert_done, d);\n\treturn ok;\n}\n\n\nstatic enum re_https_verify_msg verify_msg(struct http_conn *conn,\n\tstruct http_msg *msg)\n{\n\tenum re_https_verify_msg res;\n\tstruct http_verify_msg_d *d;\n\n\tif (!conn->sock)\n\t\treturn HTTPS_MSG_IGNORE;\n\telse if (!conn->sock->verifyh)\n\t\treturn HTTPS_MSG_OK;\n\n\tres = conn->sock->verifyh(conn, msg, conn->sock->arg);\n\n\tif (res == HTTPS_MSG_REQUEST_CERT) {\n\n\t\td = mem_zalloc(sizeof(*d), verify_msg_destructor);\n\t\tif (!d) {\n\t\t\tres = HTTPS_MSG_IGNORE;\n\t\t\tgoto out;\n\t\t}\n\n\t\td->conn = conn;\n\t\td->err = ETIMEDOUT;\n\t\td->scode = 408;\n\t\td->reason = \"Request Timeout\";\n\t\td->msg = msg;\n\n\t\tmem_deref(conn->verify_msg_d);\n\t\tconn->verify_msg_d = d;\n\t\ttmr_start(&d->tmr, TIMEOUT_IDLE, verify_cert_done, d);\n\n\t\tint err = tls_set_verify_client_handler(http_conn_tls(conn),\n\t\t\t-1, http_verify_handler, d);\n\t\tif (err) {\n\t\t\tres = HTTPS_MSG_IGNORE;\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = tls_verify_client_post_handshake(\n\t\t\t\thttp_conn_tls(conn));\n\t\tif (err) {\n\t\t\tres = HTTPS_MSG_IGNORE;\n\t\t\tgoto out;\n\t\t}\n\t}\n\nout:\n\treturn res;\n}\n#endif\n\n\nstatic void recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct http_conn *conn = arg;\n\tint err = 0;\n\n\tif (conn->mb) {\n\n\t\tconst size_t len = mbuf_get_left(mb), pos = conn->mb->pos;\n\n\t\tif ((mbuf_get_left(conn->mb) + len) >\n\t\t    conn->sock->max_body_size) {\n\t\t\terr = EOVERFLOW;\n\t\t\tgoto out;\n\t\t}\n\n\t\tconn->mb->pos = conn->mb->end;\n\n\t\terr = mbuf_write_mem(conn->mb, mbuf_buf(mb), len);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tconn->mb->pos = pos;\n\t}\n\telse {\n\t\tconn->mb = mem_ref(mb);\n\t}\n\n\twhile (conn->mb) {\n\t\tsize_t end, pos = conn->mb->pos;\n\t\tstruct http_msg *msg;\n\n\t\terr = http_msg_decode(&msg, conn->mb, true);\n\t\tif (err) {\n\t\t\tif (err == ENODATA) {\n\t\t\t\tconn->mb->pos = pos;\n\t\t\t\terr = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (mbuf_get_left(conn->mb) < msg->clen) {\n\t\t\tconn->mb->pos = pos;\n\t\t\tmem_deref(msg);\n\t\t\tbreak;\n\t\t}\n\n\t\tmem_deref(msg->mb);\n\t\tmsg->mb = mem_ref(msg->_mb);\n\n\t\tmb = conn->mb;\n\n\t\tend     = mb->end;\n\t\tmb->end = mb->pos + msg->clen;\n\n\t\tif (end > mb->end) {\n\t\t\tstruct mbuf *mbn = mbuf_alloc(end - mb->end);\n\t\t\tif (!mbn) {\n\t\t\t\tmem_deref(msg);\n\t\t\t\terr = ENOMEM;\n\t\t\t\tgoto out;\n\t\t\t}\n\n\t\t\t(void)mbuf_write_mem(mbn, mb->buf + mb->end,\n\t\t\t\t\t     end - mb->end);\n\t\t\tmbn->pos = 0;\n\n\t\t\tmem_deref(conn->mb);\n\t\t\tconn->mb = mbn;\n\t\t}\n\t\telse {\n\t\t\tconn->mb = mem_deref(conn->mb);\n\t\t}\n\n#ifdef HAVE_TLS1_3_POST_HANDSHAKE_AUTH\n\t\tif (verify_msg(conn, msg) == HTTPS_MSG_OK) {\n\t\t\tconn->sock->reqh(conn, msg, conn->sock->arg);\n\t\t\tmem_deref(msg);\n\t\t}\n#else\n\t\tconn->sock->reqh(conn, msg, conn->sock->arg);\n\t\tmem_deref(msg);\n#endif\n\t\tif (!conn->tc) {\n\t\t\terr = ENOTCONN;\n\t\t\tgoto out;\n\t\t}\n\n\t\ttmr_start(&conn->tmr, TIMEOUT_IDLE, timeout_handler, conn);\n\t}\n\n out:\n\tif (err) {\n\t\tconn_close(conn);\n\t\tmem_deref(conn);\n\t}\n}\n\n\nstatic void close_handler(int err, void *arg)\n{\n\tstruct http_conn *conn = arg;\n\t(void)err;\n\n\tconn_close(conn);\n\tmem_deref(conn);\n}\n\n\nstatic void connect_handler(const struct sa *peer, void *arg)\n{\n\tstruct http_sock *sock = arg;\n\tstruct http_conn *conn;\n\tint err;\n\n\tconn = mem_zalloc(sizeof(*conn), conn_destructor);\n\tif (!conn) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tlist_append(&sock->connl, &conn->le, conn);\n\tconn->peer = *peer;\n\tconn->sock = sock;\n\n\terr = tcp_accept(&conn->tc, sock->ts, NULL, recv_handler,\n\t\t\t close_handler, conn);\n\tif (err)\n\t\tgoto out;\n\n#ifdef USE_TLS\n\tif (sock->tls) {\n\t\terr = tls_start_tcp(&conn->sc, sock->tls, conn->tc, 0);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n#endif\n\n\ttmr_start(&conn->tmr, TIMEOUT_INIT, timeout_handler, conn);\n\n out:\n\tif (err) {\n\t\tmem_deref(conn);\n\t\ttcp_reject(sock->ts);\n\t}\n}\n\n\n/**\n * Create an HTTP socket from file descriptor\n *\n * @param sockp Pointer to returned HTTP Socket\n * @param fd    File descriptor\n * @param reqh  Request handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_listen_fd(struct http_sock **sockp, re_sock_t fd, http_req_h *reqh,\n\t\t   void *arg)\n{\n\tstruct http_sock *sock;\n\tint err;\n\n\tif (!sockp || fd == RE_BAD_SOCK || !reqh)\n\t\treturn EINVAL;\n\n\tsock = mem_zalloc(sizeof(*sock), sock_destructor);\n\tif (!sock)\n\t\treturn ENOMEM;\n\n\terr = tcp_sock_alloc_fd(&sock->ts, fd, connect_handler, sock);\n\tif (err)\n\t\tgoto out;\n\n\tsock->reqh = reqh;\n\tsock->arg  = arg;\n\tsock->max_body_size = BUFSIZE_MAX;\n\nout:\n\tif (err)\n\t\tmem_deref(sock);\n\telse\n\t\t*sockp = sock;\n\n\treturn err;\n}\n\n\n/**\n * Create an HTTP socket\n *\n * @param sockp Pointer to returned HTTP Socket\n * @param laddr Network address to listen on\n * @param reqh  Request handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_listen(struct http_sock **sockp, const struct sa *laddr,\n\t\thttp_req_h *reqh, void *arg)\n{\n\tstruct http_sock *sock;\n\tint err;\n\n\tif (!sockp || !laddr || !reqh)\n\t\treturn EINVAL;\n\n\tsock = mem_zalloc(sizeof(*sock), sock_destructor);\n\tif (!sock)\n\t\treturn ENOMEM;\n\n\terr = tcp_listen(&sock->ts, laddr, connect_handler, sock);\n\tif (err)\n\t\tgoto out;\n\n\tsock->reqh = reqh;\n\tsock->arg  = arg;\n\tsock->max_body_size = BUFSIZE_MAX;\n\n out:\n\tif (err)\n\t\tmem_deref(sock);\n\telse\n\t\t*sockp = sock;\n\n\treturn err;\n}\n\n\n/**\n * Create an HTTP secure socket\n *\n * @param sockp Pointer to returned HTTP Socket\n * @param laddr Network address to listen on\n * @param cert  File path of TLS certificate\n * @param reqh  Request handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint https_listen(struct http_sock **sockp, const struct sa *laddr,\n\t\t const char *cert, http_req_h *reqh, void *arg)\n{\n\tstruct http_sock *sock;\n\tint err;\n\n\tif (!sockp || !laddr || !cert || !reqh)\n\t\treturn EINVAL;\n\n\terr = http_listen(&sock, laddr, reqh, arg);\n\tif (err)\n\t\treturn err;\n\n#ifdef USE_TLS\n\terr = tls_alloc(&sock->tls, TLS_METHOD_SSLV23, cert, NULL);\n#else\n\terr = EPROTONOSUPPORT;\n#endif\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(sock);\n\telse\n\t\t*sockp = sock;\n\n\treturn err;\n}\n\n\n/**\n * Set verify http msg handler. (Needs TLS v1.3 post-handshake auth)\n *\n * This handler allows to decide whether e.g. a certificate\n * should be requested from the client or not.\n * E.g. This decision can done based on the http path contained in\n * struct http_msg.\n *\n * @param sock \t\tHTTP socket\n * @param verifyh \tVerify handler called before the https request\n *                      handler is called to return a http response\n *                      to the client.\n *\n * @return 0 if success, otherwise errorcode\n */\nint  https_set_verify_msgh(struct http_sock *sock,\n\thttps_verify_msg_h *verifyh)\n{\n#ifdef HAVE_TLS1_3_POST_HANDSHAKE_AUTH\n\tif (!sock || !verifyh)\n\t\treturn EINVAL;\n\n\n\tsock->verifyh = verifyh;\n\treturn 0;\n#else\n\t(void)sock;\n\t(void)verifyh;\n\treturn ENOTSUP;\n#endif\n}\n\n\n/**\n * Set Request buffer size limit\n *\n * @param sock  HTTP socket\n * @param limit New limit in bytes\n */\nvoid http_set_max_body_size(struct http_sock *sock, size_t limit)\n{\n\tif (!sock)\n\t\treturn;\n\n\tsock->max_body_size = limit;\n}\n\n\n/**\n * Get the TCP socket of an HTTP socket\n *\n * @param sock HTTP socket\n *\n * @return TCP socket\n */\nstruct tcp_sock *http_sock_tcp(struct http_sock *sock)\n{\n\treturn sock ? sock->ts : NULL;\n}\n\n\n/**\n * Get the TLS struct of an HTTP sock\n *\n * @param sock HTTP socket\n *\n * @return TLS struct\n */\nstruct tls *http_sock_tls(const struct http_sock *sock)\n{\n\treturn sock ? sock->tls : NULL;\n}\n\n\n/**\n * Get the peer address of an HTTP connection\n *\n * @param conn HTTP connection\n *\n * @return Peer address\n */\nconst struct sa *http_conn_peer(const struct http_conn *conn)\n{\n\treturn conn ? &conn->peer : NULL;\n}\n\n\n/**\n * Get the TCP connection of an HTTP connection\n *\n * @param conn HTTP connection\n *\n * @return TCP connection\n */\nstruct tcp_conn *http_conn_tcp(struct http_conn *conn)\n{\n\treturn conn ? conn->tc : NULL;\n}\n\n\n/**\n * Get the TLS connection of an HTTP connection\n *\n * @param conn HTTP connection\n *\n * @return TLS connection\n */\nstruct tls_conn *http_conn_tls(struct http_conn *conn)\n{\n\treturn conn ? conn->sc : NULL;\n}\n\n\n/**\n * Reset IDLE Timeout of an HTTP Connection\n *\n * @param conn HTTP connection\n */\nvoid http_conn_reset_timeout(struct http_conn *conn)\n{\n\ttmr_start(&conn->tmr, TIMEOUT_IDLE, timeout_handler, conn);\n}\n\n\n/**\n * Close the HTTP connection\n *\n * @param conn HTTP connection\n */\nvoid http_conn_close(struct http_conn *conn)\n{\n\tif (!conn)\n\t\treturn;\n\n\tconn->sc = mem_deref(conn->sc);\n\tconn->tc = mem_deref(conn->tc);\n}\n\n\nstatic int http_vreply(struct http_conn *conn, uint16_t scode,\n\t\t       const char *reason, const char *fmt, va_list ap)\n{\n\tstruct mbuf *mb;\n\tint err;\n\n\tif (!conn || !scode || !reason)\n\t\treturn EINVAL;\n\n\tif (!conn->tc)\n\t\treturn ENOTCONN;\n\n\tmb = mbuf_alloc(8192);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = mbuf_printf(mb, \"HTTP/1.1 %u %s\\r\\n\", scode, reason);\n\tif (fmt)\n\t\terr |= mbuf_vprintf(mb, fmt, ap);\n\telse\n\t\terr |= mbuf_write_str(mb, \"Content-Length: 0\\r\\n\\r\\n\");\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\n\terr = tcp_send(conn->tc, mb);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/**\n * Send an HTTP response\n *\n * @param conn   HTTP connection\n * @param scode  Response status code\n * @param reason Response reason phrase\n * @param fmt    Formatted HTTP message\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_reply(struct http_conn *conn, uint16_t scode, const char *reason,\n\t       const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = http_vreply(conn, scode, reason, fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Send an HTTP response with content formatting\n *\n * @param conn   HTTP connection\n * @param scode  Response status code\n * @param reason Response reason phrase\n * @param ctype  Content type\n * @param fmt    Formatted HTTP content\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_creply(struct http_conn *conn, uint16_t scode, const char *reason,\n\t\tconst char *ctype, const char *fmt, ...)\n{\n\tstruct mbuf *mb;\n\tva_list ap;\n\tint err;\n\n\tif (!ctype || !fmt)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(8192);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tva_start(ap, fmt);\n\terr = mbuf_vprintf(mb, fmt, ap);\n\tva_end(ap);\n\tif (err)\n\t\tgoto out;\n\n\terr = http_reply(conn, scode, reason,\n\t\t\t \"Content-Type: %s\\r\\n\"\n\t\t\t \"Content-Length: %zu\\r\\n\"\n\t\t\t \"\\r\\n\"\n\t\t\t \"%b\",\n\t\t\t ctype,\n\t\t\t mb->end,\n\t\t\t mb->buf, mb->end);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/**\n * Send an HTTP error response\n *\n * @param conn   HTTP connection\n * @param scode  Response status code\n * @param reason Response reason phrase\n *\n * @return 0 if success, otherwise errorcode\n */\nint http_ereply(struct http_conn *conn, uint16_t scode, const char *reason)\n{\n\treturn http_creply(conn, scode, reason, \"text/html\",\n\t\t\t   \"<!DOCTYPE html>\\n\"\n\t\t\t   \"<html>\\n\"\n\t\t\t   \"<head><title>%u %s</title></head>\\n\"\n\t\t\t   \"<body><h2>%u %s</h2></body>\\n\"\n\t\t\t   \"</html>\\n\",\n\t\t\t   scode, reason,\n\t\t\t   scode, reason);\n}\n"
  },
  {
    "path": "src/httpauth/basic.c",
    "content": "/**\n * @file basic.c HTTP Basic authentication\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_base64.h>\n#include <re_mem.h>\n#include <re_fmt.h>\n#include <re_httpauth.h>\n\n\n#define DEBUG_MODULE \"httpauth_basic\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic void httpauth_basic_destr(void *arg)\n{\n\tstruct httpauth_basic *basic = arg;\n\n\tmem_deref(basic->mb);\n}\n\n\nstruct httpauth_basic *httpauth_basic_alloc(void)\n{\n\tstruct httpauth_basic *basic = mem_zalloc(sizeof(*basic),\n\t\t\thttpauth_basic_destr);\n\n\tif (!basic)\n\t\tDEBUG_WARNING(\"could not allocate httpauth_basic\\n\");\n\n\treturn basic;\n}\n\n\n/**\n * Decode a Basic response\n *\n * @param basic Basic response object\n * @param hval Header value to decode from\n *\n * @return 0 if successfully decoded, otherwise errorcode\n */\nint httpauth_basic_decode(struct httpauth_basic *basic,\n\t\t\t\t    const struct pl *hval)\n{\n\tif (!basic || !hval)\n\t\treturn EINVAL;\n\n\tif (re_regex(hval->p, hval->l,\n\t\t\t\"[ \\t\\r\\n]*Basic[ \\t\\r\\n]+realm[ \\t\\r\\n]*=[ \\t\\r\\n]*\"\n\t\t\t\t\"[~ \\t\\r\\n,]*\",\n\t\t\tNULL, NULL, NULL, NULL, &basic->realm) ||\n\t\t\t!pl_isset(&basic->realm))\n\t\treturn EBADMSG;\n\n\treturn 0;\n}\n\n\nint httpauth_basic_make_response(struct httpauth_basic *basic,\n\t\tconst char *user, const char *pwd)\n{\n\tuint8_t *in;\n\tchar *out;\n\tsize_t si, so;\n\tsize_t poso;\n\tint err;\n\n\tif (!basic || !user || !pwd)\n\t\treturn EINVAL;\n\n\tsi = strlen(user) + strlen(pwd) + 1;\n\tso = 4 * (si + 2) / 3;\n\tbasic->mb = mbuf_alloc(si + so + 1);\n\tif (!basic->mb)\n\t\treturn ENOMEM;\n\n\terr = mbuf_printf(basic->mb, \"%s:%s\", user, pwd);\n\tposo = basic->mb->pos;\n\n\terr |= mbuf_fill(basic->mb, 0, so + 1);\n\tif (err)\n\t\tgoto fault;\n\n\tmbuf_set_pos(basic->mb, 0);\n\tin = mbuf_buf(basic->mb);\n\tmbuf_set_pos(basic->mb, poso);\n\tout = (char*) mbuf_buf(basic->mb);\n\terr = base64_encode(in, si, out, &so);\n\tif (err)\n\t\tgoto fault;\n\n\tpl_set_str(&basic->auth, out);\n\n\treturn 0;\n\nfault:\n\tmem_deref(basic->mb);\n\treturn err;\n}\n\nint httpauth_basic_encode(const struct httpauth_basic *basic, struct mbuf *mb)\n{\n\tint err;\n\n\tif (!basic || !mb || !pl_isset(&basic->auth))\n\t\treturn EINVAL;\n\n\terr = mbuf_resize(mb, basic->auth.l + 21);\n\tif (err)\n\t\treturn err;\n\n\terr = mbuf_write_str(mb, \"Authorization: Basic \");\n\terr |= mbuf_write_pl(mb, &basic->auth);\n\tif (err)\n\t\treturn err;\n\n\tmbuf_set_pos(mb, 0);\n\treturn 0;\n}\n\n/* HTTPAUTH BASIC REQUESTS*/\n\nstatic void httpauth_basic_request_destructor(void *arg)\n{\n\tstruct httpauth_basic_req *req = arg;\n\n\tmem_deref(req->realm);\n\tmem_deref(req->charset);\n}\n\n\nint httpauth_basic_request_print(struct re_printf *pf,\n\tconst struct httpauth_basic_req *req)\n{\n\tint err = 0;\n\n\tif (!pf || !req)\n\t\treturn EINVAL;\n\n\terr = re_hprintf(pf, \"Basic realm=\\\"%s\\\"\", req->realm);\n\tif (str_isset(req->charset))\n\t\terr |= re_hprintf(pf, \", charset=\\\"%s\\\"\", req->charset);\n\n\treturn err;\n}\n\n\n/**\n * Verify received credentials\n *\n * @param hval   http authentication header value containing the credentials\n * @param user   user name (may be an UTF-8 string)\n * @param passwd user password (may be an UTF-8 string)\n *\n * @return 0 if successfully verified, otherwise errorcode\n */\nint httpauth_basic_verify(const struct pl *hval, const char *user,\n\tconst char *passwd)\n{\n\tstruct pl b64c = PL_INIT;\n\tstruct mbuf *mb = NULL;\n\tchar *c = NULL;\n\tsize_t clen = 0;\n\tint err = 0;\n\n\tif (!hval || !user || !passwd)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(str_len(user) + str_len(passwd) + 1);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tif (re_regex(hval->p, hval->l, \"[ \\t\\r\\n]*Basic[ \\t\\r\\n]+[~ \\t\\r\\n]*\",\n\t\tNULL, NULL, &b64c) || !pl_isset(&b64c)) {\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\tclen = b64c.l;\n\tc = mem_zalloc(clen, NULL);\n\tif (!c) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = base64_decode(b64c.p, b64c.l, (uint8_t *) c, &clen);\n\tif (err)\n\t\tgoto out;\n\n\terr = mbuf_printf(mb, \"%s:%s\", user, passwd);\n\tif (err)\n\t\tgoto out;\n\n\tif (mem_seccmp(mb->buf, (uint8_t *)c, clen) != 0)\n\t\terr = EACCES;\n\nout:\n\tif (c)\n\t\tmem_secclean(c, clen);\n\n\tif (mb)\n\t\tmem_secclean(mb->buf, mb->size);\n\n\tmem_deref(c);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/**\n * Create a Basic Authentication Request\n *\n * @param preq    httpauth_basic_req object ptr\n * @param realm   realm\n * @param charset optional charset\n *\n * @return 0 if successful, otherwise errorcode\n */\nint httpauth_basic_request(struct httpauth_basic_req **preq,\n\tconst char *realm, const char *charset)\n{\n\tstruct httpauth_basic_req *req = NULL;\n\tint err = 0;\n\n\tif (!preq || !realm)\n\t\treturn EINVAL;\n\n\treq = mem_zalloc(sizeof(*req), httpauth_basic_request_destructor);\n\tif (!req)\n\t\treturn ENOMEM;\n\n\terr = str_dup(&req->realm, realm);\n\tif (str_isset(charset) && str_casecmp(charset, \"UTF-8\") == 0)\n\t\terr |= str_dup(&req->charset, charset);\n\n\tif (err)\n\t\tmem_deref(req);\n\telse\n\t\t*preq = req;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/httpauth/digest.c",
    "content": "/**\n * @file digest.c  HTTP Digest authentication (RFC 2617) - obsolete\n *                 HTTP Digest authentication (RFC 7616) - wip\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <time.h>\n#include <re_atomic.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_mem.h>\n#include <re_md5.h>\n#include <re_sha.h>\n#include <re_sys.h>\n#include <re_httpauth.h>\n\n\ntypedef void (digest_decode_h)(const struct pl *name, const struct pl *val,\n\t\t\t       void *arg);\n\n\n/* General fields   */\nstatic const struct pl param_realm     = PL(\"realm\");\nstatic const struct pl param_nonce     = PL(\"nonce\");\nstatic const struct pl param_opaque    = PL(\"opaque\");\nstatic const struct pl param_algorithm = PL(\"algorithm\");\nstatic const struct pl param_qop       = PL(\"qop\");\nstatic const struct pl param_stale     = PL(\"stale\");\n\n/* Challenge fields */\nstatic const struct pl param_domain    = PL(\"domain\");\n\n/* Response fields  */\nstatic const struct pl param_response  = PL(\"response\");\nstatic const struct pl param_uri       = PL(\"uri\");\nstatic const struct pl param_username  = PL(\"username\");\n/* static const struct pl param_userstar  = PL(\"username*\"); future use */\nstatic const struct pl param_cnonce    = PL(\"cnonce\");\nstatic const struct pl param_nc        = PL(\"nc\");\n\n/* Optional fields  */\nstatic const struct pl param_charset   = PL(\"charset\");\nstatic const struct pl param_userhash  = PL(\"userhash\");\n\n\nstatic void challenge_decode(const struct pl *name, const struct pl *val,\n\t\t\t     void *arg)\n{\n\tstruct httpauth_digest_chall *chall = arg;\n\n\tif (!pl_casecmp(name, &param_realm))\n\t\tchall->realm = *val;\n\telse if (!pl_casecmp(name, &param_domain))\n\t\tchall->domain = *val;\n\telse if (!pl_casecmp(name, &param_nonce))\n\t\tchall->nonce = *val;\n\telse if (!pl_casecmp(name, &param_opaque))\n\t\tchall->opaque= *val;\n\telse if (!pl_casecmp(name, &param_stale))\n\t\tchall->stale = *val;\n\telse if (!pl_casecmp(name, &param_algorithm))\n\t\tchall->algorithm = *val;\n\telse if (!pl_casecmp(name, &param_qop))\n\t\tchall->qop = *val;\n\telse if (!pl_casecmp(name, &param_charset))\n\t\tchall->charset = *val;\n\telse if (!pl_casecmp(name, &param_userhash))\n\t\tchall->userhash = *val;\n}\n\n\nstatic void algorithm_decode(struct httpauth_digest_resp *resp,\n\tconst struct pl *val)\n{\n\tresp->algorithm = *val;\n\tif (pl_strstr(val, \"SHA-256\")) {\n\t\tresp->hashh = &sha256;\n\t\tresp->hash_length = SHA256_DIGEST_LENGTH;\n\t}\n\telse {\n\t\tresp->hashh = &md5;\n\t\tresp->hash_length = MD5_SIZE;\n\t}\n}\n\n\nstatic void response_decode(const struct pl *name, const struct pl *val,\n\t\t\t    void *arg)\n{\n\tstruct httpauth_digest_resp *resp = arg;\n\n\tif (!pl_casecmp(name, &param_realm))\n\t\tresp->realm = *val;\n\telse if (!pl_casecmp(name, &param_nonce))\n\t\tresp->nonce = *val;\n\telse if (!pl_casecmp(name, &param_response))\n\t\tresp->response = *val;\n\telse if (!pl_casecmp(name, &param_username))\n\t\tresp->username = *val;\n\telse if (!pl_casecmp(name, &param_uri))\n\t\tresp->uri = *val;\n\telse if (!pl_casecmp(name, &param_nc))\n\t\tresp->nc = *val;\n\telse if (!pl_casecmp(name, &param_cnonce))\n\t\tresp->cnonce = *val;\n\telse if (!pl_casecmp(name, &param_qop))\n\t\tresp->qop = *val;\n\telse if (!pl_casecmp(name, &param_algorithm))\n\t\talgorithm_decode(resp, val);\n\telse if (!pl_casecmp(name, &param_charset))\n\t\tresp->charset = *val;\n\telse if (!pl_casecmp(name, &param_userhash))\n\t\tresp->userhash = *val;\n}\n\n\nstatic int digest_decode(const struct pl *hval, digest_decode_h *dech,\n\t\t\t void *arg)\n{\n\tstruct pl r = *hval, start, end, name, val;\n\n\tif (re_regex(r.p, r.l, \"[ \\t\\r\\n]*Digest[ \\t\\r\\n]+\", &start, &end) ||\n\t    start.p != r.p)\n\t\treturn EBADMSG;\n\n\tpl_advance(&r, end.p - r.p);\n\n\twhile (!re_regex(r.p, r.l,\n\t\t\t \"[ \\t\\r\\n,]+[a-z]+[ \\t\\r\\n]*=[ \\t\\r\\n]*[~ \\t\\r\\n,]*\",\n\t\t\t NULL, &name, NULL, NULL, &val)) {\n\n\t\tpl_advance(&r, val.p + val.l - r.p);\n\n\t\tdech(&name, &val, arg);\n\t}\n\n\treturn 0;\n}\n\n\nstatic void response_destructor(void *data)\n{\n\tstruct httpauth_digest_resp *resp = data;\n\n\tmem_deref(resp->mb);\n}\n\n\n/**\n * Decode a Digest challenge\n *\n * @param chall Digest challenge object to decode into\n * @param hval  Header value to decode from\n *\n * @return 0 if successfully decoded, otherwise errorcode\n */\nint httpauth_digest_challenge_decode(struct httpauth_digest_chall *chall,\n\t\t\t\t     const struct pl *hval)\n{\n\tint err;\n\n\tif (!chall || !hval)\n\t\treturn EINVAL;\n\n\tmemset(chall, 0, sizeof(*chall));\n\n\terr = digest_decode(hval, challenge_decode, chall);\n\tif (err)\n\t\treturn err;\n\n\tif (!chall->realm.p || !chall->nonce.p)\n\t\treturn EBADMSG;\n\n\treturn 0;\n}\n\n\n/**\n * Decode a Digest response\n *\n * @param resp Digest response object to decode into\n * @param hval Header value to decode from\n *\n * @return 0 if successfully decoded, otherwise errorcode\n */\nint httpauth_digest_response_decode(struct httpauth_digest_resp *resp,\n\t\t\t\t    const struct pl *hval)\n{\n\tint err;\n\n\tif (!resp || !hval)\n\t\treturn EINVAL;\n\n\tmemset(resp, 0, sizeof(*resp));\n\n\terr = digest_decode(hval, response_decode, resp);\n\tif (err)\n\t\treturn err;\n\n\tif (!resp->realm.p    ||\n\t    !resp->nonce.p    ||\n\t    !resp->response.p ||\n\t    !resp->username.p ||\n\t    !resp->uri.p)\n\t\treturn EBADMSG;\n\n\treturn 0;\n}\n\n\n/**\n * Authenticate a digest response\n *\n * @param resp   Digest response\n * @param method Request method\n * @param ha1    HA1 value from MD5(username:realm:password)\n *\n * @return 0 if successfully authenticated, otherwise errorcode\n */\nint httpauth_digest_response_auth(const struct httpauth_digest_resp *resp,\n\t\t\t\t  const struct pl *method, const uint8_t *ha1)\n{\n\tuint8_t ha2[MD5_SIZE], digest[MD5_SIZE], response[MD5_SIZE];\n\tconst char *p;\n\tuint32_t i;\n\tint err;\n\n\tif (!resp || !method || !ha1)\n\t\treturn EINVAL;\n\n\tif (resp->response.l != 32)\n\t\treturn EAUTH;\n\n\terr = md5_printf(ha2, \"%r:%r\", method, &resp->uri);\n\tif (err)\n\t\treturn err;\n\n\tif (pl_isset(&resp->qop))\n\t\terr = md5_printf(digest, \"%w:%r:%r:%r:%r:%w\",\n\t\t\t\t ha1, (size_t)MD5_SIZE,\n\t\t\t\t &resp->nonce,\n\t\t\t\t &resp->nc,\n\t\t\t\t &resp->cnonce,\n\t\t\t\t &resp->qop,\n\t\t\t\t ha2, sizeof(ha2));\n\telse\n\t\terr = md5_printf(digest, \"%w:%r:%w\",\n\t\t\t\t ha1, (size_t)MD5_SIZE,\n\t\t\t\t &resp->nonce,\n\t\t\t\t ha2, sizeof(ha2));\n\tif (err)\n\t\treturn err;\n\n\tfor (i=0, p=resp->response.p; i<sizeof(response); i++) {\n\t\tresponse[i]  = ch_hex(*p++) << 4;\n\t\tresponse[i] += ch_hex(*p++);\n\t}\n\n\tif (memcmp(digest, response, MD5_SIZE))\n\t\treturn EAUTH;\n\n\treturn 0;\n}\n\n\nstatic RE_ATOMIC uint32_t nc = 1;\n\nint httpauth_digest_make_response(struct httpauth_digest_resp **presp,\n\t\tconst struct httpauth_digest_chall *chall,\n\t\tconst char *path, const char *method, const char *user,\n\t\tconst char *pwd, struct mbuf *body)\n{\n\tstruct httpauth_digest_resp *resp;\n\tsize_t p1, p2;\n\tuint8_t ha1[MD5_SIZE], ha2[MD5_SIZE], response[MD5_SIZE];\n\tuint32_t cnonce;\n\tstruct mbuf *mb = NULL;\n\tint err;\n\n\tif (!presp || !chall || !method || !user || !path || !pwd)\n\t\treturn EINVAL;\n\n\tresp = mem_zalloc(sizeof(*resp), response_destructor);\n\tif (!resp) {\n\t\treturn ENOMEM;\n\t}\n\n\tmb = mbuf_alloc(256);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tresp->realm = chall->realm;\n\tresp->nonce = chall->nonce;\n\tpl_set_str(&resp->username, user);\n\tpl_set_str(&resp->uri, path);\n\tresp->qop = chall->qop;\n\n\terr = mbuf_printf(mb, \"%x\", re_atomic_rlx(&nc));\n\terr |= mbuf_write_u8(mb, 0);\n\tif (err)\n\t\tgoto out;\n\n\t/* Client nonce should change, so we use random value. */\n\tcnonce = rand_u32();\n\tp1 = mb->pos;\n\terr = mbuf_printf(mb, \"%x\", cnonce);\n\terr |= mbuf_write_u8(mb, 0);\n\tif (err)\n\t\tgoto out;\n\n\t/* compute response */\n\t/* HA1 = MD5(username:realm:password) */\n\tp2 = mb->pos;\n\terr = mbuf_printf(mb, \"%r:%r:%s\", &resp->username, &resp->realm,\n\t\t\tpwd);\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(mb, p2);\n\tmd5(mbuf_buf(mb), mbuf_get_left(mb), ha1);\n\tmbuf_skip_to_end(mb);\n\tif (0 == pl_strcmp(&chall->algorithm, \"MD5-sess\")) {\n\t\t/* HA1 = MD5(HA1:nonce:cnonce) */\n\t\tp2 = mb->pos;\n\t\terr = mbuf_printf(mb, \"%w:%r:%x\", ha1, sizeof(ha1),\n\t\t\t\t&resp->nonce, cnonce);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tmbuf_set_pos(mb, p2);\n\t\tmd5(mbuf_buf(mb), mbuf_get_left(mb), ha1);\n\t\tmbuf_skip_to_end(mb);\n\t}\n\n\t/* HA2 */\n\tp2 = mb->pos;\n\tif (0 == pl_strcmp(&resp->qop, \"auth-int\") && mbuf_get_left(body)) {\n\t\t/* HA2 = MD5(method:digestURI:MD5(entityBody)) */\n\t\terr = mbuf_write_mem(mb, mbuf_buf(body), mbuf_get_left(body));\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tmbuf_set_pos(mb, p2);\n\t\tmd5(mbuf_buf(mb), mbuf_get_left(mb), ha2);\n\t\tmbuf_skip_to_end(mb);\n\t\tp2 = mb->pos;\n\t\terr = mbuf_printf(mb, \"%s:%r:%w\", method, &resp->uri,\n\t\t\t\tha2, sizeof(ha2));\n\t}\n\telse {\n\t\t/* HA2 = MD5(method:digestURI) */\n\t\terr = mbuf_printf(mb, \"%s:%r\", method, &resp->uri);\n\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(mb, p2);\n\tmd5(mbuf_buf(mb), mbuf_get_left(mb), ha2);\n\tmbuf_skip_to_end(mb);\n\n\t/* response */\n\tp2 = mb->pos;\n\tif (0 == pl_strcmp(&resp->qop, \"auth-int\") ||\n\t\t\t0 == pl_strcmp(&resp->qop, \"auth\")) {\n\t/* response = MD5(HA1:nonce:nonceCount:cnonce:qop:HA2) */\n\t\terr = mbuf_printf(mb, \"%w:%r:%x:%x:%r:%w\",\n\t\t\t\tha1, sizeof(ha1), &resp->nonce,\n\t\t\t\tre_atomic_rlx(&nc), cnonce,\n\t\t\t\t&resp->qop, ha2, sizeof(ha2));\n\t}\n\telse {\n\t/* response = MD5(HA1:nonce:HA2) */\n\t\terr = mbuf_printf(mb, \"%w:%r:%w\", ha1, sizeof(ha1),\n\t\t\t\t&resp->nonce, ha2, sizeof(ha2));\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(mb, p2);\n\tmd5(mbuf_buf(mb), mbuf_get_left(mb), response);\n\tmbuf_skip_to_end(mb);\n\n\tp2 = mb->pos;\n\terr = mbuf_printf(mb, \"%w\", response, sizeof(response));\n\terr |= mbuf_write_u8(mb, 0);\n\tif (err)\n\t\tgoto out;\n\n\tre_atomic_rlx_add(&nc, 1);\n\tmbuf_set_pos(mb, 0);\n\tpl_set_str(&resp->nc, (const char*) mbuf_buf(mb));\n\tmbuf_set_pos(mb, p1);\n\tpl_set_str(&resp->cnonce, (const char*) mbuf_buf(mb));\n\tmbuf_set_pos(mb, p2);\n\tpl_set_str(&resp->response, (const char*) mbuf_buf(mb));\nout:\n\tresp->mb = mb;\n\tif (err)\n\t\tmem_deref(resp);\n\telse\n\t\t*presp = resp;\n\n\treturn err;\n}\n\n\nint httpauth_digest_response_encode(const struct httpauth_digest_resp *resp,\n\t\t\t\t    struct mbuf *mb)\n{\n\tint err;\n\tsize_t s;\n\n\tif (!resp || !mb)\n\t\treturn EINVAL;\n\n\t/* length of string literals */\n\ts = 93;\n\tif (pl_isset(&resp->qop))\n\t\ts += 26;\n\n\t/* length of values */\n\ts += resp->username.l + resp->realm.l + resp->nonce.l + resp->uri.l;\n\ts += resp->response.l;\n\tif (pl_isset(&resp->qop))\n\t\ts += resp->qop.l + resp->nc.l + resp->cnonce.l;\n\n\tif (s > mb->size) {\n\t\terr = mbuf_resize(mb, s);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\terr = mbuf_write_str(mb, \"Authorization: \");\n\terr |= mbuf_printf(mb, \"Digest username=\\\"%r\\\"\", &resp->username);\n\terr |= mbuf_printf(mb, \", realm=\\\"%r\\\"\", &resp->realm);\n\terr |= mbuf_printf(mb, \", nonce=\\\"%r\\\"\", &resp->nonce);\n\terr |= mbuf_printf(mb, \", uri=\\\"%r\\\"\", &resp->uri);\n\terr |= mbuf_printf(mb, \", response=\\\"%r\\\"\", &resp->response);\n\n\tif (pl_isset(&resp->qop)) {\n\t\terr |= mbuf_printf(mb, \", qop=%r\", &resp->qop);\n\t\terr |= mbuf_printf(mb, \", nc=%r\", &resp->nc);\n\t\terr |= mbuf_printf(mb, \", cnonce=\\\"%r\\\"\", &resp->cnonce);\n\t}\n\n\tmbuf_set_pos(mb, 0);\n\treturn err;\n}\n\n\nstatic void httpauth_digest_chall_req_destructor(void *arg)\n{\n\tstruct httpauth_digest_chall_req *req = arg;\n\n\tmem_deref(req->realm);\n\tmem_deref(req->domain);\n\tmem_deref(req->nonce);\n\tmem_deref(req->opaque);\n\tmem_deref(req->algorithm);\n\tmem_deref(req->qop);\n\tmem_deref(req->charset);\n}\n\n\nstatic int generate_nonce(char **pnonce, const time_t ts,\n\tconst char *etag, const char *secret)\n{\n\tstruct mbuf *mb = NULL;\n\tchar *nonce = NULL;\n\tuint8_t hash [SHA256_DIGEST_LENGTH];\n\tint err = 0;\n\n\tmb = mbuf_alloc(32);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tif (str_isset(secret))\n\t\terr = mbuf_printf(mb, \"%Lu:%s:%s\", (uint64_t)ts, etag, secret);\n\telse\n\t\terr = mbuf_printf(mb, \"%Lu:%s\", (uint64_t)ts, etag);\n\n\tif (err)\n\t\tgoto out;\n\n\tsha256(mb->buf, mb->end, hash);\n\tmbuf_rewind(mb);\n\n\terr = mbuf_printf(mb, \"%w%016Lx\", hash, sizeof(hash), (uint64_t)ts);\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(mb, 0);\n\terr = mbuf_strdup(mb, &nonce, mbuf_get_left(mb));\n\nout:\n\tif (err)\n\t\tmem_deref(nonce);\n\telse\n\t\t*pnonce = nonce;\n\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic int check_nonce(const char *req_nonce, const struct pl *resp_nonce,\n\tconst char *etag)\n{\n\tstruct pl pl = PL_INIT;\n\ttime_t ts;\n\tchar *renonce = NULL;\n\tint err = 0;\n\n\tif (!req_nonce || !resp_nonce || !etag)\n\t\treturn EINVAL;\n\n\tpl = *resp_nonce;\n\tpl.p = pl.p + (pl.l - 16);\n\tpl.l = 16;\n\tts = (time_t) pl_x64(&pl);\n\n\tif (time(NULL) - ts > 300) {\n\t\terr = ETIMEDOUT;\n\t\tgoto out;\n\t}\n\n\terr = generate_nonce(&renonce, ts, etag, NULL);\n\tif (err)\n\t\tgoto out;\n\n\tif (str_casecmp(req_nonce, renonce))\n\t\terr = EAUTH;\n\nout:\n\tmem_deref(renonce);\n\treturn err;\n}\n\n\nstatic int digest_verify(struct httpauth_digest_chall_req *req,\n\tstruct httpauth_digest_resp *resp, const struct pl *method,\n\tconst char *user, const char *passwd, const char *entitybody)\n{\n\tuint8_t *hash1 = NULL;\n\tuint8_t *hash2 = NULL;\n\tstruct mbuf *mb = NULL;\n\tint err = 0;\n\n\tmb = mbuf_alloc(str_len(user) + str_len(passwd) +\n\t\tstr_len(req->realm) + 2);\n\thash1 = mem_zalloc(resp->hash_length, NULL);\n\thash2 = mem_zalloc(resp->hash_length, NULL);\n\tif (!mb || !hash1 || !hash2) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\t/* HASH H2 */\n\tif (pl_strstr(&resp->qop, \"auth-int\")) {\n\t\tif (!str_isset(entitybody))\n\t\t\tresp->hashh((uint8_t *)\"\", str_len(\"\"), hash1);\n\t\telse\n\t\t\tresp->hashh((uint8_t *)entitybody, str_len(entitybody),\n\t\t\t\thash1);\n\n\t\terr = mbuf_printf(mb, \"%r:%r:%w\", method, &resp->uri, hash1,\n\t\t\tresp->hash_length);\n\t}\n\telse {\n\t\terr = mbuf_printf(mb, \"%r:%r\", method, &resp->uri);\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\tresp->hashh(mb->buf, mb->end, hash2);\n\tmbuf_rewind(mb);\n\n\t/* HASH H1 */\n\tif (pl_strcmp(&resp->username, user) != 0) {\n\t\terr = EACCES;\n\t\tgoto out;\n\t}\n\n\terr = mbuf_printf(mb, \"%s:%r:%s\", user, &resp->realm, passwd);\n\tif (err)\n\t\tgoto out;\n\n\tresp->hashh(mb->buf, mb->end, hash1);\n\tmbuf_rewind(mb);\n\n\tif (pl_strstr(&resp->algorithm, \"-sess\")) {\n\t\terr = mbuf_printf(mb, \"%w:%r:%r\",\n\t\t\thash1, resp->hash_length, &resp->nonce, &resp->cnonce);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tresp->hashh(mb->buf, mb->end, hash1);\n\t\tmbuf_rewind(mb);\n\t}\n\n\t/* DIGEST */\n\tif (pl_isset(&resp->qop)) {\n\t\terr = mbuf_printf(mb, \"%w:%r:%r:%r:%r:%w\", hash1,\n\t\t\tresp->hash_length, &resp->nonce, &resp->nc,\n\t\t\t&resp->cnonce, &resp->qop, hash2, resp->hash_length);\n\t}\n\telse {\n\t\terr = mbuf_printf(mb, \"%w:%r:%w\", hash1, resp->hash_length,\n\t\t\t&resp->nonce, hash2, resp->hash_length);\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\tresp->hashh(mb->buf, mb->end, hash1);\n\tmbuf_rewind(mb);\n\n\t/* VERIFICATION */\n\terr = pl_hex(&resp->response, hash2, resp->hash_length);\n\tif (err)\n\t\tgoto out;\n\n\terr = mem_seccmp(hash1, hash2, resp->hash_length) == 0 ? 0 : EACCES;\n\nout:\n\tmem_deref(hash1);\n\tmem_deref(hash2);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nint httpauth_digest_verify(struct httpauth_digest_chall_req *req,\n\tconst struct pl *hval, const struct pl *method, const char *etag,\n\tconst char *user, const char *passwd, const char *entitybody)\n{\n\tstruct httpauth_digest_resp resp;\n\tint err = 0;\n\n\tif (!req || !hval || !method || !user || !passwd)\n\t\treturn EINVAL;\n\n\terr = httpauth_digest_response_decode(&resp, hval);\n\tif (err)\n\t\treturn err;\n\n\tif (pl_strcasecmp(&resp.realm, req->realm))\n\t\treturn EINVAL;\n\n\terr = check_nonce(req->nonce, &resp.nonce, etag);\n\tif (err == ETIMEDOUT || err == EAUTH) {\n\t\treq->stale = true;\n\t\treturn EAUTH;\n\t}\n\telse if (err) {\n\t\treturn err;\n\t}\n\n\treturn digest_verify(req, &resp, method, user, passwd, entitybody);\n}\n\n\n/**\n * Prints / encodes an HTTP digest request challenge\n *\n * @param pf  Re_printf object\n * @param req Request to print\n *\n * @return 0 if success, otherwise errorcode\n */\nint httpauth_digest_chall_req_print(struct re_printf *pf,\n\tconst struct httpauth_digest_chall_req *req)\n{\n\tint err = 0;\n\n\tif (!req)\n\t\treturn EINVAL;\n\n\t/* historical reason quoted strings:   */\n\t/*   realm, domain, nonce, opaque, qop */\n\t/* historical reason unquoted strings: */\n\t/*   stale, algorithm                  */\n\terr = re_hprintf(pf, \"Digest realm=\\\"%s\\\", \"\n\t\t\"qop=\\\"%s\\\", nonce=\\\"%s\\\", algorithm=%s\",\n\t\treq->realm, req->qop, req->nonce, req->algorithm);\n\n\tif (str_isset(req->opaque))\n\t\terr |= re_hprintf(pf, \", opaque=\\\"%s\\\"\", req->opaque);\n\tif (str_isset(req->domain))\n\t\terr |= re_hprintf(pf, \", domain=\\\"%s\\\"\", req->domain);\n\tif (req->stale)\n\t\terr |= re_hprintf(pf, \", stale=true\");\n\tif (str_isset(req->charset))\n\t\terr |= re_hprintf(pf, \", charset=\\\"%s\\\"\", req->charset);\n\tif (req->userhash)\n\t\terr |= re_hprintf(pf, \", userhash=true\");\n\n\treturn err;\n}\n\n\n/**\n * Create a digest authentication request\n *\n * @param preq  Httpauth_digest_chall_req object ptr\n * @param realm Realm\n * @param etag  Changing data for nonce creation\n *              (HTTP ETag header / SIP msg src address)\n * @param qop   Quality of protection\n *\n * @return 0 if success, otherwise errorcode\n */\nint httpauth_digest_chall_request(struct httpauth_digest_chall_req **preq,\n\tconst char *realm, const char *etag, const char *qop)\n{\n\treturn httpauth_digest_chall_request_full(preq, realm, NULL, etag,\n\t\tNULL, false, NULL, qop, NULL, false);\n}\n\n\n/**\n * Create a full configurable digest authentication request\n *\n * @param preq      Httpauth_digest_chall_req object ptr\n * @param realm     Realm\n * @param domain    Domain (not used in SIP)\n * @param etag      Changing data for nonce creation\n *                  (HTTP ETag header / SIP msg src address)\n * @param opaque    Opaque\n * @param stale     Stale\n * @param algo      Supported algorithm (MD5, SHA1, SHA256 and sess versions)\n * @param qop       Quality of protection\n * @param charset   Character set used (not used in SIP)\n * @param userhash  Userhash support (not used in SIP)\n *\n * @return 0 if success, otherwise errorcode\n */\nint httpauth_digest_chall_request_full(struct httpauth_digest_chall_req **preq,\n\tconst char *realm, const char *domain, const char *etag,\n\tconst char *opaque, const bool stale, const char *algo,\n\tconst char *qop, const char *charset, const bool userhash)\n{\n\tstruct httpauth_digest_chall_req *req = NULL;\n\tint err = 0;\n\n\tif (!preq || !realm || !etag || !qop)\n\t\treturn EINVAL;\n\n\treq = mem_zalloc(sizeof(*req), httpauth_digest_chall_req_destructor);\n\tif (!req)\n\t\treturn ENOMEM;\n\n\treq->stale    = stale;\n\treq->userhash = userhash;\n\terr  = str_dup(&req->realm, realm);\n\terr |= str_dup(&req->qop, qop);\n\n\tif (str_isset(algo))\n\t\terr |= str_dup(&req->algorithm, algo);\n\telse\n\t\terr |= str_dup(&req->algorithm, \"MD5\");\n\n\tif (str_isset(domain))\n\t\terr |= str_dup(&req->domain, domain);\n\tif (str_isset(opaque))\n\t\terr |= str_dup(&req->opaque, opaque);\n\tif (str_isset(charset) && str_casecmp(charset, \"UTF-8\") == 0)\n\t\terr |= str_dup(&req->charset, charset);\n\n\tif (err)\n\t\tgoto out;\n\n\terr = generate_nonce(&req->nonce, time(NULL), etag, NULL);\n\nout:\n\tif (err)\n\t\tmem_deref(req);\n\telse\n\t\t*preq = req;\n\n\treturn err;\n}\n\n\nstatic void httpauth_digest_response_destructor(void *arg)\n{\n\tstruct httpauth_digest_enc_resp *resp = arg;\n\n\tmem_deref(resp->realm);\n\tmem_deref(resp->nonce);\n\tmem_deref(resp->opaque);\n\tmem_deref(resp->algorithm);\n\tmem_deref(resp->qop);\n\tmem_deref(resp->response);\n\tmem_deref(resp->username);\n\tmem_deref(resp->username_star);\n\tmem_deref(resp->uri);\n\tmem_deref(resp->charset);\n}\n\n\nstatic int digest_response(struct httpauth_digest_enc_resp *resp,\n\tconst struct httpauth_digest_chall *chall,\n\tconst struct pl *method, const char *user,\n\tconst char *passwd, const char *entitybody)\n{\n\tuint8_t *hash1 = NULL;\n\tuint8_t *hash2 = NULL;\n\tstruct mbuf *mb = NULL;\n\tint err = 0, n = 0;\n\n\tif (!resp || !resp->hashh)\n\t\treturn EINVAL;\n\n\tsize_t hashstringl = (resp->hash_length * 2) + 1;\n\n\tmb = mbuf_alloc(str_len(user) + str_len(passwd) + chall->realm.l + 2);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\thash1 = mem_zalloc(resp->hash_length, NULL);\n\thash2 = mem_zalloc(resp->hash_length, NULL);\n\tif (!resp->response)\n\t\tresp->response = mem_zalloc(hashstringl, NULL);\n\n\tif (!resp->response || !hash1 || !hash2) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\t/* HASH A2 */\n\tif (str_isset(resp->qop) && str_str(resp->qop, \"auth-int\")) {\n\t\tif (!entitybody || str_casecmp(entitybody, \"\") == 0) {\n\t\t\tresp->hashh((uint8_t *)\"\", 0, hash1);\n\t\t}\n\t\telse {\n\t\t\tresp->hashh((uint8_t *)entitybody,\n\t\t\t\tstr_len(entitybody), hash1);\n\t\t}\n\n\t\terr = mbuf_printf(mb, \"%r:%s:%w\",\n\t\t\tmethod, resp->uri, hash1, resp->hash_length);\n\t}\n\telse {\n\t\terr = mbuf_printf(mb, \"%r:%s\", method, resp->uri);\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\tresp->hashh(mb->buf, mb->end, hash2);\n\tmbuf_rewind(mb);\n\n\t/* HASH A1 */\n\tif (resp->userhash) {\n\t\tif (!resp->username)\n\t\t\tresp->username = mem_zalloc(hashstringl, NULL);\n\n\t\tif (!resp->username) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = mbuf_printf(mb, \"%s:%s\", user, resp->realm);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tresp->hashh(mb->buf, mb->end, hash1);\n\t\tn = re_snprintf(resp->username, hashstringl, \"%w\",\n\t\t\thash1, hashstringl);\n\t\tif (n == -1 || n != (int)hashstringl -1) {\n\t\t\terr = ERANGE;\n\t\t\tgoto out;\n\t\t}\n\n\t\tmbuf_rewind(mb);\n\t\terr = mbuf_printf(mb, \"%w:%s:%s\",\n\t\t\thash1, resp->hash_length, resp->realm, passwd);\n\t}\n\telse {\n\t\terr  = mbuf_printf(mb, \"%s:%s:%s\", user, resp->realm, passwd);\n\t\tresp->username = mem_deref(resp->username);\n\t\terr |= str_dup(&resp->username, user);\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\tresp->hashh(mb->buf, mb->end, hash1);\n\tmbuf_rewind(mb);\n\n\tif (str_str(resp->algorithm, \"-sess\")) {\n\t\terr = mbuf_printf(mb, \"%w:%s:%08x\",\n\t\t\thash1, resp->hash_length, resp->nonce, resp->cnonce);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tresp->hashh(mb->buf, mb->end, hash1);\n\t\tmbuf_rewind(mb);\n\t}\n\n\t/* DIGEST */\n\tif (str_isset(resp->qop)) {\n\t\terr = mbuf_printf(mb, \"%w:%s:%08x:%08x:%s:%w\",\n\t\t\thash1, resp->hash_length, resp->nonce, resp->nc,\n\t\t\tresp->cnonce, resp->qop, hash2, resp->hash_length);\n\t}\n\telse {\n\t\terr = mbuf_printf(mb, \"%w:%s:%w\", hash1, resp->hash_length,\n\t\t\tresp->nonce, hash2, resp->hash_length);\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\tresp->hashh(mb->buf, mb->end, hash1);\n\tn = re_snprintf(resp->response, hashstringl, \"%w\",\n\t\thash1, resp->hash_length);\n\tif (n == -1 || n != (int)hashstringl - 1)\n\t\terr = ERANGE;\n\nout:\n\tmem_deref(mb);\n\tmem_deref(hash1);\n\tmem_deref(hash2);\n\n\treturn err;\n}\n\n\n/**\n * Prints / encodes an HTTP digest response\n *\n * @param pf   Re_printf object\n * @param resp Response to print\n *\n * @return 0 if success, otherwise errorcode\n */\nint httpauth_digest_response_print(struct re_printf *pf,\n\tconst struct httpauth_digest_enc_resp *resp)\n{\n\tint err = 0;\n\n\tif (!resp)\n\t\treturn EINVAL;\n\n\t/* historical reason quoted strings:   */\n\t/*   username, realm, nonce, uri,      */\n\t/*   response, cnonce, opaque          */\n\t/* historical reason unquoted strings: */\n\t/*   qop, algorithm, nc                */\n\terr = re_hprintf(pf, \"Digest realm=\\\"%s\\\",\"\n\t\t\" nonce=\\\"%s\\\", username=\\\"%s\\\", uri=\\\"%s\\\",\"\n\t\t\" response=\\\"%s\\\"\",\n\t\tresp->realm, resp->nonce, resp->username,\n\t\tresp->uri, resp->response);\n\n\tif (str_isset(resp->opaque))\n\t\terr |= re_hprintf(pf, \", opaque=\\\"%s\\\"\", resp->opaque);\n\tif (str_isset(resp->algorithm))\n\t\terr |= re_hprintf(pf, \", algorithm=%s\", resp->algorithm);\n\tif (str_isset(resp->qop))\n\t\terr |= re_hprintf(pf, \", qop=%s, cnonce=\\\"%08x\\\", nc=\\\"%08x\\\"\",\n\t\t\tresp->qop, resp->cnonce, resp->nc);\n\n\tif (resp->userhash)\n\t\terr |= re_hprintf(pf, \", userhash=true\");\n\tif (str_isset(resp->charset))\n\t\terr |= re_hprintf(pf, \", charset=\\\"%s\\\"\", resp->charset);\n\n\treturn err;\n}\n\n\n/**\n * Set cnonce and nc and recalculate the response value.\n * This function should be used only for unit tests\n *\n * @param resp          Httpauth_new_digest_response object pointer\n * @param chall         Received and decoded digest challenge\n * @param method        Used method\n * @param user          Username\n * @param passwd        User password\n * @param entitybody    Entitybody if qop=auth-int\n * @param cnonce        Cnonce\n * @param nonce_cnt     Nonce counter\n *\n * @return 0 if success, otherwise errorcode\n */\nint httpauth_digest_response_set_cnonce(struct httpauth_digest_enc_resp *resp,\n\tconst struct httpauth_digest_chall *chall, const struct pl *method,\n\tconst char *user,\tconst char *passwd, const char *entitybody,\n\tuint32_t cnonce, uint32_t nonce_cnt)\n{\n\tif (!resp || !chall || !method || !passwd)\n\t\treturn EINVAL;\n\n\tresp->cnonce = cnonce;\n\tresp->nc = nonce_cnt;\n\n\treturn digest_response(resp, chall, method,\n\t\tuser, passwd, entitybody);\n}\n\n\n/**\n * Create a digest authentication response\n *\n * @param presp      Httpauth_new_digest_response object pointer\n * @param chall      Received and decoded digest challenge\n * @param method     Used method\n * @param uri        Accessed uri\n * @param user       Username\n * @param passwd     User password\n * @param qop        Quality of protection\n * @param entitybody Entitybody if qop=auth-int\n *\n * @return 0 if success, otherwise errorcode\n */\nint httpauth_digest_response(struct httpauth_digest_enc_resp **presp,\n\tconst struct httpauth_digest_chall *chall, const struct pl *method,\n\tconst char *uri, const char *user, const char *passwd, const char *qop,\n\tconst char *entitybody)\n{\n\treturn httpauth_digest_response_full(presp, chall, method, uri,\n\t\tuser, passwd, qop, entitybody, NULL, false);\n}\n\n\n/**\n * Create a full configurable digest authentication response\n *\n * @param presp      Httpauth_new_digest_response object pointer\n * @param chall      Received and decoded digest challenge\n * @param method     Used method\n * @param uri        Accessed uri\n * @param user       Username\n * @param passwd     User password\n * @param qop        Quality of protection\n * @param entitybody Entitybody if qop=auth-int\n * @param charset    Used character set (only UTF-8 or NULL allowed)\n * @param userhash   Enable hashed usernames\n *\n * @return 0 if success, otherwise errorcode\n */\nint httpauth_digest_response_full(struct httpauth_digest_enc_resp **presp,\n\tconst struct httpauth_digest_chall *chall, const struct pl *method,\n\tconst char *uri, const char *user, const char *passwd, const char *qop,\n\tconst char *entitybody, const char *charset, const bool userhash)\n{\n\tstruct httpauth_digest_enc_resp *resp = NULL;\n\tint err = 0;\n\n\tif (!presp || !chall || !method || !uri || !user || !passwd)\n\t\treturn EINVAL;\n\n\tresp = mem_zalloc(sizeof(*resp), httpauth_digest_response_destructor);\n\tif (!resp) {\n\t\treturn ENOMEM;\n\t}\n\n\t/* create cnonce & nonce count */\n\tresp->cnonce = rand_u32();\n\tresp->nc = (uint32_t) re_atomic_rlx_add(&nc, 1);\n\n\t/* copy fields */\n\terr = pl_strdup(&resp->realm, &chall->realm);\n\terr |= pl_strdup(&resp->nonce, &chall->nonce);\n\terr |= pl_strdup(&resp->opaque, &chall->opaque);\n\tif (err) {\n\t\tgoto out;\n\t}\n\n\t/* userhash supported by server */\n\tif (userhash && (pl_strcasecmp(&chall->userhash, \"true\") == 0))\n\t\tresp->userhash = true;\n\n\t/* only allowed qop Nothing, \"auth\" or \"auth-int\" */\n\tif (str_isset(qop) && (str_casecmp(qop, \"auth\")) &&\n\t\t(str_casecmp(qop, \"auth-int\"))) {\n\t\terr = EPROTONOSUPPORT;\n\t\tgoto out;\n\t}\n\n\t/* qop supported by server */\n\tif (pl_isset(&chall->qop) && str_isset(qop) &&\n\t\tpl_strstr(&chall->qop, qop)) {\n\t\terr = str_dup(&resp->qop, qop);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\t/* only allowed charset Nothing or \"UTF-8\" */\n\tif (str_isset(charset) && str_casecmp(charset, \"UTF-8\")) {\n\t\terr = EPROTONOSUPPORT;\n\t\tgoto out;\n\t}\n\n\t/* charset supported by server */\n\tif (pl_isset(&chall->charset) && str_isset(charset) &&\n\t\tpl_strstr(&chall->charset, charset) == 0) {\n\t\terr = str_dup(&resp->charset, charset);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = str_dup(&resp->uri, uri);\n\tif (err)\n\t\tgoto out;\n\n\tif (pl_strstr(&chall->algorithm, \"SHA-256-sess\")) {\n\t\tresp->hashh = &sha256;\n\t\tresp->hash_length = SHA256_DIGEST_LENGTH;\n\t\terr = str_dup(&resp->algorithm, \"SHA-256-sess\");\n\t}\n\telse if (pl_strstr(&chall->algorithm, \"SHA-256\")) {\n\t\tresp->hashh = &sha256;\n\t\tresp->hash_length = SHA256_DIGEST_LENGTH;\n\t\terr = str_dup(&resp->algorithm, \"SHA-256\");\n\t}\n\telse if (pl_strstr(&chall->algorithm, \"MD5-sess\")) {\n\t\tresp->hashh = &md5;\n\t\tresp->hash_length = MD5_SIZE;\n\t\terr = str_dup(&resp->algorithm, \"MD5-sess\");\n\t}\n\telse if (!pl_isset(&chall->algorithm) ||\n\t\tpl_strstr(&chall->algorithm, \"MD5\")) {\n\t\tresp->hashh = &md5;\n\t\tresp->hash_length = MD5_SIZE;\n\t\terr = str_dup(&resp->algorithm, \"MD5\");\n\t}\n\telse {\n\t\terr = EPROTONOSUPPORT;\n\t\tgoto out;\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\terr = digest_response(resp, chall, method, user, passwd, entitybody);\n\nout:\n\tif (err)\n\t\tmem_deref(resp);\n\telse\n\t\t*presp = resp;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/ice/cand.c",
    "content": "/**\n * @file cand.c  ICE Candidates\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_sys.h>\n#include <re_stun.h>\n#include <re_turn.h>\n#include <re_ice.h>\n#include \"ice.h\"\n\n\n#define DEBUG_MODULE \"icecand\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic void cand_destructor(void *arg)\n{\n\tstruct ice_cand *cand = arg;\n\n\tlist_unlink(&cand->le);\n\tmem_deref(cand->foundation);\n\tmem_deref(cand->ifname);\n\n\tif (cand != cand->base)\n\t\tmem_deref(cand->base);\n}\n\n\n/** Foundation is a hash of IP address and candidate type */\nstatic int compute_foundation(struct ice_cand *cand)\n{\n\tuint32_t v;\n\n\tv  = sa_hash(&cand->addr, SA_ADDR);\n\tv ^= cand->type;\n\n\treturn re_sdprintf(&cand->foundation, \"%08x\", v);\n}\n\n\nstatic int cand_alloc(struct ice_cand **candp, struct icem *icem,\n\t\t      enum ice_cand_type type, unsigned compid,\n\t\t      uint32_t prio, const char *ifname,\n\t\t      enum ice_transp transp, const struct sa *addr)\n{\n\tstruct ice_cand *cand;\n\tint err;\n\n\tif (!icem)\n\t\treturn EINVAL;\n\n\tcand = mem_zalloc(sizeof(*cand), cand_destructor);\n\tif (!cand)\n\t\treturn ENOMEM;\n\n\tlist_append(&icem->lcandl, &cand->le, cand);\n\n\tcand->type   = type;\n\tcand->compid = compid;\n\tcand->prio   = prio;\n\tcand->transp = transp;\n\n\tsa_cpy(&cand->addr, addr);\n\n\terr = compute_foundation(cand);\n\n\tif (ifname)\n\t\terr |= str_dup(&cand->ifname, ifname);\n\n\tif (err)\n\t\tmem_deref(cand);\n\telse if (candp)\n\t\t*candp = cand;\n\n\treturn err;\n}\n\n\nint icem_lcand_add_base(struct icem *icem, enum ice_cand_type type,\n\t\t\tunsigned compid, uint16_t lprio, const char *ifname,\n\t\t\tenum ice_transp transp, const struct sa *addr)\n{\n\tstruct icem_comp *comp;\n\tstruct ice_cand *cand;\n\tint err;\n\n\tif (icem->conf.policy == ICE_POLICY_RELAY &&\n\t    type != ICE_CAND_TYPE_RELAY)\n\t\treturn 0;\n\n\tif (type != ICE_CAND_TYPE_HOST && type != ICE_CAND_TYPE_RELAY)\n\t\treturn EINVAL;\n\n\tcomp = icem_comp_find(icem, compid);\n\tif (!comp)\n\t\treturn ENOENT;\n\n\terr = cand_alloc(&cand, icem, type, compid,\n\t\t\t ice_cand_calc_prio(type, lprio, compid),\n\t\t\t ifname, transp, addr);\n\tif (err)\n\t\treturn err;\n\n\t/* the base is itself */\n\tcand->base = cand;\n\n\tif (type == ICE_CAND_TYPE_RELAY)\n\t\tsa_cpy(&cand->rel, addr);\n\n\tif (type == ICE_CAND_TYPE_HOST)\n\t\tsa_set_port(&cand->addr, comp->lport);\n\n\treturn 0;\n}\n\n\nint icem_lcand_add(struct icem *icem, struct ice_cand *base,\n\t\t   enum ice_cand_type type,\n\t\t   const struct sa *addr)\n{\n\tstruct ice_cand *cand;\n\tint err;\n\n\tif (icem->conf.policy == ICE_POLICY_RELAY)\n\t\treturn 0;\n\n\tif (!base)\n\t\treturn EINVAL;\n\n\tif (type == ICE_CAND_TYPE_HOST || type == ICE_CAND_TYPE_RELAY)\n\t\treturn EINVAL;\n\n\terr = cand_alloc(&cand, icem, type, base->compid,\n\t\t\t ice_cand_calc_prio(type, 0, base->compid),\n\t\t\t base->ifname, base->transp, addr);\n\tif (err)\n\t\treturn err;\n\n\tcand->base = mem_ref(base);\n\tsa_cpy(&cand->rel, &base->addr);\n\n\treturn 0;\n}\n\n\nint icem_rcand_add(struct icem *icem, enum ice_cand_type type, unsigned compid,\n\t\t   uint32_t prio, const struct sa *addr,\n\t\t   const struct sa *rel_addr, const struct pl *foundation)\n{\n\tstruct ice_cand *rcand;\n\tint err;\n\n\tif (!icem || !foundation)\n\t\treturn EINVAL;\n\n\trcand = mem_zalloc(sizeof(*rcand), cand_destructor);\n\tif (!rcand)\n\t\treturn ENOMEM;\n\n\tlist_append(&icem->rcandl, &rcand->le, rcand);\n\n\trcand->type   = type;\n\trcand->compid = compid;\n\trcand->prio   = prio;\n\n\tsa_cpy(&rcand->addr, addr);\n\tsa_cpy(&rcand->rel, rel_addr);\n\n\terr = pl_strdup(&rcand->foundation, foundation);\n\n\tif (err)\n\t\tmem_deref(rcand);\n\n\treturn err;\n}\n\n\nint icem_rcand_add_prflx(struct ice_cand **rcp, struct icem *icem,\n\t\t\t unsigned compid, uint32_t prio,\n\t\t\t const struct sa *addr)\n{\n\tstruct ice_cand *rcand;\n\tint err;\n\n\tif (!icem || !addr)\n\t\treturn EINVAL;\n\n\trcand = mem_zalloc(sizeof(*rcand), cand_destructor);\n\tif (!rcand)\n\t\treturn ENOMEM;\n\n\tlist_append(&icem->rcandl, &rcand->le, rcand);\n\n\trcand->type   = ICE_CAND_TYPE_PRFLX;\n\trcand->compid = compid;\n\trcand->prio   = prio;\n\trcand->addr   = *addr;\n\n\terr = re_sdprintf(&rcand->foundation, \"%08x\", rand_u32());\n\tif (err)\n\t\tgoto out;\n\n\ticecomp_printf(icem_comp_find(icem, compid),\n\t\t       \"added PeerReflexive remote candidate\"\n\t\t       \" with priority %u (%J)\\n\", prio, addr);\n\n out:\n\tif (err)\n\t\tmem_deref(rcand);\n\telse if (rcp)\n\t\t*rcp = rcand;\n\n\treturn err;\n}\n\n\nstruct ice_cand *icem_cand_find(const struct list *lst, unsigned compid,\n\t\t\t\tconst struct sa *addr)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\n\t\tstruct ice_cand *cand = le->data;\n\n\t\tif (compid && cand->compid != compid)\n\t\t\tcontinue;\n\n\t\tif (addr && !sa_cmp(&cand->addr, addr, SA_ALL))\n\t\t\tcontinue;\n\n\t\treturn cand;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Find the highest priority LCAND on the check-list of type HOST/RELAY\n *\n * @param icem    ICE Media object\n * @param compid  Component ID\n *\n * @return Local candidate if found, otherwise NULL\n */\nstruct ice_cand *icem_lcand_find_checklist(const struct icem *icem,\n\t\t\t\t\t   unsigned compid)\n{\n\tstruct le *le;\n\n\tfor (le = icem->checkl.head; le; le = le->next) {\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tif (cp->lcand->compid != compid)\n\t\t\tcontinue;\n\n\t\tswitch (cp->lcand->type) {\n\n\t\tcase ICE_CAND_TYPE_HOST:\n\t\tcase ICE_CAND_TYPE_RELAY:\n\t\t\treturn cp->lcand;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\nstruct ice_cand *icem_lcand_base(struct ice_cand *lcand)\n{\n\treturn lcand ? lcand->base : NULL;\n}\n\n\nconst struct sa *icem_lcand_addr(const struct ice_cand *cand)\n{\n\treturn cand ? &cand->addr : NULL;\n}\n\n\nint icem_cands_debug(struct re_printf *pf, const struct list *lst)\n{\n\tstruct le *le;\n\tint err;\n\n\terr = re_hprintf(pf, \" (%u)\\n\", list_count(lst));\n\n\tfor (le = list_head(lst); le && !err; le = le->next) {\n\n\t\tconst struct ice_cand *cand = le->data;\n\n\t\terr |= re_hprintf(pf, \"  {%u} fnd=%-2s prio=%08x %24H\",\n\t\t\t\t  cand->compid, cand->foundation, cand->prio,\n\t\t\t\t  icem_cand_print, cand);\n\n\t\tif (sa_isset(&cand->rel, SA_ADDR))\n\t\t\terr |= re_hprintf(pf, \" (rel-addr=%J)\", &cand->rel);\n\n\t\terr |= re_hprintf(pf, \"\\n\");\n\t}\n\n\treturn err;\n}\n\n\nint icem_cand_print(struct re_printf *pf, const struct ice_cand *cand)\n{\n\tint err = 0;\n\n\tif (!cand)\n\t\treturn 0;\n\n\tif (cand->ifname)\n\t\terr |= re_hprintf(pf, \"%s:\", cand->ifname);\n\n\terr |= re_hprintf(pf, \"%s:%J\",\n\t\t\t  ice_cand_type2name(cand->type), &cand->addr);\n\n\treturn err;\n}\n\nenum ice_cand_type icem_cand_type(const struct ice_cand *cand)\n{\n\treturn cand ? cand->type : (enum ice_cand_type)-1;\n}\n"
  },
  {
    "path": "src/ice/candpair.c",
    "content": "/**\n * @file candpair.c  ICE Candidate Pairs\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include \"ice.h\"\n\n\n#define DEBUG_MODULE \"cndpair\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic void candpair_destructor(void *arg)\n{\n\tstruct ice_candpair *cp = arg;\n\n\tlist_unlink(&cp->le);\n\tmem_deref(cp->ct_conn);\n\tmem_deref(cp->lcand);\n\tmem_deref(cp->rcand);\n}\n\n\nstatic bool sort_handler(struct le *le1, struct le *le2, void *arg)\n{\n\tconst struct ice_candpair *cp1 = le1->data, *cp2 = le2->data;\n\t(void)arg;\n\n\treturn cp1->pprio >= cp2->pprio;\n}\n\n\nstatic void candpair_set_pprio(struct ice_candpair *cp)\n{\n\tuint32_t g, d;\n\n\tif (ICE_ROLE_CONTROLLING == cp->icem->lrole) {\n\t\tg = cp->lcand->prio;\n\t\td = cp->rcand->prio;\n\t}\n\telse {\n\t\tg = cp->rcand->prio;\n\t\td = cp->lcand->prio;\n\t}\n\n\tcp->pprio = ice_calc_pair_prio(g, d);\n}\n\n\n/**\n * Add candidate pair to list, sorted by pair priority (highest is first)\n */\nstatic void list_add_sorted(struct list *list, struct ice_candpair *cp)\n{\n\tstruct le *le;\n\n\t/* find our slot */\n\tfor (le = list_tail(list); le; le = le->prev) {\n\t\tstruct ice_candpair *cp0 = le->data;\n\n\t\tif (cp->pprio < cp0->pprio) {\n\t\t\tlist_insert_after(list, le, &cp->le, cp);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tlist_prepend(list, &cp->le, cp);\n}\n\n\nint icem_candpair_alloc(struct ice_candpair **cpp, struct icem *icem,\n\t\t\tstruct ice_cand *lcand, struct ice_cand *rcand)\n{\n\tstruct ice_candpair *cp;\n\tstruct icem_comp *comp;\n\n\tif (!icem || !lcand || !rcand)\n\t\treturn EINVAL;\n\n\tcomp = icem_comp_find(icem, lcand->compid);\n\tif (!comp)\n\t\treturn ENOENT;\n\n\tcp = mem_zalloc(sizeof(*cp), candpair_destructor);\n\tif (!cp)\n\t\treturn ENOMEM;\n\n\tcp->icem  = icem;\n\tcp->comp  = comp;\n\tcp->lcand = mem_ref(lcand);\n\tcp->rcand = mem_ref(rcand);\n\tcp->state = ICE_CANDPAIR_FROZEN;\n\tcp->def   = comp->def_lcand == lcand && comp->def_rcand == rcand;\n\n\tcandpair_set_pprio(cp);\n\n\tlist_add_sorted(&icem->checkl, cp);\n\n\tif (cpp)\n\t\t*cpp = cp;\n\n\treturn 0;\n}\n\n\nint icem_candpair_clone(struct ice_candpair **cpp, struct ice_candpair *cp0,\n\t\t\tstruct ice_cand *lcand, struct ice_cand *rcand)\n{\n\tstruct ice_candpair *cp;\n\n\tif (!cp0)\n\t\treturn EINVAL;\n\n\tcp = mem_zalloc(sizeof(*cp), candpair_destructor);\n\tif (!cp)\n\t\treturn ENOMEM;\n\n\tcp->icem      = cp0->icem;\n\tcp->comp      = cp0->comp;\n\tcp->lcand     = mem_ref(lcand ? lcand : cp0->lcand);\n\tcp->rcand     = mem_ref(rcand ? rcand : cp0->rcand);\n\tcp->def       = cp0->def;\n\tcp->valid     = cp0->valid;\n\tcp->nominated = cp0->nominated;\n\tcp->state     = cp0->state;\n\tcp->pprio     = cp0->pprio;\n\tcp->err       = cp0->err;\n\tcp->scode     = cp0->scode;\n\n\tlist_add_sorted(&cp0->icem->checkl, cp);\n\n\tif (cpp)\n\t\t*cpp = cp;\n\n\treturn 0;\n}\n\n\n/**\n * Computing Pair Priority and Ordering Pairs\n *\n * @param lst Checklist (struct ice_candpair)\n */\nvoid icem_candpair_prio_order(struct list *lst)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tcandpair_set_pprio(cp);\n\t}\n\n\tlist_sort(lst, sort_handler, NULL);\n}\n\n\n/* cancel transaction */\nvoid icem_candpair_cancel(struct ice_candpair *cp)\n{\n\tif (!cp)\n\t\treturn;\n\n\tcp->ct_conn = mem_deref(cp->ct_conn);\n}\n\n\nvoid icem_candpair_make_valid(struct ice_candpair *cp)\n{\n\tif (!cp)\n\t\treturn;\n\n\tcp->err = 0;\n\tcp->scode = 0;\n\tcp->valid = true;\n\n\ticem_candpair_set_state(cp, ICE_CANDPAIR_SUCCEEDED);\n\n\tlist_unlink(&cp->le);\n\tlist_add_sorted(&cp->icem->validl, cp);\n}\n\n\nvoid icem_candpair_failed(struct ice_candpair *cp, int err, uint16_t scode)\n{\n\tif (!cp)\n\t\treturn;\n\n\tcp->err = err;\n\tcp->scode = scode;\n\tcp->valid = false;\n\n\ticem_candpair_set_state(cp, ICE_CANDPAIR_FAILED);\n}\n\n\nvoid icem_candpair_set_state(struct ice_candpair *cp,\n\t\t\t     enum ice_candpair_state state)\n{\n\tif (!cp)\n\t\treturn;\n\tif (cp->state == state || icem_candpair_iscompleted(cp))\n\t\treturn;\n\n\ticecomp_printf(cp->comp,\n\t\t       \"%5s <---> %5s  FSM:  %10s ===> %-10s\\n\",\n\t\t       ice_cand_type2name(cp->lcand->type),\n\t\t       ice_cand_type2name(cp->rcand->type),\n\t\t       ice_candpair_state2name(cp->state),\n\t\t       ice_candpair_state2name(state));\n\n\tcp->state = state;\n}\n\n\n/**\n * Delete all Candidate-Pairs where the Local candidate is of a given type\n *\n * @param lst     Checklist or Validlist\n * @param type    Candidate type\n * @param compid  Component ID\n */\nvoid icem_candpairs_flush(struct list *lst, enum ice_cand_type type,\n\t\t\t  unsigned compid)\n{\n\tstruct le *le = list_head(lst);\n\n\twhile (le) {\n\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tle = le->next;\n\n\t\tif (cp->lcand->compid != compid)\n\t\t\tcontinue;\n\n\t\tif (cp->lcand->type != type)\n\t\t\tcontinue;\n\n\t\tmem_deref(cp);\n\t}\n}\n\n\nbool icem_candpair_iscompleted(const struct ice_candpair *cp)\n{\n\tif (!cp)\n\t\treturn false;\n\n\treturn cp->state == ICE_CANDPAIR_FAILED ||\n\t\tcp->state == ICE_CANDPAIR_SUCCEEDED;\n}\n\n\n/**\n * Compare local and remote candidates of two candidate pairs\n *\n * @param cp1  First Candidate pair\n * @param cp2  Second Candidate pair\n *\n * @return true if match\n */\nbool icem_candpair_cmp(const struct ice_candpair *cp1,\n\t\t       const struct ice_candpair *cp2)\n{\n\tif (!sa_cmp(&cp1->lcand->addr, &cp2->lcand->addr, SA_ALL))\n\t\treturn false;\n\n\treturn sa_cmp(&cp1->rcand->addr, &cp2->rcand->addr, SA_ALL);\n}\n\n\n/**\n * Find the highest-priority candidate-pair in a given list, with\n * optional match parameters\n *\n * @param lst    List of candidate pairs\n * @param lcand  Local candidate (optional)\n * @param rcand  Remote candidate (optional)\n *\n * @return Matching candidate pair if found, otherwise NULL\n *\n * note: assume list is sorted by priority\n */\nstruct ice_candpair *icem_candpair_find(const struct list *lst,\n\t\t\t\t    const struct ice_cand *lcand,\n\t\t\t\t    const struct ice_cand *rcand)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tif (!cp->lcand || !cp->rcand) {\n\t\t\tDEBUG_WARNING(\"corrupt candpair %p\\n\", cp);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (lcand && cp->lcand != lcand)\n\t\t\tcontinue;\n\n\t\tif (rcand && cp->rcand != rcand)\n\t\t\tcontinue;\n\n\t\treturn cp;\n\t}\n\n\treturn NULL;\n}\n\n\nstruct ice_candpair *icem_candpair_find_st(const struct list *lst,\n\t\t\t\t\t   unsigned compid,\n\t\t\t\t\t   enum ice_candpair_state state)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tif (compid && cp->lcand->compid != compid)\n\t\t\tcontinue;\n\n\t\tif (cp->state != state)\n\t\t\tcontinue;\n\n\t\treturn cp;\n\t}\n\n\treturn NULL;\n}\n\n\nstruct ice_candpair *icem_candpair_find_compid(const struct list *lst,\n\t\t\t\t\t   unsigned compid)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tif (cp->lcand->compid != compid)\n\t\t\tcontinue;\n\n\t\treturn cp;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Find a remote candidate in the checklist or validlist\n *\n * @param icem    ICE Media object\n * @param rcand   Remote candidate\n *\n * @return Candidate pair if found, otherwise NULL\n */\nstruct ice_candpair *icem_candpair_find_rcand(struct icem *icem,\n\t\t\t\t\t  const struct ice_cand *rcand)\n{\n\tstruct ice_candpair *cp;\n\n\tcp = icem_candpair_find(&icem->checkl, NULL, rcand);\n\tif (cp)\n\t\treturn cp;\n\n\tcp = icem_candpair_find(&icem->validl, NULL, rcand);\n\tif (cp)\n\t\treturn cp;\n\n\treturn NULL;\n}\n\n\nbool icem_candpair_cmp_fnd(const struct ice_candpair *cp1,\n\t\t\t   const struct ice_candpair *cp2)\n{\n\tif (!cp1 || !cp2)\n\t\treturn false;\n\n\treturn 0 == strcmp(cp1->lcand->foundation, cp2->lcand->foundation) &&\n\t\t0 == strcmp(cp1->rcand->foundation, cp2->rcand->foundation);\n}\n\n\nint icem_candpair_debug(struct re_printf *pf, const struct ice_candpair *cp)\n{\n\tint err;\n\n\tif (!cp)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"{comp=%u} %10s {%c%c%c} %28H <---> %28H\",\n\t\t\t cp->lcand->compid,\n\t\t\t ice_candpair_state2name(cp->state),\n\t\t\t cp->def ? 'D' : ' ',\n\t\t\t cp->valid ? 'V' : ' ',\n\t\t\t cp->nominated ? 'N' : ' ',\n\t\t\t icem_cand_print, cp->lcand,\n\t\t\t icem_cand_print, cp->rcand);\n\n\tif (cp->err)\n\t\terr |= re_hprintf(pf, \" (%m)\", cp->err);\n\n\tif (cp->scode)\n\t\terr |= re_hprintf(pf, \" [%u]\", cp->scode);\n\n\treturn err;\n}\n\n\nint icem_candpairs_debug(struct re_printf *pf, const struct list *list)\n{\n\tstruct le *le;\n\tbool ansi_output = true;\n\tint err;\n\n\tif (!list)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \" (%u)\\n\", list_count(list));\n\n\tfor (le = list->head; le && !err; le = le->next) {\n\n\t\tconst struct ice_candpair *cp = le->data;\n\t\tbool is_selected = (cp == cp->comp->cp_sel);\n\t\tbool ansi = false;\n\n\t\tif (ansi_output) {\n\t\t\tif (cp->state == ICE_CANDPAIR_SUCCEEDED) {\n\t\t\t\terr |= re_hprintf(pf, \"\\x1b[32m\");\n\t\t\t\tansi = true;\n\t\t\t}\n\t\t\telse if (cp->err || cp->scode) {\n\t\t\t\terr |= re_hprintf(pf, \"\\x1b[31m\");\n\t\t\t\tansi = true;\n\t\t\t}\n\t\t}\n\n\t\terr |= re_hprintf(pf, \"  %c  %H\\n\",\n\t\t\t\t  is_selected ? '*' : ' ',\n\t\t\t\t  icem_candpair_debug, cp);\n\n\t\tif (ansi)\n\t\t\terr |= re_hprintf(pf, \"\\x1b[;m\");\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/ice/chklist.c",
    "content": "/**\n * @file chklist.c  ICE Checklist\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include \"ice.h\"\n\n\n#define DEBUG_MODULE \"ice\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/**\n * Forming Candidate Pairs\n */\nstatic int candpairs_form(struct icem *icem)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tif (list_isempty(&icem->lcandl))\n\t\treturn ENOENT;\n\n\tif (list_isempty(&icem->rcandl)) {\n\t\tDEBUG_WARNING(\"form: '%s' no remote candidates\\n\", icem->name);\n\t\treturn ENOENT;\n\t}\n\n\tfor (le = icem->lcandl.head; le; le = le->next) {\n\n\t\tstruct ice_cand *lcand = le->data;\n\t\tstruct le *rle;\n\n\t\tfor (rle = icem->rcandl.head; rle; rle = rle->next) {\n\n\t\t\tstruct ice_cand *rcand = rle->data;\n\n\t\t\tif (lcand->compid != rcand->compid)\n\t\t\t\tcontinue;\n\n\t\t\tif (sa_af(&lcand->addr) != sa_af(&rcand->addr))\n\t\t\t\tcontinue;\n\n\t\t\tif (icem_candpair_find(&icem->checkl, lcand, rcand))\n\t\t\t\tcontinue;\n\n\t\t\tif (icem_candpair_find(&icem->validl, lcand, rcand))\n\t\t\t\tcontinue;\n\n\t\t\terr = icem_candpair_alloc(NULL, icem, lcand, rcand);\n\t\t\tif (err)\n\t\t\t\treturn err;\n\t\t}\n\t}\n\n\treturn err;\n}\n\n\n/* Replace server reflexive candidates by its base */\nstatic const struct sa *cand_srflx_addr(const struct ice_cand *c)\n{\n\treturn (ICE_CAND_TYPE_SRFLX == c->type) ? &c->base->addr : &c->addr;\n}\n\n\n/* return: NULL to keep, pointer to remove object */\nstatic void *unique_handler(struct le *le1, struct le *le2)\n{\n\tstruct ice_candpair *cp1 = le1->data, *cp2 = le2->data;\n\n\tif (cp1->comp->id != cp2->comp->id)\n\t\treturn NULL;\n\n\tif (!sa_cmp(cand_srflx_addr(cp1->lcand),\n\t\t    cand_srflx_addr(cp2->lcand), SA_ALL) ||\n\t    !sa_cmp(&cp1->rcand->addr, &cp2->rcand->addr, SA_ALL))\n\t\treturn NULL;\n\n\treturn cp1->pprio < cp2->pprio ? cp1 : cp2;\n}\n\n\n/**\n * Pruning the Pairs\n */\nstatic void candpair_prune(struct icem *icem)\n{\n\t/* The agent MUST prune the list.\n\t   This is done by removing a pair if its local and remote\n\t   candidates are identical to the local and remote candidates\n\t   of a pair higher up on the priority list.\n\n\t   NOTE: This logic assumes the list is sorted by priority\n\t*/\n\n\tuint32_t n = ice_list_unique(&icem->checkl, unique_handler);\n\tif (n > 0) {\n\t\tDEBUG_INFO(\"%s: pruned candidate pairs: %u\\n\",\n\t\t\t     icem->name, n);\n\t}\n}\n\n\n/**\n * Computing States\n *\n * @param icem    ICE Media object\n */\nvoid ice_candpair_set_states(struct icem *icem)\n{\n\tstruct le *le, *le2;\n\n\t/*\n\tFor all pairs with the same foundation, it sets the state of\n\tthe pair with the lowest component ID to Waiting.  If there is\n\tmore than one such pair, the one with the highest priority is\n\tused.\n\t*/\n\n\tfor (le = icem->checkl.head; le; le = le->next) {\n\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tfor (le2 = icem->checkl.head; le2; le2 = le2->next) {\n\n\t\t\tstruct ice_candpair *cp2 = le2->data;\n\n\t\t\tif (!icem_candpair_cmp_fnd(cp, cp2))\n\t\t\t\tcontinue;\n\n\t\t\tif (cp2->lcand->compid < cp->lcand->compid &&\n\t\t\t    cp2->pprio > cp->pprio)\n\t\t\t\tcp = cp2;\n\t\t}\n\n\t\ticem_candpair_set_state(cp, ICE_CANDPAIR_WAITING);\n\t}\n}\n\n\n/**\n * Forming the Check Lists\n *\n *   To form the check list for a media stream,\n *   the agent forms candidate pairs, computes a candidate pair priority,\n *   orders the pairs by priority, prunes them, and sets their states.\n *   These steps are described in this section.\n *\n * @param icem ICE Media object\n *\n * @return 0 if success, otherwise errorcode\n */\nint icem_checklist_form(struct icem *icem)\n{\n\tint err;\n\n\tif (!icem)\n\t\treturn EINVAL;\n\n\t/* 1. form candidate pairs */\n\terr = candpairs_form(icem);\n\tif (err)\n\t\treturn err;\n\n\t/* 2. compute a candidate pair priority */\n\t/* 3. order the pairs by priority */\n\ticem_candpair_prio_order(&icem->checkl);\n\n\t/* 4. prune the pairs */\n\tcandpair_prune(icem);\n\n\treturn 0;\n}\n\n\n/* If all of the pairs in the check list are now either in the Failed or\n   Succeeded state:\n */\nstatic bool iscompleted(const struct icem *icem)\n{\n\tstruct le *le;\n\n\tfor (le = icem->checkl.head; le; le = le->next) {\n\n\t\tconst struct ice_candpair *cp = le->data;\n\n\t\tif (!icem_candpair_iscompleted(cp))\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/* 8.  Concluding ICE Processing */\nstatic void concluding_ice(struct icem_comp *comp)\n{\n\tstruct ice_candpair *cp;\n\tbool use_cand;\n\n\tif (!comp || comp->concluded)\n\t\treturn;\n\n\t/* pick the best candidate pair, highest priority */\n\tcp = icem_candpair_find_st(&comp->icem->validl, comp->id,\n\t\t\t\t   ICE_CANDPAIR_SUCCEEDED);\n\tif (!cp) {\n\t\tDEBUG_WARNING(\"{%s.%u} conclude: no valid candpair found\"\n\t\t\t      \" (validlist=%u)\\n\",\n\t\t\t      comp->icem->name, comp->id,\n\t\t\t      list_count(&comp->icem->validl));\n\t\treturn;\n\t}\n\n\ticem_comp_set_selected(comp, cp);\n\n\t/* Regular nomination */\n\n\tuse_cand = comp->icem->lrole == ICE_ROLE_CONTROLLING;\n\n\t/* send STUN request with USE_CAND flag via triggered queue */\n\t(void)icem_conncheck_send(cp, use_cand, true);\n\ticem_conncheck_schedule_check(comp->icem);\n\n\tcomp->concluded = true;\n}\n\n\n/**\n * Check List and Timer State Updates\n *\n * @param icem    ICE Media object\n */\nvoid icem_checklist_update(struct icem *icem)\n{\n\tstruct le *le;\n\tbool compl;\n\tint err = 0;\n\n\tcompl = iscompleted(icem);\n\tif (!compl)\n\t\treturn;\n\n\t/*\n\t * If there is not a pair in the valid list for each component of the\n\t * media stream, the state of the check list is set to Failed.\n\t */\n\tfor (le = icem->compl.head; le; le = le->next) {\n\n\t\tstruct icem_comp *comp = le->data;\n\n\t\tif (!icem_candpair_find_compid(&icem->validl, comp->id)) {\n\t\t\tDEBUG_WARNING(\"{%s.%u} checklist update:\"\n\t\t\t\t      \" no valid candidate pair\"\n\t\t\t\t      \" (validlist=%u)\\n\",\n\t\t\t\t      icem->name, comp->id,\n\t\t\t\t      list_count(&icem->validl));\n\t\t\terr = ENOENT;\n\t\t\tbreak;\n\t\t}\n\n\t\tconcluding_ice(comp);\n\n\t\tif (!comp->cp_sel)\n\t\t\tcontinue;\n\n\t\ticem_comp_keepalive(comp, true);\n\t}\n\n\ticem->state = err ? ICE_CHECKLIST_FAILED : ICE_CHECKLIST_COMPLETED;\n\n\tif (icem->chkh) {\n\t\ticem->chkh(err, icem->lrole == ICE_ROLE_CONTROLLING,\n\t\t\t   icem->arg);\n\t}\n}\n\n\n/**\n * Get the Local address of the Selected Candidate pair, if available\n *\n * @param icem   ICE Media object\n * @param compid Component ID\n *\n * @return Local address if available, otherwise NULL\n */\nconst struct sa *icem_selected_laddr(const struct icem *icem, unsigned compid)\n{\n\tconst struct ice_cand *cand = icem_selected_lcand(icem, compid);\n\treturn icem_lcand_addr(cand);\n}\n\n\n/**\n * Get the Local candidate of the Selected Candidate pair, if available\n *\n * @param icem   ICE Media object\n * @param compid Component ID\n *\n * @return Local candidate if available, otherwise NULL\n */\nconst struct ice_cand *icem_selected_lcand(const struct icem *icem,\n\t\tunsigned compid)\n{\n\tconst struct icem_comp *comp = icem_comp_find(icem, compid);\n\tif (!comp || !comp->cp_sel)\n\t\treturn NULL;\n\n\treturn comp->cp_sel->lcand;\n}\n\n\n/**\n * Get the Remote candidate of the Selected Candidate pair, if available\n *\n * @param icem   ICE Media object\n * @param compid Component ID\n *\n * @return Remote candidate if available, otherwise NULL\n */\nconst struct ice_cand *icem_selected_rcand(const struct icem *icem,\n\t\tunsigned compid)\n{\n\tconst struct icem_comp *comp = icem_comp_find(icem, compid);\n\tif (!comp || !comp->cp_sel)\n\t\treturn NULL;\n\n\treturn comp->cp_sel->rcand;\n}\n"
  },
  {
    "path": "src/ice/comp.c",
    "content": "/**\n * @file comp.c  ICE Media component\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sys.h>\n#include <re_sa.h>\n#include <re_udp.h>\n#include <re_stun.h>\n#include <re_turn.h>\n#include <re_ice.h>\n#include \"ice.h\"\n\n\n#define DEBUG_MODULE \"icecomp\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {COMPID_MIN = 1, COMPID_MAX = 255};\n\n\nstatic bool helper_recv_handler(struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct icem_comp *comp = arg;\n\tstruct icem *icem = comp->icem;\n\tstruct stun_msg *msg = NULL;\n\tstruct stun_unknown_attr ua;\n\tconst size_t start = mb->pos;\n\n#if 0\n\tre_printf(\"{%d} UDP recv_helper: %u bytes from %J\\n\",\n\t\t  comp->id, mbuf_get_left(mb), src);\n#endif\n\n\tif (stun_msg_decode(&msg, mb, &ua))\n\t\treturn false;\n\n\tif (STUN_METHOD_BINDING == stun_msg_method(msg)) {\n\n\t\tswitch (stun_msg_class(msg)) {\n\n\t\tcase STUN_CLASS_REQUEST:\n\t\t\t(void)icem_stund_recv(comp, src, msg, start);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\t(void)stun_ctrans_recv(icem->stun, msg, &ua);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tmem_deref(msg);\n\n\treturn true;  /* handled */\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct icem_comp *comp = arg;\n\n\ttmr_cancel(&comp->tmr_ka);\n\tmem_deref(comp->turnc);\n\tmem_deref(comp->cp_sel);\n\tmem_deref(comp->def_lcand);\n\tmem_deref(comp->def_rcand);\n\tmem_deref(comp->uh);\n\tmem_deref(comp->sock);\n}\n\n\nstatic struct ice_cand *cand_default(const struct list *lcandl,\n\t\t\t\t     unsigned compid)\n{\n\tstruct ice_cand *def = NULL;\n\tstruct le *le;\n\n\t/* NOTE: list must be sorted by priority */\n\tfor (le = list_head(lcandl); le; le = le->next) {\n\n\t\tstruct ice_cand *cand = le->data;\n\n\t\tif (cand->compid != compid)\n\t\t\tcontinue;\n\n\t\tswitch (cand->type) {\n\n\t\tcase ICE_CAND_TYPE_RELAY:\n\t\t\treturn cand;\n\n\t\tcase ICE_CAND_TYPE_SRFLX:\n\t\t\tif (!def || ICE_CAND_TYPE_SRFLX != def->type)\n\t\t\t\tdef = cand;\n\t\t\tbreak;\n\n\t\tcase ICE_CAND_TYPE_HOST:\n\t\t\tif (!def)\n\t\t\t\tdef = cand;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn def;\n}\n\n\nint icem_comp_alloc(struct icem_comp **cp, struct icem *icem, int id,\n\t\t    void *sock)\n{\n\tstruct icem_comp *comp;\n\tstruct sa local;\n\tint err;\n\n\tif (!cp || !icem || id<1 || id>255 || !sock)\n\t\treturn EINVAL;\n\n\tcomp = mem_zalloc(sizeof(*comp), destructor);\n\tif (!comp)\n\t\treturn ENOMEM;\n\n\tcomp->id = id;\n\tcomp->sock = mem_ref(sock);\n\tcomp->icem = icem;\n\n\terr = udp_register_helper(&comp->uh, sock, icem->layer,\n\t\t\t\t  NULL, helper_recv_handler, comp);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_local_get(comp->sock, &local);\n\tif (err)\n\t\tgoto out;\n\n\tcomp->lport = sa_port(&local);\n\n out:\n\tif (err)\n\t\tmem_deref(comp);\n\telse\n\t\t*cp = comp;\n\n\treturn err;\n}\n\n\nint icem_comp_set_default_cand(struct icem_comp *comp)\n{\n\tstruct ice_cand *cand;\n\n\tif (!comp)\n\t\treturn EINVAL;\n\n\tcand = cand_default(&comp->icem->lcandl, comp->id);\n\tif (!cand)\n\t\treturn ENOENT;\n\n\tmem_deref(comp->def_lcand);\n\tcomp->def_lcand = mem_ref(cand);\n\n\treturn 0;\n}\n\n\nvoid icem_comp_set_default_rcand(struct icem_comp *comp,\n\t\t\t\t struct ice_cand *rcand)\n{\n\tif (!comp)\n\t\treturn;\n\n\ticecomp_printf(comp, \"Set default remote candidate: %s:%J\\n\",\n\t\t       ice_cand_type2name(rcand->type), &rcand->addr);\n\n\tmem_deref(comp->def_rcand);\n\tcomp->def_rcand = mem_ref(rcand);\n\n\tif (comp->turnc) {\n\t\ticecomp_printf(comp, \"Add TURN Channel to peer %J\\n\",\n\t\t\t       &rcand->addr);\n\n\t\t(void)turnc_add_chan(comp->turnc, &rcand->addr, NULL, NULL);\n\t}\n}\n\n\nvoid icem_comp_set_selected(struct icem_comp *comp, struct ice_candpair *cp)\n{\n\tif (!comp || !cp)\n\t\treturn;\n\n\tif (cp->state != ICE_CANDPAIR_SUCCEEDED) {\n\t\tDEBUG_WARNING(\"{%s.%u} set_selected: invalid state '%s'\"\n\t\t\t      \" [%H]\\n\",\n\t\t\t      comp->icem->name, comp->id,\n\t\t\t      ice_candpair_state2name(cp->state),\n\t\t\t      icem_candpair_debug, cp);\n\t}\n\n\tmem_deref(comp->cp_sel);\n\tcomp->cp_sel = mem_ref(cp);\n}\n\n\nstruct icem_comp *icem_comp_find(const struct icem *icem, unsigned compid)\n{\n\tstruct le *le;\n\n\tif (!icem)\n\t\treturn NULL;\n\n\tfor (le = icem->compl.head; le; le = le->next) {\n\n\t\tstruct icem_comp *comp = le->data;\n\n\t\tif (comp->id == compid)\n\t\t\treturn comp;\n\t}\n\n\treturn NULL;\n}\n\n\nstatic void timeout(void *arg)\n{\n\tstruct icem_comp *comp = arg;\n\tstruct ice_candpair *cp;\n\n\ttmr_start(&comp->tmr_ka, ICE_DEFAULT_Tr * 1000 + rand_u16() % 1000,\n\t\t  timeout, comp);\n\n\t/* find selected candidate-pair */\n\tcp = comp->cp_sel;\n\tif (!cp)\n\t\treturn;\n\n\t(void)stun_indication(comp->icem->proto, comp->sock, &cp->rcand->addr,\n\t\t\t      (cp->lcand->type == ICE_CAND_TYPE_RELAY) ? 4 : 0,\n\t\t\t      STUN_METHOD_BINDING, NULL, 0, true, 0);\n}\n\n\nvoid icem_comp_keepalive(struct icem_comp *comp, bool enable)\n{\n\tif (!comp)\n\t\treturn;\n\n\tif (enable) {\n\t\ttmr_start(&comp->tmr_ka, ICE_DEFAULT_Tr * 1000, timeout, comp);\n\t}\n\telse {\n\t\ttmr_cancel(&comp->tmr_ka);\n\t}\n}\n\n\nvoid icecomp_printf(struct icem_comp *comp, const char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (!comp || !comp->icem->conf.debug)\n\t\treturn;\n\n\tva_start(ap, fmt);\n\t(void)re_printf(\"{%11s.%u} %v\", comp->icem->name, comp->id, fmt, &ap);\n\tva_end(ap);\n}\n\n\nint icecomp_debug(struct re_printf *pf, const struct icem_comp *comp)\n{\n\tif (!comp)\n\t\treturn 0;\n\n\treturn re_hprintf(pf, \"id=%u ldef=%J rdef=%J concluded=%d\",\n\t\t\t  comp->id,\n\t\t\t  comp->def_lcand ? &comp->def_lcand->addr : NULL,\n\t\t\t  comp->def_rcand ? &comp->def_rcand->addr : NULL,\n\t\t\t  comp->concluded);\n}\n\n\nint icem_set_turn_client(struct icem *icem, unsigned compid,\n\t\t\t struct turnc *turnc)\n{\n\tstruct icem_comp *comp;\n\n\tcomp = icem_comp_find(icem, compid);\n\tif (!comp)\n\t\treturn ENOENT;\n\n\tcomp->turnc = mem_deref(comp->turnc);\n\n\tif (turnc)\n\t\tcomp->turnc = mem_ref(turnc);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/ice/connchk.c",
    "content": "/**\n * @file connchk.c  ICE Connectivity Checks\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_stun.h>\n#include <re_turn.h>\n#include <re_ice.h>\n#include \"ice.h\"\n\n\n#define DEBUG_MODULE \"connchk\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic void pace_next(struct icem *icem)\n{\n\tif (icem->state != ICE_CHECKLIST_RUNNING)\n\t\treturn;\n\n\ticem_conncheck_schedule_check(icem);\n\n\tif (icem->state != ICE_CHECKLIST_RUNNING)\n\t\treturn;\n\n\ticem_checklist_update(icem);\n}\n\n\n/**\n * Constructing a Valid Pair\n *\n * @return The valid pair\n */\nstatic struct ice_candpair *construct_valid_pair(struct icem *icem,\n\t\t\t\t\t     struct ice_candpair *cp,\n\t\t\t\t\t     const struct sa *mapped,\n\t\t\t\t\t     const struct sa *dest)\n{\n\tstruct ice_cand *lcand, *rcand;\n\tstruct ice_candpair *cp2;\n\tint err;\n\n\tlcand = icem_cand_find(&icem->lcandl, cp->lcand->compid, mapped);\n\trcand = icem_cand_find(&icem->rcandl, cp->rcand->compid, dest);\n\n\tif (!lcand) {\n\t\tDEBUG_WARNING(\"no such local candidate: %J\\n\", mapped);\n\t\treturn NULL;\n\t}\n\tif (!rcand) {\n\t\tDEBUG_WARNING(\"no such remote candidate: %J\\n\", dest);\n\t\treturn NULL;\n\t}\n\n\t/* New candidate? -- implicit success */\n\tif (lcand != cp->lcand || rcand != cp->rcand) {\n\n\t\tif (lcand != cp->lcand) {\n\t\t\ticecomp_printf(cp->comp,\n\t\t\t\t       \"New local candidate for mapped %J\\n\",\n\t\t\t\t       mapped);\n\t\t}\n\t\tif (rcand != cp->rcand) {\n\t\t\ticecomp_printf(cp->comp,\n\t\t\t\t       \"New remote candidate for dest %J\\n\",\n\t\t\t\t       dest);\n\t\t}\n\n\t\t/* The original candidate pair is set to 'Failed' because\n\t\t * the implicitly discovered pair is 'better'.\n\t\t * This happens for UAs behind NAT where the original\n\t\t * pair is of type 'host' and the implicit pair is 'srflx'\n\t\t */\n\n\t\ticem_candpair_make_valid(cp);\n\n\t\tcp2 = icem_candpair_find(&icem->validl, lcand, rcand);\n\t\tif (cp2)\n\t\t\treturn cp2;\n\n\t\terr = icem_candpair_clone(&cp2, cp, lcand, rcand);\n\t\tif (err)\n\t\t\treturn NULL;\n\n\t\ticem_candpair_make_valid(cp2);\n\t\t/*icem_candpair_failed(cp, EINTR, 0);*/\n\n\t\treturn cp2;\n\t}\n\telse {\n\t\t/* Add to VALID LIST, the pair that generated the check */\n\t\ticem_candpair_make_valid(cp);\n\n\t\treturn cp;\n\t}\n}\n\n\nstatic void handle_success(struct icem *icem, struct ice_candpair *cp,\n\t\t\t   const struct sa *laddr)\n{\n\tif (!icem_cand_find(&icem->lcandl, cp->lcand->compid, laddr)) {\n\n\t\tint err;\n\n\t\ticecomp_printf(cp->comp, \"adding local PRFLX Candidate: %J\\n\",\n\t\t\t       laddr);\n\n\t\terr = icem_lcand_add(icem, cp->lcand,\n\t\t\t\t     ICE_CAND_TYPE_PRFLX, laddr);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"failed to add PRFLX: %m\\n\", err);\n\t\t}\n\t}\n\n\tcp = construct_valid_pair(icem, cp, laddr, &cp->rcand->addr);\n\tif (!cp) {\n\t\tDEBUG_WARNING(\"{%s} no valid candidate pair for %J\\n\",\n\t\t\t      icem->name, laddr);\n\t\treturn;\n\t}\n\n\ticem_candpair_make_valid(cp);\n\ticem_comp_set_selected(cp->comp, cp);\n\n\tcp->nominated = true;\n\n#if 0\n\t/* stop conncheck now -- conclude */\n\ticem_conncheck_stop(icem, 0);\n#endif\n}\n\n\n#if ICE_TRACE\nstatic int print_err(struct re_printf *pf, const int *err)\n{\n\tif (err && *err)\n\t\treturn re_hprintf(pf, \" (%m)\", *err);\n\n\treturn 0;\n}\n#endif\n\n\nstatic void stunc_resp_handler(int err, uint16_t scode, const char *reason,\n\t\t\t       const struct stun_msg *msg, void *arg)\n{\n\tstruct ice_candpair *cp = arg;\n\tstruct icem *icem = cp->icem;\n\tstruct stun_attr *attr;\n\n\t(void)reason;\n\n#if ICE_TRACE\n\ticecomp_printf(cp->comp, \"Rx %H <--- %H '%u %s'%H\\n\",\n\t\t       icem_cand_print, cp->lcand,\n\t\t       icem_cand_print, cp->rcand,\n\t\t       scode, reason, print_err, &err);\n#endif\n\n\tif (err) {\n\t\ticem_candpair_failed(cp, err, scode);\n\t\tgoto out;\n\t}\n\n\tswitch (scode) {\n\n\tcase 0: /* Success case */\n\t\tattr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);\n\t\tif (!attr) {\n\t\t\tDEBUG_WARNING(\"no XOR-MAPPED-ADDR in response\\n\");\n\t\t\ticem_candpair_failed(cp, EBADMSG, 0);\n\t\t\tbreak;\n\t\t}\n\n\t\thandle_success(icem, cp, &attr->v.sa);\n\t\tbreak;\n\n\tcase 487: /* Role Conflict */\n\t\tice_switch_local_role(icem);\n\t\t(void)icem_conncheck_send(cp, false, true);\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"{%s.%u} STUN Response: %u %s\\n\",\n\t\t\t      icem->name, cp->comp->id, scode, reason);\n\t\ticem_candpair_failed(cp, err, scode);\n\t\tbreak;\n\t}\n\n out:\n\tpace_next(icem);\n}\n\n\nint icem_conncheck_send(struct ice_candpair *cp, bool use_cand, bool trigged)\n{\n\tstruct ice_cand *lcand;\n\tstruct icem *icem;\n\tchar username_buf[64];\n\tsize_t presz = 0;\n\tuint32_t prio_prflx;\n\tuint16_t ctrl_attr;\n\tint err = 0;\n\n\tif (!cp)\n\t\treturn EINVAL;\n\n\tlcand = cp->lcand;\n\ticem = cp->icem;\n\n\tif (!str_isset(icem->rufrag)) {\n\t\tDEBUG_WARNING(\"send: name='%s' no remote ufrag\"\n\t\t\t      \" [use=%d, trig=%d]\\n\",\n\t\t\t      icem->name, use_cand, trigged);\n\t\treturn EPROTO;\n\t}\n\n\ticem_candpair_set_state(cp, ICE_CANDPAIR_INPROGRESS);\n\n\t(void)re_snprintf(username_buf, sizeof(username_buf),\n\t\t\t  \"%s:%s\", icem->rufrag, icem->lufrag);\n\n\t/* PRIORITY and USE-CANDIDATE */\n\tprio_prflx = ice_cand_calc_prio(ICE_CAND_TYPE_PRFLX, 0, lcand->compid);\n\n\tswitch (icem->lrole) {\n\n\tcase ICE_ROLE_CONTROLLING:\n\t\tctrl_attr = STUN_ATTR_CONTROLLING;\n\t\tbreak;\n\n\tcase ICE_ROLE_CONTROLLED:\n\t\tctrl_attr = STUN_ATTR_CONTROLLED;\n\n\t\tif (use_cand) {\n\t\t\tDEBUG_WARNING(\"send: use_cand=true, but\"\n\t\t\t\t      \" role is controlled (trigged=%d)\"\n\t\t\t\t      \" [%H]\\n\", trigged,\n\t\t\t\t      icem_candpair_debug, cp);\n\t\t\treturn EINVAL;\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\treturn EINVAL;\n\t}\n\n#if ICE_TRACE\n\ticecomp_printf(cp->comp, \"Tx %H ---> %H (%s) %s %s\\n\",\n\t\t       icem_cand_print, cp->lcand, icem_cand_print, cp->rcand,\n\t\t       ice_candpair_state2name(cp->state),\n\t\t       use_cand ? \"[USE]\" : \"\",\n\t\t       trigged ? \"[Trigged]\" : \"\");\n#else\n\t(void)trigged;\n#endif\n\n\t/* A connectivity check MUST utilize the STUN short term credential\n\t   mechanism. */\n\n\t/* The password is equal to the password provided by the peer */\n\tif (!icem->rpwd) {\n\t\tDEBUG_WARNING(\"send: no remote password!\\n\");\n\t}\n\n\tif (cp->ct_conn) {\n\t\tDEBUG_WARNING(\"send_req: CONNCHECK already Pending!\\n\");\n\t\treturn EBUSY;\n\t}\n\n\tswitch (lcand->type) {\n\n\tcase ICE_CAND_TYPE_RELAY:\n\t\t/* Creating Permissions for Relayed Candidates */\n\t\terr = turnc_add_chan(cp->comp->turnc, &cp->rcand->addr,\n\t\t\t\t     NULL, NULL);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"add channel: %m\\n\", err);\n\t\t\tbreak;\n\t\t}\n\t\tpresz = 4;\n\t\t/*@fallthrough@*/\n\n\tcase ICE_CAND_TYPE_HOST:\n\tcase ICE_CAND_TYPE_SRFLX:\n\tcase ICE_CAND_TYPE_PRFLX:\n\t\tcp->ct_conn = mem_deref(cp->ct_conn);\n\t\terr = stun_request(&cp->ct_conn, icem->stun, icem->proto,\n\t\t\t\t   cp->comp->sock, &cp->rcand->addr, presz,\n\t\t\t\t   STUN_METHOD_BINDING,\n\t\t\t\t   (uint8_t *)icem->rpwd, str_len(icem->rpwd),\n\t\t\t\t   true, stunc_resp_handler, cp,\n\t\t\t\t   4,\n\t\t\t\t   STUN_ATTR_USERNAME, username_buf,\n\t\t\t\t   STUN_ATTR_PRIORITY, &prio_prflx,\n\t\t\t\t   ctrl_attr, &icem->tiebrk,\n\t\t\t\t   STUN_ATTR_USE_CAND,\n\t\t\t\t   use_cand ? &use_cand : 0);\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"unknown candidate type %d\\n\", lcand->type);\n\t\terr = EINVAL;\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nstatic void abort_ice(struct icem *icem, int err)\n{\n\ticem->state = ICE_CHECKLIST_FAILED;\n\ttmr_cancel(&icem->tmr_pace);\n\n\tif (icem->chkh) {\n\t\ticem->chkh(err, icem->lrole == ICE_ROLE_CONTROLLING,\n\t\t\t   icem->arg);\n\t}\n\n\ticem->chkh = NULL;\n}\n\n\nstatic void do_check(struct ice_candpair *cp)\n{\n\tint err;\n\n\terr = icem_conncheck_send(cp, false, false);\n\tif (err) {\n\t\ticem_candpair_failed(cp, err, 0);\n\n\t\tif (err == ENOMEM) {\n\t\t\tabort_ice(cp->icem, err);\n\t\t}\n\t\telse {\n\t\t\tpace_next(cp->icem);\n\t\t}\n\t}\n}\n\n\n/**\n * Scheduling Checks\n *\n * @param icem    ICE Media object\n */\nvoid icem_conncheck_schedule_check(struct icem *icem)\n{\n\tstruct ice_candpair *cp;\n\n\t/* Find the highest priority pair in that check list that is in the\n\t   Waiting state. */\n\tcp = icem_candpair_find_st(&icem->checkl, 0, ICE_CANDPAIR_WAITING);\n\tif (cp) {\n\t\tdo_check(cp);\n\t\treturn;\n\t}\n\n\t/* If there is no such pair: */\n\n\t/* Find the highest priority pair in that check list that is in\n\t   the Frozen state. */\n\tcp = icem_candpair_find_st(&icem->checkl, 0, ICE_CANDPAIR_FROZEN);\n\tif (cp) { /* If there is such a pair: */\n\n\t\t/* Unfreeze the pair.\n\t\t   Perform a check for that pair, causing its state to\n\t\t   transition to In-Progress. */\n\t\tdo_check(cp);\n\t\treturn;\n\t}\n\n\t/* If there is no such pair: */\n\n\t/* Terminate the timer for that check list. */\n\n#if 0\n\ticem->state = ICE_CHECKLIST_COMPLETED;\n#endif\n}\n\n\nstatic void pace_timeout(void *arg)\n{\n\tstruct icem *icem = arg;\n\n\tpace_next(icem);\n}\n\n\nstatic void rcand_wait_timeout(void *arg)\n{\n\tstruct icem *icem = arg;\n\n\t/* Avoid long startup delay */\n\ticem->rcand_wait = false;\n\n\ticem_printf(icem, \"conncheck_start: \"\n\t\t\t\"mDNS timeout for remote candidate...\\n\");\n\n\ticem_conncheck_start(icem);\n}\n\n\n/**\n * Scheduling Checks\n *\n * @param icem    ICE Media object\n *\n * @return 0 if success, otherwise errorcode\n */\nint icem_conncheck_start(struct icem *icem)\n{\n\tint err;\n\n\tif (!icem)\n\t\treturn EINVAL;\n\n\tif (icem->rcand_wait) {\n\t\ticem_printf(icem, \"conncheck_start: \"\n\t\t\t\t  \"waiting mDNS for remote candidate...\\n\");\n\t\ttmr_start(&icem->tmr_rcand, 100, rcand_wait_timeout, icem);\n\t\treturn 0;\n\t}\n\n\terr = icem_checklist_form(icem);\n\tif (err)\n\t\treturn err;\n\n\ticem->state = ICE_CHECKLIST_RUNNING;\n\n\ticem_printf(icem, \"starting connectivity checks\"\n\t\t    \" with %u candidate pairs\\n\",\n\t\t    list_count(&icem->checkl));\n\n\t/* add some delay, to wait for call to be 'established' */\n\ttmr_start(&icem->tmr_pace, 0, pace_timeout, icem);\n\n\treturn 0;\n}\n\n\nvoid icem_conncheck_continue(struct icem *icem)\n{\n\tif (!tmr_isrunning(&icem->tmr_pace))\n\t\ttmr_start(&icem->tmr_pace, 1, pace_timeout, icem);\n}\n\n\n/**\n * Stop checklist, cancel all connectivity checks\n *\n * @param icem    ICE Media object\n * @param err     Error code\n */\nvoid icem_conncheck_stop(struct icem *icem, int err)\n{\n\tstruct le *le;\n\n\ticem->state = err ? ICE_CHECKLIST_FAILED : ICE_CHECKLIST_COMPLETED;\n\n\ttmr_cancel(&icem->tmr_pace);\n\n\tfor (le = icem->checkl.head; le; le = le->next) {\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tif (!icem_candpair_iscompleted(cp)) {\n\t\t\ticem_candpair_cancel(cp);\n\t\t\ticem_candpair_failed(cp, EINTR, 0);\n\t\t}\n\t}\n\n\ticem_checklist_update(icem);\n}\n"
  },
  {
    "path": "src/ice/ice.h",
    "content": "/**\n * @file ice.h  Internal Interface to ICE\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#ifndef RELEASE\n#define ICE_TRACE 1    /**< Trace connectivity checks */\n#endif\n\n\nenum ice_checkl_state {\n\tICE_CHECKLIST_NULL = -1,\n\tICE_CHECKLIST_RUNNING,\n\tICE_CHECKLIST_COMPLETED,\n\tICE_CHECKLIST_FAILED\n};\n\n\n/** ICE protocol values */\nenum {\n\tICE_DEFAULT_Tr          =  15, /**< Keepalive interval [s]          */\n\tICE_DEFAULT_Ta_RTP      =  20, /**< Pacing interval RTP [ms]        */\n\tICE_DEFAULT_Ta_NON_RTP  = 500, /**< Pacing interval [ms]            */\n\tICE_DEFAULT_RTO_RTP     = 100, /**< Retransmission TimeOut RTP [ms] */\n\tICE_DEFAULT_RTO_NONRTP  = 500, /**< Retransmission TimeOut [ms]     */\n\tICE_DEFAULT_RC          =   7  /**< Retransmission count            */\n};\n\n\n/** Defines a media-stream component */\nstruct icem_comp {\n\tstruct le le;                /**< Linked-list element               */\n\tstruct icem *icem;           /**< Parent ICE media                  */\n\tstruct ice_cand *def_lcand;  /**< Default local candidate           */\n\tstruct ice_cand *def_rcand;  /**< Default remote candidate          */\n\tstruct ice_candpair *cp_sel; /**< Selected candidate-pair           */\n\tstruct udp_helper *uh;       /**< UDP helper                        */\n\tvoid *sock;                  /**< Transport socket                  */\n\tuint16_t lport;              /**< Local port number                 */\n\tunsigned id;                 /**< Component ID                      */\n\tbool concluded;              /**< Concluded flag                    */\n\tstruct turnc *turnc;         /**< TURN Client                       */\n\tstruct tmr tmr_ka;           /**< Keep-alive timer                  */\n};\n\n/** Defines an ICE media-stream */\nstruct icem {\n\tstruct ice_conf conf;        /**< ICE Configuration                  */\n\tstruct stun *stun;           /**< STUN Transport                     */\n\tstruct sa stun_srv;          /**< STUN Server IP address and port    */\n\tstruct list lcandl;          /**< List of local candidates           */\n\tstruct list rcandl;          /**< List of remote candidates          */\n\tstruct list checkl;          /**< Check List of cand pairs (sorted)  */\n\tstruct list validl;          /**< Valid List of cand pairs (sorted)  */\n\tuint64_t tiebrk;             /**< Tie-break value for roleconflict   */\n\tbool mismatch;               /**< ICE mismatch flag                  */\n\tbool rmode_lite;             /**< Remote mode is Lite                */\n\tenum ice_role lrole;         /**< Local role                         */\n\tstruct tmr tmr_pace;         /**< Timer for pacing STUN requests     */\n\tstruct tmr tmr_rcand;        /**< Timer for remote candidate wait    */\n\tint proto;                   /**< Transport protocol                 */\n\tint layer;                   /**< Protocol layer                     */\n\tenum ice_checkl_state state; /**< State of the checklist             */\n\tstruct list compl;           /**< ICE media components               */\n\tchar *lufrag;                /**< Local Username fragment            */\n\tchar *lpwd;                  /**< Local Password                     */\n\tchar *rufrag;                /**< Remote Username fragment           */\n\tchar *rpwd;                  /**< Remote Password                    */\n\tice_connchk_h *chkh;         /**< Connectivity check handler         */\n\tvoid *arg;                   /**< Handler argument                   */\n\tchar name[32];               /**< Name of the media stream           */\n\tbool rcand_wait;             /**< Waiting for remote candidate       */\n};\n\n/** Defines a candidate */\nstruct ice_cand {\n\tstruct le le;                /**< List element                       */\n\tenum ice_cand_type type;     /**< Candidate type                     */\n\tuint32_t prio;               /**< Priority of this candidate         */\n\tchar *foundation;            /**< Foundation                         */\n\tunsigned compid;             /**< Component ID (1-256)               */\n\tstruct sa rel;               /**< Related IP address and port number */\n\tstruct sa addr;              /**< Transport address                  */\n\tenum ice_transp transp;      /**< Transport protocol                 */\n\n\t/* extra for local */\n\tstruct ice_cand *base;       /**< Links to base candidate, if any    */\n\tchar *ifname;                /**< Network interface, for diagnostics */\n};\n\n/** Defines a candidate pair */\nstruct ice_candpair {\n\tstruct le le;                /**< List element                       */\n\tstruct icem *icem;           /**< Pointer to parent ICE media        */\n\tstruct icem_comp *comp;      /**< Pointer to media-stream component  */\n\tstruct ice_cand *lcand;      /**< Local candidate                    */\n\tstruct ice_cand *rcand;      /**< Remote candidate                   */\n\tbool def;                    /**< Default flag                       */\n\tbool valid;                  /**< Valid flag                         */\n\tbool nominated;              /**< Nominated flag                     */\n\tenum ice_candpair_state state;/**< Candidate pair state              */\n\tuint64_t pprio;              /**< Pair priority                      */\n\tstruct stun_ctrans *ct_conn; /**< STUN Transaction for conncheck     */\n\tint err;                     /**< Saved error code, if failed        */\n\tuint16_t scode;              /**< Saved STUN code, if failed         */\n};\n\n\n/* cand */\nint icem_rcand_add(struct icem *icem, enum ice_cand_type type, unsigned compid,\n\t\t   uint32_t prio, const struct sa *addr,\n\t\t   const struct sa *rel_addr, const struct pl *foundation);\nint icem_rcand_add_prflx(struct ice_cand **rcp, struct icem *icem,\n\t\t\t unsigned compid, uint32_t prio,\n\t\t\t const struct sa *addr);\nstruct ice_cand *icem_lcand_find_checklist(const struct icem *icem,\n\t\t\t\t\t   unsigned compid);\nint icem_cands_debug(struct re_printf *pf, const struct list *lst);\nint icem_cand_print(struct re_printf *pf, const struct ice_cand *cand);\n\n\n/* candpair */\nint  icem_candpair_alloc(struct ice_candpair **cpp, struct icem *icem,\n\t\t\t struct ice_cand *lcand, struct ice_cand *rcand);\nint  icem_candpair_clone(struct ice_candpair **cpp, struct ice_candpair *cp0,\n\t\t\t struct ice_cand *lcand, struct ice_cand *rcand);\nvoid icem_candpair_prio_order(struct list *lst);\nvoid icem_candpair_cancel(struct ice_candpair *cp);\nvoid icem_candpair_make_valid(struct ice_candpair *cp);\nvoid icem_candpair_failed(struct ice_candpair *cp, int err, uint16_t scode);\nvoid icem_candpair_set_state(struct ice_candpair *cp,\n\t\t\t     enum ice_candpair_state state);\nvoid icem_candpairs_flush(struct list *lst, enum ice_cand_type type,\n\t\t\t  unsigned compid);\nbool icem_candpair_iscompleted(const struct ice_candpair *cp);\nbool icem_candpair_cmp(const struct ice_candpair *cp1,\n\t\t       const struct ice_candpair *cp2);\nbool icem_candpair_cmp_fnd(const struct ice_candpair *cp1,\n\t\t\t   const struct ice_candpair *cp2);\nstruct ice_candpair *icem_candpair_find(const struct list *lst,\n\t\t\t\t    const struct ice_cand *lcand,\n\t\t\t\t    const struct ice_cand *rcand);\nstruct ice_candpair *icem_candpair_find_st(const struct list *lst,\n\t\t\t\t\t   unsigned compid,\n\t\t\t\t\t   enum ice_candpair_state state);\nstruct ice_candpair *icem_candpair_find_compid(const struct list *lst,\n\t\t\t\t\t   unsigned compid);\nstruct ice_candpair *icem_candpair_find_rcand(struct icem *icem,\n\t\t\t\t\t  const struct ice_cand *rcand);\nint  icem_candpair_debug(struct re_printf *pf, const struct ice_candpair *cp);\nint  icem_candpairs_debug(struct re_printf *pf, const struct list *list);\n\n\n/* stun server */\nint icem_stund_recv(struct icem_comp *comp, const struct sa *src,\n\t\t    struct stun_msg *req, size_t presz);\n\n\n/* ICE media */\nvoid icem_printf(struct icem *icem, const char *fmt, ...);\n\n\n/* Checklist */\nint  icem_checklist_form(struct icem *icem);\nvoid icem_checklist_update(struct icem *icem);\n\n\n/* component */\nint  icem_comp_alloc(struct icem_comp **cp, struct icem *icem, int id,\n\t\t     void *sock);\nint  icem_comp_set_default_cand(struct icem_comp *comp);\nvoid icem_comp_set_default_rcand(struct icem_comp *comp,\n\t\t\t\t struct ice_cand *rcand);\nvoid icem_comp_set_selected(struct icem_comp *comp, struct ice_candpair *cp);\nstruct icem_comp *icem_comp_find(const struct icem *icem, unsigned compid);\nvoid icem_comp_keepalive(struct icem_comp *comp, bool enable);\nvoid icecomp_printf(struct icem_comp *comp, const char *fmt, ...);\nint  icecomp_debug(struct re_printf *pf, const struct icem_comp *comp);\n\n\n/* conncheck */\nvoid icem_conncheck_schedule_check(struct icem *icem);\nvoid icem_conncheck_continue(struct icem *icem);\nint  icem_conncheck_send(struct ice_candpair *cp, bool use_cand, bool trigger);\n\n\n/* icestr */\nconst char    *ice_checkl_state2name(enum ice_checkl_state cst);\n\n\n/* util */\ntypedef void * (list_unique_h)(struct le *le1, struct le *le2);\n\nuint64_t ice_calc_pair_prio(uint32_t g, uint32_t d);\nvoid ice_switch_local_role(struct icem *icem);\nuint32_t ice_list_unique(struct list *list, list_unique_h *uh);\n"
  },
  {
    "path": "src/ice/icem.c",
    "content": "/**\n * @file icem.c  ICE Media stream\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_stun.h>\n#include <re_turn.h>\n#include <re_ice.h>\n#include \"ice.h\"\n\n\n#define DEBUG_MODULE \"icem\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/*\n * ICE Implementation as of RFC 5245\n */\n\n\nstatic const struct ice_conf conf_default = {\n\tICE_DEFAULT_RTO_RTP,\n\tICE_DEFAULT_RC,\n\tICE_POLICY_ALL,\n\tfalse\n};\n\n\n/** Determining Role */\nstatic void ice_determine_role(struct icem *icem, enum ice_role role)\n{\n\tif (!icem)\n\t\treturn;\n\n\tif (icem->rmode_lite)\n\t\ticem->lrole = ICE_ROLE_CONTROLLING;\n\telse\n\t\ticem->lrole = role;\n}\n\n\nstatic void icem_destructor(void *data)\n{\n\tstruct icem *icem = data;\n\n\ttmr_cancel(&icem->tmr_rcand);\n\ttmr_cancel(&icem->tmr_pace);\n\tlist_flush(&icem->compl);\n\tlist_flush(&icem->validl);\n\tlist_flush(&icem->checkl);\n\tlist_flush(&icem->lcandl);\n\tlist_flush(&icem->rcandl);\n\tmem_deref(icem->lufrag);\n\tmem_deref(icem->lpwd);\n\tmem_deref(icem->rufrag);\n\tmem_deref(icem->rpwd);\n\tmem_deref(icem->stun);\n}\n\n\n/**\n * Add a new ICE Media object to the ICE Session\n *\n * @param icemp   Pointer to allocated ICE Media object\n * @param role    Local ICE role\n * @param proto   Transport protocol\n * @param layer   Protocol stack layer\n * @param tiebrk  Tie-breaker value, must be same for all media streams\n * @param lufrag  Local username fragment\n * @param lpwd    Local password\n * @param chkh    Connectivity check handler\n * @param arg     Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint  icem_alloc(struct icem **icemp, enum ice_role role, int proto, int layer,\n\t\tuint64_t tiebrk, const char *lufrag, const char *lpwd,\n\t\tice_connchk_h *chkh, void *arg)\n{\n\tstruct icem *icem;\n\tint err = 0;\n\n\tif (!icemp || !tiebrk || !lufrag || !lpwd)\n\t\treturn EINVAL;\n\n\tif (str_len(lufrag) < 4 || str_len(lpwd) < 22) {\n\t\tDEBUG_WARNING(\"alloc: lufrag/lpwd is too short\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tif (proto != IPPROTO_UDP)\n\t\treturn EPROTONOSUPPORT;\n\n\ticem = mem_zalloc(sizeof(*icem), icem_destructor);\n\tif (!icem)\n\t\treturn ENOMEM;\n\n\ticem->conf = conf_default;\n\n\ttmr_init(&icem->tmr_pace);\n\ttmr_init(&icem->tmr_rcand);\n\tlist_init(&icem->lcandl);\n\tlist_init(&icem->rcandl);\n\tlist_init(&icem->checkl);\n\tlist_init(&icem->validl);\n\n\ticem->layer = layer;\n\ticem->proto = proto;\n\ticem->state = ICE_CHECKLIST_NULL;\n\ticem->chkh  = chkh;\n\ticem->arg   = arg;\n\ticem->tiebrk = tiebrk;\n\n\terr = str_dup(&icem->lufrag, lufrag);\n\terr |= str_dup(&icem->lpwd, lpwd);\n\tif (err)\n\t\tgoto out;\n\n\tice_determine_role(icem, role);\n\n\terr = stun_alloc(&icem->stun, NULL, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\t/* Update STUN Transport */\n\tstun_conf(icem->stun)->rto = icem->conf.rto;\n\tstun_conf(icem->stun)->rc = icem->conf.rc;\n\n out:\n\tif (err)\n\t\tmem_deref(icem);\n\telse if (icemp)\n\t\t*icemp = icem;\n\n\treturn err;\n}\n\n\n/**\n * Get the ICE Configuration\n *\n * @param icem  ICE Media object\n *\n * @return ICE Configuration\n */\nstruct ice_conf *icem_conf(struct icem *icem)\n{\n\treturn icem ? &icem->conf : NULL;\n}\n\n\nenum ice_role icem_local_role(const struct icem *icem)\n{\n\treturn icem ? icem->lrole : ICE_ROLE_UNKNOWN;\n}\n\n\nvoid icem_set_conf(struct icem *icem, const struct ice_conf *conf)\n{\n\tif (!icem || !conf)\n\t\treturn;\n\n\ticem->conf = *conf;\n\n\tif (icem->stun) {\n\n\t\t/* Update STUN Transport */\n\t\tstun_conf(icem->stun)->rto = icem->conf.rto;\n\t\tstun_conf(icem->stun)->rc = icem->conf.rc;\n\t}\n}\n\n\n/**\n * Set the local role on the ICE Session\n *\n * @param icem    ICE Media object\n * @param role    Local ICE role\n */\nvoid icem_set_role(struct icem *icem, enum ice_role role)\n{\n\tif (!icem)\n\t\treturn;\n\n\tice_determine_role(icem, role);\n}\n\n\n/**\n * Set the name of the ICE Media object, used for debugging\n *\n * @param icem  ICE Media object\n * @param name  Media name\n */\nvoid icem_set_name(struct icem *icem, const char *name)\n{\n\tif (!icem)\n\t\treturn;\n\n\tstr_ncpy(icem->name, name, sizeof(icem->name));\n}\n\n\n/**\n * Add a new component to the ICE Media object\n *\n * @param icem    ICE Media object\n * @param compid  Component ID\n * @param sock    Application protocol socket\n *\n * @return 0 if success, otherwise errorcode\n */\nint icem_comp_add(struct icem *icem, unsigned compid, void *sock)\n{\n\tstruct icem_comp *comp;\n\tint err;\n\n\tif (!icem)\n\t\treturn EINVAL;\n\n\tif (icem_comp_find(icem, compid))\n\t\treturn EALREADY;\n\n\terr = icem_comp_alloc(&comp, icem, compid, sock);\n\tif (err)\n\t\treturn err;\n\n\tlist_append(&icem->compl, &comp->le, comp);\n\n\treturn 0;\n}\n\n\nstatic void *unique_handler(struct le *le1, struct le *le2)\n{\n\tstruct ice_cand *c1 = le1->data, *c2 = le2->data;\n\n\tif (c1->base != c2->base || !sa_cmp(&c1->addr, &c2->addr, SA_ALL))\n\t\treturn NULL;\n\n\t/* remove candidate with lower priority */\n\treturn c1->prio < c2->prio ? c1 : c2;\n}\n\n\n/**\n * Eliminating Redundant Candidates\n *\n * @param icem    ICE Media object\n */\nvoid icem_cand_redund_elim(struct icem *icem)\n{\n\tuint32_t n;\n\n\tn = ice_list_unique(&icem->lcandl, unique_handler);\n\tif (n > 0) {\n\t\ticem_printf(icem, \"redundant candidates eliminated: %u\\n\", n);\n\t}\n}\n\n\n/**\n * Get the Default Local Candidate\n *\n * @param icem   ICE Media object\n * @param compid Component ID\n *\n * @return Default Local Candidate address if set, otherwise NULL\n */\nconst struct sa *icem_cand_default(struct icem *icem, unsigned compid)\n{\n\tconst struct icem_comp *comp = icem_comp_find(icem, compid);\n\n\tif (!comp || !comp->def_lcand)\n\t\treturn NULL;\n\n\treturn &comp->def_lcand->addr;\n}\n\n\n/**\n * Verifying ICE Support and set default remote candidate\n *\n * @param icem   ICE Media\n * @param compid Component ID\n * @param raddr  Address of default remote candidate\n *\n * @return True if ICE is supported, otherwise false\n */\nbool icem_verify_support(struct icem *icem, unsigned compid,\n\t\t\t const struct sa *raddr)\n{\n\tstruct ice_cand *rcand;\n\tbool match;\n\n\tif (!icem)\n\t\treturn false;\n\n\trcand = icem_cand_find(&icem->rcandl, compid, raddr);\n\tmatch = rcand != NULL;\n\n\tif (!match)\n\t\ticem->mismatch = true;\n\n\tif (rcand) {\n\t\ticem_comp_set_default_rcand(icem_comp_find(icem, compid),\n\t\t\t\t\t    rcand);\n\t}\n\n\treturn match;\n}\n\n\n/**\n * Add a TURN Channel for the selected remote address\n *\n * @param icem   ICE Media object\n * @param compid Component ID\n * @param raddr  Remote network address\n *\n * @return 0 if success, otherwise errorcode\n */\nint icem_add_chan(struct icem *icem, unsigned compid, const struct sa *raddr)\n{\n\tstruct icem_comp *comp;\n\n\tif (!icem)\n\t\treturn EINVAL;\n\n\tcomp = icem_comp_find(icem, compid);\n\tif (!comp)\n\t\treturn ENOENT;\n\n\tif (comp->turnc) {\n\t\tDEBUG_NOTICE(\"{%s.%u} Add TURN Channel to peer %J\\n\",\n\t\t\t     comp->icem->name, comp->id, raddr);\n\n\t\treturn turnc_add_chan(comp->turnc, raddr, NULL, NULL);\n\t}\n\n\treturn 0;\n}\n\n\nstatic void purge_relayed(struct icem *icem, struct icem_comp *comp)\n{\n\tif (comp->turnc) {\n\t\tDEBUG_NOTICE(\"{%s.%u} purge local RELAY candidates\\n\",\n\t\t\t     icem->name, comp->id);\n\t}\n\n\t/*\n\t * Purge all Candidate-Pairs where the Local candidate\n\t * is of type \"Relay\"\n\t */\n\ticem_candpairs_flush(&icem->checkl, ICE_CAND_TYPE_RELAY, comp->id);\n\ticem_candpairs_flush(&icem->validl, ICE_CAND_TYPE_RELAY, comp->id);\n\n\tcomp->turnc = mem_deref(comp->turnc);\n}\n\n\n/**\n * Update the ICE Media object\n *\n * @param icem ICE Media object\n */\nvoid icem_update(struct icem *icem)\n{\n\tstruct le *le;\n\n\tif (!icem)\n\t\treturn;\n\n\tfor (le = icem->compl.head; le; le = le->next) {\n\n\t\tstruct icem_comp *comp = le->data;\n\n\t\t/* remove TURN client if not used by local \"Selected\" */\n\t\tif (comp->cp_sel) {\n\n\t\t\tif (comp->cp_sel->lcand->type != ICE_CAND_TYPE_RELAY)\n\t\t\t\tpurge_relayed(icem, comp);\n\t\t}\n\t}\n}\n\n\n/**\n * Get the ICE Mismatch flag of the ICE Media object\n *\n * @param icem ICE Media object\n *\n * @return True if ICE mismatch, otherwise false\n */\nbool icem_mismatch(const struct icem *icem)\n{\n\treturn icem ? icem->mismatch : true;\n}\n\n\n/**\n * Print debug information for the ICE Media\n *\n * @param pf   Print function for debug output\n * @param icem ICE Media object\n *\n * @return 0 if success, otherwise errorcode\n */\nint icem_debug(struct re_printf *pf, const struct icem *icem)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tif (!icem)\n\t\treturn 0;\n\n\terr |= re_hprintf(pf, \"----- ICE Media <%s> -----\\n\", icem->name);\n\n\terr |= re_hprintf(pf, \" local_mode=Full, remote_mode=%s\",\n\t\t\t  icem->rmode_lite ? \"Lite\" : \"Full\");\n\terr |= re_hprintf(pf, \", local_role=%s\\n\", ice_role2name(icem->lrole));\n\terr |= re_hprintf(pf, \" local_ufrag=\\\"%s\\\" local_pwd=\\\"%s\\\"\\n\",\n\t\t\t  icem->lufrag, icem->lpwd);\n\n\terr |= re_hprintf(pf, \" Components: (%u)\\n\", list_count(&icem->compl));\n\tfor (le = icem->compl.head; le; le = le->next) {\n\t\tstruct icem_comp *comp = le->data;\n\n\t\terr |= re_hprintf(pf, \"  %H\\n\", icecomp_debug, comp);\n\t}\n\n\terr |= re_hprintf(pf, \" Local Candidates: %H\",\n\t\t\t  icem_cands_debug, &icem->lcandl);\n\terr |= re_hprintf(pf, \" Remote Candidates: %H\",\n\t\t\t  icem_cands_debug, &icem->rcandl);\n\terr |= re_hprintf(pf, \" Check list: [state=%s]%H\",\n\t\t\t  ice_checkl_state2name(icem->state),\n\t\t\t  icem_candpairs_debug, &icem->checkl);\n\terr |= re_hprintf(pf, \" Valid list: %H\",\n\t\t\t  icem_candpairs_debug, &icem->validl);\n\n\terr |= stun_debug(pf, icem->stun);\n\n\treturn err;\n}\n\n\n/**\n * Get the list of Local Candidates (struct cand)\n *\n * @param icem ICE Media object\n *\n * @return List of Local Candidates\n */\nstruct list *icem_lcandl(const struct icem *icem)\n{\n\treturn icem ? (struct list *)&icem->lcandl : NULL;\n}\n\n\n/**\n * Get the list of Remote Candidates (struct cand)\n *\n * @param icem ICE Media object\n *\n * @return List of Remote Candidates\n */\nstruct list *icem_rcandl(const struct icem *icem)\n{\n\treturn icem ? (struct list *)&icem->rcandl : NULL;\n}\n\n\n/**\n * Get the checklist of Candidate Pairs\n *\n * @param icem ICE Media object\n *\n * @return Checklist (struct ice_candpair)\n */\nstruct list *icem_checkl(const struct icem *icem)\n{\n\treturn icem ? (struct list *)&icem->checkl : NULL;\n}\n\n\n/**\n * Get the list of valid Candidate Pairs\n *\n * @param icem ICE Media object\n *\n * @return Validlist (struct ice_candpair)\n */\nstruct list *icem_validl(const struct icem *icem)\n{\n\treturn icem ? (struct list *)&icem->validl : NULL;\n}\n\n\nint icem_comps_set_default_cand(struct icem *icem)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tif (!icem)\n\t\treturn EINVAL;\n\n\tfor (le = icem->compl.head; le; le = le->next) {\n\n\t\tstruct icem_comp *comp = le->data;\n\n\t\terr |= icem_comp_set_default_cand(comp);\n\t}\n\n\treturn err;\n}\n\n\nstruct stun *icem_stun(struct icem *icem)\n{\n\treturn icem ? icem->stun : NULL;\n}\n\n\nvoid icem_printf(struct icem *icem, const char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (!icem || !icem->conf.debug)\n\t\treturn;\n\n\tva_start(ap, fmt);\n\t(void)re_printf(\"{%11s. } %v\", icem->name, fmt, &ap);\n\tva_end(ap);\n}\n\n\n/**\n * Check if remote ICE candidates are ready\n *\n * @param icem ICE Media object\n *\n * @return true if ready otherwise false\n */\nbool icem_rcand_ready(struct icem *icem)\n{\n\tif (!icem)\n\t\treturn false;\n\n\tif (icem->rcand_wait)\n\t\treturn true;\n\n\treturn !list_isempty(&icem->rcandl);\n}\n"
  },
  {
    "path": "src/ice/icesdp.c",
    "content": "/**\n * @file icesdp.c  SDP Attributes for ICE\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#ifndef WIN32\n#include <netdb.h>\n#endif\n\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_net.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include <re_main.h>\n#include \"ice.h\"\n\n\n#define DEBUG_MODULE \"icesdp\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nconst char ice_attr_cand[]        = \"candidate\";\nconst char ice_attr_remote_cand[] = \"remote-candidates\";\nconst char ice_attr_lite[]        = \"ice-lite\";\nconst char ice_attr_ufrag[]       = \"ice-ufrag\";\nconst char ice_attr_pwd[]         = \"ice-pwd\";\nconst char ice_attr_mismatch[]    = \"ice-mismatch\";\n\n\nstatic const char rel_addr_str[] = \"raddr\";\nstatic const char rel_port_str[] = \"rport\";\n\n\nstruct rcand {\n\tint ai_family;\n\tstruct icem *icem;\n\tenum ice_cand_type type;\n\tunsigned cid;\n\tuint32_t prio;\n\tuint32_t port;\n\tstruct sa caddr;\n\tstruct sa rel_addr;\n\tstruct pl foundation;\n\tchar domain[128];\n};\n\n\n/* Encode SDP Attributes */\n\n\nstatic const char *transp_name(enum ice_transp transp)\n{\n\tswitch (transp) {\n\n\tcase ICE_TRANSP_UDP: return \"UDP\";\n\tdefault:             return \"???\";\n\t}\n}\n\n\nstatic enum ice_transp transp_resolve(const struct pl *transp)\n{\n\tif (!pl_strcasecmp(transp, \"UDP\"))\n\t\treturn ICE_TRANSP_UDP;\n\n\treturn ICE_TRANSP_NONE;\n}\n\n\n/**\n * Encode SDP candidate attribute\n *\n * @param pf    Print function\n * @param cand  Candidate to encode\n *\n * @return 0 if success, otherwise errorcode\n */\nint ice_cand_encode(struct re_printf *pf, const struct ice_cand *cand)\n{\n\tint err;\n\n\terr = re_hprintf(pf, \"%s %u %s %u %j %u typ %s\",\n\t\t\t cand->foundation, cand->compid,\n\t\t\t transp_name(cand->transp), cand->prio,\n\t\t\t &cand->addr, sa_port(&cand->addr),\n\t\t\t ice_cand_type2name(cand->type));\n\n\tif (sa_isset(&cand->rel, SA_ADDR))\n\t\terr |= re_hprintf(pf, \" raddr %j\", &cand->rel);\n\n\tif (sa_isset(&cand->rel, SA_PORT))\n\t\terr |= re_hprintf(pf, \" rport %u\", sa_port(&cand->rel));\n\n\treturn err;\n}\n\n\n/**\n * Check if remote candidates are available\n *\n * @param icem ICE Media object\n *\n * @return True if available, otherwise false\n */\nbool ice_remotecands_avail(const struct icem *icem)\n{\n\tif (!icem)\n\t\treturn false;\n\n\treturn icem->lrole == ICE_ROLE_CONTROLLING &&\n\t\ticem->state == ICE_CHECKLIST_COMPLETED;\n}\n\n\n/**\n * Encode the SDP \"remote-candidates\" Attribute\n *\n * @param pf   Print function\n * @param icem ICE Media object\n *\n * @return 0 if success, otherwise errorcode\n */\nint ice_remotecands_encode(struct re_printf *pf, const struct icem *icem)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tif (!icem)\n\t\treturn EINVAL;\n\n\tfor (le = icem->rcandl.head; le && !err; le = le->next) {\n\n\t\tconst struct ice_cand *rcand = le->data;\n\n\t\terr = re_hprintf(pf, \"%s%d %j %u\",\n\t\t\t\t icem->rcandl.head==le ? \"\" : \" \",\n\t\t\t\t rcand->compid,\n\t\t\t\t &rcand->addr, sa_port(&rcand->addr));\n\t}\n\n\treturn err;\n}\n\n\n/* Decode SDP Attributes */\n\n\nstatic int ufrag_decode(struct icem *icem, const char *value)\n{\n\tchar *ufrag = NULL;\n\tint err;\n\n\terr = str_dup(&ufrag, value);\n\tif (err)\n\t\treturn err;\n\n\tmem_deref(icem->rufrag);\n\ticem->rufrag = mem_ref(ufrag);\n\n\tmem_deref(ufrag);\n\n\treturn 0;\n}\n\n\nstatic int pwd_decode(struct icem *icem, const char *value)\n{\n\tchar *pwd = NULL;\n\tint err;\n\n\terr = str_dup(&pwd, value);\n\tif (err)\n\t\treturn err;\n\n\tmem_deref(icem->rpwd);\n\ticem->rpwd = mem_ref(pwd);\n\n\tmem_deref(pwd);\n\n\treturn 0;\n}\n\n\nstatic int media_ufrag_decode(struct icem *icem, const char *value)\n{\n\ticem->rufrag = mem_deref(icem->rufrag);\n\n\treturn str_dup(&icem->rufrag, value);\n}\n\n\nstatic int media_pwd_decode(struct icem *icem, const char *value)\n{\n\ticem->rpwd = mem_deref(icem->rpwd);\n\n\treturn str_dup(&icem->rpwd, value);\n}\n\n\nstatic int getaddr_rcand(void *arg)\n{\n\tstruct rcand *rcand = arg;\n\tstruct addrinfo *res, *res0 = NULL;\n\n\tstruct addrinfo hints = {.ai_flags  = AI_ADDRCONFIG,\n\t\t\t\t .ai_family = rcand->ai_family};\n\tint err;\n\n\terr = getaddrinfo(rcand->domain, NULL, &hints, &res0);\n\tif (err)\n\t\treturn EADDRNOTAVAIL;\n\n\tfor (res = res0; res; res = res->ai_next) {\n\t\terr = sa_set_sa(&rcand->caddr, res->ai_addr);\n\t\tif (err)\n\t\t\tcontinue;\n\n\t\tbreak;\n\t}\n\n\tsa_set_port(&rcand->caddr, rcand->port);\n\n\tfreeaddrinfo(res0);\n\n\treturn 0;\n}\n\n\nstatic void delayed_rcand(int err, void *arg)\n{\n\tstruct rcand *rcand = arg;\n\n\tif (err)\n\t\tgoto out;\n\n\tif (!rcand->icem->rcand_wait) {\n\t\tDEBUG_WARNING(\"late mDNS candidate: %s - %J\\n\", rcand->domain,\n\t\t\t      rcand->caddr);\n\t\tgoto out;\n\t}\n\n\t/* add only if not exist */\n\tif (icem_cand_find(&rcand->icem->rcandl, rcand->cid, &rcand->caddr))\n\t\tgoto out;\n\n\ticem_rcand_add(rcand->icem, rcand->type, rcand->cid, rcand->prio,\n\t\t       &rcand->caddr, &rcand->rel_addr, &rcand->foundation);\n\nout:\n\tmem_deref(rcand);\n}\n\n\nstatic void rcand_dealloc(void *arg)\n{\n\tstruct rcand *rcand = arg;\n\n\tmem_deref(rcand->icem);\n\tmem_deref((char *)rcand->foundation.p);\n}\n\n\nstatic int cand_decode(struct icem *icem, const char *val)\n{\n\tstruct pl foundation, compid, transp, prio, addr, port, cand_type;\n\tstruct pl extra = pl_null;\n\tstruct sa caddr, rel_addr;\n\tchar type[8];\n\tuint8_t cid;\n\tint err;\n\n\tsa_init(&rel_addr, AF_INET);\n\n\terr = re_regex(val, strlen(val),\n\t\t       \"[^ ]+ [0-9]+ [^ ]+ [0-9]+ [^ ]+ [0-9]+ typ [a-z]+[^]*\",\n\t\t       &foundation, &compid, &transp, &prio,\n\t\t       &addr, &port, &cand_type, &extra);\n\tif (err)\n\t\treturn err;\n\n\tif (ICE_TRANSP_NONE == transp_resolve(&transp)) {\n\t\tDEBUG_INFO(\"<%s> ignoring candidate with\"\n\t\t\t     \" unknown transport=%r (%r:%r)\\n\",\n\t\t\t     icem->name, &transp, &cand_type, &addr);\n\t\treturn 0;\n\t}\n\n\tif (pl_isset(&extra)) {\n\n\t\tstruct pl name, value;\n\n\t\t/* Loop through \" SP attr SP value\" pairs */\n\t\twhile (!re_regex(extra.p, extra.l, \" [^ ]+ [^ ]+\",\n\t\t\t\t &name, &value)) {\n\n\t\t\tpl_advance(&extra, value.p + value.l - extra.p);\n\n\t\t\tif (0 == pl_strcasecmp(&name, rel_addr_str)) {\n\t\t\t\terr = sa_set(&rel_addr, &value,\n\t\t\t\t\t     sa_port(&rel_addr));\n\t\t\t\tif (err)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (0 == pl_strcasecmp(&name, rel_port_str)) {\n\t\t\t\tsa_set_port(&rel_addr, pl_u32(&value));\n\t\t\t}\n\t\t}\n\t}\n\n\t(void)pl_strcpy(&cand_type, type, sizeof(type));\n\tcid = pl_u32(&compid);\n\n\t/* check for mdns .local address */\n\tif (pl_strstr(&addr, \".local\") != NULL) {\n\t\t/* try non blocking getaddr mdns resolution */\n\t\ticem_printf(icem, \"mDNS remote cand: %r\\n\", &addr);\n\t\ticem->rcand_wait = true;\n\n\t\t/* AF_INET IPv4 candidate */\n\t\tstruct rcand *rcand =\n\t\t\tmem_zalloc(sizeof(struct rcand), rcand_dealloc);\n\t\tif (!rcand)\n\t\t\treturn ENOMEM;\n\n\t\trcand->ai_family = AF_INET;\n\t\trcand->icem\t = mem_ref(icem);\n\t\trcand->type\t = ice_cand_name2type(type);\n\t\trcand->cid\t = cid;\n\t\trcand->prio\t = pl_u32(&prio);\n\t\trcand->port\t = pl_u32(&port);\n\t\trcand->rel_addr\t = rel_addr;\n\n\t\tpl_dup(&rcand->foundation, &foundation);\n\t\t(void)pl_strcpy(&addr, rcand->domain, sizeof(rcand->domain));\n\n\t\terr = re_thread_async(getaddr_rcand, delayed_rcand, rcand);\n\t\tif (err)\n\t\t\tmem_deref(rcand);\n\n\t\t/* AF_INET6 IPv6 candidate\n\t\t * mDNS resolving can lead to long timeouts (~5s), so it's\n\t\t * better to resolve IPv4 and IPv6 separately to avoid long ice\n\t\t * startup delays.\n\t\t */\n\t\tstruct rcand *rcand6 =\n\t\t\tmem_zalloc(sizeof(struct rcand), rcand_dealloc);\n\t\tif (!rcand6)\n\t\t\treturn ENOMEM;\n\n\t\trcand6->ai_family = AF_INET6;\n\t\trcand6->icem\t  = mem_ref(icem);\n\t\trcand6->type\t  = ice_cand_name2type(type);\n\t\trcand6->cid\t  = cid;\n\t\trcand6->prio\t  = pl_u32(&prio);\n\t\trcand6->port\t  = pl_u32(&port);\n\t\trcand6->rel_addr  = rel_addr;\n\n\t\tpl_dup(&rcand6->foundation, &foundation);\n\t\t(void)pl_strcpy(&addr, rcand6->domain, sizeof(rcand6->domain));\n\n\t\terr = re_thread_async(getaddr_rcand, delayed_rcand, rcand6);\n\t\tif (err)\n\t\t\tmem_deref(rcand6);\n\n\t\treturn err;\n\t}\n\n\terr = sa_set(&caddr, &addr, pl_u32(&port));\n\tif (err)\n\t\treturn err;\n\n\t/* add only if not exist */\n\tif (icem_cand_find(&icem->rcandl, cid, &caddr))\n\t\treturn 0;\n\n\treturn icem_rcand_add(icem, ice_cand_name2type(type), cid,\n\t\t\t      pl_u32(&prio), &caddr, &rel_addr, &foundation);\n}\n\n\n/**\n * Decode SDP session attributes\n *\n * @param icem  ICE Media object\n * @param name  Name of the SDP attribute\n * @param value Value of the SDP attribute (optional)\n *\n * @return 0 if success, otherwise errorcode\n */\nint ice_sdp_decode(struct icem *icem, const char *name, const char *value)\n{\n\tif (!icem)\n\t\treturn EINVAL;\n\n\tif (0 == str_casecmp(name, ice_attr_lite)) {\n\t\ticem->rmode_lite = true;\n\t\ticem->lrole = ICE_ROLE_CONTROLLING;\n\t}\n\telse if (0 == str_casecmp(name, ice_attr_ufrag))\n\t\treturn ufrag_decode(icem, value);\n\telse if (0 == str_casecmp(name, ice_attr_pwd))\n\t\treturn pwd_decode(icem, value);\n\n\treturn 0;\n}\n\n\n/**\n * Decode SDP media attributes\n *\n * @param icem  ICE Media object\n * @param name  Name of the SDP attribute\n * @param value Value of the SDP attribute (optional)\n *\n * @return 0 if success, otherwise errorcode\n */\nint icem_sdp_decode(struct icem *icem, const char *name, const char *value)\n{\n\tif (!icem)\n\t\treturn EINVAL;\n\n\tif (0 == str_casecmp(name, ice_attr_cand))\n\t\treturn cand_decode(icem, value);\n\telse if (0 == str_casecmp(name, ice_attr_mismatch))\n\t\ticem->mismatch = true;\n\telse if (0 == str_casecmp(name, ice_attr_ufrag))\n\t\treturn media_ufrag_decode(icem, value);\n\telse if (0 == str_casecmp(name, ice_attr_pwd))\n\t\treturn media_pwd_decode(icem, value);\n\n\treturn 0;\n}\n\n\nstatic const char *ice_tcptype_name(enum ice_tcptype tcptype)\n{\n\tswitch (tcptype) {\n\n\tcase ICE_TCP_ACTIVE:  return \"active\";\n\tcase ICE_TCP_PASSIVE: return \"passive\";\n\tcase ICE_TCP_SO:      return \"so\";\n\tdefault: return \"???\";\n\t}\n}\n\n\nstatic enum ice_tcptype ice_tcptype_resolve(const struct pl *pl)\n{\n\tif (0 == pl_strcasecmp(pl, \"active\"))  return ICE_TCP_ACTIVE;\n\tif (0 == pl_strcasecmp(pl, \"passive\")) return ICE_TCP_PASSIVE;\n\tif (0 == pl_strcasecmp(pl, \"so\"))      return ICE_TCP_SO;\n\n\treturn (enum ice_tcptype)-1;\n}\n\n\nint ice_cand_attr_encode(struct re_printf *pf,\n\t\t\t const struct ice_cand_attr *cand)\n{\n\tint err = 0;\n\n\tif (!cand)\n\t\treturn 0;\n\n\terr |= re_hprintf(pf, \"%s %u %s %u %j %u typ %s\",\n\t\t\t  cand->foundation, cand->compid,\n\t\t\t  net_proto2name(cand->proto), cand->prio,\n\t\t\t  &cand->addr, sa_port(&cand->addr),\n\t\t\t  ice_cand_type2name(cand->type));\n\n\tif (sa_isset(&cand->rel_addr, SA_ADDR))\n\t\terr |= re_hprintf(pf, \" raddr %j\", &cand->rel_addr);\n\n\tif (sa_isset(&cand->rel_addr, SA_PORT))\n\t\terr |= re_hprintf(pf, \" rport %u\", sa_port(&cand->rel_addr));\n\n\tif (cand->proto == IPPROTO_TCP) {\n\t\terr |= re_hprintf(pf, \" tcptype %s\",\n\t\t\t\t  ice_tcptype_name(cand->tcptype));\n\t}\n\n\treturn err;\n}\n\n\nint ice_cand_attr_decode(struct ice_cand_attr *cand, const char *val)\n{\n\tstruct pl pl_fnd, pl_compid, pl_transp, pl_prio, pl_addr, pl_port;\n\tstruct pl pl_type, pl_raddr, pl_rport, pl_opt = PL_INIT;\n\tsize_t len;\n\tchar type[8];\n\tint err;\n\n\tif (!cand || !val)\n\t\treturn EINVAL;\n\n\tmemset(cand, 0, sizeof(*cand));\n\n\tlen = str_len(val);\n\n\terr = re_regex(val, len,\n\t\t       \"[^ ]+ [0-9]+ [a-z]+ [0-9]+ [^ ]+ [0-9]+ typ [a-z]+\"\n\t\t       \"[^]*\",\n\t\t       &pl_fnd, &pl_compid, &pl_transp, &pl_prio,\n\t\t       &pl_addr, &pl_port, &pl_type, &pl_opt);\n\tif (err)\n\t\treturn err;\n\n\t(void)pl_strcpy(&pl_fnd, cand->foundation, sizeof(cand->foundation));\n\n\tif (0 == pl_strcasecmp(&pl_transp, \"UDP\"))\n\t\tcand->proto = IPPROTO_UDP;\n\telse if (0 == pl_strcasecmp(&pl_transp, \"TCP\"))\n\t\tcand->proto = IPPROTO_TCP;\n\telse\n\t\tcand->proto = 0;\n\n\terr = sa_set(&cand->addr, &pl_addr, pl_u32(&pl_port));\n\tif (err)\n\t\treturn err;\n\n\tcand->compid = pl_u32(&pl_compid);\n\tcand->prio   = pl_u32(&pl_prio);\n\n\t(void)pl_strcpy(&pl_type, type, sizeof(type));\n\n\tcand->type = ice_cand_name2type(type);\n\n\t/* optional */\n\n\tif (0 == re_regex(pl_opt.p, pl_opt.l, \"raddr [^ ]+ rport [0-9]+\",\n\t\t\t  &pl_raddr, &pl_rport)) {\n\n\t\terr = sa_set(&cand->rel_addr, &pl_raddr, pl_u32(&pl_rport));\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (cand->proto == IPPROTO_TCP) {\n\n\t\tstruct pl tcptype;\n\n\t\terr = re_regex(pl_opt.p, pl_opt.l, \"tcptype [^ ]+\",\n\t\t\t       &tcptype);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tcand->tcptype = ice_tcptype_resolve(&tcptype);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/ice/icestr.c",
    "content": "/**\n * @file icestr.c  ICE Strings\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include \"ice.h\"\n\n\nconst char *ice_cand_type2name(enum ice_cand_type type)\n{\n\tswitch (type) {\n\n\tcase ICE_CAND_TYPE_HOST:  return \"host\";\n\tcase ICE_CAND_TYPE_SRFLX: return \"srflx\";\n\tcase ICE_CAND_TYPE_PRFLX: return \"prflx\";\n\tcase ICE_CAND_TYPE_RELAY: return \"relay\";\n\tdefault:                  return \"???\";\n\t}\n}\n\n\nenum ice_cand_type ice_cand_name2type(const char *name)\n{\n\tif (0 == str_casecmp(name, \"host\"))  return ICE_CAND_TYPE_HOST;\n\tif (0 == str_casecmp(name, \"srflx\")) return ICE_CAND_TYPE_SRFLX;\n\tif (0 == str_casecmp(name, \"prflx\")) return ICE_CAND_TYPE_PRFLX;\n\tif (0 == str_casecmp(name, \"relay\")) return ICE_CAND_TYPE_RELAY;\n\n\treturn (enum ice_cand_type)-1;\n}\n\n\nconst char *ice_role2name(enum ice_role role)\n{\n\tswitch (role) {\n\n\tcase ICE_ROLE_UNKNOWN:     return \"Unknown\";\n\tcase ICE_ROLE_CONTROLLING: return \"Controlling\";\n\tcase ICE_ROLE_CONTROLLED:  return \"Controlled\";\n\tdefault:               return \"???\";\n\t}\n}\n\n\nconst char *ice_candpair_state2name(enum ice_candpair_state st)\n{\n\tswitch (st) {\n\n\tcase ICE_CANDPAIR_FROZEN:     return \"Frozen\";\n\tcase ICE_CANDPAIR_WAITING:    return \"Waiting\";\n\tcase ICE_CANDPAIR_INPROGRESS: return \"InProgress\";\n\tcase ICE_CANDPAIR_SUCCEEDED:  return \"Succeeded\";\n\tcase ICE_CANDPAIR_FAILED:     return \"Failed\";\n\tdefault:                      return \"???\";\n\t}\n}\n\n\nconst char *ice_checkl_state2name(enum ice_checkl_state cst)\n{\n\tswitch (cst) {\n\n\tcase ICE_CHECKLIST_NULL:      return \"(NULL)\";\n\tcase ICE_CHECKLIST_RUNNING:   return \"Running\";\n\tcase ICE_CHECKLIST_COMPLETED: return \"Completed\";\n\tcase ICE_CHECKLIST_FAILED:    return \"Failed\";\n\tdefault:                      return \"???\";\n\t}\n}\n"
  },
  {
    "path": "src/ice/stunsrv.c",
    "content": "/**\n * @file stunsrv.c  Basic STUN Server for Connectivity checks\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include <re_sys.h>\n#include \"ice.h\"\n\n\n#define DEBUG_MODULE \"stunsrv\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic const char *sw = \"ice stunsrv v\" RE_VERSION \" (\" ARCH \"/\" OS \")\";\n\n\nstatic void triggered_check(struct icem *icem, struct ice_cand *lcand,\n\t\t\t    struct ice_cand *rcand)\n{\n\tstruct ice_candpair *cp = NULL;\n\tint err;\n\n\tif (lcand && rcand)\n\t\tcp = icem_candpair_find(&icem->checkl, lcand, rcand);\n\n\tif (cp) {\n\n\t\tswitch (cp->state) {\n\n#if 0\n\t\t\t/* TODO: I am not sure why we should cancel the\n\t\t\t *       pending Connectivity check here. this\n\t\t\t *       can lead to a deadlock situation where\n\t\t\t *       both agents are stuck on sending\n\t\t\t *       triggered checks on the same candidate pair\n\t\t\t */\n\t\tcase ICE_CANDPAIR_INPROGRESS:\n\t\t\ticem_candpair_cancel(cp);\n\t\t\t/*@fallthrough@*/\n#endif\n\n\t\tcase ICE_CANDPAIR_FAILED:\n\t\t\ticem_candpair_set_state(cp, ICE_CANDPAIR_WAITING);\n\t\t\t/*@fallthrough@*/\n\n\t\tcase ICE_CANDPAIR_FROZEN:\n\t\tcase ICE_CANDPAIR_WAITING:\n\t\t\terr = icem_conncheck_send(cp, false, true);\n\t\t\tif (err) {\n\t\t\t\tDEBUG_WARNING(\"triggered check failed\\n\");\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase ICE_CANDPAIR_SUCCEEDED:\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\telse {\n\n#if 0\n\t\terr = icem_candpair_alloc(&cp, icem, lcand, rcand);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"failed to allocate candpair:\"\n\t\t\t\t      \" lcand=%p rcand=%p (%m)\\n\",\n\t\t\t\t      lcand, rcand, err);\n\t\t\treturn;\n\t\t}\n\n\t\ticem_candpair_prio_order(&icem->checkl);\n\n\t\ticem_candpair_set_state(cp, ICE_CANDPAIR_WAITING);\n\n\t\t(void)icem_conncheck_send(cp, false, true);\n#endif\n\n\t}\n}\n\n\n/*\n * 7.2.1.  Additional Procedures for Full Implementations\n */\nstatic int handle_stun_full(struct icem *icem,\n\t\t\t    struct icem_comp *comp, const struct sa *src,\n\t\t\t    uint32_t prio, bool use_cand, bool tunnel)\n{\n\tstruct ice_cand *lcand = NULL, *rcand;\n\tstruct ice_candpair *cp = NULL;\n\tint err;\n\n\trcand = icem_cand_find(&icem->rcandl, comp->id, src);\n\tif (!rcand) {\n\t\terr = icem_rcand_add_prflx(&rcand, icem, comp->id, prio, src);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tcp = icem_candpair_find_rcand(icem, rcand);\n\tif (cp)\n\t\tlcand = cp->lcand;\n\telse\n\t\tlcand = icem_lcand_find_checklist(icem, comp->id);\n\n\tif (!lcand) {\n\t\tDEBUG_NOTICE(\"{%s.%u} local candidate not found\"\n\t\t\t     \" (checklist=%u) (src=%J)\\n\",\n\t\t\t     icem->name, comp->id,\n\t\t\t     list_count(&icem->checkl), src);\n\t\treturn 0;\n\t}\n\n\ttriggered_check(icem, lcand, rcand);\n\n\tif (!cp) {\n\t\tcp = icem_candpair_find_rcand(icem, rcand);\n\t\tif (!cp) {\n\t\t\tDEBUG_WARNING(\"{%s.%u} candidate pair not found:\"\n\t\t\t\t      \" source=%J\\n\",\n\t\t\t\t      icem->name, comp->id, src);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n#if ICE_TRACE\n\ticecomp_printf(comp, \"Rx Binding Request from %J via %s\"\n\t\t       \" (candpair=%s) %s\\n\",\n\t\t       src, tunnel ? \"Tunnel\" : \"Socket\",\n\t\t       ice_candpair_state2name(cp->state),\n\t\t       use_cand ? \"[USE]\" : \"\");\n#else\n\t(void)tunnel;\n#endif\n\n\t/* 7.2.1.5.  Updating the Nominated Flag */\n\tif (use_cand) {\n\t\tif (icem->lrole == ICE_ROLE_CONTROLLED &&\n\t\t    cp->state == ICE_CANDPAIR_SUCCEEDED) {\n\n\t\t\tif (!cp->nominated) {\n\t\t\t\ticecomp_printf(comp, \"setting NOMINATED\"\n\t\t\t\t\t       \" flag on candpair [%H]\\n\",\n\t\t\t\t\t       icem_candpair_debug, cp);\n\t\t\t}\n\n\t\t\tcp->nominated = true;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\nstatic int stunsrv_ereply(struct icem_comp *comp, const struct sa *src,\n\t\t\t  size_t presz, const struct stun_msg *req,\n\t\t\t  uint16_t scode, const char *reason)\n{\n\tstruct icem *icem = comp->icem;\n\n\treturn stun_ereply(icem->proto, comp->sock, src, presz, req,\n\t\t\t   scode, reason,\n\t\t\t   (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 1,\n\t\t\t   STUN_ATTR_SOFTWARE, sw);\n}\n\n\nint icem_stund_recv(struct icem_comp *comp, const struct sa *src,\n\t\t    struct stun_msg *req, size_t presz)\n{\n\tstruct icem *icem = comp->icem;\n\tstruct stun_attr *attr;\n\tstruct pl lu, ru;\n\tenum ice_role rrole = ICE_ROLE_UNKNOWN;\n\tuint64_t tiebrk = 0;\n\tuint32_t prio_prflx;\n\tbool use_cand = false;\n\tint err;\n\n\t/* RFC 5389: Fingerprint errors are silently discarded */\n\terr = stun_msg_chk_fingerprint(req);\n\tif (err)\n\t\treturn err;\n\n\terr = stun_msg_chk_mi(req, (uint8_t *)icem->lpwd, strlen(icem->lpwd));\n\tif (err) {\n\t\tif (err == EBADMSG)\n\t\t\tgoto unauth;\n\t\telse\n\t\t\tgoto badmsg;\n\t}\n\n\tattr = stun_msg_attr(req, STUN_ATTR_USERNAME);\n\tif (!attr)\n\t\tgoto badmsg;\n\n\terr = re_regex(attr->v.username, strlen(attr->v.username),\n\t\t       \"[^:]+:[^]+\", &lu, &ru);\n\tif (err) {\n\t\tDEBUG_WARNING(\"could not parse USERNAME attribute (%s)\\n\",\n\t\t\t      attr->v.username);\n\t\tgoto unauth;\n\t}\n\tif (pl_strcmp(&lu, icem->lufrag))\n\t\tgoto unauth;\n\tif (str_isset(icem->rufrag) && pl_strcmp(&ru, icem->rufrag))\n\t\tgoto unauth;\n\n\tattr = stun_msg_attr(req, STUN_ATTR_CONTROLLED);\n\tif (attr) {\n\t\trrole = ICE_ROLE_CONTROLLED;\n\t\ttiebrk = attr->v.uint64;\n\t}\n\n\tattr = stun_msg_attr(req, STUN_ATTR_CONTROLLING);\n\tif (attr) {\n\t\trrole = ICE_ROLE_CONTROLLING;\n\t\ttiebrk = attr->v.uint64;\n\t}\n\n\tif (rrole == icem->lrole) {\n\t\tif (icem->tiebrk >= tiebrk)\n\t\t\tice_switch_local_role(icem);\n\t\telse\n\t\t\tgoto conflict;\n\t}\n\n\tattr = stun_msg_attr(req, STUN_ATTR_PRIORITY);\n\tif (attr)\n\t\tprio_prflx = attr->v.uint32;\n\telse\n\t\tgoto badmsg;\n\n\tattr = stun_msg_attr(req, STUN_ATTR_USE_CAND);\n\tif (attr) {\n\t\tuse_cand = true;\n\t}\n\n\tif (rrole == ICE_ROLE_CONTROLLED && use_cand) {\n\t\tDEBUG_NOTICE(\"remote peer is Controlled and\"\n\t\t\t      \" should not send USE-CANDIDATE\\n\");\n\t}\n\n\terr = handle_stun_full(icem, comp, src, prio_prflx,\n\t\t\t       use_cand, presz > 0);\n\n\tif (err)\n\t\tgoto badmsg;\n\n\treturn stun_reply(icem->proto, comp->sock, src, presz, req,\n\t\t\t  (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 2,\n\t\t\t  STUN_ATTR_XOR_MAPPED_ADDR, src,\n\t\t\t  STUN_ATTR_SOFTWARE, sw);\n\n badmsg:\n\treturn stunsrv_ereply(comp, src, presz, req, 400, \"Bad Request\");\n\n unauth:\n\treturn stunsrv_ereply(comp, src, presz, req, 401, \"Unauthorized\");\n\n conflict:\n\treturn stunsrv_ereply(comp, src, presz, req, 487, \"Role Conflict\");\n}\n"
  },
  {
    "path": "src/ice/util.c",
    "content": "/**\n * @file ice/util.c  ICE Utilities\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#ifdef HAVE_SYS_TIME_H\n#include <sys/time.h>\n#endif\n#ifndef WIN32\n#include <time.h>\n#endif\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_stun.h>\n#include <re_sys.h>\n#include <re_ice.h>\n#include \"ice.h\"\n\n\n#define DEBUG_MODULE \"iceutil\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tCAND_PRIO_RELAY =   0,\n\tCAND_PRIO_SRFLX = 100,\n\tCAND_PRIO_PRFLX = 110,\n\tCAND_PRIO_HOST  = 126\n};\n\n\nstatic uint32_t type_prio(enum ice_cand_type type)\n{\n\tswitch (type) {\n\n\tcase ICE_CAND_TYPE_HOST:   return CAND_PRIO_HOST;\n\tcase ICE_CAND_TYPE_SRFLX:  return CAND_PRIO_SRFLX;\n\tcase ICE_CAND_TYPE_PRFLX:  return CAND_PRIO_PRFLX;\n\tcase ICE_CAND_TYPE_RELAY:  return CAND_PRIO_RELAY;\n\tdefault: return 0;\n\t}\n}\n\n\nuint32_t ice_cand_calc_prio(enum ice_cand_type type, uint16_t lpref,\n\t\t\t    unsigned compid)\n{\n\treturn type_prio(type)<<24 | (uint32_t)lpref<<8 | (256 - compid);\n}\n\n\n/*\n * g = controlling agent\n * d = controlled agent\n\n pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)\n\n */\nuint64_t ice_calc_pair_prio(uint32_t g, uint32_t d)\n{\n\tconst uint64_t m = min(g, d);\n\tconst uint64_t x = max(g, d);\n\n\treturn (m<<32) + 2*x + (g>d?1:0);\n}\n\n\nvoid ice_switch_local_role(struct icem *icem)\n{\n\tenum ice_role new_role;\n\n\tif (ICE_ROLE_CONTROLLING == icem->lrole)\n\t\tnew_role = ICE_ROLE_CONTROLLED;\n\telse\n\t\tnew_role = ICE_ROLE_CONTROLLING;\n\n\tDEBUG_NOTICE(\"Switch local role from %s to %s\\n\",\n\t\t     ice_role2name(icem->lrole), ice_role2name(new_role));\n\n\ticem->lrole = new_role;\n\n#if 0\n\t/* recompute pair priorities for all media streams */\n\tfor (le = icem->le.list->head; le; le = le->next) {\n\t\ticem = le->data;\n\t\ticem_candpair_prio_order(&icem->checkl);\n\t}\n#endif\n}\n\n\n/**\n * Remove duplicate elements from list, preserving order\n *\n * @param list  Linked list\n * @param uh    Unique handler (return object to remove)\n *\n * @return Number of elements removed\n *\n * @note:    O (n ^ 2)\n */\nuint32_t ice_list_unique(struct list *list, list_unique_h *uh)\n{\n\tstruct le *le1 = list_head(list);\n\tuint32_t n = 0;\n\n\twhile (le1 && le1 != list->tail) {\n\n\t\tstruct le *le2 = le1->next;\n\t\tvoid *data = NULL;\n\n\t\twhile (le2) {\n\n\t\t\tdata = uh(le1, le2);\n\n\t\t\tle2 = le2->next;\n\n\t\t\tif (!data)\n\t\t\t\tcontinue;\n\n\t\t\tif (le1->data == data)\n\t\t\t\tbreak;\n\t\t\telse {\n\t\t\t\tdata = mem_deref(data);\n\t\t\t\t++n;\n\t\t\t}\n\t\t}\n\n\t\tle1 = le1->next;\n\n\t\tif (data) {\n\t\t\tmem_deref(data);\n\t\t\t++n;\n\t\t}\n\t}\n\n\treturn n;\n}\n"
  },
  {
    "path": "src/json/decode.c",
    "content": "/**\n * @file json/decode.c  JSON decoder\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_odict.h>\n#include <re_json.h>\n\n\nstatic inline long double mypower10(uint64_t e)\n{\n\tlong double p = 10, n = 1;\n\n\twhile (e > 0) {\n\t\tif (e & 1)\n\t\t\tn *= p;\n\n\t\tp *= p;\n\t\te >>= 1;\n\t}\n\n\treturn n;\n}\n\n\nstatic bool is_string(struct pl *c, const struct pl *pl)\n{\n\tif (pl->l < 2)\n\t\treturn false;\n\n\tif (pl->p[0] != '\"'|| pl->p[pl->l-1] != '\"')\n\t\treturn false;\n\n\tc->p = pl->p + 1;\n\tc->l = pl->l - 2;\n\n\treturn true;\n}\n\n\nstatic bool is_number(long double *d, bool *isfloat, const struct pl *pl)\n{\n\tbool neg = false, pos = false, frac = false, exp = false;\n\tlong double v = 0, mul = 1;\n\tconst char *p;\n\tint64_t e = 0;\n\n\tif (!pl->l)\n\t\treturn false;\n\n\tp = &pl->p[pl->l - 1];\n\n\twhile (p >= pl->p) {\n\t\tconst char ch = *p;\n\t\t--p;\n\n\t\tif (ch == 'e' || ch == 'E') {\n\n\t\t\tif (exp || frac)\n\t\t\t\treturn false;\n\n\t\t\texp = true;\n\t\t\te   = (int64_t)(neg ? -v : v);\n\t\t\tv   = 0;\n\t\t\tmul = 1;\n\t\t\tneg = false;\n\t\t\tpos = false;\n\t\t}\n\t\telse if (pos || neg) {\n\t\t\treturn false;\n\t\t}\n\t\telse if (ch == '.') {\n\n\t\t\tif (frac)\n\t\t\t\treturn false;\n\n\t\t\tfrac = true;\n\t\t\tv /= mul;\n\t\t\tmul = 1;\n\t\t}\n\t\telse if ('0' <= ch && ch <= '9') {\n\t\t\tv += mul * (ch - '0');\n\t\t\tmul *= 10;\n\t\t}\n\t\telse if (ch == '-') {\n\t\t\tneg = true;\n\t\t}\n\t\telse if (ch == '+') {\n\t\t\tpos = true;\n\t\t}\n\t\telse {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t*isfloat = (frac || exp);\n\n\tif (exp) {\n\t\tif (e < 0)\n\t\t\tv /= mypower10(-e);\n\t\telse\n\t\t\tv *= mypower10(e);\n\t}\n\n\tif (neg)\n\t\tv = -v;\n\n\t*d = v;\n\n\treturn true;\n}\n\n\nstatic int decode_name(char **str, const struct pl *pl)\n{\n\tstruct pl pls;\n\n\tif (!pl->p)\n\t\treturn EBADMSG;\n\n\tif (!is_string(&pls, pl))\n\t\treturn EBADMSG;\n\n\treturn re_sdprintf(str, \"%H\", utf8_decode, &pls);\n}\n\n\nstatic int decode_value(struct json_value *val, const struct pl *pl)\n{\n\tlong double dbl;\n\tstruct pl pls;\n\tbool isfloat;\n\tint err = 0;\n\n\tif (!pl->p)\n\t\treturn EBADMSG;\n\n\tif (is_string(&pls, pl)) {\n\n\t\terr = re_sdprintf(&val->v.str, \"%H\", utf8_decode, &pls);\n\t\tval->type = JSON_STRING;\n\t}\n\telse if (is_number(&dbl, &isfloat, pl)) {\n\n\t\tif (isfloat) {\n\t\t\tval->type  = JSON_DOUBLE;\n\t\t\tval->v.dbl = dbl;\n\t\t}\n\t\telse {\n\t\t\tval->type      = JSON_INT;\n\t\t\tval->v.integer = (int64_t)dbl;\n\t\t}\n\t}\n\telse if (!pl_strcasecmp(pl, \"false\")) {\n\n\t\tval->v.boolean = false;\n\t\tval->type  = JSON_BOOL;\n\t}\n\telse if (!pl_strcasecmp(pl, \"true\")) {\n\n\t\tval->v.boolean = true;\n\t\tval->type  = JSON_BOOL;\n\t}\n\telse if (!pl_strcasecmp(pl, \"null\")) {\n\n\t\tval->type = JSON_NULL;\n\t}\n\telse {\n\t\terr = EBADMSG;\n\t}\n\n\treturn err;\n}\n\n\nstatic int object_entry(const struct pl *pl_name, const struct pl *pl_val,\n\t\t\tjson_object_entry_h *oeh, void *arg)\n{\n\tstruct json_value val;\n\tchar *name;\n\tint err;\n\n\terr = decode_name(&name, pl_name);\n\tif (err)\n\t\treturn err;\n\n\terr = decode_value(&val, pl_val);\n\tif (err)\n\t\tgoto out;\n\n\tif (oeh)\n\t\terr = oeh(name, &val, arg);\n\n\tif (val.type == JSON_STRING)\n\t\tmem_deref(val.v.str);\n\n out:\n\tmem_deref(name);\n\n\treturn err;\n}\n\n\nstatic int array_entry(unsigned idx, const struct pl *pl_val,\n\t\t       json_array_entry_h *aeh, void *arg)\n{\n\tstruct json_value val;\n\tint err;\n\n\terr = decode_value(&val, pl_val);\n\tif (err)\n\t\treturn err;\n\n\tif (aeh)\n\t\terr = aeh(idx, &val, arg);\n\n\tif (val.type == JSON_STRING)\n\t\tmem_deref(val.v.str);\n\n\treturn err;\n}\n\n\nstatic int object_start(const struct pl *pl_name, unsigned idx,\n\t\t\tstruct json_handlers *h)\n{\n\tchar *name = NULL;\n\tint err = 0;\n\n\tif (pl_name->p) {\n\n\t\terr = decode_name(&name, pl_name);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (h->oh)\n\t\terr = h->oh(name, idx, h);\n\n\tmem_deref(name);\n\n\treturn err;\n}\n\n\nstatic int array_start(const struct pl *pl_name, unsigned idx,\n\t\t       struct json_handlers *h)\n{\n\tchar *name = NULL;\n\tint err = 0;\n\n\tif (pl_name->p) {\n\n\t\terr = decode_name(&name, pl_name);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (h->ah)\n\t\terr = h->ah(name, idx, h);\n\n\tmem_deref(name);\n\n\treturn err;\n}\n\n\nstatic inline int chkval(struct pl *val, const char *p)\n{\n\tif (!val->p || p<val->p)\n\t\treturn EINVAL;\n\n\tval->l = p - val->p;\n\n\treturn 0;\n}\n\n\nstatic int _json_decode(const char **str, size_t *len,\n\t\t\tunsigned depth, unsigned maxdepth,\n\t\t\tjson_object_h *oh, json_array_h *ah,\n\t\t\tjson_object_entry_h *oeh, json_array_entry_h *aeh,\n\t\t\tvoid *arg)\n{\n\tbool esc = false, inquot = false, inobj = false, inarray = false;\n\tstruct pl name = PL_INIT, val = PL_INIT;\n\tsize_t ws = 0;\n\tunsigned idx = 0;\n\tint err;\n\n\tfor (; *len>0; ++(*str), --(*len)) {\n\n\t\tif (inquot) {\n\t\t\tif (esc)\n\t\t\t\tesc = false;\n\t\t\telse if (**str == '\\\"')\n\t\t\t\tinquot = false;\n\t\t\telse if (**str == '\\\\')\n\t\t\t\tesc = true;\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tswitch (**str) {\n\n\t\tcase ':':\n\t\t\tif (!inobj || name.p || chkval(&val, *str - ws))\n\t\t\t\treturn EBADMSG;\n\n\t\t\tname = val;\n\t\t\tval  = pl_null;\n\t\t\tbreak;\n\n\t\tcase ',':\n\t\t\tif (chkval(&val, *str - ws))\n\t\t\t\tbreak;\n\n\t\t\tif (inobj) {\n\n\t\t\t\tif (!name.p)\n\t\t\t\t\treturn EBADMSG;\n\n\t\t\t\terr = object_entry(&name, &val, oeh, arg);\n\t\t\t\tif (err)\n\t\t\t\t\treturn err;\n\t\t\t}\n\t\t\telse if (inarray) {\n\n\t\t\t\terr = array_entry(idx, &val, aeh, arg);\n\t\t\t\tif (err)\n\t\t\t\t\treturn err;\n\n\t\t\t\t++idx;\n\t\t\t}\n\t\t\telse\n\t\t\t\treturn EBADMSG;\n\n\t\t\tname = pl_null;\n\t\t\tval  = pl_null;\n\t\t\tbreak;\n\n\t\tcase '{':\n\t\t\tif (inobj || inarray) {\n\n\t\t\t\tstruct json_handlers h = {oh,ah,oeh,aeh,arg};\n\n\t\t\t\tif (depth >= maxdepth)\n\t\t\t\t\treturn EOVERFLOW;\n\n\t\t\t\tif (inobj && !name.p)\n\t\t\t\t\treturn EBADMSG;\n\n\t\t\t\terr = object_start(&name, idx, &h);\n\t\t\t\tif (err)\n\t\t\t\t\treturn err;\n\n\t\t\t\tname = pl_null;\n\n\t\t\t\terr = _json_decode(str, len, depth + 1,\n\t\t\t\t\t\t   maxdepth, h.oh, h.ah,\n\t\t\t\t\t\t   h.oeh, h.aeh, h.arg);\n\t\t\t\tif (err)\n\t\t\t\t\treturn err;\n\n\t\t\t\tif (inarray)\n\t\t\t\t\t++idx;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tinobj = true;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase '[':\n\t\t\tif (inobj || inarray) {\n\n\t\t\t\tstruct json_handlers h = {oh,ah,oeh,aeh,arg};\n\n\t\t\t\tif (depth >= maxdepth)\n\t\t\t\t\treturn EOVERFLOW;\n\n\t\t\t\tif (inobj && !name.p)\n\t\t\t\t\treturn EBADMSG;\n\n\t\t\t\terr = array_start(&name, idx, &h);\n\t\t\t\tif (err)\n\t\t\t\t\treturn err;\n\n\t\t\t\tname = pl_null;\n\n\t\t\t\terr = _json_decode(str, len, depth + 1,\n\t\t\t\t\t\t   maxdepth, h.oh, h.ah,\n\t\t\t\t\t\t   h.oeh, h.aeh, h.arg);\n\t\t\t\tif (err)\n\t\t\t\t\treturn err;\n\n\t\t\t\tif (inarray)\n\t\t\t\t\t++idx;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tinarray = true;\n\t\t\t\tidx = 0;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase '}':\n\t\t\tif (!inobj)\n\t\t\t\treturn EBADMSG;\n\n\t\t\tif (chkval(&val, *str - ws))\n\t\t\t\treturn 0;\n\n\t\t\tif (!name.p)\n\t\t\t\treturn EBADMSG;\n\n\t\t\treturn object_entry(&name, &val, oeh, arg);\n\n\t\tcase ']':\n\t\t\tif (!inarray)\n\t\t\t\treturn EBADMSG;\n\n\t\t\tif (chkval(&val, *str - ws))\n\t\t\t\treturn 0;\n\n\t\t\treturn array_entry(idx, &val, aeh, arg);\n\n\t\tcase ' ':\n\t\tcase '\\t':\n\t\tcase '\\r':\n\t\tcase '\\n':\n\t\t\t++ws;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tif (val.p)\n\t\t\t\tbreak;\n\n\t\t\tif (**str == '\\\"')\n\t\t\t\tinquot = true;\n\t\t\tval.p = *str;\n\t\t\tval.l = 0;\n\t\t\tws = 0;\n\n\t\t\tif (!inobj && !inarray) {\n\t\t\t\tval.l = *len;\n\t\t\t\tif (array_entry(idx, &val, aeh, arg))\n\t\t\t\t\tval.l = 0;\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (inobj || inarray)\n\t\treturn EBADMSG;\n\n\treturn 0;\n}\n\n\nint json_decode(const char *str, size_t len, unsigned maxdepth,\n\t\tjson_object_h *oh, json_array_h *ah,\n\t\tjson_object_entry_h *oeh, json_array_entry_h *aeh, void *arg)\n{\n\tif (!str)\n\t\treturn EINVAL;\n\n\treturn _json_decode(&str, &len, 0, maxdepth, oh, ah, oeh, aeh, arg);\n}\n"
  },
  {
    "path": "src/json/decode_odict.c",
    "content": "/**\n * @file json/decode_odict.c  JSON odict decode\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_odict.h>\n#include <re_json.h>\n\n\nstatic int container_add(const char *name, unsigned idx,\n\t\t\t enum odict_type type, struct json_handlers *h)\n{\n\tstruct odict *o = h->arg, *oc;\n\tchar index[64];\n\tint err;\n\n\tif (!name) {\n\t\tif (re_snprintf(index, sizeof(index), \"%u\", idx) < 0)\n\t\t\treturn ENOMEM;\n\n\t\tname = index;\n\t}\n\n\terr = odict_alloc(&oc, hash_bsize(o->ht));\n\tif (err)\n\t\treturn err;\n\n\terr = odict_entry_add(o, name, type, oc);\n\tmem_deref(oc);\n\th->arg = oc;\n\n\treturn err;\n}\n\n\nstatic int object_handler(const char *name, unsigned idx,\n\t\t\t  struct json_handlers *h)\n{\n\treturn container_add(name, idx, ODICT_OBJECT, h);\n}\n\n\nstatic int array_handler(const char *name, unsigned idx,\n\t\t\t struct json_handlers *h)\n{\n\treturn container_add(name, idx, ODICT_ARRAY, h);\n}\n\n\nstatic int entry_add(struct odict *o, const char *name,\n\t\t     const struct json_value *val)\n{\n\tswitch (val->type) {\n\n\tcase JSON_STRING:\n\t\treturn odict_entry_add(o, name, ODICT_STRING, val->v.str);\n\n\tcase JSON_INT:\n\t\treturn odict_entry_add(o, name, ODICT_INT, val->v.integer);\n\n\tcase JSON_DOUBLE:\n\t\treturn odict_entry_add(o, name, ODICT_DOUBLE, val->v.dbl);\n\n\tcase JSON_BOOL:\n\t\treturn odict_entry_add(o, name, ODICT_BOOL, val->v.boolean);\n\n\tcase JSON_NULL:\n\t\treturn odict_entry_add(o, name, ODICT_NULL);\n\n\tdefault:\n\t\treturn ENOSYS;\n\t}\n}\n\n\nstatic int object_entry_handler(const char *name, const struct json_value *val,\n\t\t\t\tvoid *arg)\n{\n\tstruct odict *o = arg;\n\n\treturn entry_add(o, name, val);\n}\n\n\nstatic int array_entry_handler(unsigned idx, const struct json_value *val,\n\t\t\t       void *arg)\n{\n\tstruct odict *o = arg;\n\tchar index[64];\n\n\tif (re_snprintf(index, sizeof(index), \"%u\", idx) < 0)\n\t\treturn ENOMEM;\n\n\treturn entry_add(o, index, val);\n}\n\n\nint json_decode_odict(struct odict **op, uint32_t hash_size, const char *str,\n\t\t      size_t len, unsigned maxdepth)\n{\n\tstruct odict *o;\n\tint err;\n\n\tif (!op || !str)\n\t\treturn EINVAL;\n\n\terr = odict_alloc(&o, hash_size);\n\tif (err)\n\t\treturn err;\n\n\terr = json_decode(str, len, maxdepth, object_handler, array_handler,\n\t\t\t  object_entry_handler, array_entry_handler, o);\n\tif (err)\n\t\tmem_deref(o);\n\telse\n\t\t*op = o;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/json/encode.c",
    "content": "/**\n * @file json/encode.c  JSON encoder\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_odict.h>\n#include <re_json.h>\n\n\nstatic int encode_entry(struct re_printf *pf, const struct odict_entry *e)\n{\n\tstruct odict *array;\n\tstruct le *le;\n\tint err;\n\n\tif (!e)\n\t\treturn 0;\n\n\tswitch (odict_entry_type(e)) {\n\n\tcase ODICT_OBJECT:\n\t\terr = json_encode_odict(pf, odict_entry_object(e));\n\t\tbreak;\n\n\tcase ODICT_ARRAY:\n\t\tarray = odict_entry_array(e);\n\t\tif (!array)\n\t\t\treturn 0;\n\n\t\terr = re_hprintf(pf, \"[\");\n\n\t\tfor (le=array->lst.head; le; le=le->next) {\n\n\t\t\tconst struct odict_entry *ae = le->data;\n\n\t\t\terr |= re_hprintf(pf, \"%H%s\",\n\t\t\t\t\t  encode_entry, ae,\n\t\t\t\t\t  le->next ? \",\" : \"\");\n\t\t}\n\n\t\terr |= re_hprintf(pf, \"]\");\n\t\tbreak;\n\n\tcase ODICT_INT:\n\t\terr = re_hprintf(pf, \"%lld\", odict_entry_int(e));\n\t\tbreak;\n\n\tcase ODICT_DOUBLE:\n\t\terr = re_hprintf(pf, \"%f\", odict_entry_dbl(e));\n\t\tbreak;\n\n\tcase ODICT_STRING:\n\t\terr = re_hprintf(pf, \"\\\"%H\\\"\", utf8_encode,\n\t\t\t\t odict_entry_str(e));\n\t\tbreak;\n\n\tcase ODICT_BOOL:\n\t\terr = re_hprintf(pf, \"%s\",\n\t\t\t\t odict_entry_boolean(e) ? \"true\" : \"false\");\n\t\tbreak;\n\n\tcase ODICT_NULL:\n\t\terr = re_hprintf(pf, \"null\");\n\t\tbreak;\n\n\tdefault:\n\t\tre_fprintf(stderr, \"json: unsupported type %d\\n\",\n\t\t\t   odict_entry_type(e));\n\t\terr = EINVAL;\n\t}\n\n\treturn err;\n}\n\n\nint json_encode_odict(struct re_printf *pf, const struct odict *o)\n{\n\tstruct le *le;\n\tint err;\n\n\tif (!o)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"{\");\n\n\tfor (le=o->lst.head; le; le=le->next) {\n\n\t\tconst struct odict_entry *e = le->data;\n\n\t\terr |= re_hprintf(pf, \"\\\"%H\\\":%H%s\",\n\t\t\t\t  utf8_encode, odict_entry_key(e),\n\t\t\t\t  encode_entry, e,\n\t\t\t\t  le->next ? \",\" : \"\");\n\t}\n\n\terr |= re_hprintf(pf, \"}\");\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/list/list.c",
    "content": "/**\n * @file list.c  Linked List implementation\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_list.h>\n#include <re_mem.h>\n\n\n#define DEBUG_MODULE \"list\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/**\n * Initialise a linked list\n *\n * @param list Linked list\n */\nvoid list_init(struct list *list)\n{\n\tif (!list)\n\t\treturn;\n\n\tlist->head = NULL;\n\tlist->tail = NULL;\n\tlist->cnt = 0;\n}\n\n\n/**\n * Flush a linked list and free all elements\n *\n * @param list Linked list\n */\nvoid list_flush(struct list *list)\n{\n\tstruct le *le;\n\n\tif (!list)\n\t\treturn;\n\n\tle = list->head;\n\twhile (le) {\n\t\tstruct le *next = le->next;\n\t\tvoid *data = le->data;\n\n\t\tlist_unlink(le);\n\t\tle->data = NULL;\n\t\tle = next;\n\t\tmem_deref(data);\n\t}\n\n\tlist_init(list);\n}\n\n\n/**\n * Clear a linked list without dereferencing the elements\n *\n * @param list Linked list\n */\nvoid list_clear(struct list *list)\n{\n\tstruct le *le;\n\n\tif (!list)\n\t\treturn;\n\n\tle = list->head;\n\twhile (le) {\n\t\tstruct le *next = le->next;\n\t\tle->list = NULL;\n\t\tle->prev = le->next = NULL;\n\t\tle->data = NULL;\n\t\tle = next;\n\t}\n\n\tlist_init(list);\n}\n\n\n/**\n * Append a list element to a linked list\n *\n * @param list  Linked list\n * @param le    List element\n * @param data  Element data\n */\nvoid list_append(struct list *list, struct le *le, void *data)\n{\n\tif (!list || !le)\n\t\treturn;\n\n\tif (le->list) {\n\t\tDEBUG_WARNING(\"append: le already linked to %p!\\n\", le->list);\n\t\treturn;\n\t}\n\n\tle->prev = list->tail;\n\tle->next = NULL;\n\tle->list = list;\n\tle->data = data;\n\n\tif (!list->head)\n\t\tlist->head = le;\n\n\tif (list->tail)\n\t\tlist->tail->next = le;\n\n\tlist->tail = le;\n\t++list->cnt;\n}\n\n\n/**\n * Prepend a list element to a linked list\n *\n * @param list  Linked list\n * @param le    List element\n * @param data  Element data\n */\nvoid list_prepend(struct list *list, struct le *le, void *data)\n{\n\tif (!list || !le)\n\t\treturn;\n\n\tif (le->list) {\n\t\tDEBUG_WARNING(\"prepend: le linked to %p\\n\", le->list);\n\t\treturn;\n\t}\n\n\tle->prev = NULL;\n\tle->next = list->head;\n\tle->list = list;\n\tle->data = data;\n\n\tif (list->head)\n\t\tlist->head->prev = le;\n\n\tif (!list->tail)\n\t\tlist->tail = le;\n\n\tlist->head = le;\n\t++list->cnt;\n}\n\n\n/**\n * Insert a list element before a given list element\n *\n * @param list  Linked list\n * @param le    Given list element\n * @param ile   List element to insert\n * @param data  Element data\n */\nvoid list_insert_before(struct list *list, struct le *le, struct le *ile,\n\t\t       void *data)\n{\n\tif (!list || !le || !ile)\n\t\treturn;\n\n\tif (ile->list) {\n\t\tDEBUG_WARNING(\"insert_before: le linked to %p\\n\", le->list);\n\t\treturn;\n\t}\n\n\tif (le->prev)\n\t\tle->prev->next = ile;\n\telse if (list->head == le)\n\t\tlist->head = ile;\n\n\tile->prev = le->prev;\n\tile->next = le;\n\tile->list = list;\n\tile->data = data;\n\n\tle->prev = ile;\n\t++list->cnt;\n}\n\n\n/**\n * Insert a list element after a given list element\n *\n * @param list  Linked list\n * @param le    Given list element\n * @param ile   List element to insert\n * @param data  Element data\n */\nvoid list_insert_after(struct list *list, struct le *le, struct le *ile,\n\t\t       void *data)\n{\n\tif (!list || !le || !ile)\n\t\treturn;\n\n\tif (ile->list) {\n\t\tDEBUG_WARNING(\"insert_after: le linked to %p\\n\", le->list);\n\t\treturn;\n\t}\n\n\tif (le->next)\n\t\tle->next->prev = ile;\n\telse if (list->tail == le)\n\t\tlist->tail = ile;\n\n\tile->prev = le;\n\tile->next = le->next;\n\tile->list = list;\n\tile->data = data;\n\n\tle->next = ile;\n\n\t++list->cnt;\n}\n\n\n/**\n * Sorted insert into linked list with order defined by the sort handler\n * (optimized for tail insert)\n *\n * @param list  Linked list\n * @param sh    Sort handler\n * @param arg   Handler argument\n * @param ile   List element to insert\n * @param data  Element data\n */\nvoid list_insert_sorted(struct list *list, list_sort_h *sh, void *arg,\n\t\t\tstruct le *ile, void *data)\n{\n\tstruct le *le;\n\n\tif (!list || !sh)\n\t\treturn;\n\n\tle\t  = list->tail;\n\tile->data = data;\n\n\twhile (le) {\n\t\tif (sh(le, ile, arg)) {\n\t\t\tlist_insert_after(list, le, ile, ile->data);\n\t\t\treturn;\n\t\t}\n\n\t\tle = le->prev;\n\t}\n\n\tlist_prepend(list, ile, ile->data);\n}\n\n\n/**\n * Remove a list element from a linked list\n *\n * @param le    List element to remove\n */\nvoid list_unlink(struct le *le)\n{\n\tstruct list *list;\n\n\tif (!le || !le->list)\n\t\treturn;\n\n\tlist = le->list;\n\n\tif (le->prev)\n\t\tle->prev->next = le->next;\n\telse\n\t\tlist->head = le->next;\n\n\tif (le->next)\n\t\tle->next->prev = le->prev;\n\telse\n\t\tlist->tail = le->prev;\n\n\tle->next = NULL;\n\tle->prev = NULL;\n\tle->list = NULL;\n\n\tif (list->cnt)\n\t\t--list->cnt;\n}\n\n\n/**\n * Sort a linked list in an order defined by the sort handler\n *\n * @param list  Linked list\n * @param sh    Sort handler\n * @param arg   Handler argument\n */\nvoid list_sort(struct list *list, list_sort_h *sh, void *arg)\n{\n\tstruct le *le;\n\tbool sort;\n\n\tif (!list || !sh)\n\t\treturn;\n\n retry:\n\tle = list->head;\n\tsort = false;\n\n\twhile (le && le->next) {\n\n\t\tif (sh(le, le->next, arg)) {\n\n\t\t\tle = le->next;\n\t\t}\n\t\telse {\n\t\t\tstruct le *tle = le->next;\n\n\t\t\tlist_unlink(le);\n\t\t\tlist_insert_after(list, tle, le, le->data);\n\t\t\tsort = true;\n\t\t}\n\t}\n\n\tif (sort) {\n\t\tgoto retry;\n\t}\n}\n\n\n/**\n * Call the apply handler for each element in a linked list\n *\n * @param list  Linked list\n * @param fwd   true to traverse from head to tail, false for reverse\n * @param ah    Apply handler\n * @param arg   Handler argument\n *\n * @return Current list element if handler returned true\n */\nstruct le *list_apply(const struct list *list, bool fwd,\n\t\t      list_apply_h *ah, void *arg)\n{\n\tstruct le *le;\n\n\tif (!list || !ah)\n\t\treturn NULL;\n\n\tif (fwd) {\n\t\tle = list->head;\n\t\tgoto fwd;\n\t}\n\n\tle = list->tail;\n\n\twhile (le) {\n\t\tstruct le *cur = le;\n\n\t\tle = le->prev;\n\n\t\tif (ah(cur, arg))\n\t\t\treturn cur;\n\t}\n\n\treturn NULL;\n\nfwd:\n\twhile (le) {\n\t\tstruct le *cur = le;\n\n\t\tle = le->next;\n\n\t\tif (ah(cur, arg))\n\t\t\treturn cur;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Get the first element in a linked list\n *\n * @param list  Linked list\n *\n * @return First list element (NULL if empty)\n */\nstruct le *list_head(const struct list *list)\n{\n\treturn list ? list->head : NULL;\n}\n\n\n/**\n * Get the last element in a linked list\n *\n * @param list  Linked list\n *\n * @return Last list element (NULL if empty)\n */\nstruct le *list_tail(const struct list *list)\n{\n\treturn list ? list->tail : NULL;\n}\n\n\n/**\n * Get the number of elements in a linked list\n *\n * @param list  Linked list\n *\n * @return Number of list elements\n */\nuint32_t list_count(const struct list *list)\n{\n\treturn list ? (uint32_t)list->cnt : 0;\n}\n"
  },
  {
    "path": "src/main/init.c",
    "content": "/**\n * @file init.c  Main initialisation routine\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdlib.h>\n#ifdef HAVE_SIGNAL\n#include <signal.h>\n#endif\n#ifdef WIN32\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\n#endif\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_net.h>\n#include <re_sys.h>\n#include <re_main.h>\n#include <re_trace.h>\n#include <re_btrace.h>\n#include \"main.h\"\n\n\nstatic bool exception_btrace = false;\n\n\n#ifdef HAVE_SIGNAL\nstatic void signal_handler(int sig)\n{\n\tstruct btrace bt;\n\n\t(void)signal(sig, NULL);\n\n\tif (!exception_btrace)\n\t\treturn;\n\n\tbtrace(&bt);\n\tre_fprintf(stderr, \"Error: Signal (%d) %H\\n\", sig, btrace_println,\n\t\t   &bt);\n\tfflush(stderr);\n}\n#endif\n\n\n#ifdef WIN32\nstatic LONG WINAPI exception_handler(EXCEPTION_POINTERS *ExceptionInfo)\n{\n\tstruct btrace bt;\n\n\tif (!exception_btrace)\n\t\treturn EXCEPTION_CONTINUE_SEARCH;\n\n\tif (EXCEPTION_STACK_OVERFLOW !=\n\t    ExceptionInfo->ExceptionRecord->ExceptionCode) {\n\t\tbtrace(&bt);\n\t\tre_fprintf(stderr, \"%H\\n\", btrace_println, &bt);\n\t}\n\telse {\n\t\tre_fprintf(stderr, \"stack overflow\\n\");\n\t}\n\n\tswitch (ExceptionInfo->ExceptionRecord->ExceptionCode) {\n\tcase EXCEPTION_ACCESS_VIOLATION:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_ACCESS_VIOLATION\\n\");\n\t\tbreak;\n\tcase EXCEPTION_ARRAY_BOUNDS_EXCEEDED:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_ARRAY_BOUNDS_EXCEEDED\\n\");\n\t\tbreak;\n\tcase EXCEPTION_BREAKPOINT:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_BREAKPOINT\\n\");\n\t\tbreak;\n\tcase EXCEPTION_DATATYPE_MISALIGNMENT:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_DATATYPE_MISALIGNMENT\\n\");\n\t\tbreak;\n\tcase EXCEPTION_FLT_DENORMAL_OPERAND:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_FLT_DENORMAL_OPERAND\\n\");\n\t\tbreak;\n\tcase EXCEPTION_FLT_DIVIDE_BY_ZERO:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_FLT_DIVIDE_BY_ZERO\\n\");\n\t\tbreak;\n\tcase EXCEPTION_FLT_INEXACT_RESULT:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_FLT_INEXACT_RESULT\\n\");\n\t\tbreak;\n\tcase EXCEPTION_FLT_INVALID_OPERATION:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_FLT_INVALID_OPERATION\\n\");\n\t\tbreak;\n\tcase EXCEPTION_FLT_OVERFLOW:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_FLT_OVERFLOW\\n\");\n\t\tbreak;\n\tcase EXCEPTION_FLT_STACK_CHECK:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_FLT_STACK_CHECK\\n\");\n\t\tbreak;\n\tcase EXCEPTION_FLT_UNDERFLOW:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_FLT_UNDERFLOW\\n\");\n\t\tbreak;\n\tcase EXCEPTION_ILLEGAL_INSTRUCTION:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_ILLEGAL_INSTRUCTION\\n\");\n\t\tbreak;\n\tcase EXCEPTION_IN_PAGE_ERROR:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_IN_PAGE_ERROR\\n\");\n\t\tbreak;\n\tcase EXCEPTION_INT_DIVIDE_BY_ZERO:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_INT_DIVIDE_BY_ZERO\\n\");\n\t\tbreak;\n\tcase EXCEPTION_INT_OVERFLOW:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_INT_OVERFLOW\\n\");\n\t\tbreak;\n\tcase EXCEPTION_INVALID_DISPOSITION:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_INVALID_DISPOSITION\\n\");\n\t\tbreak;\n\tcase EXCEPTION_NONCONTINUABLE_EXCEPTION:\n\t\tre_fprintf(stderr,\n\t\t\t   \"Error: EXCEPTION_NONCONTINUABLE_EXCEPTION\\n\");\n\t\tbreak;\n\tcase EXCEPTION_PRIV_INSTRUCTION:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_PRIV_INSTRUCTION\\n\");\n\t\tbreak;\n\tcase EXCEPTION_SINGLE_STEP:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_SINGLE_STEP\\n\");\n\t\tbreak;\n\tcase EXCEPTION_STACK_OVERFLOW:\n\t\tre_fprintf(stderr, \"Error: EXCEPTION_STACK_OVERFLOW\\n\");\n\t\tbreak;\n\tdefault:\n\t\tre_fprintf(stderr, \"Error: Unrecognized Exception\\n\");\n\t\tbreak;\n\t}\n\n\tfflush(stderr);\n\n\treturn EXCEPTION_EXECUTE_HANDLER;\n}\n#endif\n\n\n/**\n * Initialise main library\n *\n * @return 0 if success, errorcode if failure\n */\nint libre_init(void)\n{\n\tint err;\n\n\tif (exception_btrace) {\n#ifdef HAVE_SIGNAL\n\t\t(void)signal(SIGSEGV, signal_handler);\n\t\t(void)signal(SIGABRT, signal_handler);\n\t\t(void)signal(SIGILL, signal_handler);\n#endif\n#ifdef WIN32\n\t\tSetUnhandledExceptionFilter(exception_handler);\n#endif\n\t}\n\n#ifdef USE_OPENSSL\n\terr = openssl_init();\n\tif (err)\n\t\treturn err;\n#endif\n\n\terr = net_sock_init();\n\tif (err) {\n\t\tnet_sock_close();\n\t\treturn err;\n\t}\n\n\terr = re_thread_init();\n\n\treturn err;\n}\n\n\n/**\n * Close library and free up all resources\n */\nvoid libre_close(void)\n{\n\t(void)fd_setsize(0);\n\tnet_sock_close();\n\tre_thread_close();\n}\n\n\n/**\n * Enable/Disable exception signal handling (SIGSEGV, SIGABRT, SIGILL...)\n *\n * @param enable True to enable, false to disable\n */\nvoid libre_exception_btrace(bool enable)\n{\n\texception_btrace = enable;\n}\n"
  },
  {
    "path": "src/main/main.c",
    "content": "/**\n * @file main.c  Main polling routine\n *\n * Copyright (C) 2010 Creytiv.com\n * Copyright (C) Sebastian Reimers\n */\n#include <stdlib.h>\n#ifdef HAVE_SYS_TIME_H\n#include <sys/time.h>\n#endif\n#include <sys/types.h>\n#undef _STRICT_ANSI\n#include <string.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef WIN32\n#include <winsock2.h>\n#else\n#include <sys/resource.h>\n#endif\n#ifdef HAVE_SIGNAL\n#include <signal.h>\n#endif\n#ifdef HAVE_SELECT_H\n#include <sys/select.h>\n#endif\n#ifdef HAVE_EPOLL\n#include <sys/epoll.h>\n#endif\n#ifdef HAVE_KQUEUE\n#include <sys/types.h>\n#include <sys/event.h>\n#include <sys/time.h>\n#undef LIST_INIT\n#undef LIST_FOREACH\n#undef LIST_FOREACH_SAFE\n#endif\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_net.h>\n#include <re_mem.h>\n#include <re_list.h>\n#include <re_thread.h>\n#include <re_tmr.h>\n#include <re_main.h>\n#include <re_btrace.h>\n#include <re_atomic.h>\n#include \"main.h\"\n\n\n#define DEBUG_MODULE \"main\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** Main loop values */\nenum {\n\tRE_THREAD_WORKERS = 4,\n\tMAX_BLOCKING\t  = 500, /**< Maximum time spent in handler in [ms] */\n#if defined(FD_SETSIZE)\n\tDEFAULT_MAXFDS = FD_SETSIZE\n#else\n\tDEFAULT_MAXFDS = 128\n#endif\n};\n\n/** File descriptor handler struct */\nstruct re_fhs {\n\tint index;\n\tre_sock_t fd;        /**< File Descriptor                   */\n\tint flags;           /**< Polling flags (Read, Write, etc.) */\n\tfd_h* fh;            /**< Event handler                     */\n\tvoid* arg;           /**< Handler argument                  */\n\tstruct re_fhs* next; /**< Next element in the delete list   */\n};\n\n/** Polling loop data */\nstruct re {\n\tint maxfds;                  /**< Maximum number of polling fds     */\n\tint nfds;                    /**< Number of active file descriptors */\n\tenum poll_method method;     /**< The current polling method        */\n\tRE_ATOMIC bool polling;      /**< Is polling flag                   */\n\tint sig;                     /**< Last caught signal                */\n\tstruct tmrl *tmrl;           /**< List of timers                    */\n\tstruct re_fhs *fhsld;        /**< fhs single-linked delete list     */\n#ifdef HAVE_SELECT\n\tstruct re_fhs **fhsl;        /**< Select fhs pointer list           */\n#endif\n#ifdef HAVE_EPOLL\n\tstruct epoll_event *events;  /**< Event set for epoll()             */\n\tint epfd;                    /**< epoll control file descriptor     */\n#endif\n\n#ifdef HAVE_KQUEUE\n\tstruct kevent *evlist;\n\tint kqfd;\n#endif\n\tmtx_t *mutex;                /**< Mutex for thread synchronization  */\n\tmtx_t *mutexp;               /**< Pointer to active mutex           */\n\tthrd_t tid;                  /**< Thread id                         */\n\tRE_ATOMIC bool thread_enter; /**< Thread enter is called            */\n\tstruct re_async *async;      /**< Async object                      */\n};\n\nstatic struct re *re_global = NULL;\nstatic tss_t key;\nstatic once_flag flag = ONCE_FLAG_INIT;\n\nstatic void poll_close(struct re *re);\n\n\nstatic void fhsld_flush(struct re *re)\n{\n\tstruct re_fhs *fhs = re->fhsld;\n\tre->fhsld = NULL;\n\n\twhile (fhs) {\n\t\tstruct re_fhs *next = fhs->next;\n\t\tmem_deref(fhs);\n\t\tfhs = next;\n\t}\n}\n\n\nstatic void re_destructor(void *arg)\n{\n\tstruct re *re = arg;\n\n\tpoll_close(re);\n\tfhsld_flush(re);\n\tmem_deref(re->mutex);\n\tmem_deref(re->async);\n\tmem_deref(re->tmrl);\n}\n\n\n/** fallback destructor if thread gets destroyed before re_thread_close() */\nstatic void thread_destructor(void *arg)\n{\n\tstruct re *re = arg;\n\n\tif (!re)\n\t\treturn;\n\n\tmem_deref(re);\n}\n\n\nint re_alloc(struct re **rep)\n{\n\tstruct re *re;\n\tint err;\n\n\tif (!rep)\n\t\treturn EINVAL;\n\n\tre = mem_zalloc(sizeof(struct re), re_destructor);\n\tif (!re)\n\t\treturn ENOMEM;\n\n\terr = mutex_alloc_tp(&re->mutex, mtx_recursive);\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"thread_init: mtx_init error\\n\");\n\t\tgoto out;\n\t}\n\tre->mutexp = re->mutex;\n\n\terr = tmrl_alloc(&re->tmrl);\n\tif (err) {\n\t\tDEBUG_WARNING(\"thread_init: tmrl_alloc error\\n\");\n\t\tgoto out;\n\t}\n\n\tre->async = NULL;\n\tre->tid = thrd_current();\n\n#ifdef HAVE_EPOLL\n\tre->epfd = -1;\n#endif\n\n#ifdef HAVE_KQUEUE\n\tre->kqfd = -1;\n#endif\n\nout:\n\tif (err)\n\t\tmem_deref(re);\n\telse\n\t\t*rep = re;\n\n\treturn err;\n}\n\n\nstatic void re_once(void)\n{\n\tint err;\n\n\terr = tss_create(&key, thread_destructor) != thrd_success;\n\tif (err) {\n\t\tDEBUG_WARNING(\"tss_create failed\\n\");\n\t\texit(ENOMEM);\n\t}\n}\n\n\n/**\n * Get thread specific re pointer (fallback to re_global if called by non re\n * thread)\n *\n * @return re pointer on success, otherwise NULL if libre_init() or\n * re_thread_init() is missing\n */\nstatic struct re *re_get(void)\n{\n\tstruct re *re;\n\n\tcall_once(&flag, re_once);\n\tre = tss_get(key);\n\tif (!re)\n\t\tre = re_global;\n\n\treturn re;\n}\n\n\nstatic inline void re_lock(struct re *re)\n{\n\tif (thrd_success != mtx_lock(re->mutexp))\n\t\tDEBUG_WARNING(\"re_lock error\\n\");\n}\n\n\nstatic inline void re_unlock(struct re *re)\n{\n\tif (thrd_success != mtx_unlock(re->mutexp))\n\t\tDEBUG_WARNING(\"re_unlock error\\n\");\n}\n\n\n#if MAIN_DEBUG\n/**\n * Call the application event handler\n *\n * @param re     Poll state\n * @param i\t File descriptor handler index\n * @param flags  Event flags\n */\nstatic void fd_handler(struct re_fhs *fhs, int flags)\n{\n\tconst uint64_t tick = tmr_jiffies();\n\tuint32_t diff;\n\n\tDEBUG_INFO(\"event on fd=%d (flags=0x%02x)...\\n\", fhs->fd, flags);\n\n\tfhs->fh(flags, fhs->arg);\n\n\tdiff = (uint32_t)(tmr_jiffies() - tick);\n\n\tif (diff > MAX_BLOCKING) {\n\t\tDEBUG_WARNING(\"long async blocking: %u>%u ms (h=%p arg=%p)\\n\",\n\t\t\t      diff, MAX_BLOCKING,\n\t\t\t      fhs->fh, fhs->arg);\n\t}\n}\n#endif\n\n\n#ifdef HAVE_SELECT\nstatic int set_select_fds(struct re *re, struct re_fhs *fhs)\n{\n\tint i = -1;\n\n\tif (!re || !fhs)\n\t\treturn EINVAL;\n\n\tif (fhs->index != -1) {\n\t\ti = fhs->index;\n\t}\n\telse {\n\t\t/* if nothing is found a linear search for the first\n\t\t * zeroed handler */\n\t\tfor (int j = 0; j < re->maxfds; j++) {\n\t\t\tif (!re->fhsl[j]) {\n\t\t\t\ti = j;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (i == -1)\n\t\treturn ERANGE;\n\n\tif (fhs->flags) {\n\t\tre->fhsl[i] = fhs;\n\t\tfhs->index  = i;\n\t}\n\telse {\n\t\tre->fhsl[i] = NULL;\n\t\tfhs->index  = -1;\n\t}\n\n\treturn 0;\n}\n#endif\n\n\n#ifdef HAVE_EPOLL\nstatic int set_epoll_fds(struct re *re, struct re_fhs *fhs)\n{\n\tstruct epoll_event event;\n\tint err = 0;\n\n\tif (!re || !fhs)\n\t\treturn EINVAL;\n\n\tre_sock_t fd = fhs->fd;\n\tint flags    = fhs->flags;\n\n\tif (re->epfd < 0)\n\t\treturn EBADFD;\n\n\tmemset(&event, 0, sizeof(event));\n\n\tDEBUG_INFO(\"set_epoll_fds: fd=%d flags=0x%02x\\n\", fd, flags);\n\n\tif (flags) {\n\t\tevent.data.ptr = fhs;\n\n\t\tif (flags & FD_READ)\n\t\t\tevent.events |= EPOLLIN;\n\t\tif (flags & FD_WRITE)\n\t\t\tevent.events |= EPOLLOUT;\n\t\tif (flags & FD_EXCEPT)\n\t\t\tevent.events |= EPOLLERR;\n\n\t\t/* Try to add it first */\n\t\tif (-1 == epoll_ctl(re->epfd, EPOLL_CTL_ADD, fd, &event)) {\n\n\t\t\t/* If already exist then modify it */\n\t\t\tif (EEXIST == errno) {\n\n\t\t\t\tif (-1 == epoll_ctl(re->epfd, EPOLL_CTL_MOD,\n\t\t\t\t\t\t    fd, &event)) {\n\t\t\t\t\terr = errno;\n\t\t\t\t\tDEBUG_WARNING(\"epoll_ctl:\"\n\t\t\t\t\t\t      \" EPOLL_CTL_MOD:\"\n\t\t\t\t\t\t      \" fd=%d (%m)\\n\",\n\t\t\t\t\t\t      fd, err);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\terr = errno;\n\t\t\t\tDEBUG_WARNING(\"epoll_ctl: EPOLL_CTL_ADD:\"\n\t\t\t\t\t      \" fd=%d (%m)\\n\",\n\t\t\t\t\t      fd, err);\n\t\t\t}\n\t\t}\n\t}\n\telse {\n\t\tif (-1 == epoll_ctl(re->epfd, EPOLL_CTL_DEL, fd, &event)) {\n\t\t\terr = errno;\n\t\t\tDEBUG_INFO(\"epoll_ctl: EPOLL_CTL_DEL: fd=%d (%m)\\n\",\n\t\t\t\t   fd, err);\n\t\t}\n\t}\n\n\treturn err;\n}\n#endif\n\n\n#ifdef HAVE_KQUEUE\nstatic int set_kqueue_fds(struct re *re, struct re_fhs *fhs)\n{\n\tstruct kevent kev[2];\n\tint r, n = 0;\n\n\tif (!fhs)\n\t\treturn EINVAL;\n\n\tre_sock_t fd = fhs->fd;\n\tint flags    = fhs->flags;\n\n\tmemset(kev, 0, sizeof(kev));\n\n\t/* always delete the events */\n\tEV_SET(&kev[0], fd, EVFILT_READ,  EV_DELETE, 0, 0, 0);\n\tEV_SET(&kev[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);\n\tkevent(re->kqfd, kev, 2, NULL, 0, NULL);\n\n\tmemset(kev, 0, sizeof(kev));\n\n\tif (flags & FD_WRITE) {\n\t\tEV_SET(&kev[n], fd, EVFILT_WRITE, EV_ADD, 0, 0, fhs);\n\t\t++n;\n\t}\n\tif (flags & FD_READ) {\n\t\tEV_SET(&kev[n], fd, EVFILT_READ, EV_ADD, 0, 0, fhs);\n\t\t++n;\n\t}\n\n\tif (n) {\n\t\tr = kevent(re->kqfd, kev, n, NULL, 0, NULL);\n\t\tif (r < 0) {\n\t\t\tint err = errno;\n\n\t\t\tDEBUG_WARNING(\"set: [fd=%d, flags=%x] kevent: %m\\n\",\n\t\t\t\t      fd, flags, err);\n\t\t\treturn err;\n\t\t}\n\t}\n\n\treturn 0;\n}\n#endif\n\n\nstatic int poll_init(struct re *re)\n{\n\tDEBUG_INFO(\"poll init (maxfds=%d)\\n\", re->maxfds);\n\n\tif (!re->maxfds) {\n\t\tDEBUG_WARNING(\"poll init: maxfds is 0\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tswitch (re->method) {\n\n#ifdef HAVE_SELECT\n\tcase METHOD_SELECT:\n\t\tif (re->fhsl)\n\t\t\treturn 0;\n\n\t\tre->fhsl = mem_zalloc(re->maxfds * sizeof(void *), NULL);\n\t\tif (!re->fhsl)\n\t\t\treturn ENOMEM;\n\t\tbreak;\n#endif\n\n#ifdef HAVE_EPOLL\n\tcase METHOD_EPOLL:\n\t\tif (!re->events) {\n\t\t\tDEBUG_INFO(\"allocate %u bytes for epoll set\\n\",\n\t\t\t\t   re->maxfds * sizeof(*re->events));\n\t\t\tre->events = mem_zalloc(re->maxfds*sizeof(*re->events),\n\t\t\t\t\t      NULL);\n\t\t\tif (!re->events)\n\t\t\t\treturn ENOMEM;\n\t\t}\n\n\t\tif (re->epfd < 0\n\t\t    && -1 == (re->epfd = epoll_create(re->maxfds))) {\n\n\t\t\tint err = errno;\n\n\t\t\tDEBUG_WARNING(\"epoll_create: %m (maxfds=%d)\\n\",\n\t\t\t\t      err, re->maxfds);\n\t\t\treturn err;\n\t\t}\n\t\tDEBUG_INFO(\"init: epoll_create() epfd=%d\\n\", re->epfd);\n\t\tbreak;\n#endif\n\n#ifdef HAVE_KQUEUE\n\tcase METHOD_KQUEUE:\n\n\t\tif (!re->evlist) {\n\t\t\tsize_t sz = re->maxfds * sizeof(*re->evlist);\n\t\t\tre->evlist = mem_zalloc(sz, NULL);\n\t\t\tif (!re->evlist)\n\t\t\t\treturn ENOMEM;\n\t\t}\n\n\t\tif (re->kqfd < 0) {\n\t\t\tre->kqfd = kqueue();\n\t\t\tif (re->kqfd < 0)\n\t\t\t\treturn errno;\n\t\t\tDEBUG_INFO(\"kqueue: fd=%d\\n\", re->kqfd);\n\t\t}\n\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\tDEBUG_WARNING(\"poll init: no method\\n\");\n\t\treturn EINVAL;\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\n\n/** Free all resources */\nstatic void poll_close(struct re *re)\n{\n\tif (!re)\n\t\treturn;\n\n\tDEBUG_INFO(\"poll close\\n\");\n\n\tre->maxfds = 0;\n\tre->nfds   = 0;\n\tre->method = METHOD_NULL;\n\n#ifdef HAVE_SELECT\n\tre->fhsl = mem_deref(re->fhsl);\n#endif\n\n#ifdef HAVE_EPOLL\n\tDEBUG_INFO(\"poll_close: epfd=%d\\n\", re->epfd);\n\n\tif (re->epfd >= 0) {\n\t\t(void)close(re->epfd);\n\t\tre->epfd = -1;\n\t}\n\n\tre->events = mem_deref(re->events);\n#endif\n\n#ifdef HAVE_KQUEUE\n\tif (re->kqfd >= 0) {\n\t\tclose(re->kqfd);\n\t\tre->kqfd = -1;\n\t}\n\n\tre->evlist = mem_deref(re->evlist);\n#endif\n}\n\n\nstatic int poll_setup(struct re *re)\n{\n\tint err;\n\n\terr = fd_setsize(DEFAULT_MAXFDS);\n\tif (err)\n\t\tgoto out;\n\n\tif (METHOD_NULL == re->method) {\n\t\terr = poll_method_set(poll_method_best());\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tDEBUG_INFO(\"poll setup: poll method not set - set to `%s'\\n\",\n\t\t\t   poll_method_name(re->method));\n\t}\n\n\terr = poll_init(re);\n\n out:\n\tif (err)\n\t\tpoll_close(re);\n\n\treturn err;\n}\n\n\n/**\n * Listen for events on a file descriptor\n *\n * @param fhsp   File descriptor handler struct pointer (don't use mem_deref(),\n *               use fd_close() instead)\n * @param fd     File descriptor\n * @param flags  Wanted event flags\n * @param fh     Event handler\n * @param arg    Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint fd_listen(struct re_fhs **fhsp, re_sock_t fd, int flags, fd_h *fh,\n\t      void *arg)\n{\n\tstruct re *re = re_get();\n\tstruct re_fhs *fhs;\n\tint err = 0;\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"fd_listen: re not ready\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tif (!fhsp || !flags || !fh)\n\t\treturn EINVAL;\n\n#ifndef RELEASE\n\terr = re_thread_check(true);\n\tif (err)\n\t\treturn err;\n#endif\n\n\tif (fd == RE_BAD_SOCK) {\n\t\tDEBUG_WARNING(\"fd_listen: corrupt fd %d\\n\", fd);\n\t\treturn EBADF;\n\t}\n\n\terr = poll_setup(re);\n\tif (err)\n\t\treturn err;\n\n\tfhs = *fhsp;\n\tif (!fhs) {\n\t\tfhs = mem_zalloc(sizeof(struct re_fhs), NULL);\n\t\tif (!fhs)\n\t\t\treturn ENOMEM;\n\n\t\tfhs->fd\t   = fd;\n\t\tfhs->index = -1;\n\n\t\tDEBUG_INFO(\"fd_listen/new: fd=%d flags=0x%02x\\n\", fd, flags);\n\n\t\tif (++re->nfds > re->maxfds) {\n\t\t\tDEBUG_WARNING(\"fd_listen maxfds reached %d > %d\\n\",\n\t\t\t\t      re->nfds, re->maxfds);\n\t\t\t--re->nfds;\n\t\t\terr = EMFILE;\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse {\n\t\tif (unlikely(fhs->fd != fd)) {\n\t\t\tDEBUG_WARNING(\"fd_listen: fhs reuse conflict %d\\n\",\n\t\t\t\t      fd);\n\t\t\treturn EBADF;\n\t\t}\n\t\tDEBUG_INFO(\"fd_listen/update: fd=%d flags=0x%02x\\n\", fd,\n\t\t\t   flags);\n\t}\n\n\tfhs->flags = flags;\n\tfhs->fh\t   = fh;\n\tfhs->arg   = arg;\n\n\tswitch (re->method) {\n#ifdef HAVE_SELECT\n\tcase METHOD_SELECT:\n\t\terr = set_select_fds(re, fhs);\n\t\tbreak;\n#endif\n#ifdef HAVE_EPOLL\n\tcase METHOD_EPOLL:\n\t\terr = set_epoll_fds(re, fhs);\n\t\tbreak;\n#endif\n\n#ifdef HAVE_KQUEUE\n\tcase METHOD_KQUEUE:\n\t\terr = set_kqueue_fds(re, fhs);\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\terr = ENOTSUP;\n\t\tbreak;\n\t}\n\nout:\n\tif (err) {\n\t\tmem_deref(fhs);\n\t\tDEBUG_WARNING(\"fd_listen err: fd=%d flags=0x%02x (%m)\\n\", fd,\n\t\t\t      flags, err);\n\t}\n\telse {\n\t\t*fhsp = fhs;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Stop and destruct listening for events on a file descriptor\n *\n * @param fhs  File descriptor handler struct pointer\n *\n * @return always NULL\n */\nstruct re_fhs *fd_close(struct re_fhs *fhs)\n{\n\tstruct re *re = re_get();\n\tint err\t      = 0;\n\n\tif (!fhs || !re)\n\t\treturn NULL;\n\n\tfhs->flags = 0;\n\tfhs->fh\t   = NULL;\n\tfhs->arg   = NULL;\n\n\tswitch (re->method) {\n#ifdef HAVE_SELECT\n\tcase METHOD_SELECT:\n\t\terr = set_select_fds(re, fhs);\n\t\tbreak;\n#endif\n#ifdef HAVE_EPOLL\n\tcase METHOD_EPOLL:\n\t\terr = set_epoll_fds(re, fhs);\n\t\tbreak;\n#endif\n\n#ifdef HAVE_KQUEUE\n\tcase METHOD_KQUEUE:\n\t\terr = set_kqueue_fds(re, fhs);\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\terr = ENOTSUP;\n\t\tbreak;\n\t}\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"fd_close err: fd=%d (%m)\\n\", fhs->fd, err);\n\t}\n\telse {\n\t\tDEBUG_INFO(\"fd_close: fd=%d\\n\", fhs->fd);\n\t}\n\n\tre_assert(fhs->next == NULL);\n\tfhs->next = re->fhsld;\n\tre->fhsld = fhs;\n\n\t--re->nfds;\n\n\treturn NULL;\n}\n\n\n/**\n * Polling loop\n *\n * @param re Poll state.\n *\n * @return 0 if success, otherwise errorcode\n */\nstatic int fd_poll(struct re *re)\n{\n\tconst uint64_t to = tmr_next_timeout(re->tmrl);\n\tint i, n;\n\tint nfds = re->nfds;\n\tint err = 0;\n\tstruct re_fhs *fhs = NULL;\n#ifdef HAVE_SELECT\n\tfd_set rfds, wfds, efds;\n#endif\n\n\tDEBUG_INFO(\"next timer: %llu ms\\n\", to);\n\n\t/* Wait for I/O */\n\tswitch (re->method) {\n\n#ifdef HAVE_SELECT\n\tcase METHOD_SELECT: {\n\t\tstruct timeval tv;\n\t\tint max_fd_plus_1 = 0;\n\t\tint cfds = 0;\n\n\t\t/* Clear and update fd sets */\n\t\tFD_ZERO(&rfds);\n\t\tFD_ZERO(&wfds);\n\t\tFD_ZERO(&efds);\n\n\t\tfor (i = 0; cfds < nfds; i++) {\n\t\t\tfhs = re->fhsl[i];\n\n\t\t\tif (!fhs || !fhs->fh)\n\t\t\t\tcontinue;\n\n\t\t\t++cfds;\n\n\t\t\tre_sock_t fd = fhs->fd;\n\t\t\tif (fhs->flags & FD_READ)\n\t\t\t\tFD_SET(fd, &rfds);\n\t\t\tif (fhs->flags & FD_WRITE)\n\t\t\t\tFD_SET(fd, &wfds);\n\t\t\tif (fhs->flags & FD_EXCEPT)\n\t\t\t\tFD_SET(fd, &efds);\n\n/* not needed on WIN32 since select nfds arg is ignored */\n#if !defined(WIN32)\n\t\t\tmax_fd_plus_1 = max(max_fd_plus_1, fd + 1);\n#endif\n\t\t}\n\n\t\tnfds = re->maxfds;\n\n#ifdef WIN32\n\t\ttv.tv_sec  = (long) to / 1000;\n#else\n\t\ttv.tv_sec  = (time_t) to / 1000;\n#endif\n\t\ttv.tv_usec = (uint32_t) (to % 1000) * 1000;\n\n\t\tre_unlock(re);\n\t\tn = select(max_fd_plus_1, &rfds, &wfds, &efds,\n\t\t\t   to ? &tv : NULL);\n\t\tre_lock(re);\n\t}\n\t\tbreak;\n#endif\n#ifdef HAVE_EPOLL\n\tcase METHOD_EPOLL:\n\t\tre_unlock(re);\n\t\tn = epoll_wait(re->epfd, re->events, re->maxfds,\n\t\t\t       to ? (int)to : -1);\n\t\tre_lock(re);\n\t\tbreak;\n#endif\n\n#ifdef HAVE_KQUEUE\n\tcase METHOD_KQUEUE: {\n\t\tstruct timespec timeout;\n\n\t\ttimeout.tv_sec = (time_t) (to / 1000);\n\t\ttimeout.tv_nsec = (to % 1000) * 1000000;\n\n\t\tre_unlock(re);\n\t\tn = kevent(re->kqfd, NULL, 0, re->evlist, re->maxfds,\n\t\t\t   to ? &timeout : NULL);\n\t\tre_lock(re);\n\t\t}\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\t(void)to;\n\t\tDEBUG_WARNING(\"no polling method set\\n\");\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\tif (n < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tgoto out;\n\t}\n\n\t/* Check for events */\n\tfor (i=0; (n > 0) && (i < nfds); i++) {\n\t\tre_sock_t fd;\n\t\tint flags = 0;\n\n\t\tswitch (re->method) {\n\n#ifdef HAVE_SELECT\n\t\tcase METHOD_SELECT:\n\t\t\tfhs = re->fhsl[i];\n\t\t\tif (!fhs)\n\t\t\t\tbreak;\n\n\t\t\tfd = fhs->fd;\n\t\t\tif (FD_ISSET(fd, &rfds))\n\t\t\t\tflags |= FD_READ;\n\t\t\tif (FD_ISSET(fd, &wfds))\n\t\t\t\tflags |= FD_WRITE;\n\t\t\tif (FD_ISSET(fd, &efds))\n\t\t\t\tflags |= FD_EXCEPT;\n\t\t\tbreak;\n#endif\n#ifdef HAVE_EPOLL\n\t\tcase METHOD_EPOLL:\n\t\t\tfhs = re->events[i].data.ptr;\n\t\t\tfd = fhs->fd;\n\n\t\t\tif (re->events[i].events & EPOLLIN)\n\t\t\t\tflags |= FD_READ;\n\t\t\tif (re->events[i].events & EPOLLOUT)\n\t\t\t\tflags |= FD_WRITE;\n\t\t\tif (re->events[i].events & (EPOLLERR|EPOLLHUP))\n\t\t\t\tflags |= FD_EXCEPT;\n\n\t\t\tif (!flags) {\n\t\t\t\tDEBUG_WARNING(\"epoll: no flags fd=%d\\n\", fd);\n\t\t\t}\n\n\t\t\tbreak;\n#endif\n\n#ifdef HAVE_KQUEUE\n\t\tcase METHOD_KQUEUE: {\n\n\t\t\tstruct kevent *kev = &re->evlist[i];\n\n\t\t\tfd = (int)kev->ident;\n\t\t\tfhs = kev->udata;\n\n\t\t\tif (kev->filter == EVFILT_READ)\n\t\t\t\tflags |= FD_READ;\n\t\t\telse if (kev->filter == EVFILT_WRITE)\n\t\t\t\tflags |= FD_WRITE;\n\t\t\telse {\n\t\t\t\tDEBUG_WARNING(\"kqueue: unhandled \"\n\t\t\t\t\t      \"filter %x\\n\",\n\t\t\t\t\t      kev->filter);\n\t\t\t}\n\n\t\t\tif (kev->flags & EV_EOF) {\n\t\t\t\tflags |= FD_EXCEPT;\n\t\t\t}\n\t\t\tif (kev->flags & EV_ERROR) {\n\t\t\t\tDEBUG_WARNING(\"kqueue: EV_ERROR on fd %d\\n\",\n\t\t\t\t\t      fd);\n\t\t\t}\n\n\t\t\tif (!flags) {\n\t\t\t\tDEBUG_WARNING(\"kqueue: no flags fd=%d\\n\", fd);\n\t\t\t}\n\t\t}\n\t\t\tbreak;\n#endif\n\n\t\tdefault:\n\t\t\terr = EINVAL;\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (!flags)\n\t\t\tcontinue;\n\n\t\tif (fhs && fhs->fh) {\n#if MAIN_DEBUG\n\t\t\tfd_handler(fhs, flags);\n#else\n\t\t\tfhs->fh(flags, fhs->arg);\n#endif\n\t\t}\n\n\t\t/* Handle only active events */\n\t\t--n;\n\t}\n\n out:\n\t/* Delayed fhs deref to avoid dangling fhs pointers */\n\tfhsld_flush(re);\n\n\treturn err;\n}\n\n\n/**\n * Set the maximum number of file descriptors\n *\n * @note Only first call inits maxfds and fhs, so call after libre_init() and\n * before re_main() in custom applications.\n *\n * @param maxfds Max FDs. 0 to free and -1 for RLIMIT_NOFILE (Linux/Unix only)\n *\n *\n * @return 0 if success, otherwise errorcode\n */\nint fd_setsize(int maxfds)\n{\n\tstruct re *re = re_get();\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"fd_setsize: re not ready\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tif (!maxfds) {\n\t\tpoll_close(re);\n\t\treturn 0;\n\t}\n\n#ifdef WIN32\n\tif (maxfds < 0)\n\t\treturn ENOSYS;\n#else\n\tif (maxfds < 0) {\n\t\tstruct rlimit limits;\n\t\tint err;\n\n\t\terr = getrlimit(RLIMIT_NOFILE, &limits);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"fd_setsize: error rlimit: %m\\n\", err);\n\t\t\treturn err;\n\t\t}\n\n\t\tmaxfds = (int)limits.rlim_cur;\n\t}\n#endif\n\n\tif (!re->maxfds)\n\t\tre->maxfds = maxfds;\n\n\treturn 0;\n}\n\n\n#ifdef HAVE_SIGNAL\n/* Thread-safe signal handling */\nstatic void signal_handler(int sig)\n{\n\tstruct re *re = re_get();\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"signal_handler: re not ready\\n\");\n\t\treturn;\n\t}\n\n\t(void)signal(sig, signal_handler);\n\tre->sig = sig;\n}\n#endif\n\n\n/**\n * Main polling loop for async I/O events. This function will only return when\n * re_cancel() is called or an error occurred.\n *\n * @param signalh Optional Signal handler\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_main(re_signal_h *signalh)\n{\n\tstruct re *re = re_get();\n\tint err;\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"re_main: re not ready\\n\");\n\t\treturn EINVAL;\n\t}\n\n#ifdef HAVE_SIGNAL\n\tif (signalh) {\n\t\t(void)signal(SIGINT, signal_handler);\n\t\t(void)signal(SIGALRM, signal_handler);\n\t\t(void)signal(SIGTERM, signal_handler);\n\t}\n#endif\n\n\tif (re_atomic_rlx(&re->polling)) {\n\t\tDEBUG_WARNING(\"main loop already polling\\n\");\n\t\treturn EALREADY;\n\t}\n\n\terr = poll_setup(re);\n\tif (err)\n\t\tgoto out;\n\n\tDEBUG_INFO(\"Using async I/O polling method: `%s'\\n\",\n\t\t   poll_method_name(re->method));\n\n\tre_atomic_rlx_set(&re->polling, true);\n\n\tre_lock(re);\n\tfor (;;) {\n\n\t\tif (re->sig) {\n\t\t\tif (signalh)\n\t\t\t\tsignalh(re->sig);\n\n\t\t\tre->sig = 0;\n\t\t}\n\n\t\tif (!re_atomic_rlx(&re->polling)) {\n\t\t\terr = 0;\n\t\t\tbreak;\n\t\t}\n\n\n\t\terr = fd_poll(re);\n\t\tif (err) {\n\t\t\tif (EINTR == err)\n\t\t\t\tcontinue;\n\n#ifdef DARWIN\n\t\t\t/* NOTE: workaround for Darwin */\n\t\t\tif (EBADF == err)\n\t\t\t\tcontinue;\n\n#endif\n#ifdef WIN32\n\t\t\tif (WSAEINVAL == err) {\n\t\t\t\ttmr_poll(re->tmrl);\n\t\t\t\tcontinue;\n\t\t\t}\n#endif\n\t\t\tbreak;\n\t\t}\n\n\t\ttmr_poll(re->tmrl);\n\t}\n\tre_unlock(re);\n\n out:\n\tre_atomic_rlx_set(&re->polling, false);\n\n\treturn err;\n}\n\n\n/**\n * Cancel the main polling loop\n */\nvoid re_cancel(void)\n{\n\tstruct re *re = re_get();\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"re_cancel: re not ready\\n\");\n\t\treturn;\n\t}\n\n\tre_atomic_rlx_set(&re->polling, false);\n}\n\n\n/**\n * Debug the main polling loop\n *\n * @param pf     Print handler where debug output is printed to\n * @param unused Unused parameter\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_debug(struct re_printf *pf, void *unused)\n{\n\tstruct re *re = re_get();\n\tint err = 0;\n\n\t(void)unused;\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"re_debug: re not ready\\n\");\n\t\treturn EINVAL;\n\t}\n\n\terr |= re_hprintf(pf, \"re main loop:\\n\");\n\terr |= re_hprintf(pf, \"  maxfds:       %d\\n\", re->maxfds);\n\terr |= re_hprintf(pf, \"  nfds:         %d\\n\", re->nfds);\n\terr |= re_hprintf(pf, \"  method:       %s\\n\",\n\t\t\t  poll_method_name(re->method));\n\terr |= re_hprintf(pf, \"  polling:      %d\\n\",\n\t\t\t  re_atomic_rlx(&re->polling));\n\terr |= re_hprintf(pf, \"  sig:          %d\\n\", re->sig);\n\terr |= re_hprintf(pf, \"  timers:       %u\\n\", tmrl_count(re->tmrl));\n\terr |= re_hprintf(pf, \"  mutex:        %p\\n\", re->mutex);\n\terr |= re_hprintf(pf, \"  tid:          %p\\n\", re->tid);\n\terr |= re_hprintf(pf, \"  thread_enter: %d\\n\",\n\t\t\t  re_atomic_rlx(&re->thread_enter));\n\terr |= re_hprintf(pf, \"  async:        %p\\n\", re->async);\n\n\treturn err;\n}\n\n\n/**\n * Get number of active file descriptors\n *\n * @return nfds\n */\nint re_nfds(void)\n{\n\tstruct re *re = re_get();\n\n\treturn re ? re->nfds : 0;\n}\n\n\n/**\n * Get current async I/O polling method.\n *\n * @return enum poll_method\n */\nenum poll_method poll_method_get(void)\n{\n\tstruct re *re = re_get();\n\n\treturn re ? re->method : METHOD_NULL;\n}\n\n\n/**\n * Set async I/O polling method. This function can only called once, before\n * poll init/setup.\n *\n * @param method New polling method\n *\n * @return 0 if success, otherwise errorcode\n */\nint poll_method_set(enum poll_method method)\n{\n\tstruct re *re = re_get();\n\tint err;\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"poll_method_set: re not ready\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tif (re->method != METHOD_NULL) {\n\t\tDEBUG_WARNING(\"poll_method_set: already set\\n\");\n\t\treturn EINVAL;\n\t}\n\n\terr = fd_setsize(DEFAULT_MAXFDS);\n\tif (err)\n\t\treturn err;\n\n\tswitch (method) {\n\n#ifdef HAVE_SELECT\n\tcase METHOD_SELECT:\n\t\tif (re->maxfds > (int)FD_SETSIZE) {\n\t\t\tDEBUG_WARNING(\"SELECT: maxfds > FD_SETSIZE\\n\");\n\t\t\treturn EMFILE;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef HAVE_EPOLL\n\tcase METHOD_EPOLL:\n\t\tbreak;\n#endif\n#ifdef HAVE_KQUEUE\n\tcase METHOD_KQUEUE:\n\t\tbreak;\n#endif\n\tdefault:\n\t\tDEBUG_WARNING(\"poll method not supported: '%s'\\n\",\n\t\t\t      poll_method_name(method));\n\t\treturn EINVAL;\n\t}\n\n\tre->method = method;\n\n\tDEBUG_INFO(\"Setting async I/O polling method to `%s'\\n\",\n\t\t   poll_method_name(re->method));\n\n\terr = poll_init(re);\n\n\treturn err;\n}\n\n\n/**\n * Add a worker thread for this thread\n *\n * @note: for main thread this is called by libre_init()\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_thread_init(void)\n{\n\tstruct re *re;\n\tint err;\n\n\tcall_once(&flag, re_once);\n\n\tre = tss_get(key);\n\tif (re) {\n\t\tDEBUG_NOTICE(\"thread_init: already added for thread\\n\");\n\t\treturn 0;\n\t}\n\n\terr = re_alloc(&re);\n\tif (err)\n\t\treturn err;\n\n\tif (!re_global)\n\t\tre_global = re;\n\n\terr = tss_set(key, re) != thrd_success;\n\tif (err) {\n\t\terr = ENOMEM;\n\t\tDEBUG_WARNING(\"thread_init: tss_set error\\n\");\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Remove the worker thread for this thread\n */\nvoid re_thread_close(void)\n{\n\tstruct re *re;\n\n\tcall_once(&flag, re_once);\n\n\tre = tss_get(key);\n\tif (re) {\n\t\tif (re == re_global)\n\t\t\tre_global = NULL;\n\t\tmem_deref(re);\n\t\ttss_set(key, NULL);\n\t}\n}\n\n\n/**\n * Enter an 're' thread\n */\nvoid re_thread_enter(void)\n{\n\tstruct re *re = re_get();\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"re_thread_enter: re not ready\\n\");\n\t\treturn;\n\t}\n\n\tif (!re_atomic_rlx(&re->polling))\n\t\treturn;\n\n\tre_lock(re);\n\n\t/* set only for non-re threads */\n\tif (!thrd_equal(re->tid, thrd_current())) {\n\t\tre_atomic_rlx_set(&re->thread_enter, true);\n\t}\n}\n\n\n/**\n * Leave an 're' thread\n */\nvoid re_thread_leave(void)\n{\n\tstruct re *re = re_get();\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"re_thread_leave: re not ready\\n\");\n\t\treturn;\n\t}\n\n\tif (!re_atomic_rlx(&re->polling))\n\t\treturn;\n\n\t/* Dummy async event, to ensure timers are properly handled */\n\tif (re->async)\n\t\tre_thread_async(NULL, NULL, NULL);\n\tre_atomic_rlx_set(&re->thread_enter, false);\n\tre_unlock(re);\n}\n\n\n/**\n * Attach the current thread to re context\n *\n * @param context Re context\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_thread_attach(struct re *context)\n{\n\tstruct re *re;\n\n\tif (!context)\n\t\treturn EINVAL;\n\n\tcall_once(&flag, re_once);\n\n\tre = tss_get(key);\n\tif (re) {\n\t\tif (re != context)\n\t\t\treturn EALREADY;\n\t\treturn 0;\n\t}\n\n\ttss_set(key, context);\n\n\treturn 0;\n}\n\n\n/**\n * Detach the current thread from re context\n */\nvoid re_thread_detach(void)\n{\n\tcall_once(&flag, re_once);\n\n\ttss_set(key, NULL);\n}\n\n\n/**\n * Set an external mutex for this thread\n *\n * @param mutexp Pointer to external mutex, NULL to use internal\n */\nvoid re_set_mutex(void *mutexp)\n{\n\tstruct re *re = re_get();\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"re_set_mutex: re not ready\\n\");\n\t\treturn;\n\t}\n\n\tre->mutexp = mutexp ? mutexp : re->mutex;\n}\n\n\n/**\n * Check for NON-RE thread calls\n *\n * @param debug True to print debug warning\n *\n * @return 0 if success, otherwise EPERM\n */\nint re_thread_check(bool debug)\n{\n\tstruct re *re = re_get();\n\n\tif (!re)\n\t\treturn EINVAL;\n\n\tif (re_atomic_rlx(&re->thread_enter))\n\t\treturn 0;\n\n\tif (thrd_equal(re->tid, thrd_current()))\n\t\treturn 0;\n\n\tif (debug) {\n\t\tDEBUG_WARNING(\n\t\t\t\"thread check: called from a NON-RE thread without \"\n\t\t\t\"thread_enter()!\\n\");\n\n#if DEBUG_LEVEL > 5\n\t\tstruct btrace trace;\n\t\tbtrace(&trace);\n\t\tDEBUG_INFO(\"%H\", btrace_println, &trace);\n#endif\n\t}\n\n\treturn EPERM;\n}\n\n\n/**\n * Get the timer-list for this thread\n *\n * @return Timer list\n *\n * @note only used by tmr module\n */\nstruct tmrl *re_tmrl_get(void)\n{\n\tstruct re *re = re_get();\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"re_tmrl_get: re not ready\\n\");\n\t\treturn NULL;\n\t}\n\n\treturn re->tmrl;\n}\n\n\n/**\n * Initialize re async object\n *\n * @param workers  Number of async worker threads\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_thread_async_init(uint16_t workers)\n{\n\tstruct re *re = re_get();\n\tint err;\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"re_thread_async_workers: re not ready\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tif (re->async)\n\t\treturn EALREADY;\n\n\terr = re_async_alloc(&re->async, workers);\n\tif (err)\n\t\tDEBUG_WARNING(\"re_async_alloc: %m\\n\", err);\n\n\treturn err;\n}\n\n\n/**\n * Close/Dereference async object\n */\nvoid re_thread_async_close(void)\n{\n\tstruct re *re = re_get();\n\n\tif (!re) {\n\t\tDEBUG_WARNING(\"re_thread_async_close: re not ready\\n\");\n\t\treturn;\n\t}\n\n\tre->async = mem_deref(re->async);\n}\n\n\n/**\n * Execute work handler for current event loop\n *\n * @param work  Work handler\n * @param cb    Callback handler (called by re poll thread)\n * @param arg   Handler argument (has to be thread-safe and mem_deref-safe)\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_thread_async(re_async_work_h *work, re_async_h *cb, void *arg)\n{\n\tstruct re *re = re_get();\n\tint err;\n\n\tif (unlikely(!re)) {\n\t\tDEBUG_WARNING(\"re_thread_async: re not ready\\n\");\n\t\treturn EAGAIN;\n\t}\n\n\tif (unlikely(!re->async)) {\n\t\t/* fallback needed for internal libre functions */\n\t\terr = re_async_alloc(&re->async, RE_THREAD_WORKERS);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn re_async(re->async, 0, work, cb, arg);\n}\n\n\n/**\n * Execute work handler for re_global main event loop\n *\n * @param work  Work handler\n * @param cb    Callback handler (called by re global main poll thread)\n * @param arg   Handler argument (has to be thread-safe and mem_deref-safe)\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_thread_async_main(re_async_work_h *work, re_async_h *cb, void *arg)\n{\n\tstruct re *re = re_global;\n\tint err;\n\n\tif (unlikely(!re)) {\n\t\tDEBUG_WARNING(\"re_thread_async: re not ready\\n\");\n\t\treturn EAGAIN;\n\t}\n\n\tif (unlikely(!re->async)) {\n\t\t/* fallback needed for internal libre functions */\n\t\terr = re_async_alloc(&re->async, RE_THREAD_WORKERS);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn re_async(re->async, 0, work, cb, arg);\n}\n\n\n/**\n * Execute work handler for current event loop with identifier\n *\n * @param id    Work identifier\n * @param work  Work handler\n * @param cb    Callback handler (called by re poll thread)\n * @param arg   Handler argument (has to be thread-safe and mem_deref-safe)\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_thread_async_id(intptr_t id, re_async_work_h *work, re_async_h *cb,\n\t\t       void *arg)\n{\n\tstruct re *re = re_get();\n\tint err;\n\n\tif (unlikely(!re)) {\n\t\tDEBUG_WARNING(\"re_thread_async_id: re not ready\\n\");\n\t\treturn EAGAIN;\n\t}\n\n\tif (unlikely(!re->async)) {\n\t\t/* fallback needed for internal libre functions */\n\t\terr = re_async_alloc(&re->async, RE_THREAD_WORKERS);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn re_async(re->async, id, work, cb, arg);\n}\n\n\n/**\n * Execute work handler for re_global main event loop with identifier\n *\n * @param id    Work identifier\n * @param work  Work handler\n * @param cb    Callback handler (called by re poll thread)\n * @param arg   Handler argument (has to be thread-safe and mem_deref-safe)\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_thread_async_main_id(intptr_t id, re_async_work_h *work, re_async_h *cb,\n\t\t\t    void *arg)\n{\n\tstruct re *re = re_global;\n\tint err;\n\n\tif (unlikely(!re)) {\n\t\tDEBUG_WARNING(\"re_thread_async_id: re not ready\\n\");\n\t\treturn EAGAIN;\n\t}\n\n\tif (unlikely(!re->async)) {\n\t\t/* fallback needed for internal libre functions */\n\t\terr = re_async_alloc(&re->async, RE_THREAD_WORKERS);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn re_async(re->async, id, work, cb, arg);\n}\n\n\n/**\n * Cancel pending async work and callback\n *\n * @param id  Work identifier\n */\nvoid re_thread_async_cancel(intptr_t id)\n{\n\tstruct re *re = re_get();\n\n\tif (unlikely(!re)) {\n\t\tDEBUG_WARNING(\"re_thread_async_cancel: re not ready\\n\");\n\t\treturn;\n\t}\n\n\tre_async_cancel(re->async, id);\n}\n\n\n/**\n * Cancel pending async work and callback for re_global main event loop\n *\n * @param id  Work identifier\n */\nvoid re_thread_async_main_cancel(intptr_t id)\n{\n\tstruct re *re = re_global;\n\n\tif (unlikely(!re)) {\n\t\tDEBUG_WARNING(\"re_thread_async_cancel: re not ready\\n\");\n\t\treturn;\n\t}\n\n\tre_async_cancel(re->async, id);\n}\n\n\n/**\n * Flush file descriptors handlers if re loop is not running\n */\nvoid re_fhs_flush(void)\n{\n\tstruct re *re = re_get();\n\tif (!re) {\n\t\tDEBUG_WARNING(\"re_fhs_flush: re not ready\\n\");\n\t\treturn;\n\t}\n\n\tif (re_atomic_rlx(&re->polling)) {\n\t\tDEBUG_WARNING(\"re_fhs_flush: re polling is running\\n\");\n\t\treturn;\n\t}\n\n\tfhsld_flush(re);\n}\n"
  },
  {
    "path": "src/main/main.h",
    "content": "/**\n * @file main.h  Internal interface to main polling loop\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifdef USE_OPENSSL\nint  openssl_init(void);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "src/main/method.c",
    "content": "/**\n * @file method.c  Polling methods\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_main.h>\n#include \"main.h\"\n\n\nstatic const char str_select[] = \"select\";   /**< POSIX.1-2001 select     */\nstatic const char str_epoll[]  = \"epoll\";    /**< Linux epoll             */\nstatic const char str_kqueue[] = \"kqueue\";\n\n\n/**\n * Choose the best async I/O polling method\n *\n * @return Polling method\n */\nenum poll_method poll_method_best(void)\n{\n#ifdef HAVE_EPOLL\n\t/* Supported from Linux 2.5.66 */\n\treturn METHOD_EPOLL;\n#endif\n\n#ifdef HAVE_KQUEUE\n\treturn METHOD_KQUEUE;\n#endif\n\n#ifdef HAVE_SELECT\n\treturn METHOD_SELECT;\n#endif\n\n\treturn METHOD_NULL;\n}\n\n\n/**\n * Get the name of the polling method\n *\n * @param method Polling method\n *\n * @return Polling name string\n */\nconst char *poll_method_name(enum poll_method method)\n{\n\tswitch (method) {\n\n\tcase METHOD_SELECT:    return str_select;\n\tcase METHOD_EPOLL:     return str_epoll;\n\tcase METHOD_KQUEUE:    return str_kqueue;\n\tdefault:               return \"???\";\n\t}\n}\n\n\n/**\n * Get the polling method type from a string\n *\n * @param method Returned polling method\n * @param name   Polling method name string\n *\n * @return 0 if success, otherwise errorcode\n */\nint poll_method_type(enum poll_method *method, const struct pl *name)\n{\n\tif (!method || !name)\n\t\treturn EINVAL;\n\n\tif (0 == pl_strcasecmp(name, str_select))\n\t\t*method = METHOD_SELECT;\n\telse if (0 == pl_strcasecmp(name, str_epoll))\n\t\t*method = METHOD_EPOLL;\n\telse if (0 == pl_strcasecmp(name, str_kqueue))\n\t\t*method = METHOD_KQUEUE;\n\telse\n\t\treturn ENOENT;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/main/openssl.c",
    "content": "/**\n * @file openssl.c  OpenSSL initialisation and multi-threading routines\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#ifdef HAVE_SIGNAL\n#include <signal.h>\n#endif\n#include <re_types.h>\n#include \"main.h\"\n\n\n#ifdef SIGPIPE\nstatic void sigpipe_handler(int x)\n{\n\t(void)x;\n\t(void)signal(SIGPIPE, sigpipe_handler);\n}\n#endif\n\n\nint openssl_init(void)\n{\n#ifdef SIGPIPE\n\t(void)signal(SIGPIPE, sigpipe_handler);\n#endif\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/mbuf/mbuf.c",
    "content": "/**\n * @file mbuf.c  Memory buffers\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n\n\n#define DEBUG_MODULE \"mbuf\"\n#define DEBUG_LEVEL 4\n#include <re_dbg.h>\n\n\nenum {DEFAULT_SIZE=512};\n\n\nstatic void mbuf_destructor(void *data)\n{\n\tstruct mbuf *mb = data;\n\n\tmem_deref(mb->buf);\n}\n\n\n/**\n * Allocate a new memory buffer\n *\n * @param size Initial buffer size\n *\n * @return New memory buffer, NULL if no memory\n */\nstruct mbuf *mbuf_alloc(size_t size)\n{\n\tstruct mbuf *mb;\n\n\tmb = mem_zalloc(sizeof(*mb), mbuf_destructor);\n\tif (!mb)\n\t\treturn NULL;\n\n\tif (mbuf_resize(mb, size ? size : DEFAULT_SIZE))\n\t\treturn mem_deref(mb);\n\n\treturn mb;\n}\n\n\n/**\n * Duplicate memory buffer\n *\n * @param mbd Memory buffer to duplicate\n *\n * @return Duplicated memory buffer, NULL if no memory\n */\nstruct mbuf *mbuf_dup(struct mbuf *mbd)\n{\n\tstruct mbuf *mb;\n\n\tif (!mbd)\n\t\treturn NULL;\n\n\tmb = mbuf_alloc(mbd->size);\n\tif (!mb)\n\t\treturn NULL;\n\n\tmb->size = mbd->size;\n\tmb->pos\t = mbd->pos;\n\tmb->end\t = mbd->end;\n\n\tmemcpy(mb->buf, mbd->buf, mbd->size);\n\n\treturn mb;\n}\n\n\n/**\n * Allocate a new memory buffer with a reference to another mbuf\n *\n * @param mbr Memory buffer to reference\n *\n * @return New memory buffer, NULL if no memory\n */\nstruct mbuf *mbuf_alloc_ref(struct mbuf *mbr)\n{\n\tstruct mbuf *mb;\n\n\tif (!mbr)\n\t\treturn NULL;\n\n\tmb = mem_zalloc(sizeof(*mb), mbuf_destructor);\n\tif (!mb)\n\t\treturn NULL;\n\n\tmb->buf  = mem_ref(mbr->buf);\n\tmb->size = mbr->size;\n\tmb->pos  = mbr->pos;\n\tmb->end  = mbr->end;\n\n\treturn mb;\n}\n\n\n/**\n * Initialize a memory buffer\n *\n * @param mb Memory buffer to initialize\n */\nvoid mbuf_init(struct mbuf *mb)\n{\n\tif (!mb)\n\t\treturn;\n\n\tmb->buf  = NULL;\n\tmb->size = 0;\n\tmb->pos  = 0;\n\tmb->end  = 0;\n}\n\n\n/**\n * Reset a memory buffer\n *\n * @param mb Memory buffer to reset\n */\nvoid mbuf_reset(struct mbuf *mb)\n{\n\tif (!mb)\n\t\treturn;\n\n\tmb->buf = mem_deref(mb->buf);\n\tmbuf_init(mb);\n}\n\n\n/**\n * Resize a memory buffer\n *\n * @param mb   Memory buffer to resize\n * @param size New buffer size\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_resize(struct mbuf *mb, size_t size)\n{\n\tuint8_t *buf;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\tbuf = mb->buf ? mem_realloc(mb->buf, size) : mem_alloc(size, NULL);\n\tif (!buf)\n\t\treturn ENOMEM;\n\n\tmb->buf  = buf;\n\tmb->size = size;\n\n\treturn 0;\n}\n\n\n/**\n * Trim unused trailing bytes in a memory buffer, resize if necessary\n *\n * @param mb   Memory buffer to trim\n */\nvoid mbuf_trim(struct mbuf *mb)\n{\n\tint err;\n\n\tif (!mb || !mb->end || mb->end == mb->size)\n\t\treturn;\n\n\t/* We shrink - this cannot fail */\n\terr = mbuf_resize(mb, mb->end);\n\tif (err) {\n\t\tDEBUG_WARNING(\"trim: resize failed (%m)\\n\", err);\n\t}\n}\n\n\n/**\n * Shift mbuf content position\n *\n * @param mb    Memory buffer to shift\n * @param shift Shift offset count\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_shift(struct mbuf *mb, ssize_t shift)\n{\n\tsize_t rsize;\n\tuint8_t *p;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\tif (((ssize_t)mb->pos + shift) < 0 ||\n\t    ((ssize_t)mb->end + shift) < 0)\n\t\treturn ERANGE;\n\n\trsize = mb->end + shift;\n\n\tif (rsize > mb->size) {\n\n\t\tint err;\n\n\t\terr = mbuf_resize(mb, rsize);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tp = mbuf_buf(mb);\n\n\tmemmove(p + shift, p, mbuf_get_left(mb));\n\n\tmb->pos += shift;\n\tmb->end += shift;\n\n\treturn 0;\n}\n\n\n/**\n * Write a block of memory to a memory buffer\n *\n * @param mb   Memory buffer\n * @param buf  Memory block to write\n * @param size Number of bytes to write\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_write_mem(struct mbuf *mb, const uint8_t *buf, size_t size)\n{\n\tsize_t rsize;\n\n\tif (!mb || !buf)\n\t\treturn EINVAL;\n\n\trsize = mb->pos + size;\n\n\tif (rsize > mb->size) {\n\t\tconst size_t dsize = mb->size ? (mb->size * 2)\n\t\t\t: DEFAULT_SIZE;\n\t\tint err;\n\n\t\terr = mbuf_resize(mb, MAX(rsize, dsize));\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tmemcpy(mb->buf + mb->pos, buf, size);\n\n\tmb->pos += size;\n\tmb->end  = MAX(mb->end, mb->pos);\n\n\treturn 0;\n}\n\n\n/**\n * Write an Pointer to a memory buffer\n *\n * @param mb Memory buffer\n * @param v  Pointer to write\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_write_ptr(struct mbuf *mb, intptr_t v)\n{\n\treturn mbuf_write_mem(mb, (uint8_t *)&v, sizeof(v));\n}\n\n\n/**\n * Write an 8-bit value to a memory buffer\n *\n * @param mb Memory buffer\n * @param v  8-bit value to write\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_write_u8(struct mbuf *mb, uint8_t v)\n{\n\treturn mbuf_write_mem(mb, (uint8_t *)&v, sizeof(v));\n}\n\n\n/**\n * Write a 16-bit value to a memory buffer\n *\n * @param mb Memory buffer\n * @param v  16-bit value to write\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_write_u16(struct mbuf *mb, uint16_t v)\n{\n\treturn mbuf_write_mem(mb, (uint8_t *)&v, sizeof(v));\n}\n\n\n/**\n * Write a 32-bit value to a memory buffer\n *\n * @param mb Memory buffer\n * @param v  32-bit value to write\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_write_u32(struct mbuf *mb, uint32_t v)\n{\n\treturn mbuf_write_mem(mb, (uint8_t *)&v, sizeof(v));\n}\n\n\n/**\n * Write a 64-bit value to a memory buffer\n *\n * @param mb Memory buffer\n * @param v  64-bit value to write\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_write_u64(struct mbuf *mb, uint64_t v)\n{\n\treturn mbuf_write_mem(mb, (uint8_t *)&v, sizeof(v));\n}\n\n\n/**\n * Write a null-terminated string to a memory buffer\n *\n * @param mb  Memory buffer\n * @param str Null terminated string to write\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_write_str(struct mbuf *mb, const char *str)\n{\n\tif (!str)\n\t\treturn EINVAL;\n\n\treturn mbuf_write_mem(mb, (const uint8_t *)str, strlen(str));\n}\n\n\n/**\n * Write a pointer-length string to a memory buffer\n *\n * @param mb  Memory buffer\n * @param pl  Pointer-length string\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_write_pl(struct mbuf *mb, const struct pl *pl)\n{\n\tif (!pl)\n\t\treturn EINVAL;\n\n\treturn mbuf_write_mem(mb, (const uint8_t *)pl->p, pl->l);\n}\n\n\n/**\n * Read a block of memory from a memory buffer\n *\n * @param mb   Memory buffer\n * @param buf  Buffer to read data to\n * @param size Size of buffer\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_read_mem(struct mbuf *mb, uint8_t *buf, size_t size)\n{\n\tif (!mb || !buf)\n\t\treturn EINVAL;\n\n\tif (size > mbuf_get_left(mb)) {\n\t\tDEBUG_WARNING(\"tried to read beyond mbuf end (%zu > %zu)\\n\",\n\t\t\t      size, mbuf_get_left(mb));\n\t\treturn EOVERFLOW;\n\t}\n\n\tmemcpy(buf, mb->buf + mb->pos, size);\n\n\tmb->pos += size;\n\n\treturn 0;\n}\n\n\n/**\n * Read an Pointer from a memory buffer\n *\n * @param mb Memory buffer\n *\n * @return Pointer on success, otherwise 0\n */\nintptr_t mbuf_read_ptr(struct mbuf *mb)\n{\n\tintptr_t v;\n\n\treturn (0 == mbuf_read_mem(mb, (uint8_t *)&v, sizeof(v))) ? v : 0;\n}\n\n\n/**\n * Read an 8-bit value from a memory buffer\n *\n * @param mb Memory buffer\n *\n * @return 8-bit value\n */\nuint8_t mbuf_read_u8(struct mbuf *mb)\n{\n\tuint8_t v;\n\n\treturn (0 == mbuf_read_mem(mb, &v, sizeof(v))) ? v : 0;\n}\n\n\n/**\n * Read a 16-bit value from a memory buffer\n *\n * @param mb Memory buffer\n *\n * @return 16-bit value\n */\nuint16_t mbuf_read_u16(struct mbuf *mb)\n{\n\tuint16_t v;\n\n\treturn (0 == mbuf_read_mem(mb, (uint8_t *)&v, sizeof(v))) ? v : 0;\n}\n\n\n/**\n * Read a 32-bit value from a memory buffer\n *\n * @param mb Memory buffer\n *\n * @return 32-bit value\n */\nuint32_t mbuf_read_u32(struct mbuf *mb)\n{\n\tuint32_t v;\n\n\treturn (0 == mbuf_read_mem(mb, (uint8_t *)&v, sizeof(v))) ? v : 0;\n}\n\n\n/**\n * Read a 64-bit value from a memory buffer\n *\n * @param mb Memory buffer\n *\n * @return 64-bit value\n */\nuint64_t mbuf_read_u64(struct mbuf *mb)\n{\n\tuint64_t v;\n\n\treturn (0 == mbuf_read_mem(mb, (uint8_t *)&v, sizeof(v))) ? v : 0;\n}\n\n\n/**\n * Read a string from a memory buffer\n *\n * @param mb   Memory buffer\n * @param str  Buffer to read string to\n * @param size Size of buffer\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_read_str(struct mbuf *mb, char *str, size_t size)\n{\n\tif (!mb || !str)\n\t\treturn EINVAL;\n\n\twhile (size--) {\n\t\tconst uint8_t c = mbuf_read_u8(mb);\n\t\t*str++ = c;\n\t\tif ('\\0' == c)\n\t\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Duplicate a null-terminated string from a memory buffer\n *\n * @param mb   Memory buffer\n * @param strp Pointer to destination string; allocated and set\n * @param len  Length of string\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_strdup(struct mbuf *mb, char **strp, size_t len)\n{\n\tchar *str;\n\tint err;\n\n\tif (!mb || !strp)\n\t\treturn EINVAL;\n\n\tstr = mem_alloc(len + 1, NULL);\n\tif (!str)\n\t\treturn ENOMEM;\n\n\terr = mbuf_read_mem(mb, (uint8_t *)str, len);\n\tif (err)\n\t\tgoto out;\n\n\tstr[len] = '\\0';\n\n out:\n\tif (err)\n\t\tmem_deref(str);\n\telse\n\t\t*strp = str;\n\n\treturn err;\n}\n\n\nstatic int vprintf_handler(const char *p, size_t size, void *arg)\n{\n\tstruct mbuf *mb = arg;\n\n\treturn mbuf_write_mem(mb, (const uint8_t *)p, size);\n}\n\n\n/**\n * Print a formatted variable argument list to a memory buffer\n *\n * @param mb  Memory buffer\n * @param fmt Formatted string\n * @param ap  Variable argument list\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_vprintf(struct mbuf *mb, const char *fmt, va_list ap)\n{\n\treturn re_vhprintf(fmt, ap, vprintf_handler, mb);\n}\n\n\n/**\n * Print a formatted string to a memory buffer\n *\n * @param mb  Memory buffer\n * @param fmt Formatted string\n *\n * @return 0 if success, otherwise errorcode\n */\nint _mbuf_printf(struct mbuf *mb, const char *fmt, ...)\n{\n\tint err = 0;\n\tva_list ap;\n\n\tva_start(ap, fmt);\n\terr = re_vhprintf(fmt, ap, vprintf_handler, mb);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Print a safe formatted string to a memory buffer\n *\n * @param mb  Memory buffer\n * @param fmt Formatted string\n *\n * @return 0 if success, otherwise errorcode\n */\nint _mbuf_printf_s(struct mbuf *mb, const char *fmt, ...)\n{\n\tint err = 0;\n\tva_list ap;\n\n\tva_start(ap, fmt);\n\terr = re_vhprintf_s(fmt, ap, vprintf_handler, mb);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Write a pointer-length string to a memory buffer, excluding a section\n *\n * @param mb   Memory buffer\n * @param pl   Pointer-length string\n * @param skip Part of pl to exclude\n *\n * @return 0 if success, otherwise errorcode\n *\n * @todo: create substf variante\n */\nint mbuf_write_pl_skip(struct mbuf *mb, const struct pl *pl,\n\t\t       const struct pl *skip)\n{\n\tstruct pl r;\n\tint err;\n\n\tif (!pl || !skip)\n\t\treturn EINVAL;\n\n\tif (pl->p > skip->p || (skip->p + skip->l) > (pl->p + pl->l))\n\t\treturn ERANGE;\n\n\tr.p = pl->p;\n\tr.l = skip->p - pl->p;\n\n\terr = mbuf_write_mem(mb, (const uint8_t *)r.p, r.l);\n\tif (err)\n\t\treturn err;\n\n\tr.p = skip->p + skip->l;\n\tr.l = pl->p + pl->l - r.p;\n\n\treturn mbuf_write_mem(mb, (const uint8_t *)r.p, r.l);\n}\n\n\n/**\n * Write n bytes of value 'c' to a memory buffer\n *\n * @param mb   Memory buffer\n * @param c    Value to write\n * @param n    Number of bytes to write\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_fill(struct mbuf *mb, uint8_t c, size_t n)\n{\n\tsize_t rsize;\n\n\tif (!mb || !n)\n\t\treturn EINVAL;\n\n\trsize = mb->pos + n;\n\n\tif (rsize > mb->size) {\n\t\tconst size_t dsize = mb->size ? (mb->size * 2)\n\t\t\t: DEFAULT_SIZE;\n\t\tint err;\n\n\t\terr = mbuf_resize(mb, MAX(rsize, dsize));\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tmemset(mb->buf + mb->pos, c, n);\n\n\tmb->pos += n;\n\tmb->end  = MAX(mb->end, mb->pos);\n\n\treturn 0;\n}\n\n\n/**\n * Set absolute position and end position\n *\n * @param mb  Memory buffer\n * @param pos Position\n * @param end End position\n */\nvoid mbuf_set_posend(struct mbuf *mb, size_t pos, size_t end)\n{\n\tif (!mb)\n\t\treturn;\n\n\tif (pos > end) {\n\t\tDEBUG_WARNING(\"set_posend: pos %zu > end %zu\\n\",\n\t\t\t      pos, end);\n\t\treturn;\n\t}\n\tif (end > mb->size) {\n\t\tDEBUG_WARNING(\"set_posend: end %zu > size %zu\\n\",\n\t\t\t      end, mb->size);\n\t\treturn;\n\t}\n\n\tmb->pos = pos;\n\tmb->end = end;\n\n\tMBUF_CHECK_POS(mb);\n\tMBUF_CHECK_END(mb);\n}\n\n\n/**\n * Debug the memory buffer\n *\n * @param pf Print handler\n * @param mb Memory buffer\n *\n * @return 0 if success, otherwise errorcode\n */\nint mbuf_debug(struct re_printf *pf, const struct mbuf *mb)\n{\n\tif (!mb)\n\t\treturn 0;\n\n\treturn re_hprintf(pf, \"buf=%p pos=%zu end=%zu size=%zu\",\n\t\t\t  mb->buf, mb->pos, mb->end, mb->size);\n}\n"
  },
  {
    "path": "src/md5/wrap.c",
    "content": "/**\n * @file wrap.c  MD5 wrappers\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#ifdef USE_OPENSSL\n#include <stddef.h>\n#include <openssl/evp.h>\n#include <openssl/md5.h>\n#elif defined (__APPLE__)\n#include <CommonCrypto/CommonDigest.h>\n#elif defined (WIN32)\n#include <windows.h>\n#include <wincrypt.h>\n#elif defined (USE_MBEDTLS)\n#include <mbedtls/md5.h>\n#include <mbedtls/error.h>\n#endif\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_md5.h>\n\n\n#define DEBUG_MODULE \"md5\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n/**\n * Calculate the MD5 hash from a buffer\n *\n * @param d  Data buffer (input)\n * @param n  Number of input bytes\n * @param md Calculated MD5 hash (output)\n */\nvoid md5(const uint8_t *d, size_t n, uint8_t *md)\n{\n#ifdef USE_OPENSSL\n\tEVP_MD_CTX *ctx = EVP_MD_CTX_new();\n\n\tEVP_DigestInit_ex(ctx, EVP_md5(), NULL);\n\tEVP_DigestUpdate(ctx, d, n);\n\tEVP_DigestFinal_ex(ctx, md, NULL);\n\tEVP_MD_CTX_free(ctx);\n#elif defined (__APPLE__)\n\tCC_MD5(d, (unsigned int)n, md);\n\n#elif defined (WIN32)\n\tHCRYPTPROV context;\n\tHCRYPTHASH hash;\n\tDWORD hash_size = MD5_SIZE;\n\n\tCryptAcquireContext(&context, 0, 0, PROV_RSA_FULL,CRYPT_VERIFYCONTEXT);\n\n\tCryptCreateHash(context, CALG_MD5, 0, 0, &hash);\n\tCryptHashData(hash, d, (DWORD)n, 0);\n\tCryptGetHashParam(hash, HP_HASHVAL, md, &hash_size, 0);\n\n\tCryptDestroyHash(hash);\n\tCryptReleaseContext(context, 0);\n#elif defined (MBEDTLS_MD_C)\n\tint err;\n\n\terr = mbedtls_md5(d, n, md);\n\tif (err)\n\t\tDEBUG_WARNING(\"mbedtls_md5: %s\\n\",\n\t\t\t      mbedtls_high_level_strerr(err));\n#else\n#error missing MD5 backend\n#endif\n}\n\n\n/**\n * Calculate the MD5 hash from a formatted string\n *\n * @param md  Calculated MD5 hash\n * @param fmt Formatted string\n *\n * @return 0 if success, otherwise errorcode\n */\nint md5_printf(uint8_t *md, const char *fmt, ...)\n{\n\tstruct mbuf mb;\n\tva_list ap;\n\tint err;\n\n\tmbuf_init(&mb);\n\n\tva_start(ap, fmt);\n\terr = mbuf_vprintf(&mb, fmt, ap);\n\tva_end(ap);\n\n\tif (!err)\n\t\tmd5(mb.buf, mb.end, md);\n\n\tmbuf_reset(&mb);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/mem/mem.c",
    "content": "/**\n * @file mem.c  Memory management with reference counting\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <ctype.h>\n#include <stdlib.h>\n#include <string.h>\n#include <re_types.h>\n#include <re_list.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_mem.h>\n#include <re_btrace.h>\n#include <re_thread.h>\n#include <re_atomic.h>\n\n\n#define DEBUG_MODULE \"mem\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#ifndef RELEASE\n#define MEM_DEBUG 1  /**< Enable memory debugging */\n#endif\n\n\n/** Defines a reference-counting memory object */\nstruct mem {\n\tRE_ATOMIC uint32_t nrefs; /**< Number of references  */\n\tuint32_t size;         /**< Size of memory object */\n\tmem_destroy_h *dh;     /**< Destroy handler       */\n#if MEM_DEBUG\n\tsize_t magic;          /**< Magic number          */\n\tstruct le le;          /**< Linked list element   */\n\tstruct btrace btraces; /**< Backtrace array       */\n#endif\n};\n\n#if MEM_DEBUG\n/* Memory debugging */\nstatic struct list meml = LIST_INIT;\nstatic const size_t mem_magic = 0xe7fb9ac4;\nstatic ssize_t threshold = -1;  /**< Memory threshold, disabled by default */\n\nstatic struct memstat memstat = {\n\t0,0\n};\n\nstatic once_flag flag = ONCE_FLAG_INIT;\nstatic mtx_t mtx;\n\nstatic void mem_lock_init(void)\n{\n\tmtx_init(&mtx, mtx_plain);\n}\n\nstatic inline void mem_lock(void)\n{\n\tcall_once(&flag, mem_lock_init);\n\n\tmtx_lock(&mtx);\n}\n\nstatic inline void mem_unlock(void)\n{\n\tmtx_unlock(&mtx);\n}\n\n/** Update statistics for mem_zalloc() */\n#define STAT_ALLOC(_m, _size) \\\n\tmem_lock(); \\\n\tmemstat.bytes_cur += (_size); \\\n\t++memstat.blocks_cur; \\\n\tmem_unlock(); \\\n\t(_m)->size = (uint32_t)(_size); \\\n\t(_m)->magic = mem_magic;\n\n/** Update statistics for mem_realloc() */\n#define STAT_REALLOC(_m, _size) \\\n\tmem_lock(); \\\n\tmemstat.bytes_cur += ((_size) - (_m)->size); \\\n\tmem_unlock(); \\\n\t(_m)->size = (uint32_t)(_size)\n\n/** Update statistics for mem_deref() */\n#define STAT_DEREF(_m) \\\n\tmem_lock(); \\\n\tmemstat.bytes_cur -= (_m)->size; \\\n\t--memstat.blocks_cur; \\\n\tmem_unlock(); \\\n\tmemset((_m), 0xb5, (size_t)mem_header_size + (_m)->size)\n\n/** Check magic number in memory object */\n#define MAGIC_CHECK(_m) \\\n\tif (mem_magic != (_m)->magic) { \\\n\t\tDEBUG_WARNING(\"%s: magic check failed 0x%08zx (%p)\\n\", \\\n\t\t\t__func__, (_m)->magic, get_mem_data((_m)));    \\\n\t\tRE_BREAKPOINT;\t\t\t\t\t      \\\n\t}\n#else\n#define STAT_ALLOC(_m, _size) (_m)->size = (uint32_t)(_size);\n#define STAT_REALLOC(_m, _size) (_m)->size = (uint32_t)(_size);\n#define STAT_DEREF(_m)\n#define MAGIC_CHECK(_m)\n#endif\n\n\nenum {\n#if defined(__x86_64__)\n\t/* Use 16-byte alignment on x86-x32 as well */\n\tmem_alignment = 16u,\n#else\n\tmem_alignment = sizeof(void*) >= 8u ? 16u : 8u,\n#endif\n\talignment_mask = mem_alignment - 1u,\n\tmem_header_size = (sizeof(struct mem) + alignment_mask) &\n\t\t(~(size_t)alignment_mask)\n};\n\n#define MEM_SIZE_MAX \\\n\t(size_t)(sizeof(size_t) > sizeof(uint32_t) ? \\\n\t\t(~(uint32_t)0u) : (~(size_t)0u) - mem_header_size)\n\n\nstatic inline struct mem *get_mem(void *p)\n{\n\treturn (struct mem *)(void *)(((unsigned char *)p) - mem_header_size);\n}\n\n\nstatic inline void *get_mem_data(struct mem *m)\n{\n\treturn (void *)(((unsigned char *)m) + mem_header_size);\n}\n\n\n/**\n * Allocate a new reference-counted memory object\n *\n * @param size Size of memory object\n * @param dh   Optional destructor, called when destroyed\n *\n * @return Pointer to allocated object\n */\nvoid *mem_alloc(size_t size, mem_destroy_h *dh)\n{\n\tstruct mem *m;\n\n\tif (size > MEM_SIZE_MAX)\n\t\treturn NULL;\n\n#if MEM_DEBUG\n\tmem_lock();\n\tif (-1 != threshold && (memstat.blocks_cur >= (size_t)threshold)) {\n\t\tmem_unlock();\n\t\treturn NULL;\n\t}\n\tmem_unlock();\n#endif\n\n\tm = malloc(mem_header_size + size);\n\tif (!m)\n\t\treturn NULL;\n\n#if MEM_DEBUG\n\tbtrace(&m->btraces);\n\tmemset(&m->le, 0, sizeof(struct le));\n\tmem_lock();\n\tlist_append(&meml, &m->le, m);\n\tmem_unlock();\n#endif\n\tre_atomic_rlx_set(&m->nrefs, 1u);\n\tm->dh    = dh;\n\n\tSTAT_ALLOC(m, size);\n\n\treturn get_mem_data(m);\n}\n\n\n/**\n * Allocate a new reference-counted memory object. Memory is zeroed.\n *\n * @param size Size of memory object\n * @param dh   Optional destructor, called when destroyed\n *\n * @return Pointer to allocated object\n */\nvoid *mem_zalloc(size_t size, mem_destroy_h *dh)\n{\n\tvoid *p;\n\n\tp = mem_alloc(size, dh);\n\tif (!p)\n\t\treturn NULL;\n\n\tmemset(p, 0, size);\n\n\treturn p;\n}\n\n\n/**\n * Re-allocate a reference-counted memory object\n *\n * @param data Memory object\n * @param size New size of memory object\n *\n * @return New pointer to allocated object\n *\n * @note Realloc NULL pointer is not supported\n */\nvoid *mem_realloc(void *data, size_t size)\n{\n\tstruct mem *m, *m2;\n\n\tif (!data)\n\t\treturn NULL;\n\n\tif (size > MEM_SIZE_MAX)\n\t\treturn NULL;\n\n\tm = get_mem(data);\n\n\tMAGIC_CHECK(m);\n\n\tif (re_atomic_acq(&m->nrefs) > 1u) {\n\t\tvoid* p = mem_alloc(size, m->dh);\n\t\tif (p) {\n\t\t\tmemcpy(p, data, (m->size < size) ? m->size : size);\n\t\t\tmem_deref(data);\n\t\t}\n\t\treturn p;\n\t}\n\n#if MEM_DEBUG\n\tmem_lock();\n\n\t/* Simulate OOM */\n\tif (-1 != threshold && size > m->size) {\n\t\tif (memstat.blocks_cur >= (size_t)threshold) {\n\t\t\tmem_unlock();\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tlist_unlink(&m->le);\n\n\tmem_unlock();\n#endif\n\n\tm2 = realloc(m, mem_header_size + size);\n\n#if MEM_DEBUG\n\tmem_lock();\n\tlist_append(&meml, m2 ? &m2->le : &m->le, m2 ? m2 : m);\n\tmem_unlock();\n#endif\n\n\tif (!m2) {\n\t\treturn NULL;\n\t}\n\n\tSTAT_REALLOC(m2, size);\n\n\treturn get_mem_data(m2);\n}\n\n\n/**\n * Re-allocate a reference-counted array\n *\n * @param ptr      Pointer to existing array, NULL to allocate a new array\n * @param nmemb    Number of members in array\n * @param membsize Number of bytes in each member\n * @param dh       Optional destructor, only used when ptr is NULL\n *\n * @return New pointer to allocated array\n */\nvoid *mem_reallocarray(void *ptr, size_t nmemb, size_t membsize,\n\t\t       mem_destroy_h *dh)\n{\n\tsize_t tsize;\n\n\tif (membsize && nmemb > MEM_SIZE_MAX / membsize) {\n\t\treturn NULL;\n\t}\n\n\ttsize = nmemb * membsize;\n\n\tif (ptr) {\n\t\treturn mem_realloc(ptr, tsize);\n\t}\n\telse {\n\t\treturn mem_alloc(tsize, dh);\n\t}\n}\n\n\n/**\n * Set or unset a destructor for a memory object\n *\n * @param data Memory object\n * @param dh   called when destroyed, NULL for remove\n */\nvoid mem_destructor(void *data, mem_destroy_h *dh)\n{\n\tstruct mem *m;\n\n\tif (!data)\n\t\treturn;\n\n\tm = get_mem(data);\n\n\tMAGIC_CHECK(m);\n\n\tm->dh = dh;\n}\n\n\n/**\n * Reference a reference-counted memory object\n *\n * @param data Memory object\n *\n * @return Memory object (same as data)\n */\nvoid *mem_ref(void *data)\n{\n\tstruct mem *m;\n\n\tif (!data)\n\t\treturn NULL;\n\n\tm = get_mem(data);\n\n\tMAGIC_CHECK(m);\n\n\tre_atomic_rlx_add(&m->nrefs, 1u);\n\n\treturn data;\n}\n\n\n/**\n * Dereference a reference-counted memory object. When the reference count\n * is zero, the destroy handler will be called (if present) and the memory\n * will be freed\n *\n * @param data Memory object\n *\n * @return Always NULL\n */\n/* coverity[-tainted_data_sink: arg-0] */\nvoid *mem_deref(void *data)\n{\n\tstruct mem *m;\n\n\tif (!data)\n\t\treturn NULL;\n\n\tm = get_mem(data);\n\n\tMAGIC_CHECK(m);\n\n\tif (re_atomic_acq_sub(&m->nrefs, 1u) > 1u) {\n\t\treturn NULL;\n\t}\n\n\tif (m->dh)\n\t\tm->dh(data);\n\n\t/* NOTE: check if the destructor called mem_ref() */\n\tif (re_atomic_rlx(&m->nrefs) > 0u)\n\t\treturn NULL;\n\n#if MEM_DEBUG\n\tmem_lock();\n\tlist_unlink(&m->le);\n\tmem_unlock();\n#endif\n\n\tSTAT_DEREF(m);\n\n\tfree(m);\n\n\treturn NULL;\n}\n\n\n/**\n * Get number of references to a reference-counted memory object\n *\n * @param data Memory object\n *\n * @return Number of references\n */\nuint32_t mem_nrefs(const void *data)\n{\n\tstruct mem *m;\n\n\tif (!data)\n\t\treturn 0;\n\n\tm = get_mem((void*)data);\n\n\tMAGIC_CHECK(m);\n\n\treturn (uint32_t)re_atomic_acq(&m->nrefs);\n}\n\n\n#if MEM_DEBUG\nstatic bool debug_handler(struct le *le, void *arg)\n{\n\tstruct mem *m = le->data;\n\tconst uint8_t *p = get_mem_data(m);\n\tsize_t i;\n\tuint32_t *last_np = arg;\n\n\n\t(void)re_fprintf(stderr, \"  %p: nrefs=%-2u\", p,\n\t\t(uint32_t)re_atomic_rlx(&m->nrefs));\n\n\t(void)re_fprintf(stderr, \" size=%-7u\", m->size);\n\n\t(void)re_fprintf(stderr, \" [\");\n\n\tfor (i=0; i<16; i++) {\n\t\tif (i >= m->size)\n\t\t\t(void)re_fprintf(stderr, \"   \");\n\t\telse\n\t\t\t(void)re_fprintf(stderr, \"%02x \", p[i]);\n\t}\n\n\t(void)re_fprintf(stderr, \"] [\");\n\n\tfor (i=0; i<16; i++) {\n\t\tif (i >= m->size)\n\t\t\t(void)re_fprintf(stderr, \" \");\n\t\telse\n\t\t\t(void)re_fprintf(stderr, \"%c\",\n\t\t\t\t\t isprint(p[i]) ? p[i] : '.');\n\t}\n\n\t(void)re_fprintf(stderr, \"]\");\n\n\tMAGIC_CHECK(m);\n\n\t(void)re_fprintf(stderr, \"\\n\");\n\n\tre_fprintf(stderr, \"%H\\n\", btrace_println, &m->btraces);\n\n\tif (last_np && !--(*last_np))\n\t\treturn true;\n\n\treturn false;\n}\n#endif\n\n\n/**\n * Debug all allocated memory objects\n */\nvoid mem_debug(void)\n{\n#if MEM_DEBUG\n\tuint32_t n;\n\n\tmem_lock();\n\tn = list_count(&meml);\n\tmem_unlock();\n\n\tif (!n)\n\t\treturn;\n\n\tDEBUG_WARNING(\"Possible memory leaks (%u):\\n\", n);\n\n\tmem_lock();\n\t(void)list_apply(&meml, true, debug_handler, NULL);\n\tmem_unlock();\n#endif\n}\n\n\n/**\n * Debug last n allocated memory objects/blocks\n *\n * @param last_n Last number of blocks\n */\nvoid mem_debug_tail(uint32_t last_n)\n{\n#if MEM_DEBUG\n\tuint32_t n;\n\n\tif (!last_n)\n\t\treturn;\n\n\tmem_lock();\n\tn = list_count(&meml);\n\tmem_unlock();\n\n\tif (!n)\n\t\treturn;\n\n\tDEBUG_WARNING(\"Possible Memory leaks (%u/%u) :\\n\", last_n, n);\n\n\tmem_lock();\n\t(void)list_apply(&meml, false, debug_handler, &last_n);\n\tmem_unlock();\n#else\n\t(void)last_n;\n#endif\n}\n\n\n/**\n * Set the memory allocation threshold. This is only used for debugging\n * and out-of-memory simulation\n *\n * @param n Threshold value\n */\nvoid mem_threshold_set(ssize_t n)\n{\n#if MEM_DEBUG\n\tmem_lock();\n\tthreshold = n;\n\tmem_unlock();\n#else\n\t(void)n;\n#endif\n}\n\n\n/**\n * Print memory status\n *\n * @param pf     Print handler for debug output\n * @param unused Unused parameter\n *\n * @return 0 if success, otherwise errorcode\n */\nint mem_status(struct re_printf *pf, void *unused)\n{\n#if MEM_DEBUG\n\tstruct memstat stat;\n\tuint32_t c;\n\tint err = 0;\n\n\t(void)unused;\n\n\tmem_lock();\n\tmemcpy(&stat, &memstat, sizeof(stat));\n\tc = list_count(&meml);\n\tmem_unlock();\n\n\terr |= re_hprintf(pf,\n\t\t\t  \"Memory status: (%zu bytes overhead per block)\\n\",\n\t\t\t  (size_t)mem_header_size);\n\terr |= re_hprintf(pf,\n\t\t\t  \" Cur:  %zu blocks, %zu bytes (total %zu bytes)\\n\",\n\t\t\t  stat.blocks_cur, stat.bytes_cur,\n\t\t\t  stat.bytes_cur\n\t\t\t  + (stat.blocks_cur * (size_t)mem_header_size));\n\terr |= re_hprintf(pf, \" Total %u blocks allocated\\n\", c);\n\n\treturn err;\n#else\n\t(void)pf;\n\t(void)unused;\n\treturn 0;\n#endif\n}\n\n\n/**\n * Get memory statistics\n *\n * @param mstat Returned memory statistics\n *\n * @return 0 if success, otherwise errorcode\n */\nint mem_get_stat(struct memstat *mstat)\n{\n\tif (!mstat)\n\t\treturn EINVAL;\n#if MEM_DEBUG\n\tmem_lock();\n\tmemcpy(mstat, &memstat, sizeof(*mstat));\n\tmem_unlock();\n\treturn 0;\n#else\n\treturn ENOSYS;\n#endif\n}\n"
  },
  {
    "path": "src/mem/mem_pool.c",
    "content": "/**\n * @file mem_pool.c  Pre-Allocated Memory pool management\n *\n * Copyright (C) 2025 Sebastian Reimers\n */\n\n#include <string.h>\n\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_thread.h>\n\n\n#define DEBUG_MODULE \"mem_pool\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstruct mem_pool {\n\tsize_t nmemb;\n\tsize_t membsize;\n\tstruct mem_pool_entry *freel; /* single linked list */\n\tmem_destroy_h *membdh;\n\tstruct mem_pool_entry **objs;\n\tmtx_t *lock;\n};\n\nstruct mem_pool_entry {\n\tstruct mem_pool_entry *next;\n\tvoid *member;\n};\n\n\nstatic void mem_pool_destroy(void *data)\n{\n\tstruct mem_pool *p = data;\n\n\tfor (size_t i = 0; i < p->nmemb; i++) {\n\t\tif (p->objs[i])\n\t\t\tmem_deref(p->objs[i]->member);\n\t\tmem_deref(p->objs[i]);\n\t}\n\n\tmem_deref(p->objs);\n\tmem_deref(p->lock);\n}\n\n\nstatic inline void next_free(struct mem_pool *pool, struct mem_pool_entry *e)\n{\n\te->next\t    = pool->freel;\n\tpool->freel = e;\n}\n\n\n/**\n * @brief Allocate a memory pool\n *\n * This function initializes a memory pool with a specified number of elements,\n * each of a given size. Optionally, a destructor callback can be provided\n * to handle cleanup when a member is released or pool is destroyed\n *\n * @param  poolp    Pointer to the memory pool pointer to be initialized\n * @param  nmemb    Number of elements to allocate in the pool\n * @param  membsize Size of each element in the pool\n * @param  dh       Optional destructor callback for pool cleanup (can be\n *                  NULL)\n *\n * @return 0 for success, otherwise error code\n */\nint mem_pool_alloc(struct mem_pool **poolp, size_t nmemb, size_t membsize,\n\t\t   mem_destroy_h *dh)\n{\n\tint err;\n\n\tif (!poolp || !nmemb || !membsize)\n\t\treturn EINVAL;\n\n\tstruct mem_pool *p = mem_zalloc(sizeof(struct mem_pool), NULL);\n\tif (!p)\n\t\treturn ENOMEM;\n\n\tp->nmemb    = nmemb;\n\tp->membsize = membsize;\n\tp->membdh   = dh;\n\n\tp->objs = mem_zalloc(nmemb * sizeof(struct mem_pool_entry *), NULL);\n\tif (!p->objs) {\n\t\terr = ENOMEM;\n\t\tgoto error;\n\t}\n\n\tmem_destructor(p, mem_pool_destroy);\n\n\terr = mutex_alloc(&p->lock);\n\tif (err)\n\t\tgoto error;\n\n\tfor (size_t i = 0; i < nmemb; i++) {\n\t\tp->objs[i] = mem_zalloc(sizeof(struct mem_pool_entry), NULL);\n\t\tif (!p->objs[i]) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto error;\n\t\t}\n\t\tp->objs[i]->member = mem_zalloc(membsize, dh);\n\t\tif (!p->objs[i]->member) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto error;\n\t\t}\n\t\tnext_free(p, p->objs[i]);\n\t}\n\n\t*poolp = p;\n\n\treturn 0;\n\nerror:\n\tmem_deref(p);\n\treturn err;\n}\n\n\n/**\n * @brief Extend an existing memory pool\n *\n * Adds additional elements to an existing memory pool\n *\n * @param pool Pointer to the memory pool to extend\n * @param num  Number of additional elements to add to the pool\n *\n * @return 0 for success, otherwise error code\n */\nint mem_pool_extend(struct mem_pool *pool, size_t num)\n{\n\tif (!pool || !num)\n\t\treturn EINVAL;\n\n\tmtx_lock(pool->lock);\n\tsize_t nmemb = pool->nmemb + num;\n\n\tstruct mem_pool_entry **objs;\n\tobjs = mem_zalloc(nmemb * sizeof(struct mem_pool_entry *), NULL);\n\tif (!objs) {\n\t\tmtx_unlock(pool->lock);\n\t\treturn ENOMEM;\n\t}\n\n\t/* Copy old members */\n\tsize_t i = 0;\n\tfor (; i < pool->nmemb; i++) {\n\t\tobjs[i] = pool->objs[i];\n\t}\n\n\t/* Allocate new members */\n\tfor (; i < nmemb; i++) {\n\t\tobjs[i] = mem_zalloc(sizeof(struct mem_pool_entry), NULL);\n\t\tif (!objs[i]) {\n\t\t\tmem_deref(objs);\n\t\t\tmtx_unlock(pool->lock);\n\t\t\treturn ENOMEM;\n\t\t}\n\t\tobjs[i]->member = mem_zalloc(pool->membsize, pool->membdh);\n\t\tif (!objs[i]->member) {\n\t\t\tmem_deref(objs[i]);\n\t\t\tmem_deref(objs);\n\t\t\tmtx_unlock(pool->lock);\n\t\t\treturn ENOMEM;\n\t\t}\n\t\tnext_free(pool, objs[i]);\n\t}\n\n\tmem_deref(pool->objs);\n\tpool->objs  = objs;\n\tpool->nmemb = nmemb;\n\n\tmtx_unlock(pool->lock);\n\n\treturn 0;\n}\n\n\n/**\n * @brief Borrow an entry from the memory pool\n *\n * Retrieves an unused entry from the memory pool for temporary use\n *\n * @param pool Pointer to the memory pool\n *\n * @return Pointer to a memory pool entry, or NULL if no entries are available\n */\nstruct mem_pool_entry *mem_pool_borrow(struct mem_pool *pool)\n{\n\tif (!pool)\n\t\treturn NULL;\n\n\tmtx_lock(pool->lock);\n\tstruct mem_pool_entry *e = pool->freel;\n\tif (e) {\n\t\tpool->freel = e->next;\n\t\tmtx_unlock(pool->lock);\n\t\treturn e;\n\t}\n\tmtx_unlock(pool->lock);\n\n\treturn NULL;\n}\n\n\n/**\n * Borrow an entry from the memory pool, extend the pool if necessary\n *\n * @param pool Pointer to the memory pool\n *\n * @return Pointer to a memory pool entry, or NULL on error\n */\nstruct mem_pool_entry *mem_pool_borrow_extend(struct mem_pool *pool)\n{\n\tstruct mem_pool_entry *e = mem_pool_borrow(pool);\n\tif (e)\n\t\treturn e;\n\n\tmem_pool_extend(pool, pool->nmemb * 2);\n\n\treturn mem_pool_borrow(pool);\n}\n\n\n/**\n * @brief Release a borrowed entry back to the memory pool\n *\n * Returns a previously borrowed memory pool entry back to the pool\n * When the entry is released, the member destructor callback (if provided)\n * is called to perform any necessary cleanup. Additionally, the memory\n * associated with the entry is re-initialized to zero to ensure a clean state\n * for future use\n *\n * @param pool  Pointer to the memory pool\n * @param e     Pointer to the memory pool entry to release\n *\n * @return Always NULL\n */\nvoid *mem_pool_release(struct mem_pool *pool, struct mem_pool_entry *e)\n{\n\tif (!pool || !e)\n\t\treturn NULL;\n\n\tmtx_lock(pool->lock);\n\n\tif (pool->membdh)\n\t\tpool->membdh(e->member);\n\n\tmemset(e->member, 0, pool->membsize);\n\tnext_free(pool, e);\n\n\tmtx_unlock(pool->lock);\n\n\treturn NULL;\n}\n\n\n/**\n * Flush mem_pool members\n *\n * @param pool Pointer to the memory pool entry\n */\nvoid mem_pool_flush(struct mem_pool *pool)\n{\n\tmtx_lock(pool->lock);\n\tfor (size_t i = 0; i < pool->nmemb; i++) {\n\t\tstruct mem_pool_entry *e = pool->objs[i];\n\t\tif (pool->membdh)\n\t\t\tpool->membdh(e->member);\n\t\tmemset(e->member, 0, pool->membsize);\n\t\tnext_free(pool, e);\n\t}\n\tmtx_unlock(pool->lock);\n}\n\n\n/**\n * Return Pool member\n *\n * @param entry Pointer to the memory pool entry\n *\n * @return Pointer to the data associated with the memory pool entry or NULL\n */\nvoid *mem_pool_member(const struct mem_pool_entry *entry)\n{\n\treturn entry ? entry->member : NULL;\n}\n"
  },
  {
    "path": "src/mem/secure.c",
    "content": "/**\n * @file mem/secure.c  Secure memory functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#if !defined(__GNUC__) && defined(WIN32)\n#if !defined(WIN32_LEAN_AND_MEAN)\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\n#endif /* !defined(__GNUC__) && defined(WIN32) */\n\n/**\n * Compare two byte strings in constant time. This function can be used\n * by secure code to compare secret data, such as authentication tags,\n * to avoid side-channel attacks.\n *\n * @param s1 First byte string\n * @param s2 Second byte string\n * @param n  Number of bytes\n *\n * @return a negative number if argument errors\n *         0 if both byte strings matching\n *         a positive number if not matching\n */\nint mem_seccmp(const uint8_t *s1, const uint8_t *s2, size_t n)\n{\n\tuint8_t val = 0;\n\tconst volatile uint8_t *p1 = s1;\n\tconst volatile uint8_t *p2 = s2;\n\n\tif (!p1 || !p2)\n\t\treturn -1;\n\n\twhile (n--)\n\t\tval |= *p1++ ^ *p2++;\n\n\treturn val;\n}\n\n\n#if !defined(__GNUC__) && !defined(WIN32)\n/* Use a volatile pointer to memset to force the compiler always\n * call it and not optimize away. */\ntypedef void *(memset_t)(void *, int, size_t);\nstatic memset_t *const volatile memset_ptr = &memset;\n#endif\n\n/**\n * Securely clean memory. This function is guaranteed not to get optimized\n * away by compiler.\n *\n * @param data Pointer to data buffer\n * @param size Size of the buffer\n */\nvoid mem_secclean(void *data, size_t size)\n{\n#if defined(__GNUC__)\n\tmemset(data, 0, size);\n\t/* Insert an asm statement that may potentially depend\n\t * on the memory contents that were affected by memset.\n\t * This prevents optimizing away the memset. */\n\t__asm__ __volatile__(\"\" : : \"r\" (data), \"r\" (size) : \"memory\");\n#elif defined(WIN32)\n\tSecureZeroMemory(data, size);\n#else\n\t(*memset_ptr)(data, 0, size);\n#endif\n}\n"
  },
  {
    "path": "src/mod/dl.c",
    "content": "/**\n * @file dl.c  Interface to dynamic linking loader\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <dlfcn.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include \"mod_internal.h\"\n\n\n#define DEBUG_MODULE \"dl\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic const int dl_flag = RTLD_NOW | RTLD_LOCAL;\n\n\n/**\n * Load a dynamic library file\n *\n * @param name Name of library to load\n *\n * @return Opaque library handle, NULL if not loaded\n */\nvoid *_mod_open(const char *name)\n{\n\tvoid *h;\n\n\tif (!name)\n\t\treturn NULL;\n\n\th = dlopen(name, dl_flag);\n\tif (!h) {\n\t\tDEBUG_WARNING(\"mod: %s (%s)\\n\", name, dlerror());\n\t\treturn NULL;\n\t}\n\n\treturn h;\n}\n\n\n/**\n * Resolve address of symbol in dynamic library\n *\n * @param h      Library handle\n * @param symbol Name of symbol to resolve\n *\n * @return Address, NULL if failure\n */\nvoid *_mod_sym(void *h, const char *symbol)\n{\n\tvoid *sym;\n\tconst char *err;\n\n\tif (!h || !symbol)\n\t\treturn NULL;\n\n\t(void)dlerror(); /* Clear any existing error */\n\n\tsym = dlsym(h, symbol);\n\terr = dlerror();\n\tif (err)  {\n\t\tDEBUG_WARNING(\"dlsym: %s\\n\", err);\n\t\treturn NULL;\n\t}\n\n\treturn sym;\n}\n\n\n/**\n * Unload a dynamic library\n *\n * @param h Library handle\n */\nvoid _mod_close(void *h)\n{\n\tint err;\n\n\tif (!h)\n\t\treturn;\n\n\terr = dlclose(h);\n\tif (0 != err) {\n\t\tDEBUG_WARNING(\"dlclose: %d\\n\", err);\n\t}\n}\n"
  },
  {
    "path": "src/mod/mod.c",
    "content": "/**\n * @file mod.c  Loadable modules\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <sys/types.h>\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_mod.h>\n#include \"mod_internal.h\"\n\n\n#define DEBUG_MODULE \"mod\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** Defines a loadable module */\nstruct mod {\n\tstruct le le;                 /**< Linked list element */\n\tvoid *h;                      /**< Module handler      */\n\tconst struct mod_export *me;  /**< Module exports      */\n};\n\n\nstatic struct list modl;  /* struct mod */\n\n\n/**\n * Initialise module loading\n */\nvoid mod_init(void)\n{\n\tlist_init(&modl);\n}\n\n\n/**\n * Unload all modules\n */\nvoid mod_close(void)\n{\n\tlist_flush(&modl);\n}\n\n\nstatic void mod_destructor(void *data)\n{\n\tstruct mod *m = data;\n\tconst struct mod_export *me = m->me;\n\tint err;\n\n\tif (me && me->close && (err = me->close())) {\n\t\tDEBUG_NOTICE(\"close: error (%m)\\n\", err);\n\t}\n\n\tlist_unlink(&m->le);\n\n\t_mod_close(m->h);\n}\n\n\n/**\n * Find a module by name in the list of loaded modules\n *\n * @param name Name of module to find\n *\n * @return Module if found, NULL if not found\n */\nstruct mod *mod_find(const char *name)\n{\n\tstruct le *le;\n\tstruct pl x;\n\n\tif (!name)\n\t\treturn NULL;\n\n\tif (re_regex(name, strlen(name), \"[/]*[^./]+\" MOD_EXT, NULL, &x))\n\t\treturn NULL;\n\n\tfor (le = modl.head; le; le = le->next) {\n\t\tstruct mod *m = le->data;\n\n\t\tif (0 == pl_strcasecmp(&x, m->me->name))\n\t\t\treturn m;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Load and initialise a loadable module by name\n *\n * @param mp   Pointer to allocated module object\n * @param name Name of loadable module\n *\n * @return 0 if success, otherwise errorcode\n */\nint mod_load(struct mod **mp, const char *name)\n{\n\tstruct mod *m;\n\tint err = 0;\n\n\tif (!mp || !name)\n\t\treturn EINVAL;\n\n\t/* check if already loaded */\n\tm = mod_find(name);\n\tif (m) {\n\t\tDEBUG_NOTICE(\"module already loaded: %s\\n\", name);\n\t\treturn EALREADY;\n\t}\n\n\tm = mem_zalloc(sizeof(*m), mod_destructor);\n\tif (!m)\n\t\treturn ENOMEM;\n\n\tlist_append(&modl, &m->le, m);\n\n\tm->h = _mod_open(name);\n\tif (!m->h) {\n\t\terr = ENOENT;\n\t\tgoto out;\n\t}\n\n\tm->me = _mod_sym(m->h, \"exports\");\n\tif (!m->me) {\n\t\terr = ELIBBAD;\n\t\tgoto out;\n\t}\n\n\tif (m->me->init && (err = m->me->init()))\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(m);\n\telse\n\t\t*mp = m;\n\n\treturn err;\n}\n\n\n/**\n * Add and initialise an external module with exports\n *\n * @param mp   Pointer to allocated module object\n * @param me   Module exports\n *\n * @return 0 if success, otherwise errorcode\n */\nint mod_add(struct mod **mp, const struct mod_export *me)\n{\n\tstruct mod *m;\n\tint err = 0;\n\n\tif (!mp || !me)\n\t\treturn EINVAL;\n\n\t/* check if already loaded */\n\tm = mod_find(me->name);\n\tif (m) {\n\t\tDEBUG_NOTICE(\"module already loaded: %s\\n\", me->name);\n\t\treturn EALREADY;\n\t}\n\n\tm = mem_zalloc(sizeof(*m), mod_destructor);\n\tif (!m)\n\t\treturn ENOMEM;\n\n\tlist_append(&modl, &m->le, m);\n\n\tm->me = me;\n\n\tif (m->me->init)\n\t\terr = m->me->init();\n\n\tif (err)\n\t\tmem_deref(m);\n\telse\n\t\t*mp = m;\n\n\treturn err;\n}\n\n\n/**\n * Get module export from a loadable module\n *\n * @param m Loadable module\n *\n * @return Module export\n */\nconst struct mod_export *mod_export(const struct mod *m)\n{\n\treturn m ? m->me : NULL;\n}\n\n\n/**\n * Get the list of loaded modules\n *\n * @return Module list\n */\nstruct list *mod_list(void)\n{\n\treturn &modl;\n}\n\n\n/**\n * Debug loadable modules\n *\n * @param pf     Print handler for debug output\n * @param unused Unused parameter\n *\n * @return 0 if success, otherwise errorcode\n */\nint mod_debug(struct re_printf *pf, void *unused)\n{\n\tstruct le *le;\n\tint err;\n\n\t(void)unused;\n\n\terr = re_hprintf(pf, \"\\n--- Modules (%u) ---\\n\", list_count(&modl));\n\n\tfor (le = modl.head; le && !err; le = le->next) {\n\t\tconst struct mod *m = le->data;\n\t\tconst struct mod_export *me = m->me;\n\n\t\terr = re_hprintf(pf, \" %16s type=%-12s ref=%u\\n\",\n\t\t\t\t me->name, me->type, mem_nrefs(m));\n\t}\n\n\terr |= re_hprintf(pf, \"\\n\");\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/mod/mod_internal.h",
    "content": "/**\n * @file mod_internal.h  Internal interface to loadable module\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n\nvoid *_mod_open(const char *name);\nvoid *_mod_sym(void *h, const char *symbol);\nvoid  _mod_close(void *h);\n\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "src/mod/win32/dll.c",
    "content": "/**\n * @file dll.c  Dynamic library loading for Windows\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <windows.h>\n#include <re_types.h>\n#include \"../mod_internal.h\"\n\n\n#define DEBUG_MODULE \"dll\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/*\n * Open a DLL file\n *\n * @param name  Name of DLL to open\n *\n * @return Handle (NULL if failed)\n */\nvoid *_mod_open(const char *name)\n{\n\tHINSTANCE DllHandle = 0;\n\n\tDEBUG_INFO(\"loading %s\\n\", name);\n\n\tDllHandle = LoadLibraryA(name);\n\tif (!DllHandle) {\n\t\tDEBUG_WARNING(\"open: %s LoadLibraryA() failed\\n\", name);\n\t\treturn NULL;\n\t}\n\n\treturn DllHandle;\n}\n\n\n/*\n * Resolve a symbol address in a DLL\n *\n * @param h       DLL Handle\n * @param symbol  Symbol to resolve\n *\n * @return Address of symbol\n */\nvoid *_mod_sym(void *h, const char *symbol)\n{\n\tHINSTANCE DllHandle = (HINSTANCE)h;\n\tunion {\n\t\tFARPROC sym;\n\t\tvoid *ptr;\n\t} u;\n\n\tif (!DllHandle)\n\t\treturn NULL;\n\n\tDEBUG_INFO(\"get symbol: %s\\n\", symbol);\n\n\tu.sym = GetProcAddress(DllHandle, symbol);\n\tif (!u.sym) {\n\t\tDEBUG_WARNING(\"GetProcAddress: no symbol %s\\n\", symbol);\n\t\treturn NULL;\n\t}\n\n\treturn u.ptr;\n}\n\n\n/*\n * Close a DLL\n *\n * @param h DLL Handle\n */\nvoid _mod_close(void *h)\n{\n\tHINSTANCE DllHandle = (HINSTANCE)h;\n\n\tDEBUG_INFO(\"unloading %p\\n\", h);\n\n\tif (!DllHandle)\n\t\treturn;\n\n\tFreeLibrary(DllHandle);\n}\n"
  },
  {
    "path": "src/mqueue/mqueue.c",
    "content": "/**\n * @file mqueue.c Thread Safe Message Queue\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_net.h>\n#include <re_main.h>\n#include <re_mqueue.h>\n#include \"mqueue.h\"\n\n\n#define MAGIC 0x14553399\n\n\n#ifdef WIN32\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#define close closesocket\n#endif\n\n\n/**\n * Defines a Thread-safe Message Queue\n *\n * The Message Queue can be used to communicate between two threads. The\n * receiving thread must run the re_main() loop which will be woken up on\n * incoming messages from other threads. The sender thread can be any thread.\n */\nstruct mqueue {\n\tre_sock_t pfd[2];\n\tstruct re_fhs *fhs;\n\tmqueue_h *h;\n\tvoid *arg;\n};\n\nstruct msg {\n\tvoid *data;\n\tuint32_t magic;\n\tint id;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct mqueue *q = arg;\n\n\tif (q->pfd[0] != RE_BAD_SOCK) {\n\t\tq->fhs = fd_close(q->fhs);\n\t\t(void)close(q->pfd[0]);\n\t}\n\tif (q->pfd[1] != RE_BAD_SOCK)\n\t\t(void)close(q->pfd[1]);\n}\n\n\nstatic void event_handler(int flags, void *arg)\n{\n\tstruct mqueue *mq = arg;\n\tstruct msg msg;\n\tssize_t n;\n\n\tif (!(flags & FD_READ))\n\t\treturn;\n\n\tn = pipe_read(mq->pfd[0], &msg, sizeof(msg));\n\tif (n < 0)\n\t\treturn;\n\n\tif (n != sizeof(msg)) {\n\t\t(void)re_fprintf(stderr, \"mqueue: short read of %d bytes\\n\",\n\t\t\t\t n);\n\t\treturn;\n\t}\n\n\tif (msg.magic != MAGIC) {\n\t\t(void)re_fprintf(stderr, \"mqueue: bad magic on read (%08x)\\n\",\n\t\t\t\t msg.magic);\n\t\treturn;\n\t}\n\n\tmq->h(msg.id, msg.data, mq->arg);\n}\n\n\n/**\n * Allocate a new Message Queue\n *\n * @param mqp Pointer to allocated Message Queue\n * @param h   Message handler\n * @param arg Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint mqueue_alloc(struct mqueue **mqp, mqueue_h *h, void *arg)\n{\n\tstruct mqueue *mq;\n\tint err = 0;\n\n\tif (!mqp || !h)\n\t\treturn EINVAL;\n\n\tmq = mem_zalloc(sizeof(*mq), destructor);\n\tif (!mq)\n\t\treturn ENOMEM;\n\n\tmq->fhs = NULL;\n\tmq->h   = h;\n\tmq->arg = arg;\n\n\tmq->pfd[0] = mq->pfd[1] = RE_BAD_SOCK;\n\tif (pipe(mq->pfd) < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tgoto out;\n\t}\n\n\terr = net_sockopt_blocking_set(mq->pfd[0], false);\n\tif (err)\n\t\tgoto out;\n\n\terr = net_sockopt_blocking_set(mq->pfd[1], false);\n\tif (err)\n\t\tgoto out;\n\n\terr = fd_listen(&mq->fhs, mq->pfd[0], FD_READ, event_handler, mq);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(mq);\n\telse\n\t\t*mqp = mq;\n\n\treturn err;\n}\n\n\n/**\n * Push a new message onto the Message Queue\n *\n * @param mq   Message Queue\n * @param id   General purpose Identifier\n * @param data Application data\n *\n * @return 0 if success, otherwise errorcode\n */\nint mqueue_push(struct mqueue *mq, int id, void *data)\n{\n\tstruct msg msg;\n\tssize_t n;\n\n\tif (!mq)\n\t\treturn EINVAL;\n\n\tmsg.id    = id;\n\tmsg.data  = data;\n\tmsg.magic = MAGIC;\n\n\tn = pipe_write(mq->pfd[1], &msg, sizeof(msg));\n\tif (n < 0)\n\t\treturn errno;\n\n\treturn (n != sizeof(msg)) ? EPIPE : 0;\n}\n"
  },
  {
    "path": "src/mqueue/mqueue.h",
    "content": "/**\n * @file mqueue.h Thread Safe Message Queue -- Internal API\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n#ifdef WIN32\nint pipe(re_sock_t fds[2]);\nssize_t pipe_read(re_sock_t s, void *buf, size_t len);\nssize_t pipe_write(re_sock_t s, const void *buf, size_t len);\n#else\nstatic inline ssize_t pipe_read(re_sock_t s, void *buf, size_t len)\n{\n\treturn read(s, buf, len);\n}\n\n\nstatic inline ssize_t pipe_write(re_sock_t s, const void *buf, size_t len)\n{\n\treturn write(s, buf, len);\n}\n#endif\n"
  },
  {
    "path": "src/mqueue/win32/pipe.c",
    "content": "/**\n * @file pipe.c Pipe-emulation for Windows\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <winsock2.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_net.h>\n#include \"../mqueue.h\"\n\n\n/*\n * Emulate pipe on Windows -- pipe() with select() is not working\n */\nint pipe(re_sock_t fds[2])\n{\n\tSOCKET s, rd, wr;\n\tstruct sockaddr_in serv_addr;\n\tint len = sizeof(serv_addr);\n\n\tif ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)\n\t\treturn ENOSYS;\n\n\tmemset((void *) &serv_addr, 0, sizeof(serv_addr));\n\tserv_addr.sin_family = AF_INET;\n\tserv_addr.sin_port = htons(0);\n\tserv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\n\tif (bind(s, (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR)\n\t\tgoto error;\n\n\tif (listen(s, 1) == SOCKET_ERROR)\n\t\tgoto error;\n\n\tif (getsockname(s, (SOCKADDR *) &serv_addr, &len) == SOCKET_ERROR)\n\t\tgoto error;\n\n\tif ((wr = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)\n\t\tgoto error;\n\n\tif (connect(wr, (SOCKADDR *) &serv_addr, len) == SOCKET_ERROR) {\n\t\tclosesocket(wr);\n\t\tgoto error;\n\t}\n\n\trd = accept(s, (SOCKADDR *) &serv_addr, &len);\n\tif (rd == INVALID_SOCKET) {\n\t\tclosesocket(wr);\n\t\tgoto error;\n\t}\n\n\tfds[0] = rd;\n\tfds[1] = wr;\n\n\tclosesocket(s);\n\treturn 0;\n\nerror:\n\tclosesocket(s);\n\treturn ENOSYS;\n}\n\n\nssize_t pipe_read(re_sock_t s, void *buf, size_t len)\n{\n\tint ret = recv(s, buf, (int)len, 0);\n\n\tif (ret < 0 && WSAGetLastError() == WSAECONNRESET)\n\t\tret = 0;\n\n\treturn ret;\n}\n\n\nssize_t pipe_write(re_sock_t s, const void *buf, size_t len)\n{\n\treturn send(s, buf, (int)len, 0);\n}\n\n"
  },
  {
    "path": "src/msg/ctype.c",
    "content": "/**\n * @file ctype.c  Content-Type decode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_msg.h>\n\n\n/**\n * Decode a pointer-length string into Content-Type header\n *\n * @param ctype Content-Type header\n * @param pl    Pointer-length string\n *\n * @return 0 for success, otherwise errorcode\n */\nint msg_ctype_decode(struct msg_ctype *ctype, const struct pl *pl)\n{\n\tstruct pl ws;\n\n\tif (!ctype || !pl)\n\t\treturn EINVAL;\n\n\tif (re_regex(pl->p, pl->l,\n\t\t     \"[ \\t\\r\\n]*[^ \\t\\r\\n;/]+[ \\t\\r\\n]*/[ \\t\\r\\n]*[^ \\t\\r\\n;]+\"\n\t\t     \"[^]*\",\n\t\t     &ws, &ctype->type, NULL, NULL, &ctype->subtype,\n\t\t     &ctype->params))\n\t\treturn EBADMSG;\n\n\tif (ws.p != pl->p)\n\t\treturn EBADMSG;\n\n\treturn 0;\n}\n\n\n/**\n * Compare Content-Type\n *\n * @param ctype   Content-Type header\n * @param type    Media type\n * @param subtype Media sub-type\n *\n * @return true if match, false if no match\n */\nbool msg_ctype_cmp(const struct msg_ctype *ctype,\n\t\t   const char *type, const char *subtype)\n{\n\tif (!ctype || !type || !subtype)\n\t\treturn false;\n\n\tif (pl_strcasecmp(&ctype->type, type))\n\t\treturn false;\n\n\tif (pl_strcasecmp(&ctype->subtype, subtype))\n\t\treturn false;\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/msg/param.c",
    "content": "/**\n * @file param.c  SIP Parameter decode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_msg.h>\n\n\n/**\n * Check if a parameter exists\n *\n * @param pl   Pointer-length string\n * @param name Parameter name\n * @param val  Returned parameter value\n *\n * @return 0 for success, otherwise errorcode\n */\nint msg_param_exists(const struct pl *pl, const char *name, struct pl *val)\n{\n\tstruct pl v1, v2;\n\tchar xpr[128];\n\n\tif (!pl || !name || !val)\n\t\treturn EINVAL;\n\n\t(void)re_snprintf(xpr, sizeof(xpr), \";[ \\t\\r\\n]*%s[ \\t\\r\\n;=]*\", name);\n\n\tif (re_regex(pl->p, pl->l, xpr, &v1, &v2))\n\t\treturn ENOENT;\n\n\tif (!v2.l && v2.p < pl->p + pl->l)\n\t\treturn ENOENT;\n\n\tval->p = v1.p - 1;\n\tval->l = v2.p - val->p;\n\n\treturn 0;\n}\n\n\n/**\n * Decode a Parameter\n *\n * @param pl   Pointer-length string\n * @param name Parameter name\n * @param val  Returned parameter value\n *\n * @return 0 for success, otherwise errorcode\n */\nint msg_param_decode(const struct pl *pl, const char *name, struct pl *val)\n{\n\tchar expr[128];\n\tstruct pl v;\n\n\tif (!pl || !name || !val)\n\t\treturn EINVAL;\n\n\t(void)re_snprintf(expr, sizeof(expr),\n\t\t\t  \";[ \\t\\r\\n]*%s[ \\t\\r\\n]*=[ \\t\\r\\n]*[~ \\t\\r\\n;]+\",\n\t\t\t  name);\n\n\tif (re_regex(pl->p, pl->l, expr, NULL, NULL, NULL, &v))\n\t\treturn ENOENT;\n\n\t*val = v;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/net/bsd/brt.c",
    "content": "/**\n * @file bsd/brt.c  BSD routing table code\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_sa.h>\n#include <re_net.h>\n#include <sys/sysctl.h>\n#include <net/route.h>\n#include <net/if.h>\n\n\n/*\n * See https://github.com/boundary/libdnet/blob/master/src/route-bsd.c\n */\n\n#ifdef __APPLE__\n#define RT_MSGHDR_ALIGNMENT sizeof(uint32_t)\n#else\n#define RT_MSGHDR_ALIGNMENT sizeof(unsigned long)\n#endif\n\n#define ROUNDUP(a) \\\n\t((a) > 0\t\t\t\t\t\t\\\n\t ? (1 + (((size_t)(a) - 1) | (RT_MSGHDR_ALIGNMENT - 1))) \\\n\t : RT_MSGHDR_ALIGNMENT)\n\n\nint net_rt_list(net_rt_h *rth, void *arg)\n{\n\t/* net.route.0.inet.flags.gateway */\n\tint mib[] = {CTL_NET, PF_ROUTE, 0, AF_UNSPEC,\n\t             NET_RT_FLAGS, RTF_GATEWAY};\n\tchar ifname[IFNAMSIZ], *buf, *p;\n\tstruct rt_msghdr *rt;\n\tstruct sockaddr *sa, *sa_tab[RTAX_MAX];\n\tstruct sa dst, gw;\n\tsize_t l;\n\tint i, err = 0;\n\n\tif (sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0)\n\t\treturn errno;\n\tif (!l)\n\t\treturn ENOENT;\n\n\tbuf = mem_alloc(l, NULL);\n\tif (!buf)\n\t\treturn ENOMEM;\n\n\tif (sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) {\n\t\terr = errno;\n\t\tgoto out;\n\t}\n\n\tfor (p = buf; p<buf+l; p += rt->rtm_msglen) {\n\t\trt = (void *)p;  /* buffer is aligned */\n\t\tsa = (struct sockaddr *)(rt + 1);\n\n\t\tif (rt->rtm_type != RTM_GET)\n\t\t\tcontinue;\n\n\t\tif (!(rt->rtm_flags & RTF_UP))\n\t\t\tcontinue;\n\n\t\tfor (i=0; i<RTAX_MAX; i++) {\n\n\t\t\tif (rt->rtm_addrs & (1 << i)) {\n\t\t\t\tsa_tab[i] = sa;\n\t\t\t\tsa = (struct sockaddr *)\n\t\t\t\t\t((char *)sa + ROUNDUP(sa->sa_len));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsa_tab[i] = NULL;\n\t\t\t}\n\t\t}\n\n\t\tif ((rt->rtm_addrs & RTA_DST) == RTA_DST) {\n\t\t\terr = sa_set_sa(&dst, sa_tab[RTAX_DST]);\n\t\t\tif (err)\n\t\t\t\tcontinue;\n\t\t}\n\t\tif ((rt->rtm_addrs & RTA_GATEWAY) == RTA_GATEWAY) {\n\t\t\terr = sa_set_sa(&gw, sa_tab[RTAX_GATEWAY]);\n\t\t\tif (err)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif_indextoname(rt->rtm_index, ifname);\n\n\t\tif (rth(ifname, &dst, 0, &gw, arg))\n\t\t\tbreak;\n\t}\n\n out:\n\tmem_deref(buf);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/net/if.c",
    "content": "/**\n * @file net/if.c  Network interface code\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_net.h>\n\n\n#define DEBUG_MODULE \"netif\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** Interface address entry */\nstruct ifentry {\n\tint af;        /**< Address family */\n\tchar *ifname;  /**< Interface name */\n\tstruct sa *ip; /**< IP address     */\n\tsize_t sz;     /**< Size of buffer */\n\tbool found;    /**< Found flag     */\n};\n\n\nstatic bool if_getname_handler(const char *ifname, const struct sa *sa,\n\t\t\t       void *arg)\n{\n\tstruct ifentry *ife = arg;\n\n\tif (ife->af != sa_af(sa))\n\t\treturn false;\n\n\tif (sa_cmp(sa, ife->ip, SA_ADDR)) {\n\t\tstr_ncpy(ife->ifname, ifname, ife->sz);\n\t\tife->found = true;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/**\n * Get the name of the interface for a given IP address\n *\n * @param ifname Buffer for returned network interface name\n * @param sz     Size of buffer\n * @param af     Address Family\n * @param ip     Given IP address\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_if_getname(char *ifname, size_t sz, int af, const struct sa *ip)\n{\n\tstruct ifentry ife;\n\tint err;\n\n\tif (!ifname || !sz || !ip)\n\t\treturn EINVAL;\n\n\tife.af     = af;\n\tife.ifname = ifname;\n\tife.ip     = (struct sa *)ip;\n\tife.sz     = sz;\n\tife.found  = false;\n\n\terr = net_if_apply(if_getname_handler, &ife);\n\tif (err)\n\t\treturn err;\n\n\tif (!ife.found)\n\t\treturn ENODEV;\n\n\treturn 0;\n}\n\n\nstatic bool if_getaddr_handler(const char *ifname,\n\t\t\t       const struct sa *sa, void *arg)\n{\n\tstruct ifentry *ife = arg;\n\n\t/* Match name of interface? */\n\tif (str_isset(ife->ifname) && 0 != str_casecmp(ife->ifname, ifname))\n\t\treturn false;\n\n\tif (!sa_isset(sa, SA_ADDR))\n\t\treturn false;\n\n#if 1\n\t/* skip loopback and link-local IP */\n\tif (sa_is_loopback(sa) || sa_is_linklocal(sa))\n\t\treturn false;\n#endif\n\n\t/* Match address family */\n\tif (ife->af != sa_af(sa))\n\t\treturn false;\n\n\t/* Match - copy address */\n\tsa_cpy(ife->ip, sa);\n\tife->found = true;\n\n\treturn ife->found;\n}\n\n\n/**\n * Get IP address for a given network interface\n *\n * @param ifname  Network interface name (optional)\n * @param af      Address Family\n * @param ip      Returned IP address\n *\n * @return 0 if success, otherwise errorcode\n *\n * @deprecated Works for IPv4 only\n */\nint net_if_getaddr(const char *ifname, int af, struct sa *ip)\n{\n\tstruct ifentry ife;\n\tint err;\n\n\tif (!ip)\n\t\treturn EINVAL;\n\n\tife.af     = af;\n\tife.ifname = (char *)ifname;\n\tife.ip     = ip;\n\tife.sz     = 0;\n\tife.found  = false;\n\n#ifdef HAVE_GETIFADDRS\n\terr = net_getifaddrs(if_getaddr_handler, &ife);\n#else\n\terr = net_if_list(if_getaddr_handler, &ife);\n#endif\n\n\treturn ife.found ? err : ENODEV;\n}\n\n\nstatic bool if_debug_handler(const char *ifname, const struct sa *sa,\n\t\t\t     void *arg)\n{\n\tstruct re_printf *pf = arg;\n\n\t(void)re_hprintf(pf, \" %10s:  %j\\n\", ifname, sa);\n\n\treturn false;\n}\n\n\n/**\n * Debug network interfaces\n *\n * @param pf     Print handler for debug output\n * @param unused Unused parameter\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_if_debug(struct re_printf *pf, void *unused)\n{\n\tint err;\n\n\t(void)unused;\n\n\terr = re_hprintf(pf, \"net interfaces:\\n\");\n\n#ifdef HAVE_GETIFADDRS\n\terr |= net_getifaddrs(if_debug_handler, pf);\n#else\n\terr |= net_if_list(if_debug_handler, pf);\n#endif\n\n\treturn err;\n}\n\n\nstatic bool linklocal_handler(const char *ifname, const struct sa *sa,\n\t\t\t      void *arg)\n{\n\tvoid **argv = arg;\n\tint af = *(int *)argv[1];\n\n\tif (argv[0] && 0 != str_casecmp(argv[0], ifname))\n\t\treturn false;\n\n\tif (af != AF_UNSPEC && af != sa_af(sa))\n\t\treturn false;\n\n\tif (sa_is_linklocal(sa)) {\n\t\t*((struct sa *)argv[2]) = *sa;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/**\n * Get the Link-local address for a specific network interface\n *\n * @param ifname Name of the interface\n * @param af     Address family\n * @param ip     Returned link-local address\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_if_getlinklocal(const char *ifname, int af, struct sa *ip)\n{\n\tstruct sa addr;\n\tvoid *argv[3];\n\tint err;\n\n\tif (!ip)\n\t\treturn EINVAL;\n\n\tsa_init(&addr, af);\n\n\targv[0] = (void *)ifname;\n\targv[1] = &af;\n\targv[2] = &addr;\n\n\terr = net_if_apply(linklocal_handler, argv);\n\tif (err)\n\t\treturn err;\n\n\tif (!sa_isset(&addr, SA_ADDR))\n\t\treturn ENOENT;\n\n\t*ip = addr;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/net/ifaddrs.c",
    "content": "/**\n * @file ifaddrs.c  Network interface code using getifaddrs().\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <unistd.h>\n#include <sys/socket.h>\n#include <net/if.h>\n#include <ifaddrs.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_sa.h>\n#include <re_net.h>\n\n\n#define DEBUG_MODULE \"ifaddrs\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/**\n * Get a list of all network interfaces including name and IP address.\n * Both IPv4 and IPv6 are supported.\n *\n * @param ifh Interface handler, called once per network interface.\n * @param arg Handler argument.\n *\n * @return 0 if success, otherwise errorcode.\n */\nint net_getifaddrs(net_ifaddr_h *ifh, void *arg)\n{\n\tstruct ifaddrs *ifa, *ifp;\n\tint err;\n\n\tif (!ifh)\n\t\treturn EINVAL;\n\n\tif (0 != getifaddrs(&ifa)) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"getifaddrs: %m\\n\", err);\n\t\treturn err;\n\t}\n\n\tfor (ifp = ifa; ifa; ifa = ifa->ifa_next) {\n\t\tstruct sa sa;\n\n\t\tDEBUG_INFO(\"ifaddr: %10s flags=%08x\\n\", ifa->ifa_name,\n\t\t\t   ifa->ifa_flags);\n\n\t\tif (ifa->ifa_flags & IFF_UP) {\n\t\t\terr = sa_set_sa(&sa, ifa->ifa_addr);\n\t\t\tif (err)\n\t\t\t\tcontinue;\n\n\t\t\tif (ifh(ifa->ifa_name, &sa, arg))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tfreeifaddrs(ifp);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/net/linux/addrs.c",
    "content": "/**\n * @file linux/addrs.c Get interface addresses (See rtnetlink(7))\n *\n * Copyright (C) 2024 Sebastian Reimers\n */\n\n#include <string.h>\n#include <unistd.h>\n#include <linux/netlink.h>\n#include <linux/rtnetlink.h>\n#include <net/if.h>\n\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_mem.h>\n#include <re_sa.h>\n#include <re_net.h>\n#include \"macros.h\"\n\n#define DEBUG_MODULE \"linuxaddrs\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nenum { RE_NETLINK_BUFSZ = 8192 };\n\nstruct iff_up_e {\n\tstruct le le;\n\tuint32_t ifi_index;\n};\n\n\nstatic void parse_rtattr(struct rtattr *tb[], struct rtattr *rta, int len)\n{\n\tmemset(tb, 0, sizeof(struct rtattr *) * (IFA_MAX + 1));\n\twhile (RTA_OK(rta, len)) {\n\t\tif (rta->rta_type <= IFA_MAX) {\n\t\t\ttb[rta->rta_type] = rta;\n\t\t}\n\t\trta = RTA_NEXT(rta, len);\n\t}\n}\n\n\nstatic bool is_ipv6_deprecated(uint32_t flags)\n{\n\tif (flags & (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC | IFA_F_DADFAILED |\n\t\t     IFA_F_DEPRECATED))\n\t\treturn true;\n\n\treturn false;\n}\n\n\nstatic int parse_msg_link(struct nlmsghdr *msg, ssize_t len,\n\t\t\t  struct list *iff_up_l)\n{\n\tstruct nlmsghdr *nlh;\n\tstruct ifinfomsg *ifi;\n\n\tfor (nlh = msg; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {\n\t\tif (nlh->nlmsg_type == NLMSG_DONE) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (nlh->nlmsg_type == NLMSG_ERROR) {\n\t\t\tDEBUG_WARNING(\"netlink recv error\\n\");\n\t\t\treturn EBADMSG;\n\t\t}\n\n\t\tifi = NLMSG_DATA(nlh);\n\n\t\tif (!(ifi->ifi_flags & IFF_UP))\n\t\t\tcontinue;\n\n\t\tstruct iff_up_e *e = mem_zalloc(sizeof(struct iff_up_e), NULL);\n\t\tif (!e)\n\t\t\treturn ENOMEM;\n\n\t\te->ifi_index = ifi->ifi_index;\n\n\t\tlist_append(iff_up_l, &e->le, e);\n\t}\n\n\treturn EALREADY;\n}\n\n\nstatic int parse_msg_addr(struct nlmsghdr *msg, ssize_t len, net_ifaddr_h *ifh,\n\t\t\t  struct list *iff_up_l, void *arg)\n{\n\tstruct nlmsghdr *nlh;\n\tfor (nlh = msg; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {\n\t\tstruct sa sa;\n\t\tuint32_t flags;\n\t\tbool iff_up = false;\n\t\tvoid *addr;\n\t\tchar if_name[IF_NAMESIZE];\n\n\t\tif (nlh->nlmsg_type == NLMSG_DONE) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (nlh->nlmsg_type == NLMSG_ERROR) {\n\t\t\tDEBUG_WARNING(\"netlink recv error\\n\");\n\t\t\treturn EBADMSG;\n\t\t}\n\n\t\tstruct ifaddrmsg *ifa = NLMSG_DATA(nlh);\n\t\tstruct rtattr *rta_tb[IFA_MAX + 1];\n\n\t\tparse_rtattr(rta_tb, IFA_RTA(ifa),\n\t\t\t     nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));\n\n\t\tif (!rta_tb[IFA_ADDRESS])\n\t\t\tcontinue;\n\n\t\tstruct le *le;\n\t\tLIST_FOREACH(iff_up_l, le)\n\t\t{\n\t\t\tstruct iff_up_e *e = le->data;\n\t\t\tif (ifa->ifa_index == e->ifi_index) {\n\t\t\t\tiff_up = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!iff_up)\n\t\t\tcontinue;\n\n\t\tif (rta_tb[IFA_FLAGS] && ifa->ifa_family == AF_INET6) {\n\t\t\tflags = *(uint32_t *)RTA_DATA(rta_tb[IFA_FLAGS]);\n\t\t\tif (is_ipv6_deprecated(flags))\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (rta_tb[IFA_LOCAL])\n\t\t\t/* looks like point-to-point network, use local\n\t\t\t * address, instead of peer */\n\t\t\taddr = RTA_DATA(rta_tb[IFA_LOCAL]);\n\t\telse\n\t\t\taddr = RTA_DATA(rta_tb[IFA_ADDRESS]);\n\n\t\tif (ifa->ifa_family == AF_INET) {\n\t\t\tsa_init(&sa, AF_INET);\n\t\t\tsa.u.in.sin_addr.s_addr = *(uint32_t *)addr;\n\t\t}\n\t\telse if (ifa->ifa_family == AF_INET6) {\n\t\t\tsa_set_in6(&sa, addr, 0);\n\t\t\tsa_set_scopeid(&sa, ifa->ifa_index);\n\t\t}\n\t\telse\n\t\t\tcontinue;\n\n\t\tif (!if_indextoname(ifa->ifa_index, if_name))\n\t\t\tcontinue;\n\n\t\tif (ifh(if_name, &sa, arg))\n\t\t\treturn 0;\n\t}\n\n\treturn EALREADY;\n}\n\n\nint net_netlink_addrs(net_ifaddr_h *ifh, void *arg)\n{\n\tint err = 0;\n\tre_sock_t sock;\n\tssize_t len;\n\tstruct list iff_up_l = LIST_INIT;\n\n\tstruct {\n\t\tstruct nlmsghdr nlh;\n\t\tunion\n\t\t{\n\t\t\tstruct ifinfomsg ifi;\n\t\t\tstruct ifaddrmsg ifa;\n\t\t} u;\n\t} req;\n\n\tif (!ifh)\n\t\treturn EINVAL;\n\n\tvoid *buffer = mem_zalloc(RE_NETLINK_BUFSZ, NULL);\n\tif (!buffer)\n\t\treturn ENOMEM;\n\n\tif ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"socket failed %m\\n\", err);\n\t\treturn err;\n\t}\n\n\tstruct timeval timeout = {5, 0};\n\tsetsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));\n\n\t/* GETLINK */\n\tmemset(&req, 0, sizeof(req));\n\treq.nlh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct ifinfomsg));\n\treq.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;\n\treq.nlh.nlmsg_type  = RTM_GETLINK;\n\n\tif (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"GETLINK send failed %m\\n\", err);\n\t\tgoto out;\n\t}\n\n\twhile ((len = recv(sock, buffer, RE_NETLINK_BUFSZ, 0)) > 0) {\n\t\terr = parse_msg_link((struct nlmsghdr *)buffer, len,\n\t\t\t\t     &iff_up_l);\n\t\tif (err != EALREADY)\n\t\t\tbreak;\n\t}\n\tif (err)\n\t\tgoto out;\n\n\tif (len < 0) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"GETLINK recv failed %m\\n\", err);\n\t\tgoto out;\n\t}\n\n\t/* GETADDR */\n\tmemset(&req, 0, sizeof(req));\n\treq.nlh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct ifaddrmsg));\n\treq.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;\n\treq.nlh.nlmsg_type  = RTM_GETADDR;\n\n\tif (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"GETADDR send failed %m\\n\", err);\n\t\tgoto out;\n\t}\n\n\twhile ((len = recv(sock, buffer, RE_NETLINK_BUFSZ, 0)) > 0) {\n\t\terr = (parse_msg_addr((struct nlmsghdr *)buffer, len, ifh,\n\t\t\t\t      &iff_up_l, arg));\n\t\tif (err != EALREADY)\n\t\t\tbreak;\n\t}\n\tif (err)\n\t\tgoto out;\n\n\tif (len < 0) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"GETADDR recv failed %m\\n\", err);\n\t}\n\nout:\n\tclose(sock);\n\tlist_flush(&iff_up_l);\n\tmem_deref(buffer);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/net/linux/macros.h",
    "content": "/* Override macros to avoid casting alignment warning */\n#undef RTM_RTA\n#define RTM_RTA(r) (void *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))\n#undef RTA_NEXT\n#define RTA_NEXT(rta, len)                                                    \\\n\t((len) -= RTA_ALIGN((rta)->rta_len),                                  \\\n\t (void *)(((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))\n#undef NLMSG_NEXT\n#define NLMSG_NEXT(nlh, len)                                                  \\\n\t((len) -= NLMSG_ALIGN((nlh)->nlmsg_len),                              \\\n\t (void *)(((char *)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))\n#undef IFA_RTA\n#define IFA_RTA(r)                                                            \\\n\t((void *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))\n"
  },
  {
    "path": "src/net/linux/rt.c",
    "content": "/**\n * @file linux/rt.c  Routing table code for Linux. See rtnetlink(7)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <unistd.h>\n#include <netdb.h>\n#include <net/if.h>\n#include <linux/types.h>\n#include <linux/netlink.h>\n#include <linux/rtnetlink.h>\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_fmt.h>\n#include <re_sa.h>\n#include <re_net.h>\n#include \"macros.h\"\n\n\n#define DEBUG_MODULE \"linuxrt\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nenum {BUFSIZE = 8192};\n\n\n/** Defines a network route */\nstruct net_rt {\n\tchar ifname[IFNAMSIZ];  /**< Interface name                 */\n\tstruct sa dst;          /**< Destination IP address/network */\n\tint dstlen;             /**< Prefix length of destination   */\n\tstruct sa gw;           /**< Gateway IP address             */\n};\n\n\nstatic int read_sock(int fd, uint8_t *buf, size_t size, uint32_t seq, int pid)\n{\n\tstruct nlmsghdr *nlhdr;\n\tint n = 0, len = 0;\n\n\tdo {\n\t\t/* Receive response from the kernel */\n\t\tif ((n = (int)recv(fd, buf, size - len, 0)) < 0) {\n\t\t\tDEBUG_WARNING(\"SOCK READ: %m\\n\", errno);\n\t\t\treturn -1;\n\t\t}\n\t\tnlhdr = (struct nlmsghdr *)(void *)buf;\n\n\t\t/* Check if the header is valid */\n\t\tif (0 == NLMSG_OK(nlhdr, (uint32_t)n) ||\n\t\t    NLMSG_ERROR == nlhdr->nlmsg_type) {\n\t\t\tDEBUG_WARNING(\"Error in received packet\\n\");\n\t\t\treturn -1;\n\t\t}\n\n\t\t/* Check if the its the last message */\n\t\tif (NLMSG_DONE == nlhdr->nlmsg_type) {\n\t\t\tbreak;\n\t\t}\n\t\telse{\n\t\t\t/* Else move the pointer to buffer appropriately */\n\t\t\tbuf += n;\n\t\t\tlen += n;\n\t\t}\n\n\t\t/* Check if its a multi part message */\n\t\tif (0 == (nlhdr->nlmsg_flags & NLM_F_MULTI)) {\n\t\t\t/* return if its not */\n\t\t\tbreak;\n\t\t}\n\t} while (nlhdr->nlmsg_seq != seq ||\n\t\t nlhdr->nlmsg_pid != (uint32_t)pid);\n\n\treturn len;\n}\n\n\n/* Parse one route */\nstatic int rt_parse(const struct nlmsghdr *nlhdr, struct net_rt *rt)\n{\n\tstruct rtmsg *rtmsg;\n\tstruct rtattr *rtattr;\n\tint len;\n\n\trtmsg = (struct rtmsg *)NLMSG_DATA(nlhdr);\n\n\t/* If the route does not belong to main routing table then return. */\n\tif (RT_TABLE_MAIN != rtmsg->rtm_table)\n\t\treturn EINVAL;\n\n\tsa_init(&rt->dst, rtmsg->rtm_family);\n\trt->dstlen = rtmsg->rtm_dst_len;\n\tsa_init(&rt->gw, rtmsg->rtm_family);\n\n\t/* get the rtattr field */\n\trtattr = (struct rtattr *)RTM_RTA(rtmsg);\n\tlen = RTM_PAYLOAD(nlhdr);\n\tfor (;RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) {\n\n\t\tswitch (rtattr->rta_type) {\n\n\t\tcase RTA_OIF:\n\t\t\tif_indextoname(*(int *)RTA_DATA(rtattr), rt->ifname);\n\t\t\tbreak;\n\n\t\tcase RTA_GATEWAY:\n\t\t\tswitch (rtmsg->rtm_family) {\n\n\t\t\tcase AF_INET:\n\t\t\t\tsa_init(&rt->gw, AF_INET);\n\t\t\t\trt->gw.u.in.sin_addr.s_addr\n\t\t\t\t\t= *(uint32_t *)RTA_DATA(rtattr);\n\t\t\t\tbreak;\n\n\t\t\tcase AF_INET6:\n\t\t\t\tsa_set_in6(&rt->gw, RTA_DATA(rtattr), 0);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tDEBUG_WARNING(\"RTA_GW: unknown family %d\\n\",\n\t\t\t\t\t      rtmsg->rtm_family);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase RTA_DST:\n\t\t\tswitch (rtmsg->rtm_family) {\n\n\t\t\tcase AF_INET:\n\t\t\t\tsa_init(&rt->dst, AF_INET);\n\t\t\t\trt->dst.u.in.sin_addr.s_addr\n\t\t\t\t\t= *(uint32_t *)RTA_DATA(rtattr);\n\t\t\t\tbreak;\n\n\t\t\tcase AF_INET6:\n\t\t\t\tsa_set_in6(&rt->dst, RTA_DATA(rtattr), 0);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tDEBUG_WARNING(\"RTA_DST: unknown family %d\\n\",\n\t\t\t\t\t      rtmsg->rtm_family);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * List all entries in the routing table\n *\n * @param rth Route entry handler\n * @param arg Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_rt_list(net_rt_h *rth, void *arg)\n{\n\tunion {\n\t\tuint8_t buf[BUFSIZE];\n\t\tstruct nlmsghdr msg[1];\n\t} u;\n\tstruct nlmsghdr *nlmsg;\n\tuint32_t seq = 0;\n\tint sock, len, err = 0;\n\n\tif (!rth)\n\t\treturn EINVAL;\n\n\t/* Create Socket */\n\tif ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {\n\t\tDEBUG_WARNING(\"list: socket(): (%m)\\n\", errno);\n\t\treturn errno;\n\t}\n\n\t/* Initialize the buffer */\n\tmemset(u.buf, 0, sizeof(u.buf));\n\n\t/* point the header and the msg structure pointers into the buffer */\n\tnlmsg = u.msg;\n\n\t/* Fill in the nlmsg header*/\n\tnlmsg->nlmsg_len   = NLMSG_LENGTH(sizeof(struct rtmsg));\n\tnlmsg->nlmsg_type  = RTM_GETROUTE;\n\tnlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;\n\tnlmsg->nlmsg_seq   = seq++;\n\tnlmsg->nlmsg_pid   = getpid();\n\n\t/* Send the request */\n\tif (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"list: write to socket failed (%m)\\n\", err);\n\t\tgoto out;\n\t}\n\n\t/* Read the response */\n\tif ((len = read_sock(sock, u.buf, sizeof(u.buf), seq, getpid())) < 0) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"list: read from socket failed (%m)\\n\", err);\n\t\tgoto out;\n\t}\n\n\t/* Parse and print the response */\n\tfor (; NLMSG_OK(nlmsg,(uint32_t)len); nlmsg = NLMSG_NEXT(nlmsg,len)) {\n\t\tstruct net_rt rt;\n\n\t\tmemset(&rt, 0, sizeof(struct net_rt));\n\t\tif (0 != rt_parse(nlmsg, &rt))\n\t\t\tcontinue;\n\n\t\tif (AF_INET6 == sa_af(&rt.dst)\n\t\t    && IN6_IS_ADDR_UNSPECIFIED(&rt.dst.u.in6.sin6_addr))\n\t\t\tcontinue;\n\n\t\tif (rth(rt.ifname, &rt.dst, rt.dstlen, &rt.gw, arg))\n\t\t\tbreak;\n\t}\n\n out:\n\tclose(sock);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/net/net.c",
    "content": "/**\n * @file net.c  Networking code.\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdlib.h>\n#include <string.h>\n#if !defined(WIN32)\n#include <unistd.h>\n#include <netdb.h>\n#endif\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_net.h>\n#include <re_udp.h>\n#include <re_mem.h>\n\n\n#define DEBUG_MODULE \"net\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/**\n * Get the source IP address for a specified destination\n *\n * @param dst Destination IP address\n * @param ip  Returned Source IP address\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_dst_source_addr_get(const struct sa *dst, struct sa *ip)\n{\n\tint err;\n\tstruct udp_sock *us;\n\n\tif (!dst || !ip || !sa_isset(dst, SA_ADDR)) {\n\t\treturn EINVAL;\n\t}\n\n\tif (sa_af(dst) == AF_INET6)\n\t\terr = sa_set_str(ip, \"::\", 0);\n\telse\n\t\terr = sa_set_str(ip, \"0.0.0.0\", 0);\n\n\tif (err)\n\t\treturn err;\n\n\terr = udp_listen(&us, ip, NULL, NULL);\n\tif (err)\n\t\treturn err;\n\n\terr = udp_connect(us, dst);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_local_get(us, ip);\n\nout:\n\tmem_deref(us);\n\treturn err;\n}\n\n\n/**\n * Get the default source IP address\n *\n * @param af  Address Family\n * @param ip  Returned IP address\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_default_source_addr_get(int af, struct sa *ip)\n{\n\tstruct sa dst;\n\tint err;\n#if !defined(WIN32)\n\tchar ifname[64] = \"\";\n#endif\n\n\tsa_init(&dst, af);\n\n\tif (af == AF_INET6)\n\t\tsa_set_str(&dst, \"1::1\", 53);\n\telse\n\t\tsa_set_str(&dst, \"1.1.1.1\", 53);\n\n\terr = net_dst_source_addr_get(&dst, ip);\n\n\tif (af == AF_INET6 && sa_is_linklocal(ip)) {\n\t\tsa_init(ip, af);\n\t\treturn 0;\n\t}\n\n\tif (!err)\n\t\treturn 0;\n\n#ifdef WIN32\n\treturn err;\n#else\n#ifdef HAVE_ROUTE_LIST\n\t/* Get interface with default route */\n\t(void)net_rt_default_get(af, ifname, sizeof(ifname));\n#endif\n\n\t/* First try with default interface */\n\tif (0 == net_if_getaddr(ifname, af, ip))\n\t\treturn 0;\n\n\t/* Then try first real IP */\n\treturn net_if_getaddr(NULL, af, ip);\n#endif\n}\n\n\n/**\n * Get a list of all network interfaces including name and IP address.\n * Both IPv4 and IPv6 are supported\n *\n * @param ifh Interface handler, called once per network interface\n * @param arg Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_if_apply(net_ifaddr_h *ifh, void *arg)\n{\n#ifdef LINUX\n\treturn net_netlink_addrs(ifh, arg);\n#elif HAVE_GETIFADDRS\n\treturn net_getifaddrs(ifh, arg);\n#else\n\treturn net_if_list(ifh, arg);\n#endif\n}\n\n\nstatic bool net_rt_handler(const char *ifname, const struct sa *dst,\n\t\t\t   int dstlen, const struct sa *gw, void *arg)\n{\n\tvoid **argv = arg;\n\tstruct sa *ip = argv[1];\n\t(void)dst;\n\t(void)dstlen;\n\n\tif (0 == str_cmp(ifname, argv[0])) {\n\t\t*ip = *gw;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/**\n * Get the IP-address of the default gateway\n *\n * @param af  Address Family\n * @param gw  Returned Gateway address\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_default_gateway_get(int af, struct sa *gw)\n{\n\tchar ifname[64];\n\tvoid *argv[2];\n\tint err;\n\n\tif (!af || !gw)\n\t\treturn EINVAL;\n\n\terr = net_rt_default_get(af, ifname, sizeof(ifname));\n\tif (err)\n\t\treturn err;\n\n\targv[0] = ifname;\n\targv[1] = gw;\n\n\terr = net_rt_list(net_rt_handler, argv);\n\tif (err)\n\t\treturn err;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/net/netstr.c",
    "content": "/**\n * @file netstr.c  Network strings\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_net.h>\n\n\n/**\n * Get the name of a protocol\n *\n * @param proto Protocol\n *\n * @return Protocol name\n */\nconst char *net_proto2name(int proto)\n{\n\tswitch (proto) {\n\n\tcase IPPROTO_UDP:     return \"UDP\";\n\tcase IPPROTO_TCP:     return \"TCP\";\n#ifdef IPPROTO_SCTP\n\tcase IPPROTO_SCTP:    return \"SCTP\";\n#endif\n\tdefault:              return \"???\";\n\t}\n}\n\n\n/**\n * Get the name of a address family\n *\n * @param af Address family\n *\n * @return Address family name\n */\nconst char *net_af2name(int af)\n{\n\tswitch (af) {\n\n\tcase AF_UNSPEC:    return \"AF_UNSPEC\";\n\tcase AF_INET:      return \"AF_INET\";\n\tcase AF_INET6:     return \"AF_INET6\";\n\tdefault:           return \"???\";\n\t}\n}\n"
  },
  {
    "path": "src/net/posix/pif.c",
    "content": "/**\n * @file posix/pif.c  POSIX network interface code\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <unistd.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <netdb.h>\n#include <net/if.h>\n#include <arpa/inet.h>\n/*#include <net/if_arp.h>*/\n#ifdef __sun\n#include <sys/sockio.h>\n#endif\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_net.h>\n\n\n#define DEBUG_MODULE \"posixif\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/**\n * Enumerate all network interfaces\n *\n * @param ifh Interface handler\n * @param arg Handler argument\n *\n * @return 0 if success, otherwise errorcode\n *\n * @deprecated Works for IPv4 only\n */\nint net_if_list(net_ifaddr_h *ifh, void *arg)\n{\n\tstruct ifreq ifrv[32], *ifr;\n\tstruct ifconf ifc;\n\tint sockfd = -1;\n\tint err = 0;\n\n\tif (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"interface list: socket(): (%m)\\n\", err);\n\t\tgoto out;\n\t}\n\n\tifc.ifc_len = sizeof(ifrv);\n\tifc.ifc_req = ifrv;\n\n\tif (0 != ioctl(sockfd, SIOCGIFCONF, &ifc)) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"interface list: ioctl SIOCFIFCONF: %m\\n\", err);\n\t\tgoto out;\n\t}\n\n\tfor (ifr = ifc.ifc_req;\n\t     (char *)ifr < ((char *)ifc.ifc_buf + ifc.ifc_len);\n\t     ++ifr) {\n\t\tstruct ifreq ifrr;\n\t\tstruct sa sa;\n\n\t\tif (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data)\n\t\t\tcontinue;  /* duplicate, skip it */\n\n\t\tif (ioctl(sockfd, SIOCGIFFLAGS, ifr))\n\t\t\tcontinue;  /* failed to get flags, skip it */\n\n#if 0\n\t\tif (ifr->ifr_flags & IFF_LOOPBACK)\n\t\t\tcontinue;\n#endif\n\n\t\tif (!(ifr->ifr_flags & IFF_UP))\n\t\t\tcontinue;\n\n\t\tifrr.ifr_addr.sa_family = AF_INET;\n\t\tstr_ncpy(ifrr.ifr_name, ifr->ifr_name, sizeof(ifrr.ifr_name));\n\n\t\tif (ioctl(sockfd, SIOCGIFADDR, &ifrr) < 0) {\n\t\t\terr = errno;\n\t\t\tcontinue;\n\t\t}\n\n\t\terr = sa_set_sa(&sa, &ifrr.ifr_ifru.ifru_addr);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"if_list: sa_set_sa %m\\n\", err);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (ifh && ifh(ifr->ifr_name, &sa, arg))\n\t\t\tbreak;\n\t}\n\n out:\n\tif (sockfd >= 0)\n\t\t(void)close(sockfd);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/net/rt.c",
    "content": "/**\n * @file net/rt.c  Generic routing table code\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_net.h>\n\n\nstruct net_rt {\n\tint af;\n\tchar *ifname;\n\tsize_t size;\n\tint prefix;\n};\n\n\nstatic bool rt_debug_handler(const char *ifname, const struct sa *dst,\n\t\t\t     int dstlen, const struct sa *gw, void *arg)\n{\n\tchar addr[64];\n\tstruct re_printf *pf = arg;\n\tint err = 0;\n\n\t(void)re_snprintf(addr, sizeof(addr), \"%j/%d\", dst, dstlen);\n\n\terr |= re_hprintf(pf, \" %-44s\", addr);\n\terr |= re_hprintf(pf, \"%-40j\", gw);\n\terr |= re_hprintf(pf, \" %-15s \", ifname);\n\n\tif (AF_INET6 == sa_af(dst)) {\n\t\tconst struct sockaddr_in6 *sin6 = &dst->u.in6;\n\t\tconst struct in6_addr *in6 = &sin6->sin6_addr;\n\n\t\tif (IN6_IS_ADDR_MULTICAST(in6))\n\t\t\terr |= re_hprintf(pf, \" MULTICAST\");\n\t\tif (IN6_IS_ADDR_LINKLOCAL(in6))\n\t\t\terr |= re_hprintf(pf, \" LINKLOCAL\");\n\t\tif (IN6_IS_ADDR_SITELOCAL(in6))\n\t\t\terr |= re_hprintf(pf, \" SITELOCAL\");\n\t}\n\n\terr |= re_hprintf(pf, \"\\n\");\n\n\treturn 0 != err;\n}\n\n\n/**\n * Dump the routing table\n *\n * @param pf     Print function for output\n * @param unused Unused parameter\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_rt_debug(struct re_printf *pf, void *unused)\n{\n\tint err = 0;\n\n\t(void)unused;\n\n\terr |= re_hprintf(pf, \"net routes:\\n\");\n\n\terr |= re_hprintf(pf, \" Destination                                 \"\n\t\t\t  \"Next Hop\"\n\t\t\t  \"                                 Iface           \"\n\t\t\t  \"Type\\n\");\n\n\terr |= net_rt_list(rt_debug_handler, pf);\n\n\treturn err;\n}\n\n\nstatic bool rt_default_get_handler(const char *_ifname, const struct sa *dst,\n\t\t\t\t   int dstlen, const struct sa *gw, void *arg)\n{\n\tstruct net_rt *rt = arg;\n\n\t(void)dstlen;\n\t(void)gw;\n\n\tif (sa_af(dst) != rt->af)\n\t\treturn false;\n\n\tswitch (rt->af) {\n\n\tcase AF_INET:\n\t\tif (0 == sa_in(dst)) {\n\t\t\tstr_ncpy(rt->ifname, _ifname, rt->size);\n\t\t\treturn true;\n\t\t}\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tif (IN6_IS_ADDR_MULTICAST(&dst->u.in6.sin6_addr))\n\t\t\treturn false;\n\t\tif (IN6_IS_ADDR_LINKLOCAL(&dst->u.in6.sin6_addr))\n\t\t\treturn false;\n\n\t\tif (dstlen < rt->prefix) {\n\t\t\trt->prefix = dstlen;\n\t\t\tstr_ncpy(rt->ifname, _ifname, rt->size);\n\t\t\treturn false;\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn false;\n}\n\n\n/**\n * Get the interface name of the default route\n *\n * @param af     Address family\n * @param ifname Buffer for returned interface name\n * @param size   Size of buffer\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_rt_default_get(int af, char *ifname, size_t size)\n{\n\tstruct net_rt rt;\n\tint err;\n\n\trt.af     = af;\n\trt.ifname = ifname;\n\trt.size   = size;\n\trt.prefix = 256;\n\n\terr = net_rt_list(rt_default_get_handler, &rt);\n\tif (err)\n\t\treturn err;\n\n\treturn '\\0' != ifname[0] ? 0 : EINVAL;\n}\n\n\n#ifndef HAVE_ROUTE_LIST\n/* We must provide a stub */\nint net_rt_list(net_rt_h *rth, void *arg)\n{\n\t(void)rth;\n\t(void)arg;\n\treturn ENOSYS;\n}\n#endif\n"
  },
  {
    "path": "src/net/sock.c",
    "content": "/**\n * @file net/sock.c  Networking sockets code\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n\n\n#define DEBUG_MODULE \"netsock\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic bool inited = false;\n\n\n#ifdef WIN32\nstatic int wsa_init(void)\n{\n\tWORD wVersionRequested = MAKEWORD(2, 2);\n\tWSADATA wsaData;\n\tint err;\n\n\terr = WSAStartup(wVersionRequested, &wsaData);\n\tif (err != 0) {\n\t\tDEBUG_WARNING(\"Could not load winsock (%m)\\n\", err);\n\t\treturn err;\n\t}\n\n\t/* Confirm that the WinSock DLL supports 2.2.*/\n\t/* Note that if the DLL supports versions greater    */\n\t/* than 2.2 in addition to 2.2, it will still return */\n\t/* 2.2 in wVersion since that is the version we      */\n\t/* requested.                                        */\n\tif (LOBYTE(wsaData.wVersion) != 2 ||\n\t    HIBYTE(wsaData.wVersion) != 2 ) {\n\t\tWSACleanup();\n\t\tDEBUG_WARNING(\"Bad winsock version (%d.%d)\\n\",\n\t\t\t      HIBYTE(wsaData.wVersion),\n\t\t\t      LOBYTE(wsaData.wVersion));\n\t\treturn EINVAL;\n\t}\n\n\treturn 0;\n}\n#endif\n\n\n/**\n * Initialise network sockets\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_sock_init(void)\n{\n\tint err = 0;\n\n\tDEBUG_INFO(\"sock init: inited=%d\\n\", inited);\n\n\tif (inited)\n\t\treturn 0;\n\n#ifdef WIN32\n\terr = wsa_init();\n#endif\n\n\tinited = true;\n\n\treturn err;\n}\n\n\n/**\n * Cleanup network sockets\n */\nvoid net_sock_close(void)\n{\n#ifdef WIN32\n\tconst int err = WSACleanup();\n\tif (0 != err) {\n\t\tDEBUG_WARNING(\"sock close: WSACleanup (%d)\\n\", err);\n\t}\n#endif\n\n\tinited = false;\n\n\tDEBUG_INFO(\"sock close\\n\");\n}\n"
  },
  {
    "path": "src/net/sockopt.c",
    "content": "/**\n * @file sockopt.c  Networking socket options\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include <fcntl.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_net.h>\n\n\n#define DEBUG_MODULE \"sockopt\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** Platform independent buffer type cast */\n#ifdef WIN32\n#define BUF_CAST (char *)\n#else\n#define BUF_CAST\n#endif\n\n\n/**\n * Set socket option blocking or non-blocking\n *\n * @param fd       Socket file descriptor\n * @param blocking true for blocking, false for non-blocking\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_sockopt_blocking_set(re_sock_t fd, bool blocking)\n{\n#ifdef WIN32\n\tunsigned long noblock = !blocking;\n\tint err = 0;\n\n\tif (0 != ioctlsocket(fd, FIONBIO, &noblock)) {\n\t\terr = WSAGetLastError();\n\t\tDEBUG_WARNING(\"nonblock set: fd=%d err=%d (%m)\\n\",\n\t\t\t      fd, err, err);\n\t}\n\treturn err;\n#else\n\tint flags;\n\tint err = 0;\n\n\tflags = fcntl(fd, F_GETFL);\n\tif (-1 == flags) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"sockopt set: fnctl F_GETFL: (%m)\\n\", err);\n\t\tgoto out;\n\t}\n\n\tif (blocking)\n\t\tflags &= ~O_NONBLOCK;\n\telse\n\t\tflags |= O_NONBLOCK;\n\n\tif (-1 == fcntl(fd, F_SETFL, flags)) {\n\t\terr = errno;\n\t\tDEBUG_WARNING(\"sockopt set: fcntl F_SETFL non-block (%m)\\n\",\n\t\t\t      err);\n\t}\n\n out:\n\treturn err;\n#endif\n}\n\n\n/**\n * Set socket option to reuse address and port\n *\n * @param fd     Socket file descriptor\n * @param reuse  true for reuse, false for no reuse\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_sockopt_reuse_set(re_sock_t fd, bool reuse)\n{\n\tint r = reuse;\n\n#ifdef SO_REUSEADDR\n\tif (-1 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,\n\t\t\t     BUF_CAST &r, sizeof(r))) {\n\t\tDEBUG_WARNING(\"SO_REUSEADDR: %m\\n\", errno);\n\t\treturn errno;\n\t}\n#endif\n\n#ifdef SO_REUSEPORT\n\tif (-1 == setsockopt(fd, SOL_SOCKET, SO_REUSEPORT,\n\t\t\t     BUF_CAST &r, sizeof(r))) {\n\t\tDEBUG_INFO(\"SO_REUSEPORT: %m\\n\", errno);\n\t\treturn errno;\n\t}\n#endif\n\n#if !defined(SO_REUSEADDR) && !defined(SO_REUSEPORT)\n\t(void)r;\n\t(void)fd;\n\t(void)reuse;\n\treturn ENOSYS;\n#else\n\treturn 0;\n#endif\n}\n\n\n/**\n * Set socket IPV6_V6ONLY option (not supported on OpenBSD - readonly)\n *\n * @param fd     Socket file descriptor\n * @param only   true for IPv6 only, false for dual socket\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_sockopt_v6only(re_sock_t fd, bool only)\n{\n\tint on = only;\n\n#ifndef OPENBSD\n#ifdef IPV6_V6ONLY\n\tif (-1 == setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,\n\t\t\t     BUF_CAST &on, sizeof(on))) {\n\t\tint err = RE_ERRNO_SOCK;\n\t\tDEBUG_WARNING(\"IPV6_V6ONLY: %m\\n\", err);\n\t\treturn err;\n\t}\n#endif\n#endif\n\treturn 0;\n}\n\n"
  },
  {
    "path": "src/net/win32/wif.c",
    "content": "/**\n * @file wif.c  Windows network interface code\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <winsock2.h>\n#include <iphlpapi.h>\n#include <ifdef.h>\n\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_net.h>\n#include <re_sa.h>\n\n\n#define DEBUG_MODULE \"wif\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/**\n * List interfaces using GetAdaptersAddresses, which handles both\n * IPv4 and IPv6 address families.\n *\n * This is available from Windows XP and Windows Server 2003\n */\nstatic int if_list_gaa(net_ifaddr_h *ifh, void *arg)\n{\n\tIP_ADAPTER_ADDRESSES addrv[64], *cur;\n\tULONG ret, len = sizeof(addrv);\n\tconst ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST;\n\tHANDLE hLib;\n\tunion {\n\t\tFARPROC proc;\n\t\tULONG (WINAPI *gaa)(ULONG, ULONG, PVOID,\n\t\t\t\t    PIP_ADAPTER_ADDRESSES, PULONG);\n\t} u;\n\tbool stop = false;\n\tint err = 0;\n\n\thLib = LoadLibrary(TEXT(\"iphlpapi.dll\"));\n\tif (!hLib)\n\t\treturn ENOSYS;\n\n\tu.proc = GetProcAddress(hLib, TEXT(\"GetAdaptersAddresses\"));\n\tif (!u.proc) {\n\t\terr = ENOSYS;\n\t\tgoto out;\n\t}\n\n\tret = (*u.gaa)(AF_UNSPEC, flags, NULL, addrv, &len);\n\tif (ret != ERROR_SUCCESS) {\n\t\tDEBUG_WARNING(\"if_list: GetAdaptersAddresses ret=%u\\n\", ret);\n\t\terr = ENODEV;\n\t\tgoto out;\n\t}\n\n\tfor (cur = addrv; cur && !stop; cur = cur->Next) {\n\t\tPIP_ADAPTER_UNICAST_ADDRESS ip;\n\n\t\tif (cur->OperStatus != IfOperStatusUp)\n\t\t\tcontinue;\n\n\t\t/* an interface can have many IP-addresses */\n\t\tfor (ip = cur->FirstUnicastAddress; ip; ip = ip->Next) {\n\t\t\tstruct sa sa;\n\n\t\t\tsa_set_sa(&sa, ip->Address.lpSockaddr);\n\n\t\t\tif (ifh && ifh(cur->AdapterName, &sa, arg)) {\n\t\t\t\tstop = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n out:\n\tFreeLibrary(hLib);\n\n\treturn err;\n}\n\n\n/**\n * List interfaces using GetAdaptersInfo, which handles only IPv4 family.\n *\n * This is available from Windows 2000, and also works under Wine.\n */\nstatic int if_list_gai(net_ifaddr_h *ifh, void *arg)\n{\n\tIP_ADAPTER_INFO info[32];\n\tPIP_ADAPTER_INFO p = info;\n\tULONG ulOutBufLen = sizeof(info);\n\tDWORD ret;\n\n\tret = GetAdaptersInfo(info, &ulOutBufLen);\n\tif (ret != ERROR_SUCCESS) {\n\t\tDEBUG_WARNING(\"if_list: GetAdaptersInfo ret=%u\\n\", ret);\n\t\treturn ENODEV;\n\t}\n\n\tfor (p = info; p; p = p->Next) {\n\t\tstruct sa sa;\n\n\t\tif (sa_set_str(&sa, p->IpAddressList.IpAddress.String, 0))\n\t\t\tcontinue;\n\n\t\tif (ifh && ifh(p->AdapterName, &sa, arg))\n\t\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\n\n/*\n * Enumerate all network interfaces\n *\n * @param ifh Interface handler\n * @param arg Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint net_if_list(net_ifaddr_h *ifh, void *arg)\n{\n\t/* Try both methods .. */\n\n\tif (!if_list_gaa(ifh, arg))\n\t\treturn 0;\n\n\treturn if_list_gai(ifh, arg);\n}\n"
  },
  {
    "path": "src/odict/entry.c",
    "content": "/**\n * @file odict/entry.c  Ordered Dictionary -- entry\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n\n#include \"re_types.h\"\n#include \"re_fmt.h\"\n#include \"re_mem.h\"\n#include \"re_list.h\"\n#include \"re_hash.h\"\n#include \"re_odict.h\"\n#include \"odict.h\"\n\n\nstatic void destructor(void *arg)\n{\n\tstruct odict_entry *e = arg;\n\n\tswitch (e->type) {\n\n\tcase ODICT_OBJECT:\n\tcase ODICT_ARRAY:\n\t\tmem_deref(e->u.odict);\n\t\tbreak;\n\n\tcase ODICT_STRING:\n\t\tmem_deref(e->u.str);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\thash_unlink(&e->he);\n\tlist_unlink(&e->le);\n\tmem_deref(e->key);\n}\n\n\nint odict_entry_add(struct odict *o, const char *key,\n\t\t    int type, ...)\n{\n\tstruct odict_entry *e;\n\tva_list ap;\n\tint err;\n\n\tif (!o || !key)\n\t\treturn EINVAL;\n\n\te = mem_zalloc(sizeof(*e), destructor);\n\tif (!e)\n\t\treturn ENOMEM;\n\n\te->type = type;\n\n\terr = str_dup(&e->key, key);\n\tif (err)\n\t\tgoto out;\n\n\tva_start(ap, type);\n\n\tswitch (e->type) {\n\n\tcase ODICT_OBJECT:\n\tcase ODICT_ARRAY:\n\t\te->u.odict = mem_ref(va_arg(ap, struct odict *));\n\t\tbreak;\n\n\tcase ODICT_STRING:\n\t\terr = str_dup(&e->u.str, va_arg(ap, const char *));\n\t\tbreak;\n\n\tcase ODICT_INT:\n\t\te->u.integer = va_arg(ap, int64_t);\n\t\tbreak;\n\n\tcase ODICT_DOUBLE:\n\t\te->u.dbl = va_arg(ap, double);\n\t\tbreak;\n\n\tcase ODICT_BOOL:\n\t\te->u.boolean = va_arg(ap, int);\n\t\tbreak;\n\n\tcase ODICT_NULL:\n\t\tbreak;\n\n\tdefault:\n\t\terr = EINVAL;\n\t\tbreak;\n\t}\n\n\tva_end(ap);\n\n\tif (err)\n\t\tgoto out;\n\n\tlist_append(&o->lst, &e->le, e);\n\thash_append(o->ht, hash_fast_str(e->key), &e->he, e);\n\n out:\n\tif (err)\n\t\tmem_deref(e);\n\n\treturn err;\n}\n\n\nint odict_pl_add(struct odict *od, const char *key,\n\t\t const struct pl *val)\n{\n\tchar *str;\n\tint err = pl_strdup(&str, val);\n\tif (err)\n\t\treturn err;\n\n\terr = odict_entry_add(od, key, ODICT_STRING, str);\n\tmem_deref(str);\n\treturn err;\n}\n\n\nvoid odict_entry_del(struct odict *o, const char *key)\n{\n\tmem_deref((struct odict_entry *)odict_lookup(o, key));\n}\n\n\nint odict_entry_debug(struct re_printf *pf, const struct odict_entry *e)\n{\n\tint err;\n\n\tif (!e)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"%s\", e->key);\n\n\tswitch (e->type) {\n\n\tcase ODICT_OBJECT:\n\tcase ODICT_ARRAY:\n\t\terr |= re_hprintf(pf, \":%H\", odict_debug, e->u.odict);\n\t\tbreak;\n\n\tcase ODICT_STRING:\n\t\terr |= re_hprintf(pf, \":%s\", e->u.str);\n\t\tbreak;\n\n\tcase ODICT_INT:\n\t\terr |= re_hprintf(pf, \":%lli\", e->u.integer);\n\t\tbreak;\n\n\tcase ODICT_DOUBLE:\n\t\terr |= re_hprintf(pf, \":%f\", e->u.dbl);\n\t\tbreak;\n\n\tcase ODICT_BOOL:\n\t\terr |= re_hprintf(pf, \":%s\", e->u.boolean ? \"true\" : \"false\");\n\t\tbreak;\n\n\tcase ODICT_NULL:\n\tcase ODICT_ERR:\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/odict/get.c",
    "content": "/**\n * @file get.c  Ordered Dictionary -- high level accessors\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include \"re_types.h\"\n#include \"re_fmt.h\"\n#include \"re_mem.h\"\n#include \"re_list.h\"\n#include \"re_hash.h\"\n#include \"re_odict.h\"\n#include \"odict.h\"\n\n\nstruct odict *odict_get_object(const struct odict *o, const char *key)\n{\n\tconst struct odict_entry *entry;\n\n\tif (!o || !key)\n\t\treturn NULL;\n\n\tentry = odict_get_type(o, ODICT_OBJECT, key);\n\tif (!entry)\n\t\treturn NULL;\n\n\treturn entry->u.odict;\n}\n\n\nstruct odict *odict_get_array(const struct odict *o, const char *key)\n{\n\tconst struct odict_entry *entry;\n\n\tif (!o || !key)\n\t\treturn NULL;\n\n\tentry = odict_get_type(o, ODICT_ARRAY, key);\n\tif (!entry)\n\t\treturn NULL;\n\n\treturn entry->u.odict;\n}\n\n\nstruct odict *odict_entry_object(const struct odict_entry *e)\n{\n\tif (!e || e->type != ODICT_OBJECT )\n\t\treturn NULL;\n\n\treturn e->u.odict;\n}\n\nstruct odict *odict_entry_array(const struct odict_entry *e)\n{\n\tif (!e || e->type != ODICT_ARRAY)\n\t\treturn NULL;\n\n\treturn e->u.odict;\n}\n\n\nchar *odict_entry_str(const struct odict_entry *e)\n{\n\tif (!e || e->type != ODICT_STRING)\n\t\treturn NULL;\n\n\treturn e->u.str;\n}\n\n\nint64_t odict_entry_int(const struct odict_entry *e)\n{\n\tif (!e || e->type != ODICT_INT)\n\t\treturn 0;\n\n\treturn e->u.integer;\n}\n\n\ndouble odict_entry_dbl(const struct odict_entry *e)\n{\n\tif (!e || e->type != ODICT_DOUBLE)\n\t\treturn 0.0;\n\n\treturn e->u.dbl;\n}\n\n\nbool odict_entry_boolean(const struct odict_entry *e)\n{\n\tif (!e || e->type != ODICT_BOOL)\n\t\treturn false;\n\n\treturn e->u.boolean;\n}\n\n\nenum odict_type odict_entry_type(const struct odict_entry *e)\n{\n\tif (!e)\n\t\treturn ODICT_ERR;\n\n\treturn e->type;\n}\n\n\nconst char *odict_entry_key(const struct odict_entry *e)\n{\n\tif (!e)\n\t\treturn NULL;\n\n\treturn e->key;\n}\n\n\nconst struct odict_entry *odict_get_type(const struct odict *o,\n\t\t\t\t\t enum odict_type type, const char *key)\n{\n\tconst struct odict_entry *entry;\n\n\tif (!o || !key)\n\t\treturn NULL;\n\n\tentry = odict_lookup(o, key);\n\tif (!entry)\n\t\treturn NULL;\n\n\tif (entry->type != type)\n\t\treturn NULL;\n\n\treturn entry;\n}\n\n\nconst char *odict_string(const struct odict *o, const char *key)\n{\n\tconst struct odict_entry *entry;\n\n\tentry = odict_get_type(o, ODICT_STRING, key);\n\tif (!entry)\n\t\treturn NULL;\n\n\treturn entry->u.str;\n}\n\n\nbool odict_get_number(const struct odict *o, uint64_t *num, const char *key)\n{\n\tconst struct odict_entry *entry;\n\n\tif (!o || !key)\n\t\treturn false;\n\n\tentry = odict_lookup(o, key);\n\tif (!entry)\n\t\treturn false;\n\n\tswitch (entry->type) {\n\n\tcase ODICT_DOUBLE:\n\t\tif (num)\n\t\t\t*num = (uint64_t)entry->u.dbl;\n\t\tbreak;\n\n\tcase ODICT_INT:\n\t\tif (num)\n\t\t\t*num = entry->u.integer;\n\t\tbreak;\n\n\tdefault:\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\nbool odict_get_boolean(const struct odict *o, bool *value, const char *key)\n{\n\tconst struct odict_entry *entry;\n\n\tentry = odict_get_type(o, ODICT_BOOL, key);\n\tif (!entry)\n\t\treturn false;\n\n\tif (value)\n\t\t*value = entry->u.boolean;\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/odict/odict.c",
    "content": "/**\n * @file odict.c  Ordered Dictionary\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include \"re_types.h\"\n#include \"re_fmt.h\"\n#include \"re_mem.h\"\n#include \"re_list.h\"\n#include \"re_hash.h\"\n#include \"re_odict.h\"\n#include \"odict.h\"\n#include <math.h>\n#include <float.h>\n\n\nstatic void destructor(void *arg)\n{\n\tstruct odict *o = arg;\n\n\thash_clear(o->ht);\n\tlist_flush(&o->lst);\n\tmem_deref(o->ht);\n}\n\n\nint odict_alloc(struct odict **op, uint32_t hash_size)\n{\n\tstruct odict *o;\n\tint err;\n\n\tif (!op || !hash_size)\n\t\treturn EINVAL;\n\n\to = mem_zalloc(sizeof(*o), destructor);\n\tif (!o)\n\t\treturn ENOMEM;\n\n\terr = hash_alloc(&o->ht, hash_valid_size(hash_size));\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(o);\n\telse\n\t\t*op = o;\n\n\treturn err;\n}\n\n\nconst struct odict_entry *odict_lookup(const struct odict *o, const char *key)\n{\n\tstruct le *le;\n\n\tif (!o || !key)\n\t\treturn NULL;\n\n\tle = list_head(hash_list(o->ht, hash_fast_str(key)));\n\n\twhile (le) {\n\t\tconst struct odict_entry *e = le->data;\n\n\t\tif (!str_cmp(e->key, key))\n\t\t\treturn e;\n\n\t\tle = le->next;\n\t}\n\n\treturn NULL;\n}\n\n\nsize_t odict_count(const struct odict *o, bool nested)\n{\n\tstruct le *le;\n\tsize_t n = 0;\n\n\tif (!o)\n\t\treturn 0;\n\n\tif (!nested)\n\t\treturn list_count(&o->lst);\n\n\tfor (le=o->lst.head; le; le=le->next) {\n\n\t\tconst struct odict_entry *e = le->data;\n\n\t\tswitch (e->type) {\n\n\t\tcase ODICT_OBJECT:\n\t\tcase ODICT_ARRAY:\n\t\t\tn += odict_count(e->u.odict, true);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tn += 1;  /* count all entries */\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn n;\n}\n\n\nint odict_debug(struct re_printf *pf, const struct odict *o)\n{\n\tstruct le *le;\n\tint err;\n\n\tif (!o)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"{\");\n\n\tfor (le=o->lst.head; le; le=le->next) {\n\n\t\tconst struct odict_entry *e = le->data;\n\n\t\terr |= re_hprintf(pf, \" %H\", odict_entry_debug, e);\n\t}\n\n\terr |= re_hprintf(pf, \" }\");\n\n\treturn err;\n}\n\n\nstatic bool cmp_double(double a, double b)\n{\n\treturn fabs(a - b) < DBL_EPSILON;\n}\n\n\nbool odict_value_compare(const struct odict_entry *e1,\n\tconst struct odict_entry *e2, bool ignore_order)\n{\n\tif (!e1 || !e2)\n\t\treturn false;\n\n\tif (odict_entry_type(e1) != odict_entry_type(e2))\n\t\treturn false;\n\n\tswitch (odict_entry_type(e1)) {\n\n\tcase ODICT_OBJECT:\n\t\treturn odict_compare(odict_entry_object(e1),\n\t\t\t\t     odict_entry_object(e2), ignore_order);\n\n\tcase ODICT_ARRAY:\n\t\treturn odict_compare(odict_entry_array(e1),\n\t\t\t\t     odict_entry_array(e2), ignore_order);\n\n\tcase ODICT_INT:\n\t\tif (odict_entry_int(e1) == odict_entry_int(e2))\n\t\t\treturn true;\n\t\tbreak;\n\n\tcase ODICT_DOUBLE:\n\t\tif (cmp_double(odict_entry_dbl(e1), odict_entry_dbl(e2)))\n\t\t\treturn true;\n\t\tbreak;\n\n\tcase ODICT_STRING:\n\t\tif ( 0 == str_cmp(odict_entry_str(e1), odict_entry_str(e2)))\n\t\t\treturn true;\n\t\tbreak;\n\n\tcase ODICT_BOOL:\n\t\tif (odict_entry_boolean(e1) == odict_entry_boolean(e2))\n\t\t\treturn true;\n\t\tbreak;\n\n\tcase ODICT_NULL: /* no check */\n\t\treturn true;\n\n\tdefault:\n\t\treturn false;\n\t}\n\n\treturn false;\n}\n\n\n/* return TRUE if equal */\nbool odict_compare(const struct odict *dict1, const struct odict *dict2,\n\tbool ignore_order)\n{\n\tstruct le *le1, *le2;\n\n\tif (!dict1 || !dict2)\n\t\treturn false;\n\n\tif (odict_count(dict1, true) != odict_count(dict2, true))\n\t\treturn false;\n\n\tfor (le1 = dict1->lst.head, le2 = dict2->lst.head;\n\t     le1 && le2;\n\t     le1 = le1->next, le2 = le2->next) {\n\n\t\tconst struct odict_entry *e1 = le1->data;\n\t\tconst struct odict_entry *e2;\n\n\t\tif (ignore_order)\n\t\t\te2 = odict_lookup(dict2, odict_entry_key(e1));\n\t\telse\n\t\t\te2 = le2->data;\n\n\t\tif (0 != str_cmp(odict_entry_key(e1), odict_entry_key(e2)))\n\t\t\treturn false;\n\n\t\tif (!odict_value_compare(e1, e2, ignore_order))\n\t\t\treturn false;\n\t}\n\n\treturn true;  /* equal */\n}\n"
  },
  {
    "path": "src/odict/odict.h",
    "content": "struct odict_entry {\n\tstruct le le, he;\n\tchar *key;\n\tunion {\n\t\tstruct odict *odict;   /* ODICT_OBJECT / ODICT_ARRAY */\n\t\tchar *str;             /* ODICT_STRING */\n\t\tint64_t integer;       /* ODICT_INT    */\n\t\tdouble dbl;            /* ODICT_DOUBLE */\n\t\tbool boolean;          /* ODICT_BOOL   */\n\t} u;\n\tenum odict_type type;\n};\n"
  },
  {
    "path": "src/odict/type.c",
    "content": "/**\n * @file type.c  Ordered Dictionary -- value types\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n\n#include \"re_types.h\"\n#include \"re_fmt.h\"\n#include \"re_mem.h\"\n#include \"re_list.h\"\n#include \"re_hash.h\"\n#include \"re_odict.h\"\n\n\nbool odict_type_iscontainer(enum odict_type type)\n{\n\tswitch (type) {\n\n\tcase ODICT_OBJECT:\n\tcase ODICT_ARRAY:\n\t\treturn true;\n\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\nbool odict_type_isreal(enum odict_type type)\n{\n\tswitch (type) {\n\n\tcase ODICT_STRING:\n\tcase ODICT_INT:\n\tcase ODICT_DOUBLE:\n\tcase ODICT_BOOL:\n\tcase ODICT_NULL:\n\t\treturn true;\n\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\nconst char *odict_type_name(enum odict_type type)\n{\n\tswitch (type) {\n\n\tcase ODICT_OBJECT: return \"Object\";\n\tcase ODICT_ARRAY:  return \"Array\";\n\tcase ODICT_STRING: return \"String\";\n\tcase ODICT_INT:    return \"Integer\";\n\tcase ODICT_DOUBLE: return \"Double\";\n\tcase ODICT_BOOL:   return \"Boolean\";\n\tcase ODICT_NULL:   return \"Null\";\n\tdefault:           return \"???\";\n\t}\n}\n"
  },
  {
    "path": "src/pcp/README",
    "content": "PCP README:\n----------\n\nPort Control Protocol (PCP) as of RFC 6887\n\n\n\n\nother PCP implementations:\n\n  https://github.com/libpcp/pcp\n\n\n"
  },
  {
    "path": "src/pcp/msg.c",
    "content": "/**\n * @file pcp/msg.c  PCP messages\n *\n * Copyright (C) 2010 - 2016 Alfred E. Heggestad\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_pcp.h>\n#include \"pcp.h\"\n\n\nstatic int pcp_map_decode(struct pcp_map *map, struct mbuf *mb)\n{\n\tuint16_t port;\n\tint err;\n\n\tif (!map || !mb)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < PCP_MAP_SZ)\n\t\treturn EBADMSG;\n\n\t(void)mbuf_read_mem(mb, map->nonce, sizeof(map->nonce));\n\tmap->proto = mbuf_read_u8(mb); mbuf_advance(mb, 3);\n\tmap->int_port = ntohs(mbuf_read_u16(mb));\n\n\tport = ntohs(mbuf_read_u16(mb));\n\terr = pcp_ipaddr_decode(mb, &map->ext_addr);\n\tsa_set_port(&map->ext_addr, port);\n\n\treturn err;\n}\n\n\nstatic int pcp_peer_decode(struct pcp_peer *peer, struct mbuf *mb)\n{\n\tuint16_t port;\n\tint err = 0;\n\n\tif (!peer || !mb)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < PCP_PEER_SZ)\n\t\treturn EBADMSG;\n\n\t/* note: the MAP and PEER opcodes are quite similar */\n\terr = pcp_map_decode(&peer->map, mb);\n\tif (err)\n\t\treturn err;\n\n\tport = ntohs(mbuf_read_u16(mb)); mbuf_advance(mb, 2);\n\terr |= pcp_ipaddr_decode(mb, &peer->remote_addr);\n\tsa_set_port(&peer->remote_addr, port);\n\n\treturn err;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct pcp_msg *msg = arg;\n\n\tlist_flush(&msg->optionl);\n}\n\n\nstatic int pcp_header_encode_request(struct mbuf *mb, enum pcp_opcode opcode,\n\t\t\t      uint32_t req_lifetime, const struct sa *int_addr)\n{\n\tint err = 0;\n\n\tif (!mb || !int_addr)\n\t\treturn EINVAL;\n\n\terr |= mbuf_write_u8(mb, PCP_VERSION);\n\terr |= mbuf_write_u8(mb, opcode);\n\terr |= mbuf_write_u16(mb, 0x0000);\n\terr |= mbuf_write_u32(mb, htonl(req_lifetime));\n\terr |= pcp_ipaddr_encode(mb, int_addr);\n\n\treturn err;\n}\n\n\nstatic int pcp_header_decode(struct pcp_hdr *hdr, struct mbuf *mb)\n{\n\tuint8_t b;\n\n\tif (!hdr || !mb)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < PCP_HDR_SZ)\n\t\treturn EBADMSG;\n\n\thdr->version = mbuf_read_u8(mb);\n\n\tif (hdr->version != PCP_VERSION) {\n\t\t(void)re_fprintf(stderr, \"pcp: unknown version %u\\n\",\n\t\t\t\t hdr->version);\n\t\treturn EPROTO;\n\t}\n\n\tb            = mbuf_read_u8(mb);\n\thdr->resp    = b>>7;\n\thdr->opcode  = b & 0x7f;\n\n\t(void)mbuf_read_u8(mb);\n\tb = mbuf_read_u8(mb);\n\n\tif (hdr->resp)\n\t\thdr->result = b;\n\n\thdr->lifetime = ntohl(mbuf_read_u32(mb));\n\n\tif (hdr->resp) {\n\t\thdr->epoch = ntohl(mbuf_read_u32(mb));\n\t\tmbuf_advance(mb, 12);\n\t}\n\telse { /* Request */\n\t\t(void)pcp_ipaddr_decode(mb, &hdr->cli_addr);\n\t}\n\n\treturn 0;\n}\n\n\nint pcp_msg_req_vencode(struct mbuf *mb, enum pcp_opcode opcode,\n\t\t\tuint32_t lifetime, const struct sa *cli_addr,\n\t\t\tconst void *payload, uint32_t optionc, va_list ap)\n{\n\tuint32_t i;\n\tint err;\n\n\tif (!mb || !cli_addr)\n\t\treturn EINVAL;\n\n\terr = pcp_header_encode_request(mb, opcode, lifetime, cli_addr);\n\tif (err)\n\t\treturn err;\n\n\tif (payload) {\n\t\terr = pcp_payload_encode(mb, opcode, payload);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\t/* encode options */\n\tfor (i=0; i<optionc; i++) {\n\n\t\tenum pcp_option_code code = va_arg(ap, int);\n\t\tconst void *v = va_arg(ap, const void *);\n\n\t\tif (!v)\n\t\t\tcontinue;\n\n\t\terr |= pcp_option_encode(mb, code, v);\n\t}\n\n\treturn err;\n}\n\n\nint pcp_msg_req_encode(struct mbuf *mb, enum pcp_opcode opcode,\n\t\t       uint32_t lifetime, const struct sa *cli_addr,\n\t\t       const void *payload, uint32_t optionc, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, optionc);\n\terr = pcp_msg_req_vencode(mb, opcode, lifetime, cli_addr,\n\t\t\t\t  payload, optionc, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\nint pcp_msg_decode(struct pcp_msg **msgp, struct mbuf *mb)\n{\n\tstruct pcp_msg *msg;\n\tsize_t len, pos;\n\tint err;\n\n\tif (!msgp || !mb)\n\t\treturn EINVAL;\n\n\tlen = mbuf_get_left(mb);\n\tif (len < PCP_MIN_PACKET || len > PCP_MAX_PACKET || len&3)\n\t\treturn EBADMSG;\n\n\tmsg = mem_zalloc(sizeof(*msg), destructor);\n\tif (!msg)\n\t\treturn ENOMEM;\n\n\tpos = mb->pos;\n\terr = pcp_header_decode(&msg->hdr, mb);\n\tif (err)\n\t\tgoto out;\n\n\tswitch (msg->hdr.opcode) {\n\n\tcase PCP_MAP:\n\t\terr = pcp_map_decode(&msg->pld.map, mb);\n\t\tbreak;\n\n\tcase PCP_PEER:\n\t\terr = pcp_peer_decode(&msg->pld.peer, mb);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\tif (err)\n\t\tgoto out;\n\n\t/* Decode PCP Options */\n\twhile (mbuf_get_left(mb) >= 4) {\n\n\t\tstruct pcp_option *opt;\n\n\t\terr = pcp_option_decode(&opt, mb);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tlist_append(&msg->optionl, &opt->le, opt);\n\t}\n\n out:\n\tif (err) {\n\t\tmb->pos = pos;\n\t\tmem_deref(msg);\n\t}\n\telse\n\t\t*msgp = msg;\n\n\treturn err;\n}\n\n\nstruct pcp_option *pcp_msg_option(const struct pcp_msg *msg,\n\t\t\t\t  enum pcp_option_code code)\n{\n\tstruct le *le = msg ? list_head(&msg->optionl) : NULL;\n\n\twhile (le) {\n\t\tstruct pcp_option *opt = le->data;\n\n\t\tle = le->next;\n\n\t\tif (opt->code == code)\n\t\t\treturn opt;\n\t}\n\n\treturn NULL;\n}\n\n\nstruct pcp_option *pcp_msg_option_apply(const struct pcp_msg *msg,\n\t\t\t\t\tpcp_option_h *h, void *arg)\n{\n\tstruct le *le = msg ? list_head(&msg->optionl) : NULL;\n\n\twhile (le) {\n\t\tstruct pcp_option *opt = le->data;\n\n\t\tle = le->next;\n\n\t\tif (h && h(opt, arg))\n\t\t\treturn opt;\n\t}\n\n\treturn NULL;\n}\n\n\nstatic bool option_print(const struct pcp_option *opt, void *arg)\n{\n\treturn 0 != pcp_option_print(arg, opt);\n}\n\n\nint pcp_msg_printhdr(struct re_printf *pf, const struct pcp_msg *msg)\n{\n\tint err;\n\n\tif (!msg)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"%s %s %usec\",\n\t\t\t msg->hdr.resp ? \"Response\" : \"Request\",\n\t\t\t pcp_opcode_name(msg->hdr.opcode),\n\t\t\t msg->hdr.lifetime);\n\n\tif (msg->hdr.resp) {\n\t\terr |= re_hprintf(pf, \" result=%s, epoch_time=%u sec\",\n\t\t\t\t  pcp_result_name(msg->hdr.result),\n\t\t\t\t  msg->hdr.epoch);\n\t}\n\telse {\n\t\terr |= re_hprintf(pf, \" client_addr=%j\", &msg->hdr.cli_addr);\n\t}\n\n\treturn err;\n}\n\n\nstatic int pcp_map_print(struct re_printf *pf, const struct pcp_map *map)\n{\n\tif (!map)\n\t\treturn 0;\n\n\treturn re_hprintf(pf,\n\t\t\t  \" nonce    = %w\\n protocol = %s\\n\"\n\t\t\t  \" int_port = %u\\n ext_addr = %J\\n\",\n\t\t\t  map->nonce, sizeof(map->nonce),\n\t\t\t  pcp_proto_name(map->proto),\n\t\t\t  map->int_port,\n\t\t\t  &map->ext_addr);\n}\n\n\nint pcp_msg_print(struct re_printf *pf, const struct pcp_msg *msg)\n{\n\tint err;\n\n\tif (!msg)\n\t\treturn 0;\n\n\terr  = pcp_msg_printhdr(pf, msg);\n\terr |= re_hprintf(pf, \"\\n\");\n\n\tswitch (msg->hdr.opcode) {\n\n\tcase PCP_MAP:\n\t\terr |= pcp_map_print(pf, &msg->pld.map);\n\t\tbreak;\n\n\tcase PCP_PEER:\n\t\terr |= pcp_map_print(pf, &msg->pld.peer.map);\n\t\terr |= re_hprintf(pf, \" remote_peer = %J\\n\",\n\t\t\t\t  &msg->pld.peer.remote_addr);\n\t\tbreak;\n\t}\n\n\tif (err)\n\t\treturn err;\n\n\tif (pcp_msg_option_apply(msg, option_print, pf))\n\t\treturn ENOMEM;\n\n\treturn 0;\n}\n\n\n/**\n * Get the payload from a PCP message\n *\n * @param msg PCP message\n *\n * @return either \"struct pcp_map\" or \"struct pcp_peer\"\n */\nconst void *pcp_msg_payload(const struct pcp_msg *msg)\n{\n\tif (!msg)\n\t\treturn NULL;\n\n\tswitch (msg->hdr.opcode) {\n\n\tcase PCP_MAP:  return &msg->pld.map;\n\tcase PCP_PEER: return &msg->pld.peer;\n\tdefault:       return NULL;\n\t}\n}\n"
  },
  {
    "path": "src/pcp/option.c",
    "content": "/**\n * @file option.c  PCP options\n *\n * Copyright (C) 2010 - 2016 Alfred E. Heggestad\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_pcp.h>\n#include \"pcp.h\"\n\n\nstatic void destructor(void *arg)\n{\n\tstruct pcp_option *opt = arg;\n\n\tlist_unlink(&opt->le);\n\n\tswitch (opt->code) {\n\n\tcase PCP_OPTION_DESCRIPTION:\n\t\tmem_deref(opt->u.description);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n}\n\n\nint pcp_option_encode(struct mbuf *mb, enum pcp_option_code code,\n\t\t      const void *v)\n{\n\tconst struct sa *sa = v;\n\tconst struct pcp_option_filter *filt = v;\n\tsize_t start, len;\n\tint err = 0;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\tmb->pos += 4;\n\tstart = mb->pos;\n\n\tswitch (code) {\n\n\tcase PCP_OPTION_THIRD_PARTY:\n\t\tif (!sa)\n\t\t\treturn EINVAL;\n\t\terr |= pcp_ipaddr_encode(mb, sa);\n\t\tbreak;\n\n\tcase PCP_OPTION_PREFER_FAILURE:\n\t\t/* no payload */\n\t\tbreak;\n\n\tcase PCP_OPTION_FILTER:\n\t\tif (!filt)\n\t\t\treturn EINVAL;\n\t\terr |= mbuf_write_u8(mb, 0x00);\n\t\terr |= mbuf_write_u8(mb, filt->prefix_length);\n\t\terr |= mbuf_write_u16(mb, htons(sa_port(&filt->remote_peer)));\n\t\terr |= pcp_ipaddr_encode(mb, &filt->remote_peer);\n\t\tbreak;\n\n\tcase PCP_OPTION_DESCRIPTION:\n\t\tif (!v)\n\t\t\treturn EINVAL;\n\t\terr |= mbuf_write_str(mb, v);\n\t\tbreak;\n\n\tdefault:\n\t\t(void)re_fprintf(stderr,\n\t\t\t\t \"pcp: unsupported option %d\\n\", code);\n\t\treturn EINVAL;\n\t}\n\n\t/* header */\n\tlen = mb->pos - start;\n\n\tmb->pos = start - 4;\n\terr |= mbuf_write_u8(mb, code);\n\terr |= mbuf_write_u8(mb, 0x00);\n\terr |= mbuf_write_u16(mb, htons((uint16_t)len));\n\tmb->pos += len;\n\n\t/* padding */\n\twhile ((mb->pos - start) & 0x03)\n\t\terr |= mbuf_write_u8(mb, 0x00);\n\n\treturn err;\n}\n\n\nint pcp_option_decode(struct pcp_option **optp, struct mbuf *mb)\n{\n\tstruct pcp_option *opt;\n\tsize_t start, len;\n\tuint16_t port;\n\tint err = 0;\n\n\tif (!optp || !mb)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < 4)\n\t\treturn EBADMSG;\n\n\topt = mem_zalloc(sizeof(*opt), destructor);\n\tif (!opt)\n\t\treturn ENOMEM;\n\n\topt->code = mbuf_read_u8(mb);\n\t(void)mbuf_read_u8(mb);\n\tlen = ntohs(mbuf_read_u16(mb));\n\n\tif (mbuf_get_left(mb) < len)\n\t\tgoto badmsg;\n\n\tstart = mb->pos;\n\n\tswitch (opt->code) {\n\n\tcase PCP_OPTION_THIRD_PARTY:\n\t\tif (len < 16)\n\t\t\tgoto badmsg;\n\t\terr = pcp_ipaddr_decode(mb, &opt->u.third_party);\n\t\tbreak;\n\n\tcase PCP_OPTION_PREFER_FAILURE:\n\t\t/* no payload */\n\t\tbreak;\n\n\tcase PCP_OPTION_FILTER:\n\t\tif (len < 20)\n\t\t\tgoto badmsg;\n\t\t(void)mbuf_read_u8(mb);\n\t\topt->u.filter.prefix_length = mbuf_read_u8(mb);\n\t\tport = ntohs(mbuf_read_u16(mb));\n\t\terr = pcp_ipaddr_decode(mb, &opt->u.filter.remote_peer);\n\t\tsa_set_port(&opt->u.filter.remote_peer, port);\n\t\tbreak;\n\n\tcase PCP_OPTION_DESCRIPTION:\n\t\terr = mbuf_strdup(mb, &opt->u.description, len);\n\t\tbreak;\n\n\tdefault:\n\t\tmb->pos += len;\n\n\t\t(void)re_printf(\"pcp: ignore option code %d (len=%zu)\\n\",\n\t\t\t\topt->code, len);\n\t\tbreak;\n\t}\n\n\tif (err)\n\t\tgoto error;\n\n\t/* padding */\n\twhile (((mb->pos - start) & 0x03) && mbuf_get_left(mb))\n\t\t++mb->pos;\n\n\t*optp = opt;\n\n\treturn 0;\n\n badmsg:\n\terr = EBADMSG;\n error:\n\tmem_deref(opt);\n\n\treturn err;\n}\n\n\nstatic const char *pcp_option_name(enum pcp_option_code code)\n{\n\tswitch (code) {\n\n\tcase PCP_OPTION_THIRD_PARTY:    return \"THIRD_PARTY\";\n\tcase PCP_OPTION_PREFER_FAILURE: return \"PREFER_FAILURE\";\n\tcase PCP_OPTION_FILTER:         return \"FILTER\";\n\tcase PCP_OPTION_DESCRIPTION:    return \"DESCRIPTION\";\n\tdefault: return \"?\";\n\t}\n}\n\n\nint pcp_option_print(struct re_printf *pf, const struct pcp_option *opt)\n{\n\tint err;\n\n\tif (!opt)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \" %-25s\", pcp_option_name(opt->code));\n\n\tswitch (opt->code) {\n\n\tcase PCP_OPTION_THIRD_PARTY:\n\t\terr |= re_hprintf(pf, \"address=%j\",\n\t\t\t\t  &opt->u.third_party);\n\t\tbreak;\n\n\tcase PCP_OPTION_PREFER_FAILURE:\n\t\tbreak;\n\n\tcase PCP_OPTION_FILTER:\n\t\terr |= re_hprintf(pf, \"prefix_length=%u, remote_peer=%J\",\n\t\t\t\t  opt->u.filter.prefix_length,\n\t\t\t\t  &opt->u.filter.remote_peer);\n\t\tbreak;\n\n\tcase PCP_OPTION_DESCRIPTION:\n\t\terr |= re_hprintf(pf, \"'%s'\", opt->u.description);\n\t\tbreak;\n\n\tdefault:\n\t\terr |= re_hprintf(pf, \"???\");\n\t\tbreak;\n\t}\n\n\terr |= re_hprintf(pf, \"\\n\");\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/pcp/payload.c",
    "content": "/**\n * @file payload.c  PCP payload encoding and decoding\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_pcp.h>\n#include \"pcp.h\"\n\n\nstatic int pcp_write_port(struct mbuf *mb, const struct sa *sa)\n{\n\tuint16_t port_be;\n\n\tif (!mb || !sa)\n\t\treturn EINVAL;\n\n\tswitch (sa->u.sa.sa_family) {\n\n\tcase AF_INET:\n\t\tport_be = sa->u.in.sin_port;\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tport_be = sa->u.in6.sin6_port;\n\t\tbreak;\n\n\tdefault:\n\t\treturn EAFNOSUPPORT;\n\t}\n\n\treturn mbuf_write_u16(mb, port_be);\n}\n\n\nstatic int pcp_map_encode(struct mbuf *mb, const struct pcp_map *map)\n{\n\tint err = 0;\n\n\tif (!mb || !map)\n\t\treturn EINVAL;\n\n\terr |= mbuf_write_mem(mb, map->nonce, sizeof(map->nonce));\n\terr |= mbuf_write_u8(mb, map->proto);\n\terr |= mbuf_fill(mb, 0x00, 3);\n\terr |= mbuf_write_u16(mb, htons(map->int_port));\n\terr |= pcp_write_port(mb, &map->ext_addr);\n\terr |= pcp_ipaddr_encode(mb, &map->ext_addr);\n\n\treturn err;\n}\n\n\nstatic int pcp_peer_encode(struct mbuf *mb, const struct pcp_peer *peer)\n{\n\tint err;\n\n\tif (!mb || !peer)\n\t\treturn EINVAL;\n\n\t/* Protocol MUST NOT be zero.\n\t * Internal port MUST NOT be zero.\n\t */\n\tif (!peer->map.proto || !peer->map.int_port)\n\t\treturn EPROTO;\n\n\t/* note: the MAP and PEER opcodes are quite similar */\n\terr = pcp_map_encode(mb, &peer->map);\n\tif (err)\n\t\treturn err;\n\n\terr  = pcp_write_port(mb, &peer->remote_addr);\n\terr |= mbuf_write_u16(mb, 0x0000);\n\terr |= pcp_ipaddr_encode(mb, &peer->remote_addr);\n\n\treturn err;\n}\n\n\nint pcp_payload_encode(struct mbuf *mb, enum pcp_opcode opcode,\n\t\t       const union pcp_payload *pld)\n{\n\tint err;\n\n\tif (!mb || !pld)\n\t\treturn EINVAL;\n\n\tswitch (opcode) {\n\n\tcase PCP_MAP:\n\t\terr = pcp_map_encode(mb, &pld->map);\n\t\tbreak;\n\n\tcase PCP_PEER:\n\t\terr = pcp_peer_encode(mb, &pld->peer);\n\t\tbreak;\n\n\tdefault:\n\t\tre_fprintf(stderr, \"pcp: dont know how to encode payload\"\n\t\t\t   \" for opcode %d\\n\", opcode);\n\t\terr = EPROTO;\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/pcp/pcp.c",
    "content": "/**\n * @file pcp/pcp.c  PCP protocol details\n *\n * Copyright (C) 2010 - 2016 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_pcp.h>\n#include \"pcp.h\"\n\n\nstatic const uint8_t pattern[12] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff};\n\n\nint pcp_ipaddr_encode(struct mbuf *mb, const struct sa *sa)\n{\n\tint err = 0;\n\n\tif (!mb || !sa)\n\t\treturn EINVAL;\n\n\tswitch (sa_af(sa)) {\n\n\tcase AF_INET:\n\t\terr |= mbuf_write_mem(mb, pattern, sizeof(pattern));\n\t\terr |= mbuf_write_mem(mb, (void *)&sa->u.in.sin_addr.s_addr,\n\t\t\t\t      4);\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\terr |= mbuf_write_mem(mb, sa->u.in6.sin6_addr.s6_addr, 16);\n\t\tbreak;\n\n\tdefault:\n\t\terr = EAFNOSUPPORT;\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nint pcp_ipaddr_decode(struct mbuf *mb, struct sa *sa)\n{\n\tuint8_t *p;\n\n\tif (!mb || !sa)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < 16)\n\t\treturn EBADMSG;\n\n\tp = mbuf_buf(mb);\n\n\tif (0 == memcmp(p, pattern, sizeof(pattern))) {\n\n\t\tsa_init(sa, AF_INET);\n\t\tmemcpy(&sa->u.in.sin_addr, p + 12, 4);\n\t}\n\telse {\n\t\tsa_init(sa, AF_INET6);\n\t\tmemcpy(sa->u.in6.sin6_addr.s6_addr, p, 16);\n\t}\n\n\tmb->pos += 16;\n\n\treturn 0;\n}\n\n\nconst char *pcp_result_name(enum pcp_result result)\n{\n\tswitch (result) {\n\n\tcase PCP_SUCCESS:                 return \"SUCCESS\";\n\tcase PCP_UNSUPP_VERSION:          return \"UNSUPP_VERSION\";\n\tcase PCP_NOT_AUTHORIZED:          return \"NOT_AUTHORIZED\";\n\tcase PCP_MALFORMED_REQUEST:       return \"MALFORMED_REQUEST\";\n\tcase PCP_UNSUPP_OPCODE:           return \"UNSUPP_OPCODE\";\n\tcase PCP_UNSUPP_OPTION:           return \"UNSUPP_OPTION\";\n\tcase PCP_MALFORMED_OPTION:        return \"MALFORMED_OPTION\";\n\tcase PCP_NETWORK_FAILURE:         return \"NETWORK_FAILURE\";\n\tcase PCP_NO_RESOURCES:            return \"NO_RESOURCES\";\n\tcase PCP_UNSUPP_PROTOCOL:         return \"UNSUPP_PROTOCOL\";\n\tcase PCP_USER_EX_QUOTA:           return \"USER_EX_QUOTA\";\n\tcase PCP_CANNOT_PROVIDE_EXTERNAL: return \"CANNOT_PROVIDE_EXTERNAL\";\n\tcase PCP_ADDRESS_MISMATCH:        return \"ADDRESS_MISMATCH\";\n\tcase PCP_EXCESSIVE_REMOTE_PEERS:  return \"EXCESSIVE_REMOTE_PEERS\";\n\tdefault: return \"?\";\n\t}\n}\n\n\nconst char *pcp_opcode_name(enum pcp_opcode opcode)\n{\n\tswitch (opcode) {\n\n\tcase PCP_ANNOUNCE: return \"ANNOUNCE\";\n\tcase PCP_MAP:      return \"MAP\";\n\tcase PCP_PEER:     return \"PEER\";\n\tdefault:           return \"?\";\n\t}\n}\n\n\nconst char *pcp_proto_name(int proto)\n{\n\tswitch (proto) {\n\n\tcase IPPROTO_UDP: return \"UDP\";\n\tcase IPPROTO_TCP: return \"TCP\";\n\tdefault: return \"?\";\n\t}\n}\n"
  },
  {
    "path": "src/pcp/pcp.h",
    "content": "/**\n * @file pcp/pcp.h  PCP protocol -- Internal interface\n *\n * Copyright (C) 2010 - 2016 Alfred E. Heggestad\n */\n\n\nint pcp_payload_encode(struct mbuf *mb, enum pcp_opcode opcode,\n\t\t       const union pcp_payload *pld);\n"
  },
  {
    "path": "src/pcp/reply.c",
    "content": "/**\n * @file pcp/reply.c  PCP reply\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_udp.h>\n#include <re_pcp.h>\n#include \"pcp.h\"\n\n\nstatic int pcp_header_encode_response(struct mbuf *mb, enum pcp_opcode opcode,\n\t\t\t\t      enum pcp_result result,\n\t\t\t\t      uint32_t lifetime, uint32_t epoch_time)\n{\n\tint err = 0;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\terr |= mbuf_write_u8(mb, PCP_VERSION);\n\terr |= mbuf_write_u8(mb, 1<<7 | opcode);\n\terr |= mbuf_write_u8(mb, 0x00);\n\terr |= mbuf_write_u8(mb, result);\n\terr |= mbuf_write_u32(mb, htonl(lifetime));\n\terr |= mbuf_write_u32(mb, htonl(epoch_time));\n\terr |= mbuf_fill(mb, 0x00, 12);\n\n\treturn err;\n}\n\n\n/**\n * Send a PCP response message\n *\n * @param us         UDP Socket\n * @param dst        Destination network address\n * @param req        Buffer containing original PCP request (optional)\n * @param opcode     PCP opcode\n * @param result     PCP result for the response\n * @param lifetime   Lifetime in [seconds]\n * @param epoch_time Server Epoch-time\n * @param payload    PCP payload, e.g. struct pcp_map (optional)\n *\n * @return 0 if success, otherwise errorcode\n */\nint pcp_reply(struct udp_sock *us, const struct sa *dst, struct mbuf *req,\n\t      enum pcp_opcode opcode, enum pcp_result result,\n\t      uint32_t lifetime, uint32_t epoch_time, const void *payload)\n{\n\tstruct mbuf *mb;\n\tsize_t start;\n\tint err;\n\n\tif (!us || !dst)\n\t\treturn EINVAL;\n\n\tif (req) {\n\t\t/* the complete Request must be included in the Response */\n\t\tmb = mem_ref(req);\n\t}\n\telse {\n\t\tmb = mbuf_alloc(128);\n\t\tif (!mb)\n\t\t\treturn ENOMEM;\n\t}\n\n\tstart = mb->pos;\n\n\t/* encode the response packet */\n\terr = pcp_header_encode_response(mb, opcode, result,\n\t\t\t\t\t lifetime, epoch_time);\n\tif (err)\n\t\tgoto out;\n\n\tif (payload) {\n\t\terr = pcp_payload_encode(mb, opcode, payload);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\tmb->pos = start;\n\terr = udp_send(us, dst, mb);\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n"
  },
  {
    "path": "src/pcp/request.c",
    "content": "/**\n * @file pcp/request.c  PCP request\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sys.h>\n#include <re_sa.h>\n#include <re_tmr.h>\n#include <re_udp.h>\n#include <re_pcp.h>\n#include \"pcp.h\"\n\n\n/*\n * Defines a PCP client request\n *\n * the application must keep a reference to this object and the\n * object must be deleted by the application. the response handler\n * might be called multiple times.\n */\nstruct pcp_request {\n\tstruct pcp_conf conf;\n\tstruct sa srv;\n\tstruct udp_sock *us;\n\tstruct mbuf *mb;\n\tstruct tmr tmr;\n\tstruct tmr tmr_dur;\n\tstruct tmr tmr_refresh;\n\tenum pcp_opcode opcode;\n\tunion pcp_payload payload;\n\tuint32_t lifetime;\n\tbool granted;\n\tunsigned txc;\n\tdouble RT;\n\tpcp_resp_h *resph;\n\tvoid *arg;\n};\n\n\n/*\n * RT:   Retransmission timeout\n * IRT:  Initial retransmission time, SHOULD be 3 seconds\n * MRC:  Maximum retransmission count, SHOULD be 0 (no maximum)\n * MRT:  Maximum retransmission time, SHOULD be 1024 seconds\n * MRD:  Maximum retransmission duration, SHOULD be 0 (no maximum)\n * RAND: Randomization factor\n */\nstatic const struct pcp_conf default_conf = {\n\t3,\n\t0,\n\t1024,\n\t0\n};\n\n\nstatic int start_sending(struct pcp_request *req);\n\n\n/* random number between -0.1 and +0.1 */\nstatic inline double RAND(void)\n{\n\treturn (1.0 * rand_u16() / 32768 - 1.0) / 10.0;\n}\n\nstatic double RT_init(const struct pcp_conf *conf)\n{\n\treturn (1.0 + RAND()) * conf->irt;\n}\n\nstatic double RT_next(const struct pcp_conf *conf, double RTprev)\n{\n\treturn (1.0 + RAND()) * min (2 * RTprev, conf->mrt);\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct pcp_request *req = arg;\n\n\t/* Destroy the mapping if it was granted */\n\tif (req->granted && req->lifetime && req->mb) {\n\n\t\t/* set the lifetime to zero */\n\t\treq->mb->pos = 4;\n\t\tmbuf_write_u32(req->mb, 0);\n\n\t\treq->mb->pos = 0;\n\t\t(void)udp_send(req->us, &req->srv, req->mb);\n\t}\n\n\ttmr_cancel(&req->tmr);\n\ttmr_cancel(&req->tmr_dur);\n\ttmr_cancel(&req->tmr_refresh);\n\tmem_deref(req->us);\n\tmem_deref(req->mb);\n}\n\n\nstatic void completed(struct pcp_request *req, int err, struct pcp_msg *msg)\n{\n\tpcp_resp_h *resph = req->resph;\n\tvoid *arg = req->arg;\n\n\ttmr_cancel(&req->tmr);\n\ttmr_cancel(&req->tmr_dur);\n\n\t/* if the request failed, we only called the\n\t   response handler once and never again */\n\tif (err || !msg || msg->hdr.result != PCP_SUCCESS ) {\n\t\treq->resph = NULL;\n\t}\n\n\tif (resph)\n\t\tresph(err, msg, arg);\n}\n\n\nstatic void refresh_timeout(void *arg)\n{\n\tstruct pcp_request *req = arg;\n\n\t/* todo: update request with new EXT-ADDR from server */\n\t(void)start_sending(req);\n}\n\n\nstatic void timeout(void *arg)\n{\n\tstruct pcp_request *req = arg;\n\tint err;\n\n\treq->txc++;\n\n\tif (req->conf.mrc > 0 && req->txc > req->conf.mrc) {\n\t\tcompleted(req, ETIMEDOUT, NULL);\n\t\treturn;\n\t}\n\n\treq->mb->pos = 0;\n\terr = udp_send(req->us, &req->srv, req->mb);\n\tif (err) {\n\t\tcompleted(req, err, NULL);\n\t\treturn;\n\t}\n\n\treq->RT = RT_next(&req->conf, req->RT);\n\ttmr_start(&req->tmr, (uint64_t)req->RT * 1000, timeout, req);\n}\n\n\nstatic void timeout_duration(void *arg)\n{\n\tstruct pcp_request *req = arg;\n\n\tcompleted(req, ETIMEDOUT, NULL);\n}\n\n\nstatic void udp_recv(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct pcp_request *req = arg;\n\tstruct pcp_msg *msg;\n\tint err;\n\n\tif (!sa_cmp(src, &req->srv, SA_ALL))\n\t\treturn;\n\n\terr = pcp_msg_decode(&msg, mb);\n\tif (err)\n\t\treturn;\n\n\tif (!msg->hdr.resp) {\n\t\t(void)re_fprintf(stderr, \"pcp: ignoring PCP request\\n\");\n\t\tgoto out;\n\t}\n\n\tif (msg->hdr.opcode != req->opcode)\n\t\tgoto out;\n\n\t/* compare opcode-specific data */\n\n\tswitch (msg->hdr.opcode) {\n\n\tcase PCP_MAP:\n\tcase PCP_PEER:\n\t\tif (0 != memcmp(msg->pld.map.nonce, req->payload.map.nonce,\n\t\t\t\tPCP_NONCE_SZ)) {\n\t\t\t(void)re_fprintf(stderr, \"ignoring unknown nonce\\n\");\n\t\t\tgoto out;\n\t\t}\n\t\treq->payload.map.ext_addr = msg->pld.map.ext_addr;\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\treq->lifetime = msg->hdr.lifetime;\n\treq->granted = (msg->hdr.result == PCP_SUCCESS);\n\n\t/* todo:\n\t *\n\t * Once a PCP client has successfully received a response from a PCP\n\t * server on that interface, it resets RT to a value randomly selected\n\t * in the range 1/2 to 5/8 of the mapping lifetime, as described in\n\t * Section 11.2.1, \"Renewing a Mapping\", and sends subsequent PCP\n\t * requests for that mapping to that same server.\n\t */\n\tif (req->granted && req->lifetime) {\n\n\t\tuint32_t v = req->lifetime * 3/4;\n\n\t\ttmr_start(&req->tmr_refresh, v * 1000, refresh_timeout, req);\n\t}\n\n\tcompleted(req, 0, msg);\n\n out:\n\tmem_deref(msg);\n}\n\n\nstatic int start_sending(struct pcp_request *req)\n{\n\tint err;\n\n\treq->txc = 1;\n\n\treq->mb->pos = 0;\n\terr = udp_send(req->us, &req->srv, req->mb);\n\tif (err)\n\t\treturn err;\n\n\treq->RT = RT_init(&req->conf);\n\ttmr_start(&req->tmr, (uint64_t)req->RT * 1000, timeout, req);\n\n\tif (req->conf.mrd) {\n\t\ttmr_start(&req->tmr_dur, req->conf.mrd * 1000,\n\t\t\t  timeout_duration, req);\n\t}\n\n\treturn 0;\n}\n\n\nstatic int pcp_vrequest(struct pcp_request **reqp, const struct pcp_conf *conf,\n\t\t\tconst struct sa *srv, enum pcp_opcode opcode,\n\t\t\tuint32_t lifetime, const void *payload,\n\t\t\tpcp_resp_h *resph, void *arg,\n\t\t\tuint32_t optionc, va_list ap)\n{\n\tconst union pcp_payload *up = payload;\n\tstruct pcp_request *req;\n\tstruct sa laddr;\n\tint err;\n\n\tif (!reqp || !srv)\n\t\treturn EINVAL;\n\n\tsa_init(&laddr, sa_af(srv));\n\n\treq = mem_zalloc(sizeof(*req), destructor);\n\tif (!req)\n\t\treturn ENOMEM;\n\n\treq->conf   = conf ? *conf : default_conf;\n\treq->opcode = opcode;\n\treq->srv    = *srv;\n\treq->resph  = resph;\n\treq->arg    = arg;\n\n\treq->lifetime = lifetime;\n\n\tif (up)\n\t\treq->payload = *up;\n\n\terr = udp_listen(&req->us, &laddr, udp_recv, req);\n\tif (err)\n\t\tgoto out;\n\n\t/*\n\t * see RFC 6887 section 16.4\n\t */\n\terr = udp_connect(req->us, srv);\n\tif (err)\n\t\tgoto out;\n\terr = udp_local_get(req->us, &laddr);\n\tif (err)\n\t\tgoto out;\n\n\treq->mb = mbuf_alloc(128);\n\tif (!req->mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = pcp_msg_req_vencode(req->mb, opcode, lifetime,\n\t\t\t\t  &laddr, up, optionc, ap);\n\tif (err)\n\t\tgoto out;\n\n\terr = start_sending(req);\n\n out:\n\tif (err)\n\t\tmem_deref(req);\n\telse\n\t\t*reqp = req;\n\n\treturn err;\n}\n\n\nint pcp_request(struct pcp_request **reqp, const struct pcp_conf *conf,\n\t\tconst struct sa *srv, enum pcp_opcode opcode,\n\t\tuint32_t lifetime, const void *payload,\n\t\tpcp_resp_h *resph, void *arg, uint32_t optionc, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, optionc);\n\terr = pcp_vrequest(reqp, conf, srv, opcode, lifetime, payload,\n\t\t\t   resph, arg, optionc, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\nvoid pcp_force_refresh(struct pcp_request *req)\n{\n\tif (!req)\n\t\treturn;\n\n\ttmr_cancel(&req->tmr);\n\ttmr_cancel(&req->tmr_dur);\n\n\ttmr_start(&req->tmr_refresh, rand_u16() % 2000, refresh_timeout, req);\n}\n"
  },
  {
    "path": "src/rtmp/README.md",
    "content": "RTMP module\n-----------\n\nThis module implements Real Time Messaging Protocol (RTMP) [1].\n\n\n\n\nFunctional overview:\n-------------------\n\n```\nRTMP Specification v1.0 .......... YES\nRTMP with TCP transport .......... YES\n\nRTMPS (RTMP over TLS) ............ NO\nRTMPE (RTMP over Adobe Encryption) NO\nRTMPT (RTMP over HTTP) ........... NO\nRTMFP (RTMP over UDP) ............ NO\n\nTransport:\nClient ........................... YES\nServer ........................... YES\nIPv4 ............................. YES\nIPv6 ............................. YES\nDNS Resolving A/AAAA ............. YES\n\nRTMP Components:\nRTMP Handshake ................... YES\nRTMP Header encoding and decoding. YES\nRTMP Chunking .................... YES\nRTMP Dechunking .................. YES\nAMF0 (Action Message Format) ..... YES\nAMF3 (Action Message Format) ..... NO\nSend and receive audio/video ..... YES\nRegular and extended timestamp ... YES\nMultiple streams ................. YES\n```\n\n\n\n\nTODO:\n----\n\n- [x] improve AMF encoding API\n- [x] implement AMF transaction matching\n- [x] add support for Data Message\n- [x] add support for AMF Strict Array (type 10)\n- [ ] add support for TLS encryption\n- [x] add support for extended timestamp\n\n\n\n\nProtocol stack:\n--------------\n\n    .-------.  .-------.  .-------.\n    |  AMF  |  | Audio |  | Video |\n    '-------'  '-------'  '-------'\n        |          |          |\n        +----------+----------'\n                   |\n               .-------.\n               |  RTMP |\n               '-------'\n                   |\n                   |\n               .-------.\n               |  TCP  |\n               '-------'\n\n\n\n\nMessage Sequence:\n----------------\n\n\n```\nClient                                      Server\n\n|----------------- TCP Connect -------------->|\n|                                             |\n|                                             |\n|                                             |\n|<-------------- 3-way Handshake ------------>|\n|                                             |\n|                                             |\n|                                             |\n|----------- Command Message(connect) ------->| chunkid=3, streamid=0, tid=1\n|                                             |\n|<------- Window Acknowledgement Size --------| chunkid=2, streamid=0\n|                                             |\n|<----------- Set Peer Bandwidth -------------| chunkid=2, streamid=0\n|                                             |\n|-------- Window Acknowledgement Size ------->|\n|                                             |\n|<------ User Control Message(StreamBegin) ---| chunkid=2, streamid=0\n|                                             |\n|<------------ Command Message ---------------| chunkid=3, streamid=0, tid=1\n|        (_result- connect response)          |\n```\n\n\nInterop:\n-------\n\n- Wowza Streaming Engine 4.7.1\n- Youtube service\n- FFmpeg's RTMP module\n\n\n\n\nReferences:\n----------\n\n[1] http://wwwimages.adobe.com/www.adobe.com/content/dam/acom/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf\n\n[2] https://wwwimages2.adobe.com/content/dam/acom/en/devnet/flv/video_file_format_spec_v10_1.pdf\n\n[3] https://en.wikipedia.org/wiki/Action_Message_Format\n\n[4] https://wwwimages2.adobe.com/content/dam/acom/en/devnet/pdf/amf0-file-format-specification.pdf\n"
  },
  {
    "path": "src/rtmp/amf.c",
    "content": "/**\n * @file rtmp/amf.c  Real Time Messaging Protocol (RTMP) -- AMF Commands\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_tcp.h>\n#include <re_sys.h>\n#include <re_odict.h>\n#include <re_rtmp.h>\n#include \"rtmp.h\"\n\n\nint rtmp_command_header_encode(struct mbuf *mb, const char *name, uint64_t tid)\n{\n\tint err;\n\n\tif (!mb || !name)\n\t\treturn EINVAL;\n\n\terr  = rtmp_amf_encode_string(mb, name);\n\terr |= rtmp_amf_encode_number(mb, (double)tid);\n\n\treturn err;\n}\n\n\nint rtmp_amf_command(const struct rtmp_conn *conn, uint32_t stream_id,\n\t\t     const char *command, unsigned body_propc, ...)\n{\n\tstruct mbuf *mb;\n\tva_list ap;\n\tint err;\n\n\tif (!conn || !command)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = rtmp_amf_encode_string(mb, command);\n\tif (err)\n\t\tgoto out;\n\n\tif (body_propc) {\n\t\tva_start(ap, body_propc);\n\t\terr = rtmp_amf_vencode_object(mb, RTMP_AMF_TYPE_ROOT,\n\t\t\t\t\t      body_propc, &ap);\n\t\tva_end(ap);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = rtmp_send_amf_command(conn, 0, RTMP_CHUNK_ID_CONN,\n\t\t\t\t    RTMP_TYPE_AMF0,\n\t\t\t\t    stream_id, mb->buf, mb->end);\n\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nint rtmp_amf_reply(struct rtmp_conn *conn, uint32_t stream_id, bool success,\n\t\t   const struct odict *req,\n\t\t   unsigned body_propc, ...)\n{\n\tstruct mbuf *mb;\n\tva_list ap;\n\tuint64_t tid;\n\tint err;\n\n\tif (!conn || !req)\n\t\treturn EINVAL;\n\n\tif (!odict_get_number(req, &tid, \"1\"))\n\t\treturn EPROTO;\n\tif (tid == 0)\n\t\treturn EPROTO;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = rtmp_command_header_encode(mb,\n\t\t\t\t\t success ? \"_result\" : \"_error\", tid);\n\tif (err)\n\t\tgoto out;\n\n\tif (body_propc) {\n\t\tva_start(ap, body_propc);\n\t\terr = rtmp_amf_vencode_object(mb, RTMP_AMF_TYPE_ROOT,\n\t\t\t\t\t      body_propc, &ap);\n\t\tva_end(ap);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = rtmp_send_amf_command(conn, 0, RTMP_CHUNK_ID_CONN,\n\t\t\t\t    RTMP_TYPE_AMF0,\n\t\t\t\t    stream_id, mb->buf, mb->end);\n\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nint rtmp_amf_data(const struct rtmp_conn *conn, uint32_t stream_id,\n\t\t  const char *command, unsigned body_propc, ...)\n{\n\tstruct mbuf *mb;\n\tva_list ap;\n\tint err;\n\n\tif (!conn || !command)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = rtmp_amf_encode_string(mb, command);\n\tif (err)\n\t\tgoto out;\n\n\tif (body_propc) {\n\t\tva_start(ap, body_propc);\n\t\terr = rtmp_amf_vencode_object(mb, RTMP_AMF_TYPE_ROOT,\n\t\t\t\t\t      body_propc, &ap);\n\t\tva_end(ap);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = rtmp_send_amf_command(conn, 0, RTMP_CHUNK_ID_CONN,\n\t\t\t\t    RTMP_TYPE_DATA,\n\t\t\t\t    stream_id, mb->buf, mb->end);\n\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/rtmp/amf_dec.c",
    "content": "/**\n * @file rtmp/amf_dec.c  Real Time Messaging Protocol (RTMP) -- AMF Decoding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_sys.h>\n#include <re_odict.h>\n#include <re_rtmp.h>\n#include \"rtmp.h\"\n\n\nenum {\n\tAMF_HASH_SIZE = 32\n};\n\n\nstatic int amf_decode_value(struct odict *dict, const char *key,\n\t\t\t    struct mbuf *mb);\n\n\nstatic int amf_decode_object(struct odict *dict, struct mbuf *mb)\n{\n\tchar *key = NULL;\n\tuint16_t len;\n\tint err = 0;\n\n\twhile (mbuf_get_left(mb) > 0) {\n\n\t\tif (mbuf_get_left(mb) < 2)\n\t\t\treturn ENODATA;\n\n\t\tlen = ntohs(mbuf_read_u16(mb));\n\n\t\tif (len == 0) {\n\t\t\tuint8_t val;\n\n\t\t\tif (mbuf_get_left(mb) < 1)\n\t\t\t\treturn ENODATA;\n\n\t\t\tval = mbuf_read_u8(mb);\n\n\t\t\tif (val == RTMP_AMF_TYPE_OBJECT_END)\n\t\t\t\treturn 0;\n\t\t\telse\n\t\t\t\treturn EBADMSG;\n\t\t}\n\n\t\tif (mbuf_get_left(mb) < len)\n\t\t\treturn ENODATA;\n\n\t\terr = mbuf_strdup(mb, &key, len);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = amf_decode_value(dict, key, mb);\n\n\t\tkey = mem_deref(key);\n\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\nstatic int amf_decode_value(struct odict *dict, const char *key,\n\t\t\t    struct mbuf *mb)\n{\n\tunion {\n\t\tuint64_t i;\n\t\tdouble f;\n\t} num;\n\tstruct odict *object = NULL;\n\tchar *str = NULL;\n\tuint32_t i, array_len;\n\tuint8_t type;\n\tuint16_t len;\n\tbool boolean;\n\tint err = 0;\n\n\tif (mbuf_get_left(mb) < 1)\n\t\treturn ENODATA;\n\n\ttype = mbuf_read_u8(mb);\n\n\tswitch (type) {\n\n\tcase RTMP_AMF_TYPE_NUMBER:\n\t\tif (mbuf_get_left(mb) < 8)\n\t\t\treturn ENODATA;\n\n\t\tnum.i = sys_ntohll(mbuf_read_u64(mb));\n\n\t\terr = odict_entry_add(dict, key, ODICT_DOUBLE, num.f);\n\t\tbreak;\n\n\tcase RTMP_AMF_TYPE_BOOLEAN:\n\t\tif (mbuf_get_left(mb) < 1)\n\t\t\treturn ENODATA;\n\n\t\tboolean = !!mbuf_read_u8(mb);\n\n\t\terr = odict_entry_add(dict, key, ODICT_BOOL, boolean);\n\t\tbreak;\n\n\tcase RTMP_AMF_TYPE_STRING:\n\t\tif (mbuf_get_left(mb) < 2)\n\t\t\treturn ENODATA;\n\n\t\tlen = ntohs(mbuf_read_u16(mb));\n\n\t\tif (mbuf_get_left(mb) < len)\n\t\t\treturn ENODATA;\n\n\t\terr = mbuf_strdup(mb, &str, len);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = odict_entry_add(dict, key, ODICT_STRING, str);\n\n\t\tmem_deref(str);\n\t\tbreak;\n\n\tcase RTMP_AMF_TYPE_NULL:\n\t\terr = odict_entry_add(dict, key, ODICT_NULL);\n\t\tbreak;\n\n\tcase RTMP_AMF_TYPE_ECMA_ARRAY:\n\t\tif (mbuf_get_left(mb) < 4)\n\t\t\treturn ENODATA;\n\n\t\tarray_len = ntohl(mbuf_read_u32(mb));\n\n\t\t(void)array_len;  /* ignore array length */\n\n\t\t/* fallthrough */\n\n\tcase RTMP_AMF_TYPE_OBJECT:\n\t\terr = odict_alloc(&object, 32);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = amf_decode_object(object, mb);\n\t\tif (err) {\n\t\t\tmem_deref(object);\n\t\t\treturn err;\n\t\t}\n\n\t\terr = odict_entry_add(dict, key, ODICT_OBJECT, object);\n\n\t\tmem_deref(object);\n\t\tbreak;\n\n\tcase RTMP_AMF_TYPE_STRICT_ARRAY:\n\t\tif (mbuf_get_left(mb) < 4)\n\t\t\treturn ENODATA;\n\n\t\tarray_len = ntohl(mbuf_read_u32(mb));\n\t\tif (!array_len || array_len > 65536)\n\t\t\treturn EPROTO;\n\n\t\terr = odict_alloc(&object, 32);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tfor (i=0; i<array_len; i++) {\n\n\t\t\tchar ix[32];\n\n\t\t\tre_snprintf(ix, sizeof(ix), \"%u\", i);\n\n\t\t\terr = amf_decode_value(object, ix, mb);\n\t\t\tif (err) {\n\t\t\t\tmem_deref(object);\n\t\t\t\treturn err;\n\t\t\t}\n\t\t}\n\n\t\terr = odict_entry_add(dict, key, ODICT_ARRAY, object);\n\n\t\tmem_deref(object);\n\t\tbreak;\n\n\tdefault:\n\t\terr = EPROTO;\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nint rtmp_amf_decode(struct odict **msgp, struct mbuf *mb)\n{\n\tstruct odict *msg;\n\tunsigned ix = 0;\n\tint err;\n\n\tif (!msgp || !mb)\n\t\treturn EINVAL;\n\n\terr = odict_alloc(&msg, AMF_HASH_SIZE);\n\tif (err)\n\t\treturn err;\n\n\t/* decode all entries on root-level */\n\twhile (mbuf_get_left(mb) > 0) {\n\n\t\tchar key[16];\n\n\t\tre_snprintf(key, sizeof(key), \"%u\", ix++);\n\n\t\t/* note: key is the numerical index */\n\t\terr = amf_decode_value(msg, key, mb);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(msg);\n\telse\n\t\t*msgp = msg;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/rtmp/amf_enc.c",
    "content": "/**\n * @file rtmp/amf_enc.c  Real Time Messaging Protocol (RTMP) -- AMF Encoding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_sys.h>\n#include <re_odict.h>\n#include <re_rtmp.h>\n#include \"rtmp.h\"\n\n\nstatic int rtmp_amf_encode_key(struct mbuf *mb, const char *key)\n{\n\tsize_t len;\n\tint err;\n\n\tlen = str_len(key);\n\n\tif (len > 65535)\n\t\treturn EOVERFLOW;\n\n\terr  = mbuf_write_u16(mb, htons((uint16_t)len));\n\terr |= mbuf_write_str(mb, key);\n\n\treturn err;\n}\n\n\nstatic int rtmp_amf_encode_object_start(struct mbuf *mb)\n{\n\treturn mbuf_write_u8(mb, RTMP_AMF_TYPE_OBJECT);\n}\n\n\nstatic int rtmp_amf_encode_array_start(struct mbuf *mb,\n\t\t\t\t       uint8_t type, uint32_t length)\n{\n\tint err;\n\n\terr  = mbuf_write_u8(mb, type);\n\terr |= mbuf_write_u32(mb, htonl(length));\n\n\treturn err;\n}\n\n\nstatic int rtmp_amf_encode_object_end(struct mbuf *mb)\n{\n\tint err;\n\n\terr  = mbuf_write_u16(mb, 0);\n\terr |= mbuf_write_u8(mb, RTMP_AMF_TYPE_OBJECT_END);\n\n\treturn err;\n}\n\n\nstatic bool container_has_key(enum rtmp_amf_type type)\n{\n\tswitch (type) {\n\n\tcase RTMP_AMF_TYPE_OBJECT:       return true;\n\tcase RTMP_AMF_TYPE_ECMA_ARRAY:   return true;\n\tcase RTMP_AMF_TYPE_STRICT_ARRAY: return false;\n\tdefault:                         return false;\n\t}\n}\n\n\nint rtmp_amf_encode_number(struct mbuf *mb, double val)\n{\n\tconst union {\n\t\tuint64_t i;\n\t\tdouble f;\n\t} num = {\n\t\t.f = val\n\t};\n\tint err;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\terr  = mbuf_write_u8(mb, RTMP_AMF_TYPE_NUMBER);\n\terr |= mbuf_write_u64(mb, sys_htonll(num.i));\n\n\treturn err;\n}\n\n\nint rtmp_amf_encode_boolean(struct mbuf *mb, bool boolean)\n{\n\tint err;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\terr  = mbuf_write_u8(mb, RTMP_AMF_TYPE_BOOLEAN);\n\terr |= mbuf_write_u8(mb, !!boolean);\n\n\treturn err;\n}\n\n\nint rtmp_amf_encode_string(struct mbuf *mb, const char *str)\n{\n\tsize_t len;\n\tint err;\n\n\tif (!mb || !str)\n\t\treturn EINVAL;\n\n\tlen = str_len(str);\n\n\tif (len > 65535)\n\t\treturn EOVERFLOW;\n\n\terr  = mbuf_write_u8(mb, RTMP_AMF_TYPE_STRING);\n\terr |= mbuf_write_u16(mb, htons((uint16_t)len));\n\terr |= mbuf_write_str(mb, str);\n\n\treturn err;\n}\n\n\nint rtmp_amf_encode_null(struct mbuf *mb)\n{\n\tif (!mb)\n\t\treturn EINVAL;\n\n\treturn mbuf_write_u8(mb, RTMP_AMF_TYPE_NULL);\n}\n\n\n/*\n * NUMBER    double\n * BOOLEAN   bool\n * STRING    const char *\n * OBJECT    const char *key    sub-count\n * NULL      NULL\n * ARRAY     const char *key    sub-count\n */\nint rtmp_amf_vencode_object(struct mbuf *mb, enum rtmp_amf_type container,\n\t\t\t    unsigned propc, va_list *ap)\n{\n\tbool encode_key;\n\tunsigned i;\n\tint err = 0;\n\n\tif (!mb || !propc || !ap)\n\t\treturn EINVAL;\n\n\tencode_key = container_has_key(container);\n\n\tswitch (container) {\n\n\tcase RTMP_AMF_TYPE_OBJECT:\n\t\terr = rtmp_amf_encode_object_start(mb);\n\t\tbreak;\n\n\tcase RTMP_AMF_TYPE_ECMA_ARRAY:\n\tcase RTMP_AMF_TYPE_STRICT_ARRAY:\n\t\terr = rtmp_amf_encode_array_start(mb, container, propc);\n\t\tbreak;\n\n\tcase RTMP_AMF_TYPE_ROOT:\n\t\tbreak;\n\n\tdefault:\n\t\treturn ENOTSUP;\n\t}\n\n\tif (err)\n\t\treturn err;\n\n\tfor (i=0; i<propc; i++) {\n\n\t\tint type = va_arg(*ap, int);\n\t\tconst char *str;\n\t\tint subcount;\n\t\tdouble dbl;\n\t\tbool b;\n\n\t\t/* add key if ARRAY or OBJECT container */\n\t\tif (encode_key) {\n\t\t\tconst char *key;\n\n\t\t\tkey = va_arg(*ap, const char *);\n\t\t\tif (!key)\n\t\t\t\treturn EINVAL;\n\n\t\t\terr = rtmp_amf_encode_key(mb, key);\n\t\t\tif (err)\n\t\t\t\treturn err;\n\t\t}\n\n\t\tswitch (type) {\n\n\t\tcase RTMP_AMF_TYPE_NUMBER:\n\t\t\tdbl = va_arg(*ap, double);\n\t\t\terr = rtmp_amf_encode_number(mb, dbl);\n\t\t\tbreak;\n\n\t\tcase RTMP_AMF_TYPE_BOOLEAN:\n\t\t\tb = va_arg(*ap, int);\n\t\t\terr = rtmp_amf_encode_boolean(mb, b);\n\t\t\tbreak;\n\n\t\tcase RTMP_AMF_TYPE_STRING:\n\t\t\tstr = va_arg(*ap, const char *);\n\t\t\terr = rtmp_amf_encode_string(mb, str);\n\t\t\tbreak;\n\n\t\tcase RTMP_AMF_TYPE_NULL:\n\t\t\terr = rtmp_amf_encode_null(mb);\n\t\t\tbreak;\n\n\t\tcase RTMP_AMF_TYPE_OBJECT:\n\t\tcase RTMP_AMF_TYPE_ECMA_ARRAY:\n\t\tcase RTMP_AMF_TYPE_STRICT_ARRAY:\n\t\t\t/* recursive */\n\t\t\tsubcount = va_arg(*ap, int);\n\t\t\terr = rtmp_amf_vencode_object(mb, type, subcount, ap);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn ENOTSUP;\n\t\t}\n\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (encode_key)\n\t\terr = rtmp_amf_encode_object_end(mb);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/rtmp/chunk.c",
    "content": "/**\n * @file rtmp/chunk.c  Real Time Messaging Protocol (RTMP) -- Chunking\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_tcp.h>\n#include <re_list.h>\n#include <re_rtmp.h>\n#include \"rtmp.h\"\n\n\n/*\n * Stateless RTMP chunker\n */\nint rtmp_chunker(unsigned format, uint32_t chunk_id,\n\t\t uint32_t timestamp, uint32_t timestamp_delta,\n\t\t uint8_t msg_type_id, uint32_t msg_stream_id,\n\t\t const uint8_t *payload, size_t payload_len,\n\t\t size_t max_chunk_sz, struct tcp_conn *tc)\n{\n\tconst uint8_t *pend = payload + payload_len;\n\tstruct rtmp_header hdr;\n\tstruct mbuf *mb;\n\tsize_t chunk_sz;\n\tint err;\n\n\tif (!payload || !payload_len || !max_chunk_sz || !tc)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(payload_len + 256);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tmemset(&hdr, 0, sizeof(hdr));\n\n\thdr.format = format;\n\thdr.chunk_id = chunk_id;\n\n\thdr.timestamp       = timestamp;\n\thdr.timestamp_delta = timestamp_delta;\n\thdr.length          = (uint32_t)payload_len;\n\thdr.type_id         = msg_type_id;\n\thdr.stream_id       = msg_stream_id;\n\n\tchunk_sz = min(payload_len, max_chunk_sz);\n\n\terr  = rtmp_header_encode(mb, &hdr);\n\terr |= mbuf_write_mem(mb, payload, chunk_sz);\n\tif (err)\n\t\tgoto out;\n\n\tpayload += chunk_sz;\n\n\thdr.format = 3;\n\n\twhile (payload < pend) {\n\n\t\tconst size_t len = pend - payload;\n\n\t\tchunk_sz = min(len, max_chunk_sz);\n\n\t\terr  = rtmp_header_encode(mb, &hdr);\n\t\terr |= mbuf_write_mem(mb, payload, chunk_sz);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tpayload += chunk_sz;\n\t}\n\n\tmb->pos = 0;\n\n\terr = tcp_send(tc, mb);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/rtmp/conn.c",
    "content": "/**\n * @file rtmp/conn.c  Real Time Messaging Protocol (RTMP) -- NetConnection\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_tcp.h>\n#include <re_srtp.h>\n#include <re_tls.h>\n#include <re_sys.h>\n#include <re_odict.h>\n#include <re_dns.h>\n#include <re_uri.h>\n#include <re_rtmp.h>\n#include \"rtmp.h\"\n\n\nenum {\n\tWINDOW_ACK_SIZE = 2500000\n};\n\n\nstatic int req_connect(struct rtmp_conn *conn);\n\n\nstatic void conn_destructor(void *data)\n{\n\tstruct rtmp_conn *conn = data;\n\n\tlist_flush(&conn->ctransl);\n\tlist_flush(&conn->streaml);\n\n\tmem_deref(conn->dnsq6);\n\tmem_deref(conn->dnsq4);\n\tmem_deref(conn->dnsc);\n\tmem_deref(conn->sc);\n\tmem_deref(conn->tc);\n\tmem_deref(conn->mb);\n\tmem_deref(conn->dechunk);\n\tmem_deref(conn->uri);\n\tmem_deref(conn->app);\n\tmem_deref(conn->host);\n\tmem_deref(conn->stream);\n}\n\n\nstatic int handle_amf_command(struct rtmp_conn *conn, uint32_t stream_id,\n\t\t\t      struct mbuf *mb)\n{\n\tstruct odict *msg = NULL;\n\tconst char *name;\n\tint err;\n\n\terr = rtmp_amf_decode(&msg, mb);\n\tif (err)\n\t\treturn err;\n\n\tname = odict_string(msg, \"0\");\n\n\tif (conn->is_client &&\n\t    (0 == str_casecmp(name, \"_result\") ||\n\t     0 == str_casecmp(name, \"_error\"))) {\n\n\t\t/* forward response to transaction layer */\n\t\trtmp_ctrans_response(&conn->ctransl, msg);\n\t}\n\telse {\n\t\tstruct rtmp_stream *strm;\n\n\t\tif (stream_id == 0) {\n\t\t\tif (conn->cmdh)\n\t\t\t\tconn->cmdh(msg, conn->arg);\n\t\t}\n\t\telse {\n\t\t\tstrm = rtmp_stream_find(conn, stream_id);\n\t\t\tif (strm) {\n\t\t\t\tif (strm->cmdh)\n\t\t\t\t\tstrm->cmdh(msg, strm->arg);\n\t\t\t}\n\t\t}\n\t}\n\n\tmem_deref(msg);\n\n\treturn 0;\n}\n\n\nstatic int handle_user_control_msg(struct rtmp_conn *conn, struct mbuf *mb)\n{\n\tstruct rtmp_stream *strm;\n\tenum rtmp_event_type event;\n\tuint32_t value;\n\tint err;\n\n\tif (mbuf_get_left(mb) < 6)\n\t\treturn EBADMSG;\n\n\tevent = ntohs(mbuf_read_u16(mb));\n\tvalue = ntohl(mbuf_read_u32(mb));\n\n\tswitch (event) {\n\n\tcase RTMP_EVENT_STREAM_BEGIN:\n\tcase RTMP_EVENT_STREAM_EOF:\n\tcase RTMP_EVENT_STREAM_DRY:\n\tcase RTMP_EVENT_STREAM_IS_RECORDED:\n\tcase RTMP_EVENT_SET_BUFFER_LENGTH:\n\n\t\tif (value != RTMP_CONTROL_STREAM_ID) {\n\n\t\t\tstrm = rtmp_stream_find(conn, value);\n\t\t\tif (strm && strm->ctrlh)\n\t\t\t\tstrm->ctrlh(event, mb, strm->arg);\n\t\t}\n\t\tbreak;\n\n\tcase RTMP_EVENT_PING_REQUEST:\n\n\t\terr = rtmp_control(conn, RTMP_TYPE_USER_CONTROL_MSG,\n\t\t\t\t   RTMP_EVENT_PING_RESPONSE, value);\n\t\tif (err)\n\t\t\treturn err;\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\n\nstatic int handle_data_message(struct rtmp_conn *conn, uint32_t stream_id,\n\t\t\t       struct mbuf *mb)\n{\n\tstruct rtmp_stream *strm;\n\tstruct odict *msg;\n\tint err;\n\n\terr = rtmp_amf_decode(&msg, mb);\n\tif (err)\n\t\treturn err;\n\n\tstrm = rtmp_stream_find(conn, stream_id);\n\tif (strm && strm->datah)\n\t\tstrm->datah(msg, strm->arg);\n\n\tmem_deref(msg);\n\n\treturn 0;\n}\n\n\nstatic int rtmp_dechunk_handler(const struct rtmp_header *hdr,\n\t\t\t\tstruct mbuf *mb, void *arg)\n{\n\tstruct rtmp_conn *conn = arg;\n\tstruct rtmp_stream *strm;\n\tuint32_t val;\n\tuint32_t was;\n\tuint8_t limit;\n\tint err = 0;\n\n\tswitch (hdr->type_id) {\n\n\tcase RTMP_TYPE_SET_CHUNK_SIZE:\n\t\tif (mbuf_get_left(mb) < 4)\n\t\t\treturn EBADMSG;\n\n\t\tval = ntohl(mbuf_read_u32(mb));\n\n\t\tval = val & 0x7fffffff;\n\n\t\trtmp_dechunker_set_chunksize(conn->dechunk, val);\n\t\tbreak;\n\n\tcase RTMP_TYPE_ACKNOWLEDGEMENT:\n\t\tif (mbuf_get_left(mb) < 4)\n\t\t\treturn EBADMSG;\n\n\t\tval = ntohl(mbuf_read_u32(mb));\n\t\t(void)val;\n\t\tbreak;\n\n\tcase RTMP_TYPE_AMF0:\n\t\terr = handle_amf_command(conn, hdr->stream_id, mb);\n\t\tbreak;\n\n\tcase RTMP_TYPE_WINDOW_ACK_SIZE:\n\t\tif (mbuf_get_left(mb) < 4)\n\t\t\treturn EBADMSG;\n\n\t\twas = ntohl(mbuf_read_u32(mb));\n\t\tif (was != 0)\n\t\t\tconn->window_ack_size = was;\n\t\tbreak;\n\n\tcase RTMP_TYPE_SET_PEER_BANDWIDTH:\n\t\tif (mbuf_get_left(mb) < 5)\n\t\t\treturn EBADMSG;\n\n\t\twas = ntohl(mbuf_read_u32(mb));\n\t\tlimit = mbuf_read_u8(mb);\n\t\t(void)limit;\n\n\t\tif (was != 0)\n\t\t\tconn->window_ack_size = was;\n\n\t\terr = rtmp_control(conn, RTMP_TYPE_WINDOW_ACK_SIZE,\n\t\t\t\t   (uint32_t)WINDOW_ACK_SIZE);\n\t\tbreak;\n\n\tcase RTMP_TYPE_USER_CONTROL_MSG:\n\t\terr = handle_user_control_msg(conn, mb);\n\t\tbreak;\n\n\t\t/* XXX: common code for audio+video */\n\tcase RTMP_TYPE_AUDIO:\n\t\tstrm = rtmp_stream_find(conn, hdr->stream_id);\n\t\tif (strm) {\n\t\t\tif (strm->auh) {\n\t\t\t\tstrm->auh(hdr->timestamp,\n\t\t\t\t\t  mb->buf, mb->end,\n\t\t\t\t\t  strm->arg);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase RTMP_TYPE_VIDEO:\n\t\tstrm = rtmp_stream_find(conn, hdr->stream_id);\n\t\tif (strm) {\n\t\t\tif (strm->vidh) {\n\t\t\t\tstrm->vidh(hdr->timestamp,\n\t\t\t\t\t   mb->buf, mb->end,\n\t\t\t\t\t   strm->arg);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tcase RTMP_TYPE_DATA:\n\t\terr = handle_data_message(conn, hdr->stream_id, mb);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nstatic struct rtmp_conn *rtmp_conn_alloc(bool is_client,\n\t\t\t\t\t rtmp_estab_h *estabh,\n\t\t\t\t\t rtmp_command_h *cmdh,\n\t\t\t\t\t rtmp_close_h *closeh,\n\t\t\t\t\t void *arg)\n{\n\tstruct rtmp_conn *conn;\n\tint err;\n\n\tconn = mem_zalloc(sizeof(*conn), conn_destructor);\n\tif (!conn)\n\t\treturn NULL;\n\n\tconn->is_client = is_client;\n\tconn->state = RTMP_STATE_UNINITIALIZED;\n\n\tconn->send_chunk_size = RTMP_DEFAULT_CHUNKSIZE;\n\tconn->window_ack_size = WINDOW_ACK_SIZE;\n\n\terr = rtmp_dechunker_alloc(&conn->dechunk, RTMP_DEFAULT_CHUNKSIZE,\n\t\t\t\t   rtmp_dechunk_handler, conn);\n\tif (err)\n\t\tgoto out;\n\n\t/* must be above 2 */\n\tconn->chunk_id_counter = RTMP_CHUNK_ID_CONN + 1;\n\n\tconn->estabh = estabh;\n\tconn->cmdh   = cmdh;\n\tconn->closeh = closeh;\n\tconn->arg    = arg;\n\n out:\n\tif (err)\n\t\treturn mem_deref(conn);\n\n\treturn conn;\n}\n\n\nstatic inline void set_state(struct rtmp_conn *conn,\n\t\t\t     enum rtmp_handshake_state state)\n{\n\tconn->state = state;\n}\n\n\nstatic int send_packet(struct rtmp_conn *conn, const uint8_t *pkt, size_t len)\n{\n\tstruct mbuf *mb;\n\tint err;\n\n\tif (!conn || !pkt || !len)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(len);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\t(void)mbuf_write_mem(mb, pkt, len);\n\n\tmb->pos = 0;\n\n\terr = tcp_send(conn->tc, mb);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic int handshake_start(struct rtmp_conn *conn)\n{\n\tuint8_t sig[1+RTMP_HANDSHAKE_SIZE];\n\tint err;\n\n\tsig[0] = RTMP_PROTOCOL_VERSION;\n\tsig[1] = 0;\n\tsig[2] = 0;\n\tsig[3] = 0;\n\tsig[4] = 0;\n\tsig[5] = VER_MAJOR;\n\tsig[6] = VER_MINOR;\n\tsig[7] = VER_PATCH;\n\tsig[8] = 0;\n\trand_bytes(sig + 9, sizeof(sig) - 9);\n\n\terr = send_packet(conn, sig, sizeof(sig));\n\tif (err)\n\t\treturn err;\n\n\tset_state(conn, RTMP_STATE_VERSION_SENT);\n\n\treturn 0;\n}\n\n\nstatic void conn_close(struct rtmp_conn *conn, int err)\n{\n\trtmp_close_h *closeh;\n\n\tconn->sc = mem_deref(conn->sc);\n\tconn->tc = mem_deref(conn->tc);\n\tconn->dnsq6 = mem_deref(conn->dnsq6);\n\tconn->dnsq4 = mem_deref(conn->dnsq4);\n\n\tcloseh = conn->closeh;\n\tif (closeh) {\n\t\tconn->closeh = NULL;\n\t\tcloseh(err, conn->arg);\n\t}\n}\n\n\nstatic void tcp_estab_handler(void *arg)\n{\n\tstruct rtmp_conn *conn = arg;\n\tint err = 0;\n\n\tif (conn->is_client) {\n\n\t\terr = handshake_start(conn);\n\t}\n\n\tif (err)\n\t\tconn_close(conn, err);\n}\n\n\n/* Send AMF0 Command or Data */\nint rtmp_send_amf_command(const struct rtmp_conn *conn,\n\t\t\t  unsigned format, uint32_t chunk_id,\n\t\t\t  uint8_t type_id,\n\t\t\t  uint32_t msg_stream_id,\n\t\t\t  const uint8_t *cmd, size_t len)\n{\n\tif (!conn || !cmd || !len)\n\t\treturn EINVAL;\n\n\treturn rtmp_chunker(format, chunk_id, 0, 0, type_id, msg_stream_id,\n\t\t\t    cmd, len, conn->send_chunk_size,\n\t\t\t    conn->tc);\n}\n\n\nstatic void connect_resp_handler(bool success, const struct odict *msg,\n\t\t\t\t void *arg)\n{\n\tstruct rtmp_conn *conn = arg;\n\trtmp_estab_h *estabh;\n\t(void)msg;\n\n\tif (!success) {\n\t\tconn_close(conn, EPROTO);\n\t\treturn;\n\t}\n\n\tconn->connected = true;\n\n\testabh = conn->estabh;\n\tif (estabh) {\n\t\tconn->estabh = NULL;\n\t\testabh(conn->arg);\n\t}\n}\n\n\nstatic int send_connect(struct rtmp_conn *conn)\n{\n\tconst int ac  = 0x0400;  /* AAC  */\n\tconst int vc  = 0x0080;  /* H264 */\n\n\treturn rtmp_amf_request(conn, RTMP_CONTROL_STREAM_ID, \"connect\",\n\t\t\t\tconnect_resp_handler, conn,\n\t\t\t\t1,\n\t\t\tRTMP_AMF_TYPE_OBJECT, 8,\n\t\t          RTMP_AMF_TYPE_STRING, \"app\", conn->app,\n\t\t          RTMP_AMF_TYPE_STRING, \"flashVer\", \"FMLE/3.0\",\n\t\t          RTMP_AMF_TYPE_STRING, \"tcUrl\", conn->uri,\n\t\t          RTMP_AMF_TYPE_BOOLEAN, \"fpad\", false,\n\t\t          RTMP_AMF_TYPE_NUMBER, \"capabilities\", 15.0,\n\t\t          RTMP_AMF_TYPE_NUMBER, \"audioCodecs\", (double)ac,\n\t\t          RTMP_AMF_TYPE_NUMBER, \"videoCodecs\", (double)vc,\n\t\t          RTMP_AMF_TYPE_NUMBER, \"videoFunction\", 1.0);\n}\n\n\nstatic int client_handle_packet(struct rtmp_conn *conn, struct mbuf *mb)\n{\n\tuint8_t s0;\n\tuint8_t s1[RTMP_HANDSHAKE_SIZE];\n\tint err = 0;\n\n\tswitch (conn->state) {\n\n\tcase RTMP_STATE_VERSION_SENT:\n\t\tif (mbuf_get_left(mb) < (1+RTMP_HANDSHAKE_SIZE))\n\t\t\treturn ENODATA;\n\n\t\ts0 = mbuf_read_u8(mb);\n\t\tif (s0 != RTMP_PROTOCOL_VERSION)\n\t\t\treturn EPROTO;\n\n\t\t(void)mbuf_read_mem(mb, s1, sizeof(s1));\n\n\t\terr = send_packet(conn, s1, sizeof(s1));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tset_state(conn, RTMP_STATE_ACK_SENT);\n\t\tbreak;\n\n\tcase RTMP_STATE_ACK_SENT:\n\t\tif (mbuf_get_left(mb) < RTMP_HANDSHAKE_SIZE)\n\t\t\treturn ENODATA;\n\n\t\t/* S2 (ignored) */\n\t\tmbuf_advance(mb, RTMP_HANDSHAKE_SIZE);\n\n\t\tconn->send_chunk_size = 4096;\n\t\terr = rtmp_control(conn, RTMP_TYPE_SET_CHUNK_SIZE,\n\t\t\t\t   conn->send_chunk_size);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = send_connect(conn);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tset_state(conn, RTMP_STATE_HANDSHAKE_DONE);\n\t\tbreak;\n\n\tcase RTMP_STATE_HANDSHAKE_DONE:\n\t\terr = rtmp_dechunker_receive(conn->dechunk, mb);\n\t\tif (err)\n\t\t\treturn err;\n\t\tbreak;\n\n\tdefault:\n\t\treturn EPROTO;\n\t}\n\n\treturn 0;\n}\n\n\nstatic int server_handle_packet(struct rtmp_conn *conn, struct mbuf *mb)\n{\n\tuint8_t c0;\n\tuint8_t c1[RTMP_HANDSHAKE_SIZE];\n\tint err = 0;\n\n\tswitch (conn->state) {\n\n\tcase RTMP_STATE_UNINITIALIZED:\n\t\tif (mbuf_get_left(mb) < 1)\n\t\t\treturn ENODATA;\n\n\t\tc0 = mbuf_read_u8(mb);\n\t\tif (c0 != RTMP_PROTOCOL_VERSION)\n\t\t\treturn EPROTO;\n\n\t\t/* Send S0 + S1 */\n\t\terr = handshake_start(conn);\n\t\tif (err)\n\t\t\treturn err;\n\t\tbreak;\n\n\tcase RTMP_STATE_VERSION_SENT:\n\t\tif (mbuf_get_left(mb) < RTMP_HANDSHAKE_SIZE)\n\t\t\treturn ENODATA;\n\n\t\t(void)mbuf_read_mem(mb, c1, sizeof(c1));\n\n\t\t/* Copy C1 to S2 */\n\t\terr = send_packet(conn, c1, sizeof(c1));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tset_state(conn, RTMP_STATE_ACK_SENT);\n\t\tbreak;\n\n\tcase RTMP_STATE_ACK_SENT:\n\t\tif (mbuf_get_left(mb) < RTMP_HANDSHAKE_SIZE)\n\t\t\treturn ENODATA;\n\n\t\t/* C2 (ignored) */\n\t\tmbuf_advance(mb, RTMP_HANDSHAKE_SIZE);\n\n\t\tconn->send_chunk_size = 4096;\n\t\terr = rtmp_control(conn, RTMP_TYPE_SET_CHUNK_SIZE,\n\t\t\t\t   conn->send_chunk_size);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tset_state(conn, RTMP_STATE_HANDSHAKE_DONE);\n\t\tbreak;\n\n\tcase RTMP_STATE_HANDSHAKE_DONE:\n\t\terr = rtmp_dechunker_receive(conn->dechunk, mb);\n\t\tif (err)\n\t\t\treturn err;\n\t\tbreak;\n\n\tdefault:\n\t\treturn EPROTO;\n\t}\n\n\treturn 0;\n}\n\n\nstatic void tcp_recv_handler(struct mbuf *mb_pkt, void *arg)\n{\n\tstruct rtmp_conn *conn = arg;\n\tint err = 0;\n\n\tconn->total_bytes += mbuf_get_left(mb_pkt);\n\n\t/* re-assembly of fragments */\n\tif (conn->mb) {\n\t\tconst size_t len = mbuf_get_left(mb_pkt), pos = conn->mb->pos;\n\n\t\tif ((mbuf_get_left(conn->mb) + len) > RTMP_MESSAGE_LEN_MAX) {\n\t\t\terr = EOVERFLOW;\n\t\t\tgoto out;\n\t\t}\n\n\t\tconn->mb->pos = conn->mb->end;\n\n\t\terr = mbuf_write_mem(conn->mb,\n\t\t\t\t     mbuf_buf(mb_pkt), mbuf_get_left(mb_pkt));\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tconn->mb->pos = pos;\n\t}\n\telse {\n\t\tconn->mb = mem_ref(mb_pkt);\n\t}\n\n\twhile (mbuf_get_left(conn->mb) > 0) {\n\n\t\tsize_t pos;\n\t\tuint32_t nrefs;\n\n\t\tpos = conn->mb->pos;\n\n\t\tmem_ref(conn);\n\n\t\tif (conn->is_client)\n\t\t\terr = client_handle_packet(conn, conn->mb);\n\t\telse\n\t\t\terr = server_handle_packet(conn, conn->mb);\n\n\t\tnrefs = mem_nrefs(conn);\n\n\t\tmem_deref(conn);\n\n\t\tif (nrefs == 1)\n\t\t\treturn;\n\n\t\tif (!conn->tc)\n\t\t\treturn;\n\n\t\tif (err) {\n\n\t\t\t/* rewind */\n\t\t\tconn->mb->pos = pos;\n\n\t\t\tif (err == ENODATA)\n\t\t\t\terr = 0;\n\t\t\tbreak;\n\t\t}\n\n\n\t\tif (conn->mb->pos >= conn->mb->end) {\n\t\t\tconn->mb = mem_deref(conn->mb);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\tif (conn->total_bytes >= (conn->last_ack + conn->window_ack_size)) {\n\n\t\tconn->last_ack = conn->total_bytes;\n\n\t\terr = rtmp_control(conn, RTMP_TYPE_ACKNOWLEDGEMENT,\n\t\t\t\t   (uint32_t)conn->total_bytes);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n out:\n\tif (err)\n\t\tconn_close(conn, err);\n}\n\n\nstatic void tcp_close_handler(int err, void *arg)\n{\n\tstruct rtmp_conn *conn = arg;\n\n\tif (conn->is_client && !conn->connected && conn->srvc > 0) {\n\t\terr = req_connect(conn);\n\t\tif (!err)\n\t\t\treturn;\n\t}\n\n\tconn_close(conn, err);\n}\n\n\nstatic int req_connect(struct rtmp_conn *conn)\n{\n\tconst struct sa *addr;\n\tint err = EINVAL;\n\n\twhile (conn->srvc > 0) {\n\n\t\t--conn->srvc;\n\n\t\taddr = &conn->srvv[conn->srvc];\n\n\t\tconn->send_chunk_size = RTMP_DEFAULT_CHUNKSIZE;\n\t\tconn->window_ack_size = WINDOW_ACK_SIZE;\n\t\tconn->state = RTMP_STATE_UNINITIALIZED;\n\t\tconn->last_ack = 0;\n\t\tconn->total_bytes = 0;\n\t\tconn->mb = mem_deref(conn->mb);\n\t\tconn->sc = mem_deref(conn->sc);\n\t\tconn->tc = mem_deref(conn->tc);\n\n\t\trtmp_dechunker_set_chunksize(conn->dechunk,\n\t\t\t\t\t     RTMP_DEFAULT_CHUNKSIZE);\n\n\t\terr = tcp_connect(&conn->tc, addr, tcp_estab_handler,\n\t\t\t\t  tcp_recv_handler, tcp_close_handler, conn);\n\n#ifdef USE_TLS\n\t\tif (conn->tls && !err) {\n\t\t\terr = tls_start_tcp(&conn->sc, conn->tls,\n\t\t\t\t\t    conn->tc, 0);\n\t\t\tif (!err)\n\t\t\t\terr = tls_set_verify_server(conn->sc,\n\t\t\t\t\t\t\t    conn->host);\n\t\t}\n#endif\n\n\t\tif (!err)\n\t\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nstatic bool rr_handler(struct dnsrr *rr, void *arg)\n{\n\tstruct rtmp_conn *conn = arg;\n\n\tif (conn->srvc >= RE_ARRAY_SIZE(conn->srvv))\n\t\treturn true;\n\n\tswitch (rr->type) {\n\n\tcase DNS_TYPE_A:\n\t\tsa_set_in(&conn->srvv[conn->srvc++], rr->rdata.a.addr,\n                          conn->port);\n\t\tbreak;\n\n\tcase DNS_TYPE_AAAA:\n\t\tsa_set_in6(&conn->srvv[conn->srvc++], rr->rdata.aaaa.addr,\n\t\t\t   conn->port);\n\t\tbreak;\n\t}\n\n\treturn false;\n}\n\n\nstatic void query_handler(int err, const struct dnshdr *hdr, struct list *ansl,\n\t\t\t  struct list *authl, struct list *addl, void *arg)\n{\n\tstruct rtmp_conn *conn = arg;\n\t(void)hdr;\n\t(void)authl;\n\t(void)addl;\n\n\tdns_rrlist_apply2(ansl, conn->host, DNS_TYPE_A, DNS_TYPE_AAAA,\n                          DNS_CLASS_IN, true, rr_handler, conn);\n\n\t/* wait for other (A/AAAA) query to complete */\n\tif (conn->dnsq4 || conn->dnsq6)\n\t\treturn;\n\n\tif (conn->srvc == 0) {\n\t\terr = err ? err : EDESTADDRREQ;\n\t\tgoto out;\n\t}\n\n\terr = req_connect(conn);\n\tif (err)\n\t\tgoto out;\n\n\treturn;\n\n out:\n\tconn_close(conn, err);\n}\n\n\n/**\n * Connect to an RTMP server\n *\n * @param connp  Pointer to allocated RTMP connection object\n * @param dnsc   DNS Client for resolving FQDN uris\n * @param uri    RTMP uri to connect to\n * @param tls    TLS Context (optional)\n * @param estabh Established handler\n * @param cmdh   Incoming command handler\n * @param closeh Close handler\n * @param arg    Handler argument\n *\n * @return 0 if success, otherwise errorcode\n *\n * Example URIs:\n *\n *     rtmp://a.rtmp.youtube.com/live2/my-stream\n *     rtmp://[::1]/vod/mp4:sample.mp4\n */\nint rtmp_connect(struct rtmp_conn **connp, struct dnsc *dnsc, const char *uri,\n\t\t struct tls *tls,\n\t\t rtmp_estab_h *estabh, rtmp_command_h *cmdh,\n\t\t rtmp_close_h *closeh, void *arg)\n{\n\tstruct rtmp_conn *conn;\n\tstruct pl pl_scheme;\n\tstruct pl pl_hostport;\n\tstruct pl pl_host;\n\tstruct pl pl_port;\n\tstruct pl pl_path;\n\tstruct pl pl_app;\n\tstruct pl pl_stream;\n\tconst char *tok;\n\tuint16_t defport;\n\tint err;\n\n\tif (!connp || !uri)\n\t\treturn EINVAL;\n\n\tif (re_regex(uri, strlen(uri), \"[a-z]+://[^/]+/[^]+\",\n\t\t     &pl_scheme, &pl_hostport, &pl_path))\n\t\treturn EINVAL;\n\n\ttok = pl_strrchr(&pl_path, '/');\n\tif (!tok)\n\t\treturn EINVAL;\n\n\tpl_app.p = pl_path.p;\n\tpl_app.l = tok - pl_path.p;\n\n\tpl_stream.p = tok + 1;\n\tpl_stream.l = pl_path.p + pl_path.l - pl_stream.p;\n\n\tif (!pl_strcasecmp(&pl_scheme, \"rtmp\")) {\n\t\ttls     = NULL;\n\t\tdefport = RTMP_PORT;\n\t}\n#ifdef USE_TLS\n\telse if (!pl_strcasecmp(&pl_scheme, \"rtmps\")) {\n\n\t\tif (!tls)\n\t\t\treturn EINVAL;\n\n\t\tdefport = 443;\n\t}\n#endif\n\telse\n\t\treturn ENOTSUP;\n\n\tif (uri_decode_hostport(&pl_hostport, &pl_host, &pl_port))\n\t\treturn EINVAL;\n\n\tconn = rtmp_conn_alloc(true, estabh, cmdh, closeh, arg);\n\tif (!conn)\n\t\treturn ENOMEM;\n\n\tconn->port = pl_isset(&pl_port) ? pl_u32(&pl_port) : defport;\n\tconn->tls = tls;\n\n\terr  = pl_strdup(&conn->app, &pl_app);\n\terr |= pl_strdup(&conn->stream, &pl_stream);\n\terr |= pl_strdup(&conn->host, &pl_host);\n\terr |= str_dup(&conn->uri, uri);\n\tif (err)\n\t\tgoto out;\n\n\tif (0 == sa_set(&conn->srvv[0], &pl_host, conn->port)) {\n\n\t\tconn->srvc = 1;\n\n\t\terr = req_connect(conn);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse {\n\t\tstruct sa tmp;\n\n\t\tif (!dnsc) {\n\t\t\terr = EINVAL;\n\t\t\tgoto out;\n\t\t}\n\n\t\tconn->dnsc = mem_ref(dnsc);\n\n\t\terr = dnsc_query(&conn->dnsq4, dnsc, conn->host, DNS_TYPE_A,\n\t\t\t\t DNS_CLASS_IN, true, query_handler, conn);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (0 == net_default_source_addr_get(AF_INET6, &tmp)) {\n\n\t\t\terr = dnsc_query(&conn->dnsq6, dnsc, conn->host,\n\t\t\t\t\t DNS_TYPE_AAAA, DNS_CLASS_IN,\n\t\t\t\t\t true, query_handler, conn);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(conn);\n\telse\n\t\t*connp = conn;\n\n\treturn err;\n}\n\n\n/**\n * Accept an incoming TCP connection creating an RTMP Server connection\n *\n * @param connp  Pointer to allocated RTMP connection object\n * @param ts     TCP socket with pending connection\n * @param tls    TLS Context (optional)\n * @param cmdh   Incoming command handler\n * @param closeh Close handler\n * @param arg    Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtmp_accept(struct rtmp_conn **connp, struct tcp_sock *ts,\n\t\tstruct tls *tls,\n\t\trtmp_command_h *cmdh, rtmp_close_h *closeh, void *arg)\n{\n\tstruct rtmp_conn *conn;\n\tint err;\n\n\tif (!connp || !ts)\n\t\treturn EINVAL;\n\n\tconn = rtmp_conn_alloc(false, NULL, cmdh, closeh, arg);\n\tif (!conn)\n\t\treturn ENOMEM;\n\n\terr = tcp_accept(&conn->tc, ts, tcp_estab_handler,\n\t\t\t tcp_recv_handler, tcp_close_handler, conn);\n\tif (err)\n\t\tgoto out;\n\n\tif (tls) {\n#ifdef USE_TLS\n\t\terr = tls_start_tcp(&conn->sc, tls, conn->tc, 0);\n\t\tif (err)\n\t\t\tgoto out;\n#endif\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(conn);\n\telse\n\t\t*connp = conn;\n\n\treturn err;\n}\n\n\nint rtmp_conn_send_msg(const struct rtmp_conn *conn,\n\t\t       unsigned format, uint32_t chunk_id,\n\t\t       uint32_t timestamp, uint32_t timestamp_delta,\n\t\t       uint8_t msg_type_id, uint32_t msg_stream_id,\n\t\t       const uint8_t *payload, size_t payload_len)\n{\n\tif (!conn || !payload || !payload_len)\n\t\treturn EINVAL;\n\n\treturn rtmp_chunker(format, chunk_id, timestamp, timestamp_delta,\n\t\t\t    msg_type_id, msg_stream_id, payload, payload_len,\n\t\t\t    conn->send_chunk_size,\n\t\t\t    conn->tc);\n}\n\n\nunsigned rtmp_conn_assign_chunkid(struct rtmp_conn *conn)\n{\n\tif (!conn)\n\t\treturn 0;\n\n\treturn ++conn->chunk_id_counter;\n}\n\n\nuint64_t rtmp_conn_assign_tid(struct rtmp_conn *conn)\n{\n\tif (!conn)\n\t\treturn 0;\n\n\treturn ++conn->tid_counter;\n}\n\n\n/**\n * Get the underlying TCP connection from an RTMP connection\n *\n * @param conn RTMP Connection\n *\n * @return TCP-Connection\n */\nstruct tcp_conn *rtmp_conn_tcpconn(const struct rtmp_conn *conn)\n{\n\treturn conn ? conn->tc : NULL;\n}\n\n\n/**\n * Get the RTMP connection stream name from rtmp_connect\n *\n * @param conn RTMP Connection\n *\n * @return RTMP Stream name or NULL\n */\nconst char *rtmp_conn_stream(const struct rtmp_conn *conn)\n{\n\treturn conn ? conn->stream : NULL;\n}\n\n\n/**\n * Set callback handlers for the RTMP connection\n *\n * @param conn   RTMP connection\n * @param cmdh   Incoming command handler\n * @param closeh Close handler\n * @param arg    Handler argument\n */\nvoid rtmp_set_handlers(struct rtmp_conn *conn, rtmp_command_h *cmdh,\n\t\t       rtmp_close_h *closeh, void *arg)\n{\n\tif (!conn)\n\t\treturn;\n\n\tconn->cmdh   = cmdh;\n\tconn->closeh = closeh;\n\tconn->arg    = arg;\n}\n\n\nstatic const char *rtmp_handshake_name(enum rtmp_handshake_state state)\n{\n\tswitch (state) {\n\n\tcase RTMP_STATE_UNINITIALIZED:  return \"UNINITIALIZED\";\n\tcase RTMP_STATE_VERSION_SENT:   return \"VERSION_SENT\";\n\tcase RTMP_STATE_ACK_SENT:       return \"ACK_SENT\";\n\tcase RTMP_STATE_HANDSHAKE_DONE: return \"HANDSHAKE_DONE\";\n\tdefault: return \"?\";\n\t}\n}\n\n\nint rtmp_conn_debug(struct re_printf *pf, const struct rtmp_conn *conn)\n{\n\tint err = 0;\n\n\tif (!conn)\n\t\treturn 0;\n\n\terr |= re_hprintf(pf, \"role:          %s\\n\",\n\t\t\t  conn->is_client ? \"Client\" : \"Server\");\n\terr |= re_hprintf(pf, \"state:         %s\\n\",\n\t\t\t  rtmp_handshake_name(conn->state));\n\terr |= re_hprintf(pf, \"connected:     %d\\n\", conn->connected);\n\terr |= re_hprintf(pf, \"chunk_size:    send=%u\\n\",\n\t\t\t  conn->send_chunk_size);\n\terr |= re_hprintf(pf, \"bytes:         %zu\\n\", conn->total_bytes);\n\terr |= re_hprintf(pf, \"streams:       %u\\n\",\n\t\t\t  list_count(&conn->streaml));\n\n\tif (conn->is_client) {\n\t\terr |= re_hprintf(pf, \"uri:           %s\\n\", conn->uri);\n\t\terr |= re_hprintf(pf, \"app:           %s\\n\", conn->app);\n\t\terr |= re_hprintf(pf, \"stream:        %s\\n\", conn->stream);\n\t}\n\n\terr |= re_hprintf(pf, \"%H\\n\", rtmp_dechunker_debug, conn->dechunk);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/rtmp/control.c",
    "content": "/**\n * @file rtmp/control.c  Real Time Messaging Protocol (RTMP) -- Control\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_rtmp.h>\n#include \"rtmp.h\"\n\n\n/**\n * Send an RTMP control message\n *\n * @param conn RTMP connection\n * @param type RTMP Packet type\n * @param ...  Optional packet arguments\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtmp_control(const struct rtmp_conn *conn, enum rtmp_packet_type type, ...)\n{\n\tstruct mbuf *mb;\n\tuint32_t u32;\n\tuint16_t event;\n\tva_list ap;\n\tint err = 0;\n\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(8);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tva_start(ap, type);\n\n\tswitch (type) {\n\n\tcase RTMP_TYPE_SET_CHUNK_SIZE:\n\tcase RTMP_TYPE_WINDOW_ACK_SIZE:\n\tcase RTMP_TYPE_ACKNOWLEDGEMENT:\n\t\tu32 = va_arg(ap, uint32_t);\n\t\terr = mbuf_write_u32(mb, htonl(u32));\n\t\tbreak;\n\n\tcase RTMP_TYPE_USER_CONTROL_MSG:\n\t\tevent = va_arg(ap, unsigned);\n\t\terr  = mbuf_write_u16(mb, htons(event));\n\t\terr |= mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));\n\t\tbreak;\n\n\tcase RTMP_TYPE_SET_PEER_BANDWIDTH:\n\t\terr  = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));\n\t\terr |= mbuf_write_u8(mb, va_arg(ap, unsigned));\n\t\tbreak;\n\n\tdefault:\n\t\terr = ENOTSUP;\n\t\tbreak;\n\t}\n\n\tva_end(ap);\n\n\tif (err)\n\t\tgoto out;\n\n\terr = rtmp_conn_send_msg(conn, 0, RTMP_CHUNK_ID_CONTROL, 0, 0, type,\n\t\t\t\t RTMP_CONTROL_STREAM_ID, mb->buf, mb->end);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/**\n * Get the event name as a string\n *\n * @param event RTMP Event type\n *\n * @return Name of the event as a string\n */\nconst char *rtmp_event_name(enum rtmp_event_type event)\n{\n\tswitch (event) {\n\n\tcase RTMP_EVENT_STREAM_BEGIN:        return \"StreamBegin\";\n\tcase RTMP_EVENT_STREAM_EOF:          return \"StreamEOF\";\n\tcase RTMP_EVENT_STREAM_DRY:          return \"StreamDry\";\n\tcase RTMP_EVENT_SET_BUFFER_LENGTH:   return \"SetBufferLength\";\n\tcase RTMP_EVENT_STREAM_IS_RECORDED:  return \"StreamIsRecorded\";\n\tcase RTMP_EVENT_PING_REQUEST:        return \"PingRequest\";\n\tcase RTMP_EVENT_PING_RESPONSE:       return \"PingResponse\";\n\tdefault: return \"?\";\n\t}\n}\n"
  },
  {
    "path": "src/rtmp/ctrans.c",
    "content": "/**\n * @file rtmp/ctrans.c  Real Time Messaging Protocol -- AMF Client Transactions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_tcp.h>\n#include <re_sys.h>\n#include <re_odict.h>\n#include <re_rtmp.h>\n#include \"rtmp.h\"\n\n\nstruct rtmp_ctrans {\n\tstruct le le;\n\tuint64_t tid;\n\trtmp_resp_h *resph;\n\tvoid *arg;\n};\n\n\nstatic void ctrans_destructor(void *data)\n{\n\tstruct rtmp_ctrans *ct = data;\n\n\tlist_unlink(&ct->le);\n}\n\n\nstatic struct rtmp_ctrans *rtmp_ctrans_find(const struct list *ctransl,\n\t\t\t\t\t    uint64_t tid)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(ctransl); le; le = le->next) {\n\t\tstruct rtmp_ctrans *ct = le->data;\n\n\t\tif (tid == ct->tid)\n\t\t\treturn ct;\n\t}\n\n\treturn NULL;\n}\n\n\nint rtmp_amf_request(struct rtmp_conn *conn, uint32_t stream_id,\n\t\t     const char *command,\n\t\t     rtmp_resp_h *resph, void *arg, unsigned body_propc, ...)\n{\n\tstruct rtmp_ctrans *ct = NULL;\n\tstruct mbuf *mb;\n\tva_list ap;\n\tint err;\n\n\tif (!conn || !command || !resph)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tct = mem_zalloc(sizeof(*ct), ctrans_destructor);\n\tif (!ct) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tct->tid   = rtmp_conn_assign_tid(conn);\n\tct->resph = resph;\n\tct->arg   = arg;\n\n\terr = rtmp_command_header_encode(mb, command, ct->tid);\n\tif (err)\n\t\tgoto out;\n\n\tif (body_propc) {\n\t\tva_start(ap, body_propc);\n\t\terr = rtmp_amf_vencode_object(mb, RTMP_AMF_TYPE_ROOT,\n\t\t\t\t\t      body_propc, &ap);\n\t\tva_end(ap);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = rtmp_send_amf_command(conn, 0, RTMP_CHUNK_ID_CONN,\n\t\t\t\t    RTMP_TYPE_AMF0,\n\t\t\t\t    stream_id, mb->buf, mb->end);\n\tif (err)\n\t\tgoto out;\n\n\tlist_append(&conn->ctransl, &ct->le, ct);\n\n out:\n\tmem_deref(mb);\n\tif (err)\n\t\tmem_deref(ct);\n\n\treturn err;\n}\n\n\nint rtmp_ctrans_response(const struct list *ctransl,\n\t\t\t const struct odict *msg)\n{\n\tstruct rtmp_ctrans *ct;\n\tuint64_t tid;\n\tbool success;\n\trtmp_resp_h *resph;\n\tvoid *arg;\n\n\tif (!ctransl || !msg)\n\t\treturn EINVAL;\n\n\tsuccess = (0 == str_casecmp(odict_string(msg, \"0\"), \"_result\"));\n\n\tif (!odict_get_number(msg, &tid, \"1\"))\n\t\treturn EPROTO;\n\n\tct = rtmp_ctrans_find(ctransl, tid);\n\tif (!ct)\n\t\treturn ENOENT;\n\n\tresph = ct->resph;\n\targ = ct->arg;\n\n\tmem_deref(ct);\n\n\tresph(success, msg, arg);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/rtmp/dechunk.c",
    "content": "/**\n * @file rtmp/dechunk.c  Real Time Messaging Protocol (RTMP) -- Dechunking\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_rtmp.h>\n#include \"rtmp.h\"\n\n\nenum {\n\tMAX_CHUNKS = 64,\n};\n\n\nstruct rtmp_chunk {\n\tstruct le le;\n\tstruct rtmp_header hdr;\n\tstruct mbuf *mb;\n};\n\n/** Defines the RTMP Dechunker */\nstruct rtmp_dechunker {\n\tstruct list chunkl;      /* struct rtmp_chunk */\n\tsize_t chunk_sz;\n\trtmp_dechunk_h *chunkh;\n\tvoid *arg;\n};\n\n\nstatic void destructor(void *data)\n{\n\tstruct rtmp_dechunker *rd = data;\n\n\tlist_flush(&rd->chunkl);\n}\n\n\nstatic void chunk_destructor(void *data)\n{\n\tstruct rtmp_chunk *chunk = data;\n\n\tlist_unlink(&chunk->le);\n\tmem_deref(chunk->mb);\n}\n\n\nstatic struct rtmp_chunk *create_chunk(struct list *chunkl,\n\t\t\t\t       const struct rtmp_header *hdr)\n{\n\tstruct rtmp_chunk *chunk;\n\n\tchunk = mem_zalloc(sizeof(*chunk), chunk_destructor);\n\tif (!chunk)\n\t\treturn NULL;\n\n\tchunk->hdr = *hdr;\n\n\tlist_append(chunkl, &chunk->le, chunk);\n\n\treturn chunk;\n}\n\n\nstatic struct rtmp_chunk *find_chunk(const struct list *chunkl,\n\t\t\t\t     uint32_t chunk_id)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(chunkl); le; le = le->next) {\n\n\t\tstruct rtmp_chunk *chunk = le->data;\n\n\t\tif (chunk_id == chunk->hdr.chunk_id)\n\t\t\treturn chunk;\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * Stateful RTMP de-chunker for receiving complete messages\n */\nint  rtmp_dechunker_alloc(struct rtmp_dechunker **rdp, size_t chunk_sz,\n\t\t\t  rtmp_dechunk_h *chunkh, void *arg)\n{\n\tstruct rtmp_dechunker *rd;\n\n\tif (!rdp || !chunk_sz || !chunkh)\n\t\treturn EINVAL;\n\n\trd = mem_zalloc(sizeof(*rd), destructor);\n\tif (!rd)\n\t\treturn ENOMEM;\n\n\trd->chunk_sz = chunk_sz;\n\n\trd->chunkh = chunkh;\n\trd->arg    = arg;\n\n\t*rdp = rd;\n\n\treturn 0;\n}\n\n\nint rtmp_dechunker_receive(struct rtmp_dechunker *rd, struct mbuf *mb)\n{\n\tstruct rtmp_header hdr;\n\tstruct rtmp_chunk *chunk;\n\tsize_t chunk_sz, left, msg_len;\n\tint err;\n\n\tif (!rd || !mb)\n\t\treturn EINVAL;\n\n\terr = rtmp_header_decode(&hdr, mb);\n\tif (err)\n\t\treturn err;\n\n\t/* find preceding chunk, from chunk id */\n\tchunk = find_chunk(&rd->chunkl, hdr.chunk_id);\n\tif (!chunk) {\n\n\t\t/* only type 0 can create a new chunk stream */\n\t\tif (hdr.format == 0) {\n\t\t\tif (list_count(&rd->chunkl) > MAX_CHUNKS)\n\t\t\t\treturn EOVERFLOW;\n\n\t\t\tchunk = create_chunk(&rd->chunkl, &hdr);\n\t\t\tif (!chunk)\n\t\t\t\treturn ENOMEM;\n\t\t}\n\t\telse\n\t\t\treturn ENOENT;\n\t}\n\n\tswitch (hdr.format) {\n\n\tcase 0:\n\tcase 1:\n\tcase 2:\n\t\tif (hdr.format == 0) {\n\n\t\t\t/* copy the whole header */\n\t\t\tchunk->hdr = hdr;\n\t\t}\n\t\telse if (hdr.format == 1) {\n\n\t\t\tchunk->hdr.timestamp_delta = hdr.timestamp_delta;\n\t\t\tchunk->hdr.length          = hdr.length;\n\t\t\tchunk->hdr.type_id         = hdr.type_id;\n\t\t}\n\t\telse if (hdr.format == 2) {\n\n\t\t\tchunk->hdr.timestamp_delta = hdr.timestamp_delta;\n\t\t}\n\n\t\tmsg_len = chunk->hdr.length;\n\n\t\tchunk_sz = min(msg_len, rd->chunk_sz);\n\n\t\tif (mbuf_get_left(mb) < chunk_sz)\n\t\t\treturn ENODATA;\n\n\t\tmem_deref(chunk->mb);\n\t\tchunk->mb = mbuf_alloc(msg_len);\n\t\tif (!chunk->mb)\n\t\t\treturn ENOMEM;\n\n\t\terr = mbuf_read_mem(mb, chunk->mb->buf, chunk_sz);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tchunk->mb->pos = chunk_sz;\n\t\tchunk->mb->end = chunk_sz;\n\n\t\tchunk->hdr.format = hdr.format;\n\t\tchunk->hdr.ext_ts = hdr.ext_ts;\n\n\t\tif (hdr.format == 1 || hdr.format == 2)\n\t\t\tchunk->hdr.timestamp += hdr.timestamp_delta;\n\t\tbreak;\n\n\tcase 3:\n\t\tif (chunk->hdr.ext_ts) {\n\n\t\t\tuint32_t ext_ts;\n\n\t\t\tif (mbuf_get_left(mb) < 4)\n\t\t\t\treturn ENODATA;\n\n\t\t\text_ts = ntohl(mbuf_read_u32(mb));\n\n\t\t\tif (chunk->hdr.format == 0)\n\t\t\t\tchunk->hdr.timestamp = ext_ts;\n\t\t\telse\n\t\t\t\tchunk->hdr.timestamp_delta = ext_ts;\n\t\t}\n\n\t\tif (!chunk->mb) {\n\n\t\t\tchunk->mb = mbuf_alloc(chunk->hdr.length);\n\t\t\tif (!chunk->mb)\n\t\t\t\treturn ENOMEM;\n\n\t\t\tif (chunk->hdr.format == 0) {\n\t\t\t\tchunk->hdr.timestamp_delta =\n\t\t\t\t\tchunk->hdr.timestamp;\n\t\t\t}\n\n\t\t\tchunk->hdr.timestamp += chunk->hdr.timestamp_delta;\n\t\t}\n\n\t\tleft = mbuf_get_space(chunk->mb);\n\n\t\tchunk_sz = min(left, rd->chunk_sz);\n\n\t\tif (mbuf_get_left(mb) < chunk_sz)\n\t\t\treturn ENODATA;\n\n\t\terr = mbuf_read_mem(mb, mbuf_buf(chunk->mb), chunk_sz);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tchunk->mb->pos += chunk_sz;\n\t\tchunk->mb->end += chunk_sz;\n\t\tbreak;\n\n\tdefault:\n\t\treturn EPROTO;\n\t}\n\n\tif (chunk->mb->pos >= chunk->mb->size) {\n\n\t\tstruct mbuf *buf;\n\n\t\tchunk->mb->pos = 0;\n\n\t\tbuf = chunk->mb;\n\t\tchunk->mb = NULL;\n\n\t\terr = rd->chunkh(&chunk->hdr, buf, rd->arg);\n\n\t\tmem_deref(buf);\n\t}\n\n\treturn err;\n}\n\n\nvoid rtmp_dechunker_set_chunksize(struct rtmp_dechunker *rd, size_t chunk_sz)\n{\n\tif (!rd || !chunk_sz)\n\t\treturn;\n\n\trd->chunk_sz = chunk_sz;\n}\n\n\nint rtmp_dechunker_debug(struct re_printf *pf, const struct rtmp_dechunker *rd)\n{\n\tstruct le *le;\n\tint err;\n\n\tif (!rd)\n\t\treturn 0;\n\n\terr  = re_hprintf(pf, \"Dechunker Debug:\\n\");\n\n\terr |= re_hprintf(pf, \"chunk list: (%u)\\n\", list_count(&rd->chunkl));\n\n\tfor (le = rd->chunkl.head; le; le = le->next) {\n\n\t\tconst struct rtmp_chunk *msg = le->data;\n\n\t\terr |= re_hprintf(pf, \".. %H\\n\",\n\t\t\t\t  rtmp_header_print, &msg->hdr);\n\t}\n\n\terr |= re_hprintf(pf, \"\\n\");\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/rtmp/hdr.c",
    "content": "/**\n * @file rtmp/hdr.c  Real Time Messaging Protocol (RTMP) -- Headers\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_sys.h>\n#include <re_rtmp.h>\n#include \"rtmp.h\"\n\n\nenum {\n\tRTMP_CHUNK_ID_MIN     = 3,\n\tRTMP_CHUNK_ID_MAX     = 65599,  /* 65535 + 64 */\n\n\tRTMP_CHUNK_OFFSET     = 64,\n\tTIMESTAMP_24MAX       = 0x00ffffff,\n};\n\n\nstatic int mbuf_write_u24_hton(struct mbuf *mb, uint32_t u24)\n{\n\tint err = 0;\n\n\terr |= mbuf_write_u8(mb, u24 >> 16);\n\terr |= mbuf_write_u8(mb, u24 >> 8);\n\terr |= mbuf_write_u8(mb, u24 >> 0);\n\n\treturn err;\n}\n\n\nstatic uint32_t mbuf_read_u24_ntoh(struct mbuf *mb)\n{\n\tuint32_t u24;\n\n\tu24  = (uint32_t)mbuf_read_u8(mb) << 16;\n\tu24 |= (uint32_t)mbuf_read_u8(mb) << 8;\n\tu24 |= (uint32_t)mbuf_read_u8(mb) << 0;\n\n\treturn u24;\n}\n\n\nstatic int encode_basic_hdr(struct mbuf *mb, unsigned fmt,\n\t\t\t    uint32_t chunk_id)\n{\n\tuint8_t v;\n\tint err = 0;\n\n\tif (chunk_id >= 320) {\n\n\t\tconst uint16_t cs_id = chunk_id - RTMP_CHUNK_OFFSET;\n\n\t\tv = fmt<<6 | 1;\n\n\t\terr |= mbuf_write_u8(mb, v);\n\t\terr |= mbuf_write_u16(mb, htons(cs_id));\n\t}\n\telse if (chunk_id >= RTMP_CHUNK_OFFSET) {\n\n\t\tconst uint8_t cs_id = chunk_id - RTMP_CHUNK_OFFSET;\n\n\t\tv = fmt<<6 | 0;\n\n\t\terr |= mbuf_write_u8(mb, v);\n\t\terr |= mbuf_write_u8(mb, cs_id);\n\t}\n\telse {\n\t\tv = fmt<<6 | chunk_id;\n\n\t\terr |= mbuf_write_u8(mb, v);\n\t}\n\n\treturn err;\n}\n\n\nstatic int decode_basic_hdr(struct rtmp_header *hdr, struct mbuf *mb)\n{\n\tuint8_t cs_id;\n\tuint8_t v;\n\n\tif (mbuf_get_left(mb) < 1)\n\t\treturn ENODATA;\n\n\tv = mbuf_read_u8(mb);\n\n\thdr->format = v>>6;\n\n\tcs_id = v & 0x3f;\n\n\tswitch (cs_id) {\n\n\tcase 0:\n\t\tif (mbuf_get_left(mb) < 1)\n\t\t\treturn ENODATA;\n\n\t\thdr->chunk_id = mbuf_read_u8(mb) + RTMP_CHUNK_OFFSET;\n\t\tbreak;\n\n\tcase 1:\n\t\tif (mbuf_get_left(mb) < 2)\n\t\t\treturn ENODATA;\n\n\t\thdr->chunk_id = ntohs(mbuf_read_u16(mb)) + RTMP_CHUNK_OFFSET;\n\t\tbreak;\n\n\tdefault:\n\t\thdr->chunk_id = cs_id;\n\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\n\nstatic uint32_t ts_24(uint32_t ts)\n{\n\treturn ts >= TIMESTAMP_24MAX ? TIMESTAMP_24MAX : ts;\n}\n\n\nstatic uint32_t ts_ext(uint32_t ts)\n{\n\treturn ts >= TIMESTAMP_24MAX ? ts : 0;\n}\n\n\nint rtmp_header_encode(struct mbuf *mb, struct rtmp_header *hdr)\n{\n\tint err = 0;\n\n\tif (!mb || !hdr)\n\t\treturn EINVAL;\n\n\terr = encode_basic_hdr(mb, hdr->format, hdr->chunk_id);\n\tif (err)\n\t\treturn err;\n\n\tswitch (hdr->format) {\n\n\tcase 0:\n\t\thdr->timestamp_ext = ts_ext(hdr->timestamp);\n\n\t\terr |= mbuf_write_u24_hton(mb, ts_24(hdr->timestamp));\n\t\terr |= mbuf_write_u24_hton(mb, hdr->length);\n\t\terr |= mbuf_write_u8(mb, hdr->type_id);\n\t\terr |= mbuf_write_u32(mb, sys_htoll(hdr->stream_id));\n\t\tbreak;\n\n\tcase 1:\n\t\thdr->timestamp_ext = ts_ext(hdr->timestamp_delta);\n\n\t\terr |= mbuf_write_u24_hton(mb, ts_24(hdr->timestamp_delta));\n\t\terr |= mbuf_write_u24_hton(mb, hdr->length);\n\t\terr |= mbuf_write_u8(mb, hdr->type_id);\n\t\tbreak;\n\n\tcase 2:\n\t\thdr->timestamp_ext = ts_ext(hdr->timestamp_delta);\n\n\t\terr |= mbuf_write_u24_hton(mb, ts_24(hdr->timestamp_delta));\n\t\tbreak;\n\n\tcase 3:\n\t\tbreak;\n\t}\n\n\tif (hdr->timestamp_ext) {\n\t\terr |= mbuf_write_u32(mb, htonl(hdr->timestamp_ext));\n\t}\n\n\treturn err;\n}\n\n\nint rtmp_header_decode(struct rtmp_header *hdr, struct mbuf *mb)\n{\n\tuint32_t *timestamp_ext = NULL;\n\tint err;\n\n\tif (!hdr || !mb)\n\t\treturn EINVAL;\n\n\tmemset(hdr, 0, sizeof(*hdr));\n\n\terr = decode_basic_hdr(hdr, mb);\n\tif (err)\n\t\treturn err;\n\n\tswitch (hdr->format) {\n\n\tcase 0:\n\t\tif (mbuf_get_left(mb) < 11)\n\t\t\treturn ENODATA;\n\n\t\thdr->timestamp = mbuf_read_u24_ntoh(mb);\n\t\thdr->length    = mbuf_read_u24_ntoh(mb);\n\t\thdr->type_id   = mbuf_read_u8(mb);\n\t\thdr->stream_id = sys_ltohl(mbuf_read_u32(mb));\n\t\tbreak;\n\n\tcase 1:\n\t\tif (mbuf_get_left(mb) < 7)\n\t\t\treturn ENODATA;\n\n\t\thdr->timestamp_delta = mbuf_read_u24_ntoh(mb);\n\t\thdr->length          = mbuf_read_u24_ntoh(mb);\n\t\thdr->type_id         = mbuf_read_u8(mb);\n\t\tbreak;\n\n\tcase 2:\n\t\tif (mbuf_get_left(mb) < 3)\n\t\t\treturn ENODATA;\n\n\t\thdr->timestamp_delta = mbuf_read_u24_ntoh(mb);\n\t\tbreak;\n\n\tcase 3:\n\t\t/* no payload */\n\t\tbreak;\n\t}\n\n\tif (hdr->timestamp == TIMESTAMP_24MAX)\n\t\ttimestamp_ext = &hdr->timestamp;\n\telse if (hdr->timestamp_delta == TIMESTAMP_24MAX)\n\t\ttimestamp_ext = &hdr->timestamp_delta;\n\n\tif (timestamp_ext) {\n\t\tif (mbuf_get_left(mb) < 4)\n\t\t\treturn ENODATA;\n\n\t\t*timestamp_ext = ntohl(mbuf_read_u32(mb));\n\t\thdr->ext_ts = true;\n\t}\n\n\treturn 0;\n}\n\n\nint rtmp_header_print(struct re_printf *pf, const struct rtmp_header *hdr)\n{\n\tif (!hdr)\n\t\treturn 0;\n\n\treturn re_hprintf(pf,\n\t\t\t  \"fmt %u, chunk %u, \"\n\t\t\t  \"timestamp %5u, ts_delta %2u,\"\n\t\t\t  \" len %3u, type %2u (%-14s) stream_id %u\",\n\t\t\t  hdr->format, hdr->chunk_id, hdr->timestamp,\n\t\t\t  hdr->timestamp_delta, hdr->length, hdr->type_id,\n\t\t\t  rtmp_packet_type_name(hdr->type_id), hdr->stream_id);\n}\n\n\nconst char *rtmp_packet_type_name(enum rtmp_packet_type type)\n{\n\tswitch (type) {\n\n\tcase RTMP_TYPE_SET_CHUNK_SIZE:    return \"Set Chunk Size\";\n\tcase RTMP_TYPE_ACKNOWLEDGEMENT:   return \"Acknowledgement\";\n\tcase RTMP_TYPE_USER_CONTROL_MSG:  return \"User Control Message\";\n\tcase RTMP_TYPE_WINDOW_ACK_SIZE:   return \"Window Acknowledgement Size\";\n\tcase RTMP_TYPE_SET_PEER_BANDWIDTH:return \"Set Peer Bandwidth\";\n\tcase RTMP_TYPE_AUDIO:             return \"Audio Message\";\n\tcase RTMP_TYPE_VIDEO:             return \"Video Message\";\n\tcase RTMP_TYPE_DATA:              return \"Data Message\";\n\tcase RTMP_TYPE_AMF0:              return \"AMF\";\n\tdefault: return \"?\";\n\t}\n}\n"
  },
  {
    "path": "src/rtmp/rtmp.h",
    "content": "/**\n * @file rtmp.h  Real Time Messaging Protocol (RTMP) -- Internal API\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nenum {\n\tRTMP_PROTOCOL_VERSION  = 3,\n\tRTMP_DEFAULT_CHUNKSIZE = 128,\n\tRTMP_HANDSHAKE_SIZE    = 1536,\n\tRTMP_MESSAGE_LEN_MAX   = 524288,\n};\n\n/* Chunk IDs */\nenum {\n\tRTMP_CHUNK_ID_CONTROL  = 2,\n\tRTMP_CHUNK_ID_CONN     = 3,\n};\n\n/** Defines the RTMP Handshake State */\nenum rtmp_handshake_state {\n\tRTMP_STATE_UNINITIALIZED = 0,\n\tRTMP_STATE_VERSION_SENT,\n\tRTMP_STATE_ACK_SENT,\n\tRTMP_STATE_HANDSHAKE_DONE\n};\n\n/**\n * Defines an RTMP Connection\n */\nstruct rtmp_conn {\n\tstruct list streaml;\n\tstruct rtmp_dechunker *dechunk;\n\tstruct tcp_conn *tc;\n\tstruct tls_conn *sc;\n\tstruct mbuf *mb;                        /* TCP reassembly buffer */\n\tenum rtmp_handshake_state state;\n\tsize_t total_bytes;\n\tsize_t last_ack;\n\tuint32_t window_ack_size;\n\tuint32_t send_chunk_size;\n\tunsigned chunk_id_counter;\n\tbool is_client;\n\tbool connected;\n\trtmp_estab_h *estabh;\n\trtmp_command_h *cmdh;\n\trtmp_close_h *closeh;\n\tvoid *arg;\n\n\t/* client specific: */\n\tstruct dnsc *dnsc;\n\tstruct dns_query *dnsq4;\n\tstruct dns_query *dnsq6;\n\tstruct list ctransl;\n\tstruct sa srvv[16];\n\tstruct tls *tls;\n\tunsigned srvc;\n\tuint64_t tid_counter;\n\tuint16_t port;\n\tchar *app;\n\tchar *uri;\n\tchar *stream;\n\tchar *host;\n};\n\n/**\n * Defines an RTMP Stream\n */\nstruct rtmp_stream {\n\tstruct le le;\n\tconst struct rtmp_conn *conn;    /**< Pointer to parent connection */\n\tbool created;\n\tuint32_t stream_id;\n\tunsigned chunk_id_audio;\n\tunsigned chunk_id_video;\n\tunsigned chunk_id_data;\n\trtmp_audio_h *auh;\n\trtmp_video_h *vidh;\n\trtmp_command_h *datah;\n\trtmp_command_h *cmdh;\n\trtmp_resp_h *resph;\n\trtmp_control_h *ctrlh;\n\tvoid *arg;\n};\n\nstruct rtmp_header {\n\tunsigned format:2;           /* type 0-3 */\n\tuint32_t chunk_id;           /* from 3-65599 */\n\n\tuint32_t timestamp;          /* 24-bit or 32-bit */\n\tuint32_t timestamp_delta;    /* 24-bit */\n\tuint32_t timestamp_ext;\n\tuint32_t length;             /* 24-bit */\n\tuint8_t type_id;             /* enum rtmp_packet_type */\n\tuint32_t stream_id;\n\tbool ext_ts;\n};\n\n\n/* Command */\n\nint rtmp_command_header_encode(struct mbuf *mb, const char *name,\n\t\t\t       uint64_t tid);\n\n/* Connection */\n\nint rtmp_conn_send_msg(const struct rtmp_conn *conn, unsigned format,\n\t\t       uint32_t chunk_id, uint32_t timestamp,\n\t\t       uint32_t timestamp_delta, uint8_t msg_type_id,\n\t\t       uint32_t msg_stream_id,\n\t\t       const uint8_t *payload, size_t payload_len);\nint rtmp_send_amf_command(const struct rtmp_conn *conn,\n\t\t\t  unsigned format, uint32_t chunk_id,\n\t\t\t  uint8_t type_id,\n\t\t\t  uint32_t msg_stream_id,\n\t\t\t  const uint8_t *cmd, size_t len);\nunsigned rtmp_conn_assign_chunkid(struct rtmp_conn *conn);\nuint64_t rtmp_conn_assign_tid(struct rtmp_conn *conn);\n\n\n/* Client Transaction */\n\n\nstruct rtmp_ctrans;\n\nint  rtmp_ctrans_response(const struct list *ctransl,\n\t\t\t  const struct odict *msg);\n\n\n/*\n * RTMP Chunk\n */\n\nint rtmp_chunker(unsigned format, uint32_t chunk_id,\n\t\t uint32_t timestamp, uint32_t timestamp_delta,\n\t\t uint8_t msg_type_id, uint32_t msg_stream_id,\n\t\t const uint8_t *payload, size_t payload_len,\n\t\t size_t max_chunk_sz, struct tcp_conn *tc);\n\n\n/*\n * RTMP Header\n */\n\nint  rtmp_header_encode(struct mbuf *mb, struct rtmp_header *hdr);\nint  rtmp_header_decode(struct rtmp_header *hdr, struct mbuf *mb);\nint  rtmp_header_print(struct re_printf *pf, const struct rtmp_header *hdr);\nconst char *rtmp_packet_type_name(enum rtmp_packet_type type);\n\n\n/*\n * RTMP De-chunker\n */\n\nstruct rtmp_dechunker;\n\ntypedef int (rtmp_dechunk_h)(const struct rtmp_header *hdr,\n\t\t\t     struct mbuf *mb, void *arg);\n\nint  rtmp_dechunker_alloc(struct rtmp_dechunker **rdp, size_t chunk_sz,\n\t\t\t  rtmp_dechunk_h *chunkh, void *arg);\nint  rtmp_dechunker_receive(struct rtmp_dechunker *rd, struct mbuf *mb);\nvoid rtmp_dechunker_set_chunksize(struct rtmp_dechunker *rd, size_t chunk_sz);\nint  rtmp_dechunker_debug(struct re_printf *pf,\n\t\t\t  const struct rtmp_dechunker *rd);\n\n\n/*\n * AMF (Action Message Format)\n */\n\nint rtmp_amf_encode_number(struct mbuf *mb, double val);\nint rtmp_amf_encode_boolean(struct mbuf *mb, bool boolean);\nint rtmp_amf_encode_string(struct mbuf *mb, const char *str);\nint rtmp_amf_encode_null(struct mbuf *mb);\nint rtmp_amf_vencode_object(struct mbuf *mb, enum rtmp_amf_type container,\n\t\t\t    unsigned propc, va_list *ap);\n\nint rtmp_amf_decode(struct odict **msgp, struct mbuf *mb);\n"
  },
  {
    "path": "src/rtmp/stream.c",
    "content": "/**\n * @file rtmp/stream.c  Real Time Messaging Protocol (RTMP) -- NetStream\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_tcp.h>\n#include <re_sys.h>\n#include <re_odict.h>\n#include <re_rtmp.h>\n#include \"rtmp.h\"\n\n\nstatic void destructor(void *data)\n{\n\tstruct rtmp_stream *strm = data;\n\n\tlist_unlink(&strm->le);\n\n\tif (strm->created) {\n\n\t\trtmp_amf_command(strm->conn, 0, \"deleteStream\",\n\t\t\t\t 3,\n\t\t\t\tRTMP_AMF_TYPE_NUMBER, 0.0,\n\t\t\t\tRTMP_AMF_TYPE_NULL,\n\t\t\t\tRTMP_AMF_TYPE_NUMBER, (double)strm->stream_id);\n\t}\n}\n\n\n/**\n * Allocate a new RTMP Stream object\n *\n * @param strmp     Pointer to allocated RTMP Stream\n * @param conn      RTMP Connection\n * @param stream_id Stream id\n * @param cmdh      Command handler\n * @param ctrlh     Control handler\n * @param auh       Audio handler\n * @param vidh      Video handler\n * @param datah     Data handler\n * @param arg       Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtmp_stream_alloc(struct rtmp_stream **strmp, struct rtmp_conn *conn,\n\t\t      uint32_t stream_id, rtmp_command_h *cmdh,\n\t\t      rtmp_control_h *ctrlh, rtmp_audio_h *auh,\n\t\t      rtmp_video_h *vidh, rtmp_command_h *datah,\n\t\t      void *arg)\n{\n\tstruct rtmp_stream *strm;\n\n\tif (!strmp || !conn)\n\t\treturn EINVAL;\n\n\tstrm = mem_zalloc(sizeof(*strm), destructor);\n\tif (!strm)\n\t\treturn ENOMEM;\n\n\tstrm->conn      = conn;\n\tstrm->stream_id = stream_id;\n\n\tstrm->cmdh   = cmdh;\n\tstrm->ctrlh  = ctrlh;\n\tstrm->auh    = auh;\n\tstrm->vidh   = vidh;\n\tstrm->datah  = datah;\n\tstrm->arg    = arg;\n\n\tstrm->chunk_id_audio = rtmp_conn_assign_chunkid(conn);\n\tstrm->chunk_id_video = rtmp_conn_assign_chunkid(conn);\n\tstrm->chunk_id_data  = rtmp_conn_assign_chunkid(conn);\n\n\tlist_append(&conn->streaml, &strm->le, strm);\n\n\t*strmp = strm;\n\n\treturn 0;\n}\n\n\nstatic void createstream_handler(bool success, const struct odict *msg,\n\t\t\t\t void *arg)\n{\n\tstruct rtmp_stream *strm = arg;\n\tuint64_t num;\n\n\tif (!success)\n\t\tgoto out;\n\n\tif (!odict_get_number(msg, &num, \"3\")) {\n\t\tsuccess = false;\n\t\tgoto out;\n\t}\n\n\tstrm->stream_id = (uint32_t)num;\n\tif (strm->stream_id == 0) {\n\t\tsuccess = false;\n\t\tgoto out;\n\t}\n\n\tstrm->created = true;\n\n out:\n\tif (strm->resph)\n\t\tstrm->resph(success, msg, strm->arg);\n}\n\n\n/**\n * Create a new RTMP Stream by sending \"createStream\" to the RTMP Server.\n *\n * @param strmp     Pointer to allocated RTMP Stream\n * @param conn      RTMP Connection\n * @param resph     RTMP Response handler\n * @param cmdh      Command handler\n * @param ctrlh     Control handler\n * @param auh       Audio handler\n * @param vidh      Video handler\n * @param datah     Data handler\n * @param arg       Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtmp_stream_create(struct rtmp_stream **strmp, struct rtmp_conn *conn,\n\t\t       rtmp_resp_h *resph, rtmp_command_h *cmdh,\n\t\t       rtmp_control_h *ctrlh, rtmp_audio_h *auh,\n\t\t       rtmp_video_h *vidh, rtmp_command_h *datah,\n\t\t       void *arg)\n{\n\tstruct rtmp_stream *strm;\n\tint err;\n\n\tif (!strmp || !conn)\n\t\treturn EINVAL;\n\n\terr = rtmp_stream_alloc(&strm, conn, (uint32_t)-1,\n\t\t\t\tcmdh, ctrlh, auh, vidh, datah, arg);\n\tif (err)\n\t\treturn err;\n\n\tstrm->resph = resph;\n\n\terr = rtmp_amf_request(conn, 0,\n\t\t\t       \"createStream\", createstream_handler, strm,\n\t\t\t       1,\n\t\t\t       RTMP_AMF_TYPE_NULL);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(strm);\n\telse\n\t\t*strmp = strm;\n\n\treturn err;\n}\n\n\n/**\n * Start playing an RTMP Stream by sending \"play\" to the RTMP Server\n *\n * @param strm RTMP Stream\n * @param name Stream name\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtmp_play(struct rtmp_stream *strm, const char *name)\n{\n\tif (!strm || !name)\n\t\treturn EINVAL;\n\n\treturn rtmp_amf_command(strm->conn, strm->stream_id, \"play\",\n\t\t\t\t4,\n\t\t\t\tRTMP_AMF_TYPE_NUMBER, 0.0,\n\t\t\t\tRTMP_AMF_TYPE_NULL,\n\t\t\t\tRTMP_AMF_TYPE_STRING, name,\n\t\t\t\tRTMP_AMF_TYPE_NUMBER, -2000.0);\n}\n\n\n/**\n * Start publishing an RTMP Stream by sending \"publish\" to the RTMP Server\n *\n * @param strm RTMP Stream\n * @param name Stream name\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtmp_publish(struct rtmp_stream *strm, const char *name)\n{\n\tif (!strm || !name)\n\t\treturn EINVAL;\n\n\treturn rtmp_amf_command(strm->conn, strm->stream_id, \"publish\",\n\t\t\t\t4,\n\t\t\t\tRTMP_AMF_TYPE_NUMBER, 0.0,\n\t\t\t\tRTMP_AMF_TYPE_NULL,\n\t\t\t\tRTMP_AMF_TYPE_STRING, name,\n\t\t\t\tRTMP_AMF_TYPE_STRING, \"live\");\n}\n\n\n/**\n * Send metadata on the stream to the RTMP Server\n *\n * @param strm RTMP Stream\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtmp_meta(struct rtmp_stream *strm)\n{\n\tif (!strm)\n\t\treturn EINVAL;\n\n\treturn rtmp_amf_data(strm->conn, strm->stream_id, \"@setDataFrame\",\n\t\t\t     2,\n\t\t\t     RTMP_AMF_TYPE_STRING, \"onMetaData\",\n\t\t\t     RTMP_AMF_TYPE_ECMA_ARRAY, 2,\n\t\t\t         RTMP_AMF_TYPE_NUMBER, \"audiocodecid\", 10.0,\n\t\t\t         RTMP_AMF_TYPE_NUMBER, \"videocodecid\",  7.0);\n}\n\n\n/**\n * Send audio packet on the RTMP Stream\n *\n * @param strm      RTMP Stream\n * @param timestamp Timestamp in [milliseconds]\n * @param pld       Audio payload\n * @param len       Payload length\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtmp_send_audio(struct rtmp_stream *strm, uint32_t timestamp,\n\t\t    const uint8_t *pld, size_t len)\n{\n\tuint32_t chunk_id;\n\n\tif (!strm || !pld || !len)\n\t\treturn EINVAL;\n\n\tchunk_id = strm->chunk_id_audio;\n\n\treturn rtmp_conn_send_msg(strm->conn, 0, chunk_id, timestamp, 0,\n\t\t\t\t  RTMP_TYPE_AUDIO, strm->stream_id, pld, len);\n}\n\n\n/**\n * Send video packet on the RTMP Stream\n *\n * @param strm      RTMP Stream\n * @param timestamp Timestamp in [milliseconds]\n * @param pld       Video payload\n * @param len       Payload length\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtmp_send_video(struct rtmp_stream *strm, uint32_t timestamp,\n\t\t    const uint8_t *pld, size_t len)\n{\n\tuint32_t chunk_id;\n\n\tif (!strm || !pld || !len)\n\t\treturn EINVAL;\n\n\tchunk_id = strm->chunk_id_video;\n\n\treturn rtmp_conn_send_msg(strm->conn, 0, chunk_id, timestamp, 0,\n\t\t\t\t  RTMP_TYPE_VIDEO, strm->stream_id, pld, len);\n}\n\n\n/**\n * Find an RTMP Stream by stream id\n *\n * @param conn      RTMP Connection\n * @param stream_id Stream id\n *\n * @return RTMP Stream if found, or NULL if not found\n */\nstruct rtmp_stream *rtmp_stream_find(const struct rtmp_conn *conn,\n\t\t\t\t     uint32_t stream_id)\n{\n\tstruct le *le;\n\n\tif (!conn)\n\t\treturn NULL;\n\n\tfor (le = list_head(&conn->streaml); le; le = le->next) {\n\n\t\tstruct rtmp_stream *strm = le->data;\n\n\t\tif (stream_id == strm->stream_id)\n\t\t\treturn strm;\n\t}\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/rtp/fb.c",
    "content": "/**\n * @file fb.c Real-time Transport Control Protocol (RTCP)-Based Feedback\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sys.h>\n#include <re_sa.h>\n#include <re_rtp.h>\n#include \"rtcp.h\"\n\n\n#define DEBUG_MODULE \"rtcp_pb\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tGNACK_SIZE = 4,\n\tFIR_SIZE = 8,\n\tSLI_SIZE   = 4\n};\n\n\n/* Encode functions */\n\n\n/**\n * Encode an RTCP Generic NACK (GNACK) message\n *\n * @param mb  Buffer to encode into\n * @param pid Packet ID\n * @param blp Bitmask of following lost packets (BLP)\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_rtpfb_gnack_encode(struct mbuf *mb, uint16_t pid, uint16_t blp)\n{\n\tint err;\n\terr  = mbuf_write_u16(mb, htons(pid));\n\terr |= mbuf_write_u16(mb, htons(blp));\n\treturn err;\n}\n\n\n/**\n * Encode an RTCP Transport-wide congestion control Feedback Message\n *\n * @param mb   Buffer to encode into\n * @param twcc Transport-wide CC message\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_rtpfb_twcc_encode(struct mbuf *mb, struct twcc *twcc)\n{\n\tint err;\n\n\tuint32_t reftime_fbcount = twcc->fbcount; /* 8 bit */\n\treftime_fbcount |= twcc->reftime << 8;\t  /* 24 bit */\n\n\terr = mbuf_write_u16(mb, htons(twcc->seq));\n\terr |= mbuf_write_u16(mb, htons(twcc->count));\n\terr |= mbuf_write_u32(mb, htonl(reftime_fbcount));\n\terr |= mbuf_write_mem(mb, mbuf_buf(twcc->chunks),\n\t\t\t      mbuf_get_left(twcc->chunks));\n\terr |= mbuf_write_mem(mb, mbuf_buf(twcc->deltas),\n\t\t\t      mbuf_get_left(twcc->deltas));\n\n\treturn err;\n}\n\n\n/* Decode functions */\n\n\n/**\n * Decode an RTCP Transport-wide congestion control Feedback Message\n *\n * @param mb  Buffer to decode\n * @param msg transport-cc struct to decode into\n * @param n   length of the RTCP packet in 32bit words minus one\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_rtpfb_twcc_decode(struct mbuf *mb, struct twcc *msg, int n)\n{\n\tsize_t j, sz;\n\n\tif (!msg)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < 8)\n\t\treturn EBADMSG;\n\n\tmsg->seq = ntohs(mbuf_read_u16(mb));\n\tmsg->count = ntohs(mbuf_read_u16(mb));\n\tif (msg->count == 0 || msg->count > 32768)\n\t\treturn EBADMSG;\n\n\tmsg->reftime = ntohl(mbuf_read_u32(mb));\n\tmsg->fbcount = msg->reftime & 0xff;\n\tmsg->reftime >>= 8;\n\n\tmsg->chunks = mbuf_alloc_ref(mb);\n\tif (!msg->chunks)\n\t\treturn ENOMEM;\n\n\tmsg->chunks->end = msg->chunks->pos;\n\tsz = 0;\n\tfor (size_t i = msg->count; i > 0;) {\n\t\tuint16_t chunk;\n\n\t\tif (mbuf_get_left(mb) < 2)\n\t\t\treturn EBADMSG;\n\t\tchunk  = ntohs(mbuf_read_u16(mb));\n\t\tmsg->chunks->end += 2;\n\t\tif (chunk & 0x8000) {\n\t\t\t/* status vector chunk */\n\t\t\tif (chunk & 0x4000) {\n\t\t\t\tfor (j = 0; j < i && j < 7; j++)\n\t\t\t\t\tsz += chunk >> (2 * (7 - 1 - j))\n\t\t\t\t\t\t& 0x03;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfor (j = 0; j < i && j < 14; j++)\n\t\t\t\t\tsz += (chunk >> (14 - 1 - j)) & 0x01;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t/* run length chunk */\n\t\t\tfor (j = 0; j < i && j < (chunk & 0x1fffu); j++)\n\t\t\t\tsz += (chunk >> 13) & 0x03;\n\t\t}\n\t\ti -= j;\n\t}\n\tif (mbuf_get_left(mb) < sz)\n\t\treturn EBADMSG;\n\n\tmsg->deltas = mbuf_alloc_ref(mb);\n\tif (!msg->deltas)\n\t\treturn ENOMEM;\n\n\tmsg->deltas->end = msg->deltas->pos + sz;\n\n\tsz = n * sizeof(uint32_t) - 8 - mbuf_get_left(msg->chunks);\n\tif (mbuf_get_left(mb) < sz)\n\t\treturn EBADMSG;\n\n\tmbuf_advance(mb, sz);\n\n\treturn 0;\n}\n\n/**\n * Decode an RTCP Transport Layer Feedback Message\n *\n * @param mb  Buffer to decode\n * @param msg RTCP Message to decode into\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_rtpfb_decode(struct mbuf *mb, struct rtcp_msg *msg)\n{\n\tsize_t i, sz;\n\tint err;\n\n\tif (!msg)\n\t\treturn EINVAL;\n\n\tswitch (msg->hdr.count) {\n\n\tcase RTCP_RTPFB_GNACK:\n\t\tsz = msg->r.fb.n * sizeof(*msg->r.fb.fci.gnackv);\n\t\tmsg->r.fb.fci.gnackv = mem_alloc(sz, NULL);\n\t\tif (!msg->r.fb.fci.gnackv)\n\t\t\treturn ENOMEM;\n\n\t\tif (mbuf_get_left(mb) < msg->r.fb.n * GNACK_SIZE)\n\t\t\treturn EBADMSG;\n\t\tfor (i=0; i<msg->r.fb.n; i++) {\n\t\t\tmsg->r.fb.fci.gnackv[i].pid = ntohs(mbuf_read_u16(mb));\n\t\t\tmsg->r.fb.fci.gnackv[i].blp = ntohs(mbuf_read_u16(mb));\n\t\t}\n\t\tbreak;\n\n\tcase RTCP_RTPFB_TWCC:\n\t\tif (mbuf_get_left(mb) < 8)\n\t\t\treturn EBADMSG;\n\t\tmsg->r.fb.fci.twccv = mem_zalloc(sizeof(*msg->r.fb.fci.twccv),\n\t\t\tNULL);\n\t\tif (!msg->r.fb.fci.twccv)\n\t\t\treturn ENOMEM;\n\t\terr = rtcp_rtpfb_twcc_decode(mb, msg->r.fb.fci.twccv,\n\t\t\tmsg->r.fb.n);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_NOTICE(\"unknown RTPFB fmt %d\\n\", msg->hdr.count);\n\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Decode an RTCP Payload-Specific Feedback Message\n *\n * @param mb  Buffer to decode\n * @param msg RTCP Message to decode into\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_psfb_decode(struct mbuf *mb, struct rtcp_msg *msg)\n{\n\tsize_t i, sz;\n\n\tif (!msg)\n\t\treturn EINVAL;\n\n\tswitch (msg->hdr.count) {\n\n\tcase RTCP_PSFB_PLI:\n\t\t/* no params */\n\t\tbreak;\n\n\tcase RTCP_PSFB_SLI:\n\t\tsz = msg->r.fb.n * sizeof(*msg->r.fb.fci.sliv);\n\t\tmsg->r.fb.fci.sliv = mem_alloc(sz, NULL);\n\t\tif (!msg->r.fb.fci.sliv)\n\t\t\treturn ENOMEM;\n\n\t\tif (mbuf_get_left(mb) < msg->r.fb.n * SLI_SIZE)\n\t\t\treturn EBADMSG;\n\t\tfor (i=0; i<msg->r.fb.n; i++) {\n\t\t\tconst uint32_t v = ntohl(mbuf_read_u32(mb));\n\n\t\t\tmsg->r.fb.fci.sliv[i].first  = v>>19 & 0x1fff;\n\t\t\tmsg->r.fb.fci.sliv[i].number = v>> 6 & 0x1fff;\n\t\t\tmsg->r.fb.fci.sliv[i].picid  = v>> 0 & 0x003f;\n\t\t}\n\t\tbreak;\n\n\tcase RTCP_PSFB_AFB:\n\t\tsz = msg->r.fb.n * 4;\n\n\t\tif (mbuf_get_left(mb) < sz)\n\t\t\treturn EBADMSG;\n\n\t\tmsg->r.fb.fci.afb = mbuf_alloc_ref(mb);\n\t\tif (!msg->r.fb.fci.afb)\n\t\t\treturn ENOMEM;\n\n\t\tmsg->r.fb.fci.afb->end = msg->r.fb.fci.afb->pos + sz;\n\t\tmbuf_advance(mb, sz);\n\t\tbreak;\n\n\tcase RTCP_PSFB_FIR:\n\t\tmsg->r.fb.n /= 2u; /* each FCI entry size is 2 32-bit words */\n\t\tsz = msg->r.fb.n * sizeof(*msg->r.fb.fci.firv);\n\t\tmsg->r.fb.fci.firv = mem_alloc(sz, NULL);\n\t\tif (!msg->r.fb.fci.firv)\n\t\t\treturn ENOMEM;\n\n\t\tif (mbuf_get_left(mb) < msg->r.fb.n * FIR_SIZE)\n\t\t\treturn EBADMSG;\n\t\tfor (i=0; i<msg->r.fb.n; i++) {\n\t\t\tmsg->r.fb.fci.firv[i].ssrc = ntohl(mbuf_read_u32(mb));\n\t\t\tmsg->r.fb.fci.firv[i].seq_n = mbuf_read_u8(mb);\n\t\t\tmbuf_advance(mb, 3);\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_NOTICE(\"unknown PSFB fmt %d\\n\", msg->hdr.count);\n\t\tbreak;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/rtp/member.c",
    "content": "/**\n * @file member.c  Real-time Transport Control Protocol member\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_sa.h>\n#include <re_rtp.h>\n#include \"rtcp.h\"\n\n\nstatic void destructor(void *data)\n{\n\tstruct rtp_member *mbr = data;\n\n\thash_unlink(&mbr->le);\n\tmem_deref(mbr->s);\n}\n\n\nstruct rtp_member *rtp_member_add(struct hash *ht, uint32_t src)\n{\n\tstruct rtp_member *mbr;\n\n\tmbr = mem_zalloc(sizeof(*mbr), destructor);\n\tif (!mbr)\n\t\treturn NULL;\n\n\thash_append(ht, src, &mbr->le, mbr);\n\tmbr->src = src;\n\n\treturn mbr;\n}\n\n\nstatic bool hash_cmp_handler(struct le *le, void *arg)\n{\n\tconst struct rtp_member *mbr = le->data;\n\n\treturn mbr->src == *(uint32_t *)arg;\n}\n\n\nstruct rtp_member *rtp_member_find(struct hash *ht, uint32_t src)\n{\n\treturn list_ledata(hash_lookup(ht, src, hash_cmp_handler, &src));\n}\n"
  },
  {
    "path": "src/rtp/ntp.c",
    "content": "/**\n * @file ntp.c  NTP Routines\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#ifdef HAVE_SYS_TIME_H\n#include <sys/time.h>\n#endif\n#include <time.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_rtp.h>\n#include <re_tmr.h>\n#include \"rtcp.h\"\n\n\n/*\n * Unix time:  seconds relative to 0h January 1, 1970\n * NTP time:   seconds relative to 0h UTC on 1 January 1900\n */\n\n\n/* Number of seconds from 1900 to 1970 */\n#define UNIX_NTP_OFFSET 0x83aa7e80\n\n\n/**\n * Convert from Unix time to NTP time\n *\n * @param ntp NTP time to convert to (output)\n * @param tv  Unix time to convert from (input)\n */\nvoid unix2ntp(struct rtp_ntp_time *ntp, const struct timeval *tv)\n{\n\tntp->hi = (uint32_t)(tv->tv_sec + UNIX_NTP_OFFSET);\n\tntp->lo = (uint32_t)((double)tv->tv_usec*(double)(1LL<<32)*1.0e-6);\n}\n\n\n/**\n * Obtain the current wallclock time in NTP and jiffies formats\n *\n * @param ntp NTP time\n * @param jfs_rt Microseconds since UNIX epoch. Optional, may be NULL.\n */\nvoid ntp_time_get(struct rtp_ntp_time *ntp, uint64_t *jfs_rt)\n{\n#if defined(WIN32)\n\t/* timeval::tv_sec on Windows is 32-bit, and it doesn't\n\t * define suseconds_t */\n\ttypedef long tv_sec_t;\n\ttypedef long tv_usec_t;\n#else\n\ttypedef time_t tv_sec_t;\n\ttypedef suseconds_t tv_usec_t;\n#endif\n\n\tstruct timeval tv;\n\tuint64_t jfs = tmr_jiffies_rt_usec();\n\tif (jfs_rt)\n\t\t*jfs_rt = jfs;\n\n\ttv.tv_sec  = (tv_sec_t)(jfs / 1000000u);\n\ttv.tv_usec = (tv_usec_t)(jfs % 1000000u);\n\tunix2ntp(ntp, &tv);\n}\n\n\n/**\n * Convert NTP time to middle 32-bits (compact representation)\n *\n * @param ntp NTP time\n *\n * @return NTP time in compact representation\n */\nuint32_t ntp_compact(const struct rtp_ntp_time *ntp)\n{\n\treturn ntp ? ((ntp->hi & 0xffff) << 16 | (ntp->lo >> 16)) : 0;\n}\n\n\n/**\n * Convert NTP compact representation to microseconds\n *\n * @param ntpc  NTP time in compact representation\n *\n * @return NTP time in microseconds\n */\nuint64_t ntp_compact2us(uint32_t ntpc)\n{\n\tconst uint32_t hi = (ntpc >> 16) & 0xffff;\n\tconst uint32_t lo = (ntpc & 0xffff) << 16;\n\n\treturn (1000000ULL * hi) + ((1000000ULL * lo) >> 32);\n}\n"
  },
  {
    "path": "src/rtp/pkt.c",
    "content": "/**\n * @file pkt.c  RTCP Packet handling\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sys.h>\n#include <re_sa.h>\n#include <re_rtp.h>\n#include \"rtcp.h\"\n\n\n#define DEBUG_MODULE \"rtcp_pkt\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic void rtcp_destructor(void *data)\n{\n\tstruct rtcp_msg *msg = data;\n\tsize_t i, j;\n\n\tswitch (msg->hdr.pt) {\n\n\tcase RTCP_SR:\n\t\tmem_deref(msg->r.sr.rrv);\n\t\tbreak;\n\n\tcase RTCP_RR:\n\t\tmem_deref(msg->r.rr.rrv);\n\t\tbreak;\n\n\tcase RTCP_SDES:\n\t\tif (!msg->r.sdesv)\n\t\t\tbreak;\n\n\t\tfor (i=0; i<msg->hdr.count; i++) {\n\t\t\tstruct rtcp_sdes *sdes = &msg->r.sdesv[i];\n\n\t\t\tfor (j=0; j<sdes->n; j++) {\n\n\t\t\t\tmem_deref(sdes->itemv[j].data);\n\t\t\t}\n\t\t\tmem_deref(sdes->itemv);\n\t\t}\n\t\tmem_deref(msg->r.sdesv);\n\t\tbreak;\n\n\tcase RTCP_BYE:\n\t\tmem_deref(msg->r.bye.srcv);\n\t\tmem_deref(msg->r.bye.reason);\n\t\tbreak;\n\n\tcase RTCP_APP:\n\t\tmem_deref(msg->r.app.data);\n\t\tbreak;\n\n\tcase RTCP_RTPFB:\n\t\tif (msg->hdr.count == RTCP_RTPFB_TWCC && msg->r.fb.fci.twccv) {\n\t\t\tmem_deref(msg->r.fb.fci.twccv->chunks);\n\t\t\tmem_deref(msg->r.fb.fci.twccv->deltas);\n\t\t}\n\t\t/*@fallthrough@*/\n\n\tcase RTCP_PSFB:\n\t\tmem_deref(msg->r.fb.fci.p);\n\t\tbreak;\n\n\tdefault:\n\t\t/* nothing allocated */\n\t\tbreak;\n\t}\n}\n\n\n/**\n * Encode the RTCP Header\n *\n * @param mb     Buffer to encode into\n * @param count  Number of sub-elements\n * @param type   RTCP Packet type\n * @param length Packet length in words\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_hdr_encode(struct mbuf *mb, uint8_t count, enum rtcp_type type,\n\t\t    uint16_t length)\n{\n\tint err;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\terr  = mbuf_write_u8(mb, RTCP_VERSION<<6 | count);\n\terr |= mbuf_write_u8(mb, type);\n\terr |= mbuf_write_u16(mb, htons(length));\n\n\treturn err;\n}\n\n\n/**\n * Decode the RTCP Header\n *\n * @param mb  Buffer to decode from\n * @param hdr RTCP Header to decode into\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_hdr_decode(struct mbuf *mb, struct rtcp_hdr *hdr)\n{\n\tuint8_t b;\n\n\tif (!hdr)\n\t\treturn EINVAL;\n\tif (mbuf_get_left(mb) < RTCP_HDR_SIZE)\n\t\treturn EBADMSG;\n\n\tb = mbuf_read_u8(mb);\n\thdr->pt = mbuf_read_u8(mb);\n\thdr->length = ntohs(mbuf_read_u16(mb));\n\n\thdr->version = (b >> 6) & 0x3;\n\thdr->p       = (b >> 5) & 0x1;\n\thdr->count   = (b >> 0) & 0x1f;\n\n\treturn 0;\n}\n\n\nint rtcp_vencode(struct mbuf *mb, enum rtcp_type type, uint32_t count,\n\t\t va_list ap)\n{\n\tsize_t i, pos;\n\tuint16_t len;\n\tconst uint8_t *data;\n\tsize_t data_len;\n\tconst uint32_t *srcv;\n\tconst char *reason;\n\trtcp_encode_h *ench;\n\tvoid *arg;\n\tint err = 0;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\tpos = mb->pos;\n\n\t/* Skip header - encoded last */\n\tmb->pos = mb->end = (mb->pos + RTCP_HDR_SIZE);\n\n\tswitch (type) {\n\n\tcase RTCP_SR:\n\t\tfor (i=0; i<6; i++)\n\t\t\terr |= mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));\n\t\tench = va_arg(ap, rtcp_encode_h *);\n\t\targ = va_arg(ap, void *);\n\t\tif (ench)\n\t\t\terr |= ench(mb, arg);\n\t\tbreak;\n\n\tcase RTCP_RR:\n\t\terr = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));\n\t\tench = va_arg(ap, rtcp_encode_h *);\n\t\targ = va_arg(ap, void *);\n\t\tif (ench)\n\t\t\terr |= ench(mb, arg);\n\t\tbreak;\n\n\tcase RTCP_SDES:\n\t\tench = va_arg(ap, rtcp_encode_h *);\n\t\targ = va_arg(ap, void *);\n\t\tif (ench)\n\t\t\terr |= ench(mb, arg);\n\t\tbreak;\n\n\tcase RTCP_BYE:\n\t\tsrcv   = va_arg(ap, uint32_t *);\n\t\treason = va_arg(ap, char *);\n\t\tfor (i=0; i<count && !err; i++) {\n\t\t\terr = mbuf_write_u32(mb, htonl(srcv[i]));\n\t\t}\n\t\tif (reason) {\n\t\t\terr |= mbuf_write_u8(mb, (uint8_t)str_len(reason));\n\t\t\terr |= mbuf_write_str(mb, reason);\n\t\t}\n\t\tbreak;\n\n\tcase RTCP_APP:\n\t\terr  = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));\n\t\terr |= mbuf_write_mem(mb, va_arg(ap, uint8_t *), 4);\n\t\tdata = va_arg(ap, const uint8_t *);\n\t\tdata_len = va_arg(ap, size_t);\n\t\tif (data) {\n\t\t\tif (data_len % 4) {\n\t\t\t\tDEBUG_WARNING(\"not a multiple of 32bits\\n\");\n\t\t\t\treturn EBADMSG;\n\t\t\t}\n\t\t\terr |= mbuf_write_mem(mb, data, data_len);\n\t\t}\n\t\tbreak;\n\n\tcase RTCP_FIR:\n\t\terr  = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));\n\t\tbreak;\n\n\tcase RTCP_NACK:\n\t\terr  = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));\n\t\terr |= mbuf_write_u16(mb, htons(va_arg(ap, uint32_t)));\n\t\terr |= mbuf_write_u16(mb, htons(va_arg(ap, uint32_t)));\n\t\tbreak;\n\n\tcase RTCP_RTPFB:\n\tcase RTCP_PSFB:\n\t\terr  = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));\n\t\terr |= mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));\n\t\tench = va_arg(ap, rtcp_encode_h *);\n\t\targ = va_arg(ap, void *);\n\t\tif (ench)\n\t\t\terr |= ench(mb, arg);\n\t\tbreak;\n\n\tcase RTCP_XR:\n\t\terr = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t)));\n\t\tench = va_arg(ap, rtcp_encode_h *);\n\t\targ = va_arg(ap, void *);\n\t\tif (ench)\n\t\t\terr |= ench(mb, arg);\n\t\tbreak;\n\n\tdefault:\n\t\treturn EINVAL;\n\t}\n\tif (err)\n\t\treturn err;\n\n\t/* padding to 32 bits */\n\twhile ((mb->end - pos) & 0x3)\n\t\terr |= mbuf_write_u8(mb, 0x00);\n\tif (err)\n\t\treturn err;\n\n\t/* Encode RTCP Header */\n\tmb->pos = pos;\n\tlen = (uint16_t)((mb->end - pos - RTCP_HDR_SIZE)/sizeof(uint32_t));\n\terr = rtcp_hdr_encode(mb, count, type, len);\n\tif (err)\n\t\treturn err;\n\n\tmb->pos = mb->end;\n\n\treturn 0;\n}\n\n\n/**\n * Encode an RTCP Packet into a buffer\n *\n * @param mb    Buffer to encode into\n * @param type  RTCP Packet type\n * @param count Packet-specific count\n * @param ...   Variable arguments, type specific\n *\n * Variable arguments for each RTCP type:\n *\n * \\verbatim\n  SR       SSRC of sender\n           NTP Timestamp (MSW)\n           NTP Timestamp (LSW)\n           RTP Timestamp\n           Sender packet count\n           Sender octet count\n           Encode handler for report block\n           Handler argument\n\n  RR       SSRC of sender\n           Encode handler for report block\n           Handler argument\n\n  SDES     Encode handler for SDES chunk\n           Handler argument\n\n  BYE      SSRCs (vector)\n           Reason string (optional)\n\n  APP      SSRC/CSRC\n           name (ASCII)\n           Data\n           Data length\n\n  FIR      SSRC\n\n  NACK     SSRC\n           FSN\n           BLP\n\n  RTPFB    SSRC packet\n           SSRC media\n           Encode handler\n           Handler argument\n\n  PSFB     SSRC packet\n           SSRC media\n           Encode handler\n           Handler argument\n   \\endverbatim\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_encode(struct mbuf *mb, enum rtcp_type type, uint32_t count, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, count);\n\terr = rtcp_vencode(mb, type, count, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Decode one RTCP message from a buffer\n *\n * @param msgp Pointer to allocated RTCP Message\n * @param mb   Buffer to decode from\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_decode(struct rtcp_msg **msgp, struct mbuf *mb)\n{\n\tstruct rtcp_msg *msg = NULL;\n\tsize_t start, i, sz, count, rem;\n\tint err;\n\n\tif (!msgp)\n\t\treturn EINVAL;\n\tif (mbuf_get_left(mb) < RTCP_HDR_SIZE)\n\t\treturn EBADMSG;\n\n\tmsg = mem_zalloc(sizeof(*msg), rtcp_destructor);\n\tif (!msg)\n\t\treturn ENOMEM;\n\n\tstart = mb->pos;\n\n\t/* decode and check header */\n\terr = rtcp_hdr_decode(mb, &msg->hdr);\n\tif (err)\n\t\tgoto out;\n\n\tif (msg->hdr.version != RTCP_VERSION)\n\t\tgoto badmsg;\n\n\t/* check length and remaining */\n\trem = msg->hdr.length * sizeof(uint32_t);\n\tif (mbuf_get_left(mb) < rem)\n\t\tgoto badmsg;\n\n\tcount = msg->hdr.count;\n\n\tswitch (msg->hdr.pt) {\n\n\tcase RTCP_SR:\n\t\tif (mbuf_get_left(mb) < (RTCP_SRC_SIZE + RTCP_SR_SIZE))\n\t\t\tgoto badmsg;\n\t\tmsg->r.sr.ssrc     = ntohl(mbuf_read_u32(mb));\n\t\tmsg->r.sr.ntp_sec  = ntohl(mbuf_read_u32(mb));\n\t\tmsg->r.sr.ntp_frac = ntohl(mbuf_read_u32(mb));\n\t\tmsg->r.sr.rtp_ts   = ntohl(mbuf_read_u32(mb));\n\t\tmsg->r.sr.psent    = ntohl(mbuf_read_u32(mb));\n\t\tmsg->r.sr.osent    = ntohl(mbuf_read_u32(mb));\n\n\t\terr = rtcp_rr_alloc(&msg->r.sr.rrv, count);\n\t\tif (err)\n\t\t\tgoto out;\n\t\tfor (i=0; i<count && !err; i++)\n\t\t\terr = rtcp_rr_decode(mb, &msg->r.sr.rrv[i]);\n\t\tbreak;\n\n\tcase RTCP_RR:\n\t\tif (mbuf_get_left(mb) < RTCP_SRC_SIZE)\n\t\t\tgoto badmsg;\n\t\tmsg->r.rr.ssrc = ntohl(mbuf_read_u32(mb));\n\n\t\terr = rtcp_rr_alloc(&msg->r.rr.rrv, count);\n\t\tif (err)\n\t\t\tgoto out;\n\t\tfor (i=0; i<count && !err; i++)\n\t\t\terr = rtcp_rr_decode(mb, &msg->r.rr.rrv[i]);\n\t\tbreak;\n\n\tcase RTCP_SDES:\n\t\tif (count == 0)\n\t\t\tbreak;\n\n\t\tsz = count * sizeof(*msg->r.sdesv);\n\t\tmsg->r.sdesv = mem_zalloc(sz, NULL);\n\t\tif (!msg->r.sdesv) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\tfor (i=0; i<msg->hdr.count && !err; i++)\n\t\t\terr = rtcp_sdes_decode(mb, &msg->r.sdesv[i]);\n\t\tbreak;\n\n\tcase RTCP_BYE:\n\t\tsz = count * sizeof(*msg->r.bye.srcv);\n\t\tmsg->r.bye.srcv = mem_alloc(sz, NULL);\n\t\tif (!msg->r.bye.srcv) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\t\tif (mbuf_get_left(mb) < sz)\n\t\t\tgoto badmsg;\n\t\tfor (i=0; i<count; i++)\n\t\t\tmsg->r.bye.srcv[i] = ntohl(mbuf_read_u32(mb));\n\n\t\t/* decode reason (optional) */\n\t\tif (rem > count*sizeof(uint32_t)) {\n\t\t\tconst size_t len = mbuf_read_u8(mb);\n\t\t\tif (mbuf_get_left(mb) < len)\n\t\t\t\tgoto badmsg;\n\n\t\t\terr = mbuf_strdup(mb, &msg->r.bye.reason, len);\n\t\t}\n\t\tbreak;\n\n\tcase RTCP_APP:\n\t\tif (mbuf_get_left(mb) < RTCP_APP_SIZE)\n\t\t\tgoto badmsg;\n\t\tmsg->r.app.src = ntohl(mbuf_read_u32(mb));\n\t\t(void)mbuf_read_mem(mb, (uint8_t *)msg->r.app.name,\n\t\t\t\t    sizeof(msg->r.app.name));\n\t\tif (rem > RTCP_APP_SIZE) {\n\t\t\tmsg->r.app.data_len = rem - RTCP_APP_SIZE;\n\t\t\tmsg->r.app.data = mem_alloc(msg->r.app.data_len, NULL);\n\t\t\tif (!msg->r.app.data) {\n\t\t\t\terr = ENOMEM;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tif (mbuf_get_left(mb) < msg->r.app.data_len)\n\t\t\t\tgoto badmsg;\n\t\t\t(void)mbuf_read_mem(mb, msg->r.app.data,\n\t\t\t\t\t    msg->r.app.data_len);\n\t\t}\n\t\tbreak;\n\n\tcase RTCP_FIR:\n\t\tif (mbuf_get_left(mb) < RTCP_FIR_SIZE)\n\t\t\tgoto badmsg;\n\t\tmsg->r.fir.ssrc = ntohl(mbuf_read_u32(mb));\n\t\tbreak;\n\n\tcase RTCP_NACK:\n\t\tif (mbuf_get_left(mb) < RTCP_NACK_SIZE)\n\t\t\tgoto badmsg;\n\t\tmsg->r.nack.ssrc = ntohl(mbuf_read_u32(mb));\n\t\tmsg->r.nack.fsn  = ntohs(mbuf_read_u16(mb));\n\t\tmsg->r.nack.blp  = ntohs(mbuf_read_u16(mb));\n\t\tbreak;\n\n\tcase RTCP_RTPFB:\n\t\tif (mbuf_get_left(mb) < RTCP_FB_SIZE)\n\t\t\tgoto badmsg;\n\n\t\tif (msg->hdr.length < 2)\n\t\t\tgoto badmsg;\n\n\t\tmsg->r.fb.ssrc_packet = ntohl(mbuf_read_u32(mb));\n\t\tmsg->r.fb.ssrc_media = ntohl(mbuf_read_u32(mb));\n\t\tmsg->r.fb.n = msg->hdr.length - 2;\n\n\t\terr = rtcp_rtpfb_decode(mb, msg);\n\t\tbreak;\n\n\tcase RTCP_PSFB:\n\t\tif (mbuf_get_left(mb) < RTCP_FB_SIZE)\n\t\t\tgoto badmsg;\n\n\t\tif (msg->hdr.length < 2)\n\t\t\tgoto badmsg;\n\n\t\tmsg->r.fb.ssrc_packet = ntohl(mbuf_read_u32(mb));\n\t\tmsg->r.fb.ssrc_media = ntohl(mbuf_read_u32(mb));\n\t\tmsg->r.fb.n = msg->hdr.length - 2;\n\n\t\terr = rtcp_psfb_decode(mb, msg);\n\t\tbreak;\n\n\tcase RTCP_XR:\n\t\tif (mbuf_get_left(mb) < RTCP_HEADROOM)\n\t\t\tgoto badmsg;\n\t\tmsg->r.xr.ssrc = ntohl(mbuf_read_u32(mb));\n\t\tmsg->r.xr.bt = mbuf_read_u8(mb);\n\n\t\t/* reserve */\n\t\tmbuf_read_u8(mb);\n\t\tmsg->r.xr.block_len = ntohs(mbuf_read_u16(mb));\n\n\t\tif (msg->r.xr.bt == RTCP_XR_RRTR) {\n\n\t\t\tif (msg->r.xr.block_len != 2)\n\t\t\t\tgoto badmsg;\n\n\t\t\tmsg->r.xr.rb.rrtrb.ntp_msw = ntohl(mbuf_read_u32(mb));\n\t\t\tmsg->r.xr.rb.rrtrb.ntp_lsw = ntohl(mbuf_read_u32(mb));\n\t\t}\n\t\telse if (msg->r.xr.bt == RTCP_XR_DLRR) {\n\n\t\t\tif (msg->r.xr.block_len != 3)\n\t\t\t\tgoto badmsg;\n\n\t\t\tmsg->r.xr.rb.dlrrb.ssrc = ntohl(mbuf_read_u32(mb));\n\t\t\tmsg->r.xr.rb.dlrrb.lrr = ntohl(mbuf_read_u32(mb));\n\t\t\tmsg->r.xr.rb.dlrrb.dlrr = ntohl(mbuf_read_u32(mb));\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\t/* unknown message type */\n\t\tmbuf_advance(mb, rem);\n\t\tbreak;\n\t}\n\tif (err)\n\t\tgoto out;\n\n\t/* slurp padding */\n\twhile ((mb->pos - start) & 0x3 && mbuf_get_left(mb))\n\t\t++mb->pos;\n\n out:\n\tif (err)\n\t\tmem_deref(msg);\n\telse\n\t\t*msgp = msg;\n\n\treturn err;\n\n badmsg:\n\tmem_deref(msg);\n\treturn EBADMSG;\n}\n"
  },
  {
    "path": "src/rtp/rr.c",
    "content": "/**\n * @file rtp/rr.c  RTCP Reception report\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_sys.h>\n#include <re_net.h>\n#include <re_rtp.h>\n#include \"rtcp.h\"\n\n\nint rtcp_rr_alloc(struct rtcp_rr **rrp, size_t count)\n{\n\tstruct rtcp_rr *rr;\n\n\tif (!rrp)\n\t\treturn EINVAL;\n\n\trr = mem_alloc(count * sizeof(*rr), NULL);\n\tif (!rr)\n\t\treturn ENOMEM;\n\n\t*rrp = rr;\n\treturn 0;\n}\n\n\nint rtcp_rr_encode(struct mbuf *mb, const struct rtcp_rr *rr)\n{\n\tint err;\n\n\tif (!mb || !rr)\n\t\treturn EINVAL;\n\n\terr  = mbuf_write_u32(mb, htonl(rr->ssrc));\n\terr |= mbuf_write_u32(mb, htonl((uint32_t)rr->fraction<<24 |\n\t\t\t\t\t(rr->lost & 0x00ffffff)));\n\terr |= mbuf_write_u32(mb, htonl(rr->last_seq));\n\terr |= mbuf_write_u32(mb, htonl(rr->jitter));\n\terr |= mbuf_write_u32(mb, htonl(rr->lsr));\n\terr |= mbuf_write_u32(mb, htonl(rr->dlsr));\n\n\treturn err;\n}\n\n\nint rtcp_rr_decode(struct mbuf *mb, struct rtcp_rr *rr)\n{\n\tuint32_t w;\n\n\tif (!rr)\n\t\treturn EINVAL;\n\tif (mbuf_get_left(mb) < RTCP_RR_SIZE)\n\t\treturn EBADMSG;\n\n\trr->ssrc     = ntohl(mbuf_read_u32(mb));\n\tw = ntohl(mbuf_read_u32(mb));\n\trr->fraction = w>>24; rr->lost = w & 0x00ffffffU;\n\trr->last_seq = ntohl(mbuf_read_u32(mb));\n\trr->jitter   = ntohl(mbuf_read_u32(mb));\n\trr->lsr      = ntohl(mbuf_read_u32(mb));\n\trr->dlsr     = ntohl(mbuf_read_u32(mb));\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/rtp/rtcp.c",
    "content": "/**\n * @file rtcp.c  Real-time Transport Control Protocol\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_rtp.h>\n#include \"rtcp.h\"\n\n\nstatic int rtcp_quick_send(struct rtp_sock *rs, enum rtcp_type type,\n\t\t\t   uint32_t count, ...)\n{\n\tstruct mbuf *mb;\n\tva_list ap;\n\tint err;\n\n\tmb = mbuf_alloc(32);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tmb->pos = RTCP_HEADROOM;\n\n\terr  = rtcp_make_sr(rs, mb);\n\terr |= rtcp_make_sdes_cname(rs, mb);\n\tif (err)\n\t\tgoto out;\n\n\tva_start(ap, count);\n\terr = rtcp_vencode(mb, type, count, ap);\n\tva_end(ap);\n\n\tmb->pos = RTCP_HEADROOM;\n\n\tif (!err)\n\t\terr = rtcp_send(rs, mb);\n\n\tif (!err)\n\t\trtcp_schedule_report(rs);\n\nout:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/**\n * Send an RTCP Application-Defined (APP) packet\n *\n * @param rs   RTP Socket\n * @param name Ascii name (4 octets)\n * @param data Application-dependent data\n * @param len  Number of bytes of data\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_send_app(struct rtp_sock *rs, const char name[4],\n\t\t  const uint8_t *data, size_t len)\n{\n\treturn rtcp_quick_send(rs, RTCP_APP, 0, rtp_sess_ssrc(rs),\n\t\t\t       name, data, len);\n}\n\n\n/**\n * Send a Full INTRA-frame Request (FIR) packet\n *\n * @param rs   RTP Socket\n * @param ssrc Synchronization source identifier for the sender of this packet\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_send_fir(struct rtp_sock *rs, uint32_t ssrc)\n{\n\treturn rtcp_quick_send(rs, RTCP_FIR, 0, ssrc);\n}\n\n\n/**\n * Send an RTCP NACK packet\n *\n * @param rs   RTP Socket\n * @param fsn  First Sequence Number lost\n * @param blp  Bitmask of lost packets\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_send_nack(struct rtp_sock *rs, uint16_t fsn, uint16_t blp)\n{\n\treturn rtcp_quick_send(rs, RTCP_NACK, 0, rtp_sess_ssrc(rs), fsn, blp);\n}\n\n\nstatic int encode_gnack(struct mbuf *mb, void *arg)\n{\n\tstruct gnack *fci = arg;\n\n\treturn rtcp_rtpfb_gnack_encode(mb, fci->pid, fci->blp);\n}\n\n\n/**\n * Send an RTCP Generic NACK packet (RFC 4585 6.2.1)\n *\n * @param rs   RTP Socket\n * @param ssrc SSRC of the target encoder\n * @param fsn  First Sequence Number lost\n * @param blp  Bitmask of lost packets\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_send_gnack(struct rtp_sock *rs, uint32_t ssrc, uint16_t fsn,\n\t\t    uint16_t blp)\n{\n\tstruct gnack fci = {fsn, blp};\n\treturn rtcp_quick_send(rs, RTCP_RTPFB, RTCP_RTPFB_GNACK,\n\t\t\t       rtp_sess_ssrc(rs), ssrc, &encode_gnack,\n\t\t\t       &fci);\n}\n\n\nstatic int encode_twcc(struct mbuf *mb, void *arg)\n{\n\tstruct twcc *twcc = arg;\n\n\treturn rtcp_rtpfb_twcc_encode(mb, twcc);\n}\n\n\n/**\n * Send an RTCP Transport-wide congestion control Feedback Message\n *\n * @param rs   RTP Socket\n * @param ssrc SSRC of the target encoder\n * @param twcc Transport-wide CC message\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_send_twcc(struct rtp_sock *rs, uint32_t ssrc, struct twcc *twcc)\n{\n\treturn rtcp_quick_send(rs, RTCP_RTPFB, RTCP_RTPFB_TWCC,\n\t\t\t       rtp_sess_ssrc(rs), ssrc, &encode_twcc, twcc);\n}\n\n\n/**\n * Send an RTCP Picture Loss Indication (PLI) packet\n *\n * @param rs      RTP Socket\n * @param fb_ssrc Feedback SSRC\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_send_pli(struct rtp_sock *rs, uint32_t fb_ssrc)\n{\n\treturn rtcp_quick_send(rs, RTCP_PSFB, RTCP_PSFB_PLI,\n\t\t\t       rtp_sess_ssrc(rs), fb_ssrc, NULL, NULL);\n}\n\n\nstatic int encode_fir_rfc5104_fci(struct mbuf *mb, void *arg)\n{\n\tstruct fir_rfc5104 *fci = arg;\n\tint err = mbuf_write_u32(mb, htonl(fci->ssrc));\n\terr |= mbuf_write_u8(mb, fci->seq_n);\n\terr |= mbuf_write_u8(mb, 0);\n\terr |= mbuf_write_u8(mb, 0);\n\terr |= mbuf_write_u8(mb, 0);\n\treturn err;\n}\n\n\n/**\n * Send an RTCP Full INTRA-frame Request (FIR) packet according to RFC 5104\n *\n * @param rs       RTP Socket\n * @param ssrc     SSRC of the target encoder\n * @param fir_seqn FIR sequence number\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_send_fir_rfc5104(struct rtp_sock *rs, uint32_t ssrc, uint8_t fir_seqn)\n{\n\tstruct fir_rfc5104 fci = { ssrc, fir_seqn };\n\treturn rtcp_quick_send(rs, RTCP_PSFB, RTCP_PSFB_FIR,\n\t\t\t       rtp_sess_ssrc(rs), (uint32_t)0u,\n\t\t\t       &encode_fir_rfc5104_fci, &fci);\n}\n\n\n/**\n * Get the name of an RTCP type\n *\n * @param type RTCP type\n *\n * @return String with RTCP name\n */\nconst char *rtcp_type_name(enum rtcp_type type)\n{\n\tswitch (type) {\n\n\tcase RTCP_FIR:   return \"FIR\";\n\tcase RTCP_NACK:  return \"NACK\";\n\tcase RTCP_SR:    return \"SR\";\n\tcase RTCP_RR:    return \"RR\";\n\tcase RTCP_SDES:  return \"SDES\";\n\tcase RTCP_BYE:   return \"BYE\";\n\tcase RTCP_APP:   return \"APP\";\n\tcase RTCP_RTPFB: return \"RTPFB\";\n\tcase RTCP_PSFB:  return \"PSFB\";\n\tcase RTCP_XR:    return \"XR\";\n\tcase RTCP_AVB:   return \"AVB\";\n\tdefault:         return \"?\";\n\t}\n}\n\n\n/**\n * Get the name of an RTCP SDES type\n *\n * @param sdes RTCP SDES type\n *\n * @return String with RTCP SDES name\n */\nconst char *rtcp_sdes_name(enum rtcp_sdes_type sdes)\n{\n\tswitch (sdes) {\n\n\tcase RTCP_SDES_END:    return \"END\";\n\tcase RTCP_SDES_CNAME:  return \"CNAME\";\n\tcase RTCP_SDES_NAME:   return \"NAME\";\n\tcase RTCP_SDES_EMAIL:  return \"EMAIL\";\n\tcase RTCP_SDES_PHONE:  return \"PHONE\";\n\tcase RTCP_SDES_LOC:    return \"LOC\";\n\tcase RTCP_SDES_TOOL:   return \"TOOL\";\n\tcase RTCP_SDES_NOTE:   return \"NOTE\";\n\tcase RTCP_SDES_PRIV:   return \"PRIV\";\n\tdefault:               return \"?\";\n\t}\n}\n\n\n/**\n * Print an RTCP Message\n *\n * @param pf  Print handler for debug output\n * @param msg RTCP Message\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtcp_msg_print(struct re_printf *pf, const struct rtcp_msg *msg)\n{\n\tsize_t i, j;\n\tint err;\n\n\tif (!msg)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"%8s pad=%d count=%-2d pt=%-3d len=%u \",\n\t\t\t rtcp_type_name((enum rtcp_type)msg->hdr.pt),\n\t\t\t msg->hdr.p,\n\t\t\t msg->hdr.count, msg->hdr.pt, msg->hdr.length);\n\tif (err)\n\t\treturn err;\n\n\tswitch (msg->hdr.pt) {\n\n\tcase RTCP_SR:\n\t\terr = re_hprintf(pf, \"%08x %u %u %u %u %u\",\n\t\t\t\t msg->r.sr.ssrc,\n\t\t\t\t msg->r.sr.ntp_sec,\n\t\t\t\t msg->r.sr.ntp_frac,\n\t\t\t\t msg->r.sr.rtp_ts,\n\t\t\t\t msg->r.sr.psent,\n\t\t\t\t msg->r.sr.osent);\n\t\tfor (i=0; i<msg->hdr.count && !err; i++) {\n\t\t\tconst struct rtcp_rr *rr = &msg->r.sr.rrv[i];\n\t\t\terr = re_hprintf(pf, \" {%08x %u %d %u %u %u %u}\",\n\t\t\t\t\t rr->ssrc, rr->fraction, rr->lost,\n\t\t\t\t\t rr->last_seq, rr->jitter,\n\t\t\t\t\t rr->lsr, rr->dlsr);\n\t\t}\n\t\tbreak;\n\n\tcase RTCP_RR:\n\t\terr = re_hprintf(pf, \"%08x\", msg->r.rr.ssrc);\n\t\tfor (i=0; i<msg->hdr.count && !err; i++) {\n\t\t\tconst struct rtcp_rr *rr = &msg->r.rr.rrv[i];\n\t\t\terr = re_hprintf(pf, \" {0x%08x %u %d %u %u %u %u}\",\n\t\t\t\t\t rr->ssrc, rr->fraction, rr->lost,\n\t\t\t\t\t rr->last_seq, rr->jitter,\n\t\t\t\t\t rr->lsr, rr->dlsr);\n\t\t}\n\t\tbreak;\n\n\tcase RTCP_SDES:\n\t\tfor (i=0; i<msg->hdr.count; i++) {\n\t\t\tconst struct rtcp_sdes *sdes = &msg->r.sdesv[i];\n\n\t\t\terr = re_hprintf(pf, \" {0x%08x n=%u\",\n\t\t\t\t\t sdes->src, sdes->n);\n\t\t\tfor (j=0; j<sdes->n && !err; j++) {\n\t\t\t\tconst struct rtcp_sdes_item *item;\n\t\t\t\titem = &sdes->itemv[j];\n\t\t\t\terr = re_hprintf(pf, \" <%s:%b>\",\n\t\t\t\t\t\t rtcp_sdes_name(item->type),\n\t\t\t\t\t\t item->data,\n\t\t\t\t\t\t (size_t)item->length);\n\t\t\t}\n\t\t\terr |= re_hprintf(pf, \"}\");\n\t\t}\n\t\tbreak;\n\n\tcase RTCP_BYE:\n\t\terr = re_hprintf(pf, \"%u srcs:\", msg->hdr.count);\n\t\tfor (i=0; i<msg->hdr.count && !err; i++) {\n\t\t\terr = re_hprintf(pf, \" %08x\",\n\t\t\t\t\t msg->r.bye.srcv[i]);\n\t\t}\n\t\terr |= re_hprintf(pf, \" '%s'\", msg->r.bye.reason);\n\t\tbreak;\n\n\tcase RTCP_APP:\n\t\terr = re_hprintf(pf, \"src=%08x '%b' data=%zu\",\n\t\t\t\t msg->r.app.src,\n\t\t\t\t msg->r.app.name, sizeof(msg->r.app.name),\n\t\t\t\t msg->r.app.data_len);\n\t\tbreak;\n\n\tcase RTCP_FIR:\n\t\terr = re_hprintf(pf, \"ssrc=%08x\", msg->r.fir.ssrc);\n\t\tbreak;\n\n\tcase RTCP_NACK:\n\t\terr = re_hprintf(pf, \"ssrc=%08x fsn=%04x blp=%04x\",\n\t\t\t\t msg->r.nack.ssrc, msg->r.nack.fsn,\n\t\t\t\t msg->r.nack.blp);\n\t\tbreak;\n\n\tcase RTCP_RTPFB:\n\t\terr = re_hprintf(pf, \"pkt=%08x med=%08x n=%u\",\n\t\t\t\t msg->r.fb.ssrc_packet,\n\t\t\t\t msg->r.fb.ssrc_media,\n\t\t\t\t msg->r.fb.n);\n\t\tif (msg->hdr.count == RTCP_RTPFB_GNACK) {\n\t\t\terr |= re_hprintf(pf, \" GNACK\");\n\t\t\tfor (i=0; i<msg->r.fb.n; i++) {\n\t\t\t\terr |= re_hprintf(pf, \" {%04x %04x}\",\n\t\t\t\t\t\t  msg->r.fb.fci.gnackv[i].pid,\n\t\t\t\t\t\t  msg->r.fb.fci.gnackv[i].blp);\n\t\t\t}\n\t\t}\n\t\telse if (msg->hdr.count == RTCP_RTPFB_TWCC) {\n\t\t\tconst struct twcc *twcc = msg->r.fb.fci.twccv;\n\n\t\t\terr |= re_hprintf(pf,\n\t\t\t\t\t  \" TWCC\"\n\t\t\t\t\t  \" base_seq=%u\"\n\t\t\t\t\t  \" pkt_status_count=%u\"\n\t\t\t\t\t  \" ref_time=%u\"\n\t\t\t\t\t  \" fb_pkt_count=%u\"\n\t\t\t\t\t  ,\n\t\t\t\t\t  twcc->seq,\n\t\t\t\t\t  twcc->count,\n\t\t\t\t\t  twcc->reftime,\n\t\t\t\t\t  twcc->fbcount);\n\t\t}\n\t\tbreak;\n\n\tcase RTCP_PSFB:\n\t\terr = re_hprintf(pf, \"pkt=%08x med=%08x n=%u\",\n\t\t\t\t msg->r.fb.ssrc_packet,\n\t\t\t\t msg->r.fb.ssrc_media,\n\t\t\t\t msg->r.fb.n);\n\t\tif (msg->hdr.count == RTCP_PSFB_SLI) {\n\t\t\terr |= re_hprintf(pf, \" SLI\");\n\t\t\tfor (i=0; i<msg->r.fb.n; i++) {\n\t\t\t\terr |= re_hprintf(pf, \" {%04x %04x %02x}\",\n\t\t\t\t\t\t  msg->r.fb.fci.sliv[i].first,\n\t\t\t\t\t\t  msg->r.fb.fci.sliv[i].number,\n\t\t\t\t\t\t  msg->r.fb.fci.sliv[i].picid);\n\t\t\t}\n\t\t}\n\t\telse if (msg->hdr.count == RTCP_PSFB_AFB) {\n\t\t\terr |= re_hprintf(pf, \" AFB %u bytes\",\n\t\t\t\t\t  msg->r.fb.n * 4);\n\t\t}\n\t\telse if (msg->hdr.count == RTCP_PSFB_FIR) {\n\t\t\terr |= re_hprintf(pf, \" FIR (RFC5104)\");\n\t\t\tfor (i=0; i<msg->r.fb.n; i++) {\n\t\t\t\terr |= re_hprintf(pf,\n\t\t\t\t\t\t  \" {ssrc=%08x seq_n=%02x}\",\n\t\t\t\t\t\t  msg->r.fb.fci.firv[i].ssrc,\n\t\t\t\t\t\t  msg->r.fb.fci.firv[i].seq_n);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\terr = re_hprintf(pf, \"<len=%u>\", msg->hdr.length);\n\t\tbreak;\n\t}\n\n\terr |= re_hprintf(pf, \"\\n\");\n\n\treturn err;\n}\n\n\n/**\n * Check if packet is RTCP packet, used for de-multiplexing\n *\n * @param mb Mbuffer with packet\n *\n * @return True if RTCP packet, otherwise false\n */\nbool rtp_is_rtcp_packet(const struct mbuf *mb)\n{\n\tuint8_t pt;\n\n\tif (mbuf_get_left(mb) < 2)\n\t\treturn false;\n\n\tpt = mbuf_buf(mb)[1] & 0x7f;\n\n\treturn rtp_pt_is_rtcp(pt);\n}\n"
  },
  {
    "path": "src/rtp/rtcp.h",
    "content": "/**\n * @file rtcp.h  Internal interface to RTCP\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** RTCP protocol values */\nenum {\n\tRTCP_HDR_SIZE  =   4,  /**< Size of common RTCP header   */\n\tRTCP_SRC_SIZE  =   4,  /**< Size of Source field         */\n\tRTCP_SR_SIZE   =  20,  /**< Size of Sender Information   */\n\tRTCP_RR_SIZE   =  24,  /**< Size of Report Block         */\n\tRTCP_APP_SIZE  =   8,  /**< Size of Application packet   */\n\tRTCP_FIR_SIZE  =   4,  /**< Size of FIR packet           */\n\tRTCP_NACK_SIZE =   8,  /**< Size of NACK packet          */\n\tRTCP_FB_SIZE   =   8,  /**< Size of Feedback packets     */\n\tRTCP_MAX_SDES  = 255,  /**< Maximum text length for SDES */\n\tRTCP_HEADROOM  =   4,  /**< Headroom in RTCP packets     */\n};\n\n\nstruct hash;\n\n\n/** RTP Member */\nstruct rtp_member {\n\tstruct le le;             /**< Hash-table element                  */\n\tstruct rtp_source *s;     /**< RTP source state                    */\n\tuint32_t src;             /**< Source - used for hash-table lookup */\n\tint cum_lost;             /**< Cumulative number of packets lost   */\n\tuint32_t jit;             /**< Jitter in [us]                      */\n\tuint32_t rtt;             /**< Round-trip time in [us]             */\n};\n\n\n/* Member */\nstruct rtp_member *rtp_member_add(struct hash *ht, uint32_t src);\nstruct rtp_member *rtp_member_find(struct hash *ht, uint32_t src);\n\n\n/* RR (Reception report) */\nint rtcp_rr_alloc(struct rtcp_rr **rrp, size_t count);\nint rtcp_rr_encode(struct mbuf *mb, const struct rtcp_rr *rr);\nint rtcp_rr_decode(struct mbuf *mb, struct rtcp_rr *rr);\n\n/* SR (Sender report) */\nint rtcp_make_sr(const struct rtp_sock *rs, struct mbuf *mb);\n\n/* SDES (Source Description) */\nint rtcp_sdes_decode(struct mbuf *mb, struct rtcp_sdes *sdes);\nint rtcp_make_sdes_cname(const struct rtp_sock *rs, struct mbuf *mb);\n\n/* RTCP Feedback */\nint rtcp_rtpfb_gnack_encode(struct mbuf *mb, uint16_t pid, uint16_t blp);\nint rtcp_rtpfb_twcc_encode(struct mbuf *mb, struct twcc *twcc);\nint rtcp_rtpfb_twcc_decode(struct mbuf *mb, struct twcc *msg, int n);\nint rtcp_rtpfb_decode(struct mbuf *mb, struct rtcp_msg *msg);\nint rtcp_psfb_decode(struct mbuf *mb, struct rtcp_msg *msg);\n\n/** NTP Time */\nstruct timeval;\nvoid unix2ntp(struct rtp_ntp_time *ntp, const struct timeval *tv);\nvoid ntp_time_get(struct rtp_ntp_time *ntp, uint64_t* jfs_rt);\nuint32_t ntp_compact(const struct rtp_ntp_time *ntp);\nuint64_t ntp_compact2us(uint32_t ntpc);\n\n/* RTP Socket */\nstruct rtcp_sess *rtp_rtcp_sess(const struct rtp_sock *rs);\n\n/* RTCP message */\ntypedef int (rtcp_encode_h)(struct mbuf *mb, void *arg);\n\nint rtcp_hdr_encode(struct mbuf *mb, uint8_t count, enum rtcp_type type,\n\t\t    uint16_t length);\nint rtcp_hdr_decode(struct mbuf *mb, struct rtcp_hdr *hdr);\nint rtcp_vencode(struct mbuf *mb, enum rtcp_type type, uint32_t count,\n\t\t va_list ap);\n\n/* RTCP Session */\nstruct rtcp_sess;\n\nint  rtcp_sess_alloc(struct rtcp_sess **sessp, struct rtp_sock *rs);\nint  rtcp_enable(struct rtcp_sess *sess, bool enabled, const char *cname);\nint  rtcp_send(struct rtp_sock *rs, struct mbuf *mb);\nvoid rtcp_handler(struct rtcp_sess *sess, struct rtcp_msg *msg);\nvoid rtcp_sess_tx_rtp(struct rtcp_sess *sess, uint32_t ts, uint64_t jfs_rt,\n\t\t      size_t payload_size);\nvoid rtcp_sess_rx_rtp(struct rtcp_sess *sess, struct rtp_header *hdr,\n\t\t      size_t payload_size, const struct sa *peer);\nvoid rtcp_schedule_report(const struct rtp_sock *rs);\n"
  },
  {
    "path": "src/rtp/rtp.c",
    "content": "/**\n * @file rtp.c  Real-time Transport Protocol\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_sys.h>\n#include <re_net.h>\n#include <re_udp.h>\n#include <re_rtp.h>\n#include <re_atomic.h>\n#include \"rtcp.h\"\n\n#define DEBUG_MODULE \"rtp\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** Defines an RTP Socket */\nstruct rtp_sock {\n\t/** Encode data */\n\tstruct {\n\t\tuint16_t seq;   /**< Sequence number       */\n\t\tuint32_t ssrc;  /**< Synchronizing source  */\n\t} enc;\n\tvoid *sock_rtp;         /**< RTP Socket            */\n\tvoid *sock_rtcp;        /**< RTCP Socket           */\n\tstruct sa local;        /**< Local RTP Address     */\n\tstruct sa rtcp_peer;    /**< RTCP address of Peer  */\n\trtp_recv_h *recvh;      /**< RTP Receive handler   */\n\trtcp_recv_h *rtcph;     /**< RTCP Receive handler  */\n\tvoid *arg;              /**< Handler argument      */\n\tstruct rtcp_sess *rtcp; /**< RTCP Session          */\n\tRE_ATOMIC bool rtcp_mux;  /**< RTP/RTCP multiplexing */\n};\n\n\n/**\n * Encode the RTP header into a buffer\n *\n * @param mb  Buffer to encode into\n * @param hdr RTP Header to be encoded\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtp_hdr_encode(struct mbuf *mb, const struct rtp_header *hdr)\n{\n\tuint8_t buf[2];\n\tint err, i;\n\n\tif (!mb || !hdr)\n\t\treturn EINVAL;\n\n\tbuf[0]  = (hdr->ver & 0x02) << 6;\n\tbuf[0] |= (hdr->pad & 0x01) << 5;\n\tbuf[0] |= (hdr->ext & 0x01) << 4;\n\tbuf[0] |= (hdr->cc  & 0x0f) << 0;\n\tbuf[1]  = (hdr->m   & 0x01) << 7;\n\tbuf[1] |= (hdr->pt  & 0x7f) << 0;\n\n\terr  = mbuf_write_mem(mb, buf, sizeof(buf));\n\terr |= mbuf_write_u16(mb, htons(hdr->seq));\n\terr |= mbuf_write_u32(mb, htonl(hdr->ts));\n\terr |= mbuf_write_u32(mb, htonl(hdr->ssrc));\n\n\tfor (i=0; i<hdr->cc; i++) {\n\t\terr |= mbuf_write_u32(mb, htonl(hdr->csrc[i]));\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Decode an RTP header from a buffer\n *\n * @param hdr RTP Header to decode into\n * @param mb  Buffer to decode from\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtp_hdr_decode(struct rtp_header *hdr, struct mbuf *mb)\n{\n\tuint8_t buf[2];\n\tint err, i;\n\tsize_t header_len;\n\n\tif (!hdr || !mb)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < RTP_HEADER_SIZE)\n\t\treturn EBADMSG;\n\n\terr = mbuf_read_mem(mb, buf, sizeof(buf));\n\tif (err)\n\t\treturn err;\n\n\thdr->ver  = (buf[0] >> 6) & 0x03;\n\thdr->pad  = (buf[0] >> 5) & 0x01;\n\thdr->ext  = (buf[0] >> 4) & 0x01;\n\thdr->cc   = (buf[0] >> 0) & 0x0f;\n\thdr->m    = (buf[1] >> 7) & 0x01;\n\thdr->pt   = (buf[1] >> 0) & 0x7f;\n\n\thdr->seq  = ntohs(mbuf_read_u16(mb));\n\thdr->ts   = ntohl(mbuf_read_u32(mb));\n\thdr->ssrc = ntohl(mbuf_read_u32(mb));\n\n\theader_len = hdr->cc*sizeof(uint32_t);\n\tif (mbuf_get_left(mb) < header_len)\n\t\treturn EBADMSG;\n\n\tfor (i=0; i<hdr->cc; i++) {\n\t\thdr->csrc[i] = ntohl(mbuf_read_u32(mb));\n\t}\n\n\tif (hdr->ext) {\n\t\tif (mbuf_get_left(mb) < 4)\n\t\t\treturn EBADMSG;\n\n\t\thdr->x.type = ntohs(mbuf_read_u16(mb));\n\t\thdr->x.len  = ntohs(mbuf_read_u16(mb));\n\n\t\tif (mbuf_get_left(mb) < hdr->x.len*sizeof(uint32_t))\n\t\t\treturn EBADMSG;\n\n\t\tmb->pos += hdr->x.len*sizeof(uint32_t);\n\t}\n\n\treturn 0;\n}\n\n\nstatic void destructor(void *data)\n{\n\tstruct rtp_sock *rs = data;\n\n\tudp_handler_set(rs->sock_rtp, NULL, NULL);\n\tudp_handler_set(rs->sock_rtcp, NULL, NULL);\n\n\t/* Destroy RTCP Session now */\n\tmem_deref(rs->rtcp);\n\n\tmem_deref(rs->sock_rtp);\n\tmem_deref(rs->sock_rtcp);\n}\n\n\nstatic void rtcp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct rtp_sock *rs = arg;\n\tstruct rtcp_msg *msg;\n\n#ifdef RE_RTP_PCAP\n\tre_text2pcap_trace(\"rtcp_recv\", \"RTCP\", true, mb);\n#endif\n\n\twhile (0 == rtcp_decode(&msg, mb)) {\n\n\t\t/* handle internally first */\n\t\trtcp_handler(rs->rtcp, msg);\n\n\t\t/* then relay to application */\n\t\tif (rs->rtcph)\n\t\t\trs->rtcph(src, msg, rs->arg);\n\n\t\tmem_deref(msg);\n\t}\n}\n\n\nstatic void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct rtp_sock *rs = arg;\n\tstruct rtp_header hdr;\n\tint err;\n\n\t/* Handle RTCP multiplexed on RTP-port */\n\tif (re_atomic_rlx(&rs->rtcp_mux)) {\n\t\tuint8_t pt;\n\n\t\tif (mbuf_get_left(mb) < 2)\n\t\t\treturn;\n\n\t\tpt = mbuf_buf(mb)[1] & 0x7f;\n\n\t\tif (rtp_pt_is_rtcp(pt)) {\n\t\t\trtcp_recv_handler(src, mb, arg);\n\t\t\treturn;\n\t\t}\n\t}\n\n#ifdef RE_RTP_PCAP\n\tre_text2pcap_trace(\"rtp_udp_recv\", \"RTP\", true, mb);\n#endif\n\n\terr = rtp_decode(rs, mb, &hdr);\n\tif (err)\n\t\treturn;\n\n\n\tif (rs->rtcp)\n\t\trtcp_sess_rx_rtp(rs->rtcp, &hdr, mbuf_get_left(mb), src);\n\n\tif (rs->recvh)\n\t\trs->recvh(src, &hdr, mb, rs->arg);\n}\n\n\nstatic int udp_range_listen(struct rtp_sock *rs, const struct sa *ip,\n\t\t\t    uint16_t min_port, uint16_t max_port)\n{\n\tstruct sa rtcp;\n\tint tries = 64;\n\tint err = 0;\n\n\trs->local = rtcp = *ip;\n\n\t/* try hard */\n\twhile (tries--) {\n\t\tstruct udp_sock *us_rtp, *us_rtcp;\n\t\tuint16_t port;\n\n\t\tport = (min_port + (rand_u16() % (max_port - min_port)));\n\t\tport &= 0xfffe;\n\n\t\tsa_set_port(&rs->local, port);\n\t\terr = udp_listen(&us_rtp, &rs->local, udp_recv_handler, rs);\n\t\tif (err)\n\t\t\tcontinue;\n\n\t\tsa_set_port(&rtcp, port + 1);\n\t\terr = udp_listen(&us_rtcp, &rtcp, rtcp_recv_handler, rs);\n\t\tif (err) {\n\t\t\tmem_deref(us_rtp);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* OK */\n\t\trs->sock_rtp = us_rtp;\n\t\trs->sock_rtcp = us_rtcp;\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Allocate a new RTP socket\n *\n * @param rsp Pointer to returned RTP socket\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtp_alloc(struct rtp_sock **rsp)\n{\n\tstruct rtp_sock *rs;\n\n\tif (!rsp)\n\t\treturn EINVAL;\n\n\trs = mem_zalloc(sizeof(*rs), destructor);\n\tif (!rs)\n\t\treturn ENOMEM;\n\n\tsa_init(&rs->rtcp_peer, AF_UNSPEC);\n\n\trs->enc.seq  = rand_u16() & 0x7fff;\n\trs->enc.ssrc = rand_u32();\n\n\t*rsp = rs;\n\n\treturn 0;\n}\n\n\n/**\n * Listen on an RTP/RTCP Socket\n *\n * @param rsp         Pointer to returned RTP socket\n * @param proto       Transport protocol\n * @param ip          Local IP address\n * @param min_port    Minimum port range\n * @param max_port    Maximum port range\n * @param enable_rtcp True to enable RTCP Session\n * @param recvh       RTP Receive handler\n * @param rtcph       RTCP Receive handler\n * @param arg         Handler argument\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtp_listen(struct rtp_sock **rsp, int proto, const struct sa *ip,\n\t       uint16_t min_port, uint16_t max_port, bool enable_rtcp,\n\t       rtp_recv_h *recvh, rtcp_recv_h *rtcph, void *arg)\n{\n\tstruct rtp_sock *rs;\n\tint err;\n\n\tif (!ip || min_port >= max_port || !recvh)\n\t\treturn EINVAL;\n\n\terr = rtp_alloc(&rs);\n\tif (err)\n\t\treturn err;\n\n\trs->recvh = recvh;\n\trs->rtcph = rtcph;\n\trs->arg   = arg;\n\n\t/* Optional RTCP */\n\tif (enable_rtcp) {\n\t\terr = rtcp_sess_alloc(&rs->rtcp, rs);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\tswitch (proto) {\n\n\tcase IPPROTO_UDP:\n\t\terr = udp_range_listen(rs, ip, min_port, max_port);\n\t\tbreak;\n\n\tdefault:\n\t\terr = EPROTONOSUPPORT;\n\t\tbreak;\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(rs);\n\telse\n\t\t*rsp = rs;\n\n\treturn err;\n}\n\n\n/**\n * Listen on a single port for RTP (no RTCP).\n *\n * Opens a UDP socket and tries to bind to the given IP and port.\n *\n * @param rsp   Pointer to returned RTP socket\n * @param ip\tLocal IP address\n * @param port  Local port\n * @param recvh RTP Receive handler\n * @param arg   Handler argument\n * @return 0 for success, otherwise errorcode\n */\nint   rtp_listen_single(struct rtp_sock **rsp, const struct sa *ip,\n\t\t\tuint16_t port, rtp_recv_h *recvh, void *arg)\n{\n\tstruct rtp_sock *rs;\n\tint err;\n\n\tif (!ip || !recvh)\n\t\treturn EINVAL;\n\n\terr = rtp_alloc(&rs);\n\tif (err)\n\t\treturn err;\n\n\trs->recvh = recvh;\n\trs->arg   = arg;\n\trs->local = *ip;\n\tsa_set_port(&rs->local, port);\n\n\tstruct udp_sock *us;\n\terr = udp_listen(&us, &rs->local, udp_recv_handler, rs);\n\tif (err)\n\t\tgoto out;\n\n\trs->sock_rtp = us;\n\n out:\n\tif (err)\n\t\tmem_deref(rs);\n\telse\n\t\t*rsp = rs;\n\n\treturn err;\n}\n\n\n/**\n * Open RTP Socket without bind.\n *\n * @param rsp         Pointer to returned RTP socket\n * @param af          Address family AF_INET or AF_INET6\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtp_open(struct rtp_sock **rsp, int af)\n{\n\tstruct rtp_sock *rs;\n\tstruct udp_sock *us_rtp;\n\tint err;\n\n\terr = rtp_alloc(&rs);\n\tif (err)\n\t\treturn err;\n\n\tus_rtp = NULL;\n\terr = udp_open(&us_rtp, af);\n\trs->sock_rtp = us_rtp;\n\n\tif (err)\n\t\tmem_deref(rs);\n\telse\n\t\t*rsp = rs;\n\n\treturn err;\n}\n\n\n/**\n * Encode a new RTP header with sequence into the beginning of the buffer\n *\n * @param rs     RTP Socket\n * @param seq    Sequence Number\n * @param ext    Extension bit\n * @param marker Marker bit\n * @param pt     Payload type\n * @param ts     Timestamp\n * @param mb     Memory buffer\n *\n * @return 0 for success, otherwise errorcode\n *\n * @note The buffer must have enough space for the RTP header\n */\nint rtp_encode_seq(struct rtp_sock *rs, uint16_t seq, bool ext, bool marker,\n\t\t   uint8_t pt, uint32_t ts, struct mbuf *mb)\n{\n\tstruct rtp_header hdr;\n\n\tif (!rs || pt&~0x7f || !mb)\n\t\treturn EINVAL;\n\n\thdr.ver  = RTP_VERSION;\n\thdr.pad  = false;\n\thdr.ext  = ext;\n\thdr.cc   = 0;\n\thdr.m    = marker ? 1 : 0;\n\thdr.pt   = pt;\n\thdr.seq  = seq;\n\thdr.ts   = ts;\n\thdr.ssrc = rs->enc.ssrc;\n\n\treturn rtp_hdr_encode(mb, &hdr);\n}\n\n\n/**\n * Encode a new RTP header into the beginning of the buffer\n *\n * @param rs     RTP Socket\n * @param ext    Extension bit\n * @param marker Marker bit\n * @param pt     Payload type\n * @param ts     Timestamp\n * @param mb     Memory buffer\n *\n * @return 0 for success, otherwise errorcode\n *\n * @note The buffer must have enough space for the RTP header\n */\nint rtp_encode(struct rtp_sock *rs, bool ext, bool marker, uint8_t pt,\n\t       uint32_t ts, struct mbuf *mb)\n{\n\tstruct rtp_header hdr;\n\n\tif (!rs || pt&~0x7f || !mb)\n\t\treturn EINVAL;\n\n\thdr.ver  = RTP_VERSION;\n\thdr.pad  = false;\n\thdr.ext  = ext;\n\thdr.cc   = 0;\n\thdr.m    = marker ? 1 : 0;\n\thdr.pt   = pt;\n\thdr.seq  = ++rs->enc.seq;\n\thdr.ts   = ts;\n\thdr.ssrc = rs->enc.ssrc;\n\n\treturn rtp_hdr_encode(mb, &hdr);\n}\n\n\n/**\n * Decode an RTP packet and return decoded RTP header and payload\n *\n * @param rs     RTP Socket\n * @param mb     Memory buffer containing RTP packet\n * @param hdr    RTP header (set on return)\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtp_decode(struct rtp_sock *rs, struct mbuf *mb,\n\t       struct rtp_header *hdr)\n{\n\tint err;\n\n\tif (!rs || !mb || !hdr)\n\t\treturn EINVAL;\n\n\tmemset(hdr, 0, sizeof(*hdr));\n\terr = rtp_hdr_decode(hdr, mb);\n\tif (err)\n\t\treturn err;\n\n\tif (RTP_VERSION != hdr->ver)\n\t\treturn EBADMSG;\n\n\treturn 0;\n}\n\n\n/**\n * Send an RTP packet to a peer\n *\n * @param rs     RTP Socket\n * @param dst    Destination address\n * @param ext    Extension bit\n * @param marker Marker bit\n * @param pt     Payload type\n * @param ts     Timestamp\n * @param jfs_rt Realtime time point in microseconds that correspond to @a ts\n * @param mb     Payload buffer\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtp_send(struct rtp_sock *rs, const struct sa *dst, bool ext,\n\t     bool marker, uint8_t pt, uint32_t ts, uint64_t jfs_rt,\n\t     struct mbuf *mb)\n{\n\tsize_t pos;\n\tint err;\n\n\tif (!rs || !mb)\n\t\treturn EINVAL;\n\n\tif (mb->pos < RTP_HEADER_SIZE) {\n\t\tDEBUG_WARNING(\"rtp_send: buffer must have space for\"\n\t\t\t      \" rtp header (pos=%u, end=%u)\\n\",\n\t\t\t      mb->pos, mb->end);\n\t\treturn EBADMSG;\n\t}\n\n\tmbuf_advance(mb, -RTP_HEADER_SIZE);\n\n\tpos = mb->pos;\n\n\terr = rtp_encode(rs, ext, marker, pt, ts, mb);\n\tif (err)\n\t\treturn err;\n\n\tif (rs->rtcp) {\n\t\trtcp_sess_tx_rtp(rs->rtcp, ts, jfs_rt,\n\t\t\t\t mbuf_get_left(mb));\n\t}\n\n\tmb->pos = pos;\n\n#ifdef RE_RTP_PCAP\n\tre_text2pcap_trace(\"rtp_send\", \"RTP\", false, mb);\n#endif\n\n\treturn udp_send(rs->sock_rtp, dst, mb);\n}\n\n\n/**\n * Resend an RTP packet to a peer (no rtcp update)\n *\n * @param rs     RTP Socket\n * @param seq    Sequence Number\n * @param dst    Destination address\n * @param ext    Extension bit\n * @param marker Marker bit\n * @param pt     Payload type\n * @param ts     Timestamp\n * @param mb     Payload buffer\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtp_resend(struct rtp_sock *rs, uint16_t seq, const struct sa *dst,\n\t       bool ext, bool marker, uint8_t pt, uint32_t ts, struct mbuf *mb)\n{\n\tsize_t pos;\n\tint err;\n\n\tif (!rs || !mb)\n\t\treturn EINVAL;\n\n\tif (mb->pos < RTP_HEADER_SIZE) {\n\t\tDEBUG_WARNING(\"rtp_resend: buffer must have space for\"\n\t\t\t      \" rtp header (pos=%u, end=%u)\\n\",\n\t\t\t      mb->pos, mb->end);\n\t\treturn EBADMSG;\n\t}\n\n\tmbuf_advance(mb, -RTP_HEADER_SIZE);\n\n\tpos = mb->pos;\n\n\terr = rtp_encode_seq(rs, seq, ext, marker, pt, ts, mb);\n\tif (err)\n\t\treturn err;\n\n\tmb->pos = pos;\n\n#ifdef RE_RTP_PCAP\n\tre_text2pcap_trace(\"rtp_resend\", \"RTP\", false, mb);\n#endif\n\n\treturn udp_send(rs->sock_rtp, dst, mb);\n}\n\n\n/**\n * Get the RTP transport socket from an RTP/RTCP Socket\n *\n * @param rs RTP Socket\n *\n * @return Transport socket for RTP\n */\nvoid *rtp_sock(const struct rtp_sock *rs)\n{\n\treturn rs ? rs->sock_rtp : NULL;\n}\n\n\n/**\n * Get the RTCP transport socket from an RTP/RTCP Socket\n *\n * @param rs RTP Socket\n *\n * @return Transport socket for RTCP\n */\nvoid *rtcp_sock(const struct rtp_sock *rs)\n{\n\treturn rs ? rs->sock_rtcp : NULL;\n}\n\n\n/**\n * Get the local RTP address for an RTP/RTCP Socket\n *\n * @param rs RTP Socket\n *\n * @return Local RTP address\n */\nconst struct sa *rtp_local(const struct rtp_sock *rs)\n{\n\treturn rs ? &rs->local : NULL;\n}\n\n\n/**\n * Get the Synchronizing source for an RTP/RTCP Socket\n *\n * @param rs RTP Socket\n *\n * @return Synchronizing source\n */\nuint32_t rtp_sess_ssrc(const struct rtp_sock *rs)\n{\n\treturn rs ? rs->enc.ssrc : 0;\n}\n\n\n/**\n * Get the last Sequence number from an RTP/RTCP Socket\n *\n * @param rs RTP Socket\n *\n * @return Sequence number\n */\nuint16_t rtp_sess_seq(const struct rtp_sock *rs)\n{\n\treturn rs ? rs->enc.seq : 0;\n}\n\n\n/**\n * Get the RTCP-Session for an RTP/RTCP Socket\n *\n * @param rs RTP Socket\n *\n * @return RTCP-Session\n */\nstruct rtcp_sess *rtp_rtcp_sess(const struct rtp_sock *rs)\n{\n\treturn rs ? rs->rtcp : NULL;\n}\n\n\n/**\n * Start the RTCP Session\n *\n * @param rs    RTP Socket\n * @param cname Canonical Name\n * @param peer  IP-Address of RTCP Peer\n */\nvoid rtcp_start(struct rtp_sock *rs, const char *cname,\n\t\tconst struct sa *peer)\n{\n\tif (!rs)\n\t\treturn;\n\n\tif (peer)\n\t\trs->rtcp_peer = *peer;\n\n\t(void)rtcp_enable(rs->rtcp, peer != NULL, cname);\n}\n\n\n/**\n * Enable RTCP-multiplexing on RTP-port\n *\n * @param rs      RTP Socket\n * @param enabled True to enable, false to disable\n */\nvoid rtcp_enable_mux(struct rtp_sock *rs, bool enabled)\n{\n\tif (!rs)\n\t\treturn;\n\n\tre_atomic_rlx_set(&rs->rtcp_mux, enabled);\n}\n\n\n/**\n * Send RTCP packet(s) to the Peer\n *\n * @param rs RTP Socket\n * @param mb Buffer containing the RTCP Packet(s)\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtcp_send(struct rtp_sock *rs, struct mbuf *mb)\n{\n\tvoid* sock;\n\tif (!rs)\n\t\treturn EINVAL;\n\n\tsock = re_atomic_rlx(&rs->rtcp_mux) ? rs->sock_rtp : rs->sock_rtcp;\n\tif (!sock || !sa_isset(&rs->rtcp_peer, SA_ALL))\n\t\treturn EINVAL;\n\n#ifdef RE_RTP_PCAP\n\tre_text2pcap_trace(\"rtcp_send\", \"RTCP\", false, mb);\n#endif\n\treturn udp_send(sock, &rs->rtcp_peer, mb);\n}\n\n\n/**\n * Clear receive buffer of RTP Socket\n *\n * @param rs RTP Socket\n *\n * @return 0 for success, otherwise errorcode\n */\nint rtp_clear(struct rtp_sock *rs)\n{\n\tif (!rs)\n\t\treturn EINVAL;\n\n\tudp_flush(rs->sock_rtp);\n\n\treturn 0;\n}\n\n\n/**\n * RTP Debug handler, use with fmt %H\n *\n * @param pf Print function\n * @param rs RTP Socket\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtp_debug(struct re_printf *pf, const struct rtp_sock *rs)\n{\n\tint err;\n\n\tif (!rs || !pf)\n\t\treturn EINVAL;\n\n\terr  = re_hprintf(pf, \"RTP debug:\\n\");\n\terr |= re_hprintf(pf, \" Encode: seq=%u ssrc=0x%x\\n\",\n\t\t\t  rs->enc.seq, rs->enc.ssrc);\n\n\tif (rs->rtcp)\n\t\terr |= rtcp_debug(pf, rs);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/rtp/sdes.c",
    "content": "/**\n * @file sdes.c  RTCP Source Description\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_rtp.h>\n#include \"rtcp.h\"\n\n\n#define DEBUG_MODULE \"rtcp_sdes\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tRTCP_SDES_MIN_SIZE = 1,\n};\n\n\n/**\n * Encode one SDES chunk into mbuffer\n *\n * @param mb    Buffer to encode into\n * @param src   First SSRC/CSRC\n * @param itemc Number of SDES items to encode\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtcp_sdes_encode(struct mbuf *mb, uint32_t src, uint32_t itemc, ...)\n{\n\tva_list ap;\n\tsize_t start;\n\tint err = 0;\n\n\tif (!mb || !itemc)\n\t\treturn EINVAL;\n\n\tva_start(ap, itemc);\n\n\tstart = mb->pos;\n\terr = mbuf_write_u32(mb, htonl(src));\n\n\t/* add all SDES items */\n\twhile (itemc-- && !err) {\n\t\tconst uint8_t type = va_arg(ap, int);\n\t\tconst char *v = va_arg(ap, const char *);\n\t\tsize_t len;\n\t\tif (!v)\n\t\t\tcontinue;\n\n\t\tlen = strlen(v); /* note: max 255 chars */\n\t\tif (len > 255) {\n\t\t\terr = EINVAL;\n\t\t\tgoto out;\n\t\t}\n\n\t\terr  = mbuf_write_u8(mb, type);\n\t\terr |= mbuf_write_u8(mb, len & 0xff);\n\t\terr |= mbuf_write_mem(mb, (uint8_t *)v, len);\n\t}\n\n\t/* END padding */\n\terr |= mbuf_write_u8(mb, RTCP_SDES_END);\n\twhile ((mb->pos - start) & 0x3)\n\t\terr |= mbuf_write_u8(mb, RTCP_SDES_END);\n\n out:\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Decode SDES items from a buffer\n *\n * @param mb   Buffer to decode from\n * @param sdes RTCP SDES to decode into\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtcp_sdes_decode(struct mbuf *mb, struct rtcp_sdes *sdes)\n{\n\tsize_t start;\n\n\tif (!sdes)\n\t\treturn EINVAL;\n\tif (mbuf_get_left(mb) < RTCP_SRC_SIZE)\n\t\treturn EBADMSG;\n\n\tstart = mb->pos;\n\tsdes->src = ntohl(mbuf_read_u32(mb));\n\n\t/* Decode all SDES items */\n\twhile (mbuf_get_left(mb) >= RTCP_SDES_MIN_SIZE) {\n\t\tuint8_t type;\n\t\tstruct rtcp_sdes_item *item;\n\n\t\ttype = mbuf_read_u8(mb);\n\t\tif (type == RTCP_SDES_END)\n\t\t\tbreak;\n\n\t\tif (mbuf_get_left(mb) < 1)\n\t\t\treturn EBADMSG;\n\n\t\tif (!sdes->itemv) {\n\t\t\tsdes->itemv = mem_alloc(sizeof(*sdes->itemv), NULL);\n\t\t\tif (!sdes->itemv)\n\t\t\t\treturn ENOMEM;\n\t\t}\n\t\telse {\n\t\t\tconst size_t sz = (sdes->n + 1) * sizeof(*sdes->itemv);\n\t\t\tstruct rtcp_sdes_item *itemv;\n\n\t\t\titemv = mem_realloc(sdes->itemv, sz);\n\t\t\tif (!itemv)\n\t\t\t\treturn ENOMEM;\n\n\t\t\tsdes->itemv = itemv;\n\t\t}\n\n\t\titem = &sdes->itemv[sdes->n];\n\n\t\titem->type = (enum rtcp_sdes_type)type;\n\t\titem->length = mbuf_read_u8(mb);\n\t\tif (mbuf_get_left(mb) < item->length)\n\t\t\treturn EBADMSG;\n\t\titem->data = mem_alloc(item->length, NULL);\n\t\tif (!item->data)\n\t\t\treturn ENOMEM;\n\t\t(void)mbuf_read_mem(mb, (uint8_t *)item->data, item->length);\n\n\t\tsdes->n++;\n\t}\n\n\t/* slurp padding */\n\twhile ((mb->pos - start) & 0x3 && mbuf_get_left(mb))\n\t\t++mb->pos;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/rtp/sess.c",
    "content": "/**\n * @file rtp/sess.c  Real-time Transport Control Protocol Session\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#ifdef HAVE_SYS_TIME_H\n#include <sys/time.h>\n#endif\n#include <time.h>\n#ifdef WIN32\n#include <winsock2.h>\n#endif\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_thread.h>\n#include <re_rtp.h>\n#include \"rtcp.h\"\n\n\n#define DEBUG_MODULE \"rtcp_sess\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** RTP protocol values */\nenum {\n\tRTCP_INTERVAL = 5000,  /**< Interval in [ms] between sending reports */\n\tMAX_MEMBERS   = 8,\n};\n\n/** RTP Transmit stats */\nstruct txstat {\n\tuint32_t psent;      /**< Total number of RTP packets sent */\n\tuint32_t osent;      /**< Total number of RTP octets  sent */\n\tuint64_t jfs_rt_ref; /**< Realtime clock timer ticks at RTP timestamp\n\t                      *   reference */\n\tuint32_t ts_ref;     /**< RTP timestamp reference (transmit)     */\n\tbool ts_synced;      /**< RTP timestamp synchronization flag     */\n};\n\n/** RTCP Session */\nstruct rtcp_sess {\n\tstruct rtp_sock *rs;        /**< RTP Socket                          */\n\tstruct hash *members;       /**< Member table                        */\n\tstruct tmr tmr;             /**< Event sender timer                  */\n\tchar *cname;                /**< Canonical Name                      */\n\tuint32_t memberc;           /**< Number of members                   */\n\tuint32_t senderc;           /**< Number of senders                   */\n\tuint32_t srate_tx;          /**< Transmit sampling rate              */\n\tuint32_t srate_rx;          /**< Receive sampling rate               */\n\tuint32_t interval;          /**< RTCP interval in [ms]               */\n\tmtx_t *lock;                /**< Lock for rtcp_sess                  */\n\n\t/* stats */\n\tstruct txstat txstat;       /**< Local transmit statistics           */\n};\n\n\n/* Prototypes */\nstatic void schedule(struct rtcp_sess *sess);\n\n\nstatic void sess_destructor(void *data)\n{\n\tstruct rtcp_sess *sess = data;\n\n\ttmr_cancel(&sess->tmr);\n\n\tmem_deref(sess->cname);\n\thash_flush(sess->members);\n\tmem_deref(sess->members);\n\tmem_deref(sess->lock);\n}\n\n\nstatic struct rtp_member *get_member(struct rtcp_sess *sess, uint32_t src)\n{\n\tstruct rtp_member *mbr;\n\n\tmbr = rtp_member_find(sess->members, src);\n\tif (mbr)\n\t\treturn mbr;\n\n\tif (sess->memberc >= MAX_MEMBERS)\n\t\treturn NULL;\n\n\tmbr = rtp_member_add(sess->members, src);\n\tif (!mbr)\n\t\treturn NULL;\n\n\t++sess->memberc;\n\n\treturn mbr;\n}\n\n\n/**\n * Calculate Round-Trip Time in [microseconds]\n *\n * @param rtt  Calculated Rount-Trip time [us]\n * @param lsr  Last SR packet from this source [compact NTP timestamp]\n * @param dlsr Delay since last SR packet [units of 1/65536 seconds]\n */\nvoid rtcp_calc_rtt(uint32_t *rtt, uint32_t lsr, uint32_t dlsr)\n{\n\tstruct rtp_ntp_time ntp_time;\n\tuint64_t a_us, lsr_us, dlsr_us;\n\n\tntp_time_get(&ntp_time, NULL);\n\n\ta_us    = ntp_compact2us(ntp_compact(&ntp_time));\n\tlsr_us  = ntp_compact2us(lsr);\n\tdlsr_us = 1000000ULL * dlsr / 65536;\n\n\t/* RTT delay is (A - LSR - DLSR) */\n\tif (rtt)\n\t\t*rtt = MAX((int)(a_us - lsr_us - dlsr_us), 0);\n}\n\n\n/** Decode Reception Report block */\nstatic void handle_rr_block(struct rtcp_sess *sess, struct rtp_member *mbr,\n\t\t\t    const struct rtcp_rr *rr)\n{\n\t/* Lost */\n\tmbr->cum_lost = rr->lost;\n\n\t/* Interarrival jitter */\n\tif (sess->srate_tx)\n\t\tmbr->jit = 1000000 * rr->jitter / sess->srate_tx;\n\n\t/* round-trip propagation delay as (A - LSR - DLSR) */\n\tif (rr->lsr && rr->dlsr)\n\t\trtcp_calc_rtt(&mbr->rtt, rr->lsr, rr->dlsr);\n}\n\n\n/** Handle incoming RR (Receiver Report) packet */\nstatic void handle_incoming_rr(struct rtcp_sess *sess,\n\t\t\t       const struct rtcp_msg *msg)\n{\n\tstruct rtp_member *mbr;\n\tuint32_t i;\n\n\tmbr = get_member(sess, msg->r.rr.ssrc);\n\tif (!mbr)\n\t\treturn;\n\n\tfor (i=0; i<msg->hdr.count; i++)\n\t\thandle_rr_block(sess, mbr, &msg->r.rr.rrv[i]);\n}\n\n\n/** Handle incoming SR (Sender Report) packet */\nstatic void handle_incoming_sr(struct rtcp_sess *sess,\n\t\t\t       const struct rtcp_msg *msg)\n{\n\tstruct rtp_member *mbr;\n\tuint32_t i;\n\n\tmbr = get_member(sess, msg->r.sr.ssrc);\n\tif (!mbr) {\n\t\tDEBUG_WARNING(\"0x%08x: could not add member\\n\",\n\t\t\t      msg->r.sr.ssrc);\n\t\treturn;\n\t}\n\n\tif (mbr->s) {\n\t\t/* Save time when SR was received */\n\t\tmbr->s->sr_recv = tmr_jiffies();\n\n\t\t/* Save NTP timestamp from SR */\n\t\tmbr->s->last_sr.hi = msg->r.sr.ntp_sec;\n\t\tmbr->s->last_sr.lo = msg->r.sr.ntp_frac;\n\t\tmbr->s->rtp_ts     = msg->r.sr.rtp_ts;\n\t\tmbr->s->psent      = msg->r.sr.psent;\n\t\tmbr->s->osent      = msg->r.sr.osent;\n\t}\n\n\tfor (i=0; i<msg->hdr.count; i++)\n\t\thandle_rr_block(sess, mbr, &msg->r.sr.rrv[i]);\n}\n\n\nstatic void handle_incoming_bye(struct rtcp_sess *sess,\n\t\t\t\tconst struct rtcp_msg *msg)\n{\n\tuint32_t i;\n\n\tfor (i=0; i<msg->hdr.count; i++) {\n\n\t\tstruct rtp_member *mbr;\n\n\t\tmbr = rtp_member_find(sess->members, msg->r.bye.srcv[i]);\n\t\tif (mbr) {\n\t\t\tif (mbr->s)\n\t\t\t\t--sess->senderc;\n\n\t\t\t--sess->memberc;\n\t\t\tmem_deref(mbr);\n\t\t}\n\t}\n}\n\n\nvoid rtcp_handler(struct rtcp_sess *sess, struct rtcp_msg *msg)\n{\n\tif (!sess || !msg)\n\t\treturn;\n\n\tmtx_lock(sess->lock);\n\tswitch (msg->hdr.pt) {\n\n\tcase RTCP_SR:\n\t\thandle_incoming_sr(sess, msg);\n\t\tbreak;\n\n\tcase RTCP_RR:\n\t\thandle_incoming_rr(sess, msg);\n\t\tbreak;\n\n\tcase RTCP_BYE:\n\t\thandle_incoming_bye(sess, msg);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\tmtx_unlock(sess->lock);\n}\n\n\nint rtcp_sess_alloc(struct rtcp_sess **sessp, struct rtp_sock *rs)\n{\n\tstruct rtcp_sess *sess;\n\tint err;\n\n\tif (!sessp)\n\t\treturn EINVAL;\n\n\tsess = mem_zalloc(sizeof(*sess), sess_destructor);\n\tif (!sess)\n\t\treturn ENOMEM;\n\n\tsess->rs = rs;\n\ttmr_init(&sess->tmr);\n\n\tsess->interval = RTCP_INTERVAL;\n\n\terr = mutex_alloc(&sess->lock);\n\tif (err)\n\t\tgoto out;\n\n\terr  = hash_alloc(&sess->members, MAX_MEMBERS);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(sess);\n\telse\n\t\t*sessp = sess;\n\n\treturn err;\n}\n\n\n/**\n * Set interval between sending reports on an RTCP Session\n *\n * @param rs  RTP Socket\n * @param n   RTCP interval in [ms]\n */\nvoid rtcp_set_interval(struct rtp_sock *rs, uint32_t n)\n{\n\tstruct rtcp_sess *sess = rtp_rtcp_sess(rs);\n\tif (!sess)\n\t\treturn;\n\n\tsess->interval = n;\n}\n\n\n/**\n * Set the Sampling-rate on an RTCP Session\n *\n * @param rs       RTP Socket\n * @param srate_tx Transmit samplerate\n * @param srate_rx Receive samplerate\n */\nvoid rtcp_set_srate(struct rtp_sock *rs, uint32_t srate_tx, uint32_t srate_rx)\n{\n\tstruct rtcp_sess *sess = rtp_rtcp_sess(rs);\n\tif (!sess)\n\t\treturn;\n\n\tmtx_lock(sess->lock);\n\tsess->srate_tx = srate_tx;\n\tsess->srate_rx = srate_rx;\n\tmtx_unlock(sess->lock);\n}\n\n\n/**\n * Set the transmit Sampling-rate on an RTCP Session\n *\n * @param rs       RTP Socket\n * @param srate_tx Transmit samplerate\n */\nvoid rtcp_set_srate_tx(struct rtp_sock *rs, uint32_t srate_tx)\n{\n\tstruct rtcp_sess *sess = rtp_rtcp_sess(rs);\n\tif (!sess)\n\t\treturn;\n\n\tmtx_lock(sess->lock);\n\tsess->srate_tx = srate_tx;\n\tmtx_unlock(sess->lock);\n}\n\n\n/**\n * Set the receive Sampling-rate on an RTCP Session\n *\n * @param rs       RTP Socket\n * @param srate_rx Receive samplerate\n */\nvoid rtcp_set_srate_rx(struct rtp_sock *rs, uint32_t srate_rx)\n{\n\tstruct rtcp_sess *sess = rtp_rtcp_sess(rs);\n\tif (!sess)\n\t\treturn;\n\n\tmtx_lock(sess->lock);\n\tsess->srate_rx = srate_rx;\n\tmtx_unlock(sess->lock);\n}\n\n\nint rtcp_enable(struct rtcp_sess *sess, bool enabled, const char *cname)\n{\n\tint err;\n\n\tif (!sess)\n\t\treturn EINVAL;\n\n\tmtx_lock(sess->lock);\n\tsess->cname = mem_deref(sess->cname);\n\terr = str_dup(&sess->cname, cname);\n\tmtx_unlock(sess->lock);\n\tif (err)\n\t\treturn err;\n\n\tif (enabled)\n\t\tschedule(sess);\n\telse\n\t\ttmr_cancel(&sess->tmr);\n\n\treturn 0;\n}\n\n\n/** Calculate LSR (middle 32 bits out of 64 in the NTP timestamp) */\nstatic uint32_t calc_lsr(const struct rtp_ntp_time *last_sr)\n{\n\treturn last_sr->hi ? ntp_compact(last_sr) : 0;\n}\n\n\nstatic uint32_t calc_dlsr(uint64_t sr_recv)\n{\n\tif (sr_recv) {\n\t\tconst uint64_t diff = tmr_jiffies() - sr_recv;\n\t\treturn (uint32_t)((65536 * diff) / 1000);\n\t}\n\telse {\n\t\treturn 0;\n\t}\n}\n\n\nstatic bool sender_apply_handler(struct le *le, void *arg)\n{\n\tstruct rtp_member *mbr = le->data;\n\tstruct rtp_source *s = mbr->s;\n\tstruct mbuf *mb = arg;\n\tstruct rtcp_rr rr;\n\n\tif (!s)\n\t\treturn false;\n\n\t/* Initialise the members */\n\trr.ssrc     = mbr->src;\n\trr.fraction = rtp_source_calc_fraction_lost(s);\n\trr.lost     = rtp_source_calc_lost(s);\n\trr.last_seq = s->cycles | s->max_seq;\n\trr.jitter   = s->jitter >> 4;\n\trr.lsr      = calc_lsr(&s->last_sr);\n\trr.dlsr     = calc_dlsr(s->sr_recv);\n\n\treturn 0 != rtcp_rr_encode(mb, &rr);\n}\n\n\nstatic int encode_handler(struct mbuf *mb, void *arg)\n{\n\tstruct hash *members = arg;\n\n\t/* copy all report blocks */\n\tif (hash_apply(members, sender_apply_handler, mb))\n\t\treturn ENOMEM;\n\n\treturn 0;\n}\n\n\n/** Create a Sender Report */\nstatic int mk_sr(struct rtcp_sess *sess, struct mbuf *mb)\n{\n\tstruct txstat txstat;\n\tuint32_t srate_tx;\n\tint err;\n\n\tmtx_lock(sess->lock);\n\ttxstat = sess->txstat;\n\tsrate_tx = sess->srate_tx;\n\tsess->txstat.ts_synced = false;\n\tmtx_unlock(sess->lock);\n\n\tif (txstat.jfs_rt_ref) {\n\t\tstruct rtp_ntp_time ntp;\n\t\tuint64_t jfs_rt, dur;\n\t\tuint32_t rtp_ts;\n\n\t\tntp_time_get(&ntp, &jfs_rt);\n\n\t\tdur = jfs_rt - txstat.jfs_rt_ref;\n\t\trtp_ts = (uint32_t)((uint64_t)txstat.ts_ref + dur *\n\t\t\t\t    srate_tx / 1000000u);\n\n\t\terr = rtcp_encode(mb, RTCP_SR, sess->senderc,\n\t\t\t\t  rtp_sess_ssrc(sess->rs), ntp.hi, ntp.lo,\n\t\t\t\t  rtp_ts, txstat.psent, txstat.osent,\n\t\t\t\t  encode_handler, sess->members);\n\t}\n\telse {\n\t\t/* No packets were sent yet, no NTP/RTP timestamps available,\n\t\t * generate receiver report */\n\t\terr = rtcp_encode(mb, RTCP_RR, sess->senderc,\n\t\t\t\t  rtp_sess_ssrc(sess->rs),\n\t\t\t\t  encode_handler, sess->members);\n\t}\n\n\treturn err;\n}\n\n\nint rtcp_make_sr(const struct rtp_sock *rs, struct mbuf *mb)\n{\n\tstruct rtcp_sess *sess = rtp_rtcp_sess(rs);\n\tif (!sess)\n\t\treturn EINVAL;\n\n\treturn mk_sr(sess, mb);\n}\n\n\nstatic int sdes_encode_handler(struct mbuf *mb, void *arg)\n{\n\tstruct rtcp_sess *sess = arg;\n\tint err;\n\n\tmtx_lock(sess->lock);\n\terr = rtcp_sdes_encode(mb, rtp_sess_ssrc(sess->rs), 1,\n\t\t\t\tRTCP_SDES_CNAME, sess->cname);\n\tmtx_unlock(sess->lock);\n\n\treturn err;\n}\n\n\nstatic int mk_sdes(struct rtcp_sess *sess, struct mbuf *mb)\n{\n\treturn rtcp_encode(mb, RTCP_SDES, 1, sdes_encode_handler, sess);\n}\n\n\nint rtcp_make_sdes_cname(const struct rtp_sock *rs, struct mbuf *mb)\n{\n\tstruct rtcp_sess *sess = rtp_rtcp_sess(rs);\n\tif (!sess)\n\t\treturn EINVAL;\n\n\treturn mk_sdes(sess, mb);\n}\n\n\nstatic int send_rtcp_report(struct rtcp_sess *sess)\n{\n\tstruct mbuf *mb;\n\tint err;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tmb->pos = RTCP_HEADROOM;\n\n\terr  = mk_sr(sess, mb);\n\terr |= mk_sdes(sess, mb);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = RTCP_HEADROOM;\n\n\terr = rtcp_send(sess->rs, mb);\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nint rtcp_send_bye_packet(struct rtp_sock *rs)\n{\n\tif (!rs)\n\t\treturn EINVAL;\n\n\tstruct rtcp_sess *sess = rtp_rtcp_sess(rs);\n\tconst uint32_t ssrc = rtp_sess_ssrc(rs);\n\tstruct mbuf *mb;\n\tint err;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tmb->pos = RTCP_HEADROOM;\n\n\terr  = rtcp_encode(mb, RTCP_BYE, 1, &ssrc, \"Adjo\");\n\terr |= mk_sdes(sess, mb);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = RTCP_HEADROOM;\n\n\terr = rtcp_send(sess->rs, mb);\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstatic void timeout(void *arg)\n{\n\tstruct rtcp_sess *sess = arg;\n\tint err;\n\n\terr = send_rtcp_report(sess);\n\tif (err) {\n\t\tDEBUG_WARNING(\"Send RTCP report failed: %m\\n\", err);\n\t}\n\n\tschedule(sess);\n}\n\n\nstatic void schedule(struct rtcp_sess *sess)\n{\n\ttmr_start(&sess->tmr, sess->interval, timeout, sess);\n}\n\n\nvoid rtcp_schedule_report(const struct rtp_sock *rs)\n{\n\tstruct rtcp_sess *sess = rtp_rtcp_sess(rs);\n\tif (!sess)\n\t\treturn;\n\n\ttmr_start(&sess->tmr, sess->interval, timeout, sess);\n}\n\n\nvoid rtcp_sess_tx_rtp(struct rtcp_sess *sess, uint32_t ts, uint64_t jfs_rt,\n\t\t\t   size_t payload_size)\n{\n\tif (!sess)\n\t\treturn;\n\n\tmtx_lock(sess->lock);\n\n\tsess->txstat.osent += (uint32_t)payload_size;\n\tsess->txstat.psent += 1;\n\n\tif (!sess->txstat.ts_synced) {\n\t\tsess->txstat.jfs_rt_ref = jfs_rt;\n\t\tsess->txstat.ts_ref     = ts;\n\t\tsess->txstat.ts_synced  = true;\n\t}\n\n\tmtx_unlock(sess->lock);\n}\n\n\nvoid rtcp_sess_rx_rtp(struct rtcp_sess *sess, struct rtp_header *hdr,\n\t\t      size_t payload_size, const struct sa *peer)\n{\n\tstruct rtp_member *mbr;\n\n\tif (!sess)\n\t\treturn;\n\n\tmtx_lock(sess->lock);\n\tmbr = get_member(sess, hdr->ssrc);\n\tif (!mbr) {\n\t\tDEBUG_NOTICE(\"could not add member: 0x%08x\\n\", hdr->ssrc);\n\t\tgoto out;\n\t}\n\n\tif (!mbr->s) {\n\t\tmbr->s = mem_zalloc(sizeof(*mbr->s), NULL);\n\t\tif (!mbr->s) {\n\t\t\tDEBUG_NOTICE(\"could not add sender: 0x%08x\\n\",\n\t\t\t\t     hdr->ssrc);\n\t\t\tgoto out;\n\t\t}\n\n\t\t/* first packet - init sequence number */\n\t\trtp_source_init_seq(mbr->s, hdr->seq);\n\t\t/* probation not used */\n\t\tsa_cpy(&mbr->s->rtp_peer, peer);\n\t\t++sess->senderc;\n\t}\n\n\tif (!rtp_source_update_seq(mbr->s, hdr->seq)) {\n\t\tDEBUG_WARNING(\"rtp_source_update_seq() returned 0\\n\");\n\t}\n\n\tif (sess->srate_rx) {\n\t\t/* Convert from wall-clock time to timestamp units */\n\t\thdr->ts_arrive = tmr_jiffies() * (sess->srate_rx / 1000);\n\n\t\t/*\n\t\t * Calculate jitter only when the timestamp is different than\n\t\t * last packet (see RTP FAQ\n\t\t * https://www.cs.columbia.edu/~hgs/rtp/faq.html#jitter).\n\t\t */\n\t\tif (hdr->ts != mbr->s->last_rtp_ts)\n\t\t\trtp_source_calc_jitter(mbr->s, hdr->ts,\n\t\t\t\t\t   (uint32_t)hdr->ts_arrive);\n\t}\n\n\tmbr->s->last_rtp_ts = hdr->ts;\n\tmbr->s->rtp_rx_bytes += payload_size;\nout:\n\tmtx_unlock(sess->lock);\n}\n\n\n/**\n * Get the RTCP Statistics for a source\n *\n * @param rs    RTP Socket\n * @param ssrc  Synchronization source\n * @param stats RTCP Statistics, set on return\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtcp_stats(struct rtp_sock *rs, uint32_t ssrc, struct rtcp_stats *stats)\n{\n\tconst struct rtcp_sess *sess = rtp_rtcp_sess(rs);\n\tstruct rtp_member *mbr;\n\tint err = 0;\n\n\tif (!sess || !stats)\n\t\treturn EINVAL;\n\n\tmtx_lock(sess->lock);\n\tmbr = rtp_member_find(sess->members, ssrc);\n\tif (!mbr) {\n\t\terr = ENOENT;\n\t\tgoto out;\n\t}\n\n\tstats->tx.sent = sess->txstat.psent;\n\n\tstats->tx.lost = mbr->cum_lost;\n\tstats->tx.jit  = mbr->jit;\n\n\tstats->rtt = mbr->rtt;\n\n\tif (!mbr->s) {\n\t\tmemset(&stats->rx, 0, sizeof(stats->rx));\n\t\tgoto out;\n\t}\n\n\tstats->rx.sent = mbr->s->received;\n\tstats->rx.lost = rtp_source_calc_lost(mbr->s);\n\tstats->rx.jit  = sess->srate_rx ?\n\t\t1000000 * (mbr->s->jitter>>4) / sess->srate_rx : 0;\n\nout:\n\tmtx_unlock(sess->lock);\n\treturn err;\n}\n\n\nstatic bool debug_handler(struct le *le, void *arg)\n{\n\tconst struct rtp_member *mbr = le->data;\n\tstruct mbuf *mb = arg;\n\tint err;\n\n\terr = mbuf_printf(mb, \"  member 0x%08x: lost=%d Jitter=%.1fms\"\n\t\t\t  \" RTT=%.1fms\\n\", mbr->src, mbr->cum_lost,\n\t\t\t  (double)mbr->jit/1000, (double)mbr->rtt/1000);\n\tif (mbr->s) {\n\t\terr |= mbuf_printf(mb,\n\t\t\t\t   \"                 IP=%J psent=%u rcvd=%u\\n\",\n\t\t\t\t   &mbr->s->rtp_peer, mbr->s->psent,\n\t\t\t\t   mbr->s->received);\n\t}\n\n\treturn err != 0;\n}\n\n\n/**\n * RTCP Debug handler, use with fmt %H\n *\n * @param pf Print function\n * @param rs RTP Socket\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtcp_debug(struct re_printf *pf, const struct rtp_sock *rs)\n{\n\tconst struct rtcp_sess *sess = rtp_rtcp_sess(rs);\n\tstruct mbuf *mb;\n\tint err = 0;\n\n\tif (!sess)\n\t\treturn 0;\n\n\tmb = mbuf_alloc(64);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr |= mbuf_printf(mb, \"----- RTCP Session: -----\\n\");\n\tmtx_lock(sess->lock);\n\terr |= mbuf_printf(mb, \"  cname=%s SSRC=0x%08x/%u rx=%uHz\\n\",\n\t\t\t  sess->cname,\n\t\t\t  rtp_sess_ssrc(sess->rs), rtp_sess_ssrc(sess->rs),\n\t\t\t  sess->srate_rx);\n\n\thash_apply(sess->members, debug_handler, mb);\n\n\terr |= mbuf_printf(mb, \"  TX: packets=%u, octets=%u\\n\",\n\t\t\t  sess->txstat.psent, sess->txstat.osent);\n\tmtx_unlock(sess->lock);\n\n\tif (err)\n\t\tgoto out;\n\n\terr = re_hprintf(pf, \"%b\", mb->buf, mb->pos);\n\nout:\n\tmem_deref(mb);\n\treturn err;\n}\n"
  },
  {
    "path": "src/rtp/source.c",
    "content": "/**\n * @file source.c  Real-time Transport Control Protocol source\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_sa.h>\n#include <re_rtp.h>\n#include \"rtcp.h\"\n\n\nenum {\n\tRTP_SEQ_MOD = 1<<16,\n};\n\n\nvoid rtp_source_init_seq(struct rtp_source *s, uint16_t seq)\n{\n\tif (!s)\n\t\treturn;\n\n\ts->base_seq = seq;\n\ts->max_seq = seq;\n\ts->bad_seq = RTP_SEQ_MOD + 1;   /* so seq == bad_seq is false */\n\ts->cycles = 0;\n\ts->received = 0;\n\ts->received_prior = 0;\n\ts->expected_prior = 0;\n\t/* other initialization */\n}\n\n\n/*\n * See RFC 3550 - A.1 RTP Data Header Validity Checks\n */\nint rtp_source_update_seq(struct rtp_source *s, uint16_t seq)\n{\n\tuint16_t udelta = seq - s->max_seq;\n\tconst int MAX_DROPOUT = 3000;\n\tconst int MAX_MISORDER = 100;\n\tconst int MIN_SEQUENTIAL = 2;\n\n\t/*\n\t * Source is not valid until MIN_SEQUENTIAL packets with\n\t * sequential sequence numbers have been received.\n\t */\n\tif (s->probation) {\n\n\t\t/* packet is in sequence */\n\t\tif (seq == s->max_seq + 1) {\n\t\t\ts->probation--;\n\t\t\ts->max_seq = seq;\n\t\t\tif (s->probation == 0) {\n\t\t\t\trtp_source_init_seq(s, seq);\n\t\t\t\ts->received++;\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\ts->probation = MIN_SEQUENTIAL - 1;\n\t\t\ts->max_seq = seq;\n\t\t}\n\t\treturn 0;\n\t}\n\telse if (udelta < MAX_DROPOUT) {\n\n\t\t/* in order, with permissible gap */\n\t\tif (seq < s->max_seq) {\n\t\t\t/*\n\t\t\t * Sequence number wrapped - count another 64K cycle.\n\t\t\t */\n\t\t\ts->cycles += RTP_SEQ_MOD;\n\t\t}\n\t\ts->max_seq = seq;\n\t}\n\telse if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) {\n\n\t\t/* the sequence number made a very large jump */\n\t\tif (seq == s->bad_seq) {\n\t\t\t/*\n\t\t\t * Two sequential packets -- assume that the other side\n\t\t\t * restarted without telling us so just re-sync\n\t\t\t * (i.e., pretend this was the first packet).\n\t\t\t */\n\t\t\trtp_source_init_seq(s, seq);\n\t\t}\n\t\telse {\n\t\t\ts->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);\n\t\t\treturn 0;\n\t\t}\n\t}\n\telse {\n\t\t/* duplicate or reordered packet */\n\t}\n\n\ts->received++;\n\treturn 1;\n}\n\n\n/* RFC 3550 A.8\n *\n * The inputs are:\n *\n *     rtp_ts:  the timestamp from the incoming RTP packet\n *     arrival: the current time in the same units.\n */\nvoid rtp_source_calc_jitter(struct rtp_source *s, uint32_t rtp_ts,\n\t\t\tuint32_t arrival)\n{\n\tconst int transit = arrival - rtp_ts;\n\tint d = transit - s->transit;\n\n\tif (!s->transit) {\n\t\ts->transit = transit;\n\t\treturn;\n\t}\n\n\ts->transit = transit;\n\n\tif (d < 0)\n\t\td = -d;\n\n\ts->jitter += d - ((s->jitter + 8) >> 4);\n}\n\n\n/* A.3 */\nint rtp_source_calc_lost(const struct rtp_source *s)\n{\n\tint extended_max = s->cycles + s->max_seq;\n\tint expected = extended_max - s->base_seq + 1;\n\tint lost;\n\n\tlost = expected - s->received;\n\n\t/* Clamp at 24 bits */\n\tif (lost > 0x7fffff)\n\t\tlost = 0x7fffff;\n\telse if (lost < -0x7fffff)\n\t\tlost = -0x7fffff;\n\n\treturn lost;\n}\n\n\n/* A.3 */\nuint8_t rtp_source_calc_fraction_lost(struct rtp_source *s)\n{\n\tint extended_max = s->cycles + s->max_seq;\n\tint expected = extended_max - s->base_seq + 1;\n\tint expected_interval = expected - s->expected_prior;\n\tint received_interval;\n\tint lost_interval;\n\tuint8_t fraction;\n\n\ts->expected_prior = expected;\n\n\treceived_interval = s->received - s->received_prior;\n\n\ts->received_prior = s->received;\n\n\tlost_interval = expected_interval - received_interval;\n\n\tif (expected_interval == 0 || lost_interval <= 0)\n\t\tfraction = 0;\n\telse\n\t\tfraction = (lost_interval << 8) / expected_interval;\n\n\treturn fraction;\n}\n"
  },
  {
    "path": "src/rtpext/rtpext.c",
    "content": "/**\n * @file rtpext.c  RTP Header Extensions\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_fmt.h>\n#include <re_rtpext.h>\n\n\n#define DEBUG_MODULE \"rtpext\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/*\n * RFC 8285 A General Mechanism for RTP Header Extensions\n *\n * - One-Byte Header:  Supported\n * - Two-Byte Header:  Supported\n *\n * https://datatracker.ietf.org/doc/html/rfc8285\n */\n\n\n/**\n * Encode the One-Byte header for all RTP extensions\n *\n * @param mb        Buffer to encode into\n * @param num_bytes Total size for all RTP extensions\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtpext_hdr_encode(struct mbuf *mb, size_t num_bytes)\n{\n\tint err = 0;\n\n\tif (!mb || !num_bytes)\n\t\treturn EINVAL;\n\n\tif (num_bytes & 0x3) {\n\t\tDEBUG_WARNING(\"hdr_encode: num_bytes (%zu) must be multiple\"\n\t\t\t      \" of 4\\n\", num_bytes);\n\t\treturn EINVAL;\n\t}\n\n\terr |= mbuf_write_u16(mb, htons(RTPEXT_TYPE_MAGIC));\n\terr |= mbuf_write_u16(mb, htons((uint16_t)(num_bytes / 4)));\n\n\treturn err;\n}\n\n\n/**\n * Encode the Two-Byte header for all RTP extensions\n *\n * @param mb        Buffer to encode into\n * @param num_bytes Total size for all RTP extensions (multiple of 4)\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtpext_hdr_encode_long(struct mbuf *mb, size_t num_bytes)\n{\n\tint err = 0;\n\n\tif (!mb || !num_bytes)\n\t\treturn EINVAL;\n\n\tif (num_bytes & 0x3) {\n\t\tDEBUG_WARNING(\"hdr_encode: num_bytes (%zu) must be multiple\"\n\t\t\t      \" of 4\\n\", num_bytes);\n\t\treturn EINVAL;\n\t}\n\n\terr |= mbuf_write_u16(mb, htons(RTPEXT_TYPE_MAGIC_LONG));\n\terr |= mbuf_write_u16(mb, htons((uint16_t)(num_bytes / 4)));\n\n\treturn err;\n}\n\n\n/**\n * Encode an RTP header extension with One-Byte header\n *\n * @param mb   Buffer to encode into\n * @param id   Identifier\n * @param len  Length of data field\n * @param data Data bytes\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtpext_encode(struct mbuf *mb, uint8_t id, size_t len,\n\t\t  const uint8_t *data)\n{\n\tsize_t start;\n\tint err;\n\n\tif (!mb || !data)\n\t\treturn EINVAL;\n\n\tif (id < RTPEXT_ID_MIN || id > RTPEXT_ID_MAX)\n\t\treturn EINVAL;\n\tif (len < RTPEXT_LEN_MIN || len > RTPEXT_LEN_MAX)\n\t\treturn EINVAL;\n\n\tstart = mb->pos;\n\n\terr  = mbuf_write_u8(mb, (uint8_t)(id << 4 | (len-1)));\n\terr |= mbuf_write_mem(mb, data, len);\n\tif (err)\n\t\treturn err;\n\n\t/* padding */\n\twhile ((mb->pos - start) & 0x03)\n\t\terr |= mbuf_write_u8(mb, 0x00);\n\n\treturn err;\n}\n\n\n/**\n * Decode an RTP header extension with One-Byte header\n *\n * @param ext RTP Extension object\n * @param mb  Buffer to decode from\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtpext_decode(struct rtpext *ext, struct mbuf *mb)\n{\n\tuint8_t v;\n\tint err;\n\n\tif (!ext || !mb)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < 1)\n\t\treturn EBADMSG;\n\n\tmemset(ext, 0, sizeof(*ext));\n\n\tv = mbuf_read_u8(mb);\n\n\text->id  = v >> 4;\n\text->len = (v & 0x0f) + 1;\n\n\tif (ext->id < RTPEXT_ID_MIN || ext->id > RTPEXT_ID_MAX) {\n\t\tDEBUG_WARNING(\"decode: invalid ID %u\\n\", ext->id);\n\t\treturn EBADMSG;\n\t}\n\tif (ext->len > mbuf_get_left(mb)) {\n\t\tDEBUG_WARNING(\"decode: short read\\n\");\n\t\treturn ENODATA;\n\t}\n\n\terr = mbuf_read_mem(mb, ext->data, ext->len);\n\tif (err)\n\t\treturn err;\n\n\t/* skip padding */\n\twhile (mbuf_get_left(mb)) {\n\t\tuint8_t pad = mbuf_buf(mb)[0];\n\n\t\tif (pad != 0x00)\n\t\t\tbreak;\n\n\t\tmbuf_advance(mb, 1);\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Encode an RTP header extension with Two-Byte header\n *\n * @param mb   Buffer to encode into\n * @param id   Identifier\n * @param len  Length of data field\n * @param data Data bytes\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtpext_encode_long(struct mbuf *mb, uint8_t id, uint8_t len,\n\t\t       const uint8_t *data)\n{\n\tif (!mb)\n\t\treturn EINVAL;\n\n\tint err  = mbuf_write_u8(mb, id);\n\terr     |= mbuf_write_u8(mb, len);\n\n\tif (data && len)\n\t\terr |= mbuf_write_mem(mb, data, len);\n\n\treturn err;\n}\n\n\n/**\n * Decode an RTP header extension with Two-Byte header\n *\n * @param ext RTP Extension object\n * @param mb  Buffer to decode from\n *\n * @return 0 if success, otherwise errorcode\n */\nint rtpext_decode_long(struct rtpext *ext, struct mbuf *mb)\n{\n\tif (!ext || !mb)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < 2)\n\t\treturn EBADMSG;\n\n\tmemset(ext, 0, sizeof(*ext));\n\n\text->id  = mbuf_read_u8(mb);\n\text->len = mbuf_read_u8(mb);\n\n\tif (ext->id == 0) {\n\t\tDEBUG_WARNING(\"decode_long: invalid ID %u\\n\", ext->id);\n\t\treturn EBADMSG;\n\t}\n\tif (ext->len > mbuf_get_left(mb)) {\n\t\tDEBUG_WARNING(\"decode_long: short read (%zu > %zu)\\n\",\n\t\t\t      ext->len, mbuf_get_left(mb));\n\t\treturn ENODATA;\n\t}\n\n\tint err = mbuf_read_mem(mb, ext->data, ext->len);\n\tif (err)\n\t\treturn err;\n\n\t/* skip padding */\n\twhile (mbuf_get_left(mb)) {\n\t\tuint8_t pad = mbuf_buf(mb)[0];\n\n\t\tif (pad != 0x00)\n\t\t\tbreak;\n\n\t\tmbuf_advance(mb, 1);\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Finds an RTP extension by its ID\n *\n * @param extv Pointer to an array of RTP extensions\n * @param extc Number of elements in the RTP extension array\n * @param id   The ID of the RTP extension to find\n *\n * @return Pointer to the matching RTP extension on success, otherwise NULL\n */\nconst struct rtpext *rtpext_find(const struct rtpext *extv, size_t extc,\n\t\t\t\t uint8_t id)\n{\n\tfor (size_t i = 0; i < extc; i++) {\n\t\tconst struct rtpext *rtpext = &extv[i];\n\n\t\tif (rtpext->id == id)\n\t\t\treturn rtpext;\n\t}\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/sa/printaddr.c",
    "content": "/**\n * @file sa/printaddr.c  Socket Address printing\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#ifdef HAVE_GETIFADDRS\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <net/if.h>\n#endif\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_sa.h>\n\n\n/**\n * Print a Socket Address including IPv6 scope identifier\n *\n * @param pf Print function\n * @param sa Socket Address\n *\n * @return 0 if success, otherwise errorcode\n */\nint sa_print_addr(struct re_printf *pf, const struct sa *sa)\n{\n\tint err;\n\n\tif (!sa)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"%j\", sa);\n\n\tif (sa_af(sa) == AF_INET6 && sa_is_linklocal(sa)) {\n#ifdef HAVE_GETIFADDRS\n\t\tchar ifname[IF_NAMESIZE];\n\n\t\tif (!if_indextoname(sa->u.in6.sin6_scope_id, ifname))\n\t\t\treturn errno;\n\n\t\terr |= re_hprintf(pf, \"%%%s\", ifname);\n#else\n\t\tuint32_t scope_id = sa_scopeid(sa);\n\t\terr |= re_hprintf(pf, \"%%%d\", scope_id);\n#endif\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sa/sa.c",
    "content": "/**\n * @file sa.c  Socket Address\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#ifndef WIN32\n#include <arpa/inet.h>\n#include <netdb.h>\n#endif\n\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_net.h>\n#include <re_list.h>\n#include <re_sa.h>\n\n\n#define DEBUG_MODULE \"sa\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/**\n * Initialize a Socket Address\n *\n * @param sa Socket Address\n * @param af Address Family\n */\nvoid sa_init(struct sa *sa, int af)\n{\n\tif (!sa)\n\t\treturn;\n\n\tmemset(sa, 0, sizeof(*sa));\n\tsa->u.sa.sa_family = af;\n\tsa->len = sizeof(sa->u);\n}\n\n\n/**\n * Set a Socket Address from a PL string\n *\n * @param sa   Socket Address\n * @param addr IP-address\n * @param port Port number\n *\n * @return 0 if success, otherwise errorcode\n */\nint sa_set(struct sa *sa, const struct pl *addr, uint16_t port)\n{\n\tchar buf[64];\n\n\t(void)pl_strcpy(addr, buf, sizeof(buf));\n\treturn sa_set_str(sa, buf, port);\n}\n\n\nint sa_addrinfo(const char *addr, struct sa *sa)\n{\n\tstruct addrinfo *res, *res0 = NULL;\n\tstruct addrinfo hints;\n\tint err = 0;\n\n\tmemset(&hints, 0, sizeof(hints));\n\thints.ai_family = AF_UNSPEC;\n\thints.ai_flags  = AI_ADDRCONFIG | AI_NUMERICHOST;\n\n\tif (getaddrinfo(addr, NULL, &hints, &res0))\n\t\treturn EADDRNOTAVAIL;\n\n\tfor (res = res0; res; res = res->ai_next) {\n\n\t\terr = sa_set_sa(sa, res->ai_addr);\n\t\tif (err)\n\t\t\tcontinue;\n\n\t\tbreak;\n\t}\n\n\tfreeaddrinfo(res0);\n\treturn err;\n}\n\n\n/**\n * Convert character string to a network address structure\n *\n * @param addr IP address string\n * @param sa   Returned socket address\n *\n * @return 0 if success, otherwise errorcode\n */\nint sa_pton(const char *addr, struct sa *sa)\n{\n\tint err = 0;\n\n\tif (!addr || !sa)\n\t\treturn EINVAL;\n\n\tmemset(sa, 0, sizeof(*sa));\n\tif (inet_pton(AF_INET, addr, &sa->u.in.sin_addr) > 0) {\n\t\tsa->u.in.sin_family = AF_INET;\n\t}\n#if HAVE_UNIXSOCK == 1\n\telse if (!strncmp(addr, \"unix:\", 5)) {\n\t\tsa->u.un.sun_family = AF_UNIX;\n\t\tstr_ncpy(sa->u.un.sun_path, addr + 5,\n\t\t\t sizeof(sa->u.un.sun_path));\n\t}\n#endif\n\telse if (!strncmp(addr, \"fe80:\", 5) && strrchr(addr, '%')) {\n\t\terr = sa_addrinfo(addr, sa);\n\t}\n\telse if (inet_pton(AF_INET6, addr, &sa->u.in6.sin6_addr) > 0) {\n\n\t\tif (IN6_IS_ADDR_V4MAPPED(&sa->u.in6.sin6_addr)) {\n\t\t\tconst uint8_t *a = &sa->u.in6.sin6_addr.s6_addr[12];\n\t\t\tsa->u.in.sin_family = AF_INET;\n\t\t\tmemcpy(&sa->u.in.sin_addr.s_addr, a, 4);\n\t\t}\n\t\telse {\n\t\t\tsa->u.in6.sin6_family = AF_INET6;\n\t\t}\n\t}\n\telse {\n\t\treturn EINVAL;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Set a Socket Address from a string\n *\n * @param sa   Socket Address\n * @param addr IP-address or UNIX path\n * @param port Port number\n *\n * @return 0 if success, otherwise errorcode\n */\nint sa_set_str(struct sa *sa, const char *addr, uint16_t port)\n{\n\tint err;\n\n\tif (!sa || !addr)\n\t\treturn EINVAL;\n\n\terr = sa_pton(addr, sa);\n\tif (err)\n\t\treturn err;\n\n\tswitch (sa->u.sa.sa_family) {\n\n#if HAVE_UNIXSOCK == 1\n\tcase AF_UNIX:\n\t\tsa->len = sizeof(struct sockaddr_un);\n\t\tbreak;\n#endif\n\n\tcase AF_INET:\n\t\tsa->u.in.sin_port = htons(port);\n\t\tsa->len = sizeof(struct sockaddr_in);\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tsa->u.in6.sin6_port = htons(port);\n\t\tsa->len = sizeof(struct sockaddr_in6);\n\t\tbreak;\n\n\tdefault:\n\t\treturn EAFNOSUPPORT;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Set a Socket Address from an IPv4 address\n *\n * @param sa   Socket Address\n * @param addr IPv4 address in host order\n * @param port Port number\n */\nvoid sa_set_in(struct sa *sa, uint32_t addr, uint16_t port)\n{\n\tif (!sa)\n\t\treturn;\n\n\tmemset(sa, 0, sizeof(*sa));\n\tsa->u.in.sin_family = AF_INET;\n\tsa->u.in.sin_addr.s_addr = htonl(addr);\n\tsa->u.in.sin_port = htons(port);\n\tsa->len = sizeof(struct sockaddr_in);\n}\n\n\n/**\n * Set a Socket Address from an IPv6 address\n *\n * @param sa   Socket Address\n * @param addr IPv6 address\n * @param port Port number\n */\nvoid sa_set_in6(struct sa *sa, const uint8_t *addr, uint16_t port)\n{\n\tif (!sa)\n\t\treturn;\n\n\tmemset(sa, 0, sizeof(*sa));\n\tsa->u.in6.sin6_family = AF_INET6;\n\tmemcpy(&sa->u.in6.sin6_addr, addr, 16);\n\tsa->u.in6.sin6_port = htons(port);\n\tsa->len = sizeof(struct sockaddr_in6);\n}\n\n\n/**\n * Set a Socket Address from a sockaddr\n *\n * @param sa Socket Address\n * @param s  Sockaddr\n *\n * @return 0 if success, otherwise errorcode\n */\nint sa_set_sa(struct sa *sa, const struct sockaddr *s)\n{\n\tif (!sa || !s)\n\t\treturn EINVAL;\n\n\tmemset(sa, 0, sizeof(*sa));\n\tswitch (s->sa_family) {\n\n\tcase AF_INET:\n\t\tmemcpy(&sa->u.in, s, sizeof(struct sockaddr_in));\n\t\tsa->len = sizeof(struct sockaddr_in);\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tmemcpy(&sa->u.in6, s, sizeof(struct sockaddr_in6));\n\t\tsa->len = sizeof(struct sockaddr_in6);\n\t\tbreak;\n\n\tdefault:\n\t\treturn EAFNOSUPPORT;\n\t}\n\n\tsa->u.sa.sa_family = s->sa_family;\n\n\treturn 0;\n}\n\n\n/**\n * Set the port number on a Socket Address\n *\n * @param sa   Socket Address\n * @param port Port number\n */\nvoid sa_set_port(struct sa *sa, uint16_t port)\n{\n\tif (!sa)\n\t\treturn;\n\n\tswitch (sa->u.sa.sa_family) {\n\n\tcase AF_INET:\n\t\tsa->u.in.sin_port = htons(port);\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tsa->u.in6.sin6_port = htons(port);\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"sa_set_port: no af %d (port %u)\\n\",\n\t\t\t      sa->u.sa.sa_family, port);\n\t\tbreak;\n\t}\n}\n\n\n/**\n * Set a socket address from a string of type \"address:port\"\n * IPv6 addresses must be encapsulated in square brackets.\n *\n * @param sa   Socket Address\n * @param str  Address and port string\n * @param len  Length of string\n *\n * @return 0 if success, otherwise errorcode\n *\n * Example strings:\n *\n * <pre>\n *   1.2.3.4:1234\n *   [::1]:1234\n *   [::]:5060\n * </pre>\n */\nint sa_decode(struct sa *sa, const char *str, size_t len)\n{\n\tstruct pl addr, port, pl;\n\tconst char *c;\n\n\tif (!sa || !str || !len)\n\t\treturn EINVAL;\n\n\tpl.p = str;\n\tpl.l = len;\n\n\tif ('[' == str[0] && (c = pl_strchr(&pl, ']'))) {\n\t\taddr.p = str + 1;\n\t\taddr.l = c - str - 1;\n\t\t++c;\n\t}\n\telse if (NULL != (c = pl_strchr(&pl, ':'))) {\n\t\taddr.p = str;\n\t\taddr.l = c - str;\n\t}\n\telse {\n\t\treturn EINVAL;\n\t}\n\n\tif (len < (size_t)(c - str + 2))\n\t\treturn EINVAL;\n\n\tif (':' != *c)\n\t\treturn EINVAL;\n\n\tport.p = ++c;\n\tport.l = len + str - c;\n\n\treturn sa_set(sa, &addr, pl_u32(&port));\n}\n\n\n/**\n * Get the Address Family of a Socket Address\n *\n * @param sa Socket Address\n *\n * @return Address Family\n */\nint sa_af(const struct sa *sa)\n{\n\treturn sa ? sa->u.sa.sa_family : AF_UNSPEC;\n}\n\n\n/**\n * Get the IPv4-address of a Socket Address\n *\n * @param sa Socket Address\n *\n * @return IPv4 address in host order\n */\nuint32_t sa_in(const struct sa *sa)\n{\n\treturn sa ? ntohl(sa->u.in.sin_addr.s_addr) : 0;\n}\n\n\n/**\n * Get the IPv6-address of a Socket Address\n *\n * @param sa   Socket Address\n * @param addr On return, contains the IPv6-address\n */\nvoid sa_in6(const struct sa *sa, uint8_t *addr)\n{\n\tif (!sa || !addr)\n\t\treturn;\n\n\tmemcpy(addr, &sa->u.in6.sin6_addr, 16);\n}\n\n\n/**\n * Convert a Socket Address to Presentation format\n *\n * @param sa   Socket Address\n * @param buf  Buffer to store presentation format\n * @param size Buffer size\n *\n * @return 0 if success, otherwise errorcode\n */\nint sa_ntop(const struct sa *sa, char *buf, int size)\n{\n\tconst char *ret;\n\n\tif (!sa || !buf || !size)\n\t\treturn EINVAL;\n\n\tswitch (sa->u.sa.sa_family) {\n#if HAVE_UNIXSOCK == 1\n\tcase AF_UNIX:\n\t\tstr_ncpy(buf, sa->u.un.sun_path, size);\n\t\tret = buf;\n\t\tbreak;\n#endif\n\n\tcase AF_INET:\n\t\tret = inet_ntop(AF_INET, &sa->u.in.sin_addr, buf, size);\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tret = inet_ntop(AF_INET6, &sa->u.in6.sin6_addr, buf, size);\n\t\tbreak;\n\n\tdefault:\n\t\treturn EAFNOSUPPORT;\n\t}\n\n\tif (!ret)\n\t\treturn RE_ERRNO_SOCK;\n\n\treturn 0;\n}\n\n\n/**\n * Get the port number from a Socket Address\n *\n * @param sa Socket Address\n *\n * @return Port number  in host order\n */\nuint16_t sa_port(const struct sa *sa)\n{\n\tif (!sa)\n\t\treturn 0;\n\n\tswitch (sa->u.sa.sa_family) {\n\n\tcase AF_INET:\n\t\treturn ntohs(sa->u.in.sin_port);\n\n\tcase AF_INET6:\n\t\treturn ntohs(sa->u.in6.sin6_port);\n\n\tdefault:\n\t\treturn 0;\n\t}\n}\n\n\n/**\n * Check if a Socket Address is set\n *\n * @param sa   Socket Address\n * @param flag Flags specifying which fields to check\n *\n * @return true if set, false if not set\n */\nbool sa_isset(const struct sa *sa, int flag)\n{\n\tif (!sa)\n\t\treturn false;\n\n\tswitch (sa->u.sa.sa_family) {\n\n#if HAVE_UNIXSOCK == 1\n\tcase AF_UNIX:\n\t\treturn str_isset(sa->u.un.sun_path);\n\t\tbreak;\n#endif\n\n\tcase AF_INET:\n\t\tif (flag & SA_ADDR)\n\t\t\tif (INADDR_ANY == sa->u.in.sin_addr.s_addr)\n\t\t\t\treturn false;\n\t\tif (flag & SA_PORT)\n\t\t\tif (0 == sa->u.in.sin_port)\n\t\t\t\treturn false;\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tif (flag & SA_ADDR)\n\t\t\tif (IN6_IS_ADDR_UNSPECIFIED(&sa->u.in6.sin6_addr))\n\t\t\t\treturn false;\n\t\tif (flag & SA_PORT)\n\t\t\tif (0 == sa->u.in6.sin6_port)\n\t\t\t\treturn false;\n\t\tbreak;\n\n\tdefault:\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/**\n * Calculate the hash value of a Socket Address\n *\n * @param sa   Socket Address\n * @param flag Flags specifying which fields to use\n *\n * @return Hash value\n */\nuint32_t sa_hash(const struct sa *sa, int flag)\n{\n\tuint32_t v = 0;\n\n\tif (!sa)\n\t\treturn 0;\n\n\tswitch (sa->u.sa.sa_family) {\n\n\tcase AF_INET:\n\t\tif (flag & SA_ADDR)\n\t\t\tv += ntohl(sa->u.in.sin_addr.s_addr);\n\t\tif (flag & SA_PORT)\n\t\t\tv += ntohs(sa->u.in.sin_port);\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tif (flag & SA_ADDR) {\n\t\t\tuint32_t *a = (uint32_t *)&sa->u.in6.sin6_addr;\n\t\t\tv += a[0] ^ a[1] ^ a[2] ^ a[3];\n\t\t}\n\t\tif (flag & SA_PORT)\n\t\t\tv += ntohs(sa->u.in6.sin6_port);\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"sa_hash: unknown af %d\\n\", sa->u.sa.sa_family);\n\t\treturn 0;\n\t}\n\n\treturn v;\n}\n\n\n/**\n * Copy a Socket Address\n *\n * @param dst Socket Address to be written\n * @param src Socket Address to be copied\n */\nvoid sa_cpy(struct sa *dst, const struct sa *src)\n{\n\tif (!dst || !src)\n\t\treturn;\n\n\tmemcpy(dst, src, sizeof(*dst));\n}\n\n\n/**\n * Compare two Socket Address objects\n *\n * @param l    Socket Address number one\n * @param r    Socket Address number two\n * @param flag Flags specifying which fields to use\n *\n * @return true if match, false if no match\n */\nbool sa_cmp(const struct sa *l, const struct sa *r, int flag)\n{\n\tif (!l || !r)\n\t\treturn false;\n\n\tif (l == r)\n\t\treturn true;\n\n\tif (l->u.sa.sa_family != r->u.sa.sa_family)\n\t\treturn false;\n\n\tswitch (l->u.sa.sa_family) {\n\n#if HAVE_UNIXSOCK == 1\n\tcase AF_UNIX:\n\t\tif (0 == str_cmp(l->u.un.sun_path, r->u.un.sun_path))\n\t\t\treturn true;\n\t\telse\n\t\t\treturn false;\n\t\tbreak;\n#endif\n\n\tcase AF_INET:\n\t\tif (flag & SA_ADDR)\n\t\t\tif (l->u.in.sin_addr.s_addr != r->u.in.sin_addr.s_addr)\n\t\t\t\treturn false;\n\t\tif (flag & SA_PORT)\n\t\t\tif (l->u.in.sin_port != r->u.in.sin_port)\n\t\t\t\treturn false;\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tif (flag & SA_ADDR)\n\t\t\tif (memcmp(&l->u.in6.sin6_addr,\n\t\t\t\t   &r->u.in6.sin6_addr, 16))\n\t\t\t\treturn false;\n\t\tif (flag & SA_PORT)\n\t\t\tif (l->u.in6.sin6_port != r->u.in6.sin6_port)\n\t\t\t\treturn false;\n\t\tbreak;\n\n\tdefault:\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/** IPv4 Link-local test */\n#define IN_IS_ADDR_LINKLOCAL(a)\t\t\t\t\t\\\n\t(((a) & htonl(0xffff0000)) == htonl (0xa9fe0000))\n\n\n/**\n * Check if socket address is a link-local address\n *\n * @param sa Socket address\n *\n * @return true if link-local address, otherwise false\n */\nbool sa_is_linklocal(const struct sa *sa)\n{\n\tif (!sa)\n\t\treturn false;\n\n\tswitch (sa_af(sa)) {\n\n\tcase AF_INET:\n\t\treturn IN_IS_ADDR_LINKLOCAL(sa->u.in.sin_addr.s_addr);\n\n\tcase AF_INET6:\n\t\treturn IN6_IS_ADDR_LINKLOCAL(&sa->u.in6.sin6_addr);\n\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\n/**\n * Check if socket address is a loopback address (127.0.0.0/8 or ::1/128)\n *\n * @param sa Socket address\n *\n * @return true if loopback address, otherwise false\n */\nbool sa_is_loopback(const struct sa *sa)\n{\n\tif (!sa)\n\t\treturn false;\n\n\tswitch (sa_af(sa)) {\n\n\tcase AF_INET:\n\t\treturn (ntohl(sa->u.in.sin_addr.s_addr) & 0xff000000) ==\n\t\t       0x7f000000;\n\n\tcase AF_INET6:\n\t\treturn IN6_IS_ADDR_LOOPBACK(&sa->u.in6.sin6_addr);\n\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n/**\n * Check if socket address is a multicast address\n *\n * @param sa Socket address\n *\n * @return true if multicast address, otherwise false\n */\nbool sa_is_multicast(const struct sa *sa)\n{\n\tif (!sa)\n\t\treturn false;\n\n\tswitch (sa_af(sa)) {\n\n\tcase AF_INET:\n\t\treturn IN_MULTICAST(ntohl(sa->u.in.sin_addr.s_addr));\n\n\tcase AF_INET6:\n\t\treturn IN6_IS_ADDR_MULTICAST(&sa->u.in6.sin6_addr);\n\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n/**\n * Check if socket address is any/unspecified address\n *\n * @param sa Socket address\n *\n * @return true if any address, otherwise false\n */\nbool sa_is_any(const struct sa *sa)\n{\n\tif (!sa)\n\t\treturn false;\n\n\tswitch (sa_af(sa)) {\n\n\tcase AF_INET:\n\t\treturn INADDR_ANY == ntohl(sa->u.in.sin_addr.s_addr);\n\n\tcase AF_INET6:\n\t\treturn IN6_IS_ADDR_UNSPECIFIED(&sa->u.in6.sin6_addr);\n\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\nvoid sa_set_scopeid(struct sa *sa, uint32_t scopeid)\n{\n\tif (!sa)\n\t\treturn;\n\n\tif (sa_af(sa) != AF_INET6)\n\t\treturn;\n\n\tsa->u.in6.sin6_scope_id = scopeid;\n}\n\n\nuint32_t sa_scopeid(const struct sa *sa)\n{\n\tif (!sa)\n\t\treturn 0;\n\n\tif (sa_af(sa) != AF_INET6)\n\t\treturn 0;\n\n\treturn sa->u.in6.sin6_scope_id;\n}\n\n\n/**\n * Get the size of 'struct sa' as compiled by the library\n *\n * @return Size of 'struct sa' in bytes\n */\nsize_t sa_struct_get_size(void)\n{\n\treturn sizeof(struct sa);\n}\n"
  },
  {
    "path": "src/sdp/attr.c",
    "content": "/**\n * @file sdp/attr.c  SDP Attributes\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_sdp.h>\n#include \"sdp.h\"\n\n\nstruct sdp_attr {\n\tstruct le le;\n\tchar *name;\n\tchar *val;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sdp_attr *attr = arg;\n\n\tlist_unlink(&attr->le);\n\tmem_deref(attr->name);\n\tmem_deref(attr->val);\n}\n\n\nint sdp_attr_add(struct list *lst, struct pl *name, struct pl *val)\n{\n\tstruct sdp_attr *attr;\n\tint err;\n\n\tattr = mem_zalloc(sizeof(*attr), destructor);\n\tif (!attr)\n\t\treturn ENOMEM;\n\n\tlist_append(lst, &attr->le, attr);\n\n\terr = pl_strdup(&attr->name, name);\n\n\tif (pl_isset(val))\n\t\terr |= pl_strdup(&attr->val, val);\n\n\tif (err)\n\t\tmem_deref(attr);\n\n\treturn err;\n}\n\n\nint sdp_attr_addv(struct list *lst, const char *name, const char *val,\n\t\t  va_list ap)\n{\n\tstruct sdp_attr *attr;\n\tint err;\n\n\tattr = mem_zalloc(sizeof(*attr), destructor);\n\tif (!attr)\n\t\treturn ENOMEM;\n\n\tlist_append(lst, &attr->le, attr);\n\n\terr = str_dup(&attr->name, name);\n\n\tif (str_isset(val))\n\t\terr |= re_vsdprintf(&attr->val, val, ap);\n\n\tif (err)\n\t\tmem_deref(attr);\n\n\treturn err;\n}\n\n\nvoid sdp_attr_del(const struct list *lst, const char *name)\n{\n\tstruct le *le = list_head(lst);\n\n\twhile (le) {\n\n\t\tstruct sdp_attr *attr = le->data;\n\n\t\tle = le->next;\n\n\t\tif (0 == str_casecmp(name, attr->name))\n\t\t\tmem_deref(attr);\n\t}\n}\n\n\nconst char *sdp_attr_apply(const struct list *lst, const char *name,\n\t\t\t   sdp_attr_h *attrh, void *arg)\n{\n\tstruct le *le = list_head(lst);\n\n\twhile (le) {\n\n\t\tconst struct sdp_attr *attr = le->data;\n\n\t\tle = le->next;\n\n\t\tif (name && (!attr->name || strcmp(name, attr->name)))\n\t\t\tcontinue;\n\n\t\tif (!attrh || attrh(attr->name, attr->val?attr->val : \"\", arg))\n\t\t\treturn attr->val ? attr->val : \"\";\n\t}\n\n\treturn NULL;\n}\n\n\nint sdp_attr_print(struct re_printf *pf, const struct sdp_attr *attr)\n{\n\tif (!attr)\n\t\treturn 0;\n\n\tif (attr->val)\n\t\treturn re_hprintf(pf, \"a=%s:%s\\r\\n\", attr->name, attr->val);\n\telse\n\t\treturn re_hprintf(pf, \"a=%s\\r\\n\", attr->name);\n}\n\n\nint sdp_attr_debug(struct re_printf *pf, const struct sdp_attr *attr)\n{\n\tif (!attr)\n\t\treturn 0;\n\n\tif (attr->val)\n\t\treturn re_hprintf(pf, \"%s='%s'\", attr->name, attr->val);\n\telse\n\t\treturn re_hprintf(pf, \"%s\", attr->name);\n}\n"
  },
  {
    "path": "src/sdp/format.c",
    "content": "/**\n * @file sdp/format.c  SDP format\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdlib.h>\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_sdp.h>\n#include \"sdp.h\"\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sdp_format *fmt = arg;\n\n\tlist_unlink(&fmt->le);\n\n\tif (fmt->ref)\n\t\tmem_deref(fmt->data);\n\n\tmem_deref(fmt->id);\n\tmem_deref(fmt->params);\n\tmem_deref(fmt->rparams);\n\tmem_deref(fmt->name);\n}\n\n\n/**\n * Add an SDP Format to an SDP Media line\n *\n * @param fmtp    Pointer to allocated SDP Format\n * @param m       SDP Media line\n * @param prepend True to prepend, False to append\n * @param id      Format identifier\n * @param name    Format name\n * @param srate   Sampling rate\n * @param ch      Number of channels\n * @param ench    Optional format encode handler\n * @param cmph    Optional format comparison handler\n * @param data    Opaque data for handler\n * @param ref     True to mem_ref() data\n * @param params  Formatted parameters\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_format_add(struct sdp_format **fmtp, struct sdp_media *m,\n\t\t   bool prepend, const char *id, const char *name,\n\t\t   uint32_t srate, uint8_t ch, sdp_fmtp_enc_h *ench,\n\t\t   sdp_fmtp_cmp_h *cmph, void *data, bool ref,\n\t\t   const char *params, ...)\n{\n\tstruct sdp_format *fmt;\n\tint err;\n\n\tif (!m)\n\t\treturn EINVAL;\n\n\tif (!id && (m->dynpt > RTP_DYNPT_END))\n\t\treturn ERANGE;\n\n\tfmt = mem_zalloc(sizeof(*fmt), destructor);\n\tif (!fmt)\n\t\treturn ENOMEM;\n\n\tif (prepend)\n\t\tlist_prepend(&m->lfmtl, &fmt->le, fmt);\n\telse\n\t\tlist_append(&m->lfmtl, &fmt->le, fmt);\n\n\tif (id)\n\t\terr = str_dup(&fmt->id, id);\n\telse\n\t\terr = re_sdprintf(&fmt->id, \"%i\", m->dynpt++);\n\tif (err)\n\t\tgoto out;\n\n\tif (name) {\n\t\terr = str_dup(&fmt->name, name);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\tif (params) {\n\t\tva_list ap;\n\n\t\tva_start(ap, params);\n\t\terr = re_vsdprintf(&fmt->params, params, ap);\n\t\tva_end(ap);\n\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\tfmt->pt    = atoi(fmt->id);\n\tfmt->srate = srate;\n\tfmt->ch    = ch;\n\tfmt->ench  = ench;\n\tfmt->cmph  = cmph;\n\tfmt->data  = ref ? mem_ref(data) : data;\n\tfmt->ref   = ref;\n\tfmt->sup   = true;\n\n out:\n\tif (err)\n\t\tmem_deref(fmt);\n\telse if (fmtp)\n\t\t*fmtp = fmt;\n\n\treturn err;\n}\n\n\nint sdp_format_radd(struct sdp_media *m, const struct pl *id)\n{\n\tstruct sdp_format *fmt;\n\tint err;\n\n\tif (!m || !id)\n\t\treturn EINVAL;\n\n\tfmt = mem_zalloc(sizeof(*fmt), destructor);\n\tif (!fmt)\n\t\treturn ENOMEM;\n\n\tlist_append(&m->rfmtl, &fmt->le, fmt);\n\n\terr = pl_strdup(&fmt->id, id);\n\tif (err)\n\t\tgoto out;\n\n\tfmt->pt = atoi(fmt->id);\n\n out:\n\tif (err)\n\t\tmem_deref(fmt);\n\n\treturn err;\n}\n\n\nstruct sdp_format *sdp_format_find(const struct list *lst, const struct pl *id)\n{\n\tstruct le *le;\n\n\tif (!lst || !id)\n\t\treturn NULL;\n\n\tfor (le=lst->head; le; le=le->next) {\n\n\t\tstruct sdp_format *fmt = le->data;\n\n\t\tif (pl_strcmp(id, fmt->id))\n\t\t\tcontinue;\n\n\t\treturn fmt;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Set the parameters of an SDP format\n *\n * @param fmt    SDP Format\n * @param params Formatted parameters\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_format_set_params(struct sdp_format *fmt, const char *params, ...)\n{\n\tint err = 0;\n\n\tif (!fmt)\n\t\treturn EINVAL;\n\n\tfmt->params = mem_deref(fmt->params);\n\n\tif (params) {\n\t\tva_list ap;\n\n\t\tva_start(ap, params);\n\t\terr = re_vsdprintf(&fmt->params, params, ap);\n\t\tva_end(ap);\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Compare two SDP Formats\n *\n * @param fmt1 First SDP format\n * @param fmt2 Second SDP format\n *\n * @return True if matching, False if not\n */\nbool sdp_format_cmp(const struct sdp_format *fmt1,\n\t\t    const struct sdp_format *fmt2)\n{\n\tif (!fmt1 || !fmt2)\n\t\treturn false;\n\n\tif (fmt1->pt < RTP_DYNPT_START && fmt2->pt < RTP_DYNPT_START) {\n\n\t\tif (!fmt1->id || !fmt2->id)\n\t\t\treturn false;\n\n\t\treturn strcmp(fmt1->id, fmt2->id) ? false : true;\n\t}\n\n\tif (str_casecmp(fmt1->name, fmt2->name))\n\t\treturn false;\n\n\tif (fmt1->srate != fmt2->srate)\n\t\treturn false;\n\n\tif (fmt1->ch != fmt2->ch)\n\t\treturn false;\n\n\tif (fmt1->cmph && !fmt1->cmph(fmt1->params, fmt2->params, fmt1->data))\n\t\treturn false;\n\n\tif (fmt2->cmph && !fmt2->cmph(fmt2->params, fmt1->params, fmt2->data))\n\t\treturn false;\n\n\treturn true;\n}\n\n\n/**\n * Print SDP Format debug information\n *\n * @param pf  Print function for output\n * @param fmt SDP Format\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_format_debug(struct re_printf *pf, const struct sdp_format *fmt)\n{\n\tint err;\n\n\tif (!fmt)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"%3s\", fmt->id);\n\n\tif (fmt->name)\n\t\terr |= re_hprintf(pf, \" %s/%u/%u\",\n\t\t\t\t  fmt->name, fmt->srate, fmt->ch);\n\n\tif (fmt->params)\n\t\terr |= re_hprintf(pf, \" (%s)\", fmt->params);\n\n\tif (fmt->sup)\n\t\terr |= re_hprintf(pf, \" *\");\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sdp/media.c",
    "content": "/**\n * @file sdp/media.c  SDP Media\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdlib.h>\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_sdp.h>\n#include \"sdp.h\"\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sdp_media *m = arg;\n\tunsigned i;\n\n\tlist_flush(&m->lfmtl);\n\tlist_flush(&m->rfmtl);\n\tlist_flush(&m->rattrl);\n\tlist_flush(&m->lattrl);\n\n\tif (m->le.list) {\n\t\tm->disabled = true;\n\t\tm->ench     = NULL;\n\t\tmem_ref(m);\n\t\treturn;\n\t}\n\n\tfor (i=0; i<RE_ARRAY_SIZE(m->protov); i++)\n\t\tmem_deref(m->protov[i]);\n\n\tlist_unlink(&m->le);\n\tmem_deref(m->name);\n\tmem_deref(m->proto);\n\tmem_deref(m->uproto);\n}\n\n\nstatic int media_alloc(struct sdp_media **mp, struct list *list)\n{\n\tstruct sdp_media *m;\n\tint i;\n\n\tm = mem_zalloc(sizeof(*m), destructor);\n\tif (!m)\n\t\treturn ENOMEM;\n\n\tlist_append(list, &m->le, m);\n\n\tm->ldir  = SDP_SENDRECV;\n\tm->rdir  = SDP_SENDRECV;\n\tm->dynpt = RTP_DYNPT_START;\n\n\tsa_init(&m->laddr, AF_INET);\n\tsa_init(&m->raddr, AF_INET);\n\tsa_init(&m->laddr_rtcp, AF_INET);\n\tsa_init(&m->raddr_rtcp, AF_INET);\n\n\tfor (i=0; i<SDP_BANDWIDTH_MAX; i++) {\n\t\tm->lbwv[i] = -1;\n\t\tm->rbwv[i] = -1;\n\t}\n\n\t*mp = m;\n\n\treturn 0;\n}\n\n\n/**\n * Add a media line to an SDP Session\n *\n * @param mp    Pointer to allocated SDP Media line object\n * @param sess  SDP Session\n * @param name  Media name\n * @param port  Port number\n * @param proto Transport protocol\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_media_add(struct sdp_media **mp, struct sdp_session *sess,\n\t\t  const char *name, uint16_t port, const char *proto)\n{\n\tstruct sdp_media *m;\n\tint err;\n\n\tif (!sess || !name || !proto)\n\t\treturn EINVAL;\n\n\terr = media_alloc(&m, &sess->lmedial);\n\tif (err)\n\t\treturn err;\n\n\terr  = str_dup(&m->name, name);\n\terr |= str_dup(&m->proto, proto);\n\tif (err)\n\t\tgoto out;\n\n\tsa_set_port(&m->laddr, port);\n\n out:\n\tif (err)\n\t\tmem_deref(m);\n\telse if (mp)\n\t\t*mp = m;\n\n\treturn err;\n}\n\n\n/**\n * Add a remote SDP media line to an SDP Session\n *\n * @param mp    Pointer to allocated SDP Media line object\n * @param sess  SDP Session\n * @param name  Media name\n * @param proto Transport protocol\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_media_radd(struct sdp_media **mp, struct sdp_session *sess,\n\t\t   const struct pl *name, const struct pl *proto)\n{\n\tstruct sdp_media *m;\n\tint err;\n\n\tif (!mp || !sess || !name || !proto)\n\t\treturn EINVAL;\n\n\terr = media_alloc(&m, &sess->medial);\n\tif (err)\n\t\treturn err;\n\n\tm->disabled = true;\n\n\terr  = pl_strdup(&m->name, name);\n\terr |= pl_strdup(&m->proto, proto);\n\n\tif (err)\n\t\tmem_deref(m);\n\telse\n\t\t*mp = m;\n\n\treturn err;\n}\n\n\n/**\n * Reset the remote part of an SDP Media line\n *\n * @param m SDP Media line\n */\nvoid sdp_media_rreset(struct sdp_media *m)\n{\n\tint i;\n\n\tif (!m)\n\t\treturn;\n\n\tsa_init(&m->raddr, AF_INET);\n\tsa_init(&m->raddr_rtcp, AF_INET);\n\n\tlist_flush(&m->rfmtl);\n\tlist_flush(&m->rattrl);\n\n\tm->rdir = SDP_SENDRECV;\n\n\tfor (i=0; i<SDP_BANDWIDTH_MAX; i++)\n\t\tm->rbwv[i] = -1;\n}\n\n\n/**\n * Compare media line protocols\n *\n * @param m      SDP Media line\n * @param proto  Transport protocol\n * @param update Update media protocol if match is found in alternate set\n *\n * @return True if matching, False if not\n */\nbool sdp_media_proto_cmp(struct sdp_media *m, const struct pl *proto,\n\t\t\t bool update)\n{\n\tunsigned i;\n\n\tif (!m || !proto)\n\t\treturn false;\n\n\tif (!pl_strcmp(proto, m->proto))\n\t\treturn true;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(m->protov); i++) {\n\n\t\tif (!m->protov[i] || pl_strcmp(proto, m->protov[i]))\n\t\t\tcontinue;\n\n\t\tif (update) {\n\t\t\tmem_deref(m->proto);\n\t\t\tm->proto = mem_ref(m->protov[i]);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/**\n * Find an SDP Media line from name and transport protocol\n *\n * @param sess  SDP Session\n * @param name  Media name\n * @param proto Transport protocol\n * @param update_proto Update media transport protocol\n *\n * @return Matching media line if found, NULL if not found\n */\nstruct sdp_media *sdp_media_find(const struct sdp_session *sess,\n\t\t\t\t const struct pl *name,\n\t\t\t\t const struct pl *proto,\n\t\t\t\t bool update_proto)\n{\n\tstruct le *le;\n\n\tif (!sess || !name || !proto)\n\t\treturn NULL;\n\n\tfor (le=sess->lmedial.head; le; le=le->next) {\n\n\t\tstruct sdp_media *m = le->data;\n\n\t\tif (pl_strcmp(name, m->name))\n\t\t\tcontinue;\n\n\t\tif (!sdp_media_proto_cmp(m, proto, update_proto))\n\t\t\tcontinue;\n\n\t\treturn m;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Align the locate/remote formats of an SDP Media line\n *\n * @param m     SDP Media line\n * @param offer True if SDP Offer, False if SDP Answer\n */\nvoid sdp_media_align_formats(struct sdp_media *m, bool offer)\n{\n\tstruct sdp_format *rfmt, *lfmt = NULL;\n\tstruct le *rle, *lle;\n\tint pt_offer = RTP_DYNPT_START;\n\n\tif (!m || m->disabled || !sa_port(&m->raddr) || m->fmt_ignore)\n\t\treturn;\n\n\tfor (lle=m->lfmtl.head; lle; lle=lle->next) {\n\n\t\tlfmt = lle->data;\n\n\t\tlfmt->rparams = mem_deref(lfmt->rparams);\n\t\tlfmt->sup = false;\n\t}\n\n\trle = m->rfmtl.tail;\n\n\twhile (rle) {\n\t\trfmt = rle->data;\n\t\trle = rle->prev;\n\n\t\tfor (lle=m->lfmtl.head; lle; lle=lle->next) {\n\n\t\t\tlfmt = lle->data;\n\n\t\t\tif (sdp_format_cmp(lfmt, rfmt))\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (!lle || !lfmt) {\n\t\t\trfmt->sup = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\tmem_deref(lfmt->rparams);\n\t\tlfmt->rparams = mem_ref(rfmt->params);\n\n\t\tlfmt->sup = true;\n\t\trfmt->sup = true;\n\n\t\tif (rfmt->ref)\n\t\t\trfmt->data = mem_deref(rfmt->data);\n\t\telse\n\t\t\trfmt->data = NULL;\n\n\t\tif (lfmt->ref)\n\t\t\trfmt->data = mem_ref(lfmt->data);\n\t\telse\n\t\t\trfmt->data = lfmt->data;\n\n\t\trfmt->ref = lfmt->ref;\n\n\t\t/* Use payload type from offer - RFC3264 - 6.1 */\n\t\tif (offer) {\n\t\t\tmem_deref(lfmt->id);\n\t\t\tlfmt->id = mem_ref(rfmt->id);\n\t\t\tlfmt->pt = atoi(lfmt->id ? lfmt->id : \"\");\n\n\t\t\tlist_unlink(&lfmt->le);\n\t\t\tlist_prepend(&m->lfmtl, &lfmt->le, lfmt);\n\t\t\tif (lfmt->pt > pt_offer)\n\t\t\t\tpt_offer = lfmt->pt;\n\t\t}\n\t}\n\n\t/* Recalculate pt and reorder unsupported codecs */\n\tif (offer) {\n\t\tfor (lle = m->lfmtl.tail; lle;) {\n\n\t\t\tlfmt = lle->data;\n\n\t\t\tlle = lle->prev;\n\n\t\t\tif (lfmt && !lfmt->sup) {\n\t\t\t\tif (lfmt->pt >= RTP_DYNPT_START) {\n\t\t\t\t\tmem_deref(lfmt->id);\n\t\t\t\t\tlfmt->pt = ++pt_offer;\n\t\t\t\t\tre_sdprintf(&lfmt->id, \"%i\", lfmt->pt);\n\t\t\t\t}\n\t\t\t\tlist_unlink(&lfmt->le);\n\t\t\t\tlist_append(&m->lfmtl, &lfmt->le, lfmt);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/**\n * Set alternative protocols for an SDP Media line\n *\n * @param m      SDP Media line\n * @param protoc Number of alternative protocols\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_media_set_alt_protos(struct sdp_media *m, unsigned protoc, ...)\n{\n\tconst char *proto;\n\tint err = 0;\n\tunsigned i;\n\tva_list ap;\n\n\tif (!m)\n\t\treturn EINVAL;\n\n\tva_start(ap, protoc);\n\n\tfor (i=0; i<RE_ARRAY_SIZE(m->protov); i++) {\n\n\t\tm->protov[i] = mem_deref(m->protov[i]);\n\n\t\tif (i >= protoc)\n\t\t\tcontinue;\n\n\t\tproto = va_arg(ap, const char *);\n\t\tif (proto)\n\t\t\terr |= str_dup(&m->protov[i], proto);\n\t}\n\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Set SDP Media line encode handler\n *\n * @param m    SDP Media line\n * @param ench Encode handler\n * @param arg  Encode handler argument\n */\nvoid sdp_media_set_encode_handler(struct sdp_media *m, sdp_media_enc_h *ench,\n\t\t\t\t  void *arg)\n{\n\tif (!m)\n\t\treturn;\n\n\tm->ench = ench;\n\tm->arg  = arg;\n}\n\n\n/**\n * Set an SDP Media line to ignore formats\n *\n * @param m          SDP Media line\n * @param fmt_ignore True for ignore formats, otherwise false\n */\nvoid sdp_media_set_fmt_ignore(struct sdp_media *m, bool fmt_ignore)\n{\n\tif (!m)\n\t\treturn;\n\n\tm->fmt_ignore = fmt_ignore;\n}\n\n\n/**\n * Set an SDP Media line to enabled/disabled\n *\n * @param m        SDP Media line\n * @param disabled True for disabled, False for enabled\n */\nvoid sdp_media_set_disabled(struct sdp_media *m, bool disabled)\n{\n\tif (!m)\n\t\treturn;\n\n\tm->disabled = disabled;\n}\n\n\n/**\n * Check if an SDP Media line is disabled\n *\n * @param  m SDP Media line\n * @return True if disabled, otherwise false\n\n */\nbool sdp_media_disabled(struct sdp_media *m)\n{\n\tif (!m)\n\t\treturn true;\n\n\treturn m->disabled;\n}\n\n\n/**\n * Set the local port number of an SDP Media line\n *\n * @param m    SDP Media line\n * @param port Port number\n */\nvoid sdp_media_set_lport(struct sdp_media *m, uint16_t port)\n{\n\tif (!m)\n\t\treturn;\n\n\tsa_set_port(&m->laddr, port);\n}\n\n\n/**\n * Set the local network address of an SDP media line\n *\n * @param m     SDP Media line\n * @param laddr Local network address\n */\nvoid sdp_media_set_laddr(struct sdp_media *m, const struct sa *laddr)\n{\n\tif (!m || !laddr)\n\t\treturn;\n\n\tm->laddr = *laddr;\n}\n\n\n/**\n * Set a local bandwidth of an SDP Media line\n *\n * @param m    SDP Media line\n * @param type Bandwidth type\n * @param bw   Bandwidth value\n */\nvoid sdp_media_set_lbandwidth(struct sdp_media *m, enum sdp_bandwidth type,\n\t\t\t      int32_t bw)\n{\n\tif (!m || type < SDP_BANDWIDTH_MIN || type >= SDP_BANDWIDTH_MAX)\n\t\treturn;\n\n\tm->lbwv[type] = bw;\n}\n\n\n/**\n * Set the local RTCP port number of an SDP Media line\n *\n * @param m    SDP Media line\n * @param port RTCP Port number\n */\nvoid sdp_media_set_lport_rtcp(struct sdp_media *m, uint16_t port)\n{\n\tif (!m)\n\t\treturn;\n\n\tsa_set_port(&m->laddr_rtcp, port);\n}\n\n\n/**\n * Set the local RTCP network address of an SDP media line\n *\n * @param m     SDP Media line\n * @param laddr Local RTCP network address\n */\nvoid sdp_media_set_laddr_rtcp(struct sdp_media *m, const struct sa *laddr)\n{\n\tif (!m || !laddr)\n\t\treturn;\n\n\tm->laddr_rtcp = *laddr;\n}\n\n\n/**\n * Set the local direction flag of an SDP Media line\n *\n * @param m   SDP Media line\n * @param dir Media direction flag\n */\nvoid sdp_media_set_ldir(struct sdp_media *m, enum sdp_dir dir)\n{\n\tif (!m)\n\t\treturn;\n\n\tm->ldir = dir;\n}\n\n\n/**\n * Set a local attribute of an SDP Media line\n *\n * @param m       SDP Media line\n * @param replace True to replace attribute, False to append\n * @param name    Attribute name\n * @param value   Formatted attribute value\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_media_set_lattr(struct sdp_media *m, bool replace,\n\t\t\tconst char *name, const char *value, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tif (!m || !name)\n\t\treturn EINVAL;\n\n\tif (replace)\n\t\tsdp_attr_del(&m->lattrl, name);\n\n\tva_start(ap, value);\n\terr = sdp_attr_addv(&m->lattrl, name, value, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Delete a local attribute of an SDP Media line\n *\n * @param m    SDP Media line\n * @param name Attribute name\n */\nvoid sdp_media_del_lattr(struct sdp_media *m, const char *name)\n{\n\tif (!m || !name)\n\t\treturn;\n\n\tsdp_attr_del(&m->lattrl, name);\n}\n\n\nconst char *sdp_media_proto(const struct sdp_media *m)\n{\n\treturn m ? m->proto : NULL;\n}\n\n\n/**\n * Get the remote port number of an SDP Media line\n *\n * @param m SDP Media line\n *\n * @return Remote port number\n */\nuint16_t sdp_media_rport(const struct sdp_media *m)\n{\n\treturn m ? sa_port(&m->raddr) : 0;\n}\n\n\n/**\n * Get the remote network address of an SDP Media line\n *\n * @param m SDP Media line\n *\n * @return Remote network address\n */\nconst struct sa *sdp_media_raddr(const struct sdp_media *m)\n{\n\treturn m ? &m->raddr : NULL;\n}\n\n\n/**\n * Get the local network address of an SDP Media line\n *\n * @param m SDP Media line\n *\n * @return Local network address\n */\nconst struct sa *sdp_media_laddr(const struct sdp_media *m)\n{\n\treturn m ? &m->laddr : NULL;\n}\n\n\n/**\n * Get the remote RTCP network address of an SDP Media line\n *\n * @param m     SDP Media line\n * @param raddr On return, contains remote RTCP network address\n */\nvoid sdp_media_raddr_rtcp(const struct sdp_media *m, struct sa *raddr)\n{\n\tif (!m || !raddr)\n\t\treturn;\n\n\tif (sa_isset(&m->raddr_rtcp, SA_ALL)) {\n\t\t*raddr = m->raddr_rtcp;\n\t}\n\telse if (sa_isset(&m->raddr_rtcp, SA_PORT)) {\n\t\t*raddr = m->raddr;\n\t\tsa_set_port(raddr, sa_port(&m->raddr_rtcp));\n\t}\n\telse {\n\t\tuint16_t port = sa_port(&m->raddr);\n\n\t\t*raddr = m->raddr;\n\t\tsa_set_port(raddr, port ? port + 1 : 0);\n\t}\n}\n\n\n/**\n * Get a remote bandwidth of an SDP Media line\n *\n * @param m    SDP Media line\n * @param type Bandwidth type\n *\n * @return Remote bandwidth value\n */\nint32_t sdp_media_rbandwidth(const struct sdp_media *m,\n\t\t\t      enum sdp_bandwidth type)\n{\n\tif (!m || type < SDP_BANDWIDTH_MIN || type >= SDP_BANDWIDTH_MAX)\n\t\treturn 0;\n\n\treturn m->rbwv[type];\n}\n\n\n/**\n * Get the local media direction of an SDP Media line\n *\n * @param m SDP Media line\n *\n * @return Local media direction\n */\nenum sdp_dir sdp_media_ldir(const struct sdp_media *m)\n{\n\treturn m ? m->ldir : SDP_INACTIVE;\n}\n\n\n/**\n * Get the remote media direction of an SDP Media line\n *\n * @param m SDP Media line\n *\n * @return Remote media direction\n */\nenum sdp_dir sdp_media_rdir(const struct sdp_media *m)\n{\n\treturn m ? m->rdir : SDP_INACTIVE;\n}\n\n\n/**\n * Get the combined media direction of an SDP Media line\n *\n * @param m SDP Media line\n *\n * @return Combined media direction\n */\nenum sdp_dir sdp_media_dir(const struct sdp_media *m)\n{\n\treturn m ? (enum sdp_dir)(m->ldir & m->rdir) : SDP_INACTIVE;\n}\n\n\n/**\n * Find a local SDP format from a payload type\n *\n * @param m  SDP Media line\n * @param pt Payload type\n *\n * @return Local SDP format if found, NULL if not found\n */\nconst struct sdp_format *sdp_media_lformat(const struct sdp_media *m, int pt)\n{\n\tstruct le *le;\n\n\tif (!m)\n\t\treturn NULL;\n\n\tfor (le=m->lfmtl.head; le; le=le->next) {\n\n\t\tconst struct sdp_format *fmt = le->data;\n\n\t\tif (pt == fmt->pt)\n\t\t\treturn fmt;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Find a remote SDP format from a format name\n *\n * @param m    SDP Media line\n * @param name Format name\n *\n * @return Remote SDP format if found, NULL if not found\n */\nconst struct sdp_format *sdp_media_rformat(const struct sdp_media *m,\n\t\t\t\t\t   const char *name)\n{\n\tstruct le *le;\n\n\tif (!m || !sa_port(&m->raddr))\n\t\treturn NULL;\n\n\tfor (le=m->rfmtl.head; le; le=le->next) {\n\n\t\tconst struct sdp_format *fmt = le->data;\n\n\t\tif (!fmt->sup)\n\t\t\tcontinue;\n\n\t\tif (name && str_casecmp(name, fmt->name))\n\t\t\tcontinue;\n\n\t\treturn fmt;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Find an SDP Format of an SDP Media line\n *\n * @param m     SDP Media line\n * @param local True if local media, False if remote\n * @param id    SDP format id\n * @param pt    Payload type\n * @param name  Format name\n * @param srate Sampling rate\n * @param ch    Number of channels\n *\n * @return SDP Format if found, NULL if not found\n */\nstruct sdp_format *sdp_media_format(const struct sdp_media *m,\n\t\t\t\t    bool local, const char *id,\n\t\t\t\t    int pt, const char *name,\n\t\t\t\t    int32_t srate, int8_t ch)\n{\n\treturn sdp_media_format_apply(m, local, id, pt, name, srate, ch,\n\t\t\t\t      NULL, NULL);\n}\n\n\n/**\n * Apply a function handler to all matching SDP formats\n *\n * @param m     SDP Media line\n * @param local True if local media, False if remote\n * @param id    SDP format id\n * @param pt    Payload type\n * @param name  Format name\n * @param srate Sampling rate\n * @param ch    Number of channels\n * @param fmth  SDP Format handler\n * @param arg   Handler argument\n *\n * @return SDP Format if found, NULL if not found\n */\nstruct sdp_format *sdp_media_format_apply(const struct sdp_media *m,\n\t\t\t\t\t  bool local, const char *id,\n\t\t\t\t\t  int pt, const char *name,\n\t\t\t\t\t  int32_t srate, int8_t ch,\n\t\t\t\t\t  sdp_format_h *fmth, void *arg)\n{\n\tstruct le *le;\n\n\tif (!m)\n\t\treturn NULL;\n\n\tle = local ? m->lfmtl.head : m->rfmtl.head;\n\n\twhile (le) {\n\n\t\tstruct sdp_format *fmt = le->data;\n\n\t\tle = le->next;\n\n\t\tif (id && (!fmt->id || strcmp(id, fmt->id)))\n\t\t\tcontinue;\n\n\t\tif (pt >= 0 && pt != fmt->pt)\n\t\t\tcontinue;\n\n\t\tif (name && str_casecmp(name, fmt->name))\n\t\t\tcontinue;\n\n\t\tif (srate >= 0 && (uint32_t)srate != fmt->srate)\n\t\t\tcontinue;\n\n\t\tif (ch >= 0 && (uint8_t)ch != fmt->ch)\n\t\t\tcontinue;\n\n\t\tif (!fmth || fmth(fmt, arg))\n\t\t\treturn fmt;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Get the list of SDP Formats\n *\n * @param m     SDP Media line\n * @param local True if local, False if remote\n *\n * @return List of SDP Formats\n */\nconst struct list *sdp_media_format_lst(const struct sdp_media *m, bool local)\n{\n\tif (!m)\n\t\treturn NULL;\n\n\treturn local ? &m->lfmtl : &m->rfmtl;\n}\n\n\n/**\n * Get a remote attribute from an SDP Media line\n *\n * @param m     SDP Media line\n * @param name  Attribute name\n *\n * @return Attribute value, NULL if not found\n */\nconst char *sdp_media_rattr(const struct sdp_media *m, const char *name)\n{\n\tif (!m || !name)\n\t\treturn NULL;\n\n\treturn sdp_attr_apply(&m->rattrl, name, NULL, NULL);\n}\n\n\n/**\n * Get a remote attribute from an SDP Media line or the SDP session\n *\n * @param m     SDP Media line\n * @param sess  SDP Session\n * @param name  Attribute name\n *\n * @return Attribute value, NULL if not found\n */\nconst char *sdp_media_session_rattr(const struct sdp_media *m,\n\t\t\t\t    const struct sdp_session *sess,\n\t\t\t\t    const char *name)\n{\n\tconst char *val;\n\n\tval = sdp_media_rattr(m, name);\n\tif (!val)\n\t\tval = sdp_session_rattr(sess, name);\n\n\treturn val;\n}\n\n\n/**\n * Apply a function handler to all matching local attributes\n *\n * @param m     SDP Media line\n * @param name  Attribute name\n * @param attrh Attribute handler\n * @param arg   Handler argument\n *\n * @return Attribute value if match\n */\nconst char *sdp_media_lattr_apply(const struct sdp_media *m, const char *name,\n\t\t\t\t  sdp_attr_h *attrh, void *arg)\n{\n\tif (!m)\n\t\treturn NULL;\n\n\treturn sdp_attr_apply(&m->lattrl, name, attrh, arg);\n}\n\n\n/**\n * Apply a function handler to all matching remote attributes\n *\n * @param m     SDP Media line\n * @param name  Attribute name\n * @param attrh Attribute handler\n * @param arg   Handler argument\n *\n * @return Attribute value if match\n */\nconst char *sdp_media_rattr_apply(const struct sdp_media *m, const char *name,\n\t\t\t\t  sdp_attr_h *attrh, void *arg)\n{\n\tif (!m)\n\t\treturn NULL;\n\n\treturn sdp_attr_apply(&m->rattrl, name, attrh, arg);\n}\n\n\n/**\n * Get the name of an SDP Media line\n *\n * @param m SDP Media line\n *\n * @return SDP Media line name\n */\nconst char *sdp_media_name(const struct sdp_media *m)\n{\n\treturn m ? m->name : NULL;\n}\n\n\n/**\n * Print SDP Media line debug information\n *\n * @param pf Print function for output\n * @param m  SDP Media line\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_media_debug(struct re_printf *pf, const struct sdp_media *m)\n{\n\tstruct le *le;\n\tint err;\n\n\tif (!m)\n\t\treturn 0;\n\n\terr  = re_hprintf(pf, \"%s %s\\n\", m->name, m->proto);\n\n\terr |= re_hprintf(pf, \"  local formats:\\n\");\n\n\tfor (le=m->lfmtl.head; le; le=le->next)\n\t\terr |= re_hprintf(pf, \"    %H\\n\", sdp_format_debug, le->data);\n\n\terr |= re_hprintf(pf, \"  remote formats:\\n\");\n\n\tfor (le=m->rfmtl.head; le; le=le->next)\n\t\terr |= re_hprintf(pf, \"    %H\\n\", sdp_format_debug, le->data);\n\n\terr |= re_hprintf(pf, \"  local attributes:\\n\");\n\n\tfor (le=m->lattrl.head; le; le=le->next)\n\t\terr |= re_hprintf(pf, \"    %H\\n\", sdp_attr_debug, le->data);\n\n\terr |= re_hprintf(pf, \"  remote attributes:\\n\");\n\n\tfor (le=m->rattrl.head; le; le=le->next)\n\t\terr |= re_hprintf(pf, \"    %H\\n\", sdp_attr_debug, le->data);\n\n\terr |= re_hprintf(pf, \"  local direction:  %s\\n\",\n\t\t\tsdp_dir_name(m->ldir));\n\n\terr |= re_hprintf(pf, \"  remote direction: %s\\n\",\n\t\t\tsdp_dir_name(m->rdir));\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sdp/msg.c",
    "content": "/**\n * @file sdp/msg.c  SDP Message processing\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_sdp.h>\n#include \"sdp.h\"\n\n\nstatic int attr_decode_fmtp(struct sdp_media *m, const struct pl *pl)\n{\n\tstruct sdp_format *fmt;\n\tstruct pl id, params;\n\n\tif (!m)\n\t\treturn 0;\n\n\tif (re_regex(pl->p, pl->l, \"[^ ]+ [^]*\", &id, &params))\n\t\treturn EBADMSG;\n\n\tfmt = sdp_format_find(&m->rfmtl, &id);\n\tif (!fmt)\n\t\treturn 0;\n\n\tfmt->params = mem_deref(fmt->params);\n\n\treturn pl_strdup(&fmt->params, &params);\n}\n\n\nstatic int attr_decode_rtcp(struct sdp_media *m, const struct pl *pl)\n{\n\tstruct pl port, addr;\n\tint err = 0;\n\n\tif (!m)\n\t\treturn 0;\n\n\tif (!re_regex(pl->p, pl->l, \"[0-9]+ IN IP[46]1 [^ ]+\",\n\t\t      &port, NULL, &addr)) {\n\t\t(void)sa_set(&m->raddr_rtcp, &addr, pl_u32(&port));\n\t}\n\telse if (!re_regex(pl->p, pl->l, \"[0-9]+\", &port)) {\n\t\tsa_set_port(&m->raddr_rtcp, pl_u32(&port));\n\t}\n\telse\n\t\terr = EBADMSG;\n\n\treturn err;\n}\n\n\nstatic int attr_decode_rtpmap(struct sdp_media *m, const struct pl *pl)\n{\n\tstruct pl id, name, srate, ch;\n\tstruct sdp_format *fmt;\n\tint err;\n\n\tif (!m)\n\t\treturn 0;\n\n\tif (re_regex(pl->p, pl->l, \"[^ ]+ [^/]+/[0-9]+[/]*[^]*\",\n\t\t     &id, &name, &srate, NULL, &ch))\n\t\treturn EBADMSG;\n\n\tfmt = sdp_format_find(&m->rfmtl, &id);\n\tif (!fmt)\n\t\treturn 0;\n\n\tfmt->name = mem_deref(fmt->name);\n\n\terr = pl_strdup(&fmt->name, &name);\n\tif (err)\n\t\treturn err;\n\n\tfmt->srate = pl_u32(&srate);\n\tfmt->ch = ch.l ? pl_u32(&ch) : 1;\n\n\treturn 0;\n}\n\n\nstatic int attr_decode(struct sdp_session *sess, struct sdp_media *m,\n\t\t       enum sdp_dir *dir, const struct pl *pl)\n{\n\tstruct pl name, val;\n\tint err = 0;\n\n\tif (re_regex(pl->p, pl->l, \"[^:]+:[^]+\", &name, &val)) {\n\t\tname = *pl;\n\t\tval  = pl_null;\n\t}\n\n\tif (!pl_strcmp(&name, \"fmtp\"))\n\t\terr = attr_decode_fmtp(m, &val);\n\n\telse if (!pl_strcmp(&name, \"inactive\"))\n\t\t*dir = SDP_INACTIVE;\n\n\telse if (!pl_strcmp(&name, \"recvonly\"))\n\t\t*dir = SDP_SENDONLY;\n\n\telse if (!pl_strcmp(&name, \"rtcp\"))\n\t\terr = attr_decode_rtcp(m, &val);\n\n\telse if (!pl_strcmp(&name, \"rtpmap\"))\n\t\terr = attr_decode_rtpmap(m, &val);\n\n\telse if (!pl_strcmp(&name, \"sendonly\"))\n\t\t*dir = SDP_RECVONLY;\n\n\telse if (!pl_strcmp(&name, \"sendrecv\"))\n\t\t*dir = SDP_SENDRECV;\n\n\telse\n\t\terr = sdp_attr_add(m ? &m->rattrl : &sess->rattrl,\n\t\t\t\t   &name, &val);\n\n\treturn err;\n}\n\n\nstatic int bandwidth_decode(int32_t *bwv, const struct pl *pl)\n{\n\tstruct pl type, bw;\n\n\tif (re_regex(pl->p, pl->l, \"[^:]+:[0-9]+\", &type, &bw))\n\t\treturn EBADMSG;\n\n\tif (!pl_strcmp(&type, \"CT\"))\n\t\tbwv[SDP_BANDWIDTH_CT] = pl_u32(&bw);\n\n\telse if (!pl_strcmp(&type, \"AS\"))\n\t\tbwv[SDP_BANDWIDTH_AS] = pl_u32(&bw);\n\n\telse if (!pl_strcmp(&type, \"RS\"))\n\t\tbwv[SDP_BANDWIDTH_RS] = pl_u32(&bw);\n\n\telse if (!pl_strcmp(&type, \"RR\"))\n\t\tbwv[SDP_BANDWIDTH_RR] = pl_u32(&bw);\n\n\telse if (!pl_strcmp(&type, \"TIAS\"))\n\t\tbwv[SDP_BANDWIDTH_TIAS] = pl_u32(&bw);\n\n\treturn 0;\n}\n\n\nstatic int conn_decode(struct sa *sa, const struct pl *pl)\n{\n\tstruct pl v;\n\n\tif (re_regex(pl->p, pl->l, \"IN IP[46]1 [^ ]+\", NULL, &v))\n\t\treturn EBADMSG;\n\n\t(void)sa_set(sa, &v, sa_port(sa));\n\n\treturn 0;\n}\n\n\nstatic int media_decode(struct sdp_media **mp, struct sdp_session *sess,\n\t\t\tbool offer, const struct pl *pl)\n{\n\tstruct pl name, port, proto, fmtv, fmt;\n\tstruct sdp_media *m;\n\tint err;\n\n\tif (re_regex(pl->p, pl->l, \"[a-z]+ [^ ]+ [^ ]+[^]*\",\n\t\t     &name, &port, &proto, &fmtv))\n\t\treturn EBADMSG;\n\n\tm = list_ledata(*mp ? (*mp)->le.next : sess->medial.head);\n\tif (!m) {\n\t\tif (!offer)\n\t\t\treturn EPROTO;\n\n\t\tm = sdp_media_find(sess, &name, &proto, true);\n\t\tif (!m) {\n\t\t\terr = sdp_media_radd(&m, sess, &name, &proto);\n\t\t\tif (err)\n\t\t\t\treturn err;\n\t\t}\n\t\telse {\n\t\t\tlist_unlink(&m->le);\n\t\t\tlist_append(&sess->medial, &m->le, m);\n\t\t}\n\n\t\tm->uproto = mem_deref(m->uproto);\n\t}\n\telse {\n\t\tif (pl_strcmp(&name, m->name))\n\t\t\treturn offer ? ENOTSUP : EPROTO;\n\n\t\tm->uproto = mem_deref(m->uproto);\n\n\t\tif (!sdp_media_proto_cmp(m, &proto, offer)) {\n\n\t\t\terr = pl_strdup(&m->uproto, &proto);\n\t\t\tif (err)\n\t\t\t\treturn err;\n\t\t}\n\t}\n\n\twhile (!re_regex(fmtv.p, fmtv.l, \" [^ ]+\", &fmt)) {\n\n\t\tpl_advance(&fmtv, fmt.p + fmt.l - fmtv.p);\n\n\t\terr = sdp_format_radd(m, &fmt);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tm->raddr = sess->raddr;\n\tsa_set_port(&m->raddr, m->uproto ? 0 : pl_u32(&port));\n\n\tm->rdir = sess->rdir;\n\n\tif (!pl_u32(&port))\n\t\tm->rdir = SDP_INACTIVE;\n\n\t*mp = m;\n\n\treturn 0;\n}\n\n\nstatic int version_decode(const struct pl *pl)\n{\n\treturn pl_strcmp(pl, \"0\") ? ENOSYS : 0;\n}\n\n\n/**\n * Decode an SDP message into an SDP Session\n *\n * @param sess  SDP Session\n * @param mb    Memory buffer containing SDP message\n * @param offer True if SDP offer, False if SDP answer\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_decode(struct sdp_session *sess, struct mbuf *mb, bool offer)\n{\n\tstruct sdp_media *m;\n\tstruct pl pl, val;\n\tstruct le *le;\n\tchar type = 0;\n\tint err = 0;\n\n\tif (!sess || !mb)\n\t\treturn EINVAL;\n\n\tsdp_session_rreset(sess);\n\n\tfor (le=sess->medial.head; le; le=le->next) {\n\n\t\tm = le->data;\n\n\t\tsdp_media_rreset(m);\n\t}\n\n\tpl.p = (const char *)mbuf_buf(mb);\n\tpl.l = mbuf_get_left(mb);\n\n\tm = NULL;\n\n\tfor (;pl.l && !err; pl.p++, pl.l--) {\n\n\t\tswitch (*pl.p) {\n\n\t\tcase '\\r':\n\t\tcase '\\n':\n\t\t\tif (!type)\n\t\t\t\tbreak;\n\n\t\t\tswitch (type) {\n\n\t\t\tcase 'a':\n\t\t\t\terr = attr_decode(sess, m,\n\t\t\t\t\t\t  m ? &m->rdir : &sess->rdir,\n\t\t\t\t\t\t  &val);\n\t\t\t\tbreak;\n\n\t\t\tcase 'b':\n\t\t\t\terr = bandwidth_decode(m? m->rbwv : sess->rbwv,\n\t\t\t\t\t\t       &val);\n\t\t\t\tbreak;\n\n\t\t\tcase 'c':\n\t\t\t\terr = conn_decode(m ? &m->raddr : &sess->raddr,\n\t\t\t\t\t\t  &val);\n\t\t\t\tbreak;\n\n\t\t\tcase 'm':\n\t\t\t\terr = media_decode(&m, sess, offer, &val);\n\t\t\t\tbreak;\n\n\t\t\tcase 'v':\n\t\t\t\terr = version_decode(&val);\n\t\t\t\tbreak;\n\t\t\t}\n\n#if 0\n\t\t\tif (err)\n\t\t\t\tre_printf(\"** %c='%r': %m\\n\", type, &val, err);\n#endif\n\n\t\t\ttype = 0;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tif (type) {\n\t\t\t\tval.l++;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (pl.l < 2 || *(pl.p + 1) != '=') {\n\t\t\t\terr = EBADMSG;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\ttype  = *pl.p;\n\t\t\tval.p = pl.p + 2;\n\t\t\tval.l = 0;\n\n\t\t\tpl.p += 1;\n\t\t\tpl.l -= 1;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (err)\n\t\treturn err;\n\n\tif (type)\n\t\treturn EBADMSG;\n\n\tfor (le=sess->medial.head; le; le=le->next)\n\t\tsdp_media_align_formats(le->data, offer);\n\n\treturn 0;\n}\n\n\nstatic int media_encode(const struct sdp_media *m, struct mbuf *mb, bool offer)\n{\n\tenum sdp_bandwidth i;\n\tconst char *proto;\n\tint err, supc = 0;\n\tbool disabled = false;\n\tbool rejected = false;\n\tstruct le *le;\n\tuint16_t port;\n\n\tfor (le=m->lfmtl.head; le; le=le->next) {\n\n\t\tconst struct sdp_format *fmt = le->data;\n\n\t\tif (fmt->sup)\n\t\t\t++supc;\n\t}\n\n\t/*disable if: local supported and (m->disabled or raddr port 0)*/\n\tif (supc && (m->disabled || (!offer && !sa_port(&m->raddr)))) {\n\t\tdisabled = true;\n\t\tport = sa_port(&m->laddr);\n\t\tproto = m->proto;\n\t}\n\t/*reject if: not supported or not supported proto in the offer*/\n\telse if (supc == 0 || (!offer && m->uproto)) {\n\t\trejected = true;\n\t\tport = 0;\n\t\tif (str_isset(m->uproto))\n\t\t\tproto = m->uproto;\n\t\telse\n\t\t\tproto = m->proto;\n\t}\n\t/*everything works*/\n\telse {\n\t\tport = sa_port(&m->laddr);\n\t\tproto = m->proto;\n\t}\n\n\terr = mbuf_printf(mb, \"m=%s %u %s\", m->name, port, proto);\n\tif (rejected) {\n\t\terr |= mbuf_write_str(mb, \" 0\\r\\n\");\n\t\treturn err;\n\t}\n\n\tfor (le=m->lfmtl.head; le; le=le->next) {\n\n\t\tconst struct sdp_format *fmt = le->data;\n\n\t\tif (!fmt->sup && !offer)\n\t\t\tcontinue;\n\n\t\terr |= mbuf_printf(mb, \" %s\", fmt->id);\n\t}\n\n\terr |= mbuf_write_str(mb, \"\\r\\n\");\n\n\tif (sa_isset(&m->laddr, SA_ADDR)) {\n\t\tconst int ipver = sa_af(&m->laddr) == AF_INET ? 4 : 6;\n\t\terr |= mbuf_printf(mb, \"c=IN IP%d %j\\r\\n\", ipver, &m->laddr);\n\t}\n\n\tfor (i=SDP_BANDWIDTH_MIN; i<SDP_BANDWIDTH_MAX; i++) {\n\n\t\tif (m->lbwv[i] < 0)\n\t\t\tcontinue;\n\n\t\terr |= mbuf_printf(mb, \"b=%s:%i\\r\\n\",\n\t\t\t\t   sdp_bandwidth_name(i), m->lbwv[i]);\n\t}\n\n\tfor (le=m->lfmtl.head; le; le=le->next) {\n\n\t\tconst struct sdp_format *fmt = le->data;\n\n\t\tif (!str_isset(fmt->name))\n\t\t\tcontinue;\n\n\t\tif (!fmt->sup && !offer)\n\t\t\tcontinue;\n\n\t\tif ((str_ncmp(m->proto, \"RTP/\", 4) == 0) ||\n\t\t\t\t(str_str(m->proto, \"/RTP/\") != NULL)) {\n\n\t\t\terr |= mbuf_printf(mb, \"a=rtpmap:%s %s/%u\",\n\t\t\t\t\t   fmt->id, fmt->name, fmt->srate);\n\n\t\t\tif (fmt->ch > 1)\n\t\t\t\terr |= mbuf_printf(mb, \"/%u\", fmt->ch);\n\n\t\t\terr |= mbuf_printf(mb, \"\\r\\n\");\n\n\t\t}\n\n\t\tif (str_isset(fmt->params))\n\t\t\terr |= mbuf_printf(mb, \"a=fmtp:%s %s\\r\\n\",\n\t\t\t\t\t   fmt->id, fmt->params);\n\t\tif (fmt->ench)\n\t\t\terr |= fmt->ench(mb, fmt, offer, fmt->data);\n\t}\n\n\tif (sa_isset(&m->laddr_rtcp, SA_ALL))\n\t\terr |= mbuf_printf(mb, \"a=rtcp:%u IN IP%d %j\\r\\n\",\n\t\t\t\t   sa_port(&m->laddr_rtcp),\n\t\t\t\t   (AF_INET == sa_af(&m->laddr_rtcp)) ? 4 : 6,\n\t\t\t\t   &m->laddr_rtcp);\n\telse if (sa_isset(&m->laddr_rtcp, SA_PORT))\n\t\terr |= mbuf_printf(mb, \"a=rtcp:%u\\r\\n\",\n\t\t\t\t   sa_port(&m->laddr_rtcp));\n\n\terr |= mbuf_printf(mb, \"a=%s\\r\\n\", disabled ?\n\t\tsdp_dir_name(SDP_INACTIVE) :\n\t\tsdp_dir_name(offer ? m->ldir : m->ldir & m->rdir));\n\n\tfor (le = m->lattrl.head; le; le = le->next)\n\t\terr |= mbuf_printf(mb, \"%H\", sdp_attr_print, le->data);\n\n\tif (m->ench)\n\t\terr |= m->ench(mb, offer, m->arg);\n\n\treturn err;\n}\n\n\n/**\n * Encode an SDP Session into a memory buffer\n *\n * @param mbp   Pointer to allocated memory buffer\n * @param sess  SDP Session\n * @param offer True if SDP Offer, False if SDP Answer\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_encode(struct mbuf **mbp, struct sdp_session *sess, bool offer)\n{\n\tint ipver;\n\tenum sdp_bandwidth i;\n\tstruct mbuf *mb;\n\tstruct le *le;\n\tint err;\n\n\tif (!mbp || !sess)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tipver = sa_af(&sess->laddr) == AF_INET ? 4 : 6;\n\terr  = mbuf_printf(mb, \"v=%u\\r\\n\", SDP_VERSION);\n\terr |= mbuf_printf(mb, \"o=- %u %u IN IP%d %j\\r\\n\",\n\t\t\t   sess->id, sess->ver++, ipver, &sess->laddr);\n\terr |= mbuf_write_str(mb, \"s=-\\r\\n\");\n\terr |= mbuf_printf(mb, \"c=IN IP%d %j\\r\\n\", ipver, &sess->laddr);\n\n\tfor (i=SDP_BANDWIDTH_MIN; i<SDP_BANDWIDTH_MAX; i++) {\n\n\t\tif (sess->lbwv[i] < 0)\n\t\t\tcontinue;\n\n\t\terr |= mbuf_printf(mb, \"b=%s:%i\\r\\n\",\n\t\t\t\t   sdp_bandwidth_name(i), sess->lbwv[i]);\n\t}\n\n\terr |= mbuf_write_str(mb, \"t=0 0\\r\\n\");\n\n\tfor (le = sess->lattrl.head; le; le = le->next)\n\t\terr |= mbuf_printf(mb, \"%H\", sdp_attr_print, le->data);\n\n\tfor (le=sess->lmedial.head; offer && le;) {\n\n\t\tstruct sdp_media *m = le->data;\n\n\t\tle = le->next;\n\n\t\tif (m->disabled)\n\t\t\tcontinue;\n\n\t\tlist_unlink(&m->le);\n\t\tlist_append(&sess->medial, &m->le, m);\n\t}\n\n\tfor (le=sess->medial.head; le; le=le->next) {\n\n\t\tstruct sdp_media *m = le->data;\n\n\t\terr |= media_encode(m, mb, offer);\n\t}\n\n\tmb->pos = 0;\n\n\tif (err)\n\t\tmem_deref(mb);\n\telse\n\t\t*mbp = mb;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sdp/sdp.h",
    "content": "/**\n * @file sdp.h  Internal SDP interface\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nenum {\n\tRTP_DYNPT_START =  96,\n\tRTP_DYNPT_END   = 127,\n};\n\n\nstruct sdp_session {\n\tstruct list lmedial;\n\tstruct list medial;\n\tstruct list lattrl;\n\tstruct list rattrl;\n\tstruct sa laddr;\n\tstruct sa raddr;\n\tint32_t lbwv[SDP_BANDWIDTH_MAX];\n\tint32_t rbwv[SDP_BANDWIDTH_MAX];\n\tuint32_t id;\n\tuint32_t ver;\n\tenum sdp_dir rdir;\n};\n\nstruct sdp_media {\n\tstruct le le;\n\tstruct list lfmtl;\n\tstruct list rfmtl;\n\tstruct list lattrl;\n\tstruct list rattrl;\n\tstruct sa laddr;\n\tstruct sa raddr;\n\tstruct sa laddr_rtcp;\n\tstruct sa raddr_rtcp;\n\tint32_t lbwv[SDP_BANDWIDTH_MAX];\n\tint32_t rbwv[SDP_BANDWIDTH_MAX];\n\tchar *name;\n\tchar *proto;\n\tchar *protov[8];\n\tchar *uproto;           /* unsupported protocol */\n\tsdp_media_enc_h *ench;\n\tvoid *arg;\n\tenum sdp_dir ldir;\n\tenum sdp_dir rdir;\n\tbool fmt_ignore;\n\tbool disabled;\n\tint dynpt;\n};\n\n\n/* session */\nvoid sdp_session_rreset(struct sdp_session *sess);\n\n\n/* media */\nint  sdp_media_radd(struct sdp_media **mp, struct sdp_session *sess,\n\t\t    const struct pl *name, const struct pl *proto);\nvoid sdp_media_rreset(struct sdp_media *m);\nbool sdp_media_proto_cmp(struct sdp_media *m, const struct pl *proto,\n\t\t\t bool update);\nstruct sdp_media *sdp_media_find(const struct sdp_session *sess,\n\t\t\t\t const struct pl *name,\n\t\t\t\t const struct pl *proto,\n\t\t\t\t bool update_proto);\nvoid sdp_media_align_formats(struct sdp_media *m, bool offer);\n\n\n/* format */\nint  sdp_format_radd(struct sdp_media *m, const struct pl *id);\nstruct sdp_format *sdp_format_find(const struct list *lst,\n\t\t\t\t   const struct pl *id);\n\n\n/* attribute */\nstruct sdp_attr;\n\nint  sdp_attr_add(struct list *lst, struct pl *name, struct pl *val);\nint  sdp_attr_addv(struct list *lst, const char *name, const char *val,\n\t\t   va_list ap);\nvoid sdp_attr_del(const struct list *lst, const char *name);\nconst char *sdp_attr_apply(const struct list *lst, const char *name,\n\t\t\t   sdp_attr_h *attrh, void *arg);\nint sdp_attr_print(struct re_printf *pf, const struct sdp_attr *attr);\nint sdp_attr_debug(struct re_printf *pf, const struct sdp_attr *attr);\n"
  },
  {
    "path": "src/sdp/session.c",
    "content": "/**\n * @file sdp/session.c  SDP Session\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_sdp.h>\n#include <re_sys.h>\n#include \"sdp.h\"\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sdp_session *sess = arg;\n\n\tlist_flush(&sess->lmedial);\n\tlist_flush(&sess->medial);\n\tlist_flush(&sess->rattrl);\n\tlist_flush(&sess->lattrl);\n}\n\n\n/**\n * Allocate a new SDP Session\n *\n * @param sessp Pointer to allocated SDP Session object\n * @param laddr Local network address\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_session_alloc(struct sdp_session **sessp, const struct sa *laddr)\n{\n\tstruct sdp_session *sess;\n\tint i;\n\n\tif (!sessp || !laddr)\n\t\treturn EINVAL;\n\n\tsess = mem_zalloc(sizeof(*sess), destructor);\n\tif (!sess)\n\t\treturn ENOMEM;\n\n\tsess->laddr = *laddr;\n\tsess->id    = rand_u32();\n\tsess->ver   = rand_u32() & 0x7fffffff;\n\tsess->rdir  = SDP_SENDRECV;\n\n\tsa_init(&sess->raddr, AF_INET);\n\n\tfor (i=0; i<SDP_BANDWIDTH_MAX; i++) {\n\t\tsess->lbwv[i] = -1;\n\t\tsess->rbwv[i] = -1;\n\t}\n\n\t*sessp = sess;\n\n\treturn 0;\n}\n\n\n/**\n * Reset the remote side of an SDP Session\n *\n * @param sess SDP Session\n */\nvoid sdp_session_rreset(struct sdp_session *sess)\n{\n\tint i;\n\n\tif (!sess)\n\t\treturn;\n\n\tsa_init(&sess->raddr, AF_INET);\n\n\tlist_flush(&sess->rattrl);\n\n\tsess->rdir = SDP_SENDRECV;\n\n\tfor (i=0; i<SDP_BANDWIDTH_MAX; i++)\n\t\tsess->rbwv[i] = -1;\n}\n\n\n/**\n * Set the local network address of an SDP Session\n *\n * @param sess  SDP Session\n * @param laddr Local network address\n */\nvoid sdp_session_set_laddr(struct sdp_session *sess, const struct sa *laddr)\n{\n\tif (!sess || !laddr)\n\t\treturn;\n\n\tsess->laddr = *laddr;\n}\n\n\n/**\n * Get the local network address of an SDP Session\n *\n * @param sess  SDP Session\n * @return Local network address\n */\nconst struct sa *sdp_session_laddr(struct sdp_session *sess)\n{\n\treturn sess ? &sess->laddr : NULL;\n}\n\n\n/**\n * Set the local bandwidth of an SDP Session\n *\n * @param sess SDP Session\n * @param type Bandwidth type\n * @param bw   Bandwidth value\n */\nvoid sdp_session_set_lbandwidth(struct sdp_session *sess,\n\t\t\t\tenum sdp_bandwidth type, int32_t bw)\n{\n\tif (!sess || type < SDP_BANDWIDTH_MIN || type >= SDP_BANDWIDTH_MAX)\n\t\treturn;\n\n\tsess->lbwv[type] = bw;\n}\n\n\n/**\n * Set a local attribute of an SDP Session\n *\n * @param sess    SDP Session\n * @param replace True to replace any existing attributes, false to append\n * @param name    Attribute name\n * @param value   Formatted attribute value\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_session_set_lattr(struct sdp_session *sess, bool replace,\n\t\t\t  const char *name, const char *value, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tif (!sess || !name)\n\t\treturn EINVAL;\n\n\tif (replace)\n\t\tsdp_attr_del(&sess->lattrl, name);\n\n\tva_start(ap, value);\n\terr = sdp_attr_addv(&sess->lattrl, name, value, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Delete a local attribute of an SDP Session\n *\n * @param sess SDP Session\n * @param name Attribute name\n */\nvoid sdp_session_del_lattr(struct sdp_session *sess, const char *name)\n{\n\tif (!sess || !name)\n\t\treturn;\n\n\tsdp_attr_del(&sess->lattrl, name);\n}\n\n\n/**\n * Get the local bandwidth of an SDP Session\n *\n * @param sess SDP Session\n * @param type Bandwidth type\n *\n * @return Bandwidth value\n */\nint32_t sdp_session_lbandwidth(const struct sdp_session *sess,\n\t\t\t       enum sdp_bandwidth type)\n{\n\tif (!sess || type < SDP_BANDWIDTH_MIN || type >= SDP_BANDWIDTH_MAX)\n\t\treturn 0;\n\n\treturn sess->lbwv[type];\n}\n\n\n/**\n * Get the remote bandwidth of an SDP Session\n *\n * @param sess SDP Session\n * @param type Bandwidth type\n *\n * @return Bandwidth value\n */\nint32_t sdp_session_rbandwidth(const struct sdp_session *sess,\n\t\t\t\tenum sdp_bandwidth type)\n{\n\tif (!sess || type < SDP_BANDWIDTH_MIN || type >= SDP_BANDWIDTH_MAX)\n\t\treturn 0;\n\n\treturn sess->rbwv[type];\n}\n\n\n/**\n * Get a remote attribute of an SDP Session\n *\n * @param sess SDP Session\n * @param name Attribute name\n *\n * @return Attribute value if exist, NULL if not exist\n */\nconst char *sdp_session_rattr(const struct sdp_session *sess, const char *name)\n{\n\tif (!sess || !name)\n\t\treturn NULL;\n\n\treturn sdp_attr_apply(&sess->rattrl, name, NULL, NULL);\n}\n\n\n/**\n * Apply a function handler of all matching remote attributes\n *\n * @param sess  SDP Session\n * @param name  Attribute name\n * @param attrh Attribute handler\n * @param arg   Handler argument\n *\n * @return Attribute value if match\n */\nconst char *sdp_session_rattr_apply(const struct sdp_session *sess,\n\t\t\t\t    const char *name,\n\t\t\t\t    sdp_attr_h *attrh, void *arg)\n{\n\tif (!sess)\n\t\treturn NULL;\n\n\treturn sdp_attr_apply(&sess->rattrl, name, attrh, arg);\n}\n\n\n/**\n * Get the list of media-lines from an SDP Session\n *\n * @param sess  SDP Session\n * @param local True for local, False for remote\n *\n * @return List of media-lines\n */\nconst struct list *sdp_session_medial(const struct sdp_session *sess,\n\t\t\t\t      bool local)\n{\n\tif (!sess)\n\t\treturn NULL;\n\n\treturn local ? &sess->lmedial : &sess->medial;\n}\n\n\n/**\n * Print SDP Session debug information\n *\n * @param pf   Print function for output\n * @param sess SDP Session\n *\n * @return 0 if success, otherwise errorcode\n */\nint sdp_session_debug(struct re_printf *pf, const struct sdp_session *sess)\n{\n\tstruct le *le;\n\tint err;\n\n\tif (!sess)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"SDP session\\n\");\n\n\terr |= re_hprintf(pf, \"  local attributes:\\n\");\n\n\tfor (le=sess->lattrl.head; le; le=le->next)\n\t\terr |= re_hprintf(pf, \"    %H\\n\", sdp_attr_debug, le->data);\n\n\terr |= re_hprintf(pf, \"  remote attributes:\\n\");\n\terr |= re_hprintf(pf, \"  remote direction: %s\\n\",\n\t\t\tsdp_dir_name(sess->rdir));\n\n\tfor (le=sess->rattrl.head; le; le=le->next)\n\t\terr |= re_hprintf(pf, \"    %H\\n\", sdp_attr_debug, le->data);\n\n\terr |= re_hprintf(pf, \"session media:\\n\");\n\n\tfor (le=sess->medial.head; le; le=le->next)\n\t\terr |= sdp_media_debug(pf, le->data);\n\n\terr |= re_hprintf(pf, \"local media:\\n\");\n\n\tfor (le=sess->lmedial.head; le; le=le->next)\n\t\terr |= sdp_media_debug(pf, le->data);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sdp/str.c",
    "content": "/**\n * @file sdp/str.c  SDP strings\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_sdp.h>\n\n\nconst char sdp_attr_fmtp[]     = \"fmtp\";      /**< fmtp                 */\nconst char sdp_attr_maxptime[] = \"maxptime\";  /**< maxptime             */\nconst char sdp_attr_ptime[]    = \"ptime\";     /**< ptime                */\nconst char sdp_attr_rtcp[]     = \"rtcp\";      /**< rtcp                 */\nconst char sdp_attr_rtpmap[]   = \"rtpmap\";    /**< rtpmap               */\n\nconst char sdp_media_audio[]   = \"audio\";     /**< Media type 'audio'   */\nconst char sdp_media_video[]   = \"video\";     /**< Media type 'video'   */\nconst char sdp_media_text[]    = \"text\";      /**< Media type 'text'    */\n\nconst char sdp_proto_rtpavp[]  = \"RTP/AVP\";   /**< RTP Profile          */\nconst char sdp_proto_rtpsavp[] = \"RTP/SAVP\";  /**< Secure RTP Profile   */\n\n\n/**\n * Get the SDP media direction name\n *\n * @param dir Media direction\n *\n * @return Name of media direction\n */\nconst char *sdp_dir_name(enum sdp_dir dir)\n{\n\tswitch (dir) {\n\n\tcase SDP_INACTIVE: return \"inactive\";\n\tcase SDP_RECVONLY: return \"recvonly\";\n\tcase SDP_SENDONLY: return \"sendonly\";\n\tcase SDP_SENDRECV: return \"sendrecv\";\n\tdefault:           return \"??\";\n\t}\n}\n\n\n/**\n * Get the SDP bandwidth name\n *\n * @param type Bandwidth type\n *\n * @return Bandwidth name\n */\nconst char *sdp_bandwidth_name(enum sdp_bandwidth type)\n{\n\tswitch (type) {\n\n\tcase SDP_BANDWIDTH_CT:   return \"CT\";\n\tcase SDP_BANDWIDTH_AS:   return \"AS\";\n\tcase SDP_BANDWIDTH_RS:   return \"RS\";\n\tcase SDP_BANDWIDTH_RR:   return \"RR\";\n\tcase SDP_BANDWIDTH_TIAS: return \"TIAS\";\n\tdefault:                 return \"??\";\n\t}\n}\n"
  },
  {
    "path": "src/sdp/util.c",
    "content": "/**\n * @file sdp/util.c  SDP utility functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_sdp.h>\n\n\n/**\n * Decode an SDP direction\n *\n * @param pl  SDP direction as string\n *\n * @return sdp_dir SDP direction, SDP_SENDRECV as fallback\n */\nenum sdp_dir sdp_dir_decode(const struct pl *pl)\n{\n       if (!pl_strcmp(pl, \"off\")) {\n               return SDP_INACTIVE;\n       }\n       else if (!pl_strcmp(pl, \"inactive\")) {\n               return SDP_INACTIVE;\n       }\n       else if (!pl_strcmp(pl, \"sendonly\")) {\n               return  SDP_SENDONLY;\n       }\n       else if (!pl_strcmp(pl, \"recvonly\")) {\n               return SDP_RECVONLY;\n       }\n\n       return SDP_SENDRECV;\n}\n\n/**\n * Decode RTP Header Extension SDP attribute value\n *\n * @param ext Extension-map object\n * @param val SDP attribute value\n *\n * @return 0 for success, otherwise errorcode\n */\nint sdp_extmap_decode(struct sdp_extmap *ext, const char *val)\n{\n\tstruct pl id, dir;\n\n\tif (!ext || !val)\n\t\treturn EINVAL;\n\n\tif (re_regex(val, strlen(val), \"[0-9]+[/]*[a-z]* [^ ]+[ ]*[^ ]*\",\n\t\t     &id, NULL, &dir, &ext->name, NULL, &ext->attrs))\n\t\treturn EBADMSG;\n\n\text->dir_set = false;\n\text->dir = SDP_SENDRECV;\n\n\tif (pl_isset(&dir)) {\n\n\t\text->dir_set = true;\n\n\t\tif      (!pl_strcmp(&dir, \"sendonly\")) ext->dir = SDP_SENDONLY;\n\t\telse if (!pl_strcmp(&dir, \"sendrecv\")) ext->dir = SDP_SENDRECV;\n\t\telse if (!pl_strcmp(&dir, \"recvonly\")) ext->dir = SDP_RECVONLY;\n\t\telse if (!pl_strcmp(&dir, \"inactive\")) ext->dir = SDP_INACTIVE;\n\t\telse ext->dir_set = false;\n\t}\n\n\text->id = pl_u32(&id);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/sha/wrap.c",
    "content": "/**\n * @file wrap.c  SHA wrappers\n *\n * Copyright (C) 2022 Alfred E. Heggestad\n * Copyright (C) 2022 Sebastian Reimers <hallo@studio-link.de>\n */\n\n#include <re_types.h>\n#include <re_mbuf.h>\n#ifdef USE_OPENSSL\n#include <openssl/sha.h>\n#elif defined (__APPLE__)\n#include <CommonCrypto/CommonDigest.h>\n#elif defined (WIN32)\n#include <windows.h>\n#include <wincrypt.h>\n#elif defined (USE_MBEDTLS)\n#include <mbedtls/sha1.h>\n#include <mbedtls/sha256.h>\n#include <mbedtls/error.h>\n#endif\n#include <re_sha.h>\n\n\n#define DEBUG_MODULE \"sha\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n#if !defined (USE_OPENSSL) && defined (WIN32)\nstatic void compute_hash(ALG_ID alg_id, const void *data, size_t data_size,\n\t\t\t uint8_t *md, DWORD hash_size)\n{\n\tHCRYPTPROV context;\n\tHCRYPTHASH hash;\n\n\tCryptAcquireContext(&context, 0, 0, PROV_RSA_AES,CRYPT_VERIFYCONTEXT);\n\n\tCryptCreateHash(context, alg_id, 0, 0, &hash);\n\tCryptHashData(hash, (BYTE*)data, (DWORD)data_size, 0);\n\tCryptGetHashParam(hash, HP_HASHVAL, md, &hash_size, 0);\n\n\tCryptDestroyHash(hash);\n\tCryptReleaseContext(context, 0);\n}\n#endif\n\n\n/**\n * Calculate the SHA1 hash from a buffer\n *\n * @param d  Data buffer (input)\n * @param n  Number of input bytes\n * @param md Calculated SHA1 hash (output)\n */\nvoid sha1(const uint8_t *d, size_t n, uint8_t *md)\n{\n#ifdef USE_OPENSSL\n\t(void)SHA1(d, n, md);\n#elif defined (__APPLE__)\n\tCC_SHA1(d, (uint32_t)n, md);\n#elif defined (WIN32)\n\tcompute_hash(CALG_SHA1, d, n, md, SHA1_DIGEST_SIZE);\n#elif defined (MBEDTLS_MD_C)\n\tint err;\n\n\terr = mbedtls_sha1(d, n, md);\n\tif (err)\n\t\tDEBUG_WARNING(\"mbedtls_sha1: %s\\n\",\n\t\t\t      mbedtls_high_level_strerr(err));\n#else\n\t(void)d;\n\t(void)n;\n\t(void)md;\n#error missing SHA-1 backend\n#endif\n}\n\n\n/**\n * Calculate the SHA256 hash from a buffer\n *\n * @param d  Data buffer (input)\n * @param n  Number of input bytes\n * @param md Calculated SHA1 hash (output)\n */\nvoid sha256(const uint8_t *d, size_t n, uint8_t *md)\n{\n#ifdef USE_OPENSSL\n\t(void)SHA256(d, n, md);\n#elif defined (__APPLE__)\n\tCC_SHA256(d, (uint32_t)n, md);\n#elif defined (WIN32)\n\tcompute_hash(CALG_SHA_256, d, n, md, SHA256_DIGEST_SIZE);\n#elif defined (MBEDTLS_MD_C)\n\tint err;\n\n\terr = mbedtls_sha256(d, n, md, 0);\n\tif (err)\n\t\tDEBUG_WARNING(\"mbedtls_sha256: %s\\n\",\n\t\t\t      mbedtls_high_level_strerr(err));\n#else\n\t(void)d;\n\t(void)n;\n\t(void)md;\n#error missing SHA-256 backend\n#endif\n}\n\n\n/**\n * Calculate the SHA-256 hash from a formatted string\n *\n * @param md  Calculated SHA-256 hash\n * @param fmt Formatted string\n *\n * @return 0 if success, otherwise errorcode\n */\nint sha256_printf(uint8_t md[32], const char *fmt, ...)\n{\n\tstruct mbuf mb;\n\tva_list ap;\n\tint err;\n\n\tmbuf_init(&mb);\n\n\tva_start(ap, fmt);\n\terr = mbuf_vprintf(&mb, fmt, ap);\n\tva_end(ap);\n\n\tif (!err)\n\t\tsha256(mb.buf, mb.end, md);\n\n\tmbuf_reset(&mb);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/shim/shim.c",
    "content": "/**\n * @file re_shim.h  Interface to SHIM layer\n *\n * Copyright (C) 2015 - 2022 Alfred E. Heggestad\n */\n\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_tcp.h>\n#include <re_net.h>\n#include <re_shim.h>\n#include <re_convert.h>\n\n\n#define DEBUG_MODULE \"shim\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstruct shim {\n\tstruct tcp_conn *tc;\n\tstruct tcp_helper *th;\n\tstruct mbuf *mb;\n\tshim_frame_h *frameh;\n\tvoid *arg;\n\n\tuint64_t n_tx;\n\tuint64_t n_rx;\n};\n\n\n/* responsible for adding the SHIM header\n   - assumes that the sent MBUF contains a complete packet\n */\nstatic bool shim_send_handler(int *err, struct mbuf *mb, void *arg)\n{\n\tstruct shim *shim = arg;\n\tint err_len;\n\tuint16_t len;\n\t(void)shim;\n\n\tif (mb->pos < SHIM_HDR_SIZE) {\n\t\tDEBUG_WARNING(\"send: not enough space for SHIM header\\n\");\n\t\t*err = ENOMEM;\n\t\treturn true;\n\t}\n\n\terr_len = try_into_u16_from_size(&len, mbuf_get_left(mb));\n\tif (err_len) {\n\t\tDEBUG_WARNING(\"send: mbuf to big\\n\");\n\t\t*err = err_len;\n\t\treturn true;\n\t}\n\n\tmb->pos -= SHIM_HDR_SIZE;\n\t*err = mbuf_write_u16(mb, htons(len));\n\tmb->pos -= SHIM_HDR_SIZE;\n\n\t++shim->n_tx;\n\n\treturn false;\n}\n\n\nstatic bool shim_recv_handler(int *errp, struct mbuf *mbx, bool *estab,\n\t\t\t      void *arg)\n{\n\tstruct shim *shim = arg;\n\tint err = 0;\n\t(void)estab;\n\n\t/* handle re-assembly */\n\tif (!shim->mb) {\n\t\tshim->mb = mbuf_alloc(1024);\n\t\tif (!shim->mb) {\n\t\t\t*errp = ENOMEM;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (shim->mb) {\n\t\tsize_t pos;\n\n\t\tpos = shim->mb->pos;\n\n\t\tshim->mb->pos = shim->mb->end;\n\n\t\terr = mbuf_write_mem(shim->mb, mbuf_buf(mbx),\n\t\t\t\t     mbuf_get_left(mbx));\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tshim->mb->pos = pos;\n\t}\n\n\t/* extract all SHIM-frames in the TCP-stream */\n\tfor (;;) {\n\n\t\tsize_t start, len, pos, end;\n\t\tbool hdld;\n\n\t\tstart = shim->mb->pos;\n\n\t\tif (mbuf_get_left(shim->mb) < (SHIM_HDR_SIZE))\n\t\t\tbreak;\n\n\t\tlen = ntohs(mbuf_read_u16(shim->mb));\n\n\t\tif (mbuf_get_left(shim->mb) < len)\n\t\t\tgoto rewind;\n\n\t\tpos = shim->mb->pos;\n\t\tend = shim->mb->end;\n\n\t\tshim->mb->end = pos + len;\n\n\t\t++shim->n_rx;\n\n\t\thdld = shim->frameh(shim->mb, shim->arg);\n\t\tif (!hdld) {\n\t\t\t/* XXX: handle multiple frames per segment */\n\n\t\t\tshim->mb->pos = pos;\n\t\t\tshim->mb->end = pos + len;\n\n\t\t\tmbx->pos = mbx->end = 2;\n\t\t\terr = mbuf_write_mem(mbx, mbuf_buf(shim->mb), len);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t\tmbx->pos = 2;\n\n\t\t\tshim->mb->pos = pos + len;\n\t\t\tshim->mb->end = end;\n\n\t\t\treturn false;  /* continue recv-handlers */\n\t\t}\n\n\t\tshim->mb->pos = pos + len;\n\t\tshim->mb->end = end;\n\n\t\tif (shim->mb->pos >= shim->mb->end) {\n\t\t\tshim->mb = mem_deref(shim->mb);\n\t\t\tbreak;\n\t\t}\n\n\t\tcontinue;\n\n\trewind:\n\t\tshim->mb->pos = start;\n\t\tbreak;\n\t}\n\n out:\n\tif (err)\n\t\t*errp = err;\n\n\treturn true;  /* always handled */\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct shim *shim = arg;\n\n\tmem_deref(shim->th);\n\tmem_deref(shim->tc);\n\tmem_deref(shim->mb);\n}\n\n\nint shim_insert(struct shim **shimp, struct tcp_conn *tc, int layer,\n\t\tshim_frame_h *frameh, void *arg)\n{\n\tstruct shim *shim;\n\tint err;\n\n\tif (!shimp || !tc || !frameh)\n\t\treturn EINVAL;\n\n\tshim = mem_zalloc(sizeof(*shim), destructor);\n\tif (!shim)\n\t\treturn ENOMEM;\n\n\tshim->tc = mem_ref(tc);\n\terr = tcp_register_helper(&shim->th, tc, layer, NULL,\n\t\t\t\t  shim_send_handler,\n\t\t\t\t  shim_recv_handler, shim);\n\tif (err)\n\t\tgoto out;\n\n\tshim->frameh = frameh;\n\tshim->arg = arg;\n\n out:\n\tif (err)\n\t\tmem_deref(shim);\n\telse\n\t\t*shimp = shim;\n\n\treturn err;\n}\n\n\nint shim_debug(struct re_printf *pf, const struct shim *shim)\n{\n\tif (!shim)\n\t\treturn 0;\n\n\treturn re_hprintf(pf, \"tx=%llu, rx=%llu\", shim->n_tx, shim->n_rx);\n}\n"
  },
  {
    "path": "src/sip/addr.c",
    "content": "/**\n * @file sip/addr.c  SIP Address decode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_uri.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_msg.h>\n#include <re_sip.h>\n\n\n/**\n * Decode a pointer-length string into a SIP Address object\n *\n * @param addr SIP Address object\n * @param pl   Pointer-length string\n *\n * @return 0 for success, otherwise errorcode\n */\nint sip_addr_decode(struct sip_addr *addr, const struct pl *pl)\n{\n\tint err;\n\n\tif (!addr || !pl)\n\t\treturn EINVAL;\n\n\tmemset(addr, 0, sizeof(*addr));\n\n\tif (0 == re_regex(pl->p, pl->l, \"[~ \\t\\r\\n<]*[ \\t\\r\\n]*<[^>]+>[^]*\",\n\t\t\t  &addr->dname, NULL, &addr->auri, &addr->params)) {\n\n\t\tif (!addr->dname.l)\n\t\t\taddr->dname.p = NULL;\n\n\t\tif (!addr->params.l)\n\t\t\taddr->params.p = NULL;\n\t}\n\telse {\n\t\tmemset(addr, 0, sizeof(*addr));\n\n\t\tif (re_regex(pl->p, pl->l, \"[^;]+[^]*\",\n\t\t\t     &addr->auri, &addr->params))\n\t\t\treturn EBADMSG;\n\t}\n\n\terr = uri_decode(&addr->uri, &addr->auri);\n\tif (err)\n\t\tmemset(addr, 0, sizeof(*addr));\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sip/auth.c",
    "content": "/**\n * @file sip/auth.c  SIP Authentication\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <time.h>\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_uri.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_sys.h>\n#include <re_md5.h>\n#include <re_sha.h>\n#include <re_httpauth.h>\n#include <re_udp.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include \"sip.h\"\n\n#define DEBUG_MODULE \"sip_auth\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nenum {\n\tNONCE_EXPIRES  = 300,\n\tNONCE_MIN_SIZE = 33,\n};\n\nstruct sip_auth {\n\tstruct list realml;\n\tsip_auth_h *authh;\n\tvoid *arg;\n\tbool ref;\n\tint err;\n};\n\n\nstruct realm {\n\tstruct le le;\n\tchar *realm;\n\tchar *nonce;\n\tchar *qop;\n\tchar *opaque;\n\tchar *user;\n\tchar *pass;\n\tchar *algorithm;\n\tuint32_t nc;\n\tenum sip_hdrid hdr;\n};\n\n\nstatic int dummy_handler(char **user, char **pass, const char *rlm, void *arg)\n{\n\t(void)user;\n\t(void)pass;\n\t(void)rlm;\n\t(void)arg;\n\n\treturn EAUTH;\n}\n\n\nstatic void realm_destructor(void *arg)\n{\n\tstruct realm *realm = arg;\n\n\tlist_unlink(&realm->le);\n\tmem_deref(realm->realm);\n\tmem_deref(realm->nonce);\n\tmem_deref(realm->qop);\n\tmem_deref(realm->opaque);\n\tmem_deref(realm->user);\n\tmem_deref(realm->pass);\n\tmem_deref(realm->algorithm);\n}\n\n\nstatic void auth_destructor(void *arg)\n{\n\tstruct sip_auth *auth = arg;\n\n\tif (auth->ref)\n\t\tmem_deref(auth->arg);\n\n\tlist_flush(&auth->realml);\n}\n\n\nstatic int mkdigest(struct mbuf **digestp, const struct realm *realm,\n\t\t    const char *met, const char *uri, uint64_t cnonce)\n{\n\tstruct mbuf *digest;\n\tuint8_t *ha1 = NULL, *ha2 = NULL;\n\tdigest_printf_h *digest_printf;\n\tint err;\n\n\tbool use_sha256 = str_casecmp(realm->algorithm, \"sha-256\") == 0;\n\tsize_t h_size\t= use_sha256 ? SHA256_DIGEST_SIZE : MD5_SIZE;\n\n\tdigest = mbuf_alloc(h_size);\n\tif (!digest)\n\t\treturn ENOMEM;\n\n\tmbuf_set_end(digest, h_size);\n\n\tha1 = mem_zalloc(h_size, NULL);\n\tif (!ha1) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tha2 = mem_zalloc(h_size, NULL);\n\tif (!ha2) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tif (use_sha256)\n\t\tdigest_printf = &sha256_printf;\n\telse\n\t\tdigest_printf = &md5_printf;\n\n\terr = digest_printf(ha1, \"%s:%s:%s\", realm->user, realm->realm,\n\t\t\t    realm->pass);\n\tif (err)\n\t\tgoto out;\n\n\terr = digest_printf(ha2, \"%s:%s\", met, uri);\n\tif (err)\n\t\tgoto out;\n\n\tif (realm->qop)\n\t\terr = digest_printf(\n\t\t\tmbuf_buf(digest), \"%w:%s:%08x:%016llx:auth:%w\", ha1,\n\t\t\th_size, realm->nonce, realm->nc, cnonce, ha2, h_size);\n\telse\n\t\terr = digest_printf(mbuf_buf(digest), \"%w:%s:%w\", ha1, h_size,\n\t\t\t\t    realm->nonce, ha2, h_size);\nout:\n\tmem_deref(ha1);\n\tmem_deref(ha2);\n\n\tif (err)\n\t\tmem_deref(digest);\n\telse\n\t\t*digestp = digest;\n\n\treturn err;\n}\n\n\nstatic bool cmp_handler(struct le *le, void *arg)\n{\n\tstruct realm *realm = le->data;\n\tstruct pl *chrealm = arg;\n\n\t/* handle multiple authenticate headers with equal realm value */\n\tif (realm->nc == 1)\n\t\treturn false;\n\n\treturn 0 == pl_strcasecmp(chrealm, realm->realm);\n}\n\n\nstatic bool auth_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,\n\t\t\t void *arg)\n{\n\tstruct httpauth_digest_chall ch;\n\tstruct sip_auth *auth = arg;\n\tstruct realm *realm   = NULL;\n\tint err;\n\t(void)msg;\n\n\tif (httpauth_digest_challenge_decode(&ch, &hdr->val)) {\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\tif (!pl_isset(&ch.algorithm))\n\t\tpl_set_str(&ch.algorithm, \"MD5\");\n\n\tif (pl_strcasecmp(&ch.algorithm, \"md5\") &&\n\t    pl_strcasecmp(&ch.algorithm, \"sha-256\")) {\n\t\terr = ENOSYS;\n\t\tgoto out;\n\t}\n\n\trealm = list_ledata(list_apply(&auth->realml, true, cmp_handler,\n\t\t\t\t       &ch.realm));\n\tif (!realm) {\n\t\trealm = mem_zalloc(sizeof(*realm), realm_destructor);\n\t\tif (!realm) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\tlist_append(&auth->realml, &realm->le, realm);\n\n\t\terr = pl_strdup(&realm->realm, &ch.realm);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = pl_strdup(&realm->algorithm, &ch.algorithm);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = auth->authh(&realm->user, &realm->pass,\n\t\t\t\t  realm->realm, auth->arg);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse {\n\t\tif (!pl_isset(&ch.stale) || pl_strcasecmp(&ch.stale, \"true\")) {\n\t\t\terr = EAUTH;\n\t\t\tgoto out;\n\t\t}\n\n\t\trealm->nonce\t = mem_deref(realm->nonce);\n\t\trealm->qop\t = mem_deref(realm->qop);\n\t\trealm->opaque\t = mem_deref(realm->opaque);\n\t}\n\n\trealm->hdr = hdr->id;\n\trealm->nc  = 1;\n\n\terr = pl_strdup(&realm->nonce, &ch.nonce);\n\n\tif (pl_isset(&ch.qop))\n\t\terr |= pl_strdup(&realm->qop, &ch.qop);\n\n\tif (pl_isset(&ch.opaque))\n\t\terr |= pl_strdup(&realm->opaque, &ch.opaque);\n\nout:\n\tif (err) {\n\t\tmem_deref(realm);\n\t\tauth->err = err;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/**\n * Update a SIP authentication state from a SIP message\n *\n * @param auth SIP Authentication state\n * @param msg  SIP Message\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_auth_authenticate(struct sip_auth *auth, const struct sip_msg *msg)\n{\n\tif (!auth || !msg)\n\t\treturn EINVAL;\n\n\tif (sip_msg_hdr_apply(msg, true, SIP_HDR_WWW_AUTHENTICATE,\n\t\t\t      auth_handler, auth))\n\t\treturn auth->err;\n\n\tif (sip_msg_hdr_apply(msg, true, SIP_HDR_PROXY_AUTHENTICATE,\n\t\t\t      auth_handler, auth))\n\t\treturn auth->err;\n\n\treturn 0;\n}\n\n\nint sip_auth_encode(struct mbuf *mb, struct sip_auth *auth, const char *met,\n\t\t    const char *uri)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tif (!mb || !auth || !met || !uri)\n\t\treturn EINVAL;\n\n\tfor (le = auth->realml.head; le; le = le->next) {\n\n\t\tconst uint64_t cnonce = rand_u64();\n\t\tstruct realm *realm = le->data;\n\t\tstruct mbuf *digest = NULL;\n\n\t\terr = mkdigest(&digest, realm, met, uri, cnonce);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tswitch (realm->hdr) {\n\n\t\tcase SIP_HDR_WWW_AUTHENTICATE:\n\t\t\terr = mbuf_write_str(mb, \"Authorization: \");\n\t\t\tbreak;\n\n\t\tcase SIP_HDR_PROXY_AUTHENTICATE:\n\t\t\terr = mbuf_write_str(mb, \"Proxy-Authorization: \");\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tcontinue;\n\t\t}\n\n\t\terr |= mbuf_printf(mb, \"Digest username=\\\"%s\\\"\", realm->user);\n\t\terr |= mbuf_printf(mb, \", realm=\\\"%s\\\"\", realm->realm);\n\t\terr |= mbuf_printf(mb, \", nonce=\\\"%s\\\"\", realm->nonce);\n\t\terr |= mbuf_printf(mb, \", uri=\\\"%s\\\"\", uri);\n\t\terr |= mbuf_printf(mb, \", response=\\\"%w\\\"\", digest->buf,\n\t\t\t\t   digest->end);\n\t\tdigest = mem_deref(digest);\n\n\t\tif (realm->opaque)\n\t\t\terr |= mbuf_printf(mb, \", opaque=\\\"%s\\\"\",\n\t\t\t\t\t   realm->opaque);\n\n\t\tif (realm->qop) {\n\t\t\terr |= mbuf_printf(mb, \", cnonce=\\\"%016llx\\\"\", cnonce);\n\t\t\terr |= mbuf_write_str(mb, \", qop=auth\");\n\t\t\terr |= mbuf_printf(mb, \", nc=%08x\", realm->nc);\n\t\t}\n\n\t\t++realm->nc;\n\n\t\terr |= mbuf_printf(mb, \", algorithm=%s\", realm->algorithm);\n\t\terr |= mbuf_write_str(mb, \"\\r\\n\");\n\t\tif (err)\n\t\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Allocate a SIP authentication state\n *\n * @param authp Pointer to allocated SIP authentication state\n * @param authh Authentication handler\n * @param arg   Handler argument\n * @param ref   True to mem_ref() argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_auth_alloc(struct sip_auth **authp, sip_auth_h *authh,\n\t\t   void *arg, bool ref)\n{\n\tstruct sip_auth *auth;\n\n\tif (!authp)\n\t\treturn EINVAL;\n\n\tauth = mem_zalloc(sizeof(*auth), auth_destructor);\n\tif (!auth)\n\t\treturn ENOMEM;\n\n\tauth->authh = authh ? authh : dummy_handler;\n\tauth->arg   = ref ? mem_ref(arg) : arg;\n\tauth->ref   = ref;\n\n\t*authp = auth;\n\n\treturn 0;\n}\n\n\n/**\n * Reset a SIP authentication state\n *\n * @param auth SIP Authentication state\n */\nvoid sip_auth_reset(struct sip_auth *auth)\n{\n\tif (!auth)\n\t\treturn;\n\n\tlist_flush(&auth->realml);\n}\n\n\nstatic int gen_nonce(char **noncep, time_t ts, const struct sa *src,\n\t\t     const char *realm)\n{\n\tuint8_t key[MD5_SIZE];\n\tstruct mbuf *mb;\n\tint err;\n\n\tmb = mbuf_alloc(40);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = mbuf_printf(mb,\"%lu%j%s\", (long unsigned)ts, src, realm);\n\tif (err)\n\t\tgoto out;\n\n\tmd5(mb->buf, mb->end, key);\n\tmbuf_rewind(mb);\n\terr = mbuf_printf(mb,\"%w%016lx\", key, sizeof(key), (long unsigned)ts);\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(mb, 0);\n\terr = mbuf_strdup(mb, noncep, mbuf_get_left(mb));\n\nout:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstatic int check_nonce(const struct pl *nonce, const struct sa *src,\n\t\t       const char *realm)\n{\n\tstruct pl pl;\n\ttime_t ts;\n\tchar *comp = NULL;\n\tbool eq;\n\tint err;\n\n\tif (!nonce || !nonce->p || nonce->l < NONCE_MIN_SIZE)\n\t\treturn EINVAL;\n\n\tpl = *nonce;\n\tpl.p = pl.p + (pl.l - 16);\n\tpl.l = 16;\n\tts = (time_t) pl_x64(&pl);\n\n\tif (time(NULL) - ts > NONCE_EXPIRES)\n\t\treturn ETIMEDOUT;\n\n\terr = gen_nonce(&comp, ts, src, realm);\n\tif (err)\n\t\treturn err;\n\n\teq = !pl_strcmp(nonce, comp);\n\tmem_deref(comp);\n\treturn eq ? 0 : EAUTH;\n}\n\n\nint sip_uas_auth_print(struct re_printf *pf,\n\t\t       const struct sip_uas_auth *auth)\n{\n\treturn re_hprintf(pf, \"WWW-Authenticate: \"\n\t\t\t      \"Digest realm=\\\"%s\\\", nonce=\\\"%s\\\", \"\n\t\t\t      \"algorithm=MD5, \"\n\t\t\t      \"qop=\\\"auth\\\"%s\"\n\t\t\t      \"\\r\\n\",\n\t\t\t      auth->realm, auth->nonce,\n\t\t\t      auth->stale ? \", stale=true\" : \"\");\n}\n\n\nstatic void sip_uas_destructor(void *arg)\n{\n\tstruct sip_uas_auth *auth = arg;\n\n\tmem_deref(auth->nonce);\n}\n\n\nint sip_uas_auth_gen(struct sip_uas_auth **authp, const struct sip_msg *msg,\n\t\t     const char *realm)\n{\n\tstruct sip_uas_auth *auth;\n\tint err;\n\n\tif (!authp || !msg)\n\t\treturn EINVAL;\n\n\tauth = mem_zalloc(sizeof(*auth), sip_uas_destructor);\n\tif (!auth)\n\t\treturn ENOMEM;\n\n\tauth->realm = realm;\n\terr  = gen_nonce(&auth->nonce, time(NULL), &msg->src, realm);\n\n\tif (err)\n\t\tmem_deref(auth);\n\telse\n\t\t*authp = auth;\n\n\treturn err;\n}\n\n\nint sip_uas_auth_check(struct sip_uas_auth *auth, const struct sip_msg *msg,\n\t\t       sip_uas_auth_h *authh, void *arg)\n{\n\tstruct httpauth_digest_resp resp;\n\tconst struct sip_hdr *hdr;\n\tuint8_t ha1[MD5_SIZE];\n\tint err;\n\n\tif (!msg || !auth || !authh)\n\t\treturn EINVAL;\n\n\thdr = sip_msg_hdr_apply(msg, true, SIP_HDR_AUTHORIZATION, NULL, NULL);\n\tif (!hdr)\n\t\treturn EAUTH;\n\n\tif (httpauth_digest_response_decode(&resp, &hdr->val))\n\t\treturn EINVAL;\n\n\tif (pl_strcasecmp(&resp.realm, auth->realm))\n\t\treturn EINVAL;\n\n\terr = check_nonce(&resp.nonce, &msg->src, auth->realm);\n\tif (err == ETIMEDOUT || err == EAUTH) {\n\t\tauth->stale = true;\n\t\treturn EAUTH;\n\t}\n\telse if (err) {\n\t\treturn err;\n\t}\n\n\tif (authh(ha1, &resp.username, auth->realm, arg))\n\t\treturn EINVAL;\n\n\tif (httpauth_digest_response_auth(&resp, &msg->met, ha1))\n\t\treturn EACCES;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/sip/contact.c",
    "content": "/**\n * @file sip/contact.c  SIP contact functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_uri.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_msg.h>\n#include <re_sip.h>\n\n\n/**\n * Set contact parameters\n *\n * @param contact SIP Contact object\n * @param uri     Username or URI\n * @param addr    IP-address and port\n * @param tp      SIP Transport\n */\nvoid sip_contact_set(struct sip_contact *contact, const char *uri,\n\t\t     const struct sa *addr, enum sip_transp tp)\n{\n\tif (!contact)\n\t\treturn;\n\n\tcontact->uri  = uri;\n\tcontact->addr = addr;\n\tcontact->tp   = tp;\n}\n\n\n/**\n * Print contact header\n *\n * @param pf      Print function\n * @param contact SIP Contact object\n *\n * @return 0 for success, otherwise errorcode\n */\nint sip_contact_print(struct re_printf *pf, const struct sip_contact *contact)\n{\n\tif (!contact)\n\t\treturn 0;\n\n\tif (contact->uri && strchr(contact->uri, ':'))\n\t\treturn re_hprintf(pf, \"Contact: <%s>\\r\\n\", contact->uri);\n\telse\n\t\treturn re_hprintf(pf, \"Contact: <sip:%s@%J%s>\\r\\n\",\n\t\t\t\t  contact->uri,\n\t\t\t\t  contact->addr,\n\t\t\t\t  sip_transp_param(contact->tp));\n}\n"
  },
  {
    "path": "src/sip/cseq.c",
    "content": "/**\n * @file cseq.c  SIP CSeq decode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_uri.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_msg.h>\n#include <re_sip.h>\n\n\n/**\n * Decode a pointer-length string into a SIP CSeq header\n *\n * @param cseq SIP CSeq header\n * @param pl   Pointer-length string\n *\n * @return 0 for success, otherwise errorcode\n */\nint sip_cseq_decode(struct sip_cseq *cseq, const struct pl *pl)\n{\n\tstruct pl num;\n\tint err;\n\n\tif (!cseq || !pl)\n\t\treturn EINVAL;\n\n\terr = re_regex(pl->p, pl->l, \"[0-9]+[ \\t\\r\\n]+[^ \\t\\r\\n]+\",\n\t\t       &num, NULL, &cseq->met);\n\tif (err)\n\t\treturn err;\n\n\tcseq->num = pl_u32(&num);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/sip/ctrans.c",
    "content": "/**\n * @file sip/ctrans.c  SIP Client Transaction\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_sys.h>\n#include <re_tmr.h>\n#include <re_udp.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include \"sip.h\"\n\n\nenum state {\n\tTRYING,\n\tCALLING,\n\tPROCEEDING,\n\tCOMPLETED,\n};\n\n\nenum {\n\tCOMPLETE_WAIT = 32000,\n};\n\n\nstruct sip_ctrans {\n\tstruct le he;\n\tstruct sa dst;\n\tstruct tmr tmr;\n\tstruct tmr tmre;\n\tstruct sip *sip;\n\tstruct mbuf *mb;\n\tstruct mbuf *mb_ack;\n\tstruct sip_msg *req;\n\tstruct sip_connqent *qent;\n\tchar *met;\n\tchar *branch;\n\tchar *host;\n\tsip_conn_h *connh;\n\tsip_resp_h *resph;\n\tvoid *arg;\n\tenum sip_transp tp;\n\tenum state state;\n\tuint32_t txc;\n\tbool invite;\n};\n\n\nstatic bool route_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,\n\t\t\t  void *arg)\n{\n\t(void)msg;\n\treturn 0 != mbuf_printf(arg, \"Route: %r\\r\\n\", &hdr->val);\n}\n\n\nstatic int request_copy(struct mbuf **mbp, struct sip_ctrans *ct,\n\t\t\tconst char *met, const struct sip_msg *resp)\n{\n\tstruct mbuf *mb;\n\tint err;\n\n\tif (!ct->req) {\n\t\terr = sip_msg_decode(&ct->req, ct->mb);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr  = mbuf_printf(mb, \"%s %r SIP/2.0\\r\\n\", met, &ct->req->ruri);\n\terr |= mbuf_printf(mb, \"Via: %r\\r\\n\", &ct->req->via.val);\n\terr |= mbuf_write_str(mb, \"Max-Forwards: 70\\r\\n\");\n\terr |= sip_msg_hdr_apply(ct->req, true, SIP_HDR_ROUTE,\n\t\t\t\t route_handler, mb) ? ENOMEM : 0;\n\terr |= mbuf_printf(mb, \"To: %r\\r\\n\",\n\t\t\t   resp ? &resp->to.val : &ct->req->to.val);\n\terr |= mbuf_printf(mb, \"From: %r\\r\\n\", &ct->req->from.val);\n\terr |= mbuf_printf(mb, \"Call-ID: %r\\r\\n\", &ct->req->callid);\n\terr |= mbuf_printf(mb, \"CSeq: %u %s\\r\\n\", ct->req->cseq.num, met);\n\tif (ct->sip->software)\n\t\terr |= mbuf_printf(mb, \"User-Agent: %s\\r\\n\",ct->sip->software);\n\terr |= mbuf_write_str(mb, \"Content-Length: 0\\r\\n\\r\\n\");\n\n\tmb->pos = 0;\n\n\tif (err)\n\t\tmem_deref(mb);\n\telse\n\t\t*mbp = mb;\n\n\treturn err;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sip_ctrans *ct = arg;\n\n\thash_unlink(&ct->he);\n\ttmr_cancel(&ct->tmr);\n\ttmr_cancel(&ct->tmre);\n\tmem_deref(ct->met);\n\tmem_deref(ct->branch);\n\tmem_deref(ct->host);\n\tmem_deref(ct->qent);\n\tmem_deref(ct->req);\n\tmem_deref(ct->mb);\n\tmem_deref(ct->mb_ack);\n}\n\n\nstatic bool cmp_handler(struct le *le, void *arg)\n{\n\tstruct sip_ctrans *ct = le->data;\n\tconst struct sip_msg *msg = arg;\n\n\tif (pl_strcmp(&msg->via.branch, ct->branch))\n\t\treturn false;\n\n\tif (pl_strcmp(&msg->cseq.met, ct->met))\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstatic void dummy_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\t(void)err;\n\t(void)msg;\n\t(void)arg;\n}\n\n\nstatic void terminate(struct sip_ctrans *ct, int err)\n{\n\tswitch (ct->state) {\n\n\tcase TRYING:\n\tcase CALLING:\n\tcase PROCEEDING:\n\t\tct->resph(err, NULL, ct->arg);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n}\n\n\nstatic void transport_handler(int err, void *arg)\n{\n\tstruct sip_ctrans *ct = arg;\n\n\tterminate(ct, err);\n\tmem_deref(ct);\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct sip_ctrans *ct = arg;\n\n\tterminate(ct, ETIMEDOUT);\n\tmem_deref(ct);\n}\n\n\nstatic int connect_handler(struct sa *src, const struct sa *dst,\n\t\t\t   struct mbuf *mb, void *arg)\n{\n\tstruct sip_ctrans *ct = arg;\n\n\tif (ct->connh)\n\t\treturn ct->connh(src, dst, mb, ct->arg);\n\n\treturn 0;\n}\n\n\nstatic void retransmit_handler(void *arg)\n{\n\tstruct sip_ctrans *ct = arg;\n\tuint32_t timeout;\n\tint err;\n\n\tct->txc++;\n\n\tswitch (ct->state) {\n\n\tcase TRYING:\n\t\ttimeout = MIN(SIP_T1<<ct->txc, SIP_T2);\n\t\tbreak;\n\n\tcase CALLING:\n\t\ttimeout = SIP_T1<<ct->txc;\n\t\tbreak;\n\n\tcase PROCEEDING:\n\t\ttimeout = SIP_T2;\n\t\tbreak;\n\n\tdefault:\n\t\treturn;\n\t}\n\n\ttmr_start(&ct->tmre, timeout, retransmit_handler, ct);\n\n\terr = sip_transp_send(&ct->qent, ct->sip, NULL, ct->tp, &ct->dst,\n\t\t\t      ct->host, ct->mb, connect_handler,\n\t\t\t      transport_handler, ct);\n\tif (err) {\n\t\tterminate(ct, err);\n\t\tmem_deref(ct);\n\t}\n}\n\n\nstatic void invite_response(struct sip_ctrans *ct, const struct sip_msg *msg)\n{\n\tswitch (ct->state) {\n\n\tcase CALLING:\n\t\ttmr_cancel(&ct->tmr);\n\t\ttmr_cancel(&ct->tmre);\n\t\t/*@fallthrough@*/\n\tcase PROCEEDING:\n\t\tif (msg->scode < 200) {\n\t\t\tct->state = PROCEEDING;\n\t\t\tct->resph(0, msg, ct->arg);\n\t\t}\n\t\telse if (msg->scode < 300) {\n\t\t\tct->resph(0, msg, ct->arg);\n\t\t\tmem_deref(ct);\n\t\t}\n\t\telse {\n\t\t\tct->state = COMPLETED;\n\n\t\t\t(void)request_copy(&ct->mb_ack, ct, \"ACK\", msg);\n\t\t\t(void)sip_send(ct->sip, NULL, ct->tp, &ct->dst,\n\t\t\t\t       ct->mb_ack);\n\n\t\t\tct->resph(0, msg, ct->arg);\n\n\t\t\tif (sip_transp_reliable(ct->tp)) {\n\t\t\t\tmem_deref(ct);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\ttmr_start(&ct->tmr, COMPLETE_WAIT, tmr_handler, ct);\n\t\t}\n\t\tbreak;\n\n\tcase COMPLETED:\n\t\tif (msg->scode < 300)\n\t\t\tbreak;\n\n\t\t(void)sip_send(ct->sip, NULL, ct->tp, &ct->dst, ct->mb_ack);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n}\n\n\nstatic bool response_handler(const struct sip_msg *msg, void *arg)\n{\n\tstruct sip_ctrans *ct;\n\tstruct sip *sip = arg;\n\n\tct = list_ledata(hash_lookup(sip->ht_ctrans,\n\t\t\t\t     hash_joaat_pl(&msg->via.branch),\n\t\t\t\t     cmp_handler, (void *)msg));\n\tif (!ct)\n\t\treturn false;\n\n\tif (ct->invite) {\n\t\tinvite_response(ct, msg);\n\t\treturn true;\n\t}\n\n\tswitch (ct->state) {\n\n\tcase TRYING:\n\tcase PROCEEDING:\n\t\tif (msg->scode < 200) {\n\t\t\tct->state = PROCEEDING;\n\t\t\tct->resph(0, msg, ct->arg);\n\t\t}\n\t\telse {\n\t\t\tct->state = COMPLETED;\n\t\t\tct->resph(0, msg, ct->arg);\n\n\t\t\tif (sip_transp_reliable(ct->tp)) {\n\t\t\t\tmem_deref(ct);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\ttmr_start(&ct->tmr, SIP_T4, tmr_handler, ct);\n\t\t\ttmr_cancel(&ct->tmre);\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n\n\nint sip_ctrans_request(struct sip_ctrans **ctp, struct sip *sip,\n\t\t       enum sip_transp tp, const struct sa *dst, char *met,\n\t\t       char *branch, char *host, struct mbuf *mb,\n\t\t       sip_conn_h *connh,\n\t\t       sip_resp_h *resph, void *arg)\n{\n\tstruct sip_ctrans *ct;\n\tint err;\n\n\tif (!sip || !dst || !met || !branch || !mb)\n\t\treturn EINVAL;\n\n\tct = mem_zalloc(sizeof(*ct), destructor);\n\tif (!ct)\n\t\treturn ENOMEM;\n\n\thash_append(sip->ht_ctrans, hash_joaat_str(branch), &ct->he, ct);\n\n\tct->invite = !strcmp(met, \"INVITE\");\n\tct->branch = mem_ref(branch);\n\tct->host   = mem_ref(host);\n\tct->met    = mem_ref(met);\n\tct->mb     = mem_ref(mb);\n\tct->dst    = *dst;\n\tct->tp     = tp;\n\tct->sip    = sip;\n\tct->state  = ct->invite ? CALLING : TRYING;\n\tct->connh  = connh;\n\tct->resph  = resph ? resph : dummy_handler;\n\tct->arg    = arg;\n\n\terr = sip_transp_send(&ct->qent, sip, NULL, tp, dst, host, mb,\n\t\t\t      connect_handler, transport_handler, ct);\n\tif (err)\n\t\tgoto out;\n\n\ttmr_start(&ct->tmr, 64 * SIP_T1, tmr_handler, ct);\n\n\tif (!sip_transp_reliable(ct->tp))\n\t\ttmr_start(&ct->tmre, SIP_T1, retransmit_handler, ct);\n\n out:\n\tif (err)\n\t\tmem_deref(ct);\n\telse if (ctp)\n\t\t*ctp = ct;\n\n\treturn err;\n}\n\n\nint sip_ctrans_cancel(struct sip_ctrans *ct)\n{\n\tstruct mbuf *mb = NULL;\n\tchar *cancel = NULL;\n\tint err;\n\n\tif (!ct)\n\t\treturn EINVAL;\n\n\tif (!ct->invite)\n\t\treturn 0;\n\n\tswitch (ct->state) {\n\n\tcase PROCEEDING:\n\t\ttmr_start(&ct->tmr, 64 * SIP_T1, tmr_handler, ct);\n\t\tbreak;\n\n\tdefault:\n\t\treturn EPROTO;\n\t}\n\n\terr = str_dup(&cancel, \"CANCEL\");\n\tif (err)\n\t\tgoto out;\n\n\terr = request_copy(&mb, ct, cancel, NULL);\n\tif (err)\n\t\tgoto out;\n\n\terr = sip_ctrans_request(NULL, ct->sip, ct->tp, &ct->dst, cancel,\n\t\t\t\t ct->branch, NULL, mb, NULL, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(cancel);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nint sip_ctrans_init(struct sip *sip, uint32_t sz)\n{\n\tint err;\n\n\terr = sip_listen(NULL, sip, false, response_handler, sip);\n\tif (err)\n\t\treturn err;\n\n\treturn hash_alloc(&sip->ht_ctrans, sz);\n}\n\n\nstatic const char *statename(enum state state)\n{\n\tswitch (state) {\n\n\tcase TRYING:     return \"TRYING\";\n\tcase CALLING:    return \"CALLING\";\n\tcase PROCEEDING: return \"PROCEEDING\";\n\tcase COMPLETED:  return \"COMPLETED\";\n\tdefault:         return \"???\";\n\t}\n}\n\n\nstatic bool debug_handler(struct le *le, void *arg)\n{\n\tstruct sip_ctrans *ct = le->data;\n\tstruct re_printf *pf = arg;\n\n\t(void)re_hprintf(pf, \"  %-10s %-10s %2llus (%s)\\n\",\n\t\t\t ct->met,\n\t\t\t statename(ct->state),\n\t\t\t tmr_get_expire(&ct->tmr)/1000,\n\t\t\t ct->branch);\n\n\treturn false;\n}\n\n\nint sip_ctrans_debug(struct re_printf *pf, const struct sip *sip)\n{\n\tint err;\n\n\terr = re_hprintf(pf, \"client transactions:\\n\");\n\thash_apply(sip->ht_ctrans, debug_handler, pf);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sip/dialog.c",
    "content": "/**\n * @file dialog.c  SIP Dialog\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_sys.h>\n#include <re_tmr.h>\n#include <re_udp.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include \"sip.h\"\n\n\nenum {\n\tROUTE_OFFSET = 7,\n};\n\nstruct sip_dialog {\n\tstruct uri route;\n\tstruct mbuf *mb;\n\tchar *callid;\n\tchar *ltag;\n\tchar *rtag;\n\tchar *uri;\n\tuint32_t hash;\n\tuint32_t lseq;\n\tuint32_t rseq;\n\tuint32_t lseqinv;\n\tsize_t cpos;\n\tsize_t rpos;\n\tenum sip_transp tp;\n\tuint32_t srcport;\n};\n\n\nstruct route_enc {\n\tstruct mbuf *mb;\n\tsize_t end;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sip_dialog *dlg = arg;\n\n\tmem_deref(dlg->callid);\n\tmem_deref(dlg->ltag);\n\tmem_deref(dlg->rtag);\n\tmem_deref(dlg->uri);\n\tmem_deref(dlg->mb);\n}\n\n\n/**\n * Allocate a SIP Dialog\n *\n * @param dlgp      Pointer to allocated SIP Dialog\n * @param uri       Target URI\n * @param to_uri    To URI\n * @param from_name From displayname (optional)\n * @param from_uri  From URI\n * @param routev    Route vector\n * @param routec    Route count\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_dialog_alloc(struct sip_dialog **dlgp,\n\t\t     const char *uri, const char *to_uri,\n\t\t     const char *from_name, const char *from_uri,\n\t\t     const char *routev[], uint32_t routec)\n{\n\tconst uint64_t ltag = rand_u64();\n\tstruct sip_dialog *dlg;\n\tstruct sip_addr addr;\n\tsize_t rend = 0;\n\tstruct pl pl;\n\tuint32_t i;\n\tint err;\n\n\tif (!dlgp || !uri || !to_uri || !from_uri)\n\t\treturn EINVAL;\n\n\tdlg = mem_zalloc(sizeof(*dlg), destructor);\n\tif (!dlg)\n\t\treturn ENOMEM;\n\n\tdlg->hash = hash_fast_str(from_uri);\n\tdlg->lseq = rand_u16();\n\tdlg->tp   = SIP_TRANSP_NONE;\n\n\terr = str_dup(&dlg->uri, uri);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_x64dup(&dlg->callid, rand_u64());\n\tif (err)\n\t\tgoto out;\n\n\terr = str_x64dup(&dlg->ltag, ltag);\n\tif (err)\n\t\tgoto out;\n\n\tdlg->mb = mbuf_alloc(512);\n\tif (!dlg->mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tfor (i=0; i<routec; i++) {\n\t\terr |= mbuf_printf(dlg->mb, \"Route: <%s;lr>\\r\\n\", routev[i]);\n\t\tif (i == 0)\n\t\t\trend = dlg->mb->pos - 2;\n\t}\n\tdlg->rpos = dlg->mb->pos;\n\terr |= mbuf_printf(dlg->mb, \"To: <%s>\\r\\n\", to_uri);\n\tdlg->cpos = dlg->mb->pos;\n\terr |= mbuf_printf(dlg->mb, \"From: %s%s%s<%s>;tag=%016llx\\r\\n\",\n\t\t\t   from_name ? \"\\\"\" : \"\", from_name,\n\t\t\t   from_name ? \"\\\" \" : \"\",\n\t\t\t   from_uri, ltag);\n\tif (err)\n\t\tgoto out;\n\n\tdlg->mb->pos = 0;\n\n\tif (rend) {\n\t\tpl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;\n\t\tpl.l = rend - ROUTE_OFFSET;\n\t\terr = sip_addr_decode(&addr, &pl);\n\t\tdlg->route = addr.uri;\n\t}\n\telse {\n\t\tpl_set_str(&pl, dlg->uri);\n\t\terr = uri_decode(&dlg->route, &pl);\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(dlg);\n\telse\n\t\t*dlgp = dlg;\n\n\treturn err;\n}\n\n\nstatic bool record_route_handler(const struct sip_hdr *hdr,\n\t\t\t\t const struct sip_msg *msg,\n\t\t\t\t void *arg)\n{\n\tstruct route_enc *renc = arg;\n\t(void)msg;\n\n\tif (mbuf_printf(renc->mb, \"Route: %r\\r\\n\", &hdr->val))\n\t\treturn true;\n\n\tif (!renc->end)\n\t        renc->end = renc->mb->pos - 2;\n\n\treturn false;\n}\n\n\n/**\n * Accept and create a SIP Dialog from an incoming SIP Message\n *\n * @param dlgp Pointer to allocated SIP Dialog\n * @param msg  SIP Message\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_dialog_accept(struct sip_dialog **dlgp, const struct sip_msg *msg)\n{\n\tconst struct sip_hdr *contact;\n\tstruct sip_dialog *dlg;\n\tstruct route_enc renc;\n\tstruct sip_addr addr;\n\tstruct pl pl;\n\tint err;\n\n\tif (!dlgp || !msg || !msg->req)\n\t\treturn EINVAL;\n\n\tcontact = sip_msg_hdr(msg, SIP_HDR_CONTACT);\n\n\tif (!contact || !msg->callid.p)\n\t\treturn EBADMSG;\n\n\tif (sip_addr_decode(&addr, &contact->val))\n\t\treturn EBADMSG;\n\n\tdlg = mem_zalloc(sizeof(*dlg), destructor);\n\tif (!dlg)\n\t\treturn ENOMEM;\n\n\tdlg->hash = rand_u32();\n\tdlg->lseq = rand_u16();\n\tdlg->rseq = msg->cseq.num;\n\tdlg->tp   = msg->tp;\n\n\terr = pl_strdup(&dlg->uri, &addr.auri);\n\tif (err)\n\t\tgoto out;\n\n\terr = pl_strdup(&dlg->callid, &msg->callid);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_x64dup(&dlg->ltag, msg->tag);\n\tif (err)\n\t\tgoto out;\n\n\terr = pl_strdup(&dlg->rtag, &msg->from.tag);\n\tif (err)\n\t\tgoto out;\n\n\tdlg->mb = mbuf_alloc(512);\n\tif (!dlg->mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\trenc.mb  = dlg->mb;\n\trenc.end = 0;\n\n\terr |= sip_msg_hdr_apply(msg, true, SIP_HDR_RECORD_ROUTE,\n\t\t\t\t record_route_handler, &renc) ? ENOMEM : 0;\n\tdlg->rpos = dlg->mb->pos;\n\terr |= mbuf_printf(dlg->mb, \"To: %r\\r\\n\", &msg->from.val);\n\tdlg->cpos = dlg->mb->pos;\n\terr |= mbuf_printf(dlg->mb, \"From: %r;tag=%016llx\\r\\n\", &msg->to.val,\n\t\t\t   msg->tag);\n\tif (err)\n\t\tgoto out;\n\n\tdlg->mb->pos = 0;\n\n\tif (renc.end) {\n\t\tpl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;\n\t\tpl.l = renc.end - ROUTE_OFFSET;\n\t\terr = sip_addr_decode(&addr, &pl);\n\t\tdlg->route = addr.uri;\n\t}\n\telse {\n\t\tpl_set_str(&pl, dlg->uri);\n\t\terr = uri_decode(&dlg->route, &pl);\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(dlg);\n\telse\n\t\t*dlgp = dlg;\n\n\treturn err;\n}\n\n\n/**\n * Initialize a SIP Dialog from an incoming SIP Message\n *\n * @param dlg SIP Dialog to initialize\n * @param msg SIP Message\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg)\n{\n\tchar *uri = NULL, *rtag = NULL;\n\tconst struct sip_hdr *contact;\n\tstruct route_enc renc;\n\tstruct sip_addr addr;\n\tstruct pl pl;\n\tint err;\n\n\tif (!dlg || dlg->rtag || !dlg->cpos || !msg)\n\t\treturn EINVAL;\n\n\tcontact = sip_msg_hdr(msg, SIP_HDR_CONTACT);\n\n\tif (!contact)\n\t\treturn EBADMSG;\n\n\tif (sip_addr_decode(&addr, &contact->val))\n\t\treturn EBADMSG;\n\n\trenc.mb = mbuf_alloc(512);\n\tif (!renc.mb)\n\t\treturn ENOMEM;\n\n\terr = pl_strdup(&uri, &addr.auri);\n\tif (err)\n\t\tgoto out;\n\n\terr = pl_strdup(&rtag, msg->req ? &msg->from.tag : &msg->to.tag);\n\tif (err)\n\t\tgoto out;\n\n\trenc.end = 0;\n\n\terr |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE,\n\t\t\t\t record_route_handler, &renc) ? ENOMEM : 0;\n\tdlg->rpos = renc.mb->pos;\n\terr |= mbuf_printf(renc.mb, \"To: %r\\r\\n\",\n\t\t\t   msg->req ? &msg->from.val : &msg->to.val);\n\n\tdlg->mb->pos = dlg->cpos;\n\tdlg->cpos = renc.mb->pos;\n\terr |= mbuf_write_mem(renc.mb, mbuf_buf(dlg->mb),\n\t\t\t      mbuf_get_left(dlg->mb));\n\tdlg->mb->pos = 0;\n\n\tif (err)\n\t\tgoto out;\n\n\trenc.mb->pos = 0;\n\n\tif (renc.end) {\n\t\tpl.p = (const char *)mbuf_buf(renc.mb) + ROUTE_OFFSET;\n\t\tpl.l = renc.end - ROUTE_OFFSET;\n\t\terr = sip_addr_decode(&addr, &pl);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tdlg->route = addr.uri;\n\t}\n\telse {\n\t\tstruct uri tmp;\n\n\t\tpl_set_str(&pl, uri);\n\t\terr = uri_decode(&tmp, &pl);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tdlg->route = tmp;\n\t}\n\n\tmem_deref(dlg->mb);\n\tmem_deref(dlg->uri);\n\n\tdlg->mb   = mem_ref(renc.mb);\n\tdlg->rtag = mem_ref(rtag);\n\tdlg->uri  = mem_ref(uri);\n\tdlg->rseq = msg->req ? msg->cseq.num : 0;\n\tdlg->tp   = msg->tp;\n\n out:\n\tmem_deref(renc.mb);\n\tmem_deref(rtag);\n\tmem_deref(uri);\n\n\treturn err;\n}\n\n\n/**\n * Fork a SIP Dialog from an incoming SIP Message\n *\n * @param dlgp Pointer to allocated SIP Dialog\n * @param odlg Original SIP Dialog\n * @param msg  SIP Message\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg,\n\t\t    const struct sip_msg *msg)\n{\n\tconst struct sip_hdr *contact;\n\tstruct sip_dialog *dlg;\n\tstruct route_enc renc;\n\tstruct sip_addr addr;\n\tstruct pl pl;\n\tint err;\n\n\tif (!dlgp || !odlg || !odlg->cpos || !msg)\n\t\treturn EINVAL;\n\n\tcontact = sip_msg_hdr(msg, SIP_HDR_CONTACT);\n\n\tif (!contact || !msg->callid.p)\n\t\treturn EBADMSG;\n\n\tif (sip_addr_decode(&addr, &contact->val))\n\t\treturn EBADMSG;\n\n\tdlg = mem_zalloc(sizeof(*dlg), destructor);\n\tif (!dlg)\n\t\treturn ENOMEM;\n\n\tdlg->callid  = mem_ref(odlg->callid);\n\tdlg->ltag    = mem_ref(odlg->ltag);\n\tdlg->hash    = odlg->hash;\n\tdlg->lseq    = odlg->lseq;\n\tdlg->lseqinv = odlg->lseqinv;\n\tdlg->rseq    = msg->req ? msg->cseq.num : 0;\n\tdlg->tp      = msg->tp;\n\n\terr = pl_strdup(&dlg->uri, &addr.auri);\n\tif (err)\n\t\tgoto out;\n\n\terr = pl_strdup(&dlg->rtag, msg->req ? &msg->from.tag : &msg->to.tag);\n\tif (err)\n\t\tgoto out;\n\n\tdlg->mb = mbuf_alloc(512);\n\tif (!dlg->mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\trenc.mb  = dlg->mb;\n\trenc.end = 0;\n\n\terr |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE,\n\t\t\t\t record_route_handler, &renc) ? ENOMEM : 0;\n\terr |= mbuf_printf(dlg->mb, \"To: %r\\r\\n\",\n\t\t\t   msg->req ? &msg->from.val : &msg->to.val);\n\n\todlg->mb->pos = odlg->cpos;\n\terr |= mbuf_write_mem(dlg->mb, mbuf_buf(odlg->mb),\n\t\t\t      mbuf_get_left(odlg->mb));\n\todlg->mb->pos = 0;\n\n\tif (err)\n\t\tgoto out;\n\n\tdlg->mb->pos = 0;\n\n\tif (renc.end) {\n\t\tpl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;\n\t\tpl.l = renc.end - ROUTE_OFFSET;\n\t\terr = sip_addr_decode(&addr, &pl);\n\t\tdlg->route = addr.uri;\n\t}\n\telse {\n\t\tpl_set_str(&pl, dlg->uri);\n\t\terr = uri_decode(&dlg->route, &pl);\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(dlg);\n\telse\n\t\t*dlgp = dlg;\n\n\treturn err;\n}\n\n\n/**\n * Update an existing SIP Dialog from a SIP Message\n *\n * @param dlg SIP Dialog to update\n * @param msg SIP Message\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_dialog_update(struct sip_dialog *dlg, const struct sip_msg *msg)\n{\n\tconst struct sip_hdr *contact;\n\tstruct sip_addr addr;\n\tstruct mbuf *mb;\n\tstruct pl pl;\n\tsize_t cpos;\n\tchar *uri;\n\tint err;\n\n\tif (!dlg || !msg)\n\t\treturn EINVAL;\n\n\tcontact = sip_msg_hdr(msg, SIP_HDR_CONTACT);\n\tif (!contact)\n\t\treturn EBADMSG;\n\n\tif (sip_addr_decode(&addr, &contact->val))\n\t\treturn EBADMSG;\n\n\terr = pl_strdup(&uri, &addr.auri);\n\tif (err)\n\t\treturn err;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = mbuf_write_mem(mb, mbuf_buf(dlg->mb), dlg->rpos);\n\terr |= mbuf_printf(mb, \"To: %r\\r\\n\",\n\t\t   msg->req ? &msg->from.val : &msg->to.val);\n\tcpos = mb->pos;\n\terr |= mbuf_write_mem(mb, mbuf_buf(dlg->mb) + dlg->cpos,\n\t\t\t      mbuf_get_left(dlg->mb) - dlg->cpos);\n\n\tif (err)\n\t\tgoto out;\n\n\tdlg->cpos = cpos;\n\tmb->pos = 0;\n\n\tdlg->rtag = mem_deref(dlg->rtag);\n\terr = pl_strdup(&dlg->rtag, msg->req ? &msg->from.tag : &msg->to.tag);\n\tif (err)\n\t\tgoto out;\n\n\tmem_deref(dlg->mb);\n\tdlg->mb = mem_ref(mb);\n\n\tif (dlg->route.scheme.p == dlg->uri) {\n\n\t\tstruct uri tmp;\n\n\t\tpl_set_str(&pl, uri);\n\t\terr = uri_decode(&tmp, &pl);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tdlg->route = tmp;\n\t}\n\telse {\n\t\tpl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;\n\t\tpl.l = dlg->rpos - ROUTE_OFFSET;\n\t\terr = sip_addr_decode(&addr, &pl);\n\t\tdlg->route = addr.uri;\n\t}\n\n\tmem_deref(dlg->uri);\n\tdlg->uri = mem_ref(uri);\n out:\n\tmem_deref(mb);\n\tmem_deref(uri);\n\n\treturn err;\n}\n\n\n/**\n * Check if a remote sequence number is valid\n *\n * @param dlg SIP Dialog\n * @param msg SIP Message\n *\n * @return True if valid, False if invalid\n */\nbool sip_dialog_rseq_valid(struct sip_dialog *dlg, const struct sip_msg *msg)\n{\n\tif (!dlg || !msg || !msg->req)\n\t\treturn false;\n\n\tif (msg->cseq.num < dlg->rseq)\n\t\treturn false;\n\n\tdlg->rseq = msg->cseq.num;\n\n\treturn true;\n}\n\n\nint sip_dialog_encode(struct mbuf *mb, struct sip_dialog *dlg, uint32_t cseq,\n\t\t      const char *met)\n{\n\tint err = 0;\n\n\tif (!mb || !dlg || !met)\n\t\treturn EINVAL;\n\n\tif (!strcmp(met, \"INVITE\"))\n\t\tdlg->lseqinv = dlg->lseq;\n\n\terr |= mbuf_write_mem(mb, mbuf_buf(dlg->mb), mbuf_get_left(dlg->mb));\n\terr |= mbuf_printf(mb, \"Call-ID: %s\\r\\n\", dlg->callid);\n\terr |= mbuf_printf(mb, \"CSeq: %u %s\\r\\n\", strcmp(met, \"ACK\") ?\n\t\t\t   dlg->lseq++ : cseq, met);\n\n\treturn err;\n}\n\n\nconst char *sip_dialog_uri(const struct sip_dialog *dlg)\n{\n\treturn dlg ? dlg->uri : NULL;\n}\n\n\nconst char *sip_dialog_ltag(const struct sip_dialog *dlg)\n{\n\treturn dlg ? dlg->ltag : NULL;\n}\n\n\nconst char *sip_dialog_rtag(const struct sip_dialog *dlg)\n{\n\treturn dlg ? dlg->rtag : NULL;\n}\n\n\nconst struct uri *sip_dialog_route(const struct sip_dialog *dlg)\n{\n\treturn dlg ? &dlg->route : NULL;\n}\n\n\nuint32_t sip_dialog_hash(const struct sip_dialog *dlg)\n{\n\treturn dlg ? dlg->hash : 0;\n}\n\n\n/**\n * Get the Call-ID from a SIP Dialog\n *\n * @param dlg SIP Dialog\n *\n * @return Call-ID string\n */\nconst char *sip_dialog_callid(const struct sip_dialog *dlg)\n{\n\treturn dlg ? dlg->callid : NULL;\n}\n\n\nint sip_dialog_set_callid(struct sip_dialog *dlg, const char *callid)\n{\n\tdlg->callid = mem_deref(dlg->callid);\n\treturn str_dup(&dlg->callid, callid);\n}\n\n\n/**\n * Set TCP source port for a SIP Dialog\n *\n * @param dlg  SIP Dialog\n * @param srcport The TCP source port to be used\n */\nvoid sip_dialog_set_srcport(struct sip_dialog *dlg, uint16_t srcport)\n{\n\tif (!dlg)\n\t\treturn;\n\n\tdlg->srcport = srcport;\n}\n\n\n/**\n * Get TCP source port for a SIP Dialog\n *\n * @param dlg  SIP Dialog\n *\n * @return TCP source port\n */\nuint16_t sip_dialog_srcport(struct sip_dialog *dlg)\n{\n\tif (!dlg)\n\t\treturn 0;\n\n\treturn dlg->srcport;\n}\n\n\n/**\n * Get the local sequence number from a SIP Dialog\n *\n * @param dlg SIP Dialog\n *\n * @return Local sequence number\n */\nuint32_t sip_dialog_lseq(const struct sip_dialog *dlg)\n{\n\treturn dlg ? dlg->lseq : 0;\n}\n\n\n/**\n * Get the local sequence number of the last sent INVITE\n *\n * @param dlg SIP Dialog\n *\n * @return Local sequence number\n */\nuint32_t sip_dialog_lseqinv(const struct sip_dialog *dlg)\n{\n\treturn dlg ? dlg->lseqinv : 0;\n}\n\n\n/**\n * Check if a SIP Dialog is established\n *\n * @param dlg SIP Dialog\n *\n * @return True if established, False if not\n */\nbool sip_dialog_established(const struct sip_dialog *dlg)\n{\n\treturn dlg && dlg->rtag;\n}\n\n\nenum sip_transp sip_dialog_tp(const struct sip_dialog *dlg)\n{\n\treturn dlg ? dlg->tp : SIP_TRANSP_NONE;\n}\n\n\n/**\n * Compare a SIP Dialog against a SIP Message\n *\n * @param dlg SIP Dialog\n * @param msg SIP Message\n *\n * @return True if match, False if no match\n */\nbool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg)\n{\n\tif (!dlg || !msg)\n\t\treturn false;\n\n\tif (pl_strcmp(&msg->callid, dlg->callid))\n\t\treturn false;\n\n\tif (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag))\n\t\treturn false;\n\n\tif (pl_strcmp(msg->req ? &msg->from.tag : &msg->to.tag, dlg->rtag))\n\t\treturn false;\n\n\treturn true;\n}\n\n\n/**\n * Compare a half SIP Dialog against a SIP Message\n *\n * @param dlg SIP Dialog\n * @param msg SIP Message\n *\n * @return True if match, False if no match\n */\nbool sip_dialog_cmp_half(const struct sip_dialog *dlg,\n\t\t\t const struct sip_msg *msg)\n{\n\tif (!dlg || !msg)\n\t\treturn false;\n\n\tif (pl_strcmp(&msg->callid, dlg->callid))\n\t\treturn false;\n\n\tif (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag))\n\t\treturn false;\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/sip/keepalive.c",
    "content": "/**\n * @file sip/keepalive.c  SIP Keepalive\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_sys.h>\n#include <re_uri.h>\n#include <re_udp.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include \"sip.h\"\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sip_keepalive *ka = arg;\n\n\tif (ka->kap)\n\t\t*ka->kap = NULL;\n\n\tlist_unlink(&ka->le);\n}\n\n\nvoid sip_keepalive_signal(struct list *kal, int err)\n{\n\tstruct le *le = list_head(kal);\n\n\twhile (le) {\n\n\t\tstruct sip_keepalive *ka = le->data;\n\t\tsip_keepalive_h *kah = ka->kah;\n\t\tvoid *arg = ka->arg;\n\n\t\tle = le->next;\n\n\t\tlist_unlink(&ka->le);\n\t\tmem_deref(ka);\n\n\t\tkah(err, arg);\n\t}\n}\n\n\nuint64_t sip_keepalive_wait(uint32_t interval)\n{\n\treturn (uint64_t) interval * (800 + rand_u16() % 201);\n}\n\n\n/**\n * Start a keepalive handler on a SIP transport\n *\n * @param kap      Pointer to allocated keepalive object\n * @param sip      SIP Stack instance\n * @param msg      SIP Message\n * @param interval Keepalive interval in seconds\n * @param kah      Keepalive handler\n * @param arg      Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_keepalive_start(struct sip_keepalive **kap, struct sip *sip,\n\t\t\tconst struct sip_msg *msg, uint32_t interval,\n\t\t\tsip_keepalive_h *kah, void *arg)\n{\n\tstruct sip_keepalive *ka;\n\tint err;\n\n\tif (!kap || !sip || !msg || !kah)\n\t\treturn EINVAL;\n\n\tka = mem_zalloc(sizeof(*ka), destructor);\n\tif (!ka)\n\t\treturn ENOMEM;\n\n\tka->kah = kah;\n\tka->arg = arg;\n\n\tswitch (msg->tp) {\n\n\tcase SIP_TRANSP_UDP:\n\t\terr = sip_keepalive_udp(ka, sip, (struct udp_sock *)msg->sock,\n\t\t\t\t\t&msg->src, interval);\n\t\tbreak;\n\n\tcase SIP_TRANSP_TCP:\n\tcase SIP_TRANSP_TLS:\n\t\terr = sip_keepalive_tcp(ka, (struct sip_conn *)msg->sock,\n\t\t\t\t\tinterval);\n\t\tbreak;\n\n\tdefault:\n\t\terr = EPROTONOSUPPORT;\n\t\tbreak;\n\t}\n\n\tif (err) {\n\t\tmem_deref(ka);\n\t}\n\telse {\n\t\tka->kap = kap;\n\t\t*kap = ka;\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sip/keepalive_udp.c",
    "content": "/**\n * @file keepalive_udp.c  SIP UDP Keepalive\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_sys.h>\n#include <re_tmr.h>\n#include <re_udp.h>\n#include <re_stun.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include \"sip.h\"\n\n\nenum {\n\tUDP_KEEPALIVE_INTVAL = 29,\n};\n\n\nstruct sip_udpconn {\n\tstruct le he;\n\tstruct list kal;\n\tstruct tmr tmr_ka;\n\tstruct sa maddr;\n\tstruct sa paddr;\n\tstruct udp_sock *us;\n\tstruct stun_ctrans *ct;\n\tstruct stun *stun;\n\tuint32_t ka_interval;\n};\n\n\nstatic void udpconn_keepalive_handler(void *arg);\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sip_udpconn *uc = arg;\n\n\tlist_flush(&uc->kal);\n\thash_unlink(&uc->he);\n\ttmr_cancel(&uc->tmr_ka);\n\tmem_deref(uc->ct);\n\tmem_deref(uc->us);\n\tmem_deref(uc->stun);\n}\n\n\nstatic void udpconn_close(struct sip_udpconn *uc, int err)\n{\n\tsip_keepalive_signal(&uc->kal, err);\n\thash_unlink(&uc->he);\n\ttmr_cancel(&uc->tmr_ka);\n\tuc->ct = mem_deref(uc->ct);\n\tuc->us = mem_deref(uc->us);\n\tuc->stun = mem_deref(uc->stun);\n}\n\n\nstatic void stun_response_handler(int err, uint16_t scode, const char *reason,\n\t\t\t\t  const struct stun_msg *msg, void *arg)\n{\n\tstruct sip_udpconn *uc = arg;\n\tstruct stun_attr *attr;\n\t(void)reason;\n\n\tif (err || scode) {\n\t\terr = err ? err : EPROTO;\n\t\tgoto out;\n\t}\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);\n\tif (!attr) {\n\t\tattr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR);\n\t\tif (!attr) {\n\t\t\terr = EPROTO;\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\tif (!sa_isset(&uc->maddr, SA_ALL)) {\n\t\tuc->maddr = attr->v.sa;\n\t}\n\telse if (!sa_cmp(&uc->maddr, &attr->v.sa, SA_ALL)) {\n\t\terr = ENOTCONN;\n\t\tgoto out;\n\t}\n\n out:\n\tif (err) {\n\t\tudpconn_close(uc, err);\n\t\tmem_deref(uc);\n\t}\n\telse {\n\t\ttmr_start(&uc->tmr_ka, sip_keepalive_wait(uc->ka_interval),\n\t\t\t  udpconn_keepalive_handler, uc);\n\t}\n}\n\n\nstatic void udpconn_keepalive_handler(void *arg)\n{\n\tstruct sip_udpconn *uc = arg;\n\tint err;\n\n\tif (!uc->kal.head) {\n\t\t/* no need for us anymore */\n\t\tudpconn_close(uc, 0);\n\t\tmem_deref(uc);\n\t\treturn;\n\t}\n\n\terr = stun_request(&uc->ct, uc->stun, IPPROTO_UDP, uc->us,\n\t\t\t   &uc->paddr, 0, STUN_METHOD_BINDING, NULL, 0,\n\t\t\t   false, stun_response_handler, uc, 1,\n\t\t\t   STUN_ATTR_SOFTWARE, stun_software);\n\tif (err) {\n\t\tudpconn_close(uc, err);\n\t\tmem_deref(uc);\n\t}\n}\n\n\nstatic struct sip_udpconn *udpconn_find(struct sip *sip, struct udp_sock *us,\n\t\t\t\t\tconst struct sa *paddr)\n{\n\tstruct le *le;\n\n\tle = list_head(hash_list(sip->ht_udpconn, sa_hash(paddr, SA_ALL)));\n\n\tfor (; le; le = le->next) {\n\n\t\tstruct sip_udpconn *uc = le->data;\n\n\t\tif (!sa_cmp(&uc->paddr, paddr, SA_ALL))\n\t\t\tcontinue;\n\n\t\tif (uc->us != us)\n\t\t\tcontinue;\n\n\t\treturn uc;\n\t}\n\n\treturn NULL;\n}\n\n\nint  sip_keepalive_udp(struct sip_keepalive *ka, struct sip *sip,\n\t\t       struct udp_sock *us, const struct sa *paddr,\n\t\t       uint32_t interval)\n{\n\tstruct sip_udpconn *uc;\n\n\tif (!ka || !sip || !us || !paddr)\n\t\treturn EINVAL;\n\n\tuc = udpconn_find(sip, us, paddr);\n\tif (!uc) {\n\t\tuc = mem_zalloc(sizeof(*uc), destructor);\n\t\tif (!uc)\n\t\t\treturn ENOMEM;\n\n\t\thash_append(sip->ht_udpconn, sa_hash(paddr, SA_ALL),\n\t\t\t    &uc->he, uc);\n\n\t\tuc->paddr = *paddr;\n\t\tuc->stun  = mem_ref(sip->stun);\n\t\tuc->us    = mem_ref(us);\n\t\tuc->ka_interval = interval ? interval : UDP_KEEPALIVE_INTVAL;\n\n\t\t/* learn mapped address immediately */\n\t\ttmr_start(&uc->tmr_ka, 0, udpconn_keepalive_handler, uc);\n\t}\n\n\tlist_append(&uc->kal, &ka->le, ka);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/sip/msg.c",
    "content": "/**\n * @file sip/msg.c  SIP Message decode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <ctype.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_sys.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_udp.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include \"sip.h\"\n\n\nenum {\n\tHDR_HASH_SIZE = 32,\n\tSTARTLINE_MAX = 8192,\n};\n\n\nstatic void hdr_destructor(void *arg)\n{\n\tstruct sip_hdr *hdr = arg;\n\n\tlist_unlink(&hdr->le);\n\thash_unlink(&hdr->he);\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sip_msg *msg = arg;\n\n\tlist_flush(&msg->hdrl);\n\thash_flush(msg->hdrht);\n\tmem_deref(msg->hdrht);\n\tmem_deref(msg->sock);\n\tmem_deref(msg->mb);\n}\n\n\nstatic enum sip_hdrid hdr_hash(const struct pl *name)\n{\n\tif (!name->l)\n\t\treturn SIP_HDR_NONE;\n\n\tif (name->l > 1) {\n\t\tswitch (name->p[0]) {\n\n\t\tcase 'x':\n\t\tcase 'X':\n\t\t\tif (name->p[1] == '-')\n\t\t\t\treturn SIP_HDR_NONE;\n\n\t\t\t/*@fallthrough@*/\n\n\t\tdefault:\n\t\t\treturn (enum sip_hdrid)\n\t\t\t\t(hash_joaat_ci(name->p, name->l) & 0xfff);\n\t\t}\n\t}\n\n\t/* compact headers */\n\tswitch (tolower(name->p[0])) {\n\n\tcase 'a': return SIP_HDR_ACCEPT_CONTACT;\n\tcase 'b': return SIP_HDR_REFERRED_BY;\n\tcase 'c': return SIP_HDR_CONTENT_TYPE;\n\tcase 'd': return SIP_HDR_REQUEST_DISPOSITION;\n\tcase 'e': return SIP_HDR_CONTENT_ENCODING;\n\tcase 'f': return SIP_HDR_FROM;\n\tcase 'i': return SIP_HDR_CALL_ID;\n\tcase 'j': return SIP_HDR_REJECT_CONTACT;\n\tcase 'k': return SIP_HDR_SUPPORTED;\n\tcase 'l': return SIP_HDR_CONTENT_LENGTH;\n\tcase 'm': return SIP_HDR_CONTACT;\n\tcase 'n': return SIP_HDR_IDENTITY_INFO;\n\tcase 'o': return SIP_HDR_EVENT;\n\tcase 'r': return SIP_HDR_REFER_TO;\n\tcase 's': return SIP_HDR_SUBJECT;\n\tcase 't': return SIP_HDR_TO;\n\tcase 'u': return SIP_HDR_ALLOW_EVENTS;\n\tcase 'v': return SIP_HDR_VIA;\n\tcase 'x': return SIP_HDR_SESSION_EXPIRES;\n\tcase 'y': return SIP_HDR_IDENTITY;\n\tdefault:  return SIP_HDR_NONE;\n\t}\n}\n\n\nstatic inline bool hdr_comma_separated(enum sip_hdrid id)\n{\n\tswitch (id) {\n\n\tcase SIP_HDR_ACCEPT:\n\tcase SIP_HDR_ACCEPT_CONTACT:\n\tcase SIP_HDR_ACCEPT_ENCODING:\n\tcase SIP_HDR_ACCEPT_LANGUAGE:\n\tcase SIP_HDR_ACCEPT_RESOURCE_PRIORITY:\n\tcase SIP_HDR_ALERT_INFO:\n\tcase SIP_HDR_ALLOW:\n\tcase SIP_HDR_ALLOW_EVENTS:\n\tcase SIP_HDR_AUTHENTICATION_INFO:\n\tcase SIP_HDR_CALL_INFO:\n\tcase SIP_HDR_CONTACT:\n\tcase SIP_HDR_CONTENT_ENCODING:\n\tcase SIP_HDR_CONTENT_LANGUAGE:\n\tcase SIP_HDR_ERROR_INFO:\n\tcase SIP_HDR_HISTORY_INFO:\n\tcase SIP_HDR_IN_REPLY_TO:\n\tcase SIP_HDR_P_ASSERTED_IDENTITY:\n\tcase SIP_HDR_P_ASSOCIATED_URI:\n\tcase SIP_HDR_P_EARLY_MEDIA:\n\tcase SIP_HDR_P_MEDIA_AUTHORIZATION:\n\tcase SIP_HDR_P_PREFERRED_IDENTITY:\n\tcase SIP_HDR_P_REFUSED_URI_LIST:\n\tcase SIP_HDR_P_VISITED_NETWORK_ID:\n\tcase SIP_HDR_PATH:\n\tcase SIP_HDR_PERMISSION_MISSING:\n\tcase SIP_HDR_PROXY_REQUIRE:\n\tcase SIP_HDR_REASON:\n\tcase SIP_HDR_RECORD_ROUTE:\n\tcase SIP_HDR_REJECT_CONTACT:\n\tcase SIP_HDR_REQUEST_DISPOSITION:\n\tcase SIP_HDR_REQUIRE:\n\tcase SIP_HDR_RESOURCE_PRIORITY:\n\tcase SIP_HDR_ROUTE:\n\tcase SIP_HDR_SECURITY_CLIENT:\n\tcase SIP_HDR_SECURITY_SERVER:\n\tcase SIP_HDR_SECURITY_VERIFY:\n\tcase SIP_HDR_SERVICE_ROUTE:\n\tcase SIP_HDR_SUPPORTED:\n\tcase SIP_HDR_TRIGGER_CONSENT:\n\tcase SIP_HDR_UNSUPPORTED:\n\tcase SIP_HDR_VIA:\n\tcase SIP_HDR_WARNING:\n\t\treturn true;\n\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\nstatic inline int hdr_add(struct sip_msg *msg, const struct pl *name,\n\t\t\t  enum sip_hdrid id, const char *p, ssize_t l,\n\t\t\t  bool atomic, bool line)\n{\n\tstruct sip_hdr *hdr;\n\tint err = 0;\n\n\thdr = mem_zalloc(sizeof(*hdr), hdr_destructor);\n\tif (!hdr)\n\t\treturn ENOMEM;\n\n\thdr->name  = *name;\n\thdr->val.p = p;\n\thdr->val.l = MAX(l, 0);\n\thdr->id    = id;\n\n\tswitch (id) {\n\n\tcase SIP_HDR_VIA:\n\tcase SIP_HDR_ROUTE:\n\t\tif (!atomic)\n\t\t\tbreak;\n\n\t\thash_append(msg->hdrht, id, &hdr->he, mem_ref(hdr));\n\t\tlist_append(&msg->hdrl, &hdr->le, mem_ref(hdr));\n\t\tbreak;\n\n\tdefault:\n\t\tif (atomic)\n\t\t\thash_append(msg->hdrht, id, &hdr->he, mem_ref(hdr));\n\t\tif (line)\n\t\t\tlist_append(&msg->hdrl, &hdr->le, mem_ref(hdr));\n\t\tbreak;\n\t}\n\n\t/* parse common headers */\n\tswitch (id) {\n\n\tcase SIP_HDR_VIA:\n\t\tif (!atomic || pl_isset(&msg->via.sentby))\n\t\t\tbreak;\n\n\t\terr = sip_via_decode(&msg->via, &hdr->val);\n\t\tbreak;\n\n\tcase SIP_HDR_TO:\n\t\terr = sip_addr_decode((struct sip_addr *)&msg->to, &hdr->val);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\t(void)msg_param_decode(&msg->to.params, \"tag\", &msg->to.tag);\n\t\tmsg->to.val = hdr->val;\n\t\tbreak;\n\n\tcase SIP_HDR_FROM:\n\t\terr = sip_addr_decode((struct sip_addr *)&msg->from,\n\t\t\t\t      &hdr->val);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\t(void)msg_param_decode(&msg->from.params, \"tag\",\n\t\t\t\t       &msg->from.tag);\n\t\tmsg->from.val = hdr->val;\n\t\tbreak;\n\n\tcase SIP_HDR_CALL_ID:\n\t\tmsg->callid = hdr->val;\n\t\tbreak;\n\n\tcase SIP_HDR_CSEQ:\n\t\terr = sip_cseq_decode(&msg->cseq, &hdr->val);\n\t\tbreak;\n\n\tcase SIP_HDR_RSEQ:\n\t\tmsg->rel_seq = pl_u32(&hdr->val);\n\t\tbreak;\n\n\tcase SIP_HDR_RACK:\n\t\terr = sip_rack_decode(&msg->rack, &hdr->val);\n\t\tbreak;\n\n\tcase SIP_HDR_MAX_FORWARDS:\n\t\tmsg->maxfwd = hdr->val;\n\t\tbreak;\n\n\tcase SIP_HDR_CONTENT_TYPE:\n\t\terr = msg_ctype_decode(&msg->ctyp, &hdr->val);\n\t\tbreak;\n\n\tcase SIP_HDR_CONTENT_LENGTH:\n\t\tmsg->clen = hdr->val;\n\t\tbreak;\n\n\tcase SIP_HDR_EXPIRES:\n\t\tmsg->expires = hdr->val;\n\t\tbreak;\n\n\tdefault:\n\t\t/* re_printf(\"%r = %u\\n\", &hdr->name, id); */\n\t\tbreak;\n\t}\n\n\tmem_deref(hdr);\n\n\treturn err;\n}\n\n\n/**\n * Decode a SIP message\n *\n * @param msgp Pointer to allocated SIP Message\n * @param mb   Buffer containing SIP Message\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_msg_decode(struct sip_msg **msgp, struct mbuf *mb)\n{\n\tstruct pl x, y, z, e, name;\n\tconst char *p, *v, *cv;\n\tstruct sip_msg *msg;\n\tbool comsep, quote;\n\tenum sip_hdrid id = SIP_HDR_NONE;\n\tuint32_t ws, lf;\n\tsize_t l;\n\tint err;\n\n\tif (!msgp || !mb)\n\t\treturn EINVAL;\n\n\tp = (const char *)mbuf_buf(mb);\n\tl = mbuf_get_left(mb);\n\n\tif (re_regex(p, l, \"[^ \\t\\r\\n]+ [^ \\t\\r\\n]+ [^\\r\\n]*[\\r]*[\\n]1\",\n\t\t     &x, &y, &z, NULL, &e) || x.p != (char *)mbuf_buf(mb))\n\t\treturn (l > STARTLINE_MAX) ? EBADMSG : ENODATA;\n\n\tmsg = mem_zalloc(sizeof(*msg), destructor);\n\tif (!msg)\n\t\treturn ENOMEM;\n\n\terr = hash_alloc(&msg->hdrht, HDR_HASH_SIZE);\n\tif (err)\n\t\tgoto out;\n\n\tmsg->tag = rand_u64();\n\tmsg->mb  = mem_ref(mb);\n\tmsg->req = (0 == pl_strcmp(&z, \"SIP/2.0\"));\n\n\tif (msg->req) {\n\n\t\tmsg->met = x;\n\t\tmsg->ruri = y;\n\t\tmsg->ver = z;\n\n\t\tif (uri_decode(&msg->uri, &y)) {\n\t\t\terr = EBADMSG;\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse {\n\t\tmsg->ver    = x;\n\t\tmsg->scode  = pl_u32(&y);\n\t\tmsg->reason = z;\n\n\t\tif (!msg->scode) {\n\t\t\terr = EBADMSG;\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\tl -= e.p + e.l - p;\n\tp = e.p + e.l;\n\n\tname.p = v = cv = NULL;\n\tname.l = ws = lf = 0;\n\tcomsep = false;\n\tquote = false;\n\n\tfor (; l > 0; p++, l--) {\n\n\t\tswitch (*p) {\n\n\t\tcase ' ':\n\t\tcase '\\t':\n\t\t\tlf = 0; /* folding */\n\t\t\t++ws;\n\t\t\tbreak;\n\n\t\tcase '\\r':\n\t\t\t++ws;\n\t\t\tbreak;\n\n\t\tcase '\\n':\n\t\t\t++ws;\n\n\t\t\tif (!lf++)\n\t\t\t\tbreak;\n\n\t\t\t++p; --l; /* eoh */\n\n\t\t\t/*@fallthrough@*/\n\n\t\tdefault:\n\t\t\tif (lf || (*p == ',' && comsep && !quote)) {\n\n\t\t\t\tif (!name.l) {\n\t\t\t\t\terr = EBADMSG;\n\t\t\t\t\tgoto out;\n\t\t\t\t}\n\n\t\t\t\terr = hdr_add(msg, &name, id, cv ? cv : p,\n\t\t\t\t\t      cv ? p - cv - ws : 0,\n\t\t\t\t\t      true, cv == v && lf);\n\t\t\t\tif (err)\n\t\t\t\t\tgoto out;\n\n\t\t\t\tif (!lf) { /* comma separated */\n\t\t\t\t\tcv = NULL;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (cv != v) {\n\t\t\t\t\terr = hdr_add(msg, &name, id,\n\t\t\t\t\t\t      v ? v : p,\n\t\t\t\t\t\t      v ? p - v - ws : 0,\n\t\t\t\t\t\t      false, true);\n\t\t\t\t\tif (err)\n\t\t\t\t\t\tgoto out;\n\t\t\t\t}\n\n\t\t\t\tif (lf > 1) { /* eoh */\n\t\t\t\t\terr = 0;\n\t\t\t\t\tgoto out;\n\t\t\t\t}\n\n\t\t\t\tcomsep = false;\n\t\t\t\tname.p = NULL;\n\t\t\t\tcv = v = NULL;\n\t\t\t\tlf = 0;\n\t\t\t}\n\n\t\t\tif (!name.p) {\n\t\t\t\tname.p = p;\n\t\t\t\tname.l = 0;\n\t\t\t\tws = 0;\n\t\t\t}\n\n\t\t\tif (!name.l) {\n\t\t\t\tif (*p != ':') {\n\t\t\t\t\tws = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tname.l = MAX((int)(p - name.p - ws), 0);\n\t\t\t\tif (!name.l) {\n\t\t\t\t\terr = EBADMSG;\n\t\t\t\t\tgoto out;\n\t\t\t\t}\n\n\t\t\t\tid = hdr_hash(&name);\n\t\t\t\tcomsep = hdr_comma_separated(id);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!cv) {\n\t\t\t\tquote = false;\n\t\t\t\tcv = p;\n\t\t\t}\n\n\t\t\tif (!v) {\n\t\t\t\tv = p;\n\t\t\t}\n\n\t\t\tif (*p == '\"')\n\t\t\t\tquote = !quote;\n\n\t\t\tws = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\terr = ENODATA;\n\n out:\n\tif (err)\n\t\tmem_deref(msg);\n\telse {\n\t\t*msgp = msg;\n\t\tmb->pos = mb->end - l;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Get a SIP Header from a SIP Message\n *\n * @param msg SIP Message\n * @param id  SIP Header ID\n *\n * @return SIP Header if found, NULL if not found\n */\nconst struct sip_hdr *sip_msg_hdr(const struct sip_msg *msg, enum sip_hdrid id)\n{\n\treturn sip_msg_hdr_apply(msg, true, id, NULL, NULL);\n}\n\n\n/**\n * Apply a function handler to certain SIP Headers\n *\n * @param msg SIP Message\n * @param fwd True to traverse forwards, false to traverse backwards\n * @param id  SIP Header ID\n * @param h   Function handler\n * @param arg Handler argument\n *\n * @return SIP Header if handler returns true, otherwise NULL\n */\nconst struct sip_hdr *sip_msg_hdr_apply(const struct sip_msg *msg,\n\t\t\t\t\tbool fwd, enum sip_hdrid id,\n\t\t\t\t\tsip_hdr_h *h, void *arg)\n{\n\tstruct list *lst;\n\tstruct le *le;\n\n\tif (!msg)\n\t\treturn NULL;\n\n\tlst = hash_list(msg->hdrht, id);\n\n\tle = fwd ? list_head(lst) : list_tail(lst);\n\n\twhile (le) {\n\t\tconst struct sip_hdr *hdr = le->data;\n\n\t\tle = fwd ? le->next : le->prev;\n\n\t\tif (hdr->id != id)\n\t\t\tcontinue;\n\n\t\tif (!h || h(hdr, msg, arg))\n\t\t\treturn hdr;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Get an unknown SIP Header from a SIP Message\n *\n * @param msg  SIP Message\n * @param name Header name\n *\n * @return SIP Header if found, NULL if not found\n */\nconst struct sip_hdr *sip_msg_xhdr(const struct sip_msg *msg, const char *name)\n{\n\treturn sip_msg_xhdr_apply(msg, true, name, NULL, NULL);\n}\n\n\n/**\n * Apply a function handler to certain unknown SIP Headers\n *\n * @param msg  SIP Message\n * @param fwd  True to traverse forwards, false to traverse backwards\n * @param name SIP Header name\n * @param h    Function handler\n * @param arg  Handler argument\n *\n * @return SIP Header if handler returns true, otherwise NULL\n */\nconst struct sip_hdr *sip_msg_xhdr_apply(const struct sip_msg *msg,\n\t\t\t\t\t bool fwd, const char *name,\n\t\t\t\t\t sip_hdr_h *h, void *arg)\n{\n\tstruct list *lst;\n\tstruct le *le;\n\tstruct pl pl;\n\n\tif (!msg || !name)\n\t\treturn NULL;\n\n\tpl_set_str(&pl, name);\n\n\tlst = hash_list(msg->hdrht, hdr_hash(&pl));\n\n\tle = fwd ? list_head(lst) : list_tail(lst);\n\n\twhile (le) {\n\t\tconst struct sip_hdr *hdr = le->data;\n\n\t\tle = fwd ? le->next : le->prev;\n\n\t\tif (pl_casecmp(&hdr->name, &pl))\n\t\t\tcontinue;\n\n\t\tif (!h || h(hdr, msg, arg))\n\t\t\treturn hdr;\n\t}\n\n\treturn NULL;\n}\n\n\nstatic bool count_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,\n\t\t\t  void *arg)\n{\n\tuint32_t *n = arg;\n\t(void)hdr;\n\t(void)msg;\n\n\t++(*n);\n\n\treturn false;\n}\n\n\n/**\n * Count the number of SIP Headers\n *\n * @param msg SIP Message\n * @param id  SIP Header ID\n *\n * @return Number of SIP Headers\n */\nuint32_t sip_msg_hdr_count(const struct sip_msg *msg, enum sip_hdrid id)\n{\n\tuint32_t n = 0;\n\n\tsip_msg_hdr_apply(msg, true, id, count_handler, &n);\n\n\treturn n;\n}\n\n\n/**\n * Count the number of unknown SIP Headers\n *\n * @param msg  SIP Message\n * @param name SIP Header name\n *\n * @return Number of SIP Headers\n */\nuint32_t sip_msg_xhdr_count(const struct sip_msg *msg, const char *name)\n{\n\tuint32_t n = 0;\n\n\tsip_msg_xhdr_apply(msg, true, name, count_handler, &n);\n\n\treturn n;\n}\n\n\nstatic bool value_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,\n\t\t\t  void *arg)\n{\n\t(void)msg;\n\n\treturn 0 == pl_strcasecmp(&hdr->val, (const char *)arg);\n}\n\n\n/**\n * Check if a SIP Header matches a certain value\n *\n * @param msg   SIP Message\n * @param id    SIP Header ID\n * @param value Header value to check\n *\n * @return True if value matches, false if not\n */\nbool sip_msg_hdr_has_value(const struct sip_msg *msg, enum sip_hdrid id,\n\t\t\t   const char *value)\n{\n\treturn NULL != sip_msg_hdr_apply(msg, true, id, value_handler,\n\t\t\t\t\t (void *)value);\n}\n\n\n/**\n * Check if an unknown SIP Header matches a certain value\n *\n * @param msg   SIP Message\n * @param name  SIP Header name\n * @param value Header value to check\n *\n * @return True if value matches, false if not\n */\nbool sip_msg_xhdr_has_value(const struct sip_msg *msg, const char *name,\n\t\t\t    const char *value)\n{\n\treturn NULL != sip_msg_xhdr_apply(msg, true, name, value_handler,\n\t\t\t\t\t  (void *)value);\n}\n\n\n/**\n * Print a SIP Message to stdout\n *\n * @param msg SIP Message\n */\nvoid sip_msg_dump(const struct sip_msg *msg)\n{\n\tstruct le *le;\n\tuint32_t i;\n\n\tif (!msg)\n\t\treturn;\n\n\tfor (i=0; i<HDR_HASH_SIZE; i++) {\n\n\t\tle = list_head(hash_list(msg->hdrht, i));\n\n\t\twhile (le) {\n\t\t\tconst struct sip_hdr *hdr = le->data;\n\n\t\t\tle = le->next;\n\n\t\t\t(void)re_printf(\"%02u '%r'='%r'\\n\", i, &hdr->name,\n\t\t\t\t\t&hdr->val);\n\t\t}\n\t}\n\n\tle = list_head(&msg->hdrl);\n\n\twhile (le) {\n\t\tconst struct sip_hdr *hdr = le->data;\n\n\t\tle = le->next;\n\n\t\t(void)re_printf(\"%02u '%r'='%r'\\n\", hdr->id, &hdr->name,\n\t\t\t\t&hdr->val);\n\t}\n}\n"
  },
  {
    "path": "src/sip/rack.c",
    "content": "/**\n * @file rack.c  SIP RAck decode (RFC 3262)\n *\n * Copyright (C) 2022 commend.com - m.fridrich@commend.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_uri.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_msg.h>\n#include <re_sip.h>\n\n/**\n * Decode a pointer-length string into a SIP RAck header\n *\n * @param rack SIP RAck header\n * @param pl   Pointer-length string\n *\n * @return 0 for success, otherwise errorcode\n */\nint sip_rack_decode(struct sip_rack *rack, const struct pl *pl)\n{\n\tstruct pl rel_seq;\n\tstruct pl cseq;\n\tint err;\n\n\tif (!rack || !pl)\n\t\treturn EINVAL;\n\n\terr = re_regex(pl->p, pl->l,\n\t\t       \"[0-9]+[ \\t\\r\\n]+[0-9]+[ \\t\\r\\n]+[^ \\t\\r\\n]+\",\n\t\t       &rel_seq, NULL, &cseq, NULL, &rack->met);\n\tif (err)\n\t\treturn err;\n\n\track->rel_seq = pl_u32(&rel_seq);\n\track->cseq = pl_u32(&cseq);\n\treturn 0;\n}\n"
  },
  {
    "path": "src/sip/reply.c",
    "content": "/**\n * @file sip/reply.c  SIP Reply\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_udp.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include \"sip.h\"\n\n\nstatic int vreplyf(struct sip_strans **stp, struct mbuf **mbp, bool trans,\n\t\t   struct sip *sip, const struct sip_msg *msg, bool rec_route,\n\t\t   uint16_t scode, const char *reason,\n\t\t   const char *fmt, va_list ap)\n{\n\tbool rport = false;\n\tuint32_t viac = 0;\n\tstruct mbuf *mb;\n\tstruct sa dst;\n\tstruct le *le;\n\tint err;\n\n\tif (!sip || !msg || !reason)\n\t\treturn EINVAL;\n\n\tif (!pl_strcmp(&msg->met, \"ACK\"))\n\t\treturn 0;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = mbuf_printf(mb, \"SIP/2.0 %u %s\\r\\n\", scode, reason);\n\n\tfor (le = msg->hdrl.head; le; le = le->next) {\n\n\t\tstruct sip_hdr *hdr = le->data;\n\t\tstruct pl rp;\n\n\t\tswitch (hdr->id) {\n\n\t\tcase SIP_HDR_VIA:\n\t\t\terr |= mbuf_printf(mb, \"%r: \", &hdr->name);\n\t\t\tif (viac++) {\n\t\t\t\terr |= mbuf_printf(mb, \"%r\\r\\n\", &hdr->val);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!msg_param_exists(&msg->via.params, \"rport\", &rp)){\n\t\t\t\terr |= mbuf_write_pl_skip(mb, &hdr->val, &rp);\n\t\t\t\terr |= mbuf_printf(mb, \";rport=%u\",\n\t\t\t\t\t\t   sa_port(&msg->src));\n\t\t\t\trport = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\terr |= mbuf_write_pl(mb, &hdr->val);\n\n\t\t\tif (rport || !sa_cmp(&msg->src, &msg->via.addr,\n\t\t\t\t\t     SA_ADDR))\n\t\t\t\terr |= mbuf_printf(mb, \";received=%j\",\n\t\t\t\t\t\t   &msg->src);\n\n\t\t\terr |= mbuf_write_str(mb, \"\\r\\n\");\n\t\t\tbreak;\n\n\t\tcase SIP_HDR_TO:\n\t\t\terr |= mbuf_printf(mb, \"%r: %r\", &hdr->name,\n\t\t\t\t\t   &hdr->val);\n\t\t\tif (!pl_isset(&msg->to.tag) && scode > 100)\n\t\t\t\terr |= mbuf_printf(mb, \";tag=%016llx\",\n\t\t\t\t\t\t   msg->tag);\n\t\t\terr |= mbuf_write_str(mb, \"\\r\\n\");\n\t\t\tbreak;\n\n\t\tcase SIP_HDR_RECORD_ROUTE:\n\t\t\tif (!rec_route)\n\t\t\t\tbreak;\n\n\t\t\t/*@fallthrough@*/\n\n\t\tcase SIP_HDR_FROM:\n\t\tcase SIP_HDR_CALL_ID:\n\t\tcase SIP_HDR_CSEQ:\n\t\t\terr |= mbuf_printf(mb, \"%r: %r\\r\\n\",\n\t\t\t\t\t   &hdr->name, &hdr->val);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (sip->software)\n\t\terr |= mbuf_printf(mb, \"Server: %s\\r\\n\", sip->software);\n\n\tif (fmt)\n\t\terr |= mbuf_vprintf(mb, fmt, ap);\n\telse\n\t\terr |= mbuf_printf(mb, \"Content-Length: 0\\r\\n\\r\\n\");\n\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\n\tsip_reply_addr(&dst, msg, rport);\n\n\tif (trans) {\n\t\terr = sip_strans_reply(stp, sip, msg, &dst, scode, mb);\n\t}\n\telse {\n\t\terr = sip_send(sip, msg->sock, msg->tp, &dst, mb);\n\t}\n\n out:\n\tif (err && stp)\n\t\t*stp = mem_deref(*stp);\n\n\tif (!err && mbp)\n\t\t*mbp = mb;\n\telse\n\t\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/**\n * Formatted reply using Server Transaction\n *\n * @param stp       Pointer to allocated SIP Server Transaction (optional)\n * @param mbp       Pointer to allocated SIP message buffer (optional)\n * @param sip       SIP Stack instance\n * @param msg       Incoming SIP message\n * @param rec_route True to copy Record-Route headers\n * @param scode     Response status code\n * @param reason    Response reason phrase\n * @param fmt       Additional formatted SIP headers and body, otherwise NULL\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_treplyf(struct sip_strans **stp, struct mbuf **mbp, struct sip *sip,\n\t\tconst struct sip_msg *msg, bool rec_route, uint16_t scode,\n\t\tconst char *reason, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = vreplyf(stp, mbp, true, sip, msg, rec_route, scode, reason,\n\t\t      fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Reply using Server Transaction\n *\n * @param stp    Pointer to allocated SIP Server Transaction (optional)\n * @param sip    SIP Stack instance\n * @param msg    Incoming SIP message\n * @param scode  Response status code\n * @param reason Response reason phrase\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_treply(struct sip_strans **stp, struct sip *sip,\n\t       const struct sip_msg *msg, uint16_t scode, const char *reason)\n{\n\treturn sip_treplyf(stp, NULL, sip, msg, false, scode, reason, NULL);\n}\n\n\n/**\n * Stateless formatted reply\n *\n * @param sip    SIP Stack instance\n * @param msg    Incoming SIP message\n * @param scode  Response status code\n * @param reason Response reason phrase\n * @param fmt    Additional formatted SIP headers and body, otherwise NULL\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_replyf(struct sip *sip, const struct sip_msg *msg, uint16_t scode,\n\t       const char *reason, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = vreplyf(NULL, NULL, false, sip, msg, false, scode, reason,\n\t\t      fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Stateless reply\n *\n * @param sip    SIP Stack instance\n * @param msg    Incoming SIP message\n * @param scode  Response status code\n * @param reason Response reason phrase\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_reply(struct sip *sip, const struct sip_msg *msg, uint16_t scode,\n\t      const char *reason)\n{\n\treturn sip_replyf(sip, msg, scode, reason, NULL);\n}\n\n\n/**\n * Get the reply address from a SIP message\n *\n * @param addr  Network address, set on return\n * @param msg   SIP message\n * @param rport Rport value\n */\nvoid sip_reply_addr(struct sa *addr, const struct sip_msg *msg, bool rport)\n{\n\tuint16_t port;\n\tstruct pl pl;\n\n\tif (!addr || !msg)\n\t\treturn;\n\n\tport = sa_port(&msg->via.addr);\n\t*addr = msg->src;\n\n\tswitch (msg->tp) {\n\n\tcase SIP_TRANSP_UDP:\n\t\tif (!msg_param_decode(&msg->via.params, \"maddr\", &pl)) {\n\t\t\t(void)sa_set(addr, &pl,sip_transp_port(msg->tp, port));\n\t\t\tbreak;\n\t\t}\n\n\t\tif (rport)\n\t\t\tbreak;\n\n\t\t/*@fallthrough@*/\n\n\tcase SIP_TRANSP_TCP:\n\tcase SIP_TRANSP_TLS:\n\t\tsa_set_port(addr, sip_transp_port(msg->tp, port));\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "src/sip/request.c",
    "content": "/**\n * @file sip/request.c  SIP Request\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_fmt.h>\n#include <re_dns.h>\n#include <re_uri.h>\n#include <re_sys.h>\n#include <re_udp.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include \"sip.h\"\n\n\nstruct sip_request {\n\tstruct le le;\n\tstruct list cachel;\n\tstruct list addrl;\n\tstruct list srvl;\n\tstruct sip_request **reqp;\n\tstruct sip_ctrans *ct;\n\tstruct dns_query *dnsq;\n\tstruct dns_query *dnsq2;\n\tstruct sip *sip;\n\tchar *met;\n\tchar *uri;\n\tchar *host;\n\tchar *branch;\n\tstruct mbuf *mb;\n\tsip_send_h *sendh;\n\tsip_resp_h *resph;\n\tvoid *arg;\n\tsize_t sortkey;\n\tenum sip_transp tp;\n\tbool tp_selected;\n\tbool stateful;\n\tbool canceled;\n\tbool provrecv;\n\tuint16_t port;\n\tuint16_t srcport;\n};\n\n\nstatic int  request_next(struct sip_request *req);\nstatic bool rr_append_handler(struct dnsrr *rr, void *arg);\nstatic void srv_handler(int err, const struct dnshdr *hdr, struct list *ansl,\n\t\t\tstruct list *authl, struct list *addl, void *arg);\nstatic int  srv_lookup(struct sip_request *req, const char *domain);\nstatic int  addr_lookup(struct sip_request *req, const char *name);\n\n\nstatic int str_ldup(char **dst, const char *src, int len)\n{\n\tstruct pl pl;\n\n\tpl.p = src;\n\tpl.l = len < 0 ? str_len(src) : (size_t)len;\n\n\treturn pl_strdup(dst, &pl);\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sip_request *req = arg;\n\n\tif (req->reqp && req->stateful) {\n\t\t/* user does deref before request has completed */\n\t\t*req->reqp = NULL;\n\t\treq->reqp  = NULL;\n\t\treq->sendh = NULL;\n\t\treq->resph = NULL;\n\t\tsip_request_cancel(mem_ref(req));\n\t\treturn;\n\t}\n\n\tlist_flush(&req->cachel);\n\tlist_flush(&req->addrl);\n\tlist_flush(&req->srvl);\n\tlist_unlink(&req->le);\n\tmem_deref(req->dnsq);\n\tmem_deref(req->dnsq2);\n\tmem_deref(req->ct);\n\tmem_deref(req->met);\n\tmem_deref(req->uri);\n\tmem_deref(req->host);\n\tmem_deref(req->branch);\n\tmem_deref(req->mb);\n}\n\n\nstatic void terminate(struct sip_request *req, int err,\n\t\t      const struct sip_msg *msg)\n{\n\tif (req->reqp) {\n\t\t*req->reqp = NULL;\n\t\treq->reqp = NULL;\n\t}\n\n\tlist_unlink(&req->le);\n\treq->sendh = NULL;\n\n\tif (req->resph) {\n\t\treq->resph(err, msg, req->arg);\n\t\treq->resph = NULL;\n\t}\n}\n\n\nstatic bool close_handler(struct le *le, void *arg)\n{\n\tstruct sip_request *req = le->data;\n\t(void)arg;\n\n\treq->dnsq  = mem_deref(req->dnsq);\n\treq->dnsq2 = mem_deref(req->dnsq2);\n\treq->ct    = mem_deref(req->ct);\n\n\tterminate(req, ECONNABORTED, NULL);\n\tmem_deref(req);\n\n\treturn false;\n}\n\n\nstatic void response_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct sip_request *req = arg;\n\n\tif (msg && msg->scode < 200) {\n\t\tif (!req->provrecv) {\n\t\t\treq->provrecv = true;\n\t\t\tif (req->canceled)\n\t\t\t\t(void)sip_ctrans_cancel(req->ct);\n\t\t}\n\n\t\tif (req->resph)\n\t\t\treq->resph(err, msg, req->arg);\n\n\t\treturn;\n\t}\n\n\treq->ct = NULL;\n\n\tif (!req->canceled && (err || (msg && msg->scode == 503)) &&\n\t    (req->addrl.head || req->srvl.head)) {\n\n\t\terr = request_next(req);\n\t\tif (!err)\n\t\t\treturn;\n\t}\n\n\tterminate(req, err, msg);\n\tmem_deref(req);\n}\n\n\nstatic int connect_handler(struct sa *src, const struct sa *dst,\n\t\t\t   struct mbuf *mb, void *arg)\n{\n\tstruct sip_request *req = arg;\n\tstruct mbuf *mbs  = NULL;\n\tstruct mbuf *cont = NULL;\n\tint err;\n\n\tif (!sa_isset(src, SA_ALL))\n\t\treturn EINVAL;\n\n\tmbuf_set_posend(mb, 0, 0);\n\tmbs = mbuf_alloc(256);\n\tif (!mbs)\n\t\treturn ENOMEM;\n\n\terr = req->sendh ? req->sendh(req->tp, src, dst, mbs, &cont,\n\t\t\t\t      req->arg) : 0;\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(mbs, 0);\n\terr  = mbuf_printf(mb, \"%s %s SIP/2.0\\r\\n\", req->met, req->uri);\n\terr |= mbuf_printf(mb, \"Via: SIP/2.0/%s %J;branch=%s;rport\\r\\n\",\n\t\t\t   sip_transp_name(req->tp), src, req->branch);\n\terr |= mbuf_write_mem(mb, mbuf_buf(mbs), mbuf_get_left(mbs));\n\terr |= mbuf_write_mem(mb, mbuf_buf(req->mb), mbuf_get_left(req->mb));\n\tif (cont) {\n\t\terr |= mbuf_write_mem(mb, mbuf_buf(cont), mbuf_get_left(cont));\n\t\tmem_deref(cont);\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\nout:\n\tif (err)\n\t\tmbuf_reset(mb);\n\n\tmem_deref(mbs);\n\treturn err;\n}\n\n\nstatic int request(struct sip_request *req, enum sip_transp tp,\n\t\t   const struct sa *dst)\n{\n\tstruct mbuf *mb   = NULL;\n\tint err = ENOMEM;\n\n\treq->provrecv = false;\n\n\tmem_deref(req->branch);\n\t(void)re_sdprintf(&req->branch, \"z9hG4bK%016llx\", rand_u64());\n\tmb  = mbuf_alloc(1024);\n\tif (!req->branch || !mb)\n\t\tgoto out;\n\n\n\tif (!req->branch || !mb)\n\t\tgoto out;\n\n\tif (req->srcport) {\n\t\tstruct sip_conncfg cfg = {.srcport = req->srcport};\n\t\terr = sip_conncfg_set(req->sip, dst, &cfg);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\tif (!req->stateful) {\n\t\terr = sip_send_conn(req->sip, NULL, tp, dst, req->host, mb,\n\t\t\t\t    connect_handler, req);\n\t}\n\telse {\n\t\terr = sip_ctrans_request(&req->ct, req->sip, tp, dst, req->met,\n\t\t\t\t\t req->branch, req->host, mb,\n\t\t\t\t\t connect_handler, response_handler,\n\t\t\t\t\t req);\n\t}\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic int request_next(struct sip_request *req)\n{\n\tstruct dnsrr *rr;\n\tstruct sa dst;\n\tint err;\n\n again:\n\trr = list_ledata(req->addrl.head);\n\tif (!rr) {\n\t\trr = list_ledata(req->srvl.head);\n\t\tif (!rr)\n\t\t\treturn ENOENT;\n\n\t\treq->port = rr->rdata.srv.port;\n\n\t\tdns_rrlist_apply2(&req->cachel, rr->rdata.srv.target,\n\t\t\t\t  DNS_TYPE_A, DNS_TYPE_AAAA, DNS_CLASS_IN,\n\t\t\t\t  true, rr_append_handler, &req->addrl);\n\n\t\tlist_unlink(&rr->le);\n\n\t\tif (req->addrl.head) {\n\t\t\tdns_rrlist_sort_addr(&req->addrl, req->sortkey);\n\t\t\tmem_deref(rr);\n\t\t\tgoto again;\n\t\t}\n\n\t\terr = addr_lookup(req, rr->rdata.srv.target);\n\t\tmem_deref(rr);\n\n\t\treturn err;\n\t}\n\n\tswitch (rr->type) {\n\n\tcase DNS_TYPE_A:\n\t\tsa_set_in(&dst, rr->rdata.a.addr, req->port);\n\t\tbreak;\n\n\tcase DNS_TYPE_AAAA:\n\t\tsa_set_in6(&dst, rr->rdata.aaaa.addr, req->port);\n\t\tbreak;\n\n\tdefault:\n\t\treturn EINVAL;\n\t}\n\n\tlist_unlink(&rr->le);\n\tmem_deref(rr);\n\n\terr = request(req, req->tp, &dst);\n\tif (err) {\n\t\tif (req->addrl.head || req->srvl.head)\n\t\t\tgoto again;\n\t}\n\telse if (!req->stateful) {\n\t\treq->resph = NULL;\n\t\tterminate(req, 0, NULL);\n\t\tmem_deref(req);\n\t}\n\n\treturn err;\n}\n\n\nstatic bool transp_next(struct sip *sip, enum sip_transp *tp)\n{\n\tenum sip_transp i;\n\n\tfor (i=(enum sip_transp)(*tp+1); i<SIP_TRANSPC; i++) {\n\n\t\tif (!sip_transp_supported(sip, i, AF_UNSPEC))\n\t\t\tcontinue;\n\n\t\t*tp = i;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nstatic bool transp_next_srv(struct sip *sip, enum sip_transp *tp)\n{\n\tenum sip_transp i;\n\n\tfor (i=(enum sip_transp)(*tp-1); i>SIP_TRANSP_NONE; i--) {\n\t\tconst char *srvid;\n\n\t\tsrvid = sip_transp_srvid(i);\n\t\tif (0 == str_cmp(srvid, \"???\"))\n\t\t\tcontinue;\n\n\t\tif (!sip_transp_supported(sip, i, AF_UNSPEC))\n\t\t\tcontinue;\n\n\t\t*tp = i;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nstatic bool transp_first(struct sip *sip, enum sip_transp *tp)\n{\n\tif (!sip || !tp)\n\t\treturn false;\n\n\tif (sip->tp_def != SIP_TRANSP_NONE &&\n\t\t\tsip_transp_supported(sip, sip->tp_def, AF_UNSPEC)) {\n\t\t*tp = sip->tp_def;\n\t\treturn true;\n\t}\n\n\t*tp = SIP_TRANSP_NONE;\n\treturn transp_next(sip, tp);\n}\n\n\nstatic bool rr_append_handler(struct dnsrr *rr, void *arg)\n{\n\tstruct dnsrr *rrdup;\n\tstruct list *lst = arg;\n\n\tswitch (rr->type) {\n\n\tcase DNS_TYPE_A:\n\tcase DNS_TYPE_AAAA:\n\tcase DNS_TYPE_SRV:\n\t\tif (0 == dns_rr_dup(&rrdup, rr))\n\t\t\tlist_append(lst, &rrdup->le, rrdup);\n\t}\n\n\treturn false;\n}\n\n\nstatic bool rr_cache_handler(struct dnsrr *rr, void *arg)\n{\n\tstruct sip_request *req = arg;\n\n\tswitch (rr->type) {\n\n\tcase DNS_TYPE_A:\n\t\tif (!sip_transp_supported(req->sip, req->tp, AF_INET))\n\t\t\tbreak;\n\n\t\tlist_unlink(&rr->le_priv);\n\t\tlist_append(&req->cachel, &rr->le_priv, rr);\n\t\tbreak;\n\n\tcase DNS_TYPE_AAAA:\n\t\tif (!sip_transp_supported(req->sip, req->tp, AF_INET6))\n\t\t\tbreak;\n\n\t\tlist_unlink(&rr->le_priv);\n\t\tlist_append(&req->cachel, &rr->le_priv, rr);\n\t\tbreak;\n\n\tcase DNS_TYPE_CNAME:\n\t\tlist_unlink(&rr->le_priv);\n\t\tlist_append(&req->cachel, &rr->le_priv, rr);\n\t\tbreak;\n\t}\n\n\treturn false;\n}\n\n\nstatic bool rr_naptr_handler(struct dnsrr *rr, void *arg)\n{\n\tstruct sip_request *req = arg;\n\tenum sip_transp tp;\n\n\tif (rr->type != DNS_TYPE_NAPTR)\n\t\treturn false;\n\n\tif (!str_casecmp(rr->rdata.naptr.services, \"SIP+D2U\"))\n\t\ttp = SIP_TRANSP_UDP;\n\telse if (!str_casecmp(rr->rdata.naptr.services, \"SIP+D2T\"))\n\t\ttp = SIP_TRANSP_TCP;\n\telse if (!str_casecmp(rr->rdata.naptr.services, \"SIPS+D2T\"))\n\t\ttp = SIP_TRANSP_TLS;\n\telse if (!str_casecmp(rr->rdata.naptr.services, \"SIP+D2W\"))\n\t\ttp = SIP_TRANSP_WS;\n\telse if (!str_casecmp(rr->rdata.naptr.services, \"SIPS+D2W\"))\n\t\ttp = SIP_TRANSP_WSS;\n\telse\n\t\treturn false;\n\n\tif (!sip_transp_supported(req->sip, tp, AF_UNSPEC))\n\t\treturn false;\n\n\treq->tp = tp;\n\treq->tp_selected = true;\n\n\treturn true;\n}\n\n\nstatic void naptr_handler(int err, const struct dnshdr *hdr, struct list *ansl,\n\t\t\t  struct list *authl, struct list *addl, void *arg)\n{\n\tstruct sip_request *req = arg;\n\tstruct dnsrr *rr;\n\t(void)hdr;\n\t(void)authl;\n\n\tdns_rrlist_sort(ansl, DNS_TYPE_NAPTR, req->sortkey);\n\n\trr = dns_rrlist_apply(ansl, NULL, DNS_TYPE_NAPTR, DNS_CLASS_IN, false,\n\t\t\t      rr_naptr_handler, req);\n\tif (!rr) {\n\t\treq->tp = SIP_TRANSPC;\n\t\tif (!transp_next_srv(req->sip, &req->tp)) {\n\t\t\terr = EPROTONOSUPPORT;\n\t\t\tgoto fail;\n\t\t}\n\n\t\terr = srv_lookup(req, req->host);\n\t\tif (err)\n\t\t\tgoto fail;\n\n\t\treturn;\n\t}\n\n\tdns_rrlist_apply(addl, rr->rdata.naptr.replace, DNS_TYPE_SRV,\n\t\t\t DNS_CLASS_IN, true, rr_append_handler, &req->srvl);\n\n\tif (!req->srvl.head) {\n\t\terr = dnsc_query(&req->dnsq, req->sip->dnsc,\n\t\t\t\t rr->rdata.naptr.replace, DNS_TYPE_SRV,\n\t\t\t\t DNS_CLASS_IN, true, srv_handler, req);\n\t\tif (err)\n\t\t\tgoto fail;\n\n\t\treturn;\n\t}\n\n\tdns_rrlist_sort(&req->srvl, DNS_TYPE_SRV, req->sortkey);\n\n\tdns_rrlist_apply(addl, NULL, DNS_QTYPE_ANY, DNS_CLASS_IN, false,\n\t\t\t rr_cache_handler, req);\n\n\terr = request_next(req);\n\tif (err)\n\t\tgoto fail;\n\n\treturn;\n\n fail:\n\tterminate(req, err, NULL);\n\tmem_deref(req);\n}\n\n\nstatic void srv_handler(int err, const struct dnshdr *hdr, struct list *ansl,\n\t\t\t struct list *authl, struct list *addl, void *arg)\n{\n\tstruct sip_request *req = arg;\n\t(void)hdr;\n\t(void)authl;\n\n\tdns_rrlist_apply(ansl, NULL, DNS_TYPE_SRV, DNS_CLASS_IN, false,\n\t\t\t rr_append_handler, &req->srvl);\n\n\tif (!req->srvl.head) {\n\t\tif (!req->tp_selected) {\n\t\t\tif (transp_next_srv(req->sip, &req->tp)) {\n\n\t\t\t\terr = srv_lookup(req, req->host);\n\t\t\t\tif (err)\n\t\t\t\t\tgoto fail;\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!transp_first(req->sip, &req->tp)) {\n\t\t\t\terr = EPROTONOSUPPORT;\n\t\t\t\tgoto fail;\n\t\t\t}\n\t\t}\n\n\t\treq->port = sip_transp_port(req->tp, 0);\n\n\t\terr = addr_lookup(req, req->host);\n\t\tif (err)\n\t\t\tgoto fail;\n\n\t\treturn;\n\t}\n\n\tdns_rrlist_sort(&req->srvl, DNS_TYPE_SRV, req->sortkey);\n\n\tdns_rrlist_apply(addl, NULL, DNS_QTYPE_ANY, DNS_CLASS_IN, false,\n\t\t\t rr_cache_handler, req);\n\n\terr = request_next(req);\n\tif (err)\n\t\tgoto fail;\n\n\treturn;\n\n fail:\n\tterminate(req, err, NULL);\n\tmem_deref(req);\n}\n\n\nstatic void addr_handler(int err, const struct dnshdr *hdr, struct list *ansl,\n\t\t\t struct list *authl, struct list *addl, void *arg)\n{\n\tstruct sip_request *req = arg;\n\t(void)hdr;\n\t(void)authl;\n\t(void)addl;\n\n\tdns_rrlist_apply2(ansl, NULL, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_CLASS_IN,\n\t\t\t  false, rr_append_handler, &req->addrl);\n\n\t/* wait for other (A/AAAA) query to complete */\n\tif (req->dnsq || req->dnsq2)\n\t\treturn;\n\n\tif (!req->addrl.head && !req->srvl.head) {\n\t\terr = err ? err : EDESTADDRREQ;\n\t\tgoto fail;\n\t}\n\n\tdns_rrlist_sort_addr(&req->addrl, req->sortkey);\n\n\terr = request_next(req);\n\tif (err)\n\t\tgoto fail;\n\n\treturn;\n\n fail:\n\tterminate(req, err, NULL);\n\tmem_deref(req);\n}\n\n\nstatic int srv_lookup(struct sip_request *req, const char *domain)\n{\n\tchar name[256];\n\n\tif (re_snprintf(name, sizeof(name), \"%s.%s\",\n\t\t\tsip_transp_srvid(req->tp), domain) < 0)\n\t\treturn ENOMEM;\n\n\treturn dnsc_query(&req->dnsq, req->sip->dnsc, name, DNS_TYPE_SRV,\n\t\t\t  DNS_CLASS_IN, true, srv_handler, req);\n}\n\n\nstatic int addr_lookup(struct sip_request *req, const char *name)\n{\n\tint err;\n\n\tif (sip_transp_supported(req->sip, req->tp, AF_INET)) {\n\n\t\terr = dnsc_query(&req->dnsq, req->sip->dnsc, name,\n\t\t\t\t DNS_TYPE_A, DNS_CLASS_IN, true,\n\t\t\t\t addr_handler, req);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (sip_transp_supported(req->sip, req->tp, AF_INET6)) {\n\n\t\terr = dnsc_query(&req->dnsq2, req->sip->dnsc, name,\n\t\t\t\t DNS_TYPE_AAAA, DNS_CLASS_IN, true,\n\t\t\t\t addr_handler, req);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (!req->dnsq && !req->dnsq2)\n\t\treturn EPROTONOSUPPORT;\n\n\treturn 0;\n}\n\n\nstatic int sip_request_alloc(struct sip_request **reqp,\n\t\tstruct sip *sip, bool stateful, const char *met, int metl,\n\t\tconst char *uri, int uril, const struct uri *route,\n\t\tenum sip_transp tp, struct mbuf *mb, size_t sortkey,\n\t\tsip_send_h *sendh, sip_resp_h *resph, void *arg)\n{\n\tstruct sip_request *req;\n\tstruct pl pl;\n\tint err;\n\n\tif (!sip || !met || !uri || !route || !mb)\n\t\treturn EINVAL;\n\n\tif (pl_strcasecmp(&route->scheme, \"sip\"))\n\t\treturn ENOSYS;\n\n\treq = mem_zalloc(sizeof(*req), destructor);\n\tif (!req)\n\t\treturn ENOMEM;\n\n\tlist_append(&sip->reql, &req->le, req);\n\n\terr = str_ldup(&req->met, met, metl);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_ldup(&req->uri, uri, uril);\n\tif (err)\n\t\tgoto out;\n\n\tif (msg_param_decode(&route->params, \"maddr\", &pl))\n\t\tpl = route->host;\n\n\terr = pl_strdup(&req->host, &pl);\n\tif (err)\n\t\tgoto out;\n\n\treq->stateful = stateful;\n\treq->sortkey = sortkey;\n\treq->mb    = mem_ref(mb);\n\treq->sip   = sip;\n\treq->sendh = sendh;\n\treq->resph = resph;\n\treq->arg   = arg;\n\n\tif (tp != SIP_TRANSP_NONE) {\n\t\treq->tp = tp;\n\t}\n\telse if (!msg_param_decode(&route->params, \"transport\", &pl)) {\n\n\t\treq->tp = sip_transp_decode(&pl);\n\t\tif (req->tp  == SIP_TRANSP_NONE) {\n\t\t\terr = EPROTONOSUPPORT;\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (!sip_transp_supported(sip, req->tp, AF_UNSPEC)) {\n\t\t\terr = EPROTONOSUPPORT;\n\t\t\tgoto out;\n\t\t}\n\n\t\treq->tp_selected = true;\n\t}\n\telse {\n\t\tif (!transp_first(sip, &req->tp)) {\n\t\t\terr = EPROTONOSUPPORT;\n\t\t\tgoto out;\n\t\t}\n\n\t\treq->tp_selected = false;\n\t}\n\nout:\n\tif (err)\n\t\tmem_deref(req);\n\telse if (reqp)\n\t\t*reqp = req;\n\n\treturn err;\n}\n\n\nstatic int sip_request_send(struct sip_request *req, struct sip *sip,\n\t\t\t    const struct uri *route)\n{\n\tstruct sa dst;\n\tint err;\n\tbool addr_only = req->tp_selected &&\n\t\tdnsc_getaddrinfo_only(req->sip->dnsc);\n\n\tif (!sa_set_str(&dst, req->host,\n\t\t\tsip_transp_port(req->tp, route->port))) {\n\n\t\terr = request(req, req->tp, &dst);\n\t\tif (!req->stateful) {\n\t\t\tmem_deref(req);\n\t\t\treturn err;\n\t\t}\n\t}\n\telse if (route->port || addr_only){\n\n\t\treq->port = sip_transp_port(req->tp, route->port);\n\t\terr = addr_lookup(req, req->host);\n\t}\n\telse if (req->tp_selected) {\n\n\t\terr = srv_lookup(req, req->host);\n\t}\n\telse {\n\t        err = dnsc_query(&req->dnsq, sip->dnsc, req->host,\n\t\t\t\t DNS_TYPE_NAPTR, DNS_CLASS_IN, true,\n\t\t\t\t naptr_handler, req);\n\t}\n\n\tif (err)\n\t\tmem_deref(req);\n\telse if (req->reqp)\n\t\t*req->reqp = req;\n\n\treturn err;\n}\n\n\n/**\n * Send a SIP request\n *\n * @param reqp     Pointer to allocated SIP request object\n * @param sip      SIP Stack\n * @param stateful Stateful client transaction\n * @param met      SIP Method string\n * @param metl     Length of SIP Method string\n * @param uri      Request URI\n * @param uril     Length of Request URI string\n * @param route    Next hop route URI\n * @param mb       Buffer containing SIP request\n * @param sortkey  Key for DNS record sorting\n * @param sendh    Send handler\n * @param resph    Response handler\n * @param arg      Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_request(struct sip_request **reqp, struct sip *sip, bool stateful,\n\t\tconst char *met, int metl, const char *uri, int uril,\n\t\tconst struct uri *route, struct mbuf *mb, size_t sortkey,\n\t\tsip_send_h *sendh, sip_resp_h *resph, void *arg)\n{\n\tstruct sip_request *req = NULL;\n\tint err;\n\n\tif (!sip || !met || !uri || !route || !mb)\n\t\treturn EINVAL;\n\n\terr = sip_request_alloc(&req, sip, stateful, met, metl, uri, uril,\n\t\t\t\troute, SIP_TRANSP_NONE, mb, sortkey, sendh,\n\t\t\t\tresph, arg);\n\tif (err)\n\t\tgoto out;\n\n\treq->reqp = reqp;\n\terr = sip_request_send(req, sip, route);\n out:\n\n\treturn err;\n}\n\n\n/**\n * Send a SIP request with formatted arguments\n *\n * @param reqp     Pointer to allocated SIP request object\n * @param sip      SIP Stack\n * @param stateful Stateful client transaction\n * @param met      Null-terminated SIP Method string\n * @param uri      Null-terminated Request URI string\n * @param route    Next hop route URI (optional)\n * @param auth     SIP authentication state\n * @param sendh    Send handler\n * @param resph    Response handler\n * @param arg      Handler argument\n * @param fmt      Formatted SIP headers and body\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_requestf(struct sip_request **reqp, struct sip *sip, bool stateful,\n\t\t const char *met, const char *uri, const struct uri *route,\n\t\t struct sip_auth *auth, sip_send_h *sendh, sip_resp_h *resph,\n\t\t void *arg, const char *fmt, ...)\n{\n\tstruct uri lroute;\n\tstruct mbuf *mb;\n\tva_list ap;\n\tint err;\n\n\tif (!sip || !met || !uri || !fmt)\n\t\treturn EINVAL;\n\n\tif (!route) {\n\t\tstruct pl uripl;\n\n\t\tpl_set_str(&uripl, uri);\n\n\t\terr = uri_decode(&lroute, &uripl);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\troute = &lroute;\n\t}\n\n\tmb = mbuf_alloc(2048);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = mbuf_write_str(mb, \"Max-Forwards: 70\\r\\n\");\n\n\tif (auth)\n\t\terr |= sip_auth_encode(mb, auth, met, uri);\n\n\tif (err)\n\t\tgoto out;\n\n\tva_start(ap, fmt);\n\terr = mbuf_vprintf(mb, fmt, ap);\n\tva_end(ap);\n\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\n\terr = sip_request(reqp, sip, stateful, met, -1, uri, -1, route, mb,\n\t\t\t  (size_t)arg, sendh, resph, arg);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/**\n * Send a SIP dialog request with formatted arguments\n *\n * @param reqp     Pointer to allocated SIP request object\n * @param sip      SIP Stack\n * @param stateful Stateful client transaction\n * @param met      Null-terminated SIP Method string\n * @param dlg      SIP Dialog state\n * @param cseq     CSeq number\n * @param auth     SIP authentication state\n * @param sendh    Send handler\n * @param resph    Response handler\n * @param arg      Handler argument\n * @param fmt      Formatted SIP headers and body\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_drequestf(struct sip_request **reqp, struct sip *sip, bool stateful,\n\t\t  const char *met, struct sip_dialog *dlg, uint32_t cseq,\n\t\t  struct sip_auth *auth, sip_send_h *sendh, sip_resp_h *resph,\n\t\t  void *arg, const char *fmt, ...)\n{\n\tstruct sip_request *req;\n\tstruct mbuf *mb;\n\tva_list ap;\n\tint err;\n\n\tif (!sip || !met || !dlg || !fmt)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(2048);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = mbuf_write_str(mb, \"Max-Forwards: 70\\r\\n\");\n\n\tif (auth)\n\t\terr |= sip_auth_encode(mb, auth, met, sip_dialog_uri(dlg));\n\n\terr |= sip_dialog_encode(mb, dlg, cseq, met);\n\n\tif (sip->software)\n\t\terr |= mbuf_printf(mb, \"User-Agent: %s\\r\\n\", sip->software);\n\n\tif (err)\n\t\tgoto out;\n\n\tva_start(ap, fmt);\n\terr = mbuf_vprintf(mb, fmt, ap);\n\tva_end(ap);\n\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\n\terr = sip_request_alloc(&req, sip, stateful, met, -1,\n\t\t\t\tsip_dialog_uri(dlg), -1, sip_dialog_route(dlg),\n\t\t\t\tsip_dialog_tp(dlg),\n\t\t\t\tmb, sip_dialog_hash(dlg), sendh, resph, arg);\n\tif (err)\n\t\tgoto out;\n\n\treq->reqp = reqp;\n\treq->srcport = sip_dialog_srcport(dlg);\n\terr = sip_request_send(req, sip, sip_dialog_route(dlg));\n\nout:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/**\n * Cancel a pending SIP Request\n *\n * @param req SIP Request\n */\nvoid sip_request_cancel(struct sip_request *req)\n{\n\tif (!req || req->canceled)\n\t\treturn;\n\n\treq->canceled = true;\n\n\tif (!req->provrecv) {\n\t\treq->ct = mem_deref(req->ct);\n\t\treturn;\n\t}\n\n\t(void)sip_ctrans_cancel(req->ct);\n}\n\n\n/**\n * Check if a provisional response was received for a SIP Request\n *\n * @param req SIP Request\n *\n * @return True if a provisional response was received, false otherwise\n */\nbool sip_request_provrecv(const struct sip_request *req)\n{\n\treturn req ? req->provrecv : false;\n}\n\n\nvoid sip_request_close(struct sip *sip)\n{\n\tif (!sip)\n\t\treturn;\n\n\tlist_apply(&sip->reql, true, close_handler, NULL);\n}\n\n\n/**\n * Check if a SIP request loops\n *\n * @param ls    Loop state\n * @param scode Status code from SIP response\n *\n * @return True if loops, otherwise false\n */\nbool sip_request_loops(struct sip_loopstate *ls, uint16_t scode)\n{\n\tbool loop = false;\n\n\tif (!ls)\n\t\treturn false;\n\n\tif (scode < 200) {\n\t\treturn false;\n\t}\n\telse if (scode < 300) {\n\t\tls->failc = 0;\n\t}\n\telse if (scode < 400) {\n\t\tloop = (++ls->failc >= 16);\n\t}\n\telse {\n\t\tswitch (scode) {\n\n\t\tdefault:\n\t\t\tif (ls->last_scode == scode)\n\t\t\t\tloop = true;\n\t\t\t/*@fallthrough@*/\n\t\tcase 401:\n\t\tcase 407:\n\t\tcase 491:\n\t\t\tif (++ls->failc >= 16)\n\t\t\t\tloop = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tls->last_scode = scode;\n\n\treturn loop;\n}\n\n\n/**\n * Reset the loop state\n *\n * @param ls Loop state\n */\nvoid sip_loopstate_reset(struct sip_loopstate *ls)\n{\n\tif (!ls)\n\t\treturn;\n\n\tls->last_scode = 0;\n\tls->failc = 0;\n}\n"
  },
  {
    "path": "src/sip/sip.c",
    "content": "/**\n * @file sip.c  SIP Core\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_sys.h>\n#include <re_tmr.h>\n#include <re_udp.h>\n#include <re_stun.h>\n#include <re_msg.h>\n#include <re_http.h>\n#include <re_websock.h>\n#include <re_sip.h>\n#include \"sip.h\"\n\n\nstatic void websock_shutdown_handler(void *arg)\n{\n\tstruct sip *sip = arg;\n\n\tif (sip->exith)\n\t\tsip->exith(sip->arg);\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sip *sip = arg;\n\n\tif (sip->closing) {\n\t\tsip->closing = false;\n\t\tmem_ref(sip);\n\n\t\tif (mem_nrefs(sip->websock) > 1) {\n\n\t\t\t/* NOTE: we must flush all connections here,\n\t\t\t   since they have a reference to websock */\n\t\t\thash_flush(sip->ht_conn);\n\t\t\tsip->ht_conn = mem_deref(sip->ht_conn);\n\n\n\t\t\twebsock_shutdown(sip->websock);\n\t\t}\n\t\telse {\n\t\t\tif (sip->exith)\n\t\t\t\tsip->exith(sip->arg);\n\n\t\t}\n\n\n\t\treturn;\n\t}\n\n\tsip_request_close(sip);\n\tsip_request_close(sip);\n\n\thash_flush(sip->ht_ctrans);\n\tmem_deref(sip->ht_ctrans);\n\n\thash_flush(sip->ht_strans);\n\thash_clear(sip->ht_strans_mrg);\n\tmem_deref(sip->ht_strans);\n\tmem_deref(sip->ht_strans_mrg);\n\n\thash_flush(sip->ht_conn);\n\tmem_deref(sip->ht_conn);\n\thash_flush(sip->ht_conncfg);\n\tmem_deref(sip->ht_conncfg);\n\n\thash_flush(sip->ht_udpconn);\n\tmem_deref(sip->ht_udpconn);\n\n\tlist_flush(&sip->transpl);\n\tlist_flush(&sip->lsnrl);\n\n\tmem_deref(sip->software);\n\tmem_deref(sip->dnsc);\n\tmem_deref(sip->stun);\n\n\tmem_deref(sip->websock);\n}\n\n\nstatic void lsnr_destructor(void *arg)\n{\n\tstruct sip_lsnr *lsnr = arg;\n\n\tif (lsnr->lsnrp)\n\t\t*lsnr->lsnrp = NULL;\n\n\tlist_unlink(&lsnr->le);\n}\n\n\n/**\n * Allocate a SIP stack instance\n *\n * @param sipp     Pointer to allocated SIP stack\n * @param dnsc     DNS Client (optional)\n * @param ctsz     Size of client transactions hashtable (power of 2)\n * @param stsz     Size of server transactions hashtable (power of 2)\n * @param tcsz     Size of SIP transport hashtable (power of 2)\n * @param software Software identifier\n * @param exith    SIP-stack exit handler\n * @param arg      Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_alloc(struct sip **sipp, struct dnsc *dnsc, uint32_t ctsz,\n\t      uint32_t stsz, uint32_t tcsz, const char *software,\n\t      sip_exit_h *exith, void *arg)\n{\n\tstruct sip *sip;\n\tint err;\n\n\tif (!sipp)\n\t\treturn EINVAL;\n\n\tsip = mem_zalloc(sizeof(*sip), destructor);\n\tif (!sip)\n\t\treturn ENOMEM;\n\n\tsip->tp_def = SIP_TRANSP_NONE;\n\terr = sip_transp_init(sip, tcsz);\n\tif (err)\n\t\tgoto out;\n\n\terr = sip_ctrans_init(sip, ctsz);\n\tif (err)\n\t\tgoto out;\n\n\terr = sip_strans_init(sip, stsz);\n\tif (err)\n\t\tgoto out;\n\n\terr = hash_alloc(&sip->ht_udpconn, tcsz);\n\tif (err)\n\t\tgoto out;\n\n\terr = stun_alloc(&sip->stun, NULL, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\tif (software) {\n\t\terr = str_dup(&sip->software, software);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\tsip->dnsc  = mem_ref(dnsc);\n\tsip->exith = exith;\n\tsip->arg   = arg;\n\n\terr = websock_alloc(&sip->websock, websock_shutdown_handler,\n\t\t\t    sip);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(sip);\n\telse\n\t\t*sipp = sip;\n\n\treturn err;\n}\n\n\n/**\n * Close the SIP stack instance\n *\n * @param sip   SIP stack instance\n * @param force Don't wait for transactions to complete\n */\nvoid sip_close(struct sip *sip, bool force)\n{\n\tif (!sip)\n\t\treturn;\n\n\tif (force) {\n\t\tsip_request_close(sip);\n\t\tsip_request_close(sip);\n\t}\n\telse if (!sip->closing) {\n\t\tsip->closing = true;\n\t\tmem_deref(sip);\n\t}\n}\n\n\n/**\n * Send a SIP message and use given SIP connected handler\n *\n * @param sip   SIP stack instance\n * @param sock  Optional socket to send from\n * @param tp    SIP transport\n * @param dst   Destination network address\n * @param host  Target hostname\n * @param mb    Buffer containing SIP message\n * @param connh SIP connected handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_send_conn(struct sip *sip, void *sock, enum sip_transp tp,\n\t\t  const struct sa *dst, char *host, struct mbuf *mb,\n\t\t  sip_conn_h *connh, void *arg)\n{\n\treturn sip_transp_send(NULL, sip, sock, tp, dst, host, mb, connh, NULL,\n\t\t\t       arg);\n}\n\n\n/**\n * Send a SIP message\n *\n * @param sip  SIP stack instance\n * @param sock Optional socket to send from\n * @param tp   SIP transport\n * @param dst  Destination network address\n * @param mb   Buffer containing SIP message\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_send(struct sip *sip, void *sock, enum sip_transp tp,\n\t     const struct sa *dst, struct mbuf *mb)\n{\n\treturn sip_transp_send(NULL, sip, sock, tp, dst, NULL, mb, NULL, NULL,\n\t\t\t       NULL);\n}\n\n\n/**\n * Listen for incoming SIP Requests and SIP Responses\n *\n * @param lsnrp Pointer to allocated listener\n * @param sip   SIP stack instance\n * @param req   True for Request, false for Response\n * @param msgh  SIP message handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_listen(struct sip_lsnr **lsnrp, struct sip *sip, bool req,\n\t       sip_msg_h *msgh, void *arg)\n{\n\tstruct sip_lsnr *lsnr;\n\n\tif (!sip || !msgh)\n\t\treturn EINVAL;\n\n\tlsnr = mem_zalloc(sizeof(*lsnr), lsnr_destructor);\n\tif (!lsnr)\n\t\treturn ENOMEM;\n\n\tlist_append(&sip->lsnrl, &lsnr->le, lsnr);\n\n\tlsnr->msgh = msgh;\n\tlsnr->arg = arg;\n\tlsnr->req = req;\n\n\tif (lsnrp) {\n\t\tlsnr->lsnrp = lsnrp;\n\t\t*lsnrp = lsnr;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Print debug information about the SIP stack\n *\n * @param pf  Print function for debug output\n * @param sip SIP stack instance\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_debug(struct re_printf *pf, const struct sip *sip)\n{\n\tint err;\n\n\tif (!sip)\n\t\treturn 0;\n\n\terr  = sip_transp_debug(pf, sip);\n\terr |= sip_ctrans_debug(pf, sip);\n\terr |= sip_strans_debug(pf, sip);\n\n\treturn err;\n}\n\n\nvoid sip_set_trace_handler(struct sip *sip, sip_trace_h *traceh)\n{\n\tif (!sip)\n\t\treturn;\n\n\tsip->traceh = traceh;\n}\n\n\nstruct sip_conncfg *sip_conncfg_find(struct sip *sip,\n\t\t\t\t     const struct sa *paddr)\n{\n\tstruct le *le;\n\n\tle = list_head(hash_list(sip->ht_conncfg, sa_hash(paddr, SA_ALL)));\n\tfor (; le; le = le->next) {\n\n\t\tstruct sip_conncfg *cfg = le->data;\n\n\t\tif (!sa_cmp(&cfg->paddr, paddr, SA_ALL))\n\t\t\tcontinue;\n\n\t\treturn cfg;\n\t}\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/sip/sip.h",
    "content": "/**\n * @file sip.h  SIP Private Interface\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nstruct sip {\n\tstruct list transpl;\n\tstruct list lsnrl;\n\tstruct list reql;\n\tstruct hash *ht_ctrans;\n\tstruct hash *ht_strans;\n\tstruct hash *ht_strans_mrg;\n\tstruct hash *ht_conn;\n\tstruct hash *ht_udpconn;\n\tstruct hash *ht_conncfg;\n\tstruct dnsc *dnsc;\n\tstruct stun *stun;\n\tstruct websock *websock;\n\tchar *software;\n\tsip_exit_h *exith;\n\tsip_trace_h *traceh;\n\tvoid *arg;\n\tbool closing;\n\tuint8_t tos;\n\tenum sip_transp tp_def;\n};\n\n\nstruct sip_lsnr {\n\tstruct le le;\n\tstruct sip_lsnr **lsnrp;\n\tsip_msg_h *msgh;\n\tvoid *arg;\n\tbool req;\n};\n\n\nstruct sip_keepalive {\n\tstruct le le;\n\tstruct sip_keepalive **kap;\n\tsip_keepalive_h *kah;\n\tvoid *arg;\n};\n\n\n/* request */\nvoid sip_request_close(struct sip *sip);\n\n\n/* ctrans */\nstruct sip_ctrans;\n\nint  sip_ctrans_request(struct sip_ctrans **ctp, struct sip *sip,\n\t\t\tenum sip_transp tp, const struct sa *dst, char *met,\n\t\t\tchar *branch, char *host, struct mbuf *mb,\n\t\t\tsip_conn_h *connh,\n\t\t\tsip_resp_h *resph, void *arg);\nint  sip_ctrans_cancel(struct sip_ctrans *ct);\nint  sip_ctrans_init(struct sip *sip, uint32_t sz);\nint  sip_ctrans_debug(struct re_printf *pf, const struct sip *sip);\n\n\n/* strans */\nint  sip_strans_init(struct sip *sip, uint32_t sz);\nint  sip_strans_debug(struct re_printf *pf, const struct sip *sip);\n\n\n/* transp */\nstruct sip_connqent;\n\ntypedef void(sip_transp_h)(int err, void *arg);\n\nint  sip_transp_init(struct sip *sip, uint32_t sz);\nint  sip_transp_send(struct sip_connqent **qentp, struct sip *sip, void *sock,\n\t\t     enum sip_transp tp, const struct sa *dst, char *host,\n\t\t     struct mbuf *mb, sip_conn_h *connh, sip_transp_h *transph,\n\t\t     void *arg);\nbool sip_transp_supported(struct sip *sip, enum sip_transp tp, int af);\nconst char *sip_transp_srvid(enum sip_transp tp);\nbool sip_transp_reliable(enum sip_transp tp);\nint  sip_transp_debug(struct re_printf *pf, const struct sip *sip);\n\n\n/* dialog */\nint  sip_dialog_encode(struct mbuf *mb, struct sip_dialog *dlg, uint32_t cseq,\n\t\t       const char *met);\nconst struct uri *sip_dialog_route(const struct sip_dialog *dlg);\nuint32_t sip_dialog_hash(const struct sip_dialog *dlg);\n\n\n/* keepalive */\nstruct sip_conn;\n\nvoid sip_keepalive_signal(struct list *kal, int err);\nuint64_t sip_keepalive_wait(uint32_t interval);\nint  sip_keepalive_tcp(struct sip_keepalive *ka, struct sip_conn *conn,\n\t\t       uint32_t interval);\nint  sip_keepalive_udp(struct sip_keepalive *ka, struct sip *sip,\n\t\t       struct udp_sock *us, const struct sa *paddr,\n\t\t       uint32_t interval);\n\n/* sip_conncfg */\nstruct sip_conncfg *sip_conncfg_find(struct sip *sip,\n\t\t\t\t     const struct sa *paddr);\n"
  },
  {
    "path": "src/sip/strans.c",
    "content": "/**\n * @file strans.c  SIP Server Transaction\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_sys.h>\n#include <re_tmr.h>\n#include <re_udp.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include \"sip.h\"\n\n\nenum state {\n\tTRYING,\n\tPROCEEDING,\n\tACCEPTED,\n\tCOMPLETED,\n\tCONFIRMED,\n};\n\n\nstruct sip_strans {\n\tstruct le he;\n\tstruct le he_mrg;\n\tstruct tmr tmr;\n\tstruct tmr tmrg;\n\tstruct sa dst;\n\tstruct sip *sip;\n\tstruct sip_msg *msg;\n\tstruct sip_msg *cancel_msg;\n\tstruct mbuf *mb;\n\tsip_cancel_h *cancelh;\n\tvoid *arg;\n\tenum state state;\n\tuint32_t txc;\n\tbool invite;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sip_strans *st = arg;\n\n\thash_unlink(&st->he);\n\thash_unlink(&st->he_mrg);\n\ttmr_cancel(&st->tmr);\n\ttmr_cancel(&st->tmrg);\n\tmem_deref(st->msg);\n\tmem_deref(st->cancel_msg);\n\tmem_deref(st->mb);\n}\n\nconst struct sip_msg *sip_strans_cancel_msg(struct sip_strans *st) {\n\tif (!st)\n\t\treturn NULL;\n\treturn st->cancel_msg;\n}\n\nstatic bool strans_cmp(const struct sip_msg *msg1, const struct sip_msg *msg2)\n{\n\tif (pl_cmp(&msg1->via.branch, &msg2->via.branch))\n\t\treturn false;\n\n\tif (pl_cmp(&msg1->via.sentby, &msg2->via.sentby))\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstatic bool cmp_handler(struct le *le, void *arg)\n{\n\tstruct sip_strans *st = le->data;\n\tconst struct sip_msg *msg = arg;\n\n\tif (!strans_cmp(st->msg, msg))\n\t\treturn false;\n\n\tif (pl_cmp(&st->msg->cseq.met, &msg->cseq.met))\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstatic bool cmp_ack_handler(struct le *le, void *arg)\n{\n\tstruct sip_strans *st = le->data;\n\tconst struct sip_msg *msg = arg;\n\n\tif (!strans_cmp(st->msg, msg))\n\t\treturn false;\n\n\tif (pl_strcmp(&st->msg->cseq.met, \"INVITE\"))\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstatic bool cmp_cancel_handler(struct le *le, void *arg)\n{\n\tstruct sip_strans *st = le->data;\n\tconst struct sip_msg *msg = arg;\n\n\tif (!strans_cmp(st->msg, msg))\n\t\treturn false;\n\n\tif (!pl_strcmp(&st->msg->cseq.met, \"CANCEL\"))\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstatic bool cmp_merge_handler(struct le *le, void *arg)\n{\n\tstruct sip_strans *st = le->data;\n\tconst struct sip_msg *msg = arg;\n\n\tif (pl_cmp(&st->msg->cseq.met, &msg->cseq.met))\n\t\treturn false;\n\n\tif (st->msg->cseq.num != msg->cseq.num)\n\t\treturn false;\n\n\tif (pl_cmp(&st->msg->callid, &msg->callid))\n\t\treturn false;\n\n\tif (pl_cmp(&st->msg->from.tag, &msg->from.tag))\n\t\treturn false;\n\n\tif (pl_cmp(&st->msg->ruri, &msg->ruri))\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstatic void dummy_handler(void *arg)\n{\n\t(void)arg;\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct sip_strans *st = arg;\n\n\tmem_deref(st);\n}\n\n\nstatic void retransmit_handler(void *arg)\n{\n\tstruct sip_strans *st = arg;\n\n\t(void)sip_send(st->sip, st->msg->sock, st->msg->tp, &st->dst,\n\t\t       st->mb);\n\n\tst->txc++;\n\ttmr_start(&st->tmrg, MIN(SIP_T1<<st->txc, SIP_T2), retransmit_handler,\n\t\t  st);\n}\n\n\nstatic bool ack_handler(struct sip *sip, const struct sip_msg *msg)\n{\n\tstruct sip_strans *st;\n\n\tst = list_ledata(hash_lookup(sip->ht_strans,\n\t\t\t\t     hash_joaat_pl(&msg->via.branch),\n\t\t\t\t     cmp_ack_handler, (void *)msg));\n\tif (!st)\n\t\treturn false;\n\n\tswitch (st->state) {\n\n\tcase ACCEPTED:\n\t\t/* make sure ACKs for 2xx are passed to TU */\n\t\treturn false;\n\n\tcase COMPLETED:\n\t\tif (sip_transp_reliable(st->msg->tp)) {\n\t\t\tmem_deref(st);\n\t\t\tbreak;\n\t\t}\n\n\t\ttmr_start(&st->tmr, SIP_T4, tmr_handler, st);\n\t\ttmr_cancel(&st->tmrg);\n\t\tst->state = CONFIRMED;\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n\n\nstatic bool cancel_handler(struct sip *sip, const struct sip_msg *msg)\n{\n\tstruct sip_strans *st;\n\n\tst = list_ledata(hash_lookup(sip->ht_strans,\n\t\t\t\t     hash_joaat_pl(&msg->via.branch),\n\t\t\t\t     cmp_cancel_handler, (void *)msg));\n\tif (!st)\n\t\treturn false;\n\n\t((struct sip_msg *)msg)->tag = st->msg->tag;\n\n\t(void)sip_reply(sip, msg, 200, \"OK\");\n\n\tswitch (st->state) {\n\n\tcase TRYING:\n\tcase PROCEEDING:\n\t\tmem_deref(st->cancel_msg);\n\t\tst->cancel_msg = mem_ref((void *)msg);\n\t\tst->cancelh(st->arg);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n\n\nstatic bool request_handler(const struct sip_msg *msg, void *arg)\n{\n\tstruct sip_strans *st;\n\tstruct sip *sip = arg;\n\n\tif (!pl_strcmp(&msg->met, \"ACK\"))\n\t\treturn ack_handler(sip, msg);\n\n\tst = list_ledata(hash_lookup(sip->ht_strans,\n\t\t\t\t     hash_joaat_pl(&msg->via.branch),\n\t\t\t\t     cmp_handler, (void *)msg));\n\tif (st) {\n\t\tswitch (st->state) {\n\n\t\tcase PROCEEDING:\n\t\tcase COMPLETED:\n\t\t\t(void)sip_send(st->sip, st->msg->sock, st->msg->tp,\n\t\t\t\t       &st->dst, st->mb);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\treturn true;\n\t}\n\telse if (!pl_isset(&msg->to.tag)) {\n\n\t\tst = list_ledata(hash_lookup(sip->ht_strans_mrg,\n\t\t\t\t\t     hash_joaat_pl(&msg->callid),\n\t\t\t\t\t     cmp_merge_handler, (void *)msg));\n\t\tif (st) {\n\t\t\t(void)sip_reply(sip, msg, 482, \"Loop Detected\");\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (!pl_strcmp(&msg->met, \"CANCEL\"))\n\t\treturn cancel_handler(sip, msg);\n\n\treturn false;\n}\n\n\n/**\n * Allocate a SIP Server Transaction\n *\n * @param stp     Pointer to allocated SIP Server Transaction\n * @param sip     SIP Stack instance\n * @param msg     Incoming SIP message\n * @param cancelh Cancel handler\n * @param arg     Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_strans_alloc(struct sip_strans **stp, struct sip *sip,\n\t\t     const struct sip_msg *msg, sip_cancel_h *cancelh,\n\t\t     void *arg)\n{\n\tstruct sip_strans *st;\n\n\tif (!stp || !sip || !msg)\n\t\treturn EINVAL;\n\n\tst = mem_zalloc(sizeof(*st), destructor);\n\tif (!st)\n\t\treturn ENOMEM;\n\n\thash_append(sip->ht_strans, hash_joaat_pl(&msg->via.branch),\n\t\t    &st->he, st);\n\n\thash_append(sip->ht_strans_mrg, hash_joaat_pl(&msg->callid),\n\t\t    &st->he_mrg, st);\n\n\tst->invite  = !pl_strcmp(&msg->met, \"INVITE\");\n\tst->msg     = mem_ref((void *)msg);\n\tst->state   = TRYING;\n\tst->cancelh = cancelh ? cancelh : dummy_handler;\n\tst->arg     = arg;\n\tst->sip     = sip;\n\n\t*stp = st;\n\n\treturn 0;\n}\n\n\n/**\n * Reply using a SIP Server Transaction\n *\n * @param stp   Pointer to allocated SIP Server Transaction\n * @param sip   SIP Stack instance\n * @param msg   Incoming SIP message\n * @param dst   Destination network address\n * @param scode Response status code\n * @param mb    Buffer containing SIP response\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_strans_reply(struct sip_strans **stp, struct sip *sip,\n\t\t     const struct sip_msg *msg, const struct sa *dst,\n\t\t     uint16_t scode, struct mbuf *mb)\n{\n\tstruct sip_strans *st = NULL;\n\tint err;\n\n\tif (!sip || !mb || !dst || (scode < 200 && !stp))\n\t\treturn EINVAL;\n\n\tif (stp)\n\t\tst = *stp;\n\n\tif (!st) {\n\t\terr = sip_strans_alloc(&st, sip, msg, NULL, NULL);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tmem_deref(st->mb);\n\tst->mb = mem_ref(mb);\n\tst->dst = *dst;\n\n\terr = sip_send(sip, st->msg->sock, st->msg->tp, dst, mb);\n\n\tif (stp)\n\t\t*stp = (err || scode >= 200) ? NULL : st;\n\n\tif (err) {\n\t\tmem_deref(st);\n\t\treturn err;\n\t}\n\n\tif (st->invite) {\n\t\tif (scode < 200) {\n\t\t\tst->state = PROCEEDING;\n\t\t}\n\t\telse if (scode < 300) {\n\t\t\ttmr_start(&st->tmr, 64 * SIP_T1, tmr_handler, st);\n\t\t\tst->state = ACCEPTED;\n\t\t}\n\t\telse {\n\t\t\ttmr_start(&st->tmr, 64 * SIP_T1, tmr_handler, st);\n\t\t\tst->state = COMPLETED;\n\n\t\t\tif (!sip_transp_reliable(st->msg->tp))\n\t\t\t\ttmr_start(&st->tmrg, SIP_T1,\n\t\t\t\t\t  retransmit_handler, st);\n\t\t}\n\t}\n\telse {\n\t\tif (scode < 200) {\n\t\t\tst->state = PROCEEDING;\n\t\t}\n\t\telse {\n\t\t\tif (!sip_transp_reliable(st->msg->tp)) {\n\t\t\t\ttmr_start(&st->tmr, 64 * SIP_T1, tmr_handler,\n\t\t\t\t\t  st);\n\t\t\t\tst->state = COMPLETED;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tmem_deref(st);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\nint sip_strans_init(struct sip *sip, uint32_t sz)\n{\n\tint err;\n\n\terr = sip_listen(NULL, sip, true, request_handler, sip);\n\tif (err)\n\t\treturn err;\n\n\terr = hash_alloc(&sip->ht_strans_mrg, sz);\n\tif (err)\n\t\treturn err;\n\n\treturn hash_alloc(&sip->ht_strans, sz);\n}\n\n\nstatic const char *statename(enum state state)\n{\n\tswitch (state) {\n\n\tcase TRYING:     return \"TRYING\";\n\tcase PROCEEDING: return \"PROCEEDING\";\n\tcase ACCEPTED:   return \"ACCEPTED\";\n\tcase COMPLETED:  return \"COMPLETED\";\n\tcase CONFIRMED:  return \"CONFIRMED\";\n\tdefault:         return \"???\";\n\t}\n}\n\n\nstatic bool debug_handler(struct le *le, void *arg)\n{\n\tstruct sip_strans *st = le->data;\n\tstruct re_printf *pf = arg;\n\n\t(void)re_hprintf(pf, \"  %-10r %-10s %2llus (%r)\\n\",\n\t\t\t &st->msg->met,\n\t\t\t statename(st->state),\n\t\t\t tmr_get_expire(&st->tmr)/1000,\n\t\t\t &st->msg->via.branch);\n\n\treturn false;\n}\n\n\nint sip_strans_debug(struct re_printf *pf, const struct sip *sip)\n{\n\tint err;\n\n\terr = re_hprintf(pf, \"server transactions:\\n\");\n\thash_apply(sip->ht_strans, debug_handler, pf);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sip/transp.c",
    "content": "/**\n * @file sip/transp.c  SIP Transport\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_sys.h>\n#include <re_tmr.h>\n#include <re_udp.h>\n#include <re_stun.h>\n#include <re_srtp.h>\n#include <re_tcp.h>\n#include <re_tls.h>\n#include <re_msg.h>\n#include <re_http.h>\n#include <re_websock.h>\n#include <re_sip.h>\n#include <re_net.h>\n#include \"sip.h\"\n\n\n#define DEBUG_MODULE \"transp\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tTCP_ACCEPT_TIMEOUT    = 32,\n\tTCP_IDLE_TIMEOUT      = 900,\n\tTCP_KEEPALIVE_TIMEOUT = 10,\n\tTCP_KEEPALIVE_INTVAL  = 120,\n\tTCP_BUFSIZE_MAX       = 65536,\n};\n\n\nstruct sip_ccert {\n\tstruct le he;\n\tstruct pl file;\n};\n\n\nstruct sip_ccert_data {\n\tuint32_t hsup;\n\tstruct sip_ccert *ccert;\n};\n\n\nstruct sip_transport {\n\tstruct le le;\n\tstruct sa laddr;\n\tstruct sip *sip;\n\tstruct hash *ht_ccert;\n\tstruct tls *tls;\n\tvoid *sock;\n\tenum sip_transp tp;\n\tuint8_t tos;\n\n\tstruct http_cli *http_cli;\n\tstruct http_sock *http_sock;\n};\n\n\nstruct sip_conn {\n\tstruct le he;\n\tstruct list ql;\n\tstruct list kal;\n\tstruct tmr tmr;\n\tstruct tmr tmr_ka;\n\tstruct sa laddr;\n\tstruct sa paddr;\n\tstruct tls_conn *sc;\n\tstruct tcp_conn *tc;\n\tstruct mbuf *mb;\n\tstruct sip *sip;\n\tuint32_t ka_interval;\n\tbool established;\n\n\tenum sip_transp tp;\n\tstruct websock_conn *websock_conn;\n};\n\n\nstruct sip_connqent {\n\tstruct le le;\n\tstruct mbuf *mb;\n\tstruct sip_connqent **qentp;\n\tsip_transp_h *transph;\n\tvoid *arg;\n};\n\n\nstatic uint8_t crlfcrlf[4] = {0x0d, 0x0a, 0x0d, 0x0a};\n\n\nstatic void internal_transport_handler(int err, void *arg)\n{\n\t(void)err;\n\t(void)arg;\n}\n\n\nstatic void transp_destructor(void *arg)\n{\n\tstruct sip_transport *transp = arg;\n\n\tif (transp->tp == SIP_TRANSP_UDP)\n\t\tudp_handler_set(transp->sock, NULL, NULL);\n\n\tlist_unlink(&transp->le);\n\thash_flush(transp->ht_ccert);\n\tmem_deref(transp->ht_ccert);\n\tmem_deref(transp->sock);\n\tmem_deref(transp->tls);\n\tmem_deref(transp->http_cli);\n\tmem_deref(transp->http_sock);\n}\n\n\nstatic void conn_destructor(void *arg)\n{\n\tstruct sip_conn *conn = arg;\n\n\ttmr_cancel(&conn->tmr_ka);\n\ttmr_cancel(&conn->tmr);\n\tlist_flush(&conn->kal);\n\tlist_flush(&conn->ql);\n\thash_unlink(&conn->he);\n\tmem_deref(conn->sc);\n\tmem_deref(conn->tc);\n\tmem_deref(conn->mb);\n\tmem_deref(conn->websock_conn);\n}\n\n\nstatic void qent_destructor(void *arg)\n{\n\tstruct sip_connqent *qent = arg;\n\n\tif (qent->qentp)\n\t\t*qent->qentp = NULL;\n\n\tlist_unlink(&qent->le);\n\tmem_deref(qent->mb);\n}\n\n\nstatic const struct sip_transport *transp_find(struct sip *sip,\n\t\t\t\t\t       enum sip_transp tp,\n\t\t\t\t\t       int af, const struct sa *dst)\n{\n\tstruct le *le;\n\tstruct sa dsttmp;\n\tconst struct sip_transport *fb = NULL;\n\n\tfor (le = sip->transpl.head; le; le = le->next) {\n\n\t\tconst struct sip_transport *transp = le->data;\n\t\tconst struct sa *laddr = &transp->laddr;\n\t\tstruct sa src;\n\n\t\tif (transp->tp != tp)\n\t\t\tcontinue;\n\n\t\tif (af != AF_UNSPEC && sa_af(laddr) != af)\n\t\t\tcontinue;\n\n\t\tif (!sa_isset(dst, SA_ADDR))\n\t\t\treturn transp;\n\n\t\tif (sa_is_linklocal(laddr) != sa_is_linklocal(dst))\n\t\t\tcontinue;\n\n\t\tif (!fb)\n\t\t\tfb = transp;\n\n\t\tsa_cpy(&dsttmp, dst);\n\t\tsa_set_scopeid(&dsttmp, sa_scopeid(laddr));\n\n\t\tif (net_dst_source_addr_get(&dsttmp, &src))\n\t\t\tcontinue;\n\n\t\tif (!sa_cmp(&src, laddr, SA_ADDR))\n\t\t\tcontinue;\n\n\t\treturn transp;\n\t}\n\n\treturn fb;\n}\n\n\nstatic struct le *transp_apply_all(struct sip *sip, enum sip_transp tp, int af,\n\t\t\t\t   list_apply_h ah, void *arg)\n{\n\tif (!ah)\n\t\treturn NULL;\n\n\tfor (struct le *le = sip->transpl.head; le; le = le->next) {\n\n\t\tconst struct sip_transport *transp = le->data;\n\t\tconst struct sa *laddr = &transp->laddr;\n\n\t\tif (transp->tp != tp)\n\t\t\tcontinue;\n\n\t\tif (af != AF_UNSPEC && sa_af(laddr) != af)\n\t\t\tcontinue;\n\n\t\tif (ah(le, arg))\n\t\t\treturn le;\n\t}\n\n\treturn NULL;\n}\n\n\nstatic struct sip_conn *conn_find(struct sip *sip, const struct sa *paddr,\n\t\t\t\t  bool secure)\n{\n\tstruct le *le;\n\n\tle = list_head(hash_list(sip->ht_conn, sa_hash(paddr, SA_ALL)));\n\n\tfor (; le; le = le->next) {\n\n\t\tstruct sip_conn *conn = le->data;\n\n\t\tif (secure && !conn->sc)\n\t\t\tcontinue;\n\n\t\tif (!secure && conn->sc)\n\t\t\tcontinue;\n\n\t\tif (!sa_cmp(&conn->paddr, paddr, SA_ALL))\n\t\t\tcontinue;\n\n\t\treturn conn;\n\t}\n\n\treturn NULL;\n}\n\n\nstatic struct sip_conn *ws_conn_find(struct sip *sip, const struct sa *paddr,\n\t\t\t\t     enum sip_transp tp)\n{\n\tstruct le *le;\n\t(void) tp;\n\n\tle = list_head(hash_list(sip->ht_conn, sa_hash(paddr, SA_ALL)));\n\n\tfor (; le; le = le->next) {\n\n\t\tstruct sip_conn *conn = le->data;\n\n\t\t/* if (tp != conn->tp)\n\t\t   continue; */\n\n\t\tif (!sa_cmp(&conn->paddr, paddr, SA_ALL))\n\t\t\tcontinue;\n\n\t\treturn conn;\n\t}\n\n\treturn NULL;\n}\n\n\nstatic void conn_close(struct sip_conn *conn, int err)\n{\n\tstruct le *le;\n\n\tconn->websock_conn = mem_deref(conn->websock_conn);\n\n\tconn->sc = mem_deref(conn->sc);\n\tconn->tc = mem_deref(conn->tc);\n\ttmr_cancel(&conn->tmr_ka);\n\ttmr_cancel(&conn->tmr);\n\thash_unlink(&conn->he);\n\n\tle = list_head(&conn->ql);\n\n\twhile (le) {\n\n\t\tstruct sip_connqent *qent = le->data;\n\t\tle = le->next;\n\t\tbool qentp_set = qent->qentp ? true : false;\n\n\t\tqent->transph(err, qent->arg);\n\n\t\tif (!qentp_set) {\n\t\t\tlist_unlink(&qent->le);\n\t\t\tmem_deref(qent);\n\t\t}\n\t}\n\n\tsip_keepalive_signal(&conn->kal, err);\n}\n\n\nstatic void conn_tmr_handler(void *arg)\n{\n\tstruct sip_conn *conn = arg;\n\n\tconn_close(conn, ETIMEDOUT);\n\tmem_deref(conn);\n}\n\n\nstatic void conn_keepalive_handler(void *arg)\n{\n\tstruct sip_conn *conn = arg;\n\tstruct mbuf mb;\n\tint err;\n\n\tmb.buf  = crlfcrlf;\n\tmb.size = sizeof(crlfcrlf);\n\tmb.pos  = 0;\n\tmb.end  = 4;\n\n\terr = tcp_send(conn->tc, &mb);\n\tif (err) {\n\t\tconn_close(conn, err);\n\t\tmem_deref(conn);\n\t\treturn;\n\t}\n\n\ttmr_start(&conn->tmr, TCP_KEEPALIVE_TIMEOUT * 1000,\n\t\t  conn_tmr_handler, conn);\n\ttmr_start(&conn->tmr_ka, sip_keepalive_wait(conn->ka_interval),\n\t\t  conn_keepalive_handler, conn);\n}\n\nstatic bool have_essential_fields(const struct sip_msg *msg)\n{\n\tif (pl_isset(&(msg->to.auri)) &&\n\t\tpl_isset(&(msg->from.auri)) &&\n\t\tpl_isset(&(msg->cseq.met)) &&\n\t\tpl_isset(&(msg->callid)) &&\n\t\tpl_isset(&(msg->via.branch)))\n\t\treturn true;\n\n\treturn false;\n}\n\nstatic void sip_recv(struct sip *sip, const struct sip_msg *msg,\n\t\t     size_t start)\n{\n\tstruct le *le = sip->lsnrl.head;\n\n\tif (sip->traceh) {\n\t\tsip->traceh(false, msg->tp, &msg->src, &msg->dst,\n\t\t\t    msg->mb->buf + start, msg->mb->end - start,\n\t\t\t    sip->arg);\n\t}\n\n\tif (msg->req) {\n\t\tif (!have_essential_fields(msg)){\n\t\t\t(void)sip_reply(sip, msg, 400, \"Bad Request\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/* check consistency between CSeq method and that of request line */\n\tif (msg->req && pl_casecmp(&(msg->cseq.met), &(msg->met))){\n\t\t(void)sip_reply(sip, msg, 400, \"Bad Request\");\n\t\treturn;\n\t}\n\n\twhile (le) {\n\t\tstruct sip_lsnr *lsnr = le->data;\n\n\t\tle = le->next;\n\n\t\tif (msg->req != lsnr->req)\n\t\t\tcontinue;\n\n\t\tif (lsnr->msgh(msg, lsnr->arg))\n\t\t\treturn;\n\t}\n\n\tif (msg->req) {\n\t\t(void)re_fprintf(stderr, \"unhandled request from %J: %r %r\\n\",\n\t\t\t\t &msg->src, &msg->met, &msg->ruri);\n\n\t\tif (!pl_strcmp(&msg->met, \"CANCEL\"))\n\t\t\t(void)sip_reply(sip, msg,\n\t\t\t\t\t481, \"Transaction Does Not Exist\");\n\t\telse\n\t\t\t(void)sip_reply(sip, msg,\n\t\t\t\t\t501, \"Not Implemented\");\n\t}\n\telse {\n\t\t(void)re_fprintf(stderr, \"unhandled response from %J:\"\n\t\t\t\t \" %u %r (%r)\\n\", &msg->src,\n\t\t\t\t msg->scode, &msg->reason, &msg->cseq.met);\n\t}\n}\n\n\nstatic void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct sip_transport *transp = arg;\n\tstruct stun_unknown_attr ua;\n\tstruct stun_msg *stun_msg;\n\tstruct sip_msg *msg;\n\tint err;\n\n\tif (mb->end <= 4)\n\t\treturn;\n\n\tif (!stun_msg_decode(&stun_msg, mb, &ua)) {\n\n\t\tif (stun_msg_method(stun_msg) == STUN_METHOD_BINDING) {\n\n\t\t\tswitch (stun_msg_class(stun_msg)) {\n\n\t\t\tcase STUN_CLASS_REQUEST:\n\t\t\t\t(void)stun_reply(IPPROTO_UDP, transp->sock,\n\t\t\t\t\t\t src, 0, stun_msg,\n\t\t\t\t\t\t NULL, 0, false, 2,\n\t\t\t\t\t\t STUN_ATTR_XOR_MAPPED_ADDR,\n\t\t\t\t\t\t src,\n\t\t\t\t\t\t STUN_ATTR_SOFTWARE,\n\t\t\t\t\t\t transp->sip->software);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\t(void)stun_ctrans_recv(transp->sip->stun,\n\t\t\t\t\t\t       stun_msg, &ua);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tmem_deref(stun_msg);\n\n\t\treturn;\n\t}\n\n\terr = sip_msg_decode(&msg, mb);\n\tif (err) {\n\t\t(void)re_fprintf(stderr, \"sip: msg decode err: %m\\n\", err);\n\t\treturn;\n\t}\n\n\tmsg->sock = mem_ref(transp->sock);\n\tmsg->src = *src;\n\tmsg->dst = transp->laddr;\n\tmsg->tp = SIP_TRANSP_UDP;\n\tsa_set_scopeid(&msg->src, sa_scopeid(&transp->laddr));\n\n\tsip_recv(transp->sip, msg, 0);\n\n\tmem_deref(msg);\n}\n\n\nstatic void tcp_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct sip_conn *conn = arg;\n\tsize_t pos;\n\tint err = 0;\n\n\tif (conn->mb) {\n\t\tpos = conn->mb->pos;\n\n\t\tconn->mb->pos = conn->mb->end;\n\n\t\terr = mbuf_write_mem(conn->mb, mbuf_buf(mb),mbuf_get_left(mb));\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tconn->mb->pos = pos;\n\n\t\tif (mbuf_get_left(conn->mb) > TCP_BUFSIZE_MAX) {\n\t\t\terr = EOVERFLOW;\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse {\n\t\tconn->mb = mem_ref(mb);\n\t}\n\n\tfor (;;) {\n\t\tstruct sip_msg *msg;\n\t\tuint32_t clen;\n\t\tsize_t end;\n\n\t\tif (mbuf_get_left(conn->mb) < 2)\n\t\t\tbreak;\n\n\t\tif (!memcmp(mbuf_buf(conn->mb), \"\\r\\n\", 2)) {\n\n\t\t\ttmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000,\n\t\t\t\t  conn_tmr_handler, conn);\n\n\t\t\tconn->mb->pos += 2;\n\n\t\t\tif (mbuf_get_left(conn->mb) >= 2 &&\n\t\t\t    !memcmp(mbuf_buf(conn->mb), \"\\r\\n\", 2)) {\n\n\t\t\t\tstruct mbuf mbr;\n\n\t\t\t\tconn->mb->pos += 2;\n\n\t\t\t\tmbr.buf  = crlfcrlf;\n\t\t\t\tmbr.size = sizeof(crlfcrlf);\n\t\t\t\tmbr.pos  = 0;\n\t\t\t\tmbr.end  = 2;\n\n\t\t\t\terr = tcp_send(conn->tc, &mbr);\n\t\t\t\tif (err)\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (mbuf_get_left(conn->mb))\n\t\t\t\tcontinue;\n\n\t\t\tconn->mb = mem_deref(conn->mb);\n\t\t\tbreak;\n\t\t}\n\n\t\tpos = conn->mb->pos;\n\n\t\terr = sip_msg_decode(&msg, conn->mb);\n\t\tif (err) {\n\t\t\tif (err == ENODATA)\n\t\t\t\terr = 0;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!msg->clen.p) {\n\t\t\tmem_deref(msg);\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\tclen = pl_u32(&msg->clen);\n\n\t\tif (mbuf_get_left(conn->mb) < clen) {\n\t\t\tconn->mb->pos = pos;\n\t\t\tmem_deref(msg);\n\t\t\tbreak;\n\t\t}\n\n\t\ttmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000,\n\t\t\t  conn_tmr_handler, conn);\n\n\t\tend = conn->mb->end;\n\n\t\tmsg->mb->end = msg->mb->pos + clen;\n\t\tmsg->sock = mem_ref(conn);\n\t\tmsg->src = conn->paddr;\n\t\tmsg->dst = conn->laddr;\n\t\tmsg->tp = conn->sc ? SIP_TRANSP_TLS : SIP_TRANSP_TCP;\n\n\t\tsip_recv(conn->sip, msg, 0);\n\t\tmem_deref(msg);\n\n\t\tif (end <= conn->mb->end) {\n\t\t\tconn->mb = mem_deref(conn->mb);\n\t\t\tbreak;\n\t\t}\n\n\t\tmb = mbuf_alloc(end - conn->mb->end);\n\t\tif (!mb) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\t(void)mbuf_write_mem(mb, &conn->mb->buf[conn->mb->end],\n\t\t\t\t     end - conn->mb->end);\n\n\t\tmb->pos = 0;\n\n\t\tmem_deref(conn->mb);\n\t\tconn->mb = mb;\n\t}\n\n out:\n\tif (err) {\n\t\tconn_close(conn, err);\n\t\tmem_deref(conn);\n\t}\n}\n\n\nstatic void trace_send(struct sip *sip, enum sip_transp tp,\n\t\t       void *sock,\n\t\t       const struct sa *dst, struct mbuf *mb)\n{\n\tstruct sa src;\n\tstruct sip_conn *conn;\n\n\tif (sip->traceh) {\n\n\t\tswitch (tp) {\n\n\t\tcase SIP_TRANSP_UDP:\n\n\t\t\tif (udp_local_get(sock, &src))\n\t\t\t\tsa_init(&src, sa_af(dst));\n\n\t\t\tbreak;\n\n\t\tcase SIP_TRANSP_TCP:\n\t\tcase SIP_TRANSP_TLS:\n\t\tcase SIP_TRANSP_WS:\n\t\tcase SIP_TRANSP_WSS:\n\t\t\tconn = sock;\n\t\t\tsrc = conn->laddr;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\treturn;\n\t\t}\n\n\t\tsip->traceh(true, tp, &src, dst,\n\t\t\t    mbuf_buf(mb), mbuf_get_left(mb),\n\t\t\t    sip->arg);\n\t}\n}\n\n\nstatic void tcp_estab_handler(void *arg)\n{\n\tstruct sip_conn *conn = arg;\n\tstruct le *le;\n\tint err;\n\n#ifdef WIN32\n\ttcp_conn_local_get(conn->tc, &conn->laddr);\n#endif\n\n\tconn->established = true;\n\n\tle = list_head(&conn->ql);\n\n\twhile (le) {\n\n\t\tstruct sip_connqent *qent = le->data;\n\t\tbool qentp_set = qent->qentp ? true : false;\n\t\tle = le->next;\n\n\t\ttrace_send(conn->sip,\n\t\t\t   conn->sc ? SIP_TRANSP_TLS : SIP_TRANSP_TCP,\n\t\t\t   conn,\n\t\t\t   &conn->paddr, qent->mb);\n\n\t\terr = tcp_send(conn->tc, qent->mb);\n\t\tif (err)\n\t\t\tqent->transph(err, qent->arg);\n\n\t\tif (!qentp_set) {\n\t\t\tlist_unlink(&qent->le);\n\t\t\tmem_deref(qent);\n\t\t}\n\t}\n}\n\n\nstatic void tcp_close_handler(int err, void *arg)\n{\n\tstruct sip_conn *conn = arg;\n\n\tconn_close(conn, err ? err : ECONNRESET);\n\tmem_deref(conn);\n}\n\n\nstatic void tcp_connect_handler(const struct sa *paddr, void *arg)\n{\n\tstruct sip_transport *transp = arg;\n\tstruct sip_conn *conn;\n\tint err;\n\n\tconn = mem_zalloc(sizeof(*conn), conn_destructor);\n\tif (!conn) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\thash_append(transp->sip->ht_conn, sa_hash(paddr, SA_ALL),\n\t\t    &conn->he, conn);\n\n\tconn->paddr = *paddr;\n\tconn->sip   = transp->sip;\n\n\terr = tcp_accept(&conn->tc, transp->sock, tcp_estab_handler,\n\t\t\t tcp_recv_handler, tcp_close_handler, conn);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_conn_local_get(conn->tc, &conn->laddr);\n\tif (err)\n\t\tgoto out;\n\n\t(void)tcp_conn_settos(conn->tc, transp->tos);\n#ifdef USE_TLS\n\tif (transp->tls) {\n\t\terr = tls_start_tcp(&conn->sc, transp->tls, conn->tc, 0);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = tls_verify_client(conn->sc);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n#endif\n\n\tconn->tp = transp->tls ? SIP_TRANSP_TLS : SIP_TRANSP_TCP;\n\n\ttmr_start(&conn->tmr, TCP_ACCEPT_TIMEOUT * 1000,\n\t\t  conn_tmr_handler, conn);\n\n out:\n\tif (err) {\n\t\ttcp_reject(transp->sock);\n\t\tmem_deref(conn);\n\t}\n}\n\n#ifdef USE_TLS\nstatic uint32_t get_hash_of_fromhdr(struct mbuf *mb)\n{\n\tstruct sip_msg *msg;\n\tstruct mbuf *sup = NULL;\n\tuint32_t hsup = 0;\n\tint err = 0;\n\n\terr = sip_msg_decode(&msg, mb);\n\tif (err)\n\t\treturn 0;\n\n\tsup = mbuf_alloc(30);\n\tif (!sup)\n\t\treturn ENOMEM;\n\n\terr = mbuf_printf(sup, \"\\\"%r\\\" <%r:%r@%r:%d>\", &msg->from.uri.user,\n\t\t&msg->from.uri.scheme, &msg->from.uri.user,\n\t\t&msg->from.uri.host, msg->from.uri.port);\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(sup, 0);\n\thsup = hash_joaat(mbuf_buf(sup), mbuf_get_left(sup));\n\tmbuf_set_pos(mb, 0);\n\n out:\n\tmem_deref(msg);\n\tmem_deref(sup);\n\n\treturn hsup;\n}\n#endif\n\n\nstatic int conn_send(struct sip_connqent **qentp, struct sip *sip, bool secure,\n\t\t     const struct sa *dst, char *host, struct mbuf *mb,\n\t\t     sip_conn_h *connh, sip_transp_h *transph, void *arg)\n{\n\tstruct sip_conn *conn, *new_conn = NULL;\n\tstruct sip_conncfg *conncfg;\n\tstruct sip_connqent *qent;\n\tint err = 0;\n\n#ifndef USE_TLS\n\t(void) host;\n#endif\n\n\tconn = conn_find(sip, dst, secure);\n\tif (conn) {\n\t\tif (connh)\n\t\t\terr = connh(&conn->laddr, dst, mb, arg);\n\n\t\tif (!conn->established)\n\t\t\tgoto enqueue;\n\n\t\ttrace_send(sip,\n\t\t\t   secure ? SIP_TRANSP_TLS : SIP_TRANSP_TCP,\n\t\t\t   conn,\n\t\t\t   dst, mb);\n\n\t\treturn tcp_send(conn->tc, mb);\n\t}\n\n\tnew_conn = conn = mem_zalloc(sizeof(*conn), conn_destructor);\n\tif (!conn)\n\t\treturn ENOMEM;\n\n\thash_append(sip->ht_conn, sa_hash(dst, SA_ALL), &conn->he, conn);\n\tconn->paddr = *dst;\n\tconn->sip   = sip;\n\tconn->tp    = secure ? SIP_TRANSP_TLS : SIP_TRANSP_TCP;\n\n\tconncfg = sip_conncfg_find(sip, dst);\n\tif (conncfg && conncfg->srcport) {\n\t\tstruct sa src;\n\t\tsa_init(&src, sa_af(dst));\n\t\tsa_set_port(&src, conncfg->srcport);\n\t\terr = tcp_connect_bind(&conn->tc, dst,\n\t\t\t\t       tcp_estab_handler, tcp_recv_handler,\n\t\t\t\t       tcp_close_handler, &src, conn);\n\t}\n\telse {\n\t\terr = tcp_connect(&conn->tc, dst,\n\t\t\t\t  tcp_estab_handler, tcp_recv_handler,\n\t\t\t\t  tcp_close_handler, conn);\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_conn_local_get(conn->tc, &conn->laddr);\n\tif (err)\n\t\tgoto out;\n\n\t/* Fallback check for any address win32 */\n\tif (!sa_isset(&conn->laddr, SA_ALL)) {\n\t\tuint16_t port = sa_port(&conn->laddr);\n\t\terr = sip_transp_laddr(sip, &conn->laddr, conn->tp, dst);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (port)\n\t\t\tsa_set_port(&conn->laddr, port);\n\t}\n\n\tif (connh) {\n\t\terr = connh(&conn->laddr, dst, mb, arg);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\t(void)tcp_conn_settos(conn->tc, sip->tos);\n#ifdef USE_TLS\n\tif (secure) {\n\t\tconst struct sip_transport *transp;\n\t\tstruct sip_ccert *ccert;\n\t\tuint32_t hash = 0;\n\n\t\ttransp = transp_find(sip, SIP_TRANSP_TLS, sa_af(dst), dst);\n\t\tif (!transp || !transp->tls) {\n\t\t\terr = EPROTONOSUPPORT;\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = tls_start_tcp(&conn->sc, transp->tls, conn->tc, 0);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\thash = get_hash_of_fromhdr(mb);\n\t\tccert = list_ledata(\n\t\t\t\tlist_head(hash_list(transp->ht_ccert, hash)));\n\t\tif (ccert) {\n\t\t\tchar *f;\n\t\t\terr = pl_strdup(&f, &ccert->file);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n\t\t\terr = tls_conn_change_cert(conn->sc, f);\n\t\t\tmem_deref(f);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\n\t\terr |= tls_set_verify_server(conn->sc, host);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n#endif\n\n\ttmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000, conn_tmr_handler, conn);\n\n enqueue:\n\tqent = mem_zalloc(sizeof(*qent), qent_destructor);\n\tif (!qent) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\n\t}\n\n\tlist_append(&conn->ql, &qent->le, qent);\n\tqent->mb = mem_ref(mb);\n\tqent->transph = transph ? transph : internal_transport_handler;\n\tqent->arg = arg;\n\n\tif (qentp) {\n\t\tqent->qentp = qentp;\n\t\t*qentp = qent;\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(new_conn);\n\n\treturn err;\n}\n\n\nstatic void websock_estab_handler(void *arg)\n{\n\tstruct sip_conn *conn = arg;\n\tstruct le *le;\n\tint err;\n\n\tre_printf(\"<%p> %s websock established to %J\\n\",\n\t\t  conn, sip_transp_name(conn->tp), &conn->paddr);\n\n\tconn->established = true;\n\n\terr = tcp_conn_local_get(websock_tcp(conn->websock_conn),\n\t\t\t\t &conn->laddr);\n\tif (err)\n\t\treturn;\n\n\tle = list_head(&conn->ql);\n\n\twhile (le) {\n\n\t\tstruct sip_connqent *qent = le->data;\n\t\tbool qentp_set = qent->qentp ? true : false;\n\t\tle = le->next;\n\n\t\ttrace_send(conn->sip,\n\t\t\t   conn->tp,\n\t\t\t   conn,\n\t\t\t   &conn->paddr, qent->mb);\n\n\t\tre_printf(\"--> send\\n\");\n\n\t\terr = websock_send(conn->websock_conn, WEBSOCK_BIN,\n\t\t\t\t   \"%b\",\n\t\t\t\t   mbuf_buf(qent->mb),\n\t\t\t\t   mbuf_get_left(qent->mb));\n\t\tif (err)\n\t\t\tqent->transph(err, qent->arg);\n\n\t\tif (!qentp_set) {\n\t\t\tlist_unlink(&qent->le);\n\t\t\tmem_deref(qent);\n\t\t}\n\t}\n}\n\n\nstatic void websock_recv_handler(const struct websock_hdr *hdr,\n\t\t\t\t struct mbuf *mb, void *arg)\n{\n\tstruct sip_conn *conn = arg;\n\tstruct sip_msg *msg;\n\tsize_t start;\n\tint err;\n\t(void) hdr;\n\n#if 0\n\tre_printf(\n\t\t  \"~ ~ ~ ~ ~ websock receive: ~ ~ ~ ~ ~\\n\"\n\t\t  \"\\x1b[32m\"\n\t\t  \"%b\"\n\t\t  \"\\x1b[;m\\t\\n\"\n\t\t  \"~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~\\n\"\n\t\t  ,\n\t\t  mbuf_buf(mb), mbuf_get_left(mb));\n#endif\n\n\tif (mb->end <= 4)\n\t\treturn;\n\n\ttmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000, conn_tmr_handler, conn);\n\n\tstart = mb->pos;\n\n\terr = sip_msg_decode(&msg, mb);\n\tif (err) {\n\t\t(void)re_fprintf(stderr, \"sip: msg decode err: %m\\n\", err);\n\t\treturn;\n\t}\n\n\tmsg->sock = mem_ref(conn);\n\tmsg->src = conn->paddr;\n\tmsg->dst = conn->laddr;\n\tmsg->tp = conn->tp;\n\n\tsip_recv(conn->sip, msg, start);\n\n\tmem_deref(msg);\n}\n\n\nstatic void websock_close_handler(int err, void *arg)\n{\n\tstruct sip_conn *conn = arg;\n\n\tre_printf(\"sip: websock connection closed (%m)\\n\", err);\n\tconn_close(conn, err ? err : ECONNRESET);\n\tmem_deref(conn);\n}\n\n\nstatic int ws_conn_send(struct sip_connqent **qentp, struct sip *sip,\n\t\t\tbool secure,\n\t\t\tconst struct sa *dst, struct mbuf *mb,\n\t\t\tsip_transp_h *transph, void *arg)\n{\n\tstruct sip_conn *conn, *new_conn = NULL;\n\tstruct sip_connqent *qent;\n\tstruct sip_transport *transp;\n\tenum sip_transp tp;\n\tconst char *prefix;\n\tchar ws_uri[256];\n\tint err = 0;\n\n\tif (secure) {\n\t\tprefix = \"wss\";\n\t\ttp = SIP_TRANSP_WSS;\n\t}\n\telse {\n\t\tprefix = \"ws\";\n\t\ttp = SIP_TRANSP_WS;\n\t}\n\n\tconn = ws_conn_find(sip, dst, tp);\n\tif (conn) {\n\t\tif (!conn->established)\n\t\t\tgoto enqueue;\n\n\t\ttrace_send(sip,\n\t\t\t   secure ? SIP_TRANSP_WSS : SIP_TRANSP_WS,\n\t\t\t   conn,\n\t\t\t   dst, mb);\n\n\t\treturn websock_send(conn->websock_conn, WEBSOCK_BIN,\n\t\t\t\t    \"%b\",\n\t\t\t\t    mbuf_buf(mb), mbuf_get_left(mb));\n\t}\n\n\ttransp = (struct sip_transport *)transp_find(sip, tp,\n\t\t\t\t\t\t     sa_af(dst), dst);\n\tif (!transp) {\n\t\terr = EPROTONOSUPPORT;\n\t\tgoto out;\n\t}\n\n\tnew_conn = conn = mem_zalloc(sizeof(*conn), conn_destructor);\n\tif (!conn)\n\t\treturn ENOMEM;\n\n\thash_append(sip->ht_conn, sa_hash(dst, SA_ALL), &conn->he, conn);\n\tconn->paddr = *dst;\n\tconn->sip   = sip;\n\tconn->tp    = tp;\n\n\t/* TODO: how to select ports of outbound SIP/WS proxy ?\n\t * TODO: http url path \"test\" is temp, add config\n\t */\n\n\t/* Use port if specified, otherwise use default HTTP/HTTPS ports */\n\tif (sa_port(dst)) {\n\t\tif (re_snprintf(ws_uri, sizeof(ws_uri),\n\t\t\t\t\"%s://%J/\", prefix, dst) < 0) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse {\n\t\tif (re_snprintf(ws_uri, sizeof(ws_uri),\n\t\t\t\t\"%s://%j/\", prefix, dst) < 0) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\tif (!transp->http_cli) {\n\t\terr = http_client_alloc(&transp->http_cli, sip->dnsc);\n\t\tif (err) {\n\t\t\tre_fprintf(stderr, \"transp: could not create\"\n\t\t\t\t   \" http client (%m)\\n\", err);\n\t\t\tgoto out;\n\t\t}\n#ifdef USE_TLS\n\t\tif (transp->tls)\n\t\t\thttp_client_set_tls(transp->http_cli, transp->tls);\n#endif\n\t}\n\n\tre_printf(\"websock: connecting to '%s'\\n\", ws_uri);\n\terr = websock_connect(&conn->websock_conn, sip->websock,\n\t\t\t      transp->http_cli, ws_uri, 15000,\n\t\t\t      websock_estab_handler, websock_recv_handler,\n\t\t\t      websock_close_handler, conn,\n\t\t\t      \"Sec-WebSocket-Protocol: sip\\r\\n\");\n\tif (err) {\n\t\tre_printf(\"websock_connect: %m\\n\", err);\n\t\tgoto out;\n\t}\n\n\ttmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000, conn_tmr_handler, conn);\n\n enqueue:\n\tqent = mem_zalloc(sizeof(*qent), qent_destructor);\n\tif (!qent) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\n\t}\n\n\tlist_append(&conn->ql, &qent->le, qent);\n\tqent->mb = mem_ref(mb);\n\tqent->transph = transph ? transph : internal_transport_handler;\n\tqent->arg = arg;\n\n\tif (qentp) {\n\t\tqent->qentp = qentp;\n\t\t*qentp = qent;\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(new_conn);\n\n\treturn err;\n}\n\n\nstatic int dst_set_scopeid(struct sip *sip, struct sa *dst, enum sip_transp tp)\n{\n\tstruct sa laddr;\n\tint err;\n\n\tif (sa_af(dst) != AF_INET6 || !sa_is_linklocal(dst))\n\t\treturn 0;\n\n\terr = sip_transp_laddr(sip, &laddr, tp, dst);\n\tif (err)\n\t\treturn err;\n\n\tsa_set_scopeid(dst, sa_scopeid(&laddr));\n\treturn 0;\n}\n\n\nint sip_transp_init(struct sip *sip, uint32_t sz)\n{\n\tint err;\n\n\terr  = hash_alloc(&sip->ht_conn, sz);\n\terr |= hash_alloc(&sip->ht_conncfg, sz);\n\treturn err;\n}\n\n\nstatic void http_req_handler(struct http_conn *hc, const struct http_msg *msg,\n\t\t\t     void *arg)\n{\n\tstruct sip_transport *transp = arg;\n\tstruct sip_conn *conn = NULL;\n\tconst struct sa *paddr;\n\tconst struct http_hdr *hdr;\n\tint err;\n\n\tpaddr = http_conn_peer(hc);\n\n\tre_printf(\"http request from %J\\n\", paddr);\n\n\thdr = http_msg_hdr(msg, HTTP_HDR_SEC_WEBSOCKET_PROTOCOL);\n\tif (!hdr) {\n\t\tre_printf(\"sip: missing Sec-WebSocket-Protocol header\\n\");\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\tif (0 != pl_strcasecmp(&hdr->val, \"sip\")) {\n\t\tre_printf(\"sip: unknown Sec-WebSocket-Protocol '%r'\\n\",\n\t\t\t  &hdr->val);\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\n\tconn = mem_zalloc(sizeof(*conn), conn_destructor);\n\tif (!conn) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\thash_append(transp->sip->ht_conn, sa_hash(paddr, SA_ALL),\n\t\t    &conn->he, conn);\n\n\tconn->paddr = *paddr;\n\tconn->sip   = transp->sip;\n\tconn->tp    = transp->tp;\n\n\terr = websock_accept_proto(&conn->websock_conn, \"sip\",\n                             transp->sip->websock, hc, msg, 15000,\n                             websock_recv_handler, websock_close_handler,\n                             conn);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_conn_local_get(websock_tcp(conn->websock_conn),\n\t\t\t\t &conn->laddr);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err) {\n\t\t(void)http_reply(hc, 500, \"Server Error\", NULL);\n\t\tmem_deref(conn);\n\t}\n}\n\n\n/**\n * Add a SIP transport\n *\n * @param sip    SIP stack instance\n * @param tp     SIP Transport\n * @param listen True to open listening socket (UDP socket always opened)\n * @param laddr  Local network address\n * @param ap     Optional transport parameters such as TLS context\n *\n * @return 0 if success, otherwise errorcode\n */\nstatic int add_transp(struct sip *sip, enum sip_transp tp,\n\t\t      bool listen, const struct sa *laddr, va_list ap)\n{\n\tstruct sip_transport *transp;\n\tstruct tls *tls;\n\tint err = 0;\n\n\tif (!sip || !laddr || !sa_isset(laddr, SA_ADDR))\n\t\treturn EINVAL;\n\n\ttransp = mem_zalloc(sizeof(*transp), transp_destructor);\n\tif (!transp)\n\t\treturn ENOMEM;\n\n\tif (tp == SIP_TRANSP_TLS) {\n\t\terr = hash_alloc(&transp->ht_ccert, 32);\n\t\tif (err) {\n\t\t\tmem_deref(transp);\n\t\t\treturn err;\n\t\t}\n\t}\n\n\tlist_append(&sip->transpl, &transp->le, transp);\n\ttransp->sip = sip;\n\ttransp->tp  = tp;\n\n\tswitch (tp) {\n\n\tcase SIP_TRANSP_UDP:\n\t\terr = udp_listen((struct udp_sock **)&transp->sock, laddr,\n\t\t\t\t udp_recv_handler, transp);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = udp_local_get(transp->sock, &transp->laddr);\n\t\tbreak;\n\n\tcase SIP_TRANSP_TLS:\n\t\ttls = va_arg(ap, struct tls *);\n\t\tif (!tls) {\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\n\t\ttransp->tls = mem_ref(tls);\n\n\t\t/*@fallthrough@*/\n\n\tcase SIP_TRANSP_TCP:\n\n\t\tif (!listen) {\n\t\t\ttransp->laddr = *laddr;\n\t\t\tsa_set_port(&transp->laddr, 0);\n\t\t\treturn err;\n\t\t}\n\n\t\terr = tcp_listen((struct tcp_sock **)&transp->sock, laddr,\n\t\t\t\t tcp_connect_handler, transp);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = tcp_sock_local_get(transp->sock, &transp->laddr);\n\t\tbreak;\n\n\tdefault:\n\t\terr = EPROTONOSUPPORT;\n\t\tbreak;\n\t}\n\n\tif (err)\n\t\tmem_deref(transp);\n\n\treturn err;\n}\n\n\n/**\n * Add a SIP transport\n *\n * @param sip   SIP stack instance\n * @param tp    SIP Transport\n * @param laddr Local network address\n * @param ...   Optional transport parameters such as TLS context\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_transp_add(struct sip *sip, enum sip_transp tp,\n\t\t   const struct sa *laddr, ...)\n{\n\tint err;\n\tva_list ap;\n\n\tva_start(ap, laddr);\n\terr = add_transp(sip, tp, true, laddr, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Add a SIP transport and open listening socket if requested\n *\n * UDP socket will always be opened even if listen is false.\n *\n * @param sip    SIP stack instance\n * @param tp     SIP Transport\n * @param listen True to open listening socket\n * @param laddr  Local network address\n * @param ...\t Optional transport parameters such as TLS context\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_transp_add_sock(struct sip *sip, enum sip_transp tp,\n\t\t\tbool listen, const struct sa *laddr, ...)\n{\n\tint err;\n\tva_list ap;\n\n\tva_start(ap, laddr);\n\terr = add_transp(sip, tp, listen, laddr, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Add a SIP websocket transport\n *\n * @param sip    SIP stack instance\n * @param tp     SIP Transport\n * @param laddr  Local network address\n * @param server True if server, otherwise false\n * @param cert   Server Certificate\n * @param tls    Optional TLS context\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_transp_add_websock(struct sip *sip, enum sip_transp tp,\n\t\t\t   const struct sa *laddr,\n\t\t\t   bool server, const char *cert, struct tls *tls)\n{\n\tstruct sip_transport *transp;\n\tbool secure = tp == SIP_TRANSP_WSS;\n\tint err = 0;\n\n\tif (!sip || !laddr || !sa_isset(laddr, SA_ADDR))\n\t\treturn EINVAL;\n\n\ttransp = mem_zalloc(sizeof(*transp), transp_destructor);\n\tif (!transp)\n\t\treturn ENOMEM;\n\n\tlist_append(&sip->transpl, &transp->le, transp);\n\ttransp->sip = sip;\n\ttransp->tp  = tp;\n\n\tif (tls)\n\t\ttransp->tls = mem_ref(tls);\n\n\tif (server) {\n\n\t\tif (secure) {\n\t\t\terr = https_listen(&transp->http_sock, laddr,\n\t\t\t\t\t   cert,\n\t\t\t\t\t   http_req_handler, transp);\n\t\t\tif (err) {\n\t\t\t\tre_fprintf(stderr,\n\t\t\t\t\t   \"websock: https_listen\"\n\t\t\t\t\t   \" error (%m)\\n\", err);\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\terr = http_listen(&transp->http_sock, laddr,\n\t\t\t\t\t  http_req_handler, transp);\n\t\t\tif (err) {\n\t\t\t\tre_fprintf(stderr, \"websock: http_listen\"\n\t\t\t\t\t   \" error (%m)\\n\", err);\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\n\t\terr = tcp_sock_local_get(http_sock_tcp(transp->http_sock),\n\t\t\t\t\t &transp->laddr);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse {\n\t\ttransp->laddr = *laddr;\n\t\tsa_set_port(&transp->laddr, 9);\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(transp);\n\n\treturn err;\n}\n\n\nstatic bool add_ccert_handler(struct le *le, void *arg)\n{\n\tconst struct sip_transport *transp = le->data;\n\tstruct sip_ccert_data *cc = arg;\n\n\tif (!cc->ccert->he.list)\n\t\thash_append(transp->ht_ccert, cc->hsup, &cc->ccert->he,\n\t\t\t    cc->ccert);\n\telse {\n\t\tstruct sip_ccert *ccert = mem_zalloc(sizeof(*ccert), NULL);\n\t\tif (!ccert)\n\t\t\treturn false;\n\n\t\tccert->file = cc->ccert->file;\n\t\thash_append(transp->ht_ccert, cc->hsup, &ccert->he, ccert);\n\t}\n\n\treturn false;\n}\n\n\n/**\n * Add a client certificate to the TLS transport object\n * Client certificates are saved as hash-table.\n * Hashtable-Key: \"username\" <sip:username\\@address:port>\n *\n * @param sip Global SIP stack\n * @param uri Account uri information\n * @param cert Certificate + Key file\n *\n * @return int 0 if success, otherwise errorcode\n */\nint sip_transp_add_ccert(struct sip *sip, const struct uri *uri,\n\t\t\t const char *cert)\n{\n\tint err = 0;\n\tstruct sip_ccert *ccert = NULL;\n\tstruct sip_ccert_data cc_data;\n\tstruct mbuf *sup = NULL;\n\n\tif (!sip || !uri || !cert)\n\t\treturn EINVAL;\n\n\tsup = mbuf_alloc(30);\n\tif (!sup)\n\t\treturn ENOMEM;\n\n\terr = mbuf_printf(sup, \"\\\"%r\\\" <%r:%r@%r:%d>\", &uri->user,\n\t\t&uri->scheme, &uri->user, &uri->host, uri->port);\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(sup, 0);\n\n\tccert = mem_zalloc(sizeof(*ccert), NULL);\n\tif (!ccert) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\tpl_set_str(&ccert->file, cert);\n\n\tcc_data.hsup = hash_joaat(mbuf_buf(sup), mbuf_get_left(sup));\n\tcc_data.ccert = ccert;\n\n\t(void)transp_apply_all(sip, SIP_TRANSP_TLS, AF_INET, add_ccert_handler,\n\t\t\t       &cc_data);\n\t(void)transp_apply_all(sip, SIP_TRANSP_TLS, AF_INET6,\n\t\t\t       add_ccert_handler, &cc_data);\n\n out:\n\tmem_deref(sup);\n\treturn err;\n}\n\n\n/**\n * Flush all transports of a SIP stack instance\n *\n * @param sip SIP stack instance\n */\nvoid sip_transp_flush(struct sip *sip)\n{\n\tif (!sip)\n\t\treturn;\n\n\thash_flush(sip->ht_conn);\n\thash_flush(sip->ht_conncfg);\n\tlist_flush(&sip->transpl);\n}\n\n\nint sip_transp_send(struct sip_connqent **qentp, struct sip *sip, void *sock,\n\t\t    enum sip_transp tp, const struct sa *dst, char *host,\n\t\t    struct mbuf *mb, sip_conn_h *connh, sip_transp_h *transph,\n\t\t    void *arg)\n{\n\tconst struct sip_transport *transp;\n\tstruct sip_conn *conn;\n\tbool secure = false;\n\tstruct sa dsttmp;\n\tstruct sa laddr;\n\tint err;\n\n\tif (!sip || !dst || !mb)\n\t\treturn EINVAL;\n\n\tsa_cpy(&dsttmp, dst);\n\terr = dst_set_scopeid(sip, &dsttmp, tp);\n\tif (err)\n\t\treturn err;\n\n\tswitch (tp) {\n\n\tcase SIP_TRANSP_UDP:\n\t\terr = sip_transp_laddr(sip, &laddr, tp, dst);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (connh)\n\t\t\tconnh(&laddr, dst, mb, arg);\n\n\t\tif (!sock) {\n\t\t\ttransp = transp_find(sip, tp, sa_af(&dsttmp), &dsttmp);\n\t\t\tif (!transp)\n\t\t\t\treturn EPROTONOSUPPORT;\n\n\t\t\tsock = transp->sock;\n\t\t}\n\n\t\ttrace_send(sip, tp, sock, &dsttmp, mb);\n\n\t\terr = udp_send(sock, &dsttmp, mb);\n\t\tbreak;\n\n\tcase SIP_TRANSP_TLS:\n\t\tsecure = true;\n\t\t/*@fallthrough@*/\n\n\tcase SIP_TRANSP_TCP:\n\t\tconn = sock;\n\n\t\tif (conn && conn->tc) {\n\t\t\tif (connh) {\n\t\t\t\terr = connh(&conn->laddr, dst, mb, arg);\n\t\t\t\tif (err)\n\t\t\t\t\treturn err;\n\t\t\t}\n\n\t\t\ttrace_send(sip, tp, conn, &dsttmp, mb);\n\n\t\t\terr = tcp_send(conn->tc, mb);\n\t\t}\n\t\telse\n\t\t\terr = conn_send(qentp, sip, secure, &dsttmp, host, mb,\n\t\t\t\t\tconnh, transph, arg);\n\t\tbreak;\n\n\tcase SIP_TRANSP_WSS:\n\t\tsecure = true;\n\t\t/*@fallthrough@*/\n\n\tcase SIP_TRANSP_WS:\n\t\t/*TODO: Ideally connh should be called if the websocket was\n\t\t * opened and the source port is known. As a workaround the\n\t\t * listen port is used for Contact and Via headers */\n\t\terr = sip_transp_laddr(sip, &laddr, tp, dst);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (connh)\n\t\t\tconnh(&laddr, dst, mb, arg);\n\n\t\tconn = sock;\n\t\tif (conn && conn->websock_conn) {\n\n\t\t\ttrace_send(sip, tp, conn, &dsttmp, mb);\n\n\t\t\terr = websock_send(conn->websock_conn, WEBSOCK_BIN,\n\t\t\t\t\t   \"%b\",\n\t\t\t\t\t   mbuf_buf(mb), mbuf_get_left(mb));\n\t\t\tif (err) {\n\t\t\t\tre_fprintf(stderr, \"websock_send failed\"\n\t\t\t\t\t   \" (%m)\\n\", err);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\terr = ws_conn_send(qentp, sip, secure, &dsttmp, mb,\n\t\t\t\t\t   transph, arg);\n\t\t\tif (err) {\n\t\t\t\tre_fprintf(stderr, \"ws_conn_send failed\"\n\t\t\t\t\t   \" (%m)\\n\", err);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\terr = EPROTONOSUPPORT;\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nint sip_transp_laddr(struct sip *sip, struct sa *laddr, enum sip_transp tp,\n\t\t      const struct sa *dst)\n{\n\tconst struct sip_transport *transp;\n\tstruct sip_conncfg *conncfg;\n\n\tif (!sip || !laddr)\n\t\treturn EINVAL;\n\n\ttransp = transp_find(sip, tp, sa_af(dst), dst);\n\tif (!transp)\n\t\treturn EPROTONOSUPPORT;\n\n\t*laddr = transp->laddr;\n\tif (tp != SIP_TRANSP_UDP) {\n\t\tconncfg = sip_conncfg_find(sip, dst);\n\t\tif (conncfg && conncfg->srcport)\n\t\t\tsa_set_port(laddr, conncfg->srcport);\n\t}\n\n\treturn 0;\n}\n\n\nbool sip_transp_supported(struct sip *sip, enum sip_transp tp, int af)\n{\n\tif (!sip)\n\t\treturn false;\n\n\treturn transp_find(sip, tp, af, NULL) != NULL;\n}\n\n\nint  sip_transp_set_default(struct sip *sip, enum sip_transp tp)\n{\n\tif (!sip)\n\t\treturn EINVAL;\n\n\tsip->tp_def = tp;\n\treturn 0;\n}\n\n\n/**\n * Check if network address is part of SIP transports\n *\n * @param sip   SIP stack instance\n * @param tp    SIP transport\n * @param laddr Local network address to check\n *\n * @return True if part of SIP transports, otherwise false\n */\nbool sip_transp_isladdr(const struct sip *sip, enum sip_transp tp,\n\t\t\tconst struct sa *laddr)\n{\n\tstruct le *le;\n\n\tif (!sip || !laddr)\n\t\treturn false;\n\n\tfor (le=sip->transpl.head; le; le=le->next) {\n\n\t\tconst struct sip_transport *transp = le->data;\n\n\t\tif (tp != SIP_TRANSP_NONE && transp->tp != tp)\n\t\t\tcontinue;\n\n\t\tif (!sa_cmp(&transp->laddr, laddr, SA_ALL))\n\t\t\tcontinue;\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/**\n * Get the name of a given SIP Transport\n *\n * @param tp SIP Transport\n *\n * @return Name of the corresponding SIP Transport\n */\nconst char *sip_transp_name(enum sip_transp tp)\n{\n\tswitch (tp) {\n\n\tcase SIP_TRANSP_UDP: return \"UDP\";\n\tcase SIP_TRANSP_TCP: return \"TCP\";\n\tcase SIP_TRANSP_TLS: return \"TLS\";\n\tcase SIP_TRANSP_WS:  return \"WS\";\n\tcase SIP_TRANSP_WSS: return \"WSS\";\n\tdefault:             return \"???\";\n\t}\n}\n\n\nconst char *sip_transp_srvid(enum sip_transp tp)\n{\n\tswitch (tp) {\n\n\tcase SIP_TRANSP_UDP: return \"_sip._udp\";\n\tcase SIP_TRANSP_TCP: return \"_sip._tcp\";\n\tcase SIP_TRANSP_TLS: return \"_sips._tcp\";\n\tdefault:             return \"???\";\n\t}\n}\n\n\n/**\n * Get the transport parameters for a given SIP Transport\n *\n * @param tp SIP Transport\n *\n * @return Transport parameters of the corresponding SIP Transport\n */\nconst char *sip_transp_param(enum sip_transp tp)\n{\n\tswitch (tp) {\n\n\tcase SIP_TRANSP_UDP: return \"\";\n\tcase SIP_TRANSP_TCP: return \";transport=tcp\";\n\tcase SIP_TRANSP_TLS: return \";transport=tls\";\n\tcase SIP_TRANSP_WS:  return \";transport=ws\";\n\tcase SIP_TRANSP_WSS: return \";transport=wss\";\n\tdefault:             return \"\";\n\t}\n}\n\n\nenum sip_transp sip_transp_decode(const struct pl *pl)\n{\n\tenum sip_transp tp = SIP_TRANSP_NONE;\n\tif (!pl_strcasecmp(pl, \"udp\"))\n\t\ttp = SIP_TRANSP_UDP;\n\telse if (!pl_strcasecmp(pl, \"tcp\"))\n\t\ttp = SIP_TRANSP_TCP;\n\telse if (!pl_strcasecmp(pl, \"tls\"))\n\t\ttp = SIP_TRANSP_TLS;\n\telse if (!pl_strcasecmp(pl, \"ws\"))\n\t\ttp = SIP_TRANSP_WS;\n\telse if (!pl_strcasecmp(pl, \"wss\"))\n\t\ttp = SIP_TRANSP_WSS;\n\n\treturn tp;\n}\n\n\nbool sip_transp_reliable(enum sip_transp tp)\n{\n\tswitch (tp) {\n\n\tcase SIP_TRANSP_UDP: return false;\n\tcase SIP_TRANSP_TCP: return true;\n\tcase SIP_TRANSP_TLS: return true;\n\tcase SIP_TRANSP_WS:  return true;\n\tcase SIP_TRANSP_WSS: return true;\n\tdefault:             return false;\n\t}\n}\n\n\n/**\n * Get the default port number for a given SIP Transport\n *\n * @param tp   SIP Transport\n * @param port Port number\n *\n * @return Corresponding port number\n */\nuint16_t sip_transp_port(enum sip_transp tp, uint16_t port)\n{\n\tif (port)\n\t\treturn port;\n\n\tswitch (tp) {\n\n\tcase SIP_TRANSP_UDP: return SIP_PORT;\n\tcase SIP_TRANSP_TCP: return SIP_PORT;\n\tcase SIP_TRANSP_TLS: return SIP_PORT_TLS;\n\tcase SIP_TRANSP_WS:  return 80;\n\tcase SIP_TRANSP_WSS: return 443;\n\tdefault:             return 0;\n\t}\n}\n\n\nint  sip_settos(struct sip *sip, uint8_t tos)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tif (!sip)\n\t\treturn EINVAL;\n\n\tsip->tos = tos;\n\n\tfor (le = sip->transpl.head; le; le = le->next) {\n\n\t\tstruct sip_transport *transp = le->data;\n\t\ttransp->tos = tos;\n\t\tswitch (transp->tp) {\n\t\tcase SIP_TRANSP_UDP:\n\t\t\terr = udp_settos(transp->sock, tos);\n\t\t\tbreak;\n\n\t\tcase SIP_TRANSP_TCP:\n\t\tcase SIP_TRANSP_TLS:\n\t\t\terr = tcp_settos(transp->sock, tos);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\tif (err)\n\t\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nstatic void sip_transports_print(struct re_printf *pf, const struct sip* sip)\n{\n\tuint32_t mask = 0;\n\n\tfor (struct le *le = sip->transpl.head; le; le = le->next) {\n\t\tconst struct sip_transport *transp = le->data;\n\t\tmask |= (1 << transp->tp);\n\t}\n\n\tfor (uint8_t i = 0; i < SIP_TRANSPC; ++i) {\n\t\tif (mask==0 || (0 != (mask & (1u << i))))\n\t\t\t(void)re_hprintf(pf, \"  %s\\n\", sip_transp_name(i));\n\t}\n}\n\n\nstatic bool debug_handler(struct le *le, void *arg)\n{\n\tconst struct sip_transport *transp = le->data;\n\tstruct re_printf *pf = arg;\n\n\tif (sa_port(&transp->laddr) == 0)\n\t\treturn false;\n\n\t(void)re_hprintf(pf, \"  %J (%s)\\n\",\n\t\t\t &transp->laddr,\n\t\t\t sip_transp_name(transp->tp));\n\n\treturn false;\n}\n\n\nstatic bool conn_debug_handler(struct le *le, void *arg)\n{\n\tstruct sip_conn *conn = le->data;\n\tstruct re_printf *pf = arg;\n\n\t(void)re_hprintf(pf, \"  [%u] %5s  %J --> %J  (%s)\\n\",\n\t\t\t mem_nrefs(conn),\n\t\t\t sip_transp_name(conn->tp),\n\t\t\t &conn->laddr, &conn->paddr,\n\t\t\t conn->established ? \"Established\" : \"...\"\n\t\t\t );\n\n\treturn false;\n}\n\n\nstatic bool conncfg_debug_handler(struct le *le, void *arg)\n{\n\tstruct sip_conncfg *conncfg = le->data;\n\tstruct re_printf *pf = arg;\n\n\t(void)re_hprintf(pf, \"  TCP source port  %u\\n\", conncfg->srcport);\n\n\treturn false;\n}\n\n\nint sip_transp_debug(struct re_printf *pf, const struct sip *sip)\n{\n\tint err;\n\n\terr = re_hprintf(pf, \"transports:\\n\");\n\tsip_transports_print(pf, sip);\n\n\terr |= re_hprintf(pf, \"transport sockets:\\n\");\n\tlist_apply(&sip->transpl, true, debug_handler, pf);\n\n\terr |= re_hprintf(pf, \"connections:\\n\");\n\thash_apply(sip->ht_conn, conn_debug_handler, pf);\n\n\terr |= re_hprintf(pf, \"connection configurations:\\n\");\n\thash_apply(sip->ht_conncfg, conncfg_debug_handler, pf);\n\n\treturn err;\n}\n\n\n/**\n * Get the TCP Connection from a SIP Message\n *\n * @param msg SIP Message\n *\n * @return TCP Connection if reliable transport, otherwise NULL\n */\nstruct tcp_conn *sip_msg_tcpconn(const struct sip_msg *msg)\n{\n\tif (!msg || !msg->sock)\n\t\treturn NULL;\n\n\tswitch (msg->tp) {\n\n\tcase SIP_TRANSP_TCP:\n\tcase SIP_TRANSP_TLS:\n\t\treturn ((struct sip_conn *)msg->sock)->tc;\n\n\tcase SIP_TRANSP_WS:\n\tcase SIP_TRANSP_WSS: {\n\t\tstruct sip_conn *conn = msg->sock;\n\t\treturn websock_tcp(conn->websock_conn);\n\t}\n\n\tdefault:\n\t\treturn NULL;\n\t}\n}\n\n\nint  sip_keepalive_tcp(struct sip_keepalive *ka, struct sip_conn *conn,\n\t\t       uint32_t interval)\n{\n\tif (!ka || !conn)\n\t\treturn EINVAL;\n\n\tif (!conn->tc || !conn->established)\n\t\treturn ENOTCONN;\n\n\tlist_append(&conn->kal, &ka->le, ka);\n\n\tif (!tmr_isrunning(&conn->tmr_ka)) {\n\n\t\tinterval = MAX(interval ? interval : TCP_KEEPALIVE_INTVAL,\n\t\t\t       TCP_KEEPALIVE_TIMEOUT * 2);\n\n\t\tconn->ka_interval = interval;\n\n\t\ttmr_start(&conn->tmr_ka, sip_keepalive_wait(conn->ka_interval),\n\t\t\t  conn_keepalive_handler, conn);\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Remove all SIP transport instances that are bound to the given local network\n * address\n *\n * @param sip   SIP stack instance\n * @param laddr Local network address\n */\nvoid sip_transp_rmladdr(struct sip *sip, const struct sa *laddr)\n{\n\tstruct le *le;\n\tstruct le *len = NULL;\n\n\tif (!sip || !laddr)\n\t\treturn;\n\n\tfor (le = sip->transpl.head; le; le = len) {\n\t\tstruct sip_transport *transp = le->data;\n\n\t\tlen = le->next;\n\t\tif (sa_cmp(&transp->laddr, laddr, SA_ADDR))\n\t\t\tmem_deref(transp);\n\t}\n}\n\n\n/**\n * Set a SIP connection configuration for a given peer address\n *\n * @param sip      SIP stack instance\n * @param paddr    Peer address\n * @param conncfg  A SIP connection configuration\n *\n * @return 0 if success, otherwise errorcode\n */\nint sip_conncfg_set(struct sip *sip, const struct sa *paddr,\n\t\t    const struct sip_conncfg *conncfg)\n{\n\tstruct sip_conncfg *cfg;\n\n\tif (!sip || !sa_isset(paddr, SA_ALL))\n\t\treturn EINVAL;\n\n\tcfg = sip_conncfg_find(sip, paddr);\n\tif (cfg) {\n\t\tcfg->srcport = conncfg->srcport;\n\t\treturn 0;\n\t}\n\telse {\n\t\tcfg = mem_zalloc(sizeof(*cfg), NULL);\n\t}\n\n\tif (!cfg)\n\t\treturn ENOMEM;\n\n\tmemcpy(cfg, conncfg, sizeof(*cfg));\n\tmemset(&cfg->he, 0, sizeof(cfg->he));\n\tsa_cpy(&cfg->paddr, paddr);\n\thash_append(sip->ht_conncfg, sa_hash(paddr, SA_ALL), &cfg->he, cfg);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/sip/via.c",
    "content": "/**\n * @file via.c  SIP Via decode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_uri.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_msg.h>\n#include <re_sip.h>\n\n\nstatic int decode_hostport(const struct pl *hostport, struct pl *host,\n\t\t\t   struct pl *port)\n{\n\t/* Try IPv6 first */\n\tif (!re_regex(hostport->p, hostport->l, \"\\\\[[0-9a-f:]+\\\\][:]*[0-9]*\",\n\t\t      host, NULL, port))\n\t\treturn 0;\n\n\t/* Then non-IPv6 host */\n\treturn re_regex(hostport->p, hostport->l, \"[^:]+[:]*[0-9]*\",\n\t\t\thost, NULL, port);\n}\n\n\n/**\n * Decode a pointer-length string into a SIP Via header\n *\n * @param via SIP Via header\n * @param pl  Pointer-length string\n *\n * @return 0 for success, otherwise errorcode\n */\nint sip_via_decode(struct sip_via *via, const struct pl *pl)\n{\n\tstruct pl transp, host, port;\n\tint err;\n\n\tif (!via || !pl)\n\t\treturn EINVAL;\n\n\terr = re_regex(pl->p, pl->l,\n\t\t       \"SIP[  \\t\\r\\n]*/[ \\t\\r\\n]*2.0[ \\t\\r\\n]*/[ \\t\\r\\n]*\"\n\t\t       \"[A-Z]+[ \\t\\r\\n]*[^; \\t\\r\\n]+[ \\t\\r\\n]*[^]*\",\n\t\t       NULL, NULL, NULL, NULL, &transp,\n\t\t       NULL, &via->sentby, NULL, &via->params);\n\tif (err)\n\t\treturn err;\n\n\tif (!pl_strcmp(&transp, \"TCP\"))\n\t\tvia->tp = SIP_TRANSP_TCP;\n\telse if (!pl_strcmp(&transp, \"TLS\"))\n\t\tvia->tp = SIP_TRANSP_TLS;\n\telse if (!pl_strcmp(&transp, \"UDP\"))\n\t\tvia->tp = SIP_TRANSP_UDP;\n\telse if (!pl_strcmp(&transp, \"WS\"))\n\t\tvia->tp = SIP_TRANSP_WS;\n\telse if (!pl_strcmp(&transp, \"WSS\"))\n\t\tvia->tp = SIP_TRANSP_WSS;\n\telse\n\t\tvia->tp = SIP_TRANSP_NONE;\n\n\terr = decode_hostport(&via->sentby, &host, &port);\n\tif (err)\n\t\treturn err;\n\n\tsa_init(&via->addr, AF_INET);\n\n\t(void)sa_set(&via->addr, &host, 0);\n\n\tif (pl_isset(&port))\n\t\tsa_set_port(&via->addr, pl_u32(&port));\n\n\tvia->val = *pl;\n\n\treturn msg_param_decode(&via->params, \"branch\", &via->branch);\n}\n"
  },
  {
    "path": "src/sipevent/listen.c",
    "content": "/**\n * @file sipevent/listen.c  SIP Event Listen\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipevent.h>\n#include \"sipevent.h\"\n\n\nstruct subcmp {\n\tconst struct sipevent_event *evt;\n\tconst struct sip_msg *msg;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sipevent_sock *sock = arg;\n\n\tmem_deref(sock->lsnr);\n\thash_flush(sock->ht_not);\n\thash_flush(sock->ht_sub);\n\tmem_deref(sock->ht_not);\n\tmem_deref(sock->ht_sub);\n}\n\n\nstatic bool event_cmp(const struct sipevent_event *evt,\n\t\t      const char *event, const char *id,\n\t\t      int32_t refer_cseq)\n{\n\tif (pl_strcmp(&evt->event, event))\n\t\treturn false;\n\n\tif (!pl_isset(&evt->id) && !id)\n\t\treturn true;\n\n\tif (!pl_isset(&evt->id))\n\t\treturn false;\n\n\tif (!id) {\n\t\tif (refer_cseq >= 0 && (int32_t)pl_u32(&evt->id) == refer_cseq)\n\t\t\treturn true;\n\n\t\treturn false;\n\t}\n\n\tif (pl_strcmp(&evt->id, id))\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstatic bool not_cmp_handler(struct le *le, void *arg)\n{\n\tconst struct subcmp *cmp = arg;\n\tstruct sipnot *not = le->data;\n\n\treturn sip_dialog_cmp(not->dlg, cmp->msg) &&\n\t\tevent_cmp(cmp->evt, not->event, not->id, -1);\n}\n\n\nstatic bool sub_cmp_handler(struct le *le, void *arg)\n{\n\tconst struct subcmp *cmp = arg;\n\tstruct sipsub *sub = le->data;\n\n\treturn sip_dialog_cmp(sub->dlg, cmp->msg) &&\n\t\t(!cmp->evt || event_cmp(cmp->evt, sub->event, sub->id,\n\t\t\t\t\tsub->refer_cseq));\n}\n\n\nstatic bool sub_cmp_half_handler(struct le *le, void *arg)\n{\n\tconst struct subcmp *cmp = arg;\n\tstruct sipsub *sub = le->data;\n\n\treturn sip_dialog_cmp_half(sub->dlg, cmp->msg) &&\n\t\t!sip_dialog_established(sub->dlg) &&\n\t\t(!cmp->evt || event_cmp(cmp->evt, sub->event, sub->id,\n\t\t\t\t\tsub->refer_cseq));\n}\n\n\nstatic struct sipnot *sipnot_find(struct sipevent_sock *sock,\n\t\t\t\t  const struct sip_msg *msg,\n\t\t\t\t  const struct sipevent_event *evt)\n{\n\tstruct subcmp cmp;\n\n\tcmp.msg = msg;\n\tcmp.evt = evt;\n\n\treturn list_ledata(hash_lookup(sock->ht_not,\n\t\t\t\t       hash_joaat_pl(&msg->callid),\n\t\t\t\t       not_cmp_handler, &cmp));\n}\n\n\nstruct sipsub *sipsub_find(struct sipevent_sock *sock,\n\t\t\t   const struct sip_msg *msg,\n\t\t\t   const struct sipevent_event *evt, bool full)\n{\n\tstruct subcmp cmp;\n\n\tcmp.msg = msg;\n\tcmp.evt = evt;\n\n\treturn list_ledata(hash_lookup(sock->ht_sub,\n\t\t\t\t       hash_joaat_pl(&msg->callid), full ?\n\t\t\t\t       sub_cmp_handler : sub_cmp_half_handler,\n\t\t\t\t       &cmp));\n}\n\n\nstatic void notify_handler(struct sipevent_sock *sock,\n\t\t\t   const struct sip_msg *msg)\n{\n\tstruct sipevent_substate state;\n\tstruct sipevent_event event;\n\tstruct sip *sip = sock->sip;\n\tconst struct sip_hdr *hdr;\n\tstruct sipsub *sub;\n\tuint32_t nrefs;\n\tchar m[256];\n\tint err;\n\n\thdr = sip_msg_hdr(msg, SIP_HDR_EVENT);\n\tif (!hdr || sipevent_event_decode(&event, &hdr->val)) {\n\t\t(void)sip_reply(sip, msg, 489, \"Bad Event\");\n\t\treturn;\n\t}\n\n\thdr = sip_msg_hdr(msg, SIP_HDR_SUBSCRIPTION_STATE);\n\tif (!hdr || sipevent_substate_decode(&state, &hdr->val)) {\n\t\t(void)sip_reply(sip, msg, 400,\"Bad Subscription-State Header\");\n\t\treturn;\n\t}\n\n\tsub = sipsub_find(sock, msg, &event, true);\n\tif (!sub) {\n\t\tsub = sipsub_find(sock, msg, &event, false);\n\t\tif (!sub) {\n\t\t\t(void)sip_reply(sip, msg,\n\t\t\t\t\t481, \"Subscription Does Not Exist\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (sub->forkh) {\n\n\t\t\tstruct sipsub *fsub;\n\n\t\t\terr = sub->forkh(&fsub, sub, msg, sub->arg);\n\t\t\tif (err) {\n\t\t\t\t(void)sip_reply(sip, msg, 500,\n\t\t\t\t\t\tstr_error(err, m, sizeof(m)));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsub = fsub;\n\t\t}\n\t\telse {\n\t\t\terr = sip_dialog_create(sub->dlg, msg);\n\t\t\tif (err) {\n\t\t\t\t(void)sip_reply(sip, msg, 500,\n\t\t\t\t\t\tstr_error(err, m, sizeof(m)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\telse {\n\t\tif (!sip_dialog_rseq_valid(sub->dlg, msg)) {\n\t\t\t(void)sip_reply(sip, msg, 500, \"Bad Sequence\");\n\t\t\treturn;\n\t\t}\n\n\t\t(void)sip_dialog_update(sub->dlg, msg);\n\t}\n\n\tif (sub->refer_cseq >= 0 && !sub->id && pl_isset(&event.id)) {\n\n\t\terr = pl_strdup(&sub->id, &event.id);\n\t\tif (err) {\n\t\t\t(void)sip_treply(NULL, sip, msg, 500,\n\t\t\t\t\t str_error(err, m, sizeof(m)));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tswitch (state.state) {\n\n\tcase SIPEVENT_ACTIVE:\n\tcase SIPEVENT_PENDING:\n\t\tif (!sub->termconf)\n\t\t\tsub->subscribed = true;\n\n\t\tif (!sub->terminated && !sub->termwait &&\n\t\t    pl_isset(&state.expires))\n\t\t\tsipsub_reschedule(sub, pl_u32(&state.expires) * 900);\n\t\tbreak;\n\n\tcase SIPEVENT_TERMINATED:\n\t\tsub->subscribed = false;\n\t\tsub->termconf = true;\n\t\tbreak;\n\t}\n\n\tmem_ref(sub);\n\tsub->notifyh(sip, msg, sub->arg);\n\tnrefs = mem_nrefs(sub);\n\tmem_deref(sub);\n\n\t/* check if subscription was deref'd from notify handler */\n\tif (nrefs == 1)\n\t\treturn;\n\n\tif (state.state == SIPEVENT_TERMINATED) {\n\n\t\tif (!sub->terminated) {\n\t\t\tsub->termwait = false;\n\t\t\tsipsub_terminate(sub, 0, msg, &state);\n\t\t}\n\t\telse if (sub->termwait) {\n\t\t\tsub->termwait = false;\n\t\t\ttmr_cancel(&sub->tmr);\n\t\t\tmem_deref(sub);\n\t\t}\n\t}\n}\n\n\nstatic void subscribe_handler(struct sipevent_sock *sock,\n\t\t\t      const struct sip_msg *msg)\n{\n\tstruct sipevent_event event;\n\tstruct sip *sip = sock->sip;\n\tconst struct sip_hdr *hdr;\n\tstruct sipnot *not;\n\tuint32_t expires;\n\n\thdr = sip_msg_hdr(msg, SIP_HDR_EVENT);\n\tif (!hdr || sipevent_event_decode(&event, &hdr->val)) {\n\t\t(void)sip_reply(sip, msg, 400, \"Bad Event Header\");\n\t\treturn;\n\t}\n\n\tnot = sipnot_find(sock, msg, &event);\n\tif (!not || not->terminated) {\n\t\t(void)sip_reply(sip, msg, 481, \"Subscription Does Not Exist\");\n\t\treturn;\n\t}\n\n\tif (pl_isset(&msg->expires))\n\t\texpires = pl_u32(&msg->expires);\n\telse\n\t\texpires = not->expires_dfl;\n\n\tif (expires > 0 && expires < not->expires_min) {\n\t\t(void)sip_replyf(sip, msg, 423, \"Interval Too Brief\",\n\t\t\t\t \"Min-Expires: %u\\r\\n\"\n\t\t\t\t \"Content-Length: 0\\r\\n\"\n\t\t\t\t \"\\r\\n\",\n\t\t\t\t not->expires_min);\n\t\treturn;\n\t}\n\n\tif (!sip_dialog_rseq_valid(not->dlg, msg)) {\n\t\t(void)sip_reply(sip, msg, 500, \"Bad Sequence\");\n\t\treturn;\n\t}\n\n\t(void)sip_dialog_update(not->dlg, msg);\n\n\tsipnot_refresh(not, expires);\n\n\t(void)sipnot_reply(not, msg, 200, \"OK\");\n\n\t(void)sipnot_notify(not);\n}\n\n\nstatic bool request_handler(const struct sip_msg *msg, void *arg)\n{\n\tstruct sipevent_sock *sock = arg;\n\n\tif (!pl_strcmp(&msg->met, \"SUBSCRIBE\")) {\n\n\t\tif (pl_isset(&msg->to.tag)) {\n\t\t\tsubscribe_handler(sock, msg);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn sock->subh ? sock->subh(msg, sock->arg) : false;\n\t}\n\telse if (!pl_strcmp(&msg->met, \"NOTIFY\")) {\n\n\t\tnotify_handler(sock, msg);\n\t\treturn true;\n\t}\n\telse {\n\t\treturn false;\n\t}\n}\n\n\nint sipevent_listen(struct sipevent_sock **sockp, struct sip *sip,\n\t\t    uint32_t htsize_not, uint32_t htsize_sub,\n\t\t    sip_msg_h *subh, void *arg)\n{\n\tstruct sipevent_sock *sock;\n\tint err;\n\n\tif (!sockp || !sip || !htsize_not || !htsize_sub)\n\t\treturn EINVAL;\n\n\tsock = mem_zalloc(sizeof(*sock), destructor);\n\tif (!sock)\n\t\treturn ENOMEM;\n\n\terr = sip_listen(&sock->lsnr, sip, true, request_handler, sock);\n\tif (err)\n\t\tgoto out;\n\n\terr = hash_alloc(&sock->ht_not, htsize_not);\n\tif (err)\n\t\tgoto out;\n\n\terr = hash_alloc(&sock->ht_sub, htsize_sub);\n\tif (err)\n\t\tgoto out;\n\n\tsock->sip  = sip;\n\tsock->subh = subh;\n\tsock->arg  = arg;\n\n out:\n\tif (err)\n\t\tmem_deref(sock);\n\telse\n\t\t*sockp = sock;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sipevent/msg.c",
    "content": "/**\n * @file sipevent/msg.c  SIP event messages\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_uri.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipevent.h>\n\n\nint sipevent_event_decode(struct sipevent_event *se, const struct pl *pl)\n{\n\tstruct pl param;\n\tint err;\n\n\tif (!se || !pl)\n\t\treturn EINVAL;\n\n\terr = re_regex(pl->p, pl->l, \"[^; \\t\\r\\n]+[ \\t\\r\\n]*[^]*\",\n\t\t       &se->event, NULL, &se->params);\n\tif (err)\n\t\treturn EBADMSG;\n\n\tif (!msg_param_decode(&se->params, \"id\", &param))\n\t\tse->id = param;\n\telse\n\t\tse->id = pl_null;\n\n\treturn 0;\n}\n\n\nint sipevent_substate_decode(struct sipevent_substate *ss, const struct pl *pl)\n{\n\tstruct pl state, param;\n\tint err;\n\n\tif (!ss || !pl)\n\t\treturn EINVAL;\n\n\terr = re_regex(pl->p, pl->l, \"[a-z]+[ \\t\\r\\n]*[^]*\",\n\t\t       &state, NULL, &ss->params);\n\tif (err)\n\t\treturn EBADMSG;\n\n\tif (!pl_strcasecmp(&state, \"active\"))\n\t\tss->state = SIPEVENT_ACTIVE;\n\telse if (!pl_strcasecmp(&state, \"pending\"))\n\t\tss->state = SIPEVENT_PENDING;\n\telse if (!pl_strcasecmp(&state, \"terminated\"))\n\t\tss->state = SIPEVENT_TERMINATED;\n\telse\n\t\tss->state = -1;\n\n\tif (!msg_param_decode(&ss->params, \"reason\", &param)) {\n\n\t\tif (!pl_strcasecmp(&param, \"deactivated\"))\n\t\t\tss->reason = SIPEVENT_DEACTIVATED;\n\t\telse if (!pl_strcasecmp(&param, \"probation\"))\n\t\t\tss->reason = SIPEVENT_PROBATION;\n\t\telse if (!pl_strcasecmp(&param, \"rejected\"))\n\t\t\tss->reason = SIPEVENT_REJECTED;\n\t\telse if (!pl_strcasecmp(&param, \"timeout\"))\n\t\t\tss->reason = SIPEVENT_TIMEOUT;\n\t\telse if (!pl_strcasecmp(&param, \"giveup\"))\n\t\t\tss->reason = SIPEVENT_GIVEUP;\n\t\telse if (!pl_strcasecmp(&param, \"noresource\"))\n\t\t\tss->reason = SIPEVENT_NORESOURCE;\n\t\telse\n\t\t\tss->reason = -1;\n\t}\n\telse {\n\t\tss->reason = -1;\n\t}\n\n\tif (!msg_param_decode(&ss->params, \"expires\", &param))\n\t\tss->expires = param;\n\telse\n\t\tss->expires = pl_null;\n\n\tif (!msg_param_decode(&ss->params, \"retry-after\", &param))\n\t\tss->retry_after = param;\n\telse\n\t\tss->retry_after = pl_null;\n\n\treturn 0;\n}\n\n\nconst char *sipevent_substate_name(enum sipevent_subst state)\n{\n\tswitch (state) {\n\n\tcase SIPEVENT_ACTIVE:     return \"active\";\n\tcase SIPEVENT_PENDING:    return \"pending\";\n\tcase SIPEVENT_TERMINATED: return \"terminated\";\n\tdefault:                  return \"unknown\";\n\t}\n}\n\n\nconst char *sipevent_reason_name(enum sipevent_reason reason)\n{\n\tswitch (reason) {\n\n\tcase SIPEVENT_DEACTIVATED: return \"deactivated\";\n\tcase SIPEVENT_PROBATION:   return \"probation\";\n\tcase SIPEVENT_REJECTED:    return \"rejected\";\n\tcase SIPEVENT_TIMEOUT:     return \"timeout\";\n\tcase SIPEVENT_GIVEUP:      return \"giveup\";\n\tcase SIPEVENT_NORESOURCE:  return \"noresource\";\n\tdefault:                   return \"unknown\";\n\t}\n}\n"
  },
  {
    "path": "src/sipevent/notify.c",
    "content": "/**\n * @file notify.c  SIP Event Notify\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_sys.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipevent.h>\n#include \"sipevent.h\"\n\n\nstatic int notify_request(struct sipnot *not, bool reset_ls);\n\n\nstatic void internal_close_handler(int err, const struct sip_msg *msg,\n\t\t\t\t   void *arg)\n{\n\t(void)err;\n\t(void)msg;\n\t(void)arg;\n}\n\n\nstatic bool terminate(struct sipnot *not, enum sipevent_reason reason)\n{\n\tnot->terminated = true;\n\tnot->reason     = reason;\n\tnot->closeh     = internal_close_handler;\n\n\tif (not->req) {\n\t\tmem_ref(not);\n\t\treturn true;\n\t}\n\n\tif (not->subscribed && !notify_request(not, true)) {\n\t\tmem_ref(not);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sipnot *not = arg;\n\n\ttmr_cancel(&not->tmr);\n\n\tif (!not->terminated) {\n\n\t\tif (terminate(not, SIPEVENT_DEACTIVATED))\n\t\t\treturn;\n\t}\n\n\thash_unlink(&not->he);\n\tmem_deref(not->req);\n\tmem_deref(not->dlg);\n\tmem_deref(not->auth);\n\tmem_deref(not->mb);\n\tmem_deref(not->event);\n\tmem_deref(not->id);\n\tmem_deref(not->cuser);\n\tmem_deref(not->hdrs);\n\tmem_deref(not->ctype);\n\tmem_deref(not->sock);\n\tmem_deref(not->sip);\n}\n\n\nstatic void sipnot_terminate(struct sipnot *not, int err,\n\t\t\t     const struct sip_msg *msg,\n\t\t\t     enum sipevent_reason reason)\n{\n\tsipnot_close_h *closeh;\n\tvoid *arg;\n\n\tcloseh = not->closeh;\n\targ    = not->arg;\n\n\ttmr_cancel(&not->tmr);\n\t(void)terminate(not, reason);\n\n\tcloseh(err, msg, arg);\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct sipnot *not = arg;\n\n\tif (not->terminated)\n\t\treturn;\n\n\tsipnot_terminate(not, ETIMEDOUT, NULL, SIPEVENT_TIMEOUT);\n}\n\n\nvoid sipnot_refresh(struct sipnot *not, uint32_t expires)\n{\n\tnot->expires = min(expires, not->expires_max);\n\n\ttmr_start(&not->tmr, not->expires * 1000, tmr_handler, not);\n}\n\n\nstatic void response_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct sipnot *not = arg;\n\n\tif (err) {\n\t\tif (err == ETIMEDOUT)\n\t\t\tnot->subscribed = false;\n\t\tgoto out;\n\t}\n\n\tif (sip_request_loops(&not->ls, msg->scode)) {\n\t\tnot->subscribed = false;\n\t\tgoto out;\n\t}\n\n\tif (msg->scode < 200) {\n\t\treturn;\n\t}\n\telse if (msg->scode < 300) {\n\n\t\t(void)sip_dialog_update(not->dlg, msg);\n\t}\n\telse {\n\t\tswitch (msg->scode) {\n\n\t\tcase 401:\n\t\tcase 407:\n\t\t\terr = sip_auth_authenticate(not->auth, msg);\n\t\t\tif (err) {\n\t\t\t\terr = (err == EAUTH) ? 0 : err;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\terr = notify_request(not, false);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\n\t\t\treturn;\n\t\t}\n\n\t\tnot->subscribed = false;\n\t}\n\n out:\n\tif (not->termsent) {\n\t\tmem_deref(not);\n\t}\n\telse if (not->terminated) {\n\t\tif (!not->subscribed || notify_request(not, true))\n\t\t\tmem_deref(not);\n\t}\n\telse if (!not->subscribed) {\n\t\tsipnot_terminate(not, err, msg, -1);\n\t}\n\telse if (not->notify_pending) {\n\t\t(void)notify_request(not, true);\n\t}\n}\n\n\nstatic int send_handler(enum sip_transp tp, struct sa *src,\n\t\t\tconst struct sa *dst, struct mbuf *mb,\n\t\t\tstruct mbuf **contp, void *arg)\n{\n\tstruct sip_contact contact;\n\tstruct sipnot *not = arg;\n\t(void)dst;\n\t(void)contp;\n\n\tsip_contact_set(&contact, not->cuser, src, tp);\n\n\treturn mbuf_printf(mb, \"%H\", sip_contact_print, &contact);\n}\n\n\nstatic int print_event(struct re_printf *pf, const struct sipnot *not)\n{\n\tif (not->id)\n\t\treturn re_hprintf(pf, \"%s;id=%s\", not->event, not->id);\n\telse\n\t\treturn re_hprintf(pf, \"%s\", not->event);\n}\n\n\nstatic int print_substate(struct re_printf *pf, const struct sipnot *not)\n{\n\tint err;\n\n\tif (not->terminated) {\n\n\t\terr = re_hprintf(pf, \"terminated;reason=%s\",\n\t\t\t\t sipevent_reason_name(not->reason));\n\n\t\tif (not->retry_after)\n\t\t\terr |= re_hprintf(pf, \";retry-after=%u\",\n\t\t\t\t\t  not->retry_after);\n\t}\n\telse {\n\t\tuint32_t expires;\n\n\t\texpires = (uint32_t)(tmr_get_expire(&not->tmr) / 1000);\n\n\t\terr = re_hprintf(pf, \"%s;expires=%u\",\n\t\t\t\t sipevent_substate_name(not->substate),\n\t\t\t\t expires);\n\t}\n\n\treturn err;\n}\n\n\nstatic int print_content(struct re_printf *pf, const struct sipnot *not)\n{\n\tif (!not->mb)\n\t\treturn re_hprintf(pf,\n\t\t\t\t  \"Content-Length: 0\\r\\n\"\n\t\t\t\t  \"\\r\\n\");\n\telse\n\t\treturn re_hprintf(pf,\n\t\t\t\t  \"Content-Type: %s\\r\\n\"\n\t\t\t\t  \"Content-Length: %zu\\r\\n\"\n\t\t\t\t  \"\\r\\n\"\n\t\t\t\t  \"%b\",\n\t\t\t\t  not->ctype,\n\t\t\t\t  mbuf_get_left(not->mb),\n\t\t\t\t  mbuf_buf(not->mb),\n\t\t\t\t  mbuf_get_left(not->mb));\n}\n\n\nstatic int notify_request(struct sipnot *not, bool reset_ls)\n{\n\tif (reset_ls)\n\t\tsip_loopstate_reset(&not->ls);\n\n\tif (not->terminated)\n\t\tnot->termsent = true;\n\n\tnot->notify_pending = false;\n\n\treturn sip_drequestf(&not->req, not->sip, true, \"NOTIFY\",\n\t\t\t     not->dlg, 0, not->auth,\n\t\t\t     send_handler, response_handler, not,\n\t\t\t     \"Event: %H\\r\\n\"\n\t\t\t     \"Subscription-State: %H\\r\\n\"\n\t\t\t     \"%s\"\n\t\t\t     \"%H\",\n\t\t\t     print_event, not,\n\t\t\t     print_substate, not,\n\t\t\t     not->hdrs,\n\t\t\t     print_content, not);\n}\n\n\nint sipnot_notify(struct sipnot *not)\n{\n\tif (not->expires == 0) {\n\t\treturn 0;\n\t}\n\n\tif (not->req) {\n\t\tnot->notify_pending = true;\n\t\treturn 0;\n\t}\n\n\treturn notify_request(not, true);\n}\n\n\nint sipnot_reply(struct sipnot *not, const struct sip_msg *msg,\n\t\t uint16_t scode, const char *reason)\n{\n\tstruct sip_contact contact;\n\tuint32_t expires;\n\n\texpires = (uint32_t)(tmr_get_expire(&not->tmr) / 1000);\n\n\tsip_contact_set(&contact, not->cuser, &msg->dst, msg->tp);\n\n\treturn sip_treplyf(NULL, NULL, not->sip, msg, true, scode, reason,\n\t\t\t   \"%H\"\n\t\t\t   \"Expires: %u\\r\\n\"\n\t\t\t   \"Content-Length: 0\\r\\n\"\n\t\t\t   \"\\r\\n\",\n\t\t\t   sip_contact_print, &contact,\n\t\t\t   expires);\n}\n\n\nint sipevent_accept(struct sipnot **notp, struct sipevent_sock *sock,\n\t\t    const struct sip_msg *msg, struct sip_dialog *dlg,\n\t\t    const struct sipevent_event *event,\n\t\t    uint16_t scode, const char *reason, uint32_t expires_min,\n\t\t    uint32_t expires_dfl, uint32_t expires_max,\n\t\t    const char *cuser, const char *ctype,\n\t\t    sip_auth_h *authh, void *aarg, bool aref,\n\t\t    sipnot_close_h *closeh, void *arg, const char *fmt, ...)\n{\n\tstruct sipnot *not;\n\tuint32_t expires;\n\tint err;\n\n\tif (!notp || !sock || !msg || !scode || !reason || !expires_dfl ||\n\t    !expires_max || !cuser || !ctype || expires_dfl < expires_min)\n\t\treturn EINVAL;\n\n\tnot = mem_zalloc(sizeof(*not), destructor);\n\tif (!not)\n\t\treturn ENOMEM;\n\n\tif (!pl_strcmp(&msg->met, \"REFER\")) {\n\n\t\terr = str_dup(&not->event, \"refer\");\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = re_sdprintf(&not->id, \"%u\", msg->cseq.num);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse {\n\t\tif (!event) {\n\t\t\terr = EINVAL;\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = pl_strdup(&not->event, &event->event);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (pl_isset(&event->id)) {\n\n\t\t\terr = pl_strdup(&not->id, &event->id);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t}\n\n\tif (dlg) {\n\t\tnot->dlg = mem_ref(dlg);\n\t}\n\telse {\n\t\terr = sip_dialog_accept(&not->dlg, msg);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\thash_append(sock->ht_not,\n\t\t    hash_joaat_str(sip_dialog_callid(not->dlg)),\n\t\t    &not->he, not);\n\n\terr = sip_auth_alloc(&not->auth, authh, aarg, aref);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_dup(&not->cuser, cuser);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_dup(&not->ctype, ctype);\n\tif (err)\n\t\tgoto out;\n\n\tif (fmt) {\n\t\tva_list ap;\n\n\t\tva_start(ap, fmt);\n\t\terr = re_vsdprintf(&not->hdrs, fmt, ap);\n\t\tva_end(ap);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\tnot->expires_min = expires_min;\n\tnot->expires_dfl = expires_dfl;\n\tnot->expires_max = expires_max;\n\tnot->substate = SIPEVENT_PENDING;\n\tnot->sock   = mem_ref(sock);\n\tnot->sip    = mem_ref(sock->sip);\n\tnot->closeh = closeh ? closeh : internal_close_handler;\n\tnot->arg    = arg;\n\n\tif (pl_isset(&msg->expires))\n\t\texpires = pl_u32(&msg->expires);\n\telse\n\t\texpires = not->expires_dfl;\n\n\tsipnot_refresh(not, expires);\n\n\terr = sipnot_reply(not, msg, scode, reason);\n\tif (err)\n\t\tgoto out;\n\n\tnot->subscribed = true;\n\n out:\n\tif (err)\n\t\tmem_deref(not);\n\telse\n\t\t*notp = not;\n\n\treturn err;\n}\n\n\nint sipevent_notify(struct sipnot *not, struct mbuf *mb,\n\t\t    enum sipevent_subst state, enum sipevent_reason reason,\n\t\t    uint32_t retry_after)\n{\n\tif (!not || not->terminated)\n\t\treturn EINVAL;\n\n\tif (mb || state != SIPEVENT_TERMINATED) {\n\t\tmem_deref(not->mb);\n\t\tnot->mb = mem_ref(mb);\n\t}\n\n\tswitch (state) {\n\n\tcase SIPEVENT_ACTIVE:\n\tcase SIPEVENT_PENDING:\n\t\tnot->substate = state;\n\t\treturn sipnot_notify(not);\n\n\tcase SIPEVENT_TERMINATED:\n\t\ttmr_cancel(&not->tmr);\n\t\tnot->retry_after = retry_after;\n\t\t(void)terminate(not, reason);\n\t\treturn 0;\n\n\tdefault:\n\t\treturn EINVAL;\n\t}\n}\n\n\nint sipevent_notifyf(struct sipnot *not, struct mbuf **mbp,\n\t\t     enum sipevent_subst state, enum sipevent_reason reason,\n\t\t     uint32_t retry_after, const char *fmt, ...)\n{\n\tstruct mbuf *mb;\n\tva_list ap;\n\tint err;\n\n\tif (!not || not->terminated || !fmt)\n\t\treturn EINVAL;\n\n\tif (mbp && *mbp)\n\t\treturn sipevent_notify(not, *mbp, state, reason, retry_after);\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tva_start(ap, fmt);\n\terr = mbuf_vprintf(mb, fmt, ap);\n\tva_end(ap);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\n\terr = sipevent_notify(not, mb, state, reason, retry_after);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err || !mbp)\n\t\tmem_deref(mb);\n\telse\n\t\t*mbp = mb;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sipevent/sipevent.h",
    "content": "/**\n * @file sipevent.h  SIP Event Private Interface\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n/* Listener Socket */\n\nstruct sipevent_sock {\n\tstruct sip_lsnr *lsnr;\n\tstruct hash *ht_not;\n\tstruct hash *ht_sub;\n\tstruct sip *sip;\n\tsip_msg_h *subh;\n\tvoid *arg;\n};\n\n\n/* Notifier */\n\nstruct sipnot {\n\tstruct le he;\n\tstruct sip_loopstate ls;\n\tstruct tmr tmr;\n\tstruct sipevent_sock *sock;\n\tstruct sip_request *req;\n\tstruct sip_dialog *dlg;\n\tstruct sip_auth *auth;\n\tstruct sip *sip;\n\tstruct mbuf *mb;\n\tchar *event;\n\tchar *id;\n\tchar *cuser;\n\tchar *hdrs;\n\tchar *ctype;\n\tsipnot_close_h *closeh;\n\tvoid *arg;\n\tuint32_t expires;\n\tuint32_t expires_min;\n\tuint32_t expires_dfl;\n\tuint32_t expires_max;\n\tuint32_t retry_after;\n\tenum sipevent_subst substate;\n\tenum sipevent_reason reason;\n\tbool notify_pending;\n\tbool subscribed;\n\tbool terminated;\n\tbool termsent;\n};\n\nvoid sipnot_refresh(struct sipnot *not, uint32_t expires);\nint  sipnot_notify(struct sipnot *not);\nint  sipnot_reply(struct sipnot *not, const struct sip_msg *msg,\n\t\t  uint16_t scode, const char *reason);\n\n\n/* Subscriber */\n\nstruct sipsub {\n\tstruct le he;\n\tstruct sip_loopstate ls;\n\tstruct tmr tmr;\n\tstruct sipevent_sock *sock;\n\tstruct sip_request *req;\n\tstruct sip_dialog *dlg;\n\tstruct sip_auth *auth;\n\tstruct sip *sip;\n\tchar *event;\n\tchar *id;\n\tchar *cuser;\n\tchar *hdrs;\n\tchar *refer_hdrs;\n\tsipsub_fork_h *forkh;\n\tsipsub_notify_h *notifyh;\n\tsipsub_close_h *closeh;\n\tvoid *arg;\n\tint32_t refer_cseq;\n\tuint32_t expires;\n\tuint32_t failc;\n\tbool subscribed;\n\tbool terminated;\n\tbool termconf;\n\tbool termwait;\n\tbool refer;\n};\n\nstruct sipsub *sipsub_find(struct sipevent_sock *sock,\n\t\t\t   const struct sip_msg *msg,\n\t\t\t   const struct sipevent_event *evt, bool full);\nvoid sipsub_reschedule(struct sipsub *sub, uint64_t wait);\nvoid sipsub_terminate(struct sipsub *sub, int err, const struct sip_msg *msg,\n\t\t      const struct sipevent_substate *substate);\n"
  },
  {
    "path": "src/sipevent/subscribe.c",
    "content": "/**\n * @file subscribe.c  SIP Event Subscribe\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_sys.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipevent.h>\n#include \"sipevent.h\"\n\n\nenum {\n\tDEFAULT_EXPIRES = 3600,\n\tRESUB_FAIL_WAIT = 60000,\n\tRESUB_FAILC_MAX = 7,\n\tNOTIFY_TIMEOUT  = 10000,\n};\n\n\nstatic int request(struct sipsub *sub, bool reset_ls);\n\n\nstatic void internal_notify_handler(struct sip *sip, const struct sip_msg *msg,\n\t\t\t\t    void *arg)\n{\n\t(void)arg;\n\n\t(void)sip_treply(NULL, sip, msg, 200, \"OK\");\n}\n\n\nstatic void internal_close_handler(int err, const struct sip_msg *msg,\n\t\t\t\t   const struct sipevent_substate *substate,\n\t\t\t\t   void *arg)\n{\n\t(void)err;\n\t(void)msg;\n\t(void)substate;\n\t(void)arg;\n}\n\n\nstatic bool terminate(struct sipsub *sub)\n{\n\tsub->terminated = true;\n\tsub->forkh      = NULL;\n\tsub->notifyh    = internal_notify_handler;\n\tsub->closeh     = internal_close_handler;\n\n\tif (sub->termwait) {\n\t\tmem_ref(sub);\n\t\treturn true;\n\t}\n\n\ttmr_cancel(&sub->tmr);\n\n\tif (sub->req) {\n\t\tmem_ref(sub);\n\t\treturn true;\n\t}\n\n\tif (sub->expires && sub->subscribed && !request(sub, true)) {\n\t\tmem_ref(sub);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sipsub *sub = arg;\n\n\tif (!sub->terminated) {\n\n\t\tif (terminate(sub))\n\t\t\treturn;\n\t}\n\n\ttmr_cancel(&sub->tmr);\n\thash_unlink(&sub->he);\n\tmem_deref(sub->req);\n\tmem_deref(sub->dlg);\n\tmem_deref(sub->auth);\n\tmem_deref(sub->event);\n\tmem_deref(sub->id);\n\tmem_deref(sub->cuser);\n\tmem_deref(sub->hdrs);\n\tmem_deref(sub->refer_hdrs);\n\tmem_deref(sub->sock);\n\tmem_deref(sub->sip);\n}\n\n\nstatic void notify_timeout_handler(void *arg)\n{\n\tstruct sipsub *sub = arg;\n\n\tsub->termwait = false;\n\n\tif (sub->terminated)\n\t\tmem_deref(sub);\n\telse\n\t\tsipsub_terminate(sub, ETIMEDOUT, NULL, NULL);\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct sipsub *sub = arg;\n\tint err;\n\n\tif (sub->req || sub->terminated)\n\t\treturn;\n\n\terr = request(sub, true);\n\tif (err) {\n\t\tif (++sub->failc < RESUB_FAILC_MAX) {\n\t\t\tsipsub_reschedule(sub, RESUB_FAIL_WAIT);\n\t\t}\n\t\telse {\n\t\t\tsipsub_terminate(sub, err, NULL, NULL);\n\t\t}\n\t}\n}\n\n\nvoid sipsub_reschedule(struct sipsub *sub, uint64_t wait)\n{\n\ttmr_start(&sub->tmr, wait, tmr_handler, sub);\n}\n\n\nvoid sipsub_terminate(struct sipsub *sub, int err, const struct sip_msg *msg,\n\t\t      const struct sipevent_substate *substate)\n{\n\tsipsub_close_h *closeh;\n\tvoid *arg;\n\n\tcloseh = sub->closeh;\n\targ    = sub->arg;\n\n\t(void)terminate(sub);\n\n\tcloseh(err, msg, substate, arg);\n}\n\n\nstatic void response_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tconst struct sip_hdr *minexp;\n\tstruct sipsub *sub = arg;\n\n\tif (err || sip_request_loops(&sub->ls, msg->scode))\n\t\tgoto out;\n\n\tif (msg->scode < 200) {\n\t\treturn;\n\t}\n\telse if (msg->scode < 300) {\n\n\t\tuint32_t wait;\n\n\t\tif (sub->forkh) {\n\n\t\t\tstruct sipsub *fsub;\n\n\t\t\tfsub = sipsub_find(sub->sock, msg, NULL, true);\n\t\t\tif (!fsub) {\n\n\t\t\t\terr = sub->forkh(&fsub, sub, msg, sub->arg);\n\t\t\t\tif (err)\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t(void)sip_dialog_update(fsub->dlg, msg);\n\t\t\t}\n\n\t\t\tsub = fsub;\n\t\t}\n\t\telse if (!sip_dialog_established(sub->dlg)) {\n\n\t\t\terr = sip_dialog_create(sub->dlg, msg);\n\t\t\tif (err) {\n\t\t\t\tsub->subscribed = false;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t/* Ignore 2xx responses for other dialogs\n\t\t\t * if forking is disabled */\n\t\t\tif (!sip_dialog_cmp(sub->dlg, msg))\n\t\t\t\treturn;\n\n\t\t\t(void)sip_dialog_update(sub->dlg, msg);\n\t\t}\n\n\t\tif (!sub->termconf)\n\t\t\tsub->subscribed = true;\n\n\t\tsub->failc = 0;\n\n\t\tif (!sub->expires && !sub->termconf) {\n\n\t\t\ttmr_start(&sub->tmr, NOTIFY_TIMEOUT,\n\t\t\t\t  notify_timeout_handler, sub);\n\t\t\tsub->termwait = true;\n\t\t\treturn;\n\t\t}\n\n\t\tif (sub->terminated)\n\t\t\tgoto out;\n\n\t\tif (sub->refer) {\n\t\t\tsub->refer = false;\n\t\t\treturn;\n\t\t}\n\n\t\tif (pl_isset(&msg->expires))\n\t\t\twait = pl_u32(&msg->expires);\n\t\telse\n\t\t\twait = sub->expires;\n\n\t\tsipsub_reschedule(sub, wait * 900);\n\t\treturn;\n\t}\n\telse {\n\t\tif (sub->terminated && !sub->subscribed)\n\t\t\tgoto out;\n\n\t\tswitch (msg->scode) {\n\n\t\tcase 401:\n\t\tcase 407:\n\t\t\terr = sip_auth_authenticate(sub->auth, msg);\n\t\t\tif (err) {\n\t\t\t\terr = (err == EAUTH) ? 0 : err;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\terr = request(sub, false);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\n\t\t\treturn;\n\n\t\tcase 403:\n\t\t\tsip_auth_reset(sub->auth);\n\t\t\tbreak;\n\n\t\tcase 423:\n\t\t\tminexp = sip_msg_hdr(msg, SIP_HDR_MIN_EXPIRES);\n\t\t\tif (!minexp || !pl_u32(&minexp->val) || !sub->expires)\n\t\t\t\tbreak;\n\n\t\t\tsub->expires = pl_u32(&minexp->val);\n\n\t\t\terr = request(sub, false);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\n\t\t\treturn;\n\n\t\tcase 481:\n\t\t\tsub->subscribed = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n out:\n\tsub->refer = false;\n\n\tif (sub->terminated) {\n\n\t\tif (!sub->expires || !sub->subscribed || request(sub, true))\n\t\t\tmem_deref(sub);\n\t}\n\telse {\n\t\tif (sub->subscribed && ++sub->failc < RESUB_FAILC_MAX)\n\t\t\tsipsub_reschedule(sub, RESUB_FAIL_WAIT);\n\t\telse\n\t\t\tsipsub_terminate(sub, err, msg, NULL);\n\t}\n}\n\n\nstatic int send_handler(enum sip_transp tp, struct sa *src,\n\t\t\tconst struct sa *dst, struct mbuf *mb,\n\t\t\tstruct mbuf **contp, void *arg)\n{\n\tstruct sip_contact contact;\n\tstruct sipsub *sub = arg;\n\t(void)dst;\n\t(void)contp;\n\n\tsip_contact_set(&contact, sub->cuser, src, tp);\n\n\treturn mbuf_printf(mb, \"%H\", sip_contact_print, &contact);\n}\n\n\nstatic int print_event(struct re_printf *pf, const struct sipsub *sub)\n{\n\tif (sub->id)\n\t\treturn re_hprintf(pf, \"%s;id=%s\", sub->event, sub->id);\n\telse\n\t\treturn re_hprintf(pf, \"%s\", sub->event);\n}\n\n\nstatic int request(struct sipsub *sub, bool reset_ls)\n{\n\tif (reset_ls)\n\t\tsip_loopstate_reset(&sub->ls);\n\n\tif (sub->refer) {\n\n\t\tsub->refer_cseq = sip_dialog_lseq(sub->dlg);\n\n\t\treturn sip_drequestf(&sub->req, sub->sip, true, \"REFER\",\n\t\t\t\t     sub->dlg, 0, sub->auth,\n\t\t\t\t     send_handler, response_handler, sub,\n\t\t\t\t     \"%s\"\n\t\t\t\t     \"Content-Length: 0\\r\\n\"\n\t\t\t\t     \"\\r\\n\",\n\t\t\t\t     sub->refer_hdrs);\n\t}\n\telse {\n\t\tif (sub->terminated)\n\t\t\tsub->expires = 0;\n\n\t\treturn sip_drequestf(&sub->req, sub->sip, true, \"SUBSCRIBE\",\n\t\t\t\t     sub->dlg, 0, sub->auth,\n\t\t\t\t     send_handler, response_handler, sub,\n\t\t\t\t     \"Event: %H\\r\\n\"\n\t\t\t\t     \"Expires: %u\\r\\n\"\n\t\t\t\t     \"%s\"\n\t\t\t\t     \"Content-Length: 0\\r\\n\"\n\t\t\t\t     \"\\r\\n\",\n\t\t\t\t     print_event, sub,\n\t\t\t\t     sub->expires,\n\t\t\t\t     sub->hdrs);\n\t}\n}\n\n\nstatic int sipsub_alloc(struct sipsub **subp, struct sipevent_sock *sock,\n\t\t\tbool refer, struct sip_dialog *dlg, const char *uri,\n\t\t\tconst char *from_name, const char *from_uri,\n\t\t\tconst char *event, const char *id, uint32_t expires,\n\t\t\tconst char *cuser,\n\t\t\tconst char *routev[], uint32_t routec,\n\t\t\tsip_auth_h *authh, void *aarg, bool aref,\n\t\t\tsipsub_fork_h *forkh, sipsub_notify_h *notifyh,\n\t\t\tsipsub_close_h *closeh, void *arg,\n\t\t\tconst char *fmt, va_list ap)\n{\n\tstruct sipsub *sub;\n\tint err;\n\n\tif (!subp || !sock || !event || !cuser)\n\t\treturn EINVAL;\n\n\tif (!dlg && (!uri || !from_uri))\n\t\treturn EINVAL;\n\n\tsub = mem_zalloc(sizeof(*sub), destructor);\n\tif (!sub)\n\t\treturn ENOMEM;\n\n\tif (dlg) {\n\t\tsub->dlg = mem_ref(dlg);\n\t}\n\telse {\n\t\terr = sip_dialog_alloc(&sub->dlg, uri, uri, from_name,\n\t\t\t\t       from_uri, routev, routec);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\thash_append(sock->ht_sub,\n\t\t    hash_joaat_str(sip_dialog_callid(sub->dlg)),\n\t\t    &sub->he, sub);\n\n\terr = sip_auth_alloc(&sub->auth, authh, aarg, aref);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_dup(&sub->event, event);\n\tif (err)\n\t\tgoto out;\n\n\tif (id) {\n\t\terr = str_dup(&sub->id, id);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = str_dup(&sub->cuser, cuser);\n\tif (err)\n\t\tgoto out;\n\n\tif (fmt) {\n\t\terr = re_vsdprintf(refer ? &sub->refer_hdrs : &sub->hdrs,\n\t\t\t\t   fmt, ap);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\tsub->refer_cseq = -1;\n\tsub->refer   = refer;\n\tsub->sock    = mem_ref(sock);\n\tsub->sip     = mem_ref(sock->sip);\n\tsub->expires = expires;\n\tsub->forkh   = forkh;\n\tsub->notifyh = notifyh ? notifyh : internal_notify_handler;\n\tsub->closeh  = closeh  ? closeh  : internal_close_handler;\n\tsub->arg     = arg;\n\n\terr = request(sub, true);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(sub);\n\telse\n\t\t*subp = sub;\n\n\treturn err;\n}\n\n\n/**\n * Allocate a SIP subscriber client\n *\n * @param subp      Pointer to allocated SIP subscriber client\n * @param sock      SIP Event socket\n * @param uri       SIP Request URI\n * @param from_name SIP From-header Name (optional)\n * @param from_uri  SIP From-header URI\n * @param event     SIP Event to subscribe to\n * @param id        SIP Event ID (optional)\n * @param expires   Subscription expires value\n * @param cuser     Contact username or URI\n * @param routev    Optional route vector\n * @param routec    Number of routes\n * @param authh     Authentication handler\n * @param aarg      Authentication handler argument\n * @param aref      True to ref argument\n * @param forkh     Fork handler\n * @param notifyh   Notify handler\n * @param closeh    Close handler\n * @param arg       Response handler argument\n * @param fmt       Formatted strings with extra SIP Headers\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipevent_subscribe(struct sipsub **subp, struct sipevent_sock *sock,\n\t\t       const char *uri, const char *from_name,\n\t\t       const char *from_uri, const char *event, const char *id,\n\t\t       uint32_t expires, const char *cuser,\n\t\t       const char *routev[], uint32_t routec,\n\t\t       sip_auth_h *authh, void *aarg, bool aref,\n\t\t       sipsub_fork_h *forkh, sipsub_notify_h *notifyh,\n\t\t       sipsub_close_h *closeh, void *arg,\n\t\t       const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = sipsub_alloc(subp, sock, false, NULL, uri, from_name, from_uri,\n\t\t\t   event, id, expires, cuser,\n\t\t\t   routev, routec, authh, aarg, aref, forkh, notifyh,\n\t\t\t   closeh, arg, fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Allocate a SIP subscriber client using an existing dialog\n *\n * @param subp      Pointer to allocated SIP subscriber client\n * @param sock      SIP Event socket\n * @param dlg       Established SIP Dialog\n * @param event     SIP Event to subscribe to\n * @param id        SIP Event ID (optional)\n * @param expires   Subscription expires value\n * @param cuser     Contact username or URI\n * @param authh     Authentication handler\n * @param aarg      Authentication handler argument\n * @param aref      True to ref argument\n * @param notifyh   Notify handler\n * @param closeh    Close handler\n * @param arg       Response handler argument\n * @param fmt       Formatted strings with extra SIP Headers\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipevent_dsubscribe(struct sipsub **subp, struct sipevent_sock *sock,\n\t\t\tstruct sip_dialog *dlg, const char *event,\n\t\t\tconst char *id, uint32_t expires, const char *cuser,\n\t\t\tsip_auth_h *authh, void *aarg, bool aref,\n\t\t\tsipsub_notify_h *notifyh, sipsub_close_h *closeh,\n\t\t\tvoid *arg, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = sipsub_alloc(subp, sock, false, dlg, NULL, NULL, NULL,\n\t\t\t   event, id, expires, cuser,\n\t\t\t   NULL, 0, authh, aarg, aref, NULL, notifyh,\n\t\t\t   closeh, arg, fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Allocate a SIP refer client\n *\n * @param subp      Pointer to allocated SIP subscriber client\n * @param sock      SIP Event socket\n * @param uri       SIP Request URI\n * @param from_name SIP From-header Name (optional)\n * @param from_uri  SIP From-header URI\n * @param cuser     Contact username or URI\n * @param routev    Optional route vector\n * @param routec    Number of routes\n * @param authh     Authentication handler\n * @param aarg      Authentication handler argument\n * @param aref      True to ref argument\n * @param forkh     Fork handler\n * @param notifyh   Notify handler\n * @param closeh    Close handler\n * @param arg       Response handler argument\n * @param fmt       Formatted strings with extra SIP Headers\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipevent_refer(struct sipsub **subp, struct sipevent_sock *sock,\n\t\t   const char *uri, const char *from_name,\n\t\t   const char *from_uri, const char *cuser,\n\t\t   const char *routev[], uint32_t routec,\n\t\t   sip_auth_h *authh, void *aarg, bool aref,\n\t\t   sipsub_fork_h *forkh, sipsub_notify_h *notifyh,\n\t\t   sipsub_close_h *closeh, void *arg,\n\t\t   const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = sipsub_alloc(subp, sock, true, NULL, uri, from_name, from_uri,\n\t\t\t   \"refer\", NULL, DEFAULT_EXPIRES, cuser,\n\t\t\t   routev, routec, authh, aarg, aref, forkh, notifyh,\n\t\t\t   closeh, arg, fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Allocate a SIP refer client using an existing dialog\n *\n * @param subp      Pointer to allocated SIP subscriber client\n * @param sock      SIP Event socket\n * @param dlg       Established SIP Dialog\n * @param cuser     Contact username or URI\n * @param authh     Authentication handler\n * @param aarg      Authentication handler argument\n * @param aref      True to ref argument\n * @param notifyh   Notify handler\n * @param closeh    Close handler\n * @param arg       Response handler argument\n * @param fmt       Formatted strings with extra SIP Headers\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipevent_drefer(struct sipsub **subp, struct sipevent_sock *sock,\n\t\t    struct sip_dialog *dlg, const char *cuser,\n\t\t    sip_auth_h *authh, void *aarg, bool aref,\n\t\t    sipsub_notify_h *notifyh, sipsub_close_h *closeh,\n\t\t    void *arg, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = sipsub_alloc(subp, sock, true, dlg, NULL, NULL, NULL,\n\t\t\t   \"refer\", NULL, DEFAULT_EXPIRES, cuser,\n\t\t\t   NULL, 0, authh, aarg, aref, NULL, notifyh,\n\t\t\t   closeh, arg, fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\nint sipevent_fork(struct sipsub **subp, struct sipsub *osub,\n\t\t  const struct sip_msg *msg,\n\t\t  sip_auth_h *authh, void *aarg, bool aref,\n\t\t  sipsub_notify_h *notifyh, sipsub_close_h *closeh,\n\t\t  void *arg)\n{\n\tstruct sipsub *sub;\n\tint err;\n\n\tif (!subp || !osub || !msg)\n\t\treturn EINVAL;\n\n\tsub = mem_zalloc(sizeof(*sub), destructor);\n\tif (!sub)\n\t\treturn ENOMEM;\n\n\terr = sip_dialog_fork(&sub->dlg, osub->dlg, msg);\n\tif (err)\n\t\tgoto out;\n\n\thash_append(osub->sock->ht_sub,\n\t\t    hash_joaat_str(sip_dialog_callid(sub->dlg)),\n\t\t    &sub->he, sub);\n\n\terr = sip_auth_alloc(&sub->auth, authh, aarg, aref);\n\tif (err)\n\t\tgoto out;\n\n\tsub->event   = mem_ref(osub->event);\n\tsub->id      = mem_ref(osub->id);\n\tsub->cuser   = mem_ref(osub->cuser);\n\tsub->hdrs    = mem_ref(osub->hdrs);\n\tsub->refer   = osub->refer;\n\tsub->sock    = mem_ref(osub->sock);\n\tsub->sip     = mem_ref(osub->sip);\n\tsub->expires = osub->expires;\n\tsub->forkh   = NULL;\n\tsub->notifyh = notifyh ? notifyh : internal_notify_handler;\n\tsub->closeh  = closeh  ? closeh  : internal_close_handler;\n\tsub->arg     = arg;\n\n\tif (!sub->expires) {\n\t\ttmr_start(&sub->tmr, NOTIFY_TIMEOUT,\n\t\t\t  notify_timeout_handler, sub);\n\t\tsub->termwait = true;\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(sub);\n\telse\n\t\t*subp = sub;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sipreg/reg.c",
    "content": "/**\n * @file reg.c  SIP Registration\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_sys.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipreg.h>\n\n\nenum {\n\tDEFAULT_EXPIRES = 3600,\n};\n\n\n/** Defines a SIP Registration client */\nstruct sipreg {\n\tstruct sip_loopstate ls;\n\tstruct sa laddr;\n\tstruct tmr tmr;\n\tstruct sip *sip;\n\tstruct sip_keepalive *ka;\n\tstruct sip_request *req;\n\tstruct sip_dialog *dlg;\n\tstruct sip_auth *auth;\n\tstruct mbuf *hdrs;\n\tchar *cuser;\n\tchar *cparams;\n\tsip_resp_h *resph;\n\tvoid *arg;\n\tuint32_t expires;\n\tuint32_t pexpires;\n\tuint32_t failc;\n\tuint32_t rwait;\n\tuint32_t wait;\n\tuint32_t fbregint;\n\tenum sip_transp tp;\n\tbool registered;\n\tbool terminated;\n\tchar *params;\n\tint regid;\n\tuint16_t srcport;\n};\n\n\nstatic int request(struct sipreg *reg, bool reset_ls);\n\n\nstatic void dummy_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\t(void)err;\n\t(void)msg;\n\t(void)arg;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sipreg *reg = arg;\n\n\ttmr_cancel(&reg->tmr);\n\n\tif (!reg->terminated) {\n\n\t\treg->resph = dummy_handler;\n\t\treg->terminated = true;\n\n\t\tif (reg->req) {\n\t\t\tmem_ref(reg);\n\t\t\treturn;\n\t\t}\n\n\t\tif (reg->registered && !request(reg, true)) {\n\t\t\tmem_ref(reg);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tmem_deref(reg->ka);\n\tmem_deref(reg->dlg);\n\tmem_deref(reg->auth);\n\tmem_deref(reg->cuser);\n\tmem_deref(reg->sip);\n\tmem_deref(reg->hdrs);\n\tmem_deref(reg->params);\n\tmem_deref(reg->cparams);\n}\n\n\nstatic uint32_t failwait(uint32_t failc)\n{\n\treturn min(1800, (30 * (1<<min(failc, 6)))) * (500 + rand_u16() % 501);\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct sipreg *reg = arg;\n\tint err;\n\n\terr = request(reg, true);\n\tif (err) {\n\t\ttmr_start(&reg->tmr, failwait(++reg->failc), tmr_handler, reg);\n\t\treg->resph(err, NULL, reg->arg);\n\t}\n}\n\n\nstatic void keepalive_handler(int err, void *arg)\n{\n\tstruct sipreg *reg = arg;\n\n\t/* failure will be handled in response handler */\n\tif (reg->req || reg->terminated)\n\t\treturn;\n\n\ttmr_start(&reg->tmr, failwait(++reg->failc), tmr_handler, reg);\n\treg->resph(err, NULL, reg->arg);\n}\n\n\nstatic void start_outbound(struct sipreg *reg, const struct sip_msg *msg)\n{\n\tconst struct sip_hdr *flowtimer;\n\n\tif (!sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, \"outbound\"))\n\t\treturn;\n\n\tflowtimer = sip_msg_hdr(msg, SIP_HDR_FLOW_TIMER);\n\n\t(void)sip_keepalive_start(&reg->ka, reg->sip, msg,\n\t\t\t\t  flowtimer ? pl_u32(&flowtimer->val) : 0,\n\t\t\t\t  keepalive_handler, reg);\n}\n\n\nstatic bool contact_handler(const struct sip_hdr *hdr,\n\t\t\t    const struct sip_msg *msg, void *arg)\n{\n\tstruct sipreg *reg = arg;\n\tstruct sip_addr c;\n\tstruct pl transp = PL(\"transport\");\n\tstruct pl pval;\n\tstruct sa host;\n\tenum sip_transp tp;\n\tint err;\n\n\tif (sip_addr_decode(&c, &hdr->val))\n\t\treturn false;\n\n\tif (pl_strcmp(&c.uri.user, reg->cuser))\n\t    return false;\n\n\terr = sa_set(&host, &c.uri.host, c.uri.port);\n\tif (err)\n\t\treturn false;\n\n\tif (!sa_cmp(&host, &reg->laddr, SA_ADDR))\n\t\treturn false;\n\n\terr = uri_param_get(&c.auri, &transp, &pval);\n\tif (err)\n\t\tpl_set_str(&pval, \"udp\");\n\n\ttp = sip_transp_decode(&pval);\n\tif (tp != reg->tp)\n\t\treturn false;\n\n\tif (!msg_param_decode(&c.params, \"expires\", &pval)) {\n\t\treg->wait = pl_u32(&pval);\n\t}\n\telse if (pl_isset(&msg->expires))\n\t\treg->wait = pl_u32(&msg->expires);\n\telse\n\t\treg->wait = DEFAULT_EXPIRES;\n\n\treturn true;\n}\n\n\nstatic void response_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tconst struct sip_hdr *minexp;\n\tstruct sipreg *reg = arg;\n\tuint16_t last_scode = reg->ls.last_scode;\n\n\treg->wait = failwait(reg->failc + 1);\n\tif (err || !msg || sip_request_loops(&reg->ls, msg->scode)) {\n\t\treg->failc++;\n\t\tgoto out;\n\t}\n\n\tif (msg->scode < 200) {\n\t\treturn;\n\t}\n\telse if (msg->scode < 300) {\n\t\treg->wait = reg->expires;\n\t\tsip_msg_hdr_apply(msg, true, SIP_HDR_CONTACT, contact_handler,\n\t\t\t\t  reg);\n\t\treg->registered = reg->wait > 0;\n\t\treg->pexpires = reg->wait;\n\t\treg->wait *= reg->rwait * (1000 / 100);\n\t\treg->failc = 0;\n\n\t\tif (reg->regid > 0 && !reg->terminated && !reg->ka)\n\t\t\tstart_outbound(reg, msg);\n\t\tgoto out;\n\t}\n\n\tif (reg->terminated && !reg->registered)\n\t\tgoto out;\n\n\tswitch (msg->scode) {\n\n\tcase 401:\n\tcase 407:\n\t\tif (reg->ls.failc > 1 && last_scode == msg->scode) {\n\t\t\treg->failc++;\n\t\t\tgoto out;\n\t\t}\n\n\t\tsip_auth_reset(reg->auth);\n\t\terr = sip_auth_authenticate(reg->auth, msg);\n\t\tif (err) {\n\t\t\terr = (err == EAUTH) ? 0 : err;\n\t\t\tbreak;\n\t\t}\n\n\t\terr = request(reg, false);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\treturn;\n\n\tcase 403:\n\t\tsip_auth_reset(reg->auth);\n\t\tbreak;\n\n\tcase 423:\n\t\tminexp = sip_msg_hdr(msg, SIP_HDR_MIN_EXPIRES);\n\t\tif (!minexp || !pl_u32(&minexp->val) || !reg->expires)\n\t\t\tbreak;\n\n\t\treg->expires = pl_u32(&minexp->val);\n\n\t\terr = request(reg, false);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\treturn;\n\t}\n\n\t++reg->failc;\n\n out:\n\tif (!reg->expires) {\n\t\tif (msg && msg->scode >= 400 && msg->scode < 500)\n\t\t\treg->fbregint = 0;\n\n\t\tif (reg->terminated) {\n\t\t\tmem_deref(reg);\n\t\t\treturn;\n\t\t}\n\n\t\tif (reg->fbregint)\n\t\t\ttmr_start(&reg->tmr, reg->fbregint * 1000,\n\t\t\t\t\t  tmr_handler, reg);\n\t\telse\n\t\t\ttmr_cancel(&reg->tmr);\n\n\t\treg->resph(err, msg, reg->arg);\n\n\t}\n\telse if (reg->terminated) {\n\t\tif (!reg->registered || request(reg, true))\n\t\t\tmem_deref(reg);\n\t}\n\telse {\n\t\ttmr_start(&reg->tmr, reg->wait, tmr_handler, reg);\n\t\treg->resph(err, msg, reg->arg);\n\t}\n}\n\n\nstatic int send_handler(enum sip_transp tp, struct sa *src,\n\t\t\tconst struct sa *dst, struct mbuf *mb,\n\t\t\tstruct mbuf **contp, void *arg)\n{\n\tstruct sipreg *reg = arg;\n\tint err;\n\n\t(void)contp;\n\t(void)dst;\n\n\treg->tp = tp;\n\tif (reg->srcport && tp != SIP_TRANSP_UDP)\n\t\tsa_set_port(src, reg->srcport);\n\n\treg->laddr = *src;\n\terr = mbuf_printf(mb, \"Contact: <sip:%s@%J%s%s%s>;expires=%u%s%s\",\n\t\t\t  reg->cuser, &reg->laddr, sip_transp_param(reg->tp),\n\t\t\t  reg->cparams ? \";\" : \"\",\n\t\t\t  reg->cparams ? reg->cparams : \"\",\n\t\t\t  reg->expires,\n\t\t\t  reg->params ? \";\" : \"\",\n\t\t\t  reg->params ? reg->params : \"\");\n\n\tif (reg->regid > 0)\n\t\terr |= mbuf_printf(mb, \";reg-id=%d\", reg->regid);\n\n\terr |= mbuf_printf(mb, \"\\r\\n\");\n\treturn err;\n}\n\n\nstatic int request(struct sipreg *reg, bool reset_ls)\n{\n\tif (reg->terminated)\n\t\treg->expires = 0;\n\n\tif (reset_ls) {\n\t\tsip_loopstate_reset(&reg->ls);\n\t}\n\n\treturn sip_drequestf(&reg->req, reg->sip, true, \"REGISTER\", reg->dlg,\n\t\t\t     0, reg->auth, send_handler, response_handler, reg,\n\t\t\t     \"%s\"\n\t\t\t     \"%b\"\n\t\t\t     \"Content-Length: 0\\r\\n\"\n\t\t\t     \"\\r\\n\",\n\t\t\t     reg->regid > 0\n\t\t\t     ? \"Supported: gruu, outbound, path\\r\\n\" : \"\",\n\t\t\t     reg->hdrs ? mbuf_buf(reg->hdrs) : NULL,\n\t\t\t     reg->hdrs ? mbuf_get_left(reg->hdrs) : (size_t)0);\n}\n\n\nstatic int vsipreg_alloc(struct sipreg **regp, struct sip *sip,\n\t\t    const char *reg_uri,\n\t\t    const char *to_uri, const char *from_name,\n\t\t    const char *from_uri, uint32_t expires,\n\t\t    const char *cuser, const char *routev[], uint32_t routec,\n\t\t    int regid, sip_auth_h *authh, void *aarg, bool aref,\n\t\t    sip_resp_h *resph, void *arg,\n\t\t    const char *params, const char *fmt, va_list ap)\n{\n\tstruct sipreg *reg;\n\tint err;\n\n\tif (!regp || !sip || !reg_uri || !to_uri || !from_uri || !cuser)\n\t\treturn EINVAL;\n\n\treg = mem_zalloc(sizeof(*reg), destructor);\n\tif (!reg)\n\t\treturn ENOMEM;\n\n\terr = sip_dialog_alloc(&reg->dlg, reg_uri, to_uri, from_name, from_uri,\n\t\t\t       routev, routec);\n\tif (err)\n\t\tgoto out;\n\n\terr = sip_auth_alloc(&reg->auth, authh, aarg, aref);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_dup(&reg->cuser, cuser);\n\tif (params)\n\t\terr |= str_dup(&reg->params, params);\n\tif (err)\n\t\tgoto out;\n\n\t/* Custom SIP headers */\n\tif (fmt) {\n\t\treg->hdrs = mbuf_alloc(256);\n\t\tif (!reg->hdrs) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = mbuf_vprintf(reg->hdrs, fmt, ap);\n\t\treg->hdrs->pos = 0;\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\treg->sip     = mem_ref(sip);\n\treg->expires = expires;\n\treg->rwait   = 90;\n\treg->resph   = resph ? resph : dummy_handler;\n\treg->arg     = arg;\n\treg->regid   = regid;\n\n out:\n\tif (err)\n\t\tmem_deref(reg);\n\telse\n\t\t*regp = reg;\n\n\treturn err;\n}\n\n\nint sipreg_send(struct sipreg *reg)\n{\n\tif (!reg)\n\t\treturn EINVAL;\n\n\treturn request(reg, true);\n}\n\n\n/**\n * Unregisters SIP Registration client\n *\n * @param reg   SIP Registration client\n */\nvoid sipreg_unregister(struct sipreg *reg)\n{\n\tif (!reg)\n\t\treturn;\n\n\treg->expires = 0;\n\n\t(void)sipreg_send(reg);\n}\n\n\n/**\n * Allocate a SIP Registration client\n *\n * @param regp     Pointer to allocated SIP Registration client\n * @param sip      SIP Stack instance\n * @param reg_uri  SIP Request URI\n * @param to_uri   SIP To-header URI\n * @param from_name  SIP From-header display name (optional)\n * @param from_uri SIP From-header URI\n * @param expires  Registration expiry time in [seconds]\n * @param cuser    Contact username\n * @param routev   Optional route vector\n * @param routec   Number of routes\n * @param regid    Register identification\n * @param authh    Authentication handler\n * @param aarg     Authentication handler argument\n * @param aref     True to ref argument\n * @param resph    Response handler\n * @param arg      Response handler argument\n * @param params   Optional Contact-header parameters\n * @param fmt      Formatted strings with extra SIP Headers\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipreg_alloc(struct sipreg **regp, struct sip *sip, const char *reg_uri,\n\t\t    const char *to_uri, const char *from_name,\n\t\t    const char *from_uri, uint32_t expires,\n\t\t    const char *cuser, const char *routev[], uint32_t routec,\n\t\t    int regid, sip_auth_h *authh, void *aarg, bool aref,\n\t\t    sip_resp_h *resph, void *arg,\n\t\t    const char *params, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = vsipreg_alloc(regp, sip, reg_uri, to_uri, from_name,\n\t\t\t    from_uri, expires, cuser, routev, routec,\n\t\t\t    regid, authh, aarg, aref, resph, arg,\n\t\t\t    params, fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Set the relative registration interval in percent from proxy expiry time. A\n * value from 5-95% is accepted.\n *\n * @param reg   SIP Registration client\n * @param rwait The relative registration interval in [%].\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipreg_set_rwait(struct sipreg *reg, uint32_t rwait)\n{\n\tif (!reg || rwait < 5 || rwait > 95)\n\t\treturn EINVAL;\n\n\treg->rwait = rwait;\n\treturn 0;\n}\n\n\n/**\n * Get the local socket address for a SIP Registration client\n *\n * @param reg SIP Registration client\n *\n * @return Local socket address\n */\nconst struct sa *sipreg_laddr(const struct sipreg *reg)\n{\n\treturn reg ? &reg->laddr : NULL;\n}\n\n\n/**\n * Get the proxy expires value of a SIP registration client\n *\n * @param reg SIP registration client\n *\n * @return the proxy expires value\n */\nuint32_t sipreg_proxy_expires(const struct sipreg *reg)\n{\n\treturn reg ? reg->pexpires : 0;\n}\n\n\nbool sipreg_registered(const struct sipreg *reg)\n{\n\treturn reg ? reg->registered : false;\n}\n\n\nbool sipreg_failed(const struct sipreg *reg)\n{\n\treturn reg ? reg->failc > 0 : false;\n}\n\n\nvoid sipreg_incfailc(struct sipreg *reg)\n{\n\tif (!reg)\n\t\treturn;\n\n\treg->failc++;\n}\n\n\nint sipreg_set_fbregint(struct sipreg *reg, uint32_t fbregint)\n{\n\tif (!reg)\n\t\treturn EINVAL;\n\n\treg->fbregint = fbregint;\n\treturn 0;\n}\n\n\n/**\n * Set TCP source port number for the SIP registration client\n *\n * @param reg      SIP registration client\n * @param srcport  TCP source port number\n */\nvoid sipreg_set_srcport(struct sipreg *reg, uint16_t srcport)\n{\n\tif (!reg || !reg->dlg)\n\t\treturn;\n\n\treg->srcport = srcport;\n\n\tsip_dialog_set_srcport(reg->dlg, srcport);\n}\n\n\n/**\n * Set contact URI optional parameters\n *\n * @param reg      SIP registration client\n * @param cparams  Contact URI optional parameters\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipreg_set_contact_params(struct sipreg *reg, const char *cparams)\n{\n\tif (!reg)\n\t\treturn EINVAL;\n\n\treturn str_dup(&reg->cparams, cparams);\n}\n"
  },
  {
    "path": "src/sipsess/accept.c",
    "content": "/**\n * @file accept.c  SIP Session Accept\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include \"sipsess.h\"\n\n\nstatic void cancel_handler(void *arg)\n{\n\tstruct sipsess *sess = arg;\n\tconst struct sip_msg *cancel_msg = sip_strans_cancel_msg(sess->st);\n\n\t(void)sip_treply(&sess->st, sess->sip, sess->msg,\n\t\t\t 487, \"Request Terminated\");\n\n\tsess->peerterm = true;\n\n\tif (sess->terminated)\n\t\treturn;\n\n\tsipsess_terminate(sess, ECONNRESET, cancel_msg);\n}\n\n\n/**\n * Accept an incoming SIP Session connection\n *\n * @param sessp     Pointer to allocated SIP Session\n * @param sock      SIP Session socket\n * @param msg       Incoming SIP message\n * @param scode     Response status code\n * @param reason    Response reason phrase\n * @param rel100    Sending 1xx reliably supported, required or disabled\n * @param cuser     Contact username or URI\n * @param ctype     Session content-type\n * @param desc      Content description (e.g. SDP)\n * @param authh     SIP Authentication handler\n * @param aarg      Authentication handler argument\n * @param aref      True to mem_ref() aarg\n * @param offerh    Session offer handler\n * @param answerh   Session answer handler\n * @param estabh    Session established handler\n * @param infoh     Session info handler\n * @param referh    Session refer handler\n * @param closeh    Session close handler\n * @param arg       Handler argument\n * @param fmt       Formatted strings with extra SIP Headers\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipsess_accept(struct sipsess **sessp, struct sipsess_sock *sock,\n\t\t   const struct sip_msg *msg, uint16_t scode,\n\t\t   const char *reason, enum rel100_mode rel100,\n\t\t   const char *cuser, const char *ctype, struct mbuf *desc,\n\t\t   sip_auth_h *authh, void *aarg, bool aref,\n\t\t   sipsess_offer_h *offerh, sipsess_answer_h *answerh,\n\t\t   sipsess_estab_h *estabh, sipsess_info_h *infoh,\n\t\t   sipsess_refer_h *referh, sipsess_close_h *closeh,\n\t\t   void *arg, const char *fmt, ...)\n{\n\tstruct sipsess *sess;\n\tva_list ap;\n\tint err;\n\n\tif (!sessp || !sock || !msg || scode < 101 || scode > 299 ||\n\t    !cuser || !ctype)\n\t\treturn EINVAL;\n\n\terr = sipsess_alloc(&sess, sock, cuser, ctype, NULL, authh, aarg, aref,\n\t\t\t    NULL, offerh, answerh, NULL, estabh, infoh, referh,\n\t\t\t    closeh, arg);\n\tif (err)\n\t\treturn err;\n\n\terr = sip_dialog_accept(&sess->dlg, msg);\n\tif (err)\n\t\tgoto out;\n\n\thash_append(sock->ht_sess,\n\t\t    hash_joaat_str(sip_dialog_callid(sess->dlg)),\n\t\t    &sess->he, sess);\n\n\tsess->msg = mem_ref((void *)msg);\n\n\terr = sip_strans_alloc(&sess->st, sess->sip, msg, cancel_handler,\n\t\t\t       sess);\n\tif (err)\n\t\tgoto out;\n\n\tif (mbuf_get_left(msg->mb))\n\t\tsess->neg_state = SDP_NEG_REMOTE_OFFER;\n\n\tva_start(ap, fmt);\n\n\tif (scode > 100 && scode < 200) {\n\t\terr = sipsess_reply_1xx(sess, msg, scode, reason, rel100, desc,\n\t\t\t\t\tfmt, &ap);\n\t}\n\telse if (scode >= 200) {\n\t\terr = sipsess_reply_2xx(sess, msg, scode, reason, desc,\n\t\t\t\t\tfmt, &ap);\n\t}\n\n\tva_end(ap);\n\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(sess);\n\telse\n\t\t*sessp = sess;\n\n\treturn err;\n}\n\n\n/**\n * Send progress response\n *\n * @param sess      SIP Session\n * @param scode     Response status code\n * @param reason    Response reason phrase\n * @param rel100    Sending 1xx reliably supported, required or disabled\n * @param desc      Content description (e.g. SDP)\n * @param fmt       Formatted strings with extra SIP Headers\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipsess_progress(struct sipsess *sess, uint16_t scode, const char *reason,\n\t\t     enum rel100_mode rel100, struct mbuf *desc,\n\t\t     const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tif (!sess || !sess->st || !sess->msg || scode < 101 || scode > 199)\n\t\treturn EINVAL;\n\n\tva_start(ap, fmt);\n\n\terr = sipsess_reply_1xx(sess, sess->msg, scode, reason, rel100, desc,\n\t\t\t\tfmt, &ap);\n\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Answer an incoming SIP Session connection\n *\n * @param sess      SIP Session\n * @param scode     Response status code\n * @param reason    Response reason phrase\n * @param desc      Content description (e.g. SDP)\n * @param fmt       Formatted strings with extra SIP Headers\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipsess_answer(struct sipsess *sess, uint16_t scode, const char *reason,\n\t\t   struct mbuf *desc, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tif (!sess || !sess->st || !sess->msg || scode < 200 || scode > 299)\n\t\treturn EINVAL;\n\n\tva_start(ap, fmt);\n\terr = sipsess_reply_2xx(sess, sess->msg, scode, reason, desc,\n\t\t\t\tfmt, &ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Reject an incoming SIP Session connection\n *\n * @param sess      SIP Session\n * @param scode     Response status code\n * @param reason    Response reason phrase\n * @param fmt       Formatted strings with extra SIP Headers\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipsess_reject(struct sipsess *sess, uint16_t scode, const char *reason,\n\t\t   const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tif (!sess || !sess->st || !sess->msg || scode < 300)\n\t\treturn EINVAL;\n\n\tva_start(ap, fmt);\n\terr = sip_treplyf(&sess->st, NULL, sess->sip, sess->msg, false,\n\t\t\t  scode, reason, fmt ? \"%v\" : NULL, fmt, &ap);\n\tva_end(ap);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sipsess/ack.c",
    "content": "/**\n * @file ack.c  SIP Session ACK\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include \"sipsess.h\"\n\n\nstruct sipsess_ack {\n\tstruct le he;\n\tstruct tmr tmr;\n\tstruct sa dst;\n\tstruct sip_request *req;\n\tstruct sip_dialog *dlg;\n\tstruct mbuf *mb;\n\tenum sip_transp tp;\n\tuint32_t cseq;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sipsess_ack *ack = arg;\n\n\thash_unlink(&ack->he);\n\ttmr_cancel(&ack->tmr);\n\tmem_deref(ack->req);\n\tmem_deref(ack->dlg);\n\tmem_deref(ack->mb);\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct sipsess_ack *ack = arg;\n\n\tmem_deref(ack);\n}\n\n\nstatic int send_handler(enum sip_transp tp, struct sa *src,\n\t\t\tconst struct sa *dst, struct mbuf *mb,\n\t\t\tstruct mbuf **contp, void *arg)\n{\n\tstruct sipsess_ack *ack = arg;\n\t(void)src;\n\t(void)contp;\n\n\tmem_deref(ack->mb);\n\tack->mb = mem_ref(mb);\n\tack->dst = *dst;\n\tack->tp  = tp;\n\n\ttmr_start(&ack->tmr, 64 * SIP_T1, tmr_handler, ack);\n\n\treturn 0;\n}\n\n\nstatic void resp_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct sipsess_ack *ack = arg;\n\t(void)err;\n\t(void)msg;\n\n\tmem_deref(ack);\n}\n\n\nint sipsess_ack(struct sipsess_sock *sock, struct sip_dialog *dlg,\n\t\tuint32_t cseq, struct sip_auth *auth,\n\t\tconst char *ctype, struct mbuf *desc)\n{\n\tstruct sipsess_ack *ack;\n\tint err;\n\n\tack = mem_zalloc(sizeof(*ack), destructor);\n\tif (!ack)\n\t\treturn ENOMEM;\n\n\thash_append(sock->ht_ack,\n\t\t    hash_joaat_str(sip_dialog_callid(dlg)),\n\t\t    &ack->he, ack);\n\n\tack->dlg  = mem_ref(dlg);\n\tack->cseq = cseq;\n\n\terr = sip_drequestf(&ack->req, sock->sip, false, \"ACK\", dlg, cseq,\n\t\t\t    auth, send_handler, resp_handler, ack,\n\t\t\t    \"%s%s%s\"\n\t\t\t    \"Content-Length: %zu\\r\\n\"\n\t\t\t    \"\\r\\n\"\n\t\t\t    \"%b\",\n\t\t\t    desc ? \"Content-Type: \" : \"\",\n\t\t\t    desc ? ctype : \"\",\n\t\t\t    desc ? \"\\r\\n\" : \"\",\n\t\t\t    desc ? mbuf_get_left(desc) : (size_t)0,\n\t\t\t    desc ? mbuf_buf(desc) : NULL,\n\t\t\t    desc ? mbuf_get_left(desc) : (size_t)0);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(ack);\n\n\treturn err;\n}\n\n\nstatic bool cmp_handler(struct le *le, void *arg)\n{\n\tstruct sipsess_ack *ack = le->data;\n\tconst struct sip_msg *msg = arg;\n\n\tif (!sip_dialog_cmp(ack->dlg, msg))\n\t\treturn false;\n\n\tif (ack->cseq != msg->cseq.num)\n\t\treturn false;\n\n\treturn true;\n}\n\n\nint sipsess_ack_again(struct sipsess_sock *sock, const struct sip_msg *msg)\n{\n\tstruct sipsess_ack *ack;\n\n\tack = list_ledata(hash_lookup(sock->ht_ack,\n\t\t\t\t      hash_joaat_pl(&msg->callid),\n\t\t\t\t      cmp_handler, (void *)msg));\n\tif (!ack)\n\t\treturn ENOENT;\n\n\treturn sip_send(sock->sip, NULL, ack->tp, &ack->dst, ack->mb);\n}\n"
  },
  {
    "path": "src/sipsess/close.c",
    "content": "/**\n * @file close.c  SIP Session Close\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include \"sipsess.h\"\n\n\nstatic void bye_resp_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct sipsess *sess = arg;\n\n\tif (err || sip_request_loops(&sess->ls, msg->scode))\n\t\tgoto out;\n\n\tif (msg->scode < 200) {\n\t\treturn;\n\t}\n\telse if (msg->scode < 300) {\n\t\t;\n\t}\n\telse {\n\t\tif (sess->peerterm)\n\t\t\tgoto out;\n\n\t\tswitch (msg->scode) {\n\n\t\tcase 401:\n\t\tcase 407:\n\t\t\terr = sip_auth_authenticate(sess->auth, msg);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\n\t\t\terr = sipsess_bye(sess, false);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\n\t\t\treturn;\n\t\t}\n\t}\n\n out:\n\tmem_deref(sess);\n}\n\n\nint sipsess_bye(struct sipsess *sess, bool reset_ls)\n{\n\tif (sess->req)\n\t\treturn EPROTO;\n\n\tif (reset_ls)\n\t\tsip_loopstate_reset(&sess->ls);\n\n\treturn sip_drequestf(&sess->req, sess->sip, true, \"BYE\",\n\t\t\t     sess->dlg, 0, sess->auth,\n\t\t\t     NULL, bye_resp_handler, sess,\n\t\t\t     \"%s\"\n\t\t\t     \"Content-Length: 0\\r\\n\"\n\t\t\t     \"\\r\\n\",\n\t\t\t     sess->close_hdrs);\n}\n"
  },
  {
    "path": "src/sipsess/connect.c",
    "content": "/**\n * @file connect.c  SIP Session Connect\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include \"sipsess.h\"\n\n\nstatic int invite(struct sipsess *sess);\n\n\nstatic int send_handler(enum sip_transp tp, struct sa *src,\n\t\t\tconst struct sa *dst, struct mbuf *mb,\n\t\t\tstruct mbuf **contp, void *arg)\n{\n\tstruct sip_contact contact;\n\tstruct sipsess *sess = arg;\n\tstruct mbuf *desc = NULL;\n\tstruct mbuf *cont = NULL;\n\tint err;\n\n\tif (sess->desch) {\n\t\terr = sess->desch(&desc, src, dst, sess->arg);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tsip_contact_set(&contact, sess->cuser, src, tp);\n\terr = mbuf_printf(mb, \"%H\", sip_contact_print, &contact);\n\tif (err)\n\t\tgoto out;\n\n\tcont = mbuf_alloc(1024);\n\tif (!cont) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr |= mbuf_printf(cont,\n\t\t\t\"%s%s%s\"\n\t\t\t\"Content-Length: %zu\\r\\n\"\n\t\t\t\"\\r\\n\"\n\t\t\t\"%b\",\n\t\t\tdesc ? \"Content-Type: \" : \"\",\n\t\t\tdesc ? sess->ctype : \"\",\n\t\t\tdesc ? \"\\r\\n\" : \"\",\n\t\t\tmbuf_get_left(desc),\n\t\t\tmbuf_buf(desc),\n\t\t\tmbuf_get_left(desc));\n\tcont->pos = 0;\n\n\tif (err)\n\t\tmem_deref(cont);\n\telse\n\t\t*contp = cont;\n\nout:\n\tif (desc)\n\t\tsess->neg_state = SDP_NEG_LOCAL_OFFER;\n\n\tmem_deref(desc);\n\treturn err;\n}\n\n\nstatic void invite_resp_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct sipsess *sess = arg;\n\tstruct mbuf *desc = NULL;\n\tbool sdp;\n\tconst struct sip_hdr *contact;\n\tstruct sip_addr addr;\n\tchar *uri;\n\n\tif (!sess)\n\t\treturn;\n\n\tif (!msg || err || sip_request_loops(&sess->ls, msg->scode))\n\t\tgoto out;\n\n\tif (!sip_dialog_cmp_half(sess->dlg, msg)\n\t\t|| sip_dialog_lseqinv(sess->dlg) != msg->cseq.num)\n\t\tgoto out;\n\n\tsdp = mbuf_get_left(msg->mb) > 0;\n\n\tif (msg->scode < 200) {\n\t\tif (msg->scode == 100)\n\t\t\treturn;\n\n\t\tcontact = sip_msg_hdr(msg, SIP_HDR_CONTACT);\n\t\tif (pl_isset(&msg->to.tag) && contact) {\n\t\t\terr = sip_dialog_established(sess->dlg) ?\n\t\t\t\t\tsip_dialog_update(sess->dlg, msg) :\n\t\t\t\t\tsip_dialog_create(sess->dlg, msg);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\n\t\tif (sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, \"100rel\")\n\t\t\t\t&& sess->rel100_supported) {\n\n\t\t\tif (sess->rel_seq && msg->rel_seq != (sess->rel_seq+1))\n\t\t\t\treturn;\n\n\t\t\tsess->rel_seq = msg->rel_seq;\n\t\t\tif (sess->neg_state == SDP_NEG_NONE && !sdp)\n\t\t\t\tgoto out;\n\n\t\t\tsess->progrh(msg, sess->arg);\n\n\t\t\tif (sdp) {\n\t\t\t\tif (sess->neg_state == SDP_NEG_LOCAL_OFFER) {\n\t\t\t\t\tsess->neg_state = SDP_NEG_DONE;\n\t\t\t\t\terr = sess->answerh(msg, sess->arg);\n\t\t\t\t}\n\t\t\t\telse if (sess->neg_state == SDP_NEG_NONE) {\n\t\t\t\t\tsess->neg_state = SDP_NEG_REMOTE_OFFER;\n\t\t\t\t\terr = sess->offerh(&desc, msg,\n\t\t\t\t\t\t\t   sess->arg);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\terr |= sipsess_prack(sess, msg->cseq.num, msg->rel_seq,\n\t\t\t\t\t     &msg->cseq.met, desc);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n\t\t\tif (sess->neg_state == SDP_NEG_REMOTE_OFFER\n\t\t\t    && mbuf_get_left(desc))\n\t\t\t\tsess->neg_state = SDP_NEG_DONE;\n\n\t\t\tmem_deref(desc);\n\t\t\tsess->desc = mem_deref(sess->desc);\n\t\t\treturn;\n\t\t}\n\n\t\tsess->progrh(msg, sess->arg);\n\n\t\tif (sdp && sess->neg_state == SDP_NEG_LOCAL_OFFER) {\n\t\t\terr = sess->answerh(msg, sess->arg);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t\treturn;\n\t}\n\telse if (msg->scode < 300) {\n\n\t\tsess->established = true;\n\n\t\tsess->hdrs = mem_deref(sess->hdrs);\n\n\t\terr = sip_dialog_established(sess->dlg) ?\n\t\t\t\tsip_dialog_update(sess->dlg, msg) :\n\t\t\t\tsip_dialog_create(sess->dlg, msg);\n\n\t\tif (sdp && !err) {\n\t\t\tif (sess->neg_state == SDP_NEG_LOCAL_OFFER) {\n\t\t\t\tsess->neg_state = SDP_NEG_DONE;\n\t\t\t\terr = sess->answerh(msg, sess->arg);\n\t\t\t}\n\t\t\telse if (sess->neg_state == SDP_NEG_NONE) {\n\t\t\t\tsess->neg_state = SDP_NEG_REMOTE_OFFER;\n\t\t\t\terr = sess->offerh(&desc, msg, sess->arg);\n\t\t\t}\n\t\t}\n\n\t\terr |= sipsess_ack(sess->sock, sess->dlg, msg->cseq.num,\n\t\t\t\t  sess->auth, sess->ctype, desc);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (sess->neg_state == SDP_NEG_NONE && !sdp)\n\t\t\tgoto out;\n\n\t\tif (sess->neg_state == SDP_NEG_REMOTE_OFFER\n\t\t    && mbuf_get_left(desc))\n\t\t\tsess->neg_state = SDP_NEG_DONE;\n\n\t\tmem_deref(desc);\n\n\t\tif (err || sess->terminated)\n\t\t\tgoto out;\n\n\t\tif (sess->modify_pending)\n\t\t\t(void)sipsess_reinvite(sess, true);\n\t\telse\n\t\t\tsess->desc = mem_deref(sess->desc);\n\n\t\tsess->estabh(msg, sess->arg);\n\t\treturn;\n\t}\n\telse if (msg->scode < 400) {\n\n\t\tif (sess->terminated)\n\t\t\tgoto out;\n\n\t\tif (sess->redirecth) {\n\n\t\t\tcontact = sip_msg_hdr(msg, SIP_HDR_CONTACT);\n\t\t\tif (!contact) {\n\t\t\t\terr = EBADMSG;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tif (sip_addr_decode(&addr, &contact->val)) {\n\t\t\t\terr = EBADMSG;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\terr = pl_strdup(&uri, &addr.auri);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n\t\t\tsess->redirecth(msg, uri, sess->arg);\n\n\t\t\tmem_deref(uri);\n\t\t}\n\t\telse {\n\t\t\t/* Redirect to first Contact */\n\n\t\t\terr = sip_dialog_update(sess->dlg, msg);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n\t\t\terr = invite(sess);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n\t\t\treturn;\n\t\t}\n\t}\n\telse {\n\t\tif (sess->terminated)\n\t\t\tgoto out;\n\n\t\tswitch (msg->scode) {\n\n\t\tcase 401:\n\t\tcase 407:\n\t\t\terr = sip_auth_authenticate(sess->auth, msg);\n\t\t\tif (err) {\n\t\t\t\terr = (err == EAUTH) ? 0 : err;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\terr = invite(sess);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\n\t\t\treturn;\n\t\t}\n\t}\n\n out:\n\tif (!sess->terminated)\n\t\tsipsess_terminate(sess, err, msg);\n\telse\n\t\tmem_deref(sess);\n\n}\n\n\nstatic int invite(struct sipsess *sess)\n{\n\tsess->modify_pending = false;\n\n\treturn sip_drequestf(&sess->req, sess->sip, true, \"INVITE\",\n\t\t\t     sess->dlg, 0, sess->auth,\n\t\t\t     send_handler, invite_resp_handler, sess,\n\t\t\t     \"%b\",\n\t\t\t     sess->hdrs ? mbuf_buf(sess->hdrs) : NULL,\n\t\t\t     sess->hdrs ? mbuf_get_left(sess->hdrs) :(size_t)0\n\t\t\t     );\n}\n\n\n/**\n * Connect to a remote SIP useragent\n *\n * @param sessp     Pointer to allocated SIP Session\n * @param sock      SIP Session socket\n * @param to_uri    To SIP uri\n * @param from_name From display name\n * @param from_uri  From SIP uri\n * @param cuser     Contact username or URI\n * @param routev    Outbound route vector\n * @param routec    Outbound route vector count\n * @param ctype     Session content-type\n * @param authh     SIP Authentication handler\n * @param aarg      Authentication handler argument\n * @param aref      True to mem_ref() aarg\n * @param callid    Call Identifier\n * @param desch     Content description handler\n * @param offerh    Session offer handler\n * @param answerh   Session answer handler\n * @param progrh    Session progress handler\n * @param estabh    Session established handler\n * @param infoh     Session info handler\n * @param referh    Session refer handler\n * @param closeh    Session close handler\n * @param arg       Handler argument\n * @param fmt       Formatted strings with extra SIP Headers\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipsess_connect(struct sipsess **sessp, struct sipsess_sock *sock,\n\t\t    const char *to_uri, const char *from_name,\n\t\t    const char *from_uri, const char *cuser,\n\t\t    const char *routev[], uint32_t routec,\n\t\t    const char *ctype,\n\t\t    sip_auth_h *authh, void *aarg, bool aref,\n\t\t    const char *callid,\n\t\t    sipsess_desc_h *desch,\n\t\t    sipsess_offer_h *offerh, sipsess_answer_h *answerh,\n\t\t    sipsess_progr_h *progrh, sipsess_estab_h *estabh,\n\t\t    sipsess_info_h *infoh, sipsess_refer_h *referh,\n\t\t    sipsess_close_h *closeh, void *arg, const char *fmt, ...)\n{\n\tstruct sipsess *sess;\n\tstruct pl hdrs;\n\tint err;\n\n\tif (!sessp || !sock || !to_uri || !from_uri || !cuser || !ctype)\n\t\treturn EINVAL;\n\n\terr = sipsess_alloc(&sess, sock, cuser, ctype, NULL, authh, aarg, aref,\n\t\t\t    desch,\n\t\t\t    offerh, answerh, progrh, estabh, infoh, referh,\n\t\t\t    closeh, arg);\n\tif (err)\n\t\treturn err;\n\n\t/* Custom SIP headers */\n\tif (fmt) {\n\t\tva_list ap;\n\n\t\tsess->hdrs = mbuf_alloc(256);\n\t\tif (!sess->hdrs) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\tva_start(ap, fmt);\n\t\terr = mbuf_vprintf(sess->hdrs, fmt, ap);\n\t\tsess->hdrs->pos = 0;\n\t\tva_end(ap);\n\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tpl_set_mbuf(&hdrs, sess->hdrs);\n\t}\n\n\tsess->owner = true;\n\tsess->rel100_supported = fmt && !re_regex(hdrs.p, hdrs.l, \"100rel\");\n\n\terr = sip_dialog_alloc(&sess->dlg, to_uri, to_uri, from_name,\n\t\t\t       from_uri, routev, routec);\n\tif (err)\n\t\tgoto out;\n\n\tif (str_isset(callid))\n\t\terr = sip_dialog_set_callid(sess->dlg, callid);\n\n\tif (err)\n\t\tgoto out;\n\n\thash_append(sock->ht_sess,\n\t\t    hash_joaat_str(sip_dialog_callid(sess->dlg)),\n\t\t    &sess->he, sess);\n\n\terr = invite(sess);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(sess);\n\telse\n\t\t*sessp = sess;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sipsess/info.c",
    "content": "/**\n * @file info.c  SIP Session Info\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include \"sipsess.h\"\n\n\nstatic int info_request(struct sipsess_request *req);\n\n\nstatic void info_resp_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct sipsess_request *req = arg;\n\n\tif (err || sip_request_loops(&req->ls, msg->scode))\n\t\tgoto out;\n\n\tif (msg->scode < 200) {\n\t\treturn;\n\t}\n\telse if (msg->scode < 300) {\n\t\t;\n\t}\n\telse {\n\t\tif (req->sess->terminated)\n\t\t\tgoto out;\n\n\t\tswitch (msg->scode) {\n\n\t\tcase 401:\n\t\tcase 407:\n\t\t\terr = sip_auth_authenticate(req->sess->auth, msg);\n\t\t\tif (err) {\n\t\t\t\terr = (err == EAUTH) ? 0 : err;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\terr = info_request(req);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\n\t\t\treturn;\n\n\t\tcase 408:\n\t\tcase 481:\n\t\t\tsipsess_terminate(req->sess, 0, msg);\n\t\t\tbreak;\n\t\t}\n\t}\n\n out:\n\tif (!req->sess->terminated) {\n\t\tif (err == ETIMEDOUT)\n\t\t\tsipsess_terminate(req->sess, err, NULL);\n\t\telse\n\t\t\treq->resph(err, msg, req->arg);\n\t}\n\n\tmem_deref(req);\n}\n\n\nstatic int info_request(struct sipsess_request *req)\n{\n\treturn sip_drequestf(&req->req, req->sess->sip, true, \"INFO\",\n\t\t\t     req->sess->dlg, 0, req->sess->auth,\n\t\t\t     NULL, info_resp_handler, req,\n\t\t\t     \"Content-Type: %s\\r\\n\"\n\t\t\t     \"Content-Length: %zu\\r\\n\"\n\t\t\t     \"\\r\\n\"\n\t\t\t     \"%b\",\n\t\t\t     req->ctype,\n\t\t\t     mbuf_get_left(req->body),\n\t\t\t     mbuf_buf(req->body), mbuf_get_left(req->body));\n}\n\n\n/**\n * Send a SIP INFO request in the SIP Session\n *\n * @param sess      SIP Session\n * @param ctype     Content-type\n * @param body      Content description (e.g. SDP)\n * @param resph     Response handler\n * @param arg       Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipsess_info(struct sipsess *sess, const char *ctype, struct mbuf *body,\n\t\t sip_resp_h *resph, void *arg)\n{\n\tstruct sipsess_request *req;\n\tint err;\n\n\tif (!sess || sess->terminated || !ctype || !body)\n\t\treturn EINVAL;\n\n\tif (!sip_dialog_established(sess->dlg))\n\t\treturn ENOTCONN;\n\n\terr = sipsess_request_alloc(&req, sess, ctype, body, resph, arg);\n\tif (err)\n\t\treturn err;\n\n\terr = info_request(req);\n\tif (err)\n\t\tmem_deref(req);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sipsess/listen.c",
    "content": "/**\n * @file sipsess/listen.c  SIP Session Listen\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include <re_sys.h>\n#include \"sipsess.h\"\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sipsess_sock *sock = arg;\n\n\tmem_deref(sock->lsnr_resp);\n\tmem_deref(sock->lsnr_req);\n\thash_flush(sock->ht_sess);\n\tmem_deref(sock->ht_sess);\n\thash_flush(sock->ht_ack);\n\tmem_deref(sock->ht_ack);\n}\n\n\nstatic void internal_connect_handler(const struct sip_msg *msg, void *arg)\n{\n\tstruct sipsess_sock *sock = arg;\n\n\t(void)sip_treply(NULL, sock->sip, msg, 486, \"Busy Here\");\n}\n\n\nstatic void info_handler(struct sipsess_sock *sock, const struct sip_msg *msg)\n{\n\tstruct sip *sip = sock->sip;\n\tstruct sipsess *sess;\n\n\tsess = sipsess_find(sock, msg);\n\tif (!sess || sess->terminated) {\n\t\t(void)sip_reply(sip, msg, 481, \"Call Does Not Exist\");\n\t\treturn;\n\t}\n\n\tif (!sip_dialog_rseq_valid(sess->dlg, msg)) {\n\t\t(void)sip_reply(sip, msg, 500, \"Server Internal Error\");\n\t\treturn;\n\t}\n\n\tif (!sess->infoh) {\n\t\t(void)sip_reply(sip, msg, 501, \"Not Implemented\");\n\t\treturn;\n\t}\n\n\tsess->infoh(sip, msg, sess->arg);\n}\n\n\nstatic void refer_handler(struct sipsess_sock *sock, const struct sip_msg *msg)\n{\n\tstruct sip *sip = sock->sip;\n\tstruct sipsess *sess;\n\n\tsess = sipsess_find(sock, msg);\n\tif (!sess || sess->terminated) {\n\t\t(void)sip_reply(sip, msg, 481, \"Call Does Not Exist\");\n\t\treturn;\n\t}\n\n\tif (!sip_dialog_rseq_valid(sess->dlg, msg)) {\n\t\t(void)sip_reply(sip, msg, 500, \"Server Internal Error\");\n\t\treturn;\n\t}\n\n\tif (!sess->referh) {\n\t\t(void)sip_reply(sip, msg, 501, \"Not Implemented\");\n\t\treturn;\n\t}\n\n\tsess->referh(sip, msg, sess->arg);\n}\n\n\nstatic void bye_handler(struct sipsess_sock *sock, const struct sip_msg *msg)\n{\n\tstruct sip *sip = sock->sip;\n\tstruct sipsess *sess;\n\n\tsess = sipsess_find(sock, msg);\n\tif (!sess) {\n\t\t(void)sip_reply(sip, msg, 481, \"Call Does Not Exist\");\n\t\treturn;\n\t}\n\n\tif (!sip_dialog_rseq_valid(sess->dlg, msg)) {\n\t\t(void)sip_reply(sip, msg, 500, \"Server Internal Error\");\n\t\treturn;\n\t}\n\n\t(void)sip_treplyf(NULL, NULL, sip, msg, false, 200, \"OK\",\n\t\t\t  \"%s\"\n\t\t\t  \"Content-Length: 0\\r\\n\"\n\t\t\t  \"\\r\\n\",\n\t\t\t  sess->close_hdrs);\n\n\tsess->peerterm = true;\n\n\tif (sess->terminated)\n\t\treturn;\n\n\tif (sess->st) {\n\t\t(void)sip_treply(&sess->st, sess->sip, sess->msg,\n\t\t\t\t 487, \"Request Terminated\");\n\t}\n\n\tsipsess_terminate(sess, ECONNRESET, NULL);\n}\n\n\nstatic void ack_handler(struct sipsess_sock *sock, const struct sip_msg *msg)\n{\n\tstruct sipsess *sess;\n\tint err = 0;\n\n\tsess = sipsess_find(sock, msg);\n\tif (!sess)\n\t\treturn;\n\n\tif (sipsess_reply_ack(sess, msg))\n\t\treturn;\n\n\tif (sess->terminated) {\n\t\tif (!sess->replyl.head) {\n\t\t\tsess->established = true;\n\t\t\tmem_deref(sess);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (sess->neg_state == SDP_NEG_LOCAL_OFFER) {\n\t\tif (!mbuf_get_left(msg->mb)) {\n\t\t\tsipsess_terminate(sess, EPROTO, NULL);\n\t\t\treturn;\n\t\t}\n\n\t\tsess->neg_state = SDP_NEG_DONE;\n\t\terr = sess->answerh(msg, sess->arg);\n\t}\n\n\tif (sess->modify_pending && !sess->replyl.head)\n\t\t(void)sipsess_reinvite(sess, true);\n\n\tif (sess->established)\n\t\treturn;\n\n\tsess->msg = mem_deref((void *)sess->msg);\n\tsess->established = true;\n\n\tif (err)\n\t\tsipsess_terminate(sess, err, NULL);\n\telse\n\t\tsess->estabh(msg, sess->arg);\n}\n\n\nstatic void prack_handler(struct sipsess_sock *sock, const struct sip_msg *msg)\n{\n\tbool sdp;\n\tstruct sipsess *sess;\n\tstruct mbuf *desc = NULL;\n\tbool awaiting_prack = false;\n\n\tsess = sipsess_find(sock, msg);\n\n\tif (!sess || sipsess_reply_prack(sess, msg, &awaiting_prack)) {\n\t\t(void)sip_reply(sock->sip, msg, 481,\n\t\t\t\t\"Transaction Does Not Exist\");\n\t\treturn;\n\t}\n\n\tif (sess->terminated) {\n\t\tif (!sess->replyl.head) {\n\t\t\tsess->established = true;\n\t\t\tmem_deref(sess);\n\t\t}\n\n\t\treturn;\n\t}\n\n\tsdp = mbuf_get_left(msg->mb);\n\n\tif (awaiting_prack)\n\t\t--sess->prack_waiting_cnt;\n\n\tif (sess->neg_state == SDP_NEG_LOCAL_OFFER) {\n\t\tif (!sdp) {\n\t\t\tsipsess_terminate(sess, EPROTO, NULL);\n\t\t\treturn;\n\t\t}\n\n\t\tsess->neg_state = SDP_NEG_DONE;\n\t\t(void)sess->answerh(msg, sess->arg);\n\t}\n\telse if (sess->neg_state == SDP_NEG_DONE && sdp) {\n\t\tsess->neg_state = SDP_NEG_REMOTE_OFFER;\n\t\t(void)sess->offerh(&desc, msg, sess->arg);\n\t}\n\n\tif (sess->prackh)\n\t\tsess->prackh(msg, sess->arg);\n\n\t(void)sipsess_reply_2xx(sess, msg, 200, \"OK\", desc, NULL, NULL);\n\n\tmem_deref(desc);\n}\n\n\nstatic void target_refresh_handler(struct sipsess_sock *sock,\n\t\t\t     const struct sip_msg *msg)\n{\n\tstruct sip *sip = sock->sip;\n\tbool is_invite;\n\tbool sdp;\n\tstruct sipsess *sess;\n\tstruct mbuf *desc = NULL;\n\tchar m[256];\n\tint err;\n\n\tsess = sipsess_find(sock, msg);\n\tif (!sess || sess->terminated) {\n\t\t(void)sip_treply(NULL, sip, msg, 481, \"Call Does Not Exist\");\n\t\treturn;\n\t}\n\n\tis_invite = !pl_strcmp(&msg->met, \"INVITE\");\n\tsdp = (mbuf_get_left(msg->mb) > 0);\n\n\tif (!sip_dialog_rseq_valid(sess->dlg, msg)) {\n\t\t(void)sip_treply(NULL, sip, msg, 500, \"Server Internal Error\");\n\t\treturn;\n\t}\n\n\tif ((is_invite && sess->st)\n\t    || (sdp && sess->neg_state == SDP_NEG_LOCAL_OFFER)) {\n\t\tif (!sess->established) {\n\t\t\tuint32_t wait = rand_u16() % 11;\n\t\t\t(void)sip_treplyf(NULL, NULL, sip, msg, false,\n\t\t\t\t\t  500, \"Server Internal Error\",\n\t\t\t\t\t  \"Retry-After: %u\\r\\n\"\n\t\t\t\t\t  \"Content-Length: 0\\r\\n\"\n\t\t\t\t\t  \"\\r\\n\", wait);\n\t\t}\n\t\telse {\n\t\t\t(void)sip_treply(NULL, sip, msg, 491,\n\t\t\t\t\t \"Request Pending\");\n\t\t}\n\t\treturn;\n\t}\n\n\tif (is_invite && sess->req) {\n\t\t(void)sip_treply(NULL, sip, msg, 491, \"Request Pending\");\n\t\treturn;\n\t}\n\n\tif (sdp && !sipsess_refresh_allowed(sess)) {\n\t\t(void)sip_reply(sip, msg, 488, \"Not Acceptable Here\");\n\t\treturn;\n\t}\n\n\tif (is_invite || sdp) {\n\t\tsess->neg_state = sdp ? SDP_NEG_REMOTE_OFFER :\n\t\t\t\t  SDP_NEG_LOCAL_OFFER;\n\t\terr = sess->offerh(&desc, msg, sess->arg);\n\t\tif (err) {\n\t\t\t(void)sip_reply(sip, msg, 488,\n\t\t\t\t\tstr_error(err, m, sizeof(m)));\n\t\t\tsess->neg_state = SDP_NEG_DONE;\n\t\t\treturn;\n\t\t}\n\t}\n\n\t(void)sip_dialog_update(sess->dlg, msg);\n\t(void)sipsess_reply_2xx(sess, msg, 200, \"OK\", desc,\n\t\t\t\tNULL, NULL);\n\n\t/* pending modifications considered outdated;\n\t   sdp may have changed in above exchange */\n\tsess->desc = mem_deref(sess->desc);\n\tsess->modify_pending = false;\n\ttmr_cancel(&sess->tmr);\n\tmem_deref(desc);\n}\n\n\nstatic void invite_handler(struct sipsess_sock *sock,\n\t\t\t   const struct sip_msg *msg)\n{\n\tsock->connh(msg, sock->arg);\n}\n\n\nstatic bool request_handler(const struct sip_msg *msg, void *arg)\n{\n\tstruct sipsess_sock *sock = arg;\n\n\tif (!pl_strcmp(&msg->met, \"INVITE\")) {\n\n\t\tif (pl_isset(&msg->to.tag))\n\t\t\ttarget_refresh_handler(sock, msg);\n\t\telse\n\t\t\tinvite_handler(sock, msg);\n\n\t\treturn true;\n\t}\n\telse if (!pl_strcmp(&msg->met, \"UPDATE\")) {\n\t\ttarget_refresh_handler(sock, msg);\n\t\treturn true;\n\t}\n\telse if (!pl_strcmp(&msg->met, \"ACK\")) {\n\t\tack_handler(sock, msg);\n\t\treturn true;\n\t}\n\telse if (!pl_strcmp(&msg->met, \"PRACK\")) {\n\t\tprack_handler(sock, msg);\n\t\treturn true;\n\t}\n\telse if (!pl_strcmp(&msg->met, \"BYE\")) {\n\t\tbye_handler(sock, msg);\n\t\treturn true;\n\t}\n\telse if (!pl_strcmp(&msg->met, \"INFO\")) {\n\t\tinfo_handler(sock, msg);\n\t\treturn true;\n\t}\n\telse if (!pl_strcmp(&msg->met, \"REFER\")) {\n\n\t\tif (!pl_isset(&msg->to.tag))\n\t\t\treturn false;\n\n\t\trefer_handler(sock, msg);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nstatic bool response_handler(const struct sip_msg *msg, void *arg)\n{\n\tstruct sipsess_sock *sock = arg;\n\n\tif (pl_strcmp(&msg->cseq.met, \"INVITE\"))\n\t\treturn false;\n\n\tif (msg->scode < 200 || msg->scode > 299)\n\t\treturn false;\n\n\t(void)sipsess_ack_again(sock, msg);\n\n\treturn true;\n}\n\n\n/**\n * Listen to a SIP Session socket for incoming connections\n *\n * @param sockp    Pointer to allocated SIP Session socket\n * @param sip      SIP Stack instance\n * @param htsize   Hashtable size\n * @param connh    Connection handler\n * @param arg      Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipsess_listen(struct sipsess_sock **sockp, struct sip *sip,\n\t\t   int htsize, sipsess_conn_h *connh, void *arg)\n{\n\tstruct sipsess_sock *sock;\n\tint err;\n\n\tif (!sockp || !sip || !htsize)\n\t\treturn EINVAL;\n\n\tsock = mem_zalloc(sizeof(*sock), destructor);\n\tif (!sock)\n\t\treturn ENOMEM;\n\n\terr = sip_listen(&sock->lsnr_resp, sip, false, response_handler, sock);\n\tif (err)\n\t\tgoto out;\n\n\terr = sip_listen(&sock->lsnr_req, sip, true, request_handler, sock);\n\tif (err)\n\t\tgoto out;\n\n\terr = hash_alloc(&sock->ht_sess, htsize);\n\tif (err)\n\t\tgoto out;\n\n\terr = hash_alloc(&sock->ht_ack, htsize);\n\tif (err)\n\t\tgoto out;\n\n\tsock->sip   = sip;\n\tsock->connh = connh ? connh : internal_connect_handler;\n\tsock->arg   = connh ? arg : sock;\n\n out:\n\tif (err)\n\t\tmem_deref(sock);\n\telse\n\t\t*sockp = sock;\n\n\treturn err;\n}\n\n\n/**\n * Close all SIP Sessions\n *\n * @param sock      SIP Session socket\n */\nvoid sipsess_close_all(struct sipsess_sock *sock)\n{\n\tif (!sock)\n\t\treturn;\n\n\thash_flush(sock->ht_sess);\n}\n"
  },
  {
    "path": "src/sipsess/modify.c",
    "content": "/**\n * @file modify.c  SIP Session Modify\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include \"sipsess.h\"\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct sipsess *sess = arg;\n\n\t(void)sipsess_reinvite(sess, true);\n}\n\n\nstatic void reinvite_resp_handler(int err, const struct sip_msg *msg,\n\t\t\t\t  void *arg)\n{\n\tstruct sipsess *sess = arg;\n\tconst struct sip_hdr *hdr;\n\tstruct mbuf *desc = NULL;\n\tbool sdp;\n\n\tif (!msg || err || sip_request_loops(&sess->ls, msg->scode))\n\t\tgoto out;\n\n\tsdp = mbuf_get_left(msg->mb) > 0;\n\n\tif (msg->scode < 200) {\n\t\treturn;\n\t}\n\telse if (msg->scode < 300) {\n\n\t\t(void)sip_dialog_update(sess->dlg, msg);\n\n\t\tif (sdp) {\n\t\t\tif (sess->neg_state == SDP_NEG_LOCAL_OFFER) {\n\t\t\t\tsess->neg_state = SDP_NEG_DONE;\n\t\t\t\terr = sess->answerh(msg, sess->arg);\n\t\t\t}\n\t\t\telse if (sess->neg_state == SDP_NEG_DONE) {\n\t\t\t\tsess->neg_state = SDP_NEG_REMOTE_OFFER;\n\t\t\t\terr = sess->offerh(&desc, msg, sess->arg);\n\t\t\t}\n\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\n\t\terr = sipsess_ack(sess->sock, sess->dlg, msg->cseq.num,\n\t\t\t\t  sess->auth, sess->ctype, desc);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (sess->neg_state == SDP_NEG_REMOTE_OFFER\n\t\t    && mbuf_get_left(desc))\n\t\t    \tsess->neg_state = SDP_NEG_DONE;\n\n\t\tmem_deref(desc);\n\t}\n\telse {\n\t\tsess->neg_state = SDP_NEG_DONE;\n\n\t\tif (sess->terminated)\n\t\t\tgoto out;\n\n\t\tswitch (msg->scode) {\n\n\t\tcase 401:\n\t\tcase 407:\n\t\t\terr = sip_auth_authenticate(sess->auth, msg);\n\t\t\tif (err) {\n\t\t\t\terr = (err == EAUTH) ? 0 : err;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\terr = sipsess_reinvite(sess, false);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\n\t\t\treturn;\n\n\t\tcase 408:\n\t\tcase 481:\n\t\t\tsipsess_terminate(sess, 0, msg);\n\t\t\treturn;\n\n\t\tcase 491:\n\t\t\ttmr_start(&sess->tmr, sess->owner ? 3000 : 1000,\n\t\t\t\t  tmr_handler, sess);\n\t\t\treturn;\n\n\t\tcase 500:\n\t\t\thdr = sip_msg_hdr(msg, SIP_HDR_RETRY_AFTER);\n\t\t\tif (!hdr)\n\t\t\t\tbreak;\n\n\t\t\ttmr_start(&sess->tmr, pl_u32(&hdr->val) * 1000,\n\t\t\t\t  tmr_handler, sess);\n\t\t\treturn;\n\t\t}\n\t}\n out:\n\tif (sess->terminated)\n\t\tmem_deref(sess);\n\telse if (err == ETIMEDOUT)\n\t\tsipsess_terminate(sess, err, NULL);\n\telse if (sess->modify_pending)\n\t\t(void)sipsess_reinvite(sess, true);\n\n\telse\n\t\tsess->desc = mem_deref(sess->desc);\n}\n\n\nstatic int send_handler(enum sip_transp tp, struct sa *src,\n\t\t\tconst struct sa *dst, struct mbuf *mb,\n\t\t\tstruct mbuf **contp, void *arg)\n{\n\tstruct sip_contact contact;\n\tstruct sipsess *sess = arg;\n\t(void)dst;\n\t(void)contp;\n\n\tsip_contact_set(&contact, sess->cuser, src, tp);\n\n\treturn mbuf_printf(mb, \"%H\", sip_contact_print, &contact);\n}\n\n\nint sipsess_reinvite(struct sipsess *sess, bool reset_ls)\n{\n\tint err;\n\n\tif (sess->req)\n\t\treturn EPROTO;\n\n\tif (reset_ls)\n\t\tsip_loopstate_reset(&sess->ls);\n\n\terr = sip_drequestf(&sess->req, sess->sip, true, \"INVITE\",\n\t\t\t    sess->dlg, 0, sess->auth,\n\t\t\t    send_handler, reinvite_resp_handler, sess,\n\t\t\t    \"%s%s%s\"\n\t\t\t    \"Content-Length: %zu\\r\\n\"\n\t\t\t    \"\\r\\n\"\n\t\t\t    \"%b\",\n\t\t\t    sess->desc ? \"Content-Type: \" : \"\",\n\t\t\t    sess->desc ? sess->ctype : \"\",\n\t\t\t    sess->desc ? \"\\r\\n\" : \"\",\n\t\t\t    sess->desc ? mbuf_get_left(sess->desc) :(size_t)0,\n\t\t\t    sess->desc ? mbuf_buf(sess->desc) : NULL,\n\t\t\t    sess->desc ? mbuf_get_left(sess->desc):(size_t)0);\n\n\tif (!err) {\n\t\tsess->modify_pending = false;\n\t\tif (sess->desc)\n\t\t\tsess->neg_state = SDP_NEG_LOCAL_OFFER;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Modify an established SIP Session sending Re-INVITE or UPDATE\n *\n * @param sess      SIP Session\n * @param desc      Content description (e.g. SDP)\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipsess_modify(struct sipsess *sess, struct mbuf *desc)\n{\n\tif (!sess || sess->terminated || !sip_dialog_established(sess->dlg))\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(desc) && (sess->neg_state != SDP_NEG_DONE\n\t    && sess->neg_state != SDP_NEG_NONE))\n\t\treturn EPROTO;\n\n\tmem_deref(sess->desc);\n\tsess->desc = mem_ref(desc);\n\n\tif (!sess->established)\n\t\treturn sipsess_update(sess);\n\n\tif (sess->req || sess->tmr.th || sess->replyl.head) {\n\t\tsess->modify_pending = true;\n\t\treturn 0;\n\t}\n\n\treturn sipsess_reinvite(sess, true);\n}\n"
  },
  {
    "path": "src/sipsess/prack.c",
    "content": "/**\n * @file prack.c  SIP Session PRACK (RFC 3262)\n *\n * Copyright (C) 2022 commend.com - m.fridrich@commend.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include \"sipsess.h\"\n\n\nstruct sipsess_prack {\n\tuint32_t cseq;\n\tuint32_t rseq;\n\tchar *met;\n\tstruct sipsess_request *req;\n};\n\n\nstatic int prack_request(struct sipsess_prack *prack);\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sipsess_prack *prack = arg;\n\n\tmem_deref(prack->met);\n\tmem_deref(prack->req);\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct sipsess_prack *prack = arg;\n\tint err;\n\n\tif (!prack)\n\t\treturn;\n\n\terr = prack_request(prack);\n\tif (err)\n\t\tmem_deref(prack);\n}\n\n\nstatic void prack_resp_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct sipsess_prack *prack = arg;\n\tstruct sipsess_request *req = prack->req;\n\tconst struct sip_hdr *hdr;\n\n\tif (!msg || err || sip_request_loops(&req->ls, msg->scode))\n\t\tgoto out;\n\n\tif (msg->scode < 200) {\n\t\treturn;\n\t}\n\telse if (msg->scode < 300) {\n\t\t(void)sip_dialog_update(req->sess->dlg, msg);\n\n\t\tif (mbuf_get_left(msg->mb)) {\n\t\t\tif (req->sess->neg_state == SDP_NEG_LOCAL_OFFER) {\n\t\t\t\treq->sess->neg_state = SDP_NEG_DONE;\n\t\t\t\t(void)req->sess->answerh(msg, req->sess->arg);\n\t\t\t}\n\n\t\t\treq->sess->desc = mem_deref(req->sess->desc);\n\t\t}\n\t}\n\telse {\n\t\tif (req->sess->terminated)\n\t\t\tgoto out;\n\n\t\tswitch (msg->scode) {\n\n\t\tcase 401:\n\t\tcase 407:\n\t\t\terr = sip_auth_authenticate(req->sess->auth, msg);\n\t\t\tif (err) {\n\t\t\t\terr = (err == EAUTH) ? 0 : err;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\terr = prack_request(prack);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\n\t\t\treturn;\n\t\tcase 408:\n\t\tcase 491:\n\t\t\ttmr_start(&req->tmr, req->sess->owner ? 3000 : 1000,\n\t\t\t\t  tmr_handler, req);\n\t\t\treturn;\n\t\tcase 500:\n\t\t\thdr = sip_msg_hdr(msg, SIP_HDR_RETRY_AFTER);\n\t\t\tif (!hdr)\n\t\t\t\tbreak;\n\n\t\t\ttmr_start(&req->tmr, pl_u32(&hdr->val) * 1000,\n\t\t\t\t  tmr_handler, req);\n\t\t\treturn;\n\t\t}\n\t}\n\nout:\n\tif (!req->sess->terminated) {\n\t\tif (err == ETIMEDOUT)\n\t\t\tsipsess_terminate(req->sess, err, NULL);\n\t}\n\n\tmem_deref(prack);\n}\n\n\nstatic int prack_request(struct sipsess_prack *prack)\n{\n\tstruct sipsess_request *req = prack->req;\n\tchar rack_header[256];\n\tint err;\n\n\tif (!req || req->tmr.th)\n\t\treturn EINVAL;\n\n\terr = re_snprintf(rack_header, sizeof(rack_header), \"%d %d %s\",\n\t\t\t  prack->rseq, prack->cseq, prack->met);\n\tif (err == -1)\n\t\treturn err;\n\n\treturn sip_drequestf(&req->req, req->sess->sip, true, \"PRACK\",\n\t\t\t     req->sess->dlg, 0, req->sess->auth, NULL,\n\t\t\t     prack_resp_handler, prack,\n\t\t\t     \"RAck: %s\\n\"\n\t\t\t     \"%s%s%s\"\n\t\t\t     \"Content-Length: %zu\\r\\n\"\n\t\t\t     \"\\r\\n\"\n\t\t\t     \"%b\",\n\t\t\t     rack_header,\n\t\t\t     req->body ? \"Content-Type: \" : \"\",\n\t\t\t     req->body ? req->sess->ctype : \"\",\n\t\t\t     req->body ? \"\\r\\n\" : \"\",\n\t\t\t     req->body ? mbuf_get_left(req->body) : (size_t)0,\n\t\t\t     req->body ? mbuf_buf(req->body) : NULL,\n\t\t\t     req->body ? mbuf_get_left(req->body) : (size_t)0);\n}\n\n\n/**\n * Send PRACK request (RFC 3262)\n *\n * @param sess      SIP Session\n * @param cseq      CSeq number to be written in RAck header\n * @param rseq      RSeq number to be written in RAck header\n * @param met       Method to be written in RAck header\n * @param desc      Content description (e.g. SDP)\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipsess_prack(struct sipsess *sess, uint32_t cseq, uint32_t rseq,\n\t\t  const struct pl *met, struct mbuf *desc)\n{\n\tstruct sipsess_prack *prack;\n\tint err;\n\n\tif (!sess || sess->terminated)\n\t\treturn EINVAL;\n\n\tprack = mem_zalloc(sizeof(*prack), destructor);\n\tif (!prack)\n\t\treturn ENOMEM;\n\n\terr = sipsess_request_alloc(&prack->req, sess, sess->ctype, desc,\n\t\t\t\t    NULL, prack);\n\tif (err)\n\t\tgoto out;\n\n\tprack->cseq = cseq;\n\tprack->rseq = rseq;\n\terr = pl_strdup(&prack->met, met);\n\tif (err)\n\t\tgoto out;\n\n\terr = prack_request(prack);\n\nout:\n\tif (err)\n\t\tmem_deref(prack);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sipsess/reply.c",
    "content": "/**\n * @file sipsess/reply.c  SIP Session Reply\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include <re_sys.h>\n#include \"sipsess.h\"\n\n\nstruct sipsess_reply {\n\tstruct le le;\n\tstruct tmr tmr;\n\tstruct tmr tmrg;\n\tconst struct sip_msg *msg;\n\tstruct mbuf *mb;\n\tstruct sipsess *sess;\n\tbool awaiting_prack;\n\tuint16_t scode;\n\tuint32_t seq;\n\tuint32_t rel_seq;\n\tuint32_t txc;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sipsess_reply *reply = arg;\n\n\tlist_unlink(&reply->le);\n\ttmr_cancel(&reply->tmr);\n\ttmr_cancel(&reply->tmrg);\n\tmem_deref((void *)reply->msg);\n\tmem_deref(reply->mb);\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct sipsess_reply *reply = arg;\n\tstruct sipsess *sess = reply->sess;\n\n\t/* we want to send bye */\n\n\tif (!sess->terminated) {\n\t\tif (reply->scode < 200 && !sess->established) {\n\t\t\t(void)sip_reply(sess->sip, reply->msg, 504,\n\t\t\t\t\t\"Timeout\");\n\t\t}\n\t\telse {\n\t\t\tsess->established = true;\n\t\t\tmem_deref(reply);\n\t\t\tsipsess_terminate(sess, ETIMEDOUT, NULL);\n\t\t\treturn;\n\t\t}\n\t}\n\telse {\n\t\tmem_deref(reply);\n\t\tmem_deref(sess);\n\t\treturn;\n\t}\n\n\tmem_deref(reply);\n}\n\n\nstatic void retransmit_handler(void *arg)\n{\n\tstruct sipsess_reply *reply = arg;\n\tuint32_t delay;\n\n\t(void)sip_send(reply->sess->sip, reply->msg->sock, reply->msg->tp,\n\t\t       &reply->msg->src, reply->mb);\n\n\treply->txc++;\n\n\tdelay = !reply->rel_seq ?\n\t\tMIN(SIP_T1 << reply->txc, SIP_T2) : SIP_T1 << reply->txc;\n\n\ttmr_start(&reply->tmrg, delay, retransmit_handler, reply);\n}\n\n\nstatic bool cancel_1xx_timers(struct le *le, void *arg)\n{\n\tstruct sipsess_reply *reply = le->data;\n\t(void)arg;\n\n\tif (reply->scode > 100 && reply->scode < 200) {\n\t\ttmr_cancel(&reply->tmr);\n\t\ttmr_cancel(&reply->tmrg);\n\t}\n\n\treturn false;\n}\n\n\nint sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg,\n\t\t      uint16_t scode, const char *reason, struct mbuf *desc,\n\t\t      const char *fmt, va_list *ap)\n{\n\tstruct sipsess_reply *reply = NULL;\n\tstruct sip_contact contact;\n\tint err = ENOMEM;\n\tbool sdp = mbuf_get_left(msg->mb) > 0;\n\tbool non_invite = !pl_strcmp(&msg->met, \"PRACK\")\n\t\t\t  || !pl_strcmp(&msg->met, \"UPDATE\");\n\n\tif (!non_invite) {\n\t\tif (sess->neg_state == SDP_NEG_NONE && !mbuf_get_left(desc))\n\t\t\treturn EINVAL;\n\t\telse if (sess->neg_state == SDP_NEG_DONE)\n\t\t\tdesc = NULL;\n\n\t\tif (sess->prack_waiting_cnt > 0)\n\t\t\treturn EAGAIN;\n\n\t\treply = mem_zalloc(sizeof(*reply), destructor);\n\t\tif (!reply)\n\t\t\tgoto out;\n\n\t\tlist_append(&sess->replyl, &reply->le, reply);\n\n\t\treply->seq  = msg->cseq.num;\n\t\treply->msg  = mem_ref((void *)msg);\n\t\treply->scode = scode;\n\t\treply->sess = sess;\n\t}\n\n\tif (non_invite && sess->neg_state != SDP_NEG_REMOTE_OFFER)\n\t\tdesc = NULL;\n\n\tsip_contact_set(&contact, sess->cuser, &msg->dst, msg->tp);\n\terr = sip_treplyf(non_invite ? NULL : &sess->st,\n\t\t\t  reply ? &reply->mb : NULL, sess->sip,\n\t\t\t  msg, true, scode, reason,\n\t\t\t  \"%H\"\n\t\t\t  \"%v\"\n\t\t\t  \"%s%s%s\"\n\t\t\t  \"Content-Length: %zu\\r\\n\"\n\t\t\t  \"\\r\\n\"\n\t\t\t  \"%b\",\n\t\t\t  sip_contact_print, &contact,\n\t\t\t  fmt, ap,\n\t\t\t  desc ? \"Content-Type: \" : \"\",\n\t\t\t  desc ? sess->ctype : \"\",\n\t\t\t  desc ? \"\\r\\n\" : \"\",\n\t\t\t  desc ? mbuf_get_left(desc) : (size_t)0,\n\t\t\t  desc ? mbuf_buf(desc) : NULL,\n\t\t\t  desc ? mbuf_get_left(desc) : (size_t)0);\n\n\tif (err)\n\t\tgoto out;\n\n\tif (!non_invite)\n\t\t(void)list_ledata(list_apply(&sess->replyl, false,\n\t\t\t\t  cancel_1xx_timers, NULL));\n\n\tif (mbuf_get_left(desc)) {\n\t\tif (sdp)\n\t\t\tsess->neg_state = SDP_NEG_DONE;\n\t\telse if (!non_invite)\n\t\t\tsess->neg_state = SDP_NEG_LOCAL_OFFER;\n\t}\n\n\tif (reply) {\n\t\ttmr_start(&reply->tmr, 64 * SIP_T1, tmr_handler, reply);\n\t\ttmr_start(&reply->tmrg, SIP_T1, retransmit_handler, reply);\n\t}\n\n out:\n\tif (err) {\n\t\tif (!non_invite)\n\t\t\tsess->st = mem_deref(sess->st);\n\n\t\tmem_deref(reply);\n\t}\n\n\treturn err;\n}\n\n\nint sipsess_reply_1xx(struct sipsess *sess, const struct sip_msg *msg,\n\t\t      uint16_t scode, const char *reason,\n\t\t      enum rel100_mode rel100, struct mbuf *desc,\n\t\t      const char *fmt, va_list *ap)\n{\n\tstruct sipsess_reply *reply;\n\tstruct sip_contact contact;\n\tchar rseq_header[64];\n\tbool reliably;\n\tenum rel100_mode rel100_peer = REL100_DISABLED;\n\tstruct pl require_header = pl_null;\n\tint err = ENOMEM;\n\n\tif (sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, \"100rel\"))\n\t\trel100_peer = REL100_REQUIRED;\n\telse if (sip_msg_hdr_has_value(msg, SIP_HDR_SUPPORTED, \"100rel\"))\n\t\trel100_peer = REL100_ENABLED;\n\n\tif (rel100 == REL100_REQUIRED && !rel100_peer) {\n\t\t(void)sip_treplyf(&sess->st, NULL, sess->sip, msg, false,\n\t\t\t\t  421, \"Extension required\",\n\t\t\t\t  \"Require: 100rel\\r\\n\"\n\t\t\t\t  \"Content-Length: 0\\r\\n\\r\\n\");\n\t\treturn EPROTO;\n\t}\n\telse if (rel100_peer == REL100_REQUIRED && !rel100) {\n\t\t(void)sip_treplyf(&sess->st, NULL, sess->sip, msg, false, 420,\n\t\t\t\t  \"Bad Extension\", \"Unsupported: 100rel\\r\\n\"\n\t\t\t\t  \"Content-Length: 0\\r\\n\\r\\n\");\n\t\treturn EPROTO;\n\t}\n\n\treliably = rel100 && rel100_peer && scode != 100;\n\n\tif (reliably && sess->prack_waiting_cnt)\n\t\treturn EAGAIN;\n\n\tif (sess->neg_state == SDP_NEG_NONE) {\n\t\tif (reliably && !mbuf_get_left(desc))\n\t\t\treturn EINVAL;\n\t\telse if (!reliably)\n\t\t\tdesc = NULL;\n\t}\n\telse if (sess->neg_state == SDP_NEG_DONE\n\t\t || sess->neg_state == SDP_NEG_LOCAL_OFFER) {\n\t\tdesc = NULL;\n\t}\n\n\tif (rel100 != REL100_REQUIRED && reliably) {\n\t\tpl_set_str(&require_header, \"Require: 100rel\\r\\n\");\n\t}\n\n\treply = mem_zalloc(sizeof(*reply), destructor);\n\tif (!reply)\n\t\tgoto out;\n\n\tlist_append(&sess->replyl, &reply->le, reply);\n\treply->seq  = msg->cseq.num;\n\treply->msg  = mem_ref((void *)msg);\n\treply->sess = sess;\n\treply->scode = scode;\n\n\tsip_contact_set(&contact, sess->cuser, &msg->dst, msg->tp);\n\tif (reliably) {\n\t\tsess->rel_seq = sess->rel_seq ? sess->rel_seq+1 : rand_u16();\n\t\treply->rel_seq = sess->rel_seq;\n\t\tre_snprintf(rseq_header, sizeof(rseq_header),\n\t\t\t\t\t\"%d\", reply->rel_seq);\n\t}\n\n\terr = sip_treplyf(&sess->st, &reply->mb, sess->sip,\n\t\t\t  msg, true, scode, reason,\n\t\t\t  \"%H\"\n\t\t\t  \"%v\"\n\t\t\t  \"%s%s%s%s%s%s%s\"\n\t\t\t  \"Content-Length: %zu\\r\\n\"\n\t\t\t  \"\\r\\n\"\n\t\t\t  \"%b\",\n\t\t\t  sip_contact_print, &contact,\n\t\t\t  fmt, ap,\n\t\t\t  require_header.p ? require_header.p : \"\",\n\t\t\t  reliably ? \"RSeq: \" : \"\",\n\t\t\t  reliably ? rseq_header : \"\",\n\t\t\t  reliably ? \"\\r\\n\" : \"\",\n\t\t\t  desc ? \"Content-Type: \" : \"\",\n\t\t\t  desc ? sess->ctype : \"\",\n\t\t\t  desc ? \"\\r\\n\" : \"\",\n\t\t\t  desc ? mbuf_get_left(desc) : (size_t)0,\n\t\t\t  desc ? mbuf_buf(desc) : NULL,\n\t\t\t  desc ? mbuf_get_left(desc) : (size_t)0);\n\n\tif (err)\n\t\tgoto out;\n\n\tif (reliably) {\n\t\ttmr_start(&reply->tmr, 64 * SIP_T1, tmr_handler, reply);\n\t\ttmr_start(&reply->tmrg, SIP_T1, retransmit_handler, reply);\n\n\t\treply->awaiting_prack = true;\n\t\t++sess->prack_waiting_cnt;\n\t\tif (desc) {\n\t\t\tsess->neg_state = mbuf_get_left(msg->mb) ?\n\t\t\t\tSDP_NEG_DONE : SDP_NEG_LOCAL_OFFER;\n\t\t}\n\t}\n\telse {\n\t\tif (desc && sess->neg_state == SDP_NEG_REMOTE_OFFER)\n\t\t\tsess->neg_state = SDP_NEG_PREVIEW_ANSWER;\n\n\t\tmem_deref(reply);\n\t}\n\n out:\n\tif (err) {\n\t\tsess->st = mem_deref(sess->st);\n\t\tmem_deref(reply);\n\t}\n\n\treturn err;\n}\n\n\nstatic bool cmp_handler_prack(struct le *le, void *arg)\n{\n\tstruct sipsess_reply *reply = le->data;\n\tconst struct sip_msg *msg = arg;\n\n\treturn msg->rack.cseq == reply->seq &&\n\t\t\tmsg->rack.rel_seq == reply->rel_seq &&\n\t\t\t!pl_cmp(&msg->rack.met, &reply->msg->met);\n}\n\n\nstatic bool cmp_handler(struct le *le, void *arg)\n{\n\tstruct sipsess_reply *reply = le->data;\n\tconst struct sip_msg *msg = arg;\n\n\treturn msg->cseq.num == reply->seq;\n}\n\n\nint sipsess_reply_prack(struct sipsess *sess, const struct sip_msg *msg,\n\t\t\tbool *awaiting_prack)\n{\n\tstruct sipsess_reply *reply;\n\n\treply = list_ledata(list_apply(&sess->replyl, false, cmp_handler_prack,\n\t\t\t\t       (void *)msg));\n\tif (!reply)\n\t\treturn ENOENT;\n\n\t*awaiting_prack = reply->awaiting_prack;\n\n\tmem_deref(reply);\n\n\treturn 0;\n}\n\n\nint sipsess_reply_ack(struct sipsess *sess, const struct sip_msg *msg)\n{\n\tstruct sipsess_reply *reply;\n\n\treply = list_ledata(list_apply(&sess->replyl, false, cmp_handler,\n\t\t\t\t       (void *)msg));\n\tif (!reply)\n\t\treturn ENOENT;\n\n\tmem_deref(reply);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/sipsess/request.c",
    "content": "/**\n * @file sipsess/request.c  SIP Session Non-INVITE Request\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include \"sipsess.h\"\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sipsess_request *req = arg;\n\n\ttmr_cancel(&req->tmr);\n\tlist_unlink(&req->le);\n\tmem_deref(req->ctype);\n\tmem_deref(req->body);\n\tmem_deref(req->req);\n\n\t/* wait for pending requests */\n\tif (req->sess->terminated && !req->sess->requestl.head)\n\t\tmem_deref(req->sess);\n}\n\n\nstatic void internal_resp_handler(int err, const struct sip_msg *msg,\n\t\t\t\t  void *arg)\n{\n\t(void)err;\n\t(void)msg;\n\t(void)arg;\n}\n\n\nint sipsess_request_alloc(struct sipsess_request **reqp, struct sipsess *sess,\n\t\t\t  const char *ctype, struct mbuf *body,\n\t\t\t  sip_resp_h *resph, void *arg)\n{\n\tstruct sipsess_request *req;\n\tint err = 0;\n\n\tif (!reqp || !sess || sess->terminated)\n\t\treturn EINVAL;\n\n\treq = mem_zalloc(sizeof(*req), destructor);\n\tif (!req)\n\t\treturn ENOMEM;\n\n\tlist_append(&sess->requestl, &req->le, req);\n\n\tif (ctype) {\n\t\terr = str_dup(&req->ctype, ctype);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\treq->sess  = sess;\n\treq->body  = mem_ref(body);\n\treq->resph = resph ? resph : internal_resp_handler;\n\treq->arg   = arg;\n\ttmr_init(&req->tmr);\n\n out:\n\tif (err)\n\t\tmem_deref(req);\n\telse\n\t\t*reqp = req;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sipsess/sess.c",
    "content": "/**\n * @file sipsess/sess.c  SIP Session Core\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include \"sipsess.h\"\n\n\nstatic int internal_offer_handler(struct mbuf **descp,\n\t\t\t\t  const struct sip_msg *msg, void *arg)\n{\n\t(void)descp;\n\t(void)msg;\n\t(void)arg;\n\n\treturn ENOSYS;\n}\n\n\nstatic int internal_answer_handler(const struct sip_msg *msg, void *arg)\n{\n\t(void)msg;\n\t(void)arg;\n\n\treturn ENOSYS;\n}\n\n\nstatic void internal_progress_handler(const struct sip_msg *msg, void *arg)\n{\n\t(void)msg;\n\t(void)arg;\n}\n\n\nstatic void internal_establish_handler(const struct sip_msg *msg, void *arg)\n{\n\t(void)msg;\n\t(void)arg;\n}\n\n\nstatic void internal_close_handler(int err, const struct sip_msg *msg,\n\t\t\t\t   void *arg)\n{\n\t(void)err;\n\t(void)msg;\n\t(void)arg;\n}\n\n\nstatic bool termwait(struct sipsess *sess)\n{\n\tbool wait = false;\n\n\tsess->terminated = 1;\n\tsess->desch   = NULL;\n\tsess->offerh  = internal_offer_handler;\n\tsess->answerh = internal_answer_handler;\n\tsess->progrh  = internal_progress_handler;\n\tsess->estabh  = internal_establish_handler;\n\tsess->infoh   = NULL;\n\tsess->referh  = NULL;\n\tsess->closeh  = internal_close_handler;\n\tsess->arg     = sess;\n\n\ttmr_cancel(&sess->tmr);\n\n\tif (sess->st) {\n\t\t(void)sip_treply(&sess->st, sess->sip, sess->msg,\n\t\t\t\t 486, \"Busy Here\");\n\t}\n\n\tif (sess->req) {\n\t\tsip_request_cancel(sess->req);\n\t\tif (!sip_request_provrecv(sess->req)) {\n\t\t\tsess->req = mem_deref(sess->req);\n\t\t}\n\t\telse {\n\t\t\tmem_ref(sess);\n\t\t\twait = true;\n\t\t}\n\t}\n\n\tif (sess->replyl.head) {\n\t\tmem_ref(sess);\n\t\twait = true;\n\t}\n\n\tif (sess->requestl.head) {\n\t\tmem_ref(sess);\n\t\twait = true;\n\t}\n\n\treturn wait;\n}\n\n\nstatic bool terminate(struct sipsess *sess)\n{\n\tsess->terminated = 2;\n\n\tif (!sess->established || sess->peerterm)\n\t\treturn false;\n\n\tif (sipsess_bye(sess, true))\n\t\treturn false;\n\n\tmem_ref(sess);\n\treturn true;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sipsess *sess = arg;\n\n\tswitch (sess->terminated) {\n\n\tcase 0:\n\t\tif (termwait(sess))\n\t\t\treturn;\n\n\t\t/*@fallthrough@*/\n\n\tcase 1:\n\t\tif (terminate(sess))\n\t\t\treturn;\n\t\tbreak;\n\t}\n\n\thash_unlink(&sess->he);\n\ttmr_cancel(&sess->tmr);\n\tlist_flush(&sess->replyl);\n\tlist_flush(&sess->requestl);\n\tmem_deref((void *)sess->msg);\n\tmem_deref(sess->req);\n\tmem_deref(sess->dlg);\n\tmem_deref(sess->auth);\n\tmem_deref(sess->cuser);\n\tmem_deref(sess->ctype);\n\tmem_deref(sess->close_hdrs);\n\tmem_deref(sess->hdrs);\n\tmem_deref(sess->desc);\n\tmem_deref(sess->sock);\n\tmem_deref(sess->sip);\n\tmem_deref(sess->st);\n}\n\n\nint sipsess_alloc(struct sipsess **sessp, struct sipsess_sock *sock,\n\t\t  const char *cuser, const char *ctype, struct mbuf *desc,\n\t\t  sip_auth_h *authh, void *aarg, bool aref,\n\t\t  sipsess_desc_h *desch,\n\t\t  sipsess_offer_h *offerh, sipsess_answer_h *answerh,\n\t\t  sipsess_progr_h *progrh, sipsess_estab_h *estabh,\n\t\t  sipsess_info_h *infoh, sipsess_refer_h *referh,\n\t\t  sipsess_close_h *closeh, void *arg)\n{\n\tstruct sipsess *sess;\n\tint err;\n\n\tsess = mem_zalloc(sizeof(*sess), destructor);\n\tif (!sess)\n\t\treturn ENOMEM;\n\n\terr = sip_auth_alloc(&sess->auth, authh, aarg, aref);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_dup(&sess->cuser, cuser);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_dup(&sess->ctype, ctype);\n\tif (err)\n\t\tgoto out;\n\n\tsess->sock    = mem_ref(sock);\n\tsess->desc    = mem_ref(desc);\n\tsess->sip     = mem_ref(sock->sip);\n\tsess->desch   = desch;\n\tsess->offerh  = offerh  ? offerh  : internal_offer_handler;\n\tsess->answerh = answerh ? answerh : internal_answer_handler;\n\tsess->progrh  = progrh  ? progrh  : internal_progress_handler;\n\tsess->estabh  = estabh  ? estabh  : internal_establish_handler;\n\tsess->infoh   = infoh;\n\tsess->referh  = referh;\n\tsess->closeh  = closeh  ? closeh  : internal_close_handler;\n\tsess->arg     = arg;\n\n out:\n\tif (err)\n\t\tmem_deref(sess);\n\telse\n\t\t*sessp = sess;\n\n\treturn err;\n}\n\n\nint  sipsess_set_redirect_handler(struct sipsess *sess,\n\t\t\t\t  sipsess_redirect_h *redirecth)\n{\n\tif (!sess || !redirecth)\n\t\treturn EINVAL;\n\n\tsess->redirecth = redirecth;\n\n\treturn 0;\n}\n\n\nint sipsess_set_prack_handler(struct sipsess *sess, sipsess_prack_h *prackh)\n{\n\tif (!sess || !prackh)\n\t\treturn EINVAL;\n\n\tsess->prackh = prackh;\n\n\treturn 0;\n}\n\n\nstatic bool cmp_handler(struct le *le, void *arg)\n{\n\tstruct sipsess *sess = le->data;\n\tconst struct sip_msg *msg = arg;\n\n\treturn sip_dialog_cmp(sess->dlg, msg);\n}\n\n\nstruct sipsess *sipsess_find(struct sipsess_sock *sock,\n\t\t\t     const struct sip_msg *msg)\n{\n\treturn list_ledata(hash_lookup(sock->ht_sess,\n\t\t\t\t       hash_joaat_pl(&msg->callid),\n\t\t\t\t       cmp_handler, (void *)msg));\n}\n\n\nvoid sipsess_terminate(struct sipsess *sess, int err,\n\t\t       const struct sip_msg *msg)\n{\n\tsipsess_close_h *closeh;\n\tvoid *arg;\n\n\tif (sess->terminated)\n\t\treturn;\n\n\tcloseh = sess->closeh;\n\targ    = sess->arg;\n\n\tif (!termwait(sess))\n\t\t(void)terminate(sess);\n\n\tcloseh(err, msg, arg);\n}\n\n\n/**\n * Get the SIP dialog from a SIP Session\n *\n * @param sess      SIP Session\n *\n * @return SIP Dialog object\n */\nstruct sip_dialog *sipsess_dialog(const struct sipsess *sess)\n{\n\treturn sess ? sess->dlg : NULL;\n}\n\n\n/**\n * Set extra SIP headers for inclusion in Session \"close\" messages\n * like BYE and 200 OK. Multiple headers can be included.\n *\n * @param sess      SIP Session\n * @param hdrs      Formatted strings with extra SIP Headers\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipsess_set_close_headers(struct sipsess *sess, const char *hdrs, ...)\n{\n\tint err = 0;\n\tva_list ap;\n\n\tif (!sess)\n\t\treturn EINVAL;\n\n\tsess->close_hdrs = mem_deref(sess->close_hdrs);\n\n\tif (hdrs) {\n\t\tva_start(ap, hdrs);\n\t\terr = re_vsdprintf(&sess->close_hdrs, hdrs, ap);\n\t\tva_end(ap);\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Send BYE and terminate session (useful when ACK has not been received)\n *\n * @param sess      SIP Session\n */\nvoid sipsess_abort(struct sipsess *sess)\n{\n\tif (!sess)\n\t\treturn;\n\n\tsipsess_bye(sess, true);\n\tsess->terminated = 2;\n}\n\n\n/**\n * Return true if session is waiting for a PRACK to a 1xx containing SDP\n *\n * @param sess      SIP Session\n *\n * @return true if session is waiting for a PRACK to a 1xx containing SDP,\n * \t   false otherwise\n */\nbool sipsess_awaiting_prack(const struct sipsess *sess)\n{\n\treturn sess ? sess->prack_waiting_cnt > 0 : false;\n}\n\n\n/**\n * Return true if a target refresh (re-INVITE or UPDATE) is currently allowed\n *\n * @param sess      SIP Session\n *\n * @return True if a target refresh is currently allowed, otherwise false\n */\nbool sipsess_refresh_allowed(const struct sipsess *sess)\n{\n\tif (!sess)\n\t\treturn false;\n\n\treturn !sess->terminated && sess->neg_state == SDP_NEG_DONE;\n}\n\n\n/**\n * Return true if there is an open SIP Session Reply for which an ACK is\n * expected\n *\n * @param sess      SIP Session\n *\n * @return True if ACK is pending, otherwise false\n */\nbool sipsess_ack_pending(const struct sipsess *sess)\n{\n\treturn sess && sess->replyl.head ? true : false;\n}\n\n\n/**\n * Get the SIP message of a SIP Session\n *\n * @param sess  SIP Session\n * @return SIP Message\n */\nconst struct sip_msg *sipsess_msg(const struct sipsess *sess)\n{\n\treturn sess ? sess->msg : NULL;\n}\n\n/**\n * Get the SDP negotiation state of a SIP Session\n *\n * @param sess  SIP Session\n *\n * @return SDP negotiation state\n */\nenum sdp_neg_state sipsess_sdp_neg_state(const struct sipsess *sess)\n{\n\treturn sess ? sess->neg_state : SDP_NEG_NONE;\n}\n"
  },
  {
    "path": "src/sipsess/sipsess.h",
    "content": "/**\n * @file sipsess.h  SIP Session Private Interface\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\nstruct sipsess {\n\tstruct le he;\n\tstruct tmr tmr;\n\tstruct list replyl;\n\tstruct list requestl;\n\tstruct sip_loopstate ls;\n\tstruct sipsess_sock *sock;\n\tconst struct sip_msg *msg;\n\tstruct sip_request *req;\n\tstruct sip_dialog *dlg;\n\tstruct sip_strans *st;\n\tstruct sip_auth *auth;\n\tstruct sip *sip;\n\tchar *cuser;\n\tchar *ctype;\n\tchar *close_hdrs;\n\tstruct mbuf *hdrs;\n\tstruct mbuf *desc;\n\tsipsess_desc_h *desch;\n\tsipsess_offer_h *offerh;\n\tsipsess_answer_h *answerh;\n\tsipsess_progr_h *progrh;\n\tsipsess_estab_h *estabh;\n\tsipsess_info_h *infoh;\n\tsipsess_refer_h *referh;\n\tsipsess_close_h *closeh;\n\tsipsess_redirect_h *redirecth;\n\tsipsess_prack_h *prackh;\n\tvoid *arg;\n\tuint32_t rel_seq;\n\tbool owner;\n\tbool modify_pending;\n\tbool established;\n\tbool peerterm;\n\tbool rel100_supported;\n\tint prack_waiting_cnt;\n\tint terminated;\n\tenum sdp_neg_state neg_state;\n};\n\n\nstruct sipsess_sock {\n\tstruct sip_lsnr *lsnr_resp;\n\tstruct sip_lsnr *lsnr_req;\n\tstruct hash *ht_sess;\n\tstruct hash *ht_ack;\n\tstruct sip *sip;\n\tsipsess_conn_h *connh;\n\tvoid *arg;\n};\n\n\nstruct sipsess_request {\n\tstruct le le;\n\tstruct sip_loopstate ls;\n\tstruct sipsess *sess;\n\tstruct sip_request *req;\n\tchar *ctype;\n\tstruct mbuf *body;\n\tsip_resp_h *resph;\n\tstruct tmr tmr;\n\tvoid *arg;\n};\n\n\nint  sipsess_alloc(struct sipsess **sessp, struct sipsess_sock *sock,\n\t\t   const char *cuser, const char *ctype, struct mbuf *desc,\n\t\t   sip_auth_h *authh, void *aarg, bool aref,\n\t\t   sipsess_desc_h *desch,\n\t\t   sipsess_offer_h *offerh, sipsess_answer_h *answerh,\n\t\t   sipsess_progr_h *progrh, sipsess_estab_h *estabh,\n\t\t   sipsess_info_h *infoh, sipsess_refer_h *referh,\n\t\t   sipsess_close_h *closeh, void *arg);\nvoid sipsess_terminate(struct sipsess *sess, int err,\n\t\t       const struct sip_msg *msg);\nint  sipsess_ack(struct sipsess_sock *sock, struct sip_dialog *dlg,\n\t\t uint32_t cseq, struct sip_auth *auth,\n\t\t const char *ctype, struct mbuf *desc);\nint  sipsess_ack_again(struct sipsess_sock *sock, const struct sip_msg *msg);\nint  sipsess_prack(struct sipsess *sess, uint32_t cseq, uint32_t rseq,\n\t\t   const struct pl *met, struct mbuf *desc);\nint  sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg,\n\t\t       uint16_t scode, const char *reason, struct mbuf *desc,\n\t\t       const char *fmt, va_list *ap);\nint  sipsess_reply_1xx(struct sipsess *sess, const struct sip_msg *msg,\n\t\t       uint16_t scode, const char *reason,\n\t\t       enum rel100_mode rel100, struct mbuf *desc,\n\t\t       const char *fmt, va_list *ap);\nint  sipsess_reply_ack(struct sipsess *sess, const struct sip_msg *msg);\nint  sipsess_reply_prack(struct sipsess *sess, const struct sip_msg *msg,\n\t\t\t bool *awaiting_prack);\nint  sipsess_reinvite(struct sipsess *sess, bool reset_ls);\nint  sipsess_update(struct sipsess *sess);\nint  sipsess_bye(struct sipsess *sess, bool reset_ls);\nint  sipsess_request_alloc(struct sipsess_request **reqp, struct sipsess *sess,\n\t\t\t   const char *ctype, struct mbuf *body,\n\t\t\t   sip_resp_h *resph, void *arg);\nstruct sipsess *sipsess_find(struct sipsess_sock *sock,\n\t\t\t     const struct sip_msg *msg);\n"
  },
  {
    "path": "src/sipsess/update.c",
    "content": "/**\n * @file update.c  SIP Session UPDATE (RFC 3311)\n *\n * Copyright (C) 2022 commend.com - m.fridrich@commend.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n#include <re_tmr.h>\n#include <re_msg.h>\n#include <re_sip.h>\n#include <re_sipsess.h>\n#include \"sipsess.h\"\n\n\nstatic int update_request(struct sipsess_request *req);\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct sipsess_request *req = arg;\n\tint err;\n\n\terr = update_request(req);\n\tif (err)\n\t\tmem_deref(req);\n}\n\n\nstatic void update_resp_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct sipsess_request *req = arg;\n\tconst struct sip_hdr *hdr;\n\n\tif (!msg || err || sip_request_loops(&req->ls, msg->scode))\n\t\tgoto out;\n\n\tif (msg->scode < 200) {\n\t\treturn;\n\t}\n\telse if (msg->scode < 300) {\n\t\t(void)sip_dialog_update(req->sess->dlg, msg);\n\n\t\tif (req->sess->neg_state == SDP_NEG_LOCAL_OFFER) {\n\t\t\t(void)req->sess->answerh(msg, req->sess->arg);\n\t\t\treq->sess->neg_state = SDP_NEG_DONE;\n\t\t}\n\t}\n\telse {\n\t\tif (req->sess->terminated)\n\t\t\tgoto out;\n\n\t\treq->sess->neg_state = SDP_NEG_DONE;\n\n\t\tswitch (msg->scode) {\n\n\t\tcase 401:\n\t\tcase 407:\n\t\t\terr = sip_auth_authenticate(req->sess->auth, msg);\n\t\t\tif (err) {\n\t\t\t\terr = (err == EAUTH) ? 0 : err;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\terr = update_request(req);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\n\t\t\treturn;\n\n\t\tcase 408:\n\t\tcase 481:\n\t\t\tsipsess_terminate(req->sess, 0, msg);\n\t\t\tbreak;\n\t\tcase 491:\n\t\t\ttmr_start(&req->tmr, req->sess->owner ? 3000 : 1000,\n\t\t\t\t  tmr_handler, req);\n\t\t\treturn;\n\t\tcase 500:\n\t\t\thdr = sip_msg_hdr(msg, SIP_HDR_RETRY_AFTER);\n\t\t\tif (!hdr)\n\t\t\t\tbreak;\n\n\t\t\ttmr_start(&req->tmr, pl_u32(&hdr->val) * 1000,\n\t\t\t\t  tmr_handler, req);\n\t\t\treturn;\n\n\t\t}\n\t}\n\nout:\n\tif (!req->sess->terminated) {\n\t\tif (err == ETIMEDOUT)\n\t\t\tsipsess_terminate(req->sess, err, NULL);\n\t\telse\n\t\t\treq->resph(err, msg, req->arg);\n\t}\n\n\tmem_deref(req);\n}\n\n\nstatic int send_handler(enum sip_transp tp, struct sa *src,\n\t\t\tconst struct sa *dst, struct mbuf *mb,\n\t\t\tstruct mbuf **contp, void *arg)\n{\n\tstruct sip_contact contact;\n\tstruct sipsess_request *req = arg;\n\t(void)dst;\n\t(void)contp;\n\n\tsip_contact_set(&contact, req->sess->cuser, src, tp);\n\n\treturn mbuf_printf(mb, \"%H\", sip_contact_print, &contact);\n}\n\n\nstatic int update_request(struct sipsess_request *req)\n{\n\tint err;\n\n\tif (!req || req->tmr.th)\n\t\treturn -1;\n\n\terr = sip_drequestf(&req->req, req->sess->sip, true, \"UPDATE\",\n\t\t\t    req->sess->dlg, 0, req->sess->auth, send_handler,\n\t\t\t    update_resp_handler, req,\n\t\t\t    \"%s%s%s\"\n\t\t\t    \"Content-Length: %zu\\r\\n\"\n\t\t\t    \"\\r\\n\"\n\t\t\t    \"%b\",\n\t\t\t    req->body ? \"Content-Type: \" : \"\",\n\t\t\t    req->body ? req->ctype : \"\",\n\t\t\t    req->body ? \"\\r\\n\" : \"\",\n\t\t\t    req->body ? mbuf_get_left(req->body) :(size_t)0,\n\t\t\t    req->body ? mbuf_buf(req->body) : NULL,\n\t\t\t    req->body ? mbuf_get_left(req->body):(size_t)0);\n\n\tif (!err && req->sess->desc)\n\t\treq->sess->neg_state = SDP_NEG_LOCAL_OFFER;\n\n\treturn err;\n}\n\n\n/**\n * Send UPDATE request (RFC 3311)\n *\n * @param sess      SIP Session\n *\n * @return 0 if success, otherwise errorcode\n */\nint sipsess_update(struct sipsess *sess)\n{\n\tstruct sipsess_request *req;\n\tint err;\n\n\tif (!sess || sess->terminated || !sess->ctype || !sess->desc)\n\t\treturn EINVAL;\n\n\terr = sipsess_request_alloc(&req, sess, sess->ctype, sess->desc, NULL,\n\t\t\t\t    NULL);\n\tif (err)\n\t\treturn err;\n\n\terr = update_request(req);\n\tif (err) {\n\t\tmem_deref(req);\n\t\treturn err;\n\t}\n\n\tsess->modify_pending = false;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/srtp/README",
    "content": "SRTP module\n-----------\n\nThe SRTP module implements Secure RTP as defined in RFC 3711.\nIt provides a clean and user friendly API and can be used\nas a standalone module.\n\n\n\n\nRequirements and features:\n\nRFC 3711                       yes\nRFC 6188                       yes\nMultiple Master keys:          no\nKey derivation rate:           0 (zero)\nSalting keys:                  yes\nSRTP protection:               yes\nSRTCP protection:              yes\nReplay protection:             yes\nEncryption:                    yes\nAuthentication:                yes\nMKI (Master Key Identifier):   no\nAuthentication tag length:     32-bit and 80-bit\nROC (Roll Over Counter):       yes\nMaster key lifetime:           no\nMultiple SSRCs:                yes\nPerformance:                   better than libsrtp\n\nCryptographic transforms:\n- AES in Counter mode:         yes\n- AES in f8-mode:              no\n- NULL Cipher:                 no\n\nAuthentication transform:\n- HMAC-SHA1:                   yes\n- NULL auth:                   no\n\nmaster key lengths:\n- 128 bits                     yes\n- 192 bits                     no\n- 256 bits                     yes\n"
  },
  {
    "path": "src/srtp/misc.c",
    "content": "/**\n * @file srtp/misc.c  SRTP functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_aes.h>\n#include <re_sa.h>\n#include <re_srtp.h>\n#include \"srtp.h\"\n\n\n/*\n * Appendix A: Pseudocode for Index Determination\n *\n * In the following, signed arithmetic is assumed.\n */\nuint64_t srtp_get_index(uint32_t roc, uint16_t s_l, uint16_t seq)\n{\n\tint v;\n\n\tif (s_l < 32768) {\n\n\t\tif ((int)seq - (int)s_l > 32768)\n\t\t\tv = (roc-1) & 0xffffffffu;\n\t\telse\n\t\t\tv = roc;\n\t}\n\telse {\n\t\tif ((int)s_l - 32768 > seq)\n\t\t\tv = (roc+1) & 0xffffffffu;\n\t\telse\n\t\t\tv = roc;\n\t}\n\n\treturn seq + v*(uint64_t)65536;\n}\n\n\nint srtp_derive(uint8_t *out, size_t out_len, uint8_t label,\n\t\tconst uint8_t *master_key, size_t key_bytes,\n\t\tconst uint8_t *master_salt, size_t salt_bytes)\n{\n\tuint8_t x[AES_BLOCK_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};\n\tstatic const uint8_t null[AES_BLOCK_SIZE * 2];\n\tstruct aes *aes;\n\tint err;\n\n\tif (!out || !master_key || !master_salt)\n\t\treturn EINVAL;\n\n\tif (out_len > sizeof(null) || salt_bytes > sizeof(x))\n\t\treturn EINVAL;\n\n\tmemcpy(x, master_salt, salt_bytes);\n\tx[7] ^= label;\n\n\t/* NOTE: Counter Mode is used for both CTR and GCM */\n\terr = aes_alloc(&aes, AES_MODE_CTR, master_key, key_bytes*8, x);\n\tif (err)\n\t\treturn err;\n\n\terr = aes_encr(aes, out, null, out_len);\n\n\tmem_deref(aes);\n\n\treturn err;\n\n}\n\n\nvoid srtp_iv_calc(union vect128 *iv, const union vect128 *k_s,\n\t\t  uint32_t ssrc, uint64_t ix)\n{\n\tif (!iv || !k_s)\n\t\treturn;\n\n\tiv->u32[0] = k_s->u32[0];\n\tiv->u32[1] = k_s->u32[1] ^ htonl(ssrc);\n\tiv->u32[2] = k_s->u32[2] ^ htonl((uint32_t)(ix>>16));\n\tiv->u16[6] = k_s->u16[6] ^ htons((uint16_t)ix);\n\tiv->u16[7] = 0;\n}\n\n\n/*\n * NOTE: The IV for AES-GCM is 12 bytes\n */\nvoid srtp_iv_calc_gcm(union vect128 *iv, const union vect128 *k_s,\n\t\t      uint32_t ssrc, uint64_t ix)\n{\n\tif (!iv || !k_s)\n\t\treturn;\n\n\tiv->u16[0] = k_s->u16[0];\n\tiv->u16[1] = k_s->u16[1] ^ htons(ssrc >> 16);\n\tiv->u16[2] = k_s->u16[2] ^ htons(ssrc & 0xffff);\n\tiv->u16[3] = k_s->u16[3] ^ htons((ix >> 32) & 0xffff);\n\tiv->u16[4] = k_s->u16[4] ^ htons((ix >> 16) & 0xffff);\n\tiv->u16[5] = k_s->u16[5] ^ htons(ix & 0xffff);\n}\n\n\nconst char *srtp_suite_name(enum srtp_suite suite)\n{\n\tswitch (suite) {\n\n\tcase SRTP_AES_CM_128_HMAC_SHA1_32:  return \"AES_CM_128_HMAC_SHA1_32\";\n\tcase SRTP_AES_CM_128_HMAC_SHA1_80:  return \"AES_CM_128_HMAC_SHA1_80\";\n\tcase SRTP_AES_256_CM_HMAC_SHA1_32:  return \"AES_256_CM_HMAC_SHA1_32\";\n\tcase SRTP_AES_256_CM_HMAC_SHA1_80:  return \"AES_256_CM_HMAC_SHA1_80\";\n\tcase SRTP_AES_128_GCM:              return \"AEAD_AES_128_GCM\";\n\tcase SRTP_AES_256_GCM:              return \"AEAD_AES_256_GCM\";\n\tdefault:                            return \"?\";\n\t}\n}\n"
  },
  {
    "path": "src/srtp/replay.c",
    "content": "/**\n * @file srtp/replay.c  SRTP replay protection\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_aes.h>\n#include <re_srtp.h>\n#include \"srtp.h\"\n\n\nenum {\n\tSRTP_WINDOW_SIZE = 64\n};\n\n\nvoid srtp_replay_init(struct replay *replay)\n{\n\tif (!replay)\n\t\treturn;\n\n\treplay->bitmap = 0;\n\treplay->lix    = 0;\n}\n\n\n/*\n * Returns false if packet disallowed, true if packet permitted\n */\nbool srtp_replay_check(struct replay *replay, uint64_t ix)\n{\n\tuint64_t diff;\n\n\tif (!replay)\n\t\treturn false;\n\n\tif (ix > replay->lix) {\n\t\tdiff = ix - replay->lix;\n\n\t\tif (diff < SRTP_WINDOW_SIZE) {   /* In window */\n\t\t\treplay->bitmap <<= diff;\n\t\t\treplay->bitmap |= 1;     /* set bit for this packet */\n\t\t}\n\t\telse\n\t\t\treplay->bitmap = 1;\n\n\t\treplay->lix = ix;\n\t\treturn true;\n\t}\n\n\tdiff = replay->lix - ix;\n\tif (diff >= SRTP_WINDOW_SIZE)\n\t\treturn false;\n\n\tif (replay->bitmap & (1ULL << diff))\n\t\treturn false; /* already seen */\n\n\t/* mark as seen */\n\treplay->bitmap |= (1ULL << diff);\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/srtp/srtcp.c",
    "content": "/**\n * @file srtcp.c  Secure Real-time Transport Control Protocol (SRTCP)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_hmac.h>\n#include <re_sha.h>\n#include <re_aes.h>\n#include <re_net.h>\n#include <re_srtp.h>\n#include \"srtp.h\"\n\n\nstatic int get_rtcp_ssrc(uint32_t *ssrc, struct mbuf *mb)\n{\n\tif (mbuf_get_left(mb) < 8)\n\t\treturn EBADMSG;\n\n\tmbuf_advance(mb, 4);\n\t*ssrc = ntohl(mbuf_read_u32(mb));\n\n\treturn 0;\n}\n\n\nint srtcp_encrypt(struct srtp *srtp, struct mbuf *mb)\n{\n\tstruct srtp_stream *strm;\n\tstruct comp *rtcp;\n\tuint32_t ssrc;\n\tsize_t start;\n\tuint32_t ep = 0;\n\tint err;\n\n\tif (!srtp || !mb)\n\t\treturn EINVAL;\n\n\trtcp = &srtp->rtcp;\n\tstart = mb->pos;\n\n\terr = get_rtcp_ssrc(&ssrc, mb);\n\tif (err)\n\t\treturn err;\n\n\terr = stream_get(&strm, srtp, ssrc);\n\tif (err)\n\t\treturn err;\n\n\tstrm->rtcp_index = (strm->rtcp_index+1) & 0x7fffffff;\n\n\tif (rtcp->aes && rtcp->mode == AES_MODE_CTR) {\n\t\tunion vect128 iv;\n\t\tuint8_t *p = mbuf_buf(mb);\n\n\t\tsrtp_iv_calc(&iv, &rtcp->k_s, ssrc, strm->rtcp_index);\n\n\t\taes_set_iv(rtcp->aes, iv.u8);\n\t\terr = aes_encr(rtcp->aes, p, p, mbuf_get_left(mb));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tep = 1;\n\t}\n\telse if (rtcp->aes && rtcp->mode == AES_MODE_GCM) {\n\n\t\tunion vect128 iv;\n\t\tuint8_t *p = mbuf_buf(mb);\n\t\tuint8_t tag[GCM_TAGLEN];\n\t\tconst uint32_t ix_be = htonl(1L<<31 | strm->rtcp_index);\n\n\t\tsrtp_iv_calc_gcm(&iv, &rtcp->k_s, ssrc, strm->rtcp_index);\n\n\t\taes_set_iv(rtcp->aes, iv.u8);\n\n\t\t/* The RTCP Header and Index is Associated Data */\n\t\terr  = aes_encr(rtcp->aes, NULL, &mb->buf[start],\n\t\t\t\tmb->pos - start);\n\t\terr |= aes_encr(rtcp->aes, NULL,\n\t\t\t\t(void *)&ix_be, sizeof(ix_be));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = aes_encr(rtcp->aes, p, p, mbuf_get_left(mb));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = aes_get_authtag(rtcp->aes, tag, sizeof(tag));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos = mb->end;\n\t\terr = mbuf_write_mem(mb, tag, sizeof(tag));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tep = 1;\n\t}\n\n\t/* append E-bit and SRTCP-index */\n\tmb->pos = mb->end;\n\terr = mbuf_write_u32(mb, htonl(ep<<31 | strm->rtcp_index));\n\tif (err)\n\t\treturn err;\n\n\tif (rtcp->hmac) {\n\t\tuint8_t tag[SHA_DIGEST_LENGTH] = {0};\n\n\t\tmb->pos = start;\n\n\t\terr = hmac_digest(rtcp->hmac, tag, sizeof(tag),\n\t\t\t\t  mbuf_buf(mb), mbuf_get_left(mb));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos = mb->end;\n\n\t\terr = mbuf_write_mem(mb, tag, rtcp->tag_len);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tmb->pos = start;\n\n\treturn 0;\n}\n\n\nint srtcp_decrypt(struct srtp *srtp, struct mbuf *mb)\n{\n\tsize_t start, eix_start, pld_start;\n\tstruct srtp_stream *strm;\n\tstruct comp *rtcp;\n\tuint32_t v, ix;\n\tuint32_t ssrc;\n\tbool ep;\n\tint err;\n\n\tif (!srtp || !mb)\n\t\treturn EINVAL;\n\n\trtcp = &srtp->rtcp;\n\tstart = mb->pos;\n\n\terr = get_rtcp_ssrc(&ssrc, mb);\n\tif (err)\n\t\treturn err;\n\n\terr = stream_get(&strm, srtp, ssrc);\n\tif (err)\n\t\treturn err;\n\n\tpld_start = mb->pos;\n\n\tif (mbuf_get_left(mb) < (4 + rtcp->tag_len))\n\t\treturn EBADMSG;\n\n\t/* Read out E-Bit, SRTCP-index and Authentication Tag */\n\teix_start = mb->end - (4 + rtcp->tag_len);\n\tmb->pos = eix_start;\n\tv = ntohl(mbuf_read_u32(mb));\n\n\tep = (v >> 31) & 1;\n\tix = v & 0x7fffffff;\n\n\tif (rtcp->hmac) {\n\t\tuint8_t tag[SHA_DIGEST_LENGTH] = {0};\n\t\tuint8_t tag_pkt[SHA_DIGEST_LENGTH] = {0};\n\t\tconst size_t tag_start = mb->pos;\n\n\t\tif (rtcp->tag_len > SHA_DIGEST_LENGTH)\n\t\t\treturn ERANGE;\n\n\t\terr = mbuf_read_mem(mb, tag_pkt, rtcp->tag_len);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos = start;\n\t\tmb->end = tag_start;\n\n\t\terr = hmac_digest(rtcp->hmac, tag, sizeof(tag),\n\t\t\t\t  mbuf_buf(mb), mbuf_get_left(mb));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (0 != memcmp(tag, tag_pkt, rtcp->tag_len))\n\t\t\treturn EAUTH;\n\n\t\t/*\n\t\t * SRTCP replay protection is as defined in Section 3.3.2,\n\t\t * but using the SRTCP index as the index i and a separate\n\t\t * Replay List that is specific to SRTCP.\n\t\t */\n\t\tif (!srtp_replay_check(&strm->replay_rtcp, ix))\n\t\t\treturn EALREADY;\n\t}\n\n\tmb->end = eix_start;\n\n\tif (rtcp->aes && ep && rtcp->mode == AES_MODE_CTR) {\n\t\tunion vect128 iv;\n\t\tuint8_t *p;\n\n\t\tmb->pos = pld_start;\n\t\tp = mbuf_buf(mb);\n\n\t\tsrtp_iv_calc(&iv, &rtcp->k_s, ssrc, ix);\n\n\t\taes_set_iv(rtcp->aes, iv.u8);\n\t\terr = aes_decr(rtcp->aes, p, p, mbuf_get_left(mb));\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\telse if (rtcp->aes && ep && rtcp->mode == AES_MODE_GCM) {\n\n\t\tunion vect128 iv;\n\t\tsize_t tag_start;\n\t\tuint8_t *p;\n\n\t\tsrtp_iv_calc_gcm(&iv, &rtcp->k_s, ssrc, ix);\n\n\t\taes_set_iv(rtcp->aes, iv.u8);\n\n\t\t/* The RTP Header is Associated Data */\n\t\terr  = aes_decr(rtcp->aes, NULL, &mb->buf[start],\n\t\t\t\tpld_start - start);\n\t\terr |= aes_decr(rtcp->aes, NULL, &mb->buf[eix_start], 4);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos = pld_start;\n\t\tp = mbuf_buf(mb);\n\n\t\tif (mbuf_get_left(mb) < GCM_TAGLEN)\n\t\t\treturn EBADMSG;\n\n\t\ttag_start = mb->end - GCM_TAGLEN;\n\n\t\terr = aes_decr(rtcp->aes, p, p, tag_start - pld_start);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = aes_authenticate(rtcp->aes, &mb->buf[tag_start],\n\t\t\t\t       GCM_TAGLEN);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->end = tag_start;\n\t}\n\n\tmb->pos = start;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/srtp/srtp.c",
    "content": "/**\n * @file srtp.c  Secure Real-time Transport Protocol (SRTP)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_hmac.h>\n#include <re_sha.h>\n#include <re_aes.h>\n#include <re_sa.h>\n#include <re_rtp.h>\n#include <re_srtp.h>\n#include \"srtp.h\"\n\n\n/** SRTP protocol values */\nenum {\n\tMAX_KEYLEN  = 32,  /**< Maximum keylength in bytes     */\n};\n\n\nstatic inline int seq_diff(uint16_t x, uint16_t y)\n{\n\treturn (int)y - (int)x;\n}\n\n\nstatic int comp_init(struct comp *c, unsigned offs,\n\t\t     const uint8_t *key, size_t key_b,\n\t\t     const uint8_t *s, size_t s_b,\n\t\t     size_t tag_len, bool encrypted, bool hash,\n\t\t     enum aes_mode mode)\n{\n\tuint8_t k_e[MAX_KEYLEN], k_a[SHA_DIGEST_LENGTH];\n\tint err = 0;\n\n\tif (key_b > sizeof(k_e))\n\t\treturn EINVAL;\n\n\tif (tag_len > SHA_DIGEST_LENGTH)\n\t\treturn EINVAL;\n\n\tc->tag_len = tag_len;\n\tc->mode = mode;\n\n\terr |= srtp_derive(k_e, key_b,       0x00+offs, key, key_b, s, s_b);\n\terr |= srtp_derive(k_a, sizeof(k_a), 0x01+offs, key, key_b, s, s_b);\n\terr |= srtp_derive(c->k_s.u8, 14,    0x02+offs, key, key_b, s, s_b);\n\tif (err)\n\t\treturn err;\n\n\tif (encrypted) {\n\t\terr = aes_alloc(&c->aes, mode, k_e, key_b*8, NULL);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (hash) {\n\t\terr = hmac_create(&c->hmac, HMAC_HASH_SHA1, k_a, sizeof(k_a));\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn err;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct srtp *srtp = arg;\n\n\tmem_deref(srtp->rtp.aes);\n\tmem_deref(srtp->rtcp.aes);\n\tmem_deref(srtp->rtp.hmac);\n\tmem_deref(srtp->rtcp.hmac);\n\n\tlist_flush(&srtp->streaml);\n}\n\n\nint srtp_alloc(struct srtp **srtpp, enum srtp_suite suite,\n\t       const uint8_t *key, size_t key_bytes, int flags)\n{\n\tstruct srtp *srtp;\n\tconst uint8_t *master_salt;\n\tsize_t cipher_bytes, salt_bytes, auth_bytes;\n\tenum aes_mode mode;\n\tbool hash;\n\tint err = 0;\n\n\tif (!srtpp || !key)\n\t\treturn EINVAL;\n\n\tswitch (suite) {\n\n\tcase SRTP_AES_CM_128_HMAC_SHA1_80:\n\t\tmode         = AES_MODE_CTR;\n\t\tcipher_bytes = 16;\n\t\tsalt_bytes   = 14;\n\t\tauth_bytes   = 10;\n\t\thash         = true;\n\t\tbreak;\n\n\tcase SRTP_AES_CM_128_HMAC_SHA1_32:\n\t\tmode         = AES_MODE_CTR;\n\t\tcipher_bytes = 16;\n\t\tsalt_bytes   = 14;\n\t\tauth_bytes   =  4;\n\t\thash         = true;\n\t\tbreak;\n\n\tcase SRTP_AES_256_CM_HMAC_SHA1_80:\n\t\tmode         = AES_MODE_CTR;\n\t\tcipher_bytes = 32;\n\t\tsalt_bytes   = 14;\n\t\tauth_bytes   = 10;\n\t\thash         = true;\n\t\tbreak;\n\n\tcase SRTP_AES_256_CM_HMAC_SHA1_32:\n\t\tmode         = AES_MODE_CTR;\n\t\tcipher_bytes = 32;\n\t\tsalt_bytes   = 14;\n\t\tauth_bytes   =  4;\n\t\thash         = true;\n\t\tbreak;\n\n\tcase SRTP_AES_128_GCM:\n\t\tmode         = AES_MODE_GCM;\n\t\tcipher_bytes = 16;\n\t\tsalt_bytes   = 12;\n\t\tauth_bytes   = 0;\n\t\thash         = false;\n\t\tbreak;\n\n\tcase SRTP_AES_256_GCM:\n\t\tmode         = AES_MODE_GCM;\n\t\tcipher_bytes = 32;\n\t\tsalt_bytes   = 12;\n\t\tauth_bytes   = 0;\n\t\thash         = false;\n\t\tbreak;\n\n\tdefault:\n\t\treturn ENOTSUP;\n\t};\n\n\tif ((cipher_bytes + salt_bytes) != key_bytes)\n\t\treturn EINVAL;\n\n\tmaster_salt = &key[cipher_bytes];\n\n\tsrtp = mem_zalloc(sizeof(*srtp), destructor);\n\tif (!srtp)\n\t\treturn ENOMEM;\n\n\terr |= comp_init(&srtp->rtp,  0, key, cipher_bytes,\n\t\t\t master_salt, salt_bytes, auth_bytes,\n\t\t\t true, hash, mode);\n\terr |= comp_init(&srtp->rtcp, 3, key, cipher_bytes,\n\t\t\t master_salt, salt_bytes, auth_bytes,\n\t\t\t !(flags & SRTP_UNENCRYPTED_SRTCP), hash, mode);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(srtp);\n\telse\n\t\t*srtpp = srtp;\n\n\treturn err;\n}\n\n\nint srtp_encrypt(struct srtp *srtp, struct mbuf *mb)\n{\n\tstruct srtp_stream *strm;\n\tstruct rtp_header hdr;\n\tstruct comp *comp;\n\tsize_t start;\n\tuint64_t ix;\n\tint err;\n\n\tif (!srtp || !mb)\n\t\treturn EINVAL;\n\n\tcomp = &srtp->rtp;\n\n\tstart = mb->pos;\n\n\terr = rtp_hdr_decode(&hdr, mb);\n\tif (err)\n\t\treturn err;\n\n\terr = stream_get_seq(&strm, srtp, hdr.ssrc, hdr.seq);\n\tif (err)\n\t\treturn err;\n\n\t/* Roll-Over Counter (ROC) */\n\tif (seq_diff(strm->s_l, hdr.seq) <= -32768) {\n\t\tstrm->roc++;\n\t\tstrm->s_l = 0;\n\t}\n\n\tix = 65536ULL * strm->roc + hdr.seq;\n\n\tif (comp->aes && comp->mode == AES_MODE_CTR) {\n\t\tunion vect128 iv;\n\t\tuint8_t *p = mbuf_buf(mb);\n\n\t\tsrtp_iv_calc(&iv, &comp->k_s, strm->ssrc, ix);\n\n\t\taes_set_iv(comp->aes, iv.u8);\n\t\terr = aes_encr(comp->aes, p, p, mbuf_get_left(mb));\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\telse if (comp->aes && comp->mode == AES_MODE_GCM) {\n\t\tunion vect128 iv;\n\t\tuint8_t *p = mbuf_buf(mb);\n\t\tuint8_t tag[GCM_TAGLEN];\n\n\t\tsrtp_iv_calc_gcm(&iv, &comp->k_s, strm->ssrc, ix);\n\n\t\taes_set_iv(comp->aes, iv.u8);\n\n\t\t/* The RTP Header is Associated Data */\n\t\terr = aes_encr(comp->aes, NULL, &mb->buf[start],\n\t\t\t       mb->pos - start);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = aes_encr(comp->aes, p, p, mbuf_get_left(mb));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = aes_get_authtag(comp->aes, tag, sizeof(tag));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos = mb->end;\n\t\terr = mbuf_write_mem(mb, tag, sizeof(tag));\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (comp->hmac) {\n\t\tconst size_t tag_start = mb->end;\n\t\tuint8_t tag[SHA_DIGEST_LENGTH] = {0};\n\n\t\tmb->pos = tag_start;\n\n\t\terr = mbuf_write_u32(mb, htonl(strm->roc));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos = start;\n\n\t\terr = hmac_digest(comp->hmac, tag, sizeof(tag),\n\t\t\t\t  mbuf_buf(mb), mbuf_get_left(mb));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos = mb->end = tag_start;\n\n\t\terr = mbuf_write_mem(mb, tag, comp->tag_len);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (hdr.seq > strm->s_l)\n\t\tstrm->s_l = hdr.seq;\n\n\tmb->pos = start;\n\n\treturn 0;\n}\n\n\nint srtp_decrypt(struct srtp *srtp, struct mbuf *mb)\n{\n\tstruct srtp_stream *strm;\n\tstruct rtp_header hdr;\n\tstruct comp *comp;\n\tuint64_t ix;\n\tsize_t start;\n\tint diff;\n\tint err;\n\n\tif (!srtp || !mb)\n\t\treturn EINVAL;\n\n\tcomp = &srtp->rtp;\n\n\tstart = mb->pos;\n\n\terr = rtp_hdr_decode(&hdr, mb);\n\tif (err)\n\t\treturn err;\n\n\terr = stream_get_seq(&strm, srtp, hdr.ssrc, hdr.seq);\n\tif (err)\n\t\treturn err;\n\n\tdiff = seq_diff(strm->s_l, hdr.seq);\n\tif (diff > 32768)\n\t\treturn ETIMEDOUT;\n\n\t/* Roll-Over Counter (ROC) */\n\tif (diff <= -32768) {\n\t\tstrm->roc++;\n\t\tstrm->s_l = 0;\n\t}\n\n\tix = srtp_get_index(strm->roc, strm->s_l, hdr.seq);\n\n\tif (comp->hmac) {\n\t\tuint8_t tag_calc[SHA_DIGEST_LENGTH] = {0};\n\t\tuint8_t tag_pkt[SHA_DIGEST_LENGTH] = {0};\n\t\tsize_t pld_start, tag_start;\n\n\t\tif (mbuf_get_left(mb) < comp->tag_len)\n\t\t\treturn EBADMSG;\n\n\t\tpld_start = mb->pos;\n\t\ttag_start = mb->end - comp->tag_len;\n\n\t\tmb->pos = tag_start;\n\n\t\terr = mbuf_read_mem(mb, tag_pkt, comp->tag_len);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos = mb->end = tag_start;\n\n\t\terr = mbuf_write_u32(mb, htonl(strm->roc));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos = start;\n\n\t\terr = hmac_digest(comp->hmac, tag_calc, sizeof(tag_calc),\n\t\t\t\t  mbuf_buf(mb), mbuf_get_left(mb));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos = pld_start;\n\t\tmb->end = tag_start;\n\n\t\tif (0 != memcmp(tag_calc, tag_pkt, comp->tag_len))\n\t\t\treturn EAUTH;\n\n\t\t/*\n\t\t * 3.3.2.  Replay Protection\n\t\t *\n\t\t * Secure replay protection is only possible when\n\t\t * integrity protection is present.\n\t\t */\n\t\tif (!srtp_replay_check(&strm->replay_rtp, ix))\n\t\t\treturn EALREADY;\n\t}\n\n\tif (comp->aes && comp->mode == AES_MODE_CTR) {\n\n\t\tunion vect128 iv;\n\t\tuint8_t *p = mbuf_buf(mb);\n\n\t\tsrtp_iv_calc(&iv, &comp->k_s, strm->ssrc, ix);\n\n\t\taes_set_iv(comp->aes, iv.u8);\n\t\terr = aes_decr(comp->aes, p, p, mbuf_get_left(mb));\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\telse if (comp->aes && comp->mode == AES_MODE_GCM) {\n\n\t\tunion vect128 iv;\n\t\tuint8_t *p = mbuf_buf(mb);\n\t\tsize_t tag_start;\n\n\t\tsrtp_iv_calc_gcm(&iv, &comp->k_s, strm->ssrc, ix);\n\n\t\taes_set_iv(comp->aes, iv.u8);\n\n\t\t/* The RTP Header is Associated Data */\n\t\terr = aes_decr(comp->aes, NULL, &mb->buf[start],\n\t\t\t       mb->pos - start);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (mbuf_get_left(mb) < GCM_TAGLEN)\n\t\t\treturn EBADMSG;\n\n\t\ttag_start = mb->end - GCM_TAGLEN;\n\n\t\terr = aes_decr(comp->aes, p, p, tag_start - mb->pos);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = aes_authenticate(comp->aes, &mb->buf[tag_start],\n\t\t\t\t       GCM_TAGLEN);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->end = tag_start;\n\n\t\t/*\n\t\t * 3.3.2.  Replay Protection\n\t\t *\n\t\t * Secure replay protection is only possible when\n\t\t * integrity protection is present.\n\t\t */\n\t\tif (!srtp_replay_check(&strm->replay_rtp, ix))\n\t\t\treturn EALREADY;\n\n\t}\n\n\tif (hdr.seq > strm->s_l)\n\t\tstrm->s_l = hdr.seq;\n\n\tmb->pos = start;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/srtp/srtp.h",
    "content": "/**\n * @file srtp.h  Secure Real-time Transport Protocol (SRTP) -- internal\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** SRTP Protocol values */\nenum {\n\tGCM_TAGLEN  = 16,  /**< GCM taglength in bytes         */\n};\n\n\n/** Defines a 128-bit vector in network order */\nunion vect128 {\n\tuint64_t u64[ 2];\n\tuint32_t u32[ 4];\n\tuint16_t u16[ 8];\n\tuint8_t   u8[16];\n};\n\n/** Replay protection */\nstruct replay {\n\tuint64_t bitmap;   /**< Session state - must be 64 bits */\n\tuint64_t lix;      /**< Last received index             */\n};\n\n/** SRTP stream/context -- shared state between RTP/RTCP */\nstruct srtp_stream {\n\tstruct le le;              /**< Linked-list element                */\n\tstruct replay replay_rtp;  /**< recv -- replay protection for RTP  */\n\tstruct replay replay_rtcp; /**< recv -- replay protection for RTCP */\n\tuint32_t ssrc;             /**< SSRC -- lookup key                 */\n\tuint32_t roc;              /**< send/recv Roll-Over Counter (ROC)  */\n\tuint16_t s_l;              /**< send/recv -- highest SEQ number    */\n\tbool s_l_set;              /**< True if s_l has been set           */\n\tuint32_t rtcp_index;       /**< RTCP-index for sending (31-bits)   */\n};\n\n/** SRTP Session */\nstruct srtp {\n\tstruct comp {\n\t\tstruct aes *aes;    /**< AES Context                       */\n\t\tenum aes_mode mode; /**< AES encryption mode               */\n\t\tstruct hmac *hmac;  /**< HMAC Context                      */\n\t\tunion vect128 k_s;  /**< Derived salting key (14 bytes)    */\n\t\tsize_t tag_len;     /**< CTR Auth. tag length [bytes]      */\n\t} rtp, rtcp;\n\n\tstruct list streaml;        /**< SRTP-streams (struct srtp_stream) */\n};\n\n\nint stream_get(struct srtp_stream **strmp, struct srtp *srtp, uint32_t ssrc);\nint stream_get_seq(struct srtp_stream **strmp, struct srtp *srtp,\n\t\t   uint32_t ssrc, uint16_t seq);\n\n\nint  srtp_derive(uint8_t *out, size_t out_len, uint8_t label,\n\t\t const uint8_t *master_key, size_t key_bytes,\n\t\t const uint8_t *master_salt, size_t salt_bytes);\nvoid srtp_iv_calc(union vect128 *iv, const union vect128 *k_s,\n\t\t  uint32_t ssrc, uint64_t ix);\nvoid srtp_iv_calc_gcm(union vect128 *iv, const union vect128 *k_s,\n\t\t      uint32_t ssrc, uint64_t ix);\nuint64_t srtp_get_index(uint32_t roc, uint16_t s_l, uint16_t seq);\n\n\n/* Replay protection */\n\nvoid srtp_replay_init(struct replay *replay);\nbool srtp_replay_check(struct replay *replay, uint64_t ix);\n"
  },
  {
    "path": "src/srtp/stream.c",
    "content": "/**\n * @file srtp/stream.c  Secure Real-time Transport Protocol (SRTP) -- stream\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_aes.h>\n#include <re_srtp.h>\n#include \"srtp.h\"\n\n\n/** SRTP protocol values */\n#ifndef SRTP_MAX_STREAMS\n#define SRTP_MAX_STREAMS  (8)  /**< Maximum number of SRTP streams */\n#endif\n\n\nstatic void stream_destructor(void *arg)\n{\n\tstruct srtp_stream *strm = arg;\n\n\tlist_unlink(&strm->le);\n}\n\n\nstatic struct srtp_stream *stream_find(struct srtp *srtp, uint32_t ssrc)\n{\n\tstruct le *le;\n\n\tfor (le = srtp->streaml.head; le; le = le->next) {\n\n\t\tstruct srtp_stream *strm = le->data;\n\n\t\tif (strm->ssrc == ssrc)\n\t\t\treturn strm;\n\t}\n\n\treturn NULL;\n}\n\n\nstatic int stream_new(struct srtp_stream **strmp, struct srtp *srtp,\n\t\t      uint32_t ssrc)\n{\n\tstruct srtp_stream *strm;\n\n\tif (list_count(&srtp->streaml) >= SRTP_MAX_STREAMS)\n\t\treturn ENOSR;\n\n\tstrm = mem_zalloc(sizeof(*strm), stream_destructor);\n\tif (!strm)\n\t\treturn ENOMEM;\n\n\tstrm->ssrc = ssrc;\n\tsrtp_replay_init(&strm->replay_rtp);\n\tsrtp_replay_init(&strm->replay_rtcp);\n\n\tlist_append(&srtp->streaml, &strm->le, strm);\n\n\tif (strmp)\n\t\t*strmp = strm;\n\n\treturn 0;\n}\n\n\nint stream_get(struct srtp_stream **strmp, struct srtp *srtp, uint32_t ssrc)\n{\n\tstruct srtp_stream *strm;\n\n\tif (!strmp || !srtp)\n\t\treturn EINVAL;\n\n\tstrm = stream_find(srtp, ssrc);\n\tif (strm) {\n\t\t*strmp = strm;\n\t\treturn 0;\n\t}\n\n\treturn stream_new(strmp, srtp, ssrc);\n}\n\n\nint stream_get_seq(struct srtp_stream **strmp, struct srtp *srtp,\n\t\t   uint32_t ssrc, uint16_t seq)\n{\n\tstruct srtp_stream *strm;\n\tint err;\n\n\tif (!strmp || !srtp)\n\t\treturn EINVAL;\n\n\terr = stream_get(&strm, srtp, ssrc);\n\tif (err)\n\t\treturn err;\n\n\t/* Set the initial sequence number once only */\n\tif (!strm->s_l_set) {\n\t\tstrm->s_l = seq;\n\t\tstrm->s_l_set = true;\n\t}\n\n\t*strmp = strm;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/stun/addr.c",
    "content": "/**\n * @file stun/addr.c  STUN Address encoding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_stun.h>\n#include \"stun.h\"\n\n\nstatic void in6_xor_tid(uint8_t *in6, const uint8_t *tid)\n{\n\tint i;\n\n\t/* XOR with Magic Cookie (alignment safe) */\n\tin6[0] ^= 0x21;\n\tin6[1] ^= 0x12;\n\tin6[2] ^= 0xa4;\n\tin6[3] ^= 0x42;\n\n\tfor (i=0; i<STUN_TID_SIZE; i++)\n\t\tin6[4+i] ^= tid[i];\n}\n\n\nint stun_addr_encode(struct mbuf *mb, const struct sa *addr,\n\t\t     const uint8_t *tid)\n{\n\tuint8_t addr6[16];\n\tuint16_t port;\n\tuint32_t addr4;\n\tint err = 0;\n\n\tif (!mb || !addr)\n\t\treturn EINVAL;\n\n\tport = tid ? sa_port(addr)^(STUN_MAGIC_COOKIE>>16) : sa_port(addr);\n\n\tswitch (sa_af(addr)) {\n\n\tcase AF_INET:\n\t\taddr4 = tid ? sa_in(addr)^STUN_MAGIC_COOKIE : sa_in(addr);\n\n\t\terr |= mbuf_write_u8(mb, 0);\n\t\terr |= mbuf_write_u8(mb, STUN_AF_IPv4);\n\t\terr |= mbuf_write_u16(mb, htons(port));\n\t\terr |= mbuf_write_u32(mb, htonl(addr4));\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tsa_in6(addr, addr6);\n\t\tif (tid)\n\t\t\tin6_xor_tid(addr6, tid);\n\n\t\terr |= mbuf_write_u8(mb, 0);\n\t\terr |= mbuf_write_u8(mb, STUN_AF_IPv6);\n\t\terr |= mbuf_write_u16(mb, htons(port));\n\t\terr |= mbuf_write_mem(mb, addr6, 16);\n\t\tbreak;\n\n\tdefault:\n\t\terr = EAFNOSUPPORT;\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nint stun_addr_decode(struct mbuf *mb, struct sa *addr, const uint8_t *tid)\n{\n\tuint8_t family, addr6[16];\n\tuint32_t addr4;\n\tuint16_t port;\n\n\tif (!mb || !addr)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < 4)\n\t\treturn EBADMSG;\n\n\t(void)mbuf_read_u8(mb);\n\tfamily = mbuf_read_u8(mb);\n\tport = ntohs(mbuf_read_u16(mb));\n\n\tif (tid)\n\t\tport ^= STUN_MAGIC_COOKIE>>16;\n\n\tswitch (family) {\n\n\tcase STUN_AF_IPv4:\n\t\tif (mbuf_get_left(mb) < 4)\n\t\t\treturn EBADMSG;\n\n\t\taddr4 = ntohl(mbuf_read_u32(mb));\n\t\tif (tid)\n\t\t\taddr4 ^= STUN_MAGIC_COOKIE;\n\n\t\tsa_set_in(addr, addr4, port);\n\t\tbreak;\n\n\tcase STUN_AF_IPv6:\n\t\tif (mbuf_get_left(mb) < 16)\n\t\t\treturn EBADMSG;\n\n\t\t(void)mbuf_read_mem(mb, addr6, 16);\n\t\tif (tid)\n\t\t\tin6_xor_tid(addr6, tid);\n\n\t\tsa_set_in6(addr, addr6, port);\n\t\tbreak;\n\n\tdefault:\n\t\treturn EAFNOSUPPORT;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/stun/attr.c",
    "content": "/**\n * @file stun/attr.c  STUN Attributes\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_fmt.h>\n#include <re_sys.h>\n#include <re_stun.h>\n#include \"stun.h\"\n\n\nstatic int str_decode(struct mbuf *mb, char **str, size_t len)\n{\n\tif (mbuf_get_left(mb) < len)\n\t\treturn EBADMSG;\n\n\treturn mbuf_strdup(mb, str, len);\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct stun_attr *attr = arg;\n\n\tswitch (attr->type) {\n\n\tcase STUN_ATTR_USERNAME:\n\tcase STUN_ATTR_REALM:\n\tcase STUN_ATTR_NONCE:\n\tcase STUN_ATTR_SOFTWARE:\n\t\tmem_deref(attr->v.str);\n\t\tbreak;\n\n\tcase STUN_ATTR_ERR_CODE:\n\t\tmem_deref(attr->v.err_code.reason);\n\t\tbreak;\n\n\tcase STUN_ATTR_DATA:\n\t\tmem_deref(attr->v.mb.buf);\n\t\tbreak;\n\t}\n\n\tlist_unlink(&attr->le);\n}\n\n\nint stun_attr_encode(struct mbuf *mb, uint16_t type, const void *v,\n\t\t     const uint8_t *tid, uint8_t padding)\n{\n\tconst struct stun_change_req *ch_req = v;\n\tconst struct stun_errcode *err_code = v;\n\tconst struct stun_unknown_attr *ua = v;\n\tconst uint32_t *num32 = v;\n\tconst uint16_t *num16 = v;\n\tconst uint8_t *num8 = v;\n\tconst struct mbuf *mbd = v;\n\tsize_t start, len;\n\tuint32_t i, n;\n\tint err = 0;\n\n\tif (!mb || !v)\n\t\treturn EINVAL;\n\n\tmb->pos += 4;\n\tstart = mb->pos;\n\n\tswitch (type) {\n\n\tcase STUN_ATTR_MAPPED_ADDR:\n\tcase STUN_ATTR_ALT_SERVER:\n\tcase STUN_ATTR_RESP_ORIGIN:\n\tcase STUN_ATTR_OTHER_ADDR:\n\t\ttid = NULL;\n\t\t/*@fallthrough@*/\n\tcase STUN_ATTR_XOR_PEER_ADDR:\n\tcase STUN_ATTR_XOR_RELAY_ADDR:\n\tcase STUN_ATTR_XOR_MAPPED_ADDR:\n\t\terr |= stun_addr_encode(mb, v, tid);\n\t\tbreak;\n\n\tcase STUN_ATTR_CHANGE_REQ:\n\t\tn = (uint32_t)ch_req->ip << 2 | (uint32_t)ch_req->port << 1;\n\t\terr |= mbuf_write_u32(mb, htonl(n));\n\t\tbreak;\n\n\tcase STUN_ATTR_USERNAME:\n\tcase STUN_ATTR_REALM:\n\tcase STUN_ATTR_NONCE:\n\tcase STUN_ATTR_SOFTWARE:\n\t\terr |= mbuf_write_str(mb, v);\n\t\tbreak;\n\n\tcase STUN_ATTR_MSG_INTEGRITY:\n\t\terr |= mbuf_write_mem(mb, v, 20);\n\t\tbreak;\n\n\tcase STUN_ATTR_ERR_CODE:\n\t\terr |= mbuf_write_u16(mb, 0x00);\n\t\terr |= mbuf_write_u8(mb,  err_code->code/100);\n\t\terr |= mbuf_write_u8(mb,  err_code->code%100);\n\t\terr |= mbuf_write_str(mb, err_code->reason);\n\t\tbreak;\n\n\tcase STUN_ATTR_UNKNOWN_ATTR:\n\t\tfor (i=0; i<ua->typec; i++)\n\t\t\terr |= mbuf_write_u16(mb, htons(ua->typev[i]));\n\t\tbreak;\n\n\tcase STUN_ATTR_CHANNEL_NUMBER:\n\tcase STUN_ATTR_RESP_PORT:\n\t\terr |= mbuf_write_u16(mb, htons(*num16));\n\t\terr |= mbuf_write_u16(mb, 0x0000);\n\t\tbreak;\n\n\tcase STUN_ATTR_LIFETIME:\n\tcase STUN_ATTR_PRIORITY:\n\tcase STUN_ATTR_FINGERPRINT:\n\t\terr |= mbuf_write_u32(mb, htonl(*num32));\n\t\tbreak;\n\n\tcase STUN_ATTR_DATA:\n\t\tif (mb == mbd) {\n\t\t\tmb->pos = mb->end;\n\t\t\tbreak;\n\t\t}\n\t\terr |= mbuf_write_mem(mb, mbuf_buf(mbd), mbuf_get_left(mbd));\n\t\tbreak;\n\n\tcase STUN_ATTR_REQ_ADDR_FAMILY:\n\tcase STUN_ATTR_REQ_TRANSPORT:\n\t\terr |= mbuf_write_u8(mb, *num8);\n\t\terr |= mbuf_write_u8(mb, 0x00);\n\t\terr |= mbuf_write_u16(mb, 0x0000);\n\t\tbreak;\n\n\tcase STUN_ATTR_EVEN_PORT:\n\t\terr |= mbuf_write_u8(mb, ((struct stun_even_port *)v)->r << 7);\n\t\tbreak;\n\n\tcase STUN_ATTR_DONT_FRAGMENT:\n\tcase STUN_ATTR_USE_CAND:\n\t\t /* no value */\n\t\tbreak;\n\n\tcase STUN_ATTR_RSV_TOKEN:\n\tcase STUN_ATTR_CONTROLLED:\n\tcase STUN_ATTR_CONTROLLING:\n\t\terr |= mbuf_write_u64(mb, sys_htonll(*(uint64_t *)v));\n\t\tbreak;\n\n\tdefault:\n\t\terr = EINVAL;\n\t\tbreak;\n\t}\n\n\t/* header */\n\tlen = mb->pos - start;\n\n\tmb->pos = start - 4;\n\terr |= mbuf_write_u16(mb, htons(type));\n\terr |= mbuf_write_u16(mb, htons((uint16_t)len));\n\tmb->pos += len;\n\n\t/* padding */\n\twhile ((mb->pos - start) & 0x03)\n\t\terr |= mbuf_write_u8(mb, padding);\n\n\treturn err;\n}\n\n\nint stun_attr_decode(struct stun_attr **attrp, struct mbuf *mb,\n\t\t     const uint8_t *tid, struct stun_unknown_attr *ua)\n{\n\tstruct stun_attr *attr;\n\tsize_t start, len;\n\tuint32_t i, n;\n\tint err = 0;\n\n\tif (!mb || !attrp)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < 4)\n\t\treturn EBADMSG;\n\n\tattr = mem_zalloc(sizeof(*attr), destructor);\n\tif (!attr)\n\t\treturn ENOMEM;\n\n\tattr->type = ntohs(mbuf_read_u16(mb));\n\tlen = ntohs(mbuf_read_u16(mb));\n\n\tif (mbuf_get_left(mb) < len)\n\t\tgoto badmsg;\n\n\tstart = mb->pos;\n\n\tswitch (attr->type) {\n\n\tcase STUN_ATTR_MAPPED_ADDR:\n\tcase STUN_ATTR_ALT_SERVER:\n\tcase STUN_ATTR_RESP_ORIGIN:\n\tcase STUN_ATTR_OTHER_ADDR:\n\t\ttid = NULL;\n\t\t/*@fallthrough@*/\n\tcase STUN_ATTR_XOR_PEER_ADDR:\n\tcase STUN_ATTR_XOR_RELAY_ADDR:\n\tcase STUN_ATTR_XOR_MAPPED_ADDR:\n\t\terr = stun_addr_decode(mb, &attr->v.sa, tid);\n\t\tbreak;\n\n\tcase STUN_ATTR_CHANGE_REQ:\n\t\tif (len != 4)\n\t\t\tgoto badmsg;\n\n\t\tn = ntohl(mbuf_read_u32(mb));\n\t\tattr->v.change_req.ip   = (n >> 2) & 0x1;\n\t\tattr->v.change_req.port = (n >> 1) & 0x1;\n\t\tbreak;\n\n\tcase STUN_ATTR_USERNAME:\n\tcase STUN_ATTR_REALM:\n\tcase STUN_ATTR_NONCE:\n\tcase STUN_ATTR_SOFTWARE:\n\t\terr = str_decode(mb, &attr->v.str, len);\n\t\tbreak;\n\n\tcase STUN_ATTR_MSG_INTEGRITY:\n\t\tif (len != 20)\n\t\t\tgoto badmsg;\n\n\t\terr = mbuf_read_mem(mb, attr->v.msg_integrity, 20);\n\t\tbreak;\n\n\tcase STUN_ATTR_ERR_CODE:\n\t\tif (len < 4)\n\t\t\tgoto badmsg;\n\n\t\tmb->pos += 2;\n\t\tattr->v.err_code.code  = (mbuf_read_u8(mb) & 0x7) * 100;\n\t\tattr->v.err_code.code += mbuf_read_u8(mb);\n\t\terr = str_decode(mb, &attr->v.err_code.reason, len - 4);\n\t\tbreak;\n\n\tcase STUN_ATTR_UNKNOWN_ATTR:\n\t\tfor (i=0; i<len/2; i++) {\n\t\t\tuint16_t type = ntohs(mbuf_read_u16(mb));\n\n\t\t\tif (i >= RE_ARRAY_SIZE(attr->v.unknown_attr.typev))\n\t\t\t\tcontinue;\n\n\t\t\tattr->v.unknown_attr.typev[i] = type;\n\t\t\tattr->v.unknown_attr.typec++;\n\t\t}\n\t\tbreak;\n\n\tcase STUN_ATTR_CHANNEL_NUMBER:\n\tcase STUN_ATTR_RESP_PORT:\n\t\tif (len < 2)\n\t\t\tgoto badmsg;\n\n\t        attr->v.uint16 = ntohs(mbuf_read_u16(mb));\n\t\tbreak;\n\n\tcase STUN_ATTR_LIFETIME:\n\tcase STUN_ATTR_PRIORITY:\n\tcase STUN_ATTR_FINGERPRINT:\n\t\tif (len != 4)\n\t\t\tgoto badmsg;\n\n\t        attr->v.uint32 = ntohl(mbuf_read_u32(mb));\n\t\tbreak;\n\n\tcase STUN_ATTR_DATA:\n\t\tattr->v.mb.buf  = mem_ref(mb->buf);\n\t\tattr->v.mb.size = mb->size;\n\t\tattr->v.mb.pos  = mb->pos;\n\t\tattr->v.mb.end  = mb->pos + len;\n\t\tmb->pos += len;\n\t\tbreak;\n\n\tcase STUN_ATTR_REQ_ADDR_FAMILY:\n\tcase STUN_ATTR_REQ_TRANSPORT:\n\t\tif (len < 1)\n\t\t\tgoto badmsg;\n\n\t        attr->v.uint8 = mbuf_read_u8(mb);\n\t\tbreak;\n\n\tcase STUN_ATTR_EVEN_PORT:\n\t\tif (len < 1)\n\t\t\tgoto badmsg;\n\n\t\tattr->v.even_port.r = (mbuf_read_u8(mb) >> 7) & 0x1;\n\t\tbreak;\n\n\tcase STUN_ATTR_DONT_FRAGMENT:\n\tcase STUN_ATTR_USE_CAND:\n\t\tif (len > 0)\n\t\t\tgoto badmsg;\n\n\t\t/* no value */\n\t\tbreak;\n\n\tcase STUN_ATTR_RSV_TOKEN:\n\tcase STUN_ATTR_CONTROLLING:\n\tcase STUN_ATTR_CONTROLLED:\n\t\tif (len != 8)\n\t\t\tgoto badmsg;\n\n\t        attr->v.uint64 = sys_ntohll(mbuf_read_u64(mb));\n\t\tbreak;\n\n\tdefault:\n\t\tmb->pos += len;\n\n\t\tif (attr->type >= 0x8000)\n\t\t\tbreak;\n\n\t\tif (ua && ua->typec < RE_ARRAY_SIZE(ua->typev))\n\t\t\tua->typev[ua->typec++] = attr->type;\n\t\tbreak;\n\t}\n\n\tif (err)\n\t\tgoto error;\n\n\t/* padding */\n\twhile (((mb->pos - start) & 0x03) && mbuf_get_left(mb))\n\t\t++mb->pos;\n\n\t*attrp = attr;\n\n\treturn 0;\n\n badmsg:\n\terr = EBADMSG;\n error:\n\tmem_deref(attr);\n\n\treturn err;\n}\n\n\n/**\n * Get the name of a STUN attribute\n *\n * @param type STUN attribute type\n *\n * @return String with attribute name\n */\nconst char *stun_attr_name(uint16_t type)\n{\n\tswitch (type) {\n\n\tcase STUN_ATTR_MAPPED_ADDR:       return \"MAPPED-ADDRESS\";\n\tcase STUN_ATTR_CHANGE_REQ:        return \"CHANGE-REQUEST\";\n\tcase STUN_ATTR_USERNAME:          return \"USERNAME\";\n\tcase STUN_ATTR_MSG_INTEGRITY:     return \"MESSAGE-INTEGRITY\";\n\tcase STUN_ATTR_ERR_CODE:          return \"ERROR-CODE\";\n\tcase STUN_ATTR_UNKNOWN_ATTR:      return \"UNKNOWN-ATTRIBUTE\";\n\tcase STUN_ATTR_CHANNEL_NUMBER:    return \"CHANNEL-NUMBER\";\n\tcase STUN_ATTR_LIFETIME:          return \"LIFETIME\";\n\tcase STUN_ATTR_XOR_PEER_ADDR:     return \"XOR-PEER-ADDRESS\";\n\tcase STUN_ATTR_DATA:              return \"DATA\";\n\tcase STUN_ATTR_REALM:             return \"REALM\";\n\tcase STUN_ATTR_NONCE:             return \"NONCE\";\n\tcase STUN_ATTR_XOR_RELAY_ADDR:    return \"XOR-RELAYED-ADDRESS\";\n\tcase STUN_ATTR_REQ_ADDR_FAMILY:   return \"REQUESTED-ADDRESS-FAMILY\";\n\tcase STUN_ATTR_EVEN_PORT:         return \"EVEN_PORT\";\n\tcase STUN_ATTR_REQ_TRANSPORT:     return \"REQUESTED-TRANSPORT\";\n\tcase STUN_ATTR_DONT_FRAGMENT:     return \"DONT-FRAGMENT\";\n\tcase STUN_ATTR_XOR_MAPPED_ADDR:   return \"XOR-MAPPED-ADDRESS\";\n\tcase STUN_ATTR_RSV_TOKEN:         return \"RESERVATION-TOKEN\";\n\tcase STUN_ATTR_PRIORITY:          return \"PRIORITY\";\n\tcase STUN_ATTR_USE_CAND:          return \"USE-CANDIDATE\";\n\tcase STUN_ATTR_RESP_PORT:         return \"RESPONSE-PORT\";\n\tcase STUN_ATTR_SOFTWARE:          return \"SOFTWARE\";\n\tcase STUN_ATTR_ALT_SERVER:        return \"ALTERNATE-SERVER\";\n\tcase STUN_ATTR_FINGERPRINT:       return \"FINGERPRINT\";\n\tcase STUN_ATTR_CONTROLLING:       return \"ICE-CONTROLLING\";\n\tcase STUN_ATTR_CONTROLLED:        return \"ICE-CONTROLLED\";\n\tcase STUN_ATTR_RESP_ORIGIN:       return \"RESPONSE-ORIGIN\";\n\tcase STUN_ATTR_OTHER_ADDR:        return \"OTHER-ADDR\";\n\tdefault:                          return \"???\";\n\t}\n}\n\n\nvoid stun_attr_dump(const struct stun_attr *a)\n{\n\tuint32_t i;\n\tsize_t len;\n\n\tif (!a)\n\t\treturn;\n\n\t(void)re_printf(\" %-25s\", stun_attr_name(a->type));\n\n\tswitch (a->type) {\n\n\tcase STUN_ATTR_MAPPED_ADDR:\n\tcase STUN_ATTR_XOR_PEER_ADDR:\n\tcase STUN_ATTR_XOR_RELAY_ADDR:\n\tcase STUN_ATTR_XOR_MAPPED_ADDR:\n\tcase STUN_ATTR_ALT_SERVER:\n\tcase STUN_ATTR_RESP_ORIGIN:\n\tcase STUN_ATTR_OTHER_ADDR:\n\t\t(void)re_printf(\"%J\", &a->v.sa);\n\t\tbreak;\n\n\tcase STUN_ATTR_CHANGE_REQ:\n\t\t(void)re_printf(\"ip=%u port=%u\", a->v.change_req.ip,\n\t\t\t\ta->v.change_req.port);\n\t\tbreak;\n\n\tcase STUN_ATTR_USERNAME:\n\tcase STUN_ATTR_REALM:\n\tcase STUN_ATTR_NONCE:\n\tcase STUN_ATTR_SOFTWARE:\n\t\t(void)re_printf(\"%s\", a->v.str);\n\t\tbreak;\n\n\tcase STUN_ATTR_MSG_INTEGRITY:\n\t\t(void)re_printf(\"%w\", a->v.msg_integrity,\n\t\t\t\tsizeof(a->v.msg_integrity));\n\t\tbreak;\n\n\tcase STUN_ATTR_ERR_CODE:\n\t\t(void)re_printf(\"%u %s\", a->v.err_code.code,\n\t\t\t\ta->v.err_code.reason);\n\t\tbreak;\n\n\tcase STUN_ATTR_UNKNOWN_ATTR:\n\t\tfor (i=0; i<a->v.unknown_attr.typec; i++)\n\t\t\t(void)re_printf(\"0x%04x \", a->v.unknown_attr.typev[i]);\n\t\tbreak;\n\n\tcase STUN_ATTR_CHANNEL_NUMBER:\n\t\t(void)re_printf(\"0x%04x\", a->v.uint16);\n\t\tbreak;\n\n\tcase STUN_ATTR_LIFETIME:\n\tcase STUN_ATTR_PRIORITY:\n\t\t(void)re_printf(\"%u\", a->v.uint32);\n\t\tbreak;\n\n\tcase STUN_ATTR_DATA:\n\t\tlen = min(mbuf_get_left(&a->v.mb), 16);\n\t\t(void)re_printf(\"%w%s (%zu bytes)\", mbuf_buf(&a->v.mb), len,\n\t\t\t\tmbuf_get_left(&a->v.mb) > 16 ? \"...\" : \"\",\n\t\t\t\tmbuf_get_left(&a->v.mb));\n\t\tbreak;\n\n\tcase STUN_ATTR_REQ_ADDR_FAMILY:\n\tcase STUN_ATTR_REQ_TRANSPORT:\n\t\t(void)re_printf(\"%u\", a->v.uint8);\n\t\tbreak;\n\n\tcase STUN_ATTR_EVEN_PORT:\n\t\t(void)re_printf(\"r=%u\", a->v.even_port.r);\n\t\tbreak;\n\n\tcase STUN_ATTR_DONT_FRAGMENT:\n\tcase STUN_ATTR_USE_CAND:\n\t\t/* no value */\n\t\tbreak;\n\n\tcase STUN_ATTR_RSV_TOKEN:\n\t\t(void)re_printf(\"0x%016llx\", a->v.rsv_token);\n\t\tbreak;\n\n\tcase STUN_ATTR_RESP_PORT:\n\t\t(void)re_printf(\"%u\", a->v.uint16);\n\t\tbreak;\n\n\tcase STUN_ATTR_FINGERPRINT:\n\t\t(void)re_printf(\"0x%08x\", a->v.fingerprint);\n\t\tbreak;\n\n\tcase STUN_ATTR_CONTROLLING:\n\tcase STUN_ATTR_CONTROLLED:\n\t\t(void)re_printf(\"%llu\", a->v.uint64);\n\t\tbreak;\n\n\tdefault:\n\t\t(void)re_printf(\"???\");\n\t\tbreak;\n\t}\n\n\t(void)re_printf(\"\\n\");\n}\n"
  },
  {
    "path": "src/stun/ctrans.c",
    "content": "/**\n * @file stun/ctrans.c  STUN Client transactions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_udp.h>\n#include <re_tcp.h>\n#include <re_srtp.h>\n#include <re_tls.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_md5.h>\n#include <re_stun.h>\n#include \"stun.h\"\n\n\nstruct stun_ctrans {\n\tstruct le le;\n\tstruct tmr tmr;\n\tstruct sa dst;\n\tuint8_t tid[STUN_TID_SIZE];\n\tstruct stun_ctrans **ctp;\n\tuint8_t *key;\n\tsize_t keylen;\n\tvoid *sock;\n\tstruct mbuf *mb;\n\tsize_t pos;\n\tstruct stun *stun;\n\tstun_resp_h *resph;\n\tvoid *arg;\n\tint proto;\n\tuint32_t txc;\n\tuint32_t ival;\n\tuint16_t met;\n};\n\n\nstatic void completed(struct stun_ctrans *ct, int err, uint16_t scode,\n\t\t      const char *reason, const struct stun_msg *msg)\n{\n\tstun_resp_h *resph = ct->resph;\n\tvoid *arg = ct->arg;\n\n\tlist_unlink(&ct->le);\n\ttmr_cancel(&ct->tmr);\n\n\tif (ct->ctp) {\n\t\t*ct->ctp = NULL;\n\t\tct->ctp = NULL;\n\t}\n\n\tct->resph = NULL;\n\n\t/* must be destroyed before calling handler */\n\tmem_deref(ct);\n\n\tif (resph)\n\t\tresph(err, scode, reason, msg, arg);\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct stun_ctrans *ct = arg;\n\n\tlist_unlink(&ct->le);\n\ttmr_cancel(&ct->tmr);\n\tmem_deref(ct->key);\n\tmem_deref(ct->sock);\n\tmem_deref(ct->mb);\n}\n\n\nstatic void timeout_handler(void *arg)\n{\n\tstruct stun_ctrans *ct = arg;\n\tconst struct stun_conf *cfg = stun_conf(ct->stun);\n\tint err = ETIMEDOUT;\n\n\tif (ct->txc++ >= cfg->rc)\n\t\tgoto error;\n\n\tct->mb->pos = ct->pos;\n\n\terr = stun_send(ct->proto, ct->sock, &ct->dst, ct->mb);\n\tif (err)\n\t\tgoto error;\n\n\tct->ival = (ct->txc >= cfg->rc) ? cfg->rto * cfg->rm : ct->ival * 2;\n\n\ttmr_start(&ct->tmr, ct->ival, timeout_handler, ct);\n\treturn;\n\n error:\n\tcompleted(ct, err, 0, NULL, NULL);\n}\n\n\nstatic bool match_handler(struct le *le, void *arg)\n{\n\tstruct stun_ctrans *ct = le->data;\n\tstruct stun_msg *msg = arg;\n\n\tif (ct->met != stun_msg_method(msg))\n\t\treturn false;\n\n\tif (memcmp(ct->tid, stun_msg_tid(msg), STUN_TID_SIZE))\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstatic void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct stun *stun = arg;\n\t(void)src;\n\n\t(void)stun_recv(stun, mb);\n}\n\n\nstatic void tcp_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct stun_ctrans *ct = arg;\n\n\t(void)stun_recv(ct->stun, mb);\n}\n\n\nstatic void tcp_estab_handler(void *arg)\n{\n\tstruct stun_ctrans *ct = arg;\n\tint err;\n\n\terr = tcp_send(ct->sock, ct->mb);\n\tif (!err)\n\t\treturn;\n\n\tcompleted(ct, err, 0, NULL, NULL);\n}\n\n\nstatic void tcp_close_handler(int err, void *arg)\n{\n\tstruct stun_ctrans *ct = arg;\n\n\tcompleted(ct, err, 0, NULL, NULL);\n}\n\n\n/**\n * Handle an incoming STUN message to a Client Transaction\n *\n * @param stun STUN instance\n * @param msg  STUN message\n * @param ua   Unknown attributes\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_ctrans_recv(struct stun *stun, const struct stun_msg *msg,\n\t\t     const struct stun_unknown_attr *ua)\n{\n\tstruct stun_errcode ec = {0, \"OK\"};\n\tstruct stun_attr *errcode;\n\tstruct stun_ctrans *ct;\n\tint err = 0, herr = 0;\n\n\tif (!stun || !msg || !ua)\n\t\treturn EINVAL;\n\n\tswitch (stun_msg_class(msg)) {\n\n\tcase STUN_CLASS_ERROR_RESP:\n\t\terrcode = stun_msg_attr(msg, STUN_ATTR_ERR_CODE);\n\t\tif (!errcode)\n\t\t\therr = EPROTO;\n\t\telse\n\t\t\tec = errcode->v.err_code;\n\t\t/*@fallthrough@*/\n\n\tcase STUN_CLASS_SUCCESS_RESP:\n\t\tct = list_ledata(list_apply(&stun->ctl, true,\n\t\t\t\t\t    match_handler, (void *)msg));\n\t\tif (!ct) {\n\t\t\terr = ENOENT;\n\t\t\tbreak;\n\t\t}\n\n\t\tswitch (ec.code) {\n\n\t\tcase 401:\n\t\tcase 438:\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tif (!ct->key)\n\t\t\t\tbreak;\n\n\t\t\terr = stun_msg_chk_mi(msg, ct->key, ct->keylen);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tif (!herr && ua->typec > 0)\n\t\t\therr = EPROTO;\n\n\t\tcompleted(ct, herr, ec.code, ec.reason, msg);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nint stun_ctrans_request(struct stun_ctrans **ctp, struct stun *stun, int proto,\n\t\t\tvoid *sock, const struct sa *dst, struct mbuf *mb,\n\t\t\tconst uint8_t tid[], uint16_t met, const uint8_t *key,\n\t\t\tsize_t keylen, stun_resp_h *resph, void *arg)\n{\n\tstruct stun_ctrans *ct;\n\tint err = 0;\n\n\tif (!stun || !mb)\n\t\treturn EINVAL;\n\n\tct = mem_zalloc(sizeof(*ct), destructor);\n\tif (!ct)\n\t\treturn ENOMEM;\n\n\tlist_append(&stun->ctl, &ct->le, ct);\n\tmemcpy(ct->tid, tid, STUN_TID_SIZE);\n\tct->proto = proto;\n\tct->sock  = mem_ref(sock);\n\tct->mb    = mem_ref(mb);\n\tct->pos   = mb->pos;\n\tct->stun  = stun;\n\tct->met   = met;\n\n\tif (key) {\n\t\tct->key = mem_alloc(keylen, NULL);\n\t\tif (!ct->key) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\tmemcpy(ct->key, key, keylen);\n\t\tct->keylen = keylen;\n\t}\n\n\tswitch (proto) {\n\n\tcase IPPROTO_UDP:\n\t\tif (!dst) {\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\n\t\tct->dst = *dst;\n\t\tct->ival = stun_conf(stun)->rto;\n\t\ttmr_start(&ct->tmr, ct->ival, timeout_handler, ct);\n\n\t\tif (!sock) {\n\t\t\terr = udp_listen((struct udp_sock **)&ct->sock, NULL,\n\t\t\t\t\t udp_recv_handler, stun);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tct->txc = 1;\n\t\terr = udp_send(ct->sock, dst, mb);\n\t\tbreak;\n\n\tcase IPPROTO_TCP:\n\t\tct->txc = stun_conf(stun)->rc;\n\t\ttmr_start(&ct->tmr, stun_conf(stun)->ti, timeout_handler, ct);\n\t\tif (sock) {\n\t\t\terr = tcp_send(sock, mb);\n\t\t\tbreak;\n\t\t}\n\n\t\terr = tcp_connect((struct tcp_conn **)&ct->sock, dst,\n\t\t\t\t  tcp_estab_handler, tcp_recv_handler,\n\t\t\t\t  tcp_close_handler, ct);\n\t\tbreak;\n\n#ifdef USE_DTLS\n\tcase STUN_TRANSP_DTLS:\n\t\tif (!sock) {\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\n\t\tct->ival = stun_conf(stun)->rto;\n\t\ttmr_start(&ct->tmr, ct->ival, timeout_handler, ct);\n\n\t\tct->txc = 1;\n\t\terr = dtls_send(ct->sock, mb);\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\terr = EPROTONOSUPPORT;\n\t\tbreak;\n\t}\n\n out:\n\tif (!err) {\n\t\tif (ctp) {\n\t\t\tct->ctp = ctp;\n\t\t\t*ctp = ct;\n\t\t}\n\n\t\tct->resph = resph;\n\t\tct->arg   = arg;\n\t}\n\telse\n\t\tmem_deref(ct);\n\n\treturn err;\n}\n\n\nstatic bool close_handler(struct le *le, void *arg)\n{\n\tstruct stun_ctrans *ct = le->data;\n\t(void)arg;\n\n\tcompleted(ct, ECONNABORTED, 0, NULL, NULL);\n\n\treturn false;\n}\n\n\nvoid stun_ctrans_close(struct stun *stun)\n{\n\tif (!stun)\n\t\treturn;\n\n\t(void)list_apply(&stun->ctl, true, close_handler, NULL);\n}\n\n\nstatic bool debug_handler(struct le *le, void *arg)\n{\n\tstruct stun_ctrans *ct = le->data;\n\tstruct re_printf *pf = arg;\n\tint err = 0;\n\n\terr |= re_hprintf(pf, \" method=%s\", stun_method_name(ct->met));\n\terr |= re_hprintf(pf, \" tid=%w\", ct->tid, sizeof(ct->tid));\n\terr |= re_hprintf(pf, \" rto=%ums\", stun_conf(ct->stun)->rto);\n\terr |= re_hprintf(pf, \" tmr=%llu\", tmr_get_expire(&ct->tmr));\n\terr |= re_hprintf(pf, \" n=%u\", ct->txc);\n\terr |= re_hprintf(pf, \" interval=%u\", ct->ival);\n\terr |= re_hprintf(pf, \"\\n\");\n\n\treturn 0 != err;\n}\n\n\nint stun_ctrans_debug(struct re_printf *pf, const struct stun *stun)\n{\n\tint err;\n\n\tif (!stun)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"STUN client transactions: (%u)\\n\",\n\t\t\t list_count(&stun->ctl));\n\n\t(void)list_apply(&stun->ctl, true, debug_handler, pf);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/stun/dnsdisc.c",
    "content": "/**\n * @file dnsdisc.c  DNS Discovery of a STUN Server\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_dns.h>\n#include <re_stun.h>\n\n\n#define DEBUG_MODULE \"dnsdisc\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** DNS Query */\nstruct stun_dns {\n\tchar domain[256];      /**< Cached domain name      */\n\tstun_dns_h *dnsh;      /**< DNS Response handler    */\n\tvoid *arg;             /**< Handler argument        */\n\tstruct sa srv;         /**< Resolved server address */\n\tstruct dnsc *dnsc;     /**< DNS Client              */\n\tstruct dns_query *dq;  /**< Current DNS query       */\n\tint af;                /**< Preferred Address family*/\n\tuint16_t port;         /**< Default Port            */\n};\n\nconst char *stun_proto_udp = \"udp\";   /**< UDP Protocol  */\nconst char *stun_proto_tcp = \"tcp\";   /**< TCP Protocol  */\n\nconst char *stun_usage_binding   = \"stun\";  /**< Binding usage */\nconst char *stuns_usage_binding  = \"stuns\"; /**< Binding usage TLS */\nconst char *stun_usage_relay     = \"turn\";\nconst char *stuns_usage_relay    = \"turns\";\n\n\nstatic void resolved(const struct stun_dns *dns, int err)\n{\n\tstun_dns_h *dnsh = dns->dnsh;\n\tvoid *dnsh_arg = dns->arg;\n\n\tDEBUG_INFO(\"resolved: %J (%m)\\n\", &dns->srv, err);\n\n\tdnsh(err, &dns->srv, dnsh_arg);\n}\n\n\nstatic void a_handler(int err, const struct dnshdr *hdr, struct list *ansl,\n\t\t      struct list *authl, struct list *addl, void *arg)\n{\n\tstruct stun_dns *dns = arg;\n\tstruct dnsrr *rr;\n\n\t(void)hdr;\n\t(void)authl;\n\t(void)addl;\n\n\t/* Find A answers */\n\trr = dns_rrlist_find(ansl, NULL, DNS_TYPE_A, DNS_CLASS_IN, false);\n\tif (!rr) {\n\t\terr = err ? err : EDESTADDRREQ;\n\t\tgoto out;\n\t}\n\n\tsa_set_in(&dns->srv, rr->rdata.a.addr, sa_port(&dns->srv));\n\n\tDEBUG_INFO(\"A answer: %j\\n\", &dns->srv);\n\n out:\n\tresolved(dns, err);\n}\n\n\nstatic void aaaa_handler(int err, const struct dnshdr *hdr, struct list *ansl,\n\t\t\t struct list *authl, struct list *addl, void *arg)\n{\n\tstruct stun_dns *dns = arg;\n\tstruct dnsrr *rr;\n\n\t(void)hdr;\n\t(void)authl;\n\t(void)addl;\n\n\t/* Find A answers */\n\trr = dns_rrlist_find(ansl, NULL, DNS_TYPE_AAAA, DNS_CLASS_IN, false);\n\tif (!rr) {\n\t\terr = err ? err : EDESTADDRREQ;\n\t\tgoto out;\n\t}\n\n\tsa_set_in6(&dns->srv, rr->rdata.aaaa.addr, sa_port(&dns->srv));\n\n\tDEBUG_INFO(\"AAAA answer: %j\\n\", &dns->srv);\n\n out:\n\tresolved(dns, err);\n}\n\n\nstatic int a_or_aaaa_query(struct stun_dns *dns, const char *name)\n{\n\tdns->dq = mem_deref(dns->dq);\n\n\tswitch (dns->af) {\n\n\tcase AF_INET:\n\t\treturn dnsc_query(&dns->dq, dns->dnsc, name, DNS_TYPE_A,\n\t\t\t\t  DNS_CLASS_IN, true, a_handler, dns);\n\n\tcase AF_INET6:\n\t\treturn dnsc_query(&dns->dq, dns->dnsc, name, DNS_TYPE_AAAA,\n\t\t\t\t  DNS_CLASS_IN, true, aaaa_handler, dns);\n\n\tdefault:\n\t\treturn EAFNOSUPPORT;\n\t}\n}\n\n\nstatic void srv_handler(int err, const struct dnshdr *hdr, struct list *ansl,\n\t\t\tstruct list *authl, struct list *addl, void *arg)\n{\n\tstruct stun_dns *dns = arg;\n\tstruct dnsrr *rr, *arr;\n\n\t(void)hdr;\n\t(void)authl;\n\n\tdns_rrlist_sort(ansl, DNS_TYPE_SRV, (size_t)dns->arg);\n\n\t/* Find SRV answers */\n\trr = dns_rrlist_find(ansl, NULL, DNS_TYPE_SRV, DNS_CLASS_IN, false);\n\tif (!rr) {\n\t\tDEBUG_INFO(\"no SRV entry, trying A lookup on \\\"%s\\\"\\n\",\n\t\t\t   dns->domain);\n\n\t\tsa_set_in(&dns->srv, 0, dns->port);\n\n\t\terr = a_or_aaaa_query(dns, dns->domain);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\treturn;\n\t}\n\n\tDEBUG_INFO(\"SRV answer: %s:%u\\n\", rr->rdata.srv.target,\n\t\t   rr->rdata.srv.port);\n\n\t/* Look for Additional information */\n\tswitch (dns->af) {\n\n\tcase AF_INET:\n\t\tarr = dns_rrlist_find(addl, rr->rdata.srv.target,\n\t\t\t\t      DNS_TYPE_A, DNS_CLASS_IN, true);\n\t\tif (arr) {\n\t\t\tsa_set_in(&dns->srv, arr->rdata.a.addr,\n\t\t\t\t  rr->rdata.srv.port);\n\t\t\tDEBUG_INFO(\"additional A: %j\\n\", &dns->srv);\n\t\t\tgoto out;\n\t\t}\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tarr = dns_rrlist_find(addl, rr->rdata.srv.target,\n\t\t\t\t      DNS_TYPE_AAAA, DNS_CLASS_IN, true);\n\t\tif (arr) {\n\t\t\tsa_set_in6(&dns->srv, arr->rdata.aaaa.addr,\n\t\t\t\t  rr->rdata.srv.port);\n\t\t\tDEBUG_INFO(\"additional AAAA: %j\\n\", &dns->srv);\n\t\t\tgoto out;\n\t\t}\n\t\tbreak;\n\t}\n\n\tsa_set_in(&dns->srv, 0, rr->rdata.srv.port);\n\n\terr = a_or_aaaa_query(dns, rr->rdata.srv.target);\n\tif (err) {\n\t\tDEBUG_WARNING(\"SRV: A lookup failed (%m)\\n\", err);\n\t\tgoto out;\n\t}\n\n\tDEBUG_INFO(\"SRV handler: doing A/AAAA lookup..\\n\");\n\n\treturn;\n\n out:\n\tresolved(dns, err);\n}\n\n\nstatic void dnsdisc_destructor(void *data)\n{\n\tstruct stun_dns *dns = data;\n\n\tmem_deref(dns->dq);\n}\n\n\n/**\n * Do a DNS Discovery of a STUN Server\n *\n * @param dnsp    Pointer to allocated DNS Discovery object\n * @param dnsc    DNS Client\n * @param service Name of service to discover (e.g. \"stun\")\n * @param proto   Transport protocol (e.g. \"udp\")\n * @param af      Preferred Address Family\n * @param domain  Domain name or IP address of STUN server\n * @param port    Port number (if 0 do SRV lookup)\n * @param dnsh    DNS Response handler\n * @param arg     Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_server_discover(struct stun_dns **dnsp, struct dnsc *dnsc,\n\t\t\t const char *service, const char *proto,\n\t\t\t int af, const char *domain, uint16_t port,\n\t\t\t stun_dns_h *dnsh, void *arg)\n{\n\tstruct stun_dns *dns;\n\tint err;\n\n\tif (!dnsp || !service || !proto || !domain || !domain[0] || !dnsh)\n\t\treturn EINVAL;\n\n\tdns = mem_zalloc(sizeof(*dns), dnsdisc_destructor);\n\tif (!dns)\n\t\treturn ENOMEM;\n\n\tdns->port = service[strlen(service)-1] == 's' ? STUNS_PORT : STUN_PORT;\n\tdns->dnsh = dnsh;\n\tdns->arg  = arg;\n\tdns->dnsc = dnsc;\n\tdns->af   = af;\n\n\t/* Numeric IP address - no lookup */\n\tif (0 == sa_set_str(&dns->srv, domain, port ? port : dns->port)) {\n\n\t\tDEBUG_INFO(\"IP (%s)\\n\", domain);\n\n\t\tresolved(dns, 0);\n\t\terr = 0;\n\t\tgoto out; /* free now */\n\t}\n\t/* Port specified - use AAAA or A lookup */\n\telse if (port) {\n\t\tsa_set_in(&dns->srv, 0, port);\n\t\tDEBUG_INFO(\"resolving A query: (%s)\\n\", domain);\n\n\t\terr = a_or_aaaa_query(dns, domain);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"%s: A/AAAA lookup failed (%m)\\n\",\n\t\t\t\t      domain, err);\n\t\t\tgoto out;\n\t\t}\n\t}\n\t/* SRV lookup */\n\telse {\n\t\tchar q[256];\n\t\tstr_ncpy(dns->domain, domain, sizeof(dns->domain));\n\t\t(void)re_snprintf(q, sizeof(q), \"_%s._%s.%s\", service, proto,\n\t\t\t\t  domain);\n\t\tDEBUG_INFO(\"resolving SRV query: (%s)\\n\", q);\n\t\terr = dnsc_query(&dns->dq, dnsc, q, DNS_TYPE_SRV, DNS_CLASS_IN,\n\t\t\t\t true, srv_handler, dns);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"%s: SRV lookup failed (%m)\\n\", q, err);\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\t*dnsp = dns;\n\n\treturn 0;\n\n out:\n\tmem_deref(dns);\n\treturn err;\n}\n"
  },
  {
    "path": "src/stun/hdr.c",
    "content": "/**\n * @file stun/hdr.c  STUN Header encoding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_sys.h>\n#include <re_stun.h>\n#include \"stun.h\"\n\n\nint stun_hdr_encode(struct mbuf *mb, const struct stun_hdr *hdr)\n{\n\tint err = 0;\n\n\tif (!mb || !hdr)\n\t\treturn EINVAL;\n\n\terr |= mbuf_write_u16(mb, htons(hdr->type & 0x3fff));\n\terr |= mbuf_write_u16(mb, htons(hdr->len));\n\terr |= mbuf_write_u32(mb, htonl(hdr->cookie));\n\terr |= mbuf_write_mem(mb, hdr->tid, sizeof(hdr->tid));\n\n\treturn err;\n}\n\n\nint stun_hdr_decode(struct mbuf *mb, struct stun_hdr *hdr)\n{\n\tif (!mb || !hdr)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < STUN_HEADER_SIZE)\n\t\treturn EBADMSG;\n\n\thdr->type = ntohs(mbuf_read_u16(mb));\n\tif (hdr->type & 0xc000)\n\t\treturn EBADMSG;\n\n\thdr->len = ntohs(mbuf_read_u16(mb));\n\tif (hdr->len & 0x3)\n\t\treturn EBADMSG;\n\n\thdr->cookie = ntohl(mbuf_read_u32(mb));\n\t(void)mbuf_read_mem(mb, hdr->tid, sizeof(hdr->tid));\n\n\tif (mbuf_get_left(mb) < hdr->len)\n\t\treturn EBADMSG;\n\n\treturn 0;\n}\n\n\nconst char *stun_class_name(uint16_t class)\n{\n\tswitch (class) {\n\n\tcase STUN_CLASS_REQUEST:      return \"Request\";\n\tcase STUN_CLASS_INDICATION:   return \"Indication\";\n\tcase STUN_CLASS_SUCCESS_RESP: return \"Success Response\";\n\tcase STUN_CLASS_ERROR_RESP:   return \"Error Response\";\n\tdefault:                      return \"???\";\n\t}\n}\n\n\nconst char *stun_method_name(uint16_t method)\n{\n\tswitch (method) {\n\n\tcase STUN_METHOD_BINDING:    return \"Binding\";\n\tcase STUN_METHOD_ALLOCATE:   return \"Allocate\";\n\tcase STUN_METHOD_REFRESH:    return \"Refresh\";\n\tcase STUN_METHOD_SEND:       return \"Send\";\n\tcase STUN_METHOD_DATA:       return \"Data\";\n\tcase STUN_METHOD_CREATEPERM: return \"CreatePermission\";\n\tcase STUN_METHOD_CHANBIND:   return \"ChannelBind\";\n\tdefault:                     return \"???\";\n\t}\n}\n\n\nvoid stun_generate_tid(uint8_t tid[STUN_TID_SIZE])\n{\n\trand_bytes(tid, STUN_TID_SIZE);\n}\n"
  },
  {
    "path": "src/stun/ind.c",
    "content": "/**\n * @file ind.c  STUN Indication\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_sys.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_stun.h>\n#include \"stun.h\"\n\n\n/**\n * Send a STUN Indication message\n *\n * @param proto   Transport Protocol\n * @param sock    Socket; UDP (struct udp_sock) or TCP (struct tcp_conn)\n * @param dst     Destination network address\n * @param presz   Number of bytes in preamble, if sending over TURN\n * @param method  STUN Method\n * @param key     Authentication key (optional)\n * @param keylen  Number of bytes in authentication key\n * @param fp      Use STUN Fingerprint attribute\n * @param attrc   Number of attributes to encode (variable arguments)\n * @param ...     Variable list of attribute-tuples\n *                Each attribute has 2 arguments, attribute type and value\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_indication(int proto, void *sock, const struct sa *dst, size_t presz,\n\t\t    uint16_t method, const uint8_t *key, size_t keylen,\n\t\t    bool fp, uint32_t attrc, ...)\n{\n\tuint8_t tid[STUN_TID_SIZE];\n\tstruct mbuf *mb;\n\tva_list ap;\n\tint err;\n\n\tif (!sock)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(2048);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tstun_generate_tid(tid);\n\n\tva_start(ap, attrc);\n\tmb->pos = presz;\n\terr = stun_msg_vencode(mb, method, STUN_CLASS_INDICATION, tid, NULL,\n\t\t\t       key, keylen, fp, 0x00, attrc, ap);\n\tva_end(ap);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = presz;\n\terr = stun_send(proto, sock, dst, mb);\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/stun/keepalive.c",
    "content": "/**\n * @file stun/keepalive.c  STUN usage for NAT Keepalives\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_udp.h>\n#include <re_stun.h>\n\n\n#define DEBUG_MODULE \"keepalive\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** Defines a STUN Keepalive session */\nstruct stun_keepalive {\n\tstruct stun_ctrans *ct;   /**< STUN client transaction              */\n\tstruct stun *stun;        /**< STUN instance                        */\n\tstruct udp_helper *uh;    /**< UDP Helper                           */\n\tint proto;                /**< Transport protocol                   */\n\tvoid *sock;               /**< Transport Socket                     */\n\tstruct sa dst;            /**< Destination network address          */\n\tstruct tmr tmr;           /**< Refresh timer                        */\n\tuint32_t interval;        /**< Refresh interval in seconds          */\n\tstun_mapped_addr_h *mah;  /**< Mapped address handler               */\n\tvoid *arg;                /**< Handler argument                     */\n\tstruct sa curmap;         /**< Currently mapped IP address and port */\n};\n\nstatic void timeout(void *arg);\n\n\nstatic void keepalive_destructor(void *data)\n{\n\tstruct stun_keepalive *ska = data;\n\n\ttmr_cancel(&ska->tmr);\n\n\tmem_deref(ska->ct);\n\tmem_deref(ska->uh);\n\tmem_deref(ska->sock);\n\tmem_deref(ska->stun);\n}\n\n\nstatic void call_handler(struct stun_keepalive *ska, int err,\n\t\t\t const struct sa *map)\n{\n\tif (ska->mah)\n\t\tska->mah(err, map, ska->arg);\n}\n\n\nstatic void stun_response_handler(int err, uint16_t scode, const char *reason,\n\t\t\t\t  const struct stun_msg *msg, void *arg)\n{\n\tstruct stun_keepalive *ska = arg;\n\tstruct stun_attr *attr;\n\t(void)reason;\n\n\t/* Restart timer */\n\tif (ska->interval > 0)\n\t\ttmr_start(&ska->tmr, ska->interval*1000, timeout, ska);\n\n\tif (err || scode) {\n\t\t/* Clear current mapped addr to force new notification */\n\t\tsa_set_in(&ska->curmap, 0, 0);\n\n\t\tgoto out;\n\t}\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);\n\tif (!attr)\n\t\tattr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR);\n\n\tif (!attr) {\n\t\terr = ENOENT;\n\t\tgoto out;\n\t}\n\n\tif (!sa_cmp(&ska->curmap, &attr->v.sa, SA_ALL)) {\n\t\tska->curmap = attr->v.sa;\n\t\tcall_handler(ska, 0, &ska->curmap);\n\t}\n\n out:\n\tif (err)\n\t\tcall_handler(ska, err, NULL);\n}\n\n\nstatic void timeout(void *arg)\n{\n\tstruct stun_keepalive *ska = arg;\n\tint err;\n\n\tif (ska->ct)\n\t\tska->ct = mem_deref(ska->ct);\n\n\terr = stun_request(&ska->ct, ska->stun, ska->proto, ska->sock,\n\t\t\t   &ska->dst, 0, STUN_METHOD_BINDING, NULL, 0, false,\n\t\t\t   stun_response_handler, ska, 1,\n\t\t\t   STUN_ATTR_SOFTWARE, stun_software);\n\tif (0 == err)\n\t\treturn;\n\n\t/* Restart timer */\n\tif (ska->interval > 0)\n\t\ttmr_start(&ska->tmr, ska->interval*1000, timeout, ska);\n\n\t/* Error */\n\tcall_handler(ska, err, NULL);\n}\n\n\nstatic bool udp_recv_handler(struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct stun_keepalive *ska = arg;\n\tstruct stun_unknown_attr ua;\n\tstruct stun_msg *msg;\n\tsize_t pos = mb->pos;\n\tbool hdld;\n\n\tif (!sa_cmp(&ska->dst, src, SA_ALL))\n\t\treturn false;\n\n\tif (stun_msg_decode(&msg, mb, &ua))\n\t\treturn false;\n\n\tif (stun_msg_method(msg) != STUN_METHOD_BINDING) {\n\t\thdld = false;\n\t\tmb->pos = pos;\n\t\tgoto out;\n\t}\n\n\tswitch (stun_msg_class(msg)) {\n\n\tcase STUN_CLASS_ERROR_RESP:\n\tcase STUN_CLASS_SUCCESS_RESP:\n\t\t(void)stun_ctrans_recv(ska->stun, msg, &ua);\n\t\thdld = true;\n\t\tbreak;\n\n\tdefault:\n\t\thdld = false;\n\t\tmb->pos = pos;\n\t\tbreak;\n\t}\n\n out:\n\tmem_deref(msg);\n\n\treturn hdld;\n}\n\n\n/**\n * Allocate a new STUN keepalive session\n *\n * @param skap  Pointer to keepalive object\n * @param proto Transport protocol\n * @param sock  Socket\n * @param layer Protocol layer\n * @param dst   Destination address\n * @param conf  Configuration\n * @param mah   Mapped address handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_keepalive_alloc(struct stun_keepalive **skap,\n\t\t\t int proto, void *sock, int layer,\n\t\t\t const struct sa *dst, const struct stun_conf *conf,\n\t\t\t stun_mapped_addr_h *mah, void *arg)\n{\n\tstruct stun_keepalive *ska;\n\tint err;\n\n\tif (!skap)\n\t\treturn EINVAL;\n\n\tska = mem_zalloc(sizeof(*ska), keepalive_destructor);\n\tif (!ska)\n\t\treturn ENOMEM;\n\n\terr = stun_alloc(&ska->stun, conf, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\ttmr_init(&ska->tmr);\n\n\tska->proto = proto;\n\tska->sock = mem_ref(sock);\n\tska->mah = mah;\n\tska->arg = arg;\n\n\tif (dst)\n\t\tska->dst = *dst;\n\n\tswitch (proto) {\n\n\tcase IPPROTO_UDP:\n\t\terr = udp_register_helper(&ska->uh, sock, layer,\n\t\t\t\t\t  NULL, udp_recv_handler, ska);\n\t\tbreak;\n\n\tdefault:\n\t\terr = 0;\n\t\tbreak;\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(ska);\n\telse\n\t\t*skap = ska;\n\n\treturn err;\n}\n\n\n/**\n * Enable or disable keepalive timer\n *\n * @param ska      Keepalive object\n * @param interval Interval in seconds (0 to disable)\n */\nvoid stun_keepalive_enable(struct stun_keepalive *ska, uint32_t interval)\n{\n\tif (!ska)\n\t\treturn;\n\n\tska->interval = interval;\n\n\ttmr_cancel(&ska->tmr);\n\tif (interval > 0)\n\t\ttmr_start(&ska->tmr, 1, timeout, ska);\n}\n"
  },
  {
    "path": "src/stun/msg.c",
    "content": "/**\n * @file stun/msg.c  STUN message encoding\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_fmt.h>\n#include <re_md5.h>\n#include <re_sha.h>\n#include <re_hmac.h>\n#include <re_crc32.h>\n#include <re_stun.h>\n#include \"stun.h\"\n\n\nenum {\n\tMI_SIZE = 24,\n\tFP_SIZE = 8\n};\n\n\n/**\n   Defines a STUN Message object\n\n   <pre>\n\n   .---------------------.      /|\\           /|\\\n   | STUN Header         |       |             |\n   |---------------------|       |             |\n   |         ....        |       |--------.    |\n   |      N Attributes   |       |        |    |----.\n   |         ....        |      \\|/       |    |    |\n   |---------------------|                |    |    |\n   |  MESSAGE-INTEGRITY  | <-(HMAC-SHA1)--'   \\|/   |\n   |---------------------|                          |\n   |     FINGERPRINT     | <-(CRC-32)---------------'\n   '---------------------'\n   </pre>\n*/\nstruct stun_msg {\n\tstruct stun_hdr hdr;\n\tstruct list attrl;\n\tstruct mbuf *mb;\n\tsize_t start;\n};\n\n\nstatic uint32_t fingerprint(const uint8_t *buf, size_t len)\n{\n\treturn re_crc32(0, buf, (unsigned int)len) ^ 0x5354554e;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct stun_msg *msg = arg;\n\n\tlist_flush(&msg->attrl);\n\tmem_deref(msg->mb);\n}\n\n\n/**\n * Decode a buffer to a STUN Message\n *\n * @param msgpp Pointer to allocation STUN message\n * @param mb    Buffer containing the raw STUN packet\n * @param ua    Unknown attributes (optional)\n *\n * @return 0 if success, otherwise errorcode\n *\n * @note `mb` will be referenced\n */\nint stun_msg_decode(struct stun_msg **msgpp, struct mbuf *mb,\n\t\t    struct stun_unknown_attr *ua)\n{\n\tstruct stun_msg *msg;\n\tstruct stun_hdr hdr;\n\tsize_t start, extra;\n\tint err;\n\n\tif (!msgpp || !mb)\n\t\treturn EINVAL;\n\n\tstart = mb->pos;\n\n\terr = stun_hdr_decode(mb, &hdr);\n\tif (err) {\n\t\tmb->pos = start;\n\t\treturn err;\n\t}\n\n\tmsg = mem_zalloc(sizeof(*msg), destructor);\n\tif (!msg) {\n\t\tmb->pos = start;\n\t\treturn ENOMEM;\n\t}\n\n\tmsg->hdr = hdr;\n\tmsg->mb = mem_ref(mb);\n\tmsg->start = start;\n\n\tif (ua)\n\t\tua->typec = 0;\n\n\t/* mbuf_get_left(mb) >= hdr.len checked in stun_hdr_decode() above */\n\textra = mbuf_get_left(mb) - hdr.len;\n\n\twhile (mbuf_get_left(mb) - extra >= 4) {\n\n\t\tstruct stun_attr *attr;\n\n\t\terr = stun_attr_decode(&attr, mb, hdr.tid, ua);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tlist_append(&msg->attrl, &attr->le, attr);\n\t}\n\n\tif (err)\n\t\tmem_deref(msg);\n\telse\n\t\t*msgpp = msg;\n\n\tmb->pos = start;\n\n\treturn err;\n}\n\n\n/**\n * Get the STUN message type\n *\n * @param msg STUN Message\n *\n * @return STUN Message type\n */\nuint16_t stun_msg_type(const struct stun_msg *msg)\n{\n\treturn msg ? msg->hdr.type : 0;\n}\n\n\n/**\n * Get the STUN message class\n *\n * @param msg STUN Message\n *\n * @return STUN Message class\n */\nuint16_t stun_msg_class(const struct stun_msg *msg)\n{\n\treturn STUN_CLASS(stun_msg_type(msg));\n}\n\n\n/**\n * Get the STUN message method\n *\n * @param msg STUN Message\n *\n * @return STUN Message method\n */\nuint16_t stun_msg_method(const struct stun_msg *msg)\n{\n\treturn STUN_METHOD(stun_msg_type(msg));\n}\n\n\n/**\n * Get the STUN message Transaction-ID\n *\n * @param msg STUN Message\n *\n * @return STUN Message Transaction-ID\n */\nconst uint8_t *stun_msg_tid(const struct stun_msg *msg)\n{\n\treturn msg ? msg->hdr.tid : NULL;\n}\n\n\n/**\n * Check if a STUN Message has the magic cookie\n *\n * @param msg STUN Message\n *\n * @return true if Magic Cookie, otherwise false\n */\nbool stun_msg_mcookie(const struct stun_msg *msg)\n{\n\treturn msg && (STUN_MAGIC_COOKIE == msg->hdr.cookie);\n}\n\n\n/**\n * Lookup a STUN attribute in a STUN message\n *\n * @param msg  STUN Message\n * @param type STUN Attribute type\n *\n * @return STUN Attribute if found, otherwise NULL\n */\nstruct stun_attr *stun_msg_attr(const struct stun_msg *msg, uint16_t type)\n{\n\tstruct le *le = msg ? list_head(&msg->attrl) : NULL;\n\n\twhile (le) {\n\t\tstruct stun_attr *attr = le->data;\n\n\t\tle = le->next;\n\n\t\tif (attr->type == type)\n\t\t\treturn attr;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Apply a function handler to all STUN attribute\n *\n * @param msg  STUN Message\n * @param h    Attribute handler\n * @param arg  Handler argument\n *\n * @return STUN attribute if handler returned true, otherwise NULL\n */\nstruct stun_attr *stun_msg_attr_apply(const struct stun_msg *msg,\n\t\t\t\t      stun_attr_h *h, void *arg)\n{\n\tstruct le *le = msg ? list_head(&msg->attrl) : NULL;\n\n\twhile (le) {\n\t\tstruct stun_attr *attr = le->data;\n\n\t\tle = le->next;\n\n\t\tif (h && h(attr, arg))\n\t\t\treturn (attr);\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Encode a STUN message\n *\n * @param mb      Buffer to encode message into\n * @param method  STUN Method\n * @param class   STUN Method class\n * @param tid     Transaction ID\n * @param ec      STUN error code (optional)\n * @param key     Authentication key (optional)\n * @param keylen  Number of bytes in authentication key\n * @param fp      Use STUN Fingerprint attribute\n * @param padding Padding byte\n * @param attrc   Number of attributes to encode (variable arguments)\n * @param ap      Variable list of attribute-tuples\n *                Each attribute has 2 arguments, attribute type and value\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_msg_vencode(struct mbuf *mb, uint16_t method, uint8_t class,\n\t\t     const uint8_t *tid, const struct stun_errcode *ec,\n\t\t     const uint8_t *key, size_t keylen, bool fp,\n\t\t     uint8_t padding, uint32_t attrc, va_list ap)\n{\n\tstruct stun_hdr hdr;\n\tsize_t start, len;\n\tint err = 0;\n\tuint32_t i;\n\n\tif (!mb || !tid)\n\t\treturn EINVAL;\n\n\tstart = mb->pos;\n\tmb->pos += STUN_HEADER_SIZE;\n\n\thdr.type   = STUN_TYPE(method, class);\n\thdr.cookie = STUN_MAGIC_COOKIE;\n\tmemcpy(hdr.tid, tid, STUN_TID_SIZE);\n\n\tif (ec)\n\t\terr |= stun_attr_encode(mb, STUN_ATTR_ERR_CODE, ec,\n\t\t\t\t\tNULL, padding);\n\n\tfor (i=0; i<attrc; i++) {\n\n\t\tuint16_t type = va_arg(ap, int);\n\t\tconst void *v = va_arg(ap, const void *);\n\n\t\tif (!v)\n\t\t\tcontinue;\n\n\t\terr |= stun_attr_encode(mb, type, v, hdr.tid, padding);\n\t}\n\n\t/* header */\n\tlen = mb->pos - start - STUN_HEADER_SIZE + (key ? MI_SIZE : 0);\n\thdr.len = (uint16_t)len;\n\tmb->pos = start;\n\terr |= stun_hdr_encode(mb, &hdr);\n\tmb->pos += hdr.len - (key ? MI_SIZE : 0);\n\n\tif (key) {\n\t\tuint8_t mi[20];\n\n\t\tmb->pos = start;\n\t\thmac_sha1(key, keylen, mbuf_buf(mb), mbuf_get_left(mb),\n\t\t\t  mi, sizeof(mi));\n\n\t\tmb->pos += STUN_HEADER_SIZE + hdr.len - MI_SIZE;\n\t\terr |= stun_attr_encode(mb, STUN_ATTR_MSG_INTEGRITY, mi,\n\t\t\t\t\tNULL, padding);\n\t}\n\n\tif (fp) {\n\t\tuint32_t fprnt;\n\n\t\t/* header */\n\t\tlen = mb->pos - start - STUN_HEADER_SIZE + FP_SIZE;\n\t\thdr.len = (uint16_t)len;\n\t\tmb->pos = start;\n\t\terr |= stun_hdr_encode(mb, &hdr);\n\n\t\tmb->pos = start;\n\t\tfprnt = fingerprint(mbuf_buf(mb), mbuf_get_left(mb));\n\n\t\tmb->pos += STUN_HEADER_SIZE + hdr.len - FP_SIZE;\n\t\terr |= stun_attr_encode(mb, STUN_ATTR_FINGERPRINT, &fprnt,\n\t\t\t\t\tNULL, padding);\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Encode a STUN message\n *\n * @param mb      Buffer to encode message into\n * @param method  STUN Method\n * @param class   STUN Method class\n * @param tid     Transaction ID\n * @param ec      STUN error code (optional)\n * @param key     Authentication key (optional)\n * @param keylen  Number of bytes in authentication key\n * @param fp      Use STUN Fingerprint attribute\n * @param padding Padding byte\n * @param attrc   Number of attributes to encode (variable arguments)\n * @param ...     Variable list of attribute-tuples\n *                Each attribute has 2 arguments, attribute type and value\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_msg_encode(struct mbuf *mb, uint16_t method, uint8_t class,\n\t\t    const uint8_t *tid, const struct stun_errcode *ec,\n\t\t    const uint8_t *key, size_t keylen, bool fp,\n\t\t    uint8_t padding, uint32_t attrc, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, attrc);\n\terr = stun_msg_vencode(mb, method, class, tid, ec, key, keylen, fp,\n\t\t\t       padding, attrc, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\n/**\n * Verify the Message-Integrity of a STUN message\n *\n * @param msg    STUN Message\n * @param key    Authentication key\n * @param keylen Number of bytes in authentication key\n *\n * @return 0 if verified, otherwise errorcode\n */\nint stun_msg_chk_mi(const struct stun_msg *msg, const uint8_t *key,\n\t\t    size_t keylen)\n{\n\tuint8_t hmac[SHA_DIGEST_LENGTH] = {0};\n\tstruct stun_attr *mi, *fp;\n\n\tif (!msg)\n\t\treturn EINVAL;\n\n\tmi = stun_msg_attr(msg, STUN_ATTR_MSG_INTEGRITY);\n\tif (!mi)\n\t\treturn EPROTO;\n\n\tmsg->mb->pos = msg->start;\n\n\tfp = stun_msg_attr(msg, STUN_ATTR_FINGERPRINT);\n\tif (fp) {\n\t\t((struct stun_msg *)msg)->hdr.len -= FP_SIZE;\n\t\t(void)stun_hdr_encode(msg->mb, &msg->hdr);\n\t\tmsg->mb->pos -= STUN_HEADER_SIZE;\n\t}\n\n\thmac_sha1(key, keylen, mbuf_buf(msg->mb),\n\t\t  STUN_HEADER_SIZE + msg->hdr.len - MI_SIZE,\n\t\t  hmac, sizeof(hmac));\n\n\tif (fp) {\n\t\t((struct stun_msg *)msg)->hdr.len += FP_SIZE;\n\t\t(void)stun_hdr_encode(msg->mb, &msg->hdr);\n\t\tmsg->mb->pos -= STUN_HEADER_SIZE;\n\t}\n\n\tif (memcmp(mi->v.msg_integrity, hmac, SHA_DIGEST_LENGTH))\n\t\treturn EBADMSG;\n\n\treturn 0;\n}\n\n\n/**\n * Check the Fingerprint of a STUN message\n *\n * @param msg STUN Message\n *\n * @return 0 if fingerprint matches, otherwise errorcode\n */\nint stun_msg_chk_fingerprint(const struct stun_msg *msg)\n{\n\tstruct stun_attr *fp;\n\tuint32_t fprnt;\n\n\tif (!msg)\n\t\treturn EINVAL;\n\n\tfp = stun_msg_attr(msg, STUN_ATTR_FINGERPRINT);\n\tif (!fp)\n\t\treturn EPROTO;\n\n\tmsg->mb->pos = msg->start;\n\n\tfprnt = fingerprint(mbuf_buf(msg->mb),\n\t\t\t    STUN_HEADER_SIZE + msg->hdr.len - FP_SIZE);\n\n\tif (fprnt != fp->v.fingerprint)\n\t\treturn EBADMSG;\n\n\treturn 0;\n}\n\n\nstatic bool attr_print(const struct stun_attr *attr, void *arg)\n{\n\t(void)arg;\n\n\tstun_attr_dump(attr);\n\n\treturn false;\n}\n\n\n/**\n * Print a STUN message to STDOUT\n *\n * @param msg STUN Message\n */\nvoid stun_msg_dump(const struct stun_msg *msg)\n{\n\tif (!msg)\n\t\treturn;\n\n\t(void)re_printf(\"%s %s (len=%u cookie=%08x tid=%w)\\n\",\n\t\t\tstun_method_name(stun_msg_method(msg)),\n\t\t\tstun_class_name(stun_msg_class(msg)),\n\t\t\tmsg->hdr.len, msg->hdr.cookie,\n\t\t\tmsg->hdr.tid, sizeof(msg->hdr.tid));\n\n\tstun_msg_attr_apply(msg, attr_print, NULL);\n}\n"
  },
  {
    "path": "src/stun/rep.c",
    "content": "/**\n * @file stun/rep.c  STUN reply\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_stun.h>\n#include \"stun.h\"\n\n\n/**\n * Send a STUN response message\n *\n * @param proto   Transport Protocol\n * @param sock    Socket; UDP (struct udp_sock) or TCP (struct tcp_conn)\n * @param dst     Destination network address\n * @param presz   Number of bytes in preamble, if sending over TURN\n * @param req     Matching STUN request\n * @param key     Authentication key (optional)\n * @param keylen  Number of bytes in authentication key\n * @param fp      Use STUN Fingerprint attribute\n * @param attrc   Number of attributes to encode (variable arguments)\n * @param ...     Variable list of attribute-tuples\n *                Each attribute has 2 arguments, attribute type and value\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_reply(int proto, void *sock, const struct sa *dst, size_t presz,\n\t       const struct stun_msg *req, const uint8_t *key,\n\t       size_t keylen, bool fp, uint32_t attrc, ...)\n{\n\tstruct mbuf *mb = NULL;\n\tint err = ENOMEM;\n\tva_list ap;\n\n\tif (!sock || !req)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(256);\n\tif (!mb)\n\t\tgoto out;\n\n\tva_start(ap, attrc);\n\tmb->pos = presz;\n\terr = stun_msg_vencode(mb, stun_msg_method(req),\n\t\t\t       STUN_CLASS_SUCCESS_RESP, stun_msg_tid(req),\n\t\t\t       NULL, key, keylen, fp, 0x00, attrc, ap);\n\tva_end(ap);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = presz;\n\terr = stun_send(proto, sock, dst, mb);\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/**\n * Send a STUN error response\n *\n * @param proto   Transport Protocol\n * @param sock    Socket; UDP (struct udp_sock) or TCP (struct tcp_conn)\n * @param dst     Destination network address\n * @param presz   Number of bytes in preamble, if sending over TURN\n * @param req     Matching STUN request\n * @param scode   Status code\n * @param reason  Reason string\n * @param key     Authentication key (optional)\n * @param keylen  Number of bytes in authentication key\n * @param fp      Use STUN Fingerprint attribute\n * @param attrc   Number of attributes to encode (variable arguments)\n * @param ...     Variable list of attribute-tuples\n *                Each attribute has 2 arguments, attribute type and value\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_ereply(int proto, void *sock, const struct sa *dst, size_t presz,\n\t\tconst struct stun_msg *req, uint16_t scode,\n\t\tconst char *reason, const uint8_t *key, size_t keylen,\n\t\tbool fp, uint32_t attrc, ...)\n{\n\tstruct stun_errcode ec;\n\tstruct mbuf *mb = NULL;\n\tint err = ENOMEM;\n\tva_list ap;\n\n\tif (!sock || !req || !scode || !reason)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(256);\n\tif (!mb)\n\t\tgoto out;\n\n\tec.code = scode;\n\tec.reason = (char *)reason;\n\n\tva_start(ap, attrc);\n\tmb->pos = presz;\n\terr = stun_msg_vencode(mb, stun_msg_method(req), STUN_CLASS_ERROR_RESP,\n\t\t\t       stun_msg_tid(req), &ec, key, keylen,\n\t\t\t       fp, 0x00, attrc, ap);\n\tva_end(ap);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = presz;\n\terr = stun_send(proto, sock, dst, mb);\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/stun/req.c",
    "content": "/**\n * @file stun/req.c  STUN request\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_sys.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_stun.h>\n#include \"stun.h\"\n\n\n/**\n * Send a STUN request using a client transaction\n *\n * @param ctp     Pointer to allocated client transaction (optional)\n * @param stun    STUN Instance\n * @param proto   Transport Protocol\n * @param sock    Socket; UDP (struct udp_sock) or TCP (struct tcp_conn)\n * @param dst     Destination network address\n * @param presz   Number of bytes in preamble, if sending over TURN\n * @param method  STUN Method\n * @param key     Authentication key (optional)\n * @param keylen  Number of bytes in authentication key\n * @param fp      Use STUN Fingerprint attribute\n * @param resph   Response handler\n * @param arg     Response handler argument\n * @param attrc   Number of attributes to encode (variable arguments)\n * @param ...     Variable list of attribute-tuples\n *                Each attribute has 2 arguments, attribute type and value\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_request(struct stun_ctrans **ctp, struct stun *stun, int proto,\n\t\t void *sock, const struct sa *dst, size_t presz,\n\t\t uint16_t method, const uint8_t *key, size_t keylen, bool fp,\n\t\t stun_resp_h *resph, void *arg, uint32_t attrc, ...)\n{\n\tuint8_t tid[STUN_TID_SIZE];\n\tstruct mbuf *mb;\n\tva_list ap;\n\tint err;\n\n\tif (!stun)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tstun_generate_tid(tid);\n\n\tva_start(ap, attrc);\n\tmb->pos = presz;\n\terr = stun_msg_vencode(mb, method, STUN_CLASS_REQUEST,\n\t\t\t       tid, NULL, key, keylen, fp, 0x00, attrc, ap);\n\tva_end(ap);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = presz;\n\terr = stun_ctrans_request(ctp, stun, proto, sock, dst, mb, tid, method,\n\t\t\t\t  key, keylen, resph, arg);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/stun/stun.c",
    "content": "/**\n * @file stun.c  STUN stack\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_udp.h>\n#include <re_tcp.h>\n#include <re_srtp.h>\n#include <re_tls.h>\n#include <re_sys.h>\n#include <re_list.h>\n#include <re_stun.h>\n#include \"stun.h\"\n\n\nconst char *stun_software = \"libre v\" RE_VERSION \" (\" ARCH \"/\" OS \")\";\n\n\nstatic const struct stun_conf conf_default = {\n\tSTUN_DEFAULT_RTO,\n\tSTUN_DEFAULT_RC,\n\tSTUN_DEFAULT_RM,\n\tSTUN_DEFAULT_TI,\n\t0x00\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct stun *stun = arg;\n\n\tstun_ctrans_close(stun);\n}\n\n\n/**\n * Allocate a new STUN instance\n *\n * @param stunp Pointer to allocated STUN instance\n * @param conf  STUN configuration (optional)\n * @param indh  STUN Indication handler (optional)\n * @param arg   STUN Indication handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_alloc(struct stun **stunp, const struct stun_conf *conf,\n\t       stun_ind_h *indh, void *arg)\n{\n\tstruct stun *stun;\n\n\tif (!stunp)\n\t\treturn EINVAL;\n\n\tstun = mem_zalloc(sizeof(*stun), destructor);\n\tif (!stun)\n\t\treturn ENOMEM;\n\n\tstun->conf = conf ? *conf : conf_default;\n\tstun->indh = indh;\n\tstun->arg  = arg;\n\n\t*stunp = stun;\n\n\treturn 0;\n}\n\n\n/**\n * Get STUN configuration object\n *\n * @param stun STUN Instance\n *\n * @return STUN configuration\n */\nstruct stun_conf *stun_conf(struct stun *stun)\n{\n\treturn stun ? &stun->conf : NULL;\n}\n\n\n/**\n * Send a STUN message\n *\n * @param proto Transport protocol (IPPROTO_UDP or IPPROTO_TCP)\n * @param sock  Socket, UDP (struct udp_sock) or TCP (struct tcp_conn)\n * @param dst   Destination network address (UDP only)\n * @param mb    Buffer containing the STUN message\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_send(int proto, void *sock, const struct sa *dst, struct mbuf *mb)\n{\n\tint err;\n\n\tif (!sock || !mb)\n\t\treturn EINVAL;\n\n\tswitch (proto) {\n\n\tcase IPPROTO_UDP:\n\t\terr = udp_send(sock, dst, mb);\n\t\tbreak;\n\n\tcase IPPROTO_TCP:\n\t\terr = tcp_send(sock, mb);\n\t\tbreak;\n\n#ifdef USE_DTLS\n\tcase STUN_TRANSP_DTLS:\n\t\terr = dtls_send(sock, mb);\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\terr = EPROTONOSUPPORT;\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Receive a STUN message\n *\n * @param stun STUN Instance\n * @param mb   Buffer containing STUN message\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_recv(struct stun *stun, struct mbuf *mb)\n{\n\tstruct stun_unknown_attr ua;\n\tstruct stun_msg *msg;\n\tint err;\n\n\tif (!stun || !mb)\n\t\treturn EINVAL;\n\n\terr = stun_msg_decode(&msg, mb, &ua);\n\tif (err)\n\t\treturn err;\n\n\tswitch (stun_msg_class(msg)) {\n\n\tcase STUN_CLASS_INDICATION:\n\t\tif (ua.typec > 0)\n\t\t\tbreak;\n\n\t\tif (stun->indh)\n\t\t\tstun->indh(msg, stun->arg);\n\t\tbreak;\n\n\tcase STUN_CLASS_ERROR_RESP:\n\tcase STUN_CLASS_SUCCESS_RESP:\n\t\terr = stun_ctrans_recv(stun, msg, &ua);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\tmem_deref(msg);\n\n\treturn err;\n}\n\n\n/**\n * Print STUN instance debug information\n *\n * @param pf   Print function\n * @param stun STUN Instance\n *\n * @return 0 if success, otherwise errorcode\n */\nint stun_debug(struct re_printf *pf, const struct stun *stun)\n{\n\tif (!stun)\n\t\treturn 0;\n\n\treturn re_hprintf(pf, \"STUN debug:\\n%H\", stun_ctrans_debug, stun);\n}\n"
  },
  {
    "path": "src/stun/stun.h",
    "content": "/**\n * @file stun.h  Internal STUN interface\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n\n/** STUN Protocol values */\nenum {\n\tSTUN_MAGIC_COOKIE = 0x2112a442 /**< Magic Cookie for 3489bis        */\n};\n\n\n/** Calculate STUN message type from method and class */\n#define STUN_TYPE(method, class)\t    \\\n\t((method)&0x0f80) << 2 |\t    \\\n\t((method)&0x0070) << 1 |\t    \\\n\t((method)&0x000f) << 0 |\t    \\\n\t((class)&0x2)     << 7 |\t    \\\n\t((class)&0x1)     << 4\n\n\n#define STUN_CLASS(type) \\\n\t((type >> 7 | type >> 4) & 0x3)\n\n\n#define STUN_METHOD(type) \\\n\t((type&0x3e00)>>2 | (type&0x00e0)>>1 | (type&0x000f))\n\n\nstruct stun_hdr {\n\tuint16_t type;               /**< Message type   */\n\tuint16_t len;                /**< Payload length */\n\tuint32_t cookie;             /**< Magic cookie   */\n\tuint8_t tid[STUN_TID_SIZE];  /**< Transaction ID */\n};\n\n\nstruct stun {\n\tstruct list ctl;\n\tstruct stun_conf conf;\n\tstun_ind_h *indh;\n\tvoid *arg;\n};\n\nint stun_hdr_encode(struct mbuf *mb, const struct stun_hdr *hdr);\nint stun_hdr_decode(struct mbuf *mb, struct stun_hdr *hdr);\n\nint stun_attr_encode(struct mbuf *mb, uint16_t type, const void *v,\n\t\t     const uint8_t *tid, uint8_t padding);\nint stun_attr_decode(struct stun_attr **attrp, struct mbuf *mb,\n\t\t     const uint8_t *tid, struct stun_unknown_attr *ua);\nvoid stun_attr_dump(const struct stun_attr *a);\n\nint  stun_addr_encode(struct mbuf *mb, const struct sa *addr,\n\t\t      const uint8_t *tid);\nint  stun_addr_decode(struct mbuf *mb, struct sa *addr, const uint8_t *tid);\n\nint stun_ctrans_request(struct stun_ctrans **ctp, struct stun *stun, int proto,\n\t\t\tvoid *sock, const struct sa *dst, struct mbuf *mb,\n\t\t\tconst uint8_t tid[], uint16_t met, const uint8_t *key,\n\t\t\tsize_t keylen, stun_resp_h *resph, void *arg);\nvoid stun_ctrans_close(struct stun *stun);\nint  stun_ctrans_debug(struct re_printf *pf, const struct stun *stun);\n"
  },
  {
    "path": "src/stun/stunstr.c",
    "content": "/**\n * @file stunstr.c  STUN Strings\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_stun.h>\n\n\n/* STUN Reason Phrase */\nconst char *stun_reason_300 = \"Try Alternate\";\nconst char *stun_reason_400 = \"Bad Request\";\nconst char *stun_reason_401 = \"Unauthorized\";\nconst char *stun_reason_403 = \"Forbidden\";\nconst char *stun_reason_420 = \"Unknown Attribute\";\nconst char *stun_reason_437 = \"Allocation Mismatch\";\nconst char *stun_reason_438 = \"Stale Nonce\";\nconst char *stun_reason_440 = \"Address Family not Supported\";\nconst char *stun_reason_441 = \"Wrong Credentials\";\nconst char *stun_reason_442 = \"Unsupported Transport Protocol\";\nconst char *stun_reason_443 = \"Peer Address Family Mismatch\";\nconst char *stun_reason_486 = \"Allocation Quota Reached\";\nconst char *stun_reason_500 = \"Server Error\";\nconst char *stun_reason_508 = \"Insufficient Capacity\";\n\n\n/**\n * Get the name of a given STUN Transport\n *\n * @param tp STUN Transport\n *\n * @return Name of the corresponding STUN Transport\n */\nconst char *stun_transp_name(enum stun_transp tp)\n{\n\tswitch (tp) {\n\n\tcase STUN_TRANSP_UDP:  return \"UDP\";\n\tcase STUN_TRANSP_TCP:  return \"TCP\";\n\tcase STUN_TRANSP_DTLS: return \"DTLS\";\n\tdefault:               return \"???\";\n\t}\n}\n"
  },
  {
    "path": "src/sys/daemon.c",
    "content": "/**\n * @file daemon.c  Daemonize process\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <sys/types.h>\n#include <sys/stat.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include <stdlib.h>\n#include <signal.h>\n#include <stdio.h>\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_sys.h>\n\n\n/**\n * Daemonize process\n *\n * @return 0 if success, otherwise errorcode\n */\nint sys_daemon(void)\n{\n#ifdef HAVE_FORK\n\tpid_t pid;\n\n\tpid = fork();\n\tif (-1 == pid)\n\t\treturn errno;\n\telse if (pid > 0)\n\t\texit(0);\n\n\tif (-1 == setsid())\n\t\treturn errno;\n\n\t(void)signal(SIGHUP, SIG_IGN);\n\n\tpid = fork();\n\tif (-1 == pid)\n\t\treturn errno;\n\telse if (pid > 0)\n\t\texit(0);\n\n\tif (-1 == chdir(\"/\"))\n\t\treturn errno;\n\t(void)umask(0);\n\n\t/* Redirect standard files to /dev/null */\n\tif (freopen(\"/dev/null\", \"r\", stdin) == NULL)\n\t\treturn errno;\n\tif (freopen(\"/dev/null\", \"w\", stdout) == NULL)\n\t\treturn errno;\n\tif (freopen(\"/dev/null\", \"w\", stderr) == NULL)\n\t\treturn errno;\n\n\treturn 0;\n#else\n\treturn ENOSYS;\n#endif\n}\n"
  },
  {
    "path": "src/sys/endian.c",
    "content": "/**\n * @file endian.c  Endianness converting routines\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_sys.h>\n\n\n/*\n * These routes are working on both little-endian and big-endian platforms.\n */\n\n\n/**\n * Convert a 16-bit value from host order to little endian\n *\n * @param v 16-bit in host order\n *\n * @return 16-bit little endian value\n */\nuint16_t sys_htols(uint16_t v)\n{\n\tuint8_t *p = (uint8_t *)&v;\n\tuint16_t l = 0;\n\n\tl |= (uint16_t)*p++ << 0;\n\tl |= (uint16_t)*p   << 8;\n\n\treturn l;\n}\n\n\n/**\n * Convert a 32-bit value from host order to little endian\n *\n * @param v 32-bit in host order\n *\n * @return 32-bit little endian value\n */\nuint32_t sys_htoll(uint32_t v)\n{\n\tuint8_t *p = (uint8_t *)&v;\n\tuint32_t l = 0;\n\n\tl |= (uint32_t)*p++ << 0;\n\tl |= (uint32_t)*p++ << 8;\n\tl |= (uint32_t)*p++ << 16;\n\tl |= (uint32_t)*p   << 24;\n\n\treturn l;\n}\n\n\n/**\n * Convert a 16-bit value from little endian to host order\n *\n * @param v 16-bit little endian value\n *\n * @return 16-bit value in host order\n */\nuint16_t sys_ltohs(uint16_t v)\n{\n\tuint16_t s;\n\tuint8_t *p = (uint8_t *)&s;\n\n\t*p++ = v>>0 & 0xff;\n\t*p   = v>>8 & 0xff;\n\n\treturn s;\n}\n\n\n/**\n * Convert a 32-bit value from little endian to host order\n *\n * @param v 32-bit little endian value\n *\n * @return 32-bit value in host order\n */\nuint32_t sys_ltohl(uint32_t v)\n{\n\tuint32_t h;\n\tuint8_t *p = (uint8_t *)&h;\n\n\t*p++ = v>>0  & 0xff;\n\t*p++ = v>>8  & 0xff;\n\t*p++ = v>>16 & 0xff;\n\t*p   = v>>24 & 0xff;\n\n\treturn h;\n}\n\n\n/**\n * Convert a 64-bit value from host to network byte-order\n *\n * @param v 64-bit host byte-order value\n *\n * @return 64-bit value in network byte-order\n */\nuint64_t sys_htonll(uint64_t v)\n{\n\tuint64_t h = 0;\n\tuint8_t *p = (uint8_t *)&v;\n\n\th |= (uint64_t)*p++ << 56;\n\th |= (uint64_t)*p++ << 48;\n\th |= (uint64_t)*p++ << 40;\n\th |= (uint64_t)*p++ << 32;\n\th |= (uint64_t)*p++ << 24;\n\th |= (uint64_t)*p++ << 16;\n\th |= (uint64_t)*p++ << 8;\n\th |= (uint64_t)*p   << 0;\n\n\treturn h;\n}\n\n\n/**\n * Convert a 64-bit value from network to host byte-order\n *\n * @param v 64-bit network byte-order value\n *\n * @return 64-bit value in host byte-order\n */\nuint64_t sys_ntohll(uint64_t v)\n{\n\tuint64_t h;\n\tuint8_t *p = (uint8_t *)&h;\n\n\t*p++ = (uint8_t) (v>>56 & 0xff);\n\t*p++ = (uint8_t) (v>>48 & 0xff);\n\t*p++ = (uint8_t) (v>>40 & 0xff);\n\t*p++ = (uint8_t) (v>>32 & 0xff);\n\t*p++ = (uint8_t) (v>>24 & 0xff);\n\t*p++ = (uint8_t) (v>>16 & 0xff);\n\t*p++ = (uint8_t) (v>>8  & 0xff);\n\t*p   = (uint8_t) (v>>0  & 0xff);\n\n\treturn h;\n}\n"
  },
  {
    "path": "src/sys/fs.c",
    "content": "/**\n * @file fs.c  File-system functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <fcntl.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef HAVE_PWD_H\n#include <pwd.h>\n#endif\n#ifdef WIN32\n#include <windows.h>\n#include <shlobj.h>\n#include <direct.h>\n#include <lmaccess.h>\n#endif\n#ifdef HAVE_IO_H\n#include <io.h>\n#endif\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_sys.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n\n\n#define DEBUG_MODULE \"fs\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#ifdef WIN32\n#define open _open\n#define read _read\n#define close _close\n#define dup _dup\n#define dup2 _dup2\n#define fileno _fileno\n#endif\n\n#define MINBUF_SIZE 1024\n\n\nstatic int dup_stdout = -1;\nstatic int dup_stderr = -1;\n\n/**\n * Create a directory with full path\n *\n * @param path Directory path\n * @param mode Access permissions\n *\n * @return 0 if success, otherwise errorcode\n */\nint fs_mkdir(const char *path, uint16_t mode)\n{\n\tint ret;\n\n\tif (!path)\n\t\treturn EINVAL;\n\n#if defined (WIN32)\n\t(void)mode;\n\tret = _mkdir(path);\n#else\n\tret = mkdir(path, mode);\n#endif\n\tif (ret < 0)\n\t\treturn errno;\n\n\treturn 0;\n}\n\n\n/**\n * Get the home directory for the current user\n *\n * @param path String to write home directory\n * @param sz   Size of path string\n *\n * @return 0 if success, otherwise errorcode\n */\nint fs_gethome(char *path, size_t sz)\n{\n#ifdef WIN32\n\tchar win32_path[MAX_PATH];\n\n\tif (!path || !sz)\n\t\treturn EINVAL;\n\n\tif (S_OK != SHGetFolderPath(NULL,\n\t\t\t\t    CSIDL_APPDATA | CSIDL_FLAG_CREATE,\n\t\t\t\t    NULL,\n\t\t\t\t    0,\n\t\t\t\t    win32_path)) {\n\t\treturn ENOENT;\n\t}\n\n\tstr_ncpy(path, win32_path, sz);\n\n\treturn 0;\n\n#elif defined(HAVE_PWD_H)\n\tconst char *loginname;\n\tstruct passwd *pw;\n\n\tif (!path || !sz)\n\t\treturn EINVAL;\n\n\tloginname = sys_username();\n\tif (!loginname)\n#ifdef HAVE_UNISTD_H\n\t\tpw = getpwuid(getuid());\n#else\n\t\treturn ENOENT;\n#endif\n\telse\n\t\tpw = getpwnam(loginname);\n\n\tif (!pw)\n\t\treturn errno;\n\n\tstr_ncpy(path, pw->pw_dir, sz);\n\n\treturn 0;\n#else\n\t(void)path;\n\t(void)sz;\n\treturn ENOSYS;\n#endif\n}\n\n\n/**\n * Check if given path is directory\n *\n * @param path Directory\n *\n * @return True if directory, False if not\n */\nbool fs_isdir(const char *path)\n{\n\tstruct stat st;\n\n\tif (!path)\n\t\treturn false;\n\n\tif (stat(path, &st) < 0)\n\t\treturn false;\n\n\tif ((st.st_mode & S_IFMT) != S_IFDIR)\n\t\treturn false;\n\n\treturn true;\n}\n\n\n/**\n * Check if given file exists and is a regular file\n *\n * @param file Filepath\n *\n * @return True if exists and is regular file, False if not\n */\nbool fs_isfile(const char *file)\n{\n\tstruct stat st;\n\n\tif (!file)\n\t\treturn false;\n\n\tif (stat(file, &st) < 0)\n\t\treturn false;\n\n\tif ((st.st_mode & S_IFMT) != S_IFREG)\n\t\treturn false;\n\n\treturn true;\n}\n\n\n/**\n * Open file with security enhancements (like fopen_s).\n * The file is created with mode 0600 if it does not exist\n *\n * @param fp   FILE pointer for allocation\n * @param file Pathname\n * @param mode fopen mode\n *\n * @return 0 if success, otherwise errorcode\n *\n */\nint fs_fopen(FILE **fp, const char *file, const char *mode)\n{\n#ifdef WIN32\n\treturn fopen_s(fp, file, mode);\n#else\n\tFILE *pfile;\n\tint fd;\n\n\tif (!fp || !file || !str_isset(mode))\n\t\treturn EINVAL;\n\n\tif (mode[0] == 'r' || fs_isfile(file))\n\t\tgoto fopen;\n\n\tfd = open(file, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR);\n\tif (fd == -1)\n\t\treturn errno;\n\n\t(void)close(fd);\n\nfopen:\n\tpfile = fopen(file, mode);\n\tif (!pfile)\n\t\treturn errno;\n\n\t*fp = pfile;\n\n\treturn 0;\n#endif\n}\n\n\n/**\n * Hide/Close stdout and stderr output (no THREAD-SAFETY)\n */\nvoid fs_stdio_hide(void)\n{\n\tdup_stdout = dup(fileno(stdout));\n\tdup_stderr = dup(fileno(stderr));\n#ifdef WIN32\n\tint fd = open(\"nul\", O_WRONLY);\n#else\n\tint fd = open(\"/dev/null\", O_WRONLY);\n#endif\n\tif (fd < 0)\n\t\treturn;\n\n\t(void)dup2(fd, fileno(stdout));\n\t(void)dup2(fd, fileno(stderr));\n\n\tclose(fd);\n}\n\n\n/**\n * Restore stdout and stderr output (no THREAD-SAFETY)\n */\nvoid fs_stdio_restore(void)\n{\n\tif (dup_stdout < 0 || dup_stderr < 0)\n\t\treturn;\n\n\t(void)dup2(dup_stdout, fileno(stdout));\n\t(void)dup2(dup_stderr, fileno(stderr));\n}\n\n\nint fs_fread(struct mbuf **mbp, const char *path)\n{\n\tFILE *f = NULL;\n\tsize_t n = 0;\n\tvoid *buf = NULL;\n\tstruct mbuf *mb = NULL;\n\tint err;\n\n\tif (!mbp || !path)\n\t\treturn EINVAL;\n\n\terr = fs_fopen(&f, path, \"r\");\n\tif (err || !f) {\n\t\tDEBUG_WARNING(\"Could not open file '%s'\\n\", path);\n\t\treturn err;\n\t}\n\n\tmb = mbuf_alloc(MINBUF_SIZE);\n\tbuf = mem_zalloc(MINBUF_SIZE, NULL);\n\tif (!mb || !buf) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\twhile (1) {\n\t\tn = fread(buf, 1, MINBUF_SIZE, f);\n\t\tif (!n)\n\t\t\tgoto out;\n\n\t\terr = mbuf_write_mem(mb, buf, n);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\t/* EOF */\n\t\tif (n < MINBUF_SIZE)\n\t\t\tgoto out;\n\t}\n\nout:\n\tif (!err && ferror(f))\n\t\terr = EIO;\n\n\tfclose(f);\n\n\tmem_deref(buf);\n\tif (err) {\n\t\tDEBUG_WARNING(\"Error reading file '%s' (%m)\\n\", path, err);\n\t\tmem_deref(mb);\n\t}\n\telse\n\t\t*mbp = mb;\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/sys/rand.c",
    "content": "/**\n * @file rand.c  Random generator\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdlib.h>\n#ifdef USE_OPENSSL\n#include <openssl/rand.h>\n#include <openssl/err.h>\n#endif\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sys.h>\n\n\n#define DEBUG_MODULE \"rand\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic const char alphanum[] =\n\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\t\"abcdefghijklmnopqrstuvwxyz\"\n\t\"0123456789\";\n\n\n#if !defined(USE_OPENSSL) && !defined(HAVE_ARC4RANDOM)\n\nstatic bool inited = false;\n\n\n/**\n * Initialise random number generator\n */\nstatic void rand_init(void)\n{\n\tsrand((uint32_t) tmr_jiffies());\n\n\tinited = true;\n}\n#endif\n\n\n/**\n * Generate an unsigned 16-bit random value\n *\n * @return 16-bit random value\n */\nuint16_t rand_u16(void)\n{\n\n\t/* Use higher-order bits (see man 3 rand) */\n\treturn rand_u32() >> 16;\n}\n\n\n/**\n * Generate an unsigned 32-bit random value\n *\n * @return 32-bit random value\n */\nuint32_t rand_u32(void)\n{\n\tuint32_t v;\n\n#ifdef USE_OPENSSL\n\tv = 0;\n\tif (RAND_bytes((unsigned char *)&v, sizeof(v)) <= 0) {\n\t\tDEBUG_WARNING(\"RAND_bytes() error: %i\\n\",\n\t\t\t      ERR_GET_REASON(ERR_get_error()));\n\t\tERR_clear_error();\n\t}\n#elif defined(HAVE_ARC4RANDOM)\n\tv = arc4random();\n#elif defined(WIN32)\n\n\tif (!inited)\n\t\trand_init();\n\n\tv = (rand() << 16) + rand(); /* note: 16-bit rand */\n#else\n\tif (!inited)\n\t\trand_init();\n\n\tv = rand();\n#endif\n\n\treturn v;\n}\n\n\n/**\n * Generate an unsigned 64-bit random value\n *\n * @return 64-bit random value\n */\nuint64_t rand_u64(void)\n{\n\n\treturn (uint64_t)rand_u32()<<32 | rand_u32();\n}\n\n\n/**\n * Generate a random printable character\n *\n * @return Random printable character\n */\nchar rand_char(void)\n{\n\tchar s[2];\n\n\trand_str(s, sizeof(s));\n\n\treturn s[0];\n}\n\n\n/**\n * Generate a string of random characters\n *\n * @param str  Pointer to string\n * @param size Size of string\n */\nvoid rand_str(char *str, size_t size)\n{\n\tsize_t i;\n\n\tif (!str || !size)\n\t\treturn;\n\n\t--size;\n\n\trand_bytes((uint8_t *)str, size);\n\n\tfor (i=0; i<size; i++)\n\t\tstr[i] = alphanum[((uint8_t)str[i]) % (sizeof(alphanum)-1)];\n\n\tstr[size] = '\\0';\n}\n\n\n/**\n * Generate a set of random bytes\n *\n * @param p    Pointer to buffer\n * @param size Size of buffer\n */\nvoid rand_bytes(uint8_t *p, size_t size)\n{\n#ifdef USE_OPENSSL\n\tif (RAND_bytes(p, (int)size) <= 0) {\n\t\tDEBUG_WARNING(\"RAND_bytes() error: %i\\n\",\n\t\t\t      ERR_GET_REASON(ERR_get_error()));\n\t\tERR_clear_error();\n\t}\n#elif defined (HAVE_ARC4RANDOM)\n\tarc4random_buf(p, size);\n#else\n\twhile (size--) {\n\t\tp[size] = rand_u32();\n\t}\n#endif\n}\n"
  },
  {
    "path": "src/sys/sleep.c",
    "content": "/**\n * @file sleep.c  System sleep functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_sys.h>\n#ifdef WIN32\n#include <windows.h>\n#endif\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef HAVE_SELECT_H\n#include <sys/select.h>\n#endif\n\n\n/**\n * Blocking sleep for [us] number of microseconds\n *\n * @param us Number of microseconds to sleep\n */\nvoid sys_usleep(unsigned int us)\n{\n\tif (!us)\n\t\treturn;\n\n#ifdef WIN32\n\tSleep(us / 1000);\n#elif defined(HAVE_SELECT)\n\tdo {\n\t\tstruct timeval tv;\n\n\t\ttv.tv_sec  = us / 1000000;\n\t\ttv.tv_usec = us % 1000000;\n\n\t\t(void)select(0, NULL, NULL, NULL, &tv);\n\t} while (0);\n#else\n\t(void)usleep(us);\n#endif\n}\n"
  },
  {
    "path": "src/sys/sys.c",
    "content": "/**\n * @file sys.c  System information\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdlib.h>\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_sys.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef HAVE_UNAME\n#include <sys/utsname.h>\n#endif\n#ifdef HAVE_SYS_TIME_H\n#include <sys/time.h>\n#endif\n#ifdef HAVE_SETRLIMIT\n#include <sys/resource.h>\n#endif\n\n#ifdef WIN32\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\n#endif\n\n#ifdef WIN32\nenum {\n\tMAX_ENVSZ = 32767\n};\n#endif\n\n\n/**\n * Get kernel name and version\n *\n * @param pf     Print function for output\n * @param unused Unused parameter\n *\n * @return 0 if success, otherwise errorcode\n */\nint sys_kernel_get(struct re_printf *pf, void *unused)\n{\n#ifdef HAVE_UNAME\n\tstruct utsname u;\n\n\t(void)unused;\n\n\tif (0 != uname(&u))\n\t\treturn errno;\n\n\treturn re_hprintf(pf, \"%s %s %s %s %s\", u.sysname, u.nodename,\n\t\t\t  u.release, u.version, u.machine);\n#else\n\tconst char *str;\n\n\t(void)unused;\n\n#if defined(WIN32)\n\tstr = \"Win32\";\n#else\n\tstr = \"?\";\n#endif\n\n\treturn re_hprintf(pf, \"%s\", str);\n#endif\n}\n\n\n/**\n * Get build info\n *\n * @param pf     Print function for output\n * @param unused Unused parameter\n *\n * @return 0 if success, otherwise errorcode\n */\nint sys_build_get(struct re_printf *pf, void *unused)\n{\n\tconst unsigned int bus_width = 8*sizeof(void *);\n\tconst char *endian = \"unknown\";\n\n\tconst uint32_t a = 0x12345678;\n\tconst uint8_t b0 = ((uint8_t *)&a)[0];\n\tconst uint8_t b1 = ((uint8_t *)&a)[1];\n\tconst uint8_t b2 = ((uint8_t *)&a)[2];\n\tconst uint8_t b3 = ((uint8_t *)&a)[3];\n\n\t(void)unused;\n\n\tif (0x12==b0 && 0x34==b1 && 0x56==b2 && 0x78==b3)\n\t\tendian = \"big\";\n\telse if (0x12==b3 && 0x34==b2 && 0x56==b1 && 0x78==b0)\n\t\tendian = \"little\";\n\n\treturn re_hprintf(pf, \"%u-bit %s endian\", bus_width, endian);\n}\n\n\n/**\n * Get architecture\n *\n * @return Architecture string\n */\nconst char *sys_arch_get(void)\n{\n#ifdef ARCH\n\treturn ARCH;\n#else\n\treturn \"?\";\n#endif\n}\n\n\n/**\n * Get name of Operating System\n *\n * @return Operating System string\n */\nconst char *sys_os_get(void)\n{\n#ifdef OS\n\treturn OS;\n#else\n\treturn \"?\";\n#endif\n}\n\n\n/**\n * Get libre version\n *\n * @return libre version string\n */\nconst char *sys_libre_version_get(void)\n{\n#ifdef RE_VERSION\n\treturn RE_VERSION;\n#else\n\treturn \"?\";\n#endif\n}\n\n\n/**\n * Return the username (login name) for the current user\n *\n * @return Username or NULL if not available\n */\nconst char *sys_username(void)\n{\n#ifdef HAVE_PWD_H\n\tchar *login;\n\n\tlogin = getenv(\"LOGNAME\");\n\tif (!login)\n\t\tlogin = getenv(\"USER\");\n#ifdef HAVE_UNISTD_H\n\tif (!login) {\n\t\tlogin = getlogin();\n\t}\n#endif\n\n\treturn str_isset(login) ? login : NULL;\n#else\n\treturn NULL;\n#endif\n}\n\n\n/**\n * Enable or disable coredump\n *\n * @param enable true to enable, false to disable coredump\n *\n * @return 0 if success, otherwise errorcode\n */\nint sys_coredump_set(bool enable)\n{\n#ifdef HAVE_SETRLIMIT\n\tconst struct rlimit rlim = {\n\t\tenable ? RLIM_INFINITY : 0,\n\t\tenable ? RLIM_INFINITY : 0\n\t};\n\n\treturn 0 == setrlimit(RLIMIT_CORE, &rlim) ? 0 : errno;\n#else\n\t(void)enable;\n\treturn ENOSYS;\n#endif\n}\n\n\n/**\n * Get an environment variable\n *\n * @param env   Pointer to destination env var\n * @param name  Environment variable name\n *\n * @return 0 if success, otherwise errorcode\n */\nint sys_getenv(char **env, const char *name)\n{\n\tif (!env || !name)\n\t\treturn EINVAL;\n\n#ifdef WIN32\n\tuint32_t rc    = 1;\n\tuint32_t bufsz = rc;\n\tchar *buf;\n\n\tbuf = mem_zalloc(bufsz, NULL);\n\tif (!buf)\n\t\treturn ENOMEM;\n\n\twhile (1) {\n\t\trc = GetEnvironmentVariableA(name, buf, bufsz);\n\t\tif (!rc || rc == bufsz || rc > MAX_ENVSZ) {\n\t\t\tmem_deref(buf);\n\t\t\treturn ENODATA;\n\t\t}\n\n\t\t/* success */\n\t\tif (rc < bufsz) {\n\t\t\t*env = buf;\n\t\t\treturn 0;\n\t\t}\n\n\t\t/* failed, getenv needs more space */\n\t\tbufsz = rc;\n\t\tbuf   = mem_realloc(buf, bufsz);\n\t\tif (!buf) {\n\t\t\tmem_deref(buf);\n\t\t\treturn ENOMEM;\n\t\t}\n\t}\n#else\n\tchar *tmp = getenv(name);\n\tif (!tmp)\n\t\treturn ENODATA;\n\n\treturn str_dup(env, tmp);\n#endif\n}\n"
  },
  {
    "path": "src/tcp/tcp.c",
    "content": "/**\n * @file tcp.c  Transport Control Protocol\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdlib.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef HAVE_IO_H\n#include <io.h>\n#endif\n#if !defined(WIN32)\n#include <netdb.h>\n#endif\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_net.h>\n#include <re_main.h>\n#include <re_sa.h>\n#include <re_tcp.h>\n\n\n#define DEBUG_MODULE \"tcp\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** Platform independent buffer type cast */\n#ifdef WIN32\n#define BUF_CAST (char *)\n#define SIZ_CAST (int)\n#define close closesocket\n#else\n#define BUF_CAST\n#define SIZ_CAST\n#endif\n\n\nenum {\n\tTCP_TXQSZ_DEFAULT = 524288,\n\tTCP_RXSZ_DEFAULT  = 8192\n};\n\n\n/** Defines a listening TCP socket */\nstruct tcp_sock {\n\tstruct re_fhs *fhs;\n\tre_sock_t fd;         /**< Listening file descriptor         */\n\tre_sock_t fdc;        /**< Cached connection file descriptor */\n\ttcp_conn_h *connh;    /**< TCP Connect handler               */\n\tvoid *arg;            /**< Handler argument                  */\n\tuint8_t tos;          /**< Type-of-service field             */\n};\n\n\n/** Defines a TCP connection */\nstruct tcp_conn {\n\tstruct list helpers;  /**< List of TCP-helpers               */\n\tstruct list sendq;    /**< Sending queue                     */\n\tstruct re_fhs *fhs;\n\tre_sock_t fdc;        /**< Connection file descriptor        */\n\ttcp_estab_h *estabh;  /**< Connection established handler    */\n\ttcp_send_h *sendh;    /**< Data send handler                 */\n\ttcp_recv_h *recvh;    /**< Data receive handler              */\n\ttcp_close_h *closeh;  /**< Connection close handler          */\n\tvoid *arg;            /**< Handler argument                  */\n\tsize_t rxsz;          /**< Maximum receive chunk size        */\n\tsize_t txqsz;\n\tsize_t txqsz_max;\n\tbool active;          /**< We are connecting flag            */\n\tbool connected;       /**< Connection is connected flag      */\n\tuint8_t tos;          /**< Type-of-service field             */\n};\n\n\n/** Defines a TCP-Connection Helper */\nstruct tcp_helper {\n\tstruct le le;\n\tint layer;\n\ttcp_helper_estab_h *estabh;\n\ttcp_helper_send_h *sendh;\n\ttcp_helper_recv_h *recvh;\n\tvoid *arg;\n};\n\n\nstruct tcp_qent {\n\tstruct le le;\n\tstruct mbuf mb;\n};\n\n\nstatic void tcp_recv_handler(int flags, void *arg);\n\n\nstatic bool helper_estab_handler(int *err, bool active, void *arg)\n{\n\t(void)err;\n\t(void)active;\n\t(void)arg;\n\treturn false;\n}\n\n\nstatic bool helper_send_handler(int *err, struct mbuf *mb, void *arg)\n{\n\t(void)err;\n\t(void)mb;\n\t(void)arg;\n\treturn false;\n}\n\n\nstatic bool helper_recv_handler(int *err, struct mbuf *mb, bool *estab,\n\t\t\t\tvoid *arg)\n{\n\t(void)err;\n\t(void)mb;\n\t(void)estab;\n\t(void)arg;\n\treturn false;\n}\n\n\nstatic void sock_destructor(void *data)\n{\n\tstruct tcp_sock *ts = data;\n\n\tif (ts->fd != RE_BAD_SOCK) {\n\t\tts->fhs = fd_close(ts->fhs);\n\t\t(void)close(ts->fd);\n\t}\n\tif (ts->fdc != RE_BAD_SOCK)\n\t\t(void)close(ts->fdc);\n}\n\n\nstatic struct tcp_sock *sock_constructor(void)\n{\n\tstruct tcp_sock *ts;\n\n\tts = mem_zalloc(sizeof(*ts), sock_destructor);\n\tif (!ts)\n\t\treturn NULL;\n\n\tts->fhs = NULL;\n\tts->fd\t= RE_BAD_SOCK;\n\tts->fdc = RE_BAD_SOCK;\n\n\treturn ts;\n}\n\n\nstatic void conn_destructor(void *data)\n{\n\tstruct tcp_conn *tc = data;\n\n\tlist_flush(&tc->helpers);\n\tlist_flush(&tc->sendq);\n\n\tif (tc->fdc != RE_BAD_SOCK) {\n\t\ttc->fhs = fd_close(tc->fhs);\n\t\t(void)close(tc->fdc);\n\t}\n}\n\n\nstatic void helper_destructor(void *data)\n{\n\tstruct tcp_helper *th = data;\n\n\tlist_unlink(&th->le);\n}\n\n\nstatic void qent_destructor(void *arg)\n{\n\tstruct tcp_qent *qe = arg;\n\n\tlist_unlink(&qe->le);\n\tmem_deref(qe->mb.buf);\n}\n\n\nstatic int enqueue(struct tcp_conn *tc, struct mbuf *mb)\n{\n\tconst size_t n = mbuf_get_left(mb);\n\tstruct tcp_qent *qe;\n\tint err;\n\n\tif (tc->txqsz + n > tc->txqsz_max)\n\t\treturn ENOSPC;\n\n\tif (!tc->sendq.head && !tc->sendh) {\n\n\t\terr = fd_listen(&tc->fhs, tc->fdc, FD_READ | FD_WRITE,\n\t\t\t\ttcp_recv_handler, tc);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tqe = mem_zalloc(sizeof(*qe), qent_destructor);\n\tif (!qe)\n\t\treturn ENOMEM;\n\n\tlist_append(&tc->sendq, &qe->le, qe);\n\n\tmbuf_init(&qe->mb);\n\n\terr = mbuf_write_mem(&qe->mb, mbuf_buf(mb), n);\n\tqe->mb.pos = 0;\n\n\tif (err)\n\t\tmem_deref(qe);\n\telse\n\t\ttc->txqsz += qe->mb.end;\n\n\treturn err;\n}\n\n\nstatic int dequeue(struct tcp_conn *tc)\n{\n\tstruct tcp_qent *qe = list_ledata(tc->sendq.head);\n\tssize_t n;\n\tint err;\n#ifdef MSG_NOSIGNAL\n\tconst int flags = MSG_NOSIGNAL; /* disable SIGPIPE signal */\n#else\n\tconst int flags = 0;\n#endif\n\tif (!qe) {\n\t\tif (tc->sendh)\n\t\t\ttc->sendh(tc->arg);\n\n\t\treturn 0;\n\t}\n\n\tn = send(tc->fdc, BUF_CAST mbuf_buf(&qe->mb),\n\t\t SIZ_CAST (qe->mb.end - qe->mb.pos), flags);\n\tif (n < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tif (err == EAGAIN)\n\t\t\treturn 0;\n#ifdef WIN32\n\t\tif (err == WSAEWOULDBLOCK)\n\t\t\treturn 0;\n#endif\n\t\treturn err;\n\t}\n\n\ttc->txqsz  -= n;\n\tqe->mb.pos += n;\n\n\tif (qe->mb.pos >= qe->mb.end)\n\t\tmem_deref(qe);\n\n\treturn 0;\n}\n\n\nstatic void conn_close(struct tcp_conn *tc, int err)\n{\n\tlist_flush(&tc->sendq);\n\ttc->txqsz = 0;\n\n\t/* Stop polling */\n\tif (tc->fdc != RE_BAD_SOCK) {\n\t\ttc->fhs = fd_close(tc->fhs);\n\t\t(void)close(tc->fdc);\n\t\ttc->fdc = RE_BAD_SOCK;\n\t}\n\n\tif (tc->closeh)\n\t\ttc->closeh(err, tc->arg);\n}\n\n\nstatic void tcp_recv_handler(int flags, void *arg)\n{\n\tstruct tcp_conn *tc = arg;\n\tstruct mbuf *mb = NULL;\n\tbool hlp_estab = false;\n\tstruct le *le;\n\tssize_t n;\n\tint err = 0;\n\tsocklen_t err_len = sizeof(err);\n\n\tif (flags & FD_EXCEPT) {\n\t\tDEBUG_INFO(\"recv handler: got FD_EXCEPT on fd=%d\\n\", tc->fdc);\n\t}\n\n\t/* check for connection errors */\n\tif (tc->active && !tc->connected) {\n\t\tif (-1 == getsockopt(tc->fdc, SOL_SOCKET, SO_ERROR,\n\t\t\t\t     BUF_CAST &err, &err_len)) {\n\t\t\tDEBUG_WARNING(\"recv handler: getsockopt: (%m)\\n\",\n\t\t\t\t      RE_ERRNO_SOCK);\n\t\t\treturn;\n\t\t}\n\t}\n\n#if 0\n\tif (EINPROGRESS != err && EALREADY != err) {\n\t\tDEBUG_WARNING(\"recv handler: Socket error (%m)\\n\", err);\n\t\treturn;\n\t}\n#endif\n\n\tif (err) {\n\t\tconn_close(tc, err);\n\t\treturn;\n\t}\n\n\tif (flags & FD_WRITE) {\n\n\t\tif (tc->connected) {\n\n\t\t\tuint32_t nrefs;\n\n\t\t\tmem_ref(tc);\n\n\t\t\terr = dequeue(tc);\n\n\t\t\tnrefs = mem_nrefs(tc);\n\t\t\tmem_deref(tc);\n\n\t\t\t/* check if connection was deref'd from send handler */\n\t\t\tif (nrefs == 1)\n\t\t\t\treturn;\n\n\t\t\tif (err) {\n\t\t\t\tconn_close(tc, err);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!tc->sendq.head && !tc->sendh) {\n\n\t\t\t\terr = fd_listen(&tc->fhs, tc->fdc, FD_READ,\n\t\t\t\t\t\ttcp_recv_handler, tc);\n\t\t\t\tif (err) {\n\t\t\t\t\tconn_close(tc, err);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (flags & FD_READ)\n\t\t\t\tgoto read;\n\n\t\t\treturn;\n\t\t}\n\n\t\ttc->connected = true;\n\n\t\terr = fd_listen(&tc->fhs, tc->fdc, FD_READ, tcp_recv_handler,\n\t\t\t\ttc);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"recv handler: fd_listen(): %m\\n\", err);\n\t\t\tconn_close(tc, err);\n\t\t\treturn;\n\t\t}\n\n\t\tle = tc->helpers.head;\n\t\twhile (le) {\n\t\t\tstruct tcp_helper *th = le->data;\n\n\t\t\tle = le->next;\n\n\t\t\tif (th->estabh(&err, tc->active, th->arg) || err) {\n\t\t\t\tif (err)\n\t\t\t\t\tconn_close(tc, err);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (tc->estabh)\n\t\t\ttc->estabh(tc->arg);\n\n\t\treturn;\n\t}\n\n read:\n\tmb = mbuf_alloc(tc->rxsz);\n\tif (!mb)\n\t\treturn;\n\n\tn = recv(tc->fdc, BUF_CAST mb->buf, SIZ_CAST mb->size, 0);\n\tif (0 == n) {\n\t\tmem_deref(mb);\n\t\tconn_close(tc, 0);\n\t\treturn;\n\t}\n\telse if (n < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tDEBUG_WARNING(\"recv handler: recv(): %m\\n\", err);\n#ifdef WIN32\n\t\tif (err == WSAECONNRESET || err == WSAECONNABORTED) {\n\t\t\tmem_deref(mb);\n\t\t\tconn_close(tc, err);\n\t\t\treturn;\n\t\t}\n#endif\n\t\tgoto out;\n\t}\n\n\tmb->end = n;\n\n\tle = tc->helpers.head;\n\twhile (le) {\n\t\tstruct tcp_helper *th = le->data;\n\t\tbool hdld = false;\n\n\t\tle = le->next;\n\n\t\tif (hlp_estab) {\n\n\t\t\thdld |= th->estabh(&err, tc->active, th->arg);\n\t\t\tif (err) {\n\t\t\t\tconn_close(tc, err);\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\n\t\tif (mb->pos < mb->end) {\n\n\t\t        hdld |= th->recvh(&err, mb, &hlp_estab, th->arg);\n\t\t\tif (err) {\n\t\t\t\tconn_close(tc, err);\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\n\t\tif (hdld)\n\t\t\tgoto out;\n\t}\n\n\tmbuf_trim(mb);\n\n\tif (hlp_estab && tc->estabh) {\n\n\t\tuint32_t nrefs;\n\n\t\tmem_ref(tc);\n\n\t\ttc->estabh(tc->arg);\n\n\t\tnrefs = mem_nrefs(tc);\n\t\tmem_deref(tc);\n\n\t\t/* check if connection was deref'ed from establish handler */\n\t\tif (nrefs == 1)\n\t\t\tgoto out;\n\t}\n\n\tif (mb->pos < mb->end && tc->recvh) {\n\t\ttc->recvh(mb, tc->arg);\n\t}\n\n out:\n\tmem_deref(mb);\n}\n\n\nstatic struct tcp_conn *conn_alloc(tcp_estab_h *eh, tcp_recv_h *rh,\n\t\t\t\t   tcp_close_h *ch, void *arg)\n{\n\tstruct tcp_conn *tc;\n\n\ttc = mem_zalloc(sizeof(*tc), conn_destructor);\n\tif (!tc)\n\t\treturn NULL;\n\n\tlist_init(&tc->helpers);\n\n\ttc->fhs\t   = NULL;\n\ttc->fdc    = RE_BAD_SOCK;\n\ttc->rxsz   = TCP_RXSZ_DEFAULT;\n\ttc->txqsz_max = TCP_TXQSZ_DEFAULT;\n\ttc->estabh = eh;\n\ttc->recvh  = rh;\n\ttc->closeh = ch;\n\ttc->arg    = arg;\n\n\treturn tc;\n}\n\n\nstatic int  tcp_sock_setopt(struct tcp_sock *ts, int level, int optname,\n\t\t    const void *optval, uint32_t optlen)\n{\n\tint err = 0;\n\n\tif (!ts)\n\t\treturn EINVAL;\n\n\tif (ts->fdc != RE_BAD_SOCK) {\n\t\tif (0 != setsockopt(ts->fdc, level, optname,\n\t\t\t\t    BUF_CAST optval, optlen))\n\t\t\terr |= RE_ERRNO_SOCK;\n\t}\n\n\tif (ts->fd != RE_BAD_SOCK) {\n\t\tif (0 != setsockopt(ts->fd, level, optname,\n\t\t\t\t    BUF_CAST optval, optlen))\n\t\t\terr |= RE_ERRNO_SOCK;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Handler for incoming TCP connections.\n *\n * @param flags  Event flags.\n * @param arg    Handler argument.\n */\nstatic void tcp_conn_handler(int flags, void *arg)\n{\n\tstruct sa peer;\n\tstruct tcp_sock *ts = arg;\n\n\t(void)flags;\n\n\tsa_init(&peer, AF_UNSPEC);\n\n\tif (ts->fdc != RE_BAD_SOCK)\n\t\t(void)close(ts->fdc);\n\n#ifdef HAVE_ACCEPT4\n\tts->fdc = accept4(ts->fd, &peer.u.sa, &peer.len, SOCK_NONBLOCK);\n\tif (ts->fdc == RE_BAD_SOCK) {\n\t\treturn;\n\t}\n#else\n\tts->fdc = accept(ts->fd, &peer.u.sa, &peer.len);\n\tif (ts->fdc == RE_BAD_SOCK) {\n\t\treturn;\n\t}\n\n\tint err = net_sockopt_blocking_set(ts->fdc, false);\n\tif (err) {\n\t\tDEBUG_WARNING(\"conn handler: nonblock set: %m\\n\", err);\n\t\t(void)close(ts->fdc);\n\t\tts->fdc = RE_BAD_SOCK;\n\t\treturn;\n\t}\n#endif\n\n\tif (ts->connh)\n\t\tts->connh(&peer, ts->arg);\n}\n\n\n/**\n * Create a TCP Socket with fd\n *\n * @param tsp   Pointer to returned TCP Socket\n * @param fd    File descriptor\n * @param ch    Incoming connection handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_sock_alloc_fd(struct tcp_sock **tsp, re_sock_t fd, tcp_conn_h *ch,\n\t\t      void *arg)\n{\n\tstruct tcp_sock *ts = NULL;\n\n\tif (!tsp || fd == RE_BAD_SOCK)\n\t\treturn EINVAL;\n\n\tts = sock_constructor();\n\tif (!ts)\n\t\treturn ENOMEM;\n\n\tts->fd\t  = fd;\n\tts->fdc   = RE_BAD_SOCK;\n\tts->connh = ch;\n\tts->arg\t  = arg;\n\n\t*tsp = ts;\n\n\treturn fd_listen(&ts->fhs, ts->fd, FD_READ, tcp_conn_handler, ts);\n}\n\n\n/**\n * Create a TCP Socket\n *\n * @param tsp   Pointer to returned TCP Socket\n * @param local Local listen address (NULL for any)\n * @param ch    Incoming connection handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_sock_alloc(struct tcp_sock **tsp, const struct sa *local,\n\t\t   tcp_conn_h *ch, void *arg)\n{\n\tstruct addrinfo hints, *res = NULL, *r;\n\tchar addr[64] = \"\";\n\tchar serv[6] = \"0\";\n\tstruct tcp_sock *ts = NULL;\n\tint error, err;\n\n\tif (!tsp)\n\t\treturn EINVAL;\n\n\tts = sock_constructor();\n\tif (!ts)\n\t\treturn ENOMEM;\n\n\tts->fd  = RE_BAD_SOCK;\n\tts->fdc = RE_BAD_SOCK;\n\n\tif (local) {\n\t\t(void)re_snprintf(addr, sizeof(addr), \"%H\",\n\t\t\t\t  sa_print_addr, local);\n\t\t(void)re_snprintf(serv, sizeof(serv), \"%u\", sa_port(local));\n\t}\n\n\tmemset(&hints, 0, sizeof(hints));\n\t/* set-up hints structure */\n\thints.ai_family   = PF_UNSPEC;\n\thints.ai_flags    = AI_PASSIVE | AI_NUMERICHOST;\n\thints.ai_socktype = SOCK_STREAM;\n\thints.ai_protocol = IPPROTO_TCP;\n\n\terror = getaddrinfo(addr[0] ? addr : NULL, serv, &hints, &res);\n\tif (error) {\n#ifdef WIN32\n\t\tDEBUG_WARNING(\"listen: getaddrinfo: wsaerr=%d\\n\",\n\t\t\t      WSAGetLastError());\n#endif\n\t\tDEBUG_WARNING(\"listen: getaddrinfo: %s:%s error=%d (%s)\\n\",\n\t\t\t      addr, serv, error, gai_strerror(error));\n\t\terr = EADDRNOTAVAIL;\n\t\tgoto out;\n\t}\n\n\terr = EINVAL;\n\tfor (r = res; r; r = r->ai_next) {\n\t\tre_sock_t fd = RE_BAD_SOCK;\n\n\t\tif (ts->fd != RE_BAD_SOCK)\n\t\t\tcontinue;\n\n\t\tfd = socket(r->ai_family, SOCK_STREAM, IPPROTO_TCP);\n\t\tif (fd == RE_BAD_SOCK) {\n\t\t\terr = RE_ERRNO_SOCK;\n\t\t\tcontinue;\n\t\t}\n\n\t\t(void)net_sockopt_reuse_set(fd, true);\n\n\t\terr = net_sockopt_blocking_set(fd, false);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"listen: nonblock set: %m\\n\", err);\n\t\t\t(void)close(fd);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* OK */\n\t\tts->fd = fd;\n\t\terr = 0;\n\t\tbreak;\n\t}\n\n\tfreeaddrinfo(res);\n\n\tif (ts->fd == RE_BAD_SOCK)\n\t\tgoto out;\n\n\tts->connh = ch;\n\tts->arg   = arg;\n\n out:\n\tif (err)\n\t\tmem_deref(ts);\n\telse\n\t\t*tsp = ts;\n\n\treturn err;\n}\n\n\n/**\n * Duplicate TCP socket\n *\n * @param tso TCP Socket to duplicate\n *\n * @return Duplicated TCP Socket if success, otherwise NULL\n */\nstruct tcp_sock *tcp_sock_dup(struct tcp_sock *tso)\n{\n\tstruct tcp_sock *ts;\n\n\tif (!tso)\n\t\treturn NULL;\n\n\tts = sock_constructor();\n\tif (!ts)\n\t\treturn NULL;\n\n\tts->fd  = RE_BAD_SOCK;\n\tts->fdc = tso->fdc;\n\n\ttso->fdc = RE_BAD_SOCK;\n\n\treturn ts;\n}\n\n\n/**\n * Bind to a TCP Socket\n *\n * @param ts    TCP Socket\n * @param local Local bind address\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_sock_bind(struct tcp_sock *ts, const struct sa *local)\n{\n\tstruct addrinfo hints, *res = NULL, *r;\n\tchar addr[64] = \"\";\n\tchar serv[NI_MAXSERV] = \"0\";\n\tint error, err;\n\n\tif (!ts || ts->fd == RE_BAD_SOCK)\n\t\treturn EINVAL;\n\n\tif (local) {\n\t\t(void)re_snprintf(addr, sizeof(addr), \"%H\",\n\t\t\t\t  sa_print_addr, local);\n\t\t(void)re_snprintf(serv, sizeof(serv), \"%u\", sa_port(local));\n\t}\n\n\tmemset(&hints, 0, sizeof(hints));\n\t/* set-up hints structure */\n\thints.ai_family   = PF_UNSPEC;\n\thints.ai_flags    = AI_PASSIVE | AI_NUMERICHOST;\n\thints.ai_socktype = SOCK_STREAM;\n\thints.ai_protocol = IPPROTO_TCP;\n\n\terror = getaddrinfo(addr[0] ? addr : NULL, serv, &hints, &res);\n\tif (error) {\n#ifdef WIN32\n\t\tDEBUG_WARNING(\"sock_bind: getaddrinfo: wsaerr=%d\\n\",\n\t\t\t      WSAGetLastError());\n#endif\n\t\tDEBUG_WARNING(\"sock_bind: getaddrinfo: %s:%s error=%d (%s)\\n\",\n\t\t\t      addr, serv, error, gai_strerror(error));\n\t\treturn EADDRNOTAVAIL;\n\t}\n\n\terr = EINVAL;\n\tfor (r = res; r; r = r->ai_next) {\n\t\t/* use dual socket */\n\t\tif (r->ai_family == AF_INET6)\n\t\t\t(void)net_sockopt_v6only(ts->fd, false);\n\n\t\tif (bind(ts->fd, r->ai_addr, SIZ_CAST r->ai_addrlen) < 0) {\n\t\t\terr = RE_ERRNO_SOCK;\n\t\t\tDEBUG_WARNING(\"sock_bind: bind: %m (af=%d, %J)\\n\",\n\t\t\t\t      err, r->ai_family, local);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* OK */\n\t\terr = 0;\n\t\tbreak;\n\t}\n\n\tfreeaddrinfo(res);\n\n\treturn err;\n}\n\n\n/**\n * Listen on a TCP Socket\n *\n * @param ts       TCP Socket\n * @param backlog  Maximum length the queue of pending connections\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_sock_listen(struct tcp_sock *ts, int backlog)\n{\n\tint err;\n\n\tif (!ts)\n\t\treturn EINVAL;\n\n\tif (ts->fd == RE_BAD_SOCK) {\n\t\tDEBUG_WARNING(\"sock_listen: invalid fd\\n\");\n\t\treturn EBADF;\n\t}\n\n\tif (listen(ts->fd, backlog) < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tDEBUG_WARNING(\"sock_listen: listen(): %m\\n\", err);\n\t\treturn err;\n\t}\n\n\treturn fd_listen(&ts->fhs, ts->fd, FD_READ, tcp_conn_handler, ts);\n}\n\n\n/**\n * Accept an incoming TCP Connection\n *\n * @param tcp Returned TCP Connection object\n * @param ts  Corresponding TCP Socket\n * @param eh  TCP Connection Established handler\n * @param rh  TCP Connection Receive data handler\n * @param ch  TCP Connection close handler\n * @param arg Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_accept(struct tcp_conn **tcp, struct tcp_sock *ts, tcp_estab_h *eh,\n\t       tcp_recv_h *rh, tcp_close_h *ch, void *arg)\n{\n\tstruct tcp_conn *tc;\n\tint err;\n\n\tif (!tcp || !ts || ts->fdc == RE_BAD_SOCK)\n\t\treturn EINVAL;\n\n\ttc = conn_alloc(eh, rh, ch, arg);\n\tif (!tc)\n\t\treturn ENOMEM;\n\n\t/* Transfer ownership to TCP connection */\n\ttc->fdc = ts->fdc;\n\tts->fdc = RE_BAD_SOCK;\n\n\terr = fd_listen(&tc->fhs, tc->fdc, FD_READ | FD_WRITE | FD_EXCEPT,\n\t\t\ttcp_recv_handler, tc);\n\tif (err) {\n\t\tDEBUG_WARNING(\"accept: fd_listen(): %m\\n\", err);\n\t}\n\n\tif (err)\n\t\tmem_deref(tc);\n\telse\n\t\t*tcp = tc;\n\n\treturn err;\n}\n\n\n/**\n * Reject an incoming TCP Connection\n *\n * @param ts  Corresponding TCP Socket\n */\nvoid tcp_reject(struct tcp_sock *ts)\n{\n\tif (!ts)\n\t\treturn;\n\n\tif (ts->fdc != RE_BAD_SOCK) {\n\t\t(void)close(ts->fdc);\n\t\tts->fdc = RE_BAD_SOCK;\n\t}\n}\n\n\n/**\n * Allocate a TCP Connection\n *\n * @param tcp  Returned TCP Connection object\n * @param peer Network address of peer\n * @param eh   TCP Connection Established handler\n * @param rh   TCP Connection Receive data handler\n * @param ch   TCP Connection close handler\n * @param arg  Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_conn_alloc(struct tcp_conn **tcp,\n\t\t   const struct sa *peer, tcp_estab_h *eh,\n\t\t   tcp_recv_h *rh, tcp_close_h *ch, void *arg)\n{\n\tstruct tcp_conn *tc;\n\tstruct addrinfo hints, *res = NULL, *r;\n\tchar addr[64];\n\tchar serv[NI_MAXSERV] = \"0\";\n\tint error, err;\n\n\tif (!tcp || !sa_isset(peer, SA_ALL))\n\t\treturn EINVAL;\n\n\ttc = conn_alloc(eh, rh, ch, arg);\n\tif (!tc)\n\t\treturn ENOMEM;\n\n\tmemset(&hints, 0, sizeof(hints));\n\t/* set-up hints structure */\n\thints.ai_family   = PF_UNSPEC;\n\thints.ai_flags    = AI_PASSIVE | AI_NUMERICHOST;\n\thints.ai_socktype = SOCK_STREAM;\n\thints.ai_protocol = IPPROTO_TCP;\n\n\t(void)re_snprintf(addr, sizeof(addr), \"%H\",\n\t\t\t  sa_print_addr, peer);\n\t(void)re_snprintf(serv, sizeof(serv), \"%u\", sa_port(peer));\n\n\terror = getaddrinfo(addr, serv, &hints, &res);\n\tif (error) {\n\t\tDEBUG_WARNING(\"connect: getaddrinfo(): (%s)\\n\",\n\t\t\t      gai_strerror(error));\n\t\terr = EADDRNOTAVAIL;\n\t\tgoto out;\n\t}\n\n\terr = EINVAL;\n\tfor (r = res; r; r = r->ai_next) {\n\n\t\ttc->fdc = socket(r->ai_family, SOCK_STREAM,\n\t\t\t\t\t  IPPROTO_TCP);\n\t\tif (tc->fdc == RE_BAD_SOCK) {\n\t\t\terr = RE_ERRNO_SOCK;\n\t\t\tcontinue;\n\t\t}\n\n\t\terr = net_sockopt_blocking_set(tc->fdc, false);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"connect: nonblock set: %m\\n\", err);\n\t\t\t(void)close(tc->fdc);\n\t\t\ttc->fdc = RE_BAD_SOCK;\n\t\t\tcontinue;\n\t\t}\n\n\t\terr = 0;\n\t\tbreak;\n\t}\n\n\tfreeaddrinfo(res);\n\n out:\n\tif (err)\n\t\tmem_deref(tc);\n\telse\n\t\t*tcp = tc;\n\n\treturn err;\n}\n\n\n/**\n * Bind a TCP Connection to a local address\n *\n * @param tc    TCP Connection object\n * @param local Local bind address\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_conn_bind(struct tcp_conn *tc, const struct sa *local)\n{\n\tstruct addrinfo hints, *res = NULL, *r;\n\tchar addr[64] = \"\";\n\tchar serv[NI_MAXSERV] = \"0\";\n\tint error, err;\n\n\tif (!tc)\n\t\treturn EINVAL;\n\n\tif (local) {\n\t\t(void)re_snprintf(addr, sizeof(addr), \"%H\",\n\t\t\t\t  sa_print_addr, local);\n\t\t(void)re_snprintf(serv, sizeof(serv), \"%u\", sa_port(local));\n\t}\n\n\tmemset(&hints, 0, sizeof(hints));\n\t/* set-up hints structure */\n\thints.ai_family   = PF_UNSPEC;\n\thints.ai_flags    = AI_PASSIVE | AI_NUMERICHOST;\n\thints.ai_socktype = SOCK_STREAM;\n\thints.ai_protocol = IPPROTO_TCP;\n\n\terror = getaddrinfo(addr[0] ? addr : NULL, serv, &hints, &res);\n\tif (error) {\n\t\tDEBUG_WARNING(\"conn_bind: getaddrinfo(): (%s)\\n\",\n\t\t\t      gai_strerror(error));\n\t\treturn EADDRNOTAVAIL;\n\t}\n\n\terr = EINVAL;\n\tfor (r = res; r; r = r->ai_next) {\n\n\t\t(void)net_sockopt_reuse_set(tc->fdc, true);\n\n\t\t/* use dual socket */\n\t\tif (r->ai_family == AF_INET6)\n\t\t\t(void)net_sockopt_v6only(tc->fdc, false);\n\n\t\t/* bind to local address */\n\t\tif (bind(tc->fdc, r->ai_addr, SIZ_CAST r->ai_addrlen) < 0) {\n\n\t\t\terr = RE_ERRNO_SOCK;\n\t\t\tDEBUG_WARNING(\"conn_bind: bind(): %J: %m\\n\",\n\t\t\t\t      local, err);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* OK */\n\t\terr = 0;\n\t\tbreak;\n\t}\n\n\tfreeaddrinfo(res);\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"conn_bind failed: %J (%m)\\n\", local, err);\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Connect to a remote peer\n *\n * @param tc   TCP Connection object\n * @param peer Network address of peer\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_conn_connect(struct tcp_conn *tc, const struct sa *peer)\n{\n\tstruct addrinfo hints, *res = NULL, *r;\n\tchar addr[64];\n\tchar serv[NI_MAXSERV];\n\tint error, err = 0;\n\n\tif (!tc || !sa_isset(peer, SA_ALL))\n\t\treturn EINVAL;\n\n\ttc->active = true;\n\n\tif (tc->fdc == RE_BAD_SOCK) {\n\t\tDEBUG_WARNING(\"invalid fd\\n\");\n\t\treturn EBADF;\n\t}\n\n\tmemset(&hints, 0, sizeof(hints));\n\t/* set-up hints structure */\n\thints.ai_family   = PF_UNSPEC;\n\thints.ai_flags    = AI_PASSIVE | AI_NUMERICHOST;\n\thints.ai_socktype = SOCK_STREAM;\n\thints.ai_protocol = IPPROTO_TCP;\n\n\t(void)re_snprintf(addr, sizeof(addr), \"%H\",\n\t\t\t  sa_print_addr, peer);\n\t(void)re_snprintf(serv, sizeof(serv), \"%u\", sa_port(peer));\n\n\terror = getaddrinfo(addr, serv, &hints, &res);\n\tif (error) {\n\t\tDEBUG_WARNING(\"connect: getaddrinfo(): (%s)\\n\",\n\t\t\t      gai_strerror(error));\n\t\treturn EADDRNOTAVAIL;\n\t}\n\n\tfor (r = res; r; r = r->ai_next) {\n\t\tstruct sockaddr *sa = r->ai_addr;\n\n\tagain:\n\t\tif (0 == connect(tc->fdc, sa, SIZ_CAST r->ai_addrlen)) {\n\t\t\terr = 0;\n\t\t\tgoto out;\n\t\t}\n\t\telse {\n#ifdef WIN32\n\t\t\t/* Special error handling for Windows */\n\t\t\tif (WSAEWOULDBLOCK == WSAGetLastError()) {\n\t\t\t\terr = 0;\n\t\t\t\tgoto out;\n\t\t\t}\n#endif\n\n\t\t\t/* Special case for mingw32/wine */\n\t\t\tif (0 == errno) {\n\t\t\t\terr = 0;\n\t\t\t\tgoto out;\n\t\t\t}\n\n\t\t\tif (EINTR == errno)\n\t\t\t\tgoto again;\n\n\t\t\tif (EINPROGRESS != errno && EALREADY != errno) {\n\t\t\t\terr = errno;\n\t\t\t\tDEBUG_INFO(\"connect: connect() %J: %m\\n\",\n\t\t\t\t\t   peer, err);\n\t\t\t}\n\t\t}\n\t}\n\n out:\n\tfreeaddrinfo(res);\n\n\tif (err)\n\t\treturn err;\n\n\treturn fd_listen(&tc->fhs, tc->fdc, FD_READ | FD_WRITE | FD_EXCEPT,\n\t\t\t tcp_recv_handler, tc);\n}\n\n\nstatic int tcp_send_internal(struct tcp_conn *tc, struct mbuf *mb,\n\t\t\t     struct le *le)\n{\n\tint err = 0;\n\tssize_t n;\n#ifdef MSG_NOSIGNAL\n\tconst int flags = MSG_NOSIGNAL; /* disable SIGPIPE signal */\n#else\n\tconst int flags = 0;\n#endif\n\n\tif (tc->fdc == RE_BAD_SOCK)\n\t\treturn ENOTCONN;\n\n\tif (!mbuf_get_left(mb)) {\n\t\tDEBUG_WARNING(\"send: empty mbuf (pos=%u end=%u)\\n\",\n\t\t\t      mb->pos, mb->end);\n\t\treturn EINVAL;\n\t}\n\n\t/* call helpers in reverse order */\n\twhile (le) {\n\t\tstruct tcp_helper *th = le->data;\n\n\t\tle = le->prev;\n\n\t\tif (th->sendh(&err, mb, th->arg) || err)\n\t\t\treturn err;\n\t}\n\n\tif (tc->sendq.head)\n\t\treturn enqueue(tc, mb);\n\n\tn = send(tc->fdc, BUF_CAST mbuf_buf(mb),\n\t\t SIZ_CAST (mb->end - mb->pos), flags);\n\tif (n < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\n\t\tif (err == EAGAIN)\n\t\t\treturn enqueue(tc, mb);\n\n#ifdef WIN32\n\t\tif (err == WSAEWOULDBLOCK)\n\t\t\treturn enqueue(tc, mb);\n#endif\n\n\t\tDEBUG_WARNING(\"send: write(): %m (fdc=%d)\\n\", err, tc->fdc);\n\n\t\treturn err;\n\t}\n\n\tif ((size_t)n < mb->end - mb->pos) {\n\n\t\tmb->pos += n;\n\t\terr = enqueue(tc, mb);\n\t\tmb->pos -= n;\n\n\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Send data on a TCP Connection to a remote peer\n *\n * @param tc TCP Connection\n * @param mb Buffer to send\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_send(struct tcp_conn *tc, struct mbuf *mb)\n{\n\tif (!tc || !mb)\n\t\treturn EINVAL;\n\n\treturn tcp_send_internal(tc, mb, tc->helpers.tail);\n}\n\n\n/**\n * Send data on a TCP Connection to a remote peer bypassing this\n * helper and the helpers above it.\n *\n * @param tc TCP Connection\n * @param mb Buffer to send\n * @param th TCP Helper\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_send_helper(struct tcp_conn *tc, struct mbuf *mb,\n\t\t    struct tcp_helper *th)\n{\n\tif (!tc || !mb || !th)\n\t\treturn EINVAL;\n\n\treturn tcp_send_internal(tc, mb, th->le.prev);\n}\n\n\n/**\n * Set the send handler on a TCP Connection, which will be called\n * every time it is ready to send data\n *\n * @param tc    TCP Connection\n * @param sendh TCP Send handler\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_set_send(struct tcp_conn *tc, tcp_send_h *sendh)\n{\n\tif (!tc)\n\t\treturn EINVAL;\n\n\ttc->sendh = sendh;\n\n\tif (tc->sendq.head || !sendh)\n\t\treturn 0;\n\n\treturn fd_listen(&tc->fhs, tc->fdc, FD_READ | FD_WRITE,\n\t\t\t tcp_recv_handler, tc);\n}\n\n\n/**\n * Set handlers on a TCP Connection\n *\n * @param tc  TCP Connection\n * @param eh  TCP Connection Established handler\n * @param rh  TCP Connection Receive data handler\n * @param ch  TCP Connection Close handler\n * @param arg Handler argument\n */\nvoid tcp_set_handlers(struct tcp_conn *tc, tcp_estab_h *eh, tcp_recv_h *rh,\n\t\t      tcp_close_h *ch, void *arg)\n{\n\tif (!tc)\n\t\treturn;\n\n\ttc->estabh = eh;\n\ttc->recvh  = rh;\n\ttc->closeh = ch;\n\ttc->arg    = arg;\n}\n\n\n/**\n * Get local network address of TCP Socket\n *\n * @param ts    TCP Socket\n * @param local Returned local network address\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_sock_local_get(const struct tcp_sock *ts, struct sa *local)\n{\n\tint err;\n\n\tif (!ts || !local)\n\t\treturn EINVAL;\n\n\tsa_init(local, AF_UNSPEC);\n\terr = getsockname(ts->fd, &local->u.sa, &local->len);\n\n\tif (err < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tDEBUG_WARNING(\"local get: getsockname(): %m\\n\", err);\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Get local network address of TCP Connection\n *\n * @param tc    TCP Connection\n * @param local Returned local network address\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_conn_local_get(const struct tcp_conn *tc, struct sa *local)\n{\n\tint err;\n\n\tif (!tc || !local)\n\t\treturn EINVAL;\n\n\tsa_init(local, AF_UNSPEC);\n\terr = getsockname(tc->fdc, &local->u.sa, &local->len);\n\n\tif (err < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tDEBUG_WARNING(\"conn local get: getsockname(): %m\\n\", err);\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Get remote peer network address of TCP Connection\n *\n * @param tc    TCP Connection\n * @param peer Returned remote peer network address\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_conn_peer_get(const struct tcp_conn *tc, struct sa *peer)\n{\n\tint err;\n\n\tif (!tc || !peer)\n\t\treturn EINVAL;\n\n\tsa_init(peer, AF_UNSPEC);\n\terr = getpeername(tc->fdc, &peer->u.sa, &peer->len);\n\n\tif (err < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tDEBUG_WARNING(\"conn peer get: getpeername(): %m\\n\", err);\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Set the maximum receive chunk size on a TCP Connection\n *\n * @param tc   TCP Connection\n * @param rxsz Maximum receive chunk size\n */\nvoid tcp_conn_rxsz_set(struct tcp_conn *tc, size_t rxsz)\n{\n\tif (!tc)\n\t\treturn;\n\n\ttc->rxsz = rxsz;\n}\n\n\n/**\n * Set the maximum send queue size on a TCP Connection\n *\n * @param tc    TCP Connection\n * @param txqsz Maximum send queue size\n */\nvoid tcp_conn_txqsz_set(struct tcp_conn *tc, size_t txqsz)\n{\n\tif (!tc)\n\t\treturn;\n\n\ttc->txqsz_max = txqsz;\n}\n\n\n/**\n * Get the current length of the transmit queue on a TCP Connection\n *\n * @param tc TCP-Connection\n *\n * @return Current transmit queue length, or 0 if errors\n */\nsize_t tcp_conn_txqsz(const struct tcp_conn *tc)\n{\n\treturn tc ? tc->txqsz : 0;\n}\n\n\nstatic bool sort_handler(struct le *le1, struct le *le2, void *arg)\n{\n\tstruct tcp_helper *th1 = le1->data, *th2 = le2->data;\n\t(void)arg;\n\n\treturn th1->layer <= th2->layer;\n}\n\n\n/**\n * Register a new TCP-helper on a TCP-Connection\n *\n * @param thp   Pointer to allocated TCP helper\n * @param tc    TCP Connection\n * @param layer Protocol layer; higher number means higher up in stack\n * @param eh    Established handler\n * @param sh    Send handler\n * @param rh    Receive handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_register_helper(struct tcp_helper **thp, struct tcp_conn *tc,\n\t\t\tint layer,\n\t\t\ttcp_helper_estab_h *eh, tcp_helper_send_h *sh,\n\t\t\ttcp_helper_recv_h *rh, void *arg)\n{\n\tstruct tcp_helper *th;\n\n\tif (!tc)\n\t\treturn EINVAL;\n\n\tth = mem_zalloc(sizeof(*th), helper_destructor);\n\tif (!th)\n\t\treturn ENOMEM;\n\n\tlist_append(&tc->helpers, &th->le, th);\n\n\tth->layer  = layer;\n\tth->estabh = eh ? eh : helper_estab_handler;\n\tth->sendh  = sh ? sh : helper_send_handler;\n\tth->recvh  = rh ? rh : helper_recv_handler;\n\tth->arg = arg;\n\n\tlist_sort(&tc->helpers, sort_handler, NULL);\n\n\tif (thp)\n\t\t*thp = th;\n\n\treturn 0;\n}\n\n\nint tcp_settos(struct tcp_sock *ts, uint32_t tos)\n{\n\tint err = 0;\n\tint v = tos;\n\tstruct sa sa;\n\n\tif (!ts)\n\t\treturn EINVAL;\n\n\tts->tos = tos;\n\terr = tcp_local_get(ts, &sa);\n\tif (err)\n\t\treturn err;\n\n\tif (sa_af(&sa) == AF_INET) {\n\t\terr = tcp_sock_setopt(ts, IPPROTO_IP, IP_TOS, &v, sizeof(v));\n\t}\n#if defined(IPV6_TCLASS) && !defined(WIN32)\n\telse  if (sa_af(&sa) == AF_INET6) {\n\t\terr = tcp_sock_setopt(ts, IPPROTO_IPV6, IPV6_TCLASS, &v,\n\t\t\t\t      sizeof(v));\n\t}\n#endif\n\n\treturn err;\n}\n\n\nint tcp_conn_settos(struct tcp_conn *tc, uint32_t tos)\n{\n\tint err = 0;\n\tint v = tos;\n\tstruct sa sa;\n\n\tif (!tc)\n\t\treturn EINVAL;\n\n\ttc->tos = tos;\n\tif (tc->fdc == RE_BAD_SOCK)\n\t\treturn err;\n\n\terr = tcp_conn_local_get(tc, &sa);\n\tif (err)\n\t\treturn err;\n\n\tif (sa_af(&sa) == AF_INET) {\n\t\tif (0 != setsockopt(tc->fdc, IPPROTO_IP, IP_TOS,\n\t\t\t\t\tBUF_CAST &v, sizeof(v)))\n\t\t\terr = RE_ERRNO_SOCK;\n\t}\n#if defined(IPV6_TCLASS) && !defined(WIN32)\n\telse  if (sa_af(&sa) == AF_INET6) {\n\t\tif (0 != setsockopt(tc->fdc, IPPROTO_IPV6, IPV6_TCLASS,\n\t\t\t\t\tBUF_CAST &v, sizeof(v)))\n\t\t\terr = RE_ERRNO_SOCK;\n\t}\n#endif\n\n\treturn err;\n}\n\n\nbool tcp_sendq_used(struct tcp_conn *tc)\n{\n\treturn tc->sendq.head != NULL;\n}\n"
  },
  {
    "path": "src/tcp/tcp_high.c",
    "content": "/**\n * @file tcp_high.c  High-level TCP functions\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_tcp.h>\n\n#ifndef RE_TCP_BACKLOG\n#define RE_TCP_BACKLOG 5\n#endif\n\n/**\n * Create and listen on a TCP Socket\n *\n * @param tsp   Pointer to returned TCP Socket\n * @param local Local listen address (NULL for any)\n * @param ch    Incoming connection handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_listen(struct tcp_sock **tsp, const struct sa *local,\n\t       tcp_conn_h *ch, void *arg)\n{\n\tstruct tcp_sock *ts = NULL;\n\tint err;\n\n\tif (!tsp)\n\t\treturn EINVAL;\n\n\terr = tcp_sock_alloc(&ts, local, ch, arg);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_sock_bind(ts, local);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_sock_listen(ts, RE_TCP_BACKLOG);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tts = mem_deref(ts);\n\telse\n\t\t*tsp = ts;\n\n\treturn err;\n}\n\n\n/**\n * Make a TCP Connection to a remote peer\n *\n * @param tcp  Returned TCP Connection object\n * @param peer Network address of peer\n * @param eh   TCP Connection Established handler\n * @param rh   TCP Connection Receive data handler\n * @param ch   TCP Connection close handler\n * @param arg  Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_connect(struct tcp_conn **tcp, const struct sa *peer,\n\t\ttcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg)\n{\n\tstruct tcp_conn *tc = NULL;\n\tint err;\n\n\tif (!tcp || !peer)\n\t\treturn EINVAL;\n\n\terr = tcp_conn_alloc(&tc, peer, eh,rh, ch, arg);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_conn_connect(tc, peer);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\ttc = mem_deref(tc);\n\telse\n\t\t*tcp = tc;\n\n\treturn err;\n}\n\n\n/**\n * Make a TCP Connection to a remote peer\n *\n * @param tcp   Returned TCP Connection object\n * @param peer  Network address of peer\n * @param eh    TCP Connection Established handler\n * @param rh    TCP Connection Receive data handler\n * @param ch    TCP Connection close handler\n * @param local Bind to local address\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_connect_bind(struct tcp_conn **tcp, const struct sa *peer,\n\t\ttcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch,\n\t\tconst struct sa *local, void *arg)\n{\n\tstruct tcp_conn *tc = NULL;\n\tint err;\n\n\tif (!tcp || !peer)\n\t\treturn EINVAL;\n\n\terr = tcp_conn_alloc(&tc, peer, eh,rh, ch, arg);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_conn_bind(tc, local);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_conn_connect(tc, peer);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\ttc = mem_deref(tc);\n\telse\n\t\t*tcp = tc;\n\n\treturn err;\n}\n\n\n/**\n * Get local network address of TCP Socket\n *\n * @param ts    TCP Socket\n * @param local Returned local network address\n *\n * @return 0 if success, otherwise errorcode\n */\nint tcp_local_get(const struct tcp_sock *ts, struct sa *local)\n{\n\treturn tcp_sock_local_get(ts, local);\n}\n"
  },
  {
    "path": "src/telev/telev.c",
    "content": "/**\n * @file telev.c  Telephony Events implementation (RFC 4733)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <ctype.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_net.h>\n#include <re_telev.h>\n\n\n/*\n * Implements a subset of RFC 4733,\n * \"RTP Payload for DTMF Digits, Telephony Tones, and Telephony Signals\"\n *\n * Call telev_send() to send telephony events, and telev_recv() to receive.\n * The function telev_poll() must be called periodically, with a minimum\n * and stable interval of less than or equal to packet time (e.g. 50ms)\n *\n * NOTE: RTP timestamp and sequence number is kept in application\n *\n *\n *  Example sending:\n *\n *               ms  M  ts  seq  ev   dur  End\n *\n *  press X -->\n *               50  1   0   1    X   400   0\n *              100  0   0   2    X   800   0\n *              150  0   0   3    X  1200   0\n *              200  0   0   4    X  1600   0\n *              ...  .   .   .    .  ....   .\n *  release X -->\n *              250  0   0   5    X  1600   1\n *              300  0   0   6    X  1600   1\n *  press Y -->\n *              350  1  2000 7    Y   400   0\n *              ...  .  .... .    .   ...   .\n */\n\n\nenum {\n\tTXC_DIGIT_MIN = 9,\n\tTXC_END       = 3,\n\tEVENT_END     = 0xff,\n\tPAYLOAD_SIZE  = 4,\n\tVOLUME        = 10,\n\tQUEUE_SIZE    = 8192,\n};\n\n\nenum state {\n\tIDLE,\n\tSENDING,\n\tENDING,\n};\n\n\nstruct telev_fmt {\n\tuint8_t event;\n\tbool end;\n\tuint8_t vol;\n\tuint16_t dur;\n};\n\n\n/** Defines a Telephony Events state */\nstruct telev {\n\t/* tx */\n\tstruct mbuf *mb;\n\tuint32_t ptime;\n\tuint16_t pdur;\n\tenum state state;\n\tint event;\n\tuint16_t dur;\n\tuint32_t txc;\n\n\t/* rx */\n\tint rx_event;\n\tbool rx_end;\n};\n\n\nconst char telev_rtpfmt[] = \"telephone-event\";\n\n\nstatic int payload_encode(struct mbuf *mb, int event, bool end, uint16_t dur)\n{\n\tsize_t pos;\n\tint err;\n\n\tif (!mb)\n\t\treturn EINVAL;\n\n\tpos = mb->pos;\n\n\terr  = mbuf_write_u8(mb, event);\n\terr |= mbuf_write_u8(mb, (end ? 0x80 : 0x00) | (VOLUME & 0x3f));\n\terr |= mbuf_write_u16(mb, htons(dur));\n\n\tif (err)\n\t\tmb->pos = pos;\n\n\treturn err;\n}\n\n\nstatic int payload_decode(struct mbuf *mb, struct telev_fmt *fmt)\n{\n\tuint8_t b;\n\n\tif (!mb || !fmt)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < PAYLOAD_SIZE)\n\t\treturn ENOENT;\n\n\tfmt->event = mbuf_read_u8(mb);\n\tb = mbuf_read_u8(mb);\n\tfmt->end = (b & 0x80) == 0x80;\n\tfmt->vol = (b & 0x3f);\n\tfmt->dur = ntohs(mbuf_read_u16(mb));\n\n\treturn 0;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct telev *t = arg;\n\n\tmem_deref(t->mb);\n}\n\n\n/**\n * Allocate a new Telephony Events state\n *\n * @param tp    Pointer to allocated object\n * @param ptime Packet time in [ms]\n *\n * @return 0 if success, otherwise errorcode\n */\nint telev_alloc(struct telev **tp, uint32_t ptime)\n{\n\tstruct telev *t;\n\tint err = 0;\n\n\tif (!tp || !ptime)\n\t\treturn EINVAL;\n\n\tt = mem_zalloc(sizeof(*t), destructor);\n\tif (!t)\n\t\treturn ENOMEM;\n\n\tt->mb = mbuf_alloc(16);\n\tif (!t->mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tt->state = IDLE;\n\tt->ptime = ptime;\n\tt->pdur = ptime * 8;\n\tt->rx_event = -1;\n\n out:\n\tif (err)\n\t\tmem_deref(t);\n\telse\n\t\t*tp = t;\n\n\treturn err;\n}\n\n\n/**\n * Sets the sampling rate\n *\n * @param tel   Telephony Event state\n * @param srate Sampling rate in [Hz]\n *\n * @return 0 if success, otherwise errorcode\n */\nint telev_set_srate(struct telev *tel, uint32_t srate)\n{\n\tif (!tel || !srate)\n\t\treturn EINVAL;\n\n\ttel->pdur = tel->ptime * srate / 1000;\n\n\treturn 0;\n}\n\n\n/**\n * Send a Telephony Event\n *\n * @param tel   Telephony Event state\n * @param event The Event to send\n * @param end   End-of-event flag\n *\n * @return 0 if success, otherwise errorcode\n */\nint telev_send(struct telev *tel, int event, bool end)\n{\n\tsize_t pos;\n\tint err;\n\n\tif (!tel)\n\t\treturn EINVAL;\n\n\tif (tel->mb->end >= QUEUE_SIZE)\n\t\treturn EOVERFLOW;\n\n\tpos = tel->mb->pos;\n\n\ttel->mb->pos = tel->mb->end;\n\terr = mbuf_write_u8(tel->mb, end ? EVENT_END : event);\n\ttel->mb->pos = pos;\n\n\treturn err;\n}\n\n\n/**\n * Receive a Telephony Event\n *\n * @param tel   Telephony Event state\n * @param mb    Buffer to decode\n * @param event The received event, set on return\n * @param end   End-of-event flag, set on return\n *\n * @return 0 if success, otherwise errorcode\n */\nint telev_recv(struct telev *tel, struct mbuf *mb, int *event, bool *end)\n{\n\tstruct telev_fmt fmt;\n\tint err;\n\n\tif (!tel || !mb || !event || !end)\n\t\treturn EINVAL;\n\n\terr = payload_decode(mb, &fmt);\n\tif (err)\n\t\treturn err;\n\n\tif (fmt.end) {\n\t\tif (tel->rx_end)\n\t\t\treturn EALREADY;\n\n\t\t*event = fmt.event;\n\t\t*end = true;\n\t\ttel->rx_event = -1;\n\t\ttel->rx_end = true;\n\t\treturn 0;\n\t}\n\n\tif (fmt.event == tel->rx_event)\n\t\treturn EALREADY;\n\n\t*event = tel->rx_event = fmt.event;\n\t*end   = tel->rx_end   = false;\n\n\treturn 0;\n}\n\n\n/**\n * Poll a Telephony Event state for sending\n *\n * @param tel    Telephony Event state\n * @param marker Marker bit, set on return\n * @param mb     Buffer with encoded data to send\n *\n * @return 0 if success, otherwise errorcode\n */\nint telev_poll(struct telev *tel, bool *marker, struct mbuf *mb)\n{\n\tbool mrk = false;\n\tint err = ENOENT;\n\n\tif (!tel || !marker || !mb)\n\t\treturn EINVAL;\n\n\tswitch (tel->state) {\n\n\tcase IDLE:\n\t\tif (!mbuf_get_left(tel->mb))\n\t\t\tbreak;\n\n\t\tmrk = true;\n\n\t\ttel->event = mbuf_read_u8(tel->mb);\n\t\ttel->dur   = tel->pdur;\n\t\ttel->state = SENDING;\n\t\ttel->txc   = 1;\n\n\t\terr = payload_encode(mb, tel->event, false, tel->dur);\n\t\tbreak;\n\n\tcase SENDING:\n\t\ttel->dur += tel->pdur;\n\n\t\terr = payload_encode(mb, tel->event, false, tel->dur);\n\n\t\tif (++tel->txc < TXC_DIGIT_MIN)\n\t\t\tbreak;\n\n\t\tif (!mbuf_get_left(tel->mb))\n\t\t\tbreak;\n\n\t\tif (mbuf_read_u8(tel->mb) != EVENT_END)\n\t\t\ttel->mb->pos--;\n\n\t\ttel->state = ENDING;\n\t\ttel->txc   = 0;\n\t\tbreak;\n\n\tcase ENDING:\n\t\terr = payload_encode(mb, tel->event, true, tel->dur);\n\n\t\tif (++tel->txc < TXC_END)\n\t\t\tbreak;\n\n\t\tif (!mbuf_get_left(tel->mb))\n\t\t\ttel->mb->pos = tel->mb->end = 0;\n\n\t\ttel->state = IDLE;\n\t\tbreak;\n\t}\n\n\tif (!err)\n\t\t*marker = mrk;\n\n\treturn err;\n}\n\n\nbool telev_is_empty(const struct telev *tel)\n{\n\tif (!tel)\n\t\treturn true;\n\n\treturn tel->state == IDLE && !mbuf_get_left(tel->mb);\n}\n\n\n/**\n * Convert DTMF digit to Event code\n *\n * @param digit DTMF Digit\n *\n * @return Event code, or -1 if error\n */\nint telev_digit2code(int digit)\n{\n\tif (isdigit(digit))\n\t\treturn digit - '0';\n\telse if (digit == '*')\n\t\treturn 10;\n\telse if (digit == '#')\n\t\treturn 11;\n\telse if ('a' <= digit && digit <= 'd')\n\t\treturn digit - 'a' + 12;\n\telse if ('A' <= digit && digit <= 'D')\n\t\treturn digit - 'A' + 12;\n\telse\n\t\treturn -1;\n}\n\n\n/**\n * Convert Event code to DTMF digit\n *\n * @param code Event code\n *\n * @return DTMF Digit, or -1 if error\n */\nint telev_code2digit(int code)\n{\n\tstatic const char map[] = \"0123456789*#ABCD\";\n\n\tif (code < 0 || code >= 16)\n\t\treturn -1;\n\n\treturn map[code];\n}\n"
  },
  {
    "path": "src/thread/posix.c",
    "content": "/**\n * @file posix.c  Pthread C11 thread implementation\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_thread.h>\n\n\nstruct thread {\n\tthrd_start_t func;\n\tvoid *arg;\n};\n\n\nstatic void *handler(void *p)\n{\n\tstruct thread *th = p;\n\n\tint ret = th->func(th->arg);\n\tmem_deref(th);\n\n\treturn (void *)(intptr_t)ret;\n}\n\n\nint thrd_create(thrd_t *thr, thrd_start_t func, void *arg)\n{\n\tstruct thread *th;\n\tint err;\n\n\tif (!thr || !func)\n\t\treturn thrd_error;\n\n\tth = mem_alloc(sizeof(struct thread), NULL);\n\tif (!th)\n\t\treturn thrd_nomem;\n\n\tth->func = func;\n\tth->arg\t = arg;\n\n\terr = pthread_create(thr, NULL, handler, th);\n\tif (err) {\n\t\tmem_deref(th);\n\t\treturn thrd_error;\n\t}\n\n\treturn thrd_success;\n}\n\n\nint thrd_equal(thrd_t lhs, thrd_t rhs)\n{\n\treturn pthread_equal(lhs, rhs);\n}\n\n\nthrd_t thrd_current(void)\n{\n\treturn pthread_self();\n}\n\n\nint thrd_detach(thrd_t thr)\n{\n\treturn (pthread_detach(thr) == 0) ? thrd_success : thrd_error;\n}\n\n\nint thrd_join(thrd_t thr, int *res)\n{\n\tvoid *code;\n\tint err;\n\n\terr = pthread_join(thr, &code);\n\n\tif (res)\n\t\t*res = (int)(intptr_t)code;\n\n\treturn err;\n}\n\n\nvoid call_once(once_flag *flag, void (*func)(void))\n{\n\tpthread_once(flag, func);\n}\n\n\nvoid thrd_exit(int res)\n{\n\tpthread_exit((void *)(intptr_t)res);\n}\n\n\nint cnd_init(cnd_t *cnd)\n{\n\tif (!cnd)\n\t\treturn thrd_error;\n\n\treturn (pthread_cond_init(cnd, NULL) == 0) ? thrd_success : thrd_error;\n}\n\n\nint cnd_signal(cnd_t *cnd)\n{\n\tif (!cnd)\n\t\treturn thrd_error;\n\n\treturn (pthread_cond_signal(cnd) == 0) ? thrd_success : thrd_error;\n}\n\n\nint cnd_broadcast(cnd_t *cnd)\n{\n\tif (!cnd)\n\t\treturn thrd_error;\n\n\treturn (pthread_cond_broadcast(cnd) == 0) ? thrd_success : thrd_error;\n}\n\n\nint cnd_wait(cnd_t *cnd, mtx_t *mtx)\n{\n\tif (!cnd || !mtx)\n\t\treturn thrd_error;\n\n\treturn (pthread_cond_wait(cnd, mtx) == 0) ? thrd_success : thrd_error;\n}\n\n\nint cnd_timedwait(cnd_t *cnd, mtx_t *mtx, const struct timespec *abstime)\n{\n\tif (!cnd || !mtx || !abstime)\n\t\treturn thrd_error;\n\n\tint ret = pthread_cond_timedwait(cnd, mtx, abstime);\n\tif (ret == ETIMEDOUT)\n\t\treturn thrd_timedout;\n\n\treturn (ret == 0) ? thrd_success : thrd_error;\n}\n\n\nvoid cnd_destroy(cnd_t *cnd)\n{\n\tif (!cnd)\n\t\treturn;\n\n\tpthread_cond_destroy(cnd);\n}\n\n\nint mtx_init(mtx_t *mtx, int type)\n{\n\tpthread_mutexattr_t attr;\n\n\tif (!mtx)\n\t\treturn thrd_error;\n\n\tif ((type & mtx_recursive) == 0) {\n\t\tpthread_mutex_init(mtx, NULL);\n\t\treturn thrd_success;\n\t}\n\n\tpthread_mutexattr_init(&attr);\n\tpthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);\n\tpthread_mutex_init(mtx, &attr);\n\tpthread_mutexattr_destroy(&attr);\n\n\treturn thrd_success;\n}\n\n\nint mtx_lock(mtx_t *mtx)\n{\n\tif (!mtx)\n\t\treturn thrd_error;\n\n\treturn (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error;\n}\n\n\nint mtx_trylock(mtx_t *mtx)\n{\n\tif (!mtx)\n\t\treturn thrd_error;\n\n\treturn (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;\n}\n\n\nint mtx_unlock(mtx_t *mtx)\n{\n\tif (!mtx)\n\t\treturn thrd_error;\n\n\treturn (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error;\n}\n\n\nvoid mtx_destroy(mtx_t *mtx)\n{\n\tif (!mtx)\n\t\treturn;\n\n\tpthread_mutex_destroy(mtx);\n}\n\n\nint tss_create(tss_t *key, tss_dtor_t destructor)\n{\n\tif (!key)\n\t\treturn thrd_error;\n\n\treturn (pthread_key_create(key, destructor) == 0) ? thrd_success\n\t\t\t\t\t\t\t  : thrd_error;\n}\n\n\nvoid *tss_get(tss_t key)\n{\n\treturn pthread_getspecific(key);\n}\n\n\nint tss_set(tss_t key, void *val)\n{\n\treturn (pthread_setspecific(key, val) == 0) ? thrd_success\n\t\t\t\t\t\t    : thrd_error;\n}\n\n\nvoid tss_delete(tss_t key)\n{\n\tpthread_key_delete(key);\n}\n"
  },
  {
    "path": "src/thread/thread.c",
    "content": "#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_thread.h>\n#include <re_trace.h>\n#ifdef HAVE_PRCTL\n#include <sys/prctl.h>\n#endif\n#ifdef WIN32\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\n#include <processthreadsapi.h>\n#endif\n#ifdef HAVE_PTHREAD\n#include <pthread.h>\n#ifdef OPENBSD\n#include <pthread_np.h>\n#endif\n#endif\n\n\nstruct thread {\n\tthrd_t *thr;\n\tconst char *name;\n\tthrd_start_t func;\n\tvoid *arg;\n};\n\n\nstatic void mutex_destructor(void *data)\n{\n\tmtx_t *mtx = data;\n\n\tmtx_destroy(mtx);\n}\n\n\nstatic int _mutex_alloc(mtx_t **mtx, int type)\n{\n\tmtx_t *m;\n\tint err;\n\n\tif (!mtx)\n\t\treturn EINVAL;\n\n\tm = mem_alloc(sizeof(mtx_t), NULL);\n\tif (!m)\n\t\treturn ENOMEM;\n\n\terr = mtx_init(m, type) != thrd_success;\n\tif (err) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tmem_destructor(m, mutex_destructor);\n\n\t*mtx = m;\n\nout:\n\tif (err)\n\t\tmem_deref(m);\n\n\treturn err;\n}\n\n\nint mutex_alloc(mtx_t **mtx)\n{\n\treturn _mutex_alloc(mtx, mtx_plain);\n}\n\n\nint mutex_alloc_tp(mtx_t **mtx, int type)\n{\n\treturn _mutex_alloc(mtx, type);\n}\n\n\nstatic int handler(void *p)\n{\n\tstruct thread th = *(struct thread *)p;\n\n\tmem_deref(p);\n\n#ifdef HAVE_PRCTL\n\t(void)prctl(PR_SET_NAME, th.name);\n#elif defined(WIN32)\n\t/* Not implemented */\n#elif defined(DARWIN)\n\t(void)pthread_setname_np(th.name);\n#elif defined(HAVE_PTHREAD)\n#if defined(OPENBSD)\n\t(void)pthread_set_name_np(*th.thr, th.name);\n#elif defined(__NetBSD__)\n\t(void)pthread_setname_np(*th.thr, \"%s\", th.name);\n#else\n\t(void)pthread_setname_np(*th.thr, th.name);\n#endif\n#endif\n\tRE_TRACE_THREAD_NAME(th.name);\n\n\treturn th.func(th.arg);\n}\n\n\nint thread_create_name(thrd_t *thr, const char *name, thrd_start_t func,\n\t\t       void *arg)\n{\n\tstruct thread *th;\n\tint ret;\n\n\tif (!thr || !func)\n\t\treturn EINVAL;\n\n\tth = mem_alloc(sizeof(struct thread), NULL);\n\tif (!th)\n\t\treturn ENOMEM;\n\n\tth->thr = thr;\n\tth->name = name;\n\tth->func = func;\n\tth->arg\t = arg;\n\n\tret = thrd_create(thr, handler, th);\n\tif (ret == thrd_success)\n\t\treturn 0;\n\n\tmem_deref(th);\n\n\tif (ret == thrd_nomem)\n\t\treturn ENOMEM;\n\n\treturn EAGAIN;\n}\n"
  },
  {
    "path": "src/thread/win32.c",
    "content": "/**\n * @file posix.c  WIN32 thread implementation\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n\n#include <time.h>\n#include <process.h>\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_thread.h>\n\n\n#define DEBUG_MODULE \"thread\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n#define TSS_DESTRUCTOR_MAX 64\n\nstatic struct tss_dtor_entry {\n\ttss_t key;\n\ttss_dtor_t dtor;\n} tss_dtor_tbl[TSS_DESTRUCTOR_MAX];\n\n\nstruct thread {\n\tthrd_start_t func;\n\tvoid *arg;\n};\n\n\nstatic int dtor_register(tss_t key, tss_dtor_t dtor)\n{\n\tint i;\n\n\tfor (i = 0; i < TSS_DESTRUCTOR_MAX; i++) {\n\t\tif (!tss_dtor_tbl[i].dtor)\n\t\t\tbreak;\n\t}\n\n\tif (i >= TSS_DESTRUCTOR_MAX) {\n\t\tDEBUG_WARNING(\"thread: max tss destructors reached\\n\");\n\t\treturn ENOMEM;\n\t}\n\n\ttss_dtor_tbl[i].key  = key;\n\ttss_dtor_tbl[i].dtor = dtor;\n\n\treturn 0;\n}\n\n\nstatic void tss_dtor_destruct(void)\n{\n\tvoid *val;\n\n\tfor (int i = 0; i < TSS_DESTRUCTOR_MAX; i++) {\n\t\tif (!tss_dtor_tbl[i].dtor)\n\t\t\tcontinue;\n\t\tval = tss_get(tss_dtor_tbl[i].key);\n\t\tif (val) {\n\t\t\ttss_dtor_tbl[i].dtor(val);\n\t\t\ttss_dtor_tbl[i].dtor = NULL;\n\t\t}\n\t}\n}\n\n\nstatic unsigned __stdcall thrd_handler(void *p)\n{\n\tstruct thread th = *(struct thread *)p;\n\tint err;\n\n\tmem_deref(p);\n\terr = th.func(th.arg);\n\n\ttss_dtor_destruct();\n\n\treturn err;\n}\n\n\nint thrd_create(thrd_t *thr, thrd_start_t func, void *arg)\n{\n\tstruct thread *th;\n\tint err = thrd_success;\n\tuintptr_t handle;\n\n\tif (!thr || !func)\n\t\treturn thrd_error;\n\n\tth = mem_alloc(sizeof(struct thread), NULL);\n\tif (!th)\n\t\treturn thrd_nomem;\n\n\tth->func = func;\n\tth->arg\t = arg;\n\n\thandle = _beginthreadex(NULL, 0, thrd_handler, th, 0, NULL);\n\tif (handle == 0) {\n\t\tif (errno == EAGAIN || errno == EACCES) {\n\t\t\terr = thrd_nomem;\n\t\t\tgoto out;\n\t\t}\n\t\terr = thrd_error;\n\t\tgoto out;\n\t}\n\n\tthr->hdl = (HANDLE)handle;\n\tthr->id\t = GetThreadId((HANDLE)handle);\nout:\n\tif (err)\n\t\tmem_deref(th);\n\n\treturn err;\n}\n\n\nint thrd_equal(thrd_t lhs, thrd_t rhs)\n{\n\treturn lhs.id == rhs.id;\n}\n\n\nthrd_t thrd_current(void)\n{\n\t/* GetCurrentThread() returns only a pseudo handle and can not used\n\t * within other threads */\n\tthrd_t t = {.hdl = GetCurrentThread(), .id = GetCurrentThreadId()};\n\treturn t;\n}\n\n\nint thrd_detach(thrd_t thr)\n{\n\tCloseHandle(thr.hdl);\n\treturn thrd_success;\n}\n\n\nint thrd_join(thrd_t thr, int *res)\n{\n\tDWORD w, code;\n\n\tw = WaitForSingleObject(thr.hdl, INFINITE);\n\tif (w != WAIT_OBJECT_0)\n\t\treturn thrd_error;\n\n\tif (res) {\n\t\tif (!GetExitCodeThread(thr.hdl, &code)) {\n\t\t\tCloseHandle(thr.hdl);\n\t\t\treturn thrd_error;\n\t\t}\n\t\t*res = (int)code;\n\t}\n\n\tCloseHandle(thr.hdl);\n\treturn thrd_success;\n}\n\n\nstruct impl_call_once_param {\n\tvoid (*func)(void);\n};\nstatic BOOL CALLBACK call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter,\n\t\t\t\t\tPVOID *Context)\n{\n\tstruct impl_call_once_param *param = Parameter;\n\t(void)InitOnce;\n\t(void)Context;\n\n\tparam->func();\n\n\treturn true;\n}\n\n\nvoid call_once(once_flag *flag, void (*func)(void))\n{\n\tstruct impl_call_once_param param;\n\tparam.func = func;\n\tInitOnceExecuteOnce(flag, call_once_callback, (PVOID)&param, NULL);\n}\n\n\nvoid thrd_exit(int res)\n{\n\t_endthreadex((unsigned)res);\n}\n\n\nint cnd_init(cnd_t *cnd)\n{\n\tif (!cnd)\n\t\treturn thrd_error;\n\n\tInitializeConditionVariable(cnd);\n\n\treturn thrd_success;\n}\n\n\nint cnd_signal(cnd_t *cnd)\n{\n\tif (!cnd)\n\t\treturn thrd_error;\n\n\tWakeConditionVariable(cnd);\n\n\treturn thrd_success;\n}\n\n\nint cnd_broadcast(cnd_t *cnd)\n{\n\tif (!cnd)\n\t\treturn thrd_error;\n\n\tWakeAllConditionVariable(cnd);\n\n\treturn thrd_success;\n}\n\n\nint cnd_wait(cnd_t *cnd, mtx_t *mtx)\n{\n\tif (!cnd || !mtx)\n\t\treturn thrd_error;\n\n\tSleepConditionVariableCS(cnd, mtx, INFINITE);\n\n\treturn thrd_success;\n}\n\n\nstatic uint32_t abs2ms(const struct timespec *ts)\n{\n\tstruct timespec now;\n\tif (!ts)\n\t\treturn 0;\n\n\tuint64_t abs_ms = (ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L);\n\n\ttmr_timespec_get(&now, 0);\n\tuint64_t now_ms = (now.tv_sec * 1000U) + (now.tv_nsec / 1000000L);\n\n\treturn (abs_ms > now_ms) ? (uint32_t)(abs_ms - now_ms) : 0;\n}\n\n\nint cnd_timedwait(cnd_t *cnd, mtx_t *mtx, const struct timespec *abstime)\n{\n\tif (!cnd || !mtx || !abstime)\n\t\treturn thrd_error;\n\n\tif (SleepConditionVariableCS(cnd, mtx, abs2ms(abstime)))\n\t\treturn thrd_success;\n\n\treturn (GetLastError() == ERROR_TIMEOUT) ? thrd_timedout : thrd_error;\n}\n\n\nvoid cnd_destroy(cnd_t *cnd)\n{\n\tif (!cnd)\n\t\treturn;\n\t/* do nothing */\n}\n\n\nint mtx_init(mtx_t *mtx, int type)\n{\n\t(void)type;\n\n\tif (!mtx)\n\t\treturn thrd_error;\n\n\tInitializeCriticalSection(mtx);\n\n\treturn thrd_success;\n}\n\n\nint mtx_lock(mtx_t *mtx)\n{\n\tif (!mtx)\n\t\treturn thrd_error;\n\n\tEnterCriticalSection(mtx);\n\n\treturn thrd_success;\n}\n\n\nint mtx_trylock(mtx_t *mtx)\n{\n\tif (!mtx)\n\t\treturn thrd_error;\n\n\treturn TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy;\n}\n\n\nint mtx_unlock(mtx_t *mtx)\n{\n\tif (!mtx)\n\t\treturn thrd_error;\n\n\tLeaveCriticalSection(mtx);\n\n\treturn thrd_success;\n}\n\n\nvoid mtx_destroy(mtx_t *mtx)\n{\n\tif (!mtx)\n\t\treturn;\n\n\tDeleteCriticalSection(mtx);\n}\n\n\nint tss_create(tss_t *key, tss_dtor_t destructor)\n{\n\tint err;\n\n\tif (!key)\n\t\treturn thrd_error;\n\n\t*key = TlsAlloc();\n\tif (*key == TLS_OUT_OF_INDEXES)\n\t\treturn thrd_error;\n\n\tif (!destructor)\n\t\treturn thrd_success;\n\n\terr = dtor_register(*key, destructor);\n\tif (err) {\n\t\tTlsFree(*key);\n\t\treturn thrd_error;\n\t}\n\n\treturn thrd_success;\n}\n\n\nvoid *tss_get(tss_t key)\n{\n\treturn TlsGetValue(key);\n}\n\n\nint tss_set(tss_t key, void *val)\n{\n\treturn TlsSetValue(key, val) ? thrd_success : thrd_error;\n}\n\n\nvoid tss_delete(tss_t key)\n{\n\tTlsFree(key);\n}\n"
  },
  {
    "path": "src/tls/openssl/sni.c",
    "content": "/**\n * @file openssl/sni.c Server Name Indication API\n *\n * Copyright (C) 2022 Commend.com - c.spielberger@commend.com\n */\n#include <openssl/tls1.h>\n#include <string.h>\n#include <openssl/err.h>\n#include <openssl/ssl.h>\n#include <openssl/x509.h>\n#include <openssl/x509v3.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_srtp.h>\n#include <re_tls.h>\n#include \"tls.h\"\n\n\n#define DEBUG_MODULE \"tls/sni\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nstruct tls_conn;\n\n\nstatic bool x509_match_common_name(X509 *x509, const char *sni)\n{\n\tconst X509_NAME *nm = X509_get_subject_name(x509);\n\n\tint lastpos = -1;\n\tfor (;;) {\n\t\tlastpos = X509_NAME_get_index_by_NID(nm, NID_commonName,\n\t\t\t\t\t\t     lastpos);\n\t\tif (lastpos == -1)\n\t\t\tbreak;\n\n\t\tconst X509_NAME_ENTRY *e = X509_NAME_get_entry(nm, lastpos);\n\t\tconst ASN1_STRING *astr\t = X509_NAME_ENTRY_get_data(e);\n\t\tif (!astr)\n\t\t\tcontinue;\n\n\t\tstruct pl cn = {(char *)ASN1_STRING_get0_data(astr),\n\t\t\t\tASN1_STRING_length(astr)};\n\n\t\tif (!pl_strcasecmp(&cn, sni))\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nstatic bool x509_match_alt_name(X509 *x509, const char *sni)\n{\n\tGENERAL_NAMES *gs = NULL;\n\tASN1_STRING *astr = NULL;\n\tASN1_OCTET_STRING *octet = NULL;\n\tbool match = false;\n\n\tgs = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL);\n\tif (!gs)\n\t\treturn false;\n\n\tfor (int i = 0; i < sk_GENERAL_NAME_num(gs); i++) {\n\t\tGENERAL_NAME *g = sk_GENERAL_NAME_value(gs, i);\n\n\t\tif (g->type == GEN_DNS) {\n\t\t\tastr = ASN1_IA5STRING_new();\n\t\t\tif (!astr)\n\t\t\t\tgoto out;\n\n\t\t\tif (!ASN1_STRING_set(astr, sni, -1))\n\t\t\t\tgoto out;\n\n\t\t\tif (!ASN1_STRING_cmp(astr, g->d.dNSName)) {\n\t\t\t\tmatch = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse if (g->type == GEN_IPADD) {\n\t\t\toctet = a2i_IPADDRESS(sni);\n\t\t\tif (!octet)\n\t\t\t\tbreak;\n\t\t\tif (!ASN1_OCTET_STRING_cmp(octet, g->d.iPAddress)) {\n\t\t\t\tmatch = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tASN1_OCTET_STRING_free(octet);\n\t\t}\n\t}\n\nout:\n\tGENERAL_NAMES_free(gs);\n\tASN1_IA5STRING_free(astr);\n\tASN1_OCTET_STRING_free(octet);\n\treturn match;\n}\n\n\n/**\n * Finds a TLS certificate that matches a given Server Name Indication (SNI)\n *\n * @param tls TLS Context\n * @param sni Server Name Indication\n *\n * @return TLS certificate or NULL if not found\n */\nstruct tls_cert *tls_cert_for_sni(const struct tls *tls, const char *sni)\n{\n\tstruct tls_cert *tls_cert = NULL;\n\tstruct le *le;\n\tconst struct list *certs = tls_certs(tls);\n\n\tif (!list_head(certs))\n\t\treturn NULL;\n\n\tif (!str_isset(sni))\n\t\treturn list_head(certs)->data;\n\n\tLIST_FOREACH(certs, le) {\n\t\tX509 *x509;\n\n\t\ttls_cert = le->data;\n\t\tx509 = tls_cert_x509(tls_cert);\n\t\tif (!x509) {\n\t\t\ttls_cert = NULL;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (x509_match_common_name(x509, sni))\n\t\t\tbreak;\n\n\t\tif (x509_match_alt_name(x509, sni))\n\t\t\tbreak;\n\n\t\ttls_cert = NULL;\n\t}\n\n\tERR_clear_error();\n\treturn tls_cert;\n}\n\n\nstatic int ssl_servername_handler(SSL *ssl, int *al, void *arg)\n{\n\tstruct tls *tls\t= arg;\n\tstruct tls_cert *uc = NULL;\n\tconst char *sni;\n\n\tif (!SSL_is_server(ssl))\n\t\treturn SSL_TLSEXT_ERR_OK;\n\n\tsni = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);\n\tif (!str_isset(sni))\n\t\treturn SSL_TLSEXT_ERR_OK;\n\n\t/* find and apply matching certificate */\n\tuc = tls_cert_for_sni(tls, sni);\n\tif (!uc) {\n\t\t*al = SSL_AD_UNRECOGNIZED_NAME;\n\t\treturn SSL_TLSEXT_ERR_ALERT_FATAL;\n\t}\n\n\tDEBUG_INFO(\"found cert for sni %s\\n\", sni);\n\tif (SSL_set_SSL_CTX(ssl, tls_cert_ctx(uc)) == NULL) {\n\t\t*al = SSL_AD_INTERNAL_ERROR;\n\t\treturn SSL_TLSEXT_ERR_ALERT_FATAL;\n\t}\n\n\treturn SSL_TLSEXT_ERR_OK;\n}\n\n\n/**\n * Enables SNI handling on the given TLS context for incoming TLS connections\n *\n * @param tls TLS Context\n */\nvoid tls_enable_sni(struct tls *tls)\n{\n\tSSL_CTX_set_tlsext_servername_callback(tls_ssl_ctx(tls),\n\t\t\t\t\t       ssl_servername_handler);\n\tSSL_CTX_set_tlsext_servername_arg(tls_ssl_ctx(tls), tls);\n}\n"
  },
  {
    "path": "src/tls/openssl/tls.c",
    "content": "/**\n * @file openssl/tls.c TLS backend using OpenSSL\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <openssl/bn.h>\n#include <openssl/evp.h>\n#include <openssl/ec.h>\n#include <openssl/pem.h>\n#include <openssl/x509.h>\n#include <openssl/x509v3.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_main.h>\n#include <re_sa.h>\n#include <re_srtp.h>\n#include <re_sys.h>\n#include <re_tcp.h>\n#include <re_tls.h>\n#include <re_thread.h>\n#include \"tls.h\"\n\n\n#define DEBUG_MODULE \"tls\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n#include <re_list.h>\n#include <re_hash.h>\n\n\nstruct session_reuse {\n\tbool enabled;\n\tstruct hash *ht_sessions;\n};\n\nstruct tls {\n\tSSL_CTX *ctx;\n\tX509 *cert;\n\tchar *pass;          /**< password for private key             */\n\tbool verify_server;  /**< Enable SIP TLS server verification   */\n\tbool verify_client;  /**< Enable SIP TLS client verification   */\n\tstruct session_reuse reuse;\n\tstruct list certs;   /**< Certificates for SNI selection       */\n};\n\n/**\n * A TLS certificate with private key, certificate chain and a host name that\n * is passed to OpenSSL for the host name check\n *\n */\nstruct tls_cert {\n\tstruct le le;\n\tSSL_CTX *ctx;\n\tchar *host;\n};\n\n\n#if defined(TRACE_SSL)\n/**\n * Global flag if key material must be appended to file\n */\nstatic bool fresh_keylog_file = true;\n\n\n/**\n * SSL Key logger callback function\n *\n * @param ssl  OpenSSL SSL object\n * @param line Key material in NSS format\n */\nstatic void tls_keylogger_cb(const SSL *ssl,\n\tconst char *line)\n{\n\tFILE *f = NULL;\n\n\t(void) ssl;\n\n\tif (fresh_keylog_file) {\n\t\tf = fopen(TRACE_SSL, \"w\");\n\t\tfresh_keylog_file = false;\n\t}\n\telse {\n\t\tf = fopen(TRACE_SSL, \"a\");\n\t}\n\n\tif (!f)\n\t\treturn;\n\n\t(void)re_fprintf(f, \"%s\\n\", line);\n\n\tif (f)\n\t\t(void)fclose(f);\n\n}\n#endif\n\n\n/* NOTE: shadow struct defined in tls_*.c */\nstruct tls_conn {\n\tSSL *ssl;\n\tstruct tls *tls;\n\tstruct tls_conn_d cd;\n};\n\n\nstatic void destructor(void *data)\n{\n\tstruct tls *tls = data;\n\n\tif (tls->ctx) {\n\t\tSSL_CTX_sess_set_new_cb(tls->ctx, NULL);\n\t\tSSL_CTX_sess_set_remove_cb(tls->ctx, NULL);\n\t\tSSL_CTX_free(tls->ctx);\n\t}\n\n\tif (tls->cert)\n\t\tX509_free(tls->cert);\n\n\thash_flush(tls->reuse.ht_sessions);\n\tmem_deref(tls->reuse.ht_sessions);\n\tmem_deref(tls->pass);\n\tlist_flush(&tls->certs);\n}\n\n\n/*The password code is not thread safe*/\nstatic int password_cb(char *buf, int size, int rwflag, void *userdata)\n{\n\tstruct tls *tls = userdata;\n\n\t(void)rwflag;\n\n\tDEBUG_NOTICE(\"password callback\\n\");\n\n\tif (size < (int)strlen(tls->pass)+1)\n\t\treturn 0;\n\n\tstrncpy(buf, tls->pass, size);\n\n\treturn (int)strlen(tls->pass);\n}\n\n\n/**\n * OpenSSL verify handler for debugging purposes. Prints only warnings in the\n * default build\n *\n * @param ok  Verification result of OpenSSL\n * @param ctx OpenSSL X509 store context set by OpenSSL\n *\n * @return passes parameter ok unchanged\n */\nstatic int tls_verify_handler(int ok, X509_STORE_CTX *ctx)\n{\n\tint err, depth;\n\n\terr = X509_STORE_CTX_get_error(ctx);\n\n#if (DEBUG_LEVEL >= 6)\n\tchar    buf[128];\n\tX509   *err_cert;\n\n\terr_cert = X509_STORE_CTX_get_current_cert(ctx);\n\n\tX509_NAME_oneline(X509_get_subject_name(err_cert), buf, 128);\n\tDEBUG_INFO(\"%s: subject_name = %s\\n\", __func__, buf);\n\n\tX509_NAME_oneline(X509_get_issuer_name(err_cert), buf, 128);\n\tDEBUG_INFO(\"%s: issuer_name  = %s\\n\", __func__, buf);\n#endif\n\n\tif (err) {\n\t\tdepth = X509_STORE_CTX_get_error_depth(ctx);\n\t\tDEBUG_WARNING(\"%s: err          = %d\\n\", __func__, err);\n\t\tDEBUG_WARNING(\"%s: error_string = %s\\n\", __func__,\n\t\t\t\tX509_verify_cert_error_string(err));\n\t\tDEBUG_WARNING(\"%s: depth        = %d\\n\", __func__, depth);\n\t}\n\n#if (DEBUG_LEVEL >= 6)\n\tDEBUG_INFO(\"tls verify ok = %d\\n\", ok);\n#endif\n\n\treturn ok;\n}\n\n\nstatic int tls_verify_idx = -1;\nstatic once_flag oflag = ONCE_FLAG_INIT;\n\nstatic void tls_init_verify_idx(void)\n{\n\tif (tls_verify_idx > -1)\n\t\treturn;\n\n\ttls_verify_idx = SSL_get_ex_new_index(0, \"tls verify ud\",\n\t\tNULL, NULL, NULL);\n}\n\n\nstatic int tls_ctx_alloc(SSL_CTX **ctxp, enum tls_method method,\n\t\t\t const char *certf, const char *pwd, struct tls *tls)\n{\n\tint err = 0;\n\tint r;\n\tSSL_CTX *ctx;\n\tint min_proto = 0;\n\n\tswitch (method) {\n\n\tcase TLS_METHOD_TLS:\n\tcase TLS_METHOD_SSLV23:\n\t\tctx\t  = SSL_CTX_new(TLS_method());\n\t\tmin_proto = TLS1_2_VERSION;\n\t\tbreak;\n\n\tcase TLS_METHOD_DTLS:\n\tcase TLS_METHOD_DTLSV1:\n\tcase TLS_METHOD_DTLSV1_2:\n\t\tctx = SSL_CTX_new(DTLS_method());\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"tls method %d not supported\\n\", method);\n\t\treturn ENOSYS;\n\t}\n\n\tif (!ctx) {\n\t\tERR_clear_error();\n\t\treturn ENOMEM;\n\t}\n\n\tSSL_CTX_set_min_proto_version(ctx, min_proto);\n\n\tif (!certf)\n\t\tgoto out;\n\n\t/* Load our keys and certificates */\n\tif (pwd && tls) {\n\t\terr = str_dup(&tls->pass, pwd);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tSSL_CTX_set_default_passwd_cb(ctx, password_cb);\n\t\tSSL_CTX_set_default_passwd_cb_userdata(ctx, tls);\n\t}\n\n\tr = SSL_CTX_use_certificate_chain_file(ctx, certf);\n\tif (r <= 0) {\n\t\tDEBUG_WARNING(\"Can't read certificate file: %s (%d)\\n\", certf,\n\t\t\t      r);\n\t\tERR_clear_error();\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\tr = SSL_CTX_use_PrivateKey_file(ctx, certf, SSL_FILETYPE_PEM);\n\tif (r <= 0) {\n\t\tDEBUG_WARNING(\"Can't read key file: %s (%d)\\n\", certf, r);\n\t\tERR_clear_error();\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\nout:\n\tif (err)\n\t\tSSL_CTX_free(ctx);\n\telse\n\t\t*ctxp = ctx;\n\n\treturn err;\n}\n\n\n/**\n * Allocate a new TLS context\n *\n * @param tlsp    Pointer to allocated TLS context\n * @param method  TLS method\n * @param keyfile Optional private key file\n * @param pwd     Optional password\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile,\n\t      const char *pwd)\n{\n\tstruct tls *tls;\n\tint err;\n\n\tif (!tlsp)\n\t\treturn EINVAL;\n\n\ttls = mem_zalloc(sizeof(*tls), destructor);\n\tif (!tls)\n\t\treturn ENOMEM;\n\n\terr = tls_ctx_alloc(&tls->ctx, method, keyfile, pwd, tls);\n\tif (err)\n\t\tgoto out;\n\n\ttls->verify_server = true;\n\n#if defined(TRACE_SSL)\n\tSSL_CTX_set_keylog_callback(tls->ctx, tls_keylogger_cb);\n#endif\n\n\terr = hash_alloc(&tls->reuse.ht_sessions, 256);\n\tif (err)\n\t\tgoto out;\n\n\tcall_once(&oflag, tls_init_verify_idx);\n\n\terr = 0;\n out:\n\tif (err)\n\t\tmem_deref(tls);\n\telse\n\t\t*tlsp = tls;\n\n\treturn err;\n}\n\n\n/**\n * Set default locations for trusted CA certificates\n *\n * @param tls    TLS Context\n * @param cafile PEM file with CA certificates\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_add_ca(struct tls *tls, const char *cafile)\n{\n\treturn tls_add_cafile_path(tls, cafile, NULL);\n}\n\n\n/**\n * Set default file and path for trusted CA certificates\n *\n * @param tls    TLS Context\n * @param cafile PEM file with CA certificate(s)\n * @param capath Path containing CA certificates files\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_add_cafile_path(struct tls *tls, const char *cafile,\n\tconst char *capath)\n{\n\tif (!tls || (!cafile && !capath) || !tls->ctx)\n\t\treturn EINVAL;\n\n\tif (capath && !fs_isdir(capath)) {\n\t\treturn ENOTDIR;\n\t}\n\n\t/* Load the CAs we trust */\n\tif (!(SSL_CTX_load_verify_locations(tls->ctx, cafile, capath))) {\n\n\t\tERR_clear_error();\n\t\treturn ENOENT;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Add trusted CA certificates given as string\n *\n * @param tls    TLS Context\n * @param capem  Trusted CA as null-terminated string given in PEM format\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_add_capem(const struct tls *tls, const char *capem)\n{\n\tX509_STORE *store;\n\tX509 *x509;\n\tBIO *bio;\n\tint ok;\n\tint err = 0;\n\n\tif (!tls || !capem || !tls->ctx)\n\t\treturn EINVAL;\n\n\tstore = SSL_CTX_get_cert_store(tls->ctx);\n\tif (!store)\n\t\treturn EINVAL;\n\n\tbio  = BIO_new_mem_buf((char *)capem, (int)strlen(capem));\n\tif (!bio)\n\t\treturn EINVAL;\n\n\tx509 = PEM_read_bio_X509(bio, NULL, 0, NULL);\n\tif (!x509) {\n\t\terr = EINVAL;\n\t\tDEBUG_WARNING(\"Could not read certificate capem\\n\");\n\t\tgoto out;\n\t}\n\n\tok = X509_STORE_add_cert(store, x509);\n\tif (!ok) {\n\t\terr = EINVAL;\n\t\tDEBUG_WARNING(\"Could not add certificate capem\\n\");\n\t}\n\nout:\n\tX509_free(x509);\n\tBIO_free(bio);\n\n\treturn err;\n}\n\n\n/**\n * Add trusted CRL certificates given as string\n *\n * @param tls  TLS Context\n * @param pem  Trusted CRL as null-terminated string given in PEM format\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_add_crlpem(const struct tls *tls, const char *pem)\n{\n\tX509_STORE *store;\n\tX509_CRL *crl;\n\tBIO *bio;\n\tint ok;\n\tint err = 0;\n\n\tif (!tls || !pem || !tls->ctx)\n\t\treturn EINVAL;\n\n\tstore = SSL_CTX_get_cert_store(tls->ctx);\n\tif (!store)\n\t\treturn EINVAL;\n\n\tbio  = BIO_new_mem_buf(pem, (int)strlen(pem));\n\tif (!bio)\n\t\treturn EINVAL;\n\n\tcrl = PEM_read_bio_X509_CRL(bio, NULL, 0, NULL);\n\tif (!crl) {\n\t\terr = EINVAL;\n\t\tDEBUG_WARNING(\"Could not read certificate crlpem\\n\");\n\t\tgoto out;\n\t}\n\n\tok = X509_STORE_add_crl(store, crl);\n\tif (!ok) {\n\t\terr = EINVAL;\n\t\tDEBUG_WARNING(\"Could not add certificate crlpem\\n\");\n\t}\n\nout:\n\tX509_CRL_free(crl);\n\tBIO_free(bio);\n\n\treturn err;\n}\n\n\n/**\n * Set SSL verification of the certificate purpose\n *\n * @param tls     TLS Context\n * @param purpose Certificate purpose as string\n *\n * @return int    0 if success, errorcode otherwise\n */\nint tls_set_verify_purpose(struct tls *tls, const char *purpose)\n{\n\tint err;\n\tint i;\n\tconst X509_PURPOSE *xptmp;\n\n\tif (!tls || !purpose)\n\t\treturn EINVAL;\n\n\ti = X509_PURPOSE_get_by_sname(purpose);\n\tif (i < 0)\n\t\treturn EINVAL;\n\n\t/* purpose index -> purpose object */\n\t/* purpose object -> purpose value */\n\txptmp = X509_PURPOSE_get0(i);\n\ti = X509_PURPOSE_get_id(xptmp);\n\terr = SSL_CTX_set_purpose(tls->ctx, i);\n\n\treturn err == 1 ? 0 : EINVAL;\n}\n\n\nstatic int tls_generate_cert(X509 **pcert, const char *cn)\n{\n\tX509 *cert = NULL;\n\tX509_NAME *subj = NULL;\n\tint e = 0;\n\n\tif (!pcert || !cn)\n\t\tgoto err;\n\n\tcert = X509_new();\n\tif (!cert)\n\t\tgoto err;\n\n\tif (!X509_set_version(cert, 2))\n\t\tgoto err;\n\n\tif (!ASN1_INTEGER_set(X509_get_serialNumber(cert), rand_u32()))\n\t\tgoto err;\n\n\tsubj = X509_NAME_new();\n\tif (!subj)\n\t\tgoto err;\n\n\tif (!X509_NAME_add_entry_by_txt(subj, \"CN\", MBSTRING_ASC,\n\t\t\t\t\t(unsigned char *)cn,\n\t\t\t\t\t(int)strlen(cn), -1, 0))\n\t\tgoto err;\n\n\tif (!X509_set_issuer_name(cert, subj) ||\n\t    !X509_set_subject_name(cert, subj))\n\t\tgoto err;\n\n\tif (!X509_gmtime_adj(X509_getm_notBefore(cert), -3600*24*365) ||\n\t    !X509_gmtime_adj(X509_getm_notAfter(cert),   3600*24*365*10))\n\t\tgoto err;\n\n\tgoto out;\n\n err:\n\te = 1;\n\n out:\n\tif (e)\n\t\tX509_free(cert);\n\telse\n\t\t*pcert = cert;\n\n\tX509_NAME_free(subj);\n\treturn e;\n}\n\n\n/**\n * Create a selfsigned X509 certificate using EC\n *\n * @param tls      TLS Context\n * @param cn       Common Name\n * @param curve_n  Known EC curve name\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_selfsigned_ec(struct tls *tls, const char *cn, const char *curve_n)\n{\n#ifndef OPENSSL_VERSION_MAJOR\n\tEC_KEY *eckey = NULL;\n\tint eccgrp;\n#endif\n\tEVP_PKEY *key = NULL;\n\tX509 *cert = NULL;\n\tint r, err = ENOMEM;\n\n\tif (!tls || !cn)\n\t\treturn EINVAL;\n\n#if OPENSSL_VERSION_MAJOR >= 3\n\tkey = EVP_EC_gen(curve_n);\n\tif (!key) {\n\t\terr = ENOTSUP;\n\t\tgoto out;\n\t}\n#else\n\teccgrp = OBJ_txt2nid(curve_n);\n\tif (eccgrp == NID_undef)\n\t\treturn ENOTSUP;\n\n\n\teckey = EC_KEY_new_by_curve_name(eccgrp);\n\tif (!eckey)\n\t\tgoto out;\n\n\tif (!EC_KEY_generate_key(eckey))\n\t\tgoto out;\n\n\tEC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);\n\n\tkey = EVP_PKEY_new();\n\tif (!key)\n\t\tgoto out;\n\n\tif (!EVP_PKEY_set1_EC_KEY(key, eckey))\n\t\tgoto out;\n#endif /* OPENSSL_VERSION_MAJOR */\n\n\tif (tls_generate_cert(&cert, cn))\n\t\tgoto out;\n\n\tif (!X509_set_pubkey(cert, key))\n\t\tgoto out;\n\n\tif (!X509_sign(cert, key, EVP_sha256()))\n\t\tgoto out;\n\n\tr = SSL_CTX_use_certificate(tls->ctx, cert);\n\tif (r != 1)\n\t\tgoto out;\n\n\tr = SSL_CTX_use_PrivateKey(tls->ctx, key);\n\tif (r != 1)\n\t\tgoto out;\n\n\tif (tls->cert)\n\t\tX509_free(tls->cert);\n\n\ttls->cert = cert;\n\tcert = NULL;\n\n\terr = 0;\n\n out:\n#ifndef OPENSSL_VERSION_MAJOR\n\tif (eckey)\n\t\tEC_KEY_free(eckey);\n#endif\n\tif (key)\n\t\tEVP_PKEY_free(key);\n\tif (cert)\n\t\tX509_free(cert);\n\n\n\treturn err;\n}\n\n\n/**\n * Set the certificate and private key on a TLS context\n *\n * @param tls      TLS Context\n * @param cert     Certificate\n * @param pkey     Private key\n * @param up_ref   If true, increment reference count of the certificate if\n *                 successfully set.\n *                 If false, the reference count is not incremented and\n *                 the ownership of the certificate is passed to the TLS\n *                 context.\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_certificate_openssl(struct tls *tls, X509* cert, EVP_PKEY* pkey,\n\t\t\t\tbool up_ref)\n{\n\tint r, err = ENOMEM;\n\n\tif (!tls || !cert || !pkey)\n\t\treturn EINVAL;\n\n\tr = SSL_CTX_use_certificate(tls->ctx, cert);\n\tif (r != 1)\n\t\tgoto out;\n\n\tr = SSL_CTX_use_PrivateKey(tls->ctx, pkey);\n\tif (r != 1) {\n\t\tDEBUG_WARNING(\"set_certificate_openssl: use_PrivateKey\"\n\t\t\t      \" failed\\n\");\n\t\tgoto out;\n\t}\n\n\tif (tls->cert)\n\t\tX509_free(tls->cert);\n\n\ttls->cert = cert;\n\n\tif (up_ref)\n\t\tX509_up_ref(tls->cert);\n\n\terr = 0;\n\nout:\n\tif (err)\n\t\tERR_clear_error();\n\n\treturn err;\n}\n\n/**\n * Set the certificate and private key on a TLS context\n *\n * @param tls      TLS Context\n * @param cert     Certificate in PEM format\n * @param len_cert Length of certificate PEM string\n * @param key      Private key in PEM format, will be read from cert if NULL\n * @param len_key  Length of private key PEM string\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_certificate_pem(struct tls *tls, const char *cert, size_t len_cert,\n\t\t\t    const char *key, size_t len_key)\n{\n\tBIO *bio = NULL, *kbio = NULL;\n\tX509 *x509 = NULL;\n\tEVP_PKEY *pkey = NULL;\n\tint r, err = ENOMEM;\n\n\tif (!tls || !cert || !len_cert || (key && !len_key))\n\t\treturn EINVAL;\n\n\tif (!key) {\n\t\tkey = cert;\n\t\tlen_key = len_cert;\n\t}\n\n\tbio  = BIO_new_mem_buf((char *)cert, (int)len_cert);\n\tkbio = BIO_new_mem_buf((char *)key, (int)len_key);\n\tif (!bio || !kbio)\n\t\tgoto out;\n\n\tx509 = PEM_read_bio_X509(bio, NULL, 0, NULL);\n\tpkey = PEM_read_bio_PrivateKey(kbio, NULL, 0, NULL);\n\tif (!x509 || !pkey)\n\t\tgoto out;\n\n\tr = SSL_CTX_use_certificate(tls->ctx, x509);\n\tif (r != 1)\n\t\tgoto out;\n\n\tr = SSL_CTX_use_PrivateKey(tls->ctx, pkey);\n\tif (r != 1) {\n\t\tDEBUG_WARNING(\"set_certificate_pem: use_PrivateKey failed\\n\");\n\t\tgoto out;\n\t}\n\n\tif (tls->cert)\n\t\tX509_free(tls->cert);\n\n\ttls->cert = x509;\n\tx509 = NULL;\n\n\terr = 0;\n\n out:\n\tif (x509)\n\t\tX509_free(x509);\n\tif (pkey)\n\t\tEVP_PKEY_free(pkey);\n\tif (bio)\n\t\tBIO_free(bio);\n\tif (kbio)\n\t\tBIO_free(kbio);\n\tif (err)\n\t\tERR_clear_error();\n\n\treturn err;\n}\n\n\n/**\n * Set the certificate and private key on a TLS context\n *\n * @param tls TLS Context\n * @param pem Certificate and private key in PEM format\n * @param len Length of PEM string\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_certificate(struct tls *tls, const char *pem, size_t len)\n{\n\treturn tls_set_certificate_pem(tls, pem, len, NULL, 0);\n}\n\n\nstatic int verify_trust_all(int ok, X509_STORE_CTX *ctx)\n{\n\t(void)ok;\n\t(void)ctx;\n\n\treturn 1;    /* We trust the certificate from peer */\n}\n\n\n/**\n * Set TLS server context to request certificate from peer\n * and set trust all certificates of peer.\n *\n * @param tls    TLS Context\n */\nvoid tls_set_verify_client_trust_all(struct tls *tls)\n{\n\tif (!tls)\n\t\treturn;\n\n\tSSL_CTX_set_verify_depth(tls->ctx, 0);\n\tSSL_CTX_set_verify(tls->ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE,\n\t\t\t   verify_trust_all);\n}\n\n\nstatic int tls_verify_handler_ud(int ok, X509_STORE_CTX *ctx)\n{\n\tint ret = ok;\n\tstruct tls_conn_d *d;\n\tSSL *ssl;\n\tint err, depth;\n\n\terr = X509_STORE_CTX_get_error(ctx);\n\n#if (DEBUG_LEVEL >= 6)\n\tchar    buf[128];\n\tX509   *err_cert;\n\n\terr_cert = X509_STORE_CTX_get_current_cert(ctx);\n\n\tX509_NAME_oneline(X509_get_subject_name(err_cert), buf, 128);\n\tDEBUG_INFO(\"%s: subject_name = %s\\n\", __func__, buf);\n\n\tX509_NAME_oneline(X509_get_issuer_name(err_cert), buf, 128);\n\tDEBUG_INFO(\"%s: issuer_name  = %s\\n\", __func__, buf);\n#endif\n\tif (err) {\n\t\tdepth = X509_STORE_CTX_get_error_depth(ctx);\n\t\tDEBUG_WARNING(\"%s: err          = %d\\n\", __func__, err);\n\t\tDEBUG_WARNING(\"%s: error_string = %s\\n\", __func__,\n\t\t\t\tX509_verify_cert_error_string(err));\n\t\tDEBUG_WARNING(\"%s: depth        = %d\\n\", __func__, depth);\n\t}\n\n#if (DEBUG_LEVEL >= 6)\n\tDEBUG_INFO(\"tls_verify_handler ok = %d\\n\", ok);\n#endif\n\n\tssl = X509_STORE_CTX_get_ex_data(ctx,\n\t\tSSL_get_ex_data_X509_STORE_CTX_idx());\n\n\tif (!ssl) {\n\t\tDEBUG_WARNING(\"X509_STORE_CTX_get_ex_data (SSL*) failed\\n\");\n\t\treturn ret;\n\t}\n\n\td = SSL_get_ex_data(ssl, tls_verify_idx);\n\tif (!d) {\n\t\tDEBUG_WARNING(\"SSL_get_app_data (struct tls_conn_d) failed\\n\");\n\t\treturn ret;\n\t}\n\n\tif (d->verifyh)\n\t\tret = d->verifyh(ok, d->arg);\n\n\treturn ret;\n}\n\n\n/**\n * Enable request certificate from peer in TLS server connection\n * Set verify handler.\n *\n * @param tc      TLS connection\n * @param depth   Max depth certificate chain accepted.\n *                A negative depth uses default depth.\n * @param verifyh SSL verify handler. If NULL default verify handler is used.\n * @param arg     Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_verify_client_handler(struct tls_conn *tc, int depth,\n\tint (*verifyh) (int ok, void *arg), void *arg)\n{\n\tint err = 0;\n\tSSL_verify_cb tls_cb = tls_verify_handler_ud;\n\tif (!tc)\n\t\treturn EINVAL;\n\n\tif (!verifyh) {\n\t\ttls_cb = tls_verify_handler;\n\t}\n\telse {\n\t\ttc->cd.verifyh = verifyh;\n\t\ttc->cd.arg = arg;\n\t\tSSL_set_ex_data(tc->ssl, tls_verify_idx, &tc->cd);\n\t}\n\n\tSSL_set_verify_depth(tc->ssl, depth < 0 ?\n\t\tSSL_get_verify_depth(tc->ssl) : depth);\n\tSSL_set_verify(tc->ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE,\n\t\ttls_cb);\n\n\treturn err;\n}\n\n\n/**\n * Set SRTP suites on TLS context\n *\n * @param tls    TLS Context\n * @param suites Secure-RTP Profiles\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_srtp(struct tls *tls, const char *suites)\n{\n\tif (!tls || !suites)\n\t\treturn EINVAL;\n\n\tif (0 != SSL_CTX_set_tlsext_use_srtp(tls->ctx, suites)) {\n\t\tERR_clear_error();\n\t\treturn ENOSYS;\n\t}\n\n\treturn 0;\n}\n\n\nstatic int cert_fingerprint(X509 *cert, enum tls_fingerprint type,\n\t\t\t    uint8_t *md, size_t size)\n{\n\tunsigned int len = (unsigned int)size;\n\tint n;\n\n\tswitch (type) {\n\n\tcase TLS_FINGERPRINT_SHA256:\n\t\tif (size < 32)\n\t\t\treturn EOVERFLOW;\n\n\t\tn = X509_digest(cert, EVP_sha256(), md, &len);\n\t\tbreak;\n\n\tdefault:\n\t\treturn ENOSYS;\n\t}\n\n\tif (n != 1) {\n\t\tERR_clear_error();\n\t\treturn ENOENT;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Get fingerprint of local certificate\n *\n * @param tls  TLS Context\n * @param type Digest type\n * @param md   Buffer for fingerprint digest\n * @param size Buffer size\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_fingerprint(const struct tls *tls, enum tls_fingerprint type,\n\t\t    uint8_t *md, size_t size)\n{\n\tif (!tls || !tls->cert || !md)\n\t\treturn EINVAL;\n\n\treturn cert_fingerprint(tls->cert, type, md, size);\n}\n\n\n/**\n * Get fingerprint of peer certificate of a TLS connection\n *\n * @param tc   TLS Connection\n * @param type Digest type\n * @param md   Buffer for fingerprint digest\n * @param size Buffer size\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_peer_fingerprint(const struct tls_conn *tc, enum tls_fingerprint type,\n\t\t\t uint8_t *md, size_t size)\n{\n\tX509 *cert;\n\tint err;\n\n\tif (!tc || !md)\n\t\treturn EINVAL;\n\n#if OPENSSL_VERSION_MAJOR >= 3\n\tcert = SSL_get1_peer_certificate(tc->ssl);\n#else\n\tcert = SSL_get_peer_certificate(tc->ssl);\n#endif\n\tif (!cert)\n\t\treturn ENOENT;\n\n\terr = cert_fingerprint(cert, type, md, size);\n\n\tX509_free(cert);\n\n\treturn err;\n}\n\n\n/**\n * Get common name of peer certificate of a TLS connection\n *\n * @param tc   TLS Connection\n * @param cn   Returned common name\n * @param size Size of common name\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_peer_common_name(const struct tls_conn *tc, char *cn, size_t size)\n{\n\tX509 *cert;\n\tint n = -1;\n\n\tif (!tc || !cn || !size)\n\t\treturn EINVAL;\n\n#if OPENSSL_VERSION_MAJOR >= 3\n\tcert = SSL_get1_peer_certificate(tc->ssl);\n#else\n\tcert = SSL_get_peer_certificate(tc->ssl);\n#endif\n\tif (!cert)\n\t\treturn ENOENT;\n\n\tconst X509_NAME *name = X509_get_subject_name(cert);\n\n\tint ix = X509_NAME_get_index_by_NID(name, NID_commonName, -1);\n\tif (ix != -1) {\n\t\tconst X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, ix);\n\t\tconst ASN1_STRING *astr = X509_NAME_ENTRY_get_data(entry);\n\n\t\tif (astr) {\n\t\t\tstr_ncpy(cn, (char *)ASN1_STRING_get0_data(astr),\n\t\t\t\t size);\n\t\t\tn = ASN1_STRING_length(astr);\n\t\t}\n\t}\n\n\tX509_free(cert);\n\n\tif (n < 0) {\n\t\tERR_clear_error();\n\t\treturn ENOENT;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Verify peer certificate of a TLS connection\n *\n * @param tc TLS Connection\n *\n * @return 0 if verified, otherwise errorcode\n */\nint tls_peer_verify(const struct tls_conn *tc)\n{\n\tif (!tc)\n\t\treturn EINVAL;\n\n\tif (SSL_get_verify_result(tc->ssl) != X509_V_OK)\n\t\treturn EAUTH;\n\n\treturn 0;\n}\n\n\n/**\n * Get SRTP suite and keying material of a TLS connection\n *\n * @param tc           TLS Connection\n * @param suite        Returned SRTP suite\n * @param cli_key      Client key\n * @param cli_key_size Client key size\n * @param srv_key      Server key\n * @param srv_key_size Server key size\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_srtp_keyinfo(const struct tls_conn *tc, enum srtp_suite *suite,\n\t\t     uint8_t *cli_key, size_t cli_key_size,\n\t\t     uint8_t *srv_key, size_t srv_key_size)\n{\n\tstatic const char *label = \"EXTRACTOR-dtls_srtp\";\n\tsize_t key_size, salt_size, size;\n\tSRTP_PROTECTION_PROFILE *sel;\n\tuint8_t keymat[256], *p;\n\n\tif (!tc || !suite || !cli_key || !srv_key)\n\t\treturn EINVAL;\n\n\tsel = SSL_get_selected_srtp_profile(tc->ssl);\n\tif (!sel)\n\t\treturn ENOENT;\n\n\tswitch (sel->id) {\n\n\tcase SRTP_AES128_CM_SHA1_80:\n\t\t*suite = SRTP_AES_CM_128_HMAC_SHA1_80;\n\t\tkey_size  = 16;\n\t\tsalt_size = 14;\n\t\tbreak;\n\n\tcase SRTP_AES128_CM_SHA1_32:\n\t\t*suite = SRTP_AES_CM_128_HMAC_SHA1_32;\n\t\tkey_size  = 16;\n\t\tsalt_size = 14;\n\t\tbreak;\n\n#ifdef SRTP_AEAD_AES_128_GCM\n\tcase SRTP_AEAD_AES_128_GCM:\n\t\t*suite = SRTP_AES_128_GCM;\n\t\tkey_size  = 16;\n\t\tsalt_size = 12;\n\t\tbreak;\n#endif\n\n#ifdef SRTP_AEAD_AES_256_GCM\n\tcase SRTP_AEAD_AES_256_GCM:\n\t\t*suite = SRTP_AES_256_GCM;\n\t\tkey_size  = 32;\n\t\tsalt_size = 12;\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\treturn ENOSYS;\n\t}\n\n\tsize = key_size + salt_size;\n\n\tif (cli_key_size < size || srv_key_size < size)\n\t\treturn EOVERFLOW;\n\n\tif (sizeof(keymat) < 2*size)\n\t\treturn EOVERFLOW;\n\n\tif (1 != SSL_export_keying_material(tc->ssl, keymat, 2*size, label,\n\t\t\t\t\t    strlen(label), NULL, 0, 0)) {\n\t\tERR_clear_error();\n\t\treturn ENOENT;\n\t}\n\n\tp = keymat;\n\n\tmemcpy(cli_key,            p, key_size);  p += key_size;\n\tmemcpy(srv_key,            p, key_size);  p += key_size;\n\tmemcpy(cli_key + key_size, p, salt_size); p += salt_size;\n\tmemcpy(srv_key + key_size, p, salt_size);\n\n\tmem_secclean(keymat, sizeof(keymat));\n\n\treturn 0;\n}\n\n\n/**\n * Get cipher name of a TLS connection\n *\n * @param tc TLS Connection\n *\n * @return name of cipher actually used.\n */\nconst char *tls_cipher_name(const struct tls_conn *tc)\n{\n\tif (!tc)\n\t\treturn NULL;\n\n\treturn SSL_get_cipher_name(tc->ssl);\n}\n\n\n/**\n * Set the ciphers to use for this TLS context\n *\n * @param tls      TLS Context\n * @param cipherv  Vector of cipher names, in order of priority\n * @param count    Number of cipher names in the vector\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_ciphers(struct tls *tls, const char *cipherv[], size_t count)\n{\n\tstruct mbuf *mb;\n\tint r, err;\n\tsize_t i;\n\n\tif (!tls || !cipherv || !count)\n\t\treturn EINVAL;\n\n\tmb = mbuf_alloc(32 * count);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tfor (i=0; i<count; i++) {\n\n\t\terr = mbuf_printf(mb, \"%s%s\", i>0 ? \":\" : \"\", cipherv[i]);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = mbuf_write_u8(mb, '\\0');\n\tif (err)\n\t\tgoto out;\n\n\tr = SSL_CTX_set_cipher_list(tls->ctx, (char *)mb->buf);\n\tif (r <= 0) {\n\t\tERR_clear_error();\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/**\n * Enable verification of server certificate and hostname (SNI)\n *\n * @param tc   TLS Connection\n * @param host Server hostname\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_verify_server(struct tls_conn *tc, const char *host)\n{\n\tstruct sa sa;\n\n\tif (!tc || !host)\n\t\treturn EINVAL;\n\n\tif (!tc->tls->verify_server)\n\t\treturn 0;\n\n\tif (sa_set_str(&sa, host, 0)) {\n\t\tSSL_set_hostflags(tc->ssl,\n\t\t\t\tX509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);\n\n#if OPENSSL_VERSION_MAJOR >= 4\n\t\tif (!SSL_set1_dnsname(tc->ssl, host)) {\n\t\t\tDEBUG_WARNING(\"SSL_set1_dnsname error\\n\");\n\t\t\tERR_clear_error();\n\t\t\treturn EPROTO;\n\t\t}\n#else\n\t\tif (!SSL_set1_host(tc->ssl, host)) {\n\t\t\tDEBUG_WARNING(\"SSL_set1_host error\\n\");\n\t\t\tERR_clear_error();\n\t\t\treturn EPROTO;\n\t\t}\n#endif\n\n\t\tif (!SSL_set_tlsext_host_name(tc->ssl, host)) {\n\t\t\tDEBUG_WARNING(\"SSL_set_tlsext_host_name error\\n\");\n\t\t\tERR_clear_error();\n\t\t\treturn EPROTO;\n\t\t}\n\t}\n\n\tSSL_set_verify(tc->ssl, SSL_VERIFY_PEER, tls_verify_handler);\n\n\treturn 0;\n}\n\n\n/**\n * Enable verification of client certificate\n *\n * @param tc   TLS Connection\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_verify_client(struct tls_conn *tc)\n{\n\tif (!tc)\n\t\treturn EINVAL;\n\n\tif (!tc->tls->verify_client)\n\t\treturn 0;\n\n\tSSL_set_verify(tc->ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE,\n\t\t       tls_verify_handler);\n\n\treturn 0;\n}\n\n\nstatic int print_error(const char *str, size_t len, void *unused)\n{\n\t(void)unused;\n\tDEBUG_WARNING(\"%b\", str, len);\n\n\treturn 1;\n}\n\n\nvoid tls_flush_error(void)\n{\n\tERR_print_errors_cb(print_error, NULL);\n}\n\n\n/**\n * Convert a X509_NAME object into a human-readable form placed in an mbuf\n *\n * @param field  X509_NAME of Cert field\n * @param mb     Memorybuffer to store the readable format\n * @param flags  X509_NAME_print_ex flags\n *\n * @return 0 if success, otherwise errorcode\n */\nstatic int convert_X509_NAME_to_mbuf(const X509_NAME *field, struct mbuf *mb,\n\tunsigned long flags)\n{\n\tBIO *outbio;\n\tchar *p;\n\tlong size;\n\tint err = ENOMEM;\n\n\tif (!field || !mb)\n\t\treturn EINVAL;\n\n\toutbio = BIO_new(BIO_s_mem());\n\tif (!outbio)\n\t\treturn ENOMEM;\n\n\tif (X509_NAME_print_ex(outbio, field, 1, flags) <= 0)\n\t\tgoto out;\n\n\tif (BIO_eof(outbio))\n\t\tgoto out;\n\n\tsize = BIO_get_mem_data(outbio, &p);\n\terr = mbuf_write_mem(mb, (uint8_t *)p, size);\n\tif (err)\n\t\tgoto out;\n\n\terr = 0;\n\n out:\n\tif (outbio)\n\t\tBIO_free(outbio);\n\n\treturn err;\n}\n\n\n/**\n * Extract a X509 certificate issuer/subject and write the result into an mbuf\n *\n * @param tls           TLS Object\n * @param mb            Memory buffer\n * @param field_getter  Functionpointer to the X509 getter function\n * @param flags         X509_NAME_print_ex flags\n *\n * @return 0 if success, otherwise errorcode\n */\nstatic int tls_get_ca_chain_field(struct tls *tls, struct mbuf *mb,\n\ttls_get_certfield_h *field_getter, unsigned long flags)\n{\n\tX509 *crt = NULL;\n\tconst X509_NAME *field;\n\n\tcrt = SSL_CTX_get0_certificate(tls->ctx);\n\tif (!crt)\n\t\treturn ENOENT;\n\n\tfield = field_getter(crt);\n\tif (!field)\n\t\treturn ENOTSUP;\n\n\treturn convert_X509_NAME_to_mbuf(field, mb, flags);\n}\n\n\n/**\n * Get the issuers fields of a certificate chain\n *\n * @param tls  TLS Object\n * @param mb   Memory Buffer\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_get_issuer(struct tls *tls, struct mbuf *mb)\n{\n\tif (!tls || !tls->ctx || !mb)\n\t\treturn EINVAL;\n\n\treturn tls_get_ca_chain_field(tls, mb, &X509_get_issuer_name,\n\t\tXN_FLAG_RFC2253);\n}\n\n\n/**\n * Get the subject fields of a certificate chain\n *\n * @param tls  TLS Object\n * @param mb   Memory Buffer\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_get_subject(struct tls *tls, struct mbuf *mb)\n{\n\tif (!tls || !tls->ctx || !mb)\n\t\treturn EINVAL;\n\n\treturn tls_get_ca_chain_field(tls, mb, &X509_get_subject_name,\n\t\tXN_FLAG_RFC2253);\n}\n\n\n/**\n * Disables SIP TLS server verifications for following requests\n *\n * @param tls     TLS Object\n */\nvoid tls_disable_verify_server(struct tls *tls)\n{\n\tif (!tls)\n\t\treturn;\n\n\ttls->verify_server = false;\n}\n\n\n/**\n * Enables SIP TLS client verifications for following requests\n *\n * @param tls     TLS Object\n * @param enable  true to enable client verification, false to disable\n */\nvoid tls_enable_verify_client(struct tls *tls, bool enable)\n{\n\tif (!tls)\n\t\treturn;\n\n\ttls->verify_client = enable;\n}\n\n\n/**\n * Set minimum TLS version\n *\n * @param tls     TLS Object\n * @param version Minimum version, e.g.: TLS1_2_VERSION\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_min_proto_version(struct tls *tls, int version)\n{\n\tif (!tls)\n\t\treturn EINVAL;\n\n\tif (SSL_CTX_set_min_proto_version(tls->ctx, version))\n\t\treturn 0;\n\n\treturn EACCES;\n}\n\n\n/**\n * Set maximum TLS version\n *\n * @param tls     TLS Object\n * @param version Maximum version, e.g. TLS1_2_VERSION\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_max_proto_version(struct tls *tls, int version)\n{\n\tif (!tls)\n\t\treturn EINVAL;\n\n\tif (SSL_CTX_set_max_proto_version(tls->ctx, version))\n\t\treturn 0;\n\n\treturn EACCES;\n}\n\n\nstruct session_entry {\n\tstruct le le;\n\tstruct sa peer;\n\tSSL_SESSION *sess;\n};\n\n\nstatic void session_destructor(void *arg)\n{\n\tstruct session_entry *e = arg;\n\n\thash_unlink(&e->le);\n\tif (e->sess)\n\t\tSSL_SESSION_free(e->sess);\n}\n\n\nstatic bool session_cmp_handler(struct le *le, void *arg)\n{\n\tconst struct session_entry *s = le->data;\n\tif (!s)\n\t\treturn false;\n\n\treturn sa_cmp(&s->peer, arg, SA_ALL);\n}\n\n\nstatic int tls_session_update_cache(const struct tls_conn *tc,\n\tSSL_SESSION *sess)\n{\n\tstruct sa peer;\n\tstruct session_entry* e = NULL;\n\tint err = 0;\n\n\tif (!tc || !tc->tls) {\n\t\tDEBUG_WARNING(\"%s: no tc or tls.\\n\", __func__);\n\t\treturn EINVAL;\n\t}\n\n\terr = tcp_conn_peer_get(tls_get_tcp_conn(tc), &peer);\n\tif (err) {\n\t\tDEBUG_WARNING(\"%s: tcp_conn_peer_get failed: (%m).\\n\",\n\t\t\t__func__, err);\n\t\treturn ENODATA;\n\t}\n\n\te = list_ledata(hash_lookup(tc->tls->reuse.ht_sessions,\n\t\t\t\t\t     sa_hash(&peer, SA_ALL),\n\t\t\t\t\t     session_cmp_handler, &peer));\n\tmem_deref(e);\n\n#if !defined(LIBRESSL_VERSION_NUMBER)\n\tif (!SSL_SESSION_is_resumable(sess)) {\n\t\treturn EINVAL;\n\t}\n#endif\n\n\te = mem_zalloc(sizeof(struct session_entry), session_destructor);\n\tif (!e) {\n\t\tDEBUG_WARNING(\"%s: error allocating session_entry.\\n\",\n\t\t\t__func__);\n\t\treturn ENOMEM;\n\t}\n\n\tsa_cpy(&e->peer, &peer);\n\te->sess = sess;\n\n\thash_append(tc->tls->reuse.ht_sessions, sa_hash(&e->peer, SA_ALL),\n\t\t&e->le, e);\n\n\treturn err;\n}\n\n\nstatic int session_new_cb(struct ssl_st *ssl, SSL_SESSION *sess)\n{\n\tBIO *wbio = NULL;\n\tconst struct tls_conn *tc = NULL;\n\n\twbio = SSL_get_wbio(ssl);\n\tif (!wbio) {\n\t\tDEBUG_WARNING(\"%s: SSL_get_rbio failed.\\n\", __func__);\n\t\treturn 0;\n\t}\n\n\ttc = BIO_get_data(wbio);\n\tif (!tc) {\n\t\tDEBUG_WARNING(\"%s: BIO_get_data tc failed.\\n\", __func__);\n\t\treturn 0;\n\t}\n\n\tif (tls_session_update_cache(tc, sess))\n\t\treturn 0;\n\n\tif (!SSL_SESSION_set_ex_data(sess, 0, tc->tls)) {\n\t\tDEBUG_WARNING(\"%s: SSL_SESSION_set_ex_data failed.\\n\",\n\t\t\t__func__);\n\t\treturn 0;\n\t}\n\n\t/* openssl will increments reference counter of sess on 1 */\n\treturn 1;\n}\n\n\nstatic bool remove_handler(struct le *le, void *arg)\n{\n\tstruct session_entry *e = le->data;\n\tif (!e || !arg)\n\t\treturn false;\n\n\tif (e->sess == arg)\n\t\tmem_deref(e);\n\n\treturn false;\n}\n\n\nstatic void session_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess)\n{\n\tstruct tls *tls = SSL_SESSION_get_ex_data(sess, 0);\n\t(void) ctx;\n\tif (!tls) {\n\t\tDEBUG_WARNING(\"%s: SSL_SESSION_get_ex_data failed.\\n\",\n\t\t\t__func__);\n\t\treturn;\n\t}\n\n\t/* iterate over all hash table entries and search for session */\n\t(void) hash_apply(tls->reuse.ht_sessions, remove_handler, sess);\n}\n\n\n/**\n * Enable/disable TLS session cache.\n *\n * @param tls  TLS Object\n * @param enabled   enabled or disable session cache. Default: disabled\n *\n * Note: session reuse in TLSv1.3 is not yet supported\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_session_reuse(struct tls *tls, int enabled)\n{\n\tif (!tls)\n\t\treturn EINVAL;\n\n\ttls->reuse.enabled = enabled;\n\n\tSSL_CTX_set_session_cache_mode(tls->ctx, enabled ?\n\t\tSSL_SESS_CACHE_BOTH : SSL_SESS_CACHE_OFF);\n\n\tif (!tls->reuse.enabled)\n\t\treturn 0;\n\n\tSSL_CTX_sess_set_new_cb(tls->ctx, session_new_cb);\n\tSSL_CTX_sess_set_remove_cb(tls->ctx, session_remove_cb);\n\n\treturn 0;\n}\n\n\n/**\n * Check if session was reused\n *\n * @param tc  tlc connection object\n *\n * @return 1 reused, 0 otherwise\n */\nbool tls_session_reused(const struct tls_conn *tc)\n{\n\tif (!tc)\n\t\treturn false;\n\n\treturn SSL_session_reused(tc->ssl);\n}\n\n\n/**\n * getter for session reuse enabled\n *\n * @param tc  tlc connection object\n *\n * @return 1 enabled, 0 disabled\n */\nbool tls_get_session_reuse(const struct tls_conn *tc)\n{\n\tif (!tc)\n\t\treturn false;\n\n\treturn tc->tls->reuse.enabled;\n}\n\n\n/**\n * Reuse session if possible\n *\n * @param tc  tlc connection object\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_reuse_session(const struct tls_conn *tc)\n{\n\tint err = 0;\n\tstruct sa peer;\n\tstruct session_entry *sess = NULL;\n\tif (!tc || !tc->tls)\n\t\treturn EINVAL;\n\n\terr = tcp_conn_peer_get(tls_get_tcp_conn(tc), &peer);\n\tif (err) {\n\t\tDEBUG_WARNING(\"%s: tcp_conn_peer_get failed: (%m).\\n\",\n\t\t\t__func__, err);\n\t\treturn 0;\n\t}\n\n\tsess = list_ledata(hash_lookup(tc->tls->reuse.ht_sessions,\n\t\t\t\t\t     sa_hash(&peer, SA_ALL),\n\t\t\t\t\t     session_cmp_handler, &peer));\n\n\tif (sess && !SSL_set_session(tc->ssl, sess->sess)) {\n\t\terr = EFAULT;\n\t\tDEBUG_WARNING(\"%s: error: %m, ssl_err=%d\\n\", __func__, err,\n\t\t\tSSL_get_error(tc->ssl, err));\n\t}\n\n\treturn err;\n}\n\n\n/**\n * update session cache manually\n *\n * @param tc  tlc connection object\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_update_sessions(const struct tls_conn *tc)\n{\n\tint err = 0;\n\tSSL_SESSION *sess = NULL;\n\tif (!tc || !tc->tls)\n\t\treturn EINVAL;\n\n\tsess = SSL_get1_session(tc->ssl);\n\tif (!sess)\n\t\treturn EINVAL;\n\n\terr = tls_session_update_cache(tc, sess);\n\tif (err)\n\t\tSSL_SESSION_free(sess);\n\n\treturn err;\n}\n\n\n/**\n * Reuse session if possible\n *\n * @param tls  tls connection object\n *\n * @return SSL_CTX* if set or NULL otherwise\n */\nSSL_CTX *tls_ssl_ctx(const struct tls *tls)\n{\n\tif (!tls)\n\t\treturn NULL;\n\n\treturn tls->ctx;\n}\n\n\nstatic void tls_cert_destructor(void *arg)\n{\n\tstruct tls_cert *uc = arg;\n\n\tmem_deref(uc->host);\n\tif (uc->ctx)\n\t\tSSL_CTX_free(uc->ctx);\n}\n\n\n/**\n * Adds a certificate for Server Name Indication (SNI) based certificate\n * selection. An incoming client hello may contain an SNI extension which\n * is used to select a local server certificate\n *\n * @param tls   TLS context\n * @param certf Filename of the certificate\n * @param host  Hostname that should match the SNI from client hello\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_add_certf(struct tls *tls, const char *certf, const char *host)\n{\n\tstruct tls_cert *uc;\n\tint err = 0;\n\n\tif (!tls || !certf)\n\t\treturn EINVAL;\n\n\tuc = mem_zalloc(sizeof(*uc), tls_cert_destructor);\n\tif (!uc)\n\t\treturn ENOMEM;\n\n\tif (str_isset(host)) {\n\t\terr = str_dup(&uc->host, host);\n\t\tif (err)\n\t\t\tgoto err;\n\t}\n\n\terr = tls_ctx_alloc(&uc->ctx, TLS_METHOD_TLS, certf, NULL, NULL);\n\tif (err)\n\t\tgoto err;\n\n\tX509_STORE *ca = SSL_CTX_get_cert_store(tls->ctx);\n\tif (ca) {\n\t\tX509_STORE_up_ref(ca);\n\t\tSSL_CTX_set_cert_store(uc->ctx, ca);\n\t}\n\n\tlist_append(&tls->certs, &uc->le, uc);\n\tif (list_count(&tls->certs) == 1)\n\t\ttls_enable_sni(tls);\n\n\treturn 0;\n\nerr:\n\tERR_clear_error();\n\tmem_deref(uc);\n\n\treturn err;\n}\n\n\n/**\n * Returns the X509 of the TLS certificate\n *\n * @param hc  TLS certificate\n *\n * @return The OpenSSL X509\n */\nX509 *tls_cert_x509(struct tls_cert *hc)\n{\n\treturn hc ? SSL_CTX_get0_certificate(hc->ctx) : NULL;\n}\n\n\nSSL_CTX *tls_cert_ctx(struct tls_cert *hc) {\n\n\treturn hc ? hc->ctx : NULL;\n}\n\n/**\n * Returns the host name of the TLS certificate\n *\n * @param hc  TLS certificate\n *\n * @return The host name\n */\nconst char *tls_cert_host(struct tls_cert *hc)\n{\n\treturn hc ? hc->host : NULL;\n}\n\n\n/**\n * Returns the list of TLS certificates\n *\n * @param tls TLS context\n *\n * @return The list\n */\nconst struct list *tls_certs(const struct tls *tls)\n{\n\treturn tls ? &tls->certs : NULL;\n}\n\n\n/**\n * Enable/disable posthandshake\n * Only on client side for TLSv1.3\n *\n * @param tls  tls object\n * @param value posthandshake auth value. 1 enabled, Default: 0\n *\n */\nvoid tls_set_posthandshake_auth(struct tls *tls, int value)\n{\n\tif (!tls)\n\t\treturn;\n\n\tSSL_CTX_set_post_handshake_auth(tls->ctx, value);\n}\n\n\n/**\n * Request client certificate using post handshake\n * Only on client side for TLSv1.3\n *\n * @param tc  tls connection\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_verify_client_post_handshake(struct tls_conn *tc)\n{\n\tint ret;\n\tint err = 0;\n\tif (!tc || !tc->ssl)\n\t\treturn EINVAL;\n\n\tif (!(ret=SSL_verify_client_post_handshake(tc->ssl))) {\n\t\terr = EFAULT;\n\t\tDEBUG_WARNING(\"SSL_verify_client_post_handshake error: \"\\\n\t\t\t\"%m, ssl_err=%d\\n\", err, SSL_get_error(tc->ssl, ret));\n\t\tERR_clear_error();\n\t\treturn err;\n\t}\n\n\tif (!(ret = SSL_do_handshake(tc->ssl))) {\n\t\terr = EIO;\n\t\tDEBUG_WARNING(\"SSL_do_handshake error: \"\\\n\t\t\t\"%m, ssl_err=%d\\n\", err, SSL_get_error(tc->ssl, ret));\n\t\tERR_clear_error();\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Set TLS session resumption mode\n *\n * @param tls  TLS Object\n * @param mode TLS session resumption mode\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_set_resumption(struct tls *tls, enum tls_resume_mode mode)\n{\n\tlong ok = 1;\n\n\tif (!tls)\n\t\treturn EINVAL;\n\n\tif (mode & TLS_RESUMPTION_IDS) {\n\t\tok = SSL_CTX_set_session_cache_mode(tls->ctx,\n\t\t\t\t\t\t    SSL_SESS_CACHE_SERVER);\n\t}\n\telse {\n\t\tok = SSL_CTX_set_session_cache_mode(tls->ctx,\n\t\t\t\t\t\t    SSL_SESS_CACHE_OFF);\n\t}\n\n\tif (mode & TLS_RESUMPTION_TICKETS) {\n\t\tok |= SSL_CTX_clear_options(tls->ctx, SSL_OP_NO_TICKET);\n\t\tok |= SSL_CTX_set_num_tickets(tls->ctx, 2);\n\t}\n\telse {\n\t\tok |= SSL_CTX_set_options(tls->ctx, SSL_OP_NO_TICKET);\n\t\tok |= SSL_CTX_set_num_tickets(tls->ctx, 0);\n\t}\n\n\tif (!ok) {\n\t\tERR_clear_error();\n\t\treturn EFAULT;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Change used certificate+key of an existing SSL object\n *\n * @param tls       TLS Object\n * @param chain     Cert (chain) + Key in PEM format\n * @param len_chain Length of certificate + key PEM string\n *\n * @return int 0 if success, otherwise errorcode\n */\nint tls_set_certificate_chain_pem(struct tls *tls, const char *chain,\n\t\t\t\t  size_t len_chain)\n{\n\tSTACK_OF(X509) *cert_stack = NULL;\n\tBIO *bio_mem = NULL;\n\tEVP_PKEY *pkey = NULL;\n\tX509 *leaf_cert = NULL;\n\tint err = ENOMEM;\n\n\tif (!tls || !chain || !len_chain)\n\t\treturn EINVAL;\n\n\tbio_mem = BIO_new_mem_buf(chain, (int)len_chain);\n\tcert_stack = sk_X509_new_null();\n\tif (!bio_mem || !cert_stack)\n\t\tgoto out;\n\n\tX509 *cert;\n\twhile ((cert = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL)) != NULL) {\n\t\tint n = sk_X509_push(cert_stack, cert);\n\t\tif (n < 1) {\n\t\t\tX509_free(cert);\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\terr = EINVAL;\n\n\tif (sk_X509_num(cert_stack) == 0)\n\t\tgoto out;\n\n\tleaf_cert = sk_X509_shift(cert_stack);\n\tlong ok = SSL_CTX_use_certificate(tls->ctx, leaf_cert);\n\tif (ok <= 0) {\n\t\tX509_free(leaf_cert);\n\t\tgoto out;\n\t}\n\n\tif (sk_X509_num(cert_stack)) {\n\t\tok = SSL_CTX_clear_chain_certs(tls->ctx);\n\t\tif (!ok)\n\t\t\tgoto out;\n\n\t\twhile((cert = sk_X509_shift(cert_stack)) != NULL){\n\t\t\tok = SSL_CTX_add0_chain_cert(tls->ctx, cert);\n\t\t\tif (!ok) {\n\t\t\t\tX509_free(cert);\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\t}\n\n\tBIO_free(bio_mem);\n\tbio_mem = BIO_new_mem_buf(chain, (int)len_chain);\n\tif (!bio_mem) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tpkey = PEM_read_bio_PrivateKey(bio_mem, NULL, NULL, NULL);\n\tif (!pkey)\n\t\tgoto out;\n\n\tok = SSL_CTX_use_PrivateKey(tls->ctx, pkey);\n\tif (ok <= 0) {\n\t\terr = EKEYREJECTED;\n\t\tgoto out;\n\t}\n\n\tok = SSL_CTX_check_private_key(tls->ctx);\n\tif (ok <= 0)\n\t\tgoto out;\n\n\tif (tls->cert)\n\t\tX509_free(tls->cert);\n\n\ttls->cert = leaf_cert;\n\tleaf_cert = NULL;\n\n\terr = 0;\n\nout:\n\tif (bio_mem)\n\t\tBIO_free(bio_mem);\n\tif (leaf_cert)\n\t\tX509_free(leaf_cert);\n\tif (cert_stack)\n\t\tsk_X509_pop_free(cert_stack, X509_free);\n\tif (pkey)\n\t\tEVP_PKEY_free(pkey);\n\tif (err)\n\t\tERR_clear_error();\n\n\treturn err;\n}\n\n\n/**\n * Change used certificate+key of an existing SSL object\n *\n * @param tls  TLS Object\n * @param path Path to Cert (chain) + Key file (PEM format)\n *\n * @return int 0 if success, otherwise errorcode\n */\nint tls_set_certificate_chain(struct tls *tls, const char *path)\n{\n\tX509 *cert;\n\tint ok = 0;\n\n\tif (!tls || !path)\n\t\treturn EINVAL;\n\n\tok = SSL_CTX_use_certificate_chain_file(tls->ctx, path);\n\tif (ok <= 0) {\n\t\tERR_clear_error();\n\t\treturn ENOENT;\n\t}\n\n\tok = SSL_CTX_use_PrivateKey_file(tls->ctx, path, SSL_FILETYPE_PEM);\n\tif (ok <= 0) {\n\t\tERR_clear_error();\n\t\treturn EKEYREJECTED;\n\t}\n\n\tcert = SSL_CTX_get0_certificate(tls->ctx);\n\tif (!cert) {\n\t\tERR_clear_error();\n\t\treturn ENOENT;\n\t}\n\n\tX509_up_ref(cert);\n\n\tif (tls->cert)\n\t\tX509_free(tls->cert);\n\n\ttls->cert = cert;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/tls/openssl/tls.h",
    "content": "/**\n * @file openssl/tls.h TLS backend using OpenSSL (Internal API)\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n/* also defined by wincrypt.h */\n#ifdef WIN32\n#undef X509_NAME\n#endif\n\n/*\n * Mapping of feature macros\n */\n\n#if defined(LIBRESSL_VERSION_NUMBER)\ntypedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx);\n#else\n#define SSL_state SSL_get_state\n#define SSL_ST_OK TLS_ST_OK\n#endif\n\n#if OPENSSL_VERSION_MAJOR >= 4\ntypedef const X509_NAME*(tls_get_certfield_h)(const X509 *);\n#else\ntypedef X509_NAME*(tls_get_certfield_h)(const X509 *);\n#endif\n\nstruct tls;\nstruct tls_cert;\n\nvoid tls_flush_error(void);\nSSL_CTX *tls_ssl_ctx(const struct tls *tls);\nX509 *tls_cert_x509(struct tls_cert *hc);\nSSL_CTX *tls_cert_ctx(struct tls_cert *hc);\n\nconst char *tls_cert_host(struct tls_cert *hc);\nconst struct list *tls_certs(const struct tls *tls);\n\nstruct tls_cert *tls_cert_for_sni(const struct tls *tls, const char *sni);\nvoid tls_enable_sni(struct tls *tls);\n"
  },
  {
    "path": "src/tls/openssl/tls_tcp.c",
    "content": "/**\n * @file openssl/tls_tcp.c TLS/TCP backend using OpenSSL\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_net.h>\n#include <re_main.h>\n#include <re_sa.h>\n#include <re_srtp.h>\n#include <re_tcp.h>\n#include <re_tls.h>\n#include \"tls.h\"\n\n\n#define DEBUG_MODULE \"tls\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/* NOTE: shadow struct defined in tls_*.c */\nstruct tls_conn {\n\tSSL *ssl;             /* inheritance */\n\tstruct tls *tls;      /* inheritance */\n\tstruct tls_conn_d cd; /* inheritance */\n\tBIO_METHOD *biomet;\n\tBIO *sbio_out;\n\tBIO *sbio_in;\n\tstruct tcp_helper *th;\n\tstruct tcp_conn *tcp;\n\tbool active;\n\tbool up;\n};\n\n\nstatic void destructor(void *arg)\n{\n\tstruct tls_conn *tc = arg;\n\n\tif (tc->ssl) {\n\t\tint r = SSL_shutdown(tc->ssl);\n\t\tif (r <= 0)\n\t\t\tERR_clear_error();\n\n\t\tSSL_free(tc->ssl);\n\t}\n\n\tif (tc->biomet)\n\t\tBIO_meth_free(tc->biomet);\n\n\tmem_deref(tc->th);\n\tmem_deref(tc->tcp);\n}\n\n\nstatic int bio_create(BIO *b)\n{\n\tBIO_set_init(b, 1);\n\tBIO_set_data(b, NULL);\n\tBIO_set_flags(b, 0);\n\n\treturn 1;\n}\n\n\nstatic int bio_destroy(BIO *b)\n{\n\tif (!b)\n\t\treturn 0;\n\n\tBIO_set_init(b, 0);\n\tBIO_set_data(b, NULL);\n\tBIO_set_flags(b, 0);\n\n\treturn 1;\n}\n\n\nstatic int bio_write(BIO *b, const char *buf, int len)\n{\n\tstruct tls_conn *tc = BIO_get_data(b);\n\tstruct mbuf mb;\n\tint err;\n\n\tmb.buf = (void *)buf;\n\tmb.pos = 0;\n\tmb.end = mb.size = len;\n\n\terr = tcp_send_helper(tc->tcp, &mb, tc->th);\n\tif (err)\n\t\treturn -1;\n\n\treturn len;\n}\n\n\nstatic long bio_ctrl(BIO *b, int cmd, long num, void *ptr)\n{\n\t(void)b;\n\t(void)num;\n\t(void)ptr;\n\n\tif (cmd == BIO_CTRL_FLUSH) {\n\t\t/* The OpenSSL library needs this */\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\n\nstatic BIO_METHOD *bio_method_tcp(void)\n{\n\tBIO_METHOD *method;\n\n\tmethod = BIO_meth_new(BIO_TYPE_SOURCE_SINK, \"tcp_send\");\n\tif (!method) {\n\t\tDEBUG_WARNING(\"alloc: BIO_meth_new() failed\\n\");\n\t\tERR_clear_error();\n\t\treturn NULL;\n\t}\n\n\tBIO_meth_set_write(method, bio_write);\n\tBIO_meth_set_ctrl(method, bio_ctrl);\n\tBIO_meth_set_create(method, bio_create);\n\tBIO_meth_set_destroy(method, bio_destroy);\n\n\treturn method;\n}\n\n\nstatic int tls_connect(struct tls_conn *tc)\n{\n\tint err = 0, r;\n\n\tERR_clear_error();\n\n\tr = SSL_connect(tc->ssl);\n\tif (r <= 0) {\n\t\tconst int ssl_err = SSL_get_error(tc->ssl, r);\n\n\t\tswitch (ssl_err) {\n\n\t\tcase SSL_ERROR_WANT_READ:\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tDEBUG_WARNING(\"connect: error (r=%d, ssl_err=%d)\\n\",\n\t\t\t\t      r, ssl_err);\n\t\t\ttls_flush_error();\n\t\t\terr = EPROTO;\n\t\t\tbreak;\n\t\t}\n\n\t\tERR_clear_error();\n\t}\n\n\treturn err;\n}\n\n\nstatic int tls_accept(struct tls_conn *tc)\n{\n\tint err = 0, r;\n\n\tERR_clear_error();\n\n\tr = SSL_accept(tc->ssl);\n\tif (r <= 0) {\n\t\tconst int ssl_err = SSL_get_error(tc->ssl, r);\n\n\t\tswitch (ssl_err) {\n\n\t\tcase SSL_ERROR_WANT_READ:\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tDEBUG_WARNING(\"accept error: (r=%d, ssl_err=%d)\\n\",\n\t\t\t\t      r, ssl_err);\n\t\t\ttls_flush_error();\n\t\t\terr = EPROTO;\n\t\t\tbreak;\n\t\t}\n\n\t\tERR_clear_error();\n\t}\n\n\treturn err;\n}\n\n\nstatic bool estab_handler(int *err, bool active, void *arg)\n{\n\tstruct tls_conn *tc = arg;\n\n\tDEBUG_INFO(\"tcp established (active=%u)\\n\", active);\n\n\tif (!active)\n\t\treturn true;\n\n\ttc->active = true;\n\tif (tls_get_session_reuse(tc))\n\t\t(void) tls_reuse_session(tc);\n\n\t*err = tls_connect(tc);\n\n\treturn true;\n}\n\n\nstatic bool recv_handler(int *err, struct mbuf *mb, bool *estab, void *arg)\n{\n\tstruct tls_conn *tc = arg;\n\tint r;\n\n\t/* feed SSL data to the BIO */\n\tr = BIO_write(tc->sbio_in, mbuf_buf(mb), (int)mbuf_get_left(mb));\n\tif (r <= 0) {\n\t\tDEBUG_WARNING(\"recv: BIO_write %d\\n\", r);\n\t\tERR_clear_error();\n\t\t*err = ENOMEM;\n\t\treturn true;\n\t}\n\n\tif (SSL_state(tc->ssl) != SSL_ST_OK) {\n\n\t\tif (tc->up && !SSL_get_secure_renegotiation_support(tc->ssl)) {\n\t\t\t*err = EPROTO;\n\t\t\treturn true;\n\t\t}\n\n\t\tif (tc->active) {\n\t\t\t*err = tls_connect(tc);\n\t\t}\n\t\telse {\n\t\t\t*err = tls_accept(tc);\n\t\t}\n\n\t\tDEBUG_INFO(\"state: %s\\n\", SSL_state_string_long(tc->ssl));\n\n\t\t/* TLS connection is established */\n\t\tif (SSL_state(tc->ssl) != SSL_ST_OK)\n\t\t\treturn true;\n\n\t\t*estab = true;\n\t\ttc->up = true;\n\t}\n\n\tmbuf_set_pos(mb, 0);\n\n\tfor (;;) {\n\t\tint n;\n\n\t\tif (mbuf_get_space(mb) < 4096) {\n\t\t\t*err = mbuf_resize(mb, mb->size + 8192);\n\t\t\tif (*err)\n\t\t\t\treturn true;\n\t\t}\n\n\t\tERR_clear_error();\n\n\t\tn = SSL_read(tc->ssl, mbuf_buf(mb), (int)mbuf_get_space(mb));\n\t\tif (n <= 0) {\n\t\t\tconst int ssl_err = SSL_get_error(tc->ssl, n);\n\n\t\t\tERR_clear_error();\n\n\t\t\tswitch (ssl_err) {\n\n\t\t\tcase SSL_ERROR_ZERO_RETURN:\n\t\t\tcase SSL_ERROR_WANT_READ:\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\t*err = EPROTO;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\tmb->pos += n;\n\t}\n\n\tmbuf_set_end(mb, mb->pos);\n\tmbuf_set_pos(mb, 0);\n\n\treturn false;\n}\n\n\nstatic bool send_handler(int *err, struct mbuf *mb, void *arg)\n{\n\tstruct tls_conn *tc = arg;\n\tint r;\n\n\tERR_clear_error();\n\n\tr = SSL_write(tc->ssl, mbuf_buf(mb), (int)mbuf_get_left(mb));\n\tif (r <= 0) {\n\t\tDEBUG_WARNING(\"SSL_write: %d\\n\", SSL_get_error(tc->ssl, r));\n\t\tERR_clear_error();\n\t\t*err = EPROTO;\n\t}\n\n\treturn true;\n}\n\n\n/**\n * Change used certificate+key of an existing SSL object\n *\n * @param tc   TLS connection object\n * @param file Cert+Key file\n *\n * @return int 0 if success, otherwise errorcode\n */\nint tls_conn_change_cert(struct tls_conn *tc, const char *file)\n{\n\tif (!tc || !file)\n\t\treturn EINVAL;\n\n#if !defined(LIBRESSL_VERSION_NUMBER)\n\tSSL_certs_clear(tc->ssl);\n\n\tint r = SSL_use_certificate_chain_file(tc->ssl, file);\n\tif (r <= 0) {\n\t\tERR_clear_error();\n\t\treturn ENOENT;\n\t}\n\n\tr = SSL_use_PrivateKey_file(tc->ssl, file, SSL_FILETYPE_PEM);\n\tif (r <= 0) {\n\t\tERR_clear_error();\n\t\treturn EKEYREJECTED;\n\t}\n\n\treturn 0;\n#else\n\treturn ENOSYS;\n#endif\n}\n\n\n/**\n * Start TLS on a TCP-connection\n *\n * @param ptc   Pointer to allocated TLS connectioon\n * @param tls   TLS Context\n * @param tcp   TCP Connection\n * @param layer Protocol stack layer\n *\n * @return 0 if success, otherwise errorcode\n */\nint tls_start_tcp(struct tls_conn **ptc, struct tls *tls, struct tcp_conn *tcp,\n\t\t  int layer)\n{\n\tstruct tls_conn *tc;\n\tint err;\n\n\tif (!ptc || !tls || !tcp)\n\t\treturn EINVAL;\n\n\ttc = mem_zalloc(sizeof(*tc), destructor);\n\tif (!tc)\n\t\treturn ENOMEM;\n\n\terr = tcp_register_helper(&tc->th, tcp, layer, estab_handler,\n\t\t\t\t  send_handler, recv_handler, tc);\n\tif (err)\n\t\tgoto out;\n\n\ttc->tcp = mem_ref(tcp);\n\ttc->tls = tls;\n\n\ttc->biomet = bio_method_tcp();\n\tif (!tc->biomet) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = ENOMEM;\n\n\t/* Connect the SSL socket */\n\ttc->ssl = SSL_new(tls_ssl_ctx(tls));\n\tif (!tc->ssl) {\n\t\tDEBUG_WARNING(\"alloc: SSL_new() failed (ctx=%p)\\n\",\n\t\t\ttls_ssl_ctx(tls));\n\t\tERR_clear_error();\n\t\tgoto out;\n\t}\n\n\ttc->sbio_in = BIO_new(BIO_s_mem());\n\tif (!tc->sbio_in) {\n\t\tDEBUG_WARNING(\"alloc: BIO_new() failed\\n\");\n\t\tERR_clear_error();\n\t\tgoto out;\n\t}\n\n\n\ttc->sbio_out = BIO_new(tc->biomet);\n\tif (!tc->sbio_out) {\n\t\tDEBUG_WARNING(\"alloc: BIO_new_socket() failed\\n\");\n\t\tERR_clear_error();\n\t\tBIO_free(tc->sbio_in);\n\t\tgoto out;\n\t}\n\n\tBIO_set_data(tc->sbio_out, tc);\n\n\tSSL_set_bio(tc->ssl, tc->sbio_in, tc->sbio_out);\n\n\terr = 0;\n\n out:\n\tif (err)\n\t\tmem_deref(tc);\n\telse\n\t\t*ptc = tc;\n\n\treturn err;\n}\n\n\n/**\n * Get tcp connection\n *\n * @param tc   TLS connection\n *\n * @return pointer to tcp connection struct\n */\nconst struct tcp_conn *tls_get_tcp_conn(const struct tls_conn *tc)\n{\n\tif (!tc)\n\t\treturn NULL;\n\n\treturn tc->tcp;\n}\n"
  },
  {
    "path": "src/tls/openssl/tls_udp.c",
    "content": "/**\n * @file openssl/tls_udp.c DTLS backend using OpenSSL\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#ifdef HAVE_SYS_TIME_H\n#include <sys/time.h>\n#endif\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_sa.h>\n#include <re_srtp.h>\n#include <re_udp.h>\n#include <re_tmr.h>\n#include <re_tls.h>\n#include \"tls.h\"\n\n\n#define DEBUG_MODULE \"dtls\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tMTU_DEFAULT  = 1400,\n\tMTU_FALLBACK = 548,\n};\n\n\n/* TLS ContentType defined in RFC 5246 */\nenum content_type {\n       TYPE_CHANGE_CIPHER_SPEC = 20,\n       TYPE_ALERT              = 21,\n       TYPE_HANDSHAKE          = 22,\n       TYPE_APPLICATION_DATA   = 23\n};\n\n\nstruct dtls_sock {\n\tstruct sa peer;\n\tstruct udp_helper *uh;\n\tstruct udp_sock *us;\n\tstruct hash *ht;\n\tstruct mbuf *mb;\n\tdtls_conn_h *connh;\n\tvoid *arg;\n\tsize_t mtu;\n\tbool single_conn;  /* If enabled, only one DTLS connection */\n};\n\n\n/* NOTE: shadow struct defined in tls_*.c */\nstruct tls_conn {\n\tSSL *ssl;             /* inheritance */\n\tstruct tls *tls;      /* inheritance */\n\tstruct tls_conn_d cd; /* inheritance */\n\tBIO_METHOD *biomet;\n\tBIO *sbio_out;\n\tBIO *sbio_in;\n\tstruct tmr tmr;\n\tstruct sa peer;\n\tstruct le he;\n\tstruct dtls_sock *sock;\n\tdtls_estab_h *estabh;\n\tdtls_recv_h *recvh;\n\tdtls_close_h *closeh;\n\tvoid *arg;\n\tbool active;\n\tbool up;\n};\n\n\n#if DEBUG_LEVEL >= 6\nstatic const char *content_type_str(enum content_type content_type)\n{\n\tswitch (content_type) {\n\n\tcase TYPE_CHANGE_CIPHER_SPEC: return \"CHANGE_CIPHER_SPEC\";\n\tcase TYPE_ALERT:              return \"ALERT\";\n\tcase TYPE_HANDSHAKE:          return \"HANDSHAKE\";\n\tcase TYPE_APPLICATION_DATA:   return \"APPLICATION_DATA\";\n\tdefault: return \"???\";\n\t}\n}\n#endif\n\n\nstatic bool first_handler(struct le *le, void *arg)\n{\n\t(void)le;\n\t(void)arg;\n\n\treturn true;  /* stop on the first element */\n}\n\n\nstatic struct le *hash_get_first(const struct hash *ht)\n{\n\treturn hash_apply(ht, first_handler, NULL);\n}\n\n\nstatic int bio_create(BIO *b)\n{\n\tBIO_set_init(b, 1);\n\tBIO_set_data(b, NULL);\n\tBIO_set_flags(b, 0);\n\n\treturn 1;\n}\n\n\nstatic int bio_destroy(BIO *b)\n{\n\tif (!b)\n\t\treturn 0;\n\n\tBIO_set_init(b, 0);\n\tBIO_set_data(b, NULL);\n\tBIO_set_flags(b, 0);\n\n\treturn 1;\n}\n\n\nstatic int bio_write(BIO *b, const char *buf, int len)\n{\n\tstruct tls_conn *tc = BIO_get_data(b);\n\tstruct mbuf *mb;\n\tenum {SPACE = 4};\n\tint err;\n\n\tmb = mbuf_alloc(SPACE + len);\n\tif (!mb)\n\t\treturn -1;\n\n\tmb->pos = SPACE;\n\t(void)mbuf_write_mem(mb, (void *)buf, len);\n\tmb->pos = SPACE;\n\n\terr = udp_send_helper(tc->sock->us, &tc->peer, mb, tc->sock->uh);\n\n\tmem_deref(mb);\n\n\treturn err ? -1 : len;\n}\n\n\nstatic long bio_ctrl(BIO *b, int cmd, long num, void *ptr)\n{\n\tstruct tls_conn *tc = BIO_get_data(b);\n\t(void)num;\n\t(void)ptr;\n\n\tswitch (cmd) {\n\n\tcase BIO_CTRL_FLUSH:\n\t\t/* The OpenSSL library needs this */\n\t\treturn 1;\n\n#if defined (BIO_CTRL_DGRAM_QUERY_MTU)\n\tcase BIO_CTRL_DGRAM_QUERY_MTU:\n\t\treturn tc ? (long)tc->sock->mtu : MTU_DEFAULT;\n#endif\n\n#if defined (BIO_CTRL_DGRAM_GET_FALLBACK_MTU)\n\tcase BIO_CTRL_DGRAM_GET_FALLBACK_MTU:\n\t\treturn MTU_FALLBACK;\n#endif\n\t}\n\n\treturn 0;\n}\n\n\nstatic BIO_METHOD *bio_method_udp(void)\n{\n\tBIO_METHOD *method;\n\n\tmethod = BIO_meth_new(BIO_TYPE_SOURCE_SINK, \"udp_send\");\n\tif (!method) {\n\t\tDEBUG_WARNING(\"alloc: BIO_meth_new() failed\\n\");\n\t\tERR_clear_error();\n\t\treturn NULL;\n\t}\n\n\tBIO_meth_set_write(method, bio_write);\n\tBIO_meth_set_ctrl(method, bio_ctrl);\n\tBIO_meth_set_create(method, bio_create);\n\tBIO_meth_set_destroy(method, bio_destroy);\n\n\treturn method;\n}\n\n\nstatic void tls_close(struct tls_conn *tc)\n{\n\tint r;\n\n\tif (!tc->ssl)\n\t\treturn;\n\n\tr = SSL_shutdown(tc->ssl);\n\tif (r <= 0)\n\t\tERR_clear_error();\n\n\tSSL_free(tc->ssl);\n\ttc->ssl = NULL;\n}\n\n\nstatic void conn_destructor(void *arg)\n{\n\tstruct tls_conn *tc = arg;\n\n\thash_unlink(&tc->he);\n\ttmr_cancel(&tc->tmr);\n\ttls_close(tc);\n\n\tif (tc->biomet)\n\t\tBIO_meth_free(tc->biomet);\n\n\tmem_deref(tc->sock);\n}\n\n\nstatic void conn_close(struct tls_conn *tc, int err)\n{\n\ttmr_cancel(&tc->tmr);\n\ttls_close(tc);\n\ttc->up = false;\n\n\tif (tc->closeh)\n\t\ttc->closeh(err, tc->arg);\n}\n\n\nstatic void check_timer(struct tls_conn *tc);\n\n\nstatic void timeout(void *arg)\n{\n\tstruct tls_conn *tc = arg;\n\n\tDEBUG_INFO(\"timeout\\n\");\n\n\tif (0 <= DTLSv1_handle_timeout(tc->ssl)) {\n\n\t\tcheck_timer(tc);\n\t}\n\telse {\n\t\tERR_clear_error();\n\t\tconn_close(tc, ETIMEDOUT);\n\t}\n}\n\n\nstatic void check_timer(struct tls_conn *tc)\n{\n\tstruct timeval tv = {0, 0};\n\n\tif (1 == DTLSv1_get_timeout(tc->ssl, &tv)) {\n\n\t\ttmr_start(&tc->tmr, tv.tv_sec * 1000 + tv.tv_usec / 1000,\n\t\t\t  timeout, tc);\n\t}\n\telse {\n\t\ttmr_cancel(&tc->tmr);\n\t}\n}\n\n\nstatic int tls_connect(struct tls_conn *tc)\n{\n\tint r;\n\n\tERR_clear_error();\n\n\tr = SSL_connect(tc->ssl);\n\tif (r <= 0) {\n\t\tconst int ssl_err = SSL_get_error(tc->ssl, r);\n\n\t\ttls_flush_error();\n\n\t\tswitch (ssl_err) {\n\n\t\tcase SSL_ERROR_WANT_READ:\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tDEBUG_WARNING(\"connect error: %i\\n\", ssl_err);\n\t\t\treturn EPROTO;\n\t\t}\n\t}\n\n\tcheck_timer(tc);\n\n\treturn 0;\n}\n\n\nstatic int tls_accept(struct tls_conn *tc)\n{\n\tint r;\n\n\tERR_clear_error();\n\n\tr = SSL_accept(tc->ssl);\n\tif (r <= 0) {\n\t\tconst int ssl_err = SSL_get_error(tc->ssl, r);\n\n\t\ttls_flush_error();\n\n\t\tswitch (ssl_err) {\n\n\t\tcase SSL_ERROR_WANT_READ:\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tDEBUG_WARNING(\"accept error: %i\\n\", ssl_err);\n\t\t\treturn EPROTO;\n\t\t}\n\t}\n\n\tcheck_timer(tc);\n\n\treturn 0;\n}\n\n\nstatic void conn_recv(struct tls_conn *tc, struct mbuf *mb)\n{\n\tint err, r;\n\n\tif (!tc->ssl)\n\t\treturn;\n\n\t/* feed SSL data to the BIO */\n\tr = BIO_write(tc->sbio_in, mbuf_buf(mb), (int)mbuf_get_left(mb));\n\tif (r <= 0) {\n\t\tDEBUG_WARNING(\"receive bio write error: %i\\n\", r);\n\t\tERR_clear_error();\n\t\tconn_close(tc, ENOMEM);\n\t\treturn;\n\t}\n\n\tif (SSL_state(tc->ssl) != SSL_ST_OK) {\n\n\t\tif (tc->up && !SSL_get_secure_renegotiation_support(tc->ssl)) {\n\t\t\tconn_close(tc, EPROTO);\n\t\t\treturn;\n\t\t}\n\n\t\tif (tc->active) {\n\t\t\terr = tls_connect(tc);\n\t\t}\n\t\telse {\n\t\t\terr = tls_accept(tc);\n\t\t}\n\n\t\tif (err) {\n\t\t\tconn_close(tc, err);\n\t\t\treturn;\n\t\t}\n\n\t\tDEBUG_INFO(\"%s: state=0x%04x\\n\",\n\t\t\t   tc->active ? \"client\" : \"server\",\n\t\t\t   SSL_state(tc->ssl));\n\n\t\t/* TLS connection is established */\n\t\tif (SSL_state(tc->ssl) != SSL_ST_OK)\n\t\t\treturn;\n\n\t\ttc->up = true;\n\n\t\tif (tc->estabh) {\n\t\t\tuint32_t nrefs;\n\n\t\t\tmem_ref(tc);\n\n\t\t\ttc->estabh(tc->arg);\n\n                        nrefs = mem_nrefs(tc);\n                        mem_deref(tc);\n\n                        /* check if connection was deref'd from handler */\n                        if (nrefs == 1)\n                                return;\n\t\t}\n\t}\n\n\tmbuf_set_pos(mb, 0);\n\n\tfor (;;) {\n\t\tint n;\n\n\t\tif (mbuf_get_space(mb) < 4096) {\n\t\t\terr = mbuf_resize(mb, mb->size + 8192);\n\t\t\tif (err) {\n\t\t\t\tconn_close(tc, err);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tERR_clear_error();\n\n\t\tn = SSL_read(tc->ssl, mbuf_buf(mb), (int)mbuf_get_space(mb));\n\t\tif (n <= 0) {\n\t\t\tconst int ssl_err = SSL_get_error(tc->ssl, n);\n\n\t\t\tERR_clear_error();\n\n\t\t\tswitch (ssl_err) {\n\n\t\t\tcase SSL_ERROR_WANT_READ:\n\t\t\t\tbreak;\n\n\t\t\tcase SSL_ERROR_ZERO_RETURN:\n\t\t\t\tconn_close(tc, ECONNRESET);\n\t\t\t\treturn;\n\n\t\t\tdefault:\n\t\t\t\tDEBUG_WARNING(\"read error: %i\\n\", ssl_err);\n\t\t\t\tconn_close(tc, EPROTO);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\tmb->pos += n;\n\t}\n\n\tmbuf_set_posend(mb, 0, mb->pos);\n\n\tif (tc->recvh && mbuf_get_left(mb) > 0)\n\t\ttc->recvh(mb, tc->arg);\n}\n\n\nstatic int conn_alloc(struct tls_conn **ptc, struct tls *tls,\n\t\t      struct dtls_sock *sock, const struct sa *peer,\n\t\t      dtls_estab_h *estabh, dtls_recv_h *recvh,\n\t\t      dtls_close_h *closeh, void *arg)\n{\n\tstruct tls_conn *tc;\n\tint err = 0;\n\n\tif (sock->single_conn) {\n\t\tif (hash_get_first(sock->ht)) {\n\t\t\tDEBUG_WARNING(\"single: only one connection allowed\\n\");\n\t\t\treturn EMFILE;\n\t\t}\n\t}\n\n\ttc = mem_zalloc(sizeof(*tc), conn_destructor);\n\tif (!tc)\n\t\treturn ENOMEM;\n\n\thash_append(sock->ht, sa_hash(peer, SA_ALL), &tc->he, tc);\n\n\ttc->sock   = mem_ref(sock);\n\ttc->peer   = *peer;\n\ttc->estabh = estabh;\n\ttc->recvh  = recvh;\n\ttc->closeh = closeh;\n\ttc->arg    = arg;\n\ttc->tls    = tls;\n\n\ttc->biomet = bio_method_udp();\n\tif (!tc->biomet) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\t/* Connect the SSL socket */\n\ttc->ssl = SSL_new(tls_ssl_ctx(tls));\n\tif (!tc->ssl) {\n\t\tDEBUG_WARNING(\"ssl new failed: %i\\n\",\n\t\t\t      ERR_GET_REASON(ERR_get_error()));\n\t\tERR_clear_error();\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\ttc->sbio_in = BIO_new(BIO_s_mem());\n\tif (!tc->sbio_in) {\n\t\tERR_clear_error();\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\ttc->sbio_out = BIO_new(tc->biomet);\n\tif (!tc->sbio_out) {\n\t\tERR_clear_error();\n\t\tBIO_free(tc->sbio_in);\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tBIO_set_data(tc->sbio_out, tc);\n\n\tSSL_set_bio(tc->ssl, tc->sbio_in, tc->sbio_out);\n\n\tSSL_set_read_ahead(tc->ssl, 1);\n\n out:\n\tif (err)\n\t\tmem_deref(tc);\n\telse\n\t\t*ptc = tc;\n\n\treturn err;\n}\n\n\n/**\n * DTLS Connect\n *\n * @param ptc    Pointer to allocated DTLS connection\n * @param tls    TLS Context\n * @param sock   DTLS Socket\n * @param peer   Peer address\n * @param estabh Establish handler\n * @param recvh  Receive handler\n * @param closeh Close handler\n * @param arg    Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint dtls_connect(struct tls_conn **ptc, struct tls *tls,\n\t\t struct dtls_sock *sock, const struct sa *peer,\n\t\t dtls_estab_h *estabh, dtls_recv_h *recvh,\n\t\t dtls_close_h *closeh, void *arg)\n{\n\tstruct tls_conn *tc;\n\tint err;\n\n\tif (!ptc || !tls || !sock || !peer)\n\t\treturn EINVAL;\n\n\terr = conn_alloc(&tc, tls, sock, peer, estabh, recvh, closeh, arg);\n\tif (err)\n\t\treturn err;\n\n\ttc->active = true;\n\n\terr = tls_connect(tc);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(tc);\n\telse\n\t\t*ptc = tc;\n\n\treturn err;\n}\n\n\n/**\n * DTLS Accept\n *\n * @param ptc    Pointer to allocated DTLS connection\n * @param tls    TLS Context\n * @param sock   DTLS Socket\n * @param estabh Establish handler\n * @param recvh  Receive handler\n * @param closeh Close handler\n * @param arg    Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint dtls_accept(struct tls_conn **ptc, struct tls *tls,\n\t\tstruct dtls_sock *sock,\n\t\tdtls_estab_h *estabh, dtls_recv_h *recvh,\n\t\tdtls_close_h *closeh, void *arg)\n{\n\tstruct tls_conn *tc;\n\tint err, r;\n\n\tif (!ptc || !tls || !sock || !sock->mb)\n\t\treturn EINVAL;\n\n\terr = conn_alloc(&tc, tls, sock, &sock->peer, estabh, recvh, closeh,\n\t\t\t arg);\n\tif (err)\n\t\treturn err;\n\n\ttc->active = false;\n\n\tr = BIO_write(tc->sbio_in, mbuf_buf(sock->mb),\n\t\t      (int)mbuf_get_left(sock->mb));\n\tif (r <= 0) {\n\t\tDEBUG_WARNING(\"accept bio write error: %i\\n\", r);\n\t\tERR_clear_error();\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = tls_accept(tc);\n\tif (err)\n\t\tgoto out;\n\n\tsock->mb = mem_deref(sock->mb);\n\n out:\n\tif (err)\n\t\tmem_deref(tc);\n\telse\n\t\t*ptc = tc;\n\n\treturn err;\n}\n\n\n/**\n * Send data on a DTLS connection\n *\n * @param tc DTLS connection\n * @param mb Buffer to send\n *\n * @return 0 if success, otherwise errorcode\n */\nint dtls_send(struct tls_conn *tc, struct mbuf *mb)\n{\n\tint r;\n\n\tif (!tc || !mb)\n\t\treturn EINVAL;\n\n\tif (!tc->up || !tc->ssl)\n\t\treturn ENOTCONN;\n\n\tERR_clear_error();\n\n\tr = SSL_write(tc->ssl, mbuf_buf(mb), (int)mbuf_get_left(mb));\n\tif (r <= 0) {\n\t\tDEBUG_WARNING(\"write error: %i\\n\", SSL_get_error(tc->ssl, r));\n\t\tERR_clear_error();\n\t\treturn EPROTO;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Set handlers on a DTLS Connection\n *\n * @param tc     DTLS Connection\n * @param estabh DTLS Connection Established handler\n * @param recvh  DTLS Connection Receive data handler\n * @param closeh DTLS Connection Close handler\n * @param arg    Handler argument\n */\nvoid dtls_set_handlers(struct tls_conn *tc, dtls_estab_h *estabh,\n\t\t       dtls_recv_h *recvh, dtls_close_h *closeh, void *arg)\n{\n\tif (!tc)\n\t\treturn;\n\n\ttc->estabh = estabh;\n\ttc->recvh  = recvh;\n\ttc->closeh = closeh;\n\ttc->arg    = arg;\n}\n\n\n/**\n * Get the remote peer of a DTLS Connection\n *\n * @param tc DTLS Connection\n *\n * @return Remote peer\n */\nconst struct sa *dtls_peer(const struct tls_conn *tc)\n{\n\treturn tc ? &tc->peer : NULL;\n}\n\n\n/**\n * Set the remote peer of a DTLS Connection\n *\n * @param tc     DTLS Connection\n * @param peer   Peer address\n */\nvoid dtls_set_peer(struct tls_conn *tc, const struct sa *peer)\n{\n\tif (!tc || !peer)\n\t\treturn;\n\n\thash_unlink(&tc->he);\n\thash_append(tc->sock->ht, sa_hash(peer, SA_ALL), &tc->he, tc);\n\n\ttc->peer = *peer;\n}\n\n\nstatic void sock_destructor(void *arg)\n{\n\tstruct dtls_sock *sock = arg;\n\n\thash_clear(sock->ht);\n\tmem_deref(sock->uh);\n\tmem_deref(sock->us);\n\tmem_deref(sock->ht);\n\tmem_deref(sock->mb);\n}\n\n\nstatic bool cmp_handler(struct le *le, void *arg)\n{\n\tstruct tls_conn *tc = le->data;\n\n\treturn sa_cmp(&tc->peer, arg, SA_ALL);\n}\n\n\nstatic struct tls_conn *conn_lookup(struct dtls_sock *sock,\n\t\t\t\t    const struct sa *peer)\n{\n\tif (sock->single_conn) {\n\t\tstruct le *le = hash_get_first(sock->ht);\n\t\treturn list_ledata(le);\n\t}\n\n\treturn list_ledata(hash_lookup(sock->ht, sa_hash(peer, SA_ALL),\n                                       cmp_handler, (void *)peer));\n}\n\n\nstatic bool recv_handler(struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct dtls_sock *sock = arg;\n\tstruct tls_conn *tc;\n\tuint8_t b;\n\n\tif (mbuf_get_left(mb) < 1)\n\t\treturn false;\n\n\tb = mb->buf[mb->pos];\n\tif (b < 20 || b > 63)\n\t\treturn false;\n\n\tDEBUG_INFO(\"receive '%s' from %J\\n\", content_type_str(b), src);\n\n\ttc = conn_lookup(sock, src);\n\tif (tc) {\n\t\tconn_recv(tc, mb);\n\t\treturn true;\n\t}\n\n\tif (sock->connh) {\n\n\t\tmem_deref(sock->mb);\n\t\tsock->mb   = mem_ref(mb);\n\t\tsock->peer = *src;\n\n\t\tsock->connh(src, sock->arg);\n\t}\n\n\treturn true;\n}\n\n\n/**\n * Create DTLS Socket\n *\n * @param sockp  Pointer to returned DTLS Socket\n * @param laddr  Local listen address (optional)\n * @param us     External UDP socket (optional)\n * @param htsize Connection hash table size\n * @param layer  UDP protocol layer\n * @param connh  Connect handler\n * @param arg    Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint dtls_listen(struct dtls_sock **sockp, const struct sa *laddr,\n\t\tstruct udp_sock *us, uint32_t htsize, int layer,\n\t\tdtls_conn_h *connh, void *arg)\n{\n\tstruct dtls_sock *sock;\n\tint err;\n\n\tif (!sockp)\n\t\treturn EINVAL;\n\n\tsock = mem_zalloc(sizeof(*sock), sock_destructor);\n\tif (!sock)\n\t\treturn ENOMEM;\n\n\tif (us) {\n\t\tsock->us = mem_ref(us);\n\t}\n\telse {\n\t\terr = udp_listen(&sock->us, laddr, NULL, NULL);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = udp_register_helper(&sock->uh, sock->us, layer,\n\t\t\t\t  NULL, recv_handler, sock);\n\tif (err)\n\t\tgoto out;\n\n\terr = hash_alloc(&sock->ht, hash_valid_size(htsize));\n\tif (err)\n\t\tgoto out;\n\n\tsock->mtu   = MTU_DEFAULT;\n\tsock->connh = connh;\n\tsock->arg   = arg;\n\n out:\n\tif (err)\n\t\tmem_deref(sock);\n\telse\n\t\t*sockp = sock;\n\n\treturn err;\n}\n\n\n/**\n * Get the underlying UDP socket of a DTLS Socket\n *\n * @param sock DTLS Socket\n *\n * @return UDP Socket\n */\nstruct udp_sock *dtls_udp_sock(struct dtls_sock *sock)\n{\n\treturn sock ? sock->us : NULL;\n}\n\n\n/**\n * Set MTU on a DTLS Socket\n *\n * @param sock DTLS Socket\n * @param mtu  MTU value\n */\nvoid dtls_set_mtu(struct dtls_sock *sock, size_t mtu)\n{\n\tif (!sock)\n\t\treturn;\n\n\tsock->mtu = mtu;\n}\n\n\n/**\n * Receive a DTLS packet\n *\n * @param sock DTLS Socket\n * @param src  Source network address\n * @param mb   Mbuffer with DTLS packet\n */\nvoid dtls_recv_packet(struct dtls_sock *sock, const struct sa *src,\n\t\t      struct mbuf *mb)\n{\n\tstruct sa addr;\n\n\tif (!sock || !src || !mb)\n\t\treturn;\n\n\taddr = *src;\n\n\trecv_handler(&addr, mb, sock);\n}\n\n\n/**\n * Set single connection mode. If enabled, only one DTLS connection is allowed.\n *\n * @param sock   DTLS Socket\n * @param single True to enable, False to disable\n */\nvoid dtls_set_single(struct dtls_sock *sock, bool single)\n{\n\tif (!sock)\n\t\treturn;\n\n\tsock->single_conn = single;\n}\n"
  },
  {
    "path": "src/tls/stub.c",
    "content": "/**\n * @file tls/stub.c TLS empty stub\n *\n# Copyright (C) 2023 Christian Spielberger\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_srtp.h>\n#include <re_tls.h>\n\n\nint tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile,\n\t      const char *pwd)\n{\n\t(void)tlsp;\n\t(void)method;\n\t(void)keyfile;\n\t(void)pwd;\n\treturn ENOSYS;\n}\n\n\nint tls_add_ca(struct tls *tls, const char *cafile)\n{\n\t(void)tls;\n\t(void)cafile;\n\treturn ENOSYS;\n}\n\nint tls_add_cafile_path(struct tls *tls, const char *cafile,\n\tconst char *capath)\n{\n\t(void)tls;\n\t(void)cafile;\n\t(void)capath;\n\treturn ENOSYS;\n}\n\n\nint tls_add_capem(const struct tls *tls, const char *capem)\n{\n\t(void)tls;\n\t(void)capem;\n\treturn ENOSYS;\n}\n\n\nint tls_add_crlpem(const struct tls *tls, const char *pem)\n{\n\t(void)tls;\n\t(void)pem;\n\treturn ENOSYS;\n}\n\n\nint tls_set_selfsigned_ec(struct tls *tls, const char *cn,\n\tconst char *curve_n)\n{\n\t(void)tls;\n\t(void)cn;\n\t(void)curve_n;\n\treturn ENOSYS;\n}\n\n\nint tls_set_certificate_pem(struct tls *tls, const char *cert, size_t len_cert,\n\t\t\t    const char *key, size_t len_key)\n{\n\t(void)tls;\n\t(void)cert;\n\t(void)len_cert;\n\t(void)key;\n\t(void)len_key;\n\treturn ENOSYS;\n}\n\n\nint tls_set_certificate(struct tls *tls, const char *pem, size_t len)\n{\n\t(void)tls;\n\t(void)pem;\n\t(void)len;\n\treturn ENOSYS;\n}\n\n\nvoid tls_set_verify_client(struct tls *tls)\n{\n\t(void)tls;\n}\n\n\nvoid tls_set_verify_client_trust_all(struct tls *tls)\n{\n\t(void)tls;\n}\n\n\nint tls_set_verify_client_handler(struct tls_conn *tc, int depth,\n\tint (*verifyh) (int ok, void *arg), void *arg)\n{\n\t(void)tc;\n\t(void)depth;\n\t(void)verifyh;\n\t(void)arg;\n\treturn ENOSYS;\n}\n\n\nint tls_set_srtp(struct tls *tls, const char *suites)\n{\n\t(void)tls;\n\t(void)suites;\n\treturn ENOSYS;\n}\n\n\nint tls_fingerprint(const struct tls *tls, enum tls_fingerprint type,\n\t\t    uint8_t *md, size_t size)\n{\n\t(void)tls;\n\t(void)type;\n\t(void)md;\n\t(void)size;\n\treturn ENOSYS;\n}\n\n\nint tls_peer_fingerprint(const struct tls_conn *tc, enum tls_fingerprint type,\n\t\t\t uint8_t *md, size_t size)\n{\n\t(void)tc;\n\t(void)type;\n\t(void)md;\n\t(void)size;\n\treturn ENOSYS;\n}\n\n\nint tls_peer_common_name(const struct tls_conn *tc, char *cn, size_t size)\n{\n\t(void)tc;\n\t(void)cn;\n\t(void)size;\n\treturn ENOSYS;\n}\n\n\nint tls_set_verify_purpose(struct tls *tls, const char *purpose)\n{\n\t(void)tls;\n\t(void)purpose;\n\treturn ENOSYS;\n}\n\n\nint tls_peer_verify(const struct tls_conn *tc)\n{\n\t(void)tc;\n\treturn ENOSYS;\n}\n\n\nint tls_srtp_keyinfo(const struct tls_conn *tc, enum srtp_suite *suite,\n\t\t     uint8_t *cli_key, size_t cli_key_size,\n\t\t     uint8_t *srv_key, size_t srv_key_size)\n{\n\t(void)tc;\n\t(void)suite;\n\t(void)cli_key;\n\t(void)cli_key_size;\n\t(void)srv_key;\n\t(void)srv_key_size;\n\treturn ENOSYS;\n}\n\n\nconst char *tls_cipher_name(const struct tls_conn *tc)\n{\n\t(void)tc;\n\treturn NULL;\n}\n\n\nint tls_set_ciphers(struct tls *tls, const char *cipherv[], size_t count)\n{\n\t(void)tls;\n\t(void)cipherv;\n\t(void)count;\n\treturn ENOSYS;\n}\n\n\nint tls_set_verify_server(struct tls_conn *tc, const char *host)\n{\n\t(void)tc;\n\t(void)host;\n\treturn ENOSYS;\n}\n\n\nint tls_get_issuer(struct tls *tls, struct mbuf *mb)\n{\n\t(void)tls;\n\t(void)mb;\n\treturn ENOSYS;\n}\n\n\nint tls_get_subject(struct tls *tls, struct mbuf *mb)\n{\n\t(void)tls;\n\t(void)mb;\n\treturn ENOSYS;\n}\n\n\nvoid tls_disable_verify_server(struct tls *tls)\n{\n\t(void)tls;\n}\n\n\nint tls_set_min_proto_version(struct tls *tls, int version)\n{\n\t(void)tls;\n\t(void)version;\n\treturn ENOSYS;\n}\n\n\nint tls_set_max_proto_version(struct tls *tls, int version)\n{\n\t(void)tls;\n\t(void)version;\n\treturn ENOSYS;\n}\n\n\nint tls_set_session_reuse(struct tls *tls, int enabled)\n{\n\t(void)tls;\n\t(void)enabled;\n\treturn ENOSYS;\n}\n\n\nbool tls_get_session_reuse(const struct tls_conn *tc)\n{\n\t(void)tc;\n\treturn false;\n}\n\n\nint tls_reuse_session(const struct tls_conn *tc)\n{\n\t(void)tc;\n\treturn ENOSYS;\n}\n\n\nbool tls_session_reused(const struct tls_conn *tc)\n{\n\t(void)tc;\n\treturn false;\n}\n\n\nint tls_update_sessions(const struct tls_conn *tc)\n{\n\t(void)tc;\n\treturn ENOSYS;\n}\n\n\nvoid tls_set_posthandshake_auth(struct tls *tls, int value)\n{\n\t(void)tls;\n\t(void)value;\n}\n\n\nint tls_conn_change_cert(struct tls_conn *tc, const char *file)\n{\n\t(void)tc;\n\t(void)file;\n\treturn ENOSYS;\n}\n\n\nint tls_start_tcp(struct tls_conn **ptc, struct tls *tls,\n\t\t  struct tcp_conn *tcp, int layer)\n{\n\t(void)ptc;\n\t(void)tls;\n\t(void)tcp;\n\t(void)layer;\n\treturn ENOSYS;\n}\n\n\nint tls_verify_client_post_handshake(struct tls_conn *tc)\n{\n\t(void)tc;\n\treturn ENOSYS;\n}\n\n\nconst struct tcp_conn *tls_get_tcp_conn(const struct tls_conn *tc)\n{\n\t(void)tc;\n\treturn NULL;\n}\n\n\nint dtls_listen(struct dtls_sock **sockp, const struct sa *laddr,\n\t\tstruct udp_sock *us, uint32_t htsize, int layer,\n\t\tdtls_conn_h *connh, void *arg)\n{\n\t(void)sockp;\n\t(void)laddr;\n\t(void)us;\n\t(void)htsize;\n\t(void)layer;\n\t(void)connh;\n\t(void)arg;\n\treturn ENOSYS;\n}\n\n\nstruct udp_sock *dtls_udp_sock(struct dtls_sock *sock)\n{\n\t(void)sock;\n\treturn NULL;\n}\n\n\nvoid dtls_set_mtu(struct dtls_sock *sock, size_t mtu)\n{\n\t(void)sock;\n\t(void)mtu;\n}\n\n\nint dtls_connect(struct tls_conn **ptc, struct tls *tls,\n\t\t struct dtls_sock *sock, const struct sa *peer,\n\t\t dtls_estab_h *estabh, dtls_recv_h *recvh,\n\t\t dtls_close_h *closeh, void *arg)\n{\n\t(void)ptc;\n\t(void)tls;\n\t(void)sock;\n\t(void)peer;\n\t(void)estabh;\n\t(void)recvh;\n\t(void)closeh;\n\t(void)arg;\n\treturn ENOSYS;\n}\n\n\nint dtls_accept(struct tls_conn **ptc, struct tls *tls,\n\t\tstruct dtls_sock *sock,\n\t\tdtls_estab_h *estabh, dtls_recv_h *recvh,\n\t\tdtls_close_h *closeh, void *arg)\n{\n\t(void)ptc;\n\t(void)tls;\n\t(void)sock;\n\t(void)estabh;\n\t(void)recvh;\n\t(void)closeh;\n\t(void)arg;\n\treturn ENOSYS;\n}\n\n\nint dtls_send(struct tls_conn *tc, struct mbuf *mb)\n{\n\t(void)tc;\n\t(void)mb;\n\treturn ENOSYS;\n}\n\n\nvoid dtls_set_handlers(struct tls_conn *tc, dtls_estab_h *estabh,\n\t\t       dtls_recv_h *recvh, dtls_close_h *closeh, void *arg)\n{\n\t(void)tc;\n\t(void)estabh;\n\t(void)recvh;\n\t(void)closeh;\n\t(void)arg;\n}\n\n\nconst struct sa *dtls_peer(const struct tls_conn *tc)\n{\n\t(void)tc;\n\treturn NULL;\n}\n\n\nvoid dtls_set_peer(struct tls_conn *tc, const struct sa *peer)\n{\n\t(void)tc;\n\t(void)peer;\n}\n\n\nvoid dtls_recv_packet(struct dtls_sock *sock, const struct sa *src,\n\t\t      struct mbuf *mb)\n{\n\t(void)sock;\n\t(void)src;\n\t(void)mb;\n}\n\n\nvoid dtls_set_single(struct dtls_sock *sock, bool single)\n{\n\t(void)sock;\n\t(void)single;\n}\n\n\nint tls_set_certificate_openssl(struct tls *tls, struct x509_st *cert,\n\t\t\t\tstruct evp_pkey_st *pkey, bool up_ref)\n{\n\t(void)tls;\n\t(void)cert;\n\t(void)pkey;\n\t(void)up_ref;\n\treturn ENOSYS;\n}\n\n\nint tls_add_certf(struct tls *tls, const char *certf, const char *host)\n{\n\t(void)tls;\n\t(void)certf;\n\t(void)host;\n\treturn ENOSYS;\n}\n"
  },
  {
    "path": "src/tmr/tmr.c",
    "content": "/**\n * @file tmr.c  Timer implementation\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#ifdef HAVE_SYS_TIME_H\n#include <sys/time.h>\n#endif\n#include <time.h>\n#include <re_types.h>\n#include <re_list.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_thread.h>\n#include <re_tmr.h>\n#include <re_net.h>\n#include <re_main.h>\n\n\n#define DEBUG_MODULE \"tmr\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n#ifdef WIN32\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#include <windows.h>\n#endif\n\n\n/** Timer values */\nenum {\n\tMAX_BLOCKING = 500   /**< Maximum time spent in handler [ms] */\n};\n\nstruct tmrl {\n\tstruct list list;\n\tmtx_t *lock;\n};\n\n\nstatic void tmrl_destructor(void *arg)\n{\n\tstruct tmrl *tmrl = arg;\n\tstruct le *le;\n\n\tmtx_lock(tmrl->lock);\n\tLIST_FOREACH(&tmrl->list, le) {\n\t\tstruct tmr *tmr = le->data;\n\t\tre_atomic_rls_set(&tmr->llock, (uintptr_t)NULL);\n\t}\n\tlist_clear(&tmrl->list);\n\tmtx_unlock(tmrl->lock);\n\n\tmem_deref(tmrl->lock);\n}\n\n\nint tmrl_alloc(struct tmrl **tmrl)\n{\n\tstruct tmrl *l;\n\tint err;\n\n\tif (!tmrl)\n\t\treturn EINVAL;\n\n\tl = mem_zalloc(sizeof(struct tmrl), NULL);\n\tif (!l)\n\t\treturn ENOMEM;\n\n\tlist_init(&l->list);\n\n\terr = mutex_alloc(&l->lock);\n\tif (err) {\n\t\tmem_deref(l);\n\t\treturn err;\n\t}\n\n\tmem_destructor(l, tmrl_destructor);\n\n\t*tmrl = l;\n\n\treturn 0;\n}\n\n\nstatic bool inspos_handler(struct le *le, void *arg)\n{\n\tstruct tmr *tmr = le->data;\n\tconst uint64_t now = *(uint64_t *)arg;\n\n\treturn tmr->jfs <= now;\n}\n\n\nstatic bool inspos_handler_0(struct le *le, void *arg)\n{\n\tstruct tmr *tmr = le->data;\n\tconst uint64_t now = *(uint64_t *)arg;\n\n\treturn tmr->jfs > now;\n}\n\n\n#if TMR_DEBUG\nstatic void call_handler(tmr_h *th, void *arg)\n{\n\tconst uint64_t tick = tmr_jiffies();\n\tuint32_t diff;\n\n\t/* Call handler */\n\tth(arg);\n\n\tdiff = (uint32_t)(tmr_jiffies() - tick);\n\n\tif (diff > MAX_BLOCKING) {\n\t\tDEBUG_WARNING(\"long async blocking: %u>%u ms (h=%p arg=%p)\\n\",\n\t\t\t      diff, MAX_BLOCKING, th, arg);\n\t}\n}\n#endif\n\n\n/**\n * Poll all timers in the current thread\n *\n * @param tmrl Timer list\n */\nvoid tmr_poll(struct tmrl *tmrl)\n{\n\tconst uint64_t jfs = tmr_jiffies();\n\n\tif (!tmrl)\n\t\treturn;\n\n\tfor (;;) {\n\t\tstruct tmr *tmr;\n\t\ttmr_h *th;\n\t\tvoid *th_arg;\n\n\t\tmtx_lock(tmrl->lock);\n\t\ttmr = list_ledata(tmrl->list.head);\n\n\t\tif (!tmr || (tmr->jfs > jfs)) {\n\t\t\tmtx_unlock(tmrl->lock);\n\t\t\tbreak;\n\t\t}\n\n\t\tth = tmr->th;\n\t\tth_arg = tmr->arg;\n\n\t\ttmr->th = NULL;\n\n\t\tlist_unlink(&tmr->le);\n\t\tre_atomic_rls_set(&tmr->llock, (uintptr_t)NULL);\n\t\tmtx_unlock(tmrl->lock);\n\n\t\tif (!th)\n\t\t\tcontinue;\n\n#if TMR_DEBUG\n\t\tcall_handler(th, th_arg);\n#else\n\t\tth(th_arg);\n#endif\n\t}\n}\n\n\n/**\n * Get the timer jiffies in microseconds\n *\n * @return Jiffies in [us]\n */\nuint64_t tmr_jiffies_usec(void)\n{\n\tuint64_t jfs;\n\n#if defined(WIN32)\n\tLARGE_INTEGER li;\n\tstatic LARGE_INTEGER freq;\n\n\tif (!freq.QuadPart)\n\t\tQueryPerformanceFrequency(&freq);\n\n\tQueryPerformanceCounter(&li);\n\tli.QuadPart *= 1000000;\n\tli.QuadPart /= freq.QuadPart;\n\n\tjfs = li.QuadPart;\n#else\n\tstruct timespec now;\n\tclockid_t clock_id;\n\n\t/* Use CLOCK_MONOTONIC_RAW, if available,\n\t   which is not subject to adjustment by NTP */\n#ifdef CLOCK_MONOTONIC_RAW\n\tclock_id = CLOCK_MONOTONIC_RAW;\n#else\n\tclock_id = CLOCK_MONOTONIC;\n#endif\n\n\tif (0 != clock_gettime(clock_id, &now)) {\n\t\tDEBUG_WARNING(\"jiffies: clock_gettime() failed (%m)\\n\", errno);\n\t\treturn 0;\n\t}\n\n\tjfs  = (long)now.tv_sec * (uint64_t)1000000;\n\tjfs += now.tv_nsec/1000;\n#endif\n\n\treturn jfs;\n}\n\n\n/**\n * Get the timer jiffies in milliseconds\n *\n * @return Jiffies in [ms]\n */\nuint64_t tmr_jiffies(void)\n{\n\treturn tmr_jiffies_usec() / 1000;\n}\n\n\n/**\n * Obtain the current realtime wallclock time in microseconds since UNIX epoch\n *\n * @return realtime wallclock time in microseconds since UNIX epoch\n */\nuint64_t tmr_jiffies_rt_usec(void)\n{\n\tuint64_t jfs_rt;\n#if defined(WIN32)\n\tFILETIME now;\n\tGetSystemTimeAsFileTime(&now);\n\tjfs_rt = (((uint64_t)now.dwHighDateTime) << 32u) |\n\t\t(uint64_t)now.dwLowDateTime;\n\tjfs_rt -= 116444736000000000ull;\n\tjfs_rt /= 10u;\n#else\n\tstruct timespec now;\n\tif (0 != clock_gettime(CLOCK_REALTIME, &now)) {\n\t\tDEBUG_WARNING(\"jiffies_rt: clock_gettime() failed (%m)\\n\",\n\t\t\t      errno);\n\t\treturn 0;\n\t}\n\n\tjfs_rt  = (uint64_t)now.tv_sec * (uint64_t)1000000u;\n\tjfs_rt += now.tv_nsec / 1000;\n#endif\n\n\treturn jfs_rt;\n}\n\n\n/**\n * Modifies the timespec object to current calendar time (TIME_UTC)\n *\n * @param tp     Pointer to timespec object\n * @param offset Offset in [ms]\n *\n * @return 0 if success, otherwise errorcode\n */\nint tmr_timespec_get(struct timespec *tp, uint64_t offset)\n{\n\tint err;\n\n\tif (!tp)\n\t\treturn EINVAL;\n\n#if defined(WIN32) && !defined(__MINGW32__)\n\terr = (timespec_get(tp, TIME_UTC) == TIME_UTC) ? 0 : EINVAL;\n#else\n\terr = (clock_gettime(CLOCK_REALTIME, tp) == 0) ? 0 : errno;\n#endif\n\n\tif (err)\n\t\treturn err;\n\n\tif (offset) {\n\t\ttp->tv_sec += (offset / 1000);\n\t\ttp->tv_nsec += ((offset * 1000000) % 1000000000LL);\n\t\twhile (tp->tv_nsec > 1000000000LL) {\n\t\t\ttp->tv_sec += 1;\n\t\t\ttp->tv_nsec -= 1000000000LL;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Get number of milliseconds until the next timer expires\n *\n * @param tmrl Timer-list\n *\n * @return Number of [ms], or 0 if no active timers\n */\nuint64_t tmr_next_timeout(struct tmrl *tmrl)\n{\n\tconst uint64_t jif = tmr_jiffies();\n\tconst struct tmr *tmr;\n\tuint64_t ret = 0;\n\n\tif (!tmrl)\n\t\treturn 0;\n\n\tmtx_lock(tmrl->lock);\n\n\ttmr = list_ledata(tmrl->list.head);\n\tif (!tmr)\n\t\tgoto out;\n\n\tif (tmr->jfs <= jif)\n\t\tret = 1;\n\telse\n\t\tret = tmr->jfs - jif;\n\nout:\n\tmtx_unlock(tmrl->lock);\n\n\treturn ret;\n}\n\n\nint tmr_status(struct re_printf *pf, void *unused)\n{\n\tstruct tmrl *tmrl = re_tmrl_get();\n\tstruct le *le;\n\tuint32_t n;\n\tint err = 0;\n\n\t(void)unused;\n\n\tif (!tmrl)\n\t\treturn EINVAL;\n\n\tmtx_lock(tmrl->lock);\n\n\tn = list_count(&tmrl->list);\n\tif (!n)\n\t\tgoto out;\n\n\terr = re_hprintf(pf, \"Timers (%u):\\n\", n);\n\n\tfor (le = tmrl->list.head; le; le = le->next) {\n\t\tconst struct tmr *tmr = le->data;\n\t\terr |= re_hprintf(pf, \"  %p: th=%p expire=%llums file=%s:%d\\n\",\n\t\t\t\t  tmr, tmr->th,\n\t\t\t\t  (unsigned long long)tmr_get_expire(tmr),\n\t\t\t\t  tmr->file, tmr->line);\n\t}\n\n\tif (n > 100)\n\t\terr |= re_hprintf(pf, \"    (Dumped Timers: %u)\\n\", n);\n\nout:\n\tmtx_unlock(tmrl->lock);\n\treturn err;\n}\n\n\n/**\n * Print timer debug info to stderr\n */\nvoid tmr_debug(void)\n{\n\t(void)re_fprintf(stderr, \"%H\", tmr_status, NULL);\n}\n\n\n/**\n * Initialise a timer object\n *\n * @param tmr Timer to initialise\n */\nvoid tmr_init(struct tmr *tmr)\n{\n\tif (!tmr)\n\t\treturn;\n\n\tmemset(tmr, 0, sizeof(*tmr));\n}\n\n\nstatic void tmr_startcont_dbg(struct tmr *tmr, uint64_t delay, bool syncnow,\n                   tmr_h *th, void *arg,\n\t\t   const char *file, int line)\n{\n\tstruct tmrl *tmrl = re_tmrl_get();\n\tstruct le *le;\n\n\tif (!tmr || !tmrl)\n\t\treturn;\n\n\t/* use old list lock for unlinking */\n\tuintptr_t lock_t = (uintptr_t)re_atomic_acq(&tmr->llock);\n\tmtx_t *lock = (mtx_t *)lock_t;\n\tif (!lock)\n\t\tlock = tmrl->lock; /* use current list lock */\n\n\tmtx_lock(lock);\n\n\tif (tmr->th)\n\t\tlist_unlink(&tmr->le);\n\n\tmtx_unlock(lock);\n\n\tlock = tmrl->lock;\n\n\tif (!th) {\n\t\tre_atomic_rls_set(&tmr->llock, (uintptr_t)NULL);\n\t\treturn;\n\t}\n\n\tre_atomic_rls_set(&tmr->llock, (uintptr_t)tmrl->lock);\n\n\tmtx_lock(lock);\n\n\ttmr->th\t  = th;\n\ttmr->arg  = arg;\n\ttmr->file = file;\n\ttmr->line = line;\n\n\tif (syncnow)\n\t\ttmr->jfs = tmr_jiffies();\n\ttmr->jfs += delay;\n\n\tif (delay == 0) {\n\t\tle = list_apply(&tmrl->list, true, inspos_handler_0,\n\t\t\t\t&tmr->jfs);\n\t\tif (le) {\n\t\t\tlist_insert_before(&tmrl->list, le, &tmr->le, tmr);\n\t\t}\n\t\telse {\n\t\t\tlist_append(&tmrl->list, &tmr->le, tmr);\n\t\t}\n\t}\n\telse {\n\t\tle = list_apply(&tmrl->list, false, inspos_handler, &tmr->jfs);\n\t\tif (le) {\n\t\t\tlist_insert_after(&tmrl->list, le, &tmr->le, tmr);\n\t\t}\n\t\telse {\n\t\t\tlist_prepend(&tmrl->list, &tmr->le, tmr);\n\t\t}\n\t}\n\n\tmtx_unlock(lock);\n}\n\n\nvoid tmr_start_dbg(struct tmr *tmr, uint64_t delay, tmr_h *th, void *arg,\n\t\t   const char *file, int line)\n{\n\ttmr_startcont_dbg(tmr, delay, true, th, arg, file, line);\n}\n\n\nvoid tmr_continue_dbg(struct tmr *tmr, uint64_t delay, tmr_h *th, void *arg,\n\t\t   const char *file, int line)\n{\n\ttmr_startcont_dbg(tmr, delay, false, th, arg, file, line);\n}\n\n\n/**\n * Cancel an active timer\n *\n * @param tmr Timer to cancel\n */\nvoid tmr_cancel(struct tmr *tmr)\n{\n\ttmr_start(tmr, 0, NULL, NULL);\n}\n\n\n/**\n * Get the time left until timer expires\n *\n * @param tmr Timer object\n *\n * @return Time in [ms] until expiration\n */\nuint64_t tmr_get_expire(const struct tmr *tmr)\n{\n\tuint64_t jfs;\n\n\tif (!tmr || !tmr->th)\n\t\treturn 0;\n\n\tjfs = tmr_jiffies();\n\n\treturn (tmr->jfs > jfs) ? (tmr->jfs - jfs) : 0;\n}\n\n\n/**\n * Get current timer list count\n *\n * @param tmrl Timer list object\n *\n * @return timer list count\n */\nuint32_t tmrl_count(struct tmrl *tmrl)\n{\n\tuint32_t c;\n\n\tif (!tmrl)\n\t\treturn 0;\n\n\tmtx_lock(tmrl->lock);\n\tc = list_count(&tmrl->list);\n\tmtx_unlock(tmrl->lock);\n\n\treturn c;\n}\n"
  },
  {
    "path": "src/trace/trace.c",
    "content": "/**\n * @file trace.c RE_TRACE helpers\n * JSON traces (chrome://tracing)\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_trace.h>\n#include <re_fmt.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_thread.h>\n#include <re_atomic.h>\n#include <re_sys.h>\n#include <re_mbuf.h>\n#include <re_main.h>\n\n#ifdef HAVE_PTHREAD\n#include <pthread.h>\n#endif\n\n#if defined(WIN32)\n#include <windows.h>\n#endif\n\n#ifdef LINUX\n#include <sys/syscall.h>\n#endif\n\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n\n#define DEBUG_MODULE \"trace\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n#ifndef TRACE_BUFFER_SIZE\n#define TRACE_BUFFER_SIZE 100000\n#endif\n\n#ifndef TRACE_FLUSH_THRESHOLD\n#define TRACE_FLUSH_THRESHOLD 1000\n#endif\n\n#ifndef TRACE_FLUSH_TMR\n#define TRACE_FLUSH_TMR 1000\n#endif\n\n\n#ifdef RE_TRACE_ENABLED\n\n\n/** Trace configuration */\nstatic struct {\n\tRE_ATOMIC bool init;\n\tint process_id;\n\tFILE *f;\n\tint event_count;\n\tstruct re_trace_event_s *event_buffer;\n\tstruct re_trace_event_s *event_buffer_flush;\n\tre_trace_line_h *trace_h;\n\tmtx_t lock;\n\tbool new;\n\tuint64_t start_time;\n\tstruct tmr flush_tmr;\n\tbool flush_active;\n} trace = {\n\t.init = false\n};\n\n\nstatic inline unsigned long get_thread_id(void)\n{\n#if defined(WIN32)\n\treturn (unsigned long)GetCurrentThreadId();\n#elif defined(LINUX)\n\treturn (unsigned long)syscall(SYS_gettid);\n#elif defined(HAVE_PTHREAD)\n#if defined(DARWIN) || defined(FREEBSD) || defined(OPENBSD) || \\\n\tdefined(NETBSD) || defined(DRAGONFLY)\n\treturn (unsigned long)(void *)pthread_self();\n#else\n\treturn (unsigned long)pthread_self();\n#endif\n#else\n\treturn 0;\n#endif\n}\n\n\nstatic inline int get_process_id(void)\n{\n#if defined(WIN32)\n\treturn (int)GetCurrentProcessId();\n#else\n\treturn (int)getpid();\n#endif\n}\n\n\nstatic int flush_worker(void *arg)\n{\n\t(void)arg;\n\n\tre_trace_flush();\n\n\treturn 0;\n}\n\n\nstatic void flush_tmr(void *arg)\n{\n\t(void)arg;\n\n\tmtx_lock(&trace.lock);\n\tif (!trace.flush_active &&\n\t    trace.event_count >= TRACE_FLUSH_THRESHOLD) {\n\t\tre_thread_async(flush_worker, NULL, NULL);\n\t}\n\tmtx_unlock(&trace.lock);\n\n\ttmr_start(&trace.flush_tmr, TRACE_FLUSH_TMR, flush_tmr, NULL);\n}\n#endif\n\n\n/**\n * Init new trace json file\n *\n * @param json_file  json file for trace events\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_trace_init(const char *json_file)\n{\n#ifdef RE_TRACE_ENABLED\n\tint err = 0;\n\n\tif (!json_file)\n\t\treturn EINVAL;\n\n\tif (re_atomic_rlx(&trace.init))\n\t\treturn EALREADY;\n\n\ttrace.event_buffer = mem_zalloc(\n\t\tTRACE_BUFFER_SIZE * sizeof(struct re_trace_event_s), NULL);\n\tif (!trace.event_buffer)\n\t\treturn ENOMEM;\n\n\ttrace.event_buffer_flush = mem_zalloc(\n\t\tTRACE_BUFFER_SIZE * sizeof(struct re_trace_event_s), NULL);\n\tif (!trace.event_buffer_flush) {\n\t\ttrace.event_buffer = mem_deref(trace.event_buffer);\n\t\treturn ENOMEM;\n\t}\n\n\terr = mtx_init(&trace.lock, mtx_plain) != thrd_success;\n\tif (err) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = fs_fopen(&trace.f, json_file, \"w+\");\n\tif (err)\n\t\tgoto out;\n\n\t(void)re_fprintf(trace.f, \"{\\t\\n\\t\\\"traceEvents\\\": [\\n\");\n\t(void)fflush(trace.f);\n\n\ttrace.start_time = tmr_jiffies_usec();\n\tre_atomic_rlx_set(&trace.init, true);\n\ttrace.new = true;\n\n\ttmr_init(&trace.flush_tmr);\n\ttmr_start(&trace.flush_tmr, TRACE_FLUSH_TMR, flush_tmr, NULL);\n\nout:\n\tif (err) {\n\t\tre_atomic_rlx_set(&trace.init, false);\n\t\ttrace.event_buffer\t = mem_deref(trace.event_buffer);\n\t\ttrace.event_buffer_flush = mem_deref(trace.event_buffer_flush);\n\t}\n\n\treturn err;\n#else\n\t(void)json_file;\n\treturn 0;\n#endif\n}\n\n\nvoid re_set_trace_line_h(re_trace_line_h *trace_h)\n{\n#ifndef RE_TRACE_ENABLED\n\t(void)trace_h;\n#else\n\ttrace.trace_h = trace_h;\n#endif\n}\n\n\n/**\n * Close and flush trace file\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_trace_close(void)\n{\n#ifdef RE_TRACE_ENABLED\n\tint err = 0;\n\n\ttmr_cancel(&trace.flush_tmr);\n\tre_trace_flush();\n\tre_atomic_rlx_set(&trace.init, false);\n\n\ttrace.event_buffer = mem_deref(trace.event_buffer);\n\ttrace.event_buffer_flush = mem_deref(trace.event_buffer_flush);\n\tmtx_destroy(&trace.lock);\n\n\t(void)re_fprintf(trace.f, \"\\n\\t]\\n}\\n\");\n\tif (trace.f)\n\t\terr = fclose(trace.f);\n\n\tif (err)\n\t\treturn errno;\n\n\ttrace.f = NULL;\n\n\treturn 0;\n#else\n\treturn 0;\n#endif\n}\n\n\n/**\n * Flush trace buffer (can be called multiple times)\n *\n * @return 0 if success, otherwise errorcode\n */\nint re_trace_flush(void)\n{\n#ifdef RE_TRACE_ENABLED\n\tchar *json_arg\t = NULL;\n\tchar name[128]\t = {0};\n\tchar id_str[128] = {0};\n\tint err\t\t = 0;\n\n\tif (!re_atomic_rlx(&trace.init))\n\t\treturn 0;\n\n\tmtx_lock(&trace.lock);\n\tif (trace.flush_active) {\n\t\tmtx_unlock(&trace.lock);\n\t\treturn EALREADY;\n\t}\n\n\ttrace.flush_active = true;\n\n\tstruct re_trace_event_s *event_tmp = trace.event_buffer_flush;\n\ttrace.event_buffer_flush = trace.event_buffer;\n\ttrace.event_buffer = event_tmp;\n\n\tint flush_count = trace.event_count;\n\ttrace.event_count = 0;\n\tmtx_unlock(&trace.lock);\n\n\tstruct mbuf *mb = mbuf_alloc(1024);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tsize_t json_arg_sz = 4096;\n\tjson_arg = mem_zalloc(json_arg_sz, NULL);\n\tif (!json_arg) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tfor (int i = 0; i < flush_count; i++)\n\t{\n\t\tstruct re_trace_event_s *e = &trace.event_buffer_flush[i];\n\n\t\tswitch (e->arg_type) {\n\t\tcase RE_TRACE_ARG_NONE:\n\t\t\tjson_arg[0] = '\\0';\n\t\t\tbreak;\n\t\tcase RE_TRACE_ARG_INT:\n\t\t\t(void)re_snprintf(json_arg, json_arg_sz,\n\t\t\t\t\t\", \\\"args\\\":{\\\"%s\\\":%i}\",\n\t\t\t\t\te->arg_name, e->arg.a_int);\n\t\t\tbreak;\n\t\tcase RE_TRACE_ARG_STRING_CONST:\n\t\t\t(void)re_snprintf(json_arg, json_arg_sz,\n\t\t\t\t\t\", \\\"args\\\":{\\\"%s\\\":\\\"%s\\\"}\",\n\t\t\t\t\te->arg_name, e->arg.a_str);\n\t\t\tbreak;\n\t\tcase RE_TRACE_ARG_STRING_COPY:\n\t\t\t(void)re_snprintf(json_arg, json_arg_sz,\n\t\t\t\t\t\", \\\"args\\\":{\\\"%s\\\":\\\"%s\\\"}\",\n\t\t\t\t\te->arg_name, e->arg.a_str);\n\n\t\t\tmem_deref((void *)e->arg.a_str);\n\t\t\tbreak;\n\t\t}\n\n\t\tre_snprintf(name, sizeof(name), \"\\\"name\\\":\\\"%s\\\"\", e->name);\n\n\t\tif (e->id) {\n\t\t\tre_snprintf(id_str, sizeof(id_str), \", \\\"id\\\":\\\"%r\\\"\",\n\t\t\t\t    e->id);\n\t\t}\n\n\t\tmbuf_printf(\n\t\t\tmb,\n\t\t\t\"{\\\"cat\\\":\\\"%s\\\",\\\"pid\\\":%i,\\\"tid\\\":%lu,\\\"ts\\\":%Lu,\"\n\t\t\t\"\\\"ph\\\":\\\"%c\\\",%s%s%s}\",\n\t\t\te->cat, e->pid, e->tid, e->ts - trace.start_time,\n\t\t\te->ph, name, e->id ? id_str : \"\",\n\t\t\tstr_isset(json_arg) ? json_arg : \"\");\n\n\t\tmbuf_set_pos(mb, 0);\n\t\tif (trace.trace_h)\n\t\t\ttrace.trace_h(e, mb);\n\t\tmbuf_set_pos(mb, 0);\n\n\t\t(void)re_fprintf(trace.f, \"%s%b\", trace.new ? \"\" : \",\\n\",\n\t\t\t\t mbuf_buf(mb), mbuf_get_left(mb));\n\t\ttrace.new = false;\n\n\t\tmem_deref(e->id);\n\t\tmbuf_rewind(mb);\n\t}\n\n\nout:\n\tif (err) {\n\t\tfor (int i = 0; i < flush_count; i++) {\n\t\t\tstruct re_trace_event_s *e =\n\t\t\t\t&trace.event_buffer_flush[i];\n\t\t\tif (e->arg_type == RE_TRACE_ARG_STRING_COPY)\n\t\t\t\tmem_deref((void *)e->arg.a_str);\n\n\t\t\tif (e->id)\n\t\t\t\tmem_deref(e->id);\n\t\t}\n\t}\n\tmem_deref(json_arg);\n\tmem_deref(mb);\n\t(void)fflush(trace.f);\n\n\tmtx_lock(&trace.lock);\n\ttrace.flush_active = false;\n\tmtx_unlock(&trace.lock);\n\n\treturn err;\n#else\n\treturn 0;\n#endif\n}\n\n\nvoid re_trace_event(const char *cat, const char *name, char ph, struct pl *id,\n\t\t    re_trace_arg_type arg_type, const char *arg_name,\n\t\t    void *arg_value)\n{\n#ifdef RE_TRACE_ENABLED\n\tstruct re_trace_event_s *e;\n\n\tif (!re_atomic_rlx(&trace.init))\n\t\treturn;\n\n\tmtx_lock(&trace.lock);\n\tif (trace.event_count >= TRACE_BUFFER_SIZE) {\n\t\tDEBUG_WARNING(\"Increase TRACE_BUFFER_SIZE\\n\");\n\t\tmtx_unlock(&trace.lock);\n\t\treturn;\n\t}\n\te = &trace.event_buffer[trace.event_count];\n\t++trace.event_count;\n\tmtx_unlock(&trace.lock);\n\n\te->ts = tmr_jiffies_usec();\n\te->id = mem_ref(id);\n\te->ph = ph;\n\te->cat = cat;\n\te->name = name;\n\te->pid = get_process_id();\n\te->tid = get_thread_id();\n\te->arg_type = arg_type;\n\te->arg_name = arg_name;\n\n\tswitch (arg_type) {\n\tcase RE_TRACE_ARG_NONE:\n\t\tbreak;\n\tcase RE_TRACE_ARG_INT:\n\t\te->arg.a_int = (int)(intptr_t)arg_value;\n\t\tbreak;\n\tcase RE_TRACE_ARG_STRING_CONST:\n\t\te->arg.a_str = (const char *)arg_value;\n\t\tbreak;\n\tcase RE_TRACE_ARG_STRING_COPY:\n\t\tstr_dup((char **)&e->arg.a_str,\n\t\t\t(const char *)arg_value);\n\t\tbreak;\n\t}\n#else\n\t(void)cat;\n\t(void)name;\n\t(void)ph;\n\t(void)id;\n\t(void)arg_type;\n\t(void)arg_name;\n\t(void)arg_value;\n#endif\n}\n"
  },
  {
    "path": "src/trice/README.md",
    "content": "ICE-NOTES:\n---------\n\n\n\n----------------------------------\nApplication layer:\n\n- Can handle any modes (Full, Ice, Trickle)\n- Can implement any nomination-type (regular, aggressive)\n- Can encode/decode SDP attributes\n- Must gather all candidates (including STUN/TURN servers)\n- Application can choose the Default Local Candidate\n- Application can choose the Default Remote Candidate\n- Can measure RTT end-to-end\n- Can apply a STUN consent timer on top of ICE\n- the application should have the freedom to choose any selected candidate-pair\n- can install Keep-Alive timer (Binding Indication) for any pairs\n- can install a consent timer for any pair\n\n\n----------------------------------\nICE layer:\n\n- All modes (Full-ICE and Trickle-ICE) are implemented\n- ICE-lite is NOT supported\n- agnostic to modes (Full, Trickle)\n- agnostic to nomination-type (regular, aggressive)\n- SDP encoding and decoding of ICE-relevant attributes\n- can handle between 1 and 2 components per media-stream\n- gathering: No candidate gathering, must be done in App\n- agnostic to transport protocol (should handle both UDP and TCP)\n- rel-addr (related address) is supported, but it is not used in the logic\n- ICE-stack does not choose the Default Local Candidate\n- ICE-stack does not choose the Default Remote Candidate\n- no TURN client\n- Each local candidate can have its own listen address/port. Yes\n- Support for UDP-transport\n- Support for TCP-transport\n- Support for IPv4 and IPv6\n- modular design with building blocks.\n- check all components before calling estabh ? no.\n- no support for \"default\" candidate/candidate-pair\n- component object: NO\n- selected pair: NO  \n- must be able to support custom UDP/TCP-transport via helpers\n\n\n----------------------------------\nInterop:\n\nOK - Firefox 31.0\nOK - Firefox 35.0\nOK - Firefox 36.0\nOK - Chrome 41\nOK - Chrome 58\n\n\nTODO:\n\ndone - remove ICE-lite mode\ndone - remove rel-addr from ICE-code\ndone - interop testing with Chrome\ndone - interop testing with Firefox - seems to be working with 31.0\ndone - Firefox: test with trickling candidates over Websock\ndone - add support for TCP-candidates (test with Chrome)\ndone - make a new test-client (reicec) and test on a public server\ndone - do we need to support LITE at all? No.\ndone - split EOC flag into local_eoc and remote_eoc\ndone - send triggered request from stunsrv\ndone - new module \"icesdp\" for SDP encoding/decoding\ndone - add a new module \"shim\" (RFC 4571)\n     - ICE module should be Conncheck-only, no data-transport\n     - test_ice_tcp: S-O not working on Linux\ndone - check when adding PRFLX that EOC-flag is set/unset (not needed)\ndone - move use_cand flag to checklist_start/send_conncheck ?\n     - verify that APP can grab udp/tcp-sock\n     - consider moving pacing-logic to application?\n\n\n\n\nArchitecture Diagram:\n--------------------\n\n\n\n```\n\n  .-------.                  .-------. \n  |  App  |                  |  App  |\n  '-------'                  '-------'\n      |                          |           \\\n      |        .--------.        |           |\n      +--------+  STUN  +--------+           |\n      |        '--------'        |           | \"ICE-layer\"\n      |                      .-------.       |\n      |                      | SHIM  |       |\n      |                      '-------'       |\n      |                          |           /\n  .-------.                  .-------.\n  |  UDP  |                  |  TCP  |\n  '-------'                  '-------'\n      |                          |\n      !                          !\n\n\n\n```\n"
  },
  {
    "path": "src/trice/cand.c",
    "content": "/**\n * @file cand.c  Common ICE Candidates\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_net.h>\n#include <re_sys.h>\n#include <re_stun.h>\n#include <re_udp.h>\n#include <re_tcp.h>\n#include <re_ice.h>\n#include <re_trice.h>\n#include \"trice.h\"\n\n\n/**\n * Get the string for the ICE TCP type\n *\n * @param tcptype ICE tcp type\n *\n * @return String with ICE-TCP type name\n */\nconst char *ice_tcptype_name(enum ice_tcptype tcptype)\n{\n\tswitch (tcptype) {\n\n\tcase ICE_TCP_ACTIVE:  return \"active\";\n\tcase ICE_TCP_PASSIVE: return \"passive\";\n\tcase ICE_TCP_SO:      return \"so\";\n\tdefault: return \"???\";\n\t}\n}\n\n\n/**\n * Get the reverse TCP-type\n *\n * @param type ICE tcp type\n *\n * @return The reverse ICE-TCP type\n *\n * \\verbatim\n   Local           Remote\n   Candidate       Candidate\n   ---------------------------\n   tcp-so          tcp-so\n   tcp-active      tcp-passive\n   tcp-passive     tcp-active\n\n * \\endverbatim\n */\nenum ice_tcptype ice_tcptype_reverse(enum ice_tcptype type)\n{\n\tswitch (type) {\n\n\tcase ICE_TCP_SO:      return ICE_TCP_SO;\n\tcase ICE_TCP_ACTIVE:  return ICE_TCP_PASSIVE;\n\tcase ICE_TCP_PASSIVE: return ICE_TCP_ACTIVE;\n\tdefault:              return (enum ice_tcptype)-1;\n\t}\n}\n\n\n/**\n * Get the base type of the candidate type\n *\n * @param type Candidate type\n *\n * @return Base candidate type\n */\nenum ice_cand_type ice_cand_type_base(enum ice_cand_type type)\n{\n\tswitch (type) {\n\n\tcase ICE_CAND_TYPE_HOST:    return ICE_CAND_TYPE_HOST;\n\tcase ICE_CAND_TYPE_SRFLX:   return ICE_CAND_TYPE_HOST;\n\tcase ICE_CAND_TYPE_PRFLX:   return ICE_CAND_TYPE_HOST;\n\tcase ICE_CAND_TYPE_RELAY:   return ICE_CAND_TYPE_RELAY;\n\tdefault: return type;\n\t}\n}\n\n\n/**\n * Print debug information for the ICE candidate\n *\n * @param pf   Print function for debug output\n * @param cand ICE candidate\n *\n * @return 0 if success, otherwise errorcode\n */\nint trice_cand_print(struct re_printf *pf, const struct ice_cand_attr *cand)\n{\n\tint err = 0;\n\n\tif (!cand)\n\t\treturn 0;\n\n\terr |= re_hprintf(pf, \"%s|%s\", ice_cand_type2name(cand->type),\n\t\t\t  net_proto2name(cand->proto));\n\n\tif (cand->proto == IPPROTO_TCP) {\n\n\t\terr |= re_hprintf(pf, \".%s\", ice_tcptype_name(cand->tcptype));\n\t}\n\n\terr |= re_hprintf(pf, \"|%J\", &cand->addr);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/trice/candpair.c",
    "content": "/**\n * @file candpair.c  ICE Candidate Pairs\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_udp.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include <re_trice.h>\n#include \"trice.h\"\n\n\n#define DEBUG_MODULE \"candpair\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/*\n * generic routines to operate on \"struct ice_candpair\"\n * (for both checkl and validl)\n */\n\n\n/*\n * g = controlling agent\n * d = controlled agent\n\n pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)\n\n */\nstatic uint64_t ice_calc_pair_prio(uint32_t g, uint32_t d)\n{\n\tconst uint64_t m = min(g, d);\n\tconst uint64_t x = max(g, d);\n\n\treturn (m<<32) + 2*x + (g>d?1:0);\n}\n\n\nstatic void candpair_destructor(void *arg)\n{\n\tstruct ice_candpair *cp = arg;\n\n\tlist_unlink(&cp->le);\n\tmem_deref(cp->lcand);\n\tmem_deref(cp->rcand);\n\tmem_deref(cp->tc);\n\n\tmem_deref(cp->conn);\n}\n\n\nstatic bool sort_handler(struct le *le1, struct le *le2, void *arg)\n{\n\tconst struct ice_candpair *cp1 = le1->data, *cp2 = le2->data;\n\t(void)arg;\n\n\treturn cp1->pprio >= cp2->pprio;\n}\n\n\nstatic void candpair_set_pprio(struct ice_candpair *cp, bool controlling)\n{\n\tuint32_t g, d;\n\n\tif (controlling) {\n\t\tg = cp->lcand->attr.prio;\n\t\td = cp->rcand->attr.prio;\n\t}\n\telse {\n\t\tg = cp->rcand->attr.prio;\n\t\td = cp->lcand->attr.prio;\n\t}\n\n\tcp->pprio = ice_calc_pair_prio(g, d);\n}\n\n\n/**\n * Add candidate pair to list, sorted by pair priority (highest is first)\n */\nstatic void list_add_sorted(struct list *list, struct ice_candpair *cp)\n{\n\tstruct le *le;\n\n\t/* find our slot */\n\tfor (le = list_tail(list); le; le = le->prev) {\n\t\tstruct ice_candpair *cp0 = le->data;\n\n\t\tif (cp->pprio < cp0->pprio) {\n\t\t\tlist_insert_after(list, le, &cp->le, cp);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tlist_prepend(list, &cp->le, cp);\n}\n\n\nint trice_candpair_alloc(struct ice_candpair **cpp, struct trice *icem,\n\t\t\tstruct ice_lcand *lcand, struct ice_rcand *rcand)\n{\n\tstruct ice_candpair *cp;\n\n\tif (!icem || !lcand || !rcand)\n\t\treturn EINVAL;\n\n\tif (icem->lrole == ICE_ROLE_UNKNOWN) {\n\t\tDEBUG_WARNING(\"trice_candpair_alloc: invalid local role!\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tcp = mem_zalloc(sizeof(*cp), candpair_destructor);\n\tif (!cp)\n\t\treturn ENOMEM;\n\n\tcp->lcand = mem_ref(lcand);\n\tcp->rcand = mem_ref(rcand);\n\tcp->state = ICE_CANDPAIR_FROZEN;\n\n\tcandpair_set_pprio(cp, icem->lrole == ICE_ROLE_CONTROLLING);\n\n\tlist_add_sorted(&icem->checkl, cp);\n\n\tif (cpp)\n\t\t*cpp = cp;\n\n\treturn 0;\n}\n\n\n/* Computing Pair Priority and Ordering Pairs */\nvoid trice_candpair_prio_order(struct list *lst, bool controlling)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tcandpair_set_pprio(cp, controlling);\n\t}\n\n\tlist_sort(lst, sort_handler, NULL);\n}\n\n\nvoid trice_candpair_make_valid(struct trice *icem, struct ice_candpair *pair)\n{\n\tif (!icem || !pair)\n\t\treturn;\n\n\tif (pair->state == ICE_CANDPAIR_FAILED) {\n\t\tDEBUG_WARNING(\"make_valid: pair already FAILED [%H]\\n\",\n\t\t\t      trice_candpair_debug, pair);\n\t}\n\n\tpair->err = 0;\n\tpair->scode = 0;\n\tpair->valid = true;\n\n\ttrice_candpair_set_state(pair, ICE_CANDPAIR_SUCCEEDED);\n\n\tlist_unlink(&pair->le);\n\tlist_add_sorted(&icem->validl, pair);\n}\n\n\nvoid trice_candpair_failed(struct ice_candpair *cp, int err, uint16_t scode)\n{\n\tif (!cp)\n\t\treturn;\n\n\tif (cp->state == ICE_CANDPAIR_SUCCEEDED) {\n\t\tDEBUG_WARNING(\"set_failed(%m): pair already SUCCEEDED [%H]\\n\",\n\t\t\t      err, trice_candpair_debug, cp);\n\t}\n\n\tcp->err = err;\n\tcp->scode = scode;\n\tcp->valid = false;\n\n\tcp->conn = mem_deref(cp->conn);\n\n\ttrice_candpair_set_state(cp, ICE_CANDPAIR_FAILED);\n}\n\n\nvoid trice_candpair_set_state(struct ice_candpair *pair,\n\t\t\t     enum ice_candpair_state state)\n{\n\tif (!pair)\n\t\treturn;\n\tif (pair->state == state)\n\t\treturn;\n\n\tif (trice_candpair_iscompleted(pair)) {\n\t\tDEBUG_WARNING(\"set_state(%s): pair is already completed\"\n\t\t\t      \" [%H]\\n\",\n\t\t\t      trice_candpair_state2name(state),\n\t\t\t      trice_candpair_debug, pair);\n\t}\n\n#if 0\n\ttrice_printf(pair->lcand->icem,\n\t\t    \"%H new state \\\"%s\\\"\\n\",\n\t\t    trice_candpair_debug, pair,\n\t\t    trice_candpair_state2name(state));\n#endif\n\n\tpair->state = state;\n}\n\n\nbool trice_candpair_iscompleted(const struct ice_candpair *cp)\n{\n\tif (!cp)\n\t\treturn false;\n\n\treturn cp->state == ICE_CANDPAIR_FAILED ||\n\t\tcp->state == ICE_CANDPAIR_SUCCEEDED;\n}\n\n\n/**\n * Find the highest-priority candidate-pair in a given list, with\n * optional match parameters\n *\n * @param lst    List of candidate pairs\n * @param lcand  Local candidate (optional)\n * @param rcand  Remote candidate (optional)\n *\n * @return Matching candidate pair if found, otherwise NULL\n *\n * note: assume list is sorted by priority\n */\nstruct ice_candpair *trice_candpair_find(const struct list *lst,\n\t\t\t\t\tconst struct ice_lcand *lcand,\n\t\t\t\t\tconst struct ice_rcand *rcand)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tif (!cp->lcand || !cp->rcand) {\n\t\t\tDEBUG_WARNING(\"corrupt candpair %p\\n\", cp);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (lcand && cp->lcand != lcand)\n\t\t\tcontinue;\n\n\t\tif (rcand && cp->rcand != rcand)\n\t\t\tcontinue;\n\n\t\treturn cp;\n\t}\n\n\treturn NULL;\n}\n\n\n/* find the first pair with a given state */\nstruct ice_candpair *trice_candpair_find_state(const struct list *lst,\n\t\t\t\t\t      enum ice_candpair_state state)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tif (cp->state != state)\n\t\t\tcontinue;\n\n\t\treturn cp;\n\t}\n\n\treturn NULL;\n}\n\n\nbool trice_candpair_cmp_fnd(const struct ice_candpair *cp1,\n\t\t\t   const struct ice_candpair *cp2)\n{\n\tif (!cp1 || !cp2)\n\t\treturn false;\n\n\treturn 0 == strcmp(cp1->lcand->attr.foundation,\n\t\t\t   cp2->lcand->attr.foundation) &&\n\t\t0 == strcmp(cp1->rcand->attr.foundation,\n\t\t\t    cp2->rcand->attr.foundation);\n}\n\n\n/*  RFC 6544 -- 6.2. Forming the Check Lists\n\n   Local           Remote\n   Candidate       Candidate\n   ---------------------------\n   tcp-so          tcp-so\n   tcp-active      tcp-passive\n   tcp-passive     tcp-active\n\n */\nstatic bool tcptype_match(enum ice_tcptype loc, enum ice_tcptype rem)\n{\n\tif (loc == ICE_TCP_SO      && rem == ICE_TCP_SO)      return true;\n\tif (loc == ICE_TCP_ACTIVE  && rem == ICE_TCP_PASSIVE) return true;\n\tif (loc == ICE_TCP_PASSIVE && rem == ICE_TCP_ACTIVE)  return true;\n\n\treturn false;\n}\n\n\n/* Replace server reflexive candidates by its base */\nstatic const struct sa *cand_srflx_addr(const struct ice_lcand *cand)\n{\n\tif (ICE_CAND_TYPE_SRFLX == cand->attr.type)\n\t\treturn &cand->base_addr;\n\telse\n\t\treturn &cand->attr.addr;\n}\n\n\nstatic struct ice_candpair *find_same_base_list(const struct list *lst,\n\t\t\t\t\t\tconst struct ice_lcand *lcand,\n\t\t\t\t\t\tconst struct ice_rcand *rcand)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tif (cp->lcand->attr.compid == lcand->attr.compid\n\t\t    &&\n\t\t    cp->lcand->attr.proto == lcand->attr.proto\n\t\t    &&\n\t\t    sa_cmp(cand_srflx_addr(cp->lcand),\n\t\t\t   cand_srflx_addr(lcand), SA_ALL)\n\t\t    &&\n\t\t    sa_cmp(&cp->rcand->attr.addr,\n\t\t\t   &rcand->attr.addr, SA_ALL)) {\n\n\t\t\treturn cp;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/* look in both check-list and valid-list */\nstatic struct ice_candpair *find_same_base(struct trice *icem,\n\t\t\t\t\t   const struct ice_lcand *lcand,\n\t\t\t\t\t   const struct ice_rcand *rcand)\n{\n\tstruct ice_candpair *cp;\n\n\tcp = find_same_base_list(&icem->checkl, lcand, rcand);\n\tif (cp)\n\t\treturn cp;\n\n\tcp = find_same_base_list(&icem->validl, lcand, rcand);\n\tif (cp)\n\t\treturn cp;\n\n\treturn NULL;\n}\n\n\n/* Pair a local candidate with a remote candidate */\nstatic int create_pair(struct trice *icem, struct ice_lcand *lcand,\n\t\t       struct ice_rcand *rcand)\n{\n\tstruct ice_candpair *cpx;\n\n\tif (lcand->attr.compid != rcand->attr.compid ||\n\t    lcand->attr.proto != rcand->attr.proto ||\n\t    sa_af(&lcand->attr.addr) != sa_af(&rcand->attr.addr)) {\n\t\treturn 0;\n\t}\n\n\t/*\n\t * IPv6 link-local: only pair with IPv6 link-local addresses\n\t * see: RFC5245bis, section 6.1.2.2\n\t */\n\tif (sa_af(&lcand->attr.addr) == AF_INET6 &&\n\t    sa_is_linklocal(&lcand->attr.addr) !=\n\t    sa_is_linklocal(&rcand->attr.addr)) {\n\t\treturn 0;\n\t}\n\n\t/* loopback pairing optimization: only pair with loopback addresses */\n\tif (icem->conf.optimize_loopback_pairing &&\n\t    sa_is_loopback(&lcand->attr.addr) !=\n\t    sa_is_loopback(&rcand->attr.addr)) {\n\t\treturn 0;\n\t}\n\n\tcpx = find_same_base(icem, lcand, rcand);\n\tif (cpx) {\n\t\ttrice_printf(icem,\n\t\t\t\t\"with: pair with same\"\n\t\t\t\t\" base already exist\"\n\t\t\t\t\" (%H)\\n\",\n\t\t\t\ttrice_candpair_debug, cpx);\n\n\t\treturn 0;\n\t}\n\n\tif (lcand->attr.proto == IPPROTO_TCP) {\n\t\tif (!tcptype_match(lcand->attr.tcptype,\n\t\t\t\t   rcand->attr.tcptype))\n\t\t\treturn 0;\n\t}\n\n\t/* add sorted */\n\treturn trice_candpair_alloc(NULL, icem, lcand, rcand);\n}\n\n\n/* Pair a candidate with all other candidates of the opposite kind */\nint trice_candpair_with_local(struct trice *icem, struct ice_lcand *lcand)\n{\n\tstruct list *lst = &icem->rcandl;\n\tstruct le *le;\n\tint err = 0;\n\n\tif (icem->lrole == ICE_ROLE_UNKNOWN) {\n\t\tDEBUG_WARNING(\"trice_candpair_with_local: invalid local role!\"\n\t\t\t\t\t  \"\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\n\t\tstruct ice_rcand *rcand = le->data;\n\n\t\terr = create_pair(icem, lcand, rcand);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n out:\n\treturn err;\n}\n\n\n/* Pair a candidate with all other candidates of the opposite kind */\nint trice_candpair_with_remote(struct trice *icem, struct ice_rcand *rcand)\n{\n\tstruct list *lst = &icem->lcandl;\n\tstruct le *le;\n\tint err = 0;\n\n\tif (icem->lrole == ICE_ROLE_UNKNOWN) {\n\t\tDEBUG_WARNING(\"trice_candpair_with_remote: invalid local role!\"\n\t\t\t\t\t  \"\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\n\t\tstruct ice_lcand *lcand = le->data;\n\n\t\terr = create_pair(icem, lcand, rcand);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n out:\n\treturn err;\n}\n\n\nint trice_candpair_debug(struct re_printf *pf, const struct ice_candpair *cp)\n{\n\tint err;\n\n\tif (!cp)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"{comp=%u} %10s {%c%c%c%c} %28H <---> %28H\",\n\t\t\t cp->lcand->attr.compid,\n\t\t\t trice_candpair_state2name(cp->state),\n\t\t\t cp->valid ? 'V' : ' ',\n\t\t\t cp->nominated ? 'N' : ' ',\n\t\t\t cp->estab ? 'E' : ' ',\n\t\t\t cp->trigged ? 'T' : ' ',\n\t\t\t trice_cand_print, cp->lcand,\n\t\t\t trice_cand_print, cp->rcand);\n\n\tif (cp->err)\n\t\terr |= re_hprintf(pf, \" (%m)\", cp->err);\n\n\tif (cp->scode)\n\t\terr |= re_hprintf(pf, \" [%u]\", cp->scode);\n\n\treturn err;\n}\n\n\nint trice_candpairs_debug(struct re_printf *pf, bool ansi_output,\n\t\t\t  const struct list *list)\n{\n\tstruct le *le;\n\tint err;\n\n\tif (!list)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \" (%u)\\n\", list_count(list));\n\n\tfor (le = list->head; le && !err; le = le->next) {\n\n\t\tconst struct ice_candpair *cp = le->data;\n\t\tbool ansi = false;\n\n\t\tif (ansi_output) {\n\t\t\tif (cp->state == ICE_CANDPAIR_SUCCEEDED) {\n\t\t\t\terr |= re_hprintf(pf, \"\\x1b[32m\");\n\t\t\t\tansi = true;\n\t\t\t}\n\t\t\telse if (cp->err || cp->scode) {\n\t\t\t\terr |= re_hprintf(pf, \"\\x1b[31m\");\n\t\t\t\tansi = true;\n\t\t\t}\n\t\t}\n\n\t\terr |= re_hprintf(pf, \"    %H\\n\",\n\t\t\t\t trice_candpair_debug, cp);\n\n\t\tif (ansi)\n\t\t\terr |= re_hprintf(pf, \"\\x1b[;m\");\n\t}\n\n\treturn err;\n}\n\n\nconst char *trice_candpair_state2name(enum ice_candpair_state st)\n{\n\tswitch (st) {\n\n\tcase ICE_CANDPAIR_FROZEN:     return \"Frozen\";\n\tcase ICE_CANDPAIR_WAITING:    return \"Waiting\";\n\tcase ICE_CANDPAIR_INPROGRESS: return \"InProgress\";\n\tcase ICE_CANDPAIR_SUCCEEDED:  return \"Succeeded\";\n\tcase ICE_CANDPAIR_FAILED:     return \"Failed\";\n\tdefault:                      return \"???\";\n\t}\n}\n"
  },
  {
    "path": "src/trice/chklist.c",
    "content": "/**\n * @file chklist.c  ICE Checklist\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include <re_trice.h>\n#include \"trice.h\"\n\n\n#define DEBUG_MODULE \"checklist\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic void destructor(void *arg)\n{\n\tstruct ice_checklist *ic = arg;\n\n\ttmr_cancel(&ic->tmr_pace);\n\tlist_flush(&ic->conncheckl);  /* flush before stun deref */\n\tmem_deref(ic->stun);\n}\n\n\nstatic void pace_timeout(void *arg)\n{\n\tstruct ice_checklist *ic = arg;\n\tstruct trice *icem = (struct trice *)ic->icem;\n\n\ttmr_start(&ic->tmr_pace, ic->interval,\n\t\t  pace_timeout, ic);\n\n\ttrice_conncheck_schedule_check(icem);\n\n\ttrice_checklist_update(icem);\n}\n\n\nint trice_checklist_start(struct trice *icem, struct stun *stun,\n\t\t\t  uint32_t interval,\n\t\t\t  trice_estab_h *estabh, trice_failed_h *failh,\n\t\t\t  void *arg)\n{\n\tstruct ice_checklist *ic;\n\tint err = 0;\n\n\tif (!icem)\n\t\treturn EINVAL;\n\n\tif (icem->lrole == ICE_ROLE_UNKNOWN) {\n\t\tDEBUG_WARNING(\"trice_checklist_start: missing local role!\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tif (icem->checklist) {\n\t\tic = icem->checklist;\n\n\t\tif (!tmr_isrunning(&ic->tmr_pace)) {\n\t\t\ttmr_start(&ic->tmr_pace, 1, pace_timeout, ic);\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/* The password is equal to the password provided by the peer */\n\tif (!str_isset(icem->rpwd)) {\n\t\tDEBUG_WARNING(\"start: remote password not set\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tic = mem_zalloc(sizeof(*ic), destructor);\n\tif (!ic)\n\t\treturn ENOMEM;\n\n\tif (stun) {\n\t\tic->stun = mem_ref(stun);\n\t}\n\telse {\n\t\terr = stun_alloc(&ic->stun, NULL, NULL, NULL);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\t/* Update STUN Transport */\n\t\tstun_conf(ic->stun)->rto = 100;\n\t\tstun_conf(ic->stun)->rc = 4;\n\n\t}\n\n\ttmr_init(&ic->tmr_pace);\n\n\tic->interval = interval;\n\tic->icem = icem;\n\tic->estabh = estabh;\n\tic->failh  = failh;\n\tic->arg    = arg;\n\n\tic->is_running = true;\n\ttmr_start(&ic->tmr_pace, 0, pace_timeout, ic);\n\n\ticem->checklist = ic;\n\n out:\n\tif (err)\n\t\tmem_deref(ic);\n\n\treturn err;\n}\n\n\nvoid trice_checklist_stop(struct trice *icem)\n{\n\tstruct ice_checklist *ic;\n\n\tif (!icem || !icem->checklist)\n\t\treturn;\n\n\tic = icem->checklist;\n\n\tic->is_running = false;\n\ttmr_cancel(&ic->tmr_pace);\n}\n\n\n/* If all of the pairs in the check list are now either in the Failed or\n   Succeeded state:\n */\nbool trice_checklist_iscompleted(const struct trice *icem)\n{\n\tstruct le *le;\n\n\tif (!icem)\n\t\treturn false;\n\n\tfor (le = icem->checkl.head; le; le = le->next) {\n\n\t\tconst struct ice_candpair *cp = le->data;\n\n\t\tif (!trice_candpair_iscompleted(cp))\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * Scheduling Checks\n */\nvoid trice_conncheck_schedule_check(struct trice *icem)\n{\n\tstruct ice_candpair *pair;\n\tbool use_cand;\n\tint err = 0;\n\n\tif (!icem)\n\t\treturn;\n\n\tuse_cand = false;\n\n\t/* Find the highest priority pair in that check list that is in the\n\t   Waiting state. */\n\tpair = trice_candpair_find_state(&icem->checkl, ICE_CANDPAIR_WAITING);\n\tif (pair) {\n\t\terr = trice_conncheck_send(icem, pair,\n\t\t\t\t\t  use_cand);\n\t\tif (err)\n\t\t\ttrice_candpair_failed(pair, err, 0);\n\t\treturn;\n\t}\n\n\t/* If there is no such pair: */\n\n\t/* Find the highest priority pair in that check list that is in\n\t   the Frozen state. */\n\tpair = trice_candpair_find_state(&icem->checkl, ICE_CANDPAIR_FROZEN);\n\tif (pair) { /* If there is such a pair: */\n\n\t\t/* Unfreeze the pair.\n\t\t   Perform a check for that pair, causing its state to\n\t\t   transition to In-Progress. */\n\t\terr = trice_conncheck_send(icem, pair,\n\t\t\t\t\t  use_cand);\n\t\tif (err)\n\t\t\ttrice_candpair_failed(pair, err, 0);\n\t\treturn;\n\t}\n\n\t/* If there is no such pair: */\n\n\t/* Terminate the timer for that check list. */\n}\n\n\n/*\n * Computing States\n */\nvoid trice_checklist_set_waiting(struct trice *icem)\n{\n\tstruct le *le, *le2;\n\n\tif (!icem)\n\t\treturn;\n\n\tif (icem->lrole == ICE_ROLE_UNKNOWN) {\n\t\tDEBUG_WARNING(\"trice_checklist_set_waiting: invalid local\"\n\t\t\t\t\t  \"role!\\n\");\n\t\treturn;\n\t}\n\n\t/*\n\tFor all pairs with the same foundation, it sets the state of\n\tthe pair with the lowest component ID to Waiting.  If there is\n\tmore than one such pair, the one with the highest priority is\n\tused.\n\t*/\n\n\tfor (le = icem->checkl.head; le; le = le->next) {\n\n\t\tstruct ice_candpair *cp = le->data;\n\n\t\tfor (le2 = icem->checkl.head; le2; le2 = le2->next) {\n\n\t\t\tstruct ice_candpair *cp2 = le2->data;\n\n\t\t\tif (!trice_candpair_cmp_fnd(cp, cp2))\n\t\t\t\tcontinue;\n\n\t\t\tif (cp2->lcand->attr.compid < cp->lcand->attr.compid &&\n\t\t\t    cp2->pprio > cp->pprio)\n\t\t\t\tcp = cp2;\n\t\t}\n\n\t\tif (cp->state == ICE_CANDPAIR_FROZEN)\n\t\t\ttrice_candpair_set_state(cp, ICE_CANDPAIR_WAITING);\n\t}\n}\n\n\nint trice_checklist_update(struct trice *icem)\n{\n\tstruct ice_checklist *ic;\n\n\tif (!icem)\n\t\treturn EINVAL;\n\n\tic = icem->checklist;\n\tif (!ic)\n\t\treturn ENOSYS;\n\n\tif (trice_checklist_iscompleted(icem)) {\n\t\ttmr_cancel(&ic->tmr_pace);\n\n\t\ttrice_printf(icem, \"ICE checklist is complete\"\n\t\t\t     \" (checkl=%u, valid=%u)\\n\",\n\t\t\t     list_count(&icem->checkl),\n\t\t\t     list_count(&icem->validl));\n\t}\n\n\treturn 0;\n}\n\n\nvoid trice_checklist_refresh(struct trice *icem)\n{\n\tstruct ice_checklist *ic;\n\n\tif (!icem || !icem->checklist)\n\t\treturn;\n\n\tic = icem->checklist;\n\n\ttmr_start(&ic->tmr_pace, ic->interval, pace_timeout, ic);\n}\n\n\nbool trice_checklist_isrunning(const struct trice *icem)\n{\n\tstruct ice_checklist *ic;\n\n\tif (!icem || !icem->checklist)\n\t\treturn false;\n\n\tic = icem->checklist;\n\n\treturn ic->is_running;\n}\n\n\nint trice_checklist_debug(struct re_printf *pf, const struct ice_checklist *ic)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tif (!ic)\n\t\treturn 0;\n\n\terr |= re_hprintf(pf, \" Checklist: %s, interval=%ums\\n\",\n\t\t  tmr_isrunning(&ic->tmr_pace) ? \"Running\" : \"Not-Running\",\n\t\t\t  ic->interval);\n\terr |= re_hprintf(pf, \" Pending connchecks: %u\\n\",\n\t\t\t  list_count(&ic->conncheckl));\n\tfor (le = ic->conncheckl.head; le; le = le->next) {\n\t\tstruct ice_conncheck *cc = le->data;\n\n\t\terr |= re_hprintf(pf, \" ...%H\\n\", trice_conncheck_debug, cc);\n\t}\n\n\terr |= stun_debug(pf, ic->stun);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/trice/connchk.c",
    "content": "/**\n * @file connchk.c  ICE Connectivity Checks\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_net.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include <re_trice.h>\n#include \"trice.h\"\n\n\n#define DEBUG_MODULE \"conncheck\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {PRESZ_RELAY = 36};\n\n\nstatic void conncheck_destructor(void *arg)\n{\n\tstruct ice_conncheck *cc = arg;\n\n\tcc->term = true;\n\tlist_unlink(&cc->le);\n\tmem_deref(cc->ct_conn);\n}\n\n\nstatic void pair_established(struct trice *icem, struct ice_candpair *pair,\n\t\t\t     const struct stun_msg *msg)\n{\n\tstruct ice_tcpconn *conn;\n\n\tif (!icem || !pair)\n\t\treturn;\n\n\tif (pair->lcand->attr.proto == IPPROTO_TCP) {\n\n\t\tconn = pair->conn;\n\n\t\tif (!conn) {\n\t\t\t/* todo: Hack to grab TCPCONN */\n\t\t\tconn = trice_conn_find(&icem->connl,\n\t\t\t\t\t     pair->lcand->attr.compid,\n\t\t\t\t\t     &pair->lcand->attr.addr,\n\t\t\t\t\t     &pair->rcand->attr.addr);\n\t\t}\n\n\t\tif (conn) {\n\t\t\tpair->tc = mem_deref(pair->tc);\n\t\t\tpair->tc = mem_ref(conn->tc);\n\t\t}\n\t\telse {\n\t\t\tDEBUG_WARNING(\"pair_established: TCP-connection \"\n\t\t\t\t      \" from %H to %H not found!\\n\",\n\t\t\t\t      trice_cand_print, pair->lcand,\n\t\t\t\t      trice_cand_print, pair->rcand);\n\t\t}\n\n\t}\n\n\tif (!pair->estab) {\n\t\tpair->estab = true;\n\n\t\tif (icem->checklist->estabh) {\n\t\t\ticem->checklist->estabh(pair, msg,\n\t\t\t\t\t\ticem->checklist->arg);\n\t\t}\n\t}\n}\n\n\n/*\n * NOTE for TCP-candidates:\n *\n *     Note also that STUN responses received on an active TCP candidate\n *     will typically produce a peer reflexive candidate.\n *\n *\n * NOTE for Trickle ICE and Peer Reflexive Candidates:\n *\n *     With Trickle ICE, it is possible that\n *     server reflexive candidates be discovered as peer reflexive in cases\n *     where incoming connectivity checks are received from these candidates\n *     before the trickle updates that carry them.\n *\n */\nstatic void handle_success(struct trice *icem, struct ice_candpair *pair,\n\t\t\t   const struct sa *mapped_addr,\n\t\t\t   const struct stun_msg *msg,\n\t\t\t   struct ice_conncheck *cc)\n{\n\tunsigned compid;\n\tint err;\n\n\tif (!icem || !pair || !pair->lcand) {\n\t\tDEBUG_WARNING(\"handle_success: invalid params\\n\");\n\t\treturn;\n\t}\n\n\tcompid = pair->lcand->attr.compid;\n\n\tif (icem &&\n\t    !trice_lcand_find(icem, -1, compid,\n\t\t\t      pair->lcand->attr.proto, mapped_addr)) {\n\n\t\tstruct ice_lcand *lcand;\n\t\tstruct ice_candpair *pair_prflx;\n\t\tuint32_t prio;\n\n\t\tprio = ice_cand_calc_prio(ICE_CAND_TYPE_PRFLX, 0, compid);\n\n\t\terr = trice_add_lcandidate(&lcand, icem, &icem->lcandl, compid,\n\t\t\t\t\t   \"FND\", pair->lcand->attr.proto,\n\t\t\t\t\t   prio, mapped_addr,\n\t\t\t\t\t   &pair->lcand->attr.addr,\n\t\t\t\t\t   ICE_CAND_TYPE_PRFLX,\n\t\t\t\t\t   &pair->lcand->attr.addr,\n\t\t\t\t\t   pair->lcand->attr.tcptype);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"failed to add PRFLX: %m\\n\", err);\n\t\t\treturn;\n\t\t}\n\n\t\tif (str_isset(pair->lcand->ifname)) {\n\t\t\tstr_ncpy(lcand->ifname, pair->lcand->ifname,\n\t\t\t\t sizeof(lcand->ifname));\n\t\t}\n\n\t\ttrice_printf(icem, \"added PRFLX local candidate (%H)\"\n\t\t\t     \" from base (%H)\\n\",\n\t\t\t     trice_cand_print, lcand,\n\t\t\t     trice_cand_print, pair->lcand);\n\n\t\t/* newly created Candidate-PAir */\n\t\terr = trice_candpair_alloc(&pair_prflx, icem,\n\t\t\t\t\t  lcand, pair->rcand);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"prflx alloc: %m\\n\", err);\n\t\t\treturn;\n\t\t}\n\n\t\tlcand->us = mem_ref(pair->lcand->us);\n\t\tpair_prflx->conn = mem_ref(pair->conn);\n\n\t\t/* mark the original HOST-one as failed */\n\t\ttrice_candpair_failed(pair, 0, 0);\n\n\t\ttrice_candpair_make_valid(icem, pair_prflx);\n\n\t\tpair_established(icem, pair_prflx, msg);\n\t\treturn;\n\t}\n\n\tpair->state = ICE_CANDPAIR_FROZEN;\n\ttrice_candpair_make_valid(icem, pair);\n\n\t/* Updating the Nominated Flag */\n\tif (icem && ICE_ROLE_CONTROLLING == icem->lrole) {\n\n\t\tif (cc->use_cand)\n\t\t\tpair->nominated = true;\n\t}\n\n\tpair_established(icem, pair, msg);\n}\n\n\nstatic int print_err(struct re_printf *pf, const int *err)\n{\n\tif (err && *err)\n\t\treturn re_hprintf(pf, \" (%m)\", *err);\n\n\treturn 0;\n}\n\n\nstatic void stunc_resp_handler(int err, uint16_t scode, const char *reason,\n\t\t\t       const struct stun_msg *msg, void *arg)\n{\n\tstruct ice_conncheck *cc = arg;\n\tstruct ice_candpair *pair = cc->pair;\n\tstruct trice *icem = cc->icem;\n\tstruct stun_attr *attr;\n\tbool success = (err == 0) && (scode == 0);\n\t(void)reason;\n\n\tif (!icem) {\n\t\tDEBUG_WARNING(\"stun response: no icem\\n\");\n\t}\n\n\tif (cc->term)\n\t\treturn;\n\n\ttrice_tracef(icem, success ? 32 : 31,\n\t\t     \"[%u] Rx %H <--- %H '%u %s'%H\\n\",\n\t\t     pair->lcand->attr.compid,\n\t\t     trice_cand_print, pair->lcand,\n\t\t     trice_cand_print, pair->rcand,\n\t\t     scode, reason, print_err, &err);\n\n\tif (err) {\n\t\tDEBUG_NOTICE(\"stun response: [%H --> %H] %m\\n\",\n\t\t\t     trice_cand_print, pair->lcand,\n\t\t\t     trice_cand_print, pair->rcand,\n\t\t\t     err);\n\n\t\ttrice_candpair_failed(pair, err, scode);\n\t\tgoto out;\n\t}\n\n\tswitch (scode) {\n\n\tcase 0: /* Success case */\n\t\tattr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);\n\t\tif (!attr) {\n\t\t\tDEBUG_WARNING(\"no XOR-MAPPED-ADDR in response\\n\");\n\t\t\ttrice_candpair_failed(pair, EPROTO, 0);\n\t\t\tbreak;\n\t\t}\n\n\t\thandle_success(icem, pair, &attr->v.sa, msg, cc);\n\t\tbreak;\n\n\tcase 487: /* Role Conflict */\n\t\ttrice_switch_local_role(icem);\n\t\t(void)trice_conncheck_send(icem, pair, cc->use_cand);\n\t\tbreak;\n\n\tdefault:\n\t\ttrice_candpair_failed(pair, err, scode);\n\t\tbreak;\n\t}\n\n out:\n\tif (err || scode) {\n\t\tif (icem && icem->checklist) {\n\t\t\ticem->checklist->failh(err, scode,\n\t\t\t\t\t       pair, icem->checklist->arg);\n\t\t}\n\t}\n\n\tmem_deref(cc);\n\treturn;\n}\n\n\nint trice_conncheck_stun_request(struct ice_checklist *ic,\n\t\t\t       struct ice_conncheck *cc,\n\t\t\t       struct ice_candpair *cp, void *sock,\n\t\t\t       bool cc_use_cand)\n{\n\tstruct ice_lcand *lcand;\n\tstruct trice *icem;\n\tchar username_buf[256];\n\tuint32_t prio_prflx;\n\tuint16_t ctrl_attr;\n\tbool use_cand = false;\n\tsize_t presz = 0;\n\tint err = 0;\n\n\tif (!cp)\n\t\treturn EINVAL;\n\n\tif (!ic)\n\t\treturn ENOSYS;\n\n\tlcand = cp->lcand;\n\ticem = ic->icem;\n\n\tif (!sock) {\n\t\tDEBUG_NOTICE(\"conncheck: no SOCK\\n\");\n\t\treturn EINVAL;\n\t}\n\n\t/* The password is equal to the password provided by the peer */\n\tif (!str_isset(icem->rpwd)) {\n\t\tDEBUG_WARNING(\"conncheck: remote password missing for\"\n\t\t\t      \" raddr=%J\\n\", &cp->rcand->attr.addr);\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\tif (lcand->attr.proto == IPPROTO_UDP &&\n\t    lcand->attr.type == ICE_CAND_TYPE_RELAY)\n\t\tpresz = PRESZ_RELAY;\n\telse if (lcand->attr.proto == IPPROTO_TCP)\n\t\tpresz = 2;\n\n\tif (re_snprintf(username_buf, sizeof(username_buf),\n\t\t\t\"%s:%s\", icem->rufrag, icem->lufrag) < 0) {\n\t\tDEBUG_WARNING(\"conncheck: username buffer too small\\n\");\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\t/* PRIORITY and USE-CANDIDATE */\n\tprio_prflx = ice_cand_calc_prio(ICE_CAND_TYPE_PRFLX, 0,\n\t\t\t\t\tlcand->attr.compid);\n\n\tswitch (icem->lrole) {\n\n\tcase ICE_ROLE_CONTROLLING:\n\t\tctrl_attr = STUN_ATTR_CONTROLLING;\n\t\tuse_cand = cc_use_cand;\n\t\tbreak;\n\n\tcase ICE_ROLE_CONTROLLED:\n\t\tctrl_attr = STUN_ATTR_CONTROLLED;\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"conncheck: invalid local role\\n\");\n\t\treturn EINVAL;\n\t}\n\n\ttrice_tracef(icem, 36,\n\t\t     \"[%u] Tx [presz=%zu] %H ---> %H (%s) %s\\n\",\n\t\t     lcand->attr.compid,\n\t\t     presz,\n\t\t     trice_cand_print, cp->lcand, trice_cand_print, cp->rcand,\n\t\t     trice_candpair_state2name(cp->state),\n\t\t     use_cand ? \"[USE]\" : \"\");\n\n\t/* A connectivity check MUST utilize the STUN short term credential\n\t   mechanism. */\n\n\terr = stun_request(&cc->ct_conn, ic->stun, lcand->attr.proto,\n\t\t\t   sock, &cp->rcand->attr.addr, presz,\n\t\t\t   STUN_METHOD_BINDING,\n\t\t\t   (uint8_t *)icem->rpwd, str_len(icem->rpwd),\n\t\t\t   true, stunc_resp_handler, cc,\n\t\t\t   4,\n\t\t\t   STUN_ATTR_USERNAME, username_buf,\n\t\t\t   STUN_ATTR_PRIORITY, &prio_prflx,\n\t\t\t   ctrl_attr, &icem->tiebrk,\n\t\t\t   STUN_ATTR_USE_CAND,\n\t\t\t   use_cand ? &use_cand : 0);\n\tif (err) {\n\t\tDEBUG_NOTICE(\"stun_request from %H to %H failed (%m)\\n\",\n\t\t\t      trice_cand_print, lcand,\n\t\t\t      trice_cand_print, cp->rcand,\n\t\t\t      err);\n\t\tgoto out;\n\t}\n\n out:\n\tif (err) {\n\t\ttrice_candpair_failed(cp, err, 0);\n\t}\n\n\treturn err;\n}\n\n\nstatic bool tcpconn_frame_handler(struct trice *icem,\n\t\t\t\t  struct tcp_conn *tc, struct sa *src,\n\t\t\t\t  struct mbuf *mb, void *arg)\n{\n\tstruct ice_lcand *lcand = arg;\n\n\treturn trice_stun_process(icem, lcand,\n\t\t\t\t  IPPROTO_TCP, tc, src, mb);\n}\n\n\nint trice_conncheck_send(struct trice *icem, struct ice_candpair *pair,\n\t\t\tbool use_cand)\n{\n\tstruct ice_checklist *ic;\n\tstruct ice_lcand *lcand;\n\tstruct ice_tcpconn *conn;\n\tstruct ice_conncheck *cc = NULL;\n\tvoid *sock;\n\tint err = 0;\n\n\tif (!icem || !pair)\n\t\treturn EINVAL;\n\n\tlcand = pair->lcand;\n\tic = icem->checklist;\n\tif (!ic) {\n\t\tDEBUG_WARNING(\"conncheck_send: no checklist\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tcc = mem_zalloc(sizeof(*cc), conncheck_destructor);\n\tif (!cc)\n\t\treturn ENOMEM;\n\n\tcc->icem = icem;\n\tcc->pair = pair;\n\tcc->use_cand = use_cand;\n\n\tif (pair->state < ICE_CANDPAIR_INPROGRESS)\n\t\ttrice_candpair_set_state(pair, ICE_CANDPAIR_INPROGRESS);\n\n\tswitch (pair->lcand->attr.proto) {\n\n\tcase IPPROTO_UDP:\n\t\tsock = trice_lcand_sock(icem, lcand);\n\n\t\terr = trice_conncheck_stun_request(ic, cc, pair,\n\t\t\t\t\t\t sock, use_cand);\n\t\tif (err)\n\t\t\tgoto out;\n\t\tbreak;\n\n\tcase IPPROTO_TCP:\n\t\tconn = trice_conn_find(&icem->connl, lcand->attr.compid,\n\t\t\t\t     &pair->lcand->attr.addr,\n\t\t\t\t     &pair->rcand->attr.addr);\n\t\tif (conn) {\n\t\t\ttrice_printf(icem, \"TCP-connection\"\n\t\t\t\t    \" already exist [%H]\\n\",\n\t\t\t\t    trice_conn_debug, conn);\n\n\t\t\tpair->conn = mem_ref(conn);  /* todo: */\n\n\t\t\terr = trice_conncheck_stun_request(ic, cc, pair,\n\t\t\t\t\t\t\t conn->tc, use_cand);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t\tbreak;\n\t\t}\n\n\t\tswitch (pair->lcand->attr.tcptype) {\n\n\t\tcase ICE_TCP_ACTIVE:\n\t\tcase ICE_TCP_SO:\n\t\t\terr = trice_conn_alloc(&icem->connl, icem,\n\t\t\t\t\t     lcand->attr.compid, true,\n\t\t\t\t\t     &lcand->attr.addr,\n\t\t\t\t\t     &pair->rcand->attr.addr,\n\t\t\t\t\t     lcand->ts, lcand->layer,\n\t\t\t\t\t     tcpconn_frame_handler, lcand);\n\t\t\tif (err) {\n\t\t\t\tDEBUG_NOTICE(\"trice_conn_alloc to\"\n\t\t\t\t\t      \" %J failed (%m)\\n\",\n\t\t\t\t\t      &pair->rcand->attr.addr, err);\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase ICE_TCP_PASSIVE:\n\t\t\t/* do nothing now. */\n\n\t\t\t/* we must wait for the other side to create a\n\t\t\t   TCP-connection to us. when this TCP-connection\n\t\t\t   is established, we can then send our\n\t\t\t   Connectivity-check */\n\n\t\t\ttrice_candpair_set_state(pair,\n\t\t\t\t\t\t ICE_CANDPAIR_INPROGRESS);\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\terr = EPROTONOSUPPORT;\n\t\tgoto out;\n\t}\n\n\tlist_append(&ic->conncheckl, &cc->le, cc);\n\n out:\n\tif (err) {\n\t\tmem_deref(cc);\n\t\ttrice_candpair_failed(pair, err, 0);\n\t}\n\n\treturn err;\n}\n\n\nint trice_conncheck_trigged(struct trice *icem, struct ice_candpair *pair,\n\t\t\t   void *sock, bool use_cand)\n{\n\tstruct ice_checklist *ic;\n\tstruct ice_conncheck *cc = NULL;\n\tint err = 0;\n\n\tif (!icem || !pair)\n\t\treturn EINVAL;\n\n\tic = icem->checklist;\n\tif (!ic) {\n\t\tDEBUG_WARNING(\"conncheck_send: no checklist\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tcc = mem_zalloc(sizeof(*cc), conncheck_destructor);\n\tif (!cc)\n\t\treturn ENOMEM;\n\n\tcc->icem = icem;\n\tcc->pair = pair;\n\tcc->use_cand = use_cand;\n\n\tif (pair->state < ICE_CANDPAIR_INPROGRESS)\n\t\ttrice_candpair_set_state(pair, ICE_CANDPAIR_INPROGRESS);\n\n\terr = trice_conncheck_stun_request(icem->checklist, cc,\n\t\t\t\t\t pair, sock, use_cand);\n\tif (err)\n\t\tgoto out;\n\n\tlist_append(&ic->conncheckl, &cc->le, cc);\n\n out:\n\tif (err) {\n\t\tmem_deref(cc);\n\t\ttrice_candpair_failed(pair, err, 0);\n\t}\n\n\treturn err;\n}\n\n\nint trice_conncheck_debug(struct re_printf *pf, const struct ice_conncheck *cc)\n{\n\tif (!cc)\n\t\treturn 0;\n\n\treturn re_hprintf(pf, \"proto=%s stun=%p use_cand=%d\"\n\t\t\t  \" state=%s\"\n\t\t\t  ,\n\t\t\t  net_proto2name(cc->pair->lcand->attr.proto),\n\t\t\t  cc->ct_conn, cc->use_cand,\n\t\t\t  trice_candpair_state2name(cc->pair->state));\n}\n"
  },
  {
    "path": "src/trice/lcand.c",
    "content": "/**\n * @file lcand.c  Local ICE Candidates\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_net.h>\n#include <re_sys.h>\n#include <re_stun.h>\n#include <re_udp.h>\n#include <re_tcp.h>\n#include <re_ice.h>\n#include <re_trice.h>\n#include \"trice.h\"\n\n\n#define DEBUG_MODULE \"icelcand\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic bool tcpconn_frame_handler(struct trice *icem,\n\t\t\t\t  struct tcp_conn *tc, struct sa *src,\n\t\t\t\t  struct mbuf *mb, void *arg)\n{\n\tstruct ice_lcand *lcand = arg;\n\t(void)icem;\n\n\treturn lcand->recvh(lcand, IPPROTO_TCP, tc,\n\t\t\t    src, mb, lcand->arg);\n}\n\n\nstatic void tcp_conn_handler(const struct sa *peer, void *arg)\n{\n\tstruct ice_lcand *lcand = arg;\n\tint err;\n\n#if 0\n\ttrice_printf(lcand->icem,\n\t\t    \"[local=%H] incoming TCP-connect from %J\\n\",\n\t\t    trice_cand_print, lcand, peer);\n#endif\n\n\terr = trice_conn_alloc(&lcand->icem->connl, lcand->icem,\n\t\t\t     lcand->attr.compid, false,\n\t\t\t     &lcand->attr.addr, peer, lcand->ts, lcand->layer,\n\t\t\t     tcpconn_frame_handler, lcand);\n\tif (err) {\n\t\tDEBUG_WARNING(\"ice_conn_alloc error (%m)\\n\", err);\n\t}\n}\n\n\nstatic void lcand_destructor(void *arg)\n{\n\tstruct ice_lcand *cand = arg;\n\n\tlist_unlink(&cand->le);\n\n\tmem_deref(cand->ts);\n\tmem_deref(cand->uh);\n\tmem_deref(cand->us);\n}\n\n\n/** Foundation is a hash of IP address and candidate type */\nstatic int compute_foundation(struct ice_lcand *cand,\n\t\t\t      const struct sa *addr, enum ice_cand_type type)\n{\n\tuint32_t v;\n\n\tv  = sa_hash(addr, SA_ADDR);\n\tv ^= type;\n\n\tif (re_snprintf(cand->attr.foundation, sizeof(cand->attr.foundation),\n\t\t\t\"%08x\", v) < 0)\n\t\treturn ENOMEM;\n\n\treturn 0;\n}\n\n\nstatic bool trice_lcand_recv_handler(struct ice_lcand *lcand,\n\t\t\t\t  int proto, void *sock, const struct sa *src,\n\t\t\t\t  struct mbuf *mb, void *arg)\n{\n\tstruct trice *icem = arg;\n\n\treturn trice_stun_process(icem, lcand, proto, sock, src, mb);\n}\n\n\nint trice_add_lcandidate(struct ice_lcand **candp,\n\t\t\t struct trice *icem, struct list *lst,\n\t\t\t unsigned compid, char *foundation, int proto,\n\t\t\t uint32_t prio, const struct sa *addr,\n\t\t\t const struct sa *base_addr,\n\t\t\t enum ice_cand_type type,\n\t\t\t const struct sa *rel_addr,\n\t\t\t enum ice_tcptype tcptype)\n{\n\tstruct ice_lcand *cand;\n\tint err = 0;\n\n\tif (!lst || !compid || !proto || !addr)\n\t\treturn EINVAL;\n\n\tcand = mem_zalloc(sizeof(*cand), lcand_destructor);\n\tif (!cand)\n\t\treturn ENOMEM;\n\n\tcand->attr.compid = compid;\n\tif (foundation)\n\t\tstr_ncpy(cand->attr.foundation, foundation,\n\t\t\t       sizeof(cand->attr.foundation));\n\telse\n\t\terr = compute_foundation(cand, addr, type);\n\tcand->attr.proto  = proto;\n\tcand->attr.prio   = prio;\n\tcand->attr.addr   = *addr;\n\tcand->attr.type   = type;\n\tcand->attr.tcptype = tcptype;\n\tif (rel_addr)\n\t\tcand->attr.rel_addr = *rel_addr;\n\tif (err)\n\t\tgoto out;\n\n\tcand->icem = icem;\n\n\tcand->recvh = trice_lcand_recv_handler;\n\tcand->arg = icem;\n\n\tif (base_addr)\n\t\tcand->base_addr = *base_addr;\n\n\tlist_append(lst, &cand->le, cand);\n\n out:\n\tif (err)\n\t\tmem_deref(cand);\n\telse if (candp)\n\t\t*candp = cand;\n\n\treturn err;\n}\n\n\n/* this one is only for Send statistics on Local Candidate */\nstatic bool udp_helper_send_handler(int *err, struct sa *dst,\n\t\t\t\t    struct mbuf *mb, void *arg)\n{\n\tstruct ice_lcand *lcand = arg;\n\t(void)err;\n\t(void)dst;\n\t(void)mb;\n\n\tlcand->stats.n_tx += 1;\n\n\treturn false;  /* NOT handled */\n}\n\n\n/*\n * lcand: on which Local Candidate to receive the packet\n *\n * return TRUE if handled\n */\nstatic bool udp_helper_recv_handler(struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct ice_lcand *lcand = arg;\n\n\tlcand->stats.n_rx += 1;\n\n\treturn lcand->recvh(lcand, IPPROTO_UDP, lcand->us,\n\t\t\t    src, mb, lcand->arg);\n}\n\n\n/* The incoming data should not get here */\nstatic void dummy_udp_recv(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct ice_lcand *lcand = arg;\n\n\tDEBUG_NOTICE(\"@@@@ NO-ONE cared about this UDP packet? @@@@@\"\n\t\t     \" (%zu bytes from %J to %s.%J)\\n\",\n\t\t     mbuf_get_left(mb), src,\n\t\t     ice_cand_type2name(lcand->attr.type),\n\t\t     &lcand->attr.addr);\n}\n\nstatic int udp_listen_range(struct udp_sock **usp, const struct sa *ip,\n\t\t\t    uint16_t min_port, uint16_t max_port,\n\t\t\t    udp_recv_h *rh, void *arg)\n{\n\tstruct sa laddr;\n\tint tries = 64;\n\tint err = 0;\n\n\tsa_cpy(&laddr, ip);\n\n\t/* try hard */\n\twhile (tries--) {\n\t\tstruct udp_sock *us;\n\t\tuint16_t port;\n\n\t\tport = (min_port + (rand_u16() % (max_port - min_port)));\n\n\t\tsa_set_port(&laddr, port);\n\t\terr = udp_listen(&us, &laddr, rh, arg);\n\t\tif (err)\n\t\t\tcontinue;\n\n\t\t/* OK */\n\t\tif (usp)\n\t\t\t*usp = us;\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\n/*\n * you can call this at any time\n *\n * @param addr HOST:     SA_ADDR portion is used\n *             non-HOST: SA_ADDR + SA_PORT portion is used\n *\n * @param base_addr  Optional\n * @param rel_addr   Optional\n *\n * @param layer  mandatory for HOST and RELAY candidates\n */\nint trice_lcand_add(struct ice_lcand **lcandp, struct trice *icem,\n\t\t    unsigned compid, int proto,\n\t\t    uint32_t prio, const struct sa *addr,\n\t\t    const struct sa *base_addr,\n\t\t    enum ice_cand_type type, const struct sa *rel_addr,\n\t\t    enum ice_tcptype tcptype,\n\t\t    void *sock, int layer)\n{\n\tstruct ice_lcand *lcand;\n\tint err = 0;\n\n\tif (!icem || !compid || !proto || !addr)\n\t\treturn EINVAL;\n\n\tif (!sa_isset(addr, SA_ADDR)) {\n\t\tDEBUG_WARNING(\"lcand_add: SA_ADDR is not set\\n\");\n\t\treturn EINVAL;\n\t}\n\tif (type != ICE_CAND_TYPE_HOST) {\n\t\tif (!sa_isset(addr, SA_PORT)) {\n\t\t\tDEBUG_WARNING(\"lcand_add: %s: SA_PORT\"\n\t\t\t\t      \" must be set (%J)\\n\",\n\t\t\t\t      ice_cand_type2name(type), addr);\n\t\t\treturn EINVAL;\n\t\t}\n\t}\n\n\t/* lookup candidate, replace if PRIO is higher */\n\n\t/* TODO: dont look up TCP-ACTIVE types for now (port is zero) */\n\tif (proto == IPPROTO_UDP) {\n\n\t\tlcand = trice_lcand_find(icem, -1, compid, proto, addr);\n\t\tif (lcand) {\n\t\t\ttrice_printf(icem,\n\t\t\t\t    \"add_local[%s.%J] --\"\n\t\t\t\t    \" candidate already exists\"\n\t\t\t\t    \" (%H)\\n\",\n\t\t\t\t    ice_cand_type2name(type), addr,\n\t\t\t\t    trice_cand_print, lcand);\n\n\t\t\tif (prio > lcand->attr.prio)\n\t\t\t\tlcand = mem_deref(lcand);\n\t\t\telse {\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\t}\n\n\terr = trice_add_lcandidate(&lcand, icem, &icem->lcandl, compid, NULL,\n\t\t\t\t   proto, prio, addr, base_addr,\n\t\t\t\t   type, rel_addr, tcptype);\n\tif (err)\n\t\treturn err;\n\n\tif (type == ICE_CAND_TYPE_HOST) {\n\n\t\tswitch (proto) {\n\n\t\tcase IPPROTO_UDP:\n\t\t\tif (sock) {\n\t\t\t\tstruct sa laddr;\n\n\t\t\t\tlcand->us = mem_ref(sock);\n\n\t\t\t\terr = udp_local_get(lcand->us,\n\t\t\t\t\t\t    &laddr);\n\t\t\t\tif (err)\n\t\t\t\t\tgoto out;\n\n\t\t\t\tlcand->attr.addr = *addr;\n\t\t\t\tsa_set_port(&lcand->attr.addr,\n\t\t\t\t\t    sa_port(&laddr));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (icem->ports.min && icem->ports.max) {\n\t\t\t\t\terr = udp_listen_range(\n\t\t\t\t\t\t      &lcand->us,\n\t\t\t\t\t\t      addr,\n\t\t\t\t\t\t      icem->ports.min,\n\t\t\t\t\t\t      icem->ports.max,\n\t\t\t\t\t\t      dummy_udp_recv,\n\t\t\t\t\t\t      lcand);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\terr = udp_listen(&lcand->us, addr,\n\t\t\t\t\t\t\t dummy_udp_recv,\n\t\t\t\t\t\t\t lcand);\n\t\t\t\t}\n\t\t\t\tif (err)\n\t\t\t\t\tgoto out;\n\n\t\t\t\terr = udp_local_get(lcand->us,\n\t\t\t\t\t\t    &lcand->attr.addr);\n\t\t\t\tif (err)\n\t\t\t\t\tgoto out;\n\t\t\t}\n\n\t\t\terr = udp_register_helper(&lcand->uh, lcand->us,\n\t\t\t\t\t\t  layer,\n\t\t\t\t\t\t  udp_helper_send_handler,\n\t\t\t\t\t\t  udp_helper_recv_handler,\n\t\t\t\t\t\t  lcand);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t\tbreak;\n\n\t\tcase IPPROTO_TCP:\n\n\t\t\t/* TCP-transport has 3 variants:\n\t\t\t   active, passive, so */\n\n\t\t\tif (lcand->attr.tcptype == ICE_TCP_ACTIVE) {\n\n\t\t\t\t/* the port MUST be set to 9 (i.e., Discard) */\n\t\t\t\t/*sa_set_port(&lcand->attr.addr, 9); */\n\t\t\t}\n\t\t\telse if (lcand->attr.tcptype == ICE_TCP_PASSIVE ||\n\t\t\t\t lcand->attr.tcptype == ICE_TCP_SO) {\n\n\t\t\t\terr = tcp_listen(&lcand->ts, addr,\n\t\t\t\t\t\t tcp_conn_handler, lcand);\n\t\t\t\tif (err)\n\t\t\t\t\tgoto out;\n\t\t\t\terr = tcp_local_get(lcand->ts,\n\t\t\t\t\t\t    &lcand->attr.addr);\n\t\t\t\tif (err)\n\t\t\t\t\tgoto out;\n\t\t\t}\n\t\t\telse {\n\t\t\t\terr = EPROTONOSUPPORT;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\terr = EPROTONOSUPPORT;\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse if (type == ICE_CAND_TYPE_RELAY) {\n\n\t\tswitch (proto) {\n\n\t\tcase IPPROTO_UDP:\n\n\t\t\tif (sock) {\n\t\t\t\tlcand->us = mem_ref(sock);\n\t\t\t}\n\t\t\telse {\n\t\t\t\terr = udp_listen(&lcand->us, NULL,\n\t\t\t\t\t\t dummy_udp_recv, lcand);\n\t\t\t\tif (err)\n\t\t\t\t\tgoto out;\n\t\t\t}\n\n\t\t\terr = udp_register_helper(&lcand->uh, lcand->us,\n\t\t\t\t\t\t  layer,\n\t\t\t\t\t\t  udp_helper_send_handler,\n\t\t\t\t\t\t  udp_helper_recv_handler,\n\t\t\t\t\t\t  lcand);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\terr = EPROTONOSUPPORT;\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse if (type == ICE_CAND_TYPE_SRFLX ||\n\t\t type == ICE_CAND_TYPE_PRFLX) {\n\n\t\t/* Special case for SRFLX UDP candidates, if he has\n\t\t * its own UDP-socket that can be used.\n\t\t */\n\t\tif (proto == IPPROTO_UDP && sock) {\n\n\t\t\tlcand->us = mem_ref(sock);\n\n\t\t\terr = udp_register_helper(&lcand->uh, lcand->us,\n\t\t\t\t\t\t  layer,\n\t\t\t\t\t\t  udp_helper_send_handler,\n\t\t\t\t\t\t  udp_helper_recv_handler,\n\t\t\t\t\t\t  lcand);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t}\n\n\tlcand->layer = layer;\n\n\tif (icem->lrole != ICE_ROLE_UNKNOWN) {\n\t\t/* pair this local-candidate with all existing\n\t\t * remote-candidates */\n\t\terr = trice_candpair_with_local(icem, lcand);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\t/* new pair -- refresh the checklist timer */\n\t\ttrice_checklist_refresh(icem);\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(lcand);\n\telse if (lcandp)\n\t\t*lcandp = lcand;\n\n\treturn err;\n}\n\n\nstruct ice_lcand *trice_lcand_find(struct trice *icem,\n\t\t\t\t   enum ice_cand_type type,\n\t\t\t\t   unsigned compid, int proto,\n\t\t\t\t   const struct sa *addr)\n{\n\tstruct list *lst;\n\tstruct le *le;\n\n\tif (!icem)\n\t\treturn NULL;\n\n\tif (!proto) {\n\t\tDEBUG_WARNING(\"find_candidate: invalid args\\n\");\n\t\treturn NULL;\n\t}\n\n\tlst = &icem->lcandl;\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\n\t\tstruct ice_cand_attr *cand = le->data;\n\n\t\tif (type != (enum ice_cand_type)-1 && type != cand->type)\n\t\t\tcontinue;\n\n\t\tif (compid && cand->compid != compid)\n\t\t\tcontinue;\n\n\t\tif (cand->proto != proto)\n\t\t\tcontinue;\n\n\t\tif (addr && !sa_cmp(&cand->addr, addr, SA_ALL))\n\t\t\tcontinue;\n\n\t\treturn (void *)cand;\n\t}\n\n\treturn NULL;\n}\n\n\nstruct ice_lcand *trice_lcand_find2(const struct trice *icem,\n\t\t\t\t    enum ice_cand_type type, int af)\n{\n\tstruct le *le;\n\n\tif (!icem)\n\t\treturn NULL;\n\n\tfor (le = list_head(&icem->lcandl); le; le = le->next) {\n\n\t\tstruct ice_cand_attr *cand = le->data;\n\n\t\tif (cand->type != type)\n\t\t\tcontinue;\n\n\t\tif (af != sa_af(&cand->addr))\n\t\t\tcontinue;\n\n\t\treturn (void *)cand;\n\t}\n\n\treturn NULL;\n}\n\n\nint trice_lcands_debug(struct re_printf *pf, const struct list *lst)\n{\n\tstruct le *le;\n\tint err;\n\n\terr = re_hprintf(pf, \" (%u)\\n\", list_count(lst));\n\n\tfor (le = list_head(lst); le && !err; le = le->next) {\n\n\t\tconst struct ice_lcand *cand = le->data;\n\n\t\terr |= re_hprintf(pf, \"  {%u} [tx=%3zu, rx=%3zu] \"\n\t\t\t\t  \"fnd=%-8s prio=%08x \",\n\t\t\t\t  cand->attr.compid,\n\t\t\t\t  cand->stats.n_tx,\n\t\t\t\t  cand->stats.n_rx,\n\t\t\t\t  cand->attr.foundation,\n\t\t\t\t  cand->attr.prio);\n\n\t\tif (str_isset(cand->ifname))\n\t\t\terr |= re_hprintf(pf, \"%s:\", cand->ifname);\n\n\t\terr |= re_hprintf(pf, \"%24H\", trice_cand_print, cand);\n\n\t\tif (sa_isset(&cand->base_addr, SA_ADDR)) {\n\t\t\terr |= re_hprintf(pf, \" (base-addr = %J)\",\n\t\t\t\t\t  &cand->base_addr);\n\t\t}\n\n\t\tif (sa_isset(&cand->attr.rel_addr, SA_ADDR)) {\n\t\t\terr |= re_hprintf(pf, \" (rel-addr = %J)\",\n\t\t\t\t\t  &cand->attr.rel_addr);\n\t\t}\n\n\t\terr |= re_hprintf(pf, \"\\n\");\n\t}\n\n\treturn err;\n}\n\n\nvoid *trice_lcand_sock(struct trice *icem, const struct ice_lcand *lcand)\n{\n\tstruct ice_lcand *base = NULL;\n\n\tif (!icem || !lcand)\n\t\treturn NULL;\n\n\tif (sa_isset(&lcand->base_addr, SA_ALL)) {\n\n\t\tenum ice_cand_type base_type;\n\n\t\tbase_type = ice_cand_type_base(lcand->attr.type);\n\n\t\tbase = trice_lcand_find(icem,\n\t\t\t\t\tbase_type,\n\t\t\t\t\tlcand->attr.compid,\n\t\t\t\t\tlcand->attr.proto,\n\t\t\t\t\t&lcand->base_addr);\n\t}\n\n\t/* note: original lcand has precedence, fallback to base-candidate */\n\tswitch (lcand->attr.type) {\n\n\tcase ICE_CAND_TYPE_HOST:\n\t\treturn lcand->us;\n\n\tcase ICE_CAND_TYPE_SRFLX:\n\tcase ICE_CAND_TYPE_PRFLX:\n\t\tif (lcand->us)\n\t\t\treturn lcand->us;\n\t\telse if (base && base->us)\n\t\t\treturn base->us;\n\t\telse {\n\t\t\tDEBUG_NOTICE(\"lcand_sock: no SOCK or BASE for \"\n\t\t\t\t     \" type '%s'\\n\",\n\t\t\t\t     ice_cand_type2name(lcand->attr.type));\n\t\t\treturn NULL;\n\t\t}\n\n\tcase ICE_CAND_TYPE_RELAY:\n\t\treturn lcand->us;\n\n\tdefault:\n\t\treturn NULL;\n\t}\n\n\treturn NULL;\n}\n\n\nvoid trice_lcand_recv_packet(struct ice_lcand *lcand,\n\t\t\t     const struct sa *src, struct mbuf *mb)\n{\n\tstruct sa addr;\n\n\tif (!lcand || !src || !mb)\n\t\treturn;\n\n\taddr = *src;\n\n\tudp_helper_recv_handler(&addr, mb, lcand);\n}\n"
  },
  {
    "path": "src/trice/rcand.c",
    "content": "/**\n * @file rcand.c  Remote ICE Candidates\n *\n * Copyright (C) 2010 - 2015 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_net.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include <re_trice.h>\n#include \"trice.h\"\n\n\n#define DEBUG_MODULE \"rcand\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic void rcand_destructor(void *data)\n{\n\tstruct ice_rcand *cand = data;\n\n\tlist_unlink(&cand->le);\n}\n\n\nstatic int trice_add_rcandidate(struct ice_rcand **candp,\n\t\t\t       struct list *lst,\n\t\t\t       unsigned compid, const char *foundation,\n\t\t\t       int proto,\n\t\t\t       uint32_t prio, const struct sa *addr,\n\t\t\t       enum ice_cand_type type,\n\t\t\t       enum ice_tcptype tcptype)\n{\n\tstruct ice_rcand *cand;\n\n\tif (!lst || !compid || !foundation || !proto || !addr)\n\t\treturn EINVAL;\n\n\tcand = mem_zalloc(sizeof(*cand), rcand_destructor);\n\tif (!cand)\n\t\treturn ENOMEM;\n\n\tcand->attr.compid  = compid;\n\tcand->attr.proto   = proto;\n\tcand->attr.prio\t   = prio;\n\tcand->attr.addr\t   = *addr;\n\tcand->attr.type\t   = type;\n\tcand->attr.tcptype = tcptype;\n\n\tstr_ncpy(cand->attr.foundation, foundation,\n\t\t sizeof(cand->attr.foundation));\n\n\tlist_append(lst, &cand->le, cand);\n\n\t*candp = cand;\n\n\treturn 0;\n}\n\n\n/* you can call this at any time */\nint trice_rcand_add(struct ice_rcand **rcandp, struct trice *icem,\n\t\t    unsigned compid, const char *foundation,\n\t\t    int proto, uint32_t prio,\n\t\t    const struct sa *addr, enum ice_cand_type type,\n\t\t    enum ice_tcptype tcptype)\n{\n\tstruct ice_rcand *rcand;\n\tint sa_flags = SA_ADDR;\n\tint err = 0;\n\n\tif (!icem || !foundation)\n\t\treturn EINVAL;\n\n\tif (proto == IPPROTO_UDP)\n\t\tsa_flags |= SA_PORT;\n\n\tif (proto == IPPROTO_TCP &&\n\t    (tcptype == ICE_TCP_PASSIVE || tcptype == ICE_TCP_SO))\n\t\tsa_flags |= SA_PORT;\n\n\tif (!sa_isset(addr, sa_flags)) {\n\t\tDEBUG_WARNING(\"add_remote_candidate: invalid address\"\n\t\t\t      \" (%J) for %s.%s\\n\",\n\t\t\t      addr, net_proto2name(proto),\n\t\t\t      ice_tcptype_name(tcptype));\n\t\treturn EINVAL;\n\t}\n\n\t/* avoid duplicates */\n\trcand = trice_rcand_find(icem, compid, proto, addr);\n\tif (rcand) {\n\n\t\tif (rcand->attr.type == ICE_CAND_TYPE_PRFLX &&\n\t\t    prio > rcand->attr.prio) {\n\n\t\t\trcand->attr.type = type;\n\t\t\trcand->attr.prio = prio;\n\t\t}\n\n\t\tgoto out;\n\t}\n\n\terr = trice_add_rcandidate(&rcand, &icem->rcandl,\n\t\t\t\t compid, foundation,\n\t\t\t\t proto, prio, addr, type, tcptype);\n\tif (err)\n\t\tgoto out;\n\n\tif (icem->lrole != ICE_ROLE_UNKNOWN) {\n\t\t/* pair this remote-candidate with all existing\n\t\t * local-candidates */\n\t\terr = trice_candpair_with_remote(icem, rcand);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\t/* new pair -- refresh the checklist timer */\n\t\ttrice_checklist_refresh(icem);\n\t}\n\n out:\n\tif (err)\n\t\tmem_deref(rcand);\n\telse if (rcandp)\n\t\t*rcandp = rcand;\n\n\treturn err;\n}\n\n\nstruct ice_rcand *trice_rcand_find(struct trice *icem,\n\t\t\t\t   unsigned compid, int proto,\n\t\t\t\t   const struct sa *addr)\n{\n\tstruct list *lst;\n\tstruct le *le;\n\n\tif (!icem)\n\t\treturn NULL;\n\n\tif (!proto) {\n\t\tDEBUG_WARNING(\"find_candidate: invalid args\\n\");\n\t\treturn NULL;\n\t}\n\n\tlst = &icem->rcandl;\n\n\tfor (le = list_head(lst); le; le = le->next) {\n\n\t\tstruct ice_cand_attr *cand = le->data;\n\n\t\tif (compid && cand->compid != compid)\n\t\t\tcontinue;\n\n\t\tif (cand->proto != proto)\n\t\t\tcontinue;\n\n\t\tif (addr && !sa_cmp(&cand->addr, addr, SA_ALL))\n\t\t\tcontinue;\n\n\t\treturn (void *)cand;\n\t}\n\n\treturn NULL;\n}\n\n\nint trice_rcands_debug(struct re_printf *pf, const struct list *lst)\n{\n\tstruct le *le;\n\tint err;\n\n\terr = re_hprintf(pf, \" (%u)\\n\", list_count(lst));\n\n\tfor (le = list_head(lst); le && !err; le = le->next) {\n\n\t\tconst struct ice_rcand *rcand = le->data;\n\n\t\terr |= re_hprintf(pf, \"  {%u} \"\n\t\t\t\t  \"fnd=%-8s prio=%08x %24H\",\n\t\t\t\t  rcand->attr.compid,\n\t\t\t\t  rcand->attr.foundation,\n\t\t\t\t  rcand->attr.prio,\n\t\t\t\t  trice_cand_print, rcand);\n\n\t\terr |= re_hprintf(pf, \"\\n\");\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/trice/stunsrv.c",
    "content": "/**\n * @file stunsrv.c  Basic STUN Server for Connectivity checks\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include <re_udp.h>\n#include <re_tcp.h>\n#include <re_sys.h>\n#include <re_trice.h>\n#include \"trice.h\"\n\n\n#define DEBUG_MODULE \"stunsrv\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic const char *sw = \"ice stunsrv v\" RE_VERSION \" (\" ARCH \"/\" OS \")\";\n\n\n/*\n * NOTE about TCP-candidates:\n *\n *     Note that STUN requests received on a passive TCP candidate\n *     will typically produce a remote peer reflexive candidate.\n */\nstatic int handle_stun_full(struct trice *icem, struct ice_lcand *lcand,\n\t\t\t    void *sock, const struct sa *src,\n\t\t\t    uint32_t prio, bool use_cand)\n{\n\tstruct ice_candpair *pair = NULL;\n\tstruct ice_rcand *rcand;\n\tenum ice_tcptype tcptype_rev;\n\tint err = 0;\n\n\ttrice_tracef(icem, 36,\n\t\t     \"[%u] STUNSRV: Rx Binding Request [%H <--- %J] %s\\n\",\n\t\t     lcand->attr.compid,\n\t\t     trice_cand_print, lcand,\n\t\t     src,\n\t\t     use_cand ? \"[USE]\" : \"\");\n\n\ttcptype_rev = ice_tcptype_reverse(lcand->attr.tcptype);\n\n\trcand = trice_rcand_find(icem, lcand->attr.compid,\n\t\t\t\t lcand->attr.proto, src);\n\tif (!rcand) {\n\n\t\terr = trice_rcand_add(&rcand, icem,\n\t\t\t\t      lcand->attr.compid,\n\t\t\t\t      \"444\", lcand->attr.proto, prio,\n\t\t\t\t      src, ICE_CAND_TYPE_PRFLX,\n\t\t\t\t      tcptype_rev);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\ttrice_printf(icem, \"{%u} added PRFLX \"\n\t\t\t     \"remote candidate (%H)\\n\",\n\t\t\t     lcand->attr.compid,\n\t\t\t     trice_cand_print, rcand);\n\t}\n\n\t/* already valid, skip */\n\tpair = trice_candpair_find(&icem->validl, lcand, rcand);\n\tif (pair)\n\t\tgoto out;\n\n\t/* note: the candidate-pair can exist in either list */\n\tpair = trice_candpair_find(&icem->checkl, lcand, rcand);\n\tif (!pair) {\n\t\tDEBUG_WARNING(\"{%u} candidate pair not found:\"\n\t\t\t      \" source=%J\\n\",\n\t\t\t      lcand->attr.compid, src);\n\t\tgoto out;\n\t}\n\n\t/* 7.2.1.5.  Updating the Nominated Flag */\n\tif (use_cand) {\n\t\tif (icem->lrole == ICE_ROLE_CONTROLLED) {\n\n\t\t\tpair->nominated = true;\n\t\t}\n\t}\n\n out:\n\t/*\n\t  send a triggered request\n\t */\n\tif (pair && use_cand) {\n\n\t\tif (icem->checklist && !pair->trigged) {\n\n\t\t\terr = trice_conncheck_trigged(icem, pair,\n\t\t\t\t\t\t     sock, use_cand);\n\t\t\tif (err) {\n\t\t\t\tDEBUG_WARNING(\"ice_checklist_stun_request\"\n\t\t\t\t\t      \" failed (%m)\\n\",\n\t\t\t\t\t      err);\n\t\t\t}\n\t\t\tpair->trigged = true;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n\nstatic int stunsrv_ereply(struct trice *icem, struct ice_lcand *lcand,\n\t\t\t  void *sock, const struct sa *src,\n\t\t\t  size_t presz, const struct stun_msg *req,\n\t\t\t  uint16_t scode, const char *reason)\n{\n\tDEBUG_WARNING(\"[%H] replying error to %J (%u %s)\\n\",\n\t\t      trice_cand_print, lcand,\n\t\t      src,\n\t\t      scode, reason);\n\n\ttrice_tracef(icem, 31,\n\t\t     \"[%u] STUNSRV: Tx error [%J <--- %H] (%u %s)\\n\",\n\t\t     lcand->attr.compid,\n\t\t     src,\n\t\t     trice_cand_print, lcand,\n\t\t     scode, reason);\n\n\treturn stun_ereply(lcand->attr.proto, sock, src, presz, req,\n\t\t\t   scode, reason,\n\t\t\t   (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 1,\n\t\t\t   STUN_ATTR_SOFTWARE, sw);\n}\n\n\nint trice_stund_recv(struct trice *icem, struct ice_lcand *lcand,\n\t\t    void *sock, const struct sa *src,\n\t\t    struct stun_msg *req, size_t presz)\n{\n\tstruct stun_attr *attr;\n\tstruct pl lu, ru;\n\tint err;\n\n\t/* RFC 5389: Fingerprint errors are silently discarded */\n\terr = stun_msg_chk_fingerprint(req);\n\tif (err)\n\t\treturn err;\n\n\terr = stun_msg_chk_mi(req, (uint8_t *)icem->lpwd, strlen(icem->lpwd));\n\tif (err) {\n\t\tDEBUG_WARNING(\"message-integrity failed (src=%J)\\n\", src);\n\t\tif (err == EBADMSG)\n\t\t\tgoto unauth;\n\t\telse\n\t\t\tgoto badmsg;\n\t}\n\n\tattr = stun_msg_attr(req, STUN_ATTR_USERNAME);\n\tif (!attr)\n\t\tgoto badmsg;\n\n\terr = re_regex(attr->v.username, strlen(attr->v.username),\n\t\t       \"[^:]+:[^]+\", &lu, &ru);\n\tif (err) {\n\t\tDEBUG_WARNING(\"could not parse USERNAME attribute (%s)\\n\",\n\t\t\t      attr->v.username);\n\t\tgoto unauth;\n\t}\n\tif (pl_strcmp(&lu, icem->lufrag)) {\n\t\tDEBUG_WARNING(\"local ufrag err (expected %s, actual %r)\\n\",\n\t\t\t      icem->lufrag, &lu);\n\t\tgoto unauth;\n\t}\n\tif (str_isset(icem->rufrag) && pl_strcmp(&ru, icem->rufrag)) {\n\t\tDEBUG_WARNING(\"remote ufrag err (expected %s, actual %r)\\n\",\n\t\t\t      icem->rufrag, &ru);\n\t\tgoto unauth;\n\t}\n\n\tif (icem->lrole == ICE_ROLE_UNKNOWN) {\n\t\terr = trice_reqbuf_append(icem, lcand, sock, src, req, presz);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"unable to buffer STUN request: %m\\n\",\n\t\t\t\t      err);\n\t\t}\n\t}\n\n\treturn trice_stund_recv_role_set(icem, lcand, sock, src, req, presz);\n\n badmsg:\n\treturn stunsrv_ereply(icem, lcand, sock, src, presz, req,\n\t\t\t      400, \"Bad Request\");\n\n unauth:\n\treturn stunsrv_ereply(icem, lcand, sock, src, presz, req,\n\t\t\t      401, \"Unauthorized\");\n}\n\n\nint trice_stund_recv_role_set(struct trice *icem, struct ice_lcand *lcand,\n\t\t    void *sock, const struct sa *src,\n\t\t    struct stun_msg *req, size_t presz)\n{\n\tstruct stun_attr *attr;\n\tenum ice_role remote_role = ICE_ROLE_UNKNOWN;\n\tuint64_t tiebrk = 0;\n\tuint32_t prio_prflx;\n\tint err;\n\tbool use_cand = false;\n\n\tattr = stun_msg_attr(req, STUN_ATTR_CONTROLLED);\n\tif (attr) {\n\t\tremote_role = ICE_ROLE_CONTROLLED;\n\t\ttiebrk = attr->v.uint64;\n\t}\n\n\tattr = stun_msg_attr(req, STUN_ATTR_CONTROLLING);\n\tif (attr) {\n\t\tremote_role = ICE_ROLE_CONTROLLING;\n\t\ttiebrk = attr->v.uint64;\n\t}\n\n\tif (remote_role == ICE_ROLE_UNKNOWN)\n\t\tgoto badmsg;\n\n\tif (remote_role == icem->lrole) {\n\t\tDEBUG_NOTICE(\"role conflict detected (both %s)\\n\",\n\t\t\t     ice_role2name(remote_role));\n\n\t\tif (icem->tiebrk >= tiebrk)\n\t\t\ttrice_switch_local_role(icem);\n\t\telse\n\t\t\tgoto conflict;\n\t}\n\n\tattr = stun_msg_attr(req, STUN_ATTR_PRIORITY);\n\tif (attr)\n\t\tprio_prflx = attr->v.uint32;\n\telse\n\t\tgoto badmsg;\n\n\tattr = stun_msg_attr(req, STUN_ATTR_USE_CAND);\n\tif (attr)\n\t\tuse_cand = true;\n\n\terr = handle_stun_full(icem, lcand, sock, src, prio_prflx, use_cand);\n\n\tif (err)\n\t\tgoto badmsg;\n\n\ttrice_tracef(icem, 32,\n\t\t     \"[%u] STUNSRV: Tx success response [%H ---> %J]\\n\",\n\t\t     lcand->attr.compid,\n\t\t     trice_cand_print, lcand, src);\n\n\treturn stun_reply(lcand->attr.proto, sock, src, presz, req,\n\t\t\t  (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 2,\n\t\t\t  STUN_ATTR_XOR_MAPPED_ADDR, src,\n\t\t\t  STUN_ATTR_SOFTWARE, sw);\n\n\n badmsg:\n\treturn stunsrv_ereply(icem, lcand, sock, src, presz, req,\n\t\t\t      400, \"Bad Request\");\n\n conflict:\n\treturn stunsrv_ereply(icem, lcand, sock, src, presz, req,\n\t\t\t      487, \"Role Conflict\");\n}\n"
  },
  {
    "path": "src/trice/tcpconn.c",
    "content": "/**\n * @file tcpconn.c  ICE handling of TCP-connections\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_tcp.h>\n#include <re_udp.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include <re_shim.h>\n#include <re_trice.h>\n#include \"trice.h\"\n\n\n#define DEBUG_MODULE \"tcpconn\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/* `mb' contains a complete frame */\nstatic bool shim_frame_handler(struct mbuf *mb, void *arg)\n{\n\tstruct ice_tcpconn *conn = arg;\n\n\treturn conn->frameh(conn->icem, conn->tc, &conn->paddr, mb, conn->arg);\n}\n\n\nstatic void tcp_estab_handler(void *arg)\n{\n\tstruct ice_tcpconn *conn = arg;\n\tstruct trice *icem = conn->icem;\n\tstruct le *le;\n\tint err;\n\n\tconn->estab = true;\n\n\ttrice_printf(icem, \"TCP established (local=%J <---> peer=%J)\\n\",\n\t\t    &conn->laddr, &conn->paddr);\n\n\terr = shim_insert(&conn->shim, conn->tc, conn->layer,\n\t\t\t  shim_frame_handler, conn);\n\tif (err)\n\t\tgoto out;\n\n\tif (!icem->checklist)\n\t\tgoto out;\n\n\t/* check all pending CONNCHECKs for TCP */\n\tle = icem->checklist->conncheckl.head;\n\twhile (le) {\n\t\tstruct ice_conncheck *cc = le->data;\n\t\tstruct ice_candpair *pair = cc->pair;\n\t\tle = le->next;\n\n\t\tif (pair->state == ICE_CANDPAIR_INPROGRESS &&\n\t\t    pair->lcand->attr.compid == conn->compid &&\n\t\t    pair->lcand->attr.proto == IPPROTO_TCP &&\n\t\t    sa_cmp(&pair->lcand->attr.addr, &conn->laddr, SA_ADDR) &&\n\t\t    sa_cmp(&pair->rcand->attr.addr, &conn->paddr, SA_ALL)) {\n\n\t\t\ttrice_printf(icem,\n\t\t\t\t    \"   estab: sending pending check\"\n\t\t\t\t    \" from %j to %J\\n\",\n\t\t\t\t    &pair->lcand->attr.addr,\n\t\t\t\t    &pair->rcand->attr.addr);\n\n\t\t\t/* todo: */\n\t\t\tpair->conn = mem_ref(conn);\n\n\t\t\terr = trice_conncheck_stun_request(icem->checklist, cc,\n\t\t\t\t\t\t\t pair, conn->tc,\n\t\t\t\t\t\t\t cc->use_cand);\n\t\t\tif (err) {\n\t\t\t\tDEBUG_WARNING(\"stun_request error (%m)\\n\",\n\t\t\t\t\t      err);\n\t\t\t}\n\t\t}\n\t}\n\n out:\n\tif (err) {\n\t\tDEBUG_WARNING(\"estab: errors (%m)\\n\", err);\n\t}\n}\n\n\n/* todo: re-connect if estab and active (with a timer) */\nstatic void tcp_close_handler(int err, void *arg)\n{\n\tstruct ice_tcpconn *conn = arg;\n\tstruct trice *icem = conn->icem;\n\tstruct le *le;\n\n\ttrice_printf(conn->icem, \"TCP-connection [%J -> %J] closed (%m)\\n\",\n\t\t    &conn->laddr, &conn->paddr, err);\n\n\terr = err ? err : ECONNRESET;\n\n\t/* note: helper must be closed before tc */\n\tconn->shim = mem_deref(conn->shim);\n\tconn->tc = mem_deref(conn->tc);\n\n\t/* todo: iterate through conncheckl and cancel all checks\n\t * that are using this conn\n\t */\n\n\tle = conn->icem->checkl.head;\n\twhile (le) {\n\t\tstruct ice_candpair *pair = le->data;\n\n\t\tle = le->next;\n\n\t\tif (pair->lcand->attr.compid == conn->compid &&\n\t\t    pair->lcand->attr.proto == IPPROTO_TCP &&\n\t\t    sa_cmp(&pair->rcand->attr.addr, &conn->paddr, SA_ALL)) {\n\n\t\t\ttrice_candpair_failed(pair, err, 0);\n\n\t\t\tif (icem->checklist) {\n\t\t\t\ticem->checklist->failh(err, 0,\n\t\t\t\t\t\t       pair,\n\t\t\t\t\t\t       icem->checklist->arg);\n\t\t\t}\n\t\t}\n\t}\n\n\tmem_deref(conn);\n}\n\n\nstatic void conn_destructor(void *arg)\n{\n\tstruct ice_tcpconn *conn = arg;\n\n\tlist_unlink(&conn->le);\n\tmem_deref(conn->shim);\n\tmem_deref(conn->tc);\n}\n\n\n/* ts: only for accept */\nint trice_conn_alloc(struct list *connl, struct trice *icem, unsigned compid,\n\t\t   bool active, const struct sa *laddr, const struct sa *peer,\n\t\t   struct tcp_sock *ts, int layer,\n\t\t   tcpconn_frame_h *frameh, void *arg)\n{\n\tstruct ice_tcpconn *conn;\n\tint err = 0;\n\n\tif (!connl || !icem || !laddr || !peer || !frameh)\n\t\treturn EINVAL;\n\n\tconn = mem_zalloc(sizeof(*conn), conn_destructor);\n\tif (!conn)\n\t\treturn ENOMEM;\n\n\tconn->icem = icem;\n\tconn->active = active;\n\tconn->paddr = *peer;\n\tconn->compid = compid;\n\tconn->layer = layer;\n\tconn->frameh = frameh;\n\tconn->arg = arg;\n\n\tif (active) {\n\n\t\ttrice_printf(conn->icem, \"<%p> TCP connecting\"\n\t\t\t    \" [laddr=%J paddr=%J] ..\\n\",\n\t\t\t    icem, laddr, peer);\n\n\t\t/* This connection is opened from the local candidate of the\n\t\t   pair to the remote candidate of the pair.\n\t\t */\n\t\terr = tcp_conn_alloc(&conn->tc, peer, tcp_estab_handler,\n\t\t\t\t     NULL, tcp_close_handler,\n\t\t\t\t     conn);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"tcp_conn_alloc [peer=%J] (%m)\\n\",\n\t\t\t\t      peer, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = tcp_conn_bind(conn->tc, laddr);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"tcp_conn_bind [laddr=%J paddr=%J]\"\n\t\t\t\t      \" (%m)\\n\",\n\t\t\t\t      laddr, peer, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = tcp_conn_connect(conn->tc, peer);\n\t\tif (err) {\n\t\t\t/* NOTE: this happens sometimes on OSX when\n\t\t\t *       setting up two S-O connections\n\t\t\t */\n\t\t\tif (err == EADDRINUSE) {\n\t\t\t\tre_printf(\"EADDRINUSE\\n\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tDEBUG_NOTICE(\"tcp_conn_connect [peer=%J]\"\n\t\t\t\t\t      \" (%d/%m)\\n\",\n\t\t\t\t\t      peer, err, err);\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\t}\n\telse {\n\t\terr = tcp_accept(&conn->tc, ts, tcp_estab_handler,\n\t\t\t\t NULL, tcp_close_handler, conn);\n\t\tif (err) {\n\t\t\ttcp_reject(ts);\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\terr = tcp_conn_local_get(conn->tc, &conn->laddr);\n\tif (err)\n\t\tgoto out;\n\n\tlist_append(connl, &conn->le, conn);\n\n out:\n\tif (err)\n\t\tmem_deref(conn);\n\n\treturn err;\n}\n\n\n/* NOTE: laddr matching is SA_ADDR only */\nstruct ice_tcpconn *trice_conn_find(struct list *connl, unsigned compid,\n\t\t\t\t  const struct sa *laddr,\n\t\t\t\t  const struct sa *peer)\n{\n\tstruct le *le;\n\n\tfor (le = list_head(connl); le; le = le->next) {\n\n\t\tstruct ice_tcpconn *conn = le->data;\n\n\t\tif (compid != conn->compid)\n\t\t\tcontinue;\n\n\t\t/* NOTE: only for established */\n\t\tif (!conn->estab)\n\t\t\tcontinue;\n\n\t\tif (sa_cmp(laddr, &conn->laddr, SA_ADDR) &&\n\t\t    sa_cmp(peer, &conn->paddr, SA_ALL))\n\t\t\treturn conn;\n\t}\n\n\treturn NULL;\n}\n\n\nint trice_conn_debug(struct re_printf *pf, const struct ice_tcpconn *conn)\n{\n\tint err;\n\n\tif (!conn)\n\t\treturn 0;\n\n\terr = re_hprintf(pf, \"... {%u} [%s|%5s] %J - %J \"\n\t\t\t  \" (usage = %u) \",\n\t\t\t  conn->compid,\n\t\t\t  conn->active ? \"Active\" : \"Passive\",\n\t\t\t  conn->estab ? \"ESTAB\" : \"     \",\n\t\t\t  &conn->laddr, &conn->paddr,\n\t\t\t  mem_nrefs(conn)-1);\n\n\tif (conn->shim)\n\t\terr |= shim_debug(pf, conn->shim);\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/trice/trice.c",
    "content": "/**\n * @file trice.c  ICE Media stream\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_stun.h>\n#include <re_ice.h>\n#include <re_sys.h>\n#include <re_trice.h>\n#include \"trice.h\"\n\n\n#define DEBUG_MODULE \"icem\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic const struct trice_conf conf_default = {\n\tfalse,\n\tfalse,\n\tfalse,\n\tfalse\n};\n\n\nstatic void trice_destructor(void *data)\n{\n\tstruct trice *icem = data;\n\n\tmem_deref(icem->checklist);\n\n\tlist_flush(&icem->validl);\n\tlist_flush(&icem->checkl);\n\tlist_flush(&icem->lcandl);\n\tlist_flush(&icem->rcandl);\n\tlist_flush(&icem->reqbufl);\n\n\tlist_flush(&icem->connl);\n\n\tmem_deref(icem->rufrag);\n\tmem_deref(icem->rpwd);\n\tmem_deref(icem->lufrag);\n\tmem_deref(icem->lpwd);\n}\n\n\n/**\n * Allocate a new ICE Media object\n *\n * @param icemp       Pointer to allocated ICE Media object\n * @param conf        ICE configuration\n * @param role        Local role\n * @param lufrag      Local username fragment\n * @param lpwd        Local password\n *\n * @return 0 if success, otherwise errorcode\n */\nint trice_alloc(struct trice **icemp, const struct trice_conf *conf,\n\t       enum ice_role role,\n\t       const char *lufrag, const char *lpwd)\n{\n\tstruct trice *icem;\n\tint err = 0;\n\n\tif (!icemp || !lufrag || !lpwd)\n\t\treturn EINVAL;\n\n\tif (str_len(lufrag) < 4 || str_len(lpwd) < 22) {\n\t\tDEBUG_WARNING(\"alloc: lufrag/lpwd is too short\\n\");\n\t\treturn EINVAL;\n\t}\n\n\ticem = mem_zalloc(sizeof(*icem), trice_destructor);\n\tif (!icem)\n\t\treturn ENOMEM;\n\n\ticem->conf = conf ? *conf : conf_default;\n\tlist_init(&icem->reqbufl);\n\tlist_init(&icem->lcandl);\n\tlist_init(&icem->rcandl);\n\tlist_init(&icem->checkl);\n\tlist_init(&icem->validl);\n\n\ticem->lrole = role;\n\ticem->tiebrk = rand_u64();\n\n\terr |= str_dup(&icem->lufrag, lufrag);\n\terr |= str_dup(&icem->lpwd, lpwd);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(icem);\n\telse\n\t\t*icemp = icem;\n\n\treturn err;\n}\n\n\n/**\n * Set the remote username fragment\n *\n * @param icem    ICE Media object\n * @param rufrag  Remote username fragment\n *\n * @return 0 if success, otherwise errorcode\n */\nint trice_set_remote_ufrag(struct trice *icem, const char *rufrag)\n{\n\tif (!icem || !rufrag)\n\t\treturn EINVAL;\n\n\ticem->rufrag = mem_deref(icem->rufrag);\n\treturn str_dup(&icem->rufrag, rufrag);\n}\n\n\n/**\n * Set the remote password\n *\n * @param icem  ICE Media object\n * @param rpwd  Remote password\n *\n * @return 0 if success, otherwise errorcode\n */\nint trice_set_remote_pwd(struct trice *icem, const char *rpwd)\n{\n\tif (!icem || !rpwd)\n\t\treturn EINVAL;\n\n\ticem->rpwd = mem_deref(icem->rpwd);\n\n\treturn str_dup(&icem->rpwd, rpwd);\n}\n\n\n/**\n * Get the ICE Configuration\n *\n * @param icem ICE Media object\n *\n * @return ICE Configuration\n */\nstruct trice_conf *trice_conf(struct trice *icem)\n{\n\treturn icem ? &icem->conf : NULL;\n}\n\n\n/* note: call this ONCE AFTER role has been set */\nstatic void trice_create_candpairs(struct trice *icem)\n{\n\tstruct list *lst;\n\tstruct le *le;\n\tbool refresh_checklist = false;\n\tint err;\n\n\tlst = &icem->lcandl;\n\tfor (le = list_head(lst); le; le = le->next) {\n\t\tstruct ice_lcand *lcand = le->data;\n\n\t\t/* pair this local-candidate with all existing\n\t\t * remote-candidates */\n\t\terr = trice_candpair_with_local(icem, lcand);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"trice_candpair_with_local: %m\\n\", err);\n\t\t}\n\t\telse {\n\t\t\trefresh_checklist = true;\n\t\t}\n\t}\n\n\tlst = &icem->rcandl;\n\tfor (le = list_head(lst); le; le = le->next) {\n\t\tstruct ice_rcand *rcand = le->data;\n\n\t\t/* pair this remote-candidate with all existing\n\t\t * local-candidates */\n\t\terr = trice_candpair_with_remote(icem, rcand);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"trice_candpair_with_remote: %m\\n\", err);\n\t\t}\n\t\telse {\n\t\t\trefresh_checklist = true;\n\t\t}\n\t}\n\n\t/* new pair -- refresh the checklist timer */\n\tif (refresh_checklist)\n\t\ttrice_checklist_refresh(icem);\n}\n\n\n/* note: call this AFTER role has been set AND candidate pairs\n * have been created */\nstatic void trice_reqbuf_process(struct trice *icem)\n{\n\tstruct le *le;\n\n\tle = list_head(&icem->reqbufl);\n\twhile (le) {\n\t\tstruct trice_reqbuf *reqbuf = le->data;\n\t\tle = le->next;\n\n\t\tDEBUG_PRINTF(\"trice_reqbuf_process: Processing buffered \"\n\t\t\t     \"request\\n\");\n\n\t\t(void)trice_stund_recv_role_set(icem, reqbuf->lcand,\n\t\t\t\treqbuf->sock, &reqbuf->src, reqbuf->req,\n\t\t\t\treqbuf->presz);\n\n\t\tmem_deref(reqbuf);\n\t}\n}\n\n\n/**\n * Set the local role to either CONTROLLING or CONTROLLED.\n * Note: The role can be set multiple times.\n *\n * @param trice ICE Media object\n * @param role  New local role\n *\n * @return 0 if success, otherwise errorcode\n */\nint trice_set_role(struct trice *trice, enum ice_role role)\n{\n\tbool refresh;\n\n\tif (!trice)\n\t\treturn EINVAL;\n\n\t/* Cannot change the role to unknown */\n\tif (role == ICE_ROLE_UNKNOWN)\n\t\treturn EINVAL;\n\n\tif (trice->lrole == role)\n\t\treturn 0;\n\n\t/* Cannot switch role manually once it has been set */\n\tif (trice->lrole == ICE_ROLE_UNKNOWN)\n\t\trefresh = false;\n\telse\n\t\trefresh = true;\n\n\ttrice->lrole = role;\n\n\t/* Create candidate pairs and process pending requests */\n\tif (refresh) {\n\t\ttrice_candpair_prio_order(&trice->checkl,\n\t\t\t\t\t  role == ICE_ROLE_CONTROLLING);\n\t}\n\telse {\n\t\ttrice_create_candpairs(trice);\n\t}\n\n\ttrice_reqbuf_process(trice);\n\n\treturn 0;\n}\n\n\n/**\n * Get the local role\n *\n * @param icem  ICE Media object\n *\n * @return Local role\n */\nenum ice_role trice_local_role(const struct trice *icem)\n{\n\tif (!icem)\n\t\treturn ICE_ROLE_UNKNOWN;\n\n\treturn icem->lrole;\n}\n\n\n/**\n * Print debug information for the ICE Media\n *\n * @param pf   Print function for debug output\n * @param icem ICE Media object\n *\n * @return 0 if success, otherwise errorcode\n */\nint trice_debug(struct re_printf *pf, const struct trice *icem)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tif (!icem)\n\t\treturn 0;\n\n\terr |= re_hprintf(pf, \"----- ICE Media <%p> -----\\n\", icem);\n\n\terr |= re_hprintf(pf, \" local_role=%s\\n\",\n\t\t\t  ice_role2name(icem->lrole));\n\terr |= re_hprintf(pf, \" local_ufrag=\\\"%s\\\" local_pwd=\\\"%s\\\"\\n\",\n\t\t\t  icem->lufrag, icem->lpwd);\n\n\terr |= re_hprintf(pf, \" Local Candidates: %H\",\n\t\t\t  trice_lcands_debug, &icem->lcandl);\n\terr |= re_hprintf(pf, \" Remote Candidates: %H\",\n\t\t\t  trice_rcands_debug, &icem->rcandl);\n\terr |= re_hprintf(pf, \" Check list: \");\n\terr |= trice_candpairs_debug(pf, icem->conf.ansi, &icem->checkl);\n\n\terr |= re_hprintf(pf, \" Valid list: \");\n\terr |= trice_candpairs_debug(pf, icem->conf.ansi, &icem->validl);\n\n\terr |= re_hprintf(pf, \" Buffered STUN Requests: (%u)\\n\",\n\t\t\t  list_count(&icem->reqbufl));\n\n\tif (icem->checklist)\n\t\terr |= trice_checklist_debug(pf, icem->checklist);\n\n\terr |= re_hprintf(pf, \" TCP Connections: (%u)\\n\",\n\t\t\t  list_count(&icem->connl));\n\n\tfor (le = list_head(&icem->connl); le; le = le->next) {\n\t\tstruct ice_tcpconn *conn = le->data;\n\n\t\terr |= re_hprintf(pf, \"      %H\\n\",\n\t\t\t\t  trice_conn_debug, conn);\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Get the list of Local Candidates (struct cand)\n *\n * @param icem ICE Media object\n *\n * @return List of Local Candidates\n */\nstruct list *trice_lcandl(const struct trice *icem)\n{\n\treturn icem ? (struct list *)&icem->lcandl : NULL;\n}\n\n\n/**\n * Get the list of Remote Candidates (struct cand)\n *\n * @param icem ICE Media object\n *\n * @return List of Remote Candidates\n */\nstruct list *trice_rcandl(const struct trice *icem)\n{\n\treturn icem ? (struct list *)&icem->rcandl : NULL;\n}\n\n\n/**\n * Get the checklist of Candidate Pairs\n *\n * @param icem ICE Media object\n *\n * @return Checklist (struct ice_candpair)\n */\nstruct list *trice_checkl(const struct trice *icem)\n{\n\treturn icem ? (struct list *)&icem->checkl : NULL;\n}\n\n\n/**\n * Get the list of valid Candidate Pairs\n *\n * @param icem ICE Media object\n *\n * @return Validlist (struct ice_candpair)\n */\nstruct list *trice_validl(const struct trice *icem)\n{\n\treturn icem ? (struct list *)&icem->validl : NULL;\n}\n\n\nvoid trice_printf(struct trice *icem, const char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (!icem || !icem->conf.debug)\n\t\treturn;\n\n\tva_start(ap, fmt);\n\t(void)re_printf(\"%v\", fmt, &ap);\n\tva_end(ap);\n}\n\n\nvoid trice_tracef(struct trice *icem, int color, const char *fmt, ...)\n{\n\tva_list ap;\n\n\tif (!icem || !icem->conf.trace)\n\t\treturn;\n\n\tif (icem->conf.ansi && color) {\n\t\tre_printf(\"\\x1b[%dm\", color);\n\t}\n\n\tva_start(ap, fmt);\n\t(void)re_printf(\"%v\", fmt, &ap);\n\tva_end(ap);\n\n\tif (icem->conf.ansi && color) {\n\t\tre_printf(\"\\x1b[;m\");\n\t}\n}\n\n\nvoid trice_switch_local_role(struct trice *ice)\n{\n\tenum ice_role new_role;\n\n\tif (!ice)\n\t\treturn;\n\n\tswitch (ice->lrole) {\n\n\tcase ICE_ROLE_CONTROLLING:\n\t\tnew_role = ICE_ROLE_CONTROLLED;\n\t\tbreak;\n\n\tcase ICE_ROLE_CONTROLLED:\n\t\tnew_role = ICE_ROLE_CONTROLLING;\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"trice_switch_local_role: local role unknown\\n\");\n\t\treturn;\n\t}\n\n\tDEBUG_NOTICE(\"Switch local role from %s to %s\\n\",\n\t\t     ice_role2name(ice->lrole), ice_role2name(new_role));\n\n\tice->lrole = new_role;\n\n\t/* recompute pair priorities for all media streams */\n\ttrice_candpair_prio_order(&ice->checkl,\n\t\t\t\t  ice->lrole == ICE_ROLE_CONTROLLING);\n}\n\n\n/* sock = [ struct udp_sock | struct tcp_conn ] */\nbool trice_stun_process(struct trice *icem, struct ice_lcand *lcand,\n\t\t       int proto, void *sock, const struct sa *src,\n\t\t       struct mbuf *mb)\n{\n\tstruct stun_msg *msg = NULL;\n\tstruct stun_unknown_attr ua;\n\tsize_t start = mb->pos;\n\t(void)proto;\n\n\tif (stun_msg_decode(&msg, mb, &ua)) {\n\t\treturn false;  /* continue recv-processing */\n\t}\n\n\tif (STUN_METHOD_BINDING == stun_msg_method(msg)) {\n\n\t\tswitch (stun_msg_class(msg)) {\n\n\t\tcase STUN_CLASS_REQUEST:\n\t\t\t(void)trice_stund_recv(icem, lcand, sock,\n\t\t\t\t\t      src, msg, start);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tif (icem->checklist) {\n\t\t\t\t(void)stun_ctrans_recv(icem->checklist->stun,\n\t\t\t\t\t\t       msg, &ua);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tDEBUG_NOTICE(\"STUN resp from %J dropped\"\n\t\t\t\t\t     \" (no checklist)\\n\",\n\t\t\t\t\t     src);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tmem_deref(msg);\n\n\treturn true;\n}\n\n\nstatic void trice_reqbuf_destructor(void *data)\n{\n\tstruct trice_reqbuf *reqbuf = data;\n\n\tlist_unlink(&reqbuf->le);\n\n\tmem_deref(reqbuf->req);\n\tmem_deref(reqbuf->sock);\n\tmem_deref(reqbuf->lcand);\n}\n\n\nint trice_reqbuf_append(struct trice *icem, struct ice_lcand *lcand,\n\t\t    void *sock, const struct sa *src,\n\t\t    struct stun_msg *req, size_t presz)\n{\n\tstruct trice_reqbuf *reqbuf;\n\n\tif (!icem || !src ||!req)\n\t\treturn EINVAL;\n\n\treqbuf = mem_zalloc(sizeof(*reqbuf), trice_reqbuf_destructor);\n\tif (!reqbuf)\n\t\treturn ENOMEM;\n\n\tDEBUG_PRINTF(\"trice_reqbuf_append: Buffering request\\n\");\n\treqbuf->lcand = mem_ref(lcand);\n\treqbuf->sock = mem_ref(sock);\n\treqbuf->src = *src;\n\treqbuf->req = mem_ref(req);\n\treqbuf->presz = presz;\n\n\tlist_append(&icem->reqbufl, &reqbuf->le, reqbuf);\n\n\treturn 0;\n}\n\n\n/**\n * Set the port range for local sockets\n *\n * @param trice     ICE Media object\n * @param min_port  Minimum port\n * @param max_port  Maximum port\n *\n * @return 0 if success, otherwise errorcode\n */\nint trice_set_port_range(struct trice *trice,\n\t\t\t uint16_t min_port, uint16_t max_port)\n{\n\tif (!trice)\n\t\treturn EINVAL;\n\n\tif (max_port < min_port)\n\t\treturn ERANGE;\n\n\ttrice->ports.min = min_port;\n\ttrice->ports.max = max_port;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/trice/trice.h",
    "content": "/**\n * @file trice.h  Internal Interface to ICE\n *\n * Copyright (C) 2010 Alfred E. Heggestad\n */\n\n\nstruct ice_tcpconn;\nstruct ice_conncheck;\n\n\n/**\n * Active Checklist. Only used by Full-ICE and Trickle-ICE\n */\nstruct ice_checklist {\n\tstruct trice *icem;     /* parent */\n\n\tstruct tmr tmr_pace;         /**< Timer for pacing STUN requests     */\n\tuint32_t interval;           /**< Interval in [ms]                   */\n\tstruct stun *stun;           /**< STUN Transport                     */\n\tstruct list conncheckl;\n\tbool is_running;             /**< Checklist is running               */\n\n\t/* callback handlers */\n\ttrice_estab_h *estabh;\n\ttrice_failed_h *failh;\n\tvoid *arg;\n};\n\n\n/**\n * Defines an ICE media-stream\n *\n * NOTE: We try to follow the Resource Acquisition Is Initialization (RAII)\n *       programming idiom, which means:\n *\n * - at any time is the number of local/remote candidates correct\n * - at any time is the checklist up to date (matching local/remote candidates)\n *\n */\nstruct trice {\n\tstruct trice_conf conf;\n\tenum ice_role lrole;         /**< Local role                         */\n\tuint64_t tiebrk;             /**< Tie-break value for roleconflict   */\n\n\t/* stun/authentication */\n\tchar *lufrag;                /**< Local Username fragment            */\n\tchar *lpwd;                  /**< Local Password                     */\n\tchar *rufrag;                /**< Remote Username fragment           */\n\tchar *rpwd;                  /**< Remote Password                    */\n\n\tstruct list lcandl;          /**< local candidates (add order)       */\n\tstruct list rcandl;          /**< remote candidates (add order)      */\n\tstruct list checkl;          /**< Check List of cand pairs (sorted)  */\n\tstruct list validl;          /**< Valid List of cand pairs (sorted)  */\n\tstruct list reqbufl;         /**< buffered incoming requests         */\n\n\tstruct ice_checklist *checklist;\n\n\tstruct list connl;           /**< TCP-connections for all components */\n\n\t/* Port range */\n\tstruct {\n\t\tuint16_t min;\n\t\tuint16_t max;\n\t} ports;\n};\n\n\n/**\n * Holds an unhandled STUN request message that will be handled once\n * the role has been determined.\n */\nstruct trice_reqbuf {\n\tstruct le le;                /**< list element                       */\n\tstruct ice_lcand *lcand;     /**< corresponding local candidate      */\n\tvoid *sock;                  /**< request's socket                   */\n\tstruct sa src;               /**< source address                     */\n\tstruct stun_msg *req;        /**< buffered STUN request              */\n\tsize_t presz;                /**< number of bytes in preamble        */\n};\n\n\n/* return TRUE if handled */\ntypedef bool (tcpconn_frame_h)(struct trice *icem,\n\t\t\t       struct tcp_conn *tc, struct sa *src,\n\t\t\t       struct mbuf *mb, void *arg);\n\n/**\n * Defines a TCP-connection from local-address to remote-address\n *\n * - one TCP-connection can be shared by multiple candidate pairs\n *\n * - one TCP-connection is always created by the Local Candidate\n */\nstruct ice_tcpconn {\n\tstruct trice *icem;      /* parent */\n\tstruct le le;\n\tstruct tcp_conn *tc;\n\tstruct shim *shim;\n\tstruct sa laddr;\n\tstruct sa paddr;\n\tunsigned compid;\n\tint layer;\n\tbool active;\n\tbool estab;\n\n\ttcpconn_frame_h *frameh;\n\tvoid *arg;\n};\n\nstruct ice_conncheck {\n\tstruct le le;\n\tstruct ice_candpair *pair;    /* pointer */\n\tstruct stun_ctrans *ct_conn;\n\tstruct trice *icem;           /* owner */\n\tbool use_cand;\n\tbool term;\n};\n\n\n/* cand */\nint trice_add_lcandidate(struct ice_lcand **candp,\n\t\t\t struct trice *icem, struct list *lst,\n\t\t\t unsigned compid, char *foundation, int proto,\n\t\t\t uint32_t prio, const struct sa *addr,\n\t\t\t const struct sa *base_addr,\n\t\t\t enum ice_cand_type type,\n\t\t\t const struct sa *rel_addr,\n\t\t\t enum ice_tcptype tcptype);\nint trice_lcands_debug(struct re_printf *pf, const struct list *lst);\nint trice_rcands_debug(struct re_printf *pf, const struct list *lst);\n\n\n/* candpair */\nint  trice_candpair_alloc(struct ice_candpair **cpp, struct trice *icem,\n\t\t\t struct ice_lcand *lcand, struct ice_rcand *rcand);\nvoid trice_candpair_prio_order(struct list *lst, bool controlling);\nvoid trice_candpair_make_valid(struct trice *icem, struct ice_candpair *pair);\nvoid trice_candpair_failed(struct ice_candpair *cp, int err, uint16_t scode);\nvoid trice_candpair_set_state(struct ice_candpair *cp,\n\t\t\t     enum ice_candpair_state state);\nbool trice_candpair_iscompleted(const struct ice_candpair *cp);\nbool trice_candpair_cmp_fnd(const struct ice_candpair *cp1,\n\t\t\t   const struct ice_candpair *cp2);\nstruct ice_candpair *trice_candpair_find(const struct list *lst,\n\t\t\t\t\tconst struct ice_lcand *lcand,\n\t\t\t\t\tconst struct ice_rcand *rcand);\nint  trice_candpair_with_local(struct trice *icem, struct ice_lcand *lcand);\nint  trice_candpair_with_remote(struct trice *icem, struct ice_rcand *rcand);\nconst char    *trice_candpair_state2name(enum ice_candpair_state st);\n\n\n/* STUN server */\nint trice_stund_recv(struct trice *icem, struct ice_lcand *lcand,\n\t\t    void *sock, const struct sa *src,\n\t\t    struct stun_msg *req, size_t presz);\nint trice_stund_recv_role_set(struct trice *icem, struct ice_lcand *lcand,\n\t\t    void *sock, const struct sa *src,\n\t\t    struct stun_msg *req, size_t presz);\n\n\n/* ICE media */\nvoid trice_switch_local_role(struct trice *ice);\nvoid trice_printf(struct trice *icem, const char *fmt, ...);\nvoid trice_tracef(struct trice *icem, int color, const char *fmt, ...);\n\n\n/* ICE checklist */\nint  trice_checklist_debug(struct re_printf *pf,\n\t\t\t   const struct ice_checklist *ic);\nvoid trice_conncheck_schedule_check(struct trice *icem);\nint  trice_checklist_update(struct trice *icem);\nvoid trice_checklist_refresh(struct trice *icem);\n\n\n/* ICE conncheck */\nint trice_conncheck_stun_request(struct ice_checklist *ic,\n\t\t\t       struct ice_conncheck *cc,\n\t\t\t       struct ice_candpair *cp, void *sock,\n\t\t\t       bool cc_use_cand);\nint trice_conncheck_trigged(struct trice *icem, struct ice_candpair *pair,\n\t\t\t   void *sock, bool use_cand);\nint trice_conncheck_debug(struct re_printf *pf,\n\t\t\t  const struct ice_conncheck *cc);\n\n\n/* TCP connections */\n\n\nint trice_conn_alloc(struct list *connl, struct trice *icem, unsigned compid,\n\t\t   bool active, const struct sa *laddr, const struct sa *peer,\n\t\t   struct tcp_sock *ts, int layer,\n\t\t   tcpconn_frame_h *frameh, void *arg);\nstruct ice_tcpconn *trice_conn_find(struct list *connl, unsigned compid,\n\t\t\t\t  const struct sa *laddr,\n\t\t\t\t  const struct sa *peer);\nint trice_conn_debug(struct re_printf *pf, const struct ice_tcpconn *conn);\n\n\nbool trice_stun_process(struct trice *icem, struct ice_lcand *lcand,\n\t\t       int proto, void *sock, const struct sa *src,\n\t\t       struct mbuf *mb);\nint trice_reqbuf_append(struct trice *icem, struct ice_lcand *lcand,\n\t\t    void *sock, const struct sa *src,\n\t\t    struct stun_msg *req, size_t presz);\n"
  },
  {
    "path": "src/turn/chan.c",
    "content": "/**\n * @file chan.c  TURN Channels handling\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_md5.h>\n#include <re_udp.h>\n#include <re_stun.h>\n#include <re_turn.h>\n#include \"turnc.h\"\n\n\nenum {\n\tCHAN_LIFETIME = 600,\n\tCHAN_REFRESH = 250,\n\tCHAN_NUMB_MIN = 0x4000,\n\tCHAN_NUMB_MAX = 0x7fff\n};\n\n\nstruct channels {\n\tstruct hash *ht_numb;\n\tstruct hash *ht_peer;\n\tuint16_t nr;\n};\n\n\nstruct chan {\n\tstruct le he_numb;\n\tstruct le he_peer;\n\tstruct loop_state ls;\n\tuint16_t nr;\n\tstruct sa peer;\n\tstruct tmr tmr;\n\tstruct turnc *turnc;\n\tstruct stun_ctrans *ct;\n\tturnc_chan_h *ch;\n\tvoid *arg;\n};\n\n\nstatic int chanbind_request(struct chan *chan, bool reset_ls);\n\n\nstatic void channels_destructor(void *data)\n{\n\tstruct channels *c = data;\n\n\t/* flush from primary hash */\n\thash_flush(c->ht_numb);\n\n\tmem_deref(c->ht_numb);\n\tmem_deref(c->ht_peer);\n}\n\n\nstatic void chan_destructor(void *data)\n{\n\tstruct chan *chan = data;\n\n\ttmr_cancel(&chan->tmr);\n\tmem_deref(chan->ct);\n\tmtx_lock(chan->turnc->lock);\n\thash_unlink(&chan->he_numb);\n\thash_unlink(&chan->he_peer);\n\tmtx_unlock(chan->turnc->lock);\n}\n\n\nstatic bool numb_hash_cmp_handler(struct le *le, void *arg)\n{\n\tconst struct chan *chan = le->data;\n\tconst uint16_t *nr = arg;\n\n\treturn chan->nr == *nr;\n}\n\n\nstatic bool peer_hash_cmp_handler(struct le *le, void *arg)\n{\n\tconst struct chan *chan = le->data;\n\n\treturn sa_cmp(&chan->peer, arg, SA_ALL);\n}\n\n\nstatic void timeout(void *arg)\n{\n\tstruct chan *chan = arg;\n\tint err;\n\n\terr = chanbind_request(chan, true);\n\tif (err)\n\t\tchan->turnc->th(err, 0, NULL, NULL, NULL, NULL,\n\t\t\t\tchan->turnc->arg);\n}\n\n\nstatic void chanbind_resp_handler(int err, uint16_t scode, const char *reason,\n\t\t\t\t  const struct stun_msg *msg, void *arg)\n{\n\tstruct chan *chan = arg;\n\n\tif (err || turnc_request_loops(&chan->ls, scode))\n\t\tgoto out;\n\n\tswitch (scode) {\n\n\tcase 0:\n\t\ttmr_start(&chan->tmr, CHAN_REFRESH * 1000, timeout, chan);\n\t\tif (chan->ch) {\n\t\t\tchan->ch(chan->arg);\n\t\t\tchan->ch  = NULL;\n\t\t\tchan->arg = NULL;\n\t\t}\n\t\treturn;\n\n\tcase 401:\n\tcase 438:\n\t\terr = turnc_keygen(chan->turnc, msg);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = chanbind_request(chan, false);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\treturn;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n out:\n\tchan->turnc->th(err, scode, reason, NULL, NULL, msg, chan->turnc->arg);\n}\n\n\nstatic int chanbind_request(struct chan *chan, bool reset_ls)\n{\n\tstruct turnc *t = chan->turnc;\n\n\tif (reset_ls)\n\t\tturnc_loopstate_reset(&chan->ls);\n\n\treturn stun_request(&chan->ct, t->stun, t->proto, t->sock, &t->srv, 0,\n\t\t\t    STUN_METHOD_CHANBIND,\n\t\t\t    t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash),\n\t\t\t    false, chanbind_resp_handler, chan, 6,\n\t\t\t    STUN_ATTR_CHANNEL_NUMBER, &chan->nr,\n\t\t\t    STUN_ATTR_XOR_PEER_ADDR, &chan->peer,\n\t\t\t    STUN_ATTR_USERNAME, t->realm ? t->username : NULL,\n\t\t\t    STUN_ATTR_REALM, t->realm,\n\t\t\t    STUN_ATTR_NONCE, t->nonce,\n\t\t\t    STUN_ATTR_SOFTWARE, stun_software);\n}\n\n\n/**\n * Add a TURN Channel for a peer\n *\n * @param turnc TURN Client\n * @param peer  Peer IP-address\n * @param ch    Channel handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint turnc_add_chan(struct turnc *turnc, const struct sa *peer,\n\t\t   turnc_chan_h *ch, void *arg)\n{\n\tstruct chan *chan;\n\tint err;\n\n\tif (!turnc || !peer)\n\t\treturn EINVAL;\n\n\tif (turnc->chans->nr >= CHAN_NUMB_MAX)\n\t\treturn ERANGE;\n\n\tif (turnc_chan_find_peer(turnc, peer))\n\t\treturn 0;\n\n\tchan = mem_zalloc(sizeof(*chan), chan_destructor);\n\tif (!chan)\n\t\treturn ENOMEM;\n\n\tchan->nr = turnc->chans->nr++;\n\tchan->peer = *peer;\n\n\tmtx_lock(turnc->lock);\n\thash_append(turnc->chans->ht_numb, chan->nr, &chan->he_numb, chan);\n\thash_append(turnc->chans->ht_peer, sa_hash(peer, SA_ALL),\n\t\t    &chan->he_peer, chan);\n\tmtx_unlock(turnc->lock);\n\n\ttmr_init(&chan->tmr);\n\tchan->turnc = turnc;\n\tchan->ch = ch;\n\tchan->arg = arg;\n\n\terr = chanbind_request(chan, true);\n\tif (err)\n\t\tmem_deref(chan);\n\n\treturn err;\n}\n\n\nint turnc_chan_hash_alloc(struct channels **cp, uint32_t bsize)\n{\n\tstruct channels *c;\n\tint err;\n\n\tif (!cp)\n\t\treturn EINVAL;\n\n\tc = mem_zalloc(sizeof(*c), channels_destructor);\n\tif (!c)\n\t\treturn ENOMEM;\n\n\terr = hash_alloc(&c->ht_numb, bsize);\n\tif (err)\n\t\tgoto out;\n\n\terr = hash_alloc(&c->ht_peer, bsize);\n\tif (err)\n\t\tgoto out;\n\n\tc->nr = CHAN_NUMB_MIN;\n\n out:\n\tif (err)\n\t\tmem_deref(c);\n\telse\n\t\t*cp = c;\n\n\treturn err;\n}\n\n\nstruct chan *turnc_chan_find_numb(const struct turnc *turnc, uint16_t nr)\n{\n\tif (!turnc)\n\t\treturn NULL;\n\n\treturn list_ledata(hash_lookup(turnc->chans->ht_numb, nr,\n\t\t\t\t       numb_hash_cmp_handler, &nr));\n}\n\n\nstruct chan *turnc_chan_find_peer(const struct turnc *turnc,\n\t\t\t\t  const struct sa *peer)\n{\n\tif (!turnc)\n\t\treturn NULL;\n\n\tmtx_lock(turnc->lock);\n\tstruct chan *c = list_ledata(\n\t\thash_lookup(turnc->chans->ht_peer, sa_hash(peer, SA_ALL),\n\t\t\t    peer_hash_cmp_handler, (void *)peer));\n\tmtx_unlock(turnc->lock);\n\n\treturn c;\n}\n\n\nuint16_t turnc_chan_numb(const struct chan *chan)\n{\n\treturn chan ? chan->nr : 0;\n}\n\n\nconst struct sa *turnc_chan_peer(const struct chan *chan)\n{\n\treturn chan ? &chan->peer : NULL;\n}\n\n\nint turnc_chan_hdr_encode(const struct chan_hdr *hdr, struct mbuf *mb)\n{\n\tint err;\n\n\tif (!hdr || !mb)\n\t\treturn EINVAL;\n\n\terr  = mbuf_write_u16(mb, htons(hdr->nr));\n\terr |= mbuf_write_u16(mb, htons(hdr->len));\n\n\treturn err;\n}\n\n\nint turnc_chan_hdr_decode(struct chan_hdr *hdr, struct mbuf *mb)\n{\n\tif (!hdr || !mb)\n\t\treturn EINVAL;\n\n\tif (mbuf_get_left(mb) < sizeof(*hdr))\n\t\treturn ENOENT;\n\n\thdr->nr  = ntohs(mbuf_read_u16(mb));\n\thdr->len = ntohs(mbuf_read_u16(mb));\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/turn/perm.c",
    "content": "/**\n * @file perm.c  TURN permission handling\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_md5.h>\n#include <re_udp.h>\n#include <re_stun.h>\n#include <re_turn.h>\n#include \"turnc.h\"\n\n\nenum {\n\tPERM_LIFETIME = 300,\n\tPERM_REFRESH = 250,\n};\n\n\nstruct perm {\n\tstruct le he;\n\tstruct loop_state ls;\n\tstruct sa peer;\n\tstruct tmr tmr;\n\tstruct turnc *turnc;\n\tstruct stun_ctrans *ct;\n\tturnc_perm_h *ph;\n\tvoid *arg;\n};\n\n\nstatic int createperm_request(struct perm *perm, bool reset_ls);\n\n\nstatic void destructor(void *arg)\n{\n\tstruct perm *perm = arg;\n\n\ttmr_cancel(&perm->tmr);\n\tmem_deref(perm->ct);\n\thash_unlink(&perm->he);\n}\n\n\nstatic bool hash_cmp_handler(struct le *le, void *arg)\n{\n\tconst struct perm *perm = le->data;\n\n\treturn sa_cmp(&perm->peer, arg, SA_ADDR);\n}\n\n\nstatic struct perm *perm_find(const struct turnc *turnc, const struct sa *peer)\n{\n\treturn list_ledata(hash_lookup(turnc->perms, sa_hash(peer, SA_ADDR),\n\t\t\t\t       hash_cmp_handler, (void *)peer));\n}\n\n\nstatic void timeout(void *arg)\n{\n\tstruct perm *perm = arg;\n\tint err;\n\n\terr = createperm_request(perm, true);\n\tif (err)\n\t\tperm->turnc->th(err, 0, NULL, NULL, NULL, NULL,\n\t\t\t\tperm->turnc->arg);\n}\n\n\nstatic void createperm_resp_handler(int err, uint16_t scode,\n\t\t\t\t    const char *reason,\n\t\t\t\t    const struct stun_msg *msg, void *arg)\n{\n\tstruct perm *perm = arg;\n\n\tif (err || turnc_request_loops(&perm->ls, scode))\n\t\tgoto out;\n\n\tswitch (scode) {\n\n\tcase 0:\n\t\ttmr_start(&perm->tmr, PERM_REFRESH * 1000, timeout, perm);\n\t\tif (perm->ph) {\n\t\t\tperm->ph(perm->arg);\n\t\t\tperm->ph  = NULL;\n\t\t\tperm->arg = NULL;\n\t\t}\n\t\treturn;\n\n\tcase 401:\n\tcase 438:\n\t\terr = turnc_keygen(perm->turnc, msg);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = createperm_request(perm, false);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\treturn;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n out:\n\tperm->turnc->th(err, scode, reason, NULL, NULL, msg, perm->turnc->arg);\n}\n\n\nstatic int createperm_request(struct perm *perm, bool reset_ls)\n{\n\tstruct turnc *t = perm->turnc;\n\n\tif (reset_ls)\n\t\tturnc_loopstate_reset(&perm->ls);\n\n\treturn stun_request(&perm->ct, t->stun, t->proto, t->sock, &t->srv, 0,\n\t\t\t    STUN_METHOD_CREATEPERM,\n\t\t\t    t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash),\n\t\t\t    false, createperm_resp_handler, perm, 5,\n\t\t\t    STUN_ATTR_XOR_PEER_ADDR, &perm->peer,\n\t\t\t    STUN_ATTR_USERNAME, t->realm ? t->username : NULL,\n\t\t\t    STUN_ATTR_REALM, t->realm,\n\t\t\t    STUN_ATTR_NONCE, t->nonce,\n\t\t\t    STUN_ATTR_SOFTWARE, stun_software);\n}\n\n\n/**\n * Add TURN Permission for a peer\n *\n * @param turnc TURN Client\n * @param peer  Peer IP-address\n * @param ph    Permission handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint turnc_add_perm(struct turnc *turnc, const struct sa *peer,\n\t\t   turnc_perm_h *ph, void *arg)\n{\n\tstruct perm *perm;\n\tint err;\n\n\tif (!turnc || !peer)\n\t\treturn EINVAL;\n\n\tif (perm_find(turnc, peer))\n\t\treturn 0;\n\n\tperm = mem_zalloc(sizeof(*perm), destructor);\n\tif (!perm)\n\t\treturn ENOMEM;\n\n\thash_append(turnc->perms, sa_hash(peer, SA_ADDR), &perm->he, perm);\n\ttmr_init(&perm->tmr);\n\tperm->peer = *peer;\n\tperm->turnc = turnc;\n\tperm->ph = ph;\n\tperm->arg = arg;\n\n\terr = createperm_request(perm, true);\n\tif (err)\n\t\tmem_deref(perm);\n\n\treturn err;\n}\n\n\nint turnc_perm_hash_alloc(struct hash **ht, uint32_t bsize)\n{\n\treturn hash_alloc(ht, bsize);\n}\n"
  },
  {
    "path": "src/turn/turnc.c",
    "content": "/**\n * @file turnc.c  TURN Client implementation\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_md5.h>\n#include <re_list.h>\n#include <re_hash.h>\n#include <re_tmr.h>\n#include <re_sa.h>\n#include <re_udp.h>\n#include <re_tcp.h>\n#include <re_srtp.h>\n#include <re_tls.h>\n#include <re_stun.h>\n#include <re_turn.h>\n#include \"turnc.h\"\n\n\n#define DEBUG_MODULE \"turnc\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** TURN Client protocol values */\nenum {\n\tPERM_HASH_SIZE = 16,\n\tCHAN_HASH_SIZE = 16,\n\tFAILC_MAX = 16, /**< Maximum number of request errors for loopcheck. */\n\tSTUN_ATTR_ADDR4_SIZE = 8,\n\tSTUN_ATTR_ADDR6_SIZE = 20,\n};\n\n\nstatic const uint8_t sendind_tid[STUN_TID_SIZE];\n\nstatic int allocate_request(struct turnc *t);\nstatic int refresh_request(struct turnc *t, uint32_t lifetime, bool reset_ls,\n\t\t\t   stun_resp_h *resph, void *arg);\nstatic void refresh_resp_handler(int err, uint16_t scode, const char *reason,\n\t\t\t\t const struct stun_msg *msg, void *arg);\n\n\nstatic void destructor(void *arg)\n{\n\tstruct turnc *turnc = arg;\n\n\tif (turnc->allocated)\n\t\t(void)refresh_request(turnc, 0, true, NULL, NULL);\n\n\ttmr_cancel(&turnc->tmr);\n\tmem_deref(turnc->ct);\n\n\thash_flush(turnc->perms);\n\tmem_deref(turnc->perms);\n\tmem_deref(turnc->chans);\n\tmem_deref(turnc->username);\n\tmem_deref(turnc->password);\n\tmem_deref(turnc->nonce);\n\tmem_deref(turnc->realm);\n\tmem_deref(turnc->stun);\n\tmem_deref(turnc->uh);\n\tmem_deref(turnc->sock);\n\tmem_deref(turnc->lock);\n}\n\n\nstatic void timeout(void *arg)\n{\n\tstruct turnc *turnc = arg;\n\tint err;\n\n\terr = refresh_request(turnc, turnc->lifetime, true,\n\t\t\t      refresh_resp_handler, turnc);\n\tif (err)\n\t\tturnc->th(err, 0, NULL, NULL, NULL, NULL, turnc->arg);\n}\n\n\nstatic void refresh_timer(struct turnc *turnc)\n{\n\tconst uint32_t t = turnc->lifetime*1000*3/4;\n\n\tDEBUG_INFO(\"Start refresh timer.. %u seconds\\n\", t/1000);\n\n\ttmr_start(&turnc->tmr, t, timeout, turnc);\n}\n\n\nstatic void allocate_resp_handler(int err, uint16_t scode, const char *reason,\n\t\t\t\t  const struct stun_msg *msg, void *arg)\n{\n\tstruct stun_attr *map = NULL, *rel = NULL, *ltm, *alt;\n\tstruct turnc *turnc = arg;\n\n\tif (err || turnc_request_loops(&turnc->ls, scode))\n\t\tgoto out;\n\n\tswitch (scode) {\n\n\tcase 0:\n\t\tmap = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);\n\t\trel = stun_msg_attr(msg, STUN_ATTR_XOR_RELAY_ADDR);\n\t\tltm = stun_msg_attr(msg, STUN_ATTR_LIFETIME);\n\t\tif (!rel || !map) {\n\t\t\tDEBUG_WARNING(\"xor_mapped/relay addr attr missing\\n\");\n\t\t\terr = EPROTO;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (ltm)\n\t\t\tturnc->lifetime = ltm->v.lifetime;\n\n\t\tturnc->allocated = true;\n\t\trefresh_timer(turnc);\n\t\tbreak;\n\n\tcase 300:\n\t\tif (turnc->proto == IPPROTO_TCP ||\n\t\t    turnc->proto == STUN_TRANSP_DTLS)\n\t\t\tbreak;\n\n\t\talt = stun_msg_attr(msg, STUN_ATTR_ALT_SERVER);\n\t\tif (!alt)\n\t\t\tbreak;\n\n\t\tturnc->psrv = turnc->srv;\n\t\tturnc->srv = alt->v.alt_server;\n\n\t\terr = allocate_request(turnc);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\treturn;\n\n\tcase 401:\n\tcase 438:\n\t\terr = turnc_keygen(turnc, msg);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = allocate_request(turnc);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\treturn;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n out:\n\tturnc->th(err, scode, reason,\n\t\t  rel ? &rel->v.xor_relay_addr : NULL,\n\t\t  map ? &map->v.xor_mapped_addr : NULL,\n\t\t  msg,\n\t\t  turnc->arg);\n}\n\n\nstatic int allocate_request(struct turnc *t)\n{\n\tconst uint8_t proto = IPPROTO_UDP;\n\n\treturn stun_request(&t->ct, t->stun, t->proto, t->sock, &t->srv, 0,\n\t\t\t    STUN_METHOD_ALLOCATE,\n\t\t\t    t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash),\n\t\t\t    false, allocate_resp_handler, t, 6,\n\t\t\t    STUN_ATTR_LIFETIME, &t->lifetime,\n\t\t\t    STUN_ATTR_REQ_TRANSPORT, &proto,\n\t\t\t    STUN_ATTR_USERNAME, t->realm ? t->username : NULL,\n\t\t\t    STUN_ATTR_REALM, t->realm,\n\t\t\t    STUN_ATTR_NONCE, t->nonce,\n\t\t\t    STUN_ATTR_SOFTWARE, stun_software);\n}\n\n\nstatic void refresh_resp_handler(int err, uint16_t scode, const char *reason,\n\t\t\t\t const struct stun_msg *msg, void *arg)\n{\n\tstruct turnc *turnc = arg;\n\tstruct stun_attr *ltm;\n\n\tif (err || turnc_request_loops(&turnc->ls, scode))\n\t\tgoto out;\n\n\tswitch (scode) {\n\n\tcase 0:\n\t\tltm = stun_msg_attr(msg, STUN_ATTR_LIFETIME);\n\t\tif (ltm)\n\t\t\tturnc->lifetime = ltm->v.lifetime;\n\t\trefresh_timer(turnc);\n\t\treturn;\n\n\tcase 401:\n\tcase 438:\n\t\terr = turnc_keygen(turnc, msg);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = refresh_request(turnc, turnc->lifetime, false,\n\t\t\t\t      refresh_resp_handler, turnc);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\treturn;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n out:\n\tturnc->th(err, scode, reason, NULL, NULL, msg, turnc->arg);\n}\n\n\nstatic int refresh_request(struct turnc *t, uint32_t lifetime, bool reset_ls,\n\t\t\t   stun_resp_h *resph, void *arg)\n{\n\tif (!t)\n\t\treturn EINVAL;\n\n\tif (reset_ls)\n\t\tturnc_loopstate_reset(&t->ls);\n\n\tif (t->ct)\n\t\tt->ct = mem_deref(t->ct);\n\n\treturn stun_request(&t->ct, t->stun, t->proto, t->sock, &t->srv, 0,\n\t\t\t    STUN_METHOD_REFRESH,\n\t\t\t    t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash),\n\t\t\t    false, resph, arg, 5,\n\t\t\t    STUN_ATTR_LIFETIME, &lifetime,\n\t\t\t    STUN_ATTR_USERNAME, t->realm ? t->username : NULL,\n\t\t\t    STUN_ATTR_REALM, t->realm,\n\t\t\t    STUN_ATTR_NONCE, t->nonce,\n\t\t\t    STUN_ATTR_SOFTWARE, stun_software);\n}\n\n\nstatic inline size_t stun_indlen(const struct sa *sa)\n{\n\tsize_t len = STUN_HEADER_SIZE + STUN_ATTR_HEADER_SIZE * 2;\n\n\tswitch (sa_af(sa)) {\n\n\tcase AF_INET:\n\t\tlen += STUN_ATTR_ADDR4_SIZE;\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tlen += STUN_ATTR_ADDR6_SIZE;\n\t\tbreak;\n\t}\n\n\treturn len;\n}\n\n\nstatic bool udp_send_handler(int *err, struct sa *dst, struct mbuf *mb,\n\t\t\t     void *arg)\n{\n\tstruct turnc *turnc = arg;\n\tsize_t pos, indlen;\n\tstruct chan *chan;\n\n\tif (mb->pos < CHAN_HDR_SIZE)\n\t\treturn false;\n\n\tchan = turnc_chan_find_peer(turnc, dst);\n\tif (chan) {\n\t\tstruct chan_hdr hdr;\n\n\t\thdr.nr  = turnc_chan_numb(chan);\n\t\thdr.len = (uint16_t)mbuf_get_left(mb);\n\n\t\tmb->pos -= CHAN_HDR_SIZE;\n\t\t*err = turnc_chan_hdr_encode(&hdr, mb);\n\t\tmb->pos -= CHAN_HDR_SIZE;\n\n\t\t*dst = turnc->srv;\n\n\t\treturn false;\n\t}\n\n\tindlen = stun_indlen(dst);\n\n\tif (mb->pos < indlen)\n\t\treturn false;\n\n\tmb->pos -= indlen;\n\tpos = mb->pos;\n\t*err = stun_msg_encode(mb, STUN_METHOD_SEND, STUN_CLASS_INDICATION,\n\t\t\t       sendind_tid, NULL, NULL, 0, false, 0x00, 2,\n\t\t\t       STUN_ATTR_XOR_PEER_ADDR, dst,\n\t\t\t       STUN_ATTR_DATA, mb);\n\tmb->pos = pos;\n\n\t*dst = turnc->srv;\n\n\treturn false;\n}\n\n\nstatic bool udp_recv_handler(struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct stun_attr *peer, *data;\n\tstruct stun_unknown_attr ua;\n\tstruct turnc *turnc = arg;\n\tstruct stun_msg *msg;\n\tbool hdld = true;\n\n\tif (!sa_cmp(&turnc->srv, src, SA_ALL) &&\n\t    !sa_cmp(&turnc->psrv, src, SA_ALL))\n\t\treturn false;\n\n\tif (stun_msg_decode(&msg, mb, &ua)) {\n\n\t\tstruct chan_hdr hdr;\n\t\tstruct chan *chan;\n\n\t\tif (turnc_chan_hdr_decode(&hdr, mb))\n\t\t\treturn true;\n\n\t\tif (mbuf_get_left(mb) < hdr.len)\n\t\t\treturn true;\n\n\t\tchan = turnc_chan_find_numb(turnc, hdr.nr);\n\t\tif (!chan)\n\t\t\treturn true;\n\n\t\t*src = *turnc_chan_peer(chan);\n\n\t\treturn false;\n\t}\n\n\tswitch (stun_msg_class(msg)) {\n\n\tcase STUN_CLASS_INDICATION:\n\t\tif (ua.typec > 0)\n\t\t\tbreak;\n\n\t\tif (stun_msg_method(msg) != STUN_METHOD_DATA)\n\t\t\tbreak;\n\n\t\tpeer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR);\n\t\tdata = stun_msg_attr(msg, STUN_ATTR_DATA);\n\t\tif (!peer || !data)\n\t\t\tbreak;\n\n\t\t*src = peer->v.xor_peer_addr;\n\n\t\tmb->pos = data->v.data.pos;\n\t\tmb->end = data->v.data.end;\n\n\t\thdld = false;\n\t\tbreak;\n\n\tcase STUN_CLASS_ERROR_RESP:\n\tcase STUN_CLASS_SUCCESS_RESP:\n\t\t(void)stun_ctrans_recv(turnc->stun, msg, &ua);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\tmem_deref(msg);\n\n\treturn hdld;\n}\n\n\n/**\n * Allocate a TURN Client\n *\n * @param turncp    Pointer to allocated TURN Client\n * @param conf      Optional STUN Configuration\n * @param proto     Transport Protocol\n * @param sock      Transport socket\n * @param layer     Transport layer\n * @param srv       TURN Server IP-address\n * @param username  Authentication username\n * @param password  Authentication password\n * @param lifetime  Allocate lifetime in [seconds]\n * @param th        TURN handler\n * @param arg       Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint turnc_alloc(struct turnc **turncp, const struct stun_conf *conf, int proto,\n\t\tvoid *sock, int layer, const struct sa *srv,\n\t\tconst char *username, const char *password,\n\t\tuint32_t lifetime, turnc_h *th, void *arg)\n{\n\tstruct turnc *turnc;\n\tint err;\n\n\tif (!turncp || !sock || !srv || !username || !password || !th)\n\t\treturn EINVAL;\n\n\tturnc = mem_zalloc(sizeof(*turnc), destructor);\n\tif (!turnc)\n\t\treturn ENOMEM;\n\n\terr = mutex_alloc(&turnc->lock);\n\tif (err)\n\t\tgoto out;\n\n\terr = stun_alloc(&turnc->stun, conf, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_dup(&turnc->username, username);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_dup(&turnc->password, password);\n\tif (err)\n\t\tgoto out;\n\n\terr = turnc_perm_hash_alloc(&turnc->perms, PERM_HASH_SIZE);\n\tif (err)\n\t\tgoto out;\n\n\terr =  turnc_chan_hash_alloc(&turnc->chans, CHAN_HASH_SIZE);\n\tif (err)\n\t\tgoto out;\n\n\ttmr_init(&turnc->tmr);\n\tturnc->proto = proto;\n\tturnc->sock = mem_ref(sock);\n\tturnc->psrv = *srv;\n\tturnc->srv = *srv;\n\tturnc->lifetime = lifetime;\n\tturnc->th = th;\n\tturnc->arg = arg;\n\n\tswitch (proto) {\n\n\tcase IPPROTO_UDP:\n\t\terr = udp_register_helper(&turnc->uh, sock, layer,\n\t\t\t\t\t  udp_send_handler, udp_recv_handler,\n\t\t\t\t\t  turnc);\n\t\tbreak;\n\n\tdefault:\n\t\terr = 0;\n\t\tbreak;\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\terr = allocate_request(turnc);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(turnc);\n\telse\n\t\t*turncp = turnc;\n\n\treturn err;\n}\n\n\nint turnc_send(struct turnc *turnc, const struct sa *dst, struct mbuf *mb)\n{\n\tsize_t pos, indlen;\n\tstruct chan *chan;\n\tint err;\n\n\tif (!turnc || !dst || !mb)\n\t\treturn EINVAL;\n\n\tchan = turnc_chan_find_peer(turnc, dst);\n\tif (chan) {\n\t\tstruct chan_hdr hdr;\n\n\t\tif (mb->pos < CHAN_HDR_SIZE)\n\t\t\treturn EINVAL;\n\n\t\thdr.nr  = turnc_chan_numb(chan);\n\t\thdr.len = (uint16_t)mbuf_get_left(mb);\n\n\t\tmb->pos -= CHAN_HDR_SIZE;\n\t\tpos = mb->pos;\n\n\t\terr = turnc_chan_hdr_encode(&hdr, mb);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (turnc->proto == IPPROTO_TCP) {\n\n\t\t\tmb->pos = mb->end;\n\n\t\t\t/* padding */\n\t\t\twhile (hdr.len++ & 0x03) {\n\t\t\t\terr = mbuf_write_u8(mb, 0x00);\n\t\t\t\tif (err)\n\t\t\t\t\treturn err;\n\t\t\t}\n\t\t}\n\n\t\tmb->pos = pos;\n\t}\n\telse {\n\t\tindlen = stun_indlen(dst);\n\n\t\tif (mb->pos < indlen)\n\t\t\treturn EINVAL;\n\n\t\tmb->pos -= indlen;\n\t\tpos = mb->pos;\n\n\t\terr = stun_msg_encode(mb, STUN_METHOD_SEND,\n\t\t\t\t      STUN_CLASS_INDICATION, sendind_tid,\n\t\t\t\t      NULL, NULL, 0, false, 0x00, 2,\n\t\t\t\t      STUN_ATTR_XOR_PEER_ADDR, dst,\n\t\t\t\t      STUN_ATTR_DATA, mb);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tmb->pos = pos;\n\t}\n\n\tswitch (turnc->proto) {\n\n\tcase IPPROTO_UDP:\n\t\terr = udp_send(turnc->sock, &turnc->srv, mb);\n\t\tbreak;\n\n\tcase IPPROTO_TCP:\n\t\terr = tcp_send(turnc->sock, mb);\n\t\tbreak;\n\n#ifdef USE_DTLS\n\tcase STUN_TRANSP_DTLS:\n\t\terr = dtls_send(turnc->sock, mb);\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\terr = EPROTONOSUPPORT;\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nint turnc_recv(struct turnc *turnc, struct sa *src, struct mbuf *mb)\n{\n\tstruct stun_attr *peer, *data;\n\tstruct stun_unknown_attr ua;\n\tstruct stun_msg *msg;\n\tint err = 0;\n\n\tif (!turnc || !src || !mb)\n\t\treturn EINVAL;\n\n\tif (stun_msg_decode(&msg, mb, &ua)) {\n\n\t\tstruct chan_hdr hdr;\n\t\tstruct chan *chan;\n\n\t\tif (turnc_chan_hdr_decode(&hdr, mb))\n\t\t\treturn EBADMSG;\n\n\t\tif (mbuf_get_left(mb) < hdr.len)\n\t\t\treturn EBADMSG;\n\n\t\tchan = turnc_chan_find_numb(turnc, hdr.nr);\n\t\tif (!chan)\n\t\t\treturn EBADMSG;\n\n\t\t*src = *turnc_chan_peer(chan);\n\n\t\treturn 0;\n\t}\n\n\tswitch (stun_msg_class(msg)) {\n\n\tcase STUN_CLASS_INDICATION:\n\t\tif (ua.typec > 0) {\n\t\t\terr = ENOSYS;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (stun_msg_method(msg) != STUN_METHOD_DATA) {\n\t\t\terr = ENOSYS;\n\t\t\tbreak;\n\t\t}\n\n\t\tpeer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR);\n\t\tdata = stun_msg_attr(msg, STUN_ATTR_DATA);\n\t\tif (!peer || !data) {\n\t\t\terr = EPROTO;\n\t\t\tbreak;\n\t\t}\n\n\t\t*src = peer->v.xor_peer_addr;\n\n\t\tmb->pos = data->v.data.pos;\n\t\tmb->end = data->v.data.end;\n\t\tbreak;\n\n\tcase STUN_CLASS_ERROR_RESP:\n\tcase STUN_CLASS_SUCCESS_RESP:\n\t\t(void)stun_ctrans_recv(turnc->stun, msg, &ua);\n\t\tmb->pos = mb->end;\n\t\tbreak;\n\n\tdefault:\n\t\terr = ENOSYS;\n\t\tbreak;\n\t}\n\n\tmem_deref(msg);\n\n\treturn err;\n}\n\n\nbool turnc_request_loops(struct loop_state *ls, uint16_t scode)\n{\n\tbool loop = false;\n\n\tswitch (scode) {\n\n\tcase 0:\n\t\tls->failc = 0;\n\t\tbreak;\n\n\tdefault:\n\t\tif (ls->last_scode == scode)\n\t\t\tloop = true;\n\t\t/*@fallthrough@*/\n\tcase 300:\n\t\tif (++ls->failc >= FAILC_MAX)\n\t\t\tloop = true;\n\n\t\tbreak;\n\t}\n\n\tls->last_scode = scode;\n\n\treturn loop;\n}\n\n\nvoid turnc_loopstate_reset(struct loop_state *ls)\n{\n\tif (!ls)\n\t\treturn;\n\n\tls->last_scode = 0;\n\tls->failc = 0;\n}\n\n\nint turnc_keygen(struct turnc *turnc, const struct stun_msg *msg)\n{\n\tstruct stun_attr *realm, *nonce;\n\n\trealm = stun_msg_attr(msg, STUN_ATTR_REALM);\n\tnonce = stun_msg_attr(msg, STUN_ATTR_NONCE);\n\tif (!realm || !nonce)\n\t\treturn EPROTO;\n\n\tmem_deref(turnc->realm);\n\tmem_deref(turnc->nonce);\n\tturnc->realm = mem_ref(realm->v.realm);\n\tturnc->nonce = mem_ref(nonce->v.nonce);\n\n\treturn md5_printf(turnc->md5_hash, \"%s:%s:%s\",\n\t\t\t  turnc->username, turnc->realm, turnc->password);\n}\n"
  },
  {
    "path": "src/turn/turnc.h",
    "content": "/**\n * @file turnc.h  Internal TURN interface\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <time.h>\n\n\nstruct loop_state {\n\tuint32_t failc;\n\tuint16_t last_scode;\n};\n\nstruct channels;\n\n/** Defines a TURN Client */\nstruct turnc {\n\tstruct loop_state ls;          /**< Loop state                      */\n\tstruct udp_helper *uh;         /**< UDP Helper for the TURN Socket  */\n\tstruct stun_ctrans *ct;        /**< Pending STUN Client Transaction */\n\tchar *username;                /**< Authentication username         */\n\tchar *password;                /**< Authentication password         */\n\tstruct sa psrv;                /**< Previous TURN Server address    */\n\tstruct sa srv;                 /**< TURN Server address             */\n\tvoid *sock;                    /**< Transport socket                */\n\tint proto;                     /**< Transport protocol              */\n\tstruct stun *stun;             /**< STUN Instance                   */\n\tuint32_t lifetime;             /**< Allocation lifetime in [seconds]*/\n\tstruct tmr tmr;                /**< Allocation refresh timer        */\n\tturnc_h *th;                   /**< Turn client handler             */\n\tvoid *arg;                     /**< Handler argument                */\n\tuint8_t md5_hash[MD5_SIZE];    /**< Cached MD5-sum of credentials   */\n\tchar *nonce;                   /**< Saved NONCE value from server   */\n\tchar *realm;                   /**< Saved REALM value from server   */\n\tstruct hash *perms;            /**< Hash-table of permissions       */\n\tstruct channels *chans;        /**< TURN Channels                   */\n\tbool allocated;                /**< Allocation was done flag        */\n\tmtx_t *lock;                   /**< Lock turnc                      */\n};\n\n\n/* Util */\nbool turnc_request_loops(struct loop_state *ls, uint16_t scode);\nvoid turnc_loopstate_reset(struct loop_state *ls);\nint  turnc_keygen(struct turnc *turnc, const struct stun_msg *msg);\n\n\n/* Permission */\nint turnc_perm_hash_alloc(struct hash **ht, uint32_t bsize);\n\n\n/* Channels */\nenum {\n\tCHAN_HDR_SIZE = 4,\n};\n\nstruct chan_hdr {\n\tuint16_t nr;\n\tuint16_t len;\n};\n\nstruct chan;\n\nint turnc_chan_hash_alloc(struct channels **cp, uint32_t bsize);\nstruct chan *turnc_chan_find_numb(const struct turnc *turnc, uint16_t nr);\nstruct chan *turnc_chan_find_peer(const struct turnc *turnc,\n\t\t\t\t  const struct sa *peer);\nuint16_t turnc_chan_numb(const struct chan *chan);\nconst struct sa *turnc_chan_peer(const struct chan *chan);\nint turnc_chan_hdr_encode(const struct chan_hdr *hdr, struct mbuf *mb);\nint turnc_chan_hdr_decode(struct chan_hdr *hdr, struct mbuf *mb);\n"
  },
  {
    "path": "src/udp/mcast.c",
    "content": "/**\n * @file mcast.c  UDP Multicast\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_sa.h>\n#include <re_udp.h>\n\n\nstatic int multicast_update(struct udp_sock *us, const struct sa *group,\n\t\t\t    bool join)\n{\n\tstruct ip_mreq mreq;\n\tstruct ipv6_mreq mreq6;\n\tint err;\n\n\tif (!us || !group)\n\t\treturn EINVAL;\n\n\tswitch (sa_af(group)) {\n\n\tcase AF_INET:\n\t\tmreq.imr_multiaddr = group->u.in.sin_addr;\n\t\tmreq.imr_interface.s_addr = 0;\n\n\t\terr = udp_setsockopt(us, IPPROTO_IP,\n\t\t\t\t     join\n\t\t\t\t     ? IP_ADD_MEMBERSHIP\n\t\t\t\t     : IP_DROP_MEMBERSHIP,\n\t\t\t\t     &mreq, sizeof(mreq));\n\t\tbreak;\n\n\tcase AF_INET6:\n\t\tmreq6.ipv6mr_multiaddr = group->u.in6.sin6_addr;\n\t\tmreq6.ipv6mr_interface = sa_scopeid(group);\n\n\t\terr = udp_setsockopt(us, IPPROTO_IPV6,\n\t\t\t\t     join\n\t\t\t\t     ? IPV6_JOIN_GROUP\n\t\t\t\t     : IPV6_LEAVE_GROUP,\n\t\t\t\t     &mreq6, sizeof(mreq6));\n\t\tbreak;\n\n\tdefault:\n\t\treturn EAFNOSUPPORT;\n\t}\n\n\treturn err;\n}\n\n\nint udp_multicast_join(struct udp_sock *us, const struct sa *group)\n{\n\treturn multicast_update(us, group, true);\n}\n\n\nint udp_multicast_leave(struct udp_sock *us, const struct sa *group)\n{\n\treturn multicast_update(us, group, false);\n}\n"
  },
  {
    "path": "src/udp/udp.c",
    "content": "/**\n * @file udp.c  User Datagram Protocol\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdlib.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef HAVE_IO_H\n#include <io.h>\n#endif\n#if !defined(WIN32)\n#include <netdb.h>\n#endif\n#include <string.h>\n#ifdef HAVE_STRINGS_H\n#include <strings.h>\n#endif\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_list.h>\n#include <re_thread.h>\n#include <re_net.h>\n#include <re_main.h>\n#include <re_sa.h>\n#include <re_udp.h>\n#ifdef WIN32\n#ifndef HAVE_QOS_FLOWID\ntypedef UINT32 QOS_FLOWID;\n#endif\n\n#ifndef HAVE_PQOS_FLOWID\ntypedef UINT32 *PQOS_FLOWID;\n#endif\n\n#include <qos2.h>\n\n#ifndef QOS_NON_ADAPTIVE_FLOW\n#define QOS_NON_ADAPTIVE_FLOW 0x00000002\n#endif\n#endif /*WIN32*/\n\n#define DEBUG_MODULE \"udp\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** Platform independent buffer type cast */\n#ifdef WIN32\n#define BUF_CAST (char *)\n#define SIZ_CAST (int)\n#define close closesocket\n#else\n#define BUF_CAST\n#define SIZ_CAST\n#endif\n\n\nenum {\n\tUDP_RXSZ_DEFAULT = 8192\n};\n\n\n/** Defines a UDP socket */\nstruct udp_sock {\n\tstruct list helpers; /**< List of UDP Helpers         */\n\tudp_send_h *sendh;\n\tudp_recv_h *rh;      /**< Receive handler             */\n\tudp_error_h *eh;     /**< Error handler               */\n\tvoid *arg;           /**< Handler argument            */\n\tstruct re_fhs *fhs;\n\tre_sock_t fd;        /**< Socket file descriptor      */\n\tbool conn;           /**< Connected socket flag       */\n\tsize_t rxsz;         /**< Maximum receive chunk size  */\n\tsize_t rx_presz;     /**< Preallocated rx buffer size */\n#ifdef WIN32\n\tHANDLE qos;          /**< QOS subsystem handle        */\n\tQOS_FLOWID qos_id;   /**< QOS flow id                 */\n#endif\n\tmtx_t *lock;         /**< A lock for helpers list     */\n};\n\n/** Defines a UDP helper */\nstruct udp_helper {\n\tstruct le le;\n\tint layer;\n\tudp_helper_send_h *sendh;\n\tudp_helper_recv_h *recvh;\n\tmtx_t *lock;         /**< A lock for the helpers list */\n\tvoid *arg;\n};\n\n\nstatic void dummy_udp_recv_handler(const struct sa *src,\n\t\t\t\t   struct mbuf *mb, void *arg)\n{\n\t(void)src;\n\t(void)mb;\n\t(void)arg;\n}\n\n\nstatic bool helper_send_handler(int *err, struct sa *dst,\n\t\t\t\tstruct mbuf *mb, void *arg)\n{\n\t(void)err;\n\t(void)dst;\n\t(void)mb;\n\t(void)arg;\n\treturn false;\n}\n\n\nstatic bool helper_recv_handler(struct sa *src,\n\t\t\t\tstruct mbuf *mb, void *arg)\n{\n\t(void)src;\n\t(void)mb;\n\t(void)arg;\n\treturn false;\n}\n\n\nstatic void udp_destructor(void *data)\n{\n\tstruct udp_sock *us = data;\n\n\tlist_flush(&us->helpers);\n\n\tmem_deref(us->lock);\n\n#ifdef WIN32\n\tif (us->qos && us->qos_id)\n\t\t(void)QOSRemoveSocketFromFlow(us->qos, 0, us->qos_id, 0);\n\tif (us->qos)\n\t\t(void)QOSCloseHandle(us->qos);\n#endif\n\n\tif (RE_BAD_SOCK != us->fd) {\n\t\tus->fhs = fd_close(us->fhs);\n\t\t(void)close(us->fd);\n\t}\n}\n\n\nstatic void udp_read(struct udp_sock *us, re_sock_t fd)\n{\n\tstruct mbuf *mb = mbuf_alloc(us->rxsz);\n\tstruct sa src;\n\tstruct le *le;\n\tint err = 0;\n\tssize_t n;\n\n\tif (!mb)\n\t\treturn;\n\n\tsrc.len = sizeof(src.u);\n\tn = recvfrom(fd, BUF_CAST mb->buf + us->rx_presz,\n\t\t     SIZ_CAST (mb->size - us->rx_presz), 0,\n\t\t     &src.u.sa, &src.len);\n\tif (n < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\n\t\tif (EAGAIN == err)\n\t\t\tgoto out;\n\n#ifdef WIN32\n\t\tif (WSAEWOULDBLOCK == err)\n\t\t\tgoto out;\n#endif\n\n#if defined (EWOULDBLOCK) && EWOULDBLOCK != EAGAIN\n\t\tif (EWOULDBLOCK == err)\n\t\t\tgoto out;\n#endif\n\t\tif (us->eh)\n\t\t\tus->eh(err, us->arg);\n\n\t\tgoto out;\n\t}\n\n\tmb->pos = us->rx_presz;\n\tmb->end = n + us->rx_presz;\n\n\t(void)mbuf_resize(mb, mb->end);\n\n\t/* call helpers */\n\tmtx_lock(us->lock);\n\tle = us->helpers.head;\n\tmtx_unlock(us->lock);\n\twhile (le) {\n\t\tstruct udp_helper *uh = le->data;\n\t\tbool hdld;\n\n\t\tmtx_lock(us->lock);\n\t\tle = le->next;\n\t\tmtx_unlock(us->lock);\n\n\t\thdld = uh->recvh(&src, mb, uh->arg);\n\t\tif (hdld)\n\t\t\tgoto out;\n\t}\n\n\tus->rh(&src, mb, us->arg);\n\n out:\n\tmem_deref(mb);\n}\n\n\nstatic void udp_read_handler(int flags, void *arg)\n{\n\tstruct udp_sock *us = arg;\n\n\t(void)flags;\n\n\tudp_read(us, us->fd);\n}\n\n\nstatic int udp_alloc(struct udp_sock **usp)\n{\n\tint err;\n\tstruct udp_sock *us;\n\n\tif (!usp)\n\t\treturn EINVAL;\n\n\tus = mem_zalloc(sizeof(*us), NULL);\n\tif (!us)\n\t\treturn ENOMEM;\n\n\tlist_init(&us->helpers);\n\n\tus->fhs\t = NULL;\n\tus->fd\t = RE_BAD_SOCK;\n\n\terr = mutex_alloc(&us->lock);\n\tif (err) {\n\t\tmem_deref(us);\n\t\treturn err;\n\t}\n\n\tmem_destructor(us, udp_destructor);\n\n\t*usp = us;\n\n\treturn 0;\n}\n\n\n/**\n * Create and listen on a UDP Socket\n *\n * @param usp   Pointer to returned UDP Socket\n * @param local Local network address\n * @param rh    Receive handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint udp_listen(struct udp_sock **usp, const struct sa *local,\n\t       udp_recv_h *rh, void *arg)\n{\n\tstruct addrinfo hints, *res = NULL, *r;\n\tstruct udp_sock *us;\n\tchar addr[64] = {0};\n\tchar serv[6] = \"0\";\n\tint af, error, err = 0;\n\n\tif (!usp)\n\t\treturn EINVAL;\n\n\terr = udp_alloc(&us);\n\tif (err)\n\t\treturn err;\n\n\tif (local) {\n\t\taf = sa_af(local);\n\t\t(void)re_snprintf(addr, sizeof(addr), \"%H\",\n\t\t\t\t  sa_print_addr, local);\n\t\t(void)re_snprintf(serv, sizeof(serv), \"%u\", sa_port(local));\n\t}\n\telse {\n\t\taf = AF_UNSPEC;\n\t}\n\n\tmemset(&hints, 0, sizeof(hints));\n\t/* set-up hints structure */\n\thints.ai_family   = af;\n\thints.ai_flags    = AI_PASSIVE | AI_NUMERICHOST;\n\thints.ai_socktype = SOCK_DGRAM;\n\thints.ai_protocol = IPPROTO_UDP;\n\n\terror = getaddrinfo(local ? addr : NULL, serv, &hints, &res);\n\tif (error) {\n#ifdef WIN32\n\t\tDEBUG_WARNING(\"listen: getaddrinfo: wsaerr=%d\\n\",\n\t\t\t      WSAGetLastError());\n#endif\n\t\tDEBUG_WARNING(\"listen: getaddrinfo: %s:%s (%s)\\n\",\n\t\t\t      addr, serv, gai_strerror(error));\n\t\terr = EADDRNOTAVAIL;\n\t\tgoto out;\n\t}\n\n\tfor (r = res; r; r = r->ai_next) {\n\t\tre_sock_t fd;\n\n\t\tif (us->fd != RE_BAD_SOCK)\n\t\t\tcontinue;\n\n\t\tDEBUG_INFO(\"listen: for: af=%d addr=%j\\n\",\n\t\t\t   r->ai_family, r->ai_addr);\n\n\t\tfd = socket(r->ai_family, SOCK_DGRAM, IPPROTO_UDP);\n\t\tif (fd == RE_BAD_SOCK) {\n\t\t\terr = RE_ERRNO_SOCK;\n\t\t\tcontinue;\n\t\t}\n\n\t\terr = net_sockopt_blocking_set(fd, false);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"udp listen: nonblock set: %m\\n\", err);\n\t\t\t(void)close(fd);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* use dual socket */\n\t\tif (r->ai_family == AF_INET6)\n\t\t\t(void)net_sockopt_v6only(fd, false);\n\n\t\tif (bind(fd, r->ai_addr, SIZ_CAST r->ai_addrlen) < 0) {\n\t\t\terr = RE_ERRNO_SOCK;\n\t\t\tDEBUG_INFO(\"listen: bind(): %m (%J)\\n\", err, local);\n\t\t\t(void)close(fd);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* OK */\n\t\tus->fd = fd;\n\t\tbreak;\n\t}\n\n\tfreeaddrinfo(res);\n\n\t/* We must have at least one socket */\n\tif (RE_BAD_SOCK == us->fd) {\n\t\tif (0 == err)\n\t\t\terr = EADDRNOTAVAIL;\n\t\tgoto out;\n\t}\n\n\terr = udp_thread_attach(us);\n\tif (err)\n\t\tgoto out;\n\n\tus->rh   = rh ? rh : dummy_udp_recv_handler;\n\tus->arg  = arg;\n\tus->rxsz = UDP_RXSZ_DEFAULT;\n\n out:\n\tif (err)\n\t\tmem_deref(us);\n\telse\n\t\t*usp = us;\n\n\treturn err;\n}\n\n\nint udp_alloc_sockless(struct udp_sock **usp,\n\t\t       udp_send_h *sendh, udp_recv_h *recvh, void *arg)\n{\n\tstruct udp_sock *us;\n\tint err;\n\n\tif (!usp || !sendh)\n\t\treturn EINVAL;\n\n\terr = udp_alloc(&us);\n\tif (err)\n\t\treturn err;\n\n\tus->sendh = sendh;\n\tus->rh    = recvh ? recvh : dummy_udp_recv_handler;\n\tus->arg   = arg;\n\tus->rxsz  = UDP_RXSZ_DEFAULT;\n\n\t*usp = us;\n\n\treturn 0;\n}\n\n\nint udp_alloc_fd(struct udp_sock **usp, re_sock_t fd,\n\t\t  udp_recv_h *recvh, void *arg)\n{\n\tstruct udp_sock *us;\n\tint err;\n\n\tif (!usp || fd == RE_BAD_SOCK)\n\t\treturn EINVAL;\n\n\terr = udp_alloc(&us);\n\tif (err)\n\t\treturn err;\n\n\tus->fd   = fd;\n\tus->rh   = recvh ? recvh : dummy_udp_recv_handler;\n\tus->arg  = arg;\n\tus->rxsz = UDP_RXSZ_DEFAULT;\n\n\t*usp = us;\n\treturn 0;\n}\n\n\n/**\n * Create an UDP socket with specified address family.\n *\n * @param usp   Pointer to returned UDP Socket\n * @param af    Address family AF_INET or AF_INET6\n *\n * @return 0 if success, otherwise errorcode\n */\nint udp_open(struct udp_sock **usp, int af)\n{\n\tstruct udp_sock *us;\n\tint err = 0;\n\tre_sock_t fd;\n\n\tif (!usp)\n\t\treturn EINVAL;\n\n\terr = udp_alloc(&us);\n\tif (err)\n\t\treturn err;\n\n\tfd = socket(af, SOCK_DGRAM, IPPROTO_UDP);\n\tif (fd == RE_BAD_SOCK) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tgoto out;\n\t}\n\n\tus->fd = fd;\n\n out:\n\tif (err)\n\t\tmem_deref(us);\n\telse\n\t\t*usp = us;\n\n\treturn err;\n}\n\n\n/**\n * Connect a UDP Socket to a specific peer.\n * When connected, this UDP Socket will only receive data from that peer.\n *\n * @param us   UDP Socket\n * @param peer Peer network address\n *\n * @return 0 if success, otherwise errorcode\n */\nint udp_connect(struct udp_sock *us, const struct sa *peer)\n{\n\tif (!us || !peer)\n\t\treturn EINVAL;\n\n\tif (0 != connect(us->fd, &peer->u.sa, peer->len))\n\t\treturn RE_ERRNO_SOCK;\n\n\tus->conn = true;\n\n\treturn 0;\n}\n\n\nstatic int udp_send_internal(struct udp_sock *us, const struct sa *dst,\n\t\t\t     struct mbuf *mb, struct le *le)\n{\n\tstruct sa hdst;\n\tint err = 0;\n\tre_sock_t fd = us->fd;\n\n\t/* call helpers in reverse order */\n\twhile (le) {\n\t\tstruct udp_helper *uh = le->data;\n\n\t\tmtx_lock(us->lock);\n\t\tle = le->prev;\n\t\tmtx_unlock(us->lock);\n\n\t\tif (dst != &hdst) {\n\t\t\tsa_cpy(&hdst, dst);\n\t\t\tdst = &hdst;\n\t\t}\n\n\t\tif (uh->sendh(&err, &hdst, mb, uh->arg) || err)\n\t\t\treturn err;\n\t}\n\n\t/* external send handler */\n\tif (us->sendh)\n\t\treturn us->sendh(dst, mb, us->arg);\n\n\t/* Connected socket? */\n\tif (us->conn) {\n\t\tif (send(fd, BUF_CAST mb->buf + mb->pos,\n\t\t\t SIZ_CAST (mb->end - mb->pos),\n\t\t\t 0) < 0)\n\t\t\treturn RE_ERRNO_SOCK;\n\t}\n\telse {\n\t\tif (sendto(fd, BUF_CAST mb->buf + mb->pos,\n\t\t\t   SIZ_CAST (mb->end - mb->pos),\n\t\t\t   0, &dst->u.sa, dst->len) < 0)\n\t\t\treturn RE_ERRNO_SOCK;\n\t}\n\n\treturn 0;\n}\n\n\n/**\n * Send a UDP Datagram to a peer\n *\n * @param us  UDP Socket\n * @param dst Destination network address\n * @param mb  Buffer to send\n *\n * @return 0 if success, otherwise errorcode\n */\nint udp_send(struct udp_sock *us, const struct sa *dst, struct mbuf *mb)\n{\n\tstruct le *le;\n\tif (!us || !dst || !mb)\n\t\treturn EINVAL;\n\n\tmtx_lock(us->lock);\n\tle = us->helpers.tail;\n\tmtx_unlock(us->lock);\n\treturn udp_send_internal(us, dst, mb, le);\n}\n\n\n/**\n * Get the local network address on the UDP Socket\n *\n * @param us    UDP Socket\n * @param local The returned local network address\n *\n * @return 0 if success, otherwise errorcode\n */\nint udp_local_get(const struct udp_sock *us, struct sa *local)\n{\n\tif (!us || !local)\n\t\treturn EINVAL;\n\n\tlocal->len = sizeof(local->u);\n\n\tif (0 == getsockname(us->fd, &local->u.sa, &local->len))\n\t\treturn 0;\n\n\treturn RE_ERRNO_SOCK;\n}\n\n\n/**\n * Set socket options on the UDP Socket\n *\n * @param us      UDP Socket\n * @param level   Socket level\n * @param optname Option name\n * @param optval  Option value\n * @param optlen  Option length\n *\n * @return 0 if success, otherwise errorcode\n */\nint udp_setsockopt(struct udp_sock *us, int level, int optname,\n\t\t   const void *optval, uint32_t optlen)\n{\n\tint err = 0;\n\n\tif (!us)\n\t\treturn EINVAL;\n\n\tif (RE_BAD_SOCK != us->fd) {\n\t\tif (0 != setsockopt(us->fd, level, optname,\n\t\t\t\t    BUF_CAST optval, optlen))\n\t\t\terr |= RE_ERRNO_SOCK;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Set the send/receive buffer size on a UDP Socket\n *\n * @param us   UDP Socket\n * @param size Buffer size in bytes\n *\n * @return 0 if success, otherwise errorcode\n */\nint udp_sockbuf_set(struct udp_sock *us, int size)\n{\n\tint err = 0;\n\n\tif (!us)\n\t\treturn EINVAL;\n\n\terr |= udp_setsockopt(us, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));\n\terr |= udp_setsockopt(us, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));\n\n\treturn err;\n}\n\n\nint udp_settos(struct udp_sock *us, uint8_t tos)\n{\n\tint err = 0;\n\tint v = tos;\n\tstruct sa sa;\n#ifdef WIN32\n\tQOS_VERSION qos_version = { 1 , 0 };\n\tQOS_TRAFFIC_TYPE qos_type = QOSTrafficTypeBestEffort;\n\tif (tos >= 32) /* >= DSCP_CS1 */\n\t\tqos_type = QOSTrafficTypeBackground;\n\tif (tos >= 40) /* >= DSCP_AF11 */\n\t\tqos_type = QOSTrafficTypeExcellentEffort;\n\tif (tos >= 136) /* >= DSCP_AF41 */\n\t\tqos_type = QOSTrafficTypeAudioVideo;\n\tif (tos >= 184) /* >= DSCP_EF */\n\t\tqos_type = QOSTrafficTypeVoice;\n\tif (tos >= 224) /* >= DSCP_CS7 */\n\t\tqos_type = QOSTrafficTypeControl;\n#endif\n\tif (!us)\n\t\treturn EINVAL;\n\n#ifdef WIN32\n\terr = QOSCreateHandle(&qos_version, &us->qos);\n\tif (!err)\n\t\treturn GetLastError();\n\n\tus->qos_id = 0;\n\tif (RE_BAD_SOCK != us->fd) {\n\t\terr = QOSAddSocketToFlow(us->qos, us->fd, NULL,\n\t\t\t\tqos_type,\n\t\t\t\tQOS_NON_ADAPTIVE_FLOW,\n\t\t\t\t&us->qos_id);\n\t\tif (!err)\n\t\t\treturn WSAGetLastError();\n\t}\n#endif\n\terr = udp_local_get(us, &sa);\n\tif (err)\n\t\treturn err;\n\n\tif (sa_af(&sa) == AF_INET) {\n\t\terr = udp_setsockopt(us, IPPROTO_IP, IP_TOS, &v, sizeof(v));\n\t}\n#if defined(IPV6_TCLASS) && !defined(WIN32)\n\telse if (sa_af(&sa) == AF_INET6) {\n\t\terr = udp_setsockopt(us, IPPROTO_IPV6, IPV6_TCLASS, &v,\n\t\t\t\t     sizeof(v));\n\t}\n#endif\n\n\treturn err;\n}\n\n\n/**\n * Set the maximum receive chunk size on a UDP Socket\n *\n * @param us   UDP Socket\n * @param rxsz Maximum receive chunk size\n */\nvoid udp_rxsz_set(struct udp_sock *us, size_t rxsz)\n{\n\tif (!us)\n\t\treturn;\n\n\tus->rxsz = rxsz;\n}\n\n\n/**\n * Set preallocated space on receive buffer.\n *\n * @param us       UDP Socket\n * @param rx_presz Size of preallocate space.\n */\nvoid udp_rxbuf_presz_set(struct udp_sock *us, size_t rx_presz)\n{\n\tif (!us)\n\t\treturn;\n\n\tus->rx_presz = rx_presz;\n}\n\n\n/**\n * Set receive handler on a UDP Socket\n *\n * @param us  UDP Socket\n * @param rh  Receive handler\n * @param arg Handler argument\n */\nvoid udp_handler_set(struct udp_sock *us, udp_recv_h *rh, void *arg)\n{\n\tif (!us)\n\t\treturn;\n\n\tus->rh  = rh ? rh : dummy_udp_recv_handler;\n\tus->arg = arg;\n}\n\n\n/**\n * Set error handler on a UDP Socket\n *\n * @param us  UDP Socket\n * @param eh  Error handler\n */\nvoid udp_error_handler_set(struct udp_sock *us, udp_error_h *eh)\n{\n\tif (!us)\n\t\treturn;\n\n\tus->eh = eh;\n}\n\n\n/**\n * Get the File Descriptor from a UDP Socket\n *\n * @param us  UDP Socket\n *\n * @return File Descriptor, or RE_BAD_SOCK for errors\n */\nre_sock_t udp_sock_fd(const struct udp_sock *us)\n{\n\treturn us ? us->fd : RE_BAD_SOCK;\n}\n\n\n/**\n * Attach the current thread to the UDP Socket\n *\n * @param us UDP Socket\n *\n * @return 0 if success, otherwise errorcode\n */\nint udp_thread_attach(struct udp_sock *us)\n{\n\tint err = 0;\n\n\tif (!us)\n\t\treturn EINVAL;\n\n\tif (RE_BAD_SOCK != us->fd) {\n\t\terr = fd_listen(&us->fhs, us->fd, FD_READ, udp_read_handler,\n\t\t\t\tus);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n out:\n\tif (err)\n\t\tudp_thread_detach(us);\n\n\treturn err;\n}\n\n\n/**\n * Detach the current thread from the UDP Socket\n *\n * @param us UDP Socket\n */\nvoid udp_thread_detach(struct udp_sock *us)\n{\n\tif (!us)\n\t\treturn;\n\n\tif (RE_BAD_SOCK != us->fd)\n\t\tus->fhs = fd_close(us->fhs);\n}\n\n\nstatic void helper_destructor(void *data)\n{\n\tstruct udp_helper *uh = data;\n\n\tmtx_lock(uh->lock);\n\tlist_unlink(&uh->le);\n\tmtx_unlock(uh->lock);\n}\n\n\nstatic bool sort_handler(struct le *le1, struct le *le2, void *arg)\n{\n\tstruct udp_helper *uh1 = le1->data, *uh2 = le2->data;\n\t(void)arg;\n\n\treturn uh1->layer <= uh2->layer;\n}\n\n\n/**\n * Register a UDP protocol stack helper\n *\n * @param uhp   Pointer to allocated UDP helper object\n * @param us    UDP socket\n * @param layer Layer number; higher number means higher up in stack\n * @param sh    Send handler\n * @param rh    Receive handler\n * @param arg   Handler argument\n *\n * @return 0 if success, otherwise errorcode\n */\nint udp_register_helper(struct udp_helper **uhp, struct udp_sock *us,\n\t\t\tint layer,\n\t\t\tudp_helper_send_h *sh, udp_helper_recv_h *rh,\n\t\t\tvoid *arg)\n{\n\tstruct udp_helper *uh;\n\n\tif (!us)\n\t\treturn EINVAL;\n\n\tuh = mem_zalloc(sizeof(*uh), helper_destructor);\n\tif (!uh)\n\t\treturn ENOMEM;\n\n\tmtx_lock(us->lock);\n\tlist_append(&us->helpers, &uh->le, uh);\n\n\tuh->lock  = us->lock;\n\tuh->layer = layer;\n\tuh->sendh = sh ? sh : helper_send_handler;\n\tuh->recvh = rh ? rh : helper_recv_handler;\n\tuh->arg   = arg;\n\n\tlist_sort(&us->helpers, sort_handler, NULL);\n\n\tif (uhp)\n\t\t*uhp = uh;\n\n\tmtx_unlock(us->lock);\n\treturn 0;\n}\n\n\n/**\n * Send a UDP Datagram to a remote peer bypassing this helper and\n * the helpers above it.\n *\n * @param us  UDP Socket\n * @param dst Destination network address\n * @param mb  Buffer to send\n * @param uh  UDP Helper\n *\n * @return 0 if success, otherwise errorcode\n */\nint udp_send_helper(struct udp_sock *us, const struct sa *dst,\n\t\t    struct mbuf *mb, struct udp_helper *uh)\n{\n\tstruct le *le;\n\n\tif (!us || !dst || !mb || !uh)\n\t\treturn EINVAL;\n\n\tmtx_lock(us->lock);\n\tle = uh->le.prev;\n\tmtx_unlock(us->lock);\n\treturn udp_send_internal(us, dst, mb, le);\n}\n\n\n/**\n * Receive a UDP Datagram on this UDP helper layer.\n *\n * @param us  UDP Socket\n * @param src Source network address\n * @param mb  Buffer to receive\n * @param uhx UDP Helper\n */\nvoid udp_recv_helper(struct udp_sock *us, const struct sa *src,\n\t\t     struct mbuf *mb, struct udp_helper *uhx)\n{\n\tstruct sa hsrc;\n\tstruct le *le;\n\n\tif (!us || !src || !mb)\n\t\treturn;\n\n\tmtx_lock(us->lock);\n\tle = uhx ? uhx->le.next : us->helpers.head;\n\tmtx_unlock(us->lock);\n\twhile (le) {\n\t\tstruct udp_helper *uh = le->data;\n\t\tbool hdld;\n\n\t\tmtx_lock(us->lock);\n\t\tle = le->next;\n\t\tmtx_unlock(us->lock);\n\n\t\tif (src != &hsrc) {\n\t\t\tsa_cpy(&hsrc, src);\n\t\t\tsrc = &hsrc;\n\t\t}\n\n\t\thdld = uh->recvh(&hsrc, mb, uh->arg);\n\t\tif (hdld)\n\t\t\treturn;\n\t}\n\n\tus->rh(src, mb, us->arg);\n}\n\n\n/**\n * Find a UDP-helper on a UDP socket\n *\n * @param us    UDP socket\n * @param layer Layer number\n *\n * @return UDP-helper if found, NULL if not found\n */\nstruct udp_helper *udp_helper_find(const struct udp_sock *us, int layer)\n{\n\tstruct le *le;\n\n\tif (!us)\n\t\treturn NULL;\n\n\tmtx_lock(us->lock);\n\tle = us->helpers.head;\n\tmtx_unlock(us->lock);\n\twhile (le) {\n\n\t\tstruct udp_helper *uh = le->data;\n\n\t\tmtx_lock(us->lock);\n\t\tle = le->next;\n\t\tmtx_unlock(us->lock);\n\n\t\tif (layer == uh->layer)\n\t\t\treturn uh;\n\t}\n\n\treturn NULL;\n}\n\n\n/**\n * Flush a given UDP socket\n *\n * @param us UDP socket\n */\nvoid udp_flush(const struct udp_sock *us)\n{\n\tif (!us)\n\t\treturn;\n\n\tif (RE_BAD_SOCK != us->fd) {\n\t\tuint8_t buf[4096];\n\n\t\twhile (recvfrom(us->fd, BUF_CAST buf, sizeof(buf),\n\t\t\t\t0, NULL, 0) > 0)\n\t\t\t;\n\t}\n}\n\n\n/**\n * Receive a UDP Datagram on this UDP socket. All helpers are processed.\n *\n * @param us  UDP Socket\n * @param src Source network address\n * @param mb  Buffer to receive\n */\nvoid udp_recv_packet(struct udp_sock *us, const struct sa *src,\n\t\t     struct mbuf *mb)\n{\n\tudp_recv_helper(us, src, mb, NULL);\n}\n"
  },
  {
    "path": "src/unixsock/unixsock.c",
    "content": "/**\n * @file unixsock/unixsock.c  Unix domain sockets\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n\n#include <stdio.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include <re_types.h>\n#include <re_sa.h>\n#include <re_net.h>\n#include <re_unixsock.h>\n\n#define DEBUG_MODULE \"unixsock\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n#ifdef WIN32\n#define close closesocket\n#define unlink _unlink\n#endif\n\n\n/**\n * Listen for incoming connections on a Unix domain socket.\n *\n * @param fdp   Pointer to a re_sock_t variable where the listening socket\n *              file descriptor will be stored.\n * @param sock  Pointer to a struct sa containing the address of the\n *              Unix domain socket.\n *\n * @return 0 if success, otherwise errorcode\n */\nint unixsock_listen_fd(re_sock_t *fdp, const struct sa *sock)\n{\n#if HAVE_UNIXSOCK\n\tint err = 0;\n\tre_sock_t fd;\n\n\tif (!fdp || !sock)\n\t\treturn EINVAL;\n\n\tif (sa_af(sock) != AF_UNIX || !sa_isset(sock, SA_ADDR))\n\t\treturn EINVAL;\n\n\tfd = socket(AF_UNIX, SOCK_STREAM, 0);\n\tif (fd == RE_BAD_SOCK) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tgoto err;\n\t}\n\n\terr = net_sockopt_blocking_set(fd, false);\n\tif (err) {\n\t\tDEBUG_WARNING(\"unix listen: nonblock set: %m\\n\", err);\n\t\tgoto err;\n\t}\n\n\t(void)unlink(sock->u.un.sun_path);\n\n\tif (bind(fd, &sock->u.sa, sock->len) < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tDEBUG_WARNING(\"bind(): %m (%J)\\n\", err, sock);\n\t\tgoto err;\n\t}\n\n\tif (listen(fd, SOMAXCONN) < 0) {\n\t\terr = RE_ERRNO_SOCK;\n\t\tDEBUG_WARNING(\"listen(): %m (%J)\\n\", err, sock);\n\t\tgoto err;\n\t}\n\n\t*fdp = fd;\n\n\treturn 0;\n\nerr:\n\tif (fd != RE_BAD_SOCK) {\n\t\t(void)close(fd);\n\t}\n\n\treturn err;\n#else\n\t(void)fdp;\n\t(void)sock;\n\treturn ENOTSUP;\n#endif\n}\n"
  },
  {
    "path": "src/uri/uri.c",
    "content": "/**\n * @file uri.c  Uniform Resource Identifier (URI) module\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_list.h>\n#include <re_sa.h>\n#include <re_uri.h>\n\n\n/**\n * Encode a URI object\n *\n * @param pf  Print function to encode into\n * @param uri URI object\n *\n * @return 0 if success, otherwise errorcode\n */\nint uri_encode(struct re_printf *pf, const struct uri *uri)\n{\n\tint err;\n\n\tif (!uri)\n\t\treturn 0;\n\n\tif (!pl_isset(&uri->scheme) || !pl_isset(&uri->host))\n\t\treturn EINVAL;\n\n\terr = re_hprintf(pf, \"%r:\", &uri->scheme);\n\tif (err)\n\t\treturn err;\n\n\tif (pl_isset(&uri->user)) {\n\t\terr = re_hprintf(pf, \"%r\", &uri->user);\n\n\t\terr |= pf->vph(\"@\", 1, pf->arg);\n\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\t/* The IPv6 address is delimited by '[' and ']' */\n\tswitch (uri->af) {\n\n\tcase AF_INET6:\n\t\terr = re_hprintf(pf, \"[%r]\", &uri->host);\n\t\tbreak;\n\n\tdefault:\n\t\terr = re_hprintf(pf, \"%r\", &uri->host);\n\t\tbreak;\n\t}\n\tif (err)\n\t\treturn err;\n\n\tif (uri->port)\n\t\terr = re_hprintf(pf, \":%u\", uri->port);\n\n\terr |= re_hprintf(pf, \"%r%r%r\", &uri->path, &uri->params,\n\t\t\t  &uri->headers);\n\n\treturn err;\n}\n\n\n/**\n * Decode host-port portion of a URI (if present)\n *\n * @param hostport Host and port input string\n * @param host     Decoded host portion\n * @param port     Decoded port portion\n *\n * @return 0 if success, otherwise errorcode\n */\nint uri_decode_hostport(const struct pl *hostport, struct pl *host,\n\t\t\tstruct pl *port)\n{\n\tif (!hostport || !host || !port)\n\t\treturn EINVAL;\n\n\t/* Try IPv6 first */\n\tif (!re_regex(hostport->p, hostport->l, \"\\\\[[0-9a-f:]+\\\\][:]*[0-9]*\",\n\t\t      host, NULL, port))\n\t\treturn 0;\n\n\t/* Then non-IPv6 host */\n\treturn re_regex(hostport->p, hostport->l, \"[^:]+[:]*[0-9]*\",\n\t\t\thost, NULL, port);\n}\n\n\n/**\n * Decode a pointer-length object into a URI object\n *\n * @param uri  URI object\n * @param pl   Pointer-length object to decode from\n *\n * @return 0 if success, otherwise errorcode\n */\nint uri_decode(struct uri *uri, const struct pl *pl)\n{\n\tstruct sa addr;\n\tstruct pl port = PL_INIT;\n\tstruct pl hostport;\n\tint err;\n\n\tif (!uri || !pl)\n\t\treturn EINVAL;\n\n\tmemset(uri, 0, sizeof(*uri));\n\tif (0 == re_regex(pl->p, pl->l,\n\t\t\t  \"[^:]+:[^@:]*[:]*[^@]*@[^/;? ]+[^;? ]*[^?]*[^]*\",\n\t\t\t  &uri->scheme, &uri->user, NULL, NULL,\n\t\t\t  &hostport, &uri->path, &uri->params,\n\t\t\t  &uri->headers)) {\n\n\t\tif (0 == uri_decode_hostport(&hostport, &uri->host, &port))\n\t\t\tgoto out;\n\t}\n\n\tmemset(uri, 0, sizeof(*uri));\n\terr = re_regex(pl->p, pl->l, \"[^:]+:[^/;? ]+[^;? ]*[^?]*[^]*\",\n\t\t       &uri->scheme, &hostport, &uri->path, &uri->params,\n\t\t       &uri->headers);\n\tif (0 == err) {\n\t\terr = uri_decode_hostport(&hostport, &uri->host, &port);\n\t\tif (0 == err)\n\t\t\tgoto out;\n\t}\n\n\treturn err;\n\n out:\n\t/* Cache host address family */\n\tif (0 == sa_set(&addr, &uri->host, 0))\n\t\turi->af = sa_af(&addr);\n\telse\n\t\turi->af = AF_UNSPEC;\n\n\tif (pl_isset(&port))\n\t\turi->port = (uint16_t)pl_u32(&port);\n\n\treturn 0;\n}\n\n\n/**\n * Get a URI parameter and possibly the value of it\n *\n * @param pl     Pointer-length string containing parameters\n * @param pname  URI Parameter name\n * @param pvalue Returned URI Parameter value\n *\n * @return 0 if success, otherwise errorcode\n */\nint uri_param_get(const struct pl *pl, const struct pl *pname,\n\t\t  struct pl *pvalue)\n{\n\tchar expr[128];\n\n\tif (!pl || !pname || !pvalue)\n\t\treturn EINVAL;\n\n\t(void)re_snprintf(expr, sizeof(expr), \";%r[=]*[^;]*\", pname);\n\n\treturn re_regex(pl->p, pl->l, expr, NULL, pvalue);\n}\n\n\n/**\n * Call the apply handler for each URI Parameter\n *\n * @param pl  Pointer-length string containing parameters\n * @param ah  Apply handler\n * @param arg Handler argument\n *\n * @return 0 if success, otherwise errorcode (returned from handler)\n */\nint uri_params_apply(const struct pl *pl, uri_apply_h *ah, void *arg)\n{\n\tstruct pl plr, pname, eq, pvalue;\n\tint err = 0;\n\n\tif (!pl || !ah)\n\t\treturn EINVAL;\n\n\tplr = *pl;\n\n\twhile (plr.l > 0) {\n\n\t\terr = re_regex(plr.p, plr.l, \";[^;=]+[=]*[^;]*\",\n\t\t\t       &pname, &eq, &pvalue);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tpl_advance(&plr, 1 + pname.l + eq.l + pvalue.l);\n\n\t\terr = ah(&pname, &pvalue, arg);\n\t\tif (err)\n\t\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Get a URI header and possibly the value of it\n *\n * @param pl     Pointer-length string containing URI Headers\n * @param hname  URI Header name\n * @param hvalue Returned URI Header value\n *\n * @return 0 if success, otherwise errorcode\n */\nint uri_header_get(const struct pl *pl, const struct pl *hname,\n\t\t   struct pl *hvalue)\n{\n\tchar expr[128];\n\n\tif (!pl || !hname || !hvalue)\n\t\treturn EINVAL;\n\n\t(void)re_snprintf(expr, sizeof(expr), \"[?&]1%r=[^&]+\", hname);\n\n\treturn re_regex(pl->p, pl->l, expr, NULL, hvalue);\n}\n\n\n/**\n * Call the apply handler for each URI Header\n *\n * @param pl  Pointer-length string containing URI Headers\n * @param ah  Apply handler\n * @param arg Handler argument\n *\n * @return 0 if success, otherwise errorcode (returned from handler)\n */\nint uri_headers_apply(const struct pl *pl, uri_apply_h *ah, void *arg)\n{\n\tstruct pl plr, sep, hname, hvalue;\n\tint err = 0;\n\n\tif (!pl || !ah)\n\t\treturn EINVAL;\n\n\tplr = *pl;\n\n\twhile (plr.l > 0) {\n\n\t\terr = re_regex(plr.p, plr.l, \"[?&]1[^=]+=[^&]+\",\n\t\t\t       &sep, &hname, &hvalue);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tpl_advance(&plr, sep.l + hname.l + 1 + hvalue.l);\n\n\t\terr = ah(&hname, &hvalue, arg);\n\t\tif (err)\n\t\t\tbreak;\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "src/uri/uric.c",
    "content": "/**\n * @file uric.c  URI component escaping/unescaping\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <ctype.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_uri.h>\n\n\n#define DEBUG_MODULE \"uric\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/** Defines the URI escape handler */\ntypedef bool (esc_h)(char c);\n\n\nstatic bool is_mark(int c)\n{\n\tswitch (c) {\n\n\tcase '-':\n\tcase '_':\n\tcase '.':\n\tcase '!':\n\tcase '~':\n\tcase '*':\n\tcase '\\'':\n\tcase '(':\n\tcase ')':\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\nstatic bool is_unreserved(char c)\n{\n\treturn isalnum((unsigned char)c) || is_mark(c);\n}\n\n\nstatic bool is_user_unreserved(int c)\n{\n\tswitch (c) {\n\n\tcase '&':\n\tcase '=':\n\tcase '+':\n\tcase '$':\n\tcase ',':\n\tcase ';':\n\tcase '?':\n\tcase '/':\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nstatic bool is_hnv_unreserved(char c)\n{\n\tswitch (c) {\n\n\tcase '[':\n\tcase ']':\n\tcase '/':\n\tcase '?':\n\tcase ':':\n\tcase '+':\n\tcase '$':\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\nstatic bool is_user(char c)\n{\n\treturn is_unreserved(c) || is_user_unreserved(c);\n}\n\n\nstatic bool is_param_unreserved(char c)\n{\n\tswitch (c) {\n\n\tcase '[':\n\tcase ']':\n\tcase '/':\n\tcase ':':\n\tcase '&':\n\tcase '+':\n\tcase '$':\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\n\nstatic bool is_paramchar(char c)\n{\n\treturn is_param_unreserved(c) || is_unreserved(c);\n}\n\n\nstatic bool is_hvalue(char c)\n{\n\treturn is_hnv_unreserved(c) || is_unreserved(c);\n}\n\n\nstatic int comp_escape(struct re_printf *pf, const struct pl *pl, esc_h *eh)\n{\n\tsize_t i;\n\tint err = 0;\n\n\tif (!pf || !pl || !eh)\n\t\treturn EINVAL;\n\n\tfor (i=0; i<pl->l && !err; i++) {\n\t\tconst char c = pl->p[i];\n\n\t\tif (eh(c)) {\n\t\t\terr = pf->vph(&c, 1, pf->arg);\n\t\t}\n\t\telse {\n\t\t\terr = re_hprintf(pf, \"%%%02X\", c);\n\t\t}\n\t}\n\n\treturn err;\n}\n\n\nstatic int comp_unescape(struct re_printf *pf, const struct pl *pl, esc_h *eh)\n{\n\tsize_t i;\n\tint err = 0;\n\n\tif (!pf || !pl || !eh)\n\t\treturn EINVAL;\n\n\tfor (i=0; i<pl->l && !err; i++) {\n\t\tconst char c = pl->p[i];\n\n\t\tif (eh(c)) {\n\t\t\terr = pf->vph(&c, 1, pf->arg);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ('%' == c) {\n\t\t\tif (i+2 < pl->l) {\n\t\t\t\tconst uint8_t hi = ch_hex(pl->p[++i]);\n\t\t\t\tconst uint8_t lo = ch_hex(pl->p[++i]);\n\t\t\t\tconst char b = hi<<4 | lo;\n\t\t\t\terr = pf->vph(&b, 1, pf->arg);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tDEBUG_WARNING(\"unescape: short uri (%u)\\n\", i);\n\t\t\t\treturn EBADMSG;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tDEBUG_WARNING(\"unescape: illegal '%c' in %r\\n\",\n\t\t\t\t      c, pl);\n\t\t\treturn EINVAL;\n\t\t}\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Escape a URI user component\n *\n * @param pf Print function\n * @param pl String to escape\n *\n * @return 0 if success, otherwise errorcode\n */\nint uri_user_escape(struct re_printf *pf, const struct pl *pl)\n{\n\treturn comp_escape(pf, pl, is_user);\n}\n\n\n/**\n * Unescape a URI user component\n *\n * @param pf Print function\n * @param pl String to unescape\n *\n * @return 0 if success, otherwise errorcode\n */\nint uri_user_unescape(struct re_printf *pf, const struct pl *pl)\n{\n\treturn comp_unescape(pf, pl, is_user);\n}\n\n\n/**\n * Escape one URI Parameter value\n *\n * @param pf Print function\n * @param pl String to escape\n *\n * @return 0 if success, otherwise errorcode\n */\nint uri_param_escape(struct re_printf *pf, const struct pl *pl)\n{\n\treturn comp_escape(pf, pl, is_paramchar);\n}\n\n\n/**\n * Unescape one URI Parameter value\n *\n * @param pf Print function\n * @param pl String to unescape\n *\n * @return 0 if success, otherwise errorcode\n */\nint uri_param_unescape(struct re_printf *pf, const struct pl *pl)\n{\n\treturn comp_unescape(pf, pl, is_paramchar);\n}\n\n\n/**\n * Escape one URI Header name/value\n *\n * @param pf Print function\n * @param pl String to escape\n *\n * @return 0 if success, otherwise errorcode\n */\nint uri_header_escape(struct re_printf *pf, const struct pl *pl)\n{\n\treturn comp_escape(pf, pl, is_hvalue);\n}\n\n\n/**\n * Unescape one URI Header name/value\n *\n * @param pf Print function\n * @param pl String to unescape\n *\n * @return 0 if success, otherwise errorcode\n */\nint uri_header_unescape(struct re_printf *pf, const struct pl *pl)\n{\n\treturn comp_unescape(pf, pl, is_hvalue);\n}\n\n"
  },
  {
    "path": "src/websock/websock.c",
    "content": "/**\n * @file websock.c  Implementation of The WebSocket Protocol\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re_types.h>\n#include <re_fmt.h>\n#include <re_mem.h>\n#include <re_mbuf.h>\n#include <re_sa.h>\n#include <re_list.h>\n#include <re_tmr.h>\n#include <re_srtp.h>\n#include <re_tcp.h>\n#include <re_tls.h>\n#include <re_msg.h>\n#include <re_http.h>\n#include <re_base64.h>\n#include <re_sha.h>\n#include <re_sys.h>\n#include <re_websock.h>\n\n\nenum {\n\tTIMEOUT_CLOSE = 10000,\n\tBUFSIZE_MAX   = 131072,\n};\n\nenum websock_state {\n\tACCEPTING = 0,\n\tCONNECTING,\n\tOPEN,\n\tCLOSING,\n\tCLOSED,\n};\n\nstruct websock {\n\twebsock_shutdown_h *shuth;\n\tvoid *arg;\n\tbool shutdown;\n};\n\nstruct websock_conn {\n\tstruct tmr tmr;\n\tchar nonce[24];\n\tstruct websock *sock;\n\tstruct tcp_conn *tc;\n\tstruct tls_conn *sc;\n\tstruct mbuf *mb;\n\tstruct http_req *req;\n\twebsock_estab_h *estabh;\n\twebsock_recv_h *recvh;\n\twebsock_close_h *closeh;\n\tvoid *arg;\n\tenum websock_state state;\n\tunsigned kaint;\n\tbool active;\n};\n\n\nstatic const char magic[] = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\";\n\n\nstatic void timeout_handler(void *arg);\n\n\nstatic void dummy_recv_handler(const struct websock_hdr *hdr, struct mbuf *mb,\n\t\t\t       void *arg)\n{\n\t(void)hdr;\n\t(void)mb;\n\t(void)arg;\n}\n\n\nstatic void internal_close_handler(int err, void *arg)\n{\n\tstruct websock_conn *conn = arg;\n\t(void)err;\n\n\tmem_deref(conn);\n}\n\n\nstatic void sock_destructor(void *arg)\n{\n\tstruct websock *sock = arg;\n\n\tif (sock->shutdown) {\n\t\tsock->shutdown = false;\n\t\tmem_ref(sock);\n\t\tif (sock->shuth)\n\t\t\tsock->shuth(sock->arg);\n\t\treturn;\n\t}\n}\n\n\nstatic void conn_destructor(void *arg)\n{\n\tstruct websock_conn *conn = arg;\n\n\tif (conn->state == OPEN)\n\t\t(void)websock_close(conn, WEBSOCK_GOING_AWAY, \"Going Away\");\n\n\tif (conn->state == CLOSING) {\n\n\t\tconn->recvh  = dummy_recv_handler;\n\t\tconn->closeh = internal_close_handler;\n\t\tconn->arg    = conn;\n\n\t\ttmr_start(&conn->tmr, TIMEOUT_CLOSE, timeout_handler, conn);\n\n\t\t/* important: the hack below depends on this */\n\t\tmem_ref(conn);\n\t\treturn;\n\t}\n\n\ttmr_cancel(&conn->tmr);\n\tmem_deref(conn->sc);\n\tmem_deref(conn->tc);\n\tmem_deref(conn->mb);\n\tmem_deref(conn->req);\n\tmem_deref(conn->sock);\n}\n\n\nstatic void conn_close(struct websock_conn *conn, int err)\n{\n\ttmr_cancel(&conn->tmr);\n\tconn->sc = mem_deref(conn->sc);\n\tconn->tc = mem_deref(conn->tc);\n\tconn->state = CLOSED;\n\n\tconn->closeh(err, conn->arg);\n}\n\n\nstatic void timeout_handler(void *arg)\n{\n\tstruct websock_conn *conn = arg;\n\n\tconn_close(conn, ETIMEDOUT);\n}\n\n\nstatic void keepalive_handler(void *arg)\n{\n\tstruct websock_conn *conn = arg;\n\n\ttmr_start(&conn->tmr, conn->kaint, keepalive_handler, conn);\n\n\t(void)websock_send(conn, WEBSOCK_PING, NULL);\n}\n\n\nstatic enum websock_scode websock_err2scode(int err)\n{\n\tswitch (err) {\n\n\tcase EOVERFLOW: return WEBSOCK_MESSAGE_TOO_BIG;\n\tcase EPROTO:    return WEBSOCK_PROTOCOL_ERROR;\n\tcase EBADMSG:   return WEBSOCK_PROTOCOL_ERROR;\n\tdefault:        return WEBSOCK_INTERNAL_ERROR;\n\t}\n}\n\n\nstatic int websock_decode(struct websock_hdr *hdr, struct mbuf *mb)\n{\n\tuint8_t v, *p;\n\tsize_t i;\n\n\tif (mbuf_get_left(mb) < 2)\n\t\treturn ENODATA;\n\n\tv = mbuf_read_u8(mb);\n\thdr->fin    = v>>7 & 0x1;\n\thdr->rsv1   = v>>6 & 0x1;\n\thdr->rsv2   = v>>5 & 0x1;\n\thdr->rsv3   = v>>4 & 0x1;\n\thdr->opcode = v    & 0x0f;\n\n\tv = mbuf_read_u8(mb);\n\thdr->mask = v>>7 & 0x1;\n\thdr->len  = v    & 0x7f;\n\n\tif (hdr->len == 126) {\n\n\t\tif (mbuf_get_left(mb) < 2)\n\t\t\treturn ENODATA;\n\n\t\thdr->len = ntohs(mbuf_read_u16(mb));\n\t}\n\telse if (hdr->len == 127) {\n\n\t\tif (mbuf_get_left(mb) < 8)\n\t\t\treturn ENODATA;\n\n\t\thdr->len = sys_ntohll(mbuf_read_u64(mb));\n\t}\n\n\tif (hdr->mask) {\n\n\t\tif (mbuf_get_left(mb) < (4 + hdr->len))\n\t\t\treturn ENODATA;\n\n\t\thdr->mkey[0] = mbuf_read_u8(mb);\n\t\thdr->mkey[1] = mbuf_read_u8(mb);\n\t\thdr->mkey[2] = mbuf_read_u8(mb);\n\t\thdr->mkey[3] = mbuf_read_u8(mb);\n\n\t\tfor (i=0, p=mbuf_buf(mb); i<hdr->len; i++)\n\t\t\tp[i] = p[i] ^ hdr->mkey[i%4];\n\t}\n\telse {\n\t\tif (mbuf_get_left(mb) < hdr->len)\n\t\t\treturn ENODATA;\n\t}\n\n\treturn 0;\n}\n\n\nstatic void recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct websock_conn *conn = arg;\n\tint err = 0;\n\n\tif (conn->mb) {\n\n\t\tconst size_t len = mbuf_get_left(mb), pos = conn->mb->pos;\n\n\t\tif ((mbuf_get_left(conn->mb) + len) > BUFSIZE_MAX) {\n\t\t\terr = EOVERFLOW;\n\t\t\tgoto out;\n\t\t}\n\n\t\tconn->mb->pos = conn->mb->end;\n\n\t\terr = mbuf_write_mem(conn->mb, mbuf_buf(mb), len);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tconn->mb->pos = pos;\n\t}\n\telse {\n\t\tconn->mb = mem_ref(mb);\n\t}\n\n\twhile (conn->mb) {\n\n\t\tstruct websock_hdr hdr;\n\t\tsize_t pos, end;\n\n\t\tpos = conn->mb->pos;\n\n\t\terr = websock_decode(&hdr, conn->mb);\n\t\tif (err) {\n\t\t\tif (err == ENODATA) {\n\t\t\t\tconn->mb->pos = pos;\n\t\t\t\terr = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (conn->active == hdr.mask) {\n\t\t\terr = EPROTO;\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (hdr.rsv1 || hdr.rsv2 || hdr.rsv3) {\n\t\t\terr = EPROTO;\n\t\t\tgoto out;\n\t\t}\n\n\t\tmb = conn->mb;\n\n\t\tend     = mb->end;\n\t\tmb->end = mb->pos + (size_t)hdr.len;\n\n\t\tif (end > mb->end) {\n\t\t\tstruct mbuf *mbn = mbuf_alloc(end - mb->end);\n\t\t\tif (!mbn) {\n\t\t\t\terr = ENOMEM;\n\t\t\t\tgoto out;\n\t\t\t}\n\n\t\t\t(void)mbuf_write_mem(mbn, mb->buf + mb->end,\n\t\t\t\t\t     end - mb->end);\n\t\t\tmbn->pos = 0;\n\n\t\t\tconn->mb = mbn;\n\t\t}\n\t\telse {\n\t\t\tconn->mb = NULL;\n\t\t}\n\n\t\tswitch (hdr.opcode) {\n\n\t\tcase WEBSOCK_CONT:\n\t\tcase WEBSOCK_TEXT:\n\t\tcase WEBSOCK_BIN:\n\t\t\tmem_ref(conn);\n\t\t\tconn->recvh(&hdr, mb, conn->arg);\n\n\t\t\tif (mem_nrefs(conn) == 1) {\n\n\t\t\t\tif (conn->state == OPEN)\n\t\t\t\t\t(void)websock_close(conn,\n\t\t\t\t\t\t            WEBSOCK_GOING_AWAY,\n\t\t\t\t\t\t\t    \"Going Away\");\n\n\t\t\t\t/*\n\t\t\t\t * This is a hack. We enforce CLOSING\n\t\t\t\t * state so we know the connection will\n\t\t\t\t * continue to live.\n\t\t\t\t */\n\t\t\t\tconn->state = CLOSING;\n\t\t\t}\n\t\t\tmem_deref(conn);\n\t\t\tbreak;\n\n\t\tcase WEBSOCK_CLOSE:\n\t\t\tif (conn->state == OPEN)\n\t\t\t\t(void)websock_send(conn, WEBSOCK_CLOSE, \"%b\",\n\t\t\t\t\t     mbuf_buf(mb), mbuf_get_left(mb));\n\t\t\tconn_close(conn, 0);\n\t\t\tmem_deref(mb);\n\t\t\treturn;\n\n\t\tcase WEBSOCK_PING:\n\t\t\t(void)websock_send(conn, WEBSOCK_PONG, \"%b\",\n\t\t\t\t\t   mbuf_buf(mb), mbuf_get_left(mb));\n\t\t\tbreak;\n\n\t\tcase WEBSOCK_PONG:\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tmem_deref(mb);\n\t\t\terr = EPROTO;\n\t\t\tgoto out;\n\t\t}\n\n\t\tmem_deref(mb);\n\t}\n\n out:\n\tif (err) {\n\t\t(void)websock_close(conn, websock_err2scode(err), NULL);\n\t\tconn_close(conn, err);\n\t}\n}\n\n\nstatic void close_handler(int err, void *arg)\n{\n\tstruct websock_conn *conn = arg;\n\n\tconn_close(conn, err);\n}\n\n\nstatic int accept_print(struct re_printf *pf, const struct pl *key)\n{\n\tuint8_t digest[SHA_DIGEST_LENGTH];\n\tuint8_t *data;\n\tsize_t len = key->l + sizeof(magic)-1;\n\n\tdata = mem_zalloc(len, NULL);\n\tif (!data)\n\t\treturn ENOMEM;\n\n\tmemcpy(data, key->p, key->l);\n\tmemcpy(data + key->l, magic, sizeof(magic)-1);\n\n\tsha1(data, len, digest);\n\tmem_deref(data);\n\n\treturn base64_print(pf, digest, sizeof(digest));\n}\n\n\nstatic void http_resp_handler(int err, const struct http_msg *msg, void *arg)\n{\n\tstruct websock_conn *conn = arg;\n\tconst struct http_hdr *hdr;\n\tstruct pl key;\n\tchar buf[32];\n\n\tif (err || msg->scode != 101)\n\t\tgoto fail;\n\n\tif (!http_msg_hdr_has_value(msg, HTTP_HDR_UPGRADE, \"websocket\"))\n\t\tgoto fail;\n\n\tif (!http_msg_hdr_has_value(msg, HTTP_HDR_CONNECTION, \"Upgrade\"))\n\t\tgoto fail;\n\n\thdr = http_msg_hdr(msg, HTTP_HDR_SEC_WEBSOCKET_ACCEPT);\n\tif (!hdr)\n\t\tgoto fail;\n\n\tkey.p = conn->nonce;\n\tkey.l = sizeof(conn->nonce);\n\n\tif (re_snprintf(buf, sizeof(buf), \"%H\", accept_print, &key) < 0)\n\t\tgoto fail;\n\n\tif (pl_strcmp(&hdr->val, buf))\n\t\tgoto fail;\n\n\t/* here we are ok */\n\n\tconn->state = OPEN;\n\n\tif (conn->kaint)\n\t\ttmr_start(&conn->tmr, conn->kaint, keepalive_handler, conn);\n\n\tconn->estabh(conn->arg);\n\treturn;\n\n fail:\n\tconn_close(conn, err ? err : EPROTO);\n}\n\n\nstatic void http_conn_handler(struct tcp_conn *tc, struct tls_conn *sc,\n\t\t\t      void *arg)\n{\n\tstruct websock_conn *conn = arg;\n\n\tconn->tc = mem_ref(tc);\n\tconn->sc = mem_ref(sc);\n\n\ttcp_set_handlers(conn->tc, NULL, recv_handler, close_handler, conn);\n}\n\n\nstatic int ws_connect(struct websock_conn **connp, const char *proto,\n\t\t\t  struct websock *sock, struct http_cli *cli,\n\t\t\t  const char *uri, unsigned kaint,\n\t\t\t  websock_estab_h *estabh, websock_recv_h *recvh,\n\t\t\t  websock_close_h *closeh, void *arg, const char *fmt,\n\t\t\t  va_list *ap)\n{\n\tstruct websock_conn *conn;\n\tuint8_t nonce[16];\n\tchar proto_hdr[64];\n\tsize_t len;\n\tint err, ret;\n\n\tif (!connp || !sock || !cli || !uri || !estabh || !recvh || !closeh)\n\t\treturn EINVAL;\n\n\tif (proto) {\n\t\tret = re_snprintf(proto_hdr, sizeof(proto_hdr),\n\t\t\t   \"Sec-WebSocket-Protocol: %s\\r\\n\", proto);\n\n\t\tif (ret == -1)\n\t\t\treturn EINVAL;\n\t}\n\n\tconn = mem_zalloc(sizeof(*conn), conn_destructor);\n\tif (!conn)\n\t\treturn ENOMEM;\n\n\t/* The nonce MUST be selected randomly for each connection */\n\trand_bytes(nonce, sizeof(nonce));\n\n\tlen = sizeof(conn->nonce);\n\n\terr = base64_encode(nonce, sizeof(nonce), conn->nonce, &len);\n\tif (err)\n\t\tgoto out;\n\n\tconn->sock   = mem_ref(sock);\n\tconn->kaint  = kaint;\n\tconn->estabh = estabh;\n\tconn->recvh  = recvh;\n\tconn->closeh = closeh;\n\tconn->arg    = arg;\n\tconn->state  = CONNECTING;\n\tconn->active = true;\n\n\t/* Protocol Handshake */\n\terr = http_request(&conn->req, cli, \"GET\", uri,\n\t\t\t   http_resp_handler, NULL, NULL, conn,\n\t\t\t   \"Upgrade: websocket\\r\\n\"\n\t\t\t   \"Connection: upgrade\\r\\n\"\n\t\t\t   \"Sec-WebSocket-Key: %b\\r\\n\"\n\t\t\t   \"Sec-WebSocket-Version: 13\\r\\n\"\n\t\t\t   \"%s\"\n\t\t\t   \"%v\"\n\t\t\t   \"\\r\\n\",\n\t\t\t   conn->nonce, sizeof(conn->nonce),\n\t\t\t   proto ? proto_hdr : \"\",\n\t\t\t   fmt, ap);\n\tif (err)\n\t\tgoto out;\n\n\thttp_req_set_conn_handler(conn->req, http_conn_handler);\n\n out:\n\tif (err)\n\t\tmem_deref(conn);\n\telse\n\t\t*connp = conn;\n\n\treturn err;\n}\n\n\nint websock_connect(struct websock_conn **connp, struct websock *sock,\n\t\t    struct http_cli *cli, const char *uri, unsigned kaint,\n\t\t    websock_estab_h *estabh, websock_recv_h *recvh,\n\t\t    websock_close_h *closeh, void *arg, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = ws_connect(connp, NULL, sock, cli, uri, kaint, estabh, recvh,\n\t\t\t closeh, arg, fmt, &ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\nint websock_connect_proto(struct websock_conn **connp, const char *proto,\n\t\t\t  struct websock *sock, struct http_cli *cli,\n\t\t\t  const char *uri, unsigned kaint,\n\t\t\t  websock_estab_h *estabh, websock_recv_h *recvh,\n\t\t\t  websock_close_h *closeh, void *arg, const char *fmt,\n\t\t\t  ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = ws_connect(connp, proto, sock, cli, uri, kaint, estabh, recvh,\n\t\t\t closeh, arg, fmt, &ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\nint websock_accept(struct websock_conn **connp, struct websock *sock,\n\t\t   struct http_conn *htconn, const struct http_msg *msg,\n\t\t   unsigned kaint, websock_recv_h *recvh,\n\t\t   websock_close_h *closeh, void *arg)\n{\n\treturn websock_accept_proto(connp, NULL, sock, htconn, msg, kaint,\n\t\t\t\t    recvh, closeh, arg);\n}\n\n\nint websock_accept_proto(struct websock_conn **connp, const char *proto,\n\t\t\t struct websock *sock, struct http_conn *htconn,\n\t\t\t const struct http_msg *msg, unsigned kaint,\n\t\t\t websock_recv_h *recvh, websock_close_h *closeh,\n\t\t\t void *arg)\n{\n\tconst struct http_hdr *key;\n\tstruct websock_conn *conn;\n\tchar proto_hdr[64];\n\tint err, ret;\n\n\tif (!connp || !sock || !htconn || !msg || !recvh || !closeh)\n\t\treturn EINVAL;\n\n\tif (proto) {\n\t\tret = re_snprintf(proto_hdr, sizeof(proto_hdr),\n\t\t\t   \"Sec-WebSocket-Protocol: %s\\r\\n\", proto);\n\n\t\tif (ret == -1)\n\t\t\treturn EINVAL;\n\t}\n\n\tif (!http_msg_hdr_has_value(msg, HTTP_HDR_UPGRADE, \"websocket\"))\n\t\treturn EBADMSG;\n\n\tif (!http_msg_hdr_has_value(msg, HTTP_HDR_CONNECTION, \"Upgrade\"))\n\t\treturn EBADMSG;\n\n\tif (!http_msg_hdr_has_value(msg, HTTP_HDR_SEC_WEBSOCKET_VERSION, \"13\"))\n\t\treturn EBADMSG;\n\n\tkey = http_msg_hdr(msg, HTTP_HDR_SEC_WEBSOCKET_KEY);\n\tif (!key)\n\t\treturn EBADMSG;\n\n\tconn = mem_zalloc(sizeof(*conn), conn_destructor);\n\tif (!conn)\n\t\treturn ENOMEM;\n\n\terr = http_reply(htconn, 101, \"Switching Protocols\",\n\t\t\t \"Upgrade: websocket\\r\\n\"\n\t\t\t \"Connection: Upgrade\\r\\n\"\n\t\t\t \"Sec-WebSocket-Accept: %H\\r\\n\"\n\t\t\t \"%s\"\n\t\t\t \"\\r\\n\",\n\t\t\t accept_print, &key->val,\n\t\t\t proto ? proto_hdr : \"\");\n\tif (err)\n\t\tgoto out;\n\n\tconn->sock   = mem_ref(sock);\n\tconn->tc     = mem_ref(http_conn_tcp(htconn));\n\tconn->sc     = mem_ref(http_conn_tls(htconn));\n\tconn->kaint  = kaint;\n\tconn->recvh  = recvh;\n\tconn->closeh = closeh;\n\tconn->arg    = arg;\n\tconn->state  = OPEN;\n\tconn->active = false;\n\n\ttcp_set_handlers(conn->tc, NULL, recv_handler, close_handler, conn);\n\thttp_conn_close(htconn);\n\n\tif (conn->kaint)\n\t\ttmr_start(&conn->tmr, conn->kaint, keepalive_handler, conn);\n\n out:\n\tif (err)\n\t\tmem_deref(conn);\n\telse\n\t\t*connp = conn;\n\n\treturn err;\n}\n\n\nstatic int websock_encode(struct mbuf *mb, bool fin,\n\t\t\t  enum websock_opcode opcode, bool mask, size_t len)\n{\n\tint err;\n\n\terr = mbuf_write_u8(mb, (fin<<7) | (opcode & 0x0f));\n\n\tif (len > 0xffff) {\n\t\terr |= mbuf_write_u8(mb, (mask<<7) | 127);\n\t\terr |= mbuf_write_u64(mb, sys_htonll(len));\n\t}\n\telse if (len > 125) {\n\t\terr |= mbuf_write_u8(mb, (mask<<7) | 126);\n\t\terr |= mbuf_write_u16(mb, htons((uint16_t)len));\n\t}\n\telse {\n\t\terr |= mbuf_write_u8(mb, (mask<<7) | (uint8_t)len);\n\t}\n\n\tif (mask) {\n\t\tuint8_t mkey[4];\n\t\tuint8_t *p;\n\t\tsize_t i;\n\n\t\trand_bytes(mkey, sizeof(mkey));\n\n\t\terr |= mbuf_write_mem(mb, mkey, sizeof(mkey));\n\n\t\tfor (i=0, p=mbuf_buf(mb); i<len; i++)\n\t\t\tp[i] = p[i] ^ mkey[i%4];\n\t}\n\n\treturn err;\n}\n\n\nstatic int websock_vsend(struct websock_conn *conn, enum websock_opcode opcode,\n\t\t\t enum websock_scode scode, const char *fmt, va_list ap)\n{\n\tconst size_t hsz = conn->active ? 14 : 10;\n\tsize_t len, start;\n\tstruct mbuf *mb;\n\tint err = 0;\n\n\tif (conn->state != OPEN)\n\t\treturn ENOTCONN;\n\n\tmb = mbuf_alloc(2048);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tmb->pos = hsz;\n\n\tif (scode)\n\t\terr |= mbuf_write_u16(mb, htons(scode));\n\tif (fmt)\n\t\terr |= mbuf_vprintf(mb, fmt, ap);\n\tif (err)\n\t\tgoto out;\n\n\tlen = mb->pos - hsz;\n\n\tif (len > 0xffff)\n\t\tstart = mb->pos = 0;\n\telse if (len > 125)\n\t\tstart = mb->pos = 6;\n\telse\n\t\tstart = mb->pos = 8;\n\n\terr = websock_encode(mb, true, opcode, conn->active, len);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = start;\n\n\terr = tcp_send(conn->tc, mb);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nint websock_send(struct websock_conn *conn, enum websock_opcode opcode,\n\t\t const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tva_start(ap, fmt);\n\terr = websock_vsend(conn, opcode, 0, fmt, ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\nint websock_close(struct websock_conn *conn, enum websock_scode scode,\n\t\t  const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tif (!conn)\n\t\treturn EINVAL;\n\n\tif (!scode)\n\t\tfmt = NULL;\n\n\tva_start(ap, fmt);\n\terr = websock_vsend(conn, WEBSOCK_CLOSE, scode, fmt, ap);\n\tva_end(ap);\n\n\tif (!err)\n\t\tconn->state = CLOSING;\n\n\treturn err;\n}\n\n\nstruct tcp_conn *websock_tcp(const struct websock_conn *conn)\n{\n\treturn conn ? conn->tc : NULL;\n}\n\n\nint websock_alloc(struct websock **sockp, websock_shutdown_h *shuth, void *arg)\n{\n\tstruct websock *sock;\n\n\tif (!sockp)\n\t\treturn EINVAL;\n\n\tsock = mem_zalloc(sizeof(*sock), sock_destructor);\n\tif (!sock)\n\t\treturn ENOMEM;\n\n\tsock->shuth = shuth;\n\tsock->arg   = arg;\n\n\t*sockp = sock;\n\n\treturn 0;\n}\n\n\nvoid websock_shutdown(struct websock *sock)\n{\n\tif (!sock || sock->shutdown)\n\t\treturn;\n\n\tsock->shutdown = true;\n\tmem_deref(sock);\n}\n"
  },
  {
    "path": "test/CMakeLists.txt",
    "content": "#\n# CMakeLists.txt\n#\n# Copyright (C) 2010 - 2022 Alfred E. Heggestad\n#\n\n##############################################################################\n#\n# Versioning\n#\n\nproject(retest C)\n\nlist(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)\n\n\n##############################################################################\n#\n# Module/Package Includes\n#\n\n\n##############################################################################\n#\n# Compile options/definitions\n#\n\noption(USE_SANITIZER \"Sanitizers like: address, thread, undefined, memory\")\ninclude(sanitizer)\n\nset(CMAKE_EXPORT_COMPILE_COMMANDS ON)\nset(CMAKE_BUILD_TYPE Debug)\nset(CMAKE_CXX_STANDARD 11)\n\nif(MSVC)\n  add_compile_options(\"/W3\")\nelse()\n\n  set(c_flags\n    -Wall\n    -Wbad-function-cast\n    -Wcast-align\n    -Wextra\n    -Wmissing-declarations\n    -Wmissing-prototypes\n    -Wnested-externs\n    -Wno-strict-aliasing\n    -Wold-style-definition\n    -Wshadow\n    -Wstrict-prototypes\n    -Wuninitialized\n    -Wvla\n  )\n\n  add_compile_options(\n    \"$<$<COMPILE_LANGUAGE:C>:${c_flags}>\"\n  )\nendif()\n\nif(CMAKE_C_COMPILER_ID MATCHES \"Clang\")\n  add_compile_options(\"$<$<COMPILE_LANGUAGE:C>:-Wshorten-64-to-32>\")\nendif()\n\ninclude_directories(\n  .\n)\n\nfind_package(re CONFIG REQUIRED HINTS ../cmake)\n\n##############################################################################\n#\n# Source/Header section\n#\n\nset(SRCS\n  aac.c\n  aes.c\n  async.c\n  au.c\n  aubuf.c\n  aulength.c\n  aulevel.c\n  aupos.c\n  auresamp.c\n  av1.c\n  base64.c\n  bfcp.c\n  btrace.c\n  conf.c\n  convert.c\n  crc32.c\n  dbg.c\n  dd.c\n  dns.c\n  dsp.c\n  dtmf.c\n  fir.c\n  fmt.c\n  g711.c\n  h264.c\n  h265.c\n  hash.c\n  hmac.c\n  http.c\n  httpauth.c\n  ice.c\n  json.c\n  list.c\n  main.c\n  mbuf.c\n  md5.c\n  mem.c\n  mem_pool.c\n  mock/dnssrv.c\n  mock/nat.c\n  mock/sipsrv.c\n  mock/stunsrv.c\n  mock/turnsrv.c\n  mqueue.c\n  net.c\n  odict.c\n  pcp.c\n  remain.c\n  rtcp.c\n  rtmp.c\n  rtp.c\n  rtpext.c\n  sa.c\n  sdp.c\n  sha.c\n  sip.c\n  sipauth.c\n  sipevent.c\n  sipreg.c\n  sipsess.c\n  srtp.c\n  stun.c\n  sys.c\n  tcp.c\n  telev.c\n  test.c\n  thread.c\n  tmr.c\n  trace.c\n  trice.c\n  turn.c\n  types.c\n  udp.c\n  unixsock.c\n  uri.c\n  vid.c\n  vidconv.c\n  websock.c\n)\n\nif(USE_OPENSSL)\n  list(APPEND SRCS\n    tls.c\n    dtls.c\n    combo/dtls_turn.c\n    mock/cert.c\n  )\nendif()\n\nlist(APPEND SRCS cplusplus.cpp)\n\n\n##############################################################################\n#\n# Main target object\n#\n\nset(LINKLIBS re ${OPENSSL_LIBRARIES})\nif(WIN32)\n  list(APPEND LINKLIBS qwave iphlpapi wsock32 ws2_32 crypt32)\nelse()\n  list(APPEND LINKLIBS m ${RESOLV_LIBRARY} stdc++)\nendif()\n\nif(ZLIB_FOUND)\n  list(APPEND LINKLIBS ZLIB::ZLIB)\nendif()\n\nadd_executable(${PROJECT_NAME} ${SRCS})\nset_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS 1)\n\ntarget_link_libraries(${PROJECT_NAME} PRIVATE ${LINKLIBS})\ntarget_compile_definitions(${PROJECT_NAME} PRIVATE ${RE_DEFINITIONS})\n\nif(USE_OPENSSL)\n  target_include_directories(${PROJECT_NAME} PRIVATE ${OPENSSL_INCLUDE_DIR})\nendif()\n"
  },
  {
    "path": "test/aac.c",
    "content": "/**\n * @file aac.c AAC (Advanced Audio Coding) Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"aactest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_aac(void)\n{\n\tstatic const uint8_t buf[2] = {0x12, 0x10};\n\tstruct aac_header hdr;\n\tint err;\n\n\terr = aac_header_decode(&hdr, buf, sizeof(buf));\n\tif (err)\n\t\treturn err;\n\n\tTEST_EQUALS(44100, hdr.sample_rate);\n\tTEST_EQUALS(2,     hdr.channels);\n\tTEST_EQUALS(1024,  hdr.frame_size);\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/aes.c",
    "content": "/**\n * @file aes.c AES (Advanced Encryption Standard) Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"aestest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/*\n * http://www.inconteam.com/software-development/41-encryption/\n *  55-aes-test-vectors#aes-crt\n *\n * AES CTR 128-bit encryption mode\n */\nstatic int test_aes_ctr_loop(void)\n{\n\tconst char *init_vec_str = \"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff\";\n\tuint8_t encr_key[16];\n\tuint8_t iv_enc[AES_BLOCK_SIZE];\n\tuint8_t iv_dec[AES_BLOCK_SIZE];\n\tsize_t i;\n\tint err = 0;\n\tstruct aes *enc = NULL, *dec = NULL;\n\n\tstatic const struct {\n\t\tchar test_str[33];\n\t\tchar *ciph_str;\n\t} testv[] = {\n\n\t\t{\"6bc1bee22e409f96e93d7e117393172a\",\n\t\t \"874d6191b620e3261bef6864990db6ce\"},\n\n\t\t{\"ae2d8a571e03ac9c9eb76fac45af8e51\",\n\t\t \"9806f66b7970fdff8617187bb9fffdff\"},\n\n\t\t{\"30c81c46a35ce411e5fbc1191a0a52ef\",\n\t\t \"5ae4df3edbd5d35e5b4f09020db03eab\"},\n\n\t\t{\"f69f2445df4f9b17ad2b417be66c3710\",\n\t\t \"1e031dda2fbe03d1792170a0f3009cee\"},\n\t};\n\n\terr |= str_hex(encr_key, sizeof(encr_key),\n\t\t       \"2b7e151628aed2a6abf7158809cf4f3c\");\n\terr |= str_hex(iv_enc, sizeof(iv_enc), init_vec_str);\n\terr |= str_hex(iv_dec, sizeof(iv_dec), init_vec_str);\n\tif (err)\n\t\treturn err;\n\n\terr  = aes_alloc(&enc, AES_MODE_CTR, encr_key, 128, iv_enc);\n\terr |= aes_alloc(&dec, AES_MODE_CTR, encr_key, 128, iv_dec);\n\tif (err)\n\t\tgoto out;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tuint8_t test_vector[16];\n\t\tuint8_t cipher_text[16];\n\t\tuint8_t out[16];\n\t\tuint8_t clear[16];\n\n\t\terr |= str_hex(test_vector, sizeof(test_vector),\n\t\t\t       testv[i].test_str);\n\t\terr |= str_hex(cipher_text, sizeof(cipher_text),\n\t\t\t       testv[i].ciph_str);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = aes_encr(enc, out, test_vector, 16);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_MEMCMP(cipher_text, sizeof(cipher_text),\n\t\t\t    out, sizeof(out));\n\n\t\terr = aes_decr(dec, clear, out, 16);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_MEMCMP(test_vector, sizeof(test_vector),\n\t\t\t    clear, sizeof(clear));\n\t}\n\n out:\n\tmem_deref(enc);\n\tmem_deref(dec);\n\n\treturn err;\n}\n\n\nstatic bool have_aes(enum aes_mode mode)\n{\n\tstatic const uint8_t nullkey[AES_BLOCK_SIZE];\n\tstruct aes *aes = NULL;\n\tint err;\n\n\terr = aes_alloc(&aes, mode, nullkey, 128, NULL);\n\n\tmem_deref(aes);\n\n\treturn err != ENOSYS;\n}\n\n\nint test_aes(void)\n{\n\tint err;\n\n\tif (!have_aes(AES_MODE_CTR)) {\n\t\t(void)re_printf(\"skipping aes ctr test\\n\");\n\t\treturn ESKIPPED;\n\t}\n\n\terr = test_aes_ctr_loop();\n\tTEST_ERR(err);\n\n\tstatic const uint8_t nullkey[AES_BLOCK_SIZE];\n\tstruct aes *aes = NULL;\n\tint lerr;\n\n\tlerr = aes_alloc(NULL, AES_MODE_CTR, nullkey, 128, NULL);\n\tASSERT_TRUE(lerr != 0);\n\n\tlerr = aes_alloc(&aes, AES_MODE_CTR, nullkey, 9999, NULL);\n\tASSERT_TRUE(lerr != 0);\n\n\tlerr = aes_alloc(&aes, AES_MODE_GCM, nullkey, 9999, NULL);\n\tASSERT_TRUE(lerr != 0);\n\n\tlerr = aes_alloc(&aes, (enum aes_mode)42, nullkey, 128, NULL);\n\tASSERT_TRUE(lerr != 0);\n\n\tlerr = aes_encr(NULL, NULL, NULL, 0);\n\tASSERT_TRUE(lerr != 0);\n\n\tlerr = aes_decr(NULL, NULL, NULL, 0);\n\tASSERT_TRUE(lerr != 0);\n\n\taes_set_iv(NULL, NULL);\n\n\tlerr = aes_get_authtag(NULL, NULL, 0);\n\tASSERT_TRUE(lerr != 0);\n\n\tlerr = aes_authenticate(NULL, NULL, 0);\n\tASSERT_TRUE(lerr != 0);\n\nout:\n\treturn err;\n}\n\n\n#define KEY_LEN 32    /* 256 bits */\n#define IV_LEN  12    /*  96 bits */\n#define MSG_LEN 16    /* 128 bits */\n#define AAD_LEN 16    /* 128 bits */\n#define TAG_LEN 16    /* 128 bits */\n\n\n/**\n * Testcases for AES GCM (Galois Counter Mode)\n *\n * NOTE: only 96-bits IV for now\n */\nint test_aes_gcm(void)\n{\n\tstruct aes *enc = NULL, *dec = NULL;\n\tsize_t i;\n\tint err = 0;\n\n\tstatic const struct test {\n\t\tconst char *encr_key_str;\n\t\tconst char *iv_str;\n\t\tconst char *plain_str;         /* optional */\n\t\tconst char *aad_str;           /* optional */\n\t\tconst char *ciph_str;          /* optional */\n\t\tconst char *tag_str;\n\t\tbool success;\n\t} testv[] = {\n\n\t\t/* no payload*/\n\t\t{\"b52c505a37d78eda5dd34f20c22540ea\"\n\t\t \"1b58963cf8e5bf8ffa85f9f2492505b4\",\n\n\t\t \"516c33929df5a3284ff463d7\",\n\t\t NULL,\n\t\t NULL,\n\t\t NULL,\n\t\t \"bdc1ac884d332457a1d2664f168c76f0\",\n\t\t true\n\t\t},\n\n\t\t/* no AAD */\n\t\t{\"31bdadd96698c204aa9ce1448ea94ae1\"\n\t\t \"fb4a9a0b3c9d773b51bb1822666b8f22\",\n\n\t\t \"0d18e06c7c725ac9e362e1ce\",\n\n\t\t \"2db5168e932556f8089a0622981d017d\",\n\n\t\t \"\",\n\n\t\t \"fa4362189661d163fcd6a56d8bf0405a\",\n\n\t\t \"d636ac1bbedd5cc3ee727dc2ab4a9489\",\n\n\t\t true\n\t\t},\n\n\t\t/* with AAD */\n\t\t{\"92e11dcdaa866f5ce790fd24501f9250\"\n\t\t \"9aacf4cb8b1339d50c9c1240935dd08b\",\n\n\t\t \"ac93a1a6145299bde902f21a\",\n\n\t\t \"2d71bcfa914e4ac045b2aa60955fad24\",\n\n\t\t \"1e0889016f67601c8ebea4943bc23ad6\",\n\n\t\t \"8995ae2e6df3dbf96fac7b7137bae67f\",\n\n\t\t \"eca5aa77d51d4a0a14d9c51e1da474ab\",\n\t\t true\n\t\t},\n\n\t\t/* from openssl code */\n\t\t{\"eebc1f57487f51921c0465665f8ae6d1\"\n\t\t \"658bb26de6f8a069a3520293a572078f\",\n\n\t\t \"99aa3e68ed8173a0eed06684\",\n\n\t\t \"f56e87055bc32d0eeb31b2eacc2bf2a5\",\n\n\t\t \"4d23c3cec334b49bdb370c437fec78de\",\n\n\t\t \"f7264413a84c0e7cd536867eb9f21736\",\n\n\t\t \"67ba0510262ae487d737ee6298f77e0c\",\n\t\t true\n\t\t},\n\n\t\t/* authentication failure */\n\t\t{\"92e11dcdaa866f5ce790fd24501f9250\"\n\t\t \"9aacf4cb8b1339d50c9c1240935dd08b\",\n\n\t\t \"ac93a1a6145299bde902f21a\",\n\n\t\t \"2d71bcfa914e4ac045b2aa60955fad24\",\n\n\t\t \"1e0889016f67601c8ebea4943bc23ad6\",\n\n\t\t \"8995ae2e6df3dbf96fac7b7137bae67f\",\n\n\t\t \"eca5aa77d51d4a0a14d9c51e1da47400\", /* wrong */\n\t\t false\n\t\t},\n\t};\n\n\tif (!have_aes(AES_MODE_GCM)) {\n\t\t(void)re_printf(\"skipping aes gcm test\\n\");\n\t\treturn ESKIPPED;\n\t}\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tconst struct test *test = &testv[i];\n\t\tuint8_t test_vector[MSG_LEN] = {0};\n\t\tuint8_t cipher_text[MSG_LEN] = {0};\n\t\tuint8_t tag_ref[TAG_LEN];\n\t\tuint8_t out[MSG_LEN];\n\t\tuint8_t clear[MSG_LEN];\n\t\tuint8_t tag[TAG_LEN];\n\t\tsize_t tagsz = sizeof(tag);\n\t\tuint8_t iv[IV_LEN];\n\t\tuint8_t encr_key[KEY_LEN];\n\t\tconst size_t key_bits = KEY_LEN * 8;\n\t\tuint8_t aad[AAD_LEN];\n\t\tsize_t clen;\n\t\tint e;\n\n\t\terr = str_hex(encr_key, sizeof(encr_key), test->encr_key_str);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"could not set key\\n\");\n\t\t\tbreak;\n\t\t}\n\n\t\terr |= str_hex(iv, sizeof(iv), test->iv_str);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"could not set IV\\n\");\n\t\t\treturn err;\n\t\t}\n\n\t\terr = aes_alloc(&enc, AES_MODE_GCM, encr_key, key_bits, iv);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (str_isset(test->aad_str)) {\n\n\t\t\terr = str_hex(aad, sizeof(aad), test->aad_str);\n\t\t\tif (err) {\n\t\t\t\tDEBUG_WARNING(\"could not set aad\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (str_isset(test->plain_str)) {\n\t\t\terr |= str_hex(test_vector, sizeof(test_vector),\n\t\t\t\t       test->plain_str);\n\t\t\tclen = sizeof(test_vector);\n\t\t}\n\t\telse {\n\t\t\tclen = 0;\n\t\t}\n\n\t\tif (str_isset(test->ciph_str)) {\n\t\t\terr |= str_hex(cipher_text, sizeof(cipher_text),\n\t\t\t\t       test->ciph_str);\n\t\t\tif (err) {\n\t\t\t\tDEBUG_WARNING(\"str_hex error\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\terr |= str_hex(tag_ref, sizeof(tag_ref), testv[i].tag_str);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"tag size mismatch\\n\");\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Encrypt */\n\t\tif (str_isset(test->aad_str)) {\n\t\t\terr = aes_encr(enc, NULL, aad, sizeof(aad));\n\t\t\tTEST_ERR(err);\n\t\t}\n\t\tif (clen) {\n\t\t\terr = aes_encr(enc, out, test_vector, clen);\n\t\t\tTEST_ERR(err);\n\n\t\t\tTEST_MEMCMP(cipher_text, sizeof(cipher_text),\n\t\t\t\t    out, sizeof(out));\n\t\t}\n\n\t\terr = aes_get_authtag(enc, tag, tagsz);\n\t\tTEST_ERR(err);\n\n\t\tif (test->success) {\n\t\t\tTEST_MEMCMP(tag_ref, sizeof(tag_ref), tag, tagsz);\n\t\t}\n\t\tenc = mem_deref(enc);\n\n\t\t/* Decrypt */\n\t\terr = aes_alloc(&dec, AES_MODE_GCM, encr_key, key_bits, iv);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (str_isset(test->aad_str)) {\n\t\t\terr = aes_decr(dec, NULL, aad, sizeof(aad));\n\t\t\tTEST_ERR(err);\n\t\t}\n\n\t\terr = aes_decr(dec, clear, out, clen);\n\t\tTEST_ERR(err);\n\n\t\te = aes_authenticate(dec, tag_ref, tagsz);\n\t\tif (test->success) {\n\t\t\tif (e) {\n\t\t\t\terr = e;\n\t\t\t\tDEBUG_WARNING(\"aes_authenticate error\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (clen) {\n\t\t\t\tTEST_MEMCMP(test_vector, sizeof(test_vector),\n\t\t\t\t\t    clear, sizeof(clear));\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tTEST_EQUALS(EAUTH, e);\n\t\t}\n\n\t\tdec = mem_deref(dec);\n\t}\n\n out:\n\tmem_deref(enc);\n\tmem_deref(dec);\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/async.c",
    "content": "/**\n * @file async.c Testcode for re async\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n#define _BSD_SOURCE 1\n#define _DEFAULT_SOURCE 1\n\n#ifndef WIN32\n#include <netdb.h>\n#endif\n\n#include <string.h>\n#include <stdlib.h>\n#include <re.h>\n#include \"test.h\"\n\n#define DEBUG_MODULE \"async\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nstruct test_cnt {\n\tint tests;\n\tint done;\n};\n\nstruct test {\n\tchar domain[128];\n\tstruct sa sa;\n\tint err;\n\tint err_expected;\n\tstruct test_cnt *cnt;\n};\n\nstatic int blocking_getaddr(void *arg)\n{\n\tint err;\n\tstruct test *test    = arg;\n\tstruct addrinfo *res = NULL;\n\tstruct addrinfo hints;\n\n\tmemset(&hints, 0, sizeof(hints));\n\thints.ai_family = AF_INET;\n#ifndef __ANDROID__\n\thints.ai_flags\t= AI_V4MAPPED;\n#endif\n\n\n\t/* Blocking */\n\terr = getaddrinfo(test->domain, NULL, &hints, &res);\n\tif (err)\n\t\treturn EADDRNOTAVAIL;\n\n\tsa_set_sa(&test->sa, res->ai_addr);\n\tfreeaddrinfo(res);\n\n\treturn 0;\n}\n\n\nstatic void completed(int err, void *arg)\n{\n\tstruct test *test = arg;\n\tstruct sa sa;\n\n\tif (err)\n\t\tgoto out;\n\n\terr = re_thread_check(false);\n\tTEST_ERR(err);\n\n\tsa_set_str(&sa, \"127.0.0.1\", 0);\n\tif (!sa_cmp(&sa, &test->sa, SA_ADDR))\n\t\terr = EINVAL;\n\n\tTEST_ERR(err);\n\nout:\n\ttest->err = err;\n\tif (++test->cnt->done >= test->cnt->tests)\n\t\tre_cancel();\n}\n\n\nstatic int test_re_thread_async(void)\n{\n\tint err;\n\n\tstruct test_cnt cnt = {0, 0};\n\n\tstruct test testv[] = {\n\t\t{\"localhost\", {.len = 0}, -1, 0, &cnt},\n\t\t{\"test.notfound\", {.len = 0}, -1, EADDRNOTAVAIL, &cnt}};\n\n\tcnt.tests = RE_ARRAY_SIZE(testv);\n\n\terr = re_thread_async_init(2);\n\tTEST_ERR(err);\n\n\tfor (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) {\n\t\terr = re_thread_async(blocking_getaddr, completed, &testv[i]);\n\t\tTEST_ERR(err);\n\t}\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\tfor (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) {\n\t\tTEST_EQUALS(testv[i].err_expected, testv[i].err);\n\t}\n\nout:\n\tre_thread_async_close();\n\treturn err;\n}\n\n\nstatic void never_callback(int err, void *arg)\n{\n\t(void)err;\n\t(void)arg;\n\n\tDEBUG_WARNING(\"async: never_callback called!\\n\");\n\tabort();\n}\n\n\nstatic void timer_cancel(void *arg)\n{\n\t(void)arg;\n\n\tre_cancel();\n}\n\n\nstatic int test_re_thread_async_cancel(void)\n{\n\tint err;\n\tstruct tmr tmr;\n\n\terr = re_thread_async_init(2);\n\tTEST_ERR(err);\n\n\terr = re_thread_async_id(1, NULL, never_callback, NULL);\n\tTEST_ERR(err);\n\n\tre_thread_async_cancel(1);\n\n\ttmr_init(&tmr);\n\ttmr_start(&tmr, 0, timer_cancel, NULL);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\nout:\n\tre_thread_async_close();\n\treturn err;\n}\n\n\nint test_async(void)\n{\n\tint err;\n\n\terr = test_re_thread_async();\n\tTEST_ERR(err);\n\n\terr = test_re_thread_async_cancel();\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/au.c",
    "content": "/**\n * @file au.c Audio testcode\n *\n * Copyright (C) 2024 Alfred E. Heggestad\n */\n\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"au\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_au(void)\n{\n\tint err = 0;\n\n\tuint32_t nsamp = au_calc_nsamp(8000, 1, 20);\n\tASSERT_EQ(160, nsamp);\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/aubuf.c",
    "content": "/**\n * @file aubuf.c Audio-buffer Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_aubuf\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n#define AUDIO_TIMEBASE 1000000U\n\nenum {\n\tFRAMES = 80,\n};\n\n\nstatic int test_aubuf_raw(void)\n{\n\tstruct aubuf *ab = NULL;\n\tint16_t sampv_in[2 * FRAMES];\n\tint16_t sampv_out[2 * FRAMES];\n\tstruct mbuf *mb;\n\tunsigned i;\n\tint err;\n\n\tmb = mbuf_alloc(FRAMES * sizeof(int16_t));\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(sampv_in); i++)\n\t\tsampv_in[i] = i;\n\tmemset(sampv_out, 0, sizeof(sampv_out));\n\n\terr = aubuf_alloc(&ab, 4 * FRAMES, 0);\n\tTEST_ERR(err);\n\tTEST_EQUALS(0, aubuf_cur_size(ab));\n\n\terr = aubuf_write(ab, (uint8_t *)sampv_in, FRAMES * sizeof(int16_t));\n\tTEST_ERR(err);\n\tTEST_EQUALS(2 * FRAMES, aubuf_cur_size(ab));\n\n\t(void)mbuf_write_mem(mb, (uint8_t *)&sampv_in[FRAMES],\n\t\t\t     FRAMES * sizeof(int16_t));\n\tmb->pos = 0;\n\n\terr = aubuf_append(ab, mb);\n\tTEST_ERR(err);\n\tTEST_EQUALS(4 * FRAMES, aubuf_cur_size(ab));\n\n\tmemset(sampv_out, 0, sizeof(sampv_out));\n\taubuf_read(ab, (uint8_t *)sampv_out, 2 * FRAMES * sizeof(int16_t));\n\tTEST_MEMCMP(sampv_in, sizeof(sampv_in), sampv_out, sizeof(sampv_out));\n\tTEST_EQUALS(0, aubuf_cur_size(ab));\n\n out:\n\tmem_deref(ab);\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstatic int test_aubuf_samp(void)\n{\n\tstruct aubuf *ab = NULL;\n\tint16_t sampv_in[2 * FRAMES];\n\tint16_t sampv_out[2 * FRAMES];\n\tunsigned i;\n\tint err;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(sampv_in); i++)\n\t\tsampv_in[i] = i;\n\tmemset(sampv_out, 0, sizeof(sampv_out));\n\n\terr = aubuf_alloc(&ab, 4 * FRAMES, 0);\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(0, aubuf_cur_size(ab));\n\n\terr |= aubuf_write_samp(ab,  sampv_in, FRAMES);\n\terr |= aubuf_write_samp(ab, &sampv_in[FRAMES], FRAMES);\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(4 * FRAMES, aubuf_cur_size(ab));\n\n\taubuf_read_samp(ab, sampv_out, RE_ARRAY_SIZE(sampv_out));\n\tTEST_MEMCMP(sampv_in, sizeof(sampv_in), sampv_out, sizeof(sampv_out));\n\tTEST_EQUALS(0, aubuf_cur_size(ab));\n\n out:\n\tmem_deref(ab);\n\treturn err;\n}\n\n\nstatic int test_aubuf_auframe(void)\n{\n\tstruct aubuf *ab = NULL;\n\tfloat sampv_in[3 * FRAMES + (FRAMES / 2)];\n\tfloat sampv_out[3 * FRAMES + (FRAMES / 2)];\n\tuint64_t dt;\n\n\tstruct auframe af;\n\tstruct auframe af_in;\n\tstruct auframe af_out;\n\tint err;\n\n\tfor (unsigned i = 0; i < RE_ARRAY_SIZE(sampv_in); i++)\n\t\tsampv_in[i] = (float)i;\n\tmemset(sampv_out, 0, sizeof(sampv_out));\n\n\terr = aubuf_alloc(&ab, FRAMES * sizeof(float),\n\t\t\t  4 * FRAMES * sizeof(float));\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(0, aubuf_cur_size(ab));\n\n\t/* write first frame (filling with wish_sz) */\n\tauframe_init(&af, AUFMT_FLOAT, sampv_in, FRAMES, 48000, 2);\n\taf_in = af;\n\n\tdt = FRAMES * AUDIO_TIMEBASE / (af_in.srate * af_in.ch);\n\n\terr = aubuf_write_auframe(ab, &af_in);\n\tTEST_ERR(err);\n\tTEST_EQUALS(FRAMES * sizeof(float), aubuf_cur_size(ab));\n\tTEST_EQUALS(auframe_size(&af), aubuf_cur_size(ab));\n\n\t/* first read after filling should start aubuf */\n\taf_out.fmt   = AUFMT_FLOAT;\n\taf_out.sampv = sampv_out;\n\taf_out.sampc = FRAMES;\n\taubuf_read_auframe(ab, &af_out);\n\tTEST_EQUALS(0, aubuf_cur_size(ab));\n\tTEST_EQUALS(0, af_out.timestamp);\n\n\t/* write one frame */\n\taf_in.sampv\t= &sampv_in[FRAMES];\n\taf_in.sampc\t= FRAMES;\n\taf_in.timestamp = dt;\n\n\terr = aubuf_write_auframe(ab, &af_in);\n\tTEST_ERR(err);\n\tTEST_EQUALS(auframe_size(&af), aubuf_cur_size(ab));\n\n\t/* read half frame */\n\taf_out.sampc = FRAMES / 2;\n\taf_out.sampv = &sampv_out[FRAMES];\n\taubuf_read_auframe(ab, &af_out);\n\n\t/* the first read drops old data: 80 - 40 = 40 */\n\tTEST_EQUALS(auframe_size(&af)/2, aubuf_cur_size(ab));\n\tTEST_EQUALS(dt, af_out.timestamp);\n\n\t/* write one frame */\n\taf_in.sampv\t= &sampv_in[2 * FRAMES];\n\taf_in.sampc\t= FRAMES;\n\taf_in.timestamp = 2 * dt;\n\n\terr = aubuf_write_auframe(ab, &af_in);\n\tTEST_ERR(err);\n\tTEST_EQUALS(auframe_size(&af) * 3 / 2, aubuf_cur_size(ab));\n\n\t/* write half frame */\n\taf_in.sampv\t= &sampv_in[3 * FRAMES];\n\taf_in.sampc\t= FRAMES / 2;\n\taf_in.timestamp = 3 * dt;\n\n\terr = aubuf_write_auframe(ab, &af_in);\n\tTEST_ERR(err);\n\tTEST_EQUALS(auframe_size(&af) * 2, aubuf_cur_size(ab));\n\n\t/* read half frame */\n\taf_out.sampv = &sampv_out[(FRAMES + (FRAMES / 2))];\n\taf_out.sampc = FRAMES / 2;\n\taubuf_read_auframe(ab, &af_out);\n\tTEST_EQUALS(auframe_size(&af) * 3 / 2, aubuf_cur_size(ab));\n\tTEST_EQUALS(3 * (dt / 2) + 1, af_out.timestamp);\n\n\t/* read one and a half frame */\n\taf_out.sampv = &sampv_out[2 * FRAMES];\n\taf_out.sampc = FRAMES + (FRAMES / 2);\n\taubuf_read_auframe(ab, &af_out);\n\n\tTEST_EQUALS(2, af_out.ch);\n\tTEST_EQUALS(48000, af_out.srate);\n\tTEST_EQUALS(2 * dt, af_out.timestamp);\n\n\tTEST_MEMCMP(sampv_in, sizeof(sampv_in), sampv_out, sizeof(sampv_out));\n\tTEST_EQUALS(0, aubuf_cur_size(ab));\n\n\t/* test automatic timestamps */\n\tmem_deref(ab);\n\terr = aubuf_alloc(&ab, 0, 0);\n\tTEST_ERR(err);\n\n\tdt = 24 * AUDIO_TIMEBASE / (af_in.srate * af_in.ch);\n\tauframe_init(&af_in,  AUFMT_FLOAT, sampv_in,  24, 48000, 2);\n\tauframe_init(&af_out, AUFMT_FLOAT, sampv_out, 24, 48000, 2);\n\n\taf_in.timestamp = 0;\n\n\terr |= aubuf_write_auframe(ab, &af_in);\n\terr |= aubuf_write_auframe(ab, &af_in);\n\terr |= aubuf_write_auframe(ab, &af_in);\n\terr |= aubuf_write_auframe(ab, &af_in);\n\tTEST_ERR(err);\n\n\taubuf_read_auframe(ab, &af_out);\n\tTEST_EQUALS(0, af_out.timestamp);\n\n\taubuf_read_auframe(ab, &af_out);\n\tTEST_EQUALS(dt, af_out.timestamp);\n\n\taf_out.sampc = 12;\n\taubuf_read_auframe(ab, &af_out);\n\tTEST_EQUALS(2*dt, af_out.timestamp);\n\n\taubuf_read_auframe(ab, &af_out);\n\tTEST_EQUALS(2*dt + dt/2, af_out.timestamp);\n\n\taubuf_read_auframe(ab, &af_out);\n\tTEST_EQUALS(3*dt, af_out.timestamp);\n\n out:\n\tmem_deref(ab);\n\treturn err;\n}\n\n\nstatic int test_aubuf_sort_auframe(void)\n{\n\tint err;\n\tstruct aubuf *ab = NULL;\n\tint16_t sampv_in[2 * FRAMES];\n\tint16_t sampv_out[2 * FRAMES];\n\tstruct auframe af[3] = {\n\t\t{\n\t\t .fmt\t    = AUFMT_S16LE,\n\t\t .sampv\t    = sampv_in,\n\t\t .sampc\t    = 2 * FRAMES,\n\t\t .timestamp = 1\n\t\t},\n\t\t{\n\t\t .fmt\t    = AUFMT_S16LE,\n\t\t .sampv\t    = sampv_in,\n\t\t .sampc\t    = 2 * FRAMES,\n\t\t .timestamp = 2\n\t\t},\n\t\t{\n\t\t .fmt\t    = AUFMT_S16LE,\n\t\t .sampv\t    = sampv_in,\n\t\t .sampc\t    = 2 * FRAMES,\n\t\t .timestamp = 3\n\t\t},\n\t};\n\tstruct auframe af_out = {\n\t\t .fmt\t    = AUFMT_S16LE,\n\t\t .sampv\t    = sampv_out,\n\t\t .sampc\t    = 2 * FRAMES,\n\t\t .timestamp = 0\n\t};\n\n\terr = aubuf_alloc(&ab, 3 * sizeof(sampv_in), 0);\n\tTEST_ERR(err);\n\n\t/* Write auframes disordered */\n\terr = aubuf_write_auframe(ab, &af[0]);\n\tTEST_ERR(err);\n\n\terr = aubuf_write_auframe(ab, &af[2]);\n\tTEST_ERR(err);\n\n\terr = aubuf_write_auframe(ab, &af[1]);\n\tTEST_ERR(err);\n\n\t/* Check sorted */\n\taubuf_read_auframe(ab, &af_out);\n\tTEST_EQUALS(1, af_out.timestamp);\n\n\taubuf_read_auframe(ab, &af_out);\n\tTEST_EQUALS(2, af_out.timestamp);\n\n\taubuf_read_auframe(ab, &af_out);\n\tTEST_EQUALS(3, af_out.timestamp);\n\n\t/* Test zero af.timestamp */\n\terr = aubuf_write_samp(ab, sampv_in, FRAMES);\n\terr |= aubuf_write_samp(ab, sampv_in, FRAMES);\n\terr |= aubuf_write_samp(ab, sampv_in, 2 * FRAMES);\n\tTEST_ERR(err);\n\n\t/* Sort - test not stuck */\n\taubuf_sort_auframe(ab);\n\tTEST_EQUALS(8 * FRAMES, aubuf_cur_size(ab));\n\nout:\n\tmem_deref(ab);\n\treturn err;\n}\n\n\nstatic int test_aubuf_resize(void)\n{\n\tstruct aubuf *ab      = NULL;\n\tint16_t sampv_in[2 * FRAMES] = {1};\n\tint16_t sampv_out[2 * FRAMES];\n\tstruct auframe af_out = {\n\t\t .fmt\t    = AUFMT_S16LE,\n\t\t .sampv\t    = sampv_out,\n\t\t .sampc\t    = FRAMES,\n\t\t .timestamp = 0\n\t};\n\tint err;\n\n\terr = aubuf_alloc(&ab, 2 * FRAMES, 2 * FRAMES);\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(0, aubuf_cur_size(ab));\n\n\terr = aubuf_write_samp(ab, sampv_in, FRAMES);\n\tTEST_ERR(err);\n\n\terr = aubuf_write_samp(ab, sampv_in, FRAMES);\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(2 * FRAMES, aubuf_cur_size(ab));\n\n\terr = aubuf_resize(ab, 2 * FRAMES, 4 * FRAMES);\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(0, aubuf_cur_size(ab));\n\n\terr  = aubuf_write_samp(ab, sampv_in, FRAMES);\n\taubuf_read_auframe(ab, &af_out);\n\terr |= aubuf_write_samp(ab, sampv_in, FRAMES);\n\tTEST_ERR(err);\n\n\terr = aubuf_write_samp(ab, sampv_in, FRAMES);\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(4 * FRAMES, aubuf_cur_size(ab));\n\nout:\n\tmem_deref(ab);\n\treturn err;\n}\n\n\nint test_aubuf(void)\n{\n\tint err;\n\n\terr = test_aubuf_raw();\n\tTEST_ERR(err);\n\n\terr = test_aubuf_samp();\n\tTEST_ERR(err);\n\n\terr = test_aubuf_auframe();\n\tTEST_ERR(err);\n\n\terr = test_aubuf_sort_auframe();\n\tTEST_ERR(err);\n\n\terr = test_aubuf_resize();\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/aulength.c",
    "content": "/**\n * @file src/aulength.c audio file duration test\n *\n * Copyright (C) 2023 Lars Immisch\n */\n\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"aulength\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_aulength(void)\n{\n\tstruct aufile *af = NULL;\n\tstruct aufile_prm prm;\n\tchar path[256];\n\n\tre_snprintf(path, sizeof(path), \"%s/beep.wav\", test_datapath());\n\n\tint err = aufile_open(&af, &prm, path, AUFILE_READ);\n\tTEST_ERR(err);\n\n\tsize_t length = aufile_get_length(af, &prm);\n\tTEST_EQUALS(67, length);\n\nout:\n\tmem_deref(af);\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/aulevel.c",
    "content": "/**\n * @file src/aulevel.c audio levels\n *\n * Copyright (C) 2010 - 2017 Alfred E. Heggestad\n */\n\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"aulevel\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define PREC .6\n\n\nint test_aulevel(void)\n{\n\tdouble level;\n\tstruct auframe af;\n\tint err = 0;\n\n\tstatic struct {\n\t\tint16_t sampv[2];\n\t\tdouble level;\n\t} testv[] = {\n\n\t\t{  {    0,     -0},    -96.0  },\n\t\t{  {    0,      1},    -93.0  },\n\t\t{  {    1,     -1},    -90.0  },\n\t\t{  {    2,     -2},    -84.0  },\n\t\t{  {    4,     -4},    -78.0  },\n\t\t{  {    8,     -8},    -72.0  },\n\t\t{  {   16,    -16},    -66.0  },\n\t\t{  {   32,    -32},    -60.0  },\n\t\t{  {   64,    -64},    -54.0  },\n\t\t{  {  128,   -128},    -48.0  },\n\t\t{  {  256,   -256},    -42.0  },\n\t\t{  {  512,   -512},    -36.0  },\n\t\t{  { 1024,  -1024},    -30.0  },\n\t\t{  { 2048,  -2048},    -24.0  },\n\t\t{  { 4096,  -4096},    -18.0  },\n\t\t{  { 8192,  -8192},    -12.0  },\n\t\t{  {16384, -16384},     -6.0  },\n\t\t{  {32767, -32768},      0.0  },\n\t};\n\n\tstatic struct {\n\t\tint16_t sampv[4];\n\t\tdouble level;\n\t} testv4[] = {\n\t\t{  {32767, -32768, 16384, -16384},  -2.0  },\n\t};\n\n\tauframe_init(&af, AUFMT_RAW, testv[0].sampv,\n\t\t     RE_ARRAY_SIZE(testv[0].sampv), 48000, 2);\n\tTEST_EQUALS(AULEVEL_UNDEF, af.level);\n\n\tlevel = auframe_level(&af);\n\tTEST_EQUALS(AULEVEL_UNDEF, level);\n\n\tauframe_init(&af, AUFMT_S16LE, NULL, 0, 48000, 2);\n\tlevel = auframe_level(&af);\n\tTEST_EQUALS(AULEVEL_UNDEF, level);\n\n\tfor (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) {\n\t\tauframe_init(&af, AUFMT_S16LE, testv[i].sampv,\n\t\t\t     RE_ARRAY_SIZE(testv[i].sampv), 48000, 2);\n\n\t\tlevel = auframe_level(&af);\n\n\t\tASSERT_DOUBLE_EQ(testv[i].level, level, PREC);\n\t}\n\n\tfor (size_t i = 0; i < RE_ARRAY_SIZE(testv4); i++) {\n\t\tauframe_init(&af, AUFMT_S16LE, testv4[i].sampv,\n\t\t\t     RE_ARRAY_SIZE(testv4[i].sampv), 48000, 2);\n\n\t\tlevel = auframe_level(&af);\n\n\t\tASSERT_DOUBLE_EQ(testv4[i].level, level, PREC);\n\t}\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/aupos.c",
    "content": "/**\n * @file src/aupos.c audio file setposition test\n *\n * Copyright (C) 2023 Lars Immisch\n */\n\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"auposition\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_auposition(void)\n{\n\tstruct aufile *af = NULL;\n\tstruct aufile_prm prm;\n\tchar path[256];\n\tuint8_t buffer[512];\n\n\tre_snprintf(path, sizeof(path), \"%s/beep.wav\", test_datapath());\n\n\tint err = aufile_open(&af, &prm, path, AUFILE_READ);\n\tTEST_ERR(err);\n\n\terr = aufile_set_position(af, &prm, 67);\n\tTEST_ERR(err);\n\n\t/* That file is exactly 67 ms long, so we shouldn't read anything */\n\tsize_t size = sizeof(buffer);\n\terr = aufile_read(af, buffer, &size);\n\tTEST_ERR(err);\n\n\t/* It's possible we read data up to a ms */\n\tTEST_ASSERT(size < 16);\n\n\taf = mem_deref(af);\n\n\terr = aufile_open(&af, &prm, path, AUFILE_READ);\n\tTEST_ERR(err);\n\n\terr = aufile_set_position(af, &prm, 37);\n\tTEST_ERR(err);\n\n\tsize = sizeof(buffer);\n\terr = aufile_read(af, buffer, &size);\n\tTEST_ERR(err);\n\n\t/* 30 ms should be left, at 8000Hz/s, one channels and 16 bit samples\n\t   that's 480 bytes */\n\tTEST_ASSERT(size - 480 < 16);\n\n\nout:\n\tmem_deref(af);\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/auresamp.c",
    "content": "/**\n * @file auresamp.c Audio-resampler Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_auresamp\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define SRATE        44100\n#define CHANNELS_IN      1\n#define CHANNELS_OUT     2\n\n\n#define SAMPLES 8\n\n\n/* samples from random.org with atmospheric noise */\nstatic const int16_t inv[CHANNELS_IN * SAMPLES] = {\n\t0x513a,\n\t0x3f11,\n\t0x4224,\n\t0x601d,\n\t0x1dc6,\n\t0x2fb1,\n\t0x66ee,\n\t0x7d53\n};\n\nstatic const int16_t ref_outv[CHANNELS_OUT * SAMPLES] = {\n\t0x513a,\n\t0x513a,\n\t0x3f11,\n\t0x3f11,\n\t0x4224,\n\t0x4224,\n\t0x601d,\n\t0x601d,\n\t0x1dc6,\n\t0x1dc6,\n\t0x2fb1,\n\t0x2fb1,\n\t0x66ee,\n\t0x66ee,\n\t0x7d53,\n\t0x7d53\n};\n\n\nint test_auresamp(void)\n{\n\tstruct auresamp rs;\n\tint16_t outv[CHANNELS_OUT * SAMPLES];\n\tsize_t outc = RE_ARRAY_SIZE(outv);\n\tint err;\n\n\tauresamp_init(&rs);\n\n\terr = auresamp_setup(&rs, SRATE, CHANNELS_IN, SRATE, CHANNELS_OUT);\n\tTEST_ERR(err);\n\n\t/* resample from mono to stereo */\n\terr = auresamp(&rs, outv, &outc, inv, RE_ARRAY_SIZE(inv));\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(RE_ARRAY_SIZE(outv), outc);\n\n#if 0\n\tre_printf(\"\\nInput samples:\\n\");\n\thexdump(stdout, inv, sizeof(inv));\n\tre_printf(\"Output samples:\\n\");\n\thexdump(stdout, outv, sizeof(outv));\n#endif\n\n\tTEST_MEMCMP(ref_outv, sizeof(ref_outv), outv, sizeof(outv));\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/av1.c",
    "content": "/**\n * @file src/av1.c AV1 testcode\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re.h>\n#include <re_av1.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"av1test\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int test_leb128(void)\n{\n\tstruct mbuf *mb = NULL;\n\tint err = 0;\n\n\tstatic const uint64_t valuev[] = {\n\n\t\t0,\n\n\t\t/* from random.org */\n\t\t449787982,\n\t\t435590144,\n\t\t64565769,\n\t\t698509268,\n\t\t524090268,\n\n\t\t0x000000ff,     /* max  8-bit */\n\t\t0x0000ffff,     /* max 16-bit */\n\t\t0xffffffff      /* max 32-bit */\n\t};\n\n\tfor (size_t i=0; i<RE_ARRAY_SIZE(valuev); i++) {\n\n\t\tuint64_t val = valuev[i];\n\t\tuint64_t val_dec;\n\n\t\tmb = mbuf_alloc(64);\n\t\tif (!mb)\n\t\t\treturn ENOMEM;\n\n\t\terr = av1_leb128_encode(mb, val);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tmb->pos = 0;\n\n\t\terr = av1_leb128_decode(mb, &val_dec);\n\t\tASSERT_EQ(0, err);\n\n\t\tASSERT_EQ(val, val_dec);\n\n\t\tmb = mem_deref(mb);\n\t}\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic int test_av1_aggr(void)\n{\n\tstatic const struct test {\n\t\tuint8_t byte;\n\t\tunsigned z;\n\t\tunsigned y;\n\t\tunsigned w;\n\t\tunsigned n;\n\t} testv[] = {\n\n\t\t/* Sample aggregation headers from Chrome 102 */\n\t\t{0x28, 0, 0, 2, 1},\n\t\t{0x50, 0, 1, 1, 0},\n\t};\n\tint err = 0;\n\n\tfor (size_t i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tconst struct test *test = &testv[i];\n\t\tstruct av1_aggr_hdr hdr;\n\t\tstruct mbuf mb = {\n\t\t\t.buf  = (uint8_t *)&test->byte,\n\t\t\t.size = 1,\n\t\t\t.pos  = 0,\n\t\t\t.end  = 1\n\t\t};\n\n\t\terr = av1_aggr_hdr_decode(&hdr, &mb);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tASSERT_EQ(test->z, hdr.z);\n\t\tASSERT_EQ(test->y, hdr.y);\n\t\tASSERT_EQ(test->w, hdr.w);\n\t\tASSERT_EQ(test->n, hdr.n);\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic int test_av1_obu(void)\n{\n\tstruct av1_obu_hdr hdr;\n\tstatic const uint8_t buf[] = {\n\n\t\t/* libaom OBU_TEMPORAL_DELIMITER [type=2 x=0 s=1 size=0] */\n\t\t0x12, 0x00,\n\n\t\t/* libaom OBU_SEQUENCE_HEADER [type=1 x=0 s=1 size=12] */\n\t\t0x0a, 0x0c, 0x00, 0x00,\n\t\t0x00, 0x04, 0x3c, 0xff,\n\t\t0xbf, 0x81, 0xb5, 0x32,\n\t\t0x00, 0x80\n\t};\n\tstruct mbuf mb = {\n\t\t.buf  = (uint8_t *)buf,\n\t\t.size = sizeof(buf),\n\t\t.pos  = 0,\n\t\t.end  = sizeof(buf)\n\t};\n\tint err;\n\n\terr = av1_obu_decode(&hdr, &mb);\n\tif (err)\n\t\tgoto out;\n\n\tASSERT_EQ(2, hdr.type);\n\tASSERT_EQ(0, hdr.x);\n\tASSERT_EQ(1, hdr.s);\n\tASSERT_EQ(0, hdr.size);\n\n\terr = av1_obu_decode(&hdr, &mb);\n\tif (err)\n\t\tgoto out;\n\n\tASSERT_EQ(1, hdr.type);\n\tASSERT_EQ(0, hdr.x);\n\tASSERT_EQ(1, hdr.s);\n\tASSERT_EQ(12, hdr.size);\n\n\tASSERT_EQ(2, av1_obu_count(buf, sizeof(buf)));\n\n out:\n\treturn err;\n}\n\n\nstatic const uint64_t dummy_ts = 0x0102030405060708ULL;\n\n#define MAX_OBUS 10\n\nstruct test {\n\t/* input: */\n\tsize_t pktsize;\n\n\t/* output: */\n\tstruct mbuf *obus[MAX_OBUS];\n\tsize_t obu_index;\n\tunsigned marker_count;\n\tunsigned new_count;\n};\n\n\nstatic int av1_packet_handler(bool marker, uint64_t rtp_ts,\n\t\t\t      const uint8_t *hdr, size_t hdr_len,\n\t\t\t      const uint8_t *pld, size_t pld_len,\n\t\t\t      void *arg)\n{\n\tstruct test *test = arg;\n\tstruct mbuf *mb = mbuf_alloc(hdr_len + pld_len);\n\tstruct av1_aggr_hdr aggr_hdr;\n\tint err = 0;\n\tunsigned count = 0;\n\tsize_t size = 0;\n\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tASSERT_EQ(dummy_ts, rtp_ts);\n\tASSERT_TRUE((hdr_len + pld_len) <= test->pktsize);\n\n\terr  = mbuf_write_mem(mb, hdr, hdr_len);\n\terr |= mbuf_write_mem(mb, pld, pld_len);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\n\terr = av1_aggr_hdr_decode(&aggr_hdr, mb);\n\tif (err)\n\t\tgoto out;\n\n\tif (aggr_hdr.n)\n\t\t++test->new_count;\n\n\tif (aggr_hdr.z) {\n\t\tASSERT_TRUE(test->obus[test->obu_index]->pos > 0);\n\t}\n\telse {\n\t\tASSERT_EQ(0, test->obus[test->obu_index]->pos);\n\t}\n\n\twhile (mbuf_get_left(mb) > 0) {\n\t\t++count;\n\t\tif (aggr_hdr.w == 0 || count < aggr_hdr.w) {\n\t\t\tuint64_t decoded_size = 0;\n\t\t\terr = av1_leb128_decode(mb, &decoded_size);\n\t\t\tif (err) {\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\t/* Note: av1_leb128_decode always uses uint64_t,\n\t\t\t * but mbuf uses size_t, which can be 32 bits */\n\t\t\tASSERT_TRUE(decoded_size <= SIZE_MAX);\n\t\t\tsize = (size_t)decoded_size;\n\t\t\tASSERT_TRUE(size <= mbuf_get_left(mb));\n\t\t}\n\t\telse {\n\t\t\tsize = mbuf_get_left(mb);\n\t\t}\n\t\terr = mbuf_write_mem(test->obus[test->obu_index],\n\t\t\tmbuf_buf(mb), size);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\t\tmbuf_advance(mb, size);\n\n\t\tif (mbuf_get_left(mb) > 0 || !aggr_hdr.y) {\n\t\t\tmbuf_set_pos(test->obus[test->obu_index], 0);\n\t\t\t++test->obu_index;\n\t\t}\n\t}\n\n\tASSERT_TRUE(aggr_hdr.w == 0 || count == aggr_hdr.w);\n\n\tif (marker) {\n\t\t++test->marker_count;\n\t}\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstatic int copy_obu(struct mbuf *mb_bs, const uint8_t *buf, size_t size)\n{\n\tstruct av1_obu_hdr hdr;\n\tstruct mbuf wrap = {\n\t\t.buf = (uint8_t *)buf,\n\t\t.size = size,\n\t\t.pos = 0,\n\t\t.end = size\n\t};\n\tchar debug[512] = \"\";\n\tbool has_size = true;\n\n\tint err = av1_obu_decode(&hdr, &wrap);\n\tif (err) {\n\t\tDEBUG_WARNING(\"av1: decode: could not decode OBU\"\n\t\t\t\" [%zu bytes]: %m\\n\", size, err);\n\t\treturn err;\n\t}\n\n\tre_snprintf(debug, sizeof(debug), \"%H\\n\", av1_obu_print, &hdr);\n\tASSERT_TRUE(str_isset(debug));\n\n\tswitch (hdr.type) {\n\n\tcase AV1_OBU_SEQUENCE_HEADER:\n\tcase AV1_OBU_FRAME_HEADER:\n\tcase AV1_OBU_METADATA:\n\tcase AV1_OBU_FRAME:\n\tcase AV1_OBU_REDUNDANT_FRAME_HEADER:\n\tcase AV1_OBU_TILE_GROUP:\n\n\t\terr = av1_obu_encode(mb_bs, hdr.type, has_size,\n\t\t\t\t     hdr.size, mbuf_buf(&wrap));\n\t\tif (err)\n\t\t\treturn err;\n\t\tbreak;\n\n\tcase AV1_OBU_TEMPORAL_DELIMITER:\n\tcase AV1_OBU_TILE_LIST:\n\tcase AV1_OBU_PADDING:\n\t\t/* MUST be ignored by receivers. */\n\t\tDEBUG_WARNING(\"av1: decode: copy: unexpected obu type %u (%s)\"\n\t\t\t\" [x=%d, s=%d, size=%zu]\\n\",\n\t\t\t      hdr.type, av1_obu_name(hdr.type),\n\t\t\thdr.x, hdr.s, hdr.size);\n\t\treturn EPROTO;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"av1: decode: copy: unknown obu type %u (%s)\"\n\t\t\t\" [x=%d, s=%d, size=%zu]\\n\",\n\t\t\thdr.type, av1_obu_name(hdr.type),\n\t\t\thdr.x, hdr.s, hdr.size);\n\t\treturn EPROTO;\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic int test_av1_packetize_base(unsigned count_bs, unsigned count_rtp,\n\t\t\t\t   size_t pktsize, const uint8_t *buf,\n\t\t\t\t   size_t size, const uint8_t *expected_buf,\n\t\t\t\t   size_t expected_size)\n{\n\tstruct test test;\n\tstruct mbuf *mb_bs = mbuf_alloc(1024);\n\tbool new_flag = true;\n\tint err;\n\n\tif (!mb_bs)\n\t\treturn ENOMEM;\n\n\tmemset(&test, 0, sizeof(test));\n\n\tASSERT_EQ(count_bs, av1_obu_count(buf, size));\n\tASSERT_EQ(count_rtp, av1_obu_count_rtp(buf, size));\n\n\ttest.pktsize = pktsize;\n\n\tfor (size_t i = 0; i < MAX_OBUS; ++i) {\n\t\ttest.obus[i] = mbuf_alloc(1024);\n\t\tif (!test.obus[i]) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\terr = av1_packetize(&new_flag, true, dummy_ts,\n\t\t\t    buf, size, test.pktsize,\n\t\t\t    av1_packet_handler, &test);\n\tif (err)\n\t\tgoto out;\n\n\tASSERT_EQ(1, test.marker_count);\n\tASSERT_EQ(1, test.new_count);\n\n\t/* prepend Temporal Delimiter */\n\terr = av1_obu_encode(mb_bs, AV1_OBU_TEMPORAL_DELIMITER, true, 0, NULL);\n\tTEST_ERR(err);\n\n\tfor (size_t i = 0; i < test.obu_index; ++i) {\n\t\terr = copy_obu(mb_bs,\n\t\t\tmbuf_buf(test.obus[i]), mbuf_get_left(test.obus[i]));\n\t\tTEST_ERR(err);\n\t}\n\n\t/* compare bitstream with test-vector */\n\tTEST_MEMCMP(expected_buf, expected_size, mb_bs->buf, mb_bs->end);\n\n out:\n\tfor (size_t i = 0; i < MAX_OBUS; ++i) {\n\t\tmem_deref(test.obus[i]);\n\t}\n\tmem_deref(mb_bs);\n\n\treturn err;\n}\n\n\nstatic const uint8_t pkt_aom[] = {\n\n\t/* Temporal Delimiter */\n\t0x12, 0x00,\n\n\t/* Sequence header */\n\t0x0a, 0x0a,\n\t0x00, 0x00,  0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80,\n};\n\nstatic const uint8_t pkt_aom5[] = {\n\n\t/* Temporal Delimiter */\n\t0x12, 0x00,\n\n\t/* Sequence header */\n\t0x0a, 0x0a,\n\t0x00, 0x00,  0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80,\n\n\t/* Frame */\n\t0x32, 0x17,\n\t0x10, 0x01, 0x92, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x57, 0xb6, 0xd3, 0xfb,\n\t0x3b, 0xe3, 0xe1, 0x31, 0xeb, 0x4f, 0x36,\n\n\t/* Frame */\n\t0x32, 0x17,\n\t0x10, 0x01, 0x92, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x57, 0xb6, 0xd3, 0xfb,\n\t0x3b, 0xe3, 0xe1, 0x31, 0xeb, 0x4f, 0x36,\n\n\t/* Frame */\n\t0x32, 0x17,\n\t0x10, 0x01, 0x92, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x57, 0xb6, 0xd3, 0xfb,\n\t0x3b, 0xe3, 0xe1, 0x31, 0xeb, 0x4f, 0x36,\n};\n\nstatic const uint8_t pkt_aom_metadata[] = {\n\n\t/* Temporal Delimiter */\n\t0x12, 0x00,\n\n\t/* Sequence header */\n\t0x0a, 0x0a,\n\t0x00, 0x00,  0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80,\n\n\t/* OBU frame header */\n\t0x1a, 0x1b,\n\t0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,\n\t0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,\n\t0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80,\n\n\t/* OBU metadata */\n\t0x2a, 0x1a,\n\t0x02, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,\n\t0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,\n\t0x14, 0x15, 0x16, 0x17, 0x18, 0x80,\n\n\t/* OBU metadata */\n\t0x2a, 0x06,\n\t0x01, 0x01, 0x02, 0x03, 0x04, 0x80,\n\n\t/* Frame */\n\t0x32, 0x17,\n\t0x10, 0x01, 0x92, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x57, 0xb6, 0xd3, 0xfb,\n\t0x3b, 0xe3, 0xe1, 0x31, 0xeb, 0x4f, 0x36,\n};\n\nstatic const uint8_t pkt_multi_seq[] = {\n\n\t/* Temporal Delimiter */\n\t0x12, 0x00,\n\n\t/* Padding */\n\t0x7a, 0x04,\n\t0x01, 0x02, 0x03, 0x04,\n\n\t/* Sequence header */\n\t0x0a, 0x0a,\n\t0x00, 0x00,  0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80,\n\n\t/* Padding */\n\t0x7a, 0x04,\n\t0x05, 0x06, 0x07, 0x08,\n\n\t/* Padding */\n\t0x7a, 0x08,\n\t0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,\n\n\t/* Duplicate sequence header */\n\t0x0a, 0x0a,\n\t0x00, 0x00,  0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80,\n\n\t/* Frame */\n\t0x32, 0x17,\n\t0x10, 0x01, 0x92, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x57, 0xb6, 0xd3, 0xfb,\n\t0x3b, 0xe3, 0xe1, 0x31, 0xeb, 0x4f, 0x36,\n};\n\nstatic const uint8_t pkt_multi_seq_expected[] = {\n\n\t/* Temporal Delimiter */\n\t0x12, 0x00,\n\n\t/* Sequence header */\n\t0x0a, 0x0a,\n\t0x00, 0x00,  0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80,\n\n\t/* Duplicate sequence header */\n\t0x0a, 0x0a,\n\t0x00, 0x00,  0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80,\n\n\t/* Frame */\n\t0x32, 0x17,\n\t0x10, 0x01, 0x92, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x57, 0xb6, 0xd3, 0xfb,\n\t0x3b, 0xe3, 0xe1, 0x31, 0xeb, 0x4f, 0x36,\n};\n\n\n/*\n * https://dl8.webmfiles.org/BeachDrone-AV1.webm\n *\n * frame   3:  size=320     pts=134  (0.134000 sec)\n * obu:  type=2,OBU_TEMPORAL_DELIMITER   x=0 s=1 size=0\n * obu:  type=3,OBU_FRAME_HEADER         x=0 s=1 size=23\n * obu:  type=4,OBU_TILE_GROUP           x=0 s=1 size=290\n *\n */\nstatic const char pkt_beach[] =\n\t\"12001a17301a2049648406a21a47fbdf\"\n\t\"cbb4180c4002041157404022a202001c\"\n\t\"64b538c87ccb8807fc1658bcd98ada85\"\n\t\"6a35745f32824a2ee8d5e11d80476188\"\n\t\"917a6662c19f0ca9eace86b8ac3ae880\"\n\t\"0561949ecbbc26f800d904d1714219a1\"\n\t\"0d1d0410370c6e0b8dead1bf1e8a291b\"\n\t\"fd0a1254a6e038998e091c7d5233b138\"\n\t\"68acf6225840618dcbfd948ed99943dd\"\n\t\"93df6037f6fda997cd2f8467b601d94e\"\n\t\"09169d57f8fa9c8d6abfcab091366231\"\n\t\"48c89c7d5a8b86544140a827f48a2b0b\"\n\t\"15d6836f4ceab733dd2f2ebbb20cb69a\"\n\t\"684dafb9403610e0560bad66b728c8fd\"\n\t\"38c315a1f63ac3d2fca0da95fdbfb9f8\"\n\t\"e61b4f18b90a455dad2fc91a32401007\"\n\t\"2942753e34c95c6d3693a555e660e6ca\"\n\t\"628a22fed94f3618d912b84a272e00da\"\n\t\"44b8cf62a7abfd5d0396e8848d8bd56d\"\n\t\"195bb21814c15700e825a4d9fe2a64f8\"\n\t;\n\n\nstatic int test_av1_packetize_range(\n\t\tunsigned count_bs, unsigned count_rtp,\n\t\tconst uint8_t *buf, size_t size,\n\t\tconst uint8_t *expected_buf, size_t expected_size) {\n\tint err = 0;\n\tfor (size_t i = 10; i <= 120; ++i) {\n\t\terr = test_av1_packetize_base(count_bs, count_rtp, i,\n\t\t\tbuf, size, expected_buf, expected_size);\n\t\tif (err) {\n\t\t\treturn err;\n\t\t}\n\t}\n\treturn err;\n}\n\n\nstatic int test_av1_packetize(void)\n{\n\tuint8_t buf[320];\n\tint err;\n\n\terr = test_av1_packetize_range(2, 1,\n\t\tpkt_aom, sizeof(pkt_aom),\n\t\tpkt_aom, sizeof(pkt_aom));\n\tif (err)\n\t\treturn err;\n\n\terr = test_av1_packetize_range(5, 4,\n\t\tpkt_aom5, sizeof(pkt_aom5),\n\t\tpkt_aom5, sizeof(pkt_aom5));\n\tif (err)\n\t\treturn err;\n\n\terr = test_av1_packetize_range(6, 5,\n\t\tpkt_aom_metadata, sizeof(pkt_aom_metadata),\n\t\tpkt_aom_metadata, sizeof(pkt_aom_metadata));\n\tif (err)\n\t\treturn err;\n\n\terr = test_av1_packetize_range(7, 3,\n\t\tpkt_multi_seq, sizeof(pkt_multi_seq),\n\t\tpkt_multi_seq_expected, sizeof(pkt_multi_seq_expected));\n\tif (err)\n\t\treturn err;\n\n\terr = str_hex(buf, sizeof(buf), pkt_beach);\n\tif (err)\n\t\treturn err;\n\n\terr = test_av1_packetize_range(3, 2,\n\t\tbuf, sizeof(buf),\n\t\tbuf, sizeof(buf));\n\tif (err)\n\t\treturn err;\n\n\treturn 0;\n}\n\n\n#define AV1_PACKET1_SIZE 1188\n#define AV1_PACKET2_SIZE 231\n\n\nstruct state {\n\tuint8_t buf_packet1[AV1_PACKET1_SIZE];\n\tuint8_t buf_packet2[AV1_PACKET2_SIZE];\n\tunsigned count;\n};\n\n\nstatic int interop_packet_handler(bool marker, uint64_t rtp_ts,\n\t\t\t\t  const uint8_t *hdr, size_t hdr_len,\n\t\t\t\t  const uint8_t *pld, size_t pld_len,\n\t\t\t\t  void *arg)\n{\n\tstruct state *state = arg;\n\tstruct mbuf *mb = mbuf_alloc(hdr_len + pld_len);\n\tint err = 0;\n\t(void)marker;\n\t(void)rtp_ts;\n\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr  = mbuf_write_mem(mb, hdr, hdr_len);\n\terr |= mbuf_write_mem(mb, pld, pld_len);\n\tif (err)\n\t\tgoto out;\n\n\tswitch (state->count) {\n\n\tcase 0:\n\t\tTEST_MEMCMP(state->buf_packet1, sizeof(state->buf_packet1),\n\t\t\t    mb->buf, mb->end);\n\t\tbreak;\n\n\tcase 1:\n\t\tTEST_MEMCMP(state->buf_packet2, sizeof(state->buf_packet2),\n\t\t\t    mb->buf, mb->end);\n\t\tbreak;\n\n\tdefault:\n\t\terr = EPROTO;\n\t\tbreak;\n\t}\n\n out:\n\tstate->count = state->count + 1;\n\n\tmem_deref(mb);\n\treturn err;\n}\n\n\n/*\n * Test AV1 interop with Chrome.\n */\nstatic int test_av1_interop(void)\n{\n#define AV1_FRAME_SIZE 1421\n\n\tstatic const char frame[] =\n\n\t\t\"12000a0a00000024cf7f0d80340132fc\"\n\t\t\"0a10717800ffffff16e6180000000b01\"\n\t\t\"bbc1318ad86995cba97034ff8767d6fd\"\n\t\t\"ade65542dbc40b9e44cc8e479f68b4b9\"\n\t\t\"5c7e78cabd7344021a5d99d51918f3e9\"\n\t\t\"b6a0afe14686c45b6dc9ff25d4dddd91\"\n\t\t\"8a4dcc3998be6af61811000b0d886601\"\n\t\t\"53036febf3c7fef1defb75b3ba396cb6\"\n\t\t\"f6a8bb84def6603617a995f270102f87\"\n\t\t\"a1eccaadac954247c9a116a1343ce905\"\n\t\t\"2aa5b90dba23bc7299b85dd829523aa0\"\n\t\t\"0a15a2db9e24dc34622ff7c772f4b0dd\"\n\t\t\"dff7afdbc95748c68d3ab706ffc4b772\"\n\t\t\"f44fd1bcbe20309a908a0dbdba8ce5db\"\n\t\t\"1dc9de2d75f48c5976e6fb941b5da795\"\n\t\t\"e96c5a8a70b5e55d0d8d8d3b084bb09d\"\n\t\t\"d32e83d121087052ee4597b17f1ac46d\"\n\t\t\"8b284742c095534146fd6dd6161e67cd\"\n\t\t\"58ef8f092a75b32585e7efecd001b4ad\"\n\t\t\"292804ce0aa4318fd7b5824497f39f19\"\n\t\t\"82174ed1ff800416f565393cfadc7c9e\"\n\t\t\"0140a4140ab96ac5d7e4b7891e2ff6d7\"\n\t\t\"6c789d81e28645f3873d1ddbb9e3152c\"\n\t\t\"4137cc1f13c743fa6454c849e7fe703e\"\n\t\t\"1e7ee19e5ea3b728b460a67b009fa952\"\n\t\t\"0608ed4dea672a6df720a892f42203c1\"\n\t\t\"13cc56903148e249e3b5f5f7266b0cf0\"\n\t\t\"539ffdac040ef551b589ee92bd4b081a\"\n\t\t\"652af89d56e7546b2b8ea35300324e2a\"\n\t\t\"3a74af972642454c5d3ba8caef3fe19c\"\n\t\t\"a2a31729113858d8f13fbde793ba7834\"\n\t\t\"b6e855f60b4302e42f8c7d32ed48e50d\"\n\t\t\"b9a87aa57ec0384293cd7fa4c02f2909\"\n\t\t\"b68d4f07afbe22059f52efaaab98d170\"\n\t\t\"ba612e8c05a68e048c3f66b7452269f6\"\n\t\t\"704346897559ab38dcc4f138e3796217\"\n\t\t\"02c0661a8f09ab7d57c1e2bbb3d58899\"\n\t\t\"28d2d189f7d33900c7fe606579a77709\"\n\t\t\"551254e1d2301f5445857e1d132edc01\"\n\t\t\"605128705cb22ff1184e70dc8985169e\"\n\t\t\"aafc996f81116ce8007f141f1908eb9c\"\n\t\t\"707c415ada0923e42f6e822453b1e330\"\n\t\t\"385b377e7f19f1d36a93a404affef91b\"\n\t\t\"6587849ef244940c636f3c458986f104\"\n\t\t\"174cb6af58160c28c0929aee986da31c\"\n\t\t\"1a0596ccecedf2dad9202ade93c4010e\"\n\t\t\"b39462aaf111aa53444fdf654e82a454\"\n\t\t\"909f97e361026a265c37a0616407589d\"\n\t\t\"01bb068ece454ba616612a29d67f61a7\"\n\t\t\"2aac84871f0503752525137a3b189c5e\"\n\t\t\"34cffb6d600c868eb54125f8861c9bac\"\n\t\t\"a580ef457eacd68b8dc30f32aa4cb7cb\"\n\t\t\"d3e20ced165b71c0617024f5423ee017\"\n\t\t\"3aad3af71a30f33609fcef771c3810b9\"\n\t\t\"fb61a350cfa97d6e5f219d593d28f4e4\"\n\t\t\"66590f89ad0851149852225eb07a042f\"\n\t\t\"9d8fb97f0f2437fb37e3102f6010794b\"\n\t\t\"e0ad882519f913c8db117aa093e663dd\"\n\t\t\"2183ac731449e62f803ba24086ea28f3\"\n\t\t\"814c33bdf9863927b544e1a74ebf6b20\"\n\t\t\"64dcb92efd8e8b71aab354601f0e75d7\"\n\t\t\"5686fe86984e6735c4ed2eef2b919236\"\n\t\t\"4c46a963e88661c5ea8f278fc1efa306\"\n\t\t\"67046926a2a75c23a5d63af373478cb8\"\n\t\t\"c55e11f9de4a61d77c5b11080fe258e4\"\n\t\t\"8509d86aa93249012678d1c40056e9f3\"\n\t\t\"44261079a1729a7b7853322b016847f4\"\n\t\t\"6ca4cdd0b107c7aa6024889ccd4b4002\"\n\t\t\"e2f69b53ed0d0063bf80936fb970bc12\"\n\t\t\"0fcabeb82b41b2c75bcb5211b6b5d404\"\n\t\t\"cbdcc175adeaad1ebac4e026989e3365\"\n\t\t\"d676ff62e674595509f48a43ee2ba010\"\n\t\t\"f12f8799e4c357fd369a108aa2f1a073\"\n\t\t\"e7a25e0cdb92be13e5267fe9d8d5e6b5\"\n\t\t\"31b8cb9f0549ad56e586670133ab39ed\"\n\t\t\"7124d942c2742f5e78c52f10c009bb48\"\n\t\t\"13b26fb55217f369c33400976663b912\"\n\t\t\"c1bd389762be20a040cee498411c47a0\"\n\t\t\"4c1e53d7b36c958dbdb56b58ebfc5a88\"\n\t\t\"faca07c3739c9bf28bfb8d7cd50f1fc5\"\n\t\t\"82d54aee4a17073b0552d989e51d6501\"\n\t\t\"35bcca12fc5f4c92924912d7a5a91b82\"\n\t\t\"edb8c0fda7e43526658c4ddd15a0d3e4\"\n\t\t\"d24a996aa902f9e51b43e67974fd59ed\"\n\t\t\"3ea2a6ede7ea3033d8d6f2d2dc624204\"\n\t\t\"558433c6a0a7315e970bba563c0dcb15\"\n\t\t\"879b64ff57418984b998bd4c70f33c95\"\n\t\t\"29d1184ad74cbcf14927771f562ae036\"\n\t\t\"fac2e439966307e5d9ae4d5984\"\n\t\t;\n\n\n\tstatic const char packet1[] =\n\n\t\t/* NOTE: W=2 */\n\t\t\"68\"\n\n\t\t\"0b0800000024cf7f0d80340130107178\"\n\t\t\"00ffffff16e6180000000b01bbc1318a\"\n\t\t\"d86995cba97034ff8767d6fdade65542\"\n\t\t\"dbc40b9e44cc8e479f68b4b95c7e78ca\"\n\t\t\"bd7344021a5d99d51918f3e9b6a0afe1\"\n\t\t\"4686c45b6dc9ff25d4dddd918a4dcc39\"\n\t\t\"98be6af61811000b0d88660153036feb\"\n\t\t\"f3c7fef1defb75b3ba396cb6f6a8bb84\"\n\t\t\"def6603617a995f270102f87a1eccaad\"\n\t\t\"ac954247c9a116a1343ce9052aa5b90d\"\n\t\t\"ba23bc7299b85dd829523aa00a15a2db\"\n\t\t\"9e24dc34622ff7c772f4b0dddff7afdb\"\n\t\t\"c95748c68d3ab706ffc4b772f44fd1bc\"\n\t\t\"be20309a908a0dbdba8ce5db1dc9de2d\"\n\t\t\"75f48c5976e6fb941b5da795e96c5a8a\"\n\t\t\"70b5e55d0d8d8d3b084bb09dd32e83d1\"\n\t\t\"21087052ee4597b17f1ac46d8b284742\"\n\t\t\"c095534146fd6dd6161e67cd58ef8f09\"\n\t\t\"2a75b32585e7efecd001b4ad292804ce\"\n\t\t\"0aa4318fd7b5824497f39f1982174ed1\"\n\t\t\"ff800416f565393cfadc7c9e0140a414\"\n\t\t\"0ab96ac5d7e4b7891e2ff6d76c789d81\"\n\t\t\"e28645f3873d1ddbb9e3152c4137cc1f\"\n\t\t\"13c743fa6454c849e7fe703e1e7ee19e\"\n\t\t\"5ea3b728b460a67b009fa9520608ed4d\"\n\t\t\"ea672a6df720a892f42203c113cc5690\"\n\t\t\"3148e249e3b5f5f7266b0cf0539ffdac\"\n\t\t\"040ef551b589ee92bd4b081a652af89d\"\n\t\t\"56e7546b2b8ea35300324e2a3a74af97\"\n\t\t\"2642454c5d3ba8caef3fe19ca2a31729\"\n\t\t\"113858d8f13fbde793ba7834b6e855f6\"\n\t\t\"0b4302e42f8c7d32ed48e50db9a87aa5\"\n\t\t\"7ec0384293cd7fa4c02f2909b68d4f07\"\n\t\t\"afbe22059f52efaaab98d170ba612e8c\"\n\t\t\"05a68e048c3f66b7452269f670434689\"\n\t\t\"7559ab38dcc4f138e379621702c0661a\"\n\t\t\"8f09ab7d57c1e2bbb3d5889928d2d189\"\n\t\t\"f7d33900c7fe606579a77709551254e1\"\n\t\t\"d2301f5445857e1d132edc0160512870\"\n\t\t\"5cb22ff1184e70dc8985169eaafc996f\"\n\t\t\"81116ce8007f141f1908eb9c707c415a\"\n\t\t\"da0923e42f6e822453b1e330385b377e\"\n\t\t\"7f19f1d36a93a404affef91b6587849e\"\n\t\t\"f244940c636f3c458986f104174cb6af\"\n\t\t\"58160c28c0929aee986da31c1a0596cc\"\n\t\t\"ecedf2dad9202ade93c4010eb39462aa\"\n\t\t\"f111aa53444fdf654e82a454909f97e3\"\n\t\t\"61026a265c37a0616407589d01bb068e\"\n\t\t\"ce454ba616612a29d67f61a72aac8487\"\n\t\t\"1f0503752525137a3b189c5e34cffb6d\"\n\t\t\"600c868eb54125f8861c9baca580ef45\"\n\t\t\"7eacd68b8dc30f32aa4cb7cbd3e20ced\"\n\t\t\"165b71c0617024f5423ee0173aad3af7\"\n\t\t\"1a30f33609fcef771c3810b9fb61a350\"\n\t\t\"cfa97d6e5f219d593d28f4e466590f89\"\n\t\t\"ad0851149852225eb07a042f9d8fb97f\"\n\t\t\"0f2437fb37e3102f6010794be0ad8825\"\n\t\t\"19f913c8db117aa093e663dd2183ac73\"\n\t\t\"1449e62f803ba24086ea28f3814c33bd\"\n\t\t\"f9863927b544e1a74ebf6b2064dcb92e\"\n\t\t\"fd8e8b71aab354601f0e75d75686fe86\"\n\t\t\"984e6735c4ed2eef2b9192364c46a963\"\n\t\t\"e88661c5ea8f278fc1efa30667046926\"\n\t\t\"a2a75c23a5d63af373478cb8c55e11f9\"\n\t\t\"de4a61d77c5b11080fe258e48509d86a\"\n\t\t\"a93249012678d1c40056e9f344261079\"\n\t\t\"a1729a7b7853322b016847f46ca4cdd0\"\n\t\t\"b107c7aa6024889ccd4b4002e2f69b53\"\n\t\t\"ed0d0063bf80936fb970bc120fcabeb8\"\n\t\t\"2b41b2c75bcb5211b6b5d404cbdcc175\"\n\t\t\"adeaad1ebac4e026989e3365d676ff62\"\n\t\t\"e674595509f48a43ee2ba010f12f8799\"\n\t\t\"e4c357fd369a108aa2f1a073e7a25e0c\"\n\t\t\"db92be13e5267fe9d8d5e6b531b8cb9f\"\n\t\t\"0549ad\"\n\t\t;\n\n\n\tstatic const char packet2[] =\n\n\t\t/* NOTE: W=1 */\n\n\t\t\"90\"\n\n\t\t\"56e586670133ab39ed7124d942c2742f\"\n\t\t\"5e78c52f10c009bb4813b26fb55217f3\"\n\t\t\"69c33400976663b912c1bd389762be20\"\n\t\t\"a040cee498411c47a04c1e53d7b36c95\"\n\t\t\"8dbdb56b58ebfc5a88faca07c3739c9b\"\n\t\t\"f28bfb8d7cd50f1fc582d54aee4a1707\"\n\t\t\"3b0552d989e51d650135bcca12fc5f4c\"\n\t\t\"92924912d7a5a91b82edb8c0fda7e435\"\n\t\t\"26658c4ddd15a0d3e4d24a996aa902f9\"\n\t\t\"e51b43e67974fd59ed3ea2a6ede7ea30\"\n\t\t\"33d8d6f2d2dc624204558433c6a0a731\"\n\t\t\"5e970bba563c0dcb15879b64ff574189\"\n\t\t\"84b998bd4c70f33c9529d1184ad74cbc\"\n\t\t\"f14927771f562ae036fac2e439966307\"\n\t\t\"e5d9ae4d5984\"\n\t\t;\n\n\tstruct state state;\n\tuint8_t buf[AV1_FRAME_SIZE];\n\tbool new_flag = true;\n\tint err;\n\n\tstate.count = 0;\n\n\terr = str_hex(buf, sizeof(buf), frame);\n\tTEST_ERR(err);\n\n\terr = str_hex(state.buf_packet1, sizeof(state.buf_packet1), packet1);\n\tTEST_ERR(err);\n\n\terr = str_hex(state.buf_packet2, sizeof(state.buf_packet2), packet2);\n\tTEST_ERR(err);\n\n\terr = av1_packetize(&new_flag, true, dummy_ts,\n\t\t\t    buf, sizeof(buf), 1188,\n\t\t\t    interop_packet_handler, &state);\n\tif (err)\n\t\tgoto out;\n\n out:\n\treturn err;\n}\n\n\nint test_av1(void)\n{\n\tint err;\n\n\terr = test_leb128();\n\tTEST_ERR(err);\n\n\terr = test_av1_aggr();\n\tTEST_ERR(err);\n\n\terr = test_av1_obu();\n\tTEST_ERR(err);\n\n\terr = test_av1_packetize();\n\tTEST_ERR(err);\n\n\terr = test_av1_interop();\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/base64.c",
    "content": "/**\n * @file base64.c Base64 Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_base64\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_base64(void)\n{\n\tconst struct {\n\t\tstruct pl pl;\n\t\tstruct pl b64;\n\t\tstruct pl b64url;\n\t} testv[] = {\n\t\t{PL(\"\"),       PL(\"\"),         PL(\"\")},\n\t\t{PL(\"f\"),      PL(\"Zg==\"),     PL(\"Zg\")},\n\t\t{PL(\"fo\"),     PL(\"Zm8=\"),     PL(\"Zm8\")},\n\t\t{PL(\"foo\"),    PL(\"Zm9v\"),     PL(\"Zm9v\")},\n\t\t{PL(\"foob\"),   PL(\"Zm9vYg==\"), PL(\"Zm9vYg\")},\n\t\t{PL(\"fooba\"),  PL(\"Zm9vYmE=\"), PL(\"Zm9vYmE\")},\n\t\t{PL(\"foobar\"), PL(\"Zm9vYmFy\"), PL(\"Zm9vYmFy\")},\n\t\t{PL(\"\\xff\\x01\\xfe\\x02\"), PL(\"/wH+Ag==\"), PL(\"_wH-Ag\")},\n\n\t\t{PL(\"asdlkjqopinzidfj84r77fsgljsdf9823r\"),\n\t\t PL(\"YXNkbGtqcW9waW56aWRmajg0cjc3ZnNnbGpzZGY5ODIzcg==\"),\n\t\t PL(\"YXNkbGtqcW9waW56aWRmajg0cjc3ZnNnbGpzZGY5ODIzcg\")},\n\t\t{PL(\"918nvbakishdl8317237dlakskdkaldj\"),\n\t\t PL(\"OTE4bnZiYWtpc2hkbDgzMTcyMzdkbGFrc2tka2FsZGo=\"),\n\t\t PL(\"OTE4bnZiYWtpc2hkbDgzMTcyMzdkbGFrc2tka2FsZGo\")},\n\t\t{PL(\"very10long..testxyzstring/.,-=-3029===7823#'];'#';]#'\"),\n\t\t PL(\"dmVyeTEwbG9uZy4udGVzdHh5enN0cmluZy8uLC0\"\n\t\t    \"9LTMwMjk9PT03ODIzIyddOycjJztdIyc=\"),\n\t\t PL(\"dmVyeTEwbG9uZy4udGVzdHh5enN0cmluZy8uLC0\"\n\t\t    \"9LTMwMjk9PT03ODIzIyddOycjJztdIyc\")},\n\t};\n\tuint32_t i;\n\tint err = 0;\n\tuint8_t b64_buf[128];\n\tsize_t olen;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tconst struct pl *pl = &testv[i].pl;\n\t\tconst struct pl *b;\n\t\tchar buf[128];\n\n\t\t/* Encode */\n\t\tolen = sizeof(buf);\n\t\terr = base64_encode((uint8_t *)pl->p, pl->l, buf, &olen);\n\t\tTEST_ERR(err);\n\n\t\tif (olen != testv[i].b64.l) {\n\t\t\tDEBUG_WARNING(\"b64_encode %u failed: l=%u olen=%u\\n\",\n\t\t\t\t      i, testv[i].b64.l, olen);\n\t\t\terr = EINVAL;\n\t\t\tTEST_ERR(err);\n\t\t}\n\t\tif (0 != memcmp(testv[i].b64.p, buf, olen)) {\n\t\t\tDEBUG_WARNING(\"b64_encode %u failed: ref=%r, enc=%b\\n\",\n\t\t\t\t      i, &testv[i].b64, buf, olen);\n\t\t\terr = EINVAL;\n\t\t\tTEST_ERR(err);\n\t\t}\n\n\t\t/* Encode URL */\n\t\tolen = sizeof(buf);\n\t\terr = base64url_encode((uint8_t *)pl->p, pl->l, buf, &olen);\n\t\tTEST_ERR(err);\n\n\t\tif (olen != testv[i].b64url.l) {\n\t\t\tDEBUG_WARNING(\"b64_encode %u failed: l=%u olen=%u\\n\",\n\t\t\t\t      i, testv[i].b64url.l, olen);\n\t\t\terr = EINVAL;\n\t\t\tTEST_ERR(err);\n\t\t}\n\t\tif (0 != memcmp(testv[i].b64url.p, buf, olen)) {\n\t\t\tDEBUG_WARNING(\"b64_encode %u failed: ref=%r, enc=%b\\n\",\n\t\t\t\t      i, &testv[i].b64url, buf, olen);\n\t\t\terr = EINVAL;\n\t\t\tTEST_ERR(err);\n\t\t}\n\n\t\t/* Decode */\n\t\tb = &testv[i].b64;\n\t\tolen = sizeof(b64_buf);\n\t\terr = base64_decode(b->p, b->l, b64_buf, &olen);\n\t\tTEST_ERR(err);\n\n\t\tif (olen != testv[i].pl.l) {\n\t\t\tDEBUG_WARNING(\"b64_decode %u failed: l=%u olen=%u\\n\",\n\t\t\t\t      i, testv[i].pl.l, olen);\n\t\t\terr = EINVAL;\n\t\t\tTEST_ERR(err);\n\t\t}\n\t\tif (0 != memcmp(testv[i].pl.p, b64_buf, olen)) {\n\t\t\tDEBUG_WARNING(\"b64_decode %u failed: ref=%r, enc=%b\\n\",\n\t\t\t\t      i, &testv[i].pl, b64_buf, olen);\n\t\t\terr = EINVAL;\n\t\t\tTEST_ERR(err);\n\t\t}\n\n\t\t/* Decode Url */\n\t\tb = &testv[i].b64url;\n\t\tolen = sizeof(b64_buf);\n\t\terr = base64_decode(b->p, b->l, b64_buf, &olen);\n\t\tTEST_ERR(err);\n\n\t\tif (olen != testv[i].pl.l) {\n\t\t\tDEBUG_WARNING(\n\t\t\t\t\"b64_decode url %u failed: l=%u olen=%u\\n\", i,\n\t\t\t\ttestv[i].pl.l, olen);\n\t\t\terr = EINVAL;\n\t\t\tTEST_ERR(err);\n\t\t}\n\t\tif (0 != memcmp(testv[i].pl.p, b64_buf, olen)) {\n\t\t\tDEBUG_WARNING(\n\t\t\t\t\"b64_decode url %u failed: ref=%r, enc=%b\\n\",\n\t\t\t\ti, &testv[i].pl, b64_buf, olen);\n\t\t\terr = EINVAL;\n\t\t\tTEST_ERR(err);\n\t\t}\n\t}\n\n\t/* Invalid checks */\n\tchar c = 'A';\n\tolen   = sizeof(b64_buf);\n\terr    = base64_decode(&c, sizeof(c), b64_buf, &olen);\n\tTEST_ERR(err);\n\n\tstruct pl inv;\n\tpl_set_str(&inv, \"Zm8=\");\n\tolen = 1;\n\terr  = base64_decode(inv.p, inv.l, b64_buf, &olen);\n\tTEST_EQUALS(EOVERFLOW, err);\n\n\terr = 0;\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/bfcp.c",
    "content": "/**\n * @file bfcp.c BFCP Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stddef.h>\n#include <stdbool.h>\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"bfcptest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic const uint8_t bfcp_msg[] =\n\n\t/* FloorRequest */\n\t\"\\x20\\x01\\x00\\x04\"  /* | ver | primitive | length  | */\n\t\"\\x01\\x02\\x03\\x04\"  /* |       conference id       | */\n\t\"\\xfe\\xdc\\xba\\x98\"  /* | transaction id | user id  | */\n\t\"\"\n\t\"\\x04\\x04\\x00\\x01\"  /* FLOOR-ID */\n\t\"\\x02\\x04\\x00\\x02\"  /* BENEFICIARY-ID */\n\t\"\\x10\\x03\\x58\\x00\"  /* PARTICIPANT-PROVIDED-INFO */\n       \t\"\\x08\\x04\\x40\\x00\"  /* PRIORITY */\n\n\t/* FloorRelease */\n\t\"\\x20\\x02\\x00\\x01\"  /* | ver | primitive | length  | */\n\t\"\\x01\\x02\\x03\\x04\"  /* |       conference id       | */\n\t\"\\xfe\\xdc\\xba\\x98\"  /* | transaction id | user id  | */\n\t\"\"\n\t\"\\x06\\x04\\x00\\x03\"  /* FLOOR-REQUEST-ID */\n\n\t/* UserStatus w/FLOOR-REQUEST-INFORMATION */\n\t\"\\x20\\x06\\x00\\x12\"  /* | ver | primitive | length  | */\n\t\"\\x01\\x02\\x03\\x04\"  /* |       conference id       | */\n\t\"\\xfe\\xdc\\xba\\x98\"  /* | transaction id | user id  | */\n\t\"\"\n\t\"\\x1e\\x48\\x88\\x99\"  /* FLOOR-ID */\n       \t\"\\x24\\x0c\\x74\\xad\"  /* OVERALL-REQUEST-STATUS */\n\t\"\\x0a\\x04\\x04\\x02\"\n\t\"\\x12\\x04\\x4f\\x4b\"\n       \t\"\\x22\\x0c\\x00\\x02\"  /* FLOOR-REQUEST-STATUS #1 */\n\t\"\\x0a\\x04\\x02\\x02\"\n\t\"\\x12\\x04\\x6f\\x6b\"\n       \t\"\\x22\\x0c\\x00\\x04\"  /* FLOOR-REQUEST-STATUS #2 */\n\t\"\\x0a\\x04\\x07\\x03\"\n\t\"\\x12\\x04\\x6a\\x61\"\n\t\"\\x1c\\x0c\\x00\\x01\"  /* BENEFICIARY-INFORMATION */\n\t\"\\x18\\x03\\x61\\x00\"\n\t\"\\x1a\\x03\\x62\\x00\"\n\t\"\\x20\\x0c\\x00\\x02\"  /* REQUESTED-BY-INFORMATION */\n\t\"\\x18\\x03\\x63\\x00\"\n\t\"\\x1a\\x03\\x64\\x00\"\n\t\"\\x08\\x04\\x40\\x00\"  /* PRIORITY */\n\t\"\\x10\\x03\\x78\\x00\"  /* PARTICIPANT-PROVIDED-INFO */\n\n\t/* Hello */\n\t\"\\x20\\x0b\\x00\\x00\"  /* | ver | primitive | length  | */\n\t\"\\x01\\x02\\x03\\x04\"  /* |       conference id       | */\n\t\"\\xfe\\xdc\\xba\\x98\"  /* | transaction id | user id  | */\n\t\"\"\n\n\t\"\";\n\n\nstatic int parse_msg(const uint8_t *p, size_t n)\n{\n\tstruct mbuf *mb = mbuf_alloc(512);\n\tint err;\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = mbuf_write_mem(mb, p, n);\n\tif (err)\n\t\treturn err;\n\n\tmb->pos = 0;\n\n\twhile (mbuf_get_left(mb) >= 4) {\n\t\tstruct bfcp_msg *msg;\n\n\t\terr = bfcp_msg_decode(&msg, mb);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tmem_deref(msg);\n\t}\n\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nint test_bfcp(void)\n{\n\tconst size_t sz = sizeof(bfcp_msg) - 1;\n\tstruct mbuf *mb;\n\tstruct bfcp_reqstatus oreqstatus, reqstatus1, reqstatus2;\n\tuint16_t floorid = 1, bfid = 2, frid = 3, freqid;\n\tuint16_t ofreqid, floorid1, floorid2, rbid;\n\tenum bfcp_priority prio = BFCP_PRIO_NORMAL;\n\tint n, err = 0;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = bfcp_msg_encode(mb, 1, false, BFCP_FLOOR_REQUEST,\n\t\t\t      0x01020304, 0xfedc, 0xba98, 4,\n\t\t\t      BFCP_FLOOR_ID,       0, &floorid,\n\t\t\t      BFCP_BENEFICIARY_ID, 0, &bfid,\n\t\t\t      BFCP_PART_PROV_INFO, 0, \"X\",\n\t\t\t      BFCP_PRIORITY,       0, &prio);\n\tif (err)\n\t\tgoto out;\n\n\terr = bfcp_msg_encode(mb, 1, false, BFCP_FLOOR_RELEASE,\n\t\t\t      0x01020304, 0xfedc, 0xba98, 1,\n\t\t\t      BFCP_FLOOR_REQUEST_ID, 0, &frid);\n\tif (err)\n\t\tgoto out;\n\n\tfreqid = 0x8899;\n\n\tofreqid           = 0x74ad;\n\toreqstatus.status = BFCP_DENIED;\n\toreqstatus.qpos   = 2;\n\n\tfloorid1          = 2;\n\treqstatus1.status = BFCP_ACCEPTED;\n\treqstatus1.qpos   = 2;\n\n\tfloorid2          = 4;\n\treqstatus2.status = BFCP_REVOKED;\n\treqstatus2.qpos   = 3;\n\n\tbfid = 1;\n\trbid = 2;\n\tprio = BFCP_PRIO_NORMAL;\n\n\terr = bfcp_msg_encode(mb, 1, false, BFCP_USER_STATUS,\n\t\t\t      0x01020304, 0xfedc, 0xba98, 1,\n\n\t\t\t      BFCP_FLOOR_REQ_INFO,     7, &freqid,\n\n\t\t\t      BFCP_OVERALL_REQ_STATUS, 2, &ofreqid,\n\t\t\t      BFCP_REQUEST_STATUS,     0, &oreqstatus,\n\t\t\t      BFCP_STATUS_INFO,        0, \"OK\",\n\n\t\t\t      BFCP_FLOOR_REQ_STATUS,   2, &floorid1,\n\t\t\t      BFCP_REQUEST_STATUS,     0, &reqstatus1,\n\t\t\t      BFCP_STATUS_INFO,        0, \"ok\",\n\n\t\t\t      BFCP_FLOOR_REQ_STATUS,   2, &floorid2,\n\t\t\t      BFCP_REQUEST_STATUS,     0, &reqstatus2,\n\t\t\t      BFCP_STATUS_INFO,        0, \"ja\",\n\n\t\t\t      BFCP_BENEFICIARY_INFO,   2, &bfid,\n\t\t\t      BFCP_USER_DISP_NAME,     0, \"a\",\n\t\t\t      BFCP_USER_URI,           0, \"b\",\n\n\t\t\t      BFCP_REQUESTED_BY_INFO,  2, &rbid,\n\t\t\t      BFCP_USER_DISP_NAME,     0, \"c\",\n\t\t\t      BFCP_USER_URI,           0, \"d\",\n\n\t\t\t      BFCP_PRIORITY,           0, &prio,\n\n\t\t\t      BFCP_PART_PROV_INFO,     0, \"x\");\n\tif (err)\n\t\tgoto out;\n\n\terr = bfcp_msg_encode(mb, 1, false, BFCP_HELLO,\n\t\t\t      0x01020304, 0xfedc, 0xba98, 0);\n\tif (err)\n\t\tgoto out;\n\n\tif (mb->end != sz) {\n\t\tDEBUG_WARNING(\"expected %u bytes, got %u bytes\\n\",\n\t\t\t      sz, mb->end);\n\n\t\t(void)re_printf(\"\\nEncoded message:\\n\");\n\t\thexdump(stderr, mb->buf, mb->end);\n\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\tif (!err) {\n\t\tn = memcmp(mb->buf, bfcp_msg, mb->end);\n\t\tif (0 != n) {\n\t\t\terr = EBADMSG;\n\t\t\tDEBUG_WARNING(\"error offset: %d\\n\", n);\n\t\t}\n\t}\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"BFCP encode error: %m\\n\", err);\n\n\t\t(void)re_printf(\"\\nReference message:\\n\");\n\t\thexdump(stderr, bfcp_msg, sz);\n\n\t\t(void)re_printf(\"\\nEncoded message:\\n\");\n\t\thexdump(stderr, mb->buf, mb->end);\n\t\tgoto out;\n\t}\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nint test_bfcp_bin(void)\n{\n\tstatic const uint8_t msg[] =\n\n\t\t\"\\x20\\x04\\x00\\x04\"\n\t\t\"\\x00\\x00\\x00\\x01\"\n\t\t\"\\x00\\x01\\x00\\x01\"\n\n\t\t\"\\x1e\\x10\\x00\\x01\"\n\t\t\"\\x24\\x08\\x00\\x01\"\n\t\t\"\\x0a\\x04\\x03\\x00\"\n\t\t\"\\x22\\x04\\x00\\x02\"\n\n\t\t\"\";\n\tint err = 0;\n\n\terr |= parse_msg(msg, sizeof(msg) - 1);\n\terr |= parse_msg(bfcp_msg, sizeof(bfcp_msg) - 1);\n\n\treturn err;\n}\n\n\nenum handler_flags {\n\tconn_handler_called = 1u,\n\testab_handler_called = 1u << 1u,\n\trecv_handler_called = 1u << 2u,\n\tresp_handler_called = 1u << 3u,\n\tclose_handler_called = 1u << 4u\n};\n\n\nstruct test_bfcp_peer {\n\tstruct bfcp_conn *bfcp;\n\tenum bfcp_transp transp;\n\tunsigned int flags;\n\tstruct sa addr, peer;\n\tint handler_err;\n\tbool client;\n};\n\n\nstatic void test_bfcp_peer_destructor(void *arg)\n{\n\tstruct test_bfcp_peer *p = (struct test_bfcp_peer *)arg;\n\tmem_deref(p->bfcp);\n}\n\n\nstatic void receive_handler(const struct bfcp_msg *msg, void *arg)\n{\n\tstruct test_bfcp_peer *p = (struct test_bfcp_peer *)arg;\n\tp->flags |= recv_handler_called;\n\n\tDEBUG_INFO(\"Receive handler called, client: %d\\n\", (int)p->client);\n\n\tp->handler_err = bfcp_reply(p->bfcp, msg, BFCP_HELLO_ACK, 0u);\n}\n\n\nstatic void response_handler(int err, const struct bfcp_msg *msg, void *arg)\n{\n\tstruct test_bfcp_peer *p = (struct test_bfcp_peer *)arg;\n\t(void)err;\n\t(void)msg;\n\n\tp->flags |= resp_handler_called;\n\n\tDEBUG_INFO(\"Response handler called, client: %d\\n\", (int)p->client);\n\n\tre_cancel();\n}\n\n\nstatic void close_handler(int err, void *arg)\n{\n\tstruct test_bfcp_peer *p = (struct test_bfcp_peer *)arg;\n\t(void)err;\n\n\tp->flags |= close_handler_called;\n\n\tDEBUG_INFO(\"Close handler called, client: %d\\n\", (int)p->client);\n}\n\n\nstatic void established_handler(void *arg)\n{\n\tstruct test_bfcp_peer *p = (struct test_bfcp_peer *)arg;\n\tp->flags |= estab_handler_called;\n\n\tDEBUG_INFO(\"Established handler called, client: %d\\n\", (int)p->client);\n\n\tif (p->transp == BFCP_TCP && p->client) {\n\t\tp->handler_err = bfcp_request(p->bfcp, &p->peer, BFCP_VER2,\n\t\t\tBFCP_HELLO, 0u, 0u, &response_handler, p, 0u);\n\t}\n}\n\n\nstatic void connection_handler(const struct sa *peer, void *arg)\n{\n\tstruct test_bfcp_peer *p = (struct test_bfcp_peer *)arg;\n\t(void)peer;\n\n\tp->flags |= conn_handler_called;\n\n\tDEBUG_INFO(\"New connection handler called, client: %d\\n\",\n\t\t   (int)p->client);\n\n\tif (p->transp == BFCP_TCP && !p->client) {\n\t\tif (!bfcp_sock(p->bfcp)) {\n\t\t\tp->handler_err = bfcp_accept(p->bfcp);\n\t\t}\n\t\telse {\n\t\t\tbfcp_reject(p->bfcp);\n\t\t\tp->handler_err = EALREADY;\n\t\t}\n\t}\n\telse {\n\t\tp->handler_err = ENOSYS;\n\t}\n}\n\n\nint test_bfcp_udp(void)\n{\n\tstruct test_bfcp_peer *cli = NULL, *srv = NULL;\n\tint err = 0;\n\n\tif (test_mode == TEST_MEMORY) {\n\t\t/* OOM testing fails because some mem_alloc fails on\n\t\t * the receiving side, and no packet gets received.\n\t\t * For UDP, this is not registered as a connection timeout. */\n\t\terr = ESKIPPED;\n\t\tgoto out;\n\t}\n\n\tcli = (struct test_bfcp_peer *)mem_zalloc(sizeof(*cli),\n\t\t&test_bfcp_peer_destructor);\n\tif (!cli) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\tcli->transp = BFCP_UDP;\n\tcli->client = true;\n\n\tsrv = (struct test_bfcp_peer *)mem_zalloc(sizeof(*srv),\n\t\t&test_bfcp_peer_destructor);\n\tif (!srv) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\tsrv->transp = BFCP_UDP;\n\n\terr = sa_set_str(&cli->addr, \"127.0.0.1\", 0);\n\tTEST_ERR(err);\n\tsrv->addr = cli->addr;\n\n\terr = bfcp_listen(&srv->bfcp, BFCP_UDP, &srv->addr, NULL, NULL,\n\t\tNULL, &receive_handler, NULL, srv);\n\tTEST_ERR(err);\n\tcli->peer = srv->addr;\n\n\terr = bfcp_connect(&cli->bfcp, BFCP_UDP, &cli->addr, &cli->peer,\n\t\tNULL, &receive_handler, NULL, cli);\n\tTEST_ERR(err);\n\tsrv->peer = cli->addr;\n\n\terr = bfcp_request(cli->bfcp, &cli->peer, BFCP_VER1, BFCP_HELLO, 0u,\n\t\t0u, &response_handler, cli, 0u);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(100);\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(resp_handler_called, cli->flags);\n\tTEST_EQUALS(recv_handler_called, srv->flags);\n\n\terr = srv->handler_err;\n\nout:\n\tmem_deref(cli);\n\tmem_deref(srv);\n\n\treturn err;\n}\n\n\nint test_bfcp_tcp(void)\n{\n\tstruct test_bfcp_peer *cli = NULL, *srv = NULL;\n\tint err = 0;\n\n\tcli = (struct test_bfcp_peer *)mem_zalloc(sizeof(*cli),\n\t\t&test_bfcp_peer_destructor);\n\tif (!cli) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\tcli->transp = BFCP_TCP;\n\tcli->client = true;\n\n\tsrv = (struct test_bfcp_peer *)mem_zalloc(sizeof(*srv),\n\t\t&test_bfcp_peer_destructor);\n\tif (!srv) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\tsrv->transp = BFCP_TCP;\n\n\terr = sa_set_str(&cli->addr, \"127.0.0.1\", 0);\n\tTEST_ERR(err);\n\tsrv->addr = cli->addr;\n\n\terr = bfcp_listen(&srv->bfcp, BFCP_TCP, &srv->addr, NULL,\n\t\t&connection_handler, &established_handler, &receive_handler,\n\t\t&close_handler, srv);\n\tTEST_ERR(err);\n\tcli->peer = srv->addr;\n\n\terr = bfcp_connect(&cli->bfcp, BFCP_TCP, &cli->addr, &cli->peer,\n\t\t&established_handler, &receive_handler, &close_handler, cli);\n\tTEST_ERR(err);\n\tsrv->peer = cli->addr;\n\n\terr = re_main_timeout(100);\n\tTEST_ERR(err);\n\n\tif (cli->handler_err) {\n\t\tDEBUG_WARNING(\"client error: %m\\n\", cli->handler_err);\n\t\terr = cli->handler_err;\n\t\tgoto out;\n\t}\n\n\tif (srv->handler_err) {\n\t\tDEBUG_WARNING(\"server error: %m\\n\", srv->handler_err);\n\t\terr = srv->handler_err;\n\t\tgoto out;\n\t}\n\n\tTEST_EQUALS((estab_handler_called | resp_handler_called), cli->flags);\n\tTEST_EQUALS((conn_handler_called | estab_handler_called |\n\t\trecv_handler_called), srv->flags);\n\n\terr = srv->handler_err;\n\nout:\n\tmem_deref(cli);\n\tmem_deref(srv);\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/btrace.c",
    "content": "/**\n * @file btrace.c  Backtrace testcode\n *\n * Copyright (C) 2025 Alfred E. Heggestad\n */\n\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"btrace\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int devnull_handler(const char *p, size_t size, void *arg)\n{\n\t(void)p;\n\t(void)size;\n\t(void)arg;\n\n\treturn 0;\n}\n\n\nint test_btrace(void)\n{\n\tif (test_mode == TEST_THREAD)\n\t\treturn ESKIPPED;\n\n\tstatic struct re_printf pf_devnull = {\n\t\t.vph = devnull_handler\n\t};\n\tstruct btrace btraces = {0};\n\n\tint err = btrace(&btraces);\n\tTEST_ERR(err);\n\n\terr = btrace_print(&pf_devnull, &btraces);\n\tTEST_ERR(err);\n\n\terr = btrace_println(&pf_devnull, &btraces);\n\tTEST_ERR(err);\n\n\terr = btrace_print_json(&pf_devnull, &btraces);\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/combo/dtls_turn.c",
    "content": "/**\n * @file combo/dtls_turn.c  DTLS over TURN combination test\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"dtls_turn\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/*\n * Combined test of DTLS over TURN, involving two agents.\n *\n *   Agent A:                          Agent B:\n *   -------                           -------\n *\n *\n *     DTLS                             DTLS\n *    Client                           Server\n *      |                                 |\n *      |                                 |\n *      |               .................UDP\n *     TURN           TURN\n *    Client         Server\n *      |              |\n *      |              |\n *     UDP............UDP\n */\n\n\nenum {\n\tLAYER_DTLS =  100,\n\tLAYER_TURN = -100\n};\n\n\nstruct agent {\n\n\t/* DTLS layer: */\n\tstruct tls *tls;\n\tstruct dtls_sock *dtls_sock;\n\tstruct tls_conn *dtls_conn;\n\tbool dtls_active;\n\tunsigned dtls_n_conn;\n\tunsigned dtls_n_estab;\n\tunsigned dtls_n_recv;\n\n\t/* TURN layer: */\n\tbool use_turn;\n\tbool turn_channels;\n\tstruct turnc *turnc;\n\tstruct turnserver *turnsrv;\n\tunsigned turn_n_alloc_resp;\n\tunsigned turn_n_perm_resp;\n\tunsigned turn_n_chan_resp;\n\n\t/* common stuff: */\n\tstruct agent *peer;\n\tstruct udp_sock *us;\n\tstruct sa addr;\n\tunsigned udp_n_recv;\n\tint err;\n};\n\n\nstatic int agent_start(struct agent *ag);\n\n\nstatic void complete_test(struct agent *ag, int err)\n{\n\tag->err = err;\n\tre_cancel();\n}\n\n\nstatic bool are_established(struct agent *ag)\n{\n\treturn ag->dtls_n_estab && ag->peer->dtls_n_estab;\n}\n\n\nstatic void dtls_estab_handler(void *arg)\n{\n\tstruct agent *ag = arg;\n\n\t++ag->dtls_n_estab;\n\n\tif (are_established(ag)) {\n\t\tre_cancel();\n\t}\n}\n\n\nstatic void dtls_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct agent *ag = arg;\n\tint err;\n\n\t++ag->dtls_n_recv;\n\n\tif (!ag->dtls_active) {\n\n\t\t/* ECHO SERVER */\n\t\terr = dtls_send(ag->dtls_conn, mb);\n\t\tif (err) {\n\t\t\tcomplete_test(ag, err);\n\t\t}\n\t}\n}\n\n\nstatic void dtls_close_handler(int err, void *arg)\n{\n\tstruct agent *ag = arg;\n\t(void)err;\n\n\tag->dtls_conn = mem_deref(ag->dtls_conn);\n}\n\n\nstatic void dtls_conn_handler(const struct sa *src, void *arg)\n{\n\tstruct agent *ag = arg;\n\tint err;\n\t(void)src;\n\n\tTEST_ASSERT(!ag->dtls_active);\n\n\t++ag->dtls_n_conn;\n\n\tTEST_ASSERT(ag->dtls_conn == NULL);\n\n\terr = dtls_accept(&ag->dtls_conn, ag->tls, ag->dtls_sock,\n\t\t\t  dtls_estab_handler, dtls_recv_handler,\n\t\t\t  dtls_close_handler, ag);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tcomplete_test(ag, err);\n}\n\n\nstatic void turnc_perm_handler(void *arg)\n{\n\tstruct agent *ag = arg;\n\n\t++ag->turn_n_perm_resp;\n\n\t/* Permission has been granted, we can start DTLS */\n\tagent_start(ag);\n}\n\n\nstatic void turnc_chan_handler(void *arg)\n{\n\tstruct agent *ag = arg;\n\n\t++ag->turn_n_chan_resp;\n\n\t/* Channel has been created, we can start DTLS */\n\tagent_start(ag);\n}\n\n\nstatic bool is_turn_ready(struct agent *ag)\n{\n\tif (ag->use_turn)\n\t\treturn ag->turn_n_alloc_resp;\n\telse\n\t\treturn true;\n}\n\n\nstatic bool are_turn_ready(struct agent *ag)\n{\n\treturn is_turn_ready(ag) && is_turn_ready(ag->peer);\n}\n\n\nstatic int agent_permchan(struct agent *ag)\n{\n\tint err;\n\n\t/* Channels or Permission is needed for sending data */\n\tif (ag->turn_channels) {\n\t\terr = turnc_add_chan(ag->turnc, &ag->peer->addr,\n\t\t\t\t     turnc_chan_handler, ag);\n\t}\n\telse {\n\t\terr = turnc_add_perm(ag->turnc, &ag->peer->addr,\n\t\t\t\t     turnc_perm_handler, ag);\n\t}\n\n\treturn err;\n}\n\n\nstatic void turnc_handler(int err, uint16_t scode, const char *reason,\n\t\t\t  const struct sa *relay_addr,\n\t\t\t  const struct sa *mapped_addr,\n\t\t\t  const struct stun_msg *msg,\n\t\t\t  void *arg)\n{\n\tstruct agent *ag = arg;\n\n\t(void)reason;\n\t(void)mapped_addr;\n\t(void)msg;\n\n\t++ag->turn_n_alloc_resp;\n\n\tif (err || scode) {\n\t\tcomplete_test(ag, err ? err : EPROTO);\n\t\treturn;\n\t}\n\n\t/* Public address must be updated */\n\tag->addr = *relay_addr;\n\n\tif (are_turn_ready(ag)) {\n\n\t\tagent_permchan(ag);\n\t\tagent_permchan(ag->peer);\n\t}\n}\n\n\n/* in this test we expect no UDP packets */\nstatic void udp_recv(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct agent *ag = arg;\n\t(void)src;\n\t(void)mb;\n\n\t++ag->udp_n_recv;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct agent *ag = arg;\n\n\tmem_deref(ag->dtls_conn);\n\tmem_deref(ag->dtls_sock);\n\tmem_deref(ag->tls);\n\n\tmem_deref(ag->turnc);\n\tmem_deref(ag->turnsrv);\n\n\tmem_deref(ag->us);\n}\n\n\nstatic int agent_alloc(struct agent **agp, uint16_t lport,\n\t\t       bool use_turn, bool turn_channels, bool dtls_active)\n{\n\tstruct agent *ag;\n\tint err;\n\n\tag = mem_zalloc(sizeof(*ag), destructor);\n\tif (!ag)\n\t\treturn ENOMEM;\n\n\t/* allocate common */\n\terr = sa_set_str(&ag->addr, \"127.0.0.1\", lport);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_listen(&ag->us, &ag->addr, udp_recv, ag);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_local_get(ag->us, &ag->addr);\n\tif (err)\n\t\tgoto out;\n\n\t/* allocate TURN */\n\tag->use_turn = use_turn;\n\tif (use_turn) {\n\t\tag->turn_channels = turn_channels;\n\n\t\terr = turnserver_alloc(&ag->turnsrv, \"127.0.0.1\");\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = turnc_alloc(&ag->turnc, NULL, IPPROTO_UDP, ag->us,\n\t\t\t\t  LAYER_TURN, &ag->turnsrv->laddr,\n\t\t\t\t  \"username\", \"password\", 600,\n\t\t\t\t  turnc_handler, ag);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\t/* allocate DTLS */\n\tag->dtls_active = dtls_active;\n\n\terr = tls_alloc(&ag->tls, TLS_METHOD_DTLSV1, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\terr = tls_set_certificate(ag->tls, test_certificate_ecdsa,\n\t\t\t\t  strlen(test_certificate_ecdsa));\n\tif (err)\n\t\tgoto out;\n\n\terr = dtls_listen(&ag->dtls_sock, NULL, ag->us, 4, LAYER_DTLS,\n\t\t\t  dtls_conn_handler, ag);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(ag);\n\telse if (agp)\n\t\t*agp = ag;\n\n\treturn err;\n}\n\n\nstatic int agent_start(struct agent *ag)\n{\n\tint err = 0;\n\n\tif (ag->dtls_active) {\n\n\t\tTEST_ASSERT(ag->dtls_conn == NULL);\n\n\t\terr = dtls_connect(&ag->dtls_conn, ag->tls, ag->dtls_sock,\n\t\t\t\t   &ag->peer->addr, dtls_estab_handler,\n\t\t\t\t   dtls_recv_handler,\n\t\t\t\t   dtls_close_handler, ag);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic int agent_verify(struct agent *ag)\n{\n\tint err = 0;\n\n\t/* common stuff */\n\tTEST_EQUALS(0, ag->err);\n\tTEST_EQUALS(0, ag->udp_n_recv);\n\n\t/* TURN */\n\tif (ag->use_turn) {\n\n\t\tTEST_EQUALS(1, ag->turn_n_alloc_resp);\n\t\tTEST_EQUALS(ag->turn_channels ? 0 : 1, ag->turn_n_perm_resp);\n\t\tTEST_EQUALS(ag->turn_channels ? 1u : 0, ag->turn_n_chan_resp);\n\n\t\tTEST_ASSERT(ag->turnsrv->n_allocate >= 1);\n\t\tif (ag->turn_channels) {\n\t\t\tTEST_ASSERT(ag->turnsrv->n_chanbind >= 1);\n\t\t\tTEST_ASSERT(ag->turnsrv->n_createperm == 0);\n\t\t\tTEST_EQUALS(0, ag->turnsrv->n_send);\n\t\t\tTEST_ASSERT(ag->turnsrv->n_raw >= 2);\n\t\t}\n\t\telse {\n\t\t\tTEST_ASSERT(ag->turnsrv->n_chanbind == 0);\n\t\t\tTEST_ASSERT(ag->turnsrv->n_createperm >= 1);\n\t\t\tTEST_EQUALS(2, ag->turnsrv->n_send);\n\t\t\tTEST_EQUALS(0, ag->turnsrv->n_raw);\n\t\t}\n\t}\n\n\t/* DTLS */\n\tTEST_ASSERT(ag->dtls_conn != NULL);\n\tTEST_EQUALS(ag->dtls_active ? 0 : 1, ag->dtls_n_conn);\n\tTEST_EQUALS(1, ag->dtls_n_estab);\n\tTEST_EQUALS(0, ag->dtls_n_recv);\n\n out:\n\treturn err;\n}\n\n\nstatic bool have_dtls_support(enum tls_method method)\n{\n\tstruct tls *tls = NULL;\n\tint err;\n\n\terr = tls_alloc(&tls, method, NULL, NULL);\n\n\tmem_deref(tls);\n\n\treturn err != ENOSYS;\n}\n\n\nint test_dtls_turn(void)\n{\n\tstruct agent *a = NULL, *b = NULL;\n\tint err = 0;\n\n\tif (!have_dtls_support(TLS_METHOD_DTLSV1)) {\n\t\tre_fprintf(stderr, \"skip DTLS/TURN test\\n\");\n\t\treturn ESKIPPED;\n\t}\n\n\terr = agent_alloc(&a, 0, true, true, true);\n\tif (err)\n\t\tgoto out;\n\terr = agent_alloc(&b, 0, false, false, false);\n\tif (err)\n\t\tgoto out;\n\n\t/* connect the 2 agents */\n\tif (a)\n\t\ta->peer = b;\n\tif (b)\n\t\tb->peer = a;\n\n\t/* start it! */\n\terr = re_main_timeout(1000);\n\tif (err)\n\t\tgoto out;\n\n\tif (a) {\n\t\tTEST_EQUALS(0, a->err);\n\t}\n\tif (b) {\n\t\tTEST_EQUALS(0, b->err);\n\t}\n\n\t/* verify results after test is complete */\n\terr |= agent_verify(a);\n\terr |= agent_verify(b);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(b);\n\tmem_deref(a);\n\treturn err;\n}\n"
  },
  {
    "path": "test/conf.c",
    "content": "/**\n * @file conf.c  Testcode for Configuration module\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n#define DEBUG_MODULE \"test_conf\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int conf_handler(const struct pl *val, void *arg)\n{\n\tuint32_t *count = arg;\n\tint err = 0;\n\n\t++(*count);\n\n\tASSERT_EQ(*count, pl_u32(val));\n\n out:\n\treturn err;\n}\n\n\nint test_conf(void)\n{\n\tstatic const char *cfg =\n\t\t\"string_val\\trattarei\\n\"\n\t\t\"u32_val       42\\n\"\n\t\t\"i32_val       -23\\n\"\n\t\t\"float_val     1.5\\n\"\n\t\t\"bool_val_1    true\\n\"\n\t\t\"bool_val_2    Yes\\n\"\n\t\t\"bool_val_3    1\\n\"\n\t\t\"bool_val_4    false\\n\"\n\t\t\"apply_val     1\\n\"\n\t\t\"apply_val     2\\n\"\n\t\t;\n\tchar str[256];\n\tstruct conf *conf;\n\tstruct pl pl;\n\tuint32_t u32;\n\tint32_t i32;\n\tdouble fl;\n\tint err;\n\n\terr = conf_alloc_buf(&conf, (uint8_t *)cfg, strlen(cfg));\n\tif (err)\n\t\treturn err;\n\n\terr = conf_get_str(conf, \"string_val\", str, sizeof(str));\n\tTEST_ERR(err);\n\tif (strcmp(str, \"rattarei\"))\n\t\tgoto badmsg;\n\n\terr = conf_get_u32(conf, \"u32_val\", &u32);\n\tTEST_ERR(err);\n\tTEST_EQUALS(42, u32);\n\n\terr = conf_get_i32(conf, \"i32_val\", &i32);\n\tTEST_ERR(err);\n\tTEST_EQUALS(-23, i32);\n\n\terr = conf_get_float(conf, \"float_val\", &fl);\n\tTEST_ERR(err);\n\tTEST_EQUALS(1.5, fl);\n\n\tbool val = false;\n\n\terr = conf_get_bool(conf, \"bool_val_1\", &val);\n\tTEST_ERR(err);\n\tASSERT_TRUE(val);\n\n\terr = conf_get_bool(conf, \"bool_val_2\", &val);\n\tTEST_ERR(err);\n\tASSERT_TRUE(val);\n\n\terr = conf_get_bool(conf, \"bool_val_3\", &val);\n\tTEST_ERR(err);\n\tASSERT_TRUE(val);\n\n\terr = conf_get_bool(conf, \"bool_val_4\", &val);\n\tTEST_ERR(err);\n\tASSERT_TRUE(!val);\n\n\tuint32_t count = 0;\n\n\terr = conf_apply(conf, \"apply_val\", conf_handler, &count);\n\tTEST_ERR(err);\n\tASSERT_EQ(2, count);\n\n\t/* Non-existing parameters */\n\tif (0 == conf_get(conf, \"rattarei\", &pl))\n\t\tgoto badmsg;\n\n\tint error = conf_get(NULL, NULL, NULL);\n\tASSERT_EQ(EINVAL, error);\n\terror = conf_get_str(NULL, NULL, NULL, 0);\n\tASSERT_EQ(EINVAL, error);\n\terror = conf_get_u32(NULL, NULL, NULL);\n\tASSERT_EQ(EINVAL, error);\n\terror = conf_get_i32(NULL, NULL, NULL);\n\tASSERT_EQ(EINVAL, error);\n\terror = conf_get_float(NULL, NULL, NULL);\n\tASSERT_EQ(EINVAL, error);\n\n out:\n\tmem_deref(conf);\n\treturn err;\n\n badmsg:\n\tmem_deref(conf);\n\treturn EBADMSG;\n}\n"
  },
  {
    "path": "test/convert.c",
    "content": "/**\n * @file convert.c Conversion Testcode\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n#include <re.h>\n#include \"test.h\"\n\n#define DEBUG_MODULE \"testconvert\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_try_into(void)\n{\n\tint err = 0;\n\tsize_t size;\n\tuint16_t u16 = 0;\n\tint i;\n\n#if __STDC_VERSION__ >= 201112L /* Needs C11 support */\n\tsize = SIZE_MAX;\n\terr  = try_into(u16, size);\n\tTEST_EQUALS(ERANGE, err);\n#endif\n\tsize = 5000;\n\terr  = try_into_u16_from_size(&u16, size);\n\tTEST_ERR(err);\n\tTEST_EQUALS(size, u16);\n\n\tsize = SIZE_MAX;\n\terr  = try_into_u16_from_size(&u16, size);\n\tTEST_EQUALS(ERANGE, err);\n\n\t/* Testing int -> uint16_t */\n\ti   = INT_MAX;\n\terr = try_into_u16_from_int(&u16, i);\n\tTEST_EQUALS(ERANGE, err);\n\n\ti   = -50;\n\terr = try_into_u16_from_int(&u16, i);\n\tTEST_EQUALS(ERANGE, err);\n\n\t/* Testing size_t -> int */\n\tsize = SIZE_MAX;\n\terr  = try_into_int_from_size(&i, size);\n\tTEST_EQUALS(ERANGE, err);\n\n\tsize = INT_MAX;\n\terr  = try_into_int_from_size(&i, size);\n\tTEST_ERR(err);\n\tTEST_EQUALS(INT_MAX, i);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/cplusplus.cpp",
    "content": "/**\n * @file cplusplus.cpp Emulate C++ applications\n *\n * Copyright (C) 2025 Alfred E. Heggestad\n */\n\n#include <iostream>\n#include <re_atomic.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"cplusplus\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_cplusplus(void)\n{\n\tstd::cout << \"test\\n\";\n\n\tDEBUG_NOTICE(\"%H\\n\", sys_kernel_get, nullptr);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/crc32.c",
    "content": "/**\n * @file crc32.c CRC32 Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"testcrc32\"\n#define DEBUG_LEVEL 4\n#include <re_dbg.h>\n\n\nint test_crc32(void)\n{\n\tconst struct {\n\t\tconst char *str;\n\t\tuint32_t crc;\n\t} testv[] = {\n\t\t{\"string\",                          0x9ebeb2a9 },\n\t\t{\"hei\",                             0x95610594 },\n\t\t{\"0ndka98d198aloidks9zaz1oqs5jilk\", 0x92a398f6 },\n\t};\n\tsize_t i;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tuint32_t crc;\n\n\t\tcrc = re_crc32(0L, (uint8_t *)testv[i].str,\n\t\t\t       (unsigned int)str_len(testv[i].str));\n\n\t\tif (testv[i].crc != crc) {\n\t\t\tDEBUG_WARNING(\"testcase %u: CRC-32 failed\"\n\t\t\t\t      \" (expected 0x%08x, got 0x%08x)\\n\",\n\t\t\t\t      i, testv[i].crc, crc);\n\t\t\treturn EINVAL;\n\t\t}\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/data/client.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDV39seOcn4ksoR\nmkUJTjPMqBjVQXZNFWhfw15zxoC7Se1XReubtJWT206cismYVIZHF+oZ+Wznx4ew\nXYpXvabRR7hhaNfnm+Ez0ArJo6+czUi95mytpX8U4ccwQsUzWEwiVMKb2tnmd4BI\nw8QqYenAoOqEJiKcNuza9sP6jXQomL8V0CpMYqjKs/3/ah3eCpm3a7upoJdcZoU1\nH/10Nh1DAUZw/P8uF07T7OqQhUXLmVkW141NtGk6fxMd9hJN/tPnyKp0KFA1yg96\ns3Y4D4o8YF0TBsXXc7D+7YpTO2t91Fo8PwBjkPurSq2ZkMAM8Oy8FZNU5GCFtmrq\njhfnIolLAgMBAAECggEBAIxBS07dEuhiSwWUJ6XclPenXPGtBkmyukniszQ1fwJT\njsPm5IWtvVHDRiA+m8m71HsW4by654JwRTmRfkHf61/fKrkmqkAG53609f4Zy/aG\nYSe3zplpqJcnIRszDmOHihYRg7X1pWQQBh6x0xkAMOzUGjcjUxsiod1OamMLFJvx\nrJQqWRKsGF2lzRSZzz3a+q4bb5xmwUHiD6thc4xN16nAR5/Qkxk0fOq/K7+dgLzd\nR08wOKHjIyYcMHJg24yuhHBvx2Lq8uYa92nux5JQPEbr/LKBcbNEMDQGTa63Z3yN\nrE2v3LQeLgzfWqz1L1e4fspnAw7rZpV2lsjRcxLFP5ECgYEA71EzROLYNLFxT46M\nJzb7Et//dde4SIDMTo91dXK8JHneZ8Kii8pILz8EyhP9/VdDsGmnWzmaO8RfLQG5\n2nVutfwdxPapmchjGq/a4mEVh64LRaKUV5LlTP2mSiiPs+I2F8QRCe5n6wbZ4SnI\n5Uw9Ub8PBm5vX+T9y5wobVpPRBcCgYEA5MicMqN9sDzFuIpwaWzUB5ds26/3BdCi\nb6eNu1pBnHxSMTnm4GhfzpC/oaSuEePvqHuuKl7APViViYGw60V+5VPq5P9kos/p\ng7I7gbCL0o0jttJAogOSMa7TLu8urSkOIVTC0HFzPygYdDTiHvxElZ9FWO+RW8gQ\nJWpL0MQTgO0CgYAoapI6BAV3gLAeO3i39rUYTvQZtsDMndPjQJ8ZorLbhillZJ2G\n9jaSpBXu+sleS6qG1JEK+wNtLJDWTQBSQJrd6SIt7WpfKlRyYViZ+sNkEqScsgRT\ndd97zSqUQ51hR8bS5K3r+3NZYeLR//iJoQKKF7Y149izXTzvcgPx09qnKQKBgQCR\nzDqmlsQRnRkP+sGane0djWWlNyWAQi5SY4ASRnmLpTqn/lNg7YMo29wTwiPgaNch\nEviyLcHrFlnt0IWT2az9rpYCK7PRghg5gksj4gOpQaLh7mmcFhSDrvsMbQbc+U30\n9dRVDAe61u2GmPEFeBSpGWATRlDnIb8QaQY528rmHQKBgDIT/YOp07bV2NsHOqCL\nckGBftZ3QMIFO6nfAZ3tC6Yh/upuVkH+fhr9jhVp7vr6ec94BeZEOjXjkHoCFmHH\n+BICeYwh0ZDdg5EkIFHkoFOnBTp5slSzcmR4DNxloLgr+RSQClFyp+++bBpjRRhD\nG10mQDaTTI/s9j2NC4tazHGQ\n-----END PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIID5TCCAs2gAwIBAgIUc9km/TNqRm8U3Aw5UYW/nH2LPw0wDQYJKoZIhvcNAQEL\nBQAwgYAxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMQ8wDQYDVQQK\nDAZSZXRlc3QxDzANBgNVBAsMBlJldGVzdDEeMBwGA1UEAwwVTXIgUmV0ZXN0IENs\naWVudCBDZXJ0MRowGAYJKoZIhvcNAQkBFgtyZUB0ZXN0Lm9yZzAgFw0yMTA0MTMw\nNzI2NTBaGA8yMTIxMDMyMDA3MjY1MFowgYAxCzAJBgNVBAYTAkFVMRMwEQYDVQQI\nDApTb21lLVN0YXRlMQ8wDQYDVQQKDAZSZXRlc3QxDzANBgNVBAsMBlJldGVzdDEe\nMBwGA1UEAwwVTXIgUmV0ZXN0IENsaWVudCBDZXJ0MRowGAYJKoZIhvcNAQkBFgty\nZUB0ZXN0Lm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANXf2x45\nyfiSyhGaRQlOM8yoGNVBdk0VaF/DXnPGgLtJ7VdF65u0lZPbTpyKyZhUhkcX6hn5\nbOfHh7Bdile9ptFHuGFo1+eb4TPQCsmjr5zNSL3mbK2lfxThxzBCxTNYTCJUwpva\n2eZ3gEjDxCph6cCg6oQmIpw27Nr2w/qNdCiYvxXQKkxiqMqz/f9qHd4Kmbdru6mg\nl1xmhTUf/XQ2HUMBRnD8/y4XTtPs6pCFRcuZWRbXjU20aTp/Ex32Ek3+0+fIqnQo\nUDXKD3qzdjgPijxgXRMGxddzsP7tilM7a33UWjw/AGOQ+6tKrZmQwAzw7LwVk1Tk\nYIW2auqOF+ciiUsCAwEAAaNTMFEwHQYDVR0OBBYEFB1cbVjA5+JuJqbXD7IJZrMX\nNij9MB8GA1UdIwQYMBaAFB1cbVjA5+JuJqbXD7IJZrMXNij9MA8GA1UdEwEB/wQF\nMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAA0Z4oRtdJ56W3gQDLWlYoTF4zDe5Y+W\nKged1kdj8UTOttBrhe3mXpH2eDNu6V+NE+GD6kwdNS5mmRDl66pDKzaEVjH1esph\nYVLvkVht7CCK9i9StvB8LgPDadZOlksNFkLvb++57dOBTQONmWwyRSSdvJKoOZRo\neMZKuYyCn/EWHf1XeAXBQfS05kwAK8uPHFig82aXq7cTAXvztASmQ9MxRbJSNzId\n16/Tk/DwqLYPbSbVclzsZo/WNjKFmZEztBryb4GnC9KuTev9P8OmjgTeFzo5tA8S\nlo5cQKmz+oObqz1w1cveC8nxl4bp4h1ZTPSxlEg//kv7GCCIrcMgNWU=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/data/client_wrongkey.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDB3Opci28FZeom\nfLfQkiCovnFXAxJ+6RTik2kbUXKc6x8TCTcAIe7jWWojgYWG5FE4pYmllSPpxuCf\nutgSKHbInKwiF11h2PlYfOjyVzswl/MYO01i+JUztdj2M6nWnSeYxZAdyHO4ZV0s\n0K5Mnj/LsQ+sG2brvk9CWdrTim58nyv/RqnRp4I4NlJUOAZ1Z/rBNh6LNS+OopFn\n4hGLKO4ur7ol8AaMK/al+smbPZroPSsITzTvvKX8KYvoyOsfUlRI9GGZczNYfh7P\nerHFVnLvlChFGuBKo1eaHh9izN6WBHTeepfDkK/JGDhUcbemEcmrCzoi/SyVGG6M\niagyAFM3AgMBAAECggEAEhm1TBS8te1REmzcoL20DO1TGcOwq2bJy2nmC2XsZJhT\nW2yl1P5WFqYXkd8a/4A70SUrl+k35DDsch145SEgDhUKwVgbIPKoPUnUWiQo452c\nVevbit0Y2S4mZr5mIiKkOtBfZ1h67YGRVIjbbU3tOPB8PR6F2ob3UYOgOdYYAMyV\n7OIoyHq4I1qx09bFuDSTWBeUE2o0tUEcw0RRLWwk5SJhXxhyOBI+48LCHx9/N3lo\n1XQCCx2CcItiXH5Jse09Zgo3JigAoxrsGEarZkUp1sk/t+1egPOFGAFXJMN3SMq0\nt9Qr+upHK3HcLH7U6HpUXxaTRbPhWe/avp/YNHvy+QKBgQD44bqSQdxjk0NzGOOI\nMjsXj9CVRmVAa/8PbB49KArCUfD39yBH95bc20YYiGZwjZ1uelfgcFx/FbvmYvor\nZ3sik5fJMXPLjNkRVY9zjxJUr0D+m7jVaFUUfEvmCBRCZma6Z84Tdh023pHDogJx\nDLNb8rBRDT5M7VmRh+0wolqZ+wKBgQDHaFkbV5XgmN1m7afZzW8XoOUsbti2+I7s\nW9xpQPcqgwkKKgy6wGh2YGm5kkqptOxz2Qc1EqkCNPaqlw8gxVCXXEPnVkCjSHZw\nHo6WfYhZfpMRg68r9q3mQijHQsPSBPFhfvZTOWZ/1K9jZ0udJY3rduHx5qGAe+RS\nGXaR55UC9QKBgQDzZebNtKVxqIq1XGjhQKaz000dP9wz1ap7DbTBAJENG4/7IW8r\nksKyuatiwLrWZljJu9KHR7vd+YZquO+KVUiCNy+hbsCdF+EN58xEKm617TM2DVsF\nMmTKhQjBLbOKgJUExhIuzDuj9qU9WwriPpaGXOD8U2RTXWhUQEfCYXUT0wKBgCUo\n//oFb2vEcPX+cdo3220G0c5e9cnbNh872y5OKgFlG+kl/utKce0leG5ZocpQagQI\nvLwaNx6vKgyOTAZqXuNmEU1qGG+VUGUCfMX0rNUorlWEOb+yn6OaTqkbewyHWUg2\n9cZ6muxVgT3AASpO1ky8Ac0nko80B095aUw1YOKFAoGAfEzhCnQKzqAwAxsiWNLO\n79gbo9Y4BMYf6LWfYP3iQP4VeUveIbG1F4GF6ERJtR0cCyIPDnVhyaJ0TqKiI/7J\nNXk3t0W+o1e/PJ/F/pv/2VcQ43RHSTBqU9ntxbcoUg9XBQ/6cBkR1SDo9rGr5Q4M\nIyrkUC90uN88FSNvs9SgB3g=\n-----END PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIDyzCCArOgAwIBAgIUZowycnjMKiu6PyWEKsZk4N8eiucwDQYJKoZIhvcNAQEL\nBQAwdDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDzANBgNVBAoM\nBlJldGVzdDEPMA0GA1UECwwGUmV0ZXN0MRIwEAYDVQQDDAlNciBSZXRlc3QxGjAY\nBgkqhkiG9w0BCQEWC3JlQHRlc3Qub3JnMCAXDTIxMDQxMzA2NTIwOFoYDzIxMjEw\nMzIwMDY1MjA4WjB0MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEP\nMA0GA1UECgwGUmV0ZXN0MQ8wDQYDVQQLDAZSZXRlc3QxEjAQBgNVBAMMCU1yIFJl\ndGVzdDEaMBgGCSqGSIb3DQEJARYLcmVAdGVzdC5vcmcwggEiMA0GCSqGSIb3DQEB\nAQUAA4IBDwAwggEKAoIBAQCsV912ab5sSX63FvHEGecl+FDdSmbj8jfzm0jchcpi\nyowOp4EE2m3yG49dvLXXvDQLfydecU0A/MTn7YHXc1mSoa3EyISlU8/i7JTYICxo\najy2EIpT6F1BW10Ey7GAVlFPcsnx8WrVtpOS51NCvx9yoLeiYF2b+tsREiaYI0Ez\n7eFoq1HT9K5XfzVLJDFC6J/p0Grj+iAoPCQhWs8VOnQNHAmntRevHZdtDcoe6AcR\nCeQfrF8pJtcFIhqfXCa5ai+F2bR+oPQL1wta7D8F4O8C80KAC+T6gz8sT/w5Armn\nXj+yn2YJ+F2zxteqSswQ7gYLch0Ap7CUgKdCOpjufcj5AgMBAAGjUzBRMB0GA1Ud\nDgQWBBSubGzpM/DpCdUwl6W5NabXZqaAIjAfBgNVHSMEGDAWgBSubGzpM/DpCdUw\nl6W5NabXZqaAIjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCl\ntYaFZ3zOSSkVoS2JmdXGfsQLEiAPH34YRCitB7us/Z4Y+ScQG+oVQLfsOfVPrQk3\n9w1x5S4JChe5Oh6vJAgpU/qkT9gpvq3V3EXEGnUmks7HrDSsNKv5iqfwrE+IWDr6\nN+boBJblRbnov0fKP79TL2vZ4Uc0ZQRmUU/uoXvZqdOY5XWpRN1ACrZjF63dQKKT\nO09nsQmERDsbJ10I4iz4wKI8F22twau3megaZQctAj6LwUFaV8pja0sunBgozBep\nfZQP6E0WG8b8Ai4fBCh+J41f+htqLMYZWIef1z1koXS5wSKqGjff7/eZ5tbHDuL5\nHUySLtoJta/uY0iWXlFo\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/data/fstab.json",
    "content": "{\n    \"/\": {\n        \"storage\": {\n            \"type\": \"disk\",\n            \"device\": \"/dev/sda1\"\n        },\n        \"fstype\": \"btrfs\",\n        \"readonly\": true\n    },\n    \"/var\": {\n        \"storage\": {\n            \"type\": \"disk\",\n            \"label\": \"8f3ba6f4-5c70-46ec-83af-0d5434953e5f\"\n        },\n        \"fstype\": \"ext4\",\n        \"options\": [ \"nosuid\" ]\n    },\n    \"/tmp\": {\n        \"storage\": {\n            \"type\": \"tmpfs\",\n            \"sizeInMB\": 64\n        }\n    },\n    \"/var/www\": {\n        \"storage\": {\n            \"type\": \"nfs\",\n            \"server\": \"my.nfs.server\",\n            \"remotePath\": \"/exports/mypath\"\n        }\n    }\n}"
  },
  {
    "path": "test/data/menu.json",
    "content": "{\"menu\": {\n    \"header\": \"SVG Viewer\",\n    \"items\": [\n        {\"id\": \"Open\"},\n        {\"id\": \"OpenNew\", \"label\": \"Open New\"},\n        null,\n        {\"id\": \"ZoomIn\", \"label\": \"Zoom In\"},\n        {\"id\": \"ZoomOut\", \"label\": \"Zoom Out\"},\n        {\"id\": \"OriginalView\", \"label\": \"Original View\"},\n        null,\n        {\"id\": \"Quality\"},\n        {\"id\": \"Pause\"},\n        {\"id\": \"Mute\"},\n        null,\n        {\"id\": \"Find\", \"label\": \"Find...\"},\n        {\"id\": \"FindAgain\", \"label\": \"Find Again\"},\n        {\"id\": \"Copy\"},\n        {\"id\": \"CopyAgain\", \"label\": \"Copy Again\"},\n        {\"id\": \"CopySVG\", \"label\": \"Copy SVG\"},\n        {\"id\": \"ViewSVG\", \"label\": \"View SVG\"},\n        {\"id\": \"ViewSource\", \"label\": \"View Source\"},\n        {\"id\": \"SaveAs\", \"label\": \"Save As\"},\n        null,\n        {\"id\": \"Help\"},\n        {\"id\": \"About\", \"label\": \"About Adobe CVG Viewer...\"}\n    ]\n}}"
  },
  {
    "path": "test/data/rfc7159.json",
    "content": "  {\n        \"Image\": {\n            \"Width\":  800,\n            \"Height\": 600,\n            \"Title\":  \"View from 15th Floor\",\n            \"Thumbnail\": {\n                \"Url\":    \"http://www.example.com/image/481989943\",\n                \"Height\": 125,\n                \"Width\":  100\n            },\n            \"Animated\" : false,\n            \"IDs\": [116, 943, 234, 38793]\n          }\n      }\n"
  },
  {
    "path": "test/data/server-ecdsa.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIICBzCCAa2gAwIBAgIUZy0UqzsDq7fGUsZh6QxkXgCa030wCgYIKoZIzj0EAwIw\nWTELMAkGA1UEBhMCTk8xEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu\ndGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJMTI3LjAuMC4xMB4XDTE5\nMDUyNDE5NTM0OFoXDTI5MDUyMTE5NTM0OFowWTELMAkGA1UEBhMCTk8xEzARBgNV\nBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0\nZDESMBAGA1UEAwwJMTI3LjAuMC4xMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\ninP/oBEqBbXRxDzyk7sbh8rRJbfbXBRG2uJl2g6YhSkYZkifGyEueJ7+A9D9LfBh\nb5+lKXuJc02XQW5IwUmToqNTMFEwHQYDVR0OBBYEFH1vSH2IBZvKYNDPfPOk41Dw\nhyTWMB8GA1UdIwQYMBaAFH1vSH2IBZvKYNDPfPOk41DwhyTWMA8GA1UdEwEB/wQF\nMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhAOm79QetPxioy/S0Rk9lhPgfBslgM6f4\ntihVBSpe0FdJAiAC6Usj7p3H8dvu9Oa1gtOXSJkh1MT6pkfW21YseRWP4A==\n-----END CERTIFICATE-----\n-----BEGIN EC PARAMETERS-----\nBggqhkjOPQMBBw==\n-----END EC PARAMETERS-----\n-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMWTO9/z24fiq13MM5UF1CVD3yJjVXRe0qpTCmmZU5ppoAoGCCqGSM49\nAwEHoUQDQgAEinP/oBEqBbXRxDzyk7sbh8rRJbfbXBRG2uJl2g6YhSkYZkifGyEu\neJ7+A9D9LfBhb5+lKXuJc02XQW5IwUmTog==\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "test/data/sni/client-interm.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIICOzCCAeGgAwIBAgIUOGllk+RwWh0SaiOIlZ28iDr9CUQwCgYIKoZIzj0EAwIw\nVzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDzANBgNVBAoMBlJl\ndGVzdDEiMCAGA1UEAwwZTXIgUmV0ZXN0IEludGVybWVkaWF0ZSBDQTAgFw0yMjEy\nMjkwOTMxMDRaGA8yMTIyMTIwMzA5MzEwNFowTzELMAkGA1UEBhMCQVUxEzARBgNV\nBAgMClNvbWUtU3RhdGUxDzANBgNVBAoMBlJldGVzdDEaMBgGA1UEAwwRcmV0ZXN0\nLmNsaWVudC5vcmcwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARpvxjuwaXKPM2d\nM9G1DNYQR/cXacJw1ZlFgDT/PqolekRtjOUtFTs+2NAC5F8so3So9QS+XOBel851\nZIfSZznco4GQMIGNMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgbAMB0GA1Ud\nDgQWBBQl0oAqn8/QfeOxwRS4iic96jokKDAfBgNVHSMEGDAWgBTGQwW6wdqJ1o5Y\nG1KSgeBw+jOwPzAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG\nCCsGAQUFBwMCMAoGCCqGSM49BAMCA0gAMEUCIEkiN/wpykIISrCZtFDa3SslEJL/\ni3bUUAKrHMab52xtAiEAkA0z6aDhxITnxIfqG3RqPR/NTlyJ8JVtrLcbHFRWRzI=\n-----END CERTIFICATE-----\n-----BEGIN EC PARAMETERS-----\nBggqhkjOPQMBBw==\n-----END EC PARAMETERS-----\n-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIJ76xyjJn4C6ZaQRszt0rNwKq0LBz/Re9QZPeDyBW4r0oAoGCCqGSM49\nAwEHoUQDQgAEab8Y7sGlyjzNnTPRtQzWEEf3F2nCcNWZRYA0/z6qJXpEbYzlLRU7\nPtjQAuRfLKN0qPUEvlzgXpfOdWSH0mc53A==\n-----END EC PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIB/TCCAaOgAwIBAgIBATAKBggqhkjOPQQDAjBPMQswCQYDVQQGEwJBVTETMBEG\nA1UECAwKU29tZS1TdGF0ZTEPMA0GA1UECgwGUmV0ZXN0MRowGAYDVQQDDBFNciBS\nZXRlc3QgUm9vdCBDQTAgFw0yMjEyMjkwOTIzNTRaGA8yMTIyMTIwNDA5MjM1NFow\nVzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDzANBgNVBAoMBlJl\ndGVzdDEiMCAGA1UEAwwZTXIgUmV0ZXN0IEludGVybWVkaWF0ZSBDQTBZMBMGByqG\nSM49AgEGCCqGSM49AwEHA0IABHn8RqyvhhIdMK0o0wc+mKfO97SghjB3FlKBLjQT\n+jIOVW3YboDBgPCRwdjGFugc661MyIHQrT74FXSpkYOWhtKjZjBkMB0GA1UdDgQW\nBBTGQwW6wdqJ1o5YG1KSgeBw+jOwPzAfBgNVHSMEGDAWgBSzp+1VNAQC/frGxi3S\nP0yfFD7bMjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAKBggq\nhkjOPQQDAgNIADBFAiEApEk3i06Pmp8JcWf+zP63qTIWRvXqi4wScxgyc9f9nLsC\nIEp5w8tyc7NxZFlAzH0ENl7L9zyLOoj685m5qnI/g9n2\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/data/sni/root-ca.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIB9DCCAZugAwIBAgIUGWM5J2Oi1FhuZiq1agTkmWGNOqEwCgYIKoZIzj0EAwIw\nTzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDzANBgNVBAoMBlJl\ndGVzdDEaMBgGA1UEAwwRTXIgUmV0ZXN0IFJvb3QgQ0EwIBcNMjIxMjI5MDkyMDEx\nWhgPMjEyMjEyMDUwOTIwMTFaME8xCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l\nLVN0YXRlMQ8wDQYDVQQKDAZSZXRlc3QxGjAYBgNVBAMMEU1yIFJldGVzdCBSb290\nIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErdUhTL7f5A5qa4GGcJEWkwBa\nl1Q7XU8DSKOiSsVnJ0w3TLHMXXoEx83yOF9yyp5xbxMtr3rSZEpFfQ6VVZ9FyKNT\nMFEwHQYDVR0OBBYEFLOn7VU0BAL9+sbGLdI/TJ8UPtsyMB8GA1UdIwQYMBaAFLOn\n7VU0BAL9+sbGLdI/TJ8UPtsyMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwID\nRwAwRAIgcml4Q6W/zcUnzPI9eb97FNBqLNwWRTlh3JPDplpfj4MCIBVrXpAPEfVy\nJsCIPpxLjYPRhvfdRIZGgvzTP5q4mGaT\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/data/sni/server-interm.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIClDCCAjmgAwIBAgIUa79XJUJxiZsuE19rM05mJwE/AuowCgYIKoZIzj0EAwIw\nVzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDzANBgNVBAoMBlJl\ndGVzdDEiMCAGA1UEAwwZTXIgUmV0ZXN0IEludGVybWVkaWF0ZSBDQTAgFw0yMjEy\nMjkwOTMxMThaGA8yMTIyMTIwMzA5MzExOFowTzELMAkGA1UEBhMCQVUxEzARBgNV\nBAgMClNvbWUtU3RhdGUxDzANBgNVBAoMBlJldGVzdDEaMBgGA1UEAwwRcmV0ZXN0\nLnNlcnZlci5vcmcwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQIJmj//cAvgPmv\nVYe5IbVUg65pQzpTwRskbFqpDqUnIqkcdFHPHCtqAVg8bkTsXBETB76m44ejAps/\nF1cii/Rfo4HoMIHlMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgbAMB0GA1Ud\nDgQWBBSTjuar4J+Cv02G6QpqgCflL7swsDB3BgNVHSMEcDBugBTGQwW6wdqJ1o5Y\nG1KSgeBw+jOwP6FTpFEwTzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3Rh\ndGUxDzANBgNVBAoMBlJldGVzdDEaMBgGA1UEAwwRTXIgUmV0ZXN0IFJvb3QgQ0GC\nAQEwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD\nAjAKBggqhkjOPQQDAgNJADBGAiEAhfU/r3G88n0V3h5jc7FwgWL+9ig+SGlc6mPG\nFsqI0eECIQCMLbYDZgMPpaFPzCWKVAmp9wxzLLpX9++C/v4Y4FVOAg==\n-----END CERTIFICATE-----\n-----BEGIN EC PARAMETERS-----\nBggqhkjOPQMBBw==\n-----END EC PARAMETERS-----\n-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIJMwouMCx2+QTFwiuo2GUYPgtmTGpx/7+0rkcw6//GQvoAoGCCqGSM49\nAwEHoUQDQgAECCZo//3AL4D5r1WHuSG1VIOuaUM6U8EbJGxaqQ6lJyKpHHRRzxwr\nagFYPG5E7FwREwe+puOHowKbPxdXIov0Xw==\n-----END EC PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIB/TCCAaOgAwIBAgIBATAKBggqhkjOPQQDAjBPMQswCQYDVQQGEwJBVTETMBEG\nA1UECAwKU29tZS1TdGF0ZTEPMA0GA1UECgwGUmV0ZXN0MRowGAYDVQQDDBFNciBS\nZXRlc3QgUm9vdCBDQTAgFw0yMjEyMjkwOTIzNTRaGA8yMTIyMTIwNDA5MjM1NFow\nVzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDzANBgNVBAoMBlJl\ndGVzdDEiMCAGA1UEAwwZTXIgUmV0ZXN0IEludGVybWVkaWF0ZSBDQTBZMBMGByqG\nSM49AgEGCCqGSM49AwEHA0IABHn8RqyvhhIdMK0o0wc+mKfO97SghjB3FlKBLjQT\n+jIOVW3YboDBgPCRwdjGFugc661MyIHQrT74FXSpkYOWhtKjZjBkMB0GA1UdDgQW\nBBTGQwW6wdqJ1o5YG1KSgeBw+jOwPzAfBgNVHSMEGDAWgBSzp+1VNAQC/frGxi3S\nP0yfFD7bMjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAKBggq\nhkjOPQQDAgNIADBFAiEApEk3i06Pmp8JcWf+zP63qTIWRvXqi4wScxgyc9f9nLsC\nIEp5w8tyc7NxZFlAzH0ENl7L9zyLOoj685m5qnI/g9n2\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "test/data/utf8.json",
    "content": "{\n  \"schlieen\" : \"schlieen\",\n  \"bernehmen\" : \"bernehmen\"\n}\n"
  },
  {
    "path": "test/data/webapp.json",
    "content": "{\"web-app\": {\n  \"servlet\": [   \n    {\n      \"servlet-name\": \"cofaxCDS\",\n      \"servlet-class\": \"org.cofax.cds.CDSServlet\",\n      \"init-param\": {\n        \"configGlossary:installationAt\": \"Philadelphia, PA\",\n        \"configGlossary:adminEmail\": \"ksm@pobox.com\",\n        \"configGlossary:poweredBy\": \"Cofax\",\n        \"configGlossary:poweredByIcon\": \"/images/cofax.gif\",\n        \"configGlossary:staticPath\": \"/content/static\",\n        \"templateProcessorClass\": \"org.cofax.WysiwygTemplate\",\n        \"templateLoaderClass\": \"org.cofax.FilesTemplateLoader\",\n        \"templatePath\": \"templates\",\n        \"templateOverridePath\": \"\",\n        \"defaultListTemplate\": \"listTemplate.htm\",\n        \"defaultFileTemplate\": \"articleTemplate.htm\",\n        \"useJSP\": false,\n        \"jspListTemplate\": \"listTemplate.jsp\",\n        \"jspFileTemplate\": \"articleTemplate.jsp\",\n        \"cachePackageTagsTrack\": 200,\n        \"cachePackageTagsStore\": 200,\n        \"cachePackageTagsRefresh\": 60,\n        \"cacheTemplatesTrack\": 100,\n        \"cacheTemplatesStore\": 50,\n        \"cacheTemplatesRefresh\": 15,\n        \"cachePagesTrack\": 200,\n        \"cachePagesStore\": 100,\n        \"cachePagesRefresh\": 10,\n        \"cachePagesDirtyRead\": 10,\n        \"searchEngineListTemplate\": \"forSearchEnginesList.htm\",\n        \"searchEngineFileTemplate\": \"forSearchEngines.htm\",\n        \"searchEngineRobotsDb\": \"WEB-INF/robots.db\",\n        \"useDataStore\": true,\n        \"dataStoreClass\": \"org.cofax.SqlDataStore\",\n        \"redirectionClass\": \"org.cofax.SqlRedirection\",\n        \"dataStoreName\": \"cofax\",\n        \"dataStoreDriver\": \"com.microsoft.jdbc.sqlserver.SQLServerDriver\",\n        \"dataStoreUrl\": \"jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon\",\n        \"dataStoreUser\": \"sa\",\n        \"dataStorePassword\": \"dataStoreTestQuery\",\n        \"dataStoreTestQuery\": \"SET NOCOUNT ON;select test='test';\",\n        \"dataStoreLogFile\": \"/usr/local/tomcat/logs/datastore.log\",\n        \"dataStoreInitConns\": 10,\n        \"dataStoreMaxConns\": 100,\n        \"dataStoreConnUsageLimit\": 100,\n        \"dataStoreLogLevel\": \"debug\",\n        \"maxUrlLength\": 500}},\n    {\n      \"servlet-name\": \"cofaxEmail\",\n      \"servlet-class\": \"org.cofax.cds.EmailServlet\",\n      \"init-param\": {\n      \"mailHost\": \"mail1\",\n      \"mailHostOverride\": \"mail2\"}},\n    {\n      \"servlet-name\": \"cofaxAdmin\",\n      \"servlet-class\": \"org.cofax.cds.AdminServlet\"},\n \n    {\n      \"servlet-name\": \"fileServlet\",\n      \"servlet-class\": \"org.cofax.cds.FileServlet\"},\n    {\n      \"servlet-name\": \"cofaxTools\",\n      \"servlet-class\": \"org.cofax.cms.CofaxToolsServlet\",\n      \"init-param\": {\n        \"templatePath\": \"toolstemplates/\",\n        \"log\": 1,\n        \"logLocation\": \"/usr/local/tomcat/logs/CofaxTools.log\",\n        \"logMaxSize\": \"\",\n        \"dataLog\": 1,\n        \"dataLogLocation\": \"/usr/local/tomcat/logs/dataLog.log\",\n        \"dataLogMaxSize\": \"\",\n        \"removePageCache\": \"/content/admin/remove?cache=pages&id=\",\n        \"removeTemplateCache\": \"/content/admin/remove?cache=templates&id=\",\n        \"fileTransferFolder\": \"/usr/local/tomcat/webapps/content/fileTransferFolder\",\n        \"lookInContext\": 1,\n        \"adminGroupID\": 4,\n        \"betaServer\": true}}],\n  \"servlet-mapping\": {\n    \"cofaxCDS\": \"/\",\n    \"cofaxEmail\": \"/cofaxutil/aemail/*\",\n    \"cofaxAdmin\": \"/admin/*\",\n    \"fileServlet\": \"/static/*\",\n    \"cofaxTools\": \"/tools/*\"},\n \n  \"taglib\": {\n    \"taglib-uri\": \"cofax.tld\",\n    \"taglib-location\": \"/WEB-INF/tlds/cofax.tld\"}}}"
  },
  {
    "path": "test/data/widget.json",
    "content": "{\"widget\": {\n    \"debug\": \"on\",\n    \"window\": {\n        \"title\": \"Sample Konfabulator Widget\",\n        \"name\": \"main_window\",\n        \"width\": 500,\n        \"height\": 500\n    },\n    \"image\": { \n        \"src\": \"Images/Sun.png\",\n        \"name\": \"sun1\",\n        \"hOffset\": 250,\n        \"vOffset\": 250,\n        \"alignment\": \"center\"\n    },\n    \"text\": {\n        \"data\": \"Click Here\",\n        \"size\": 36,\n        \"style\": \"bold\",\n        \"name\": \"text1\",\n        \"hOffset\": 250,\n        \"vOffset\": 100,\n        \"alignment\": \"center\",\n        \"onMouseUp\": \"sun1.opacity = (sun1.opacity / 100) * 90;\"\n    }\n}}    "
  },
  {
    "path": "test/dbg.c",
    "content": "/**\n * @file dbg.c Testcode for debug module\n *\n * Copyright (C) 2025 Alfred E. Heggestad\n */\n\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"dbg\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_dbg(void)\n{\n\tint err = 0;\n\n\tfor (int level=0; level<8; level++) {\n\t\tconst char *str = dbg_level_str(level);\n\t\tASSERT_TRUE(str_isset(str));\n\t}\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/dd.c",
    "content": "/**\n * @file test/dd.c Dependency Descriptor (DD) testcode\n *\n * Copyright (C) 2010 - 2023 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re.h>\n#include <re_dd.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"ddtest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int test_dd_mand(void)\n{\n\tstruct dd dd = { 0 };\n\tstruct mbuf *mb = mbuf_alloc(512);\n\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tint err = dd_encode(mb, &dd);\n\tTEST_ERR(err);\n\n\t/* Mandatory Descriptor Fields -- 3 bytes only */\n\tstatic const uint8_t buf_exp[] = { 0, 0, 0 };\n\n\tTEST_MEMCMP(buf_exp, sizeof(buf_exp), mb->buf, mb->end);\n\n\tstruct dd dd_dec;\n\n\terr = dd_decode(&dd_dec, mb->buf, mb->end);\n\tTEST_ERR(err);\n\n\tASSERT_EQ(0, dd_dec.start_of_frame);\n\tASSERT_EQ(0, dd_dec.end_of_frame);\n\tASSERT_EQ(0, dd_dec.frame_dependency_template_id);\n\tASSERT_EQ(0, dd_dec.frame_number);\n\n\tASSERT_TRUE(!dd_dec.ext);\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\n/*\n\n80012f800214eaa860414d1410208426\n\n\n\"startOfFrame\":true,\n\"endOfFrame\":false,\n\"frameDependencyTemplateId\":0,\n\"frameNumber\":303,\n\"templateStructure\":{\n\t\"templateIdOffset\":0,\n\t\"templateInfo\":{\n\t\t\"0\":{\n\t\t\t\"spatialId\":0,\n\t\t\t\"temporalId\":0,\n\t\t\t\"dti\":[\n\t\t\t\t\"SWITCH\",\n\t\t\t\t\"SWITCH\",\n\t\t\t\t\"SWITCH\"\n\t\t\t],\n\t\t\t\"fdiff\":[\n\n\t\t\t],\n\t\t\t\"chains\":[\n\t\t\t\t0\n\t\t\t]\n\t\t},\n\t\t\"1\":{\n\t\t\t\"spatialId\":0,\n\t\t\t\"temporalId\":0,\n\t\t\t\"dti\":[\n\t\t\t\t\"SWITCH\",\n\t\t\t\t\"SWITCH\",\n\t\t\t\t\"SWITCH\"\n\t\t\t],\n\t\t\t\"fdiff\":[\n\t\t\t\t4\n\t\t\t],\n\t\t\t\"chains\":[\n\t\t\t\t4\n\t\t\t]\n\t\t},\n\t\t\"2\":{\n\t\t\t\"spatialId\":0,\n\t\t\t\"temporalId\":1,\n\t\t\t\"dti\":[\n\t\t\t\t\"NOT_PRESENT\",\n\t\t\t\t\"DISCARDABLE\",\n\t\t\t\t\"SWITCH\"\n\t\t\t],\n\t\t\t\"fdiff\":[\n\t\t\t\t2\n\t\t\t],\n\t\t\t\"chains\":[\n\t\t\t\t2\n\t\t\t]\n\t\t},\n\t\t\"3\":{\n\t\t\t\"spatialId\":0,\n\t\t\t\"temporalId\":2,\n\t\t\t\"dti\":[\n\t\t\t\t\"NOT_PRESENT\",\n\t\t\t\t\"NOT_PRESENT\",\n\t\t\t\t\"DISCARDABLE\"\n\t\t\t],\n\t\t\t\"fdiff\":[\n\t\t\t\t1\n\t\t\t],\n\t\t\t\"chains\":[\n\t\t\t\t1\n\t\t\t]\n\t\t},\n\t\t\"4\":{\n\t\t\t\"spatialId\":0,\n\t\t\t\"temporalId\":2,\n\t\t\t\"dti\":[\n\t\t\t\t\"NOT_PRESENT\",\n\t\t\t\t\"NOT_PRESENT\",\n\t\t\t\t\"DISCARDABLE\"\n\t\t\t],\n\t\t\t\"fdiff\":[\n\t\t\t\t1\n\t\t\t],\n\t\t\t\"chains\":[\n\t\t\t\t3\n\t\t\t]\n\t\t}\n\t},\n\t\"decodeTargetInfo\":{\n\t\t\"0\":{\n\t\t\t\"protectedBy\":0,\n\t\t\t\"spatialId\":0,\n\t\t\t\"temporalId\":0\n\t\t},\n\t\t\"1\":{\n\t\t\t\"protectedBy\":0,\n\t\t\t\"spatialId\":0,\n\t\t\t\"temporalId\":1\n\t\t},\n\t\t\"2\":{\n\t\t\t\"protectedBy\":0,\n\t\t\t\"spatialId\":0,\n\t\t\t\"temporalId\":2\n\t\t}\n\t},\n\t\"maxSpatialId\":0,\n\t\"maxTemporalId\":2\n}\n}\n\n\n */\nstatic int test_dd_decode(void)\n{\n\tstatic const char *str = \"80012f800214eaa860414d1410208426\";\n\tstruct mbuf *mb = mbuf_alloc(8);\n\tuint8_t buf[16];\n\tint err;\n\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = str_hex(buf, sizeof(buf), str);\n\tTEST_ERR(err);\n\n\tstruct dd dd;\n\n\terr = dd_decode(&dd, buf, sizeof(buf));\n\tTEST_ERR(err);\n\n#if 0\n\tdd_print(&dd);\n#endif\n\n\tASSERT_EQ(1, dd.start_of_frame);\n\tASSERT_EQ(0, dd.end_of_frame);\n\tASSERT_EQ(0, dd.frame_dependency_template_id);\n\tASSERT_EQ(303, dd.frame_number);\n\n\tASSERT_EQ(1, dd.template_dependency_structure_present_flag);\n\tASSERT_EQ(0, dd.active_decode_targets_present_flag);\n\tASSERT_EQ(0, dd.custom_dtis_flag);\n\tASSERT_EQ(0, dd.custom_fdiffs_flag);\n\tASSERT_EQ(0, dd.custom_chains_flag);\n\n\tASSERT_EQ(7, dd.active_decode_targets_bitmask);\n\tASSERT_EQ(0, dd.template_id_offset);\n\tASSERT_EQ(3, dd.dt_cnt);\n\tASSERT_EQ(5, dd.template_cnt);\n\tASSERT_EQ(0, dd.max_spatial_id);\n\n\tASSERT_EQ(0, dd.template_spatial_id[0]);\n\tASSERT_EQ(0, dd.template_spatial_id[1]);\n\tASSERT_EQ(0, dd.template_spatial_id[2]);\n\tASSERT_EQ(0, dd.template_spatial_id[3]);\n\tASSERT_EQ(0, dd.template_spatial_id[4]);\n\n\tASSERT_EQ(0, dd.template_temporal_id[0]);\n\tASSERT_EQ(0, dd.template_temporal_id[1]);\n\tASSERT_EQ(1, dd.template_temporal_id[2]);\n\tASSERT_EQ(2, dd.template_temporal_id[3]);\n\tASSERT_EQ(2, dd.template_temporal_id[4]);\n\n\tASSERT_TRUE(!dd.resolutions_present_flag);\n\tASSERT_EQ(0, dd.render_count);\n\n\tASSERT_EQ(2, dd.template_dti[0][0]);\n\tASSERT_EQ(2, dd.template_dti[0][1]);\n\tASSERT_EQ(2, dd.template_dti[0][2]);\n\tASSERT_EQ(2, dd.template_dti[1][0]);\n\tASSERT_EQ(2, dd.template_dti[1][1]);\n\tASSERT_EQ(2, dd.template_dti[1][2]);\n\tASSERT_EQ(0, dd.template_dti[2][0]);\n\tASSERT_EQ(1, dd.template_dti[2][1]);\n\tASSERT_EQ(2, dd.template_dti[2][2]);\n\tASSERT_EQ(0, dd.template_dti[3][0]);\n\tASSERT_EQ(0, dd.template_dti[3][1]);\n\tASSERT_EQ(1, dd.template_dti[3][2]);\n\tASSERT_EQ(0, dd.template_dti[4][0]);\n\tASSERT_EQ(0, dd.template_dti[4][1]);\n\tASSERT_EQ(1, dd.template_dti[4][2]);\n\n\tASSERT_EQ(1, dd.chain_cnt);\n\n\terr = dd_encode(mb, &dd);\n\tTEST_ERR(err);\n\n\tTEST_MEMCMP(buf, sizeof(buf), mb->buf, mb->end);\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\n/*\n * Interop test with Chrome Version 118.0.5993.70\n */\nstatic int test_dd_chrome(void)\n{\n\tstatic const char *str = \"80000180003a40813f80ef80\";\n\tstruct mbuf *mb = mbuf_alloc(16);\n\tchar *debug = NULL;\n\tuint8_t buf[12];\n\tint err;\n\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = str_hex(buf, sizeof(buf), str);\n\tTEST_ERR(err);\n\n\tstruct dd dd;\n\n\terr = dd_decode(&dd, buf, sizeof(buf));\n\tTEST_ERR(err);\n\n\terr = re_sdprintf(&debug, \"%H\", dd_print, &dd);\n\tTEST_ERR(err);\n\tASSERT_TRUE(str_isset(debug));\n\n\tASSERT_EQ(1, dd.start_of_frame);\n\tASSERT_EQ(0, dd.end_of_frame);\n\tASSERT_EQ(0, dd.frame_dependency_template_id);\n\tASSERT_EQ(1, dd.frame_number);\n\n\tASSERT_EQ(1, dd.template_dependency_structure_present_flag);\n\tASSERT_EQ(0, dd.active_decode_targets_present_flag);\n\tASSERT_EQ(0, dd.custom_dtis_flag);\n\tASSERT_EQ(0, dd.custom_fdiffs_flag);\n\tASSERT_EQ(0, dd.custom_chains_flag);\n\n\tASSERT_EQ(1, dd.active_decode_targets_bitmask);\n\tASSERT_EQ(0, dd.template_id_offset);\n\tASSERT_EQ(1, dd.dt_cnt);\n\tASSERT_EQ(2, dd.template_cnt);\n\tASSERT_EQ(0, dd.max_spatial_id);\n\n\tASSERT_EQ(0, dd.template_spatial_id[0]);\n\tASSERT_EQ(0, dd.template_spatial_id[1]);\n\n\tASSERT_EQ(0, dd.template_temporal_id[0]);\n\tASSERT_EQ(0, dd.template_temporal_id[1]);\n\n\tASSERT_TRUE(dd.resolutions_present_flag);\n\tASSERT_EQ(1, dd.render_count);\n\tASSERT_EQ(639, dd.max_render_width_minus_1[0]);\n\tASSERT_EQ(479, dd.max_render_height_minus_1[0]);\n\n\tASSERT_EQ(2, dd.template_dti[0][0]);\n\tASSERT_EQ(2, dd.template_dti[1][0]);\n\n\tASSERT_EQ(0, dd.chain_cnt);\n\n\terr = dd_encode(mb, &dd);\n\tTEST_ERR(err);\n\n\tTEST_MEMCMP(buf, sizeof(buf), mb->buf, mb->end);\n\n out:\n\tmem_deref(debug);\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nint test_dd(void)\n{\n\tint err;\n\n\terr = test_dd_mand();\n\tif (err)\n\t\treturn err;\n\n\terr = test_dd_decode();\n\tif (err)\n\t\treturn err;\n\n\terr = test_dd_chrome();\n\tif (err)\n\t\treturn err;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/dns.c",
    "content": "/**\n * @file dns.c DNS Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"dns\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tNUM_TESTS    = 32,\n\tIP_127_0_0_1 = 0x7f000001,\n\tIP_127_0_0_2 = 0x7f000002,\n\tIP_127_0_0_3 = 0x7f000003,\n\tIP_127_0_0_4 = 0x7f000004,\n\tIP_127_0_0_5 = 0x7f000005,\n};\n\nstatic const uint8_t IP6_1[16] = {\n\t0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01\n};\n\nstatic const uint8_t IP6_2[16] = {\n\t0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02\n};\n\nstatic const uint8_t IP6_3[16] = {\n\t0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03\n};\n\nstatic const uint8_t IP6_4[16] = {\n\t0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04\n};\n\n\nstatic int mkstr(char **strp)\n{\n\tsize_t sz = 8;\n\tchar *str;\n\n\tstr = mem_alloc(sz, NULL);\n\tif (!str)\n\t\treturn ENOMEM;\n\n\trand_str(str, sz);\n\n\t*strp = str;\n\n\treturn 0;\n}\n\n\nstatic int mkrr(struct dnsrr *rr, uint16_t type)\n{\n\tint err;\n\n\terr = mkstr(&rr->name);\n\tif (err)\n\t\treturn err;\n\n\trr->type = type;\n\trr->dnsclass = DNS_CLASS_IN;\n\trr->ttl = 3600;\n\trr->rdlen = 2;\n\n\tswitch (type) {\n\n\tcase DNS_TYPE_A:\n\t\trr->rdata.a.addr = rand_u32();\n\t\tbreak;\n\n\tcase DNS_TYPE_NS:\n\t\terr |= mkstr(&rr->rdata.ns.nsdname);\n\t\tbreak;\n\n\tcase DNS_TYPE_CNAME:\n\t\terr |= mkstr(&rr->rdata.cname.cname);\n\t\tbreak;\n\n\tcase DNS_TYPE_SOA:\n\t\terr |= mkstr(&rr->rdata.soa.mname);\n\t\terr |= mkstr(&rr->rdata.soa.rname);\n\t\trr->rdata.soa.serial  = rand_u32();\n\t\trr->rdata.soa.refresh = rand_u32();\n\t\trr->rdata.soa.retry   = rand_u32();\n\t\trr->rdata.soa.expire  = rand_u32();\n\t\trr->rdata.soa.ttlmin  = rand_u32();\n\t\tbreak;\n\n\tcase DNS_TYPE_PTR:\n\t\terr |= mkstr(&rr->rdata.ptr.ptrdname);\n\t\tbreak;\n\n\tcase DNS_TYPE_MX:\n\t\trr->rdata.mx.pref = rand_u16();\n\t\terr |= mkstr(&rr->rdata.mx.exchange);\n\t\tbreak;\n\n\tcase DNS_TYPE_TXT:\n\t\terr |= mkstr(&rr->rdata.txt.data);\n\t\tbreak;\n\n\tcase DNS_TYPE_AAAA:\n\t\trand_bytes(rr->rdata.aaaa.addr, 16);\n\t\tbreak;\n\n\tcase DNS_TYPE_SRV:\n\t\trr->rdata.srv.pri    = rand_u16();\n\t\trr->rdata.srv.weight = rand_u16();\n\t\trr->rdata.srv.port   = rand_u16();\n\t\terr |= mkstr(&rr->rdata.srv.target);\n\t\tbreak;\n\n\tcase DNS_TYPE_NAPTR:\n\t\trr->rdata.naptr.order = rand_u16();\n\t\trr->rdata.naptr.pref  = rand_u16();\n\t\terr |= mkstr(&rr->rdata.naptr.flags);\n\t\terr |= mkstr(&rr->rdata.naptr.services);\n\t\terr |= mkstr(&rr->rdata.naptr.regexp);\n\t\terr |= mkstr(&rr->rdata.naptr.replace);\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nint test_dns_hdr(void)\n{\n\tstruct mbuf *mb;\n\tuint16_t u16 = 9753;  /* pseudo-random (predictable) */\n\tsize_t i;\n\tint err = 0;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tfor (i=0; i<NUM_TESTS; i++) {\n\n\t\tstruct dnshdr hdr, hdr2;\n\n\t\tmemset(&hdr, 0, sizeof(hdr));\n\t\tmemset(&hdr2, 0, sizeof(hdr2));\n\n\t\thdr.id     = u16;\n\t\thdr.qr     = u16 & 1;\n\t\thdr.opcode = u16 & 0xf;\n\t\thdr.aa     = u16 & 1;\n\t\thdr.tc     = u16 & 1;\n\t\thdr.rd     = u16 & 1;\n\t\thdr.ra     = u16 & 1;\n\t\thdr.z      = u16 & 0x7;\n\t\thdr.rcode  = u16 & 0xf;\n\t\thdr.nq     = u16;\n\t\thdr.nans   = u16;\n\t\thdr.nauth  = u16;\n\t\thdr.nadd   = u16;\n\n\t\tmb->pos = mb->end = 0;\n\t\terr = dns_hdr_encode(mb, &hdr);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tmb->pos = 0;\n\t\terr = dns_hdr_decode(mb, &hdr2);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tif (0 != memcmp(&hdr, &hdr2, sizeof(hdr))) {\n\t\t\t(void)re_fprintf(stderr,\n\t\t\t\t\t \"dnshdr mismatch:\\n%02w\\n%02w\\n\",\n\t\t\t\t\t &hdr, sizeof(hdr),\n\t\t\t\t\t &hdr2, sizeof(hdr2));\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\tu16 *= 17;\n\t}\n\n\tfor (uint8_t j=0; j<10; j++) {\n\n\t\tchar debug[256] = \"\";\n\n\t\tre_snprintf(debug, sizeof(debug), \"%s%s\",\n\t\t\t  dns_hdr_opcodename(j), dns_hdr_rcodename(j));\n\n\t\tASSERT_TRUE(str_isset(debug));\n\t}\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nint test_dns_rr(void)\n{\n\tstruct hash *ht = NULL;\n\tstruct dnsrr *rr = NULL, *rr2 = NULL;\n\tstruct mbuf *mb;\n\tsize_t i;\n\tchar debug[256] = \"\";\n\tint err = ENOMEM;\n\n\tstatic const uint16_t typev[] = {\n\t\tDNS_TYPE_A,    DNS_TYPE_NS,  DNS_TYPE_CNAME,\n\t\tDNS_TYPE_SOA,  DNS_TYPE_PTR, DNS_TYPE_MX,\n\t\tDNS_TYPE_AAAA, DNS_TYPE_SRV, DNS_TYPE_NAPTR,\n\t\tDNS_TYPE_TXT\n\t};\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = hash_alloc(&ht, 32);\n\tif (err)\n\t\tgoto out;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(typev); i++) {\n\n\t\thash_flush(ht);\n\n\t\trr = dns_rr_alloc();\n\t\tif (!rr) {\n\t\t\terr = ENOMEM;\n\t\t\tbreak;\n\t\t}\n\n\t\terr = mkrr(rr, typev[i]);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tmb->pos = mb->end = 0;\n\t\terr = dns_rr_encode(mb, rr, 0, ht, 0);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tmb->pos = 0;\n\t\terr = dns_rr_decode(mb, &rr2, 0);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tif (!dns_rr_cmp(rr, rr2, true)) {\n\t\t\t(void)re_fprintf(stderr,\n\t\t\t\t\t \"dns_rr:\\nrr:  %02w\\n\\nrr2: %02w\\n\",\n\t\t\t\t\t rr, sizeof(*rr), rr2, sizeof(*rr2));\n\t\t\thexdump(stderr, mb->buf, mb->end);\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\tre_snprintf(debug, sizeof(debug), \"%H\", dns_rr_print, rr);\n\t\tTEST_ASSERT(str_isset(debug));\n\n\t\trr = mem_deref(rr);\n\t\trr2 = mem_deref(rr2);\n\t}\n\n out:\n\thash_flush(ht);\n\tmem_deref(ht);\n\tmem_deref(rr2);\n\tmem_deref(rr);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nint test_dns_rr_dup(void)\n{\n\tstruct dnsrr *rr = NULL, *dup = NULL;\n\tsize_t i;\n\tint err = ENOMEM;\n\n\tstatic const uint16_t typev[] = {\n\t\tDNS_TYPE_A,    DNS_TYPE_NS,  DNS_TYPE_CNAME,\n\t\tDNS_TYPE_SOA,  DNS_TYPE_PTR, DNS_TYPE_MX,\n\t\tDNS_TYPE_AAAA, DNS_TYPE_SRV, DNS_TYPE_NAPTR,\n\t\tDNS_TYPE_TXT\n\t};\n\n\tfor (i=0; i<RE_ARRAY_SIZE(typev); i++) {\n\n\t\trr = dns_rr_alloc();\n\t\tif (!rr) {\n\t\t\terr = ENOMEM;\n\t\t\tbreak;\n\t\t}\n\n\t\terr = mkrr(rr, typev[i]);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = dns_rr_dup(&dup, rr);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tif (!dns_rr_cmp(rr, dup, true)) {\n\t\t\t(void)re_fprintf(stderr,\n\t\t\t\t\t \"dns_rr_dup mismatch for type %s:\\n\",\n\t\t\t\t\t dns_rr_typename(typev[i]));\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\tASSERT_TRUE(rr != dup);\n\t\tASSERT_TRUE(rr->name != dup->name);\n\n\t\tswitch (typev[i]) {\n\t\tcase DNS_TYPE_NS:\n\t\t\tASSERT_TRUE(rr->rdata.ns.nsdname !=\n\t\t\t\t    dup->rdata.ns.nsdname);\n\t\t\tbreak;\n\t\tcase DNS_TYPE_CNAME:\n\t\t\tASSERT_TRUE(rr->rdata.cname.cname !=\n\t\t\t\t    dup->rdata.cname.cname);\n\t\t\tbreak;\n\t\tcase DNS_TYPE_SOA:\n\t\t\tASSERT_TRUE(rr->rdata.soa.mname !=\n\t\t\t\t    dup->rdata.soa.mname);\n\t\t\tASSERT_TRUE(rr->rdata.soa.rname !=\n\t\t\t\t    dup->rdata.soa.rname);\n\t\t\tbreak;\n\t\tcase DNS_TYPE_PTR:\n\t\t\tASSERT_TRUE(rr->rdata.ptr.ptrdname !=\n\t\t\t\t    dup->rdata.ptr.ptrdname);\n\t\t\tbreak;\n\t\tcase DNS_TYPE_MX:\n\t\t\tASSERT_TRUE(rr->rdata.mx.exchange !=\n\t\t\t\t    dup->rdata.mx.exchange);\n\t\t\tbreak;\n\t\tcase DNS_TYPE_TXT:\n\t\t\tASSERT_TRUE(rr->rdata.txt.data !=\n\t\t\t\t    dup->rdata.txt.data);\n\t\t\tbreak;\n\t\tcase DNS_TYPE_SRV:\n\t\t\tASSERT_TRUE(rr->rdata.srv.target !=\n\t\t\t\t    dup->rdata.srv.target);\n\t\t\tbreak;\n\t\tcase DNS_TYPE_NAPTR:\n\t\t\tASSERT_TRUE(rr->rdata.naptr.flags !=\n\t\t\t\t    dup->rdata.naptr.flags);\n\t\t\tASSERT_TRUE(rr->rdata.naptr.services !=\n\t\t\t\t    dup->rdata.naptr.services);\n\t\t\tASSERT_TRUE(rr->rdata.naptr.regexp !=\n\t\t\t\t    dup->rdata.naptr.regexp);\n\t\t\tASSERT_TRUE(rr->rdata.naptr.replace !=\n\t\t\t\t    dup->rdata.naptr.replace);\n\t\t\tbreak;\n\t\t}\n\n\t\trr = mem_deref(rr);\n\t\tdup = mem_deref(dup);\n\t}\n\n out:\n\tmem_deref(dup);\n\tmem_deref(rr);\n\n\treturn err;\n}\n\n\n/* Testcase to reproduce dname_decode looping error */\nint test_dns_dname(void)\n{\n\tstatic struct test {\n\t\tconst char *str;\n\t} testv[] = {\n\t\t{\n\t\t\t\"c000000c000100000e10002725324a57\"\n\t\t\t\"4d6e3837745836435541597754705361\"\n\t\t\t\"4c4c626743726e3475424e3642365957\"\n\t\t\t\"524e00\"\n\t\t},\n\t\t{\n\t\t\t\"31203700a22c9f17ea75de16785277fa\"\n\t\t\t\"db1094a7782b65a177715e45ffc59f9a\"\n\t\t\t\"73143748aaaf99aede63325c1f48e7fa\"\n\t\t\t\"56f9da\"\n\t\t},\n\t};\n\tstruct mbuf *mb;\n\tchar *name = NULL;\n\tsize_t i;\n\tint err = 0;\n\n\tmb = mbuf_alloc(4096);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tconst struct test *test = &testv[i];\n\t\tconst size_t size = str_len(test->str) / 2;\n\t\tint e;\n\n\t\terr = str_hex(mb->buf, size, test->str);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tmb->pos = 0;\n\t\tmb->end = size;\n\t\tmb->size = size;\n\n\t\t/* Expect EINVAL */\n\t\te = dns_dname_decode(mb, &name, 0);\n\t\tTEST_EQUALS(EINVAL, e);\n\n\t\tname = mem_deref(name);\n\t}\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstruct test_dns {\n\tint err;\n\tunion {\n\t\tuint32_t ipv4;\n\t\tuint8_t ipv6[16];\n\t} addr;\n\tstruct dnsc *dnsc;\n\tstruct dnsrr *rr;\n};\n\n\nstatic void query_handler(int err, const struct dnshdr *hdr, struct list *ansl,\n\t\t\t  struct list *authl, struct list *addl, void *arg)\n{\n\tstruct dnsrr *rr      = list_ledata(list_head(ansl));\n\tstruct test_dns *data = arg;\n\tstruct sa sa;\n\t(void)hdr;\n\t(void)authl;\n\t(void)addl;\n\t(void)arg;\n\n\tif (!data || !rr) {\n\t\tre_cancel();\n\t\treturn;\n\t}\n\n\tTEST_ERR(err);\n\n\tdata->rr = mem_ref(rr);\n\n\tif (rr->type == DNS_TYPE_A) {\n\t\tsa_set_in(&sa, rr->rdata.a.addr, 0);\n\t\tDEBUG_INFO(\"%s. IN A %j\\n\", rr->name, &sa);\n\t}\n\telse if (rr->type == DNS_TYPE_AAAA) {\n\t\tsa_set_in6(&sa, rr->rdata.aaaa.addr, 0);\n\t\tDEBUG_INFO(\"%s. IN AAAA %j\\n\", rr->name, &sa);\n\t}\n\nout:\n\tdata->err = err;\n\tre_cancel();\n}\n\n\nstatic int check_dns_async(struct dns_query **qp,\n\t\t\t   struct test_dns *data, const char *name,\n\t\t\t   uint32_t addr)\n{\n\tint err;\n\n\tdata->addr.ipv4 = addr;\n\tdata->err = ENODATA;\n\tdata->rr  = NULL;\n\n\terr = dnsc_query(qp, data->dnsc, name, DNS_TYPE_A, DNS_CLASS_IN,\n\t\t\t true, query_handler, data);\n\tTEST_ERR(err);\nout:\n\treturn err;\n}\n\n\nstatic int check_dns6_async(struct dns_query **qp,\n\t\t\t    struct test_dns *data, const char *name,\n\t\t\t    const uint8_t addr[16])\n{\n\tint err;\n\n\tmemcpy(data->addr.ipv6, addr, 16);\n\tdata->err = ENODATA;\n\tdata->rr  = NULL;\n\n\terr = dnsc_query(qp, data->dnsc, name, DNS_TYPE_AAAA, DNS_CLASS_IN,\n\t\t\t true, query_handler, data);\n\tTEST_ERR(err);\nout:\n\treturn err;\n}\n\n\nstatic int check_dns(struct test_dns *data, const char *name, uint32_t addr)\n{\n\tstruct dns_query *q = NULL;\n\tint err;\n\n\terr = check_dns_async(&q, data, name, addr);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(100);\n\tTEST_ERR(err);\n\n\t/* check query handler result */\n\terr = data->err;\n\tif (err)\n\t\tgoto out;\n\n\tTEST_ASSERT(data->rr);\n\tTEST_EQUALS(DNS_TYPE_A, data->rr->type);\n\tTEST_EQUALS(addr, data->rr->rdata.a.addr);\nout:\n\tmem_deref(q);\n\tmem_deref(data->rr);\n\treturn err;\n}\n\n\nstatic int check_dns6(struct test_dns *data, const char *name,\n\t\t      const uint8_t addr[16])\n{\n\tstruct dns_query *q = NULL;\n\tint err;\n\n\terr = check_dns6_async(&q, data, name, addr);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(100);\n\tTEST_ERR(err);\n\n\t/* check query handler result */\n\terr = data->err;\n\tif (err)\n\t\tgoto out;\n\n\tTEST_ASSERT(data->rr);\n\tTEST_EQUALS(data->rr->type, DNS_TYPE_AAAA);\n\tTEST_EQUALS(0, memcmp(data->addr.ipv6, data->rr->rdata.aaaa.addr, 16));\nout:\n\tmem_deref(q);\n\tmem_deref(data->rr);\n\treturn err;\n}\n\n\nstatic int test_dns_integration_param(const char *laddr)\n{\n\tstruct dns_server *srv = NULL;\n\tstruct test_dns data = {0};\n\tint err;\n\n\t/* Setup Mocking DNS Server */\n\terr = dns_server_alloc(&srv, laddr);\n\tTEST_ERR(err);\n\n\terr = dns_server_add_a(srv, \"test1.example.net\", IP_127_0_0_1, 1);\n\tTEST_ERR(err);\n\n\terr = dns_server_add_aaaa(srv, \"test1.example.net\", IP6_1, 1);\n\tTEST_ERR(err);\n\n\terr = dnsc_alloc(&data.dnsc, NULL, &srv->addr, 1);\n\tTEST_ERR(err);\n\n\t/* Test system getaddrinfo */\n\tdnsc_getaddrinfo(data.dnsc, true);\n\terr = check_dns(&data, \"localhost\", IP_127_0_0_1);\n\tTEST_EQUALS(dnsc_getaddrinfo_enabled(data.dnsc), true);\n\tTEST_ERR(err);\n\tdnsc_getaddrinfo(data.dnsc, false);\n\tTEST_EQUALS(dnsc_getaddrinfo_enabled(data.dnsc), false);\n\n\terr = check_dns(&data, \"test1.example.net\", IP_127_0_0_1);\n\tTEST_ERR(err);\n\n\terr = check_dns6(&data, \"test1.example.net\", IP6_1);\n\tTEST_ERR(err);\n\n\t/* Test does not exist */\n\terr = check_dns(&data, \"test2.example.net\", IP_127_0_0_1);\n\tTEST_EQUALS(ENODATA, err);\n\n\terr = check_dns6(&data, \"test2.example.net\", IP6_1);\n\tTEST_EQUALS(ENODATA, err);\n\n\tdns_server_flush(srv);\n\n\terr = dns_server_add_a(srv, \"test1.example.net\", IP_127_0_0_2, 1);\n\tTEST_ERR(err);\n\n\terr = dns_server_add_a(srv, \"test2.example.net\", IP_127_0_0_3, 1);\n\tTEST_ERR(err);\n\n\terr = dns_server_add_a(srv, \"test3.example.net\", IP_127_0_0_4, 1);\n\tTEST_ERR(err);\n\n\terr = dns_server_add_aaaa(srv, \"test1.example.net\", IP6_2, 1);\n\tTEST_ERR(err);\n\n\terr = dns_server_add_aaaa(srv, \"test2.example.net\", IP6_3, 1);\n\tTEST_ERR(err);\n\n\terr = dns_server_add_aaaa(srv, \"test3.example.net\", IP6_4, 1);\n\tTEST_ERR(err);\n\n\n\t/* --- Test DNS Cache --- */\n\terr = check_dns(&data, \"test1.example.net\", IP_127_0_0_1);\n\tTEST_ERR(err);\n\n\terr = check_dns6(&data, \"test1.example.net\", IP6_1);\n\tTEST_ERR(err);\n\n\terr = check_dns(&data, \"test2.example.net\", IP_127_0_0_3);\n\tTEST_ERR(err);\n\n\terr = check_dns(&data, \"test2.example.net\", IP_127_0_0_3);\n\tTEST_ERR(err);\n\n\terr = check_dns6(&data, \"test2.example.net\", IP6_3);\n\tTEST_ERR(err);\n\n\terr = check_dns6(&data, \"test2.example.net\", IP6_3);\n\tTEST_ERR(err);\n\n\t/* Check another resource record afterwards */\n\terr = check_dns(&data, \"test3.example.net\", IP_127_0_0_4);\n\tTEST_ERR(err);\n\n\terr = check_dns6(&data, \"test3.example.net\", IP6_4);\n\tTEST_ERR(err);\n\n\tsys_msleep(100);\n\tre_main_timeout(1);\n\n\t/* --- Check expired TTL --- */\n\terr = check_dns(&data, \"test1.example.net\", IP_127_0_0_2);\n\tTEST_ERR(err);\n\n\terr = check_dns6(&data, \"test1.example.net\", IP6_2);\n\tTEST_ERR(err);\n\n\t/* --- Test explicit DNS cache flush --- */\n\tdns_server_flush(srv);\n\terr = dns_server_add_a(srv, \"test1.example.net\", IP_127_0_0_5, 1);\n\tTEST_ERR(err);\n\tdnsc_cache_flush(data.dnsc);\n\terr = check_dns(&data, \"test1.example.net\", IP_127_0_0_5);\n\tTEST_ERR(err);\n\n\t/* --- Again DNS Cache --- */\n\terr = check_dns(&data, \"test1.example.net\", IP_127_0_0_5);\n\tTEST_ERR(err);\n\nout:\n\tmem_deref(data.dnsc);\n\tmem_deref(srv);\n\n\treturn err;\n}\n\n\nint test_dns_integration(void)\n{\n\tint err;\n\n\terr = test_dns_integration_param(\"127.0.0.1\");\n\tTEST_ERR(err);\n\n\tif (test_ipv6_supported()) {\n\t\terr = test_dns_integration_param(\"::1\");\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic int test_dns_reg_param(const char *laddr)\n{\n\tstruct dns_server *srv = NULL;\n\tstruct test_dns data = {0};\n\tstruct dns_query *q;\n\tint err;\n\n\t/* Setup Mocking DNS Server */\n\terr = dns_server_alloc(&srv, laddr);\n\tTEST_ERR(err);\n\n\terr = dns_server_add_a(srv, \"test1.example.net\", IP_127_0_0_1, 1);\n\tTEST_ERR(err);\n\n\terr = dns_server_add_aaaa(srv, \"test1.example.net\", IP6_1, 1);\n\tTEST_ERR(err);\n\n\terr = dnsc_alloc(&data.dnsc, NULL, &srv->addr, 1);\n\tTEST_ERR(err);\n\n\terr = check_dns(&data, \"test1.example.net\", IP_127_0_0_1);\n\tTEST_ERR(err);\n\n\terr = check_dns6(&data, \"test1.example.net\", IP6_1);\n\tTEST_ERR(err);\n\n\tdns_server_flush(srv);\n\n\t/* --- Test DNS Cache --- */\n\terr = check_dns(&data, \"test1.example.net\", IP_127_0_0_1);\n\tTEST_ERR(err);\n\n\terr = check_dns6(&data, \"test1.example.net\", IP6_1);\n\tTEST_ERR(err);\n\n\tdnsc_cache_flush(data.dnsc);\n\n\t/* --- Test early query cancellation --- */\n\terr = dnsc_query(&q, data.dnsc, \"test1.example.net\", DNS_TYPE_A,\n\t\t\t DNS_CLASS_IN, true, query_handler, &data);\n\tTEST_ERR(err);\n\tmem_deref(q);\n\n\t/* --- Leave query open for cleanup test --- */\n\terr = dnsc_query(NULL, data.dnsc, \"test1.example.net\", DNS_TYPE_A,\n\t\t\t DNS_CLASS_IN, true, query_handler, &data);\n\tTEST_ERR(err);\n\n\terr = dnsc_query(NULL, data.dnsc, \"test1.example.net\", DNS_TYPE_AAAA,\n\t\t\t DNS_CLASS_IN, true, query_handler, &data);\n\tTEST_ERR(err);\n\nout:\n\tmem_deref(data.dnsc);\n\tmem_deref(srv);\n\n\treturn err;\n}\n\n\nint test_dns_reg(void)\n{\n\tint err;\n\n\terr = test_dns_reg_param(\"127.0.0.1\");\n\tTEST_ERR(err);\n\n\tif (test_ipv6_supported()) {\n\t\terr = test_dns_reg_param(\"::1\");\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\treturn err;\n}\n\n\nint test_dns_nameservers(void)\n{\n\tstruct sa srvv[8];\n\tuint32_t srvc = RE_ARRAY_SIZE(srvv);\n\n\tint err = dns_srv_get(NULL, 0, srvv, &srvc);\n\tTEST_ERR(err);\n\n\tASSERT_TRUE(srvc >= 1);\n\n\tfor (uint32_t i=0; i<srvc; i++) {\n\t\tASSERT_TRUE(sa_isset(&srvv[i], SA_ALL));\n\t}\n\n out:\n\treturn err;\n}\n\n\nstruct fixture {\n\tstruct dnsc *dnsc;\n\tconst struct sa *srv_addr;\n\tuint32_t srvc;\n\tunsigned answers;\n\tint proto;\n\tint err;\n};\n\n\nenum { EXPECTED_ANSWERS = 2 };\n\n\nstatic void dns_query_handler(int err, const struct dnshdr *hdr,\n\t\t\t      struct list *ansl, struct list *authl,\n\t\t\t      struct list *addl, void *arg)\n{\n\tstruct fixture *fix = arg;\n\t(void)hdr;\n\t(void)authl;\n\t(void)addl;\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"dns query error: %m\\n\", err);\n\t\tfix->err = err;\n\t\tre_cancel();\n\t\treturn;\n\t}\n\n\tfix->answers += list_count(ansl);\n\n\tif (fix->answers < EXPECTED_ANSWERS) {\n\n\t\terr = dnsc_query_srv(NULL, fix->dnsc, \"foo.example.com\",\n\t\t\t\t     DNS_TYPE_AAAA, DNS_CLASS_IN,\n\t\t\t\t     fix->proto, fix->srv_addr,\n\t\t\t\t     &fix->srvc, false,\n\t\t\t\t     dns_query_handler, fix);\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\tif (fix->answers >= EXPECTED_ANSWERS || err) {\n\t\tfix->err = err;\n\t\tre_cancel();\n\t}\n}\n\n\nstatic int test_dns_param(const char *laddr, int proto)\n{\n\tstruct dns_server *srv = NULL;\n\tstruct fixture fix = {\n\t\t.srvc = 1,\n\t\t.proto = proto\n\t};\n\n\tint err = dnsc_alloc(&fix.dnsc, NULL, NULL, 0);\n\tTEST_ERR(err);\n\n\tdnsc_cache_max(fix.dnsc, 0);\n\n\terr = dns_server_alloc(&srv, laddr);\n\tTEST_ERR(err);\n\n\tuint8_t ipv6_addr[16] = {0};\n\terr = dns_server_add_aaaa(srv, \"foo.example.com\", ipv6_addr, 3600);\n\tTEST_ERR(err);\n\n\tswitch (proto) {\n\n\tcase IPPROTO_UDP:\n\t\tfix.srv_addr = &srv->addr;\n\t\tbreak;\n\n\tcase IPPROTO_TCP:\n\t\tfix.srv_addr = &srv->addr_tcp;\n\t\tbreak;\n\t}\n\n\terr = dnsc_query_srv(NULL, fix.dnsc, \"foo.example.com\",\n\t\t\t     DNS_TYPE_AAAA, DNS_CLASS_IN, proto,\n\t\t\t     fix.srv_addr, &fix.srvc, false,\n\t\t\t     dns_query_handler, &fix);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(5000);\n\tTEST_ERR(err);\n\n\terr = fix.err;\n\tTEST_ERR(err);\n\n\tASSERT_TRUE(fix.answers >= EXPECTED_ANSWERS);\n\n out:\n\tmem_deref(fix.dnsc);\n\tmem_deref(srv);\n\n\treturn err;\n}\n\n\nint test_dns_proto(void)\n{\n\tint err;\n\n\terr = test_dns_param(\"127.0.0.1\", IPPROTO_UDP);\n\tTEST_ERR(err);\n\n\terr = test_dns_param(\"127.0.0.1\", IPPROTO_TCP);\n\tTEST_ERR(err);\n\n\tif (test_ipv6_supported()) {\n\n\t\terr = test_dns_param(\"::1\", IPPROTO_UDP);\n\t\tTEST_ERR(err);\n\n\t\terr = test_dns_param(\"::1\", IPPROTO_TCP);\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/dsp.c",
    "content": "/**\n * @file dsp.c Testcode for librem's DSP module\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test/dsp\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int test_saturate(void)\n{\n\tint err = 0;\n\n\t/* saturate_u8 */\n\tTEST_EQUALS(  0, saturate_u8(-100));\n\tTEST_EQUALS(  0, saturate_u8(   0));\n\tTEST_EQUALS( 42, saturate_u8(  42));\n\tTEST_EQUALS(255, saturate_u8( 255));\n\tTEST_EQUALS(255, saturate_u8( 355));\n\tTEST_EQUALS(255, saturate_u8(9692));\n\n\t/* saturate_s16 */\n\tTEST_EQUALS(-32768, saturate_s16(-65535));\n\tTEST_EQUALS(-32768, saturate_s16(-32768));\n\tTEST_EQUALS(     0, saturate_s16(     0));\n\tTEST_EQUALS( 32767, saturate_s16( 32767));\n\tTEST_EQUALS( 32767, saturate_s16( 65535));\n\n\t/* saturate_add16 */\n\tTEST_EQUALS(-32768, saturate_add16(-30000, -30000));\n\tTEST_EQUALS( -2000, saturate_add16( -1000,  -1000));\n\tTEST_EQUALS(     2, saturate_add16(     1,      1));\n\tTEST_EQUALS( 32767, saturate_add16( 32766,      1));\n\tTEST_EQUALS( 32767, saturate_add16( 30000,  30000));\n\n\t/* saturate_sub16 */\n\tTEST_EQUALS(-32768, saturate_sub16(-50000, -10000));\n\tTEST_EQUALS( -2000, saturate_sub16( -1000,   1000));\n\tTEST_EQUALS(     0, saturate_sub16(     1,      1));\n\tTEST_EQUALS( 32765, saturate_sub16( 32766,      1));\n\tTEST_EQUALS( 32767, saturate_sub16( 50000,  10000));\n\n out:\n\treturn err;\n}\n\n\nint test_dsp(void)\n{\n\tint err;\n\n\terr = test_saturate();\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/dtls.c",
    "content": "/**\n * @file dtls.c DTLS Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"dtls_test\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstruct dtls_test {\n\tbool dtls_srtp;\n\tstruct dtls_sock *sock_cli, *sock_srv;\n\tstruct tls_conn *conn_cli, *conn_srv;\n\tstruct tls *tls;\n\tint err;\n\n\tstruct {\n\t\tenum srtp_suite suite;\n\t\tuint8_t cli_key[12+32];\n\t\tuint8_t srv_key[12+32];\n\t} cli, srv;\n\n\tuint8_t fp[32];\n\tchar cn[64];\n\tunsigned n_srv_estab;\n\tunsigned n_srv_recv;\n\tunsigned n_cli_estab;\n\tunsigned n_cli_recv;\n\tunsigned n_conn;\n};\n\nstatic const char *common_name = \"127.0.0.1\";\nstatic const char *payload_str = \"hello from a cute DTLS client\";\n\n\nstatic void abort_test(struct dtls_test *t, int err)\n{\n\tt->err = err;\n\tre_cancel();\n}\n\n\nstatic int send_data(struct dtls_test *t, const char *data)\n{\n\tstruct mbuf mb;\n\tint err;\n\n\tTEST_ASSERT(t->conn_cli != NULL);\n\n\tmb.buf  = (void *)data;\n\tmb.pos  = 0;\n\tmb.end  = str_len(data);\n\tmb.size = str_len(data);\n\n\terr = dtls_send(t->conn_cli, &mb);\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n\n\nstatic void srv_estab_handler(void *arg)\n{\n\tstruct dtls_test *t = arg;\n\tint err = 0;\n\n\t++t->n_srv_estab;\n\n\tif (t->dtls_srtp) {\n\t\terr = tls_srtp_keyinfo(t->conn_srv, &t->srv.suite,\n\t\t\t\t       t->srv.cli_key, sizeof(t->srv.cli_key),\n\t\t\t\t       t->srv.srv_key, sizeof(t->srv.srv_key));\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\tif (err)\n\t\tabort_test(t, err);\n}\n\n\nstatic void srv_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct dtls_test *t = arg;\n\tint err;\n\n\t++t->n_srv_recv;\n\n\t/* echo */\n\terr = dtls_send(t->conn_srv, mb);\n\tTEST_ERR(err);\n\n out:\n\tif (err)\n\t\tabort_test(t, err);\n}\n\n\nstatic void srv_close_handler(int err, void *arg)\n{\n\tstruct dtls_test *t = arg;\n\t(void)err;\n\n\tt->conn_srv = mem_deref(t->conn_srv);\n}\n\n\nstatic void cli_estab_handler(void *arg)\n{\n\tstruct dtls_test *t = arg;\n\tint err;\n\n\t++t->n_cli_estab;\n\n\terr = tls_peer_fingerprint(t->conn_cli, TLS_FINGERPRINT_SHA256,\n\t\t\t\t   t->fp, sizeof(t->fp));\n\tTEST_ERR(err);\n\n\terr = tls_peer_common_name(t->conn_cli, t->cn, sizeof(t->cn));\n\tTEST_ERR(err);\n\n\tif (t->dtls_srtp) {\n\n\t\terr = tls_srtp_keyinfo(t->conn_cli, &t->cli.suite,\n\t\t\t\t       t->cli.cli_key, sizeof(t->cli.cli_key),\n\t\t\t\t       t->cli.srv_key, sizeof(t->cli.srv_key));\n\t\tTEST_ERR(err);\n\t}\n\n\terr = send_data(t, payload_str);\n\tTEST_ERR(err);\n\n out:\n\tif (err)\n\t\tabort_test(t, err);\n}\n\n\nstatic void cli_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct dtls_test *t = arg;\n\tint err = 0;\n\n\t++t->n_cli_recv;\n\n\tTEST_STRCMP(payload_str, strlen(payload_str),\n\t\t    mbuf_buf(mb), mbuf_get_left(mb));\n\n out:\n\tabort_test(t, err);\n}\n\n\nstatic void cli_close_handler(int err, void *arg)\n{\n\tstruct dtls_test *t = arg;\n\t(void)err;\n\n\tt->conn_cli = mem_deref(t->conn_cli);\n}\n\n\nstatic void conn_handler(const struct sa *src, void *arg)\n{\n\tstruct dtls_test *t = arg;\n\tint err;\n\t(void)src;\n\n\t++t->n_conn;\n\n\tTEST_ASSERT(t->conn_srv == NULL);\n\n\terr = dtls_accept(&t->conn_srv, t->tls, t->sock_srv,\n\t\t\t  srv_estab_handler, srv_recv_handler,\n\t\t\t  srv_close_handler, t);\n\tif (err) {\n\t\tif (err == EPROTO)\n\t\t\terr = ENOMEM;\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\tif (err)\n\t\tabort_test(t, err);\n}\n\n\nstatic int test_dtls_srtp_base(enum tls_method method, bool dtls_srtp,\n\t\t\t       const char *srtp_suites, const char *laddr)\n{\n\tstruct dtls_test test;\n\tstruct udp_sock *us = NULL;\n\tstruct sa cli, srv;\n\tuint8_t fp[32];\n\tint err;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.dtls_srtp = dtls_srtp;\n\n\terr = tls_alloc(&test.tls, method, NULL, NULL);\n\tTEST_ERR(err);\n\n\terr = tls_set_certificate(test.tls, test_certificate_ecdsa,\n\t\t\t\t  strlen(test_certificate_ecdsa));\n\tTEST_ERR(err);\n\n\tif (dtls_srtp) {\n\t\terr = tls_set_srtp(test.tls, srtp_suites);\n\n\t\t/* SRTP not supported */\n\t\tif (err == ENOSYS) {\n\t\t\terr = 0;\n\t\t\tgoto out;\n\t\t}\n\n\t\tTEST_ERR(err);\n\t}\n\n\terr = tls_fingerprint(test.tls, TLS_FINGERPRINT_SHA256,\n\t\t\t      fp, sizeof(fp));\n\tTEST_EQUALS(0, err);\n\n\t(void)sa_set_str(&cli, laddr, 0);\n\t(void)sa_set_str(&srv, laddr, 0);\n\n\terr = udp_listen(&us, &srv, NULL, NULL);\n\tTEST_ERR(err);\n\n\terr = udp_local_get(us, &srv);\n\tTEST_ERR(err);\n\n\terr = dtls_listen(&test.sock_srv, NULL, us, 4, 0, conn_handler, &test);\n\tTEST_ERR(err);\n\n\terr = dtls_listen(&test.sock_cli, &cli, NULL, 4, 0, NULL, NULL);\n\tTEST_ERR(err);\n\n\tdtls_set_single(test.sock_cli, true);\n\n\t/* Set a low MTU to force fragmentation and reassembly */\n\tdtls_set_mtu(test.sock_srv, 128);\n\n\terr = dtls_connect(&test.conn_cli, test.tls, test.sock_cli,\n\t\t\t   &srv, cli_estab_handler,\n\t\t\t   cli_recv_handler, cli_close_handler, &test);\n\tif (err) {\n\t\tif (err == EPROTO)\n\t\t\terr = ENOMEM;\n\t\tTEST_ERR(err);\n\t}\n\n\tdtls_set_handlers(test.conn_cli, cli_estab_handler,\n\t\t\t  cli_recv_handler, cli_close_handler, &test);\n\n\terr = re_main_timeout(800);\n\tTEST_ERR(err);\n\n\tif (test.err) {\n\t\terr = test.err;\n\t\tgoto out;\n\t}\n\n\t/* verify result after test is complete */\n\tTEST_EQUALS(1, test.n_srv_estab);\n\tTEST_EQUALS(1, test.n_srv_recv);\n\tTEST_EQUALS(1, test.n_cli_estab);\n\tTEST_EQUALS(1, test.n_cli_recv);\n\tTEST_EQUALS(1, test.n_conn);\n\n\tTEST_MEMCMP(fp, sizeof(fp), test.fp, sizeof(test.fp));\n\tTEST_STRCMP(common_name, strlen(common_name),\n\t\t    test.cn, strlen(test.cn));\n\n\tif (dtls_srtp) {\n\n\t\tTEST_EQUALS(test.cli.suite, test.srv.suite);\n\t\tTEST_MEMCMP(test.cli.cli_key, sizeof(test.cli.cli_key),\n\t\t\t    test.srv.cli_key, sizeof(test.srv.cli_key));\n\t\tTEST_MEMCMP(test.cli.srv_key, sizeof(test.cli.srv_key),\n\t\t\t    test.srv.srv_key, sizeof(test.srv.srv_key));\n\t}\n\n out:\n\ttest.conn_cli = mem_deref(test.conn_cli);\n\ttest.conn_srv = mem_deref(test.conn_srv);\n\ttest.sock_cli = mem_deref(test.sock_cli);\n\ttest.sock_srv = mem_deref(test.sock_srv);\n\ttest.tls = mem_deref(test.tls);\n\tmem_deref(us);\n\n\treturn err;\n}\n\n\nstatic bool have_dtls_support(enum tls_method method)\n{\n\tstruct tls *tls = NULL;\n\tint err;\n\n\terr = tls_alloc(&tls, method, NULL, NULL);\n\n\tmem_deref(tls);\n\n\treturn err != ENOSYS;\n}\n\n\nint test_dtls(void)\n{\n\tint err = 0;\n\n\t/* NOTE: DTLS v1.0 should be available on all\n\t *       supported platforms.\n\t */\n\tif (!have_dtls_support(TLS_METHOD_DTLSV1)) {\n\t\t(void)re_printf(\"skip DTLS 1.0 tests\\n\");\n\t\treturn ESKIPPED;\n\t}\n\telse {\n\t\terr = test_dtls_srtp_base(TLS_METHOD_DTLSV1, false, NULL,\n\t\t\t\t\t  \"127.0.0.1\");\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (test_ipv6_supported()) {\n\t\t\terr = test_dtls_srtp_base(TLS_METHOD_DTLSV1, false,\n\t\t\t\t\t\t  NULL, \"::1\");\n\t\t\tTEST_ERR(err);\n\t\t}\n\t}\n\n out:\n\treturn err;\n}\n\n\nint test_dtls_srtp(void)\n{\n\tint err = 0;\n\n\tif (!have_dtls_support(TLS_METHOD_DTLSV1)) {\n\t\t(void)re_printf(\"skip DTLS tests\\n\");\n\t\treturn ESKIPPED;\n\t}\n\n\terr = test_dtls_srtp_base(TLS_METHOD_DTLSV1, true,\n\t\t\t\t  \"SRTP_AES128_CM_SHA1_80\", \"127.0.0.1\");\n\tTEST_ERR(err);\n\n\terr = test_dtls_srtp_base(TLS_METHOD_DTLSV1, true,\n\t\t\t\t  \"SRTP_AES128_CM_SHA1_32\", \"127.0.0.1\");\n\tTEST_ERR(err);\n\n\terr = test_dtls_srtp_base(TLS_METHOD_DTLSV1, true,\n\t\t\t\t  \"SRTP_AEAD_AES_128_GCM\", \"127.0.0.1\");\n\tTEST_ERR(err);\n\n\terr = test_dtls_srtp_base(TLS_METHOD_DTLSV1, true,\n\t\t\t\t  \"SRTP_AEAD_AES_256_GCM\", \"127.0.0.1\");\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/dtmf.c",
    "content": "/**\n * @file dtmf.c Testcode for librem's DTMF module\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test/dtmf\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic void dtmf_dec_handler(char digit, void *arg)\n{\n\tchar *buf = arg;\n\n\tbuf[str_len(buf)] = digit;\n}\n\n\nint test_dtmf(void)\n{\n#define SRATE 8000\n\tstatic const char digits[] = \"2*A#7\";\n\tchar dbuf[256] = \"\";\n\tstruct dtmf_dec *dec = NULL;\n\tstruct mbuf *mb = NULL;\n\tsize_t i;\n\tint err = 0;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = dtmf_dec_alloc(&dec, SRATE, 1, dtmf_dec_handler, dbuf);\n\tif (err)\n\t\tgoto out;\n\n\t/* generate audio samples with test digits */\n\tfor (i=0; i<str_len(digits); i++) {\n\t\terr = autone_dtmf(mb, SRATE, digits[i]);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\t/* use the DTMF detector to decode digits */\n\tdtmf_dec_probe(dec, (void *)mb->buf, mb->end / 2);\n\n\tTEST_STRCMP(digits, str_len(digits), dbuf, str_len(dbuf));\n\n out:\n\tmem_deref(dec);\n\tmem_deref(mb);\n\treturn err;\n}\n"
  },
  {
    "path": "test/fir.c",
    "content": "/**\n * @file fir.c FIR-filter Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"fir\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/* 48kHz sample-rate, 8kHz cutoff (pass 0-7kHz, stop 9-24kHz) */\nstatic const int16_t fir_48_8[] = {\n\t238,    198,   -123,   -738,  -1268,  -1204,   -380,    714,\n       1164,    376,  -1220,  -2206,  -1105,   2395,   6909,  10069,\n      10069,   6909,   2395,  -1105,  -2206,  -1220,    376,   1164,\n\t714,   -380,  -1204,  -1268,   -738,   -123,    198,    238\n};\n\n\nint test_fir(void)\n{\n#define NUM_SAMPLES 8\n\tstruct fir fir;\n\tstatic const int16_t samp_in[NUM_SAMPLES] =\n\t\t{-8000, -4000, -2000, 0, 2000, 4000, 8000, 4000};\n\tstatic const int16_t samp_out_exp[NUM_SAMPLES] =\n\t\t{ -59, -78, -9, 183, 421, 534, 391, -38};\n\tint16_t samp_out[NUM_SAMPLES];\n\tint err = 0;\n\n\tfir_reset(&fir);\n\n\t/* verify FIR-filter state */\n\tTEST_EQUALS(0, fir.index);\n\n\t/* process the FIR filter */\n\tfir_filter(&fir, samp_out, samp_in, RE_ARRAY_SIZE(samp_in),\n\t\t   1, fir_48_8, RE_ARRAY_SIZE(fir_48_8));\n\n\t/* verify FIR-filter state */\n\tTEST_EQUALS(NUM_SAMPLES, fir.index);\n\tTEST_ASSERT(NUM_SAMPLES <= RE_ARRAY_SIZE(fir.history));\n\tTEST_MEMCMP(samp_in, sizeof(samp_in), fir.history, sizeof(samp_in));\n\n\t/* verify output samples */\n\tTEST_MEMCMP(samp_out_exp, sizeof(samp_out_exp),\n\t\t    samp_out, sizeof(samp_out));\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/fmt.c",
    "content": "/**\n * @file fmt.c Formatting Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <stdlib.h>\n#include <wchar.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"testfmt\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_fmt_pl(void)\n{\n\tint err;\n\tconst struct pl pl   = PL(\"rattarei\");\n\tconst struct pl pl0  = PL(\"rattarei\");\n\tconst struct pl pl0_ = PL(\"rAtTaReI\");\n\tconst struct pl pl1  = PL(\"foobar\");\n\tconst struct pl pl2  = PL(\"rarei\");\n\tconst struct pl pl3  = PL(\"8zmoijdalij32li34jkljsldsjalkfj9jshruhga\"\n\t\t\t\t  \"laksjdliasjf98uasehr98wehrdisflsdifholis\"\n\t\t\t\t  \"djlweijrdisfjslkdfjslfjowiejrodflaijsdla\"\n\t\t\t\t  \"ksjdlaskdfjslkdfjsldfkjsdlfkjsdlkrtjqwli\"\n\t\t\t\t  \"ejsldfjsldfkjsdlfkjdiajsduhiafhurjiejidi\");\n\tconst struct pl pl4  = PL(\"rattarei4\");\n\tconst char str0[] = \"rattarei\";\n\tconst char str1[] = \"rattaray\";\n\tconst char str2[] = \"foo\";\n\tconst char str3[] = \"bar\";\n\tstruct pl pl5, pl6;\n\tconst struct pl pl7   = PL(\"hei\");\n\tconst struct pl pl7_  = PL(\"Hei\");\n\tconst struct pl pl7__ = PL(\"Duz\");\n\tconst struct pl pl_empty = PL(\"\");\n\n\t/* pl_cmp() */\n\tif (EINVAL != pl_cmp(NULL, NULL))\n\t\tgoto out;\n\tif (0 != pl_cmp(&pl, &pl0))\n\t\tgoto out;\n\tif (0 == pl_cmp(&pl, &pl1))\n\t\tgoto out;\n\tif (0 != pl_cmp(&pl, &pl0))\n\t\tgoto out;\n\tif (0 == pl_cmp(&pl, &pl2))\n\t\tgoto out;\n\tif (0 != pl_cmp(&pl3, &pl3))\n\t\tgoto out;\n\tif (0 != pl_cmp(&pl_null, &pl_null))\n\t\tgoto out;\n\n\t/* pl_casecmp() */\n\tif (EINVAL != pl_casecmp(NULL, NULL))\n\t\tgoto out;\n\tif (0 != pl_casecmp(&pl, &pl0))\n\t\tgoto out;\n\tif (0 != pl_casecmp(&pl, &pl0_))\n\t\tgoto out;\n\tif (0 == pl_casecmp(&pl, &pl1))\n\t\tgoto out;\n\tif (0 == pl_casecmp(&pl, &pl4))\n\t\tgoto out;\n\tpl5.p = str0;\n\tpl5.l = 6;\n\tpl6.p = str1;\n\tpl6.l = 6;\n\tif (0 != pl_casecmp(&pl5, &pl6))\n\t\tgoto out;\n\tif (0 != pl_casecmp(&pl, &pl0))\n\t\tgoto out;\n\tif (0 != pl_casecmp(&pl, &pl0_))\n\t\tgoto out;\n\tif (0 == pl_casecmp(&pl, &pl2))\n\t\tgoto out;\n\tif (0 != pl_casecmp(&pl7, &pl7_))\n\t\tgoto out;\n\tif (0 == pl_casecmp(&pl7, &pl7__))\n\t\tgoto out;\n\tif (0 != pl_casecmp(&pl_null, &pl_null))\n\t\tgoto out;\n\n\t/* pl_strcmp() */\n\tif (EINVAL != pl_strcmp(NULL, NULL))\n\t\tgoto out;\n\tif (0 != pl_strcmp(&pl0, str0))\n\t\tgoto out;\n\tif (0 == pl_strcmp(&pl0, str1))\n\t\tgoto out;\n\tif (0 == pl_strcmp(&pl3, str0))\n\t\tgoto out;\n\n\t/* pl_strncmp() */\n\terr = pl_strncmp(&pl, \"rat\", 3);\n\tTEST_ERR(err);\n\terr = pl_strncmp(&pl, \"RAT\", 3);\n\tTEST_EQUALS(EINVAL, err);\n\n\t/* pl_strncasecmp() */\n\terr = pl_strncasecmp(&pl, \"RaT\", 3);\n\tTEST_ERR(err);\n\n\t/* pl_strcasecmp() */\n\tif (EINVAL != pl_strcasecmp(NULL, NULL))\n\t\tgoto out;\n\tif (0 != pl_strcasecmp(&pl0_, str0))\n\t\tgoto out;\n\tif (0 == pl_strcasecmp(&pl0_, str1))\n\t\tgoto out;\n\tif (0 == pl_strcasecmp(&pl3, str0))\n\t\tgoto out;\n\n\t/* pl_strchr() */\n\tif (pl0.p != pl_strchr(&pl0, 'r'))\n\t\tgoto out;\n\tif (NULL != pl_strchr(&pl0, 'B'))\n\t\tgoto out;\n\n\t/* pl_strrchr() */\n\tif (pl0.p + 5 != pl_strrchr(&pl0, 'r'))\n\t\tgoto out;\n\tif (NULL != pl_strrchr(&pl0, 'B'))\n\t\tgoto out;\n\tif (NULL != pl_strrchr(&pl_empty, 'r'))\n\t\tgoto out;\n\n\t/* pl_strstr() */\n\tif (pl.p != pl_strstr(&pl, str0))\n\t\tgoto out;\n\tif (pl1.p != pl_strstr(&pl1, str2))\n\t\tgoto out;\n\tif (pl1.p + 3 != pl_strstr(&pl1, str3))\n\t\tgoto out;\n\tif (NULL != pl_strstr(&pl, str1))\n\t\tgoto out;\n\tif (pl.p != pl_strstr(&pl, \"\"))\n\t\tgoto out;\n\tif (NULL != pl_strstr(&pl1, str0))\n\t\tgoto out;\n\n\t/* pl_strip_html */\n\tstruct pl pl_html = PL_INIT;\n\tchar str_html[]\t  = \"abc <script>alert(1)</script> <= test <><a\";\n\tpl_set_str(&pl_html, str_html);\n\tpl_strip_html(&pl_html);\n\tTEST_EQUALS(23, pl_html.l);\n\tTEST_EQUALS(0, pl_strcmp(&pl_html, \"abc alert(1) <= test <>\"));\n\n\treturn 0;\n out:\n\treturn EINVAL;\n}\n\n\nint test_fmt_pl_alloc_dup(void)\n{\n\tint err\t\t= 0;\n\tconst struct pl pl0 = PL(\"rAtTaReI\");\n\n\tstruct pl *pl = pl_alloc_dup(&pl0);\n\tif (!pl)\n\t\treturn ENOMEM;\n\n\tTEST_EQUALS(pl0.l, pl->l);\n\tTEST_MEMCMP(pl0.p, pl0.l, pl->p, pl->l);\n\terr = pl_cmp(&pl0, pl);\n\tTEST_ERR(err);\n\nout:\n\tmem_deref(pl);\n\n\treturn err;\n}\n\n\nint test_fmt_pl_alloc_str(void)\n{\n\tint err\t\t= 0;\n\tchar test_str[] = \"Test String\";\n\n\tstruct pl *pl = pl_alloc_str(test_str);\n\tif (!pl)\n\t\treturn ENOMEM;\n\n\tTEST_MEMCMP(test_str, str_len(test_str), pl->p, pl->l);\n\nout:\n\tmem_deref(pl);\n\n\treturn err;\n}\n\n\nint test_fmt_pl_i32(void)\n{\n\tconst struct {\n\t\tconst struct pl pl;\n\t\tint32_t v;\n\t} testv[] = {\n\t\t/* Error cases */\n\t\t{PL(\"hei\"),  0},\n\t\t{PL(\"abc\"),  0},\n\t\t{PL(\"\"),     0},\n\t\t{{NULL, 2},  0},\n\t\t{{\"fo\", 0},  0},\n\t\t{PL(\"2147483648\"), -2147483647 - 1},\n\t\t{PL(\"9223372036854775808\"), 0},\n\n\t\t/* Working cases */\n\t\t{PL(\"0\"),         0},\n\t\t{PL(\"1\"),         1},\n\t\t{PL(\"-1\"),       -1},\n\t\t{PL(\"123\"),     123},\n\t\t{PL(\"-123\"),   -123},\n\t\t{PL(\"5467\"),   5467},\n\t\t{PL(\"-123\"),   -123},\n\t\t{PL(\"-5467\"), -5467},\n\t\t{PL(\"2147483647\"),   2147483647},\n\t\t{PL(\"+2147483647\"),  2147483647},\n\t\t{PL(\"-2147483648\"), -2147483647 - 1},\n\t};\n\tuint32_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tconst int32_t v = pl_i32(&testv[i].pl);\n\n\t\tTEST_EQUALS(testv[i].v, v);\n\t}\n\nout:\n\tif (err)\n\t\tDEBUG_WARNING(\"failed pl was %r\\n\", &testv[i].pl);\n\n\treturn err;\n}\n\n\nint test_fmt_pl_i64(void)\n{\n\tconst struct {\n\t\tconst struct pl pl;\n\t\tint64_t v;\n\t} testv[] = {\n\t\t/* Error cases */\n\t\t{PL(\"hei\"),  0},\n\t\t{PL(\"abc\"),  0},\n\t\t{PL(\"\"),     0},\n\t\t{{NULL, 2},  0},\n\t\t{{\"fo\", 0},  0},\n\t\t{PL(\"9223372036854775808\"), INT64_MIN},\n\t\t{PL(\"9223372036854775809\"), -9223372036854775807L},\n\n\t\t/* Working cases */\n\t\t{PL(\"0\"),         0},\n\t\t{PL(\"1\"),         1},\n\t\t{PL(\"-1\"),       -1},\n\t\t{PL(\"123\"),     123},\n\t\t{PL(\"-123\"),   -123},\n\t\t{PL(\"5467\"),   5467},\n\t\t{PL(\"-123\"),   -123},\n\t\t{PL(\"-5467\"), -5467},\n\t\t{PL(\"2147483647\"),   2147483647},\n\t\t{PL(\"2147483648\"), 2147483648L},\n\t\t{PL(\"-2147483648\"), -2147483647L - 1L},\n\t\t{PL(\"9223372036854775807\"), 9223372036854775807L},\n\t\t{PL(\"+9223372036854775807\"), 9223372036854775807L},\n\t\t{PL(\"-9223372036854775808\"), -9223372036854775807L - 1L},\n\t};\n\tuint64_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tconst int64_t v = pl_i64(&testv[i].pl);\n\n\t\tTEST_EQUALS(testv[i].v, v);\n\t}\n\nout:\n\tif (err)\n\t\tDEBUG_WARNING(\"failed pl was %r\\n\", &testv[i].pl);\n\n\treturn err;\n}\n\n\nint test_fmt_pl_u32(void)\n{\n\tconst struct {\n\t\tconst struct pl pl;\n\t\tuint32_t v;\n\t} testv[] = {\n\t\t/* Error cases */\n\t\t{PL(\"hei\"),  0},\n\t\t{PL(\"abc\"),  0},\n\t\t{PL(\"\"),     0},\n\t\t{{NULL, 2},  0},\n\t\t{{\"fo\", 0},  0},\n\t\t{PL(\"4294967296\"), 0},\n\t\t{PL(\"18446744073709551616\"), 0},\n\n\t\t/* Working cases */\n\t\t{PL(\"0\"),    0},\n\t\t{PL(\"1\"),    1},\n\t\t{PL(\"123\"),  123},\n\t\t{PL(\"1234\"), 1234},\n\t\t{PL(\"4294967295\"), 4294967295U},\n\t};\n\tuint32_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tconst uint32_t v = pl_u32(&testv[i].pl);\n\n\t\tif (testv[i].v != v) {\n\t\t\tDEBUG_WARNING(\"pl_u32 test %u failed %llu != %llu\\n\",\n\t\t\t\t      i, testv[i].v, v);\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn err;\n}\n\n\nint test_fmt_pl_u64(void)\n{\n\tconst struct {\n\t\tconst struct pl pl;\n\t\tuint64_t v;\n\t} testv[] = {\n\t\t/* Error cases */\n\t\t{PL(\"hei\"),  0},\n\t\t{PL(\"abc\"),  0},\n\t\t{PL(\"\"),     0},\n\t\t{{NULL, 2},  0},\n\t\t{{\"fo\", 0},  0},\n\t\t{PL(\"18446744073709551616\"), 0},\n\n\t\t/* Working cases */\n\t\t{PL(\"0\"),    0},\n\t\t{PL(\"1\"),    1},\n\t\t{PL(\"123\"),  123},\n\t\t{PL(\"1234\"), 1234},\n\t\t{PL(\"4294967295\"), 4294967295UL},\n\t\t{PL(\"4294967296\"), 4294967296ULL},\n\t\t{PL(\"18446744073709551615\"), 18446744073709551615ULL}\n\t};\n\tuint32_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tconst uint64_t v = pl_u64(&testv[i].pl);\n\n\t\tif (testv[i].v != v) {\n\t\t\tDEBUG_WARNING(\"pl_u64 test %u failed %llu != %llu\\n\",\n\t\t\t\t      i, testv[i].v, v);\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn err;\n}\n\n\nint test_fmt_pl_x3264(void)\n{\n\tconst struct {\n\t\tconst char *str;\n\t\tuint32_t x32;\n\t\tuint64_t x64;\n\t} testv[] = {\n\t\t/* Working cases */\n\t\t{\"0\",            0x0,        0x0              },\n\t\t{\"1\",            0x1,        0x1              },\n\t\t{\"123\",          0x123,      0x123            },\n\t\t{\"1234\",         0x1234,     0x1234           },\n\t\t{\"abc\",          0xabc,      0xabc            },\n\t\t{\"94967295\",     0x94967295, 0x94967295       },\n\t\t{\"4bca9ef2\",     0x4bca9ef2, 0x4bca9ef2       },\n\t\t{\"18ab44cd8954\", 0x44cd8954, 0x18ab44cd8954ULL},\n\n\t\t/* Error cases */\n\t\t{\"hei\",          0,          0                },\n\t\t{\"\",             0,          0                },\n\t\t{\"0x123\",        0,          0                },\n\t\t{\",.!$%\",        0,          0                },\n\t};\n\tuint32_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tstruct pl pl;\n\t\tuint32_t x32;\n\t\tuint64_t x64;\n\n\t\tpl_set_str(&pl, testv[i].str);\n\n\t\tx32 = pl_x32(&pl);\n\t\tx64 = pl_x64(&pl);\n\n\t\tif (testv[i].x32 != x32) {\n\t\t\tDEBUG_WARNING(\"pl_x32 test %u failed %x != %x\\n\",\n\t\t\t\t      i, testv[i].x32, x32);\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (testv[i].x64 != x64) {\n\t\t\tDEBUG_WARNING(\"pl_x64 test %u failed %lx != %lx\\n\",\n\t\t\t\t      i, testv[i].x64, x64);\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn err;\n}\n\n\nint test_fmt_pl_float(void)\n{\n\tstruct pl pos = PL(\"123.456\"), neg = PL(\"-123.456\");\n\tdouble v;\n\tint err = 0;\n\n\tv = pl_float(&pos);\n\tTEST_ASSERT(v > .0);\n\n\tv = pl_float(&neg);\n\tTEST_ASSERT(v < .0);\n\n out:\n\treturn err;\n}\n\n\nint test_fmt_regex(void)\n{\n\tconst struct pl pl  = PL(\"hei42sann!\");\n\tconst struct pl pl1 = PL(\"42\");\n\tconst struct pl pl2 = PL(\"sann\");\n\tconst struct pl pl3 = PL(\";foo=\\\"bla;bla\\\"\");\n\tconst struct pl pl4 = PL(\"bla;bla\");\n\tconst struct pl pl5 = PL(\"a \\\"b \\\"1123\\\"\\\" c\");\n\tconst struct pl pl6 = PL(\"-42\");\n\tconst struct pl pla = PL(\"a\");\n\tconst struct pl plb = PL(\"b \\\"1123\\\"\");\n\tconst struct pl plc = PL(\"c\");\n\tstruct pl pln, pls, foo, a, b, c, d, e;\n\tint err = 0;\n\n\t/* Successful case */\n\terr = re_regex(pl.p, pl.l, \"Hei[0-9]+[^!]+\", &pln, &pls);\n\tif (err)\n\t\tgoto out;\n\terr = pl_cmp(&pln, &pl1);\n\tif (err)\n\t\tgoto out;\n\terr = pl_cmp(&pls, &pl2);\n\tif (err)\n\t\tgoto out;\n\n\t/* Quoted strings */\n\terr = re_regex(pl3.p, pl3.l, \";foo=\\\"[^\\\"]+\\\"\", &foo);\n\tif (err)\n\t\tgoto out;\n\terr = pl_cmp(&foo, &pl4);\n\tif (err) {\n\t\tDEBUG_WARNING(\"regex quoted string failed (%r)\\n\", &foo);\n\t\tgoto out;\n\t}\n\n\terr = re_regex(pl3.p, pl3.l, \";foo=[~]+\", &foo);\n\tif (err)\n\t\tgoto out;\n\terr = pl_cmp(&foo, &pl4);\n\tif (err)\n\t\tgoto out;\n\n\t/* re_regex('a \"b \\\"1123\\\"\" c', \"[^ ]+ [~ ]+ [^ ]+\", &a, &b, &c);\n\t   result:  a='a', b='b \\\"1123\\\"', c='c' */\n\terr = re_regex(pl5.p, pl5.l, \"[^ ]+ [~ ]+ [^ ]+\", &a, &b, &c);\n\tif (err)\n\t\tgoto out;\n\terr = pl_cmp(&a, &pla);\n\tif (err)\n\t\tgoto out;\n\terr = pl_cmp(&b, &plb);\n\tif (err)\n\t\tgoto out;\n\terr = pl_cmp(&c, &plc);\n\tif (err)\n\t\tgoto out;\n\n\t/* Failing case */\n\tif (0 == re_regex(pl.p, pl.l, \"tull\")) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\tif (0 == re_regex(pl.p, pl.l, \"[^\\r\\n]+\\r\\n\", &pln)) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\t/* Test escaping */\n\tif (0 == re_regex(pl.p, pl.l, \"[\\\\^0-9]*\\\\]\", NULL)) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\terr = re_regex(pl6.p, pl6.l, \"[\\\\-0-9\\\\^]+\", &pln);\n\tif (err)\n\t\tgoto out;\n\terr = pl_strcmp(&pln, \"-42\");\n\tif (err)\n\t\tgoto out;\n\n\t/* verify that optional matching sets the PL to zero\n\t   if there is no match */\n\te.p = \"x\";\n\te.l = 42;\n\terr = re_regex(pl4.p, pl4.l, \"[a-z]+;[0-9]*\", &d, &e);\n\tif (err)\n\t\tgoto out;\n\tTEST_ASSERT(!pl_isset(&e));\n\n\treturn 0;\n\n out:\n\tDEBUG_WARNING(\"regex failed (%d)\\n\", err);\n\n\treturn err;\n}\n\n\nstatic int fooprint(struct re_printf *pf, void *arg)\n{\n\tint a = *(int *)arg;\n\n\treturn re_hprintf(pf, \"[a=%d]\", a);\n}\n\n\nstatic int va_printf(struct mbuf *mb, const char *fmt, ...)\n{\n\tva_list ap;\n\tint err;\n\n\tva_start(ap, fmt);\n\terr = mbuf_printf(mb, \"[%v]\", fmt, &ap);\n\tva_end(ap);\n\n\treturn err;\n}\n\n\nint test_fmt_print(void)\n{\n\tconst struct pl ref1 = PL(\"-12345 -1234567890 -1234567890123456789\");\n\tconst struct pl ref2 = PL(\"12345 1234567890 1234567890123456789\");\n\tconst struct pl ref3 = PL(\"65535 4294967295 18446744073709551615\");\n\tstruct pl pl;\n\tstruct mbuf mb;\n\tconst int a = 42;\n\tchar *s = NULL;\n\tint err;\n\n\tmbuf_init(&mb);\n\terr = mbuf_printf(&mb, \"%d %ld %lld\", -12345, -1234567890L,\n\t\t\t  -1234567890123456789LL);\n\tif (err)\n\t\tgoto out;\n\tpl.p = (char *)mb.buf;\n\tpl.l = mb.end;\n\terr = pl_cmp(&pl, &ref1);\n\tif (err) {\n\t\tDEBUG_WARNING(\"print 1: ref=(%r) buf=(%r)\\n\", &ref1, &pl);\n\t\tgoto out;\n\t}\n\n\tmbuf_reset(&mb);\n\terr = mbuf_printf(&mb, \"%u %lu %llu\", 12345, 1234567890UL,\n\t\t\t  1234567890123456789ULL);\n\tif (err)\n\t\tgoto out;\n\tpl.p = (char *)mb.buf;\n\tpl.l = mb.end;\n\terr = pl_cmp(&pl, &ref2);\n\tif (err) {\n\t\tDEBUG_WARNING(\"print 2: buf: %r\\n\", &pl);\n\t\tgoto out;\n\t}\n\n\tmbuf_reset(&mb);\n\terr = mbuf_printf(&mb, \"%u %lu %llu\", 65535, 4294967295UL,\n\t\t\t  18446744073709551615ULL);\n\tif (err)\n\t\tgoto out;\n\tpl.p = (char *)mb.buf;\n\tpl.l = mb.end;\n\terr = pl_cmp(&pl, &ref3);\n\tif (err) {\n\t\tDEBUG_WARNING(\"print 3: buf: %r\\n\", &pl);\n\t\tgoto out;\n\t}\n\n\tmbuf_reset(&mb);\n\terr = mbuf_printf(&mb, \"fookokaoskdokoskdookokokq%Hbar\", fooprint, &a);\n\tif (err)\n\t\tgoto out;\n\tpl.p = (char *)mb.buf;\n\tpl.l = mb.end;\n\terr = pl_strcmp(&pl, \"fookokaoskdokoskdookokokq[a=42]bar\");\n\tif (err) {\n\t\tDEBUG_WARNING(\"print 4: buf: %r\\n\", &pl);\n\t\tgoto out;\n\t}\n\n\tmbuf_reset(&mb);\n\terr = va_printf(&mb, \"foo%d%s\", 42, \"barrompabarplaplsdpalspdlplplp\");\n\tif (err)\n\t\tgoto out;\n\tpl.p = (char *)mb.buf;\n\tpl.l = mb.end;\n\terr = pl_strcmp(&pl, \"[foo42barrompabarplaplsdpalspdlplplp]\");\n\tif (err) {\n\t\tDEBUG_WARNING(\"print 5: buf: %r\\n\", &pl);\n\t\tgoto out;\n\t}\n\n\t/* dynamic print */\n\terr = re_sdprintf(&s, \"okaspdokaspodkjalsj%fkmzl12kpdokasdlkj\", 3.14);\n\tif (err)\n\t\tgoto out;\n\n\tpl_set_str(&pl, s);\n\terr = pl_strcmp(&pl, \"okaspdokaspodkjalsj3.140000kmzl12kpdokasdlkj\");\n\tif (err) {\n\t\tDEBUG_WARNING(\"sdprintf: %r\\n\", &pl);\n\t\tgoto out;\n\t}\n\n out:\n\tmbuf_reset(&mb);\n\tmem_deref(s);\n\n\treturn err;\n}\n\n\nint test_fmt_snprintf(void)\n{\n\tconst struct pl ref3 = PL(\"65535 4294967295 18446744073709551615\");\n\tconst uint8_t v[] = {0xfa, 0xce, 0xb0, 0x0c};\n\tstruct sa sa4;\n\tconst char addr4[] = \"1.2.3.4\";\n\tstruct sa sa6;\n\tconst char addr6[] = \"2001:5c0:8fff:ffff::d\";\n\tchar buf[128], sbuf[8];\n\tint n, err;\n\n\t/* Test binary vector printing */\n\tn = re_snprintf(buf, sizeof(buf), \"%w\", v, sizeof(v));\n\tif (2*sizeof(v) != n) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\tif (0 != strcmp(buf, \"faceb00c\")) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\t/* Test sockaddr printing */\n\terr = sa_set_str(&sa4, addr4, 0);\n\tif (err) {\n\t\tDEBUG_WARNING(\"sa_set_str4: %m\\n\", err);\n\t\tgoto out;\n\t}\n\terr = sa_set_str(&sa6, addr6, 0);\n\tif (err) {\n\t\tDEBUG_WARNING(\"sa_set_str6: %m\\n\", err);\n\t\tgoto out;\n\t}\n\n\t(void)re_snprintf(buf, sizeof(buf), \"%j\", &sa4);\n\tif (0 != strcmp(buf, addr4)) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\t(void)re_snprintf(buf, sizeof(buf), \"%j\", &sa6);\n\tif (0 != strcmp(buf, addr6)) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\t/* Overflow */\n\tn = re_snprintf(buf, 3, \"12\");\n\tif (2 != n) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\tn = re_snprintf(buf, 3, \"123\");\n\tif (-1 != n) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\tn = re_snprintf(buf, 4, \"%u\", 12345);\n\tif (-1 != n) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\tn = re_snprintf(buf, 4, \"%s\", \"asdasd\");\n\tif (-1 != n) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\tn = re_snprintf(buf, 37, \"%r\", &ref3);\n\tif (-1 != n) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\t/* Double */\n\t(void)re_snprintf(buf, sizeof(buf), \"%f\", 123.456);\n\tif (0 != strcmp(buf, \"123.456000\"))\n\t\tgoto perr;\n\t(void)re_snprintf(buf, sizeof(buf), \"%f\", -123.456);\n\tif (0 != strcmp(buf, \"-123.456000\"))\n\t\tgoto perr;\n\t(void)re_snprintf(buf, sizeof(buf), \"%.3f\", 123.456);\n\tif (0 != strcmp(buf, \"123.456\"))\n\t\tgoto perr;\n\t(void)re_snprintf(buf, sizeof(buf), \"%6.3f\", 3.14);\n\tif (0 != strcmp(buf, \" 3.140\"))\n\t\tgoto perr;\n\t(void)re_snprintf(buf, sizeof(buf), \"%06.3f\", 3.14);\n\tif (0 != strcmp(buf, \"03.140\"))\n\t\tgoto perr;\n\t(void)re_snprintf(buf, sizeof(buf), \"%6.3f\", -3.14);\n\tif (0 != strcmp(buf, \"-3.140\"))\n\t\tgoto perr;\n\n#if 0\n\t(void)re_snprintf(buf, sizeof(buf), \"%05f\", strtod(\"inf\", NULL));\n\tif (0 != strcmp(buf, \"  inf\"))\n\t\tgoto perr;\n#endif\n\n\t(void)re_snprintf(buf, sizeof(buf), \"%.2f\", 123123123123.00);\n\tif (0 != strcmp(buf, \"123123123123.00\"))\n\t\tgoto perr;\n\n\tmemset(sbuf, 0xff, sizeof(sbuf));\n\tn = re_snprintf(sbuf, sizeof(sbuf), \"ab %d cd\", 42);\n\tif (n != -1 || strcmp(sbuf, \"ab 42\")) {\n\t\tDEBUG_WARNING(\"n=%d sbuf='%s'\\n\", n, sbuf);\n\t\tgoto perr;\n\t}\n\n out:\n\treturn err;\n perr:\n\tDEBUG_WARNING(\"bad msg: '%s'\\n\", buf);\n\treturn EBADMSG;\n}\n\n\nint test_fmt_str(void)\n{\n\tconst struct {\n\t\tconst char *dst;\n\t\tconst char *src;\n\t\tuint32_t n;\n\t} testv[] = {\n\t\t{\"foo\", \"foo\", 64},\n\t\t{\"foo\", \"foo\", 4},\n\t\t{\"fo\",  \"foo\", 3},\n\t\t{\"123456789\",  \"1234567890\", 10}\n\t};\n\tchar buf[64];\n\tsize_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tconst size_t sz = strlen(testv[i].dst) + 1;\n\t\tstr_ncpy(buf, testv[i].src, testv[i].n);\n\n\t\tif (0 != memcmp(testv[i].dst, buf, sz)) {\n\t\t\tDEBUG_WARNING(\"test %u failed %s != %s\\n\",\n\t\t\t\t      i, buf, testv[i].dst);\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn err;\n}\n\n\nstatic const struct {\n\tconst char *pname;\n\tconst char *pval;\n\tbool present;\n} testv[] = {\n\t/* existing parameters: */\n\t{\"bitrate\",     \"42\",  true},\n\t{\"foo\",         0,     true},\n\t{\"bar\",         \"BAR\", true},\n\n\t/* non-existing parameters: */\n\t{\"xbitrate\",    0,     false},\n\t{\"bitratex\",    0,     false},\n\t{\"itrat\",       0,     false},\n\t{\"fo\",          0,     false},\n\t{\"oo\",          0,     false},\n};\nstatic const struct pl params = PL(\"bitrate=42; foo ; bar = \\\"BAR\\\"\");\n\n\nstatic void fmt_param_handler(const struct pl *name, const struct pl *val,\n\t\t\t      void *arg)\n{\n\tvoid **argv = arg;\n\tsize_t *i = argv[0];\n\tint *err = argv[1];\n\n\tif (*i >= RE_ARRAY_SIZE(testv)) {\n\t\tDEBUG_WARNING(\"param: too many parameters (%u > %u)\\n\",\n\t\t\t      *i, RE_ARRAY_SIZE(testv));\n\t\t*err = EOVERFLOW;\n\t\treturn;\n\t}\n\n\tif (!testv[*i].present) {\n\t\tDEBUG_WARNING(\"param: %u: unexpected param '%r'\\n\",\n\t\t\t      *i, name);\n\t\t*err = EBADMSG;\n\t}\n\n\tif (0 != pl_strcmp(name, testv[*i].pname)) {\n\t\tDEBUG_WARNING(\"param: %u: name mismatch: '%r' != '%s'\\n\",\n\t\t\t      *i, name, testv[*i].pname);\n\t\t*err = EBADMSG;\n\t}\n\n\tif (testv[*i].pval && 0 != pl_strcmp(val, testv[*i].pval)) {\n\t\tDEBUG_WARNING(\"param: %u: value mismatch: '%r' != '%s'\\n\",\n\t\t\t      *i, val, testv[*i].pval);\n\t\t*err = EBADMSG;\n\t}\n\n\t++(*i);\n}\n\n\nint test_fmt_param(void)\n{\n\tsize_t i;\n\tint err = 0;\n\tvoid *argv[2];\n\n\targv[0] = &i;\n\targv[1] = &err;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tstruct pl val;\n\t\tbool res;\n\n\t\tres = fmt_param_exists(&params, testv[i].pname);\n\t\tif (!res != !testv[i].present) {\n\t\t\tDEBUG_WARNING(\"%u: unexpected %d != %d\\n\",\n\t\t\t\t      i, testv[i].present, res);\n\t\t\treturn EBADMSG;\n\t\t}\n\n\t\tif (!testv[i].pval)\n\t\t\tcontinue;\n\n\t\tres = fmt_param_get(&params, testv[i].pname, &val);\n\t\tif (!res != !testv[i].present) {\n\t\t\tDEBUG_WARNING(\"%u: unexpected %d != %d\\n\",\n\t\t\t\t      i, testv[i].present, res);\n\t\t\treturn EBADMSG;\n\t\t}\n\n\t\tif (res && 0 != pl_strcmp(&val, testv[i].pval)) {\n\t\t\tDEBUG_WARNING(\"%u: value mismatch %r != %s\\n\",\n\t\t\t\t      i, &val, testv[i].pval);\n\t\t\treturn EINVAL;\n\t\t}\n\t}\n\n\ti = 0;\n\tfmt_param_apply(&params, fmt_param_handler, argv);\n\n\treturn err;\n}\n\n\nint test_fmt_gmtime(void)\n{\n\tconst char ref1[] = \"Thu, 01 Jan 1970 00:00:00 GMT\";\n\tconst char ref2[] = \"Fri, 24 Jun 2022 07:22:34 GMT\";\n\tchar buf[256];\n\tuint64_t sec;\n\tint n;\n\tint err = 0;\n\n\tsec = 0;\n\t(void)re_snprintf(buf, sizeof(buf), \"%H\", fmt_gmtime, &sec);\n\tTEST_STRCMP(ref1, strlen(ref1), buf, strlen(buf));\n\n\tsec = 19167 * 3600 * 24 + 7*3600 + 22*60 + 34;\n\tn = re_snprintf(buf, sizeof(buf), \"%H\", fmt_gmtime, &sec);\n\tTEST_EQUALS(29, n);\n\tTEST_STRCMP(ref2, strlen(ref2), buf, strlen(buf));\n\n out:\n\treturn err;\n}\n\n\nint test_fmt_human_time(void)\n{\n\tconst char ref1[] = \"1 day 2 hours 3 mins 4 secs\";\n\tconst char ref2[] = \"1 min 2 secs\";\n\tchar buf[256];\n\tuint32_t sec;\n\tint err = 0;\n\n\tsec = 1*24*60*60 + 2*60*60 + 3*60 + 4;\n\t(void)re_snprintf(buf, sizeof(buf), \"%H\", fmt_human_time, &sec);\n\tTEST_STRCMP(ref1, strlen(ref1), buf, strlen(buf));\n\n\tsec = 0*24*60*60 + 0*60*60 + 1*60 + 2;\n\t(void)re_snprintf(buf, sizeof(buf), \"%H\", fmt_human_time, &sec);\n\tTEST_STRCMP(ref2, strlen(ref2), buf, strlen(buf));\n\n out:\n\treturn err;\n}\n\n\nint test_fmt_timestamp(void)\n{\n\tchar buf[256];\n\tstruct pl pl;\n\tint n;\n\tint err = 0;\n\n\tn = re_snprintf(buf, sizeof(buf), \"%H\", fmt_timestamp, NULL);\n\tTEST_ASSERT(n >= 0);\n\n\tpl_set_str(&pl, buf);\n\tTEST_EQUALS(pl.l, (size_t)n);\n\tTEST_EQUALS(pl.l, 12);\n\n\terr = re_regex(pl.p, pl.l,\n\t\t       \"[0-2]1[0-9]1:[0-5]1[0-9]1:[0-5]1[0-9]1\\\\.[0-9]3\",\n\t\t       NULL, NULL, NULL, NULL, NULL, NULL, NULL);\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n\n\nint test_fmt_str_error(void)\n{\n\tchar buf[256];\n\tint err = 0;\n\n\tTEST_ASSERT(str_isset(str_error(EINVAL, buf, sizeof(buf))));\n out:\n\treturn err;\n}\n\n\nint test_fmt_unicode(void)\n{\n\tconst char input[] = \"abc\\\\b\\\\f\\\\n\\\\r\\\\t\\\\u0001\";\n\tchar buf[1024], buf2[1024];\n\tstruct pl pl;\n\tint err = 0;\n\n\tpl_set_str(&pl, input);\n\n\tre_snprintf(buf, sizeof(buf), \"%H\", utf8_decode, &pl);\n\n\tTEST_STRCMP(\"abc\\b\\f\\n\\r\\t\\x01\", 9U, buf, str_len(buf));\n\n\tre_snprintf(buf2, sizeof(buf2), \"%H\", utf8_encode, buf);\n\n\tTEST_STRCMP(input, str_len(input), buf2, str_len(buf2));\n\n out:\n\treturn err;\n}\n\n\nint test_fmt_unicode_decode(void)\n{\n\tstatic const struct test {\n\t\tconst char *utf8_ref;\n\t\tconst char *str;\n\t} unitestv[] = {\n\n\t/* UTF8 Binary:       Unicode:        */\n\t{ \"\\x40\",             \"\\\\u0040\"        },  /* The '@' symbol */\n\t{ \"\\xc3\\x85\",         \"\\\\u00C5\"        },\n\t{ \"\\xE2\\x82\\xAC\",     \"\\\\u20AC\"        },  /* Euro symbol */\n\t{ \"\\xF0\\x9D\\x84\\x9E\", \"\\\\uD834\\\\uDD1E\" },  /* G-key */\n\t{ \"\\xF0\\x9F\\x98\\x82\", \"\\\\uD83D\\\\uDE02\" },  /* Face with tears of joy */\n\n\t};\n\tsize_t i;\n\tchar buf[256];\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(unitestv); i++) {\n\n\t\tconst struct test *test = &unitestv[i];\n\t\tstruct pl pl;\n\t\tint n_exp;\n\t\tint n;\n\n\t\tn_exp = (int)str_len(test->utf8_ref);\n\n\t\tpl_set_str(&pl, test->str);\n\n\t\tn = re_snprintf(buf, sizeof(buf), \"%H\", utf8_decode, &pl);\n\n#if 0\n\t\tre_printf(\"printed: '%s'\\n\", buf);\n#endif\n\n\t\tTEST_MEMCMP(test->utf8_ref, n_exp, buf, n);\n\t}\n\n out:\n\treturn err;\n}\n\n\nint test_fmt_str_bool(void)\n{\n\tbool en;\n\tint err = 0;\n\tsize_t i;\n\n\tenum curstate {\n\t\tSTATE_TRUE,\n\t\tSTATE_FALSE,\n\t\tSTATE_UNSUP\n\t};\n\n\n\tenum curstate state = STATE_TRUE;\n\n\tstatic const char *truestr[] = {\n\t\t\"1\",\n\t\t\"true\",\n\t\t\"enable\",\n\t\t\"on\",\n\t\t\"yes\",\n\t\t\"Yes\",\n\t\t\"YEs\",\n\t};\n\n\tstatic const char *falsestr[] = {\n\t\t\"0\",\n\t\t\"false\",\n\t\t\"disable\",\n\t\t\"off\",\n\t\t\"no\",\n\t\t\"NO\",\n\t};\n\n\tstatic const char *notsup[] = {\n\t\t\"xxx\",\n\t\t\"not\",\n\t\t\"sure\",\n\t\t\"notsure\",\n\t\t\"YESS\",\n\t};\n\n\tfor (i = 0; i < RE_ARRAY_SIZE(truestr); i++) {\n\t\terr = str_bool(&en, truestr[i]);\n\t\tTEST_ERR(err);\n\t\tTEST_EQUALS(true, en);\n\n\t\tstruct pl s;\n\t\tpl_set_str(&s, truestr[i]);\n\t\terr = pl_bool(&en, &s);\n\t\tTEST_ERR(err);\n\t\tTEST_EQUALS(true, en);\n\t}\n\n\tstate = STATE_FALSE;\n\tfor (i = 0; i < RE_ARRAY_SIZE(falsestr); i++) {\n\t\terr = str_bool(&en, falsestr[i]);\n\t\tTEST_ERR(err);\n\t\tTEST_EQUALS(false, en);\n\n\t\tstruct pl s;\n\t\tpl_set_str(&s, falsestr[i]);\n\t\terr = pl_bool(&en, &s);\n\t\tTEST_ERR(err);\n\t\tTEST_EQUALS(false, en);\n\t}\n\n\tstate = STATE_UNSUP;\n\tfor (i = 0; i < RE_ARRAY_SIZE(notsup); i++) {\n\t\terr = str_bool(&en, notsup[i]);\n\t\tTEST_EQUALS(err, EINVAL);\n\n\t\tstruct pl s;\n\t\tpl_set_str(&s, notsup[i]);\n\t\terr = pl_bool(&en, &s);\n\t\tTEST_EQUALS(err, EINVAL);\n\t}\n\n\terr = 0;\n out:\n\tif (err && state == STATE_UNSUP) {\n\t\tDEBUG_WARNING(\"processed unsupported string number %d: %s\\n\",\n\t\t\t      i, notsup[i]);\n\t}\n\telse if (err) {\n\t\tDEBUG_WARNING(\"could not successfully convert %s\\n\",\n\t\t\t\tstate == STATE_TRUE ? truestr[i] :\n\t\t\t\tfalsestr[i]);\n\t}\n\n\treturn err;\n}\n\n\nint test_fmt_str_itoa(void)\n{\n\tchar buf[ITOA_BUFSZ];\n\tchar *s;\n\tint err = 0;\n\n\ts = str_itoa(0, buf, 10);\n\tTEST_ASSERT(!str_casecmp(s, \"0\"));\n\n\ts = str_itoa(42, buf, 10);\n\tTEST_ASSERT(!str_casecmp(s, \"42\"));\n\n\ts = str_itoa(UINT32_MAX, buf, 10);\n\tTEST_ASSERT(!str_casecmp(s, \"4294967295\"));\n\n\ts = str_itoa(UINT32_MAX, buf, 16);\n\tTEST_ASSERT(!str_casecmp(s, \"FFFFFFFF\"));\n\n\ts = str_itoa(23, buf, 2);\n\tTEST_ASSERT(!str_casecmp(s, \"10111\"));\n\n\ts = str_itoa(UINT32_MAX, buf, 2);\n\tTEST_ASSERT(!str_casecmp(s, \"11111111111111111111111111111111\"));\n\nout:\n\tif (err)\n\t\tDEBUG_WARNING(\"err itoa string: %s\\n\", s);\n\n\treturn err;\n}\n\n\nint test_fmt_str_wchar(void)\n{\n\twchar_t s1[] = L\"Test String\";\n\twchar_t *s2;\n\tint err = 0;\n\n\ts2 = str_wchar(\"Test String\");\n\tif (!s2)\n\t\treturn ENOMEM;\n\n\tTEST_EQUALS(0, wcscmp(s1, s2));\n\nout:\n\tmem_deref(s2);\n\treturn err;\n}\n\n\nint test_fmt_hexdump(void)\n{\n\tconst char buf[] =\n\t\t\"0lnmdj2ihickdspjkm2ffd0jcpbk5l1n\"\n\t\t\"8abcjt5m950gxvkuvippcvt60me9z5zh\"\n\t\t;\n\n#ifdef WIN32\n\tFILE *f = fopen(\"nul\", \"w\");\n#else\n\tFILE *f = fopen(\"/dev/null\", \"w\");\n#endif\n\tif (!f)\n\t\treturn EINVAL;\n\n\thexdump(f, buf, str_len(buf));\n\n\tfclose(f);\n\n\treturn 0;\n}\n\n\nint test_text2pcap(void)\n{\n\tchar test[64];\n\tstruct mbuf *mb;\n\tint err = 0;\n\n\tmb = mbuf_alloc(2);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tmbuf_write_u8(mb, 42);\n\tmbuf_write_u8(mb, 23);\n\n\tmbuf_set_pos(mb, 0);\n\n\tstruct re_text2pcap pcap = {.id = \"test\", .in = true, .mb = mb};\n\n\tint ret = re_snprintf(test, sizeof(test), \"%H\", re_text2pcap, &pcap);\n\n\tTEST_EQUALS(35, ret);\n\n\tmbuf_set_pos(mb, 0);\n\tre_text2pcap_trace(\"retest\", \"RETEST\", true, mb);\n\nout:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nint test_fmt_trim(void)\n{\n\tconst struct pl pla = PL(\"  heisann  \");\n\tconst struct pl pll = PL(\"heisann  \");\n\tconst struct pl plr = PL(\"  heisann\");\n\tconst struct pl plb = PL(\"heisann\");\n\n\tconst struct pl pl  = PL(\"\t \\r\t\\n  heisann\t\\n \\r  \");\n\tconst struct pl pl1 = PL(\"heisann\t\\n \\r  \");\n\tconst struct pl pl2 = PL(\"\t \\r\t\\n  heisann\");\n\tconst struct pl pl3 = PL(\"heisann\");\n\n\tconst struct pl pl4 = PL(\"\t \\r \\n\");\n\tconst struct pl pl5 = PL(\"\");\n\tstruct pl pln;\n\n\tint err = 0;\n\n\tpln = pla;\n\terr = pl_ltrim(&pln);\n\tTEST_ERR(err);\n\terr = pl_cmp(&pln, &pll);\n\tTEST_ERR(err);\n\n\tpln = pla;\n\terr = pl_rtrim(&pln);\n\tTEST_ERR(err);\n\terr = pl_cmp(&pln, &plr);\n\tTEST_ERR(err);\n\n\tpln = pla;\n\terr = pl_trim(&pln);\n\tTEST_ERR(err);\n\terr = pl_cmp(&pln, &plb);\n\tTEST_ERR(err);\n\n\tpln = pl1;\n\terr = pl_ltrim(&pln);\n\tTEST_ERR(err);\n\terr = pl_cmp(&pln, &pl1);\n\tTEST_ERR(err);\n\n\tpln = pl;\n\terr = pl_rtrim(&pln);\n\tTEST_ERR(err);\n\terr = pl_cmp(&pln, &pl2);\n\tTEST_ERR(err);\n\n\tpln = pl;\n\terr = pl_trim(&pln);\n\tTEST_ERR(err);\n\terr = pl_cmp(&pln, &pl3);\n\tTEST_ERR(err);\n\n\tpln = pl4;\n\terr = pl_ltrim(&pln);\n\tTEST_ERR(err);\n\terr = pl_cmp(&pln, &pl5);\n\tTEST_ERR(err);\n\n\tpln = pl4;\n\terr = pl_rtrim(&pln);\n\tTEST_ERR(err);\n\terr = pl_cmp(&pln, &pl5);\n\tTEST_ERR(err);\n\n\tpln = pl4;\n\terr = pl_trim(&pln);\n\tTEST_ERR(err);\n\terr = pl_cmp(&pln, &pl5);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/g711.c",
    "content": "/**\n * @file g711.c  G.711 test vectors\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include <rem_g711.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_g711\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/* G.711 reference tables - copied from re/src/g711/g711.c */\n\n\nstatic const signed short _st_alaw2linear16[256] = {\n\t-5504,   -5248,   -6016,   -5760,   -4480,   -4224,   -4992,\n\t-4736,   -7552,   -7296,   -8064,   -7808,   -6528,   -6272,\n\t-7040,   -6784,   -2752,   -2624,   -3008,   -2880,   -2240,\n\t-2112,   -2496,   -2368,   -3776,   -3648,   -4032,   -3904,\n\t-3264,   -3136,   -3520,   -3392,  -22016,  -20992,  -24064,\n\t-23040,  -17920,  -16896,  -19968,  -18944,  -30208,  -29184,\n\t-32256,  -31232,  -26112,  -25088,  -28160,  -27136,  -11008,\n\t-10496,  -12032,  -11520,   -8960,   -8448,   -9984,   -9472,\n\t-15104,  -14592,  -16128,  -15616,  -13056,  -12544,  -14080,\n\t-13568,    -344,    -328,    -376,    -360,    -280,    -264,\n\t-312,    -296,    -472,    -456,    -504,    -488,    -408,\n\t-392,    -440,    -424,     -88,     -72,    -120,    -104,\n\t-24,      -8,     -56,     -40,    -216,    -200,    -248,\n\t-232,    -152,    -136,    -184,    -168,   -1376,   -1312,\n\t-1504,   -1440,   -1120,   -1056,   -1248,   -1184,   -1888,\n\t-1824,   -2016,   -1952,   -1632,   -1568,   -1760,   -1696,\n\t-688,    -656,    -752,    -720,    -560,    -528,    -624,\n\t-592,    -944,    -912,   -1008,    -976,    -816,    -784,\n\t-880,    -848,    5504,    5248,    6016,    5760,    4480,\n\t4224,    4992,    4736,    7552,    7296,    8064,    7808,\n\t6528,    6272,    7040,    6784,    2752,    2624,    3008,\n\t2880,    2240,    2112,    2496,    2368,    3776,    3648,\n\t4032,    3904,    3264,    3136,    3520,    3392,   22016,\n\t20992,   24064,   23040,   17920,   16896,   19968,   18944,\n\t30208,   29184,   32256,   31232,   26112,   25088,   28160,\n\t27136,   11008,   10496,   12032,   11520,    8960,    8448,\n\t9984,    9472,   15104,   14592,   16128,   15616,   13056,\n\t12544,   14080,   13568,     344,     328,     376,     360,\n\t280,     264,     312,     296,     472,     456,     504,\n\t488,     408,     392,     440,     424,      88,      72,\n\t120,     104,      24,       8,      56,      40,     216,\n\t200,     248,     232,     152,     136,     184,     168,\n\t1376,    1312,    1504,    1440,    1120,    1056,    1248,\n\t1184,    1888,    1824,    2016,    1952,    1632,    1568,\n\t1760,    1696,     688,     656,     752,     720,     560,\n\t528,     624,     592,     944,     912,    1008,     976,\n\t816,     784,     880,     848\n};\n\n\nstatic const signed short _st_ulaw2linear16[256] = {\n\t-32124,  -31100,  -30076,  -29052,  -28028,  -27004,  -25980,\n\t-24956,  -23932,  -22908,  -21884,  -20860,  -19836,  -18812,\n\t-17788,  -16764,  -15996,  -15484,  -14972,  -14460,  -13948,\n\t-13436,  -12924,  -12412,  -11900,  -11388,  -10876,  -10364,\n\t-9852,   -9340,   -8828,   -8316,   -7932,   -7676,   -7420,\n\t-7164,   -6908,   -6652,   -6396,   -6140,   -5884,   -5628,\n\t-5372,   -5116,   -4860,   -4604,   -4348,   -4092,   -3900,\n\t-3772,   -3644,   -3516,   -3388,   -3260,   -3132,   -3004,\n\t-2876,   -2748,   -2620,   -2492,   -2364,   -2236,   -2108,\n\t-1980,   -1884,   -1820,   -1756,   -1692,   -1628,   -1564,\n\t-1500,   -1436,   -1372,   -1308,   -1244,   -1180,   -1116,\n\t-1052,    -988,    -924,    -876,    -844,    -812,    -780,\n\t-748,    -716,    -684,    -652,    -620,    -588,    -556,\n\t-524,    -492,    -460,    -428,    -396,    -372,    -356,\n\t-340,    -324,    -308,    -292,    -276,    -260,    -244,\n\t-228,    -212,    -196,    -180,    -164,    -148,    -132,\n\t-120,    -112,    -104,     -96,     -88,     -80,     -72,\n\t-64,     -56,     -48,     -40,     -32,     -24,     -16,\n\t-8,       -2,   32124,   31100,   30076,   29052,   28028,\n\t27004,   25980,   24956,   23932,   22908,   21884,   20860,\n\t19836,   18812,   17788,   16764,   15996,   15484,   14972,\n\t14460,   13948,   13436,   12924,   12412,   11900,   11388,\n\t10876,   10364,    9852,    9340,    8828,    8316,    7932,\n\t7676,    7420,    7164,    6908,    6652,    6396,    6140,\n\t5884,    5628,    5372,    5116,    4860,    4604,    4348,\n\t4092,    3900,    3772,    3644,    3516,    3388,    3260,\n\t3132,    3004,    2876,    2748,    2620,    2492,    2364,\n\t2236,    2108,    1980,    1884,    1820,    1756,    1692,\n\t1628,    1564,    1500,    1436,    1372,    1308,    1244,\n\t1180,    1116,    1052,     988,     924,     876,     844,\n\t812,     780,     748,     716,     684,     652,     620,\n\t588,     556,     524,     492,     460,     428,     396,\n\t372,     356,     340,     324,     308,     292,     276,\n\t260,     244,     228,     212,     196,     180,     164,\n\t148,     132,     120,     112,     104,      96,      88,\n\t80,      72,      64,      56,      48,      40,      32,\n\t24,      16,       8,       2\n};\n\n\nint test_g711_alaw(void)\n{\n\tuint32_t i;\n\tuint8_t val;\n\tint n = 0;\n\tint err = 0;\n\n\tval = g711_pcm2alaw(-32768);\n\tTEST_EQUALS(42, val);\n\n\tfor (i=0; i<256; i++) {\n\t\tuint8_t alaw = i, alaw2;\n\t\tint16_t pcm_ref = _st_alaw2linear16[alaw];\n\t\tint16_t pcm = g711_alaw2pcm(alaw);\n\n\t\tif (pcm_ref != pcm) {\n\t\t\tDEBUG_INFO(\"alaw: %u: bogus sample: ref=%d gen=%d\\n\",\n\t\t\t\t   i, pcm_ref, pcm);\n\t\t\t++n;\n\t\t}\n\n\t\talaw2 = g711_pcm2alaw(pcm);\n\t\tif (alaw2 != alaw) {\n\t\t\tDEBUG_INFO(\"alaw: %u: bogus alaw %d != %d\\n\",\n\t\t\t\t   i, alaw, alaw2);\n\t\t\t++n;\n\t\t}\n\t}\n\n\tif (n) {\n\t\tDEBUG_WARNING(\"alaw: error samples: %d\\n\", n);\n\t}\n\nout:\n\treturn n ? EINVAL : err;\n}\n\n\nint test_g711_ulaw(void)\n{\n\tuint32_t i;\n\tuint8_t val;\n\tint n = 0;\n\tint err = 0;\n\n\tval = g711_pcm2ulaw(-32768);\n\tTEST_EQUALS(0, val);\n\n\tfor (i=0; i<256; i++) {\n\t\tuint8_t ulaw = i, ulaw2;\n\t\tint16_t pcm_ref = _st_ulaw2linear16[ulaw];\n\t\tint16_t pcm = g711_ulaw2pcm(ulaw);\n\n\t\tif (pcm != pcm_ref) {\n\t\t\tDEBUG_INFO(\"ulaw: %u: bogus sample ref=%d gen=%d\\n\",\n\t\t\t\t   i, pcm_ref, pcm);\n\t\t\t++n;\n\t\t}\n\n\t\tulaw2 = g711_pcm2ulaw(pcm_ref);\n\t\tif (ulaw2 != ulaw) {\n\t\t\tDEBUG_INFO(\"ulaw: %u: bogus ulaw %d != %d\\n\",\n\t\t\t\t   i, ulaw, ulaw2);\n\t\t\t++n;\n\t\t}\n\t}\n\n\tif (n) {\n\t\tDEBUG_WARNING(\"ulaw: error samples: %d\\n\", n);\n\t}\nout:\n\treturn n ? EINVAL : err;\n}\n"
  },
  {
    "path": "test/h264.c",
    "content": "/**\n * @file h264.c H.264 Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"h264test\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum { DUMMY_TS = 36000 };\n\n\n#if 0\nstatic void dump_annexb(const uint8_t *start, size_t size)\n{\n\tconst uint8_t *end = start + size;\n\tunsigned count = 0;\n\n\tre_printf(\"---- H.264 Annex-B: ----\\n\");\n\n\tconst uint8_t *r = h264_find_startcode(start, end);\n\n\twhile (r < end) {\n\n\t\tstruct h264_nal_header hdr;\n\n\t\t/* skip zeros */\n\t\twhile (!*(r++))\n\t\t\t;\n\n\t\tconst uint8_t *r1 = h264_find_startcode(r, end);\n\t\tsize_t nal_len = r1 - r;\n\n\t\th264_nal_header_decode_buf(&hdr, r);\n\n\t\tre_printf(\".... nal:  len=%2zu  nri=%u  type=%s\\n\",\n\t\t\t  nal_len,\n\t\t\t  hdr.nri, h264_nal_unit_name(hdr.type));\n\n\t\tr = r1;\n\n\t\t++count;\n\t}\n\n\tre_printf(\"Total NAL units: %u\\n\", count);\n\tre_printf(\"\\n\");\n}\n\n\nstatic void dump_rtp(const uint8_t *p, size_t size)\n{\n\tstruct h264_nal_header hdr;\n\n\th264_nal_header_decode_buf(&hdr, p);\n\n\tre_printf(\"RTP NAL: size=%zu nri=%u type=%u(%s)\\n\",\n\t\t  size,\n\t\t  hdr.nri, hdr.type, h264_nal_unit_name(hdr.type));\n\tre_printf(\"\\n\");\n}\n#endif\n\n\nstatic int test_h264_stap_a_encode_base(const uint8_t *frame, size_t len,\n\t\t\t\t   bool long_startcode)\n{\n\tenum { MAX_NRI = 3 };\n\tstruct mbuf *mb_pkt   = mbuf_alloc(256);\n\tstruct mbuf *mb_frame = mbuf_alloc(256);\n\tstruct h264_nal_header hdr;\n\tint err;\n\n\tif (!mb_pkt || !mb_frame) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = h264_stap_encode(mb_pkt, frame, len);\n\tif (err)\n\t\tgoto out;\n\n\tmb_pkt->pos = 0;\n\n\terr = h264_nal_header_decode(&hdr, mb_pkt);\n\tASSERT_EQ(0, err);\n\n\tASSERT_EQ(MAX_NRI,          hdr.nri);              /* NOTE: max NRI */\n\tASSERT_EQ(H264_NALU_STAP_A, hdr.type);\n\n\tif (long_startcode) {\n\t\terr = h264_stap_decode_annexb_long(mb_frame, mb_pkt);\n\t\tASSERT_EQ(0, err);\n\t}\n\telse {\n\t\terr = h264_stap_decode_annexb(mb_frame, mb_pkt);\n\t\tASSERT_EQ(0, err);\n\t}\n\n\tTEST_MEMCMP(frame, len, mb_frame->buf, mb_frame->end);\n\n out:\n\tmem_deref(mb_frame);\n\tmem_deref(mb_pkt);\n\n\treturn err;\n}\n\n\nstatic int test_h264_stap_a_encode(void)\n{\n\tstatic const uint8_t frame[] = {\n\n\t\t/* AUD */\n\t\t0x00, 0x00, 0x01,\n\t\t0x09, 0x10,\n\n\t\t/* SPS */\n\t\t0x00, 0x00, 0x01,\n\t\t0x67, 0x42, 0xc0, 0x1f, 0x8c, 0x8d, 0x40,\n\n\t\t/* PPS */\n\t\t0x00, 0x00, 0x01,\n\t\t0x68, 0xce, 0x3c, 0x80,\n\n\t\t/* IDR_SLICE */\n\t\t0x00, 0x00, 0x01,\n\t\t0x65, 0xb8, 0x00, 0x04, 0x00, 0x00, 0x05, 0x39,\n\t};\n\tstatic const uint8_t frame_long[] = {\n\n\t\t/* AUD */\n\t\t0x00, 0x00, 0x00, 0x01,\n\t\t0x09, 0x10,\n\n\t\t/* SPS */\n\t\t0x00, 0x00, 0x00, 0x01,\n\t\t0x67, 0x42, 0xc0, 0x1f, 0x8c, 0x8d, 0x40,\n\n\t\t/* PPS */\n\t\t0x00, 0x00, 0x00, 0x01,\n\t\t0x68, 0xce, 0x3c, 0x80,\n\n\t\t/* IDR_SLICE */\n\t\t0x00, 0x00, 0x00, 0x01,\n\t\t0x65, 0xb8, 0x00, 0x04, 0x00, 0x00, 0x05, 0x39,\n\t};\n\tint err;\n\n\terr = test_h264_stap_a_encode_base(frame, sizeof(frame), false);\n\tTEST_ERR(err);\n\n\terr = test_h264_stap_a_encode_base(frame_long, sizeof(frame_long),\n\t\t\t\t\t   true);\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n\n\nstatic int test_h264_stap_a_decode(void)\n{\n\tstatic const uint8_t pkt[] = {\n\n\t\t/* SPS */\n\t\t0x00, 0x0e,\n\t\t0x67, 0x42, 0xc0, 0x1f, 0x8c, 0x8d,\n\t\t0x40, 0x50, 0x1e, 0xd0, 0x0f, 0x08,\n\t\t0x84, 0x6a,\n\n\t\t/* PPS */\n\t\t0x00, 0x04,\n\t\t0x68, 0xce, 0x3c, 0x80,\n\n\t\t/* AUD */\n\t\t0x00, 0x02,\n\t\t0x09, 0x10,\n\t};\n\tstruct mbuf *mb_pkt   = mbuf_alloc(256);\n\tstruct mbuf *mb_frame = mbuf_alloc(256);\n\tstruct mbuf *mb_pkt2  = mbuf_alloc(256);\n\tint err;\n\n\tif (!mb_pkt || !mb_frame || !mb_pkt2) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = mbuf_write_mem(mb_pkt, pkt, sizeof(pkt));\n\tASSERT_EQ(0, err);\n\n\tmb_pkt->pos = 0;\n\n\terr = h264_stap_decode_annexb(mb_frame, mb_pkt);\n\tTEST_ERR(err);\n\n\terr = h264_stap_encode(mb_pkt2, mb_frame->buf, mb_frame->end);\n\tASSERT_EQ(0, err);\n\n\tTEST_MEMCMP(pkt, sizeof(pkt), mb_pkt2->buf+1, mb_pkt2->end-1);\n\n out:\n\tmem_deref(mb_frame);\n\tmem_deref(mb_pkt2);\n\tmem_deref(mb_pkt);\n\n\treturn err;\n}\n\n\nint test_h264(void)\n{\n\tstruct h264_nal_header hdr, hdr2;\n\tstatic const uint8_t nal = 0x25;\n\tint err;\n\n\tstruct mbuf *mb = mbuf_alloc(1);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\thdr.f = 0;\n\thdr.nri = 1;\n\thdr.type = H264_NALU_IDR_SLICE;\n\n\terr = h264_nal_header_encode(mb, &hdr);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(1, mb->pos);\n\tTEST_EQUALS(1, mb->end);\n\tTEST_EQUALS(nal, mb->buf[0]);\n\n\tmb->pos = 0;\n\n\terr = h264_nal_header_decode(&hdr2, mb);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(1, mb->pos);\n\tTEST_EQUALS(1, mb->end);\n\n\tTEST_EQUALS(0, hdr2.f);\n\tTEST_EQUALS(1, hdr2.nri);\n\tTEST_EQUALS(5, hdr2.type);\n\n\terr = test_h264_stap_a_encode();\n\tif (err)\n\t\tgoto out;\n\n\terr = test_h264_stap_a_decode();\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nint test_h264_sps(void)\n{\n\tstatic const struct test {\n\t\tconst char *buf;\n\t\tstruct h264_sps sps;\n\t\tstruct vidsz size;\n\t} testv[] = {\n\n\t\t/* sony 1920 x 1080 (scaling list)\n\t\t *\n\t\t * sps:0 profile:122/41 poc:0 ref:0 120x68 MB-AFF 8B8\n\t\t *       crop:0/0/0/8 VUI 422 1/50 b10 reo:-1\n\t\t *\n\t\t */\n\t\t{\n\t\t\t.buf =\n\t\t\t\"7a1029b6d420223319c6632321011198ce33191821033a46\"\n\t\t\t\"656a6524ade91232141a2634ada441822301502b1a246948\"\n\t\t\t\"30402e111208c68c0441284c34f01e0113f2e03c60202028\"\n\t\t\t\"0000030008000003019420\",\n\t\t\t.sps = {\n\t\t\t\t122,41,0,2,\n\t\t\t\t4,0,0,120,68,\n\t\t\t\t0,0,0,8\n\t\t\t},\n\t\t\t.size = {1920, 1080}\n\t\t},\n\n\t\t/* rv\n\t\t *\n\t\t * sps:0 profile:66/52 poc:2 ref:1 120x68 FRM 8B8\n\t\t * crop:0/0/0/8 VUI 420 1/360 b8 reo:0\n\t\t */\n\t\t{\n\t\t\t.buf = \"42c034da01e0089f961000000300\",\n\t\t\t.sps = {\n\t\t\t\t66,52,0,1,\n\t\t\t\t4,2,1,120,68,\n\t\t\t\t0,0,0,8\n\t\t\t},\n\t\t\t.size = {1920, 1080}\n\t\t},\n\n\t\t/* confcall\n\t\t *\n\t\t * sps:0 profile:100/40 poc:0 ref:3 120x68 FRM\n\t\t * 8B8 crop:0/0/0/8 VUI 420 1/60 b8 reo:1\n\t\t */\n\n\t\t{\n\t\t\t.buf =\n\t\t\t\"640028acd100780227e5c05a808080\"\n\t\t\t\"a0000003002000000781e3062240\",\n\t\t\t.sps = {\n\t\t\t\t100,40,0,1,\n\t\t\t\t4,0,3,120,68,\n\t\t\t\t0,0,0,8\n\t\t\t},\n\t\t\t.size = {1920, 1080}\n\t\t},\n\n\t\t/* expert\n\t\t *\n\t\t * sps:0 profile:100/31 poc:0 ref:4 80x45 FRM\n\t\t */\n\t\t{\n\t\t\t.buf =\n\t\t\t\"64001facd9405005bb011000000300100000030320f1831960\",\n\t\t\t.sps = {\n\t\t\t\t100,31,0,1,\n\t\t\t\t4,0,4,80,45,\n\t\t\t\t0,0,0,0\n\t\t\t},\n\t\t\t.size = {1280, 720}\n\t\t},\n\n\t\t/* px\n\t\t *\n\t\t * sps:0 profile:66/31 poc:2 ref:1 80x45 FRM\n\t\t *       crop:0/0/0/0 VUI 420 2000/120000 b8 reo:0\n\t\t */\n\t\t{\n\t\t\t.buf =\n\t\t\t\"42c01f95a014016c8400001f40000753023c2211a8\",\n\t\t\t.sps = {\n\t\t\t\t66,31,0,1,\n\t\t\t\t8,2,1,80,45,\n\t\t\t\t0,0,0,0\n\t\t\t},\n\t\t\t.size = {1280, 720}\n\t\t},\n\n\t\t/* allonsy 854x480\n\t\t *\n\t\t * sps:0 profile:77/30 poc:0 ref:3 54x30 FRM 8B8\n\t\t *       crop:0/10/0/0 VUI 420 1/50 b8 reo:1\n\t\t *\n\t\t */\n\t\t{\n\t\t\t.buf =\n\t\t\t\"4d401ee8806c1ef37808800000030080000019078b1689\",\n\t\t\t.sps = {\n\t\t\t\t77,30,0,1,\n\t\t\t\t4,0,3,54,30,\n\t\t\t\t0,10,0,0\n\t\t\t},\n\t\t\t.size = {854, 480}\n\t\t},\n\n\t\t/* sony 1920x1080\n\t\t *\n\t\t * sps:0 profile:122/40 poc:0 ref:4 120x68 FRM 8B8\n\t\t *       crop:0/0/0/8 VUI 422 1/50 b10 reo:2\n\t\t *\n\t\t */\n\t\t{\n\t\t\t.buf =\n\t\t\t\"7a0028b6cd940780227e2701100\"\n\t\t\t\"0000300100000030320f1831960\",\n\t\t\t.sps = {\n\t\t\t\t122,40,0,2,\n\t\t\t\t4,0,4,120,68,\n\t\t\t\t0,0,0,8\n\t\t\t},\n\t\t\t.size = {1920, 1080}\n\t\t},\n\n\t\t/* testsrc2 yuv444 400x200\n\t\t *\n\t\t * sps:0 profile:244/13 poc:0 ref:4 25x13 FRM 8B8\n\t\t *       crop:0/0/0/8 VUI 444 1/50 b8 reo:2\n\t\t *\n\t\t */\n\t\t{\n\t\t\t.buf =\n\t\t\t\"f4000d919b283237f13808800000030080000019078a14cb\",\n\t\t\t.sps = {\n\t\t\t\t244,13,0,3,\n\t\t\t\t4,0,4,25,13,\n\t\t\t\t0,0,0,8\n\t\t\t},\n\t\t\t.size = {400, 200}\n\t\t},\n\n\t\t/* jellyfish 4K 3840 x 2160\n\t\t *\n\t\t * sps:0 profile:100/51 poc:0 ref:3 240x135 FRM 8B8\n\t\t *       crop:0/0/0/0 VUI 420 1001/60000 b8 reo:1\n\t\t *\n\t\t */\n\t\t{\n\t\t\t.buf =\n\t\t\t\"640033ac2ca400f0010fbff0001000152020202800001f\"\n\t\t\t\"4800075307510001cd9400000568bc37e31c1da162d120\",\n\t\t\t.sps = {\n\t\t\t\t100,51,0,1,\n\t\t\t\t8,0,3,240,135,\n\t\t\t\t0,0,0,0\n\t\t\t},\n\t\t\t.size = {3840, 2160}\n\t\t},\n\t};\n\tconst struct test *test_short;\n\tstruct h264_sps sps;\n\tuint8_t buf[256];\n\tsize_t i;\n\tsize_t max_len;\n\tint e, err;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tconst struct test *test = &testv[i];\n\t\tstruct h264_sps ref = test->sps;\n\t\tsize_t len = str_len(test->buf)/2;\n\t\tstruct vidsz size;\n\n\t\terr = str_hex(buf, len, test->buf);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = h264_sps_decode(&sps, buf, len);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\th264_sps_resolution(&sps, &size.w, &size.h);\n\n\t\tTEST_EQUALS(ref.profile_idc, sps.profile_idc);\n\n\t\tTEST_EQUALS(ref.level_idc, sps.level_idc);\n\n\t\tTEST_EQUALS(ref.seq_parameter_set_id,\n\t\t\t    sps.seq_parameter_set_id);\n\n\t\tTEST_EQUALS(ref.chroma_format_idc,\n\t\t\t    sps.chroma_format_idc);\n\n\t\tTEST_EQUALS(ref.log2_max_frame_num,\n\t\t\t    sps.log2_max_frame_num);\n\n\t\tTEST_EQUALS(ref.pic_order_cnt_type,\n\t\t\t    sps.pic_order_cnt_type);\n\n\t\tTEST_EQUALS(ref.max_num_ref_frames,\n\t\t\t    sps.max_num_ref_frames);\n\n\t\tTEST_EQUALS(ref.pic_width_in_mbs,\n\t\t\t    sps.pic_width_in_mbs);\n\n\t\tTEST_EQUALS(ref.pic_height_in_map_units,\n\t\t\t    sps.pic_height_in_map_units);\n\n\t\tTEST_EQUALS(ref.frame_crop_left_offset,\n\t\t\t    sps.frame_crop_left_offset);\n\t\tTEST_EQUALS(ref.frame_crop_right_offset,\n\t\t\t    sps.frame_crop_right_offset);\n\t\tTEST_EQUALS(ref.frame_crop_top_offset,\n\t\t\t    sps.frame_crop_top_offset);\n\t\tTEST_EQUALS(ref.frame_crop_bottom_offset,\n\t\t\t    sps.frame_crop_bottom_offset);\n\n\t\t/* verify correct resolution */\n\t\tTEST_EQUALS(test->size.w, size.w);\n\t\tTEST_EQUALS(test->size.h, size.h);\n\t}\n\n\ttest_short = &testv[0];\n\tmax_len = str_len(test_short->buf) / 2;\n\n\terr = str_hex(buf, max_len, test_short->buf);\n\tif (err)\n\t\treturn err;\n\n\tfor (i = 1; i <= max_len; i++) {\n\n\t\tsize_t len = i;\n\n\t\te = h264_sps_decode(&sps, buf, len);\n\n\t\tswitch (e) {\n\n\t\tcase EBADMSG:\n\t\tcase EINVAL:\n\t\tcase 0:\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tDEBUG_WARNING(\"unexpected error code %d (%m)\\n\",\n\t\t\t\t      e, e);\n\t\t\treturn EINVAL;\n\t\t}\n\t}\n\n out:\n\treturn err;\n}\n\n\nstruct state {\n\n\t/* depacketizer */\n\tstruct mbuf *mb;\n\tsize_t frag_start;\n\tbool frag;\n\tbool long_startcode;\n\n\t/* test */\n\tuint8_t buf[256];\n\tsize_t len;\n\tunsigned count;\n\tbool complete;\n};\n\n\nstatic void fragment_rewind(struct state *vds)\n{\n\tvds->mb->pos = vds->frag_start;\n\tvds->mb->end = vds->frag_start;\n}\n\n\nstatic int depack_handle_h264(struct state *st, bool marker,\n\t\t\t      struct mbuf *src)\n{\n\tstatic const uint8_t nal_seq3[3] = {0, 0, 1};\n\tstatic const uint8_t nal_seq4[4] = {0, 0, 0, 1};\n\tstruct h264_nal_header h264_hdr;\n\tsize_t nal_seq_len = st->long_startcode ?\n\t\tsizeof(nal_seq4) : sizeof(nal_seq3);\n\tint err;\n\n\terr = h264_nal_header_decode(&h264_hdr, src);\n\tif (err)\n\t\treturn err;\n\n#if 0\n\tre_printf(\"decode: %s %s type=%2d %s  \\n\",\n\t\t  marker ? \"[M]\" : \"   \",\n\t\t  h264_is_keyframe(h264_hdr.type) ? \"<KEY>\" : \"     \",\n\t\t  h264_hdr.type,\n\t\t  h264_nal_unit_name(h264_hdr.type));\n#endif\n\n\tif (h264_hdr.f) {\n\t\tDEBUG_WARNING(\"H264 forbidden bit set!\\n\");\n\t\treturn EBADMSG;\n\t}\n\n\t/* handle NAL types */\n\tif (1 <= h264_hdr.type && h264_hdr.type <= 23) {\n\n\t\t--src->pos;\n\n\t\t/* prepend H.264 NAL start sequence */\n\t\terr  = mbuf_write_mem(st->mb,\n\t\t\t\t      st->long_startcode ? nal_seq4 : nal_seq3,\n\t\t\t\t      nal_seq_len);\n\n\t\terr |= mbuf_write_mem(st->mb, mbuf_buf(src),\n\t\t\t\t      mbuf_get_left(src));\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse if (H264_NALU_FU_A == h264_hdr.type) {\n\n\t\tstruct h264_fu fu;\n\n\t\terr = h264_fu_hdr_decode(&fu, src);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\th264_hdr.type = fu.type;\n\n\t\tif (fu.s) {\n\t\t\tif (st->frag) {\n\t\t\t\tDEBUG_WARNING(\"start: lost fragments;\"\n\t\t\t\t\t      \" ignoring previous NAL\\n\");\n\t\t\t\tfragment_rewind(st);\n\t\t\t}\n\n\t\t\tst->frag_start = st->mb->pos;\n\t\t\tst->frag = true;\n\n\t\t\t/* prepend H.264 NAL start sequence */\n\t\t\tmbuf_write_mem(st->mb,\n\t\t\t       st->long_startcode ? nal_seq4 : nal_seq3,\n\t\t\t       nal_seq_len);\n\n\t\t\t/* encode NAL header back to buffer */\n\t\t\terr = h264_nal_header_encode(st->mb, &h264_hdr);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t\telse {\n\t\t\tif (!st->frag) {\n\t\t\t\tre_printf(\"ignoring fragment\"\n\t\t\t\t      \" (nal=%u)\\n\", fu.type);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\n\t\terr = mbuf_write_mem(st->mb, mbuf_buf(src),\n\t\t\t\t     mbuf_get_left(src));\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (fu.e)\n\t\t\tst->frag = false;\n\t}\n\telse if (H264_NALU_STAP_A == h264_hdr.type) {\n\n\t\tif (st->long_startcode)\n\t\t\terr = h264_stap_decode_annexb_long(st->mb, src);\n\t\telse\n\t\t\terr = h264_stap_decode_annexb(st->mb, src);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse {\n\t\tDEBUG_WARNING(\"decode: unknown NAL type %u\\n\",\n\t\t\t      h264_hdr.type);\n\t\treturn EBADMSG;\n\t}\n\n\tif (!marker)\n\t\treturn 0;\n\n\t/* verify complete packet */\n\tst->complete = true;\n\tTEST_MEMCMP(st->buf, st->len, st->mb->buf, st->mb->end);\n\n out:\n\tmbuf_rewind(st->mb);\n\tst->frag = false;\n\n\treturn err;\n}\n\n\nstatic int packet_handler(bool marker, uint64_t rtp_ts,\n\t\t\t  const uint8_t *hdr, size_t hdr_len,\n\t\t\t  const uint8_t *pld, size_t pld_len,\n\t\t\t  void *arg)\n{\n\tstruct state *state = arg;\n\tstruct mbuf *mb_pkt = mbuf_alloc(hdr_len + pld_len);\n\tint err = 0;\n\n\tif (!mb_pkt)\n\t\treturn ENOMEM;\n\n\tASSERT_EQ(DUMMY_TS, rtp_ts);\n\n\t++state->count;\n\n\tif (hdr && hdr_len)\n\t\terr |= mbuf_write_mem(mb_pkt, hdr, hdr_len);\n\n\tif (pld && pld_len)\n\t\terr |= mbuf_write_mem(mb_pkt, pld, pld_len);\n\n\tif (err)\n\t\tgoto out;\n\n\tmb_pkt->pos = 0;\n\n\terr = depack_handle_h264(state, marker, mb_pkt);\n\n out:\n\tmem_deref(mb_pkt);\n\treturn err;\n}\n\n\nstatic int test_h264_packet_base(const char *bs, bool long_startcode,\n\t\t\t\t size_t max_pktsize)\n{\n\tstruct state state;\n\tint err;\n\n\tmemset(&state, 0, sizeof(state));\n\n\tstate.long_startcode = long_startcode;\n\tstate.len = strlen(bs)/2;\n\n\terr = str_hex(state.buf, state.len, bs);\n\tif (err)\n\t\treturn err;\n\n\tstate.mb = mbuf_alloc(1024);\n\tif (!state.mb)\n\t\treturn ENOMEM;\n\n\terr = h264_packetize(DUMMY_TS, state.buf, state.len, max_pktsize,\n\t\t\t     packet_handler, &state);\n\tif (err)\n\t\tgoto out;\n\n\tASSERT_TRUE(state.count >= 1);\n\tASSERT_TRUE(state.complete);\n\n out:\n\tmem_deref(state.mb);\n\n\treturn err;\n}\n\n\n/* bitstream in Annex-B format (with startcode 00 00 01) */\nstatic const char *bitstream =\n\t\"0000016701020304\"\n\t\"0000016801020304\"\n\t\"000001650010e2238712983719283719823798\";\n\n\n/* bitstream in Annex-B format (with startcode 00 00 00 01) */\nstatic const char *bitstream_long =\n\t\"000000016701020304\"\n\t\"000000016801020304\"\n\t\"00000001650010e2238712983719283719823798\";\n\n\nint test_h264_packet(void)\n{\n\tconst size_t MAX_PKTSIZE = 8;\n\tint err;\n\n\terr = test_h264_packet_base(bitstream, false, MAX_PKTSIZE);\n\tTEST_ERR(err);\n\n\terr = test_h264_packet_base(bitstream_long, true, MAX_PKTSIZE);\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/h265.c",
    "content": "/**\n * @file h265.c H.265 Testcode\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re.h>\n#include <re_h265.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"h265test\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_h265(void)\n{\n\tuint8_t buf[H265_HDR_SIZE];\n\tstruct h265_nal hdr;\n\tenum {TID = 1};\n\tint err;\n\n\th265_nal_encode(buf, H265_NAL_VPS_NUT, TID);\n\n\terr = h265_nal_decode(&hdr, buf);\n\tif (err)\n\t\tgoto out;\n\n\tASSERT_EQ(32, hdr.nal_unit_type);\n\tASSERT_EQ(TID, hdr.nuh_temporal_id_plus1);\n\n\tASSERT_TRUE(!h265_is_keyframe(H265_NAL_VPS_NUT));\n\tASSERT_TRUE( h265_is_keyframe(H265_NAL_IDR_W_RADL));\n\n\tfor (unsigned i=0; i<=49; i++) {\n\n\t\tchar debug[256] = \"\";\n\t\tstruct h265_nal nal = {\n\t\t\t.nal_unit_type = i\n\t\t};\n\n\t\tre_snprintf(debug, sizeof(debug), \"%H\", h265_nal_print, &nal);\n\t\tASSERT_TRUE(str_isset(debug));\n\t}\n\n out:\n\treturn err;\n}\n\n\nenum {\n\tH265_FU_HDR_SIZE = 1\n};\n\nstruct h265_fu {\n\tunsigned s:1;\n\tunsigned e:1;\n\tunsigned type:6;\n};\n\n\nstatic inline int h265_fu_decode(struct h265_fu *fu, struct mbuf *mb)\n{\n\tuint8_t v;\n\n\tif (mbuf_get_left(mb) < 1)\n\t\treturn EBADMSG;\n\n\tv = mbuf_read_u8(mb);\n\n\tfu->s    = v>>7 & 0x1;\n\tfu->e    = v>>6 & 0x1;\n\tfu->type = v>>0 & 0x3f;\n\n\treturn 0;\n}\n\n\nstruct state {\n\n\t/* depacketizer */\n\tstruct mbuf *mb;\n\n\t/* test */\n\tuint8_t buf[256];\n\tsize_t len;\n\tunsigned count;\n\tbool complete;\n};\n\n\nstatic int depack_handle_h265(struct state *st, bool marker,\n\t\t\t      struct mbuf *mb)\n{\n\tstatic const uint8_t nal_seq[3] = {0, 0, 1};\n\tstruct h265_nal hdr;\n\tint err;\n\n\tif (mbuf_get_left(mb) < H265_HDR_SIZE)\n\t\treturn EBADMSG;\n\n\terr = h265_nal_decode(&hdr, mbuf_buf(mb));\n\tif (err)\n\t\treturn err;\n\n\tmbuf_advance(mb, H265_HDR_SIZE);\n\n#if 0\n\tre_printf(\"h265: decode:  [%s]  %s  type=%2d  %s\\n\",\n\t      marker ? \"M\" : \" \",\n\t      h265_is_keyframe(hdr.nal_unit_type) ? \"<KEY>\" : \"     \",\n\t      hdr.nal_unit_type,\n\t      h265_nalunit_name(hdr.nal_unit_type));\n#endif\n\n\t/* handle NAL types */\n\tif (hdr.nal_unit_type <= 40) {\n\n\t\tmb->pos -= H265_HDR_SIZE;\n\n\t\terr  = mbuf_write_mem(st->mb, nal_seq, 3);\n\t\terr |= mbuf_write_mem(st->mb, mbuf_buf(mb),mbuf_get_left(mb));\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse if (H265_NAL_FU == hdr.nal_unit_type) {\n\n\t\tstruct h265_fu fu;\n\n\t\terr = h265_fu_decode(&fu, mb);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tif (fu.s) {\n\t\t\thdr.nal_unit_type = fu.type;\n\n\t\t\terr  = mbuf_write_mem(st->mb, nal_seq, 3);\n\t\t\terr |= h265_nal_encode_mbuf(st->mb, &hdr);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\n\t\terr = mbuf_write_mem(st->mb, mbuf_buf(mb), mbuf_get_left(mb));\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse if (hdr.nal_unit_type == H265_NAL_AP) {\n\n\t\twhile (mbuf_get_left(mb) >= 2) {\n\n\t\t\tconst uint16_t len = ntohs(mbuf_read_u16(mb));\n\n\t\t\tif (mbuf_get_left(mb) < len)\n\t\t\t\treturn EBADMSG;\n\n\t\t\terr  = mbuf_write_mem(st->mb, nal_seq, 3);\n\t\t\terr |= mbuf_write_mem(st->mb, mbuf_buf(mb), len);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n                        mb->pos += len;\n\t\t}\n\t}\n\telse {\n\t\tDEBUG_WARNING(\"unknown H265 NAL type %u (%s) [%zu bytes]\\n\",\n\t\t\thdr.nal_unit_type,\n\t\t\th265_nalunit_name(hdr.nal_unit_type),\n\t\t\tmbuf_get_left(mb));\n\t\treturn EPROTO;\n\t}\n\n\tif (!marker)\n\t\treturn 0;\n\n\t/* verify complete packet */\n\tst->complete = true;\n\tTEST_MEMCMP(st->buf, st->len, st->mb->buf, st->mb->end);\n\n out:\n\tmbuf_rewind(st->mb);\n\n\treturn err;\n}\n\n\nenum { DUMMY_TS = 36000 };\n\n\nstatic int packet_handler(bool marker, uint64_t rtp_ts,\n\t\t\t  const uint8_t *hdr, size_t hdr_len,\n\t\t\t  const uint8_t *pld, size_t pld_len,\n\t\t\t  void *arg)\n{\n\tstruct state *state = arg;\n\tstruct mbuf *mb_pkt = mbuf_alloc(hdr_len + pld_len);\n\tint err;\n\n\tif (!mb_pkt)\n\t\treturn ENOMEM;\n\n\tASSERT_EQ(DUMMY_TS, rtp_ts);\n\n\t++state->count;\n\n\tif (hdr && hdr_len) {\n\t\terr = mbuf_write_mem(mb_pkt, hdr, hdr_len);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = mbuf_write_mem(mb_pkt, pld, pld_len);\n\tif (err)\n\t\tgoto out;\n\n\tmb_pkt->pos = 0;\n\n\terr = depack_handle_h265(state, marker, mb_pkt);\n\n out:\n\tmem_deref(mb_pkt);\n\treturn err;\n}\n\n\n/* bitstream in Annex-B format (with startcode 00 00 01) */\n/* H265_NAL_VPS_NUT */\nstatic const char *bitstream =\n\t\"00000140010c01ffff01600000030090000003000003003cba024000000001\";\n\n\nstatic int test_h265_packet_param(size_t pktsize)\n{\n\tstruct state state;\n\tint err;\n\n\tmemset(&state, 0, sizeof(state));\n\n\tstate.len = strlen(bitstream)/2;\n\n\terr = str_hex(state.buf, state.len, bitstream);\n\tif (err)\n\t\treturn err;\n\n\tstate.mb = mbuf_alloc(1024);\n\tif (!state.mb)\n\t\treturn ENOMEM;\n\n\terr = h265_packetize(DUMMY_TS, state.buf, state.len, pktsize,\n\t\t\t     packet_handler, &state);\n\tif (err)\n\t\tgoto out;\n\n\tASSERT_TRUE(state.count >= 1);\n\tASSERT_TRUE(state.complete);\n\n out:\n\tmem_deref(state.mb);\n\n\treturn err;\n}\n\n\nint test_h265_packet(void)\n{\n\tint err;\n\n\terr = test_h265_packet_param(8);\n\tTEST_ERR(err);\n\n\terr = test_h265_packet_param(1500);\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/hash.c",
    "content": "/**\n * @file hash.c Hash Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"testhash\"\n#define DEBUG_LEVEL 4\n#include <re_dbg.h>\n\n\nstruct my_elem {\n\tstruct le he;\n\tconst struct pl *name;   /* hash key */\n};\n\nconst struct pl martin = PL(\"Martin\"),\n\talfred = PL(\"Alfred\"),\n\tatle = PL(\"Atle\");\n\n\nstatic bool hash_cmp_handler(struct le *le, void *arg)\n{\n\tconst struct my_elem *me = le->data;\n\tconst struct pl *name = arg;\n\n\treturn 0==pl_cmp(me->name, name);\n}\n\n\nstatic int test_hash_basic(void)\n{\n#define BUCKET_SIZE 4\n\tstruct my_elem elems[3];\n\tstruct hash *h;\n\tstruct my_elem *elem;\n\tint err;\n\n\t/* Clear hash elements */\n\tmemset(elems, 0, sizeof(elems));\n\n\telems[0].name = &martin;\n\telems[1].name = &alfred;\n\telems[2].name = &atle;\n\n\terr = hash_alloc(&h, BUCKET_SIZE);\n\tif (err)\n\t\treturn err;\n\n\tTEST_EQUALS(BUCKET_SIZE, hash_bsize(h));\n\n\t/* API test */\n\tif (hash_lookup(NULL, hash_joaat_pl(elems[0].name),\n\t\t\thash_cmp_handler, (void *)elems[0].name)) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\tif (hash_lookup(h, hash_joaat_pl(elems[0].name),\n\t\t\tNULL, (void *)elems[0].name)) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\t/* Hashtable is empty */\n\thash_unlink(&elems[0].he);\n\thash_unlink(&elems[1].he);\n\n\t/* Hashtable with 1 element */\n\thash_append(h, hash_joaat_pl(elems[0].name), &elems[0].he, &elems[0]);\n\n\telem = list_ledata(hash_lookup(h, hash_joaat_pl(elems[0].name),\n\t\t\t\t       hash_cmp_handler,\n\t\t\t\t       (void *)elems[0].name));\n\tif (elem != &elems[0]) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\telem = list_ledata(hash_lookup(h, hash_joaat_pl(elems[1].name),\n\t\t\t\t       hash_cmp_handler,\n\t\t\t\t       (void *)elems[1].name));\n\tif (elem) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\t/* Hashtable with 2 elements */\n\thash_append(h, hash_joaat_pl(elems[1].name), &elems[1].he, &elems[1]);\n\n\telem = list_ledata(hash_lookup(h, hash_joaat_pl(elems[0].name),\n\t\t\t\t       hash_cmp_handler,\n\t\t\t\t       (void *)elems[0].name));\n\tif (elem != &elems[0]) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\telem = list_ledata(hash_lookup(h, hash_joaat_pl(elems[1].name),\n\t\t\t\t       hash_cmp_handler,\n\t\t\t\t       (void *)elems[1].name));\n\tif (elem != &elems[1]) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\thash_unlink(&elems[0].he);\n\thash_unlink(&elems[1].he);\n\n\terr = 0;\n out:\n\th = mem_deref(h);\n\treturn err;\n}\n\n\nstatic int test_hash_robustapi(void)\n{\n\tstruct hash *h = NULL;\n\tstruct le he;\n\tint err = 0;\n\n\tTEST_EQUALS(EINVAL, hash_alloc(NULL, 4));\n\tTEST_EQUALS(EINVAL, hash_alloc(&h, 0));\n\n\thash_append(h, 0, NULL, NULL);\n\thash_append(NULL, 0, &he, NULL);\n\n\thash_unlink(NULL);\n\n out:\n\treturn err;\n}\n\n\n#define MAGIC1 0x7fbb0001\n#define MAGIC2 0x7fbb0002\nstruct object {\n\tuint32_t magic1;\n\tstruct le he;\n\tuint32_t magic2;\n\tchar buffer[32];\n\tuint32_t key;\n};\n\n\nstatic void obj_destructor(void *arg)\n{\n\tstruct object *obj = arg;\n\n\tlist_unlink(&obj->he);\n}\n\n\nstatic bool cmp_handler(struct le *le, void *arg)\n{\n\tstruct object *obj = le->data;\n\n\treturn obj->key == *(uint32_t *)arg;\n}\n\n\nstatic int test_hash_large(void)\n{\n#define SZ 8\n#define NUM_ENTRIES SZ*SZ\n\tstruct hash *ht = NULL;\n\tunsigned i;\n\tint err = 0;\n\n\terr = hash_alloc(&ht, SZ);\n\tif (err)\n\t\tgoto out;\n\n\t/* add a lot of objects to hash-table */\n\tfor (i=0; i<NUM_ENTRIES; i++) {\n\n\t\tstruct object *obj;\n\t\tuint32_t key = i;\n\n\t\tobj = mem_zalloc(sizeof(*obj), obj_destructor);\n\t\tif (!obj) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\t\tobj->magic1 = MAGIC1;\n\t\tobj->magic2 = MAGIC2;\n\t\tobj->key = key;\n\n\t\t/* ownership of 'obj' transferred to the hash-table */\n\t\thash_append(ht, key, &obj->he, obj);\n\t}\n\n\t/* verify that all objects can be found */\n\tfor (i=0; i<NUM_ENTRIES; i++) {\n\n\t\tstruct object *obj;\n\n\t\tobj = list_ledata(hash_lookup(ht, i, cmp_handler, &i));\n\t\tTEST_ASSERT(obj != NULL);\n\n\t\tTEST_EQUALS(MAGIC1, obj->magic1);\n\t\tTEST_EQUALS(MAGIC2, obj->magic2);\n\t\tTEST_EQUALS(i, obj->key);\n\t}\n\n out:\n\thash_flush(ht);  /* destroys all the objects */\n\tmem_deref(ht);\n\n\treturn err;\n}\n\n\nint test_hash(void)\n{\n\tint err;\n\n\terr = test_hash_basic();\n\tif (err)\n\t\treturn err;\n\n\terr = test_hash_robustapi();\n\tif (err)\n\t\treturn err;\n\n\terr = test_hash_large();\n\tif (err)\n\t\treturn err;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/hmac.c",
    "content": "/**\n * @file hmac.c HMAC Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include <re_sha.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"testhmac\"\n#define DEBUG_LEVEL 4\n#include <re_dbg.h>\n\n\n#ifndef SHA256_DIGEST_LENGTH\n#define SHA256_DIGEST_LENGTH    32\n#endif\n\n\nint test_hmac_sha1(void)\n{\n\t/* RFC 2202 */\n\tconst struct {\n\t\tconst void *key;\n\t\tuint32_t key_len;\n\t\tconst void *data;\n\t\tuint32_t data_len;\n\t\tchar digest[40 + 1];\n\t} testv[] = {\n\t\t{\"\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\"\n\t\t \"\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\\x0b\", 20,\n\t\t \"Hi There\", 8,\n\t\t \"b617318655057264e28bc0b6fb378c8ef146be00\"},\n\n\t\t{\"Jefe\", 4,\n\t\t \"what do ya want for nothing?\", 28,\n\t\t \"effcdf6ae5eb2fa2d27416d5f184df9c259a7c79\"},\n\n\t\t{\"\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa\"\n\t\t \"\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa\\xaa\", 20,\n\t\t \"\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\"\n\t\t \"\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\"\n\t\t \"\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\"\n\t\t \"\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\"\n\t\t \"\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\\xdd\", 50,\n\t\t \"125d7342b9ac11cd91a39af48aa17b4f63f175d3\"},\n\n\t\t{\"01234567890123456789\", 20,\n \t\t \"dalskdmlkasndoiqwjeoi3hjoijqweolk6y52fsdfsfgh66h\"\n\t\t \"91928ha8shdoalijelwjeoriwjeorijwe98fj98j98j384jo\"\n\t\t \"dalskdmlkasndoiqwjeoi3hjoijqweolk6y52fsdfsfgh66h\"\n\t\t \"91928ha8shdoalijelwjeoriwjeorijwe98fj98j98j38s4f\"\n\t\t \"dalskdmlkasndoiqwjeoi3hjoijqweolk6y52fsdfsfghsda\"\n\t\t \"91928ha8shdoalijelwjeoriwjeorijwe98fj98j98j384jo\"\n\t\t \"dalskdmlkasndoiqwjeoi3hjoijqweolk6y52fsdfsfgh66h\"\n\t\t \"91928ha8shdoalijelwjeoriwjeorijwe98fj98jqwe98j38\", 384,\n\t\t \"4b00628735c763b3c0dc398deb4370e99f822490\"}\n\t};\n\tstruct hmac *hmac = NULL;\n\tuint32_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tchar buf[43];\n\t\tuint8_t md[SHA_DIGEST_LENGTH];\n\t\tuint32_t md_len = SHA_DIGEST_LENGTH;\n\t\tuint8_t md2[SHA_DIGEST_LENGTH], md_ref[SHA_DIGEST_LENGTH];\n\n\t\thmac_sha1(testv[i].key,      /* secret key */\n\t\t\t  testv[i].key_len,  /* length of the key in bytes */\n\t\t\t  testv[i].data,     /* data */\n\t\t\t  testv[i].data_len, /* length of data in bytes */\n\t\t\t  md,          /* output buffer, at least \"t\" bytes */\n\t\t\t  md_len);\n\n\t\t(void)re_snprintf(buf, sizeof(buf), \"%02w\", md, sizeof(md));\n\t\tif (0 != strcmp(testv[i].digest, buf)) {\n\t\t\tDEBUG_WARNING(\"testcase %u: HMAC failed\"\n\t\t\t\t      \" (expected %s, got %s)\\n\",\n\t\t\t\t      i, testv[i].digest, buf);\n\t\t\treturn EINVAL;\n\t\t}\n\n\t\t/*\n\t\t * This part is testing the Stateful API\n\t\t */\n\t\terr = hmac_create(&hmac, HMAC_HASH_SHA1,\n\t\t\t\t  testv[i].key, testv[i].key_len);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = hmac_digest(hmac, md2, sizeof(md2),\n\t\t\t\t  testv[i].data, testv[i].data_len);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = str_hex(md_ref, sizeof(md_ref), testv[i].digest);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_MEMCMP(md_ref, sizeof(md_ref), md2, sizeof(md2));\n\n\t\thmac = mem_deref(hmac);\n\t}\n\n out:\n\tmem_deref(hmac);\n\n\treturn err;\n}\n\n\n/*\n * RFC 4231 -- Identifiers and Test Vectors for HMAC-SHA-256\n */\nint test_hmac_sha256(void)\n{\n#define MAX_KEY_LEN  132\n#define MAX_DATA_LEN 152\n\tconst struct test {\n\t\tconst char *key;\n\t\tconst char *data;\n\t\tconst char *digest;\n\t} testv[] = {\n\n\t\t/* Test Case 1 */\n\t\t{\n\t\t\t\"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b\"\n\t\t\t\"0b0b0b0b\"\n\t\t\t,\n\t\t\t\"4869205468657265\"\n\t\t\t,\n\t\t\t\"b0344c61d8db38535ca8afceaf0bf12b\"\n\t\t\t\"881dc200c9833da726e9376c2e32cff7\"\n\t\t},\n\n\t\t/* Test Case 2 */\n\t\t{\n\t\t\t\"4a656665\"\n\t\t\t,\n\t\t\t\"7768617420646f2079612077616e7420\"\n\t\t\t\"666f72206e6f7468696e673f\"\n\t\t\t,\n\t\t\t\"5bdcc146bf60754e6a042426089575c7\"\n\t\t\t\"5a003f089d2739839dec58b964ec3843\"\n\t\t},\n\n\t\t/* Test Case 3 */\n\t\t{\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaa\"\n\t\t\t,\n\t\t\t\"dddddddddddddddddddddddddddddddd\"\n\t\t\t\"dddddddddddddddddddddddddddddddd\"\n\t\t\t\"dddddddddddddddddddddddddddddddd\"\n\t\t\t\"dddd\"\n\t\t\t,\n\t\t\t\"773ea91e36800e46854db8ebd09181a7\"\n\t\t\t\"2959098b3ef8c122d9635514ced565fe\"\n\t\t},\n\n\t\t/* Test Case 4 */\n\t\t{\n\t\t\t\"0102030405060708090a0b0c0d0e0f10\"\n\t\t\t\"111213141516171819\"\n\t\t\t,\n\t\t\t\"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\"\n\t\t\t\"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\"\n\t\t\t\"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd\"\n\t\t\t\"cdcd\"\n\t\t\t,\n\t\t\t\"82558a389a443c0ea4cc819899f2083a\"\n\t\t\t\"85f0faa3e578f8077a2e3ff46729665b\"\n\t\t},\n\n\t\t/* Test Case 6 */\n\t\t{\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaa\"\n\t\t\t,\n\t\t\t\"54657374205573696e67204c61726765\"\n\t\t\t\"72205468616e20426c6f636b2d53697a\"\n\t\t\t\"65204b6579202d2048617368204b6579\"\n\t\t\t\"204669727374\"\n\t\t\t,\n\t\t\t\"60e431591ee0b67f0d8a26aacbf5b77f\"\n\t\t\t\"8e0bc6213728c5140546040f0ee37f54\"\n\t\t},\n\n\t\t/* Test Case 7 */\n\t\t{\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t\t\t\"aaaaaa\"\n\t\t\t,\n\t\t\t\"54686973206973206120746573742075\"\n\t\t\t\"73696e672061206c6172676572207468\"\n\t\t\t\"616e20626c6f636b2d73697a65206b65\"\n\t\t\t\"7920616e642061206c61726765722074\"\n\t\t\t\"68616e20626c6f636b2d73697a652064\"\n\t\t\t\"6174612e20546865206b6579206e6565\"\n\t\t\t\"647320746f2062652068617368656420\"\n\t\t\t\"6265666f7265206265696e6720757365\"\n\t\t\t\"642062792074686520484d414320616c\"\n\t\t\t\"676f726974686d2e\"\n\t\t\t,\n\t\t\t\"9b09ffa71b942fcb27635fbcd5b0e944\"\n\t\t\t\"bfdc63644f0713938a7f51535c3a35e2\"\n\t\t},\n\t};\n\tstruct hmac *hmac = NULL;\n\tsize_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tconst struct test *test = &testv[i];\n\t\tuint8_t key[MAX_KEY_LEN];\n\t\tsize_t key_len = str_len(test->key) / 2;\n\t\tuint8_t data[MAX_DATA_LEN];\n\t\tsize_t data_len = str_len(test->data) / 2;\n\t\tuint8_t digest[SHA256_DIGEST_LENGTH];\n\t\tuint8_t md[SHA256_DIGEST_LENGTH];\n\n\t\tTEST_ASSERT(key_len <= sizeof(key));\n\t\tTEST_ASSERT(data_len <= sizeof(data));\n\n\t\terr |= str_hex(key, key_len, test->key);\n\t\terr |= str_hex(data, data_len, test->data);\n\t\terr |= str_hex(digest, sizeof(digest), test->digest);\n\t\tTEST_ERR(err);\n\n\t\terr = hmac_create(&hmac, HMAC_HASH_SHA256, key, key_len);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = hmac_digest(hmac, md, sizeof(md), data, data_len);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_MEMCMP(digest, sizeof(digest), md, sizeof(md));\n\n\t\thmac = mem_deref(hmac);\n\n\t\t/* Test Stateless API */\n\n\t\tuint8_t md2[SHA256_DIGEST_LENGTH];\n\n\t\thmac_sha256(key, key_len,\n\t\t\t    data, data_len,\n\t\t\t    md2, sizeof(md2));\n\n\t\tTEST_MEMCMP(digest, sizeof(digest), md2, sizeof(md2));\n\t}\n\n out:\n\tmem_deref(hmac);\n\n\tif (err == ENOTSUP)\n\t\terr = ESKIPPED;\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/http.c",
    "content": "/**\n * @file http.c HTTP Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_http\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nenum large_body_test {\n\tREQ_BODY_CHUNK_SIZE = 26 * 42,\n\tREQ_BODY_SIZE = REQ_BODY_CHUNK_SIZE * 480 - 26,\n\tREQ_HTTP_REQUESTS = 3\n};\n\nenum {\n\tIP_127_0_0_1 = 0x7f000001,\n};\n\nstatic int test_http_response_no_reasonphrase(void)\n{\n\tstruct http_msg *msg = NULL;\n\tstruct mbuf *mb;\n\tint err;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = mbuf_write_str(mb, /*       _---- no space here! */\n\t\t\t     \"HTTP/1.1 429\\r\\n\"\n\t\t\t     \"Server: nginx\\r\\n\"\n\t\t\t     \"Content-Length: 0\\r\\n\"\n\t\t\t     \"\\r\\n\");\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\n\terr = http_msg_decode(&msg, mb, false);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_STRCMP(\"1.1\", 3, msg->ver.p, msg->ver.l);\n\tTEST_EQUALS(429, msg->scode);\n\tTEST_STRCMP(\"\", 0, msg->reason.p, msg->reason.l);\n\n out:\n\tmem_deref(msg);\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nint test_http(void)\n{\n\tstatic const char req[] =\n\t\t\"GET /path/file.html HTTP/1.1\\r\\n\"\n\t\t\"From: plopp@klukk.no\\r\\n\"\n\t\t\"User-Agent: libre HTTP client/1.0\\r\\n\"\n\t\t\"Allow: GET, HEAD, PUT\\r\\n\"\n\t\t\"\\r\\n\"\n\t\t\"\";\n\tstruct mbuf *mb;\n\tstruct http_msg *msg = NULL;\n\tconst struct http_hdr *hdr;\n\tint err;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = mbuf_write_str(mb, req);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\terr = http_msg_decode(&msg, mb, true);\n\tif (err)\n\t\tgoto out;\n\n\tif (0 != pl_strcmp(&msg->met, \"GET\"))              goto badmsg;\n\tif (0 != pl_strcmp(&msg->path, \"/path/file.html\")) goto badmsg;\n\tif (0 != pl_strcmp(&msg->ver, \"1.1\"))              goto badmsg;\n\tif (pl_isset(&msg->prm))                           goto badmsg;\n\n\thdr = http_msg_hdr(msg, HTTP_HDR_FROM);\n\tif (!hdr || 0 != pl_strcmp(&hdr->val, \"plopp@klukk.no\"))\n\t\tgoto badmsg;\n\thdr = http_msg_hdr(msg, HTTP_HDR_USER_AGENT);\n\tif (!hdr || 0 != pl_strcmp(&hdr->val, \"libre HTTP client/1.0\"))\n\t\tgoto badmsg;\n\thdr = http_msg_hdr(msg, HTTP_HDR_CONTENT_TYPE);\n\tif (hdr)\n\t\tgoto badmsg;\n\tif (msg->clen != 0)\n\t\tgoto badmsg;\n\n\tif (!http_msg_hdr_has_value(msg, HTTP_HDR_ALLOW, \"GET\")  ||\n\t    !http_msg_hdr_has_value(msg, HTTP_HDR_ALLOW, \"HEAD\") ||\n\t    !http_msg_hdr_has_value(msg, HTTP_HDR_ALLOW, \"PUT\"))\n\t\tgoto badmsg;\n\tif (3 != http_msg_hdr_count(msg, HTTP_HDR_ALLOW))\n\t\tgoto badmsg;\n\n\terr = test_http_response_no_reasonphrase();\n\tif (err)\n\t\tgoto out;\n\n\tgoto out;\n\n badmsg:\n\t(void)re_fprintf(stderr, \"%H\\n\", http_msg_print, msg);\n\terr = EBADMSG;\n\n out:\n\tmem_deref(msg);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstruct test {\n\tstruct mbuf *mb_body;\n\tsize_t clen;\n\tuint32_t n_request;\n\tuint32_t n_response;\n\tsize_t i_req_body;\n\tbool secure;\n\tbool cert_auth;\n\tint err;\n};\n\n\nstatic void abort_test(struct test *t, int err)\n{\n\tt->err = err;\n\tre_cancel();\n}\n\n\nstatic enum re_https_verify_msg https_verify_msg_handler(\n\tstruct http_conn *conn, const struct http_msg *msg, void *arg)\n{\n\t(void) conn;\n\tstruct test *t = arg;\n\tint err = 0;\n\tenum re_https_verify_msg res = HTTPS_MSG_OK;\n\n\tif (!t->cert_auth)\n\t\tgoto out;\n\n\tTEST_STRCMP(\"/auth/index.html\", 11+5, msg->path.p, msg->path.l);\n\n\t/* cert authorisation required, request client certificate */\n\tres = HTTPS_MSG_REQUEST_CERT;\nout:\n\tif (err) {\n\t\tres = HTTPS_MSG_IGNORE;\n\t\tabort_test(t, err);\n\t}\n\n\treturn res;\n}\n\n\nstatic void http_req_handler(struct http_conn *conn,\n\t\t\t     const struct http_msg *msg, void *arg)\n{\n\tstruct test *t = arg;\n\tstruct mbuf *mb_body = mbuf_alloc(1024);\n\tint err = 0;\n\n\tif (!mb_body) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\t++t->n_request;\n\n\tif (t->secure) {\n\t\tTEST_ASSERT(http_conn_tls(conn) != NULL);\n\t}\n\telse {\n\t\tTEST_ASSERT(http_conn_tls(conn) == NULL);\n\t}\n\n\t/* verify HTTP request */\n\tTEST_STRCMP(\"1.1\", 3, msg->ver.p, msg->ver.l);\n\tTEST_STRCMP(\"GET\", 3, msg->met.p, msg->met.l);\n\tif (t->cert_auth) {\n\t\tTEST_STRCMP(\"/auth/index.html\", 11+5, msg->path.p,\n\t\t\tmsg->path.l);\n\t}\n\telse {\n\t\tTEST_STRCMP(\"/index.html\", 11, msg->path.p, msg->path.l);\n\t}\n\n\tTEST_STRCMP(\"\", 0, msg->prm.p, msg->prm.l);\n\tTEST_EQUALS(t->clen, msg->clen);\n\tTEST_STRCMP(\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\", 52,\n\t\tmbuf_buf(msg->mb), mbuf_get_left(msg->mb));\n\n\t/* Create a chunked response body */\n\terr = mbuf_write_str(mb_body,\n\t\t\t     \"2\\r\\n\"\n\t\t\t     \"ab\\r\\n\"\n\n\t\t\t     \"4\\r\\n\"\n\t\t\t     \"cdef\\r\\n\"\n\n\t\t\t     \"8\\r\\n\"\n\t\t\t     \"ghijklmn\\r\\n\"\n\n\t\t\t     \"c\\r\\n\"\n\t\t\t     \"opqrstuvwxyz\\r\\n\"\n\n\t\t\t     \"0\\r\\n\"\n\t\t\t     \"\\r\\n\"\n\t\t\t     );\n\tif (err)\n\t\tgoto out;\n\n\tt->clen = mb_body->end;\n\terr = http_reply(conn, 200, \"OK\",\n\t\t\t\"Transfer-Encoding: chunked\\r\\n\"\n\t\t\t\"Content-Type: text/plain\\r\\n\"\n\t\t\t\"Content-Length: %zu\\r\\n\"\n\t\t\t\"\\r\\n\"\n\t\t\t\"%b\",\n\t\t\tmb_body->end,\n\t\t\tmb_body->buf, mb_body->end\n\t\t\t);\n\n out:\n\tmem_deref(mb_body);\n\tif (err)\n\t\tabort_test(t, err);\n}\n\n\nstatic void http_put_req_handler(struct http_conn *conn,\n\t\t\t     const struct http_msg *msg, void *arg)\n{\n\tstruct test *t = arg;\n\tstruct mbuf *mb_body = mbuf_alloc(1024);\n\tint err = 0;\n\tsize_t l = 0;\n\tsize_t cmp_len;\n\n\tif (!mb_body) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\t++t->n_request;\n\n\tif (t->secure) {\n\t\tTEST_ASSERT(http_conn_tls(conn) != NULL);\n\t}\n\telse {\n\t\tTEST_ASSERT(http_conn_tls(conn) == NULL);\n\t}\n\n\t/* verify HTTP request */\n\tTEST_STRCMP(\"1.1\", 3, msg->ver.p, msg->ver.l);\n\tTEST_STRCMP(\"PUT\", 3, msg->met.p, msg->met.l);\n\tTEST_STRCMP(\"/index.html\", 11, msg->path.p, msg->path.l);\n\tTEST_STRCMP(\"\", 0, msg->prm.p, msg->prm.l);\n\tTEST_EQUALS(t->clen, msg->clen);\n\n\tl = mbuf_get_left(msg->mb);\n\n\twhile (l > 0) {\n\t\tcmp_len = min(l, 26);\n\t\tTEST_STRCMP(\"abcdefghijklmnopqrstuvwxyz\", cmp_len,\n\t\t\tmbuf_buf(msg->mb), cmp_len);\n\t\tmbuf_advance(msg->mb, cmp_len);\n\t\tl -= cmp_len;\n\t}\n\n\t/* Create a chunked response body */\n\terr = mbuf_write_str(mb_body,\n\t\t\t     \"2\\r\\n\"\n\t\t\t     \"ab\\r\\n\"\n\n\t\t\t     \"4\\r\\n\"\n\t\t\t     \"cdef\\r\\n\"\n\n\t\t\t     \"8\\r\\n\"\n\t\t\t     \"ghijklmn\\r\\n\"\n\n\t\t\t     \"c\\r\\n\"\n\t\t\t     \"opqrstuvwxyz\\r\\n\"\n\n\t\t\t     \"0\\r\\n\"\n\t\t\t     \"\\r\\n\"\n\t\t\t     );\n\tif (err)\n\t\tgoto out;\n\n\tt->clen = mb_body->end;\n\n\terr = http_reply(conn, 200, \"OK\",\n\t\t\t \"Transfer-Encoding: chunked\\r\\n\"\n\t\t\t \"Content-Type: text/plain\\r\\n\"\n\t\t\t \"Content-Length: %zu\\r\\n\"\n\t\t\t \"\\r\\n\"\n\t\t\t \"%b\",\n\t\t\t mb_body->end,\n\t\t\t mb_body->buf, mb_body->end\n\t\t\t );\n\n out:\n\tmem_deref(mb_body);\n\tif (err)\n\t\tabort_test(t, err);\n}\n\n\nstatic void http_resp_handler(int err, const struct http_msg *msg, void *arg)\n{\n\tstruct test *t = arg;\n\tbool chunked;\n\n\tif (err) {\n\t\t/* translate error code */\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n#if 0\n\tre_printf(\"%H\\n\", http_msg_print, msg);\n\tre_printf(\"BODY: %b\\n\", msg->mb->buf, msg->mb->end);\n#endif\n\n\t++t->n_response;\n\n\t/* verify HTTP response */\n\tTEST_STRCMP(\"1.1\", 3, msg->ver.p, msg->ver.l);\n\tTEST_ASSERT(!msg->met.p);\n\tTEST_ASSERT(!msg->path.p);\n\tTEST_ASSERT(!msg->prm.p);\n\tTEST_EQUALS(200, msg->scode);\n\tTEST_STRCMP(\"OK\", 2, msg->reason.p, msg->reason.l);\n\tTEST_EQUALS(t->clen, msg->clen);\n\n\tchunked = http_msg_hdr_has_value(msg, HTTP_HDR_TRANSFER_ENCODING,\n\t\t\t\t\t \"chunked\");\n\tTEST_ASSERT(chunked);\n\n\tTEST_STRCMP(\"text\", 4, msg->ctyp.type.p, msg->ctyp.type.l);\n\tTEST_STRCMP(\"plain\", 5, msg->ctyp.subtype.p, msg->ctyp.subtype.l);\n\n\tre_cancel();\n\n out:\n\tif (err)\n\t\tabort_test(t, err);\n}\n\n\nstatic int http_data_handler(const uint8_t *buf, size_t size,\n\t\t\t     const struct http_msg *msg, void *arg)\n{\n\tstruct test *t = arg;\n\t(void)msg;\n\n\tif (!t->mb_body)\n\t\tt->mb_body = mbuf_alloc(256);\n\tif (!t->mb_body)\n\t\treturn 0;\n\n\treturn mbuf_write_mem(t->mb_body, buf, size);\n}\n\n\nstatic size_t http_req_body_handler(struct mbuf *mb, void *arg)\n{\n\tstruct test *t = arg;\n\n\tif (t->i_req_body >= t->clen)\n\t\treturn 0;\n\n\tif (mbuf_write_mem(mb,\n\t\t\t(const uint8_t*) \"abcdefghijklmnopqrstuvwxyz\",\n\t\t\tstrlen(\"abcdefghijklmnopqrstuvwxyz\"))) {\n\t\tmbuf_reset(mb);\n\t\treturn 0;\n\t}\n\n\tt->i_req_body += strlen(\"abcdefghijklmnopqrstuvwxyz\");\n\treturn strlen(\"abcdefghijklmnopqrstuvwxyz\");\n}\n\n\nstatic size_t http_req_long_body_handler(struct mbuf *mb, void *arg)\n{\n\tstruct test *t = arg;\n\tsize_t l = 0;\n\tsize_t wlen;\n\n\t/* Create a chunked response body */\n\twhile ( l < REQ_BODY_CHUNK_SIZE && t->i_req_body < t->clen) {\n\t\twlen = min(min(26, REQ_BODY_CHUNK_SIZE - l),\n\t\t\tt->clen - t->i_req_body);\n\t\tif (wlen <= 0)\n\t\t\treturn l;\n\n\t\tif (mbuf_write_mem(mb,\n\t\t\t(const uint8_t*) \"abcdefghijklmnopqrstuvwxyz\",\n\t\t\twlen)) {\n\t\t\tmbuf_reset(mb);\n\t\t\treturn 0;\n\t\t}\n\t\tl += wlen;\n\t\tt->i_req_body += (uint32_t)wlen;\n\t}\n\n\treturn l;\n}\n\n\nstatic int test_http_loop_base(bool secure, const char *met, bool http_conn,\n\tbool dns_srv_query, bool dns_set_conf_test, bool post_handshake)\n{\n\tstruct http_sock *sock = NULL;\n\tstruct http_cli *cli = NULL;\n\tstruct http_req *req = NULL;\n\tstruct http_reqconn *conn = NULL;\n\tstruct dnsc *dnsc = NULL;\n\tstruct dns_server *dns_srv = NULL;\n\tstruct sa srv, dns;\n\tstruct test t;\n\tchar url[256];\n\tchar path[256];\n\tint err = 0;\n\tunsigned int i;\n\tbool put = false;\n\tstruct mbuf *mb_body = NULL;\n\tstruct pl pl;\n\tstruct dnsc_conf dconf;\n\tstruct http_conf hconf = {\n\t\t30000,\n\t\t30000,\n\t\t900000,\n\t};\n\n\n\tif (!strcmp(met, \"PUT\"))\n\t\tput = true;\n\n\tmemset(&t, 0, sizeof(t));\n\n\tt.secure = secure;\n\tt.cert_auth = secure && post_handshake;\n\n\tif (dns_srv_query) {\n\t\t/* Setup Mocking DNS Server */\n\t\terr = dns_server_alloc(&dns_srv, \"127.0.0.1\");\n\t\tTEST_ERR(err);\n\n\t\terr = dns_server_add_a(dns_srv, \"test1.example.net\",\n\t\t\tIP_127_0_0_1, 1);\n\t\tTEST_ERR(err);\n\t}\n\n\terr |= sa_set_str(&srv, \"127.0.0.1\", 0);\n\terr |= sa_set_str(&dns, \"127.0.0.1\", 53);    /* note: unused */\n\tTEST_ERR(err);\n\n\tif (secure) {\n\t\tif (t.cert_auth)\n\t\t\tre_snprintf(path, sizeof(path),\n\t\t\t\t\"%s/sni/server-interm.pem\", test_datapath());\n\t\telse\n\t\t\tre_snprintf(path, sizeof(path), \"%s/server-ecdsa.pem\",\n\t\t\t\t\ttest_datapath());\n\n\t\terr = https_listen(&sock, &srv, path,\n\t\t\tput ? http_put_req_handler : http_req_handler, &t);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (t.cert_auth)\n\t\t\terr = https_set_verify_msgh(sock,\n\t\t\t\thttps_verify_msg_handler);\n\t}\n\telse {\n\t\terr = http_listen(&sock, &srv,\n\t\t\tput ? http_put_req_handler : http_req_handler, &t);\n\t}\n\tif (err)\n\t\tgoto out;\n\n\thttp_set_max_body_size(sock, 1024 * 1024 / 2);\n\n\terr = tcp_sock_local_get(http_sock_tcp(sock), &srv);\n\tif (err)\n\t\tgoto out;\n\n\terr = dnsc_alloc(&dnsc, NULL, dns_srv ? &dns_srv->addr : &dns, 1);\n\tif (err)\n\t\tgoto out;\n\n\tdconf.query_hash_size = 16;\n\tdconf.tcp_hash_size\t = 2;\n\tdconf.conn_timeout\t = hconf.conn_timeout;\n\tdconf.idle_timeout\t = hconf.idle_timeout;\n\tdconf.cache_ttl_max\t = 1800;\n\tdconf.getaddrinfo\t = dnsc_getaddrinfo_enabled(dnsc);\n\n\tif (dns_set_conf_test) {\n\t\terr = dnsc_conf_set(dnsc, &dconf);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = http_client_alloc(&cli, dnsc);\n\tif (err)\n\t\tgoto out;\n\n\terr = http_client_set_config(cli, &hconf);\n\tif (err)\n\t\tgoto out;\n\n#ifdef USE_TLS\n\tif (secure) {\n\t\tstruct tls*\t cli_tls;\n\t\thttp_client_get_tls(cli, &cli_tls);\n\n\t\tif (t.cert_auth) {\n\t\t\tre_snprintf(path, sizeof(path),\n\t\t\t\t\"%s/sni/client-interm.pem\", test_datapath());\n\t\t\terr |= http_client_set_cert(cli, path);\n\t\t\terr |= http_client_set_key(cli, path);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n\t\t\ttls_set_posthandshake_auth(cli_tls, 1);\n\n\t\t\t/* add CAs to http server */\n\t\t\tre_snprintf(path, sizeof(path), \"%s/sni/root-ca.pem\",\n\t\t\t\ttest_datapath());\n\n\t\t\terr |=  tls_add_ca(http_sock_tls(sock), path);\n\t\t\tre_snprintf(path, sizeof(path),\n\t\t\t\t\"%s/sni/server-interm.pem\", test_datapath());\n\n\t\t\terr |=  tls_add_ca(http_sock_tls(sock), path);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\n\t\tif (http_conn && !t.cert_auth)\n\t\t\terr = tls_set_session_reuse(cli_tls, true);\n\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n#endif\n\n\tif (put)\n\t\thttp_client_set_bufsize_max(cli, REQ_BODY_CHUNK_SIZE + 128);\n\n#ifdef USE_TLS\n\t/* add root CA to http client */\n\tif (secure) {\n\t\tif (t.cert_auth)\n\t\t\tre_snprintf(path, sizeof(path), \"%s/sni/root-ca.pem\",\n\t\t\t\t\ttest_datapath());\n\t\telse\n\t\t\tre_snprintf(path, sizeof(path), \"%s/server-ecdsa.pem\",\n\t\t\t\t\ttest_datapath());\n\t\terr = http_client_add_ca(cli, path);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\tif (t.cert_auth) {\n\t\tre_snprintf(path, sizeof(path), \"%s/sni/client-interm.pem\",\n\t\t\t\ttest_datapath());\n\t\terr |= http_client_set_cert(cli, path);\n\t\terr |= http_client_set_key(cli, path);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n#endif\n\n\t(void)re_snprintf(url, sizeof(url),\n\t\t\t\t\t\"http%s://%s:%u/%sindex.html\",\n\t\t\t\t\tsecure ? \"s\" : \"\",\n\t\t\t\t\tdns_srv_query ?\n\t\t\t\t\t\"test1.example.net\"\n\t\t\t\t\t: \"127.0.0.1\",\n\t\t\t\t\tsa_port(&srv),\n\t\t\t\t\tt.cert_auth ? \"auth/\" : \"\");\n\n\tfor (i = 1; i <= REQ_HTTP_REQUESTS; i++) {\n\t\tt.i_req_body = 0;\n\n\t\tt.clen = put ? REQ_BODY_SIZE :\n\t\t\t2 * strlen(\"abcdefghijklmnopqrstuvwxyz\");\n\n\t\tif (http_conn) {\n\t\t\terr = http_reqconn_alloc(&conn, cli,\n\t\t\t\thttp_resp_handler, http_data_handler, &t);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n\t\t\tif (put) {\n\t\t\t\terr = http_reqconn_set_req_bodyh(conn,\n\t\t\t\t\tput ? \thttp_req_long_body_handler :\n\t\t\t\t\t\thttp_req_body_handler,\n\t\t\t\t\tt.clen);\n\t\t\t\tif (err)\n\t\t\t\t\tgoto out;\n\n\t\t\t\tpl_set_str(&pl, \"PUT\");\n\t\t\t\terr = http_reqconn_set_method(conn, &pl);\n\t\t\t\tif (err)\n\t\t\t\t\tgoto out;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tmb_body = mbuf_alloc(t.clen);\n\t\t\t\tif (!mb_body)\n\t\t\t\t\tgoto out;\n\n\t\t\t\tif (mbuf_write_str(mb_body,\n\t\t\t\t\t\"abcdefghijklmnopqrstuvwxyz\"\\\n\t\t\t\t\t\"abcdefghijklmnopqrstuvwxyz\"))\n\t\t\t\t\tgoto out;\n\n\t\t\t\terr = http_reqconn_set_body(conn, mb_body);\n\t\t\t\tmb_body = mem_deref(mb_body);\n\t\t\t\tif (err)\n\t\t\t\t\tgoto out;\n\t\t\t}\n\n\t\t\tpl_set_str(&pl, url);\n\t\t\terr = http_reqconn_send(conn, &pl);\n\t\t}\n\t\telse {\n\t\t\terr = http_request(&req, cli, met, url,\n\t\t\t\thttp_resp_handler, http_data_handler,\n\t\t\t\tput ? \thttp_req_long_body_handler :\n\t\t\t\t\thttp_req_body_handler,\n\t\t\t\t&t,\n\t\t\t\t\"Content-Length: %zu\\r\\n%s\\r\\n%s\",\n\t\t\t\tt.clen,\n\t\t\t\tt.clen > REQ_BODY_CHUNK_SIZE ?\n\t\t\t\t\t\"Expect: 100-continue\\r\\n\" : \"\",\n\t\t\t\t\"abcdefghijklmnopqrstuvwxyz\");\n\t\t}\n\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = re_main_timeout(secure ? 1800 : 900);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (t.err) {\n\t\t\terr = t.err;\n\t\t\tgoto out;\n\t\t}\n\n\t\t/* verify results after HTTP traffic */\n\t\tTEST_EQUALS(i, t.n_request);\n\t\tTEST_EQUALS(i, t.n_response);\n\n\t\tif (t.mb_body)\n\t\t\tTEST_STRCMP(\"abcdefghijklmnopqrstuvwxyz\", 26,\n\t\t\t\tt.mb_body->buf, t.mb_body->end);\n\n\t\tt.mb_body = mem_deref(t.mb_body);\n\t\treq =  mem_deref(req);\n\t\tconn = mem_deref(conn);\n\t}\n\n out:\n\tmem_deref(t.mb_body);\n\tmem_deref(mb_body);\n\tmem_deref(req);\n\tmem_deref(conn);\n\tmem_deref(cli);\n\tmem_deref(dnsc);\n\tmem_deref(sock);\n\tmem_deref(dns_srv);\n\n\treturn err;\n}\n\n\n#ifdef USE_TLS\nint test_http_client_set_tls(void)\n{\n\tstruct sa dns;\n\tstruct dnsc *dnsc = NULL;\n\tstruct http_cli *cli = NULL;\n\tstruct tls *tls = NULL, *tls_test = NULL, *tls_cli = NULL;\n\tint err;\n\n\tTEST_EINVAL(http_client_get_tls, NULL, NULL);\n\tTEST_EINVAL(http_client_set_tls, NULL, NULL);\n\n\t/* Setup */\n\terr = sa_set_str(&dns, \"127.0.0.1\", 53);    /* note: unused */\n\tTEST_ERR(err);\n\n\terr = dnsc_alloc(&dnsc, NULL, &dns, 1);\n\tTEST_ERR(err);\n\n\terr = http_client_alloc(&cli, dnsc);\n\tTEST_ERR(err);\n\n\t/* Test original Http Client TLS Context */\n\tTEST_EINVAL(http_client_get_tls, cli, NULL);\n\terr = http_client_get_tls(cli, &tls_cli);\n\tTEST_ERR(err);\n\ttls_cli = mem_ref(tls_cli);\n\tTEST_EQUALS(2, mem_nrefs(tls_cli));\n\n\t/* Allocate new TLS Context */\n\terr = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL);\n\tTEST_ERR(err);\n\tTEST_NOT_EQUALS(tls, tls_cli);\n\n\t/* Set and verify new TLS Context */\n\tTEST_EINVAL(http_client_set_tls, cli, NULL);\n\terr = http_client_set_tls(cli, tls);\n\tTEST_ERR(err);\n\tTEST_EQUALS(2, mem_nrefs(tls));\n\tTEST_EQUALS(1, mem_nrefs(tls_cli));\n\n\terr = http_client_get_tls(cli, &tls_test);\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(tls, tls_test);\n\nout:\n\tif (cli) {\n\t\tmem_deref(cli);\n\t\tmem_deref(tls_cli);\n\t}\n\n\tif (dnsc)\n\t\tmem_deref(dnsc);\n\n\tif (tls) {\n\t\tTEST_EQUALS(1, mem_nrefs(tls));\n\t\tmem_deref(tls);\n\t}\n\n\treturn err;\n}\n#endif\n\n\nint test_http_loop(void)\n{\n\treturn test_http_loop_base(false, \"GET\", false, false, false, false);\n}\n\n\n#ifdef USE_TLS\nint test_https_loop(void)\n{\n\treturn test_http_loop_base(true, \"GET\", false, false, false, false);\n}\n#endif\n\n\nint test_http_large_body(void)\n{\n\treturn test_http_loop_base(false, \"PUT\", false, false, false, false);\n}\n\n\n#ifdef USE_TLS\nint test_https_large_body(void)\n{\n\treturn test_http_loop_base(true, \"PUT\", false, false, false, false);\n}\n#endif\n\n\nint test_http_conn(void)\n{\n\treturn test_http_loop_base(false, \"GET\", true, false, false, false);\n}\n\n\nint test_http_conn_large_body(void)\n{\n\treturn test_http_loop_base(false, \"PUT\", true, false, false, false);\n}\n\n\nint test_dns_http_integration(void)\n{\n\treturn test_http_loop_base(false, \"GET\", true, true, false, false);\n}\n\n\nint test_dns_cache_http_integration(void)\n{\n\treturn test_http_loop_base(false, \"GET\", true, true, true, false);\n}\n\n#ifdef HAVE_TLS1_3_POST_HANDSHAKE_AUTH\nint test_https_conn_post_handshake(void)\n{\n\treturn test_http_loop_base(true, \"GET\", true, false, false, true);\n}\n#endif\n\n\nstatic void http_req_addr_handler(struct http_conn *conn,\n\t\t\t\t   const struct http_msg *msg, void *arg)\n{\n\tstruct test *t = arg;\n\t(void)msg;\n\n\t++t->n_request;\n\thttp_reply(conn, 200, \"OK\", \"Content-Length: 0\\r\\n\\r\\n\");\n}\n\n\nstatic void http_resp_addr_handler(int err, const struct http_msg *msg,\n\t\t\t\t   void *arg)\n{\n\tstruct test *t = arg;\n\n\tif (err)\n\t\tgoto out;\n\n\t++t->n_response;\n\tTEST_EQUALS(200, msg->scode);\n\tre_cancel();\n\n out:\n\tif (err)\n\t\tabort_test(t, err);\n}\n\n\nstatic int test_http_request_addr_base(bool secure)\n{\n\tstruct http_sock *sock = NULL;\n\tstruct http_cli *cli = NULL;\n\tstruct http_req *req = NULL;\n\tstruct dnsc *dnsc = NULL;\n\tstruct dns_server *dns_srv = NULL;\n\tstruct sa srv;\n\tstruct test t;\n\tchar url[256];\n\tint err = 0;\n#ifdef USE_TLS\n\tchar path[256];\n#endif\n\tmemset(&t, 0, sizeof(t));\n\tt.secure = secure;\n\n\terr = sa_set_str(&srv, \"127.0.0.1\", 0);\n\tTEST_ERR(err);\n\n\t/* Mock DNS server with no records */\n\terr = dns_server_alloc(&dns_srv, \"127.0.0.1\");\n\tTEST_ERR(err);\n\n#ifdef USE_TLS\n\tif (secure) {\n\t\tre_snprintf(path, sizeof(path), \"%s/server-ecdsa.pem\",\n\t\t\t    test_datapath());\n\t\terr = https_listen(&sock, &srv, path, http_req_addr_handler,\n\t\t\t\t   &t);\n\t}\n\telse\n#endif\n\t{\n\t\terr = http_listen(&sock, &srv, http_req_addr_handler, &t);\n\t}\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_sock_local_get(http_sock_tcp(sock), &srv);\n\tif (err)\n\t\tgoto out;\n\n\terr = dnsc_alloc(&dnsc, NULL, &dns_srv->addr, 1);\n\tif (err)\n\t\tgoto out;\n\n\terr = http_client_alloc(&cli, dnsc);\n\tif (err)\n\t\tgoto out;\n\n#ifdef USE_TLS\n\tif (secure) {\n\t\tre_snprintf(path, sizeof(path), \"%s/server-ecdsa.pem\",\n\t\t\t    test_datapath());\n\t\terr = http_client_add_ca(cli, path);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n#endif\n\n\t/* Port 1 in URL — without explicit addr the connect goes to port 1\n\t * (nothing listens), proving http_request_addr bypasses ip and port\n\t * in the URL and uses the given ip and port */\n\tre_snprintf(url, sizeof(url), \"http%s://127.0.0.1:1/index.html\",\n\t\t    secure ? \"s\" : \"\");\n\n\terr = http_request_addr(&req, cli, \"GET\", url, &srv,\n\t\t\t\thttp_resp_addr_handler, NULL, NULL, &t,\n\t\t\t\t\"\\r\\n\");\n\tif (err)\n\t\tgoto out;\n\n\terr = re_main_timeout(secure ? 1800 : 900);\n\tif (err)\n\t\tgoto out;\n\n\tif (t.err) {\n\t\terr = t.err;\n\t\tgoto out;\n\t}\n\n\tTEST_EQUALS(1, t.n_request);\n\tTEST_EQUALS(1, t.n_response);\n\n out:\n\tmem_deref(t.mb_body);\n\tmem_deref(req);\n\tmem_deref(cli);\n\tmem_deref(dnsc);\n\tmem_deref(sock);\n\tmem_deref(dns_srv);\n\n\treturn err;\n}\n\n\nint test_http_request_addr(void)\n{\n\treturn test_http_request_addr_base(false);\n}\n\n\n#ifdef USE_TLS\nint test_https_request_addr(void)\n{\n\treturn test_http_request_addr_base(true);\n}\n#endif\n"
  },
  {
    "path": "test/httpauth.c",
    "content": "/**\n * @file httpauth.c HTTP Authentication Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"httpauth\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int pl_equal(const char *name, const struct pl *a, const struct pl *b)\n{\n\tstatic const struct pl plnil = PL(\"nil\");\n\tint err;\n\n\tif ((a->p && !b->p) || (!a->p && b->p))\n\t\terr = EINVAL;\n\telse\n\t\terr = pl_cmp(a, b);\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"%s mismatch: '%r' vs '%r'\\n\",\n\t\t\t      name,\n\t\t\t      a->p ? a : &plnil,\n\t\t\t      b->p ? b : &plnil);\n\t}\n\n\treturn err;\n}\n\n\nstatic bool chall_equal(const struct httpauth_digest_chall *a,\n\t\t\tconst struct httpauth_digest_chall *b)\n{\n\tint err = 0;\n\n\terr |= pl_equal(\"realm\",     &a->realm,     &b->realm);\n\terr |= pl_equal(\"nonce\",     &a->nonce,     &b->nonce);\n\terr |= pl_equal(\"opaque\",    &a->opaque,    &b->opaque);\n\terr |= pl_equal(\"stale\",     &a->stale,     &b->stale);\n\terr |= pl_equal(\"algorithm\", &a->algorithm, &b->algorithm);\n\terr |= pl_equal(\"qop\",       &a->qop,       &b->qop);\n\terr |= pl_equal(\"domain\",    &a->domain,    &b->domain);\n\terr |= pl_equal(\"charset\",   &a->charset,   &b->charset);\n\terr |= pl_equal(\"userhash\",  &a->userhash,  &b->userhash);\n\n\treturn err == 0;\n}\n\n\nstatic bool resp_equal(const struct httpauth_digest_resp *a,\n\t\t       const struct httpauth_digest_resp *b)\n{\n\tint err = 0;\n\n\terr |= pl_equal(\"realm\",     &a->realm,     &b->realm);\n\terr |= pl_equal(\"nonce\",     &a->nonce,     &b->nonce);\n\terr |= pl_equal(\"response\",  &a->response,  &b->response);\n\terr |= pl_equal(\"username\",  &a->username,  &b->username);\n\terr |= pl_equal(\"uri\",       &a->uri,       &b->uri);\n\terr |= pl_equal(\"nc\",        &a->nc,        &b->nc);\n\terr |= pl_equal(\"cnonce\",    &a->cnonce,    &b->cnonce);\n\terr |= pl_equal(\"qop\",       &a->qop,       &b->qop);\n\n\treturn err == 0;\n}\n\n\nint test_httpauth_chall(void)\n{\n\tstatic const struct {\n\t\tconst char *hval;\n\t\tstruct httpauth_digest_chall chall;\n\t\tint err;\n\t} testv[] = {\n\t\t{\n\t\t\t\"Digest realm=\\\"realm\\\",\"\n\t\t\t\" nonce=\\\"4ee102da2fb730e04a26e8da913249b264f391c3\\\",\"\n\t\t\t\" opaque=\\\"123\\\", stale=\\\"true\\\"\"\n\t\t\t\" algorithm=\\\"MD5\\\"\"\n\t\t\t,\n\t\t\t{PL(\"realm\"),\n\t\t\t PL(\"4ee102da2fb730e04a26e8da913249b264f391c3\"),\n\t\t\t PL(\"123\"),\n\t\t\t PL(\"true\"),\n\t\t\t PL(\"MD5\"),\n\t\t\t PL_INIT,\n\t\t\t PL_INIT,\n\t\t\t PL_INIT,\n\t\t\t PL_INIT},\n\t\t\t0\n\t\t},\n\n\t\t{\n\t\t\t\"Digest realm=\\\"creytiv.com\\\",\"\n\t\t\t\" nonce=\\\"9c916919cbc6ad7f54a4f64e5b5115074ee109fa\\\"\"\n\t\t\t\", qop=\\\"auth\\\"\",\n\t\t\t{PL(\"creytiv.com\"),\n\t\t\t PL(\"9c916919cbc6ad7f54a4f64e5b5115074ee109fa\"),\n\t\t\t PL_INIT, PL_INIT, PL_INIT,\n\t\t\t PL(\"auth\"),\n\t\t\t PL_INIT, PL_INIT, PL_INIT\n\t\t\t},\n\t\t\t0\n\t\t},\n\n\t\t{\n\t\t\t\"Basic bogus\",\n\t\t\t{PL_INIT, PL_INIT, PL_INIT, PL_INIT, PL_INIT, PL_INIT,\n\t\t\t PL_INIT, PL_INIT, PL_INIT},\n\t\t\tEBADMSG\n\t\t},\n\t};\n\tsize_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tstruct httpauth_digest_chall chall;\n\t\tstruct pl pl;\n\t\tint terr;\n\n\t\tpl_set_str(&pl, testv[i].hval);\n\t\tterr = httpauth_digest_challenge_decode(&chall, &pl);\n\t\tif (terr != testv[i].err) {\n\t\t\tDEBUG_WARNING(\"chall: expected error %d, got %d\\n\",\n\t\t\t\t      testv[i].err, terr);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (terr)\n\t\t\tcontinue;\n\n\t\tif (!chall_equal(&testv[i].chall, &chall)) {\n\t\t\tDEBUG_WARNING(\"chall: test %d failed\\n\", i);\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn err;\n}\n\n\nint test_httpauth_resp(void)\n{\n\tstatic const struct {\n\t\tconst char *hval;\n\t\tstruct httpauth_digest_resp resp;\n\t\tstruct pl method;\n\t\tre_nonstring uint8_t ha1[MD5_SIZE];\n\t\tint err;\n\t} testv[] = {\n\t\t{\n\n\t\t\t\"Digest username=\\\"aeh\\\", realm=\\\"creytiv.com\\\",\"\n\t\t\t\" nonce=\\\"9c916919cbc6ad7f54a4f64e5b5115074ee109fa\\\"\"\n\t\t\t\", uri=\\\"sip:creytiv.com;transport=udp\\\",\"\n\t\t\t\" response=\\\"bb996865add5a86217f39e1f369c29ea\\\",\"\n\t\t\t\" cnonce=\\\"66a7a21e46ad8edd\\\", qop=auth, nc=00000002\"\n\t\t\t,\n\t\t\t{PL(\"creytiv.com\"),\n\t\t\t PL(\"9c916919cbc6ad7f54a4f64e5b5115074ee109fa\"),\n\t\t\t PL(\"bb996865add5a86217f39e1f369c29ea\"),\n\t\t\t PL(\"aeh\"),\n\t\t\t PL(\"sip:creytiv.com;transport=udp\"),\n\t\t\t PL(\"00000002\"),\n\t\t\t PL(\"66a7a21e46ad8edd\"),\n\t\t\t PL(\"auth\"), PL_INIT, PL_INIT,\n\t\t\t PL_INIT, NULL, 0, NULL},\n\t\t\tPL(\"REGISTER\"),\n\t\t\t\"\\x1c\\x0a\\x98\\x61\\x5b\\x7b\\x37\\xc6\"\n\t\t\t\"\\x94\\x51\\xae\\xb6\\x4b\\x2f\\x11\\x02\",\n\t\t\t0\n\t\t},\n\t\t{\n\t\t\t\"Digest bogus tull\",\n\t\t\t{PL_INIT, PL_INIT, PL_INIT, PL_INIT, PL_INIT,\n\t\t\t PL_INIT, PL_INIT, PL_INIT, PL_INIT, PL_INIT,\n\t\t\t PL_INIT, NULL, 0 , NULL},\n\t\t\tPL_INIT,\n\t\t\t\"\",\n\t\t\tEBADMSG\n\t\t},\n\t};\n\tsize_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tstruct httpauth_digest_resp resp;\n\t\tstruct pl pl;\n\t\tint terr;\n\n\t\tpl_set_str(&pl, testv[i].hval);\n\t\tterr = httpauth_digest_response_decode(&resp, &pl);\n\t\tif (terr != testv[i].err) {\n\t\t\tDEBUG_WARNING(\"resp: expected error %d, got %d\\n\",\n\t\t\t\t      testv[i].err, terr);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (terr)\n\t\t\tcontinue;\n\n\t\tif (!resp_equal(&testv[i].resp, &resp)) {\n\t\t\tDEBUG_WARNING(\"resp: test %d failed\\n\", i);\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\terr = httpauth_digest_response_auth(&resp, &testv[i].method,\n\t\t\t\t\t\t    testv[i].ha1);\n\t\tif (err) {\n\t\t\tif (err != ENOMEM) {\n\t\t\t\tDEBUG_WARNING(\"resp: auth failed %m\\n\", err);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn err;\n}\n\n\nint test_httpauth_basic_request(void) {\n\tstatic const struct {\n\t\tconst char *hval;\n\t\tstruct pl hval_response;\n\t\tconst char *realm;\n\t\tconst char *charset;\n\t\tconst char *user;\n\t\tconst char *passwd;\n\t\tint err;\n\t\tint auth_err;\n\t} testv[] = {\n\t\t{\n\t\t\t\"Basic realm=\\\"/my/home\\\"\",\n\t\t\tPL(\"Basic cmV0ZXN0OnJldGVzdHBhc3N3ZA==\"),\n\t\t\t\"/my/home\", NULL, \"retest\", \"retestpasswd\", 0, 0\n\t\t},\n\t\t{\n\t\t\t\"Basic realm=\\\"/my/home\\\", charset=\\\"UTF-8\\\"\",\n\t\t\tPL(\"Basic cmV0ZXN0OnJldGVzdHBhc3N3ZOKCrA==\"),\n\t\t\t\"/my/home\", \"UTF-8\", \"retest\",\n\t\t\t\"retestpasswd\\xe2\\x82\\xac\",\n\t\t\t0, 0\n\t\t},\n\t\t{\n\t\t\t\"Basic realm=\\\"/my/home\\\"\",\n\t\t\tPL(\"Basic d3Jvbmc6Y3JlZGVudGlhbHM==\"), \"/my/home\",\n\t\t\tNULL, \"retest\", \"retestpasswd\", 0, EACCES\n\t\t},\n\t};\n\tunsigned int i;\n\tint err = 0;\n\n\tfor (i = 0; i < RE_ARRAY_SIZE(testv); ++i) {\n\t\tstruct httpauth_basic_req *req = NULL;\n\t\tstruct mbuf *mb = NULL;\n\t\tint terr = 0;\n\t\tint tauth_err = 0;\n\n\t\tterr = httpauth_basic_request(&req,\n\t\t\ttestv[i].realm, testv[i].charset);\n\t\tif (terr == ENOMEM) {\n\t\t\terr = ENOMEM;\n\t\t\tbreak;\n\t\t}\n\t\telse if (terr != testv[i].err) {\n\t\t\tDEBUG_WARNING(\"basic req: expected error %d, got %m\\n\",\n\t\t\t\ttestv[i].err, terr);\n\t\t\terr = terr;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (str_casecmp(req->realm, testv[i].realm) != 0) {\n\t\t\tDEBUG_WARNING(\"basic req: expected realm %s, got %s\\n\",\n\t\t\t\ttestv[i].realm, req->realm);\n\t\t\terr = EBADMSG;\n\t\t\tmem_deref(req);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (testv[i].charset) {\n\t\t\tif (str_casecmp(req->charset, testv[i].charset) != 0) {\n\t\t\t\tDEBUG_WARNING(\"basic req: expected charset\"\n\t\t\t\t\t\"%s, got %s\\n\", testv[i].charset,\n\t\t\t\t\treq->charset);\n\t\t\t\terr = EBADMSG;\n\t\t\t\tmem_deref(req);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tmb = mbuf_alloc(512);\n\t\tif (!mb) {\n\t\t\terr = ENOMEM;\n\t\t\tmem_deref(req);\n\t\t\tbreak;\n\t\t}\n\n\t\terr = mbuf_printf(mb, \"%H\", httpauth_basic_request_print, req);\n\t\tif (err) {\n\t\t\tmem_deref(mb);\n\t\t\tmem_deref(req);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (memcmp(testv[i].hval, mb->buf,\n\t\t\tstr_len(testv[i].hval)) != 0) {\n\t\t\tDEBUG_WARNING(\"basic req: expected hval %s, got %s\\n\",\n\t\t\t\ttestv[i].hval, mb->buf);\n\t\t\terr = EBADMSG;\n\t\t\tmem_deref(mb);\n\t\t\tmem_deref(req);\n\t\t\tbreak;\n\t\t}\n\n\t\tmem_deref(mb);\n\t\ttauth_err = httpauth_basic_verify(&testv[i].hval_response,\n\t\t\ttestv[i].user, testv[i].passwd);\n\t\tif (tauth_err != testv[i].auth_err) {\n\t\t\tDEBUG_WARNING(\"basic req:\"\n\t\t\t\t\"authentication expected %d, got %d\\n\",\n\t\t\t\ttestv[i].auth_err, tauth_err);\n\t\t\tmem_deref(req);\n\t\t\tbreak;\n\t\t}\n\n\t\tmem_deref(req);\n\t}\n\n\treturn err;\n}\n\n\nint test_httpauth_digest_request(void)\n{\n\tstatic const struct {\n\t\tconst char *hval_fmt;\n\t\tconst char *realm;\n\t\tconst char *domain;\n\t\tconst char *etag;\n\t\tconst char *opaque;\n\t\tconst bool stale;\n\t\tconst char *algorithm;\n\t\tconst char *qop;\n\t\tconst char *charset;\n\t\tconst bool userhash;\n\t\tint err;\n\t} testv [] = {\n\t\t{\n\t\t\t\"\",\n\t\t\tNULL, NULL, \"\", NULL, false, NULL, \"auth\", NULL, false,\n\t\t\tEINVAL\n\t\t},\n\t\t{\n\t\t\t\"Digest realm=\\\"/my/home\\\", qop=\\\"\\\",\"\n\t\t\t\" nonce=\\\"%s\\\", algorithm=MD5\",\n\t\t\t\"/my/home\", NULL, \"localhost:5060\", NULL, false,\n\t\t\tNULL, \"\", NULL, false,\n\t\t\t0\n\t\t},\n\t\t{\n\t\t\t\"Digest realm=\\\"/my/home\\\", qop=\\\"\\\",\"\n\t\t\t\" nonce=\\\"%s\\\", algorithm=MD5\",\n\t\t\t\"/my/home\", NULL, \"localhost:5060\", NULL, false,\n\t\t\tNULL, \"\", NULL, false, 0\n\t\t},\n\t\t{\n\t\t\t\"Digest realm=\\\"/my/home\\\", qop=\\\"auth\\\",\"\n\t\t\t\" nonce=\\\"%s\\\", algorithm=SHA-256\",\n\t\t\t\"/my/home\", NULL, \"localhost:5060\", NULL, false,\n\t\t\t\"SHA-256\", \"auth\", NULL, false, 0\n\t\t},\n\t\t{\n\t\t\t\"Digest realm=\\\"/my/home\\\", qop=\\\"auth\\\",\"\n\t\t\t\" nonce=\\\"%s\\\", algorithm=SHA-256-sess, stale=true\",\n\t\t\t\"/my/home\", NULL, \"localhost:5060\", NULL, true,\n\t\t\t\"SHA-256-sess\", \"auth\", NULL, false, 0\n\t\t},\n\t\t{\n\t\t\t\"Digest realm=\\\"/my/home\\\", qop=\\\"auth\\\",\"\n\t\t\t\" nonce=\\\"%s\\\", algorithm=SHA-256,\"\n\t\t\t\" domain=\\\"example.com\\\", stale=true,\"\n\t\t\t\" charset=\\\"UTF-8\\\", userhash=true\",\n\t\t\t\"/my/home\", \"example.com\", \"localhost:5060\", NULL,\n\t\t\ttrue, \"SHA-256\", \"auth\", \"UTF-8\", true, 0\n\t\t},\n\t\t{\n\t\t\t\"Digest realm=\\\"/my/home\\\", qop=\\\"auth-int\\\",\"\n\t\t\t\" nonce=\\\"%s\\\", algorithm=MD5-sess,\"\n\t\t\t\" domain=\\\"example.com\\\", stale=true,\"\n\t\t\t\" charset=\\\"UTF-8\\\", userhash=true\",\n\t\t\t\"/my/home\", \"example.com\", \"localhost:5060\", NULL,\n\t\t\ttrue, \"MD5-sess\", \"auth-int\", \"UTF-8\", true, 0\n\t\t},\n\t\t{\n\t\t\t\"Digest realm=\\\"/my/home\\\", qop=\\\"auth-int\\\",\"\n\t\t\t\" nonce=\\\"%s\\\", algorithm=MD5,\"\n\t\t\t\" domain=\\\"example.com\\\", stale=true,\"\n\t\t\t\" charset=\\\"UTF-8\\\", userhash=true\",\n\t\t\t\"/my/home\", \"example.com\", \"129842\", NULL,\n\t\t\ttrue, NULL, \"auth-int\", \"UTF-8\", true, 0\n\t\t},\n\t};\n\n\tint err = 0;\n\tfor (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) {\n\t\tstruct httpauth_digest_chall_req *req = NULL;\n\t\tstruct mbuf *mb_refval = NULL;\n\t\tstruct mbuf *mb_printed = NULL;\n\n\t\tmb_refval = mbuf_alloc(512);\n\t\tmb_printed = mbuf_alloc(512);\n\t\tif (!mb_refval || !mb_printed) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = httpauth_digest_chall_request_full(&req, testv[i].realm,\n\t\t\ttestv[i].domain, testv[i].etag, testv[i].opaque,\n\t\t\ttestv[i].stale, testv[i].algorithm, testv[i].qop,\n\t\t\ttestv[i].charset, testv[i].userhash);\n\t\tif (err == ENOMEM) {\n\t\t\tgoto out;\n\t\t}\n\t\telse if (err != testv[i].err) {\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" Expected return value %m, got %m\\n\",\n\t\t\t\ti, testv[i].err, err);\n\t\t}\n\t\telse if (err) {\n\t\t\tgoto for_continue;\n\t\t}\n\n\t\terr = mbuf_printf(mb_refval, testv[i].hval_fmt, req->nonce);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" No reference created %m\\n\", i, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = mbuf_printf(mb_printed, \"%H\",\n\t\t\thttpauth_digest_chall_req_print, req);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" Digest request print error %m\\n\", i, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (mb_refval->end != mb_printed->end) {\n\t\t\tDEBUG_WARNING(\"[%d] Expected header len %d, got %d\\n\",\n\t\t\t\ti, mb_refval->end, mb_printed->end);\n\t\t\t\terr = EINVAL;\n\t\t\t\tgoto out;\n\t\t}\n\n\t\tif (memcmp(mb_refval->buf, mb_printed->buf, mb_refval->end)) {\n\t\t\tDEBUG_WARNING(\"[%d] Expected header %b, got %b\\n\", i,\n\t\t\t\tmb_refval->buf, mb_refval->end,\n\t\t\t\tmb_printed->buf, mb_printed->end);\n\t\t\terr = EINVAL;\n\t\t\tgoto out;\n\t\t}\n\nfor_continue:\n\t\tmem_deref(req);\n\t\tmem_deref(mb_refval);\n\t\tmem_deref(mb_printed);\n\t\tcontinue;\n\nout:\n\t\tmem_deref(req);\n\t\tmem_deref(mb_refval);\n\t\tmem_deref(mb_printed);\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nint test_httpauth_digest_response(void)\n{\n\tstatic const struct {\n\t\tconst struct httpauth_digest_chall chall;\n\t\tconst char *user;\n\t\tconst char *passwd;\n\t\tconst char *qop;\n\t\tconst struct pl method;\n\t\tconst char *uri;\n\t\tconst char *entitybody;\n\t\tconst char *precalc_digest;\n\t\tconst char *resp_hval;\n\t} testv [] = {\n\t\t{\n\t\t\t{\n\t\t\t\tPL(\"/my/home\"),\n\t\t\t\tPL(\"b5c64f319d37323ac652b77012817ccaa\"\n\t\t\t\t\"6e9a7e4e7563155f1f9556414dd4615\"),\n\t\t\t\tPL(\"324DF3428BCF42D29A\"), PL_INIT,\n\t\t\t\tPL(\"MD5\"), PL(\"auth\"), PL_INIT, PL_INIT,\n\t\t\t\tPL_INIT\n\t\t\t},\n\t\t\t\"retest\", \"sec_pwd_retest\", \"auth\", PL(\"GET\"),\n\t\t\t\"example.com/my/home/something\", NULL,\n\t\t\t\"88f41f7227700e07d0d65256714a5a1a\",\n\n\t\t\t\"Digest realm=\\\"/my/home\\\",\"\n\t\t\t\" nonce=\\\"b5c64f319d37323ac652b77012817ccaa6e\"\n\t\t\t\"9a7e4e7563155f1f9556414dd4615\\\",\"\n\t\t\t\" username=\\\"retest\\\",\"\n\t\t\t\" uri=\\\"example.com/my/home/something\\\",\"\n\t\t\t\" response=\\\"88f41f7227700e07d0d65256714a5a1a\\\",\"\n\t\t\t\" opaque=\\\"324DF3428BCF42D29A\\\", algorithm=MD5,\"\n\t\t\t\" qop=auth, cnonce=\\\"deadbeef\\\", nc=\\\"00000001\\\"\",\n\t\t},\n\t\t{\n\t\t\t{\n\t\t\t\tPL(\"/my/home\"),\n\t\t\t\tPL(\"b5c64f319d37323ac652b77012817ccaa\"\n\t\t\t\t\"6e9a7e4e7563155f1f9556414dd4615\"),\n\t\t\t\tPL(\"324DF3428BCF42D29A\"), PL_INIT,\n\t\t\t\tPL(\"SHA-256\"), PL(\"auth\"), PL_INIT, PL_INIT,\n\t\t\t\tPL_INIT\n\t\t\t},\n\t\t\t\"retest\", \"sec_pwd_retest\", \"auth\", PL(\"GET\"),\n\t\t\t\"example.com/my/home/something\", NULL,\n\t\t\t\"c22b56ce81bbb59570f0fbbc0ba27210dbbfcb2b23fe\"\n\t\t\t\"a371d214722f319dc41c\",\n\n\t\t\t\"Digest realm=\\\"/my/home\\\",\"\n\t\t\t\" nonce=\\\"b5c64f319d37323ac652b77012817ccaa6e\"\n\t\t\t\"9a7e4e7563155f1f9556414dd4615\\\", username=\\\"retest\\\",\"\n\t\t\t\" uri=\\\"example.com/my/home/something\\\",\"\n\t\t\t\" response=\\\"c22b56ce81bbb59570f0fbbc0ba27210dbbfcb2b2\"\n\t\t\t\"3fea371d214722f319dc41c\\\",\"\n\t\t\t\" opaque=\\\"324DF3428BCF42D29A\\\", algorithm=SHA-256,\"\n\t\t\t\" qop=auth, cnonce=\\\"deadbeef\\\", nc=\\\"00000001\\\"\",\n\t\t},\n\t\t{\n\t\t\t{\n\t\t\t\tPL(\"/my/home\"),\n\t\t\t\tPL(\"b5c64f319d37323ac652b77012817ccaa\"\n\t\t\t\t\"6e9a7e4e7563155f1f9556414dd4615\"),\n\t\t\t\tPL(\"324DF3428BCF42D29A\"), PL_INIT,\n\t\t\t\tPL(\"MD5-sess\"), PL(\"auth\"), PL_INIT, PL_INIT,\n\t\t\t\tPL_INIT\n\t\t\t},\n\t\t\t\"retest\", \"sec_pwd_retest\", \"auth\", PL(\"GET\"),\n\t\t\t\"example.com/my/home/something\", NULL,\n\t\t\t\"1e79ac7105a4fdf416aaacfc50349110\",\n\n\t\t\t\"Digest realm=\\\"/my/home\\\",\"\n\t\t\t\" nonce=\\\"b5c64f319d37323ac652b77012817ccaa6e9a7e4e756\"\n\t\t\t\"3155f1f9556414dd4615\\\", username=\\\"retest\\\",\"\n\t\t\t\" uri=\\\"example.com/my/home/something\\\",\"\n\t\t\t\" response=\\\"1e79ac7105a4fdf416aaacfc50349110\\\",\"\n\t\t\t\" opaque=\\\"324DF3428BCF42D29A\\\", algorithm=MD5-sess,\"\n\t\t\t\" qop=auth, cnonce=\\\"deadbeef\\\", nc=\\\"00000001\\\"\",\n\t\t},\n\t\t{\n\t\t\t{\n\t\t\t\tPL(\"/my/home\"),\n\t\t\t\tPL(\"b5c64f319d37323ac652b77012817ccaa\"\n\t\t\t\t\"6e9a7e4e7563155f1f9556414dd4615\"),\n\t\t\t\tPL(\"324DF3428BCF42D29A\"), PL_INIT,\n\t\t\t\tPL(\"SHA-256\"), PL(\"auth-int\"), PL_INIT,\n\t\t\t\tPL_INIT, PL_INIT\n\t\t\t},\n\t\t\t\"retest\", \"sec_pwd_retest\", \"auth-int\", PL(\"GET\"),\n\t\t\t\"example.com/my/home/something\", \"\",\n\t\t\t\"2c0746b7174441314164d8d9a980d8920732de32e163\"\n\t\t\t\"03f0e6a82970230e79e4\",\n\n\t\t\t\"Digest realm=\\\"/my/home\\\",\"\n\t\t\t\" nonce=\\\"b5c64f319d37323ac652b77012817ccaa6e9a7e4e756\"\n\t\t\t\"3155f1f9556414dd4615\\\", username=\\\"retest\\\",\"\n\t\t\t\" uri=\\\"example.com/my/home/something\\\",\"\n\t\t\t\" response=\\\"2c0746b7174441314164d8d9a980d8920732de32e\"\n\t\t\t\"16303f0e6a82970230e79e4\\\",\"\n\t\t\t\" opaque=\\\"324DF3428BCF42D29A\\\", algorithm=SHA-256,\"\n\t\t\t\" qop=auth-int, cnonce=\\\"deadbeef\\\", nc=\\\"00000001\\\"\",\n\t\t},\n\t};\n\n\tint err = 0;\n\n\tfor (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) {\n\t\tstruct httpauth_digest_enc_resp *resp = NULL;\n\t\tstruct mbuf *mb_printed = NULL;\n\n\t\tmb_printed = mbuf_alloc(512);\n\t\tif (!mb_printed) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = httpauth_digest_response_full(&resp, &testv[i].chall,\n\t\t\t&testv[i].method, testv[i].uri, testv[i].user,\n\t\t\ttestv[i].passwd, testv[i].qop, testv[i].entitybody,\n\t\t\tNULL, false);\n\t\tif (err == ENOMEM) {\n\t\t\tgoto out;\n\t\t}\n\t\telse if (err) {\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" Could not generate response %m\\n\", i, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = httpauth_digest_response_set_cnonce(resp,\n\t\t\t&testv[i].chall, &testv[i].method, testv[i].user,\n\t\t\ttestv[i].passwd, testv[i].entitybody,\n\t\t\t0xdeadbeef, 0x00000001);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" Response recalculation failed %m\\n\", i, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = mbuf_printf(mb_printed, \"%H\",\n\t\t\thttpauth_digest_response_print, resp);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (str_casecmp(resp->response,\n\t\t\ttestv[i].precalc_digest) != 0) {\n\t\t\terr = EINVAL;\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" Expected response %s, got %s\\n\", i,\n\t\t\t\ttestv[i].precalc_digest,\n\t\t\t\tresp->response ? resp->response : \"(nil)\");\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (memcmp(testv[i].resp_hval,\n\t\t\tmb_printed->buf, mb_printed->end)) {\n\t\t\terr = EINVAL;\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" Expected header %s, got %b\\n\",\n\t\t\t\ti, testv[i].resp_hval,\n\t\t\t\tmb_printed->buf, mb_printed->end);\n\t\t\tgoto out;\n\t\t}\n\n\t\tmem_deref(mb_printed);\n\t\tmem_deref(resp);\n\t\tcontinue;\n\nout:\n\t\tmem_deref(mb_printed);\n\t\tmem_deref(resp);\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n\n\nint test_httpauth_digest_verification(void)\n{\n\tstatic const struct {\n\t\tconst char *realm;\n\t\tconst char *domain;\n\t\tconst char *opaque;\n\t\tconst bool stale;\n\t\tconst char *algorithm;\n\t\tconst char *qop;\n\t\tconst char *charset;\n\t\tconst bool userhash;\n\n\t\tconst char *etag;\n\t\tconst char *entitybody;\n\n\t\tconst char *user;\n\t\tconst char *passwd;\n\t\tconst char *uri;\n\t\tconst struct pl method;\n\t\tconst char *huser;\n\t} testv [] = {\n\t\t/* qop=auth & normal algorithm */\n\t\t{\n\t\t\t\"/my/home\", \"example.com\",\n\t\t\t\"185803523d335c8fe52cf633391d47f7\",\n\t\t\tfalse, \"MD5\", \"auth\", NULL, false,\n\t\t\t\"localhost:5060\", NULL, \"retest\", \"sec_passwd\",\n\t\t\t\"example.com/my/home/something\", PL(\"GET\"), NULL,\n\t\t},\n\t\t{\n\t\t\t\"/my/home\", \"example.com\",\n\t\t\t\"185803523d335c8fe52cf633391d47f7\",\n\t\t\tfalse, \"SHA-256\", \"auth\", NULL, false,\n\t\t\t\"localhost:5060\", NULL, \"retest\", \"sec_passed\",\n\t\t\t\"example.com/my/home/something\", PL(\"GET\"), NULL\n\t\t},\n\t\t/* qop=auth & session algorithm */\n\t\t{\n\t\t\t\"/my/home\", \"example.com\",\n\t\t\t\"185803523d335c8fe52cf633391d47f7\",\n\t\t\tfalse, \"MD5-sess\", \"auth\", NULL, false,\n\t\t\t\"localhost:5060\", NULL, \"retest\", \"sec_passed\",\n\t\t\t\"example.com/my/home/something\", PL(\"GET\"), NULL\n\t\t},\n\t\t{\n\t\t\t\"/my/home\", \"example.com\",\n\t\t\t\"185803523d335c8fe52cf633391d47f7\",\n\t\t\tfalse, \"SHA-256-sess\", \"auth\", NULL, false,\n\t\t\t\"localhost:5060\", NULL, \"retest\", \"sec_passed\",\n\t\t\t\"example.com/my/home/something\", PL(\"GET\"), NULL\n\t\t},\n\t\t/* qop=auth-int & normal algorithm */\n\t\t{\n\t\t\t\"/my/home\", \"example.com\",\n\t\t\t\"185803523d335c8fe52cf633391d47f7\",\n\t\t\tfalse, \"MD5\", \"auth-int\", NULL, false,\n\t\t\t\"localhost:5060\", NULL, \"retest\", \"sec_passed\",\n\t\t\t\"example.com/my/home/something\", PL(\"GET\"), NULL\n\t\t},\n\t\t{\n\t\t\t\"/my/home\", \"example.com\",\n\t\t\t\"185803523d335c8fe52cf633391d47f7\",\n\t\t\tfalse, \"SHA-256\", \"auth-int\", NULL, false,\n\t\t\t\"localhost:5060\", \"Strange body with content\",\n\t\t\t\"retest\", \"sec_passed\",\n\t\t\t\"example.com/my/home/something\", PL(\"GET\"), NULL\n\t\t},\n\t\t/* qop=auth-int & session algorithm */\n\t\t{\n\t\t\t\"/my/home\", \"example.com\",\n\t\t\t\"185803523d335c8fe52cf633391d47f7\",\n\t\t\tfalse, \"MD5-sess\", \"auth-int\", NULL, false,\n\t\t\t\"localhost:5060\", \"NOT NULL\", \"retest\", \"sec_passed\",\n\t\t\t\"example.com/my/home/something\", PL(\"GET\"), NULL\n\t\t},\n\t\t{\n\t\t\t\"/my/home\", \"example.com\",\n\t\t\t\"185803523d335c8fe52cf633391d47f7\",\n\t\t\tfalse, \"SHA-256-sess\", \"auth-int\", NULL, false,\n\t\t\t\"localhost:5060\", \"NULL as String :D\",\n\t\t\t\"retest\", \"sec_passed\",\n\t\t\t\"example.com/my/home/something\", PL(\"GET\"), NULL\n\t\t},\n\t};\n\n\tint err = 0;\n\tfor (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) {\n\t\tstruct httpauth_digest_chall_req *req = NULL;\n\t\tstruct httpauth_digest_enc_resp *resp = NULL;\n\t\tstruct httpauth_digest_chall chall;\n\t\tstruct mbuf *mb_req = NULL;\n\t\tstruct mbuf *mb_resp = NULL;\n\t\tstruct pl plreq;\n\t\tstruct pl plresp;\n\n\t\tmb_req = mbuf_alloc(512);\n\t\tmb_resp = mbuf_alloc(512);\n\t\tif (!mb_req || !mb_resp) {\n\t\t\terr = ENOMEM;\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" Could not allocate memory buffers \\n\", i);\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = httpauth_digest_chall_request_full(&req, testv[i].realm,\n\t\t\ttestv[i].domain, testv[i].etag, testv[i].opaque,\n\t\t\ttestv[i].stale, testv[i].algorithm, testv[i].qop,\n\t\t\ttestv[i].charset, testv[i].userhash);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" Could not generate request (%m)\\n\", i, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = mbuf_printf(mb_req, \"%H\",\n\t\t\thttpauth_digest_chall_req_print, req);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" Could not write digest request (%m)\",\n\t\t\t\ti, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\tmbuf_set_pos(mb_req, 0);\n\t\tpl_set_mbuf(&plreq, mb_req);\n\t\terr = httpauth_digest_challenge_decode(&chall, &plreq);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"[%d] Could not\"\n\t\t\t\t\" decode \\\"received\\\" challenge (%m)\",\n\t\t\t\ti, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = httpauth_digest_response_full(&resp, &chall,\n\t\t\t&testv[i].method, testv[i].uri, testv[i].user,\n\t\t\ttestv[i].passwd, testv[i].qop, testv[i].entitybody,\n\t\t\ttestv[i].charset, testv[i].userhash);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" Could not generate response (%m)\\n\", i, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = mbuf_printf(mb_resp, \"%H\",\n\t\t\thttpauth_digest_response_print, resp);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"[%d] Could not\"\n\t\t\t\t\" decode \\\"received\\\" response (%m)\\n\",\n\t\t\t\ti, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\tmbuf_set_pos(mb_resp, 0);\n\t\tpl_set_mbuf(&plresp, mb_resp);\n\t\terr = httpauth_digest_verify(req, &plresp,\n\t\t\t&testv[i].method, testv[i].etag,\n\t\t\ttestv[i].user, testv[i].passwd, testv[i].entitybody);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"[%d]\"\n\t\t\t\t\" Verification failed (%m)\\n\", i, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\tmem_deref(req);\n\t\tmem_deref(resp);\n\t\tmem_deref(mb_req);\n\t\tmem_deref(mb_resp);\n\t\tcontinue;\n\nout:\n\t\tmem_deref(req);\n\t\tmem_deref(resp);\n\t\tmem_deref(mb_req);\n\t\tmem_deref(mb_resp);\n\t\tbreak;\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/ice.c",
    "content": "/**\n * @file ice.c ICE Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_ice\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/*\n * Protocol testcode for 2 ICE agents. We setup 2 ICE agents A and B,\n * with only a basic host candidate. Gathering is done using a small\n * STUN Server running on localhost, SDP is exchanged with Offer/Answer.\n * Finally the connectivity checks are run and the result is verified.\n *\n *\n *                    .-------------.\n *                    | STUN Server |\n *                    '-------------'\n *              STUN /               \\ STUN\n *                  /                 \\\n *                 /                   \\\n *  .-------------.                     .-------------.\n *  | ICE-Agent A |---------------------| ICE-Agent B |\n *  '-------------'     Connectivity    '-------------'\n *                         checks\n *\n */\n\n\nstruct attrs {\n\tstruct attr {\n\t\tchar name[16];\n\t\tchar value[128];\n\t} attrv[16];\n\tunsigned attrc;\n};\n\nstruct agent {\n\tstruct icem *icem;\n\tstruct udp_sock *us;\n\tstruct sa laddr;\n\tstruct attrs attr_s;\n\tstruct attrs attr_m;\n\tstruct ice_test *it;  /* parent */\n\tstruct stunserver *stun;\n\tstruct turnserver *turn;\n\tchar name[16];\n\tuint8_t compid;\n\tbool offerer;\n\tbool use_turn;\n\tsize_t n_cand;\n\n\tchar lufrag[8];\n\tchar lpwd[32];\n\n\t/* results: */\n\tbool gathering_ok;\n\tbool conncheck_ok;\n\n\tstruct stun_ctrans *ct_gath;\n};\n\nstruct ice_test {\n\tstruct agent *a;\n\tstruct agent *b;\n\tstruct tmr tmr;\n\tint err;\n};\n\n\nstatic void icetest_check_gatherings(struct ice_test *it);\nstatic void icetest_check_connchecks(struct ice_test *it);\n\n\n/*\n * Test tools\n */\n\n\nstatic void complete_test(struct ice_test *it, int err)\n{\n\tit->err = err;\n\n#if 0\n\tre_printf(\"\\n\\x1b[32m%H\\x1b[;m\\n\", icem_debug, it->a->icem);\n\tre_printf(\"\\n\\x1b[36m%H\\x1b[;m\\n\", icem_debug, it->b->icem);\n#endif\n\n\tre_cancel();\n}\n\n\nstatic bool find_debug_string(struct icem *icem, const char *str)\n{\n\tchar buf[1024];\n\n\tif (re_snprintf(buf, sizeof(buf), \"%H\", icem_debug, icem) < 0)\n\t\treturn false;\n\n\treturn 0 == re_regex(buf, strlen(buf), str);\n}\n\n\nstatic int attr_add(struct attrs *attrs, const char *name,\n\t\t    const char *value, ...)\n{\n\tstruct attr *attr = &attrs->attrv[attrs->attrc];\n\tva_list ap;\n\tint r, err = 0;\n\n\tTEST_ASSERT(attrs->attrc <= RE_ARRAY_SIZE(attrs->attrv));\n\n\tTEST_ASSERT(strlen(name) < sizeof(attr->name));\n\tstr_ncpy(attr->name, name, sizeof(attr->name));\n\n\tif (value) {\n\t\tva_start(ap, value);\n\t\tr = re_vsnprintf(attr->value, sizeof(attr->value), value, ap);\n\t\tva_end(ap);\n\t\tTEST_ASSERT(r > 0);\n\t}\n\n\tattrs->attrc++;\n\n out:\n\treturn err;\n}\n\n\nstatic const char *attr_find(const struct attrs *attrs, const char *name)\n{\n\tunsigned i;\n\n\tif (!attrs || !name)\n\t\treturn NULL;\n\n\tfor (i=0; i<attrs->attrc; i++) {\n\t\tconst struct attr *attr = &attrs->attrv[i];\n\n\t\tif (0 == str_casecmp(attr->name, name))\n\t\t\treturn attr->value;\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * ICE Agent\n */\n\n\nstatic void agent_destructor(void *arg)\n{\n\tstruct agent *agent = arg;\n\n\tmem_deref(agent->icem);\n\tmem_deref(agent->us);\n\tmem_deref(agent->stun);\n\tmem_deref(agent->turn);\n}\n\n\nstatic struct agent *agent_other(struct agent *agent)\n{\n\tif (agent->it->a == agent)\n\t\treturn agent->it->b;\n\telse\n\t\treturn agent->it->a;\n}\n\n\nstatic int agent_encode_sdp(struct agent *ag)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tfor (le = icem_lcandl(ag->icem)->head; le; le = le->next) {\n\n\t\tstruct cand *cand = le->data;\n\n\t\terr = attr_add(&ag->attr_m, \"candidate\", \"%H\",\n\t\t\t       ice_cand_encode, cand);\n\t\tif (err)\n\t\t\tbreak;\n\t}\n\n\terr |= attr_add(&ag->attr_m, \"ice-ufrag\", ag->lufrag);\n\terr |= attr_add(&ag->attr_m, \"ice-pwd\", ag->lpwd);\n\n\treturn err;\n}\n\n\nstatic int agent_verify_outgoing_sdp(const struct agent *agent)\n{\n\tconst char *cand, *ufrag, *pwd;\n\tchar buf[1024];\n\tint err = 0;\n\n\tif (re_snprintf(buf, sizeof(buf),\n\t\t\t\"7f000001 %u UDP 2113929465 127.0.0.1 %u typ host\",\n\t\t\tagent->compid, sa_port(&agent->laddr)) < 0) {\n\t\treturn ENOMEM;\n\t}\n\tcand = attr_find(&agent->attr_m, \"candidate\");\n\tTEST_STRCMP(buf, str_len(buf), cand, str_len(cand));\n\n\tufrag = attr_find(&agent->attr_m, \"ice-ufrag\");\n\tpwd   = attr_find(&agent->attr_m, \"ice-pwd\");\n\tTEST_STRCMP(agent->lufrag, str_len(agent->lufrag),\n\t\t    ufrag, str_len(ufrag));\n\tTEST_STRCMP(agent->lpwd, str_len(agent->lpwd),\n\t\t    pwd, str_len(pwd));\n\n\tTEST_ASSERT(NULL == attr_find(&agent->attr_s, \"ice-lite\"));\n\n out:\n\treturn err;\n}\n\n\nstatic int agent_decode_sdp(struct agent *agent, struct agent *other)\n{\n\tunsigned i;\n\tint err = 0;\n\n\tfor (i=0; i<other->attr_s.attrc; i++) {\n\t\tstruct attr *attr = &other->attr_s.attrv[i];\n\t\terr = ice_sdp_decode(agent->icem, attr->name, attr->value);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tfor (i=0; i<other->attr_m.attrc; i++) {\n\t\tstruct attr *attr = &other->attr_m.attrv[i];\n\t\terr = icem_sdp_decode(agent->icem, attr->name, attr->value);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn err;\n}\n\n\nstatic int send_sdp(struct agent *agent)\n{\n\tint err;\n\n\t/* verify ICE states */\n\tTEST_ASSERT(!icem_mismatch(agent->icem));\n\n\t/* after gathering is complete we expect:\n\t *   1 local candidate\n\t *   0 remote candidates\n\t *   checklist and validlist is empty\n\t */\n\tTEST_EQUALS(agent->n_cand, list_count(icem_lcandl(agent->icem)));\n\tTEST_EQUALS(0, list_count(icem_rcandl(agent->icem)));\n\tTEST_EQUALS(0, list_count(icem_checkl(agent->icem)));\n\tTEST_EQUALS(0, list_count(icem_validl(agent->icem)));\n\n\tif (agent->use_turn) {\n\t\t/* verify that default candidate is the relayed address */\n\t\tTEST_SACMP(&agent->turn->relay,\n\t\t\t   icem_cand_default(agent->icem, agent->compid),\n\t\t\t   SA_ALL);\n\t}\n\telse {\n\t\t/* verify that default candidate is our local address */\n\t\tTEST_SACMP(&agent->laddr,\n\t\t\t   icem_cand_default(agent->icem, agent->compid),\n\t\t\t   SA_ALL);\n\t}\n\n\t/* we should not have selected candidate-pairs yet */\n\tTEST_ASSERT(!icem_selected_laddr(agent->icem, agent->compid));\n\n\terr = agent_encode_sdp(agent);\n\tTEST_ERR(err);\n\n\terr = agent_verify_outgoing_sdp(agent);\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n\n\nstatic void agent_gather_handler(int err, uint16_t scode, const char *reason,\n\t\t\t\t void *arg)\n{\n\tstruct agent *agent = arg;\n\n\tif (err)\n\t\tgoto out;\n\tif (scode) {\n\t\tDEBUG_WARNING(\"gathering failed: %u %s\\n\", scode, reason);\n\t\tcomplete_test(agent->it, EPROTO);\n\t\treturn;\n\t}\n\n\t/* Eliminate redundant local candidates */\n\ticem_cand_redund_elim(agent->icem);\n\n\terr = icem_comps_set_default_cand(agent->icem);\n\tif (err) {\n\t\tDEBUG_WARNING(\"ice: set default cands failed (%m)\\n\", err);\n\t\tgoto out;\n\t}\n\n\tagent->gathering_ok = true;\n\n\terr = send_sdp(agent);\n\tif (err)\n\t\tgoto out;\n\n\ticetest_check_gatherings(agent->it);\n\n\treturn;\n\n out:\n\tcomplete_test(agent->it, err);\n}\n\n\nstatic void stun_resp_handler(int err, uint16_t scode, const char *reason,\n\t\t\t      const struct stun_msg *msg, void *arg)\n{\n\tstruct agent *ag = arg;\n\tstruct stun_attr *attr;\n\tstruct ice_cand *lcand;\n\n\tif (err || scode > 0) {\n\t\tDEBUG_WARNING(\"STUN Request failed: %m\\n\", err);\n\t\tgoto out;\n\t}\n\n\t/* base candidate */\n\tlcand = icem_cand_find(icem_lcandl(ag->icem), ag->compid, NULL);\n\tif (!lcand)\n\t\tgoto out;\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);\n\tif (!attr)\n\t\tattr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR);\n\tif (!attr) {\n\t\tDEBUG_WARNING(\"no Mapped Address in Response\\n\");\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\n\terr = icem_lcand_add(ag->icem, icem_lcand_base(lcand),\n\t\t\t     ICE_CAND_TYPE_SRFLX,\n\t\t\t     &attr->v.sa);\n\n out:\n\tagent_gather_handler(err, scode, reason, ag);\n}\n\n\nstatic int icem_gather_srflx(struct agent *ag, const struct sa *srv)\n{\n\tint err;\n\n\terr = stun_request(&ag->ct_gath, icem_stun(ag->icem), IPPROTO_UDP,\n\t\t\t   ag->us, srv, 0,\n\t\t\t   STUN_METHOD_BINDING,\n\t\t\t   NULL, false, 0,\n\t\t\t   stun_resp_handler, ag, 1,\n\t\t\t   STUN_ATTR_SOFTWARE, stun_software);\n\tif (err)\n\t\treturn err;\n\n\treturn 0;\n}\n\n\nstatic void agent_connchk_handler(int err, bool update, void *arg)\n{\n\tstruct agent *agent = arg;\n\tstruct agent *other = agent_other(agent);\n\tconst struct sa *laddr, *raddr;\n\n\tif (err) {\n\t\tif (err != ENOMEM) {\n\t\t\tDEBUG_WARNING(\"%s: connectivity checks failed: %m\\n\",\n\t\t\t\t      agent->name, err);\n\t\t}\n\n\t\tcomplete_test(agent->it, err);\n\t\treturn;\n\t}\n\n\tif (agent->offerer ^ update) {\n\t\tDEBUG_WARNING(\"error in update flag\\n\");\n\t\tcomplete_test(agent->it, EPROTO);\n\t\treturn;\n\t}\n\n\tagent->conncheck_ok = true;\n\n\t/* verify ICE states */\n\tTEST_ASSERT(!icem_mismatch(agent->icem));\n\n\t/* after connectivity checks are complete we expect:\n\t *   1 local candidate\n\t *   1 remote candidates\n\t */\n\tTEST_EQUALS(agent->n_cand, list_count(icem_lcandl(agent->icem)));\n\tTEST_EQUALS(other->n_cand, list_count(icem_rcandl(agent->icem)));\n\tTEST_EQUALS(0, list_count(icem_checkl(agent->icem)));\n\tTEST_EQUALS(agent->n_cand * other->n_cand,\n\t\t    list_count(icem_validl(agent->icem)));\n\n\tladdr = icem_selected_laddr(agent->icem, agent->compid);\n\traddr = &agent_other(agent)->laddr;\n\n\tif (!sa_cmp(&agent->laddr, laddr, SA_ALL)) {\n\t\tDEBUG_WARNING(\"unexpected selected address: %J\\n\", laddr);\n\t\tcomplete_test(agent->it, EPROTO);\n\t\treturn;\n\t}\n\n\tif (!icem_verify_support(agent->icem, agent->compid, raddr)) {\n\t\tcomplete_test(agent->it, EPROTO);\n\t\treturn;\n\t}\n\n#if 0\n\t(void)re_printf(\"Agent %s -- Selected address: local=%J  remote=%J\\n\",\n\t\t\tagent->name, laddr, raddr);\n#endif\n\n\ticetest_check_connchecks(agent->it);\n\n out:\n\tif (err)\n\t\tcomplete_test(agent->it, err);\n}\n\n\nstatic int agent_alloc(struct agent **agentp, struct ice_test *it,\n\t\t       bool use_turn,\n\t\t       const char *name, uint8_t compid, bool offerer)\n{\n\tstruct agent *agent;\n\tenum ice_role lrole;\n\tuint64_t tiebrk;\n\tint err;\n\n\tagent = mem_zalloc(sizeof(*agent), agent_destructor);\n\tif (!agent)\n\t\treturn ENOMEM;\n\n\tre_snprintf(agent->lufrag, sizeof(agent->lufrag),\n\t\t    \"ufrag-%s\", name);\n\tre_snprintf(agent->lpwd, sizeof(agent->lpwd),\n\t\t    \"password-0123456789abcdef-%s\", name);\n\n\tagent->use_turn = use_turn;\n\tagent->it = it;\n\tstr_ncpy(agent->name, name, sizeof(agent->name));\n\tagent->compid = compid;\n\tagent->offerer = offerer;\n\n\tif (agent->use_turn) {\n\t\terr = turnserver_alloc(&agent->turn, \"127.0.0.1\");\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse {\n\t\terr = stunserver_alloc(&agent->stun, \"127.0.0.1\");\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = sa_set_str(&agent->laddr, \"127.0.0.1\", 0);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_listen(&agent->us, &agent->laddr, 0, 0);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_local_get(agent->us, &agent->laddr);\n\tif (err)\n\t\tgoto out;\n\n\tlrole = offerer ? ICE_ROLE_CONTROLLING : ICE_ROLE_CONTROLLED;\n\ttiebrk = offerer ? 2 : 1;\n\n\terr = icem_alloc(&agent->icem, lrole, IPPROTO_UDP, 0,\n\t\t\t tiebrk, agent->lufrag, agent->lpwd,\n\t\t\t agent_connchk_handler, agent);\n\tif (err)\n\t\tgoto out;\n\n#if 0\n\ticem_conf(agent->icem)->debug = true;\n#endif\n\n\tif (offerer) {\n\t\tTEST_EQUALS(ICE_ROLE_CONTROLLING,\n\t\t\t    icem_local_role(agent->icem));\n\t}\n\telse {\n\t\tTEST_EQUALS(ICE_ROLE_CONTROLLED,\n\t\t\t    icem_local_role(agent->icem));\n\t}\n\n\ticem_set_name(agent->icem, name);\n\n\terr = icem_comp_add(agent->icem, compid, agent->us);\n\tif (err)\n\t\tgoto out;\n\n\terr = icem_lcand_add_base(agent->icem, ICE_CAND_TYPE_HOST, compid, 0,\n\t\t\t\t  \"eth0\", ICE_TRANSP_UDP, &agent->laddr);\n\tTEST_ERR(err);\n\n\t++agent->n_cand;\n\n\t/* Start gathering now -- full mode only\n\t *\n\t * A lite implementation doesn't gather candidates;\n\t * it includes only host candidates for any media stream.\n\t */\n\n\tif (agent->use_turn) {\n\n\t\tre_printf(\"turn disabled\\n\");\n\t}\n\telse {\n\t\terr = icem_gather_srflx(agent,\n\t\t\t\t\t&agent->stun->laddr);\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(agent);\n\telse\n\t\t*agentp = agent;\n\n\treturn err;\n}\n\n\n/*\n * ICE Test\n */\n\n\nstatic int verify_after_sdp_exchange(struct agent *agent)\n{\n\tstruct agent *other = agent_other(agent);\n\tint err = 0;\n\n\t/* verify remote mode (after SDP exchange) */\n\tTEST_ASSERT(find_debug_string(agent->icem,\n\t\t\t\t      \"remote_mode=Full\"));\n\n\t/* verify ICE states */\n\tTEST_ASSERT(!icem_mismatch(agent->icem));\n\n\t/* after SDP was exchanged, we expect:\n\t *   1 local candidate\n\t *   1 remote candidates\n\t *   checklist and validlist is empty\n\t */\n\tTEST_EQUALS(agent->n_cand, list_count(icem_lcandl(agent->icem)));\n\tTEST_EQUALS(other->n_cand, list_count(icem_rcandl(agent->icem)));\n\tTEST_EQUALS(0, list_count(icem_checkl(agent->icem)));\n\tTEST_EQUALS(0, list_count(icem_validl(agent->icem)));\n\n\tif (agent->use_turn) {\n\t\t/* verify that default candidate is the relayed address */\n\t\tTEST_SACMP(&agent->turn->relay,\n\t\t\t   icem_cand_default(agent->icem, agent->compid),\n\t\t\t   SA_ALL);\n\t}\n\telse {\n\t\t/* verify that default candidate is our local address */\n\t\tTEST_SACMP(&agent->laddr,\n\t\t\t   icem_cand_default(agent->icem, agent->compid),\n\t\t\t   SA_ALL);\n\t}\n\n\t/* we should not have selected candidate-pairs yet */\n\tTEST_ASSERT(!icem_selected_laddr(agent->icem, agent->compid));\n\n out:\n\tif (err) {\n\t\tDEBUG_WARNING(\"agent %s failed\\n\", agent->name);\n\t}\n\treturn err;\n}\n\n\nstatic int agent_start(struct agent *agent)\n{\n\tstruct agent *other = agent_other(agent);\n\tint err = 0;\n\n\t/* verify that check-list is empty before we start */\n\tTEST_EQUALS(0, list_count(icem_checkl(agent->icem)));\n\tTEST_EQUALS(0, list_count(icem_validl(agent->icem)));\n\n\terr = icem_conncheck_start(agent->icem);\n\tif (err)\n\t\treturn err;\n\n\tTEST_EQUALS(agent->n_cand * other->n_cand,\n\t\t    list_count(icem_checkl(agent->icem)));\n\n\tTEST_EQUALS(0, list_count(icem_validl(agent->icem)));\n\n out:\n\treturn err;\n}\n\n\nstatic int agent_verify_completed(struct agent *agent)\n{\n\tstruct agent *other = agent_other(agent);\n\tuint32_t validc;\n\tint err = 0;\n\n\tTEST_ASSERT(agent->gathering_ok);\n\tTEST_ASSERT(agent->conncheck_ok);\n\n\tTEST_EQUALS(0, list_count(icem_checkl(agent->icem)));\n\tvalidc = list_count(icem_validl(agent->icem));\n\n\tTEST_EQUALS(agent->n_cand * other->n_cand, validc);\n\n\t/* verify state of STUN/TURN server */\n\tif (agent->use_turn) {\n\t\tTEST_ASSERT(agent->turn->n_allocate >= 1);\n\t\tTEST_ASSERT(agent->turn->n_chanbind >= 1);\n\t}\n\telse {\n\t\tTEST_ASSERT(agent->stun->nrecv >= 1);\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic void icetest_check_gatherings(struct ice_test *it)\n{\n\tint err;\n\n\tif (!it->a->gathering_ok)\n\t\treturn;\n\tif (!it->b->gathering_ok)\n\t\treturn;\n\n\t/* both gatherings are complete\n\t * exchange SDP and start conncheck\n\t */\n\n\terr = agent_decode_sdp(it->a, it->b);\n\tif (err)\n\t\tgoto out;\n\terr = agent_decode_sdp(it->b, it->a);\n\tif (err)\n\t\tgoto out;\n\n\terr = verify_after_sdp_exchange(it->a);\n\tif (err)\n\t\tgoto error;\n\terr = verify_after_sdp_exchange(it->b);\n\tif (err)\n\t\tgoto error;\n\n\terr  = agent_start(it->a);\n\tif (err)\n\t\tgoto out;\n\terr = agent_start(it->b);\n\tif (err)\n\t\tgoto out;\n\n\treturn;\n\n out:\n error:\n\tcomplete_test(it, err);\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct ice_test *it = arg;\n\n#if 0\n\tre_printf(\"\\n\\x1b[32m%H\\x1b[;m\\n\", icem_debug, it->a->icem);\n\tre_printf(\"\\n\\x1b[36m%H\\x1b[;m\\n\", icem_debug, it->b->icem);\n#endif\n\n\tcomplete_test(it, 0);\n}\n\n\nstatic void icetest_check_connchecks(struct ice_test *it)\n{\n\tif (!it->a->conncheck_ok)\n\t\treturn;\n\tif (!it->b->conncheck_ok)\n\t\treturn;\n\n\t/* start an async timer to let the socket traffic complete */\n\ttmr_start(&it->tmr, 1, tmr_handler, it);\n}\n\n\nstatic void icetest_destructor(void *arg)\n{\n\tstruct ice_test *it = arg;\n\n\ttmr_cancel(&it->tmr);\n\tmem_deref(it->b);\n\tmem_deref(it->a);\n}\n\n\nstatic int icetest_alloc(struct ice_test **itp,\n\t\t\t bool turn_a,\n\t\t\t bool turn_b)\n{\n\tstruct ice_test *it;\n\tint err;\n\n\tit = mem_zalloc(sizeof(*it), icetest_destructor);\n\tif (!it)\n\t\treturn ENOMEM;\n\n\terr = agent_alloc(&it->a, it, turn_a, \"A\", 7, true);\n\tif (err)\n\t\tgoto out;\n\n\terr = agent_alloc(&it->b, it, turn_b, \"B\", 7, false);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(it);\n\telse\n\t\t*itp = it;\n\n\treturn err;\n}\n\n\nstatic int _test_ice_loop(bool turn_a,\n\t\t\t  bool turn_b)\n{\n\tstruct ice_test *it = NULL;\n\tint err;\n\n\terr = icetest_alloc(&it, turn_a, turn_b);\n\tif (err)\n\t\tgoto out;\n\n\terr = re_main_timeout(300);\n\tif (err)\n\t\tgoto out;\n\n\t/* read back global errorcode */\n\tif (it->err) {\n\t\terr = it->err;\n\t\tgoto out;\n\t}\n\n\t/* now verify all results after test was finished */\n\terr  = agent_verify_completed(it->a);\n\terr |= agent_verify_completed(it->b);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(it);\n\n\treturn err;\n}\n\n\n/* also verify that these symbols are exported */\nstatic int test_ice_basic_candidate(void)\n{\n\tstatic const enum ice_cand_type typev[4] = {\n\t\tICE_CAND_TYPE_HOST,\n\t\tICE_CAND_TYPE_SRFLX,\n\t\tICE_CAND_TYPE_PRFLX,\n\t\tICE_CAND_TYPE_RELAY\n\t};\n\tunsigned i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(typev); i++) {\n\n\t\tconst char *name;\n\t\tenum ice_cand_type type;\n\n\t\tname = ice_cand_type2name(typev[i]);\n\t\tTEST_ASSERT(str_isset(name));\n\n\t\ttype = ice_cand_name2type(name);\n\t\tTEST_EQUALS(typev[i], type);\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic int test_ice_cand_prio(void)\n{\n\tint err = 0;\n\n\tTEST_EQUALS(0x7e0000ff, ice_cand_calc_prio(ICE_CAND_TYPE_HOST,  0, 1));\n\tTEST_EQUALS(0x640004ff, ice_cand_calc_prio(ICE_CAND_TYPE_SRFLX, 4, 1));\n\tTEST_EQUALS(0x6e0000fe, ice_cand_calc_prio(ICE_CAND_TYPE_PRFLX, 0, 2));\n\tTEST_EQUALS(0x000004fe, ice_cand_calc_prio(ICE_CAND_TYPE_RELAY, 4, 2));\n out:\n\treturn err;\n}\n\n\nstatic const char *testv[] = {\n\"1 1 UDP 2130706431 10.0.1.1 5000 typ host\",\n\"1 2 UDP 2130706431 10.0.1.1 5001 typ host\",\n\"2 1 UDP 1694498815 192.0.2.3 5000 typ srflx raddr 10.0.1.1 rport 8998\",\n\"2 2 UDP 1694498815 192.0.2.3 5001 typ srflx raddr 10.0.1.1 rport 8998\",\n\n\"1 1 TCP 2128609279 10.0.1.1 9 typ host tcptype active\",\n\"2 1 TCP 2124414975 10.0.1.1 8998 typ host tcptype passive\",\n\"3 1 TCP 2120220671 10.0.1.1 8999 typ host tcptype so\",\n\"4 1 TCP 1688207359 192.0.2.3 9 typ srflx raddr\"\n  \" 10.0.1.1 rport 9 tcptype active\",\n\"5 1 TCP 1684013055 192.0.2.3 45664 typ srflx raddr\"\n  \" 10.0.1.1 rport 8998 tcptype passive\",\n\"6 1 TCP 1692401663 192.0.2.3 45687 typ srflx raddr\"\n  \" 10.0.1.1 rport 8999 tcptype so\",\n\n\"H76f0ae12 1 UDP 2130706431 fda8:de2d:e95f:4811::1 6054 typ host\",\n\n\"3113280040 1 UDP 2122255103 2001:aaaa:5ef5:79fb:1847:2c0d:a230:23ab 53329\"\n  \" typ host\",\n\n};\n\n\nstatic int test_ice_cand_attribute(void)\n{\n\tunsigned i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tstruct ice_cand_attr cand;\n\t\tchar buf[256];\n\t\tint n;\n\n\t\terr = ice_cand_attr_decode(&cand, testv[i]);\n\t\tTEST_ERR(err);\n\n\t\t/* sanity-check of decoded attribute */\n\t\tTEST_ASSERT(str_isset(cand.foundation));\n\t\tTEST_ASSERT(1 <= cand.compid && cand.compid <= 2);\n\t\tTEST_ASSERT(cand.proto == IPPROTO_UDP ||\n\t\t\t    cand.proto == IPPROTO_TCP);\n\t\tTEST_ASSERT(cand.prio > 0);\n\t\tTEST_ASSERT(sa_isset(&cand.addr, SA_ALL));\n\n\t\tn = re_snprintf(buf, sizeof(buf), \"%H\",\n\t\t\t\tice_cand_attr_encode, &cand);\n\t\tif (n < 0)\n\t\t\treturn ENOMEM;\n\n\t\tTEST_STRCMP(testv[i], strlen(testv[i]), buf, (unsigned)n);\n\t}\n\n out:\n\treturn err;\n}\n\n\nint test_ice_cand(void)\n{\n\tint err = 0;\n\n\terr = test_ice_basic_candidate();\n\tTEST_ERR(err);\n\n\terr = test_ice_cand_prio();\n\tTEST_ERR(err);\n\n\terr = test_ice_cand_attribute();\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\nint test_ice_loop(void)\n{\n\treturn _test_ice_loop(false, false);\n}\n"
  },
  {
    "path": "test/json.c",
    "content": "/**\n * @file json.c Testcode for JSON parser\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n#include <stdlib.h>\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"json\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tDICT_BSIZE = 32,\n\tMAX_LEVELS =  8,\n};\n\n\nstatic int test_json_basic_parser(void)\n{\n\tstatic const char *str =\n\t\t\"{\"\n\t\t\"  \\\"name\\\"      : \\\"Herr Alfred\\\",\"\n\t\t\"  \\\"height\\\"    : 1.86,\"\n\t\t\"  \\\"weight\\\"    : 90,\"\n\t\t\"  \\\"has_depth\\\" : false,\"\n\t\t\"  \\\"has_money\\\" : true,\"\n\t\t\"  \\\"array\\\"     : [1, 2, 3, \\\"x\\\", \\\"y\\\"],\"\n\t\t\"  \\\"negative\\\"  : -42,\"\n\t\t\"  \\\"negativef\\\" : -0.0042,\"\n\t\t\"  \\\"expo_pos\\\"  : 2.0E3,\"\n\t\t\"  \\\"expo_neg\\\"  : 2.0E-3,\"\n\t\t\"  \\\"foo\\x1d\\\"   : \\\"foo\\x1d\\\",\"\n\t\t\"  \\\"object\\\"    : {\"\n\t\t\"    \\\"one\\\" : 1,\"\n\t\t\"    \\\"two\\\" : 2\"\n\t\t\"  }\"\n\t\t\"}\";\n\n\tstruct odict *dict = NULL, *sub;\n\tconst struct odict_entry *o, *e;\n\tint err;\n\n\terr = json_decode_odict(&dict, DICT_BSIZE,\n\t\t\t\tstr, strlen(str), MAX_LEVELS);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(12U, odict_count(dict, false));\n\tTEST_EQUALS(17U, odict_count(dict, true));\n\n\to = odict_lookup(dict, \"name\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_STRING, odict_entry_type(o));\n\tTEST_STRCMP(\"Herr Alfred\", 11, odict_entry_str(o),\n\t\t    str_len(odict_entry_str(o)));\n\n\to = odict_lookup(dict, \"height\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_DOUBLE, odict_entry_type(o));\n\tTEST_ASSERT(odict_entry_dbl(o) > .0);\n\n\to = odict_lookup(dict, \"weight\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_INT, odict_entry_type(o));\n\tTEST_EQUALS(90, odict_entry_int(o));\n\n\to = odict_lookup(dict, \"has_depth\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_BOOL, odict_entry_type(o));\n\tTEST_ASSERT(!odict_entry_boolean(o));\n\n\to = odict_lookup(dict, \"has_money\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_BOOL, odict_entry_type(o));\n\tTEST_ASSERT(odict_entry_boolean(o));\n\n\to = odict_lookup(dict, \"array\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_ARRAY, odict_entry_type(o));\n\tTEST_EQUALS(5U, odict_count(odict_entry_array(o), false));\n\te = odict_get_type(odict_entry_array(o), ODICT_INT, \"0\");\n\tTEST_EQUALS(1, odict_entry_int(e));\n\te = odict_get_type(odict_entry_array(o), ODICT_INT, \"1\");\n\tTEST_EQUALS(2, odict_entry_int(e));\n\te = odict_get_type(odict_entry_array(o), ODICT_INT, \"2\");\n\tTEST_EQUALS(3, odict_entry_int(e));\n\n\to = odict_lookup(dict, \"negative\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_INT, odict_entry_type(o));\n\tTEST_EQUALS(-42, odict_entry_int(o));\n\n\to = odict_lookup(dict, \"negativef\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_DOUBLE, odict_entry_type(o));\n\tTEST_ASSERT(odict_entry_dbl(o) < .0);\n\n\to = odict_lookup(dict, \"expo_pos\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_DOUBLE, odict_entry_type(o));\n\tTEST_ASSERT(odict_entry_dbl(o) > .0);\n\n\to = odict_lookup(dict, \"expo_neg\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_DOUBLE, odict_entry_type(o));\n\tTEST_ASSERT(odict_entry_dbl(o) > .0);\n\n\to = odict_lookup(dict, \"foo\\x1d\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_STRING, odict_entry_type(o));\n\tTEST_STRCMP(\"foo\\x1d\", 4, odict_entry_str(o),\n\t\t    str_len(odict_entry_str(o)));\n\n\t/* object */\n\to = odict_lookup(dict, \"object\");\n\tTEST_ASSERT(o != NULL);\n\tTEST_EQUALS(ODICT_OBJECT, odict_entry_type(o));\n\tsub = odict_entry_object(o);\n\te = odict_lookup(sub, \"one\");\n\tTEST_ASSERT(e != NULL);\n\tTEST_EQUALS(ODICT_INT, odict_entry_type(e));\n\tTEST_EQUALS(1, odict_entry_int(e));\n\te = odict_lookup(sub, \"two\");\n\tTEST_ASSERT(e != NULL);\n\tTEST_EQUALS(ODICT_INT, odict_entry_type(e));\n\tTEST_EQUALS(2, odict_entry_int(e));\n\n\t/* non-existing entry */\n\to = odict_lookup(dict, \"not-found\");\n\tTEST_ASSERT(o == NULL);\n\n out:\n\tmem_deref(dict);\n\treturn err;\n}\n\n\n/* verify a bunch of JSON messages */\nstatic int test_json_verify_decode(void)\n{\n\tstatic const struct test {\n\t\tunsigned num;\n\t\tunsigned num_total;\n\t\tchar *str;\n\t} testv[] = {\n\t\t{\n\t\t\t0,\n\t\t\t0,\n\t\t\t\"{}\"\n\t\t},\n\t\t{\n\t\t\t1,\n\t\t\t1,\n\t\t\t\"\\\"yyyyyyyyyy\\\"\"\n\t\t},\n\t\t{\n\t\t\t1,\n\t\t\t1,\n\t\t\t\"42\"\n\t\t},\n\t\t{\n\t\t\t1,\n\t\t\t1,\n\t\t\t\"1.30142114406914976E17\"\n\t\t},\n\t\t{\n\t\t\t1,\n\t\t\t1,\n\t\t\t\"true\"\n\t\t},\n\t\t{\n\t\t\t1,\n\t\t\t1,\n\t\t\t\"{\\\"a\\\":1}\"\n\t\t},\n\t\t{\n\t\t\t2,\n\t\t\t2,\n\t\t\t\"{\\\"a\\\":1,\\\"b\\\":2}\"\n\t\t},\n\t\t{\n\t\t\t5,\n\t\t\t5,\n\t\t\t\"{\"\n\t\t\t\"  \\\"aaaaa\\\"  :  \\\"yyyyyyyyyy\\\",\"\n\t\t\t\"  \\\"bbbbb\\\"  :  \\\"yyyyyyyyyy\\\",\"\n\t\t\t\"  \\\"ccccc\\\"  :  \\\"yyyyyyyyyy\\\",\"\n\t\t\t\"  \\\"ddddd\\\"  :  \\\"yyyyyyyyyy\\\",\"\n\t\t\t\"  \\\"eeeee\\\"  :  \\\"yyyyyyyyyy\\\"\"\n\t\t\t\"}\"\n\t\t},\n\t\t{\n\t\t\t2,\n\t\t\t2,\n\t\t\t\"{\\\"num\\\":42,\\\"str\\\":\\\"hei du\\\"}\"\n\t\t},\n\t\t{\n\t\t\t6,\n\t\t\t6,\n\t\t\t\"{\"\n\t\t\t\"  \\\"zero\\\"  : 0,\"\n\t\t\t\"  \\\"one\\\"   : 1,\"\n\t\t\t\"  \\\"false\\\" : 0,\"\n\t\t\t\"  \\\"true\\\"  : 1,\"\n\t\t\t\"  \\\"0\\\"     : false,\"\n\t\t\t\"  \\\"1\\\"     : true\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t/* arrays */\n\t\t{\n\t\t\t2,\n\t\t\t8,\n\t\t\t\"{\"\n\t\t\t\"  \\\"array\\\" : [1,2,3,4,5],\"\n\t\t\t\"  \\\"arraz\\\" : [\\\"ole\\\", \\\"dole\\\", \\\"doffen\\\"]\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t{\n\t\t\t1,\n\t\t\t0,\n\t\t\t\"{\"\n\t\t\t\"  \\\"empty_array\\\" : []\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t{\n\t\t\t1,\n\t\t\t1,\n\t\t\t\"{\"\n\t\t\t\"  \\\"array_with_object\\\" : [ { \\\"key\\\" : 42 } ]\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t{\n\t\t\t1,\n\t\t\t3,\n\t\t\t\"{\"\n\t\t\t\"  \\\"array_with_bool_and_null\\\" : [\"\n\t\t\t\"    true, false, null\"\n\t\t\t\"  ]\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t{\n\t\t\t1,\n\t\t\t30,\n\t\t\t\"{\"\n\t\t\t\"  \\\"array\\\" : [\"\n\t\t\t\"     0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\"\n\t\t\t\"    10,11,12,13,14,15,16,17,18,19,\"\n\t\t\t\"    20,21,22,23,24,25,26,27,28,29\"\n\t\t\t\"  ]\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t/* nested arrays */\n\t\t{\n\t\t\t1,\n\t\t\t4,\n\t\t\t\"{\"\n\t\t\t\"   \\\"array\\\": [\"\n\t\t\t\"     1,\"\n\t\t\t\"     2,\"\n\t\t\t\"     [\"\n\t\t\t\"       \\\"[][][][\\\",\"\n\t\t\t\"       \\\"][][][\\\"\"\n\t\t\t\"     ]\"\n\t\t\t\"   ]\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t/* null */\n\t\t{\n\t\t\t1,\n\t\t\t1,\n\t\t\t\"{\"\n\t\t\t\"   \\\"empty\\\": null\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t/* escaped string */\n\t\t{\n\t\t\t2,\n\t\t\t2,\n\t\t\t\"{\"\n\t\t\t\"  \\\"string1\\\": \\\"\\\\\\\"\\\\/\\\\b\\\\f\\\\n\\\\r\\\\t\\\", \"\n\t\t\t\"  \\\"string2\\\": \\\"\\\\\\\"/\\\\b\\\\f\\\\n\\\\r\\\\t\\\"\"\n\t\t\t\" }\"\n\t\t},\n\n\t\t{\n\t\t\t2,\n\t\t\t2,\n\t\t\t\"{\"\n\t\t\t\"    \\\"string\\\"  : \\\"\\\\r\\\\n\\\" , \"\n\t\t\t\"    \\\"boolean\\\" : true\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t{\n\t\t\t2,\n\t\t\t2,\n\t\t\t\"{\"\n\t\t\t\"    \\\"string\\\"  : \\\"a\\\\r\\\\n\\\" , \"\n\t\t\t\"    \\\"null\\\"    : null\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t/* key with escaped string */\n\t\t{\n\t\t\t1,\n\t\t\t1,\n\t\t\t\"{ \\\"\\\\\\\"\\\\b\\\\f\\\\n\\\\r\\\\t\\\":\\\"value\\\"}\"\n\t\t},\n\n\t\t{\n\t\t\t2,\n\t\t\t3,\n\t\t\t\"{\"\n\t\t\t\"  \\\"type\\\": \\\"object\\\",\"\n\t\t\t\"  \\\"properties\\\": {\"\n\t\t\t\"    \\\"id\\\": {\"\n\t\t\t\"      \\\"description\\\": \\\"The unique identifier\\\",\"\n\t\t\t\"      \\\"type\\\": \\\"integer\\\"\"\n\t\t\t\"    }\"\n\t\t\t\"  }\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t{\n\t\t\t1,\n\t\t\t2,\n\t\t\t\"{\"\n\t\t\t\"  \\\"a\\\": {\"\n\t\t\t\"    \\\"b\\\": {\"\n\t\t\t\"      \\\"c\\\": {\"\n\t\t\t\"        \\\"d\\\": {\"\n\t\t\t\"          \\\"e\\\": {\"\n\t\t\t\"            \\\"f\\\": {\"\n\t\t\t\"              \\\"string\\\": \\\"hei hei\\\",\"\n\t\t\t\"              \\\"number\\\": 4242\"\n\t\t\t\"            }\"\n\t\t\t\"          }\"\n\t\t\t\"        }\"\n\t\t\t\"      }\"\n\t\t\t\"    }\"\n\t\t\t\"  }\"\n\t\t\t\"}\"\n\t\t},\n\n\t\t/* unicode */\n\t\t{\n\t\t\t1,\n\t\t\t1,\n\t\t\t\"{  \\\"\\\\u0001key\\\": \\\"val\\\\u0002\\\" }\"\n\t\t},\n\n\t\t/* numbers */\n\n\t\t{\n\t\t\t2,\n\t\t\t2,\n\t\t\t\"{  \\\"start\\\":  1372701600000,  \"\n\t\t\t\"   \\\"stop\\\":  -1372701600000  }\"\n\t\t},\n\n\t\t{\n\t\t\t4,\n\t\t\t4,\n\t\t\t\"{\"\n\t\t\t\"    \\\"a\\\":  1.30142114406914976E17, \"\n\t\t\t\"    \\\"b\\\":  1.7555215491128452E-19, \"\n\t\t\t\"    \\\"c\\\": -4.57371918053102129E18, \"\n\t\t\t\"    \\\"d\\\": -1.3014211440691497E-17  \"\n\t\t\t\"}\"\n\t\t},\n\n\t\t/* array with objects (legal JSON) */\n\t\t{\n\t\t\t2,\n\t\t\t4,\n\t\t\t\"[\"\n\t\t\t\"  {\"\n\t\t\t\"    \\\"foo\\\" : 111,\"\n\t\t\t\"    \\\"bar\\\" : 111\"\n\t\t\t\"  },\"\n\t\t\t\"  {\"\n\t\t\t\"    \\\"foo\\\" : 222,\"\n\t\t\t\"    \\\"bar\\\" : 222\"\n\t\t\t\"  }\"\n\t\t\t\"]\"\n\t\t}\n\t};\n\tstruct odict *dict = NULL, *dict2 = NULL;\n\tstruct mbuf *mb_enc = NULL;\n\tunsigned i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tconst struct test *t = &testv[i];\n\n\t\t/* check with native JSON decoder */\n\t\terr = json_decode_odict(&dict, DICT_BSIZE,\n\t\t\t\t\tt->str, str_len(t->str), MAX_LEVELS);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tTEST_EQUALS(t->num,       odict_count(dict, false));\n\t\tTEST_EQUALS(t->num_total, odict_count(dict, true));\n\n\t\tmb_enc = mbuf_alloc(1024);\n\t\tif (!mb_enc) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\t/* verify that the JSON object can be encoded */\n\t\terr = mbuf_printf(mb_enc, \"%H\", json_encode_odict, dict);\n\t\tTEST_ERR(err);\n\n\t\t/* decode it again */\n\t\terr = json_decode_odict(&dict2, DICT_BSIZE,\n\t\t\t\t\t(void *)mb_enc->buf, mb_enc->end,\n\t\t\t\t\tMAX_LEVELS);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\n\t\tTEST_ASSERT(odict_compare(dict, dict2, false));\n\n\t\tdict = mem_deref(dict);\n\t\tdict2 = mem_deref(dict2);\n\t\tmb_enc = mem_deref(mb_enc);\n\t}\n\n out:\n\tmem_deref(dict2);\n\tmem_deref(dict);\n\tmem_deref(mb_enc);\n\n\treturn err;\n}\n\n\nstatic int test_json_exponent(void)\n{\n\tstatic const char *str =\n\t\t\"{\"\n\t\t\"  \\\"exponents\\\" : [1e2, 1e-2, 9E18, -9E18]\"\n\t\t\"}\";\n\tstruct odict *dict = NULL;\n\tconst struct odict_entry *arr, *e;\n\tstatic const double values[] = {\n\t\t100.0,\n\t\t0.01,\n\t\t9000000000000000000.0,\n\t\t-9000000000000000000.0,\n\t};\n\tstruct le *le;\n\tunsigned i;\n\tint err;\n\n\terr = json_decode_odict(&dict, DICT_BSIZE,\n\t\t\t\tstr, strlen(str), MAX_LEVELS);\n\tif (err)\n\t\tgoto out;\n\n\tarr = odict_lookup(dict, \"exponents\");\n\n\tTEST_EQUALS(RE_ARRAY_SIZE(values),\n\t\t    odict_count(odict_entry_array(arr), false));\n\n\tfor (le = list_head(&odict_entry_array(arr)->lst), i = 0; le;\n\t     le = le->next, ++i) {\n\n\t\te = le->data;\n\n\t\tTEST_ASSERT(e != NULL);\n\t\tTEST_EQUALS(ODICT_DOUBLE, odict_entry_type(e));\n\t\tTEST_EQUALS( values[i], odict_entry_dbl(e));\n\t}\n\n out:\n\tmem_deref(dict);\n\treturn err;\n}\n\n\nint test_json(void)\n{\n\tint err = 0;\n\n\terr = test_json_exponent();\n\tTEST_ERR(err);\n\n\terr = test_json_basic_parser();\n\tTEST_ERR(err);\n\n\terr = test_json_verify_decode();\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\n/* check a bunch of bad JSON messages, unparsable */\nint test_json_bad(void)\n{\n\tstatic const struct test {\n\t\tint err;\n\t\tchar *str;\n\t} testv[] = {\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"}\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"{]\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"{[}\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"]\"\n\t\t},\n\n\t\t/* boolean values */\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"{ \\\"short_true\\\" : t }\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"{ \\\"short_false\\\" : f }\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"{ \\\"short_null\\\" : n }\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"{ \\\"a\\\" : frue }\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"{ \\\"a\\\" : talse }\"\n\t\t},\n\n\t\t/* string values */\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"{ \\\"invalid_unicode\\\" : \\\"\\\\u000g\\\" }\"\n\t\t},\n\n\t\t/* corrupt data */\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"10t[3e9e66\\\"49\\\"[[72677:[f58{.fn}0{59\\\":8\\\"e}[\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"1t34:{{:f{1.n{\\\"\\\"n8[0f7e}:53e6{7:28:{n{00:7\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"}3][ne5}.5n41ef96f99\\\":n47{9[n[1:0f5\\\"}985}{\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"}3][ne5}.5n41ef96f99\\\":n47{9[n[1:0f5\\\"}985}{\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"8n0}3:28e27}8]75:[:e47968e96n[:2f]n1:]n2[t\"\n\t\t},\n\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"{\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"[\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"{ \\\"broken_key }\"\n\t\t},\n\t\t{\n\t\t\tEBADMSG,\n\t\t\t\"{ \\\"key\\\" : \\\"broken_value }\"\n\t\t},\n\t\t{\n\t\t\t0,\n\t\t\t\"\\\"hei\\\"\"\n\t\t},\n\t\t{\n\t\t\t0,\n\t\t\t\"123\"\n\t\t},\n\t};\n\tstruct odict *dict = NULL;\n\tunsigned i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tconst struct test *t = &testv[i];\n\t\tint e;\n\n\t\t/* check with native JSON decoder */\n\t\te = json_decode_odict(&dict, DICT_BSIZE,\n\t\t\t\t      t->str, str_len(t->str), MAX_LEVELS);\n\t\tif (e == ENOMEM)\n\t\t\tbreak;\n\t\tTEST_EQUALS(t->err, e);\n\n\t\tif (e) {\n\t\t\tTEST_ASSERT(dict == NULL);\n\t\t}\n\t\telse {\n\t\t\tTEST_ASSERT(dict != NULL);\n\t\t}\n\n\t\tdict = mem_deref(dict);\n\t}\n\n out:\n\tmem_deref(dict);\n\treturn err;\n}\n\n\nstatic int test_json_file_parse(const char *filename)\n{\n\tstruct mbuf *mb_ref = NULL, *mb_enc = NULL;\n\tstruct odict *dict = NULL, *dict2 = NULL;\n\tchar path[256];\n\tunsigned max_levels = 480;\n\tint err;\n\n\tmb_ref = mbuf_alloc(1024);\n\tmb_enc = mbuf_alloc(1024);\n\tif (!mb_ref || !mb_enc) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tre_snprintf(path, sizeof(path), \"%s/%s\", test_datapath(), filename);\n\n\terr = test_load_file(mb_ref, path);\n\tif (err)\n\t\tgoto out;\n\n\terr = json_decode_odict(&dict, DICT_BSIZE,\n\t\t\t\t(void *)mb_ref->buf, mb_ref->end,\n\t\t\t\tmax_levels);\n\tif (err) {\n\t\tgoto out;\n\t}\n\n\tTEST_ASSERT(dict != NULL);\n\tTEST_ASSERT(odict_count(dict, true) > 0);\n\n#if 0\n\tre_printf(\"%s: JSON parsed OK (%zu elements)\\n\",\n\t\t  filename, odict_count(dict, true));\n#endif\n\n\t/* verify that JSON object can be encoded */\n\terr = mbuf_printf(mb_enc, \"%H\", json_encode_odict, dict);\n\tTEST_ERR(err);\n\n\t/* decode it again */\n\terr = json_decode_odict(&dict2, DICT_BSIZE,\n\t\t\t\t(void *)mb_enc->buf, mb_enc->end, max_levels);\n\tif (err) {\n\t\tgoto out;\n\t}\n\n\tTEST_ASSERT(odict_compare(dict, dict2, false));\n\n out:\n\tmem_deref(dict2);\n\tmem_deref(dict);\n\tmem_deref(mb_enc);\n\tmem_deref(mb_ref);\n\treturn err;\n}\n\n\nint test_json_file(void)\n{\n\tconst char *files[] = {\n\t\t\"fstab.json\",\n\t\t\"menu.json\",\n\t\t\"rfc7159.json\",\n\t\t\"webapp.json\",\n\t\t\"widget.json\",\n\t};\n\tunsigned i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(files); i++) {\n\t\terr = test_json_file_parse(files[i]);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn err;\n}\n\n\nint test_json_unicode(void)\n{\n\tstruct odict *dict=0, *dict2=0;\n\tstatic const char *key = \"nul\\x01key\";\n\tchar buf[1024];\n\tint err = 0;\n\n\terr = odict_alloc(&dict, 32);\n\tif (err)\n\t\tgoto out;\n\n\terr = odict_entry_add(dict, key, ODICT_STRING, \"foo\\x1d\");\n\tif (err)\n\t\tgoto out;\n\n\tre_snprintf(buf, sizeof(buf), \"%H\", json_encode_odict, dict);\n\n\terr = json_decode_odict(&dict2, DICT_BSIZE,\n\t\t\t\tbuf, str_len(buf), MAX_LEVELS);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_ASSERT(odict_compare(dict, dict2, false));\n\n out:\n\tmem_deref(dict2);\n\tmem_deref(dict);\n\treturn err;\n}\n\n\nstatic int verify_array(const struct odict *arr, unsigned num)\n{\n\tconst struct odict_entry *e;\n\tstruct le *le;\n\tchar buf[32];\n\tunsigned i;\n\tint err = 0;\n\n\tfor (i=0; i<num; i++) {\n\n\t\tre_snprintf(buf, sizeof(buf), \"%u\", i);\n\n\t\te = odict_lookup(arr, buf);\n\t\tTEST_ASSERT(e != NULL);\n\t}\n\n\tfor (le = arr->lst.head, i=0; le; le = le->next, ++i) {\n\n\t\tstruct odict_entry *ae = le->data;\n\t\tunsigned key;\n\n\t\tkey = atoi(odict_entry_key(ae));\n\n\t\tTEST_EQUALS(i, key);\n\t}\n\n\t/* should not exist */\n\tre_snprintf(buf, sizeof(buf), \"%u\", num);\n\te = odict_lookup(arr, buf);\n\tTEST_ASSERT(e == NULL);\n\n out:\n\treturn err;\n}\n\n\nint test_json_array(void)\n{\n\tstatic const char *str =\n\t\t\"{\"\n\t\t\"  \\\"array1\\\" : [0,1,2,3,4,5,6,7],\"\n\t\t\"  \\\"array2\\\" : [\\\"ole\\\",\\\"dole\\\",\\\"doffen\\\"],\"\n\t\t\"  \\\"array3\\\" : [ {\\\"x\\\":0}, {\\\"x\\\":0}, {\\\"x\\\":0} ],\"\n\t\t\"  \\\"object\\\" : {\"\n\t\t\"    \\\"array4\\\" : [0,1,2,3]\"\n\t\t\"  }\"\n\t\t\"}\";\n\n\tstruct odict *dict = NULL, *obj;\n\tint err;\n\n\terr = json_decode_odict(&dict, DICT_BSIZE,\n\t\t\t\tstr, strlen(str), MAX_LEVELS);\n\tif (err)\n\t\tgoto out;\n\n\terr |= verify_array(odict_get_array(dict, \"array1\"), 8);\n\terr |= verify_array(odict_get_array(dict, \"array2\"), 3);\n\terr |= verify_array(odict_get_array(dict, \"array3\"), 3);\n\tif (err)\n\t\tgoto out;\n\n\tobj = odict_get_object(dict, \"object\");\n\tTEST_ASSERT(obj != NULL);\n\terr |= verify_array(odict_get_array(obj, \"array4\"), 4);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(dict);\n\treturn err;\n}\n"
  },
  {
    "path": "test/list.c",
    "content": "/**\n * @file list.c Linked-lists Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <assert.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"testlist\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstruct node {\n\tstruct le le;\n\tstruct le le2;\n\tint value;\n};\n\nint test_list(void)\n{\n\tstruct node node1 = {.value = 1}, node2 = {.value = 2};\n\tstruct list list;\n\tint err = EINVAL;\n\n\tlist_init(&list);\n\n\t/* Test empty list */\n\tTEST_EQUALS(0, list_count(&list));\n\n\t/* Test with one node */\n\tlist_append(&list, &node1.le, &node1);\n\tTEST_EQUALS(1, list_count(&list));\n\n\tlist_unlink(&node1.le);\n\n\tTEST_EQUALS(0, list_count(&list));\n\n\t/* Test with two nodes */\n\tlist_append(&list, &node1.le, &node1);\n\tlist_append(&list, &node2.le, &node2);\n\tTEST_EQUALS(2, list_count(&list));\n\n\tlist_unlink(&node1.le);\n\n\tTEST_EQUALS(1, list_count(&list));\n\n\tlist_unlink(&node2.le);\n\n\t/* Test empty list */\n\tTEST_EQUALS(0, list_count(&list));\n\n\tlist_append(&list, &node1.le, &node1);\n\tlist_append(&list, &node2.le, &node2);\n\n\tstruct le *le;\n\tint i = 0;\n\tLIST_FOREACH(&list, le) {\n\t\tstruct node *n = list_ledata(le);\n\t\t++i;\n\t\tTEST_EQUALS(i, n->value);\n\t}\n\n\tstruct le *tmp;\n\ti = 0;\n\tLIST_FOREACH_SAFE(&list, le, tmp) {\n\t\tstruct node *n = list_ledata(le);\n\t\t++i;\n\t\tTEST_EQUALS(i, n->value);\n\t\tlist_unlink(le);\n\t}\n\n\t/* Test empty list */\n\tTEST_EQUALS(0, list_count(&list));\n\n\terr = 0;\n\n out:\n\treturn err;\n}\n\n\nstatic void node_destructor(void *arg)\n{\n\tstruct node *node = arg;\n\n\tif (node->le.prev || node->le.next || node->le.list || node->le.data) {\n\t\tDEBUG_WARNING(\"le: prev=%p next=%p data=%p\\n\",\n\t\t\t      node->le.prev, node->le.next, node->le.data);\n\t}\n\n\tlist_unlink(&node->le);\n}\n\n\n/**\n * Test linked list with external reference to objects\n */\nint test_list_ref(void)\n{\n\tstruct list list;\n\tstruct node *node, *node2;\n\tint err = 0;\n\n\tlist_init(&list);\n\n\tnode = mem_zalloc(sizeof(*node), node_destructor);\n\tnode2 = mem_zalloc(sizeof(*node2), node_destructor);\n\tif (!node || !node2) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tmem_ref(node);\n\n\tlist_append(&list, &node->le, node);\n\tlist_append(&list, &node2->le, node2);\n\n out:\n\tlist_flush(&list);\n\tmemset(&list, 0xa5, sizeof(list));  /* mark as deleted */\n\n\t/* note: done after list_flush() */\n\tmem_deref(node);\n\n\treturn err;\n}\n\n\nstatic bool sort_handler1(struct le *le1, struct le *le2, void *arg)\n{\n\tstruct node *node1 = le1->data;\n\tstruct node *node2 = le2->data;\n\t(void)arg;\n\n\t/* NOTE: important to use less than OR equal to, otherwise\n\t   the list_sort function may be stuck in a loop */\n\treturn node1->value <= node2->value;\n}\n\n\nstatic bool sort_handler2(struct le *le1, struct le *le2, void *arg)\n{\n\tstruct node *node1 = le1->data;\n\tstruct node *node2 = le2->data;\n\t(void)arg;\n\n\t/* NOTE: important to use greater than OR equal to, otherwise\n\t   the list_sort function may be stuck in a loop */\n\treturn node1->value >= node2->value;\n}\n\n\n#define NUM_ELEMENTS 100\nstatic int test_sort(bool sorted)\n{\n\tstruct list lst_1;\n\tstruct list lst_2;\n\tstruct le *le;\n\tint prev_value = 0;\n\tbool prev_value_set = false;\n\tunsigned i;\n\tunsigned value_counter = 7;\n\tint err = 0;\n\n\tlist_init(&lst_1);\n\tlist_init(&lst_2);\n\n\t/* add many elements with a random value */\n\tfor (i=0; i<NUM_ELEMENTS; i++) {\n\n\t\tstruct node *node;\n\n\t\tnode = mem_zalloc(sizeof(*node), node_destructor);\n\t\tif (!node) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\tnode->value = -50 + (value_counter % 100);\n\t\tvalue_counter *= 3;\n\n\t\tif (sorted) {\n\t\t\tlist_insert_sorted(&lst_1, sort_handler1, NULL,\n\t\t\t\t\t   &node->le, node);\n\t\t\tlist_insert_sorted(&lst_2, sort_handler2, NULL,\n\t\t\t\t\t   &node->le2, node);\n\t\t}\n\t\telse {\n\t\t\tlist_append(&lst_1, &node->le, node);\n\t\t\tlist_append(&lst_2, &node->le2, node);\n\t\t}\n\t}\n\n\t/* sort the list in ascending order */\n\tif (!sorted) {\n\t\tlist_sort(&lst_1, sort_handler1, NULL);\n\t\tlist_sort(&lst_2, sort_handler2, NULL);\n\t}\n\n\t/* verify that the list is sorted */\n\tfor (le = lst_1.head; le; le = le->next) {\n\t\tstruct node *node = le->data;\n\n\t\tif (prev_value_set) {\n\t\t\tTEST_ASSERT(node->value >= prev_value);\n\t\t}\n\n\t\tprev_value = node->value;\n\t\tprev_value_set = true;\n\t}\n\n\tprev_value_set = false;\n\tfor (le = lst_2.head; le; le = le->next) {\n\t\tstruct node *node = le->data;\n\n\t\tif (prev_value_set) {\n\t\t\tTEST_ASSERT(node->value <= prev_value);\n\t\t}\n\n\t\tprev_value = node->value;\n\t\tprev_value_set = true;\n\t}\n\n out:\n\tlist_flush(&lst_1);\n\n\treturn err;\n}\n\n\nint test_list_sort(void)\n{\n\tint err;\n\n\terr = test_sort(false);\n\tTEST_ERR(err);\n\n\terr = test_sort(true);\n\tTEST_ERR(err);\n out:\n\treturn err;\n}\n\n\nstruct flush_data {\n\tstruct le le;\n\tstruct list *flushl;\n};\n\n\nstatic void data_destroy(void *arg)\n{\n\tstruct flush_data *data = arg;\n\tstruct le *le;\n\n\tLIST_FOREACH(data->flushl, le)\n\t{\n\t\tassert(list_count(data->flushl));\n\t}\n}\n\n\nint test_list_flush(void)\n{\n\tstruct flush_data *data[2];\n\tstruct list flushl = LIST_INIT;\n\tint err = 0;\n\n\tdata[0] = mem_zalloc(sizeof(struct flush_data), data_destroy);\n\tif (!data[0])\n\t\treturn ENOMEM;\n\n\tdata[1] = mem_zalloc(sizeof(struct flush_data), data_destroy);\n\tif (!data[1]) {\n\t\tmem_deref(data[0]);\n\t\treturn ENOMEM;\n\t}\n\n\tdata[0]->flushl = &flushl;\n\tdata[1]->flushl = &flushl;\n\n\tlist_append(&flushl, &data[0]->le, data[0]);\n\tlist_append(&flushl, &data[1]->le, data[1]);\n\n\tlist_flush(&flushl);\n\n\tTEST_EQUALS(0, list_count(&flushl));\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/main.c",
    "content": "/**\n * @file main.c  Main regression testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <stdlib.h>\n#include <stdio.h>\n#include <signal.h>\n#include <string.h>\n#ifdef HAVE_GETOPT\n#include <getopt.h>\n#endif\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"retest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#ifdef HAVE_SIGNAL\nstatic void signal_handler(int num)\n{\n\tre_fprintf(stderr, \"forced exit by signal %d -- test aborted\\n\", num);\n\n\texit(0);\n}\n#endif\n\n\n#ifdef HAVE_GETOPT\nstatic void usage(void)\n{\n\t(void)re_fprintf(stderr, \"Usage: retest [options] <testcase>\\n\");\n\n\t(void)re_fprintf(stderr, \"\\ntest group options:\\n\");\n\t(void)re_fprintf(stderr, \"\\t-r        Run regular tests\\n\");\n\t(void)re_fprintf(stderr, \"\\t-o        Run OOM memory tests\\n\");\n\t(void)re_fprintf(stderr, \"\\t-i        Run integration tests\\n\");\n\t(void)re_fprintf(stderr, \"\\t-p        Run performance tests\\n\");\n\t(void)re_fprintf(stderr, \"\\t-t        Run tests in multi-threads\\n\");\n\t(void)re_fprintf(stderr, \"\\t-a        Run all tests (default)\\n\");\n\t(void)re_fprintf(stderr, \"\\t-l        List all testcases and exit\\n\");\n\n\t(void)re_fprintf(stderr, \"\\ncommon options:\\n\");\n\t(void)re_fprintf(stderr, \"\\t-d <path> Path to data files\\n\");\n\t(void)re_fprintf(stderr, \"\\t-h        Help\\n\");\n\t(void)re_fprintf(stderr, \"\\t-m <met>  Async polling method to use\\n\");\n\t(void)re_fprintf(stderr, \"\\t-v        Verbose output\\n\");\n}\n#endif\n\n\nstatic void dbg_handler(int level, const char *p, size_t len, void *arg)\n{\n\t(void)level;\n\t(void)arg;\n\n\tprintf(\"%.*s\", (int)len, p);\n}\n\n\nint main(int argc, char *argv[])\n{\n\tstruct memstat mstat;\n\tbool do_reg = false;\n\tbool do_oom = false;\n\tbool do_int = false;\n\tbool do_perf = false;\n\tbool do_all = true;    /* run all tests is default */\n\tbool do_list = false;\n\tbool do_thread = false;\n\tenum dbg_flags flags;\n\tbool verbose = false;\n\tconst char *name = NULL;\n\tenum poll_method method = poll_method_best();\n\tint err = 0;\n\n#ifdef HAVE_SIGNAL\n\tsignal(SIGINT, signal_handler);\n\tsignal(SIGTERM, signal_handler);\n#endif\n\n\t(void)sys_coredump_set(true);\n\n#ifdef HAVE_GETOPT\n\tfor (;;) {\n\t\tconst int c = getopt(argc, argv, \"hroipaltvm:d:\");\n\t\tif (0 > c)\n\t\t\tbreak;\n\n\t\tswitch (c) {\n\n\t\tcase '?':\n\t\tcase 'h':\n\t\t\tusage();\n\t\t\treturn -2;\n\n\t\tcase 'r':\n\t\t\tdo_reg = true;\n\t\t\tdo_all = false;\n\t\t\tbreak;\n\n\t\tcase 'o':\n\t\t\tdo_oom = true;\n\t\t\tdo_all = false;\n\t\t\tbreak;\n\n\t\tcase 'i':\n\t\t\tdo_int = true;\n\t\t\tdo_all = false;\n\t\t\tbreak;\n\n\t\tcase 'p':\n\t\t\tdo_perf = true;\n\t\t\tdo_all = false;\n\t\t\tbreak;\n\n\t\tcase 'a':\n\t\t\tdo_all = true;\n\t\t\tbreak;\n\n\t\tcase 'l':\n\t\t\tdo_list = true;\n\t\t\tdo_all = false;\n\t\t\tbreak;\n\n\t\tcase 't':\n\t\t\tdo_thread = true;\n\t\t\tdo_all = false;\n\t\t\tbreak;\n\n\t\tcase 'v':\n\t\t\tverbose = true;\n\t\t\tbreak;\n\n\t\tcase 'm': {\n\t\t\tstruct pl pollname;\n\t\t\tpl_set_str(&pollname, optarg);\n\t\t\terr = poll_method_type(&method, &pollname);\n\t\t\tif (err) {\n\t\t\t\tre_fprintf(stderr,\n\t\t\t\t\t   \"could not resolve async polling\"\n\t\t\t\t\t   \" method '%r'\\n\", &pollname);\n\t\t\t\treturn err;\n\t\t\t}\n\t\t}\n\t\t\tbreak;\n\n\t\tcase 'd':\n\t\t\ttest_set_datapath(optarg);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\targc -= optind;\n\n\tif (argc < 0 || argc > 1) {\n\t\tusage();\n\t\treturn -2;\n\t}\n\n\tif (argc >= 1) {\n\t\tname = argv[optind];\n\t\tprintf(\"single testcase: %s\\n\", name);\n\t}\n\n#else\n\t(void)argc;\n\t(void)argv;\n\tdo_reg\t  = true;\n\tdo_int\t  = true;\n\tdo_thread = true;\n\tdo_oom\t  = false;\n\tdo_perf\t  = false;\n\tdo_all\t  = false;\n\tverbose\t  = true;\n#endif\n\n\t/* Initialise debugging */\n#if defined(WIN32)\n\tflags = 0;\n#else\n\tflags = DBG_ANSI;\n#endif\n\n\tdbg_init(DBG_INFO, flags);\n\n\n\t/* Initialise library */\n\tlibre_exception_btrace(true);\n\n\terr = libre_init();\n\tif (err)\n\t\tgoto out;\n\n\terr = poll_method_set(method);\n\tif (err) {\n\t\tDEBUG_WARNING(\"could not set polling method '%s' (%m)\\n\",\n\t\t\t      poll_method_name(method), err);\n\t\tgoto out;\n\t}\n\n\tdbg_handler_set(dbg_handler, 0);\n\n\tDEBUG_NOTICE(\"libre version %s (%s/%s)\\n\", sys_libre_version_get(),\n\t\t     sys_arch_get(), sys_os_get());\n\n\tdbg_handler_set(NULL, 0);\n\n\tif (do_all) {\n\t\tdo_reg = true;\n\t\tdo_oom = true;\n\t\tdo_int = true;\n\t\tdo_thread = true;\n\t}\n\n\tif (do_list) {\n\t\ttest_listcases();\n\t\tgoto out;\n\t}\n\n\t/*\n\t * Different test-groups specified below:\n\t */\n\n\tre_printf(\"using async polling method '%s'\\n\",\n\t\t  poll_method_name(method));\n\n\tif (verbose) {\n\t\tre_printf(\"using datapath '%s'\\n\", test_datapath());\n\t}\n\n\tif (do_reg) {\n\t\terr = test_reg(name, verbose);\n\t\tTEST_ERR(err);\n\t}\n\n\tif (do_oom) {\n\t\terr = test_oom(name, verbose);\n\t\tTEST_ERR(err);\n\t}\n\n\tif (do_int) {\n\t\terr = test_integration(name, verbose);\n\t\tTEST_ERR(err);\n\t}\n\n\tif (do_perf) {\n\t\terr = test_perf(name, verbose);\n\t\tTEST_ERR(err);\n\t}\n\n\tif (do_thread) {\n\t\terr = test_multithread();\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\tre_thread_async_close();\n\n\t/* Check for open timers */\n\ttmr_debug();\n\n\tlibre_close();\n\n\t/* Check for memory leaks */\n\tmem_debug();\n\n\tif (0 == mem_get_stat(&mstat)) {\n\n\t\tif (mstat.bytes_cur || mstat.blocks_cur)\n\t\t\treturn 2;\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/mbuf.c",
    "content": "/**\n * @file mbuf.c Mbuffer Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_mbuf\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int test_mbuf_basic(void)\n{\n\tstruct mbuf mb;\n\tstruct pl pl, hei = PL(\"hei\"), foo = PL(\"foo\");\n\tstatic const char *pattern = \"mmmmmmmmm\";\n\tchar *str = NULL;\n\tint err;\n\n\tmbuf_init(&mb);\n\n\t/* write */\n\terr = mbuf_write_u8(&mb, 0x5a);\n\tif (err)\n\t\tgoto out;\n\terr = mbuf_write_u16(&mb, 0x5a5a);\n\tif (err)\n\t\tgoto out;\n\terr = mbuf_write_u32(&mb, 0x5a5a5a5a);\n\tif (err)\n\t\tgoto out;\n\terr = mbuf_write_str(&mb, \"hei foo\");\n\tif (err)\n\t\tgoto out;\n\n\t/* read */\n\tmb.pos = 0;\n\tif (0x5a != mbuf_read_u8(&mb)) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\tif (0x5a5a != mbuf_read_u16(&mb)) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\tif (0x5a5a5a5a != mbuf_read_u32(&mb)) {\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\tpl.p = (char *)mbuf_buf(&mb);\n\tpl.l = 3;\n\terr = pl_cmp(&hei, &pl);\n\tif (err)\n\t\tgoto out;\n\n\tmb.pos += 4;\n\tpl.p = (char *)mbuf_buf(&mb);\n\tpl.l = mbuf_get_left(&mb);\n\terr = pl_cmp(&foo, &pl);\n\tif (err)\n\t\tgoto out;\n\n\t/* Test mbuf_strdup() */\n\terr = mbuf_strdup(&mb, &str, 3);\n\tif (err)\n\t\tgoto out;\n\terr = pl_strcmp(&foo, str);\n\tTEST_ERR(err);\n\n\tmb.pos = mb.end = 0;\n\terr = mbuf_fill(&mb, 'm', 9);\n\tif (err)\n\t\tgoto out;\n\tif (mb.pos != strlen(pattern) ||\n\t    mb.end != strlen(pattern) ||\n\t    0 != memcmp(mb.buf, pattern, 9)) {\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\t/* Test position and end */\n\tmbuf_set_posend(&mb, 2, 4);\n\n\tASSERT_EQ(2, mbuf_pos(&mb));\n\tASSERT_EQ(4, mbuf_end(&mb));\n\n out:\n\tmbuf_reset(&mb);\n\tmem_deref(str);\n\n\treturn err;\n}\n\n\nstatic int test_mbuf_shift(void)\n{\n\tstatic const uint8_t payload[10] = {0,1,2,3,4,5,6,7,8,9};\n\tstruct mbuf *mb;\n\tint err;\n\n\tmb = mbuf_alloc(sizeof(payload));\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = mbuf_write_mem(mb, payload, sizeof(payload));\n\tif (err)\n\t\tgoto out;\n\tmb->pos = 0;\n\n\t/* inject a header in the front */\n\terr = mbuf_shift(mb, 64);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(64, mb->pos);\n\tTEST_EQUALS(64+10, mb->end);\n\tTEST_MEMCMP(payload, sizeof(payload),\n\t\t    mbuf_buf(mb), mbuf_get_left(mb));\n\n\t/* remove a header in the front */\n\terr = mbuf_shift(mb, -1);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(63, mb->pos);\n\tTEST_EQUALS(63+10, mb->end);\n\tTEST_MEMCMP(payload, sizeof(payload),\n\t\t    mbuf_buf(mb), mbuf_get_left(mb));\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic int test_mbuf_ptr(void)\n{\n\tstruct mbuf *buf;\n\tint err;\n\n\tbuf = mbuf_alloc(1 * sizeof(void *));\n\tif (!buf)\n\t\treturn ENOMEM;\n\n\terr = mbuf_write_ptr(buf, (intptr_t)buf);\n\n\tbuf->pos = 0;\n\tintptr_t p = mbuf_read_ptr(buf);\n\n\tTEST_EQUALS((intptr_t)buf, p);\n\nout:\n\tmem_deref(buf);\n\treturn err;\n}\n\n\nint test_mbuf(void)\n{\n\tint err;\n\n\terr = test_mbuf_basic();\n\tTEST_ERR(err);\n\n\terr = test_mbuf_shift();\n\tTEST_ERR(err);\n\n\terr = test_mbuf_ptr();\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/md5.c",
    "content": "/**\n * @file md5.c MD5 Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"testmd5\"\n#define DEBUG_LEVEL 4\n#include <re_dbg.h>\n\n\nint test_md5(void)\n{\n\tconst struct pl str = PL(\"a93akjshdla81mx.kjda09sdkjl12jdlksaldkjas\");\n\tconst uint8_t ref[16] = {\n\t\t0x9d, 0x97, 0xa5, 0xf8, 0x8d, 0x1b, 0x09, 0x7c,\n\t\t0x9f, 0xf9, 0xe2, 0x9d, 0xd5, 0x43, 0xb1, 0x1d\n\t};\n\tuint8_t digest[16];\n\tint err;\n\n\t/* Test constants */\n\tif (16 != MD5_SIZE) {\n\t\tDEBUG_WARNING(\"MD5_SIZE is %u (should be 16)\\n\", MD5_SIZE);\n\t\treturn EINVAL;\n\t}\n\tif (33 != MD5_STR_SIZE) {\n\t\tDEBUG_WARNING(\"MD5_STR_SIZE is %u (should be 33)\\n\",\n\t\t\t      MD5_STR_SIZE);\n\t\treturn EINVAL;\n\t}\n\n\t/* Test md5() */\n\tmd5((const uint8_t *)str.p, str.l, digest);\n\n\tif (0 != memcmp(digest, ref, sizeof(digest))) {\n\t\tDEBUG_WARNING(\"md5 b0Rken: %02w\\n\", digest, sizeof(digest));\n\t\treturn EINVAL;\n\t}\n\n\t/* Test md5_printf() */\n\terr = md5_printf(digest, \"%r\", &str);\n\tif (err)\n\t\tgoto out;\n\n\tif (0 != memcmp(digest, ref, sizeof(digest))) {\n\t\tDEBUG_WARNING(\"md5_printf() is b0Rken: %02w\\n\", digest,\n\t\t\t      sizeof(digest));\n\t\treturn EINVAL;\n\t}\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/mem.c",
    "content": "/**\n * @file mem.c Memory Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_mem\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define PATTERN 0xfcfcfcfc\n\nenum {\n#if defined(__x86_64__)\n\t/* Use 16-byte alignment on x86-x32 as well */\n\tmem_alignment = 16u,\n#else\n\tmem_alignment = sizeof(void*) >= 8u ? 16u : 8u,\n#endif\n};\n\nstruct obj {\n\tuint32_t pattern;\n};\n\nstatic void destructor(void *arg)\n{\n\tstruct obj *obj = arg;\n\n\tif (PATTERN != obj->pattern) {\n\t\tDEBUG_WARNING(\"destroy error: %08x\\n\", obj->pattern);\n\t}\n}\n\n\nint test_mem(void)\n{\n\tstruct obj *obj, *old = NULL, *tmp = NULL;\n\tint err = EINVAL;\n\n\tobj = mem_alloc(sizeof(*obj), destructor);\n\tif (!obj)\n\t\treturn ENOMEM;\n\n\tobj->pattern = PATTERN;\n\n\tTEST_EQUALS(1, mem_nrefs(obj));\n\tTEST_ASSERT(re_is_aligned(obj, mem_alignment));\n\n\tobj = mem_ref(obj);\n\tTEST_EQUALS(2, mem_nrefs(obj));\n\n\tmem_deref(obj);\n\n\tTEST_EQUALS(1, mem_nrefs(obj));\n\n\told = obj;\n\tobj = mem_realloc(old, sizeof(*obj) + 16);\n\n\tif (!obj) {\n\t\told = mem_deref(old);\n\t\terr = ENOMEM;\n\t\tTEST_ERR(err);\n\t}\n\n\tTEST_ASSERT(re_is_aligned(obj, mem_alignment));\n\n\told = mem_ref(obj);\n\tTEST_EQUALS(2, mem_nrefs(obj));\n\n\tobj = mem_realloc(obj, sizeof(*obj) + 64);\n\tTEST_EQUALS(1, mem_nrefs(old));\n\tmem_deref(old);\n\tTEST_EQUALS(1, mem_nrefs(obj));\n\n\told = mem_ref(obj);\n\tTEST_EQUALS(2, mem_nrefs(obj));\n\n\ttmp = mem_realloc(obj, sizeof(*obj) + 16);\n\tif (!tmp) {\n\t\tmem_deref(obj);\n\t\terr = ENOMEM;\n\t\tTEST_ERR(err);\n\t}\n\tTEST_EQUALS(1, mem_nrefs(old));\n\tTEST_EQUALS(1, mem_nrefs(tmp));\n\n\terr = 0;\n\n out:\n\tmem_deref(tmp);\n\tmem_deref(old);\n\treturn err;\n}\n\n\n#ifndef SIZE_MAX\n#define SIZE_MAX    (~((size_t)0))\n#endif\n\n\nint test_mem_reallocarray(void)\n{\n\tvoid *a, *b;\n\tint err = 0;\n\n\t/* expect success */\n\ta = mem_reallocarray(NULL, 10, 10, NULL);\n\tif (!a)\n\t\treturn ENOMEM;\n\n\t/* expect failure */\n\tb = mem_reallocarray(NULL, SIZE_MAX, SIZE_MAX, NULL);\n\tTEST_ASSERT(b == NULL);\n\n out:\n\tmem_deref(a);\n\n\treturn err;\n}\n\n\nint test_mem_secure(void)\n{\n\tint r, err = 0;\n\n\t/* compare */\n\tr = mem_seccmp(NULL, NULL, 42);\n\tTEST_ASSERT(r < 0);\n\n\tr = mem_seccmp((uint8_t *)\"abc\", (uint8_t *)\"abc\", 3);\n\tTEST_EQUALS(0, r);\n\n\tr = mem_seccmp((uint8_t *)\"aaa\", (uint8_t *)\"bbb\", 3);\n\tTEST_ASSERT(r > 0);\n\n\tr = mem_seccmp((uint8_t *)\"ccc\", (uint8_t *)\"aaa\", 3);\n\tTEST_ASSERT(r > 0);\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/mem_pool.c",
    "content": "/**\n * @file mem_pool.c Memory Pool Testcode\n *\n * Copyright (C) 2025 Sebastian Reimers\n */\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_mem_pool\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nstruct object {\n\tint a;\n};\n\nenum {\n\tNUM_OBJECTS = 10,\n};\n\n\nint test_mem_pool(void)\n{\n\tint err;\n\n\tstruct mem_pool *pool = NULL;\n\terr = mem_pool_alloc(&pool, NUM_OBJECTS, sizeof(struct object), NULL);\n\tTEST_ERR(err);\n\n\tstruct mem_pool_entry *e;\n\tstruct object *o;\n\n\tfor (int i = 0; i < NUM_OBJECTS; i++) {\n\t\te = mem_pool_borrow(pool);\n\t\tTEST_ASSERT(e);\n\n\t\to = mem_pool_member(e);\n\t\tTEST_NOT_EQUALS(o->a, i + 1);\n\n\t\to->a = i + 1;\n\t}\n\n\tTEST_ASSERT(!mem_pool_borrow(pool));\n\n\te = mem_pool_release(pool, e);\n\tTEST_ASSERT(!e);\n\n\te = mem_pool_borrow(pool);\n\tTEST_ASSERT(e);\n\n\tTEST_ASSERT(!mem_pool_borrow(pool));\n\n\tmem_pool_flush(pool);\n\n\tfor (int i = 0; i < NUM_OBJECTS; i++) {\n\t\te = mem_pool_borrow(pool);\n\t\tTEST_ASSERT(e);\n\t}\n\n\tTEST_ASSERT(!mem_pool_borrow(pool));\n\n\terr = mem_pool_extend(pool, 1);\n\tTEST_ERR(err);\n\n\te = mem_pool_borrow(pool);\n\tTEST_ASSERT(e);\n\nout:\n\tmem_deref(pool);\n\treturn err;\n}\n"
  },
  {
    "path": "test/mock/cert.c",
    "content": "/**\n * @file cert.c  TLS Certificate\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re.h>\n#include \"test.h\"\n\n\n/**\n * X509/PEM certificate with ECDSA keypair\n *\n *  $ openssl ecparam -out ec_key.pem -name prime256v1 -genkey\n *  $ openssl req -new -key ec_key.pem -x509 -nodes -days 3650 -out cert.pem\n */\nconst char test_certificate_ecdsa[] =\n\"-----BEGIN CERTIFICATE-----\\r\\n\"\n\"MIICBzCCAa2gAwIBAgIUZy0UqzsDq7fGUsZh6QxkXgCa030wCgYIKoZIzj0EAwIw\\r\\n\"\n\"WTELMAkGA1UEBhMCTk8xEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu\\r\\n\"\n\"dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJMTI3LjAuMC4xMB4XDTE5\\r\\n\"\n\"MDUyNDE5NTM0OFoXDTI5MDUyMTE5NTM0OFowWTELMAkGA1UEBhMCTk8xEzARBgNV\\r\\n\"\n\"BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0\\r\\n\"\n\"ZDESMBAGA1UEAwwJMTI3LjAuMC4xMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\\r\\n\"\n\"inP/oBEqBbXRxDzyk7sbh8rRJbfbXBRG2uJl2g6YhSkYZkifGyEueJ7+A9D9LfBh\\r\\n\"\n\"b5+lKXuJc02XQW5IwUmToqNTMFEwHQYDVR0OBBYEFH1vSH2IBZvKYNDPfPOk41Dw\\r\\n\"\n\"hyTWMB8GA1UdIwQYMBaAFH1vSH2IBZvKYNDPfPOk41DwhyTWMA8GA1UdEwEB/wQF\\r\\n\"\n\"MAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhAOm79QetPxioy/S0Rk9lhPgfBslgM6f4\\r\\n\"\n\"tihVBSpe0FdJAiAC6Usj7p3H8dvu9Oa1gtOXSJkh1MT6pkfW21YseRWP4A==\\r\\n\"\n\"-----END CERTIFICATE-----\\r\\n\"\n\"-----BEGIN EC PARAMETERS-----\\r\\n\"\n\"BggqhkjOPQMBBw==\\r\\n\"\n\"-----END EC PARAMETERS-----\\r\\n\"\n\"-----BEGIN EC PRIVATE KEY-----\\r\\n\"\n\"MHcCAQEEIMWTO9/z24fiq13MM5UF1CVD3yJjVXRe0qpTCmmZU5ppoAoGCCqGSM49\\r\\n\"\n\"AwEHoUQDQgAEinP/oBEqBbXRxDzyk7sbh8rRJbfbXBRG2uJl2g6YhSkYZkifGyEu\\r\\n\"\n\"eJ7+A9D9LfBhb5+lKXuJc02XQW5IwUmTog==\\r\\n\"\n\"-----END EC PRIVATE KEY-----\\r\\n\"\n\t;\n"
  },
  {
    "path": "test/mock/dnssrv.c",
    "content": "/**\n * @file mock/dnssrv.c Mock DNS server\n *\n * Copyright (C) 2010 - 2016 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re.h>\n#include \"../test.h\"\n\n\n#define DEBUG_MODULE \"mock/dnssrv\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define LOCAL_PORT 0\n\n\nstatic bool rrlist_handler(struct dnsrr *rr, void *arg)\n{\n\tstruct list *rrl = arg;\n\n\tlist_append(rrl, &rr->le_priv, rr);\n\n\treturn false;\n}\n\n\nstatic void decode_dns_query(struct dns_server *srv, const struct sa *src,\n\t\t\t     struct mbuf *mb, int proto)\n{\n\tstruct list rrl = LIST_INIT;\n\tstruct dnshdr hdr;\n\tstruct le *le;\n\tchar *qname = NULL;\n\tsize_t start, end;\n\tuint16_t type, dnsclass;\n\tint err = 0;\n\n\tstart = mb->pos;\n\tend   = mb->end;\n\n\tif (dns_hdr_decode(mb, &hdr) || hdr.qr || hdr.nq != 1) {\n\t\tDEBUG_WARNING(\"unable to decode query header\\n\");\n\t\treturn;\n\t}\n\n\terr = dns_dname_decode(mb, &qname, start);\n\tif (err) {\n\t\tDEBUG_WARNING(\"unable to decode query name\\n\");\n\t\tgoto out;\n\t}\n\n\tif (mbuf_get_left(mb) < 4) {\n\t\tDEBUG_WARNING(\"unable to decode query type/class\\n\");\n\t\tgoto out;\n\t}\n\n\ttype\t = ntohs(mbuf_read_u16(mb));\n\tdnsclass = ntohs(mbuf_read_u16(mb));\n\n\tDEBUG_INFO(\"dnssrv: type=%s query-name='%s'\\n\", dns_rr_typename(type),\n\t\t   qname);\n\n\tif (dnsclass == DNS_CLASS_IN) {\n\t\tdns_rrlist_apply(&srv->rrl, qname, type, DNS_CLASS_IN,\n\t\t\t\t hdr.rd, rrlist_handler, &rrl);\n\t}\n\n\thdr.qr\t  = true;\n\thdr.tc\t  = false;\n\thdr.rcode = DNS_RCODE_OK;\n\thdr.nq\t  = 1;\n\thdr.nans  = list_count(&rrl);\n\n\tmb->pos = start;\n\n\terr = dns_hdr_encode(mb, &hdr);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = end;\n\n\tDEBUG_INFO(\"dnssrv: @@ found %u answers for %s\\n\", list_count(&rrl),\n\t\t   qname);\n\n\tfor (le = rrl.head; le; le = le->next) {\n\t\tstruct dnsrr *rr = le->data;\n\n\t\terr = dns_rr_encode(mb, rr, 0, NULL, start);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\tmb->pos = start;\n\n\tswitch (proto) {\n\n\tcase IPPROTO_UDP:\n\t\t(void)udp_send(srv->us, src, mb);\n\t\tbreak;\n\n\tcase IPPROTO_TCP: {\n\t\tsize_t length = mb->end - start;\n\t\tstruct mbuf *mb_tcp = mbuf_alloc(sizeof(uint16_t) + length);\n\t\tif (!mb_tcp)\n\t\t\tgoto out;\n\n\t\tmbuf_write_u16(mb_tcp, htons((uint16_t)length));\n\t\tmbuf_write_mem(mb_tcp, mbuf_buf(mb), length);\n\t\tmbuf_set_pos(mb_tcp, 0);\n\n\t\ttcp_send(srv->tc, mb_tcp);\n\n\t\tmem_deref(mb_tcp);\n\t}\n\t\tbreak;\n\t}\n\nout:\n\tlist_clear(&rrl);\n\tmem_deref(qname);\n}\n\n\nstatic void udp_recv(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct dns_server *srv = arg;\n\n\tdecode_dns_query(srv, src, mb, IPPROTO_UDP);\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct dns_server *srv = arg;\n\n\tlist_flush(&srv->rrl);\n\tmem_deref(srv->us);\n\tmem_deref(srv->tc);\n\tmem_deref(srv->ts);\n\tmem_deref(srv->mb);\n}\n\n\nstatic void tcp_recv_handler(struct mbuf *mbrx, void *arg)\n{\n\tstruct dns_server *srv = arg;\n\tstruct mbuf *mb = srv->mb;\n\tint err = 0;\n\tsize_t n;\n\n next:\n\t/* frame length */\n\tif (!srv->flen) {\n\n\t\tn = min(2 - mb->end, mbuf_get_left(mbrx));\n\n\t\terr = mbuf_write_mem(mb, mbuf_buf(mbrx), n);\n\t\tif (err)\n\t\t\tgoto error;\n\n\t\tmbrx->pos += n;\n\n\t\tif (mb->end < 2)\n\t\t\treturn;\n\n\t\tmb->pos = 0;\n\t\tsrv->flen = ntohs(mbuf_read_u16(mb));\n\t\tmbuf_rewind(mb);\n\t}\n\n\tn = min(srv->flen - mb->end, mbuf_get_left(mbrx));\n\n\terr = mbuf_write_mem(mb, mbuf_buf(mbrx), n);\n\tif (err)\n\t\tgoto error;\n\n\tmbrx->pos += n;\n\n\tif (mb->end < srv->flen)\n\t\treturn;\n\n\tmb->pos = 0;\n\n\tdecode_dns_query(srv, NULL, mb, IPPROTO_TCP);\n\n\tsrv->flen = 0;\n\tmbuf_rewind(mb);\n\n\tif (mbuf_get_left(mbrx) > 0) {\n\t\tDEBUG_INFO(\"%zu bytes of tcp data left\\n\",\n\t\t\t   mbuf_get_left(mbrx));\n\t\tgoto next;\n\t}\n\n\treturn;\n\n error:\n\tsrv->tc = mem_deref(srv->tc);\n}\n\n\nstatic void tcp_close_handler(int err, void *arg)\n{\n\tstruct dns_server *srv = arg;\n\t(void)err;\n\n\tsrv->tc = mem_deref(srv->tc);\n\tsrv->mb = mem_deref(srv->mb);\n\tsrv->flen = 0;\n}\n\n\nstatic void tcp_conn_handler(const struct sa *peer, void *arg)\n{\n\tstruct dns_server *srv = arg;\n\tint err = 0;\n\t(void)peer;\n\n\t/* max 1 TCP connection */\n\tTEST_ASSERT(srv->tc == NULL);\n\n\tsrv->mb = mbuf_alloc(1500);\n\tif (!srv->mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tsrv->flen = 0;\n\n\terr = tcp_accept(&srv->tc, srv->ts, NULL, tcp_recv_handler,\n\t\t\t tcp_close_handler, srv);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err) {\n\t\ttcp_reject(srv->ts);\n\t\tsrv->mb = mem_deref(srv->mb);\n\t\tsrv->flen = 0;\n\t}\n}\n\n\nvoid dns_server_flush(struct dns_server *srv)\n{\n\tlist_flush(&srv->rrl);\n}\n\n\nint dns_server_alloc(struct dns_server **srvp, const char *laddr)\n{\n\tstruct dns_server *srv;\n\tstruct sa laddr_tcp;\n\tint err;\n\n\tif (!srvp)\n\t\treturn EINVAL;\n\n\tsa_set_str(&laddr_tcp, laddr, 0);\n\n\tsrv = mem_zalloc(sizeof(*srv), destructor);\n\tif (!srv)\n\t\treturn ENOMEM;\n\n\terr = sa_set_str(&srv->addr, laddr, LOCAL_PORT);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_listen(&srv->us, &srv->addr, udp_recv, srv);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_local_get(srv->us, &srv->addr);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_listen(&srv->ts, &laddr_tcp, tcp_conn_handler, srv);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_local_get(srv->ts, &srv->addr_tcp);\n\tif (err)\n\t\tgoto out;\n\nout:\n\tif (err)\n\t\tmem_deref(srv);\n\telse\n\t\t*srvp = srv;\n\n\treturn err;\n}\n\n\nint dns_server_add_a(struct dns_server *srv, const char *name, uint32_t addr,\n\t\t     int64_t ttl)\n{\n\tstruct dnsrr *rr;\n\tint err;\n\n\tif (!srv || !name)\n\t\treturn EINVAL;\n\n\trr = dns_rr_alloc();\n\tif (!rr)\n\t\treturn ENOMEM;\n\n\terr = str_dup(&rr->name, name);\n\tif (err)\n\t\tgoto out;\n\n\trr->type     = DNS_TYPE_A;\n\trr->dnsclass = DNS_CLASS_IN;\n\trr->ttl\t     = ttl;\n\trr->rdlen    = 0;\n\n\trr->rdata.a.addr = addr;\n\n\tlist_append(&srv->rrl, &rr->le, rr);\n\nout:\n\tif (err)\n\t\tmem_deref(rr);\n\n\treturn err;\n}\n\n\nint dns_server_add_aaaa(struct dns_server *srv, const char *name,\n\t\t\tconst uint8_t *addr, int64_t ttl)\n{\n\tstruct dnsrr *rr;\n\tint err;\n\n\tif (!srv || !name)\n\t\treturn EINVAL;\n\n\trr = dns_rr_alloc();\n\tif (!rr)\n\t\treturn ENOMEM;\n\n\terr = str_dup(&rr->name, name);\n\tif (err)\n\t\tgoto out;\n\n\trr->type     = DNS_TYPE_AAAA;\n\trr->dnsclass = DNS_CLASS_IN;\n\trr->ttl\t     = ttl;\n\trr->rdlen    = 0;\n\n\tmemcpy(rr->rdata.aaaa.addr, addr, 16);\n\n\tlist_append(&srv->rrl, &rr->le, rr);\n\nout:\n\tif (err)\n\t\tmem_deref(rr);\n\n\treturn err;\n}\n\n\nint dns_server_add_srv(struct dns_server *srv, const char *name, uint16_t pri,\n\t\t       uint16_t weight, uint16_t port, const char *target,\n\t\t       int64_t ttl)\n{\n\tstruct dnsrr *rr;\n\tint err;\n\n\tif (!srv || !name || !port || !target)\n\t\treturn EINVAL;\n\n\trr = dns_rr_alloc();\n\tif (!rr)\n\t\treturn ENOMEM;\n\n\terr = str_dup(&rr->name, name);\n\tif (err)\n\t\tgoto out;\n\n\trr->type     = DNS_TYPE_SRV;\n\trr->dnsclass = DNS_CLASS_IN;\n\trr->ttl\t     = ttl;\n\trr->rdlen    = 0;\n\n\trr->rdata.srv.pri    = pri;\n\trr->rdata.srv.weight = weight;\n\trr->rdata.srv.port   = port;\n\tstr_dup(&rr->rdata.srv.target, target);\n\n\tlist_append(&srv->rrl, &rr->le, rr);\n\nout:\n\tif (err)\n\t\tmem_deref(rr);\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/mock/nat.c",
    "content": "/**\n * @file mock/nat.c Mock NAT-box\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"mock/nat\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tLAYER_NAT = -1000\n};\n\n\nstatic void nat_binding_add(struct nat *nat, const struct sa *addr)\n{\n\tif (!nat || !addr)\n\t\treturn;\n\n\tif (nat->bindingc >= RE_ARRAY_SIZE(nat->bindingv)) {\n\t\tDEBUG_WARNING(\"NAT-box at max capacity\\n\");\n\t\treturn;\n\t}\n\n\tnat->bindingv[nat->bindingc++] = *addr;\n}\n\n\nstatic struct sa *nat_binding_find_addr(struct nat *nat, const struct sa *addr)\n{\n\tunsigned i;\n\n\tif (!nat || !addr)\n\t\treturn NULL;\n\n\tfor (i=0; i<nat->bindingc; i++) {\n\n\t\tif (sa_cmp(addr, &nat->bindingv[i], SA_ALL))\n\t\t\treturn &nat->bindingv[i];\n\t}\n\n\treturn NULL;\n}\n\n\nstatic struct sa *nat_binding_find(struct nat *nat, uint16_t port)\n{\n\tunsigned i;\n\n\tif (!nat || !port)\n\t\treturn NULL;\n\n\tfor (i=0; i<nat->bindingc; i++) {\n\n\t\tif (port == sa_port(&nat->bindingv[i]))\n\t\t\treturn &nat->bindingv[i];\n\t}\n\n\treturn NULL;\n}\n\n\nstatic bool nat_helper_send(int *err, struct sa *dst,\n\t\t\t    struct mbuf *mb, void *arg)\n{\n\tstruct nat *nat = arg;\n\tstruct sa *cli;\n\t(void)mb;\n\n\tcli = nat_binding_find(nat, sa_port(dst));\n\n#if 0\n\tre_printf(\"nat: send INGRESS %J -> %J\\n\", dst, cli);\n#endif\n\n\tif (cli) {\n\t\t*dst = *cli;\n\t\treturn false;\n\t}\n\telse {\n\t\t*err = ENOTCONN;\n\t\tDEBUG_WARNING(\"nat: binding to %J not found\\n\", dst);\n\t\treturn true;\n\t}\n}\n\n\nstatic bool nat_helper_recv(struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct nat *nat = arg;\n\tstruct sa map;\n\t(void)mb;\n\n\tif (!nat_binding_find(nat, sa_port(src))) {\n\t\tnat_binding_add(nat, src);\n\t}\n\n\tmap = nat->public_addr;\n\tsa_set_port(&map, sa_port(src));\n\n#if 0\n\tre_printf(\"nat: recv EGRESS %J -> %J\\n\", src, &map);\n#endif\n\n\t*src = map;\n\n\treturn false;\n}\n\n\nstatic bool firewall_egress(int *err, struct sa *dst,\n\t\t\t    struct mbuf *mb, void *arg)\n{\n\tstruct nat *nat = arg;\n\t(void)err;\n\t(void)mb;\n\n\t/* add egress mapping to external addr */\n\tif (!nat_binding_find_addr(nat, dst)) {\n\t\tnat_binding_add(nat, dst);\n\t}\n\n\treturn false;\n}\n\n\nstatic bool firewall_ingress(struct sa *src,\n\t\t\t     struct mbuf *mb, void *arg)\n{\n\tstruct nat *nat = arg;\n\t(void)mb;\n\n\t/* check if external address has a mapping */\n\tif (!nat_binding_find_addr(nat, src)) {\n\n\t\tDEBUG_NOTICE(\"firewall: drop 1 packet from %J\\n\", src);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nstatic void nat_destructor(void *arg)\n{\n\tstruct nat *nat = arg;\n\n\tmem_deref(nat->uh);\n\tmem_deref(nat->us);\n}\n\n\n/* inbound NAT */\nint nat_alloc(struct nat **natp, enum natbox_type type,\n\t      struct udp_sock *us, const struct sa *public_addr)\n{\n\tstruct nat *nat;\n\tint err = 0;\n\n\tif (!natp || !us)\n\t\treturn EINVAL;\n\n\tif (type == NAT_INBOUND_SNAT && !public_addr)\n\t\treturn EINVAL;\n\n\tif (udp_helper_find(us, LAYER_NAT)) {\n\t\tDEBUG_WARNING(\"udp helper already exist on layer %d\\n\",\n\t\t\t      LAYER_NAT);\n\t\treturn EPROTO;\n\t}\n\n\tnat = mem_zalloc(sizeof(*nat), nat_destructor);\n\tif (!nat)\n\t\treturn ENOMEM;\n\n\tnat->type = type;\n\tif (public_addr)\n\t\tnat->public_addr = *public_addr;\n\tnat->us = mem_ref(us);\n\n\tswitch (type) {\n\n\tcase NAT_INBOUND_SNAT:\n\t\terr = udp_register_helper(&nat->uh, us, LAYER_NAT,\n\t\t\t\t\t  nat_helper_send,\n\t\t\t\t\t  nat_helper_recv, nat);\n\t\tbreak;\n\n\tcase NAT_FIREWALL:\n\t\terr = udp_register_helper(&nat->uh, us, LAYER_NAT,\n\t\t\t\t\t  firewall_egress,\n\t\t\t\t\t  firewall_ingress, nat);\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"invalid NAT type %d\\n\", type);\n\t\terr = ENOTSUP;\n\t\tbreak;\n\t}\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(nat);\n\telse\n\t\t*natp = nat;\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/mock/sipsrv.c",
    "content": "/**\n * @file mock/sipsrv.c Mock SIP server\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"mock/sipsrv\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define LOCAL_PORT        0\n#define LOCAL_SECURE_PORT 0\n\n\nstatic bool sip_msg_handler(const struct sip_msg *msg, void *arg)\n{\n\tstruct sip_server *srv = arg;\n\tint err;\n\n\tif (0 == pl_strcmp(&msg->met, \"REGISTER\")) {\n\t\tmbuf_set_pos(msg->mb, 0);\n\t\tif (sip_msg_decode(&srv->sip_msgs[srv->n_register_req],\n\t\t\t\t\tmsg->mb)) {\n\t\t\tDEBUG_NOTICE(\"sip message cannot be parsed (%r)\\n\",\n\t\t\t\t\t&msg->met);\n\t\t\treturn false;\n\t\t}\n\t\t++srv->n_register_req;\n\t}\n\telse if (0 == pl_strcmp(&msg->met, \"OPTIONS\")) {\n\t\t++srv->n_options_req;\n\t}\n\telse {\n\t\tDEBUG_NOTICE(\"method not handled (%r)\\n\", &msg->met);\n\t\treturn false;\n\t}\n\n\tif (srv->terminate)\n\t\terr = sip_reply(srv->sip, msg, 503, \"Server Error\");\n\telse {\n\n\t\tif (sip_msg_hdr_has_value(msg, SIP_HDR_SUPPORTED,\n\t\t\t\t\t  \"outbound\")) {\n\n\t\t\terr = sip_replyf(srv->sip, msg, 200, \"OK\",\n\t\t\t\t\t \"Contact: <%r>\\r\\n\"\n\t\t\t\t\t \"Content-Length: 0\\r\\n\"\n\t\t\t\t\t \"Require: outbound\\r\\n\"\n\t\t\t\t\t \"Flow-Timer: 1\\r\\n\"\n\t\t\t\t\t \"\\r\\n\"\n\t\t\t\t\t ,\n\t\t\t\t\t &msg->to.auri);\n\t\t}\n\t\telse {\n\t\t\terr = sip_reply(srv->sip, msg, 200, \"OK\");\n\t\t}\n\t}\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"could not reply: %m\\n\", err);\n\t}\n\n#if 0\n\tif (srv->terminate)\n\t\tre_cancel();\n#endif\n\n\treturn true;\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct sip_server *srv = arg;\n\n\tsrv->terminate = true;\n\n\tsip_close(srv->sip, false);\n\tfor (unsigned i=0; i<RE_ARRAY_SIZE(srv->sip_msgs); i++)\n\t    mem_deref(srv->sip_msgs[i]);\n\tmem_deref(srv->sip);\n}\n\n\nint sip_server_alloc(struct sip_server **srvp)\n{\n\tstruct sip_server *srv;\n\tstruct sa laddr, laddrs;\n\tstruct tls *tls = NULL;\n\tint err;\n\n\tif (!srvp)\n\t\treturn EINVAL;\n\n\tsrv = mem_zalloc(sizeof *srv, destructor);\n\tif (!srv)\n\t\treturn ENOMEM;\n\n\terr  = sa_set_str(&laddr,  \"127.0.0.1\", LOCAL_PORT);\n\terr |= sa_set_str(&laddrs, \"127.0.0.1\", LOCAL_SECURE_PORT);\n\tif (err)\n\t\tgoto out;\n\n\terr = sip_alloc(&srv->sip, NULL, 16, 16, 16,\n\t\t\t\"mock SIP server\", NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\terr |= sip_transp_add(srv->sip, SIP_TRANSP_UDP, &laddr);\n\terr |= sip_transp_add(srv->sip, SIP_TRANSP_TCP, &laddr);\n\tif (err)\n\t\tgoto out;\n\n#ifdef USE_TLS\n\terr = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\terr = tls_set_certificate(tls, test_certificate_ecdsa,\n\t\t\t\t  strlen(test_certificate_ecdsa));\n\tif (err)\n\t\tgoto out;\n\n\terr |= sip_transp_add(srv->sip, SIP_TRANSP_TLS, &laddrs, tls);\n#endif\n\tif (err)\n\t\tgoto out;\n\n\terr = sip_listen(&srv->lsnr, srv->sip, true, sip_msg_handler, srv);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(tls);\n\tif (err)\n\t\tmem_deref(srv);\n\telse\n\t\t*srvp = srv;\n\n\treturn err;\n}\n\n\nint sip_server_uri(struct sip_server *srv, char *uri, size_t sz,\n\t\t   enum sip_transp tp)\n{\n\tstruct sa laddr;\n\tint err;\n\n\tif (!srv || !uri || !sz)\n\t\treturn EINVAL;\n\n\terr = sip_transp_laddr(srv->sip, &laddr, tp, NULL);\n\tif (err)\n\t\treturn err;\n\n\tif (re_snprintf(uri, sz, \"sip:%J%s\", &laddr, sip_transp_param(tp)) < 0)\n\t\treturn ENOMEM;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "test/mock/stunsrv.c",
    "content": "/**\n * @file mock/stunsrv.c Mock STUN server\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"mock/stunsrv\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tTCP_MAX_LENGTH = 2048,\n};\n\n\nstatic void process_msg(struct stunserver *stun,\n\t\t\tint proto, void *sock,\n\t\t\tconst struct sa *src, const struct sa *dst,\n\t\t\tstruct mbuf *mb)\n{\n\tstruct stun_msg *msg;\n\tbool fp = false;\n\tint err;\n\t(void)dst;\n\n\tstun->nrecv++;\n\n\terr = stun_msg_decode(&msg, mb, NULL);\n\tif (err)\n\t\treturn;\n\n#if 0\n\tstun_msg_dump(msg);\n#endif\n\n\tTEST_EQUALS(0x0001, stun_msg_type(msg));\n\tTEST_EQUALS(STUN_CLASS_REQUEST, stun_msg_class(msg));\n\tTEST_EQUALS(STUN_METHOD_BINDING, stun_msg_method(msg));\n\n\t/* mirror FINGERPRINT attribute back in response */\n\tfp = NULL != stun_msg_attr(msg, STUN_ATTR_FINGERPRINT);\n\tif (fp) {\n\t\tTEST_EQUALS(0, stun_msg_chk_fingerprint(msg));\n\t}\n\n\terr = stun_reply(proto, sock, src,\n\t\t\t 0, msg, NULL, 0, fp, 2,\n\t\t\t STUN_ATTR_MAPPED_ADDR, src,\n\t\t\t STUN_ATTR_XOR_MAPPED_ADDR, src);\n\n out:\n\tif (err) {\n\t\t(void)stun_ereply(proto, sock, src, 0, msg, 400,\n\t\t\t\t  \"Bad Request\", NULL, 0, fp, 0);\n\t}\n\n\tmem_deref(msg);\n}\n\n\nstatic void stunserver_udp_recv(const struct sa *src, struct mbuf *mb,\n\t\t\t\tvoid *arg)\n{\n\tstruct stunserver *stun = arg;\n\n\tprocess_msg(stun, IPPROTO_UDP, stun->us, src, &stun->laddr, mb);\n}\n\n\nstatic void tcp_recv(struct mbuf *mb, void *arg)\n{\n\tstruct stunserver *stun = arg;\n\tint err = 0;\n\n\tif (stun->mb) {\n\t\tsize_t pos;\n\n\t\tpos = stun->mb->pos;\n\n\t\tstun->mb->pos = stun->mb->end;\n\n\t\terr = mbuf_write_mem(stun->mb, mbuf_buf(mb),mbuf_get_left(mb));\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\n\t\tstun->mb->pos = pos;\n\t}\n\telse {\n\t\tstun->mb = mem_ref(mb);\n\t}\n\n\tfor (;;) {\n\n\t\tsize_t len, pos, end;\n\t\tuint16_t typ;\n\n\t\tif (mbuf_get_left(stun->mb) < 4)\n\t\t\tbreak;\n\n\t\ttyp = ntohs(mbuf_read_u16(stun->mb));\n\t\tlen = ntohs(mbuf_read_u16(stun->mb));\n\n\t\tif (len > TCP_MAX_LENGTH) {\n\t\t\tDEBUG_WARNING(\"tcp: bad length: %zu\\n\", len);\n\t\t\terr = EBADMSG;\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (typ < 0x4000)\n\t\t\tlen += STUN_HEADER_SIZE;\n\t\telse if (typ < 0x8000)\n\t\t\tlen += 4;\n\t\telse {\n\t\t\tDEBUG_WARNING(\"tcp: bad type: 0x%04x\\n\", typ);\n\t\t\terr = EBADMSG;\n\t\t\tgoto out;\n\t\t}\n\n\t\tstun->mb->pos -= 4;\n\n\t\tif (mbuf_get_left(stun->mb) < len)\n\t\t\tbreak;\n\n\t\tpos = stun->mb->pos;\n\t\tend = stun->mb->end;\n\n\t\tstun->mb->end = pos + len;\n\n\t\tprocess_msg(stun, IPPROTO_TCP, stun->tc, &stun->paddr,\n\t\t\t    &stun->laddr_tcp, stun->mb);\n\n\t\t/* 4 byte alignment */\n\t\twhile (len & 0x03)\n\t\t\t++len;\n\n\t\tstun->mb->pos = pos + len;\n\t\tstun->mb->end = end;\n\n\t\tif (stun->mb->pos >= stun->mb->end) {\n\t\t\tstun->mb = mem_deref(stun->mb);\n\t\t\tbreak;\n\t\t}\n\t}\n\n out:\n\tif (err) {\n\t\tstun->mb = mem_deref(stun->mb);\n\t}\n}\n\n\nstatic void tcp_close(int err, void *arg)\n{\n\tstruct stunserver *stun = arg;\n\t(void)err;\n\n\tstun->tc = mem_deref(stun->tc);\n}\n\n\nstatic void tcp_conn_handler(const struct sa *peer, void *arg)\n{\n\tstruct stunserver *stun = arg;\n\tint err;\n\n\t/* max 1 TCP connection */\n\tTEST_ASSERT(stun->tc == NULL);\n\terr = tcp_accept(&stun->tc, stun->ts, NULL, tcp_recv, tcp_close, stun);\n\tif (err)\n\t\tgoto out;\n\n\tstun->paddr = *peer;\n\n out:\n\tif (err) {\n\t\t/* save the error code */\n\t\tstun->err = err;\n\n\t\ttcp_reject(stun->ts);\n\t}\n}\n\n\nstatic void stunserver_destructor(void *arg)\n{\n\tstruct stunserver *stun = arg;\n\n\tmem_deref(stun->us);\n\tmem_deref(stun->mb);\n\tmem_deref(stun->tc);\n\tmem_deref(stun->ts);\n}\n\n\n/* Both UDP- and TCP-transport enabled by default */\nint stunserver_alloc(struct stunserver **stunp, const char *laddr_str)\n{\n\tstruct stunserver *stun;\n\tstruct sa laddr;\n\tint err;\n\n\tif (!stunp)\n\t\treturn EINVAL;\n\n\tstun = mem_zalloc(sizeof(*stun), stunserver_destructor);\n\tif (!stun)\n\t\treturn ENOMEM;\n\n\tsa_set_str(&laddr, laddr_str, 0);\n\n\terr = udp_listen(&stun->us, &laddr, stunserver_udp_recv, stun);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_local_get(stun->us, &stun->laddr);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_listen(&stun->ts, &laddr, tcp_conn_handler, stun);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_local_get(stun->ts, &stun->laddr_tcp);\n\tif (err)\n\t\tgoto out;\n\n#if 0\n\tDEBUG_NOTICE(\"stunserver: udp=%J, tcp=%J\\n\",\n\t\t     &stun->laddr, &stun->laddr_tcp);\n#endif\n\n out:\n\tif (err)\n\t\tmem_deref(stun);\n\telse\n\t\t*stunp = stun;\n\n\treturn err;\n}\n\n\nconst struct sa *stunserver_addr(const struct stunserver *stun, int proto)\n{\n\tif (!stun)\n\t\treturn NULL;\n\n\tswitch (proto) {\n\n\tcase IPPROTO_UDP: return &stun->laddr;\n\tcase IPPROTO_TCP: return &stun->laddr_tcp;\n\tdefault: return NULL;\n\t}\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "test/mock/turnsrv.c",
    "content": "/**\n * @file mock/turnsrv.c Mock TURN server\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <time.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"mock/turnsrv\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {\n\tTCP_MAX_LENGTH = 2048,\n\n\tNONCE_EXPIRY   = 3600,  /* seconds */\n\tNONCE_MAX_SIZE = 48,\n\tNONCE_MIN_SIZE = 33,\n};\n\n\nstruct auth_context {\n\tuint8_t key[MD5_SIZE];\n\tsize_t keylen;\n};\n\n\nstatic struct channel *find_channel_numb(struct turnserver *tt, uint16_t nr)\n{\n\tsize_t i;\n\n\tif (!tt)\n\t\treturn NULL;\n\n\tfor (i=0; i<tt->chanc; i++) {\n\n\t\tif (tt->chanv[i].nr == nr)\n\t\t\treturn &tt->chanv[i];\n\t}\n\n\treturn NULL;\n}\n\n\nstatic struct channel *find_channel_peer(struct turnserver *tt,\n\t\t\t\t\t const struct sa *peer)\n{\n\tsize_t i;\n\n\tif (!tt)\n\t\treturn NULL;\n\n\tfor (i=0; i<tt->chanc; i++) {\n\n\t\tif (sa_cmp(&tt->chanv[i].peer, peer, SA_ALL))\n\t\t\treturn &tt->chanv[i];\n\t}\n\n\treturn NULL;\n}\n\n\nstatic int add_permission(struct turnserver *tt, const struct sa *peer)\n{\n\tint err = 0;\n\n\tTEST_ASSERT(tt->permc < RE_ARRAY_SIZE(tt->permv));\n\ttt->permv[tt->permc] = *peer;\n\t++tt->permc;\n out:\n\treturn err;\n}\n\n\nstatic struct sa *find_permission(struct turnserver *tt,\n\t\t\t\t  const struct sa *peer)\n{\n\tsize_t i;\n\n\tif (!tt)\n\t\treturn NULL;\n\n\tfor (i=0; i<tt->permc; i++) {\n\n\t\tif (sa_cmp(&tt->permv[i], peer, SA_ADDR))\n\t\t\treturn &tt->permv[i];\n\t}\n\n\treturn NULL;\n}\n\n\n/* Receive packet on the \"relayed\" address -- relay to the client */\nstatic void relay_udp_recv(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct turnserver *turn = arg;\n\tstruct channel *chan;\n\tint err = 0;\n\n\t++turn->n_recv;\n\n\tchan = find_channel_peer(turn, src);\n\tif (chan) {\n\t\tuint16_t len = (uint16_t)mbuf_get_left(mb);\n\t\tsize_t start;\n\n\t\tif (mb->pos < 4) {\n\t\t\tDEBUG_WARNING(\"relay_udp_recv: mb pos < 4\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\tmb->pos -= 4;\n\t\tstart = mb->pos;\n\n\t\t(void)mbuf_write_u16(mb, htons(chan->nr));\n\t\t(void)mbuf_write_u16(mb, htons(len));\n\n\t\tmb->pos = start;\n\n\t\tif (turn->tc)\n\t\t\terr = tcp_send(turn->tc, mb);\n\t\telse\n\t\t\terr = udp_send(turn->us, &turn->cli, mb);\n\t}\n\telse {\n\t\tint proto = turn->tc ? IPPROTO_TCP : IPPROTO_UDP;\n\t\tvoid *sock = turn->tc ? (void *)turn->tc : (void *)turn->us;\n\n\t\terr = stun_indication(proto, sock,\n\t\t\t\t      &turn->cli, 0, STUN_METHOD_DATA,\n\t\t\t\t      NULL, 0, false, 2,\n\t\t\t\t      STUN_ATTR_XOR_PEER_ADDR, src,\n\t\t\t\t      STUN_ATTR_DATA, mb);\n\t}\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"relay_udp_recv: error %m\\n\", err);\n\t}\n}\n\n\nstatic const char *mknonce(struct turnserver *turn, char *nonce,\n\t\t\t   time_t now, const struct sa *src)\n{\n\tuint8_t key[MD5_SIZE];\n\tuint64_t nv[3];\n\n\tnv[0] = now;\n\tnv[1] = turn->auth_secret;\n\tnv[2] = sa_hash(src, SA_ADDR);\n\n\tmd5((uint8_t *)nv, sizeof(nv), key);\n\n\t(void)re_snprintf(nonce, NONCE_MAX_SIZE + 1, \"%w%llx\",\n\t\t\t  key, sizeof(key), nv[0]);\n\n\treturn nonce;\n}\n\n\nstatic bool nonce_validate(struct turnserver *turn,\n\t\t\t   const char *nonce, time_t now, const struct sa *src)\n{\n\tuint8_t nkey[MD5_SIZE], ckey[MD5_SIZE];\n\tuint64_t nv[3];\n\tstruct pl pl;\n\n\tpl.p = nonce;\n\tpl.l = str_len(nonce);\n\n\tif (pl.l < NONCE_MIN_SIZE || pl.l > NONCE_MAX_SIZE) {\n\t\tDEBUG_NOTICE(\"auth: bad nonce length (%zu)\\n\", pl.l);\n\t\treturn false;\n\t}\n\n\tfor (size_t i=0; i<sizeof(nkey); i++) {\n\t\tnkey[i]  = ch_hex(*pl.p++) << 4;\n\t\tnkey[i] += ch_hex(*pl.p++);\n\t\tpl.l -= 2;\n\t}\n\n\tnv[0] = pl_x64(&pl);\n\tnv[1] = turn->auth_secret;\n\tnv[2] = sa_hash(src, SA_ADDR);\n\n\tmd5((uint8_t *)nv, sizeof(nv), ckey);\n\n\tif (memcmp(nkey, ckey, MD5_SIZE)) {\n\t\tDEBUG_NOTICE(\"auth: invalid nonce (%j)\\n\", src);\n\t\treturn false;\n\t}\n\n\tint64_t age = now - nv[0];\n\n\tif (age < 0 || age > NONCE_EXPIRY) {\n\t\tDEBUG_NOTICE(\"auth: nonce expired, age: %lli secs\\n\", age);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\nstatic int auth_get_ha1(struct turnserver *turn, const char *username,\n\t\t\tuint8_t *ha1)\n{\n\tstatic const char auth_password[] = \"password\";\n\n\treturn md5_printf(ha1, \"%s:%s:%s\",\n\t\t\t  username, turn->auth_realm, auth_password);\n}\n\n\nstatic bool auth_request(struct turnserver *turn,\n\t\t\t struct auth_context *ctx, int proto, void *sock,\n\t\t\t const struct sa *src, const struct stun_msg *msg)\n{\n\tstruct stun_attr *mi, *user, *realm, *nonce;\n\tconst time_t now = time(NULL);\n\tchar nstr[NONCE_MAX_SIZE + 1];\n\tint err;\n\n\tmi    = stun_msg_attr(msg, STUN_ATTR_MSG_INTEGRITY);\n\tuser  = stun_msg_attr(msg, STUN_ATTR_USERNAME);\n\trealm = stun_msg_attr(msg, STUN_ATTR_REALM);\n\tnonce = stun_msg_attr(msg, STUN_ATTR_NONCE);\n\n\tif (!mi) {\n\t\terr = stun_ereply(proto, sock, src, 0, msg,\n\t\t\t\t  401, \"Unauthorized\",\n\t\t\t\t  NULL, 0, false, 2,\n\t\t\t\t  STUN_ATTR_REALM, turn->auth_realm,\n\t\t\t\t  STUN_ATTR_NONCE,\n\t\t\t\t    mknonce(turn, nstr, now, src));\n\t\tgoto unauth;\n\t}\n\n\tif (turn->error_scode) {\n\n\t\terr = stun_ereply(proto, sock, src, 0, msg,\n\t\t\t\t  turn->error_scode, \"Error\",\n\t\t\t\t  NULL, 0, false, 2,\n\t\t\t\t  STUN_ATTR_REALM, turn->auth_realm,\n\t\t\t\t  STUN_ATTR_NONCE,\n\t\t\t\t    mknonce(turn, nstr, now, src));\n\n\t\tturn->error_scode = 0;\n\n\t\tgoto unauth;\n\t}\n\n\tif (!user || !realm || !nonce) {\n\t\terr = stun_ereply(proto, sock, src, 0, msg,\n\t\t\t\t  400, \"Bad Request\",\n\t\t\t\t  NULL, 0, false, 0);\n\t\tgoto unauth;\n\t}\n\n\tif (!nonce_validate(turn, nonce->v.nonce, now, src)) {\n\t\terr = stun_ereply(proto, sock, src, 0, msg,\n\t\t\t\t  438, \"Stale Nonce\",\n\t\t\t\t  NULL, 0, false, 2,\n\t\t\t\t  STUN_ATTR_REALM, turn->auth_realm,\n\t\t\t\t  STUN_ATTR_NONCE,\n\t\t\t\t    mknonce(turn, nstr, now, src));\n\t\tgoto unauth;\n\t}\n\n\tctx->keylen = MD5_SIZE;\n\n\tif (auth_get_ha1(turn, user->v.username, ctx->key)) {\n\t\tDEBUG_NOTICE(\"auth: unknown user '%s' (%j)\\n\",\n\t\t\t     user->v.username, src);\n\t\terr = stun_ereply(proto, sock, src, 0, msg,\n\t\t\t\t  401, \"Unauthorized\",\n\t\t\t\t  NULL, 0, false, 2,\n\t\t\t\t  STUN_ATTR_REALM, turn->auth_realm,\n\t\t\t\t  STUN_ATTR_NONCE,\n\t\t\t\t    mknonce(turn, nstr, now, src));\n\t\tgoto unauth;\n\t}\n\n\tif (stun_msg_chk_mi(msg, ctx->key, ctx->keylen)) {\n\t\tDEBUG_NOTICE(\"auth: bad password for user '%s' (%j)\\n\",\n\t\t\t     user->v.username, src);\n\t\terr = stun_ereply(proto, sock, src, 0, msg,\n\t\t\t\t  401, \"Unauthorized\",\n\t\t\t\t  NULL, 0, false, 2,\n\t\t\t\t  STUN_ATTR_REALM, turn->auth_realm,\n\t\t\t\t  STUN_ATTR_NONCE,\n\t\t\t\t    mknonce(turn, nstr, now, src));\n\t\tgoto unauth;\n\t}\n\n\treturn false;\n\n unauth:\n\tif (err) {\n\t\tDEBUG_WARNING(\"auth reply error: %m\\n\", err);\n\t}\n\n\treturn true;\n}\n\n\nstatic void process_msg(struct turnserver *turn, int proto, void *sock,\n\t\t\tconst struct sa *src, struct mbuf *mb)\n{\n\tstruct auth_context ctx = {0};\n\tstruct stun_msg *msg = NULL;\n\tstruct sa laddr;\n\tint err = 0;\n\tconst uint32_t alloc_lifetime = TURN_DEFAULT_LIFETIME;\n\n\tif (stun_msg_decode(&msg, mb, NULL)) {\n\n\t\tuint16_t numb, len;\n\t\tstruct channel *chan;\n\n\t\tif (!turn->us_relay)\n\t\t\treturn;\n\n\t\t++turn->n_raw;\n\n\t\tnumb = ntohs(mbuf_read_u16(mb));\n\t\tlen  = ntohs(mbuf_read_u16(mb));\n\n\t\tif (mbuf_get_left(mb) < len) {\n\t\t\tDEBUG_WARNING(\"short length: %zu < %u\\n\",\n\t\t\t\t      mbuf_get_left(mb), len);\n\t\t}\n\n\t\tchan = find_channel_numb(turn, numb);\n\t\tif (!chan) {\n\t\t\tDEBUG_WARNING(\"channel not found: numb=%u\\n\", numb);\n\t\t\treturn;\n\t\t}\n\n\t\t/* relay data from channel to peer */\n\t\t(void)udp_send(turn->us_relay, &chan->peer, mb);\n\t\treturn;\n\t}\n\n#if 0\n\tre_printf(\"process: %s:%p:%J %s\\n\",\n\t\t  net_proto2name(proto), sock, src,\n\t\t  stun_method_name(stun_msg_method(msg)));\n#endif\n\n\tif (stun_msg_class(msg) == STUN_CLASS_REQUEST) {\n\n\t\tbool unauth = auth_request(turn, &ctx, proto, sock, src, msg);\n\t\tif (unauth)\n\t\t\tgoto out;\n\t}\n\n\tswitch (stun_msg_method(msg)) {\n\n\tcase STUN_METHOD_ALLOCATE:\n\t\t/* Max 1 allocation for now */\n\t\t++turn->n_allocate;\n\n\t\tif (turn->us_relay) {\n\t\t\terr = EALREADY;\n\t\t\tgoto out;\n\t\t}\n\n\t\tturn->cli = *src;\n\n\t\terr = sa_set_str(&laddr, turn->addr, 0);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = udp_listen(&turn->us_relay, &laddr,\n\t\t\t\t relay_udp_recv, turn);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = udp_local_get(turn->us_relay, &turn->relay);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tudp_rxbuf_presz_set(turn->us_relay, 4);\n\n\t\terr = stun_reply(proto, sock, src, 0,\n\t\t\t\t msg, ctx.key, ctx.keylen, false,\n\t\t\t\t 3,\n\t\t\t\t STUN_ATTR_XOR_MAPPED_ADDR, src,\n\t\t\t\t STUN_ATTR_XOR_RELAY_ADDR, &turn->relay,\n\t\t\t\t STUN_ATTR_LIFETIME, &alloc_lifetime);\n\t\tbreak;\n\n\tcase STUN_METHOD_CREATEPERM: {\n\t\tstruct stun_attr *peer;\n\n\t\t++turn->n_createperm;\n\n\t\tpeer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR);\n\t\tTEST_ASSERT(peer != NULL);\n\n\t\tadd_permission(turn, &peer->v.xor_peer_addr);\n\n\t\t/* todo: install permissions and check them */\n\t\terr = stun_reply(proto, sock, src, 0,\n\t\t\t\t msg, ctx.key, ctx.keylen, false,\n\t\t\t\t 0);\n\t}\n\t\tbreak;\n\n\tcase STUN_METHOD_CHANBIND: {\n\t\tstruct stun_attr *chnr, *peer;\n\n\t\t++turn->n_chanbind;\n\n\t\tTEST_ASSERT(turn->us_relay != NULL);\n\n\t\tchnr = stun_msg_attr(msg, STUN_ATTR_CHANNEL_NUMBER);\n\t\tpeer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR);\n\t\tif (!chnr || !peer) {\n\t\t\tDEBUG_WARNING(\"CHANBIND: missing chnr/peer attrib\\n\");\n\t\t\tgoto out;\n\t\t}\n\n\t\tTEST_ASSERT(turn->chanc < RE_ARRAY_SIZE(turn->chanv));\n\t\tturn->chanv[turn->chanc].nr   = chnr->v.channel_number;\n\t\tturn->chanv[turn->chanc].peer = peer->v.xor_peer_addr;\n\t\t++turn->chanc;\n\n\t\terr = stun_reply(proto, sock, src, 0,\n\t\t\t\t msg, ctx.key, ctx.keylen, false,\n\t\t\t\t 0);\n\t}\n\t\tbreak;\n\n\tcase STUN_METHOD_SEND: {\n\t\tstruct stun_attr *peer, *data;\n\n\t\t++turn->n_send;\n\n\t\tTEST_ASSERT(turn->us_relay != NULL);\n\n\t\tpeer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR);\n\t\tdata = stun_msg_attr(msg, STUN_ATTR_DATA);\n\n\t\tif (!peer || !data) {\n\t\t\tDEBUG_WARNING(\"SEND: missing peer/data attrib\\n\");\n\t\t\tgoto out;\n\t\t}\n\n\t\t/* check for valid Permission */\n\t\tif (!find_permission(turn, &peer->v.xor_peer_addr)) {\n\t\t\tDEBUG_NOTICE(\"no permission to peer %j\\n\",\n\t\t\t\t     &peer->v.xor_peer_addr);\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = udp_send(turn->us_relay, &peer->v.xor_peer_addr,\n\t\t\t       &data->v.data);\n\t}\n\t\tbreak;\n\n\tcase STUN_METHOD_REFRESH: {\n\t\tuint32_t lifetime = 1; /* short test lifetime */\n\t\terr = stun_reply(proto, sock, src, 0, msg, NULL, 0, false, 1,\n\t\t\t\t STUN_ATTR_LIFETIME, &lifetime);\n\t}\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"unknown STUN method: %s\\n\",\n\t\t\t      stun_method_name(stun_msg_method(msg)));\n\t\terr = EPROTO;\n\t\tbreak;\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err && stun_msg_class(msg) == STUN_CLASS_REQUEST) {\n\t\t(void)stun_ereply(proto, sock, src, 0, msg,\n\t\t\t\t  500, \"Server Error\",\n\t\t\t\t  NULL, 0, false, 0);\n\t}\n\n\tmem_deref(msg);\n}\n\n\n/* Simulated TURN server */\nstatic void srv_udp_recv(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct turnserver *turn = arg;\n\n\tprocess_msg(turn, IPPROTO_UDP, turn->us, src, mb);\n}\n\n\nstatic void tcp_estab_handler(void *arg)\n{\n\tstruct turnserver *turn = arg;\n\t(void)turn;\n}\n\n\nstatic void tcp_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct turnserver *conn = arg;\n\tint err = 0;\n\n\tif (conn->mb) {\n\t\tsize_t pos;\n\n\t\tpos = conn->mb->pos;\n\n\t\tconn->mb->pos = conn->mb->end;\n\n\t\terr = mbuf_write_mem(conn->mb, mbuf_buf(mb),mbuf_get_left(mb));\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"tcp: buffer write error: %m\\n\", err);\n\t\t\tgoto out;\n\t\t}\n\n\t\tconn->mb->pos = pos;\n\t}\n\telse {\n\t\tconn->mb = mem_ref(mb);\n\t}\n\n\tfor (;;) {\n\n\t\tsize_t len, pos, end;\n\t\tuint16_t typ;\n\n\t\tif (mbuf_get_left(conn->mb) < 4)\n\t\t\tbreak;\n\n\t\ttyp = ntohs(mbuf_read_u16(conn->mb));\n\t\tlen = ntohs(mbuf_read_u16(conn->mb));\n\n\t\tif (len > TCP_MAX_LENGTH) {\n\t\t\tre_printf(\"tcp: bad length: %zu\\n\", len);\n\t\t\terr = EBADMSG;\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (typ < 0x4000)\n\t\t\tlen += STUN_HEADER_SIZE;\n\t\telse if (typ < 0x8000)\n\t\t\tlen += 4;\n\t\telse {\n\t\t\tre_printf(\"tcp: bad type: 0x%04x\\n\", typ);\n\t\t\terr = EBADMSG;\n\t\t\tgoto out;\n\t\t}\n\n\t\tconn->mb->pos -= 4;\n\n\t\tif (mbuf_get_left(conn->mb) < len)\n\t\t\tbreak;\n\n\t\tpos = conn->mb->pos;\n\t\tend = conn->mb->end;\n\n\t\tconn->mb->end = pos + len;\n\n\t\tprocess_msg(conn, IPPROTO_TCP, conn->tc, &conn->paddr,\n\t\t\t    conn->mb);\n\n\t\t/* 4 byte alignment */\n\t\twhile (len & 0x03)\n\t\t\t++len;\n\n\t\tconn->mb->pos = pos + len;\n\t\tconn->mb->end = end;\n\n\t\tif (conn->mb->pos >= conn->mb->end) {\n\t\t\tconn->mb = mem_deref(conn->mb);\n\t\t\tbreak;\n\t\t}\n\t}\n\n out:\n\tif (err) {\n\t\tconn->mb = mem_deref(conn->mb);\n\t}\n}\n\n\nstatic void tcp_close_handler(int err, void *arg)\n{\n\tstruct turnserver *turn = arg;\n\t(void)err;\n\n\tturn->tc = mem_deref(turn->tc);\n}\n\n\nstatic void tcp_conn_handler(const struct sa *peer, void *arg)\n{\n\tstruct turnserver *turn = arg;\n\tint err = 0;\n\n\tif (turn->tc) {\n\t\ttcp_reject(turn->ts);\n\t}\n\telse {\n\t\terr = tcp_accept(&turn->tc, turn->ts, tcp_estab_handler,\n\t\t\t\t tcp_recv_handler, tcp_close_handler, turn);\n\t\tif (err)\n\t\t\ttcp_reject(turn->ts);\n\n\t\tturn->paddr = *peer;\n\t}\n}\n\n\nstatic void destructor(void *arg)\n{\n\tstruct turnserver *turn = arg;\n\n\tmem_deref(turn->us);\n\tmem_deref(turn->us_relay);\n\tmem_deref(turn->tc);\n\tmem_deref(turn->ts);\n\tmem_deref(turn->mb);\n}\n\n\nint turnserver_alloc(struct turnserver **turnp, const char *addr)\n{\n\tstruct turnserver *turn;\n\tstruct sa laddr;\n\tint err = 0;\n\n\tif (!turnp)\n\t\treturn EINVAL;\n\n\tturn = mem_zalloc(sizeof(*turn), destructor);\n\tif (!turn)\n\t\treturn ENOMEM;\n\n\tstr_ncpy(turn->addr, addr, sizeof(turn->addr));\n\n\terr = sa_set_str(&laddr, addr, 0);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_listen(&turn->us, &laddr, srv_udp_recv, turn);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_local_get(turn->us, &turn->laddr);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_listen(&turn->ts, &laddr, tcp_conn_handler, turn);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_sock_local_get(turn->ts, &turn->laddr_tcp);\n\tif (err)\n\t\tgoto out;\n\n\tturn->auth_realm = \"RETEST-REALM\";\n\tturn->auth_secret = 1234;\n\n out:\n\tif (err)\n\t\tmem_deref(turn);\n\telse\n\t\t*turnp = turn;\n\n\treturn err;\n}\n\n\nvoid turnserver_force_error(struct turnserver *turn, uint16_t scode)\n{\n\tif (!turn)\n\t\treturn;\n\n\tturn->error_scode = scode;\n}\n"
  },
  {
    "path": "test/mqueue.c",
    "content": "/**\n * @file mqueue.c Message queue testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"mqueue_test\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define NUM_EVENTS 3\n\n\nstruct test {\n\tint idv[NUM_EVENTS];\n\tvoid *datav[NUM_EVENTS];\n\tunsigned idc;\n};\n\n\nstatic void mqueue_handler(int id, void *data, void *arg)\n{\n\tstruct test *test = arg;\n\n\ttest->idv  [test->idc] = id;\n\ttest->datav[test->idc] = data;\n\n\ttest->idc++;\n\n\tif (test->idc >= NUM_EVENTS)\n\t\tre_cancel();\n}\n\n\nint test_mqueue(void)\n{\n\tstruct mqueue *mq;\n\tstruct test test;\n\tint i;\n\tint err;\n\n\tmemset(&test, 0, sizeof(test));\n\n\terr = mqueue_alloc(&mq, mqueue_handler, &test);\n\tif (err)\n\t\treturn err;\n\n\tfor (i=0; i<NUM_EVENTS; i++) {\n\t\terr = mqueue_push(mq, 42+i, &test);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = re_main_timeout(100);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(NUM_EVENTS, test.idc);\n\tfor (i=0; i<NUM_EVENTS; i++) {\n\t\tTEST_EQUALS(42+i, test.idv[i]);\n\t\tTEST_EQUALS(&test, test.datav[i]);\n\t}\n\n out:\n\tmem_deref(mq);\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/net.c",
    "content": "/**\n * @file net.c Network Testcode\n */\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_net\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic bool ipv6_handler(const char *ifname, const struct sa *sa, void *arg)\n{\n\tbool *supp = arg;\n\t(void)ifname;\n\n\tif (AF_INET6 == sa_af(sa)) {\n\t\t*supp = true;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nbool test_ipv6_supported(void)\n{\n\tbool supp = false;\n\n\tnet_if_apply(ipv6_handler, &supp);\n\n\treturn supp;\n}\n\n\nint test_net_dst_source_addr_get(void)\n{\n\tstruct sa dst;\n\tstruct sa ip;\n\tint err;\n\n\tsa_init(&dst, AF_INET);\n\tsa_init(&ip, AF_UNSPEC);\n\n\tsa_set_str(&dst, \"127.0.0.1\", 53);\n\n\terr = net_dst_source_addr_get(&dst, &ip);\n\tif (err)\n\t\treturn err;\n\n\tTEST_ASSERT(sa_is_loopback(&ip));\n\n\tif (test_ipv6_supported()) {\n\n\t\tsa_init(&dst, AF_INET6);\n\t\tsa_init(&ip, AF_UNSPEC);\n\t\tsa_set_str(&dst, \"::1\", 53);\n\n\t\terr = net_dst_source_addr_get(&dst, &ip);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tTEST_ASSERT(sa_is_loopback(&ip));\n\t}\n\telse {\n\t\tDEBUG_NOTICE(\"ipv6 disabled\\n\");\n\t}\n\nout:\n\treturn err;\n}\n\n\nint test_net_if(void)\n{\n\tstruct sa ip;\n\tint err;\n\tchar ifname[255];\n\n\tsa_set_str(&ip, \"127.0.0.1\", 0);\n\n\terr = net_if_getname(ifname, sizeof(ifname), AF_INET, &ip);\n\tTEST_ERR(err);\n\tTEST_ASSERT(str_isset(ifname));\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/odict.c",
    "content": "/**\n * @file odict.c Testcode for Ordered Dictionary\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"odict\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstruct dtest {\n\tconst char *key;\n\tenum odict_type type;\n\tunion {\n\t\tint64_t i;\n\t\tdouble d;\n\t\tchar *s;\n\t\tbool b;\n\t} u;\n};\n\n\nstatic int compare(const struct dtest *test, const struct odict_entry *entry)\n{\n\tint err = 0;\n\n\tTEST_ASSERT(entry != NULL);\n\n\tTEST_STRCMP(test->key, str_len(test->key),\n\t\t    odict_entry_key(entry), strlen(odict_entry_key(entry)));\n\n\tTEST_EQUALS(test->type, odict_entry_type(entry));\n\n\tswitch (test->type) {\n\n\tcase ODICT_INT:\n\t\tTEST_EQUALS(test->u.i, odict_entry_int(entry));\n\t\tbreak;\n\n\tcase ODICT_BOOL:\n\t\tTEST_EQUALS(test->u.b, odict_entry_boolean(entry));\n\t\tbreak;\n\n\tcase ODICT_DOUBLE:\n\t\tTEST_EQUALS(test->u.d, odict_entry_dbl(entry));\n\t\tbreak;\n\n\tcase ODICT_STRING:\n\t\tTEST_STRCMP(test->u.s, str_len(test->u.s),\n\t\t\t    odict_entry_str(entry),\n\t\t\t    str_len(odict_entry_str(entry)));\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"cannot compare type %d\\n\", test->type);\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n out:\n\treturn err;\n}\n\n\nint test_odict(void)\n{\n\tstatic const struct dtest testv[] = {\n\t\t{\"a\",   ODICT_INT,     {.i = 42  }         },\n\t\t{\"b\",   ODICT_DOUBLE,  {.d = 3.14}         },\n\t\t{\"c\",   ODICT_STRING,  {.s = \"rattarei\"}   },\n\t\t{\"d\",   ODICT_BOOL,    {.b = true  }       },\n\t\t{\"e\",   ODICT_BOOL,    {.b = false  }       },\n\n\t\t{\"ha73ofhjausdhkjquhriasjdhkaksjdkjqkjhwkjhqweaaaaaa\",\n\t\t ODICT_INT,     {.i = 617284773  }         },\n\t\t{\"mkkpnaidhs747fusyudyaisdiayiakakakaakajdjiwi123456\",\n\t\t ODICT_INT,     {.i = 12345678  }         },\n\t\t{\"lllalsidjjlalksjdlaksjdlkjasd\",\n\t\t ODICT_DOUBLE,  {.d = 123456.123456}         },\n\t\t{\"8adhw6fjshdkhjaskjdhakdhaskhdkjh\",\n\t\t ODICT_DOUBLE,  {.d = -0.007}         },\n\n\t\t{\"hafgsdudue6dhcgfjgieiwuehsdkjhsdjkhasdkjasdkjhasd\",\n\t\t ODICT_STRING,\n\t\t {.s = \"kasjdsuher7yw783897njxdvkhjskhdkhsdkhjsdkhjasdasd\"}\n\t\t},\n\t\t{\"liajsdoiausdoaudoaisudaoisdjalsijdalsidjalidjaslidj\",\n\t\t ODICT_STRING,\n\t\t {.s = \"bdjkhdiuasd7y78yw4y78ryuseyugfasygdasygdh\"}\n\t\t},\n\t};\n\tstruct odict *dict = NULL;\n\tchar *debug_str = NULL;\n\tsize_t i;\n\tint err;\n\n\tTEST_ASSERT(odict_type_iscontainer(ODICT_OBJECT));\n\tTEST_ASSERT(odict_type_iscontainer(ODICT_ARRAY));\n\tTEST_ASSERT(!odict_type_iscontainer(ODICT_INT));\n\n\tTEST_ASSERT(odict_type_isreal(ODICT_INT));\n\tTEST_ASSERT(odict_type_isreal(ODICT_DOUBLE));\n\tTEST_ASSERT(odict_type_isreal(ODICT_STRING));\n\tTEST_ASSERT(odict_type_isreal(ODICT_BOOL));\n\tTEST_ASSERT(odict_type_isreal(ODICT_NULL));\n\tTEST_ASSERT(!odict_type_isreal(ODICT_OBJECT));\n\n\terr = odict_alloc(&dict, 64);\n\tif (err)\n\t\tgoto out;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tconst struct dtest *test = &testv[i];\n\t\tconst struct odict_entry *entry = NULL;\n\n\t\t/* verify that this entry does not exist (based on key) */\n\t\tTEST_ASSERT(NULL == odict_lookup(dict, test->key));\n\n\t\tswitch (test->type) {\n\n\t\tcase ODICT_INT:\n\t\t\terr = odict_entry_add(dict, test->key,\n\t\t\t\t\t      test->type, test->u.i);\n\t\t\tbreak;\n\n\t\tcase ODICT_BOOL:\n\t\t\terr = odict_entry_add(dict, test->key,\n\t\t\t\t\t      test->type, (int)test->u.b);\n\t\t\tbreak;\n\n\t\tcase ODICT_DOUBLE:\n\t\t\terr = odict_entry_add(dict, test->key,\n\t\t\t\t\t      test->type, test->u.d);\n\t\t\tbreak;\n\n\t\tcase ODICT_STRING:\n\t\t\terr = odict_entry_add(dict, test->key,\n\t\t\t\t\t      test->type, test->u.s);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\terr = EINVAL;\n\t\t\tgoto out;\n\t\t\tbreak;\n\t\t}\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\t/* verify that entry exist after adding */\n\t\tentry = odict_lookup(dict, test->key);\n\t\tTEST_ASSERT(entry != NULL);\n\n\t\terr = compare(test, entry);\n\t\tTEST_ERR(err);\n\t}\n\n\t/* verify size of dictionary, after adding all entries */\n\tTEST_EQUALS(RE_ARRAY_SIZE(testv), odict_count(dict, false));\n\n\t/* compare dictionary with itself */\n\tTEST_ASSERT(odict_compare(dict, dict, false));\n\n\terr = re_sdprintf(&debug_str, \"%H\", odict_debug, dict);\n\tTEST_ERR(err);\n\tASSERT_TRUE(str_isset(debug_str));\n\n\t/* remove all entries */\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tconst struct dtest *test = &testv[i];\n\t\tstruct odict_entry *e;\n\n\t\t/* delete entry from dictionary */\n\t\todict_entry_del(dict, test->key);\n\n\t\t/* entry should not exist anymore */\n\t\te = (struct odict_entry *)odict_lookup(dict, test->key);\n\t\tTEST_ASSERT(e == NULL);\n\t}\n\n\t/* verify size of dictionary, after removing all entries */\n\tTEST_EQUALS(0, odict_count(dict, false));\n\n out:\n\tmem_deref(debug_str);\n\tmem_deref(dict);\n\treturn err;\n}\n\n\nint test_odict_pl(void)\n{\n\tstruct odict *od = NULL;\n\tconst struct odict_entry *e;\n\tint err;\n\n\tstatic struct pl pl=PL(\"liajsdoiausdoaudoaisudaoisdjal\");\n\n\terr = odict_alloc(&od, 64);\n\tif (err)\n\t\tgoto out;\n\n\t/* add pl */\n\terr = odict_pl_add(od, \"pl1\", &pl);\n\tTEST_ERR(err);\n\n\te = odict_lookup(od, \"pl1\");\n\tTEST_ASSERT(e != NULL);\n\n\tTEST_STRCMP(pl.p, pl.l,\n\t     odict_entry_str(e), str_len(odict_entry_str(e)));\n\n\tmem_deref((struct odict_entry *) e);\n\t/* entry should not exist anymore */\n\te = (struct odict_entry *)odict_lookup(od, \"pl1\");\n\tTEST_ASSERT(e == NULL);\n\n out:\n\tmem_deref(od);\n\treturn err;\n}\n\n\nint test_odict_array(void)\n{\n\tstruct odict *arr = NULL;\n\tconst struct odict_entry *e;\n\tint err;\n\n\terr = odict_alloc(&arr, 64);\n\tif (err)\n\t\tgoto out;\n\n\t/* test an empty array */\n\tTEST_EQUALS(0, odict_count(arr, false));\n\tTEST_ASSERT(NULL == odict_lookup(arr, \"0\"));\n\tTEST_ASSERT(NULL == odict_lookup(arr, \"255\"));\n\n\t/* add some elements of variying types */\n\terr = odict_entry_add(arr, \"0\", ODICT_STRING, \"hei\");\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(1, odict_count(arr, false));\n\n\terr  = odict_entry_add(arr, \"1\", ODICT_INT, 1LL);\n\terr |= odict_entry_add(arr, \"2\", ODICT_INT, 2LL);\n\terr |= odict_entry_add(arr, \"3\", ODICT_INT, 3LL);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(4, odict_count(arr, false));\n\n\t/* verify that elements are correct */\n\te = odict_lookup(arr, \"0\");\n\tTEST_ASSERT(e != NULL);\n\tTEST_EQUALS(ODICT_STRING, odict_entry_type(e));\n\tTEST_STRCMP(\"hei\", (size_t)3, odict_entry_str(e),\n\t\t    str_len(odict_entry_str(e)));\n\n\te = odict_lookup(arr, \"3\");\n\tTEST_ASSERT(e != NULL);\n\tTEST_EQUALS(ODICT_INT, odict_entry_type(e));\n\tTEST_EQUALS(3LL, odict_entry_int(e));\n\n\tuint64_t num = 0;\n\tbool ret = odict_get_number(arr, &num, \"1\");\n\tASSERT_TRUE(ret);\n\tASSERT_EQ(1, num);\n\n\tret = odict_get_number(arr, &num, \"hei\");\n\tASSERT_TRUE(!ret);\n\n#if 0\n\tre_printf(\"%H\\n\", odict_debug, arr);\n#endif\n\n out:\n\tmem_deref(arr);\n\treturn err;\n}\n"
  },
  {
    "path": "test/pcp.c",
    "content": "/**\n * @file pcp.c Port Control Protocol (PCP) Testcode\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"pcptest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int test_pcp_request_loop(size_t offset)\n{\n\tstruct mbuf *mb;\n\tstruct pcp_msg *msg = NULL;\n\tstatic const uint8_t nonce[12] = {\n\t\t0xc0, 0xff, 0xee, 0x00,\n\t\t0xc0, 0xff, 0xee, 0x00,\n\t\t0xc0, 0xff, 0xee, 0x00,\n\t};\n\tsize_t i;\n\tint err = 0;\n\tconst uint16_t int_port = 2000;\n\tstatic const struct {\n\t\tenum pcp_opcode opcode;\n\t\tuint32_t lifetime;\n\t\tconst char *ipaddr;\n\t} testreqv[] = {\n\t\t{PCP_MAP,       600,    \"10.0.0.20\"                          },\n\t\t{PCP_MAP,       3600,   \"2a02:fe0:cf12:91:226:8ff:fee1:cdf3\" },\n\t\t{PCP_PEER,      600,    \"46.123.65.9\"                        },\n\t\t{PCP_PEER,      999999, \"2a02:fe0:cf12:91:226:8ff:fee1:cdf3\" },\n\t};\n\n\tmb = mbuf_alloc(offset + 512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testreqv); i++) {\n\n\t\tstruct sa sa;\n\t\tstruct pcp_peer peer;\n\n\t\terr = sa_set_str(&sa, testreqv[i].ipaddr, int_port);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tmemcpy(peer.map.nonce, nonce, sizeof(peer.map.nonce));\n\t\tpeer.map.proto       = IPPROTO_UDP;\n\t\tpeer.map.int_port    = int_port;\n\t\tpeer.map.ext_addr    = sa;\n\t\tpeer.remote_addr = sa;\n\n\t\tmb->pos = mb->end = offset;\n\t\terr = pcp_msg_req_encode(mb, testreqv[i].opcode,\n\t\t\t\t\t testreqv[i].lifetime,\n\t\t\t\t\t &sa, &peer, 0);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_ASSERT(mb->pos != offset);\n\t\tTEST_ASSERT(mb->end != offset);\n\n\t\tmb->pos = offset;\n\t\terr = pcp_msg_decode(&msg, mb);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_EQUALS(PCP_VERSION, msg->hdr.version);\n\t\tTEST_EQUALS(false, msg->hdr.resp);\n\t\tTEST_EQUALS(testreqv[i].opcode, msg->hdr.opcode);\n\t\tTEST_EQUALS(testreqv[i].lifetime, msg->hdr.lifetime);\n\t\tTEST_SACMP(&sa, &msg->hdr.cli_addr, SA_ADDR);\n\n\t\tswitch (testreqv[i].opcode) {\n\n\t\tcase PCP_MAP:\n\t\tcase PCP_PEER:\n\t\t\tTEST_MEMCMP(nonce, sizeof(nonce),\n\t\t\t\t    msg->pld.map.nonce,\n\t\t\t\t    sizeof(msg->pld.map.nonce));\n\t\t\tTEST_EQUALS(IPPROTO_UDP, msg->pld.map.proto);\n\t\t\tTEST_EQUALS(int_port, msg->pld.map.int_port);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\tTEST_EQUALS(0, list_count(&msg->optionl));\n\n\t\tmsg = mem_deref(msg);\n\t}\n\n out:\n\tmem_deref(msg);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic const uint8_t peer_request[] = {\n\n\t0x02, 0x02, 0x00, 0x00,  /*  version | opcode             */\n\t0x00, 0x00, 0x02, 0x58,  /*  lifetime                     */\n\t0x2a, 0x00, 0x14, 0x50,  /*  .                            */\n\t0x40, 0x0f, 0x08, 0x03,  /*  |client IP-address           */\n\t0x00, 0x00, 0x00, 0x00,  /*  |                            */\n\t0x00, 0x00, 0x10, 0x00,  /*  '                            */\n\n\t0xa9, 0x5f, 0xc9, 0xb7,  /*                               */\n\t0x12, 0x3b, 0xa9, 0x66,  /* nonce                         */\n\t0x33, 0xcd, 0xe2, 0xb9,  /*                               */\n\n\t0x11, 0x00, 0x00, 0x00,  /* protocol                      */\n\t0x13, 0x8c, 0x13, 0x8d,  /* internal port | external port */\n\t0x2a, 0x00, 0x14, 0x50,  /*  .                            */\n\t0x40, 0x0f, 0x08, 0x03,  /*  |external IP Address         */\n\t0x00, 0x00, 0x00, 0x00,  /*  |                            */\n\t0x00, 0x00, 0x20, 0x00,  /*  '                            */\n\n\t0x13, 0x8e, 0x00, 0x00,  /* remote peer port              */\n\n\t0x2a, 0x00, 0x14, 0x50,  /*                               */\n\t0x40, 0x0f, 0x08, 0x03,  /* remote peer IP-address        */\n\t0x00, 0x00, 0x00, 0x00,  /*                               */\n\t0x00, 0x00, 0x30, 0x00,  /*                               */\n\n\t0x01, 0x00, 0x00, 0x10,  /* opcode THIRD_PARTY header     */\n\t0x2a, 0x00, 0x14, 0x50,  /* .                             */\n\t0x40, 0x0f, 0x08, 0x03,  /* |internal IP address          */\n\t0x00, 0x00, 0x00, 0x00,  /* |                             */\n\t0x00, 0x00, 0x40, 0x00,  /* '                             */\n\n\t};\n\n\nstatic int test_pcp_message(void)\n{\n\tenum pcp_opcode opcode = PCP_PEER;\n\tuint32_t lifetime = 600;\n\tstatic const uint8_t nonce[] = {\n\t\t0xa9, 0x5f, 0xc9, 0xb7,\n\t\t0x12, 0x3b, 0xa9, 0x66,\n\t\t0x33, 0xcd, 0xe2, 0xb9,\n\t};\n\tint proto = IPPROTO_UDP;\n\tstruct pcp_peer peer;\n\tstruct sa int_addr, thi_addr;\n\tstruct mbuf *mb;\n\tstruct pcp_msg *msg = NULL;\n\tenum {DUMMY_OFFSET = 64};\n\tint err = 0;\n\n\tmemcpy(peer.map.nonce, nonce, sizeof(peer.map.nonce));\n\tpeer.map.proto       = IPPROTO_UDP;\n\tpeer.map.int_port    = 5004;\n\n\terr |= sa_set_str(&int_addr, \"2a00:1450:400f:803::1000\", 0);\n\terr |= sa_set_str(&peer.map.ext_addr, \"2a00:1450:400f:803::2000\",\n\t\t\t  5005);\n\terr |= sa_set_str(&peer.remote_addr, \"2a00:1450:400f:803::3000\", 5006);\n\terr |= sa_set_str(&thi_addr, \"2a00:1450:400f:803::4000\", 0);\n\tif (err)\n\t\treturn err;\n\n\tmb = mbuf_alloc(DUMMY_OFFSET + 512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tmb->pos = DUMMY_OFFSET;\n\terr = pcp_msg_req_encode(mb, opcode, lifetime, &int_addr, &peer,\n\t\t\t\t 1, PCP_OPTION_THIRD_PARTY, &thi_addr);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_ASSERT(mb->pos != DUMMY_OFFSET);\n\tTEST_ASSERT(mb->pos == mb->end);\n\n\tmb->pos = DUMMY_OFFSET;\n\n\tTEST_MEMCMP(peer_request, sizeof(peer_request),\n\t\t    mbuf_buf(mb), mbuf_get_left(mb));\n\n\terr = pcp_msg_decode(&msg, mb);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(PCP_VERSION, msg->hdr.version);\n\tTEST_EQUALS(false, msg->hdr.resp);\n\tTEST_EQUALS(opcode, msg->hdr.opcode);\n\tTEST_EQUALS(lifetime, msg->hdr.lifetime);\n\tTEST_SACMP(&int_addr, &msg->hdr.cli_addr, SA_ALL);\n\n\tTEST_MEMCMP(nonce, sizeof(nonce),\n\t\t    msg->pld.peer.map.nonce, sizeof(msg->pld.peer.map.nonce));\n\tTEST_EQUALS(proto, msg->pld.peer.map.proto);\n\tTEST_EQUALS(5004, msg->pld.peer.map.int_port);\n\tTEST_SACMP(&peer.map.ext_addr, &msg->pld.peer.map.ext_addr, SA_ALL);\n\tTEST_SACMP(&peer.remote_addr, &msg->pld.peer.remote_addr, SA_ALL);\n\n out:\n\tmem_deref(mb);\n\tmem_deref(msg);\n\treturn err;\n}\n\n\nstatic int test_pcp_option(void)\n{\n\tstruct mbuf *mb;\n\tstruct pcp_option *opt = NULL;\n\tstruct sa addr;\n\tstatic const uint8_t opt_pkt[] = {\n\t\t0x01, 0x00, 0x00, 0x10,\n\t\t0x00, 0x00, 0x00, 0x00,\n\t\t0x00, 0x00, 0x00, 0x00,\n\t\t0x00, 0x00, 0xff, 0xff,\n\t\t0x0a, 0x00, 0x00, 0x01\n\t};\n\tint err;\n\n\tmb = mbuf_alloc(64);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = sa_set_str(&addr, \"10.0.0.1\", 0);\n\tif (err)\n\t\tgoto out;\n\n\terr = pcp_option_encode(mb, PCP_OPTION_THIRD_PARTY, &addr);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_MEMCMP(opt_pkt, sizeof(opt_pkt), mb->buf, mb->end);\n\n\tmb->pos = 0;\n\terr = pcp_option_decode(&opt, mb);\n\n\tTEST_EQUALS(PCP_OPTION_THIRD_PARTY, opt->code);\n\tTEST_SACMP(&addr, &opt->u.third_party, SA_ADDR);\n\n out:\n\tmem_deref(opt);\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nint test_pcp(void)\n{\n\tint err;\n\n\terr = test_pcp_request_loop(0);\n\tTEST_ERR(err);\n\n\terr = test_pcp_request_loop(4);\n\tTEST_ERR(err);\n\n\terr = test_pcp_message();\n\tTEST_ERR(err);\n\n\terr = test_pcp_option();\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/remain.c",
    "content": "/**\n * @file remain.c Testcode for re main loop\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"remain\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstruct data {\n\tthrd_t tid;\n\tmtx_t *mutex;\n\tbool thread_started;\n\tbool thread_exited;\n\tunsigned tmr_called;\n\tint err;\n};\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct data *data = arg;\n\tint err = 0;\n\n\tmtx_lock(data->mutex);\n\n\t/* verify that timer is called from the new thread */\n\tTEST_ASSERT(0 != thrd_equal(data->tid, thrd_current()));\n\n\t++data->tmr_called;\n\n out:\n\tif (err)\n\t\tdata->err = err;\n\n\tmtx_unlock(data->mutex);\n\n\t/* Stop re_main loop */\n\tre_cancel();\n}\n\n\nstatic int thread_handler(void *arg)\n{\n\tstruct data *data = arg;\n\tstruct tmr tmr;\n\tint err;\n\n\tdata->thread_started = true;\n\n\ttmr_init(&tmr);\n\n\t/* Add a worker thread for this thread */\n\terr = re_thread_init();\n\tif (err) {\n\t\tDEBUG_WARNING(\"re thread init: %m\\n\", err);\n\t\tdata->err = err;\n\t\treturn err;\n\t}\n\n#ifndef WIN32\n\terr = fd_setsize(-1);\n\tTEST_ERR(err);\n#endif\n\terr = re_thread_init();\n\tASSERT_TRUE(err == 0 || err == EALREADY);\n\n\ttmr_start(&tmr, 1, tmr_handler, data);\n\n\t/* run the main loop now */\n\terr = re_main(NULL);\n\nout:\n\tif (err)\n\t\tdata->err = err;\n\ttmr_cancel(&tmr);\n\n\ttmr_debug();\n\n\t/* Remove the worker thread for this thread */\n\tre_thread_close();\n\n\tdata->thread_exited = true;\n\n\treturn err;\n}\n\n\nstatic int test_remain_thread(void)\n{\n\tstruct data data = { 0 };\n\tint err;\n\n\terr = mutex_alloc(&data.mutex);\n\tif (err)\n\t\treturn err;\n\n\terr = thread_create_name(&data.tid, \"remain\", thread_handler, &data);\n\tTEST_ERR(err);\n\n\t/* wait for timer to be called */\n\tfor (size_t i=0; i<500; i++) {\n\t\tmtx_lock(data.mutex);\n\n\t\tif (data.tmr_called || data.err) {\n\t\t\tmtx_unlock(data.mutex);\n\t\t\tbreak;\n\t\t}\n\n\t\tmtx_unlock(data.mutex);\n\n\t\tsys_msleep(1);\n\t}\n\n\tdata.mutex = mem_deref(data.mutex);\n\n\t/* wait for thread to end */\n\tthrd_join(data.tid, NULL);\n\n\tif (data.err)\n\t\treturn data.err;\n\n\tTEST_ASSERT(data.thread_started);\n\tTEST_ASSERT(data.thread_exited);\n\tTEST_EQUALS(1, data.tmr_called);\n\tTEST_EQUALS(0, data.err);\n\n out:\n\tmem_deref(data.mutex);\n\treturn err;\n}\n\n\nint test_remain(void)\n{\n\tint err = 0;\n\n\terr = test_remain_thread();\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/rtcp.c",
    "content": "/**\n * @file rtcp.c Tests for RTCP\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"rtcp_test\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/*\n *  .------.       RTP\n *  | RTP  | <--------------- [ Packet Generator ]\n *  | RTCP |\n *  '------'\n */\n\n\nstatic const uint32_t GENERATOR_SSRC = 0x00000001;\n\n\nstruct fixture {\n\tstruct rtp_sock *rtp;\n\tstruct sa rtp_addr;\n\tsize_t n_recv;\n\tsize_t num_packets;\n};\n\n\nstatic int send_rtp_packet(struct udp_sock *us, const struct sa *dst,\n\t\t\t   uint16_t seq, uint32_t ssrc)\n{\n\tstruct rtp_header hdr;\n\tstruct mbuf *mb = mbuf_alloc(256);\n\tint err;\n\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tmemset(&hdr, 0, sizeof(hdr));\n\thdr.ver  = RTP_VERSION;\n\thdr.seq  = seq;\n\thdr.ts   = 0;\n\thdr.ssrc = ssrc;\n\n\terr = rtp_hdr_encode(mb, &hdr);\n\tif (err)\n\t\tgoto out;\n\tmbuf_fill(mb, 160, 0x00);\n\tmb->pos = 0;\n\n\terr = udp_send(us, dst, mb);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstatic void rtp_recv(const struct sa *src, const struct rtp_header *hdr,\n\t\t     struct mbuf *mb, void *arg)\n{\n\tstruct fixture *f = arg;\n\t(void)src;\n\t(void)hdr;\n\t(void)mb;\n\n\t++f->n_recv;\n\n\tif (f->n_recv >= f->num_packets) {\n\t\tre_cancel();\n\t}\n}\n\n\nstatic int fixture_init(struct fixture *f)\n{\n\tint err;\n\n\tmemset(f, 0, sizeof(*f));\n\n\terr = sa_set_str(&f->rtp_addr, \"127.0.0.1\", 0);\n\tTEST_ERR(err);\n\n\terr = rtp_listen(&f->rtp, IPPROTO_UDP, &f->rtp_addr, 10000, 49152,\n\t\t\t true, rtp_recv, NULL, f);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_local_get(rtp_sock(f->rtp), &f->rtp_addr);\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n\n\nstatic void fixture_close(struct fixture *f)\n{\n\tmem_deref(f->rtp);\n}\n\n\nstatic int test_loss(const uint16_t *seqv, size_t seqc,\n\t\t     int exp_lost)\n{\n\tstruct fixture fix, *f = &fix;\n\tstruct rtcp_stats stats;\n\tuint32_t ssrc = GENERATOR_SSRC;\n\tunsigned i;\n\tint err, e;\n\n\terr = fixture_init(f);\n\tif (err)\n\t\tgoto out;\n\n\tf->num_packets = seqc;\n\n\t/* no sources exist yet */\n\te = rtcp_stats(f->rtp, ssrc, &stats);\n\tTEST_EQUALS(ENOENT, e);\n\n\t/* start the RTP packet generator, send X number of RTP packets\n\t * to the RTP-stack (fixture)\n\t */\n\tfor (i=0; i<seqc; i++) {\n\t\tuint16_t seq = seqv[i];\n\n\t\terr = send_rtp_packet(rtp_sock(f->rtp), &f->rtp_addr,\n\t\t\t\t      seq, ssrc);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\terr = rtcp_stats(f->rtp, ssrc, &stats);\n\tif (err) {\n\t\tif (err == ENOENT)\n\t\t\terr = ENOMEM;\n\t\tTEST_ERR(err);\n\t}\n\n\t/* in OOM-test, detect if member/sender was not allocated */\n\tif (stats.rx.sent == 0 &&\n\t    stats.rx.lost == 0 &&\n\t    stats.rx.jit == 0) {\n\n\t\terr = ENOMEM;\n\t\tTEST_ERR(err);\n\t}\n\n\tif (test_mode == TEST_MEMORY)\n\t\tgoto out;\n\n\t/* verify expected packets sent and packet loss */\n\tTEST_EQUALS(seqc, stats.rx.sent);\n\tTEST_EQUALS(exp_lost, stats.rx.lost);\n\n\tTEST_EQUALS(seqc, f->n_recv);\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nint test_rtcp_packetloss(void)\n{\n\tstatic const uint16_t seqv1[] = {0, 1, 2};\n\tstatic const uint16_t seqv2[] = {0,1,3,2,5,4};\n\tstatic const uint16_t seqv3[] = {65534, 65535, 0, 1};\n\tstatic const uint16_t seqv4[] = {65534, 0, 1};\n\tstatic const uint16_t seqv5[] = {65534, 1, 2};\n\tstatic const uint16_t seqv6[] = {65534, 1, 2, 65535};\n\tstatic const uint16_t seqv7[] = {1,2,8,9,10};\n\tint err = 0;\n\n\terr = test_loss(seqv1, RE_ARRAY_SIZE(seqv1), 0);\n\tTEST_ERR(err);\n\terr = test_loss(seqv2, RE_ARRAY_SIZE(seqv2), 0);\n\tTEST_ERR(err);\n\terr = test_loss(seqv3, RE_ARRAY_SIZE(seqv3), 0);\n\tTEST_ERR(err);\n\terr = test_loss(seqv4, RE_ARRAY_SIZE(seqv4), 1);\n\tTEST_ERR(err);\n\terr = test_loss(seqv5, RE_ARRAY_SIZE(seqv5), 2);\n\tTEST_ERR(err);\n\terr = test_loss(seqv6, RE_ARRAY_SIZE(seqv6), 1);\n\tTEST_ERR(err);\n\terr = test_loss(seqv7, RE_ARRAY_SIZE(seqv7), 5);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\nstruct agent {\n\tstruct agent *peer;\n\tstruct rtp_sock *rtp_sock;\n\tstruct sa laddr_rtp;\n\tstruct sa laddr_rtcp;\n\tunsigned step;\n\tunsigned rtp_count;\n\tunsigned psfb_count;\n\tunsigned rtpfb_count;\n\tunsigned gnack_count;\n\tunsigned app_count;\n\tint err;\n};\n\n\nstatic bool agents_are_complete(const struct agent *ag)\n{\n\treturn ag->app_count && ag->peer->app_count;\n}\n\n\nstatic int agent_send_rtcp_packet(struct agent *ag)\n{\n\tstruct mbuf *mb_chunks = NULL;\n\tstruct mbuf *mb_deltas = NULL;\n\tconst uint8_t fir_seqn = 22;\n\tint err = 0;\n\n\tswitch (ag->step) {\n\n\tcase 0:\n\t\terr = rtcp_send_fir_rfc5104(ag->rtp_sock,\n\t\t\t\t\t    rtp_sess_ssrc(ag->peer->rtp_sock),\n\t\t\t\t\t    fir_seqn);\n\t\tbreak;\n\n\tcase 1:\n\t\terr = rtcp_send_gnack(ag->rtp_sock,\n\t\t\t\t      rtp_sess_ssrc(ag->peer->rtp_sock),\n\t\t\t\t      42, 0);\n\t\tbreak;\n\n\tcase 2:\n\t\terr = rtcp_send_pli(ag->rtp_sock,\n\t\t\t\t    rtp_sess_ssrc(ag->peer->rtp_sock));\n\t\tbreak;\n\n\tcase 3: {\n\t\tmb_chunks = mbuf_alloc(32);\n\t\tmb_deltas = mbuf_alloc(32);\n\t\tif (!mb_chunks || !mb_deltas) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\tstatic const uint8_t chunks[] = {\n\t\t\t0xad, 0xe0\n\t\t};\n\t\tstatic const uint8_t deltas[] = {\n\t\t\t0x14, 0x18, 0x18, 0x38, 0x00, 0x00, 0x00\n\t\t};\n\t\tmbuf_write_mem(mb_chunks, chunks, sizeof(chunks));\n\t\tmbuf_write_mem(mb_deltas, deltas, sizeof(deltas));\n\n\t\tmbuf_set_pos(mb_chunks, 0);\n\t\tmbuf_set_pos(mb_deltas, 0);\n\n\t\tstruct twcc twcc = {\n\t\t\t.seq     = 11,\n\t\t\t.count   = 22,\n\t\t\t.reftime = 33,\n\t\t\t.fbcount = 44,\n\t\t\t.chunks  = mb_chunks,\n\t\t\t.deltas  = mb_deltas,\n\t\t};\n\n\t\terr = rtcp_send_twcc(ag->rtp_sock,\n\t\t\t\t     rtp_sess_ssrc(ag->peer->rtp_sock), &twcc);\n\t}\n\t\tbreak;\n\n\tcase 4:\n\t\t/* NOTE: must be last */\n\t\terr = rtcp_send_app(ag->rtp_sock, \"PING\", (void *)\"PONG\", 4);\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_NOTICE(\"agent_send_rtcp_packet: invalid step (%u)\\n\",\n\t\t\t     ag->step);\n\t\tbreak;\n\t}\n\n\t++ag->step;\n\n out:\n\tmem_deref(mb_chunks);\n\tmem_deref(mb_deltas);\n\n\treturn err;\n}\n\n\nstatic void rtp_recv_handler(const struct sa *src,\n\t\t\t     const struct rtp_header *hdr,\n\t\t\t     struct mbuf *mb, void *arg)\n{\n\tstruct agent *ag = arg;\n\t(void)src;\n\t(void)hdr;\n\t(void)mb;\n\n\t++ag->rtp_count;\n\n\tif (ag->step == 0) {\n\n\t\tint err = agent_send_rtcp_packet(ag);\n\t\tif (err) {\n\t\t\tag->err = err;\n\t\t\tre_cancel();\n\t\t}\n\t}\n}\n\n\nstatic void rtcp_recv_handler(const struct sa *src, struct rtcp_msg *msg,\n\t\t\t      void *arg)\n{\n\tstruct agent *ag = arg;\n\tint err = 0;\n\t(void)src;\n\n\tswitch (msg->hdr.pt) {\n\n\tcase RTCP_RTPFB:\n\t\tif (msg->r.fb.fci.gnackv->pid == 42)\n\t\t\t++ag->gnack_count;\n\t\t++ag->rtpfb_count;\n\t\terr = agent_send_rtcp_packet(ag);\n\t\tbreak;\n\n\tcase RTCP_PSFB:\n\t\t++ag->psfb_count;\n\n\t\terr = agent_send_rtcp_packet(ag);\n\t\tbreak;\n\n\tcase RTCP_APP:\n\t\t++ag->app_count;\n\t\tbreak;\n\n\tcase RTCP_SR:\n\tcase RTCP_SDES:\n\t\t/* ignore */\n\t\tbreak;\n\n\tdefault:\n\t\tDEBUG_WARNING(\"unexpected RTCP message: %H\\n\",\n\t\t\t      rtcp_msg_print, msg);\n\t\terr = EPROTO;\n\t\tbreak;\n\t}\n\n\tif (agents_are_complete(ag)) {\n\t\tre_cancel();\n\t\treturn;\n\t}\n\n\tif (err) {\n\t\tag->err = err;\n\t\tre_cancel();\n\t}\n}\n\n\nstatic int agent_init(struct agent *ag, bool mux, const char *laddr_str)\n{\n\tstruct sa laddr;\n\n\tsa_set_str(&laddr, laddr_str, 0);\n\n\tint err = rtp_listen(&ag->rtp_sock, IPPROTO_UDP,\n\t\t\t     &laddr, 1024, 65535, true,\n\t\t\t     rtp_recv_handler, rtcp_recv_handler, ag);\n\tif (err)\n\t\treturn err;\n\n\trtcp_enable_mux(ag->rtp_sock, mux);\n\n\tudp_local_get(rtp_sock(ag->rtp_sock), &ag->laddr_rtp);\n\tudp_local_get(rtcp_sock(ag->rtp_sock), &ag->laddr_rtcp);\n\n\treturn 0;\n}\n\n\nstatic int test_rtcp_loop_param(bool mux, const char *laddr)\n{\n\tstruct agent a = {0}, b = {0};\n\tstruct mbuf *mb = NULL;\n\tint err;\n\n\terr = agent_init(&a, mux, laddr);\n\tTEST_ERR(err);\n\terr = agent_init(&b, mux, laddr);\n\tTEST_ERR(err);\n\n\ta.peer = &b;\n\tb.peer = &a;\n\n\trtcp_start(a.rtp_sock, \"cname\", &b.laddr_rtcp);\n\trtcp_start(b.rtp_sock, \"cname\", &a.laddr_rtcp);\n\n\tmb = mbuf_alloc(RTP_HEADER_SIZE + 1);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tmbuf_fill(mb, 0x00, RTP_HEADER_SIZE + 1);\n\n\t/* Send some RTP-packets to enable RTCP-SR */\n\tfor (unsigned i=0; i<4; i++) {\n\n\t\tuint64_t jfs = tmr_jiffies_rt_usec();\n\n\t\tmb->pos = RTP_HEADER_SIZE;\n\n\t\terr = rtp_send(a.rtp_sock, &b.laddr_rtp, false,\n\t\t\t       false, 0, 160, jfs, mb);\n\t\tTEST_ERR(err);\n\n\t\tmb->pos = RTP_HEADER_SIZE;\n\n\t\terr = rtp_send(b.rtp_sock, &a.laddr_rtp, false,\n\t\t\t       false, 0, 160, jfs, mb);\n\t\tTEST_ERR(err);\n\t}\n\n\terr = re_main_timeout(1000);\n\tTEST_ERR(err);\n\n\terr = a.err;\n\tTEST_ERR(err);\n\terr = b.err;\n\tTEST_ERR(err);\n\n\tASSERT_TRUE(a.rtp_count >= 1);\n\tASSERT_EQ(2, a.psfb_count);\n\tASSERT_EQ(1, a.app_count);\n\n\tASSERT_TRUE(b.rtp_count >= 1);\n\tASSERT_EQ(2, b.psfb_count);\n\tASSERT_EQ(2, b.rtpfb_count);\n\tASSERT_EQ(1, b.gnack_count);\n\tASSERT_EQ(1, b.app_count);\n\n out:\n\tmem_deref(b.rtp_sock);\n\tmem_deref(a.rtp_sock);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nint test_rtcp_loop(void)\n{\n\tint err;\n\n\terr = test_rtcp_loop_param(false, \"127.0.0.1\");\n\tTEST_ERR(err);\n\n\terr = test_rtcp_loop_param(true, \"127.0.0.1\");\n\tTEST_ERR(err);\n\n\tif (test_ipv6_supported()) {\n\n\t\terr = test_rtcp_loop_param(false, \"::1\");\n\t\tTEST_ERR(err);\n\n\t\terr = test_rtcp_loop_param(true, \"::1\");\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic int rrtr_encode_handler(struct mbuf *mb, void *arg)\n{\n\tint err = 0;\n\t(void)arg;\n\n\terr |= mbuf_write_u8(mb, RTCP_XR_RRTR);\n\terr |= mbuf_write_u8(mb, 0);  /* reserved */\n\terr |= mbuf_write_u16(mb, htons(2));\n\terr |= mbuf_write_u32(mb, htonl(0x01020304));\n\terr |= mbuf_write_u32(mb, htonl(0x05060708));\n\n\treturn err;\n}\n\n\nstatic int test_rtcp_xr_rrtr(void)\n{\n\tconst uint8_t packet[] = {\n\t\t/* header */\n\t\t0x80, 0xcf, 0x00, 0x04,\n\n\t\t/* RTCP-XR */\n\t\t0x00, 0x00, 0x00, 0x01,\n\t\t0x04, 0x00, 0x00, 0x02,\n\t\t0x01, 0x02, 0x03, 0x04,\n\t\t0x05, 0x06, 0x07, 0x08\n\t};\n\n\tstruct mbuf *buf = mbuf_alloc(sizeof(packet));\n\tif (!buf)\n\t\treturn ENOMEM;\n\n\tstruct rtcp_msg *msg = NULL;\n\tconst uint32_t ssrc = 1;\n\tint err = 0;\n\n\terr = rtcp_encode(buf, RTCP_XR, 0, ssrc, rrtr_encode_handler, NULL);\n\tTEST_ERR(err);\n\n\tmbuf_set_pos(buf, 0);\n\n\tTEST_MEMCMP(packet, sizeof(packet), mbuf_buf(buf), mbuf_get_left(buf));\n\n\terr = rtcp_decode(&msg, buf);\n\tTEST_ERR(err);\n\n\tASSERT_EQ(RTCP_XR,      msg->hdr.pt);\n\tASSERT_EQ(4,            msg->hdr.length);\n\tASSERT_EQ(ssrc,         msg->r.xr.ssrc);\n\tASSERT_EQ(RTCP_XR_RRTR, msg->r.xr.bt);\n\tASSERT_EQ(2,            msg->r.xr.block_len);\n\tASSERT_EQ(0x01020304,   msg->r.xr.rb.rrtrb.ntp_msw);\n\tASSERT_EQ(0x05060708,   msg->r.xr.rb.rrtrb.ntp_lsw);\n\nout:\n\tmem_deref(buf);\n\tmem_deref(msg);\n\treturn err;\n}\n\n\nstatic int dlrr_encode_handler(struct mbuf *mb, void *arg)\n{\n\tconst uint16_t block_length = 3;\n\tconst uint32_t ssrc = 0xa534b254;\n\tconst uint32_t lrr = 0x03040506;\n\tconst uint32_t dlrr = 0x00008376;\n\tint err;\n\t(void)arg;\n\n\terr  = mbuf_write_u8(mb, RTCP_XR_DLRR);\n\terr |= mbuf_write_u8(mb, 0); /* reserved */\n\terr |= mbuf_write_u16(mb, htons(block_length));\n\terr |= mbuf_write_u32(mb, htonl(ssrc));\n\terr |= mbuf_write_u32(mb, htonl(lrr));\n\terr |= mbuf_write_u32(mb, htonl(dlrr));\n\n\treturn err;\n}\n\n\nstatic int test_rtcp_xr_dlrr(void)\n{\n\t/* RTCP-XR packet from Chrome 126 */\n\tstatic const uint8_t packet[] = {\n\n\t\t/* header */\n\t\t0x80, 0xcf, 0x00, 0x05,\n\n\t\t/* RTCP-XR */\n\t\t0x62, 0x8e, 0x09, 0xbd,\n\t\t0x05, 0x00, 0x00, 0x03,\n\t\t0xa5, 0x34, 0xb2, 0x54,\n\t\t0x03, 0x04, 0x05, 0x06,\n\t\t0x00, 0x00, 0x83, 0x76,\n\t};\n\n\tstruct mbuf *mb = mbuf_alloc(sizeof(packet));\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tstruct rtcp_msg *msg = NULL;\n\tconst uint32_t ssrc = 0x628e09bd;\n\n\tint err = rtcp_encode(mb, RTCP_XR, 0, ssrc, dlrr_encode_handler, NULL);\n\tTEST_ERR(err);\n\n\tmbuf_set_pos(mb, 0);\n\n\tTEST_MEMCMP(packet, sizeof(packet), mbuf_buf(mb), mbuf_get_left(mb));\n\n\terr = rtcp_decode(&msg, mb);\n\tTEST_ERR(err);\n\n\tASSERT_EQ(RTCP_XR,      msg->hdr.pt);\n\tASSERT_EQ(ssrc,         msg->r.xr.ssrc);\n\tASSERT_EQ(RTCP_XR_DLRR, msg->r.xr.bt);\n\tASSERT_EQ(3,            msg->r.xr.block_len);\n\tASSERT_EQ(0xa534b254,   msg->r.xr.rb.dlrrb.ssrc);\n\tASSERT_EQ(0x03040506,   msg->r.xr.rb.dlrrb.lrr);\n\tASSERT_EQ(0x00008376,   msg->r.xr.rb.dlrrb.dlrr);\n\n out:\n\tmem_deref(msg);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nint test_rtcp_xr(void)\n{\n\tint err;\n\n\terr = test_rtcp_xr_dlrr();\n\tTEST_ERR(err);\n\n\terr = test_rtcp_xr_rrtr();\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/rtmp.c",
    "content": "/**\n * @file rtmp.c RTMP Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"rtmp\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define NUM_MEDIA_PACKETS 4\n\n\n/* Force testing of Extended Timestamp */\n#define TS_OFFSET 0x00fffffe\n\n\n#define DUMMY_STREAM_ID 42\n\n\n#define CHUNK_SIZE 4096\n\n\nenum mode {\n\tMODE_PLAY,\n\tMODE_PUBLISH,\n};\n\n\nstruct rtmp_endpoint {\n\tstruct rtmp_endpoint *other;\n\tstruct rtmp_conn *conn;\n\tstruct rtmp_stream *stream;\n\tstruct tcp_sock *ts;     /* server only */\n\tstruct tls *tls;\n\tconst char *tag;\n\tenum mode mode;\n\tuint32_t stream_id;\n\tbool is_client;\n\tunsigned n_estab;\n\tunsigned n_cmd;\n\tunsigned n_stream_cmd;\n\tunsigned n_close;\n\tunsigned n_ready;\n\tunsigned n_play;\n\tunsigned n_publish;\n\tunsigned n_publish_start;\n\tunsigned n_deletestream;\n\tunsigned n_audio;\n\tunsigned n_video;\n\tunsigned n_data;\n\tint err;\n};\n\n\nstatic const uint8_t fake_audio_packet[CHUNK_SIZE + 6] = {\n\t0x5b, 0xb2, 0xfb, 0x11, 0x46, 0xe9\n};\n\nstatic const uint8_t fake_video_packet[CHUNK_SIZE + 8] = {\n\t0xcb, 0x9c, 0xb5, 0x60, 0x7f, 0xe9, 0xbd, 0xe1\n};\n\nstatic const char *fake_stream_name = \"sample.mp4\";\nstatic const char *fake_app_inst = \"vod\";\n\n\nstatic void endpoint_terminate(struct rtmp_endpoint *ep, int err)\n{\n\tif (err) {\n\t\tDEBUG_INFO(\"[ %s ] terminate: %m\\n\", ep->tag, err);\n\t}\n\n\tep->err = err;\n\tre_cancel();\n}\n\n\n/* criteria for test to be finished */\nstatic bool client_is_finished(const struct rtmp_endpoint *ep)\n{\n\tswitch (ep->mode) {\n\n\tcase MODE_PLAY:\n\t\treturn ep->n_ready > 0 &&\n\t\t\tep->n_audio >= NUM_MEDIA_PACKETS &&\n\t\t\tep->n_video >= NUM_MEDIA_PACKETS;\n\n\tcase MODE_PUBLISH:\n\t\treturn ep->n_ready > 0 &&\n\t\t\tep->n_publish_start > 0;\n\t}\n\n\treturn false;\n}\n\n\nstatic bool server_is_finished(const struct rtmp_endpoint *ep)\n{\n\tswitch (ep->mode) {\n\n\tcase MODE_PLAY:\n\t\treturn ep->n_play > 0;\n\n\tcase MODE_PUBLISH:\n\t\treturn ep->n_publish > 0 &&\n\t\t\tep->n_audio >= NUM_MEDIA_PACKETS &&\n\t\t\tep->n_video >= NUM_MEDIA_PACKETS;\n\t}\n\n\treturn false;\n}\n\n\nstatic bool endpoints_are_finished(const struct rtmp_endpoint *ep)\n{\n\tif (ep->is_client) {\n\t\treturn client_is_finished(ep) &&\n\t\t\tserver_is_finished(ep->other);\n\t}\n\telse {\n\t\treturn client_is_finished(ep->other) &&\n\t\t\tserver_is_finished(ep);\n\t}\n}\n\n\nstatic int send_media(struct rtmp_endpoint *ep_cli)\n{\n\tunsigned i;\n\tint err = 0;\n\n\t/* Send some dummy media packets to server */\n\tfor (i=0; i<NUM_MEDIA_PACKETS; i++) {\n\n\t\terr = rtmp_send_audio(ep_cli->stream, i,\n\t\t\t\t      fake_audio_packet,\n\t\t\t\t      sizeof(fake_audio_packet));\n\t\tif (err)\n\t\t\treturn err;\n\n\t\terr = rtmp_send_video(ep_cli->stream,\n\t\t\t\t      TS_OFFSET + i,\n\t\t\t\t      fake_video_packet,\n\t\t\t\t      sizeof(fake_video_packet));\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\nstatic void stream_command_handler(const struct odict *msg, void *arg)\n{\n\tstruct rtmp_endpoint *ep = arg;\n\tconst char *name;\n\tint err = 0;\n\n\tname = odict_string(msg, \"0\");\n\n\tDEBUG_INFO(\"[%s] stream command: %s\\n\", ep->tag, name);\n\n\tTEST_EQUALS(DUMMY_STREAM_ID, ep->stream_id);\n\n\t++ep->n_stream_cmd;\n\n\tif (0 == str_casecmp(name, \"play\")) {\n\n\t\tconst char *stream_name;\n\t\tuint64_t tid;\n\t\tuint32_t i;\n\n\t\t++ep->n_play;\n\n\t\tif (!odict_get_number(msg, &tid, \"1\")) {\n\t\t\terr = EPROTO;\n\t\t\tgoto out;\n\t\t}\n\t\tTEST_EQUALS(0, tid);\n\n\t\tstream_name = odict_string(msg, \"3\");\n\t\tTEST_STRCMP(fake_stream_name, strlen(fake_stream_name),\n\t\t\t    stream_name, str_len(stream_name));\n\n\t\terr = rtmp_amf_data(ep->conn, DUMMY_STREAM_ID,\n\t\t\t\t    \"|RtmpSampleAccess\",\n\t\t\t\t    2,\n\t\t\t\t        RTMP_AMF_TYPE_BOOLEAN, false,\n\t\t\t\t        RTMP_AMF_TYPE_BOOLEAN, false);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\t/* Send some dummy media packets to client */\n\n\t\tfor (i=0; i<NUM_MEDIA_PACKETS; i++) {\n\n\t\t\terr = rtmp_send_audio(ep->stream, i,\n\t\t\t\t\t      fake_audio_packet,\n\t\t\t\t\t      sizeof(fake_audio_packet));\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n\t\t\terr = rtmp_send_video(ep->stream, TS_OFFSET + i,\n\t\t\t\t\t      fake_video_packet,\n\t\t\t\t\t      sizeof(fake_video_packet));\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t}\n\telse if (0 == str_casecmp(name, \"publish\")) {\n\n\t\tconst char *stream_name;\n\t\tconst char *code = \"NetStream.Publish.Start\";\n\t\tuint64_t tid;\n\n\t\t++ep->n_publish;\n\n\t\tif (!odict_get_number(msg, &tid, \"1\")) {\n\t\t\terr = EPROTO;\n\t\t\tgoto out;\n\t\t}\n\t\tTEST_EQUALS(0, tid);\n\n\t\tstream_name = odict_string(msg, \"3\");\n\t\tTEST_STRCMP(fake_stream_name, strlen(fake_stream_name),\n\t\t\t    stream_name, str_len(stream_name));\n\n\t\terr = rtmp_amf_command(ep->conn, ep->stream_id, \"onStatus\",\n\t\t\t       3,\n\t\t\t       RTMP_AMF_TYPE_NUMBER, (double)0,\n\t\t\t       RTMP_AMF_TYPE_NULL,\n\t\t\t       RTMP_AMF_TYPE_OBJECT, 2,\n\t\t\t           RTMP_AMF_TYPE_STRING, \"level\", \"status\",\n\t\t\t           RTMP_AMF_TYPE_STRING, \"code\", code);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse if (0 == str_casecmp(name, \"onStatus\")) {\n\n\t\tstruct odict *obj;\n\t\tconst char *level;\n\n\t\tobj = odict_get_object(msg, \"3\");\n\n\t\tlevel = odict_string(obj, \"level\");\n\t\tif (0 == str_casecmp(level, \"status\")) {\n\n\t\t\tconst char *code = odict_string(obj, \"code\");\n\t\t\tconst char *exp_code = \"NetStream.Publish.Start\";\n\n\t\t\t++ep->n_publish_start;\n\n\t\t\tTEST_STRCMP(exp_code, str_len(exp_code),\n\t\t\t\t    code, str_len(code));\n\n\t\t\terr = rtmp_meta(ep->stream);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\n\t\t\terr = send_media(ep);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\t\telse {\n\t\t\tDEBUG_WARNING(\"unsupported level %s\\n\", level);\n\t\t}\n\t}\n\telse {\n\t\tDEBUG_NOTICE(\"[ %s ] stream: command not handled (%s)\\n\",\n\t\t\t     ep->tag, name);\n\t\terr = ENOTSUP;\n\t\tgoto out;\n\t}\n\n out:\n\tif (err)\n\t\tendpoint_terminate(ep, err);\n}\n\n\nstatic void test_done(struct rtmp_endpoint *ep)\n{\n\tstruct rtmp_endpoint *client;\n\n\tif (ep->is_client)\n\t\tclient = ep;\n\telse\n\t\tclient = ep->other;\n\n\t/* Force destruction here to test robustness */\n\n\tclient->stream = mem_deref(client->stream);\n}\n\n\nstatic void stream_control_handler(enum rtmp_event_type event, struct mbuf *mb,\n\t\t\t\t   void *arg)\n{\n\tstruct rtmp_endpoint *ep = arg;\n\tint err = 0;\n\t(void)mb;\n\n\tTEST_EQUALS(DUMMY_STREAM_ID, ep->stream_id);\n\n\tDEBUG_INFO(\"[ %s ] got control event:  event=%d (%s)\\n\",\n\t\t     ep->tag, event, rtmp_event_name(event));\n\n\tswitch (event) {\n\n\tdefault:\n\t\tbreak;\n\t}\n\n out:\n\tif (err)\n\t\tendpoint_terminate(ep, err);\n}\n\n\nstatic void audio_handler(uint32_t timestamp,\n\t\t\t  const uint8_t *pld, size_t len, void *arg)\n{\n\tstruct rtmp_endpoint *ep = arg;\n\tint err = 0;\n\n\tTEST_EQUALS(DUMMY_STREAM_ID, ep->stream_id);\n\n\tTEST_EQUALS(ep->n_audio, timestamp);\n\n\t++ep->n_audio;\n\n\tTEST_MEMCMP(fake_audio_packet, sizeof(fake_audio_packet), pld, len);\n\n\t/* Test complete ? */\n\tif (endpoints_are_finished(ep)) {\n\n\t\ttest_done(ep);\n\t\treturn;\n\t}\n\n out:\n\tif (err)\n\t\tendpoint_terminate(ep, err);\n}\n\n\nstatic void video_handler(uint32_t timestamp,\n\t\t\t  const uint8_t *pld, size_t len, void *arg)\n{\n\tstruct rtmp_endpoint *ep = arg;\n\tint err = 0;\n\n\tTEST_EQUALS(DUMMY_STREAM_ID, ep->stream_id);\n\n\tTEST_EQUALS(TS_OFFSET + ep->n_video, timestamp);\n\n\t++ep->n_video;\n\n\tTEST_MEMCMP(fake_video_packet, sizeof(fake_video_packet), pld, len);\n\n\t/* Test complete ? */\n\tif (endpoints_are_finished(ep)) {\n\n\t\ttest_done(ep);\n\t\treturn;\n\t}\n\n out:\n\tif (err)\n\t\tendpoint_terminate(ep, err);\n}\n\n\nstatic void stream_data_handler(const struct odict *msg, void *arg)\n{\n\tstruct rtmp_endpoint *ep = arg;\n\tconst char *command;\n\tbool ret;\n\tbool value;\n\tint err = 0;\n\n\tTEST_EQUALS(DUMMY_STREAM_ID, ep->stream_id);\n\n\t++ep->n_data;\n\n\tcommand = odict_string(msg, \"0\");\n\n\tif (ep->is_client) {\n\n\t\tTEST_STRCMP(\"|RtmpSampleAccess\", 17,\n\t\t\t    command, str_len(command));\n\n\t\tret = odict_get_boolean(msg, &value, \"1\");\n\t\tTEST_ASSERT(ret);\n\t\tTEST_ASSERT(!value);\n\n\t\tret = odict_get_boolean(msg, &value, \"2\");\n\t\tTEST_ASSERT(ret);\n\t\tTEST_ASSERT(!value);\n\t}\n\telse {\n\t\tconst struct odict_entry *e;\n\t\tuint64_t num;\n\n\t\tTEST_STRCMP(\"@setDataFrame\", 13, command, str_len(command));\n\n\t\te = odict_get_type(msg, ODICT_OBJECT, \"2\");\n\t\tTEST_ASSERT(e != NULL);\n\n\t\tret = odict_get_number(odict_entry_object(e), &num,\n\t\t\t\t       \"audiocodecid\");\n\t\tTEST_ASSERT(ret);\n\t\tTEST_EQUALS(10ULL, num);\n\n\t\tret = odict_get_number(odict_entry_object(e), &num,\n\t\t\t\t       \"videocodecid\");\n\t\tTEST_ASSERT(ret);\n\t\tTEST_EQUALS(7ULL, num);\n\t}\n\n out:\n\tif (err)\n\t\tendpoint_terminate(ep, err);\n}\n\n\nstatic void stream_create_resp_handler(bool success,\n\t\t\t\t       const struct odict *msg, void *arg)\n{\n\tstruct rtmp_endpoint *ep = arg;\n\tuint64_t stream_id;\n\tint err;\n\n\tTEST_ASSERT(success);\n\n\t++ep->n_ready;\n\n\t/* the stream-id was assigned by the server */\n\tif (!odict_get_number(msg, &stream_id, \"3\")) {\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\tep->stream_id = (uint32_t)stream_id;\n\n\tswitch (ep->mode) {\n\n\tcase MODE_PLAY:\n\t\terr = rtmp_play(ep->stream, fake_stream_name);\n\t\tif (err)\n\t\t\tgoto out;\n\t\tbreak;\n\n\tcase MODE_PUBLISH:\n\t\terr = rtmp_publish(ep->stream, fake_stream_name);\n\t\tif (err)\n\t\t\tgoto out;\n\t\tbreak;\n\n\tdefault:\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\n out:\n\tif (err)\n\t\tendpoint_terminate(ep, err);\n}\n\n\nstatic void estab_handler(void *arg)\n{\n\tstruct rtmp_endpoint *ep = arg;\n\tint err = 0;\n\n\tDEBUG_INFO(\"[%s] Established\\n\", ep->tag);\n\n\t++ep->n_estab;\n\n\tif (ep->is_client) {\n\n\t\tTEST_ASSERT(ep->stream == NULL);\n\n\t\terr = rtmp_stream_create(&ep->stream, ep->conn,\n\t\t\t\t\t stream_create_resp_handler,\n\t\t\t\t\t stream_command_handler,\n\t\t\t\t\t stream_control_handler,\n\t\t\t\t\t audio_handler,\n\t\t\t\t\t video_handler, stream_data_handler,\n\t\t\t\t\t ep);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n out:\n\tif (err)\n\t\tendpoint_terminate(ep, err);\n}\n\n\n/* Server */\nstatic int server_send_reply(struct rtmp_conn *conn, const struct odict *req)\n{\n\tconst char *code = \"NetConnection.Connect.Success\";\n\tconst char *descr = \"Connection succeeded.\";\n\tint err;\n\n\terr = rtmp_amf_reply(conn, 0, true, req,\n\t\t\t\t2,\n\n\t\tRTMP_AMF_TYPE_OBJECT, 3,\n\t\t\tRTMP_AMF_TYPE_STRING, \"fmsVer\",       \"FMS/3,5,7,7009\",\n\t\t\tRTMP_AMF_TYPE_NUMBER, \"capabilities\", 31.0,\n\t\t\tRTMP_AMF_TYPE_NUMBER, \"mode\",         1.0,\n\n\t\tRTMP_AMF_TYPE_OBJECT, 6,\n\t\t\tRTMP_AMF_TYPE_STRING, \"level\",        \"status\",\n\t\t\tRTMP_AMF_TYPE_STRING, \"code\",         code,\n\t\t\tRTMP_AMF_TYPE_STRING, \"description\",  descr,\n\t\t\tRTMP_AMF_TYPE_ECMA_ARRAY,  \"data\",         1,\n\t\t\t    RTMP_AMF_TYPE_STRING, \"version\",      \"3,5,7,7009\",\n\t\t\tRTMP_AMF_TYPE_NUMBER, \"clientid\",     734806661.0,\n\t\t\tRTMP_AMF_TYPE_NUMBER, \"objectEncoding\", 0.0);\n\n\treturn err;\n}\n\n\nstatic void command_handler(const struct odict *msg, void *arg)\n{\n\tstruct rtmp_endpoint *ep = arg;\n\tconst char *name;\n\tint err = 0;\n\n\tTEST_ASSERT(!ep->is_client);\n\n\tname = odict_string(msg, \"0\");\n\n\t++ep->n_cmd;\n\n\tif (0 == str_casecmp(name, \"connect\")) {\n\n\t\tconst struct odict_entry *entry;\n\t\tuint32_t window_ack_size = 32000;\n\t\tconst char *app;\n\n\t\tentry = odict_lookup(msg, \"2\");\n\t\tTEST_ASSERT(entry != NULL);\n\n\t\tapp = odict_string(odict_entry_object(entry), \"app\");\n\t\tTEST_STRCMP(fake_app_inst, strlen(fake_app_inst),\n\t\t\t    app, str_len(app));\n\n\t\terr = rtmp_control(ep->conn, RTMP_TYPE_WINDOW_ACK_SIZE,\n\t\t\t\t   (uint32_t)window_ack_size);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = server_send_reply(ep->conn, msg);\n\t\tif (err) {\n\t\t\tre_printf(\"rtmp: reply failed (%m)\\n\", err);\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse if (0 == str_casecmp(name, \"createStream\")) {\n\n\t\tuint32_t stream_id = DUMMY_STREAM_ID;\n\n\t\tTEST_ASSERT(ep->stream == NULL);\n\n\t\tep->stream_id = stream_id;\n\n\t\terr = rtmp_stream_alloc(&ep->stream, ep->conn, stream_id,\n\t\t\t\t\tstream_command_handler,\n\t\t\t\t\tstream_control_handler, audio_handler,\n\t\t\t\t\tvideo_handler, stream_data_handler,\n\t\t\t\t\tep);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = rtmp_amf_reply(ep->conn, 0, true, msg,\n\t\t\t\t\t2,\n\t\t\t\tRTMP_AMF_TYPE_NULL,\n\t\t\t\tRTMP_AMF_TYPE_NUMBER, (double)stream_id);\n\t\tif (err) {\n\t\t\tre_printf(\"rtmp: reply failed (%m)\\n\", err);\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse if (0 == str_casecmp(name, \"deleteStream\")) {\n\n\t\tuint64_t stream_id;\n\n\t\t++ep->n_deletestream;\n\n\t\tif (!odict_get_number(msg, &stream_id, \"3\")) {\n\t\t\terr = EPROTO;\n\t\t\tgoto out;\n\t\t}\n\n\t\tTEST_EQUALS(DUMMY_STREAM_ID, stream_id);\n\n\t\tif (stream_id == ep->stream_id)\n\t\t\tep->stream = mem_deref(ep->stream);\n\n\t\t/* re_main will be stopped when the\n\t\t * TCP connection is closed\n\t\t */\n\n\t\tendpoint_terminate(ep, 0);\n\t}\n\telse {\n\t\tDEBUG_NOTICE(\"[ %s ] command not handled (%s)\\n\",\n\t\t\t     ep->tag, name);\n\t\terr = ENOTSUP;\n\t\tgoto out;\n\t}\n\n out:\n\tif (err)\n\t\tendpoint_terminate(ep, err);\n}\n\n\nstatic void close_handler(int err, void *arg)\n{\n\tstruct rtmp_endpoint *ep = arg;\n\n\tif (err) {\n\t\tDEBUG_INFO(\"[ %s ] rtmp connection closed (%m)\\n\",\n\t\t\t     ep->tag, err);\n\t}\n\n\t++ep->n_close;\n\n\tendpoint_terminate(ep, err);\n}\n\n\nstatic void endpoint_destructor(void *data)\n{\n\tstruct rtmp_endpoint *ep = data;\n\n\tmem_deref(ep->stream);\n\tmem_deref(ep->conn);\n\tmem_deref(ep->tls);\n\tmem_deref(ep->ts);\n}\n\n\nstatic struct rtmp_endpoint *rtmp_endpoint_alloc(enum mode mode,\n\t\t\t\t\t\t bool is_client, bool secure)\n{\n\tstruct rtmp_endpoint *ep;\n\tint err = 0;\n\n\tep = mem_zalloc(sizeof(*ep), endpoint_destructor);\n\tif (!ep)\n\t\treturn NULL;\n\n\tep->is_client = is_client;\n\tep->mode = mode;\n\n\tif (secure) {\n\n#ifdef USE_TLS\n\t\tchar path[256];\n\n\t\tre_snprintf(path, sizeof(path), \"%s/server-ecdsa.pem\",\n\t\t\t    test_datapath());\n\n\t\terr = tls_alloc(&ep->tls, TLS_METHOD_SSLV23,\n\t\t\t\tis_client ? NULL : path, NULL);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\t/* Client: Add the server's certificate as a CA cert.\n\t\t *         This is required for authentication to work.\n\t\t */\n\t\tif (is_client) {\n\n\t\t\terr = tls_add_ca(ep->tls, path);\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n#else\n\t\terr = ENOSYS;\n\t\tgoto out;\n#endif\n\t}\n\n\tep->tag = is_client ? \"Client\" : \"Server\";\n\n out:\n\tif (err)\n\t\treturn mem_deref(ep);\n\n\treturn ep;\n}\n\n\nstatic void tcp_conn_handler(const struct sa *peer, void *arg)\n{\n\tstruct rtmp_endpoint *ep = arg;\n\tint err;\n\t(void)peer;\n\n\terr = rtmp_accept(&ep->conn, ep->ts, ep->tls, command_handler,\n\t\t\t  close_handler, ep);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tendpoint_terminate(ep, err);\n}\n\n\nstatic int test_rtmp_client_server_conn(enum mode mode, bool secure)\n{\n\tstruct rtmp_endpoint *cli, *srv;\n\tstruct sa srv_addr;\n\tchar uri[256];\n\tint err = 0;\n\n\tcli = rtmp_endpoint_alloc(mode, true, secure);\n\tsrv = rtmp_endpoint_alloc(mode, false, secure);\n\tif (!cli || !srv) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tcli->other = srv;\n\tsrv->other = cli;\n\n\terr = sa_set_str(&srv_addr, \"127.0.0.1\", 0);\n\tTEST_ERR(err);\n\n\terr = tcp_listen(&srv->ts, &srv_addr, tcp_conn_handler, srv);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_local_get(srv->ts, &srv_addr);\n\tTEST_ERR(err);\n\n\tre_snprintf(uri, sizeof(uri), \"rtmp%s://%J/%s/foo\",\n\t\t    secure ? \"s\" : \"\", &srv_addr, fake_app_inst);\n\n\terr = rtmp_connect(&cli->conn, NULL, uri, cli->tls, estab_handler,\n\t\t\t   command_handler, close_handler, cli);\n\tif (err)\n\t\tgoto out;\n\n\terr = re_main_timeout(1000);\n\tif (err)\n\t\tgoto out;\n\n\tif (cli->err) {\n\t\terr = cli->err;\n\t\tgoto out;\n\t}\n\tif (srv->err) {\n\t\terr = srv->err;\n\t\tgoto out;\n\t}\n\n\tTEST_EQUALS(1, cli->n_estab);\n\tTEST_EQUALS(0, srv->n_estab);\n\tTEST_EQUALS(0, cli->n_cmd);\n\tTEST_EQUALS(3, srv->n_cmd);\n\n\tTEST_EQUALS(1, srv->n_stream_cmd);\n\n\tTEST_EQUALS(0, cli->n_close);\n\n\tTEST_EQUALS(1, cli->n_ready);\n\tTEST_EQUALS(0, srv->n_ready);\n\tTEST_EQUALS(0, cli->n_deletestream);\n\tTEST_EQUALS(1, srv->n_deletestream);\n\n\tswitch (mode) {\n\n\tcase MODE_PLAY:\n\t\tTEST_EQUALS(1, srv->n_play);\n\t\tTEST_EQUALS(0, srv->n_publish);\n\n\t\tTEST_EQUALS(NUM_MEDIA_PACKETS, cli->n_audio);\n\t\tTEST_EQUALS(NUM_MEDIA_PACKETS, cli->n_video);\n\t\tTEST_EQUALS(1,                 cli->n_data);\n\t\tTEST_EQUALS(0, srv->n_audio);\n\t\tTEST_EQUALS(0, srv->n_video);\n\t\tTEST_EQUALS(0, srv->n_data);\n\t\tbreak;\n\n\tcase MODE_PUBLISH:\n\t\tTEST_EQUALS(0, srv->n_play);\n\t\tTEST_EQUALS(1, srv->n_publish);\n\n\t\tTEST_EQUALS(0, cli->n_audio);\n\t\tTEST_EQUALS(0, cli->n_video);\n\t\tTEST_EQUALS(0, cli->n_data);\n\t\tTEST_EQUALS(NUM_MEDIA_PACKETS, srv->n_audio);\n\t\tTEST_EQUALS(NUM_MEDIA_PACKETS, srv->n_video);\n\t\tTEST_EQUALS(1,                 srv->n_data);\n\t\tbreak;\n\t}\n\n out:\n\tmem_deref(srv);\n\tmem_deref(cli);\n\n\treturn err;\n}\n\n\nint test_rtmp_play(void)\n{\n\tint err = 0;\n\n\terr = test_rtmp_client_server_conn(MODE_PLAY, false);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\nint test_rtmp_publish(void)\n{\n\tint err = 0;\n\n\terr = test_rtmp_client_server_conn(MODE_PUBLISH, false);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\n#ifdef USE_TLS\nint test_rtmps_publish(void)\n{\n\tint err = 0;\n\n\terr = test_rtmp_client_server_conn(MODE_PUBLISH, true);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n#endif\n"
  },
  {
    "path": "test/rtp.c",
    "content": "/**\n * @file rtp.c RTP/RTCP Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"rtptest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum {PAYLOAD_SIZE = 160};\n\n\nint test_rtp(void)\n{\n\tstruct rtp_sock *rtp = NULL;\n\tstruct mbuf *mb = NULL;\n\tstatic const uint8_t payload[PAYLOAD_SIZE];\n\tint j;\n\tint err;\n\n\tmb = mbuf_alloc(RTP_HEADER_SIZE);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = rtp_alloc(&rtp);\n\tif (err)\n\t\tgoto out;\n\n\tfor (j=0; j<100; j++) {\n\t\tstruct rtp_header hdr, hdr2;\n\n\t\tmemset(&hdr, 0, sizeof(hdr));\n\n\t\thdr.m  = j & 0x01;\n\t\thdr.pt = j & 0x7f;\n\t\thdr.ts = 160 + j;\n\n\t\tmb->pos = mb->end = RTP_HEADER_SIZE;\n\t\terr = mbuf_write_mem(mb, payload, sizeof(payload));\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tmb->pos = 0;\n\t\terr = rtp_encode(rtp, false, hdr.m, hdr.pt, hdr.ts, mb);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tmb->pos = 0;\n\n\t\terr = rtp_decode(rtp, mb, &hdr2);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tif (hdr.m != hdr2.m) {\n\t\t\tDEBUG_WARNING(\"marker bit mismatch (%d != %d)\\n\",\n\t\t\t\t      hdr.m, hdr2.m);\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (hdr.pt != hdr2.pt) {\n\t\t\tDEBUG_WARNING(\"payload type mismatch (%u != %u)\\n\",\n\t\t\t\t      hdr.pt, hdr2.pt);\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (hdr.ts != hdr2.ts) {\n\t\t\tDEBUG_WARNING(\"timestamp mismatch (%lu != %lu)\\n\",\n\t\t\t\t      hdr.ts, hdr2.ts);\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (hdr2.pad) {\n\t\t\tDEBUG_WARNING(\"unexpected padding bit\\n\");\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (hdr2.ext) {\n\t\t\tDEBUG_WARNING(\"unexpected extension bit\\n\");\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (RTP_HEADER_SIZE != mb->pos ||\n\t\t    (RTP_HEADER_SIZE + PAYLOAD_SIZE) != mb->end) {\n\t\t\tDEBUG_WARNING(\"invalid mbuf size (pos=%u end=%u)\\n\",\n\t\t\t\t      mb->pos, mb->end);\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (0 != memcmp(mbuf_buf(mb), payload, sizeof(payload))) {\n\t\t\tDEBUG_WARNING(\"RTP payload mismatch\\n\");\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\t}\n\n out:\n\tmem_deref(rtp);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic const uint8_t rtcp_msg[] =\n\t/* SR */\n\t\"\\x81\\xc8\\x00\\x0c\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x00\\x11\\x22\\x33\"\n\t\"\\x44\\x55\\x66\\x77\"\n\t\"\\x22\\x33\\x22\\x33\"\n\t\"\\x00\\x00\\x11\\x11\"\n\t\"\\x00\\x00\\x22\\x22\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x12\\x34\\x56\\x78\"\n\n\t/* RR */\n\t\"\\x81\\xc9\\x00\\x07\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x12\\x34\\x56\\x78\"\n\n\t/* SDES */\n\t\"\\x81\\xca\\x00\\x03\"\n\t\"\\xde\\xad\\xbe\\xef\"\n\t\"\\x01\\x03\\x61\\x62\"\n\t\"\\x63\\x00\\x00\\x00\"\n\n\t/* BYE */\n\t\"\\x82\\xcb\\x00\\x04\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x00\\xab\\xcd\\xef\"\n\t\"\\x04\\x63\\x69\\x61\"\n\t\"\\x6f\\x00\\x00\\x00\"\n\n\t/* APP */\n\t\"\\x80\\xcc\\x00\\x03\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x6e\\x61\\x6d\\x65\"\n\t\"\\x64\\x61\\x74\\x61\"\n\n\t/* FIR */\n\t\"\\x80\\xc0\\x00\\x01\"\n\t\"\\x12\\x34\\x56\\x78\"\n\n\t/* NACK */\n\t\"\\x80\\xc1\\x00\\x02\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x89\\xab\\xcd\\xef\"\n\n\t/* RTPFB */\n\t\"\\x81\\xcd\\x00\\x05\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\xfe\\xdc\\xba\\x98\"\n\t\"\\x01\\x23\\x04\\x56\"\n\t\"\\x01\\x23\\x04\\x56\"\n\t\"\\x01\\x23\\x04\\x56\"\n\n\t/* PSFB - PLI */\n\t\"\\x81\\xce\\x00\\x02\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\xfe\\xdc\\xba\\x98\"\n\n\t/* PSFB - SLI */\n\t\"\\x82\\xce\\x00\\x04\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\xfe\\xdc\\xba\\x98\"\n\t\"\\xca\\xfe\\xca\\xfe\"\n\t\"\\xca\\xfe\\xca\\xfe\"\n\n\t\"\";\n\n\nstatic int encode_handler(struct mbuf *mb, void *arg)\n{\n\tint err = 0;\n\tsize_t i;\n\n\t(void)arg;\n\n\tfor (i=0; i<6 && !err; i++)\n\t\terr = mbuf_write_u32(mb, htonl(0x12345678));\n\n\treturn err;\n}\n\n\nstatic int sdes_encode_handler(struct mbuf *mb, void *arg)\n{\n\t(void)arg;\n\treturn rtcp_sdes_encode(mb, 0xdeadbeef, 1, RTCP_SDES_CNAME, \"abc\");\n}\n\n\nstatic int gnack_encode(struct mbuf *mb, void *arg)\n{\n\tint err = 0, n=3;\n\t(void)arg;\n\n\twhile (n--) {\n\t\terr |= mbuf_write_u16(mb, htons(0x0123));\n\t\terr |= mbuf_write_u16(mb, htons(0x0456));\n\t}\n\n\treturn err;\n}\n\n\nstatic int sli_encode(struct mbuf *mb, void *arg)\n{\n\tint err = 0, n=2;\n\t(void)arg;\n\n\twhile (n--) {\n\t\terr |= mbuf_write_u32(mb, htonl(0xcafecafe));\n\t}\n\n\treturn err;\n}\n\n\nint test_rtcp_encode(void)\n{\n\tstruct mbuf *mb;\n\tconst size_t sz = sizeof(rtcp_msg) - 1;\n\tconst uint32_t srcv[2] = {0x12345678, 0x00abcdef};\n\tchar debug_buf[512];\n\tint err = 0;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\n\terr |= rtcp_encode(mb, RTCP_SR, 1, 0x12345678, 0x00112233,\n\t\t\t   0x44556677, 0x22332233, 0x00001111, 0x00002222,\n\t\t\t   encode_handler, 0);\n\terr |= rtcp_encode(mb, RTCP_RR, 1, 0x12345678, encode_handler, 0);\n\terr |= rtcp_encode(mb, RTCP_SDES, 1, sdes_encode_handler, 0);\n\terr |= rtcp_encode(mb, RTCP_BYE, 2, srcv, \"ciao\");\n\terr |= rtcp_encode(mb, RTCP_APP, 0, 0x12345678, \"name\", \"data\",\n\t\t\t   (size_t)4);\n\terr |= rtcp_encode(mb, RTCP_FIR, 0, 0x12345678);\n\terr |= rtcp_encode(mb, RTCP_NACK, 0, 0x12345678, 0x89ab, 0xcdef);\n\terr |= rtcp_encode(mb, RTCP_RTPFB, RTCP_RTPFB_GNACK,\n\t\t\t   0x12345678, 0xfedcba98, gnack_encode, 0);\n\terr |= rtcp_encode(mb, RTCP_PSFB, RTCP_PSFB_PLI,\n\t\t\t   0x12345678, 0xfedcba98, NULL, 0);\n\terr |= rtcp_encode(mb, RTCP_PSFB, RTCP_PSFB_SLI,\n\t\t\t   0x12345678, 0xfedcba98, sli_encode, 0);\n\tif (err)\n\t\tgoto out;\n\n\tif (mb->end != sz) {\n\t\terr = EPROTO;\n\t}\n\tif (0 != memcmp(mb->buf, rtcp_msg, mb->end)) {\n\t\terr = EBADMSG;\n\t}\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"encode error: %m\\n\", err);\n\t\thexdump(stderr, mb->buf, mb->end);\n\t}\n\n\tmb->pos = 0;\n\twhile (mbuf_get_left(mb) >= 4 && !err) {\n\t\tstruct rtcp_msg *msg = NULL;\n\t\terr = rtcp_decode(&msg, mb);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\t/* Check that debug print works */\n\t\tdebug_buf[0] = '\\0';\n\t\tre_snprintf(debug_buf, sizeof(debug_buf),\n\t\t\t    \"%H\", rtcp_msg_print, msg);\n\n\t\tmsg = mem_deref(msg);\n\n\t\tASSERT_TRUE(str_isset(debug_buf));\n\t}\n\tif (err)\n\t\tgoto out;\n\n\t/* verify that rtcp_decode() read the whole buffer */\n\tTEST_EQUALS(mb->end, mb->pos);\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstatic const uint8_t rtcp_sdes[] =\n\t/* SDES */\n\t\"\\x83\\xca\\x00\\x09\"\n\t\"\\x11\\x22\\x33\\x44\"\n\t\"\\x01\\x02\\x41\\x61\"  /* cname */\n\t\"\\x00\\x00\\x00\\x00\"\n\t\"\\x55\\x66\\x77\\x88\"\n\t\"\\x07\\x02\\x42\\x62\"  /* note */\n\t\"\\x02\\x01\\x43\\x00\"  /* name */\n\t\"\\xaa\\xbb\\xcc\\xdd\"\n\t\"\\x04\\x05\\x31\\x32\"  /* phone */\n\t\"\\x33\\x34\\x35\\x00\"\n\n\t/* APP */\n\t\"\\x80\\xcc\\x00\\x03\"\n\t\"\\x12\\x34\\x56\\x78\"\n\t\"\\x6e\\x61\\x6d\\x65\"\n\t\"\\x64\\x61\\x74\\x61\"\n\t\"\";\n\n\nint test_rtcp_decode_badmsg(void)\n{\n\tstruct rtcp_msg *msg = NULL;\n\tuint32_t ssrc = 0xcafebabe;\n\n\tstruct mbuf *mb = mbuf_alloc(128);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tint err = rtcp_encode(mb, RTCP_PSFB, RTCP_PSFB_SLI,\n\t\t\t      ssrc, ssrc, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\t/* simulate a corrupt RTCP packet */\n\tmb->pos = 2;\n\t(void)mbuf_write_u16(mb, htons(0));\n\n\tmb->pos = 0;\n\n\tint ret = rtcp_decode(&msg, mb);\n\tif (EBADMSG != ret && ret != ENOMEM) {\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n out:\n\tmem_deref(msg);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nint test_rtcp_decode(void)\n{\n\tstruct rtcp_msg *msg = NULL;\n\tstruct mbuf *mb;\n\tint err = 0;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr |= mbuf_write_u8(mb, 0x55); /* overhead -- test padding */\n\terr |= mbuf_write_mem(mb, rtcp_sdes, sizeof(rtcp_sdes));\n\terr |= mbuf_write_u8(mb, 0xaa); /* junk */\n\tTEST_ERR(err);\n\tmb->pos = 1;\n\n\t/* SDES */\n\terr = rtcp_decode(&msg, mb);\n\tTEST_ERR(err);\n\n\tASSERT_EQ(  2, msg->hdr.version);\n\tASSERT_EQ(  0, msg->hdr.p);\n\tASSERT_EQ(  3, msg->hdr.count);\n\tASSERT_EQ(202, msg->hdr.pt);\n\tASSERT_EQ(  9, msg->hdr.length);\n\n\tconst struct rtcp_sdes *sdes = &msg->r.sdesv[0];\n\n\tASSERT_EQ(0x11223344, sdes->src);\n\tASSERT_EQ(1, sdes->n);\n\n\tconst struct rtcp_sdes_item *item = &sdes->itemv[0];\n\n\tASSERT_EQ(1, item->type);\n\tASSERT_EQ(2, item->length);\n\tTEST_STRCMP(\"Aa\", 2, item->data, item->length);\n\n\tmsg = mem_deref(msg);\n\n\t/* APP */\n\terr = rtcp_decode(&msg, mb);\n\tTEST_ERR(err);\n\n\tASSERT_EQ(  2, msg->hdr.version);\n\tASSERT_EQ(  0, msg->hdr.p);\n\tASSERT_EQ(  0, msg->hdr.count);\n\tASSERT_EQ(204, msg->hdr.pt);\n\tASSERT_EQ(  3, msg->hdr.length);\n\n\tASSERT_EQ(  0x12345678, msg->r.app.src);\n\tTEST_STRCMP(\"name\", 4, msg->r.app.name, 4);\n\tTEST_STRCMP(\"data\", 4, msg->r.app.data, msg->r.app.data_len);\n\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(msg);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic int afb_encode_handler(struct mbuf *mb, void *arg)\n{\n\treturn mbuf_write_str(mb, arg);\n}\n\n\nint test_rtcp_encode_afb(void)\n{\n\tuint32_t ssrc_packet_sender, ssrc_media_source;\n\tconst char *afb_payload = \"AFB tull\";\n\tstruct rtcp_msg *msg = NULL;\n\tstruct mbuf *mb;\n\tint err = 0;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tssrc_packet_sender = 0xbad00bad;\n\tssrc_media_source = 0; /* always 0 */\n\terr = rtcp_encode(mb, RTCP_PSFB, RTCP_PSFB_AFB,\n\t\t\t  ssrc_packet_sender, ssrc_media_source,\n\t\t\t  afb_encode_handler, afb_payload);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\terr = rtcp_decode(&msg, mb);\n\tif (err)\n\t\tgoto out;\n\n\tif (msg->hdr.count != RTCP_PSFB_AFB) {\n\t\tDEBUG_WARNING(\"expected AFB, got fmt=%u\\n\", msg->hdr.count);\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\n\tif (msg->r.fb.ssrc_packet != ssrc_packet_sender ||\n\t    msg->r.fb.ssrc_media  != ssrc_media_source) {\n\t\tDEBUG_WARNING(\"error in SSRC encoding\\n\");\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\tif (!msg->r.fb.fci.afb ||\n\t    mbuf_get_left(msg->r.fb.fci.afb) != strlen(afb_payload)) {\n\t\tDEBUG_WARNING(\"error in AFB mbuf (left=%u, size=%u)\\n\",\n\t\t\t      mbuf_get_left(msg->r.fb.fci.afb),\n\t\t\t      strlen(afb_payload));\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\tif (0 != memcmp(mbuf_buf(msg->r.fb.fci.afb),\n\t\t\tafb_payload,\n\t\t\tstrlen(afb_payload))) {\n\t\tDEBUG_WARNING(\"error in AFB mbuf content\\n\");\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\t/* verify that rtcp_decode() read the whole buffer */\n\tTEST_EQUALS(mb->end, mb->pos);\n\n out:\n\tmem_deref(mb);\n\tmem_deref(msg);\n\treturn err;\n}\n\n\nstruct rtp_test {\n\tstruct rtp_sock *rtp;\n\tstruct mbuf *mb;\n\tuint32_t n;\n\tuint32_t f;\n};\n\n\nstatic void rtp_recv_handler(const struct sa *src,\n\t\t\t     const struct rtp_header *hdr, struct mbuf *mb,\n\t\t\t     void *arg)\n{\n\tstruct rtp_test *test = arg;\n\tchar bufs[5];\n\tchar bufr[5];\n\t(void) src;\n\t(void) hdr;\n\n\tmbuf_read_str(test->mb, bufs, sizeof(bufs));\n\tmbuf_read_str(mb, bufr, sizeof(bufr));\n\n\tif (!strncmp(bufr, bufs, sizeof(bufs)))\n\t\ttest->n++;\n\telse\n\t\ttest->f++;\n\n\tif (test->n + test->f == 2)\n\t\tre_cancel();\n\telse\n\t\tmbuf_advance(test->mb, RTP_HEADER_SIZE);\n}\n\n\nstatic int test_rtp_listen_priv(bool clear, bool single)\n{\n\tstruct rtp_test test;\n\tstruct sa sa;\n\tsize_t pos;\n\tint err;\n\n\tsa_init(&sa, AF_INET);\n\tmemset(&test, 0, sizeof(test));\n\tif (single) {\n\t\tfor (int i=0; i<10; i++) {\n\t\t\terr = rtp_listen_single(&test.rtp, &sa, 49152 + i,\n\t\t\t\t\t\trtp_recv_handler, &test);\n\t\t\tif (!err || err == ENOMEM)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\telse {\n\t\terr = rtp_listen(&test.rtp, IPPROTO_UDP, &sa, 1024, 49152,\n\t\t\t\t false, rtp_recv_handler, NULL, &test);\n\t}\n\tTEST_ERR(err);\n\n\ttest.mb = mbuf_alloc(2 * (RTP_HEADER_SIZE + 5));\n\tif (!test.mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tpos = RTP_HEADER_SIZE;\n\ttest.mb->pos = test.mb->end = pos;\n\tmbuf_write_str(test.mb, \"abcd\");\n\tmbuf_write_u8(test.mb, 0);\n\ttest.mb->pos = pos;\n\tsa_set_str(&sa, \"127.0.0.1\", sa_port(rtp_local(test.rtp)));\n\terr = rtp_send(test.rtp, &sa, false, true, 0, 160,\n\t\t       tmr_jiffies_rt_usec(), test.mb);\n\tTEST_ERR(err);\n\n\tpos = test.mb->end + RTP_HEADER_SIZE;\n\ttest.mb->pos = test.mb->end = pos;\n\tmbuf_write_str(test.mb, \"bcde\");\n\tmbuf_write_u8(test.mb, 0);\n\ttest.mb->pos = pos;\n\terr = rtp_send(test.rtp, &sa, false, false, 0, 320,\n\t\t       tmr_jiffies_rt_usec(), test.mb);\n\tTEST_ERR(err);\n\n\tif (clear) {\n\t\terr = rtp_clear(test.rtp);\n\t\tTEST_ERR(err);\n\t}\n\n\ttest.mb->pos = RTP_HEADER_SIZE;\n\tif (!clear) {\n\t\terr = re_main_timeout(100);\n\t\tTEST_ERR(err);\n\t}\n\n\tTEST_EQUALS(clear ? 0 : 2, test.n);\n\tTEST_EQUALS(0, test.f);\n\nout:\n\tmem_deref(test.rtp);\n\tmem_deref(test.mb);\n\treturn err;\n}\n\n\nint test_rtp_listen(void)\n{\n\tint err;\n\n\terr = test_rtp_listen_priv(false, false);\n\tTEST_ERR(err);\n\n\terr = test_rtp_listen_priv(true, false);\n\tTEST_ERR(err);\n\n\terr = test_rtp_listen_priv(false, true);\n\tTEST_ERR(err);\n\n\terr = test_rtp_listen_priv(true, true);\n\tTEST_ERR(err);\nout:\n\treturn err;\n}\n\n\nint test_rtcp_twcc(void)\n{\n\t/*\n\t  A RTCP transport-cc parser test. Done as part of WebRtcTransport\n\t  which uses it. TWCC rtcp packets have been extracted from what\n\t  Chrome sends using Wireshark and concatenated to form a single\n\t  compound packet.\n\t*/\n\tuint8_t packets[] = {\n\n\t\t/* First packet */\n\t\t0xaf, 0xcd, 0x00, 0x07, 0xfa, 0x17,\n\t\t0xfa, 0x17, 0x00, 0x00, 0x00,\n\t\t0x02, 0x00, 0x03, 0x00, 0x09, 0x00,\n\t\t0x42, 0x6d, 0x00, 0xad, 0xe0,\n\t\t0x14, 0x18, 0x18, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,\n\n\t\t/* Second packet */\n\t\t0xaf, 0xcd, 0x00, 0x0c, 0xfa, 0x17,\n\t\t0xfa, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00,\n\t\t0x0c, 0x00, 0x1e, 0x00, 0x42, 0x6d,\n\t\t0x01, 0x96, 0xf7, 0xbb, 0xf3, 0x20, 0x02,\n\t\t0xb0, 0x14, 0x08, 0x58, 0x18, 0x00,\n\t\t0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t\t0x00, 0x00, 0x00, 0x00, 0x04, 0x00,\n\t\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,\n\n\t\t/* Third packet */\n\t\t0xaf, 0xcd, 0x00, 0x07, 0xfa, 0x17,\n\t\t0xfa, 0x17, 0x00, 0x00, 0x00,\n\t\t0x02, 0x00, 0x2a, 0x00, 0x0b, 0x00,\n\t\t0x42, 0x6e, 0x02, 0x9b, 0xf8,\n\t\t0xb8, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,\n\n\t\t/* Chrome 114 */\n\t\t0x8f, 0xcd, 0x00, 0x08,\n\t\t0xfa, 0x17, 0xfa, 0x17,\n\t\t0x4f, 0x10, 0x01, 0x6f,\n\t\t0x00, 0x08, 0x00, 0x0e,\n\t\t0x25, 0x27, 0x0c, 0x02,\n\t\t0x20, 0x0e, 0xb9, 0x0b,\n\t\t0x27, 0x00, 0x15, 0x0b,\n\t\t0x0a, 0x00, 0x00, 0x10,\n\t\t0x0b, 0x10, 0x10, 0x1d\n\t};\n\n\tstruct mbuf *buf = mbuf_alloc(sizeof(packets));\n\tif (!buf)\n\t\treturn ENOMEM;\n\n\tstruct rtcp_msg *msg = NULL;\n\tint err = 0;\n\n\tmbuf_write_mem(buf, packets, sizeof(packets));\n\tmbuf_set_pos(buf, 0);\n\n\t/* TWCC n=5 base=3 count=9 reftime=17005 fbcount=0 chunks=2 deltas=7\n\t   (with padding 00 00 03) */\n\terr = rtcp_decode(&msg, buf);\n\tTEST_ERR(err);\n\tASSERT_EQ(err, 0);\n\tASSERT_EQ(msg->hdr.count, RTCP_RTPFB_TWCC);\n\tASSERT_EQ(msg->r.fb.n, 5);\n\tASSERT_TRUE(msg->r.fb.fci.twccv != NULL);\n\tASSERT_EQ(msg->r.fb.fci.twccv->seq, 3);\n\tASSERT_EQ(msg->r.fb.fci.twccv->count, 9);\n\tASSERT_EQ(msg->r.fb.fci.twccv->reftime, 17005);\n\tASSERT_EQ(msg->r.fb.fci.twccv->fbcount, 0);\n\tASSERT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->chunks), 2);\n\tASSERT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->deltas), 7);\n\tmsg = mem_deref(msg);\n\n\t/* TWCC n=10 base=12 count=30 reftime=17005 fbcount=1 chunks=6\n\t   deltas=23 (with padding 00 00 03) */\n\terr = rtcp_decode(&msg, buf);\n\tTEST_ERR(err);\n\tASSERT_EQ(err, 0);\n\tASSERT_EQ(msg->hdr.count, RTCP_RTPFB_TWCC);\n\tASSERT_EQ(msg->r.fb.n, 10);\n\tASSERT_EQ(msg->r.fb.fci.twccv->seq, 12);\n\tASSERT_EQ(msg->r.fb.fci.twccv->count, 30);\n\tASSERT_EQ(msg->r.fb.fci.twccv->reftime, 17005);\n\tASSERT_EQ(msg->r.fb.fci.twccv->fbcount, 1);\n\tASSERT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->chunks), 6);\n\tASSERT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->deltas), 23);\n\tmsg = mem_deref(msg);\n\n\t/* TWCC n=5 base=42 count=11 reftime=17006 fbcount=2 chunks=2\n\t   deltas=9 (with padding 01) */\n\terr = rtcp_decode(&msg, buf);\n\tTEST_ERR(err);\n\tASSERT_EQ(err, 0);\n\tASSERT_EQ(msg->hdr.count, RTCP_RTPFB_TWCC);\n\tASSERT_EQ(msg->r.fb.n, 5);\n\tASSERT_EQ(msg->r.fb.fci.twccv->seq, 42);\n\tASSERT_EQ(msg->r.fb.fci.twccv->count, 11);\n\tASSERT_EQ(msg->r.fb.fci.twccv->reftime, 17006);\n\tASSERT_EQ(msg->r.fb.fci.twccv->fbcount, 2);\n\tASSERT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->chunks), 2);\n\tASSERT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->deltas), 9);\n\tmsg = mem_deref(msg);\n\n\t/* Chrome 114 */\n\terr = rtcp_decode(&msg, buf);\n\tTEST_ERR(err);\n\tASSERT_TRUE(!msg->hdr.p);\n\tASSERT_EQ(RTCP_RTPFB_TWCC, msg->hdr.count);\n\tASSERT_EQ(205, msg->hdr.pt);\n\tASSERT_EQ(8, msg->hdr.length);\n\tASSERT_EQ(0xfa17fa17, msg->r.fb.ssrc_packet);\n\tASSERT_EQ(0x4f10016f, msg->r.fb.ssrc_media);\n\tASSERT_EQ(6, msg->r.fb.n);\n\tconst struct twcc *twcc = msg->r.fb.fci.twccv;\n\tASSERT_TRUE(twcc != NULL);\n\tASSERT_EQ(8, twcc->seq);\n\tASSERT_EQ(14, twcc->count);\n\tASSERT_EQ(2434828, twcc->reftime);\n\tASSERT_EQ(2, twcc->fbcount);\n\tASSERT_EQ(2, mbuf_get_left(twcc->chunks));\n\tASSERT_EQ(14, mbuf_get_left(twcc->deltas));\n\tmsg = mem_deref(msg);\n\n\t/* Assert we have processed everything. */\n\tASSERT_EQ(mbuf_get_left(buf), 0);\n\n out:\n\tmem_deref(buf);\n\tmem_deref(msg);\n\treturn err;\n}\n"
  },
  {
    "path": "test/rtpext.c",
    "content": "/**\n * @file rtpext.c  RTP Header Extensions\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"rtpext\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic const uint8_t packet_bytes[] = {\n\n\t/* Header */\n\t0xbe, 0xde, 0x00, 0x01,\n\n\t/* First Extension */\n\t0x40, 0x30,\n\n\t/* Second Extension */\n\t0x10, 0xe0\n};\n\n\nstruct rtpext_header {\n\tuint16_t type;\n\tuint16_t num_bytes;\n};\n\n\n/* Common for One-Byte and Two-Byte headers */\nstatic int rtpext_hdr_decode(struct rtpext_header *hdr, struct mbuf *mb)\n{\n\tif (mbuf_get_left(mb) < RTPEXT_HDR_SIZE)\n\t\treturn EBADMSG;\n\n\thdr->type      = ntohs(mbuf_read_u16(mb));\n\thdr->num_bytes = ntohs(mbuf_read_u16(mb)) * 4;\n\n\tif (mbuf_get_left(mb) < hdr->num_bytes)\n\t\treturn EBADMSG;\n\n\treturn 0;\n}\n\n\nstatic int test_rtpext_long(void)\n{\n\tstatic const uint8_t TEST_EXTENSION_ID_TWOBYTE = 0xf0;\n#define TEST_DATA_LENGTH 3\n#define NUM_BYTES_LONG 8\n\tstatic const uint8_t packet[RTPEXT_HDR_SIZE + NUM_BYTES_LONG] = {\n\t\t0x10, 0x00, 0x00, 0x02,\n\t\t0xf0, 0x03, 0x01, 0x02,\n\t\t0x03, 0x00, 0x00, 0x00\n\t};\n\n\tstatic const uint8_t data[TEST_DATA_LENGTH] = { 0x01, 0x02, 0x03 };\n\n\tstruct mbuf *mb = mbuf_alloc(1024);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tstruct rtpext_header hdr;\n\tstruct rtpext ext;\n\n\t/* Encode packet */\n\n\tint err = rtpext_hdr_encode_long(mb, NUM_BYTES_LONG);\n\tASSERT_EQ(0, err);\n\n\terr = rtpext_encode_long(mb, TEST_EXTENSION_ID_TWOBYTE,\n\t\t\t\t TEST_DATA_LENGTH, data);\n\tASSERT_EQ(0, err);\n\n\t/* padding */\n\tmbuf_fill(mb, 0x00, 3);\n\n\tTEST_MEMCMP(packet, sizeof(packet), mb->buf, mb->end);\n\n\tmb->pos = 0;\n\n\t/* Decode packet */\n\n\terr = rtpext_hdr_decode(&hdr, mb);\n\tASSERT_EQ(0, err);\n\n\tASSERT_EQ(RTPEXT_TYPE_MAGIC_LONG, hdr.type);\n\tASSERT_EQ(NUM_BYTES_LONG,         hdr.num_bytes);\n\n\terr = rtpext_decode_long(&ext, mb);\n\tASSERT_EQ(0, err);\n\n\tASSERT_EQ(TEST_EXTENSION_ID_TWOBYTE, ext.id);\n\tASSERT_EQ(TEST_DATA_LENGTH,          ext.len);\n\tTEST_MEMCMP(data, sizeof(data), ext.data, ext.len);\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/*\n       0                   1                   2                   3\n       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n      |       0x10    |    0x00       |           length=3            |\n      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n      |      ID       |     L=0       |     ID        |     L=1       |\n      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n      |       data    |    0 (pad)    |       ID      |      L=4      |\n      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n      |                          data                                 |\n      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\n*/\nstatic int test_rtpext_long_rfc(void)\n{\n#define NUM_BYTES_RFC (12)\n\tstruct mbuf *mb = mbuf_alloc(1024);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tint err = rtpext_hdr_encode_long(mb, NUM_BYTES_RFC);\n\tASSERT_EQ(0, err);\n\n\t/* Encode */\n\n\terr = rtpext_encode_long(mb, 0x80, 0, NULL);\n\tASSERT_EQ(0, err);\n\n\terr = rtpext_encode_long(mb, 0x81, 1, (uint8_t *)\"A\" );\n\tASSERT_EQ(0, err);\n\n\t/* padding */\n\tmbuf_write_u8(mb, 0x00);\n\n\terr = rtpext_encode_long(mb, 0x82, 4, (uint8_t *)\"ABCD\" );\n\tASSERT_EQ(0, err);\n\n\tstatic const uint8_t packet[RTPEXT_HDR_SIZE + NUM_BYTES_RFC] = {\n\t\t0x10, 0x00, 0x00, 0x03,\n\t\t0x80, 0x00, 0x81, 0x01,\n\t\t0x41, 0x00, 0x82, 0x04,\n\t\t0x41, 0x42, 0x43, 0x44,\n\t};\n\n\tTEST_MEMCMP(packet, sizeof(packet), mb->buf, mb->end);\n\n\tmb->pos = 0;\n\n\t/* Decode */\n\n\tstruct rtpext_header hdr;\n\n\terr = rtpext_hdr_decode(&hdr, mb);\n\tASSERT_EQ(0, err);\n\n\tASSERT_EQ(RTPEXT_TYPE_MAGIC_LONG, hdr.type);\n\tASSERT_EQ(NUM_BYTES_RFC,          hdr.num_bytes);\n\n\tstruct rtpext ext;\n\n\terr = rtpext_decode_long(&ext, mb);\n\tASSERT_EQ(0, err);\n\n\tASSERT_EQ(0x80, ext.id);\n\tASSERT_EQ(0,    ext.len);\n\n\terr = rtpext_decode_long(&ext, mb);\n\tASSERT_EQ(0, err);\n\n\tASSERT_EQ(0x81, ext.id);\n\tASSERT_EQ(1,    ext.len);\n\tASSERT_EQ(0x41,    ext.data[0]);\n\n\terr = rtpext_decode_long(&ext, mb);\n\tASSERT_EQ(0, err);\n\n\tASSERT_EQ(0x82, ext.id);\n\tASSERT_EQ(4,    ext.len);\n\tTEST_MEMCMP(&packet[12], 4, ext.data, ext.len);\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nint test_rtpext(void)\n{\n\tstruct mbuf *mb = mbuf_alloc(sizeof(packet_bytes));\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tint err = mbuf_write_mem(mb, packet_bytes, sizeof(packet_bytes));\n\tASSERT_EQ(0, err);\n\n\tmb->pos = 0;\n\n\t/* decode header */\n\tuint16_t type  = ntohs(mbuf_read_u16(mb));\n\tuint16_t words = ntohs(mbuf_read_u16(mb));\n\n\tASSERT_EQ(RTPEXT_TYPE_MAGIC, type);\n\tASSERT_EQ(1, words);\n\n\tsize_t num_bytes = words * sizeof(uint32_t);\n\n\tASSERT_EQ(num_bytes, mbuf_get_left(mb));\n\n\tstruct rtpext ext;\n\n\t/* First extension */\n\terr = rtpext_decode(&ext, mb);\n\tASSERT_EQ(0, err);\n\tASSERT_EQ(4, ext.id);\n\tASSERT_EQ(1, ext.len);\n\tASSERT_EQ(0x30, ext.data[0]);\n\n\t/* Second extension */\n\terr = rtpext_decode(&ext, mb);\n\tASSERT_EQ(0, err);\n\tASSERT_EQ(1, ext.id);\n\tASSERT_EQ(1, ext.len);\n\tASSERT_EQ(0xe0, ext.data[0]);\n\n\terr = test_rtpext_long();\n\tif (err)\n\t\tgoto out;\n\n\terr = test_rtpext_long_rfc();\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n"
  },
  {
    "path": "test/sa.c",
    "content": "/**\n * @file sa.c Socket address Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_sa\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_sa_cmp(void)\n{\n\tconst struct {\n\t\tconst char *host1;\n\t\tuint16_t port1;\n\t\tconst char *host2;\n\t\tuint16_t port2;\n\t\tbool eq;\n\t} testv[] = {\n#if HAVE_UNIXSOCK\n\t\t{\n\t\t\t\"unix:/test.sock\", 0,\n\t\t\t\"unix:/test.sock\", 0,\n\t\t\ttrue\n\t\t},\n#endif\n\t\t{\n\t\t\t\"1.2.3.4\", 12345,\n\t\t\t\"1.2.3.4\", 12345,\n\t\t\ttrue\n\t\t},\n\t\t{\n\t\t\t\"1.2.3.4\", 12345,\n\t\t\t\"1.2.3.5\", 12345,\n\t\t\tfalse\n\t\t},\n\t\t{\n\t\t\t\"1.2.3.4\", 12345,\n\t\t\t\"1.2.3.4\", 12344,\n\t\t\tfalse\n\t\t},\n\t\t{\n\t\t\t\"0:0:0:0:0:0:0:0\", 123,\n\t\t\t\"::\", 123,\n\t\t\ttrue\n\t\t},\n\t\t{\n\t\t\t\"0:0:0:0:0:0:0:1\", 123,\n\t\t\t\"::1\", 123,\n\t\t\ttrue\n\t\t},\n\t\t{\n\t\t\t\"0:0:0:0:0:0:1:1\", 123,\n\t\t\t\"::1\", 123,\n\t\t\tfalse\n\t\t},\n\t\t{\n\t\t\t\"2001:0:53aa:64c:0:fbff:ab2e:1eac\",        2001,\n\t\t\t\"2001:0000:53aa:064c:0000:fbff:ab2e:1eac\", 2001,\n\t\t\ttrue\n\t\t},\n\t\t{\n\t\t\t\"3001:0:53aa:64c:0:fbff:ab2e:1eac\",        2001,\n\t\t\t\"2001:0000:53aa:064c:0000:fbff:ab2e:1eac\", 2001,\n\t\t\tfalse\n\t\t},\n\t\t{\n\t\t\t\"192.168.1.1\",                             123,\n\t\t\t\"2001:0000:53aa:064c:0000:fbff:ab2e:1eac\", 123,\n\t\t\tfalse\n\t\t},\n\t\t{ /* IPv6-mapped IPv4-address */\n\t\t\t\"::ffff:208.68.208.201\",                   3478,\n\t\t\t\"208.68.208.201\",                          3478,\n\t\t\ttrue\n\t\t},\n\t\t{\n\t\t\t\"fe80::215:58ff:fe2d:90ab\", 3333,\n\t\t\t\"fe80:0000:0000:0000:0215:58ff:fe2d:90ab\", 3333,\n\t\t\ttrue\n\t\t},\n\t\t{\n\t\t\t\"fe80::215:58ff:fe2d:90ab\", 3333,\n\t\t\t\"fe80:0000:0000:0000:1215:58ff:fe2d:90ab\", 3333,\n\t\t\tfalse\n\t\t},\n\t};\n\tsize_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tstruct sa sa1, sa2;\n\t\tbool eq;\n\n\t\terr = sa_set_str(&sa1, testv[i].host1, testv[i].port1);\n\t\tif (err)\n\t\t\tbreak;\n\t\terr = sa_set_str(&sa2, testv[i].host2, testv[i].port2);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\teq = sa_cmp(&sa1, &sa2, SA_ALL);\n\t\tif (!testv[i].eq == !eq)\n\t\t\tcontinue;\n\n\t\tDEBUG_WARNING(\"sa cmp %u: (%J) (%J) expected (%d),\"\n\t\t\t      \" got (%d)\\n\", i, &sa1, &sa2,\n\t\t\t      testv[i].eq, eq);\n\t\treturn EINVAL;\n\t}\n\n\treturn err;\n}\n\n\nint test_sa_decode(void)\n{\n\tconst struct {\n\t\tint err;\n\t\tint af;\n\t\tconst char *str;\n\t\tconst char *addr;\n\t\tuint16_t port;\n\t} testv[] = {\n\t\t{0,      AF_INET,  \"1.2.3.4:1234\",  \"1.2.3.4\", 1234},\n\t\t{0,      AF_INET,  \"1.2.3.4:0\",     \"1.2.3.4\", 0},\n\t\t{EINVAL, AF_INET,  \"1.2.3.4\",       \"\",        0},\n\t\t{EINVAL, AF_INET,  \"1.2.3.4.:1234\", \"\",        0},\n\t\t{0, AF_INET6, \"[::1]:1\", \"::1\", 1},\n\t\t{0, AF_INET6, \"[fe80::215:58ff:fe2d:90ab]:3333\",\n\t\t \"fe80::215:58ff:fe2d:90ab\", 3333},\n\t\t{EINVAL, AF_INET6, \"[::1]\", \"\", 0},\n\t};\n\tuint32_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tstruct sa sa, sa2;\n\t\tchar buf[64];\n\t\tint e;\n\n\t\te = sa_decode(&sa, testv[i].str, strlen(testv[i].str));\n\t\tif (testv[i].err != e) {\n\t\t\tDEBUG_WARNING(\"sa_decode: test %u:\"\n\t\t\t\t      \" expected (%m) got (%m) [%s]\\n\", i,\n\t\t\t\t      testv[i].err, e, testv[i].str);\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t\tif (e)\n\t\t\tcontinue;\n\n\t\tif (testv[i].af != sa_af(&sa)) {\n\t\t\tDEBUG_WARNING(\"sa_decode: af mismatch %d != %d\\n\",\n\t\t\t\t      testv[i].af, sa_af(&sa));\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\n\t\terr = sa_set_str(&sa2, testv[i].addr, testv[i].port);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tif (!sa_cmp(&sa, &sa2, SA_ALL)) {\n\t\t\tDEBUG_WARNING(\"sa_decode: sa_cmp() failed\\n\");\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\n\t\t(void)re_snprintf(buf, sizeof(buf), \"%J\", &sa);\n\t\tif (0 != strcmp(testv[i].str, buf)) {\n\t\t\tDEBUG_WARNING(\"%u: strcmp: %s != %s\\n\",\n\t\t\t\t      testv[i].str, buf);\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn err;\n}\n\n\n/* Test classification of loopback and link-local IP address */\nint test_sa_class(void)\n{\n\tconst struct {\n\t\tbool lo;\n\t\tbool ll;\n\t\tbool any;\n\t\tconst char *addr;\n\t} testv[] = {\n\t\t{false, false, true,  \"0.0.0.0\"},\n\t\t{false, false, false, \"1.2.3.4\"},\n\t\t{true,  false, false, \"127.0.0.0\"},\n\t\t{true,  false, false, \"127.0.0.1\"},\n\t\t{true,  false, false, \"127.3.0.3\"},\n\t\t{false, true,  false, \"169.254.1.2\"},\n\t\t{false, false, true,  \"::\"},\n\t\t{true,  false, false, \"::1\"},\n\t\t{false, true,  false, \"fe80::215:58ff:fe2d:90ab\"},\n\t\t{false, false, false, \"2610:a0:c779:b::d1ad:35b4\"}\n\t};\n\tuint32_t i;\n\tint err = 0;\n\n\t/*\n\t * NOTE: The application and library must use the same build flags,\n\t *       so that the size of \"struct sa\" is the same.\n\t */\n\tASSERT_EQ(sizeof(struct sa), sa_struct_get_size());\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tstruct sa sa;\n\t\tint lo, ll, any;\n\n\t\terr = sa_set_str(&sa, testv[i].addr, 0);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tlo = sa_is_loopback(&sa);\n\t\tif ((int)testv[i].lo != lo) {\n\t\t\tDEBUG_WARNING(\"%u: %s: loopback mismatch %d!=%d\\n\",\n\t\t\t\t      i, testv[i].addr, testv[i].lo, lo);\n\t\t\terr = EINVAL;\n\t\t\tgoto out;\n\t\t}\n\n\t\tll = sa_is_linklocal(&sa);\n\t\tif ((int)testv[i].ll != ll) {\n\t\t\tDEBUG_WARNING(\"%u: %s: linklocal mismatch %d!=%d\\n\",\n\t\t\t\t      i, testv[i].addr, testv[i].ll, ll);\n\t\t\terr = EINVAL;\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (ll && sa_af(&sa)==AF_INET6) {\n\t\t\tsa_set_scopeid(&sa, 2);\n\t\t\tTEST_EQUALS(2, sa_scopeid(&sa));\n\t\t}\n\n\t\tany = sa_is_any(&sa);\n\t\tif ((int)testv[i].any != any) {\n\t\t\tDEBUG_WARNING(\"%u: %s: any mismatch %d!=%d\\n\",\n\t\t\t\t      i, testv[i].addr, testv[i].any, any);\n\t\t\terr = EINVAL;\n\t\t\tgoto out;\n\t\t}\n\t}\n\n#if 0\n\t{\n\t\tstruct sa sax;\n\t\tTEST_ASSERT(sizeof(sax.u) <= sizeof(sax.u.padding));\n\t}\n#endif\n\n out:\n\treturn err;\n}\n\n\nint test_sa_ntop(void)\n{\n\tconst struct {\n\t\tint af;\n\t\tconst char *addr;\n\t} testv[] = {\n\t\t{AF_INET,  \"0.0.0.0\"},\n\t\t{AF_INET,  \"1.2.3.4\"},\n\t\t{AF_INET,  \"255.254.253.128\"},\n\t\t{AF_INET6, \"::1\"},\n\t\t{AF_INET6, \"fe80::215:58ff:fe2d:90ab\"},\n\t\t{AF_INET6, \"2610:a0:c779:b::d1ad:35b4\"}\n\t};\n\tuint32_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tstruct sa sa0, sa;\n\t\tchar buf[64];\n\n\t\terr = sa_set_str(&sa0, testv[i].addr, 0);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tif (testv[i].af != sa_af(&sa0)) {\n\t\t\tDEBUG_WARNING(\"ntop: af mismatch %d != %d\\n\",\n\t\t\t\t      testv[i].af, sa_af(&sa0));\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\n\t\terr = sa_ntop(&sa0, buf, 2);\n\t\tTEST_NOT_EQUALS(0, err);\n\n\t\terr = sa_ntop(&sa0, buf, sizeof(buf));\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tif (0 != strcmp(buf, testv[i].addr)) {\n\t\t\tDEBUG_WARNING(\"ntop: addr mismatch (%s) != (%s)\\n\",\n\t\t\t\t      testv[i].addr, buf);\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\n\t\terr = sa_set_sa(&sa, &sa0.u.sa);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"sa_set_sa: %m\\n\", err);\n\t\t\tbreak;\n\t\t}\n\t\tif (testv[i].af != sa_af(&sa)) {\n\t\t\terr = EINVAL;\n\t\t\tDEBUG_WARNING(\"af mismatch (test=%d sa=%d)\\n\",\n\t\t\t\t      testv[i].af, sa_af(&sa));\n\t\t\tbreak;\n\t\t}\n\t}\n\nout:\n\treturn err;\n}\n\n\nint test_sa_pton(void)\n{\n\tstruct sa sa;\n\tint err = 0;\n\tconst struct {\n\t\tconst char *addr;\n\t\tint err;\n\t} testv[] = {\n\t\t{\"github.com\",                      EINVAL       },\n\t\t{\"6002\",                            EINVAL       },\n\t\t{\"ga01::3a28\",                      EINVAL       },\n\t\t{\"fa01::2a29\",                      0            },\n\t\t{\"127.0.0.1\",                       0            },\n\t\t{\"192.168.110.2\",                   0            },\n#if HAVE_UNIXSOCK\n\t\t{\"unix:/test.sock\",                 0            },\n#endif\n\t\t{\"fe80::xxxx:d8d9:ddc3:25dd:%eth0\", EADDRNOTAVAIL},\n\t};\n\n\tfor (size_t i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tint e = sa_pton(testv[i].addr, &sa);\n\t\tTEST_EQUALS(testv[i].err, e);\n\t}\n\n out:\n\treturn err;\n}\n\n\nint test_sa_pton_linklocal(void)\n{\n\tconst char test_ipv6ll_scope[] = \"fe80::3a28:d8d9:ddc3:25dd%\";\n\tchar buf[256];\n\tstruct sa sa_default_ip, sa;\n\tint err;\n#ifndef WIN32\n\tchar ifname[64];\n#endif\n\n\tif (0 != net_if_getlinklocal(NULL, AF_INET6, &sa_default_ip))\n\t\treturn ESKIPPED;\n\n\t/* Use IPv4 since not all test systems have a default IPv6 route */\n\tnet_default_source_addr_get(AF_INET, &sa_default_ip);\n\n#ifdef WIN32\n\tre_snprintf(buf, sizeof(buf), \"%s%d\",\n\t\t    test_ipv6ll_scope, sa_scopeid(&sa_default_ip));\n#else\n\tnet_if_getname(ifname, sizeof(ifname), AF_INET, &sa_default_ip);\n\tre_snprintf(buf, sizeof(buf), \"%s%s\",\n\t\t    test_ipv6ll_scope, ifname);\n#endif\n\n\terr = sa_pton(buf, &sa);\n\tTEST_ERR(err);\n\n\tASSERT_EQ(AF_INET6, sa_af(&sa));\n\tASSERT_TRUE(sa_is_linklocal(&sa));\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/sdp.c",
    "content": "/**\n * @file sdp.c SDP Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define TEST_STRCMP_LEN(exp, actual) \\\n\tTEST_STRCMP((exp), str_len((exp)), (actual), str_len((actual)))\n\n\nstatic const char ref_host[] = \"1.2.3.4\";\nstatic const uint16_t ref_port = 5004;\nstatic const char ref_pt[] = \"0\";\nstatic const char *ref_cname   = \"PCMU\";\nstatic const char *cname_speex = \"speex\";\nstatic const uint32_t ref_srate = 8000;\nstatic const char ref_msg[] =\n\t\"v=0\\r\\n\"\n\t\"o=- 1234 5678 IN IP4 1.2.3.4\\r\\n\"\n\t\"s=-\\r\\n\"\n\t\"c=IN IP4 1.2.3.4\\r\\n\"\n\t\"t=0 0\\r\\n\"\n\t\"m=audio 5004 RTP/AVP 0 110\\r\\n\"\n\t\"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\"a=rtpmap:110 speex/16000/2\\r\\n\"\n\t\"a=sendrecv\\r\\n\";\n\n\nstatic const char *msgs[] = {\n\t/* Counterpath */\n\t\"v=0\\n\"\n\t\"o=- 1 2 IN IP4 84.209.220.122\\r\\n\"\n\t\"s=<CounterPath eyeBeam 1.5>\\r\\n\"\n\t\"c=IN IP4 84.209.220.122\\r\\n\"\n\t\"t=0 0\\r\\n\"\n\t\"m=audio 24484 RTP/AVP 107 119 0 98 8 3 101\\r\\n\"\n\t\"a=alt:1 2 : 8KiUNmDF 2AKrU/iZ 192.168.1.100 24484\\r\\n\"\n\t\"a=alt:2 1 : uEmq9erD rG6uFpsK 84.209.220.122 24484\\r\\n\"\n\t\"a=fmtp:101 0-15\\r\\n\"\n\t\"a=rtpmap:107 BV32/16000\\r\\n\"\n\t\"a=rtpmap:119 BV32-FEC/16000\\r\\n\"\n\t\"a=rtpmap:98 iLBC/8000\\r\\n\"\n\t\"a=rtpmap:101 telephone-event/8000\\r\\n\"\n\t\"a=sendrecv\\r\\n\"\n\t\"a=x-rtp-session-id:EE42E5DC96034E1A95B8843DA28640E4\\r\\n\"\n\t\"m=video 5040 RTP/AVP 115 34\\r\\n\"\n\t\"a=alt:1 2 : 6zn66DoK 0TsJT2lQ 192.168.1.100 5040\\r\\n\"\n\t\"a=alt:2 1 : JTVNzu+a 8pvqo0dE 84.209.220.122 5040\\r\\n\"\n\t\"a=fmtp:115 QCIF=2 MAXBR=2180\\r\\n\"\n\t\"a=fmtp:34 QCIF=2 MAXBR=2180\\r\\n\"\n\t\"a=rtpmap:115 H263-1998/90000\\r\\n\"\n\t\"a=rtpmap:34 H263/90000\\r\\n\"\n\t\"a=sendrecv\\r\\n\"\n\t\"a=x-rtp-session-id:E6856C1F08904D6B88B129266C82D351\\r\\n\",\n\n\t/** Freeswitch 1.0rc1 */\n\t\"v=0\\r\\n\"\n\t\"o=FreeSWITCH 531003883936 28814208941 IN IP4 1.2.3.4\\r\\n\"\n\t\"s=FreeSWITCH\\r\\n\"\n\t\"c=IN IP4 1.2.3.4\\r\\n\"\n\t\"t=0 0\\r\\n\"\n\t\"a=sendrecv\\r\\n\"\n\t\"m=audio 16610 RTP/AVP 8 99 13\\r\\n\"\n\t\"a=rtpmap:8 PCMA/8000\\r\\n\"\n\t\"a=rtpmap:99 telephone-event/8000\\r\\n\"\n\t\"a=fmtp:99 0-16\\r\\n\"\n\t\"a=rtpmap:13 CN/8000\\r\\n\"\n\t\"a=ptime:20\\r\\n\"\n\t\"a=nortpproxy:yes\\r\\n\",\n\n\t/* newline termination */\n\t\"v=0\\n\"\n\t\"o=- 531003883936 28814208941 IN IP4 1.2.3.4\\n\"\n\t\"s=-\\n\"\n\t\"t=0 0\\n\"\n\t\"m=audio 16610 RTP/AVP 8\\n\"\n\t\"c=IN IP4 1.2.3.4\\n\"\n\t\"a=rtpmap:8 PCMA/8000\\n\",\n\n\t/* Polycom */\n\t\"v=0\\r\\n\"\n\t\"o=- 1197975037 1197975037 IN IP4 192.168.9.74\\r\\n\"\n\t\"s=Polycom IP Phone\\r\\n\"\n\t\"c=IN IP4 192.168.9.74\\r\\n\"\n\t\"t=0 0\\r\\n\"\n\t\"m=audio 49200 RTP/AVP 8 0 9 18 96\\r\\n\"\n\t\"a=sendrecv\\r\\n\"\n\t\"a=crypto:1 AES_CM_128_HMAC_SHA1_80\"\n\t\" inline:tMuyik1Aiiq9p4DQVHhAASSWDEP7K7wo0cICOn39\\r\\n\"\n\t\"a=rtpmap:8 PCMA/8000\\r\\n\"\n\t\"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\"a=rtpmap:9 G722/8000\\r\\n\"\n\t\"a=rtpmap:18 G729/8000\\r\\n\"\n\t\"a=rtpmap:96 telephone-event/8000\\r\\n\",\n\n\t/* Ekiga 3.0 */\n\t\"v=0\\r\\n\"\n\t\"o=- 1235562135 1235562135 IN IP4 192.168.1.55\\r\\n\"\n\t\"s=Opal SIP Session\\r\\n\"\n\t\"c=IN IP4 192.168.1.55\\r\\n\"\n\t\"t=0 0\\r\\n\"\n\t\"m=audio 5062 RTP/AVP 106 105 9 117 8 0 104 103 102 120 3 116 101\\r\\n\"\n\t\"a=sendrecv\\r\\n\"\n\t\"a=rtpmap:106 CELT/48000/1\\r\\n\"\n\t\"a=rtpmap:105 CELT/32000/1\\r\\n\"\n\t\"a=rtpmap:9 G722/8000/1\\r\\n\"\n\t\"a=rtpmap:117 Speex/16000/1\\r\\n\"\n\t\"a=fmtp:117 sr=16000,mode=any\\r\\n\"\n\t\"a=rtpmap:8 PCMA/8000/1\\r\\n\"\n\t\"a=rtpmap:0 PCMU/8000/1\\r\\n\"\n\t\"a=rtpmap:104 G726-16/8000/1\\r\\n\"\n\t\"a=rtpmap:103 G726-24/8000/1\\r\\n\"\n\t\"a=rtpmap:102 G726-32/8000/1\\r\\n\"\n\t\"a=rtpmap:120 G726-40/8000/1\\r\\n\"\n\t\"a=rtpmap:3 gsm/8000/1\\r\\n\"\n\t\"a=rtpmap:116 Speex/8000/1\\r\\n\"\n\t\"a=fmtp:116 sr=8000,mode=any\\r\\n\"\n\t\"a=rtpmap:101 telephone-event/8000\\r\\n\"\n\t\"a=fmtp:101 0-16,32,36\\r\\n\"\n\n\t/* LG */\n\t\"v=0\\r\\n\"\n\t\"o=LGN_IP_PHONE 29386 29386 IN IP4 192.168.1.97\\r\\n\"\n\t\"s=SIP Call\\r\\n\"\n\t\"c=IN IP4 85.112.135.82\\r\\n\"\n\t\"t=0 0\\r\\n\"\n\t\"m=audio 17412 RTP/AVP 9 8 0 18 101\\r\\n\"\n\t\"c=IN IP4 85.112.135.82\\r\\n\"\n\t\"b=AS:82\\r\\n\"\n\t\"a=rtpmap:9 G722/8000\\r\\n\"\n\t\"a=rtpmap:8 PCMA/8000\\r\\n\"\n\t\"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\"a=rtpmap:18 G729/8000\\r\\n\"\n\t\"a=fmtp:18 annexb=no\\r\\n\"\n\t\"a=rtpmap:101 telephone-event/8000\\r\\n\"\n\t\"a=fmtp:101 0-15\\r\\n\"\n\t\"a=ptime:20\\r\\n\"\n\t\"a=sendrecv\\r\\n\"\n\t\"m=video 16512 RTP/AVP 98 102 34 105\\r\\n\"\n\t\"c=IN IP4 85.112.135.82\\r\\n\"\n\t\"b=TIAS:329000\\r\\n\"\n\t\"b=AS:366\\r\\n\"\n\t\"a=rtpmap:98 H264/90000\\r\\n\"\n\t\"a=rtpmap:102 H264/90000\\r\\n\"\n\t\"a=fmtp:98 profile-level-id=42800C; packetization-mode=0\\r\\n\"\n\t\"a=fmtp:102 profile-level-id=42800C; packetization-mode=1\\r\\n\"\n\t\"a=rtpmap:34 H263/90000\\r\\n\"\n\t\"a=fmtp:34 CIF=1;QCIF=1\\r\\n\"\n\t\"a=rtpmap:105 MP4V-ES/90000\\r\\n\"\n};\n\n\n/** Compare two SDP messages line-by-line (exclude owner) */\nstatic bool sdp_cmp(struct mbuf *mb, const char *msg)\n{\n\tstruct pl pl;\n\n\tif (!mb || !msg)\n\t\treturn false;\n\n\tpl.p = (char *)mb->buf;\n\tpl.l = mb->end;\n\n\twhile (pl.l && strlen(msg)) {\n\t\tstruct pl n1, v1, n2, v2;\n\n\t\tif (re_regex(pl.p, pl.l,\n\t\t\t     \"[^=]1=[^\\r\\n]+\", &n1, &v1))\n\t\t\treturn false;\n\n\t\tif (re_regex(msg, strlen(msg),\n\t\t\t     \"[^=]1=[^\\r\\n]+\", &n2, &v2))\n\t\t\treturn false;\n\n\t\tpl_advance(&pl, 2 + v1.l + 2);\n\t\tmsg += (2 + v2.l + 2);\n\n\t\tif (0 != pl_cmp(&n1, &n2)) {\n\t\t\tDEBUG_WARNING(\"name mismatch: %r=%r\\n\", &n1, &v1);\n\t\t\treturn false;\n\t\t}\n\n\t\t/* ignore owner */\n\t\tif (n1.p[0] == 'o')\n\t\t\tcontinue;\n\n\t\tif (0 != pl_cmp(&v1, &v2)) {\n\t\t\tDEBUG_WARNING(\"value mismatch: %r=%r\\n\",\n\t\t\t\t      &n1, &v1);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (pl.l) {\n\t\tDEBUG_WARNING(\"%u bytes junk at end: %r\\n\", pl.l, &pl);\n\t}\n\n\tif (strlen(msg)) {\n\t\tDEBUG_WARNING(\"%u bytes junk at end: %s\\n\", strlen(msg), msg);\n\t}\n\n\treturn !pl.l && !strlen(msg);\n}\n\n\nint test_sdp_all(void)\n{\n\tstruct sdp_session *sess = NULL;\n\tstruct sdp_media *audio = NULL;\n\tstruct mbuf *desc = NULL;\n\tstruct sa ref;\n\tconst struct sdp_format *rc = NULL, *sc;\n\tstruct sa laddr;\n\tint err;\n\n\t(void)sa_set_str(&laddr, ref_host, 0);\n\n\terr = sdp_session_alloc(&sess, &laddr);\n\tif (err)\n\t\tgoto out;\n\n\terr = sdp_media_add(&audio, sess, sdp_media_audio, 5004,\n\t\t\t    sdp_proto_rtpavp);\n\tif (err)\n\t\tgoto out;\n\n\terr  = sdp_format_add(NULL, audio, false, ref_pt, ref_cname,\n\t\t\t      ref_srate, 1, NULL, NULL, NULL, false, NULL);\n\terr |= sdp_format_add(NULL, audio, false, \"110\", cname_speex,\n\t\t\t      16000, 2, NULL, NULL, NULL, false, NULL);\n\tif (err)\n\t\tgoto out;\n\n\t/* find codec - expected */\n\tsc = sdp_media_format(audio, true, NULL, 0, \"PCMU\", 8000, 1);\n\tif (!sc) {\n\t\tDEBUG_WARNING(\"codec not found\\n\");\n\t\terr = ENOENT;\n\t\tgoto out;\n\t}\n\n\tsc = sdp_media_format(audio, true, NULL, 110, \"Speex\", 16000, 2);\n\tif (!sc) {\n\t\tDEBUG_WARNING(\"codec not found: speex\\n\");\n\t\terr = ENOENT;\n\t\tgoto out;\n\t}\n\n\t/* find codec - not expected */\n\tsc = sdp_media_format(audio, true, NULL, -1, \"Speex\", 8000, 1);\n\tif (sc) {\n\t\tDEBUG_WARNING(\"unexpected codec found\\n\");\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\terr = sdp_encode(&desc, sess, true);\n\tif (err)\n\t\tgoto out;\n\n\tif (!sdp_cmp(desc, ref_msg)) {\n\t\tDEBUG_WARNING(\"ref: %s\\n\", ref_msg);\n\t\tDEBUG_WARNING(\"sdp: %b\\n\", desc->buf, desc->end);\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\terr = sdp_decode(sess, desc, false);\n\tif (err)\n\t\tgoto out;\n\n\trc = sdp_media_rformat(audio, NULL);\n\tif (!rc) {\n\t\terr = ENOENT;\n\t\tgoto out;\n\t}\n\n\terr = sa_set_str(&ref, ref_host, ref_port);\n\tif (err)\n\t\tgoto out;\n\n\terr = EINVAL;\n\n\tif (!sa_cmp(sdp_media_raddr(audio), &ref, SA_ALL))\n\t\tgoto out;\n\n\tif (0 != strcmp(rc->id, ref_pt))\n\t\tgoto out;\n\n\tif (0 != strcmp(ref_cname, rc->name))\n\t\tgoto out;\n\n\tif (rc->srate != ref_srate)\n\t\tgoto out;\n\n\terr = 0;\n\n out:\n\tmem_deref(audio);\n\tmem_deref(sess);\n\tmem_deref(desc);\n\n\treturn err;\n}\n\n\n/**\n * Test parsing of various SDP messages from various vendors\n */\nint test_sdp_parse(void)\n{\n\tstruct sdp_session *sess = NULL;\n\tstruct sdp_media *audio;\n\tstruct mbuf *mb;\n\tstruct sa laddr;\n\tuint32_t i;\n\tint err = 0;\n\n\tmb = mbuf_alloc(2048);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tsa_init(&laddr, AF_INET);\n\n\tfor (i=0; i<RE_ARRAY_SIZE(msgs); i++) {\n\n\t\tsess = mem_deref(sess);\n\n\t\terr = sdp_session_alloc(&sess, &laddr);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = sdp_media_add(&audio, sess, sdp_media_audio, 5004,\n\t\t\t\t    sdp_proto_rtpavp);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = sdp_format_add(NULL, audio, false, ref_pt, ref_cname,\n\t\t\t\t     ref_srate, 1, NULL, NULL, NULL, false,\n\t\t\t\t     NULL);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = sdp_format_add(NULL, audio, false, \"8\", \"PCMA\", 8000, 1,\n\t\t\t\t     NULL, NULL, NULL, false, NULL);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tmbuf_rewind(mb);\n\t\t(void)mbuf_write_str(mb, msgs[i]);\n\t\tmb->pos = 0;\n\n\t\terr = sdp_decode(sess, mb, true);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n out:\n\tmem_deref(sess);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstruct oa {\n\tstruct sdp_session *alice, *bob;\n};\n\n\nstatic int oa_init(struct oa *oa)\n{\n\tstruct sa laddr;\n\tint err;\n\n\tif (!oa->alice) {\n\t\t(void)sa_set_str(&laddr, \"1.2.3.4\", 0);\n\t\terr = sdp_session_alloc(&oa->alice, &laddr);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\tif (!oa->bob) {\n\t\t(void)sa_set_str(&laddr, \"5.6.7.8\", 0);\n\t\terr = sdp_session_alloc(&oa->bob, &laddr);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\nstatic void oa_reset(struct oa *oa)\n{\n\toa->alice = mem_deref(oa->alice);\n\toa->bob = mem_deref(oa->bob);\n}\n\n\nstatic int oa_addmedia(struct oa *oa, bool local,\n\t\t       const char *mname, uint16_t port, const char *transp,\n\t\t       enum sdp_dir dir, uint32_t ncodec, ...)\n{\n\tstruct sdp_media *m;\n\tva_list ap;\n\tint err;\n\n\terr = oa_init(oa);\n\tif (err)\n\t\treturn err;\n\n\terr = sdp_media_add(&m, local ? oa->alice : oa->bob,\n\t\t\t    mname, port, transp);\n\tif (err)\n\t\treturn err;\n\n\tsdp_media_set_ldir(m, dir);\n\n\tva_start(ap, ncodec);\n\n\twhile (ncodec--) {\n\t\tconst char *id = va_arg(ap, char *);\n\t\tconst char *cname = va_arg(ap, char *);\n\t\tint srate = va_arg(ap, int);\n\n\t\terr = sdp_format_add(NULL, m, false, id, cname, srate, 1,\n\t\t\t\t     NULL, NULL, NULL, false, NULL);\n\t\tif (err)\n\t\t\tbreak;\n\t}\n\n\tva_end(ap);\n\n\treturn err;\n}\n\n\nstatic int oa_offeranswer(struct oa *oa, const char *offer, const char *answer)\n{\n\tstruct mbuf *mbo = NULL, *mba = NULL;\n\tint err = 0;\n\n\t/* create and send offer, compare offer */\n\terr = sdp_encode(&mbo, oa->alice, true);\n\tif (err)\n\t\tgoto out;\n\n\tif (!sdp_cmp(mbo, offer)) {\n\t\tDEBUG_WARNING(\"offer failed:\\n%b\", mbo->buf, mbo->end);\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\t/* bob decodes offer */\n\terr = sdp_decode(oa->bob, mbo, true);\n\tif (err)\n\t\tgoto out;\n\n\t/* create and send answer, compare answer */\n\terr = sdp_encode(&mba, oa->bob, false);\n\tif (err)\n\t\tgoto out;\n\n\tif (!sdp_cmp(mba, answer)) {\n\t\tDEBUG_WARNING(\"answer failed:\\n%b\", mba->buf, mba->end);\n\t\terr = EBADMSG;\n\t\tTEST_ERR(err);\n\t}\n\n\terr = sdp_decode(oa->alice, mba, false);\n\n out:\n\toa_reset(oa);\n\tmem_deref(mbo);\n\tmem_deref(mba);\n\n\treturn err;\n}\n\n\n/* RFC 4317 - section 2.1 */\nstatic int rfc4317_section2_1(struct oa *oa)\n{\n\tint err = 0;\n\n\terr |= oa_addmedia(oa, 1, \"audio\", 49170, \"RTP/AVP\", SDP_SENDRECV, 3,\n\t\t\t   \"0\", \"PCMU\", 8000,\n\t\t\t   \"8\", \"PCMA\", 8000,\n\t\t\t   \"97\", \"iLBC\", 8000);\n\terr |= oa_addmedia(oa, 1, \"video\", 51372, \"RTP/AVP\", SDP_SENDRECV, 2,\n\t\t\t   \"31\", \"H261\", 90000,\n\t\t\t   \"32\", \"MPV\", 90000);\n\terr |= oa_addmedia(oa, 0, \"audio\", 49174, \"RTP/AVP\", SDP_SENDRECV, 1,\n\t\t\t   \"0\", \"PCMU\", 8000);\n\terr |= oa_addmedia(oa, 0, \"video\", 49170, \"RTP/AVP\", SDP_SENDRECV, 1,\n\t\t\t   \"32\", \"MPV\", 90000);\n\tif (err)\n\t\treturn err;\n\n\terr = oa_offeranswer(oa,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=alice 2890844526 2890844526 IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49170 RTP/AVP 0 8 97\\r\\n\"\n\t\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:8 PCMA/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:97 iLBC/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=video 51372 RTP/AVP 31 32\\r\\n\"\n\t\t\t     \"a=rtpmap:31 H261/90000\\r\\n\"\n\t\t\t     \"a=rtpmap:32 MPV/90000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     ,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=bob 2808844564 2808844564 IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49174 RTP/AVP 0\\r\\n\"\n\t\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=video 49170 RTP/AVP 32\\r\\n\"\n\t\t\t     \"a=rtpmap:32 MPV/90000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\");\n\treturn err;\n}\n\n\n/* RFC 4317 - section 2.2 */\nstatic int rfc4317_section2_2(struct oa *oa)\n{\n\tint err = 0;\n\n\terr |= oa_addmedia(oa, 1, \"audio\", 49170, \"RTP/AVP\", SDP_SENDRECV, 3,\n\t\t\t   \"0\", \"PCMU\", 8000,\n\t\t\t   \"8\", \"PCMA\", 8000,\n\t\t\t   \"97\", \"iLBC\", 8000);\n\terr |= oa_addmedia(oa, 1, \"video\", 51372, \"RTP/AVP\", SDP_SENDRECV, 2,\n\t\t\t   \"31\", \"H261\", 90000,\n\t\t\t   \"32\", \"MPV\", 90000);\n\terr |= oa_addmedia(oa, 0, \"audio\", 49172, \"RTP/AVP\", SDP_SENDRECV, 2,\n\t\t\t   \"0\", \"PCMU\", 8000,\n\t\t\t   \"8\", \"PCMA\", 8000);\n\tif (err)\n\t\treturn err;\n\n\treturn oa_offeranswer(oa,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=alice 2890844526 2890844526 IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49170 RTP/AVP 0 8 97\\r\\n\"\n\t\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:8 PCMA/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:97 iLBC/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=video 51372 RTP/AVP 31 32\\r\\n\"\n\t\t\t     \"a=rtpmap:31 H261/90000\\r\\n\"\n\t\t\t     \"a=rtpmap:32 MPV/90000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     ,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=bob 2808844564 2808844564 IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49172 RTP/AVP 0 8\\r\\n\"\n\t\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:8 PCMA/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=video 0 RTP/AVP 0\\r\\n\"\n\t\t\t     );\n}\n\n\n/* RFC 4317 - section 2.4 */\nstatic int rfc4317_section2_4(struct oa *oa)\n{\n\tint err = 0;\n\n\terr |= oa_addmedia(oa, 1, \"audio\", 49170, \"RTP/AVP\", SDP_SENDRECV, 2,\n\t\t\t   \"0\", \"PCMU\", 8000,\n\t\t\t   \"97\", \"iLBC\", 8000);\n\terr |= oa_addmedia(oa, 1, \"audio\", 49172, \"RTP/AVP\", SDP_SENDRECV, 1,\n\t\t\t   \"98\", \"telephone-event\", 8000);\n\terr |= oa_addmedia(oa, 0, \"audio\", 49172, \"RTP/AVP\", SDP_SENDRECV, 1,\n\t\t\t   \"97\", \"iLBC\", 8000);\n\terr |= oa_addmedia(oa, 0, \"audio\", 49174, \"RTP/AVP\", SDP_RECVONLY, 1,\n\t\t\t   \"98\", \"telephone-event\", 8000);\n\tif (err)\n\t\treturn err;\n\n\terr = oa_offeranswer(oa,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=alice 2890844526 2890844526 IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49170 RTP/AVP 0 97\\r\\n\"\n\t\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:97 iLBC/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=audio 49172 RTP/AVP 98\\r\\n\"\n\t\t\t     \"a=rtpmap:98 telephone-event/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     ,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=bob 2808844564 2808844564 IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49172 RTP/AVP 97\\r\\n\"\n\t\t\t     \"a=rtpmap:97 iLBC/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=audio 49174 RTP/AVP 98\\r\\n\"\n\t\t\t     \"a=rtpmap:98 telephone-event/8000\\r\\n\"\n\t\t\t     \"a=recvonly\\r\\n\");\n\treturn err;\n}\n\n\n/* RFC 4317 - section 5.1 */\nstatic int rfc4317_section5_1(struct oa *oa)\n{\n\tint err = 0;\n\n\terr |= oa_init(oa);\n\n\tif (err)\n\t\treturn err;\n\n\terr = oa_offeranswer(oa,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=alice 2890844526 2890844526 IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     ,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=bob 2808844564 2808844564 IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\");\n\treturn err;\n}\n\n\n/** Test SDP Offer/Answer examples in RFC 4317 */\nint test_sdp_oa(void)\n{\n\tstruct oa oa;\n\tint err = 0;\n\n\tmemset(&oa, 0, sizeof(oa));\n\n\terr |= rfc4317_section2_1(&oa);\n\terr |= rfc4317_section2_2(&oa);\n\terr |= rfc4317_section2_4(&oa);\n\terr |= rfc4317_section5_1(&oa);\n\n\toa_reset(&oa);\n\n\treturn err;\n}\n\n\n/** Test BFCP in SDP -- RFC 4583 */\nint test_sdp_bfcp(void)\n{\n\tstatic const char *msg_offer =\n\t\t\"v=0\\r\\n\"\n\t\t\"o=alice 2890844526 2890844526 IN IP4 1.2.3.4\\r\\n\"\n\t\t\"s=-\\r\\n\"\n\t\t\"c=IN IP4 1.2.3.4\\r\\n\"\n\t\t\"t=0 0\\r\\n\"\n\t\t\"m=application 50000 TCP/BFCP *\\r\\n\"\n\t\t\"a=sendrecv\\r\\n\"\n\t\t\"a=setup:passive\\r\\n\"\n\t\t\"a=connection:new\\r\\n\"\n\t\t\"a=floorctrl:s-only\\r\\n\"\n\t\t\"a=confid:4321\\r\\n\"\n\t\t\"a=userid:1234\\r\\n\"\n\t\t\"a=floorid:1 m-stream:10\\r\\n\"\n\t\t\"a=floorid:2 m-stream:11\\r\\n\"\n\t\t\"m=audio 50002 RTP/AVP 0\\r\\n\"\n\t\t\"a=sendrecv\\r\\n\"\n\t\t\"a=label:10\\r\\n\"\n\t\t\"m=video 50004 RTP/AVP 31\\r\\n\"\n\t\t\"a=sendrecv\\r\\n\"\n\t\t\"a=label:11\\r\\n\"\n\t\t;\n\tstruct sdp_session *alice = NULL, *bob = NULL;\n\tstruct sdp_media *bfcp, *audio, *video;\n\tstruct mbuf *mbo = NULL, *mba = NULL;\n\tstruct sa laddr;\n\tint err;\n\n\t/* create sessions */\n\t(void)sa_set_str(&laddr, \"1.2.3.4\", 0);\n\terr  = sdp_session_alloc(&alice, &laddr);\n\tif (err)\n\t\tgoto out;\n\n\terr = sdp_media_add(&bfcp, alice, \"application\", 50000, \"TCP/BFCP\");\n\tif (err)\n\t\tgoto out;\n\n\terr |= sdp_media_set_lattr(bfcp, true, \"setup\", \"passive\");\n\terr |= sdp_media_set_lattr(bfcp, true, \"connection\", \"new\");\n\terr |= sdp_media_set_lattr(bfcp, true, \"floorctrl\", \"s-only\");\n\terr |= sdp_media_set_lattr(bfcp, true, \"confid\", \"4321\");\n\terr |= sdp_media_set_lattr(bfcp, true, \"userid\", \"1234\");\n\terr |= sdp_media_set_lattr(bfcp, false, \"floorid\", \"1 m-stream:10\");\n\tsdp_media_del_lattr(bfcp, \"floorid\"); /* test attr delete */\n\terr |= sdp_media_set_lattr(bfcp, false, \"floorid\", \"1 m-stream:10\");\n\terr |= sdp_media_set_lattr(bfcp, false, \"floorid\", \"2 m-stream:11\");\n\tif (err)\n\t\tgoto out;\n\n\terr = sdp_media_add(&audio, alice, \"audio\", 50002, \"RTP/AVP\");\n\tif (err)\n\t\tgoto out;\n\n\terr = sdp_media_add(&video, alice, \"video\", 50004, \"RTP/AVP\");\n\tif (err)\n\t\tgoto out;\n\n\terr |= sdp_media_set_lattr(audio, true, \"label\", \"10\");\n\terr |= sdp_media_set_lattr(video, true, \"label\", \"11\");\n\tif (err)\n\t\tgoto out;\n\n\terr  = sdp_format_add(NULL, bfcp, false, \"*\", NULL, 0, 0,\n\t\t\t      NULL, NULL, NULL, false, NULL);\n\terr |= sdp_format_add(NULL, audio, false, \"0\", NULL, 0, 0,\n\t\t\t      NULL, NULL, NULL, false, NULL);\n\terr |= sdp_format_add(NULL, video, false, \"31\", NULL, 0, 0,\n\t\t\t      NULL, NULL, NULL, false, NULL);\n\tif (err)\n\t\tgoto out;\n\n\t/* create and send offer, compare offer */\n\terr = sdp_encode(&mbo, alice, true);\n\tif (err)\n\t\tgoto out;\n\n\tif (!sdp_cmp(mbo, msg_offer)) {\n\t\tDEBUG_WARNING(\"offer failed:\\n%b\", mbo->buf, mbo->end);\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n out:\n\tmem_deref(alice);\n\tmem_deref(bob);\n\tmem_deref(mbo);\n\tmem_deref(mba);\n\n\treturn err;\n}\n\n\nint test_sdp_extmap(void)\n{\n\tstatic const char *extmapv[3] = {\n\t\t\"extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\",\n\t\t\"extmap:2/sendrecv http://example.com/ext.htm#xmeta short\",\n\t\t\"extmap:4096/recvonly URI-gps-string\"\n\t};\n\tstruct sdp_extmap ext;\n\tint err = 0;\n\n\t/* extmap 1 */\n\terr = sdp_extmap_decode(&ext, extmapv[0]);\n\tTEST_EQUALS(0, err);\n\tTEST_STRCMP(\"urn:ietf:params:rtp-hdrext:ssrc-audio-level\",\n\t\t    strlen(\"urn:ietf:params:rtp-hdrext:ssrc-audio-level\"),\n\t\t    ext.name.p, ext.name.l);\n\tTEST_ASSERT(!pl_isset(&ext.attrs));\n\tTEST_EQUALS(SDP_SENDRECV, ext.dir);\n\tTEST_ASSERT(!ext.dir_set);\n\tTEST_EQUALS(1, ext.id);\n\n\t/* extmap 2 */\n\terr = sdp_extmap_decode(&ext, extmapv[1]);\n\tTEST_EQUALS(0, err);\n\tTEST_STRCMP(\"http://example.com/ext.htm#xmeta\",\n\t\t    strlen(\"http://example.com/ext.htm#xmeta\"),\n\t\t    ext.name.p, ext.name.l);\n\tTEST_STRCMP(\"short\", strlen(\"short\"),\n\t\t    ext.attrs.p, ext.attrs.l);\n\tTEST_EQUALS(SDP_SENDRECV, ext.dir);\n\tTEST_ASSERT(ext.dir_set);\n\tTEST_EQUALS(2, ext.id);\n\n\t/* extmap 3 */\n\terr = sdp_extmap_decode(&ext, extmapv[2]);\n\tTEST_EQUALS(0, err);\n\tTEST_STRCMP(\"URI-gps-string\",\n\t\t    strlen(\"URI-gps-string\"),\n\t\t    ext.name.p, ext.name.l);\n\tTEST_ASSERT(!pl_isset(&ext.attrs));\n\tTEST_EQUALS(SDP_RECVONLY, ext.dir);\n\tTEST_ASSERT(ext.dir_set);\n\tTEST_EQUALS(4096, ext.id);\n\n out:\n\treturn err;\n}\n\n\nstatic int disabled_local_medialine(struct oa *oa)\n{\n\tint err = 0;\n\n\terr |= oa_addmedia(oa, 1, \"audio\", 49170, \"RTP/AVP\", SDP_SENDRECV, 3,\n\t\t\t   \"0\", \"PCMU\", 8000,\n\t\t\t   \"8\", \"PCMA\", 8000,\n\t\t\t   \"97\", \"iLBC\", 8000);\n\terr |= oa_addmedia(oa, 1, \"video\", 51372, \"RTP/AVP\", SDP_INACTIVE, 2,\n\t\t\t   \"31\", \"H261\", 90000,\n\t\t\t   \"32\", \"MPV\", 90000);\n\terr |= oa_addmedia(oa, 0, \"audio\", 49174, \"RTP/AVP\", SDP_SENDRECV, 1,\n\t\t\t   \"0\", \"PCMU\", 8000);\n\terr |= oa_addmedia(oa, 0, \"video\", 49170, \"RTP/AVP\", SDP_SENDRECV, 1,\n\t\t\t   \"32\", \"MPV\", 90000);\n\tif (err)\n\t\treturn err;\n\n\terr = oa_offeranswer(oa,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=alice 2890844526 2890844526 IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49170 RTP/AVP 0 8 97\\r\\n\"\n\t\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:8 PCMA/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:97 iLBC/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=video 51372 RTP/AVP 31 32\\r\\n\"\n\t\t\t     \"a=rtpmap:31 H261/90000\\r\\n\"\n\t\t\t     \"a=rtpmap:32 MPV/90000\\r\\n\"\n\t\t\t     \"a=inactive\\r\\n\"\n\t\t\t     ,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=bob 2808844564 2808844564 IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49174 RTP/AVP 0\\r\\n\"\n\t\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=video 49170 RTP/AVP 32\\r\\n\"\n\t\t\t     \"a=rtpmap:32 MPV/90000\\r\\n\"\n\t\t\t     \"a=inactive\\r\\n\");\n\treturn err;\n}\n\n\nstatic int disabled_remote_medialine(struct oa *oa)\n{\n\tint err = 0;\n\n\terr |= oa_addmedia(oa, 1, \"audio\", 49170, \"RTP/AVP\", SDP_SENDRECV, 3,\n\t\t\t   \"0\", \"PCMU\", 8000,\n\t\t\t   \"8\", \"PCMA\", 8000,\n\t\t\t   \"97\", \"iLBC\", 8000);\n\terr |= oa_addmedia(oa, 1, \"video\", 51372, \"RTP/AVP\", SDP_SENDRECV, 2,\n\t\t\t   \"31\", \"H261\", 90000,\n\t\t\t   \"32\", \"MPV\", 90000);\n\terr |= oa_addmedia(oa, 0, \"audio\", 49174, \"RTP/AVP\", SDP_SENDRECV, 1,\n\t\t\t   \"0\", \"PCMU\", 8000);\n\terr |= oa_addmedia(oa, 0, \"video\", 49170, \"RTP/AVP\", SDP_INACTIVE, 1,\n\t\t\t   \"32\", \"MPV\", 90000);\n\tif (err)\n\t\treturn err;\n\n\terr = oa_offeranswer(oa,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=alice 2890844526 2890844526 IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49170 RTP/AVP 0 8 97\\r\\n\"\n\t\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:8 PCMA/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:97 iLBC/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=video 51372 RTP/AVP 31 32\\r\\n\"\n\t\t\t     \"a=rtpmap:31 H261/90000\\r\\n\"\n\t\t\t     \"a=rtpmap:32 MPV/90000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     ,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=bob 2808844564 2808844564 IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49174 RTP/AVP 0\\r\\n\"\n\t\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=video 49170 RTP/AVP 32\\r\\n\"\n\t\t\t     \"a=rtpmap:32 MPV/90000\\r\\n\"\n\t\t\t     \"a=inactive\\r\\n\");\n\treturn err;\n}\n\n\nstatic int reject_video_medialine(struct oa *oa)\n{\n\tint err = 0;\n\n\terr |= oa_addmedia(oa, 1, \"audio\", 49170, \"RTP/AVP\", SDP_SENDRECV, 3,\n\t\t\t   \"0\", \"PCMU\", 8000,\n\t\t\t   \"8\", \"PCMA\", 8000,\n\t\t\t   \"97\", \"iLBC\", 8000);\n\terr |= oa_addmedia(oa, 1, \"video\", 51372, \"RTP/AVP\", SDP_SENDRECV, 2,\n\t\t\t   \"31\", \"H261\", 90000,\n\t\t\t   \"32\", \"MPV\", 90000);\n\terr |= oa_addmedia(oa, 0, \"audio\", 49174, \"RTP/AVP\", SDP_SENDRECV, 1,\n\t\t\t   \"0\", \"PCMU\", 8000);\n\n\tif (err)\n\t\treturn err;\n\n\terr = oa_offeranswer(oa,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=alice 2890844526 2890844526 IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 1.2.3.4\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49170 RTP/AVP 0 8 97\\r\\n\"\n\t\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:8 PCMA/8000\\r\\n\"\n\t\t\t     \"a=rtpmap:97 iLBC/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=video 51372 RTP/AVP 31 32\\r\\n\"\n\t\t\t     \"a=rtpmap:31 H261/90000\\r\\n\"\n\t\t\t     \"a=rtpmap:32 MPV/90000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     ,\n\t\t\t     \"v=0\\r\\n\"\n\t\t\t     \"o=bob 2808844564 2808844564 IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"s=-\\r\\n\"\n\t\t\t     \"c=IN IP4 5.6.7.8\\r\\n\"\n\t\t\t     \"t=0 0\\r\\n\"\n\t\t\t     \"m=audio 49174 RTP/AVP 0\\r\\n\"\n\t\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t\t     \"a=sendrecv\\r\\n\"\n\t\t\t     \"m=video 0 RTP/AVP 0\\r\\n\");\n\treturn err;\n}\n\n\nint test_sdp_disabled_rejected(void)\n{\n\tstruct oa oa;\n\tint err = 0;\n\n\tmemset(&oa, 0, sizeof(oa));\n\n\terr |= disabled_local_medialine(&oa);\n\terr |= disabled_remote_medialine(&oa);\n\terr |= reject_video_medialine(&oa);\n\n\toa_reset(&oa);\n\n\treturn err;\n}\n\n\nstruct fixture {\n\tstruct sdp_session *sess;\n\tstruct sdp_media *audio;\n\tstruct sdp_media *video;\n};\n\nstruct attr {\n\tconst char *name;\n\tconst char *value;\n};\n\nstruct codec {\n\tconst char *id;\n\tconst char *name;\n\tuint32_t srate;\n\tuint8_t ch;\n\tbool audio;\n};\n\nstruct args {\n\tconst struct attr *attrv;\n\tsize_t attrc;\n\tsize_t ix;\n};\n\n\nstatic int fixture_init(struct fixture *fix,\n\t\t\tconst struct attr *session_attrv,\n\t\t\tsize_t session_attrc,\n\t\t\tconst struct attr *media_attrv,\n\t\t\tsize_t media_attrc,\n\t\t\tconst struct codec *codecv,\n\t\t\tsize_t codecc)\n{\n\tstruct sa laddr;\n\n\tint err = sa_set_str(&laddr, \"127.0.0.1\", 9);\n\tTEST_ERR(err);\n\n\terr = sdp_session_alloc(&fix->sess, &laddr);\n\tTEST_ERR(err);\n\n\tfor (size_t i=0; i<session_attrc; i++) {\n\t\tconst struct attr *attr = &session_attrv[i];\n\n\t\terr = sdp_session_set_lattr(fix->sess, false,\n\t\t\t\t\t    attr->name, attr->value);\n\t\tTEST_ERR(err);\n\t}\n\n\terr = sdp_media_add(&fix->audio, fix->sess, \"audio\",\n\t\t\t    9, \"UDP/TLS/RTP/SAVPF\");\n\tTEST_ERR(err);\n\n\tfor (size_t i=0; i<media_attrc; i++) {\n\t\tconst struct attr *attr = &media_attrv[i];\n\n\t\terr = sdp_media_set_lattr(fix->audio, false,\n\t\t\t\t\t  attr->name, attr->value);\n\t\tTEST_ERR(err);\n\t}\n\n\tfor (size_t i=0; i<codecc; i++) {\n\t\tconst struct codec *codec = &codecv[i];\n\n\t\tif (!codec->audio)\n\t\t\tcontinue;\n\n\t\terr = sdp_format_add(NULL, fix->audio, false, codec->id,\n\t\t\t\t     codec->name, codec->srate, codec->ch,\n\t\t\t\t     NULL, NULL, NULL, false, NULL);\n\t\tTEST_ERR(err);\n\t}\n\n\terr = sdp_media_add(&fix->video, fix->sess, \"video\",\n\t\t\t    10, \"RTP/AVP\");\n\tTEST_ERR(err);\n\n\tfor (size_t i=0; i<codecc; i++) {\n\t\tconst struct codec *codec = &codecv[i];\n\n\t\tif (codec->audio)\n\t\t\tcontinue;\n\n\t\terr = sdp_format_add(NULL, fix->video, false, codec->id,\n\t\t\t\t     codec->name, codec->srate, codec->ch,\n\t\t\t\t     NULL, NULL, NULL, false, NULL);\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic void fixture_close(struct fixture *fix)\n{\n\tmem_deref(fix->audio);\n\tmem_deref(fix->sess);\n}\n\n\nstatic bool sdp_attr_handler(const char *name, const char *value, void *arg)\n{\n\tstruct args *args = arg;\n\tint err = 0;\n\n\tif (args->ix >= args->attrc) {\n\t\tDEBUG_WARNING(\"sdp_attr_handler: attr count mismatch\\n\");\n\t\treturn true;\n\t}\n\n\tconst struct attr *attr = &args->attrv[args->ix];\n\n\tTEST_STRCMP_LEN(attr->name, name);\n\tTEST_STRCMP_LEN(attr->value, value);\n\n\t++args->ix;\n\n out:\n\treturn err != 0;\n}\n\n\nstatic int test_sdp_param(const char *sdp,\n\t\t\t  const struct attr *session_attrv,\n\t\t\t  size_t session_attrc,\n\t\t\t  const struct attr *media_attrv,\n\t\t\t  size_t media_attrc,\n\t\t\t  const struct codec *codecv,\n\t\t\t  size_t codecc)\n{\n\tstruct fixture fix = { 0 };\n\tstruct mbuf *offer = mbuf_alloc(str_len(sdp));\n\n\tif (!offer)\n\t\treturn ENOMEM;\n\n\tint err = mbuf_write_str(offer, sdp);\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(offer, 0);\n\n\terr = fixture_init(&fix,\n\t\t\t   session_attrv, session_attrc,\n\t\t\t   media_attrv, media_attrc,\n\t\t\t   codecv, codecc);\n\tTEST_ERR(err);\n\n\terr = sdp_decode(fix.sess, offer, true);\n\tTEST_ERR(err);\n\n\tfor (size_t i=0; i<session_attrc; i++) {\n\t\tconst struct attr *attr = &session_attrv[i];\n\n\t\tconst char *rattr = sdp_session_rattr(fix.sess, attr->name);\n\t\tTEST_STRCMP_LEN(attr->value, rattr);\n\t}\n\n\tstruct args args = {\n\t\t.attrv = media_attrv,\n\t\t.attrc = media_attrc\n\t};\n\n\tsdp_media_rattr_apply(fix.audio, NULL, sdp_attr_handler, &args);\n\tASSERT_EQ(media_attrc, args.ix);\n\n\tTEST_STRCMP_LEN(\"UDP/TLS/RTP/SAVPF\", sdp_media_proto(fix.audio));\n\n\tconst struct sdp_format *audio_fmt =\n\t\tsdp_media_rformat(fix.audio, NULL);\n\tTEST_STRCMP_LEN(\"opus\", audio_fmt->name);\n\n\tconst struct sdp_format *video_fmt =\n\t\tsdp_media_lformat(fix.video, 102);\n\tTEST_ASSERT(video_fmt);\n\tTEST_STRCMP_LEN(\"H264\", video_fmt->name);\n\n out:\n\tfixture_close(&fix);\n\tmem_deref(offer);\n\n\treturn err;\n}\n\n\nint test_sdp_interop(void)\n{\n\tstatic const char sdp_chrome[] =\n\n\t\"v=0\\r\\n\"\n\t\"o=- 6851975412855494469 2 IN IP4 127.0.0.1\\r\\n\"\n\t\"s=-\\r\\n\"\n\t\"c=IN IP4 127.0.0.1\\r\\n\"\n\t\"t=0 0\\r\\n\"\n\t\"a=group:BUNDLE 0 1\\r\\n\"\n\t\"a=extmap-allow-mixed\\r\\n\"\n\t\"a=msid-semantic: WMS 2a30d377-cd13-4454-974c-0144db0118a6\\r\\n\"\n\t\"m=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126\\r\\n\"\n\t\"c=IN IP4 0.0.0.0\\r\\n\"\n\t\"a=rtcp:9 IN IP4 0.0.0.0\\r\\n\"\n\t\"a=ice-ufrag:ie02\\r\\n\"\n\t\"a=ice-pwd:CWO5WLKWo2j5QmsY1396cvFe\\r\\n\"\n\t\"a=ice-options:trickle\\r\\n\"\n\t\"a=fingerprint:sha-256\"\n\t  \" A9:1F:F1:FD:FB:90:7D:4D:F7:DF:C4:6E:F8:6A:7B:E7\"\n\t  \":87:1B:07:4E:22:3C:80:99:83:E6:9A:34:BD:93:F5:CE\\r\\n\"\n\t\"a=setup:actpass\\r\\n\"\n\t\"a=mid:0\\r\\n\"\n\t\"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\\r\\n\"\n\t\"a=extmap:2\"\n\t  \" http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\\r\\n\"\n\t\"a=extmap:3 http://www.ietf.org/id/\"\n\t  \"draft-holmer-rmcat-transport-wide-cc-extensions-01\\r\\n\"\n\t\"a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\\r\\n\"\n\t\"a=sendrecv\\r\\n\"\n\t\"a=msid:2a30d377-cd13-4454-974c-0144db0118a6\"\n\t  \" c061a2b9-95bc-45fb-9e5d-6df08d8e1d0f\\r\\n\"\n\t\"a=rtcp-mux\\r\\n\"\n\t\"a=rtcp-rsize\\r\\n\"\n\t\"a=rtpmap:111 opus/48000/2\\r\\n\"\n\t\"a=rtcp-fb:111 transport-cc\\r\\n\"\n\t\"a=fmtp:111 minptime=10;useinbandfec=1\\r\\n\"\n\t\"a=rtpmap:63 red/48000/2\\r\\n\"\n\t\"a=fmtp:63 111/111\\r\\n\"\n\t\"a=rtpmap:9 G722/8000\\r\\n\"\n\t\"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\"a=rtpmap:8 PCMA/8000\\r\\n\"\n\t\"a=rtpmap:13 CN/8000\\r\\n\"\n\t\"a=rtpmap:110 telephone-event/48000\\r\\n\"\n\t\"a=rtpmap:126 telephone-event/8000\\r\\n\"\n\t\"a=ssrc:2161565476 cname:P6e47zI3iVPviKRL\\r\\n\"\n\t\"a=ssrc:2161565476 msid:2a30d377-cd13-4454-974c-0144db0118a6\"\n\t  \" c061a2b9-95bc-45fb-9e5d-6df08d8e1d0f\\r\\n\"\n\t\"m=video 29942 RTP/AVP 102 104 106 108 127 112 116\\r\\n\"\n\t\"b=AS:3072\\r\\n\"\n\t\"a=rtpmap:102 H264/90000\\r\\n\"\n\t\"a=fmtp:102 level-asymmetry-allowed=1;\"\n\t  \"packetization-mode=1;profile-level-id=42001f\\r\\n\"\n\t\"a=rtpmap:104 H264/90000\\r\\n\"\n\t\"a=fmtp:104 level-asymmetry-allowed=1;\"\n\t  \"packetization-mode=0;profile-level-id=42001f\\r\\n\"\n\t\"a=rtpmap:106 H264/90000\\r\\n\"\n\t\"a=fmtp:106 level-asymmetry-allowed=1;\"\n\t  \"packetization-mode=1;profile-level-id=42e01f\\r\\n\"\n\t\"a=rtpmap:108 H264/90000\\r\\n\"\n\t\"a=fmtp:108 level-asymmetry-allowed=1;\"\n\t  \"packetization-mode=0;profile-level-id=42e01f\\r\\n\"\n\t\"a=rtpmap:127 H264/90000\\r\\n\"\n\t\"a=fmtp:127 level-asymmetry-allowed=1;\"\n\t  \"packetization-mode=1;profile-level-id=4d001f\\r\\n\"\n\t\"a=rtpmap:112 H264/90000\\r\\n\"\n\t\"a=fmtp:112 level-asymmetry-allowed=1;\"\n\t  \"packetization-mode=1;profile-level-id=64001f\\r\\n\"\n\t\"a=rtpmap:116 H265/90000\\r\\n\"\n\t\"a=fmtp:116 level-id=93;profile-id=1;tier-flag=0;tx-mode=SRST\\r\\n\"\n\t\"a=rtcp-fb:102 ccm fir\\r\\n\"\n\t\"a=rtcp-fb:102 ccm tmmbr\\r\\n\"\n\t\"a=rtcp-fb:102 nack\\r\\n\"\n\t\"a=rtcp-fb:102 nack pli\\r\\n\"\n\t\"a=rtcp-fb:104 ccm fir\\r\\n\"\n\t\"a=rtcp-fb:104 ccm tmmbr\\r\\n\"\n\t\"a=rtcp-fb:104 nack\\r\\n\"\n\t\"a=rtcp-fb:104 nack pli\\r\\n\"\n\t\"a=rtcp-fb:106 ccm fir\\r\\n\"\n\t\"a=rtcp-fb:106 ccm tmmbr\\r\\n\"\n\t\"a=rtcp-fb:106 nack\\r\\n\"\n\t\"a=rtcp-fb:106 nack pli\\r\\n\"\n\t\"a=rtcp-fb:108 ccm fir\\r\\n\"\n\t\"a=rtcp-fb:108 ccm tmmbr\\r\\n\"\n\t\"a=rtcp-fb:108 nack\\r\\n\"\n\t\"a=rtcp-fb:108 nack pli\\r\\n\"\n\t\"a=rtcp-fb:127 ccm fir\\r\\n\"\n\t\"a=rtcp-fb:127 ccm tmmbr\\r\\n\"\n\t\"a=rtcp-fb:127 nack\\r\\n\"\n\t\"a=rtcp-fb:127 nack pli\\r\\n\"\n\t\"a=rtcp-fb:112 ccm fir\\r\\n\"\n\t\"a=rtcp-fb:112 ccm tmmbr\\r\\n\"\n\t\"a=rtcp-fb:112 nack\\r\\n\"\n\t\"a=rtcp-fb:112 nack pli\\r\\n\"\n\t\"a=rtcp-fb:116 ccm fir\\r\\n\"\n\t\"a=rtcp-fb:116 ccm tmmbr\\r\\n\"\n\t\"a=rtcp-fb:116 nack\\r\\n\"\n\t\"a=rtcp-fb:116 nack pli\\r\\n\"\n\t;\n\n\tstatic const struct attr session_attrv[] = {\n\n\t{\"group\",              \"BUNDLE 0 1\"},\n\t{\"extmap-allow-mixed\", \"\"},\n\t{\"msid-semantic\",      \" WMS 2a30d377-cd13-4454-974c-0144db0118a6\"},\n\t};\n\n\tstatic const struct attr audio_attrv[] = {\n\n\t{\"ice-ufrag\",    \"ie02\"},\n\t{\"ice-pwd\",      \"CWO5WLKWo2j5QmsY1396cvFe\"},\n\t{\"ice-options\",  \"trickle\"},\n\t{\"fingerprint\",  \"sha-256 A9:1F:F1:FD:FB:90:7D:4D:F7:DF:C4:6E:F8:6A\"\n\t                 \":7B:E7:87:1B:07:4E:22:3C:80\"\n\t                 \":99:83:E6:9A:34:BD:93:F5:CE\"},\n\t{\"setup\",        \"actpass\"},\n\t{\"mid\",          \"0\"},\n\t{\"extmap\",       \"1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\"},\n\t{\"extmap\",       \"2 http://www.webrtc.org/experiments/\"\n\t                 \"rtp-hdrext/abs-send-time\"},\n\t{\"extmap\",       \"3 http://www.ietf.org/id/\"\n\t                 \"draft-holmer-rmcat-transport-wide-cc-extensions-01\"},\n\t{\"extmap\",       \"4 urn:ietf:params:rtp-hdrext:sdes:mid\"},\n\t{\"msid\",         \"2a30d377-cd13-4454-974c-0144db0118a6\"\n\t                 \" c061a2b9-95bc-45fb-9e5d-6df08d8e1d0f\"},\n\t{\"rtcp-mux\",     \"\"},\n\t{\"rtcp-rsize\",   \"\"},\n\t{\"rtcp-fb\",      \"111 transport-cc\"},\n\t{\"ssrc\",         \"2161565476 cname:P6e47zI3iVPviKRL\"},\n\t{\"ssrc\",         \"2161565476 msid:2a30d377-cd13-4454-974c-0144db0118a6\"\n\t                 \" c061a2b9-95bc-45fb-9e5d-6df08d8e1d0f\"},\n\t};\n\n\tstatic const struct codec codecv[] = {\n\n\t\t{  NULL, \"opus\",            48000, 2, true},\n\t\t{  \"63\", \"red\",             48000, 2, true},\n\t\t{   \"9\", \"G722\",             8000, 1, true},\n\t\t{   \"0\", \"PCMU\",             8000, 1, true},\n\t\t{   \"8\", \"PCMA\",             8000, 1, true},\n\t\t{  \"13\", \"CN\",               8000, 1, true},\n\t\t{ \"110\", \"telephone-event\", 48000, 1, true},\n\t\t{ \"126\", \"telephone-event\",  8000, 1, true},\n\t\t{ \"150\", \"H264\",            90000, 1, false},\n\t};\n\n\tint err;\n\n\terr = test_sdp_param(sdp_chrome,\n\t\t\t     session_attrv, RE_ARRAY_SIZE(session_attrv),\n\t\t\t     audio_attrv, RE_ARRAY_SIZE(audio_attrv),\n\t\t\t     codecv, RE_ARRAY_SIZE(codecv));\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "test/sha.c",
    "content": "/**\n * @file sha.c SHA Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include <re_sha.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"testsha1\"\n#define DEBUG_LEVEL 4\n#include <re_dbg.h>\n\n\nstatic const char *test_data[] = {\n\t\"abc\",\n\t\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\",\n\n\t/* 64 bytes */\n\t\"9293haoijsdlasjd9ehr98wehrlsihdflskidjflaisjdlaisdjalsdkjasdlsda\",\n\n\t/* 96 bytes */\n\t\"9293haoijsdlasjd9ehr98wehrlsihdflskidjflaisjdlaisdjalsdkjasdlsda\"\n\t\"9293haoijsdlasjd9ehr98wehrlsihdf\",\n\n\t/* 128 bytes */\n\t\"9293haoijsdlasjd9ehr98wehrlsihdflskidjflaisjdlaisdjalsdkjasdlsda\"\n\t\"9293haoijsdlasjd82halsdlkajsdlkjasldkjasldjlskjd9ehr98wehrlsihdd\",\n\n\t/* 256 bytes */\n\t\"9293haoijsdlasjd9ehr98wehrlsihdflskidjflaisjdlaisdjalsdkjasdlsda\"\n\t\"9293haoijsdlasjd82halsdlkajsdlkjasldkjasldjlskjd9ehr98wehrlsihdd\"\n\t\"9293haoijsdlasjd9ehr98wehrlsihdflskidjflaisjdlaisdjalsdkjasdlsda\"\n\t\"9293haoijsdlasjd82halsdlkajsdlkjasldkjasldjlskjd9ehr98wehrlsihdd\",\n};\nstatic const char *test_results[] = {\n\t\"a9993e364706816aba3e25717850c26c9cd0d89d\",\n\t\"84983e441c3bd26ebaae4aa1f95129e5e54670f1\",\n\t\"105104a6ee22de58c0888d2f9cdd56d95c14d4e7\",\n\t\"9962f530d85f354304efcf35ceaa29a279a3208d\",\n\t\"17307171329ed5aeaccf4cd4f6d02223a69af9fb\",\n\t\"4f051b5c4fcd0916df00f9c9dbab8608cd3355a7\"};\n\n\nint test_sha1(void)\n{\n\tuint32_t k;\n\tuint8_t digest[20];\n\tchar output[80];\n\n\tfor (k = 0; k < RE_ARRAY_SIZE(test_data); k++) {\n\n\t\tsha1((uint8_t *)test_data[k], strlen(test_data[k]), digest);\n\n\t\t(void)re_snprintf(output, sizeof(output), \"%02w\", digest,\n\t\t\t\t  sizeof(digest));\n\t\tif (strcmp(output, test_results[k])) {\n\t\t\tDEBUG_WARNING(\"* hash of \\\"%s\\\" incorrect:\\n\",\n\t\t\t\t      test_data[k]);\n\t\t\tDEBUG_WARNING(\"\\t%s returned\\n\", output);\n\t\t\tDEBUG_WARNING(\"\\t%s is correct\\n\", test_results[k]);\n\t\t\treturn EINVAL;\n\t\t}\n\t}\n\n\t/* success */\n\treturn 0;\n}\n"
  },
  {
    "path": "test/sip.c",
    "content": "/**\n * @file sip.c SIP Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"siptest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int sip_addr_encode(const struct sip_addr *addr, struct mbuf *mb)\n{\n\tbool anglebr = addr->dname.p || addr->uri.params.p;\n\tint err;\n\n\tif (addr->dname.p) {\n\t\terr = mbuf_printf(mb, \"\\\"%r\\\" \", &addr->dname);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (anglebr) {\n\t\terr = mbuf_write_u8(mb, '<');\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\terr = mbuf_printf(mb, \"%H\", uri_encode, &addr->uri);\n\tif (err)\n\t\treturn err;\n\n\tif (anglebr) {\n\t\terr = mbuf_write_u8(mb, '>');\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\tif (addr->params.p) {\n\t\terr = mbuf_write_pl(mb, &addr->params);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn 0;\n}\n\n\nint test_sip_addr(void)\n{\n\tconst char *addrv[] = {\n\n\t\t/* With Display name */\n\t\t\"\\\"Agmund Bolt\\\" <sip:agmund@bolt.com:5060;lr;user=phone>\",\n\t\t\"\\\"Agmund Bolt\\\" <sip:agmund@bolt.com:5060;user=phone>\",\n\t\t\"\\\"Agmund Bolt\\\" <sip:agmund@bolt.com:5060>\",\n\t\t\"\\\"Agmund Bolt\\\" <sip:agmund@bolt.com>\",\n\t\t\"\\\"Agmund Bolt\\\" <sip:bolt.com:5060>\",\n\t\t\"\\\"Agmund Bolt\\\" <sip:bolt.com>\",\n\n\t\t\"<sip:user@host:5060;transport=udp>\",\n\t\t\"<sip:user@host:5060>\",\n\t\t\"<sip:host:5060>\",\n\t\t\"<sip:host>\",\n\t\t\"<sip:user@81.187.91.2:28481>\",\n\t\t\"<sip:user@81.187.91.2:28481>\",\n\t\t\"<sip:81.187.91.2:28481>\",\n\t\t\"<sips:81.187.91.2:28481>\",\n\n\t\t/* RFC 3261 */\n\t\t\"<sip:alice@atlanta.com>\",\n\t\t\"<sips:alice@atlanta.co\"\n\t\t  \"?subject=project%20x&priority=urgent>\",\n\t\t\"<sips:1212@gateway.com>\",\n\t\t\"<sip:alice@192.0.2.4>\",\n\t\t\"<sip:atlanta.com;method=REGISTER?to=alice%40atlanta.com>\",\n\t\t\"<sip:alice;day=tuesday@atlanta.com>\",\n\n\t\t/* With address parameters */\n\t\t\"\\\"Agmund Bolt\\\" <sip:agmund@bolt.com:5060;lr;user=phone>\"\n\t\t    \";tag=foo123\",\n\t\t\"\\\"Agmund Bolt\\\" <sip:agmund@bolt.com:5060;lr;user=phone>\"\n\t\t   \";tag=foo123;bar=9d7j3\",\n\n\t\t/* RFC 5118 - SIP Torture Test Messages for IPv6 */\n\t\t\"\\\"Caller\\\" <sip:caller@[2001:db8::1]>\",\n\t\t\"\\\"Caller\\\" <sip:caller@[2001:db8::1]:5080>\",\n\n\t\t/* gruu */\n\t\t\"\\\"hei\\\" <sip:alfred1@10.0.0.4:32829;keepalive=crlf>\"\n\t\t\";expires=3845\"\n\t\t\";gruu=\\\"sip:alfred1@devel.sip.su.se\"\n\t\t\";opaque=t49sgjnwu8;gruu\\\"\"\n\t\t\";+sip.instance=\"\n\t\t\"\\\"<urn:uuid:6B3639EB-77A3-4882-97F7-A357A1F1B4D0>\\\"\"\n\t\t\";reg-id=1\",\n\t};\n\tstruct sip_addr addr;\n\tstruct mbuf mb;\n\tint err = EINVAL;\n\tsize_t i;\n\n\tmbuf_init(&mb);\n\n\tfor (i=0; i<RE_ARRAY_SIZE(addrv); i++) {\n\t\tstruct pl pl, pl2;\n\n\t\tpl_set_str(&pl, addrv[i]);\n\n\t\t/* Decode */\n\t\terr = sip_addr_decode(&addr, &pl);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"sip_addr: test %u: sip_addr_decode()\"\n\t\t\t\t      \" failed (%s) (%m)\\n\",\n\t\t\t\t      i, addrv[i], err);\n\t\t\tgoto out;\n\t\t}\n\n\t\tDEBUG_INFO(\"addr: dname=(%r) uri=(%r@%r) params=(%r)\\n\",\n\t\t\t   &addr.dname, &addr.uri.user, &addr.uri.host,\n\t\t\t   &addr.params);\n\n\t\t/* Encode */\n\t\tmbuf_reset(&mb);\n\t\terr = sip_addr_encode(&addr, &mb);\n\t\tif (err) {\n\t\t\tDEBUG_INFO(\"sip_addr: sip_addr_encode failed (%m)\\n\",\n\t\t\t\t   err);\n\t\t\tgoto out;\n\t\t}\n\n\t\t/* Compare */\n\t\tpl2.p = (const char *)mb.buf;\n\t\tpl2.l = mb.end;\n\t\terr = pl_cmp(&pl2, &pl);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"%u: sip addr cmp: ref='%r' gen='%r'\\n\",\n\t\t\t\t      i, &pl, &pl2);\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\terr = 0;\n\n out:\n\tmbuf_reset(&mb);\n\treturn err;\n}\n\n\nint test_sip_via(void)\n{\n\tstruct {\n\t\tenum sip_transp tp;\n\t\tconst char *host;\n\t\tuint16_t port;\n\t\tconst char *branch;\n\t} testv[] = {\n\t\t{SIP_TRANSP_UDP, \"1.2.3.4\",         1234, \"z9ghkdkasd\"},\n\t\t{SIP_TRANSP_TCP, \"123.123.123.123\", 0,    \"b0ajsd01abcdef918\"},\n\t\t{SIP_TRANSP_TCP, \"myhost.com\",      0,    \"b0ajsd01ab2838475\"},\n\t\t{SIP_TRANSP_TCP, \"fe80::215:58ff:fe2d:90ab\", 5060,\n\t\t \"b0ajs01cde38475\"},\n\t\t{SIP_TRANSP_TLS, \"fe80::215:58ff:fe2d:90ab\", 0, \"47daasd5\"}\n\t};\n\tstruct sip_msg *msg = NULL;\n\tstruct mbuf *mb;\n\tint err = EINVAL;\n\tsize_t i;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tbool ipaddr;\n\t\tstruct sa addr;\n\n\t\tmbuf_rewind(mb);\n\n\t\t/* Encode message */\n\t\terr = mbuf_printf(mb,\n\t\t\t\t  \"BYE sip:foo SIP/2.0\\r\\n\"\n\t\t\t\t  \"Via : SIP / 2.0 / %s \",\n\t\t\t\t  sip_transp_name(testv[i].tp));\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tipaddr = !sa_set_str(&addr, testv[i].host, testv[i].port);\n\n\t\tif (ipaddr && AF_INET6 == sa_af(&addr))\n\t\t\terr = mbuf_printf(mb, \"[%s]\", testv[i].host);\n\t\telse\n\t\t\terr = mbuf_printf(mb, \"%s\", testv[i].host);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (testv[i].port)\n\t\t\terr = mbuf_printf(mb, \":%u\", testv[i].port);\n\t\terr |= mbuf_printf(mb, \";branch=%s\\r\\n\\r\\n\",\n\t\t\t\t   testv[i].branch);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tmbuf_set_pos(mb, 0);\n\n\t\t/* Decode message */\n\n\t\terr = sip_msg_decode(&msg, mb);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\n\t\t/* Compare */\n\n\t\tif (msg->via.tp != testv[i].tp) {\n\t\t\tDEBUG_WARNING(\"%u: via transp: '%s' != '%s'\\n\",\n\t\t\t\t      i, sip_transp_name(msg->via.tp),\n\t\t\t\t      sip_transp_name(testv[i].tp));\n\t\t\tgoto out;\n\t\t}\n\n\t\t/* Numeric IP address */\n\t\tif (ipaddr) {\n\t\t\tif (!sa_cmp(&msg->via.addr, &addr, SA_ALL)) {\n\t\t\t\tDEBUG_WARNING(\"%u: via addr: addr=%J\\n\",\n\t\t\t\t\t      i, &msg->via.addr);\n\t\t\t\terr = EINVAL;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\terr = pl_strcmp(&msg->via.sentby, testv[i].host);\n\t\t\tif (err) {\n\t\t\t\tDEBUG_WARNING(\"%u: via uri: sentby='%r'\\n\",\n\t\t\t\t\t      i, &msg->via.sentby);\n\t\t\t\tgoto out;\n\t\t\t}\n\n\t\t\tif (sa_port(&msg->via.addr) != testv[i].port) {\n\t\t\t\tDEBUG_WARNING(\"%u: via: port mismatch (%u)\\n\",\n\t\t\t\t\t      i, sa_port(&msg->via.addr));\n\t\t\t\terr = EINVAL;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\n\t\terr = pl_strcmp(&msg->via.branch, testv[i].branch);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"%u: via branch: '%r' != '%s'\\n\",\n\t\t\t\t      i, &msg->via.branch, testv[i].branch);\n\t\t\tgoto out;\n\t\t}\n\n\t\tmsg = mem_deref(msg);\n\t}\n\n\terr = 0;\n out:\n\tmem_deref(mb);\n\tmem_deref(msg);\n\treturn err;\n}\n\n\nstruct apply {\n\tuint32_t n;\n\tint err;\n};\n\n\nstatic bool apply_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,\n\t\t\t  void *arg)\n{\n\tconst char *ref = \"SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\";\n\tstruct apply *apply = arg;\n\n\t(void)msg;\n\t(void)arg;\n\n\tif (hdr->id != SIP_HDR_VIA) {\n\t\tapply->err = EINVAL;\n\t\treturn true;\n\t}\n\n\tapply->err = pl_strcmp(&hdr->val, ref);\n\tif (apply->err) {\n\t\treturn true;\n\t}\n\n\t++apply->n;\n\n\treturn false;\n}\n\n\nint test_sip_apply(void)\n{\n\tstruct {\n\t\tuint32_t n;\n\t\tconst char *msg;\n\t} testv[] = {\n\t\t{0,\n\t\t \"Tull: tull\\r\\n\"},\n\t\t{1,\n\t\t \"Via: SIP/2.0/UDP 123.45.67.89:12345\"\n\t\t \";branch=z9hG4bK123\\r\\n\"},\n\t\t{1,\n\t\t \"Via:   SIP/2.0/UDP 123.45.67.89:12345\"\n\t\t \";branch=z9hG4bK123\\r\\n\"},\n\t\t{2,\n\t\t \"Via: SIP/2.0/UDP 123.45.67.89:12345\"\n\t\t \";branch=z9hG4bK123\\r\\n\"\n\t\t \"Via: SIP/2.0/UDP 123.45.67.89:12345\"\n\t\t \";branch=z9hG4bK123\\r\\n\"\n\t\t},\n\t\t{2,\n\t\t \"Via: SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123,\"\n\t\t \" SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\\r\\n\"\n\t\t},\n\t\t{2,\n\t\t \"v: SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123,\"\n\t\t \" SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\\r\\n\"\n\t\t},\n\t\t{2,\n\t\t \"v: SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\"\n\t\t \",SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\\r\\n\"\n\t\t},\n\t\t{3,\n\t\t \"Via: SIP/2.0/UDP 123.45.67.89:12345\"\n\t\t \";branch=z9hG4bK123\\r\\n\"\n\t\t \"v: SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\\r\\n\"\n\t\t \"Via: SIP/2.0/UDP 123.45.67.89:12345\"\n\t\t \";branch=z9hG4bK123\\r\\n\"\n\t\t},\n\t\t{3,\n\t\t \"Via: SIP/2.0/UDP 123.45.67.89:12345\"\n\t\t \";branch=z9hG4bK123\\r\\n\"\n\t\t \"Tull: tull\\r\\n\"\n\t\t \"v: SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\\r\\n\"\n\t\t \"Via: SIP/2.0/UDP 123.45.67.89:12345\"\n\t\t \";branch=z9hG4bK123\\r\\n\"\n\t\t},\n\t};\n\tstruct sip_msg *msg = NULL;\n\tstruct mbuf *mb;\n\tint err = 0;\n\tsize_t i;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tstruct apply apply;\n\n\t\tmemset(&apply, 0, sizeof(apply));\n\n\t\tmbuf_rewind(mb);\n\t\terr = mbuf_printf(mb, \"OPTIONS sip:213.175.63.232:56102\"\n\t\t\t\t  \";transport=TCP;dstip=212.13.202.25\"\n\t\t\t\t  \";dstport=5060 SIP/2.0\\r\\n%s\\r\\n\",\n\t\t\t\t  testv[i].msg);\n\t\tif (err)\n\t\t\tgoto out;\n\t\tmbuf_set_pos(mb, 0);\n\n\t\terr = sip_msg_decode(&msg, mb);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\n\t\tapply.n = 0;\n\t\tif (sip_msg_hdr_apply(msg, true, SIP_HDR_VIA,\n\t\t\t\t      apply_handler, &apply)) {\n\t\t\terr = apply.err;\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (apply.n != testv[i].n) {\n\t\t\tDEBUG_WARNING(\"%u: apply: expected\"\n\t\t\t\t      \" %u headers, got %u\\n\",\n\t\t\t\t      i, testv[i].n, apply.n);\n\t\t\terr = EINVAL;\n\t\t\tgoto out;\n\t\t}\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"%u: apply: header cmp failed (%m)\\n\",\n\t\t\t\t      i, err);\n\t\t\tgoto out;\n\t\t}\n\n\t\tmsg = mem_deref(msg);\n\t}\n\n\terr = 0;\n out:\n\tmem_deref(mb);\n\tmem_deref(msg);\n\treturn err;\n}\n\n\nint test_sip_param(void)\n{\n\tstatic const struct {\n\t\tconst char *str;\n\t\tconst char *exist;  /* param exist */\n\t\tconst char *nexist; /* param not exist */\n\t\tconst char *val;    /* expected value, if any */\n\t} testv[] = {\n\t\t{\n\t\t\t\" ; rport \",\n\t\t\t\"rport\",\n\t\t\t\"port\",\n\t\t\tNULL,\n\t\t},\n\t\t{\n\t\t\t\" ; branch = 123 \",\n\t\t\t\"branch\",\n\t\t\t\"bra\",\n\t\t\t\"123\",\n\t\t},\n\t\t{\n\t\t\t\" ; expires = 3600 ; reg-id = 1 \",\n\t\t\t\"expires\",\n\t\t\t\"xpires\",\n\t\t\t\"3600\",\n\t\t},\n\t\t{\n\t\t\t\" ; expires = 3600 ; reg-id = 1 \",\n\t\t\t\"reg-id\",\n\t\t\t\"regid\",\n\t\t\t\"1\",\n\t\t},\n\t\t{\n\t\t\t\" ; gruu = \\\"sip:alfred1@devel.sip.su.se\"\n\t\t\t    \";opaque=t49sgjnwu8;gruu\\\"; foo\",\n\t\t\t\"gruu\",\n\t\t\t\"ruu\",\n\t\t\t\"sip:alfred1@devel.sip.su.se\"\n\t\t\t   \";opaque=t49sgjnwu8;gruu\",\n\t\t},\n\t};\n\n\tint err = EINVAL;\n\tsize_t i;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\t\tstruct pl pl, foo;\n\n\t\tpl_set_str(&pl, testv[i].str);\n\n\t\terr = msg_param_exists(&pl, testv[i].exist, &foo);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"%u: expected param not exist (%s)\\n\", i,\n\t\t\t\t      testv[i].exist);\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (0 == msg_param_exists(&pl, testv[i].nexist, &foo)) {\n\t\t\tDEBUG_WARNING(\"%u: unexpected param (%s)\\n\", i,\n\t\t\t\t      testv[i].nexist);\n\t\t\terr = EINVAL;\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (testv[i].val) {\n\t\t\tstruct pl val;\n\n\t\t\terr = msg_param_decode(&pl, testv[i].exist, &val);\n\t\t\tif (err) {\n\t\t\t\tDEBUG_WARNING(\"%u: could not get param (%r)\\n\",\n\t\t\t\t\t      i, &testv[i].exist);\n\t\t\t\tgoto out;\n\t\t\t}\n\n\t\t\terr = pl_strcmp(&val, testv[i].val);\n\t\t\tif (err) {\n\t\t\t\tDEBUG_WARNING(\"%u: cmp: got='%r' exp='%s'\\n\",\n\t\t\t\t\t      i, &val, testv[i].val);\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\t}\n\n\terr = 0;\n\n out:\n\treturn err;\n}\n\n\nint test_sip_parse(void)\n{\n\tconst char str_raw[] =\n\t\t\"INVITE sip:bob@biloxi.com SIP/2.0\\r\\n\"\n\t\t\"Via : SIP/2.0/UDP 127.0.0.1:1234;branch=z9hG4bK.2ed0447\\r\\n\"\n\t\t\"Max-Forwards: 70\\r\\n\"\n\t\t\"Record-Route: <sip:p2.domain.com;lr>\\r\\n\"\n\t\t\"t: Bob <sip:bob@biloxi.com>\\r\\n\"\n\t\t\"f: Alice <sip:alice@atlanta.com>\\r\\n\"\n\t\t\" ;tag=1928301774\\r\\n\"\n\t\t\"Call-ID : a84b4c76e66710@pc33.atlanta.com\\r\\n\"\n\t\t\"CSeq  : 314159 INVITE\\r\\n\"\n\t\t\"Contact:  <sip:alice@pc33.atlanta.com>\\r\\n\"\n\t\t\"Content-Type: application/sdp\\r\\n\"\n\t\t\"Content-Length: 142\\r\\n\"\n\t\t\"\\r\\n\";\n\tconst char hdr_maxf[]   = \"70\";\n\tconst char hdr_from[]   = \"sip:alice@atlanta.com\";\n\tconst char hdr_to[]     = \"sip:bob@biloxi.com\";\n\tconst char hdr_callid[] = \"a84b4c76e66710@pc33.atlanta.com\";\n\tstruct mbuf *mb;\n\tstruct sip_msg *msg = NULL;\n\tint err = EINVAL;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = mbuf_write_str(mb, str_raw);\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(mb, 0);\n\terr = sip_msg_decode(&msg, mb);\n\tif (err) {\n\t\tgoto out;\n\t}\n\n\t/* Max-Forwards */\n\terr = pl_strcmp(&msg->maxfwd, hdr_maxf);\n\tif (err)\n\t\tgoto out;\n\n\t/* From */\n\terr = pl_strcmp(&msg->from.auri, hdr_from);\n\tif (err) {\n\t\tDEBUG_WARNING(\"from header mismatch (%r)\\n\", &msg->from.auri);\n\t\tgoto out;\n\t}\n\n\t/* To */\n\terr = pl_strcmp(&msg->to.auri, hdr_to);\n\tif (err) {\n\t\tDEBUG_WARNING(\"to header mismatch\\n\");\n\t\tgoto out;\n\t}\n\n\t/* Call-ID */\n\terr = pl_strcmp(&msg->callid, hdr_callid);\n\tif (err) {\n\t\tDEBUG_WARNING(\"callid header mismatch\\n\");\n\t\tgoto out;\n\t}\n\n\t/* CSeq */\n\tif (314159 != msg->cseq.num) {\n\t\tDEBUG_WARNING(\"cseq number mismatch\\n\");\n\t\tgoto out;\n\t}\n\terr = pl_strcmp(&msg->cseq.met, \"INVITE\");\n\tif (err) {\n\t\tDEBUG_WARNING(\"cseq method mismatch\\n\");\n\t\tgoto out;\n\t}\n\n\t/* Content-Type */\n\tif (!msg_ctype_cmp(&msg->ctyp, \"application\", \"sdp\")) {\n\t\tDEBUG_WARNING(\"content type mismatch (%r/%r)\\n\",\n\t\t\t      &msg->ctyp.type, &msg->ctyp.subtype);\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\t/* Content-Length */\n\tif (142 != pl_u32(&msg->clen)) {\n\t\tDEBUG_WARNING(\"content length mismatch\\n\");\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\terr = 0;\n\n out:\n\tmem_deref(mb);\n\tmem_deref(msg);\n\treturn err;\n}\n\n\nstatic bool count_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,\n\t\t\t  void *arg)\n{\n\t(void)hdr;\n\t(void)msg;\n\t++(*(uint32_t *)arg);\n\treturn false;\n}\n\n\nstatic uint32_t xhdr_count(const struct sip_msg *msg, const char *name)\n{\n\tuint32_t n = 0;\n\tsip_msg_xhdr_apply(msg, true, name, count_handler, &n);\n\treturn n;\n}\n\n\nint test_sip_hdr(void)\n{\n\tconst char str[] =\n\t\t\"REGISTER sip:telio.no SIP/2.0\\r\\n\"\n\t\t\"Via: SIP/2.0/UDP 85.119.136.184:5080\"\n\t\t\" ;branch=z9hG4bKe282.0c5b6835.0;i=2b505\\r\\n\"\n\t\t\"Via: SIP/2.0/TCP 172.17.18.219:5060;received=85.0.35.235\"\n\t\t\" ;branch=z9hG4bK6ec163d6cebbbe491e1940b91.1;rport=49505\\r\\n\"\n\t\t\"Call-ID: 2e60298e76751681@172.17.18.219\\r\\n\"\n\t\t\"CSeq: 67139 REGISTER\\r\\n\"\n\t\t\"Contact: <sip:21696001@85.0.35.235:49505;transport=tcp>\\r\\n\"\n\t\t\"From: <sip:21696001@telio.no>;tag=1ea582725e044bf6\\r\\n\"\n\t\t\"To: <sip:21696001@telio.no>\\r\\n\"\n\t\t\"Max-Forwards: 16\\r\\n\"\n\t\t\"Allow: INVITE,ACK,CANCEL,BYE,UPDATE,INFO,OPTIONS\\r\\n\"\n\t\t\"User-Agent: TANDBERG/67 (F7.2 PAL)\\r\\n\"\n\t\t\"Expires: 3600\\r\\n\"\n\t\t\"Supported: replaces,100rel,timer\\r\\n\"\n\t\t\"Content-Length: 0\\r\\n\"\n\t\t\"\\r\\n\";\n\tstruct mbuf *mb;\n\tstruct sip_msg *msg = NULL;\n\tint err = EINVAL;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = mbuf_write_str(mb, str);\n\tif (err)\n\t\tgoto out;\n\n\tmbuf_set_pos(mb, 0);\n\terr = sip_msg_decode(&msg, mb);\n\tif (err)\n\t\tgoto out;\n\n\tif (xhdr_count(msg, \"Call-ID\") != 1) {\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\tif (xhdr_count(msg, \"Supported\") != 3) {\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\tif (xhdr_count(msg, \"Allow\") != 7) {\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\tif (xhdr_count(msg, \"NonExisting\") != 0) {\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n\n out:\n\tmem_deref(msg);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/** SIP Authenticated Request */\nstruct sip_req {\n\tstruct sip_request *req;\n\tstruct sip_auth *auth;\n\tstruct sip_dialog *dlg;\n\tstruct sip *sip;\n};\n\n\nstatic int do_sip_drequestf(struct sa *laddr)\n{\n\tstruct sip_req *sr;\n\tint err;\n\tchar uri[64];\n\tchar touri[64];\n\n\tsr = mem_zalloc(sizeof(*sr), NULL);\n\tif (!sr)\n\t\treturn ENOMEM;\n\n\tre_snprintf(uri, sizeof(uri), \"sip:%J;transport=UDP\", laddr);\n\tre_snprintf(touri, sizeof(touri), \"sip:test@%J\", laddr);\n\n\terr = sip_dialog_alloc(&sr->dlg, uri,\n\t\t\t       touri, NULL,\n\t\t\t       touri, NULL, 0);\n\tTEST_ERR(err);\n\n\terr = sip_auth_alloc(&sr->auth, NULL, NULL, false);\n\tTEST_ERR(err);\n\n\terr = sip_alloc(&sr->sip, NULL, 32, 32, 32, \"retest\", NULL, NULL);\n\tTEST_ERR(err);\n\n\terr  = sip_transp_add(sr->sip, SIP_TRANSP_UDP, laddr);\n\tTEST_ERR(err);\n\n\terr = sip_drequestf(&sr->req, sr->sip, true, \"REGISTER\", sr->dlg, 0,\n\t\t\t    sr->auth, NULL, NULL, NULL, \"\");\n\tTEST_ERR(err);\n\nout:\n\tmem_deref(sr->dlg);\n\tmem_deref(sr->auth);\n\tmem_deref(sr->sip);\n\tmem_deref(sr);\n\n\treturn err;\n}\n\n\nint test_sip_drequestf(void)\n{\n\tint err;\n\tstruct sa laddr;\n\n\terr = sa_set_str(&laddr, \"127.0.0.1\", 0);\n\tTEST_ERR(err);\n\n\terr = do_sip_drequestf(&laddr);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\nint test_sip_drequestf_network(void)\n{\n\tstruct sa laddr;\n\tint err = 0;\n\n\tsa_init(&laddr, AF_INET6);\n\n\tif (0 == net_if_getlinklocal(NULL, AF_INET6, &laddr)) {\n\n\t\terr = do_sip_drequestf(&laddr);\n\t\tTEST_ERR(err);\n\t}\n\nout:\n\treturn err;\n}\n\n\n#ifdef USE_TLS\nstruct sip_transp_tls {\n\tstruct sip *sip;\n\tstruct tls *tls;\n\tstruct uri uri;\n\tconst char *ccert_cn;\n};\n\n\nint test_sip_transp_add_client_cert(void)\n{\n\tstruct sip_transp_tls *stt;\n\tstruct sa laddr;\n\tint err;\n\tchar clientcert[256];\n\tchar cafile[256];\n\n\tconst char *user    = \"abcd\";\n\tconst char *scheme  = \"sip\";\n\tconst char *host    = \"localhost\";\n\tconst uint16_t port = 5061;\n\n\tmemset(clientcert, 0, sizeof(clientcert));\n\t(void)re_snprintf(clientcert, sizeof(clientcert), \"%s/client.pem\",\n\t\ttest_datapath());\n\n\tstt = mem_zalloc(sizeof(*stt), NULL);\n\tif (!stt)\n\t\treturn ENOMEM;\n\n\tpl_set_str(&stt->uri.user, user);\n\tpl_set_str(&stt->uri.scheme, scheme);\n\tpl_set_str(&stt->uri.host, host);\n\tstt->uri.port = port;\n\n\n\terr = sa_set_str(&laddr, \"127.0.0.1\", 0);\n\tTEST_ERR(err);\n\n\terr = tls_alloc(&stt->tls, TLS_METHOD_SSLV23, NULL, NULL);\n\tTEST_ERR(err);\n\n\t(void)re_snprintf(cafile, sizeof(cafile), \"%s/server-ecdsa.pem\",\n\t\ttest_datapath());\n\n\terr = tls_add_ca(stt->tls, cafile);\n\tTEST_ERR(err);\n\n\terr = sip_alloc(&stt->sip, NULL, 32, 32, 32, \"retest\", NULL, NULL);\n\tTEST_ERR(err);\n\n\terr = sip_transp_add(stt->sip, SIP_TRANSP_TLS, &laddr, stt->tls);\n\tTEST_ERR(err);\n\n\t/* actual test cases */\n\terr = sip_transp_add_ccert(NULL, &stt->uri, clientcert);\n\tif (err == EINVAL) {\n\t\terr = 0;\n\t\tgoto out;\n\t}\n\tTEST_ERR(err);\n\n\terr = sip_transp_add_ccert(stt->sip, NULL, clientcert);\n\tif (err == EINVAL) {\n\t\terr = 0;\n\t\tgoto out;\n\t}\n\tTEST_ERR(err);\n\n\terr = sip_transp_add_ccert(stt->sip, &stt->uri, NULL);\n\tif (err == EINVAL) {\n\t\terr = 0;\n\t\tgoto out;\n\t}\n\tTEST_ERR(err);\n\n\terr = sip_transp_add_ccert(stt->sip, &stt->uri, clientcert);\n\tTEST_EQUALS(0, err);\n\n out:\n\tmem_deref(stt->sip);\n\tmem_deref(stt->tls);\n\tmem_deref(stt);\n\n\treturn err;\n}\n#endif\n\n\nstruct sip_dns {\n\tint req1_err;\n\tint req2_err;\n\tint req3_err;\n\tbool req1_done;\n\tbool req2_done;\n\tbool req3_done;\n};\n\n\nstatic void req1_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct sip_dns *sipdns = arg;\n\t(void)msg;\n\n\tDEBUG_INFO(\"req1_handler: err=%m\\n\", err);\n\tsipdns->req1_err = err;\n\tsipdns->req1_done = true;\n\tre_cancel();\n}\n\n\nstatic void req2_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct sip_dns *sipdns = arg;\n\t(void)msg;\n\n\tDEBUG_INFO(\"req2_handler: err=%m\\n\", err);\n\tsipdns->req2_err = err;\n\tsipdns->req2_done = true;\n\tif (sipdns->req3_done)\n\t\tre_cancel();\n}\n\n\nstatic void req3_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct sip_dns *sipdns = arg;\n\t(void)msg;\n\n\tDEBUG_INFO(\"req3_handler: err=%m\\n\", err);\n\tsipdns->req3_err = err;\n\tsipdns->req3_done = true;\n\tif (sipdns->req2_done)\n\t\tre_cancel();\n}\n\n\n/* Send a SIP request to example.com with mock DNS server and mock SIP server.\n * After response has been received, send two SIP reqeusts to example.com\n * simultanesously. Both requests must work. Tests DNS and DNS caching.\n */\nint test_sip_dns(void)\n{\n\tchar from_uri[256];\n\tchar to_uri[256];\n\tstruct sip_server *sipsrv = NULL;\n\tstruct dns_server *dnssrv = NULL;\n\tstruct dnsc *dnsc = NULL;\n\tstruct sip *sip = NULL;\n\tstruct sip_auth *auth = NULL;\n\tstruct sip_dialog *dlg = NULL;\n\tstruct sip_request *req1 = NULL;\n\tstruct sip_request *req2 = NULL;\n\tstruct sip_request *req3 = NULL;\n\tstruct sa laddr, sipsrv_addr;\n\tint err = 0;\n\tstruct sip_dns sipdns;\n\n\tmemset(&sipdns, 0, sizeof(sipdns));\n\n\t/* Set up mock SIP server */\n\terr = sip_server_alloc(&sipsrv);\n\tTEST_ERR(err);\n\n\t/* Get SIP server address */\n\terr = sip_transp_laddr(sipsrv->sip, &sipsrv_addr, SIP_TRANSP_UDP,\n\t\t\t       NULL);\n\tTEST_ERR(err);\n\n\tDEBUG_INFO(\"SIP server listening on %J\\n\", &sipsrv_addr);\n\n\t/* Set up mock DNS server */\n\terr = dns_server_alloc(&dnssrv, \"127.0.0.1\");\n\tTEST_ERR(err);\n\n\t/* Add A record pointing to SIP server  */\n\terr = dns_server_add_a(dnssrv, \"example.com\",\n\t                       sa_in(&sipsrv_addr), 1);\n\tTEST_ERR(err);\n\n\tDEBUG_INFO(\"DNS resolves example.com to %J\\n\", &sipsrv_addr);\n\n\t/* Create DNS client */\n\terr = dnsc_alloc(&dnsc, NULL, &dnssrv->addr, 1);\n\tTEST_ERR(err);\n\n\t/* Set up SIP stack with both IPv4 and IPv6 support */\n\terr = sip_alloc(&sip, dnsc, 32, 32, 32, \"retest\", NULL, NULL);\n\tTEST_ERR(err);\n\n\t/* Create SIP auth object */\n\terr = sip_auth_alloc(&auth, NULL, NULL, false);\n\tTEST_ERR(err);\n\n\t/* Add IPv4 transport */\n\terr = sa_set_str(&laddr, \"127.0.0.1\", 0);\n\tTEST_ERR(err);\n\terr = sip_transp_add(sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\t/* Add IPv6 transport */\n\terr = sa_set_str(&laddr, \"::1\", 0);\n\tTEST_ERR(err);\n\terr = sip_transp_add(sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\t/* Build From and To URIs */\n\tre_snprintf(from_uri, sizeof(from_uri), \"sip:test@127.0.0.1\");\n\tre_snprintf(to_uri, sizeof(to_uri), \"sip:user@example.com:%u\",\n\t            sa_port(&sipsrv_addr));\n\n\terr = sip_dialog_alloc(&dlg, to_uri, to_uri, NULL, from_uri, NULL, 0);\n\tTEST_ERR(err);\n\n\terr = sip_drequestf(&req1, sip, true, \"OPTIONS\", dlg, 0, auth, NULL,\n\t\t\t    req1_handler, &sipdns,\n\t                    \"Content-Length: 0\\r\\n\\r\\n\");\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(500);\n\tTEST_ERR(err);\n\n\tASSERT_TRUE(sipdns.req1_done);\n\n\terr = sip_drequestf(&req2, sip, true, \"OPTIONS\", dlg, 0, auth, NULL,\n\t\t\t    req2_handler, &sipdns,\n\t                    \"Content-Length: 0\\r\\n\\r\\n\");\n\tTEST_ERR(err);\n\n\t/* Send third request immediately */\n\terr = sip_drequestf(&req3, sip, true, \"OPTIONS\", dlg, 0, auth, NULL,\n\t                    req3_handler, &sipdns,\n\t                    \"Content-Length: 0\\r\\n\\r\\n\");\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(500);\n\tTEST_ERR(err);\n\n\t/* Verify handlers were called */\n\tASSERT_TRUE(sipdns.req2_done);\n\tASSERT_TRUE(sipdns.req3_done);\n\n\t/* Verify requests reached the transport layer */\n\tASSERT_EQ(0, sipdns.req2_err);\n\tASSERT_EQ(0, sipdns.req3_err);\n\n\t/* Verify all three OPTIONS were received */\n\tASSERT_EQ(3, sipsrv->n_options_req);\n\nout:\n\tmem_deref(req3);\n\tmem_deref(req2);\n\tmem_deref(req1);\n\tmem_deref(dlg);\n\tmem_deref(auth);\n\tif (sip) {\n\t\tsip_close(sip, false);\n\t\tmem_deref(sip);\n\t}\n\tmem_deref(dnsc);\n\tmem_deref(dnssrv);\n\tmem_deref(sipsrv);\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/sipauth.c",
    "content": "/**\n * @file sipauth.c SIP Auth testcode\n */\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_sipauth\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic const char *testv[] = {\n\t/* without algorithm - default MD5 */\n\t\"SIP/2.0 401 Unauthorized\\r\\n\"\n\t\"Via: SIP/2.0/TLS \"\n\t\"10.0.0.1:37589;branch=z9hG4bK5625ce6f310a0fc8;rport=13718;\"\n\t\"received=10.0.0.2\\r\\n\"\n\t\"WWW-Authenticate: Digest realm=\\\"example.net\\\", \"\n\t\"nonce=\\\"YZlVk2GZVGegVBZVKaMHpnxmUA+QyoSl\\\"\\r\\n\"\n\t\"Content-Length: 0\\r\\n\\r\\n\",\n\n\t/* explicit MD5 */\n\t\"SIP/2.0 401 Unauthorized\\r\\n\"\n\t\"Via: SIP/2.0/TLS \"\n\t\"10.0.0.1:37589;branch=z9hG4bK5625ce6f310a0fc8;rport=13718;\"\n\t\"received=10.0.0.2\\r\\n\"\n\t\"WWW-Authenticate: Digest realm=\\\"example.net\\\", \"\n\t\"algorithm=\\\"MD5\\\", \"\n\t\"nonce=\\\"YZlVk2GZVGegVBZVKaMHpnxmUA+QyoSl\\\"\\r\\n\"\n\t\"Content-Length: 0\\r\\n\\r\\n\",\n\n\t/* explicit SHA-256 */\n\t\"SIP/2.0 401 Unauthorized\\r\\n\"\n\t\"Via: SIP/2.0/TLS \"\n\t\"10.0.0.1:37589;branch=z9hG4bK5625ce6f310a0fc8;rport=13718;\"\n\t\"received=10.0.0.2\\r\\n\"\n\t\"WWW-Authenticate: Digest realm=\\\"example.net\\\", \"\n\t\"algorithm=\\\"SHA-256\\\", \"\n\t\"nonce=\\\"YZlVk2GZVGegVBZVKaMHpnxmUA+QyoSl\\\"\\r\\n\"\n\t\"Content-Length: 0\\r\\n\\r\\n\",\n\n\t/* explicit SHA-256 qop */\n\t\"SIP/2.0 401 Unauthorized\\r\\n\"\n\t\"Via: SIP/2.0/TLS \"\n\t\"10.0.0.1:37589;branch=z9hG4bK5625ce6f310a0fc8;rport=13718;\"\n\t\"received=10.0.0.2\\r\\n\"\n\t\"WWW-Authenticate: Digest realm=\\\"example.net\\\", \"\n\t\"algorithm=\\\"SHA-256\\\", \"\n\t\"qop=\\\"auth\\\", \"\n\t\"nonce=\\\"YZlVk2GZVGegVBZVKaMHpnxmUA+QyoS\\\"\\r\\n\"\n\t\"Content-Length: 0\\r\\n\\r\\n\"\n};\n\n\nstatic const char *testr[] = {\n\t\"algorithm=MD5\",\n\t\"algorithm=MD5\",\n\t\"algorithm=SHA-256\",\n\t\"algorithm=SHA-256\"\n};\n\n\nstatic int auth_handler(char **user, char **pass, const char *rlm, void *arg)\n{\n\t(void)user;\n\t(void)pass;\n\t(void)rlm;\n\t(void)arg;\n\n\treturn 0;\n}\n\n\nstatic int test_sip_auth_encode(void)\n{\n\tint err = 0;\n\tstruct mbuf *mb, *mb_enc;\n\tstruct sip_auth *auth = NULL;\n\tchar buf[1024] = {0};\n\tstruct sip_msg *msg = NULL;\n\tconst char met[]    = \"REGISTER\";\n\tconst char uri[]    = \"<sip:user@host:5060;transport=udp>\";\n\n\tmb = mbuf_alloc(2048);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tmb_enc = mbuf_alloc(2048);\n\tif (!mb_enc) {\n\t\tmem_deref(mb);\n\t\treturn ENOMEM;\n\t}\n\n\tfor (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) {\n\t\tmbuf_rewind(mb);\n\t\tmbuf_rewind(mb_enc);\n\n\t\terr = sip_auth_alloc(&auth, auth_handler, NULL, false);\n\t\tTEST_ERR(err);\n\n\t\terr = mbuf_write_str(mb, testv[i]);\n\t\tTEST_ERR(err);\n\n\t\tmbuf_set_pos(mb, 0);\n\n\t\terr = sip_msg_decode(&msg, mb);\n\t\tTEST_ERR(err);\n\n\t\terr = sip_auth_authenticate(auth, msg);\n\t\tTEST_ERR(err);\n\n\t\terr = sip_auth_encode(mb_enc, auth, met, uri);\n\t\tTEST_ERR(err);\n\n\t\tmbuf_set_pos(mb_enc, 0);\n\t\tmbuf_read_str(mb_enc, buf, mbuf_get_left(mb_enc));\n\n\t\terr = re_regex(buf, str_len(buf), testr[i]);\n\t\tTEST_ERR(err);\n\n\t\tmem_deref(msg);\n\t\tmem_deref(auth);\n\t}\n\nout:\n\tmem_deref(mb);\n\tmem_deref(mb_enc);\n\tif (err) {\n\t\tmem_deref(msg);\n\t\tmem_deref(auth);\n\t}\n\n\treturn err;\n}\n\n\nint test_sip_auth(void)\n{\n\tint err;\n\n\terr = test_sip_auth_encode();\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/sipevent.c",
    "content": "/**\n * @file sipevent.c SIP Event regression testcode\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_sipevent\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/*\n * A is the subscriber to event changes from B\n *\n *   .-----.               .-----.\n *   |  A  |               |  B  |\n *   '-----'               '-----'\n * (Subscriber)           (Notifier)\n *\n *       ----- SUBSCRIBE ----->\n *\n *       <----- 200 OK --------\n *\n *\n *\n *\n *                             <------- X somekind of event happened\n *       <----- NOTIFY --------\n *  X <--\n *       ------- 200 OK ------>\n *\n */\n\n\nstruct agent {\n\tstruct agent *peer;\n\tstruct sip *sip;\n\tstruct sipevent_sock *sock;\n\tstruct sipsub *sub;\n\tstruct sipnot *not;\n\tchar name[32];\n\tbool exited;\n\tchar uri[256];\n\tunsigned subc;\n\tunsigned notc;\n\tunsigned closec;\n\tint err;\n};\n\n\nstatic const char *test_event = \"my-event\";\n\n\nstatic void complete(struct agent *ag, int err)\n{\n\tag->err = err;\n\tre_cancel();\n}\n\n\nstatic int send_notify(struct agent *ag)\n{\n\tconst char *aor = \"tull\";\n\tstruct mbuf *mb;\n\tint err;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = mbuf_printf(mb,\n\t\"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\" standalone=\\\"no\\\"?>\\r\\n\"\n\t\"<presence xmlns=\\\"urn:ietf:params:xml:ns:pidf\\\"\\r\\n\"\n\t\"    xmlns:dm=\\\"urn:ietf:params:xml:ns:pidf:data-model\\\"\\r\\n\"\n\t\"    xmlns:rpid=\\\"urn:ietf:params:xml:ns:pidf:rpid\\\"\\r\\n\"\n\t\"    entity=\\\"%s\\\">\\r\\n\"\n\t\"  <dm:person id=\\\"p4159\\\"><rpid:activities/></dm:person>\\r\\n\"\n\t\"  <tuple id=\\\"t4109\\\">\\r\\n\"\n\t\"    <status>\\r\\n\"\n\t\"      <basic>%s</basic>\\r\\n\"\n\t\"    </status>\\r\\n\"\n\t\"    <contact>%s</contact>\\r\\n\"\n\t\"  </tuple>\\r\\n\"\n\t\"</presence>\\r\\n\"\n\t\t    ,aor, \"open\", aor);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\n\terr = sipevent_notify(ag->not, mb, SIPEVENT_ACTIVE, 0, 0);\n\tif (err) {\n\t\tDEBUG_WARNING(\"presence: notify to %s failed (%m)\\n\",\n\t\t\t      aor, err);\n\t}\n\n out:\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstatic void sipnot_close_handler(int err, const struct sip_msg *msg,\n\t\t\t\t void *arg)\n{\n\tstruct agent *ag = arg;\n\t(void)msg;\n\n\tDEBUG_WARNING(\"[ %s ] sip notification closed (%m)\\n\",\n\t\t      ag->name, err);\n\n\t++ag->closec;\n\n\tcomplete(ag, err);\n}\n\n\n/*\n * Agent `B' -- the Notifier\n *\n * Handle incoming SUBSCRIBE message from A\n\nSUBSCRIBE sip:b@127.0.0.1:20000 SIP/2.0.\nVia: SIP/2.0/UDP 127.0.0.1:10000;branch=z9hG4bKf7f2e9e48bbaea6b;rport.\nContact: <sip:a@127.0.0.1:10000>.\nMax-Forwards: 70.\nTo: <sip:b@127.0.0.1:20000>.\nFrom: \"a\" <sip:a@127.0.0.1:10000>;tag=d3d00d9fb5ee45d5.\nCall-ID: 26ac32ea11a58011.\nCSeq: 37745 SUBSCRIBE.\nUser-Agent: a.\nEvent: my-event.\nExpires: 600.\nContent-Length: 0.\n\n\n */\nstatic bool subscribe_handler(const struct sip_msg *msg, void *arg)\n{\n\tstruct agent *ag = arg;\n\tstruct agent *peer = ag->peer;\n\tconst struct sip_hdr *hdr;\n\tstruct sipevent_event se;\n\tint err = 0;\n\n\tDEBUG_INFO(\"[ %s ] recv SIP msg (%r)\\n\", ag->name, &msg->met);\n\n\t++ag->subc;\n\n\tTEST_ASSERT(msg != NULL);\n\tTEST_STRCMP(\"SUBSCRIBE\", 9U, msg->met.p, msg->met.l);\n\thdr = sip_msg_hdr(msg, SIP_HDR_CONTACT);\n\tTEST_ASSERT(hdr != NULL);\n\tTEST_STRCMP(ag->uri, strlen(ag->uri), msg->to.auri.p, msg->to.auri.l);\n\tTEST_STRCMP(peer->uri, strlen(peer->uri),\n\t\t    msg->from.auri.p, msg->from.auri.l);\n\tTEST_STRCMP(\"SUBSCRIBE\", 9U, msg->cseq.met.p, msg->cseq.met.l);\n\tTEST_ASSERT(pl_u32(&msg->expires) > 0);\n\n\thdr = sip_msg_hdr(msg, SIP_HDR_EVENT);\n\tif (!hdr) {\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\n\terr = sipevent_event_decode(&se, &hdr->val);\n\tif (err)\n\t\tgoto out;\n\n\tif (pl_strcasecmp(&se.event, test_event)) {\n\t\tDEBUG_WARNING(\"presence: unexpected event '%r'\\n\", &se.event);\n\t\terr = EPROTO;\n\t\tgoto out;\n\t}\n\n\terr = sipevent_accept(&ag->not, ag->sock, msg, NULL, &se, 200, \"OK\",\n\t\t\t      600, 600, 600, ag->name, \"application/pidf\",\n\t\t\t      NULL, NULL, false,\n\t\t\t      sipnot_close_handler, ag, NULL);\n\tif (err)\n\t\tgoto out;\n\n\terr = send_notify(ag);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err) {\n\t\tcomplete(ag, err);\n\t}\n\n\treturn true;\n}\n\n\n/*\n * Agent `A' -- the Subscriber\n *\n * handle incoming NOTIFY messages from B\n *\n\nNOTIFY sip:a@127.0.0.1:10000 SIP/2.0.\nVia: SIP/2.0/UDP 127.0.0.1:20000;branch=z9hG4bK056ce9e0dabdea16;rport.\nContact: <sip:b@127.0.0.1:20000>.\nMax-Forwards: 70.\nTo: \"a\" <sip:a@127.0.0.1:10000>;tag=b8c2a38adecb1bea.\nFrom: <sip:b@127.0.0.1:20000>;tag=426f09aed9f32053.\nCall-ID: f3b22da3f4223935.\nCSeq: 7364 NOTIFY.\nUser-Agent: b.\nEvent: my-event.\nSubscription-State: active;expires=600.\nContent-Type: application/pidf.\nContent-Length: 417.\n\n */\nstatic void sipsub_notify_handler(struct sip *sip, const struct sip_msg *msg,\n\t\t\t\t  void *arg)\n{\n\tstruct agent *ag = arg;\n\tstruct agent *peer = ag->peer;\n\tconst struct sip_hdr *hdr;\n\tstruct sipevent_substate substate;\n\tint err = 0;\n\t(void)sip;\n\n\tDEBUG_INFO(\"[ %s ] subscriber -- incoming notify\\n\", ag->name);\n\n\tTEST_ASSERT(NULL != ag->sub);\n\n\t++ag->notc;\n\n\t/* verify the SIP message */\n\tTEST_ASSERT(msg != NULL);\n\tTEST_STRCMP(\"NOTIFY\", 6U, msg->met.p, msg->met.l);\n\thdr = sip_msg_hdr(msg, SIP_HDR_CONTACT);\n\tTEST_ASSERT(hdr != NULL);\n\tTEST_STRCMP(ag->uri, strlen(ag->uri), msg->to.auri.p, msg->to.auri.l);\n\tTEST_STRCMP(peer->uri, strlen(peer->uri),\n\t\t    msg->from.auri.p, msg->from.auri.l);\n\tTEST_STRCMP(\"NOTIFY\", 6U, msg->cseq.met.p, msg->cseq.met.l);\n\thdr = sip_msg_hdr(msg, SIP_HDR_EVENT);\n\tTEST_ASSERT(hdr != NULL);\n\tTEST_STRCMP(test_event, str_len(test_event), hdr->val.p, hdr->val.l);\n\n\thdr = sip_msg_hdr(msg, SIP_HDR_SUBSCRIPTION_STATE);\n\tTEST_ASSERT(hdr != NULL);\n\terr = sipevent_substate_decode(&substate, &hdr->val);\n\tTEST_ERR(err);\n\n\t/* verify that state is active */\n\tTEST_EQUALS(SIPEVENT_ACTIVE, substate.state);\n\tTEST_ASSERT(pl_u32(&substate.expires) > 0);\n\n\tsip_treply(NULL, sip, msg, 200, \"OK\");\n\n\tcomplete(ag, 0);\n\treturn;\n\n out:\n\tif (err)\n\t\tcomplete(ag, err);\n}\n\n\nstatic void sipsub_close_handler(int err, const struct sip_msg *msg,\n\t\t\t\t const struct sipevent_substate *substate,\n\t\t\t\t void *arg)\n{\n\tstruct agent *ag = arg;\n\t(void)msg;\n\t(void)substate;\n\n\tDEBUG_WARNING(\"[ %s ] subscriber -- closed (%m)\\n\", ag->name, err);\n\n\t++ag->closec;\n\n\tcomplete(ag, err);\n}\n\n\nstatic void exit_handler(void *arg)\n{\n\tstruct agent *ag = arg;\n\n\tag->exited = true;\n\n\tif (ag->peer->exited)\n\t\tre_cancel();\n}\n\n\nstatic void destructor(void *data)\n{\n\tstruct agent *ag = data;\n\n\tmem_deref(ag->sub);\n\tmem_deref(ag->not);\n\n\tmem_deref(ag->sock);\n\n\tsip_close(ag->sip, true);\n\tmem_deref(ag->sip);\n}\n\n\nstatic int agent_alloc(struct agent **agp, const char *name,\n\t\tconst struct sa *laddr)\n{\n\tstruct sa sa;\n\tstruct agent *ag;\n\tint err;\n\n\tag = mem_zalloc(sizeof(*ag), destructor);\n\tif (!ag)\n\t\treturn ENOMEM;\n\n\tstr_ncpy(ag->name, name, sizeof(ag->name));\n\n\terr = sip_alloc(&ag->sip, NULL, 32, 32, 32,\n\t\t\tname, exit_handler, ag);\n\tif (err)\n\t\tgoto out;\n\n\terr = sip_transp_add(ag->sip, SIP_TRANSP_UDP, laddr);\n\tif (err)\n\t\tgoto out;\n\n\terr = sip_transp_laddr(ag->sip, &sa, SIP_TRANSP_UDP, NULL);\n\tif (err)\n\t\tgoto out;\n\n\terr = sipevent_listen(&ag->sock, ag->sip, 32, 32,\n\t\t\t      subscribe_handler, ag);\n\tif (err)\n\t\tgoto out;\n\n\tre_snprintf(ag->uri, sizeof(ag->uri), \"sip:%s@%J\", name, &sa);\n\n#if 0\n\tre_printf(\"agent %s (%s)\\n\", name, ag->uri);\n#endif\n\n out:\n\tif (err)\n\t\tmem_deref(ag);\n\telse\n\t\t*agp = ag;\n\n\treturn err;\n}\n\n\nstatic int agent_subscribe(struct agent *ag, struct agent *peer)\n{\n\tif (!ag || !peer)\n\t\treturn EINVAL;\n\n\treturn sipevent_subscribe(&ag->sub, ag->sock, peer->uri, ag->name,\n\t\t\t\t  ag->uri, test_event, NULL, 600, ag->name,\n\t\t\t\t  NULL, 0, NULL, NULL, false,\n\t\t\t\t  NULL, sipsub_notify_handler,\n\t\t\t\t  sipsub_close_handler, ag, NULL);\n}\n\n\nstatic int do_sipevent(struct sa *laddr)\n{\n\tstruct agent *a = NULL, *b = NULL;\n\tint err = 0;\n\n\terr = agent_alloc(&a, \"a\", laddr);\n\tif (err)\n\t\tgoto out;\n\terr = agent_alloc(&b, \"b\", laddr);\n\tif (err)\n\t\tgoto out;\n\n\ta->peer = b;\n\tb->peer = a;\n\n\terr = agent_subscribe(a, b);\n\tif (err)\n\t\tgoto out;\n\n\terr = re_main_timeout(500);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_ERR(a->err);\n\tTEST_ERR(b->err);\n\n\tTEST_EQUALS(0, a->subc);\n\tTEST_EQUALS(1, a->notc);\n\tTEST_EQUALS(0, a->closec);\n\n\tTEST_EQUALS(1, b->subc);\n\tTEST_EQUALS(0, b->notc);\n\tTEST_EQUALS(0, b->closec);\n\n out:\n\tmem_deref(b);\n\tmem_deref(a);\n\n\treturn err;\n}\n\n\nint test_sipevent(void)\n{\n\tint err;\n\tstruct sa laddr;\n\n\terr = sa_set_str(&laddr, \"127.0.0.1\", 0);\n\tTEST_ERR(err);\n\n\terr = do_sipevent(&laddr);\n\nout:\n\treturn err;\n}\n\n\nint test_sipevent_network(void)\n{\n\tstruct sa laddr;\n\tint err = 0;\n\n\tsa_init(&laddr, AF_INET6);\n\n\tif (0 == net_if_getlinklocal(NULL, AF_INET6, &laddr)) {\n\n\t\terr = do_sipevent(&laddr);\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/sipreg.c",
    "content": "/**\n * @file sipreg.c SIP Register client regression testcode\n *\n * Copyright (C) 2010 - 2015 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_sipreg\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define LOCAL_PORT        0\n#define LOCAL_SECURE_PORT 0\n\n\nstruct test {\n\tstruct tmr tmr;\n\tenum sip_transp tp;\n\tunsigned n_resp;\n\tuint16_t srcport;\n\tint err;\n};\n\n\nstatic void exit_handler(void *arg)\n{\n\t(void)arg;\n\tre_cancel();\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct test *test = arg;\n\t(void)test;\n\n\tre_cancel();\n}\n\n\nstatic int sipstack_fixture(struct sip **sipp)\n{\n\tstruct sa laddr, laddrs;\n\tstruct sip *sip = NULL;\n\tstruct tls *tls = NULL;\n#ifdef USE_TLS\n\tchar cafile[256];\n#endif\n\tint err;\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", LOCAL_PORT);\n\t(void)sa_set_str(&laddrs, \"127.0.0.1\", LOCAL_SECURE_PORT);\n\n\terr = sip_alloc(&sip, NULL, 32, 32, 32, \"retest\", exit_handler, NULL);\n\tif (err)\n\t\tgoto out;\n\n\terr |= sip_transp_add(sip, SIP_TRANSP_UDP, &laddr);\n\terr |= sip_transp_add(sip, SIP_TRANSP_TCP, &laddr);\n\tif (err)\n\t\tgoto out;\n\n#ifdef USE_TLS\n\t/* TLS-context for client -- no certificate needed */\n\terr = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\tre_snprintf(cafile, sizeof(cafile), \"%s/server-ecdsa.pem\",\n\t\t    test_datapath());\n\n\terr = tls_add_ca(tls, cafile);\n\tif (err)\n\t\tgoto out;\n\n\terr |= sip_transp_add(sip, SIP_TRANSP_TLS, &laddrs, tls);\n\tif (err)\n\t\tgoto out;\n#endif\n\n out:\n\tmem_deref(tls);\n\tif (err)\n\t\tmem_deref(sip);\n\telse\n\t\t*sipp = sip;\n\n\treturn err;\n}\n\n\nstatic void sip_resp_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\n\tif (err) {\n\t\ttest->err = err;\n\t\tre_cancel();\n\t\treturn;\n\t}\n\n\t++test->n_resp;\n\n\t/* verify the SIP response message */\n\tTEST_ASSERT(msg != NULL);\n\tTEST_EQUALS(200, msg->scode);\n\tTEST_STRCMP(\"REGISTER\", 8, msg->cseq.met.p, msg->cseq.met.l);\n\tTEST_EQUALS(test->tp, msg->tp);\n\tif (test->srcport)\n\t\tTEST_EQUALS(test->srcport, sa_port(&msg->dst));\n\n out:\n\tif (err)\n\t\ttest->err = err;\n\n\t/* done */\n\tif (test->tp == SIP_TRANSP_UDP)\n\t\ttmr_start(&test->tmr, 50, tmr_handler, test);\n\telse\n\t\tre_cancel();\n}\n\n#define CPARAMS \"some-param=test;other-param=123;\"\n\n\nstatic int reg_test(enum sip_transp tp, uint16_t srcport)\n{\n\tstruct test test;\n\tstruct sip_server *srv = NULL;\n\tstruct sipreg *reg = NULL;\n\tstruct sip *sip = NULL;\n\tconst struct sip_hdr *contact_hdr = NULL;\n\tchar reg_uri[256];\n\tint err;\n\n\tmemset(&test, 0, sizeof(test));\n\ttest.tp = tp;\n\ttest.srcport = srcport;\n\n\terr = sip_server_alloc(&srv);\n\tTEST_ERR(err);\n\n\terr = sipstack_fixture(&sip);\n\tTEST_ERR(err);\n\n\terr = sip_server_uri(srv, reg_uri, sizeof(reg_uri), tp);\n\tTEST_ERR(err);\n\n\tconst int regid = 1;\n\n\terr = sipreg_alloc(&reg, sip, reg_uri,\n\t\t      \"sip:x@test\", NULL,\n\t\t      \"sip:x@test\",\n\t\t      3600, \"x\", NULL, 0, regid, NULL, NULL, false,\n\t\t      sip_resp_handler, &test, NULL, \"X-Custom: 1234\\r\\n\");\n\tTEST_ERR(err);\n\n\terr = sipreg_set_contact_params(reg, CPARAMS);\n\tTEST_ERR(err);\n\n\tif (srcport)\n\t\tsipreg_set_srcport(reg, srcport);\n\n\terr = sipreg_send(reg);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(1000);\n\tTEST_ERR(err);\n\n\tif (test.err) {\n\t\terr = test.err;\n\t\tTEST_ERR(err);\n\t}\n\n\tTEST_ASSERT(srv->n_register_req > 0);\n\tTEST_ASSERT(test.n_resp > 0);\n\n\tcontact_hdr = sip_msg_hdr(\n\t\t\tsrv->sip_msgs[srv->n_register_req - 1],\n\t\t\tSIP_HDR_CONTACT);\n\terr = re_regex(contact_hdr->val.p, contact_hdr->val.l,\n\t\t\";\" CPARAMS \">;expires=\", NULL);\n\tTEST_ERR(err);\n\n\tASSERT_TRUE(sipreg_registered(reg));\n\tASSERT_TRUE(!sipreg_failed(reg));\n\n out:\n\ttmr_cancel(&test.tmr);\n\n\tsipreg_unregister(reg);\n\tmem_deref(reg);\n\n\tsip_close(sip, true);\n\tmem_deref(sip);\n\n\tmem_deref(srv);\n\n\treturn err;\n}\n\n\nint test_sipreg_udp(void)\n{\n\treturn reg_test(SIP_TRANSP_UDP, 0);\n}\n\n\nint test_sipreg_tcp(void)\n{\n\treturn reg_test(SIP_TRANSP_TCP, 0);\n}\n\n\n#ifdef USE_TLS\nint test_sipreg_tls(void)\n{\n\treturn reg_test(SIP_TRANSP_TLS, 0);\n}\n#endif\n"
  },
  {
    "path": "test/sipsess.c",
    "content": "/**\n * @file sipsess.c SIP Session regression testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_sipsess\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\ntypedef void (prack_func)(void *arg);\n\n\nenum neg_state {\n\tINITIAL = 0,\n\tOFFER_RECEIVED,\n\tANSWER_RECEIVED,\n\tEARLY_CONFIRMED\n};\n\n\nenum rel100_state {\n\tREL100_NONE = 0,\n\tREL100_SUPPORTED = 1,\n\tREL100_REQUIRE = 2\n};\n\n\nenum connect_action {\n\tCONN_PROGRESS = 1,\n\tCONN_PROGR_ANS = 2,\n\tCONN_PROGR_UPD = 4,\n\tCONN_ANSWER = 8,\n\tCONN_BUSY = 16\n};\n\n\nenum answer_action {\n\tANSW_NONE   = 0,\n\tANSW_CANCEL = 1,\n};\n\n\nenum offer_action {\n\tOFFER_NONE   = 0,\n\tOFFER_ANSW   = 1,\n};\n\n\nstruct test {\n\tstruct sip *sip;\n\tstruct sipsess_sock *sock;\n\tstruct sipsess *a;\n\tstruct sipsess *b;\n\tstruct tmr ans_tmr;\n\tstruct tmr ack_tmr;\n\tbool estab_a;\n\tbool estab_b;\n\tbool answr_a;\n\tbool answr_b;\n\tbool progr_a;\n\tbool progr_b;\n\tbool offer_a;\n\tbool offer_b;\n\tbool ack_a;\n\tbool ack_b;\n\tenum rel100_mode rel100_a;\n\tenum rel100_mode rel100_b;\n\tenum neg_state sdp_state;\n\tenum rel100_state rel100_state_a;\n\tenum rel100_state rel100_state_b;\n\tenum connect_action conn_action;\n\tenum offer_action offer_action;\n\tenum answer_action answ_action;\n\tprack_func *prack_action;\n\tint progr_ret_code;\n\tint answ_ret_code;\n\tint ack_cnt;\n\tbool upd_a;\n\tbool upd_b;\n\tstruct mbuf *desc;\n\tbool blind_transfer;\n\tuint16_t altaddr_port;\n\tint err;\n};\n\n\nconst char sdp_a[] = \"v=0\\r\\n\"\n\t\t     \"o=alice 2890844526 2890844526 IN IP4 1.2.3.4\\r\\n\"\n\t\t     \"s=-\\r\\n\"\n\t\t     \"c=IN IP4 1.2.3.4\\r\\n\"\n\t\t     \"t=0 0\\r\\n\"\n\t\t     \"m=audio 49170 RTP/AVP 0 8 97\\r\\n\"\n\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t     \"a=rtpmap:8 PCMA/8000\\r\\n\"\n\t\t     \"a=rtpmap:97 iLBC/8000\\r\\n\"\n\t\t     \"a=sendrecv\\r\\n\"\n\t\t     \"m=video 51372 RTP/AVP 31 32\\r\\n\"\n\t\t     \"a=rtpmap:31 H261/90000\\r\\n\"\n\t\t     \"a=rtpmap:32 MPV/90000\\r\\n\"\n\t\t     \"a=sendrecv\\r\\n\";\nconst char sdp_b[] = \"v=0\\r\\n\"\n\t\t     \"o=bob 2808844564 2808844564 IN IP4 5.6.7.8\\r\\n\"\n\t\t     \"s=-\\r\\n\"\n\t\t     \"c=IN IP4 5.6.7.8\\r\\n\"\n\t\t     \"t=0 0\\r\\n\"\n\t\t     \"m=audio 49174 RTP/AVP 0\\r\\n\"\n\t\t     \"a=rtpmap:0 PCMU/8000\\r\\n\"\n\t\t     \"a=sendrecv\\r\\n\"\n\t\t     \"m=video 49170 RTP/AVP 32\\r\\n\"\n\t\t     \"a=rtpmap:32 MPV/90000\\r\\n\"\n\t\t     \"a=sendrecv\\r\\n\";\n\n\nstatic void stop_test(void)\n{\n\tre_cancel();\n}\n\n\nstatic void abort_test(struct test *test, int err)\n{\n\ttest->err = err;\n\tre_cancel();\n}\n\n\nstatic void exit_handler(void *arg)\n{\n\t(void)arg;\n\tre_cancel();\n}\n\n\nstatic void check_ack(void *arg)\n{\n\tstruct test *test = arg;\n\tbool ack_a = sipsess_ack_pending(test->a);\n\tbool ack_b = sipsess_ack_pending(test->b);\n\n\ttest->ack_a |= ack_a;\n\ttest->ack_b |= ack_b;\n\n\tif (ack_a || ack_b)\n\t\ttmr_start(&test->ack_tmr, 1, check_ack, test);\n\telse\n\t\tstop_test();\n}\n\n\nstatic void wait_for_ack(struct test *test)\n{\n\ttest->ack_a = sipsess_ack_pending(test->a);\n\ttest->ack_b = sipsess_ack_pending(test->b);\n\n\tif (!test->ack_a && !test->ack_b)\n\t\treturn;\n\n\ttmr_start(&test->ack_tmr, 1, check_ack, test);\n}\n\n\nstatic void send_answer_b(void *arg)\n{\n\tstruct test *test = arg;\n\tint err;\n\n\terr = sipsess_answer(test->b, 200, \"Answering\", NULL, NULL);\n\tif (err) {\n\t\tabort_test(test, err);\n\t}\n}\n\n\nstatic int make_sdp(struct mbuf **mbp, const char *sdp)\n{\n\tstruct mbuf *desc;\n\tint err;\n\n\tdesc = mbuf_alloc(strlen(sdp) + 1);\n\tif (!desc)\n\t\treturn ENOMEM;\n\n\terr = mbuf_write_str(desc, sdp);\n\tTEST_ERR(err);\n\n\tmbuf_set_pos(desc, 0);\nout:\n\tif (err)\n\t\tmem_deref(desc);\n\telse\n\t\t*mbp = desc;\n\n\treturn err;\n}\n\n\nstatic void send_update_a(void *arg)\n{\n\tstruct test *test = arg;\n\tstruct mbuf *desc = NULL;\n\tint err;\n\n\terr = make_sdp(&desc, sdp_a);\n\tTEST_ERR(err);\n\n\terr = sipsess_modify(test->a, desc);\n\tTEST_ERR(err);\n\nout:\n\tmem_deref(desc);\n\tif (err)\n\t\tabort_test(test, err);\n\n}\n\n\nstatic void send_update_b(void *arg)\n{\n\tstruct test *test = arg;\n\tstruct mbuf *desc = NULL;\n\tint err;\n\n\terr = make_sdp(&desc, sdp_b);\n\tTEST_ERR(err);\n\n\terr = sipsess_modify(test->b, desc);\n\tTEST_ERR(err);\n\nout:\n\tmem_deref(desc);\n\tif (err)\n\t\tabort_test(test, err);\n\n}\n\n\nstatic int desc_handler(struct mbuf **descp, const struct sa *src,\n\t\t\t\tconst struct sa *dst, void *arg)\n{\n\tstruct test *test = arg;\n\t(void)src;\n\t(void)dst;\n\n\ttest->desc = mbuf_alloc(1);\n\tif (!test->desc)\n\t\treturn ENOMEM;\n\n\t*descp = test->desc;\n\treturn 0;\n}\n\n\nstatic int desc_handler_a(struct mbuf **descp, const struct sa *src,\n\t\t\t\tconst struct sa *dst, void *arg)\n{\n\tstruct mbuf *desc;\n\tint err = 0;\n\t(void)src;\n\t(void)dst;\n\t(void)arg;\n\n\terr = make_sdp(&desc, sdp_a);\n\tTEST_ERR(err);\n\n\t*descp = desc;\n\nout:\n\treturn err;\n}\n\n\nstatic int offer_handler_a(struct mbuf **descp, const struct sip_msg *msg,\n\t\t\t   void *arg)\n{\n\tstruct test *test = arg;\n\t(void)descp;\n\t(void)msg;\n\n\tif (test->sdp_state == INITIAL || test->sdp_state == EARLY_CONFIRMED)\n\t\ttest->sdp_state = OFFER_RECEIVED;\n\n\tif (!pl_strcmp(&msg->met, \"UPDATE\"))\n\t\ttest->upd_a = true;\n\n\ttest->offer_a = true;\n\n\treturn 0;\n}\n\n\nstatic int offer_handler_b(struct mbuf **descp, const struct sip_msg *msg,\n\t\t\t   void *arg)\n{\n\tstruct test *test = arg;\n\t(void)msg;\n\tint err = 0;\n\n\tif (test->sdp_state == INITIAL || test->sdp_state == EARLY_CONFIRMED)\n\t\ttest->sdp_state = OFFER_RECEIVED;\n\n\tif (!pl_strcmp(&msg->met, \"UPDATE\"))\n\t\ttest->upd_b = true;\n\n\ttest->offer_b = true;\n\n\tif (test->offer_action == OFFER_ANSW) {\n\t\terr = make_sdp(descp, sdp_b);\n\t\tTEST_ERR(err);\n\t}\n\nout:\n\treturn err;\n}\n\n\nstatic int answer_handler_a(const struct sip_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\ttest->answr_a = true;\n\tif (mbuf_get_left(msg->mb))\n\t\ttest->sdp_state = ANSWER_RECEIVED;\n\n\tif (sip_msg_hdr_has_value(msg, SIP_HDR_SUPPORTED, \"100rel\"))\n\t\ttest->rel100_a |= (enum rel100_mode)REL100_SUPPORTED;\n\n\tif (sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, \"100rel\"))\n\t\ttest->rel100_a |= (enum rel100_mode)REL100_REQUIRE;\n\n\tif (!pl_strcmp(&msg->cseq.met, \"UPDATE\")) {\n\t\tif (msg->scode < 200 || msg->scode > 299) {\n\t\t\tabort_test(test, msg->scode);\n\t\t\treturn msg->scode;\n\t\t}\n\n\t\ttmr_start(&test->ans_tmr, 0, send_answer_b, test);\n\t}\n\n\tif (test->answ_action == ANSW_CANCEL)\n\t\twait_for_ack(test);\n\n\treturn 0;\n}\n\n\nstatic int answer_handler_b(const struct sip_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\ttest->answr_b = true;\n\tif (mbuf_get_left(msg->mb))\n\t\ttest->sdp_state = ANSWER_RECEIVED;\n\n\tif (sip_msg_hdr_has_value(msg, SIP_HDR_SUPPORTED, \"100rel\"))\n\t\ttest->rel100_state_b |= REL100_SUPPORTED;\n\n\tif (sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, \"100rel\"))\n\t\ttest->rel100_state_b |= REL100_REQUIRE;\n\n\tif (!pl_strcmp(&msg->cseq.met, \"UPDATE\")) {\n\t\tif (msg->scode < 200 || msg->scode > 299) {\n\t\t\tabort_test(test, msg->scode);\n\t\t\treturn msg->scode;\n\t\t}\n\n\t\ttmr_start(&test->ans_tmr, 0, send_answer_b, test);\n\t}\n\n\treturn 0;\n}\n\n\nstatic void progr_handler_a(const struct sip_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\t(void)msg;\n\n\ttest->progr_a = true;\n}\n\n\nstatic void prack_handler(const struct sip_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\t(void)msg;\n\n\tif (test->sdp_state == ANSWER_RECEIVED)\n\t\ttest->sdp_state = EARLY_CONFIRMED;\n\n\tif (test->prack_action)\n\t\ttmr_start(&test->ans_tmr, 0, test->prack_action, test);\n}\n\n\nstatic void estab_handler_a(const struct sip_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\t(void)msg;\n\n\ttest->estab_a = true;\n\tif (test->estab_b)\n\t\tstop_test();\n}\n\n\nstatic void estab_handler_b(const struct sip_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\t(void)msg;\n\n\ttest->estab_b = true;\n\tif (test->estab_a)\n\t\tstop_test();\n}\n\n\nstatic void close_handler(int err, const struct sip_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\t(void)msg;\n\n\tif (!err && test->conn_action == CONN_BUSY)\n\t\terr = EBUSY;\n\n\tabort_test(test, err ? err : ENOMEM);\n}\n\n\nstatic void conn_handler(const struct sip_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\tstruct mbuf *desc = NULL;\n\tint err;\n\tchar *hdrs = test->rel100_b == REL100_REQUIRED ?\n\t\t     \"Require: 100rel\\r\\n\" : \"\";\n\n\tif (mbuf_get_left(msg->mb)) {\n\t\ttest->sdp_state = OFFER_RECEIVED;\n\t\ttest->offer_b = true;\n\t}\n\n\tif (sip_msg_hdr_has_value(msg, SIP_HDR_SUPPORTED, \"100rel\"))\n\t\ttest->rel100_state_b |= REL100_SUPPORTED;\n\n\tif (sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, \"100rel\"))\n\t\ttest->rel100_state_b |= REL100_REQUIRE;\n\n\terr = make_sdp(&desc, sdp_b);\n\tTEST_ERR(err);\n\n\ttest->desc = desc;\n\n\tif (test->conn_action & CONN_PROGRESS\n\t    || test->conn_action & CONN_PROGR_ANS\n\t    || test->conn_action & CONN_PROGR_UPD) {\n\t\terr = sipsess_accept(&test->b, test->sock, msg, 183,\n\t\t\t\t\"Progress\", test->rel100_b, \"b\",\n\t\t\t\t\"application/sdp\", desc, NULL, NULL, false,\n\t\t\t\toffer_handler_b, answer_handler_b,\n\t\t\t\testab_handler_b, NULL, NULL, close_handler,\n\t\t\t\ttest, hdrs);\n\t\tif (err != test->progr_ret_code) {\n\t\t\ttest->progr_ret_code = err;\n\t\t\tgoto out;\n\t\t}\n\n\t\tif (err)\n\t\t\tmem_deref(desc);\n\n\t\terr = sipsess_set_prack_handler(test->b, prack_handler);\n\t\tif (err)\n\t\t\tabort_test(test, err);\n\t}\n\n\tif (test->conn_action & CONN_PROGR_ANS) {\n\t\terr = sipsess_answer(test->b, 200, \"Answering\", NULL, NULL);\n\t\tif (err != test->answ_ret_code) {\n\t\t\ttest->answ_ret_code = err;\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse if (test->conn_action & CONN_PROGR_UPD) {\n\t\tmem_deref(desc);\n\t\tdesc = mbuf_alloc(0);\n\t\tif (!desc) {\n\t\t\terr = ENOMEM;\n\t\t\tgoto out;\n\t\t}\n\n\t\tmbuf_set_pos(desc, 0);\n\t\ttest->desc = desc;\n\n\t\terr = sipsess_modify(test->b, desc);\n\t\tTEST_ERR(err);\n\t}\n\telse if (test->conn_action & CONN_ANSWER) {\n\t\terr = sipsess_accept(&test->b, test->sock, msg, 200, \"OK\",\n\t\t\t\ttest->rel100_b, \"b\", \"application/sdp\",\n\t\t\t\tdesc, NULL, NULL, false, offer_handler_b,\n\t\t\t\tanswer_handler_b, estab_handler_b, NULL, NULL,\n\t\t\t\tclose_handler, test, hdrs);\n\t\tif (err != test->answ_ret_code) {\n\t\t\ttest->answ_ret_code = err;\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse if (test->conn_action & CONN_BUSY) {\n\t\terr = sipsess_accept(&test->b, test->sock, msg, 180,\n\t\t\t\t\"Ringing\", test->rel100_b, \"b\",\n\t\t\t\t\"application/sdp\", NULL, NULL, NULL, false,\n\t\t\t\toffer_handler_b, answer_handler_b,\n\t\t\t\testab_handler_b, NULL, NULL, close_handler,\n\t\t\t\ttest, hdrs);\n\t\tif (err != test->answ_ret_code) {\n\t\t\ttest->answ_ret_code = err;\n\t\t\tgoto out;\n\t\t}\n\t\terr |= sipsess_reject(test->b, 486, \"Busy Here\", NULL);\n\t\tif (err != test->answ_ret_code) {\n\t\t\ttest->answ_ret_code = err;\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\tif (test->conn_action & (CONN_ANSWER | CONN_PROGR_ANS | CONN_BUSY))\n\t\tmem_deref(desc);\n\n\treturn;\n\nout:\n\tmem_deref(desc);\n\tabort_test(test, err);\n}\n\n\nstatic void conn_transfer_handler(const struct sip_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\tint err = 0;\n\n\tif (test->blind_transfer) {\n\t\tconn_handler(msg, arg);\n\t}\n\telse {\n\t\terr = sip_replyf(test->sip, msg, 302, \"Moved Temporarily\",\n\t\t\t\"Contact: \\\"alt retest\\\" \"\n\t\t\t\"<sip:127.0.0.1:%u>\\r\\n\\r\\n\", test->altaddr_port);\n\t\tif (err) {\n\t\t\tabort_test(test, err);\n\t\t}\n\t}\n\n\treturn;\n}\n\n\nint test_sipsess(void)\n{\n\tstruct test test;\n\tstruct sa laddr;\n\tchar to_uri[256];\n\tint err;\n\tuint16_t port;\n\tchar *callid;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.rel100_a = REL100_ENABLED;\n\ttest.rel100_b = REL100_ENABLED;\n\ttest.conn_action = CONN_ANSWER;\n\n\terr = sip_alloc(&test.sip, NULL, 32, 32, 32,\n\t\t\t\"retest\", exit_handler, NULL);\n\tif (err)\n\t\tgoto out;\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr);\n\tif (err)\n\t\tgoto out;\n\n\terr = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL);\n\tif (err)\n\t\tgoto out;\n\n\tport = sa_port(&laddr);\n\n\terr = sipsess_listen(&test.sock, test.sip, 32, conn_handler, &test);\n\tif (err)\n\t\tgoto out;\n\n\terr = str_x64dup(&callid, rand_u64());\n\tif (err)\n\t\tgoto out;\n\n\t/* Connect to \"b\" */\n\t(void)re_snprintf(to_uri, sizeof(to_uri), \"sip:b@127.0.0.1:%u\", port);\n\terr = sipsess_connect(&test.a, test.sock, to_uri, NULL,\n\t\t\t      \"sip:a@127.0.0.1\", \"a\", NULL, 0,\n\t\t\t      \"application/sdp\", NULL, NULL, false,\n\t\t\t      callid, desc_handler_a,\n\t\t\t      offer_handler_a, answer_handler_a, NULL,\n\t\t\t      estab_handler_a, NULL, NULL,\n\t\t\t      close_handler, &test, NULL);\n\tmem_deref(callid);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\tif (test.err) {\n\t\terr = test.err;\n\t\tgoto out;\n\t}\n\n\t/* okay here -- verify */\n\tASSERT_TRUE(test.estab_a);\n\tASSERT_TRUE(test.estab_b);\n\tASSERT_TRUE(test.desc);\n\tASSERT_TRUE(test.answr_a);\n\tASSERT_TRUE(test.offer_b);\n\tASSERT_TRUE(!test.offer_a);\n\tASSERT_TRUE(!test.answr_b);\n\n\t/* test re-invite with wait for ACK */\n\ttest.sdp_state = INITIAL;\n\ttest.answ_action = ANSW_CANCEL;\n\ttest.offer_action = OFFER_ANSW;\n\terr = make_sdp(&test.desc, sdp_a);\n\tTEST_ERR(err);\n\terr = sipsess_modify(test.a, test.desc);\n\tTEST_ERR(err);\n\ttest.desc = mem_deref(test.desc);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\tif (test.err) {\n\t\terr = test.err;\n\t\tgoto out;\n\t}\n\n\tASSERT_TRUE(test.ack_b);\n\tASSERT_TRUE(!sipsess_ack_pending(test.a));\n\tASSERT_TRUE(!sipsess_ack_pending(test.b));\n\tASSERT_TRUE(test.sdp_state == ANSWER_RECEIVED);\n\n out:\n\ttest.a = mem_deref(test.a);\n\ttest.b = mem_deref(test.b);\n\n\tsipsess_close_all(test.sock);\n\ttest.sock = mem_deref(test.sock);\n\n\tsip_close(test.sip, false);\n\ttest.sip = mem_deref(test.sip);\n\n\treturn err;\n}\n\n\nint test_sipsess_reject(void)\n{\n\tstruct test test;\n\tstruct sa laddr;\n\tchar to_uri[256];\n\tint err;\n\tuint16_t port;\n\tchar *callid;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.rel100_a = REL100_DISABLED;\n\ttest.rel100_b = REL100_DISABLED;\n\ttest.conn_action = CONN_BUSY;\n\n\terr = sip_alloc(&test.sip, NULL, 32, 32, 32,\n\t\t\t\"retest\", exit_handler, NULL);\n\tTEST_ERR(err);\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\terr = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL);\n\tTEST_ERR(err);\n\n\tport = sa_port(&laddr);\n\n\terr = sipsess_listen(&test.sock, test.sip, 32, conn_handler, &test);\n\tTEST_ERR(err);\n\n\terr = str_x64dup(&callid, rand_u64());\n\tTEST_ERR(err);\n\n\t/* Connect to \"b\" */\n\t(void)re_snprintf(to_uri, sizeof(to_uri), \"sip:b@127.0.0.1:%u\", port);\n\terr = sipsess_connect(&test.a, test.sock, to_uri, NULL,\n\t\t\t      \"sip:a@127.0.0.1\", \"a\", NULL, 0,\n\t\t\t      \"application/sdp\", NULL, NULL, false,\n\t\t\t      callid, desc_handler,\n\t\t\t      offer_handler_a, answer_handler_a, NULL,\n\t\t\t      estab_handler_a, NULL, NULL,\n\t\t\t      close_handler, &test, NULL);\n\tmem_deref(callid);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\t/* okay here -- verify */\n\tASSERT_TRUE(test.err == EBUSY);\n\tASSERT_TRUE(!test.estab_a);\n\tASSERT_TRUE(!test.estab_b);\n\tASSERT_TRUE(test.desc);\n\tASSERT_TRUE(!test.answr_a);\n\tASSERT_TRUE(!test.offer_b);\n\nout:\n\ttest.a = mem_deref(test.a);\n\ttest.b = mem_deref(test.b);\n\n\tsipsess_close_all(test.sock);\n\ttest.sock = mem_deref(test.sock);\n\n\tsip_close(test.sip, false);\n\ttest.sip = mem_deref(test.sip);\n\n\treturn err;\n}\n\n\nint test_sipsess_blind_transfer(void)\n{\n\tstruct test test;\n\tstruct sa laddr, altaddr;\n\tchar to_uri[256];\n\tint err;\n\tuint16_t port;\n\tchar *callid;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.rel100_a = REL100_ENABLED;\n\ttest.rel100_b = REL100_ENABLED;\n\ttest.conn_action = CONN_ANSWER;\n\n\terr = sip_alloc(&test.sip, NULL, 32, 32, 32,\n\t\t\t\"retest\", exit_handler, NULL);\n\tTEST_ERR(err);\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\terr = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL);\n\tTEST_ERR(err);\n\n\tport = sa_port(&laddr);\n\n\terr = sipsess_listen(&test.sock, test.sip, 32, conn_transfer_handler,\n\t\t&test);\n\tTEST_ERR(err);\n\n\t(void)sa_set_str(&altaddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &altaddr);\n\tTEST_ERR(err);\n\n\terr = sip_transp_laddr(test.sip, &altaddr, SIP_TRANSP_UDP, NULL);\n\tTEST_ERR(err);\n\n\ttest.altaddr_port = sa_port(&altaddr);\n\n\terr = str_x64dup(&callid, rand_u64());\n\tif (err)\n\t\tgoto out;\n\n\t/* Connect to \"b\" */\n\t(void)re_snprintf(to_uri, sizeof(to_uri), \"sip:b@127.0.0.1:%u\", port);\n\terr = sipsess_connect(&test.a, test.sock, to_uri, NULL,\n\t\t\t      \"sip:a@127.0.0.1\", \"a\", NULL, 0,\n\t\t\t      \"application/sdp\", NULL, NULL, false,\n\t\t\t      callid, desc_handler_a,\n\t\t\t      offer_handler_a, answer_handler_a, NULL,\n\t\t\t      estab_handler_a, NULL, NULL,\n\t\t\t      close_handler, &test, NULL);\n\tmem_deref(callid);\n\tTEST_ERR(err);\n\n\ttest.blind_transfer = true;\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\tif (test.err) {\n\t\terr = test.err;\n\t\tTEST_ERR(err);\n\t}\n\n\t/* okay here -- verify */\n\tASSERT_TRUE(test.blind_transfer);\n\tASSERT_TRUE(test.estab_a);\n\tASSERT_TRUE(test.estab_b);\n\tASSERT_TRUE(test.desc);\n\tASSERT_TRUE(test.answr_a);\n\tASSERT_TRUE(test.offer_b);\n\tASSERT_TRUE(!test.offer_a);\n\tASSERT_TRUE(!test.answr_b);\n\n out:\n\ttest.a = mem_deref(test.a);\n\ttest.b = mem_deref(test.b);\n\n\tsipsess_close_all(test.sock);\n\ttest.sock = mem_deref(test.sock);\n\n\tsip_close(test.sip, false);\n\ttest.sip = mem_deref(test.sip);\n\n\treturn err;\n}\n\nint test_sipsess_100rel_caller_require(void)\n{\n\tstruct test test;\n\tstruct sa laddr;\n\tchar to_uri[256];\n\tint err;\n\tuint16_t port;\n\tchar *callid;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.rel100_a = REL100_REQUIRED;\n\ttest.rel100_b = REL100_ENABLED;\n\ttest.conn_action = CONN_PROGRESS;\n\ttest.prack_action = send_answer_b;\n\n\terr = sip_alloc(&test.sip, NULL, 32, 32, 32,\n\t\t\t\"retest\", exit_handler, NULL);\n\tTEST_ERR(err);\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\terr = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL);\n\tTEST_ERR(err);\n\n\tport = sa_port(&laddr);\n\n\terr = sipsess_listen(&test.sock, test.sip, 32, conn_handler,\n\t\t\t     &test);\n\tTEST_ERR(err);\n\n\terr = str_x64dup(&callid, rand_u64());\n\tTEST_ERR(err);\n\n\t/* Connect to \"b\" */\n\t(void)re_snprintf(to_uri, sizeof(to_uri), \"sip:b@127.0.0.1:%u\", port);\n\terr = sipsess_connect(&test.a, test.sock, to_uri, NULL,\n\t\t\t      \"sip:a@127.0.0.1\", \"a\", NULL, 0,\n\t\t\t      \"application/sdp\", NULL, NULL, false,\n\t\t\t      callid, desc_handler_a,\n\t\t\t      offer_handler_a, answer_handler_a,\n\t\t\t      progr_handler_a, estab_handler_a, NULL,\n\t\t\t      NULL, close_handler, &test,\n\t\t\t      \"Require: 100rel\\r\\n\");\n\tmem_deref(callid);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\tif (test.err) {\n\t\terr = test.err;\n\t\tTEST_ERR(err);\n\t}\n\n\t/* okay here -- verify */\n\tASSERT_TRUE(test.estab_a);\n\tASSERT_TRUE(test.estab_b);\n\tASSERT_TRUE(test.desc);\n\tASSERT_TRUE(test.offer_b);\n\tASSERT_TRUE(!test.answr_b);\n\tASSERT_TRUE(test.progr_a);\n\tASSERT_TRUE(test.rel100_state_b & REL100_REQUIRE);\n\tASSERT_TRUE((test.rel100_state_b & REL100_SUPPORTED) == 0);\n\tASSERT_TRUE(test.sdp_state == EARLY_CONFIRMED);\n\nout:\n\ttmr_cancel(&test.ans_tmr);\n\ttest.a = mem_deref(test.a);\n\ttest.b = mem_deref(test.b);\n\n\tsipsess_close_all(test.sock);\n\ttest.sock = mem_deref(test.sock);\n\n\tsip_close(test.sip, false);\n\ttest.sip = mem_deref(test.sip);\n\n\tmem_deref(test.desc);\n\n\treturn err;\n}\n\n\nint test_sipsess_100rel_supported(void)\n{\n\tstruct test test;\n\tstruct sa laddr;\n\tchar to_uri[256];\n\tint err;\n\tuint16_t port;\n\tchar *callid;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.rel100_a = REL100_ENABLED;\n\ttest.rel100_b = REL100_ENABLED;\n\ttest.conn_action = CONN_PROGRESS;\n\ttest.prack_action = send_answer_b;\n\n\terr = sip_alloc(&test.sip, NULL, 32, 32, 32,\n\t\t\t\"retest\", exit_handler, NULL);\n\tTEST_ERR(err);\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\terr = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL);\n\tTEST_ERR(err);\n\n\tport = sa_port(&laddr);\n\n\terr = sipsess_listen(&test.sock, test.sip, 32, conn_handler,\n\t\t\t     &test);\n\tTEST_ERR(err);\n\n\terr = str_x64dup(&callid, rand_u64());\n\tTEST_ERR(err);\n\n\t/* Connect to \"b\" */\n\t(void)re_snprintf(to_uri, sizeof(to_uri), \"sip:b@127.0.0.1:%u\", port);\n\terr = sipsess_connect(&test.a, test.sock, to_uri, NULL,\n\t\t\t      \"sip:a@127.0.0.1\", \"a\", NULL, 0,\n\t\t\t      \"application/sdp\", NULL, NULL, false,\n\t\t\t      callid, desc_handler_a,\n\t\t\t      offer_handler_a, answer_handler_a,\n\t\t\t      progr_handler_a, estab_handler_a, NULL,\n\t\t\t      NULL, close_handler, &test,\n\t\t\t      \"Supported: 100rel\\r\\n\");\n\tmem_deref(callid);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\tif (test.err) {\n\t\terr = test.err;\n\t\tTEST_ERR(err);\n\t}\n\n\t/* okay here -- verify */\n\tASSERT_TRUE(test.estab_a);\n\tASSERT_TRUE(test.estab_b);\n\tASSERT_TRUE(test.desc);\n\tASSERT_TRUE(test.answr_a);\n\tASSERT_TRUE(!test.offer_a);\n\tASSERT_TRUE(test.offer_b);\n\tASSERT_TRUE(!test.answr_b);\n\tASSERT_TRUE(test.progr_a);\n\tASSERT_TRUE(test.rel100_state_b & REL100_SUPPORTED);\n\tASSERT_TRUE((test.rel100_state_b & REL100_REQUIRE) == 0);\n\tASSERT_TRUE(test.sdp_state == EARLY_CONFIRMED);\n\nout:\n\ttmr_cancel(&test.ans_tmr);\n\ttest.a = mem_deref(test.a);\n\ttest.b = mem_deref(test.b);\n\n\tsipsess_close_all(test.sock);\n\ttest.sock = mem_deref(test.sock);\n\n\tsip_close(test.sip, false);\n\ttest.sip = mem_deref(test.sip);\n\n\tmem_deref(test.desc);\n\n\treturn err;\n}\n\n\nint test_sipsess_100rel_answer_not_allowed(void)\n{\n\tstruct test test;\n\tstruct sa laddr;\n\tchar to_uri[256];\n\tint err;\n\tuint16_t port;\n\tchar *callid;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.rel100_a = REL100_ENABLED;\n\ttest.rel100_b = REL100_ENABLED;\n\ttest.conn_action = CONN_PROGR_ANS;\n\ttest.answ_ret_code = EAGAIN;\n\ttest.prack_action = send_answer_b;\n\n\terr = sip_alloc(&test.sip, NULL, 32, 32, 32,\n\t\t\t\"retest\", exit_handler, NULL);\n\tTEST_ERR(err);\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\terr = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL);\n\tTEST_ERR(err);\n\n\tport = sa_port(&laddr);\n\n\terr = sipsess_listen(&test.sock, test.sip, 32, conn_handler, &test);\n\tTEST_ERR(err);\n\n\terr = str_x64dup(&callid, rand_u64());\n\tTEST_ERR(err);\n\n\t/* Connect to \"b\" */\n\t(void)re_snprintf(to_uri, sizeof(to_uri), \"sip:b@127.0.0.1:%u\", port);\n\terr = sipsess_connect(&test.a, test.sock, to_uri, NULL,\n\t\t\t      \"sip:a@127.0.0.1\", \"a\", NULL, 0,\n\t\t\t      \"application/sdp\", NULL, NULL, false,\n\t\t\t      callid, desc_handler_a,\n\t\t\t      offer_handler_a, answer_handler_a,\n\t\t\t      progr_handler_a, estab_handler_a, NULL,\n\t\t\t      NULL, close_handler, &test,\n\t\t\t      \"Supported: 100rel\\r\\n\");\n\tmem_deref(callid);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\tif (test.err) {\n\t\terr = test.err;\n\t\tTEST_ERR(err);\n\t}\n\n\tTEST_ERR(test.progr_ret_code);\n\tASSERT_TRUE(test.answ_ret_code == EAGAIN);\n\n\t/* okay here -- verify */\n\tASSERT_TRUE(test.estab_a);\n\tASSERT_TRUE(test.estab_b);\n\tASSERT_TRUE(test.progr_a);\n\tASSERT_TRUE(test.rel100_state_b & REL100_SUPPORTED);\n\tASSERT_TRUE((test.rel100_state_b & REL100_REQUIRE) == 0);\n\nout:\n\ttmr_cancel(&test.ans_tmr);\n\ttest.a = mem_deref(test.a);\n\ttest.b = mem_deref(test.b);\n\n\tsipsess_close_all(test.sock);\n\ttest.sock = mem_deref(test.sock);\n\n\tsip_close(test.sip, false);\n\ttest.sip = mem_deref(test.sip);\n\n\treturn err;\n}\n\n\nint test_sipsess_100rel_420(void)\n{\n\tstruct test test;\n\tstruct sa laddr;\n\tchar to_uri[256];\n\tint err;\n\tuint16_t port;\n\tchar *callid;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.rel100_a = REL100_REQUIRED;\n\ttest.rel100_b = REL100_DISABLED;\n\ttest.conn_action = CONN_PROGRESS;\n\ttest.progr_ret_code = -1;\n\n\terr = sip_alloc(&test.sip, NULL, 32, 32, 32,\n\t\t\t\"retest\", exit_handler, NULL);\n\tTEST_ERR(err);\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\terr = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL);\n\tTEST_ERR(err);\n\n\tport = sa_port(&laddr);\n\n\terr = sipsess_listen(&test.sock, test.sip, 32, conn_handler,\n\t\t\t     &test);\n\tTEST_ERR(err);\n\n\terr = str_x64dup(&callid, rand_u64());\n\tTEST_ERR(err);\n\n\t/* Connect to \"b\" */\n\t(void)re_snprintf(to_uri, sizeof(to_uri), \"sip:b@127.0.0.1:%u\", port);\n\terr = sipsess_connect(&test.a, test.sock, to_uri, NULL,\n\t\t\t      \"sip:a@127.0.0.1\", \"a\", NULL, 0,\n\t\t\t      \"application/sdp\", NULL, NULL, false,\n\t\t\t      callid, desc_handler,\n\t\t\t      offer_handler_a, answer_handler_a, NULL,\n\t\t\t      estab_handler_a, NULL, NULL,\n\t\t\t      close_handler, &test,\n\t\t\t      \"Require: 100rel\\r\\n\");\n\tmem_deref(callid);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\tASSERT_TRUE(test.err == EPROTO);\n\n\t/* okay here -- verify */\n\tASSERT_TRUE(!test.b);\n\tASSERT_TRUE(!test.estab_a);\n\tASSERT_TRUE(!test.estab_b);\n\tASSERT_TRUE(test.desc);\n\nout:\n\ttmr_cancel(&test.ans_tmr);\n\ttest.a = mem_deref(test.a);\n\ttest.b = mem_deref(test.b);\n\n\tsipsess_close_all(test.sock);\n\ttest.sock = mem_deref(test.sock);\n\n\tsip_close(test.sip, false);\n\ttest.sip = mem_deref(test.sip);\n\n\treturn err;\n}\n\n\nint test_sipsess_100rel_421(void)\n{\n\tstruct test test;\n\tstruct sa laddr;\n\tchar to_uri[256];\n\tint err;\n\tuint16_t port;\n\tchar *callid;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.rel100_a = REL100_DISABLED;\n\ttest.rel100_b = REL100_REQUIRED;\n\ttest.conn_action = CONN_PROGRESS;\n\ttest.progr_ret_code = -1;\n\n\terr = sip_alloc(&test.sip, NULL, 32, 32, 32,\n\t\t\t\"retest\", exit_handler, NULL);\n\tTEST_ERR(err);\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\terr = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL);\n\tTEST_ERR(err);\n\n\tport = sa_port(&laddr);\n\n\terr = sipsess_listen(&test.sock, test.sip, 32, conn_handler,\n\t\t\t     &test);\n\tTEST_ERR(err);\n\n\terr = str_x64dup(&callid, rand_u64());\n\tTEST_ERR(err);\n\n\t/* Connect to \"b\" */\n\t(void)re_snprintf(to_uri, sizeof(to_uri), \"sip:b@127.0.0.1:%u\", port);\n\terr = sipsess_connect(&test.a, test.sock, to_uri, NULL,\n\t\t\t      \"sip:a@127.0.0.1\", \"a\", NULL, 0,\n\t\t\t      \"application/sdp\", NULL, NULL, false,\n\t\t\t      callid, desc_handler,\n\t\t\t      offer_handler_a, answer_handler_a, NULL,\n\t\t\t      estab_handler_a, NULL, NULL,\n\t\t\t      close_handler, &test, NULL);\n\tmem_deref(callid);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\tASSERT_TRUE(test.err == EPROTO);\n\n\t/* okay here -- verify */\n\tASSERT_TRUE(!test.b);\n\tASSERT_TRUE(!test.estab_a);\n\tASSERT_TRUE(!test.estab_b);\n\tASSERT_TRUE(test.desc);\n\nout:\n\ttmr_cancel(&test.ans_tmr);\n\ttest.a = mem_deref(test.a);\n\ttest.b = mem_deref(test.b);\n\n\tsipsess_close_all(test.sock);\n\ttest.sock = mem_deref(test.sock);\n\n\tsip_close(test.sip, false);\n\ttest.sip = mem_deref(test.sip);\n\n\treturn err;\n}\n\n\nint test_sipsess_update_uac(void)\n{\n\tstruct test test;\n\tstruct sa laddr;\n\tchar to_uri[256];\n\tstruct mbuf *desc_a = NULL;\n\tint err;\n\tuint16_t port;\n\tchar *callid;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.rel100_a = REL100_ENABLED;\n\ttest.rel100_b = REL100_ENABLED;\n\ttest.conn_action = CONN_PROGRESS;\n\ttest.prack_action = send_update_a;\n\n\terr = sip_alloc(&test.sip, NULL, 32, 32, 32,\n\t\t\t\"retest\", exit_handler, NULL);\n\tTEST_ERR(err);\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\terr = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL);\n\tTEST_ERR(err);\n\n\tport = sa_port(&laddr);\n\n\terr = sipsess_listen(&test.sock, test.sip, 32, conn_handler,\n\t\t\t     &test);\n\tTEST_ERR(err);\n\n\terr = str_x64dup(&callid, rand_u64());\n\tTEST_ERR(err);\n\n\t/* Connect to \"b\" */\n\t(void)re_snprintf(to_uri, sizeof(to_uri), \"sip:b@127.0.0.1:%u\", port);\n\terr = sipsess_connect(&test.a, test.sock, to_uri, NULL,\n\t\t\t      \"sip:a@127.0.0.1\", \"a\", NULL, 0,\n\t\t\t      \"application/sdp\", NULL, NULL, false,\n\t\t\t      callid, desc_handler_a,\n\t\t\t      offer_handler_a, answer_handler_a,\n\t\t\t      progr_handler_a, estab_handler_a, NULL,\n\t\t\t      NULL, close_handler, &test,\n\t\t\t      \"Supported: 100rel\\r\\n\");\n\tmem_deref(callid);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\tif (test.err) {\n\t\terr = test.err;\n\t\tTEST_ERR(err);\n\t}\n\n\t/* okay here -- verify */\n\tASSERT_TRUE(test.estab_a);\n\tASSERT_TRUE(test.estab_b);\n\tASSERT_TRUE(test.answr_a);\n\tASSERT_TRUE(!test.answr_b);\n\tASSERT_TRUE(!test.offer_a);\n\tASSERT_TRUE(test.offer_b);\n\tASSERT_TRUE(test.progr_a);\n\tASSERT_TRUE(test.upd_b);\n\tASSERT_TRUE(!test.upd_a);\n\nout:\n\ttmr_cancel(&test.ans_tmr);\n\ttest.a = mem_deref(test.a);\n\ttest.b = mem_deref(test.b);\n\n\tsipsess_close_all(test.sock);\n\ttest.sock = mem_deref(test.sock);\n\n\tsip_close(test.sip, false);\n\ttest.sip = mem_deref(test.sip);\n\n\tmem_deref(desc_a);\n\tmem_deref(test.desc);\n\n\treturn err;\n}\n\n\nint test_sipsess_update_uas(void)\n{\n\tstruct test test;\n\tstruct sa laddr;\n\tchar to_uri[256];\n\tstruct mbuf *desc_a = NULL;\n\tint err;\n\tuint16_t port;\n\tchar *callid;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.rel100_a = REL100_ENABLED;\n\ttest.rel100_b = REL100_ENABLED;\n\ttest.conn_action = CONN_PROGRESS;\n\ttest.prack_action = send_update_b;\n\n\terr = sip_alloc(&test.sip, NULL, 32, 32, 32,\n\t\t\t\"retest\", exit_handler, NULL);\n\tTEST_ERR(err);\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\terr = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL);\n\tTEST_ERR(err);\n\n\tport = sa_port(&laddr);\n\n\terr = sipsess_listen(&test.sock, test.sip, 32, conn_handler,\n\t\t\t     &test);\n\tTEST_ERR(err);\n\n\terr = str_x64dup(&callid, rand_u64());\n\tTEST_ERR(err);\n\n\t/* Connect to \"b\" */\n\t(void)re_snprintf(to_uri, sizeof(to_uri), \"sip:b@127.0.0.1:%u\", port);\n\terr = sipsess_connect(&test.a, test.sock, to_uri, NULL,\n\t\t\t      \"sip:a@127.0.0.1\", \"a\", NULL, 0,\n\t\t\t      \"application/sdp\", NULL, NULL, false,\n\t\t\t      callid, desc_handler_a,\n\t\t\t      offer_handler_a, answer_handler_a,\n\t\t\t      progr_handler_a, estab_handler_a, NULL,\n\t\t\t      NULL, close_handler, &test,\n\t\t\t      \"Supported: 100rel\\r\\n\");\n\tmem_deref(callid);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\tif (test.err) {\n\t\terr = test.err;\n\t\tTEST_ERR(err);\n\t}\n\n\t/* okay here -- verify */\n\tASSERT_TRUE(test.estab_a);\n\tASSERT_TRUE(test.estab_b);\n\tASSERT_TRUE(test.answr_a);\n\tASSERT_TRUE(test.answr_b);\n\tASSERT_TRUE(test.offer_a);\n\tASSERT_TRUE(test.offer_b);\n\tASSERT_TRUE(test.progr_a);\n\tASSERT_TRUE(test.upd_a);\n\tASSERT_TRUE(!test.upd_b);\n\nout:\n\ttmr_cancel(&test.ans_tmr);\n\ttest.a = mem_deref(test.a);\n\ttest.b = mem_deref(test.b);\n\n\tsipsess_close_all(test.sock);\n\ttest.sock = mem_deref(test.sock);\n\n\tsip_close(test.sip, false);\n\ttest.sip = mem_deref(test.sip);\n\n\tmem_deref(desc_a);\n\tmem_deref(test.desc);\n\n\treturn err;\n}\n\n\nint test_sipsess_update_no_sdp(void)\n{\n\tstruct test test;\n\tstruct sa laddr;\n\tchar to_uri[256];\n\tstruct mbuf *desc_a = NULL;\n\tint err;\n\tuint16_t port;\n\tchar *callid;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.rel100_a = REL100_DISABLED;\n\ttest.rel100_b = REL100_DISABLED;\n\ttest.conn_action = CONN_PROGR_UPD;\n\n\terr = sip_alloc(&test.sip, NULL, 32, 32, 32,\n\t\t\t\"retest\", exit_handler, NULL);\n\tTEST_ERR(err);\n\n\t(void)sa_set_str(&laddr, \"127.0.0.1\", 0);\n\terr = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr);\n\tTEST_ERR(err);\n\n\terr = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL);\n\tTEST_ERR(err);\n\n\tport = sa_port(&laddr);\n\n\terr = sipsess_listen(&test.sock, test.sip, 32, conn_handler,\n\t\t\t     &test);\n\tTEST_ERR(err);\n\n\terr = str_x64dup(&callid, rand_u64());\n\tTEST_ERR(err);\n\n\t/* Connect to \"b\" */\n\t(void)re_snprintf(to_uri, sizeof(to_uri), \"sip:b@127.0.0.1:%u\", port);\n\terr = sipsess_connect(&test.a, test.sock, to_uri, NULL,\n\t\t\t      \"sip:a@127.0.0.1\", \"a\", NULL, 0,\n\t\t\t      \"application/sdp\", NULL, NULL, false,\n\t\t\t      callid, desc_handler_a,\n\t\t\t      offer_handler_a, answer_handler_a,\n\t\t\t      progr_handler_a, estab_handler_a, NULL,\n\t\t\t      NULL, close_handler, &test, NULL);\n\tmem_deref(callid);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\tif (test.err) {\n\t\terr = test.err;\n\t\tTEST_ERR(err);\n\t}\n\n\t/* okay here -- verify */\n\tASSERT_TRUE(test.estab_a);\n\tASSERT_TRUE(test.estab_b);\n\tASSERT_TRUE(test.answr_a);\n\tASSERT_TRUE(test.answr_b);\n\tASSERT_TRUE(!test.offer_a);\n\tASSERT_TRUE(test.offer_b);\n\tASSERT_TRUE(test.progr_a);\n\nout:\n\ttmr_cancel(&test.ans_tmr);\n\ttest.a = mem_deref(test.a);\n\ttest.b = mem_deref(test.b);\n\n\tsipsess_close_all(test.sock);\n\ttest.sock = mem_deref(test.sock);\n\n\tsip_close(test.sip, false);\n\ttest.sip = mem_deref(test.sip);\n\n\tmem_deref(desc_a);\n\tmem_deref(test.desc);\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/srtp.c",
    "content": "/**\n * @file srtp.c SRTP Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"srtptest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define SSRC 0x31323334\n\n\nenum {\n\tSALT_LEN_CTR = 14\n};\n\n\nstatic const uint8_t fixed_payload[20] = {\n\t0x55, 0x55, 0x55, 0x55,\n\t0x11, 0x11, 0x11, 0x11,\n\t0xee, 0xee, 0xee, 0xee,\n\t0x11, 0x11, 0x11, 0x11,\n\t0x55, 0x55, 0x55, 0x55,\n};\n\n\nstatic size_t get_keylen(enum srtp_suite suite)\n{\n\tswitch (suite) {\n\n\tcase SRTP_AES_CM_128_HMAC_SHA1_32: return 16;\n\tcase SRTP_AES_CM_128_HMAC_SHA1_80: return 16;\n\tcase SRTP_AES_256_CM_HMAC_SHA1_32: return 32;\n\tcase SRTP_AES_256_CM_HMAC_SHA1_80: return 32;\n\tcase SRTP_AES_128_GCM:             return 16;\n\tcase SRTP_AES_256_GCM:             return 32;\n\tdefault: return 0;\n\t}\n}\n\n\nstatic size_t get_saltlen(enum srtp_suite suite)\n{\n\tswitch (suite) {\n\n\tcase SRTP_AES_CM_128_HMAC_SHA1_32: return 14;\n\tcase SRTP_AES_CM_128_HMAC_SHA1_80: return 14;\n\tcase SRTP_AES_256_CM_HMAC_SHA1_32: return 14;\n\tcase SRTP_AES_256_CM_HMAC_SHA1_80: return 14;\n\tcase SRTP_AES_128_GCM:             return 12;\n\tcase SRTP_AES_256_GCM:             return 12;\n\tdefault: return 0;\n\t}\n}\n\n\nstatic size_t get_taglen(enum srtp_suite suite)\n{\n\tswitch (suite) {\n\n\tcase SRTP_AES_CM_128_HMAC_SHA1_32: return 4;\n\tcase SRTP_AES_CM_128_HMAC_SHA1_80: return 10;\n\tcase SRTP_AES_256_CM_HMAC_SHA1_32: return 4;\n\tcase SRTP_AES_256_CM_HMAC_SHA1_80: return 10;\n\tcase SRTP_AES_128_GCM:             return 16;\n\tcase SRTP_AES_256_GCM:             return 16;\n\tdefault: return 0;\n\t}\n}\n\n\n/*\n * RFC 3711 B.2.  AES-CM Test Vectors\n */\nstatic int test_srtp_aescm128(void)\n{\n\tuint8_t k_e[16], iv[16];\n\tstruct aes *aes = NULL;\n\tuint8_t keystream[16], nulldata[16];\n\tsize_t i;\n\tint err = 0;\n\tstatic const struct {\n\t\tconst char *counter;\n\t\tconst char *keystream;\n\t} testv[] = {\n\t\t{\"F0F1F2F3F4F5F6F7F8F9FAFBFCFD0000\",\n\t\t \"E03EAD0935C95E80E166B16DD92B4EB4\"},\n\n\t\t{\"F0F1F2F3F4F5F6F7F8F9FAFBFCFD0001\",\n\t\t \"D23513162B02D0F72A43A2FE4A5F97AB\"},\n\n\t\t{\"F0F1F2F3F4F5F6F7F8F9FAFBFCFD0002\",\n\t\t \"41E95B3BB0A2E8DD477901E4FCA894C0\"},\n\t};\n\n\tmemset(nulldata, 0, sizeof(nulldata));\n\n\terr |= str_hex(k_e, sizeof(k_e), \"2B7E151628AED2A6ABF7158809CF4F3C\");\n\terr |= str_hex(iv,  sizeof(iv),  \"F0F1F2F3F4F5F6F7F8F9FAFBFCFD0000\");\n\tif (err)\n\t\treturn err;\n\n\terr = aes_alloc(&aes, AES_MODE_CTR, k_e, 128, iv);\n\tif (err)\n\t\treturn err;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tuint8_t t_count[16], t_kstrm[16];\n\n\t\terr |= str_hex(t_count, sizeof(t_count), testv[i].counter);\n\t\terr |= str_hex(t_kstrm, sizeof(t_kstrm), testv[i].keystream);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = aes_encr(aes, keystream, nulldata, sizeof(nulldata));\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_MEMCMP(t_kstrm, sizeof(t_kstrm),\n\t\t\t    keystream, sizeof(keystream));\n\t}\n\n out:\n\tmem_deref(aes);\n\treturn err;\n}\n\n\n/*\n * RFC 6188 7.1.  AES-256-CM Test Cases\n */\nstatic int test_srtp_aescm256(void)\n{\n\tuint8_t k_e[32], iv[16];\n\tstruct aes *aes = NULL;\n\tuint8_t keystream[16], nulldata[16];\n\tsize_t i;\n\tint err = 0;\n\tstatic const struct {\n\t\tconst char *counter;\n\t\tconst char *keystream;\n\t} testv[] = {\n\t\t{\"F0F1F2F3F4F5F6F7F8F9FAFBFCFD0000\",\n\t\t \"92bdd28a93c3f52511c677d08b5515a4\"},\n\n\t\t{\"F0F1F2F3F4F5F6F7F8F9FAFBFCFD0001\",\n\t\t \"9da71b2378a854f67050756ded165bac\"},\n\n\t\t{\"F0F1F2F3F4F5F6F7F8F9FAFBFCFD0002\",\n\t\t \"63c4868b7096d88421b563b8c94c9a31\"},\n\t};\n\n\tmemset(nulldata, 0, sizeof(nulldata));\n\n\terr |= str_hex(k_e, sizeof(k_e),\n\t\t       \"57f82fe3613fd170a85ec93c40b1f092\"\n\t\t       \"2ec4cb0dc025b58272147cc438944a98\");\n\terr |= str_hex(iv, sizeof(iv),\n\t\t       \"F0F1F2F3F4F5F6F7F8F9FAFBFCFD0000\");\n\tif (err)\n\t\treturn err;\n\n\terr = aes_alloc(&aes, AES_MODE_CTR, k_e, 256, iv);\n\tif (err)\n\t\treturn err;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tuint8_t t_keystream[16];\n\n\t\terr |= str_hex(t_keystream, sizeof(t_keystream),\n\t\t\t       testv[i].keystream);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = aes_encr(aes, keystream, nulldata, sizeof(nulldata));\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_MEMCMP(t_keystream, sizeof(t_keystream),\n\t\t\t    keystream, sizeof(keystream));\n\t}\n\n out:\n\tmem_deref(aes);\n\treturn err;\n}\n\n\n#if 0\n/*\n * RFC 3711, B.3.  Key Derivation Test Vectors\n */\nstatic int test_srtp_keys(void)\n{\n\tuint8_t key[16], salt[14];\n\tuint8_t k_e[16], k_s[14], k_a[20];\n\tuint8_t k_e_ref[16], k_s_ref[14], k_a_ref[20];\n\tint err = 0;\n\n\terr |= str_hex(key,  sizeof(key),  \"E1F97A0D3E018BE0D64FA32C06DE4139\");\n\terr |= str_hex(salt, sizeof(salt), \"0EC675AD498AFEEBB6960B3AABE6\");\n\tif (err)\n\t\treturn err;\n\n\terr |= str_hex(k_e_ref, 16, \"C61E7A93744F39EE10734AFE3FF7A087\");\n\terr |= str_hex(k_s_ref, 14, \"30CBBC08863D8C85D49DB34A9AE1\");\n\terr |= str_hex(k_a_ref, 20,\n\t\t       \"CEBE321F6FF7716B6FD4AB49AF256A156D38BAA4\");\n\tif (err)\n\t\treturn err;\n\n\terr |= srtp_derive_key(k_e, 16, 0x00, key, sizeof(key),\n\t\t\t       salt, sizeof(salt));\n\terr |= srtp_derive_key(k_a, 20, 0x01, key, sizeof(key),\n\t\t\t       salt, sizeof(salt));\n\terr |= srtp_derive_key(k_s, 14, 0x02, key, sizeof(key),\n\t\t\t       salt, sizeof(salt));\n\tif (err)\n\t\treturn err;\n\n\tTEST_MEMCMP(k_e_ref, sizeof(k_e_ref), k_e, 16);\n\tTEST_MEMCMP(k_a_ref, sizeof(k_a_ref), k_a, 20);\n\tTEST_MEMCMP(k_s_ref, sizeof(k_s_ref), k_s, 14);\n\n out:\n\treturn err;\n}\n\n\n/*\n * RFC 6188, 7.2.  AES_256_CM_PRF Test Cases\n */\nstatic int test_srtp_keys256(void)\n{\n\tuint8_t key[32], salt[14];\n\tuint8_t k_e[32], k_s[14], k_a[20];\n\tuint8_t k_e_ref[32], k_s_ref[14], k_a_ref[20];\n\tint err = 0;\n\n\terr |= str_hex(key, sizeof(key),\n\t\t       \"f0f04914b513f2763a1b1fa130f10e29\"\n\t\t       \"98f6f6e43e4309d1e622a0e332b9f1b6\");\n\terr |= str_hex(salt, sizeof(salt),\n\t\t       \"3b04803de51ee7c96423ab5b78d2\");\n\tif (err)\n\t\treturn err;\n\n\terr |= str_hex(k_e_ref, 32,\n\t\t       \"5ba1064e30ec51613cad926c5a28ef73\"\n\t\t       \"1ec7fb397f70a960653caf06554cd8c4\");\n\terr |= str_hex(k_s_ref, 14, \"fa31791685ca444a9e07c6c64e93\");\n\terr |= str_hex(k_a_ref, 20,\n\t\t       \"fd9c32d39ed5fbb5a9dc96b30818454d1313dc05\");\n\tif (err)\n\t\treturn err;\n\n\t/* verify cipher key */\n\terr |= srtp_derive_key(k_e, 32, 0x00, key, sizeof(key),\n\t\t\t       salt, sizeof(salt));\n\tTEST_MEMCMP(k_e_ref, sizeof(k_e_ref), k_e, sizeof(k_e));\n\n\t/* verify auth key */\n\terr |= srtp_derive_key(k_a, 20, 0x01, key, sizeof(key),\n\t\t\t       salt, sizeof(salt));\n\tTEST_MEMCMP(k_a_ref, sizeof(k_a_ref), k_a, sizeof(k_a));\n\n\t/* verify cipher salt */\n\terr |= srtp_derive_key(k_s, 14, 0x02, key, sizeof(key),\n\t\t\t       salt, sizeof(salt));\n\tTEST_MEMCMP(k_s_ref, sizeof(k_s_ref), k_s, sizeof(k_s));\n\n out:\n\treturn err;\n}\n\n\nstatic int test_srtcp_keys(void)\n{\n\tuint8_t key[16], salt[14];\n\tuint8_t k_e[16], k_e_ref[16];\n\tuint8_t k_a[20], k_a_ref[20];\n\tuint8_t k_s[14], k_s_ref[14];\n\tint err = 0;\n\n\terr |= str_hex(key,  sizeof(key),  \"E1F97A0D3E018BE0D64FA32C06DE4139\");\n\terr |= str_hex(salt, sizeof(salt), \"0EC675AD498AFEEBB6960B3AABE6\");\n\tif (err)\n\t\treturn err;\n\n\terr |= str_hex(k_e_ref, 16,\"4c1aa45a81f73d61c800bbb00fbb1eaa\");\n\terr |= str_hex(k_a_ref, 20,\"8d54534feb49ae8e7993a6bd0b844fc323a93dfd\");\n\terr |= str_hex(k_s_ref, 14,\"9581c7ad87b3e530bf3e4454a8b3\");\n\tif (err)\n\t\treturn err;\n\n\terr |= srtp_derive_key(k_e, 16, 0x03, key, sizeof(key),\n\t\t\t       salt, sizeof(salt));\n\terr |= srtp_derive_key(k_a, 20, 0x04, key, sizeof(key),\n\t\t\t       salt, sizeof(salt));\n\terr |= srtp_derive_key(k_s, 14, 0x05, key, sizeof(key),\n\t\t\t       salt, sizeof(salt));\n\n\tTEST_MEMCMP(k_e_ref, sizeof(k_e_ref), k_e, 16);\n\tTEST_MEMCMP(k_a_ref, sizeof(k_a_ref), k_a, 20);\n\tTEST_MEMCMP(k_s_ref, sizeof(k_s_ref), k_s, 14);\n\n out:\n\treturn err;\n}\n#endif\n\n\nstatic int test_srtp_loop(size_t offset, enum srtp_suite suite, uint16_t seq)\n{\n\tstruct srtp *ctx_tx = NULL, *ctx_rx = NULL;\n\tstruct mbuf *mb = NULL;\n\tconst size_t key_len = get_keylen(suite);\n\tconst size_t salt_len = get_saltlen(suite);\n\tconst size_t tag_len = get_taglen(suite);\n\tunsigned i;\n\tint err = 0;\n\n\tstatic const uint8_t master_key[16+16+14] = {\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t};\n\n\tmb = mbuf_alloc(offset + 32);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr  = srtp_alloc(&ctx_tx, suite, master_key, key_len + salt_len, 0);\n\terr |= srtp_alloc(&ctx_rx, suite, master_key, key_len + salt_len, 0);\n\tif (err)\n\t\tgoto out;\n\n\tfor (i=0; i<10; i++) {\n\t\tstruct rtp_header hdr;\n\t\tuint8_t hdrbuf[12];\n\t\tsize_t end;\n\n\t\tmb->pos = mb->end = offset;\n\n\t\tmemset(&hdr, 0, sizeof(hdr));\n\n\t\thdr.ver  = RTP_VERSION;\n\t\thdr.seq  = seq++;\n\t\thdr.ssrc = SSRC;\n\n\t\terr = rtp_hdr_encode(mb, &hdr);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tmemcpy(hdrbuf, &mb->buf[mb->pos-12], 12);\n\n\t\terr = mbuf_write_mem(mb, fixed_payload, sizeof(fixed_payload));\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tend = mb->end;\n\n\t\t/* tx */\n\t\tmb->pos = offset;\n\t\terr = srtp_encrypt(ctx_tx, mb);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_EQUALS(offset, mb->pos);\n\t\tTEST_EQUALS(end + tag_len, mb->end);\n\n\t\t/* verify that srtp_encrypt() did not tamper with RTP header */\n\t\tTEST_MEMCMP(hdrbuf, sizeof(hdrbuf), &mb->buf[offset], 12);\n\n\t\t/* rx */\n\t\tmb->pos = offset;\n\t\terr = srtp_decrypt(ctx_rx, mb);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"srtp_decrypt: %m\\n\", err);\n\t\t\tbreak;\n\t\t}\n\n\t\tTEST_EQUALS(offset, mb->pos);\n\t\tTEST_EQUALS(end, mb->end);\n\n\t\tmb->pos = offset + RTP_HEADER_SIZE;\n\n\t\tTEST_MEMCMP(fixed_payload, sizeof(fixed_payload),\n\t\t\t    mbuf_buf(mb), mbuf_get_left(mb));\n\t}\n\n out:\n\tmem_deref(ctx_tx);\n\tmem_deref(ctx_rx);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic int test_srtcp_loop(size_t offset, enum srtp_suite suite,\n\t\t\t   enum rtcp_type type)\n{\n\tstruct srtp *ctx_tx = NULL, *ctx_rx = NULL;\n\tstruct mbuf *mb1 = NULL, *mb2 = NULL;\n\tconst size_t key_len = get_keylen(suite);\n\tconst size_t salt_len = get_saltlen(suite);\n\tconst size_t tag_len = get_taglen(suite);\n\tunsigned i;\n\tint err = 0;\n\n\tstatic const uint8_t master_key[16+16+14] = {\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t};\n\n\tmb1 = mbuf_alloc(1024);\n\tmb2 = mbuf_alloc(1024);\n\tif (!mb1 || !mb2) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr  = srtp_alloc(&ctx_tx, suite, master_key, key_len + salt_len, 0);\n\terr |= srtp_alloc(&ctx_rx, suite, master_key, key_len + salt_len, 0);\n\tif (err)\n\t\tgoto out;\n\n\tfor (i=0; i<10; i++) {\n\n\t\tconst uint32_t srcv[2] = {0x12345678, 0x00abcdef};\n\t\tsize_t end;\n\n\t\tmb1->pos = mb1->end = offset;\n\t\tmb2->pos = mb2->end = offset;\n\n\t\tif (type == RTCP_BYE) {\n\t\t\terr = rtcp_encode(mb1, RTCP_BYE, 2, srcv, \"ciao\");\n\t\t}\n\t\telse if (type == RTCP_RR) {\n\t\t\terr = rtcp_encode(mb1, RTCP_RR, 0, srcv[0],\n\t\t\t\t\t  NULL, NULL);\n\t\t}\n\t\telse {\n\t\t\tre_printf(\"unknown type %d\\n\", type);\n\t\t\terr = EINVAL;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tend = mb1->end;\n\n\t\tmb1->pos = offset;\n\t\t(void)mbuf_write_mem(mb2, mbuf_buf(mb1), mbuf_get_left(mb1));\n\t\tmb2->pos = offset;\n\n\t\t/* tx */\n\t\tmb1->pos = offset;\n\t\terr = srtcp_encrypt(ctx_tx, mb1);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_EQUALS(offset, mb1->pos);\n\t\tTEST_ASSERT(mb1->end != end);\n\t\tTEST_EQUALS((mbuf_get_left(mb2) + 4 + tag_len),\n\t\t\t    mbuf_get_left(mb1));\n\n\t\t/* rx */\n\t\tmb1->pos = offset;\n\t\terr = srtcp_decrypt(ctx_rx, mb1);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_EQUALS(offset, mb1->pos);\n\t\tTEST_EQUALS(end, mb1->end);\n\t\tTEST_MEMCMP(mbuf_buf(mb2), mbuf_get_left(mb2),\n\t\t\t    mbuf_buf(mb1), mbuf_get_left(mb1));\n\t}\n\n out:\n\tmem_deref(ctx_tx);\n\tmem_deref(ctx_rx);\n\tmem_deref(mb1);\n\tmem_deref(mb2);\n\n\treturn err;\n}\n\n\n/*\n * Reference SRTP-packet generated by libsrtp\n *\n * cipher:       AES-CM-128\n * auth:         HMAC-SHA1 80-bits tag\n * master key:   0x22222222222222222222222222222222\n * master salt:  0x4444444444444444444444444444\n * SSRC:         0x01020304\n * Seq:          0x0001\n * RTP payload:  0xa5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5\n */\nstatic const char *srtp_libsrtp =\n\t\"800000010000000001020304\"\n\t\"f5b44b7e3ad4eb057bc6480c45df6547bb70bcc2\"\n\t\"7b136e1f3d3a62821b15\";\n\n\nstatic int test_srtp_libsrtp(void)\n{\n\tuint8_t pkt[12+20+10];\n\tstruct srtp *srtp_enc = NULL;\n\tre_nonstring static const uint8_t mast_key[16+14] =\n\t\t\"\\x22\\x22\\x22\\x22\\x22\\x22\\x22\\x22\"\n\t\t\"\\x22\\x22\\x22\\x22\\x22\\x22\\x22\\x22\"\n\t\t\"\\x44\\x44\\x44\\x44\\x44\\x44\\x44\"\n\t\t\"\\x44\\x44\\x44\\x44\\x44\\x44\\x44\";\n\tre_nonstring static const uint8_t rtp_payload[20] =\n\t\t\"\\xa5\\xa5\\xa5\\xa5\\xa5\\xa5\\xa5\\xa5\\xa5\\xa5\"\n\t\t\"\\xa5\\xa5\\xa5\\xa5\\xa5\\xa5\\xa5\\xa5\\xa5\\xa5\";\n\tstruct mbuf *mb;\n\tstruct rtp_header hdr;\n\tint err = 0;\n\n\tmemset(&hdr, 0, sizeof(hdr));\n\thdr.ver  = RTP_VERSION;\n\thdr.ssrc = 0x01020304;\n\thdr.seq  = 0x0001;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = str_hex(pkt, sizeof(pkt), srtp_libsrtp);\n\tif (err)\n\t\tgoto out;\n\n\terr = srtp_alloc(&srtp_enc, SRTP_AES_CM_128_HMAC_SHA1_80,\n\t\t\t mast_key, sizeof(mast_key), 0);\n\tif (err)\n\t\tgoto out;\n\n\terr  = rtp_hdr_encode(mb, &hdr);\n\terr |= mbuf_write_mem(mb, rtp_payload, sizeof(rtp_payload));\n\tif (err)\n\t\tgoto out;\n\tmb->pos = 0;\n\n\terr = srtp_encrypt(srtp_enc, mb);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_MEMCMP(pkt, sizeof(pkt), mb->buf, mb->end);\n\n out:\n\tmem_deref(srtp_enc);\n\tmem_deref(mb);\n\treturn err;\n}\n\n\n/*\n * Reference SRTCP-packet generated by libsrtp\n *\n * cipher:       AES-CM-128\n * auth:         HMAC-SHA1 32-bits tag\n * master key:   0x22222222222222222222222222222222\n * master salt:  0x4444444444444444444444444444\n * SSRC:         0x01020304\n * RTCP packet:  BYE-message\n */\nstatic const char *srtcp_libsrtp =\n\t\"81cb00020102030487c9fcdb80000001e9442fcc\";\n/*                               ^^^^^^^^________\n *                                index    tag\n */\n\n\nstatic int test_srtcp_libsrtp(void)\n{\n\tuint8_t pkt[12+4+4];\n\tstruct srtp *srtp_enc = NULL;\n\tre_nonstring static const uint8_t mast_key[16+14] =\n\t\t\"\\x22\\x22\\x22\\x22\\x22\\x22\\x22\\x22\"\n\t\t\"\\x22\\x22\\x22\\x22\\x22\\x22\\x22\\x22\"\n\t\t\"\\x44\\x44\\x44\\x44\\x44\\x44\\x44\"\n\t\t\"\\x44\\x44\\x44\\x44\\x44\\x44\\x44\";\n\tconst uint32_t srcv[1] = {0x01020304};\n\tstruct mbuf *mb;\n\tint err = 0;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = str_hex(pkt, sizeof(pkt), srtcp_libsrtp);\n\tif (err)\n\t\tgoto out;\n\n\terr = srtp_alloc(&srtp_enc, SRTP_AES_CM_128_HMAC_SHA1_32,\n\t\t\t mast_key, sizeof(mast_key), 0);\n\tif (err)\n\t\tgoto out;\n\n\terr = rtcp_encode(mb, RTCP_BYE, 1, srcv, \"b\");\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\n\terr = srtcp_encrypt(srtp_enc, mb);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_MEMCMP(pkt, sizeof(pkt), mb->buf, mb->end);\n\n out:\n\tmem_deref(srtp_enc);\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstatic int send_rtp_packet(struct srtp *srtp, struct mbuf *mb, uint16_t seq)\n{\n\tstruct rtp_header hdr;\n\tsize_t len;\n\tint err;\n\n\tmemset(&hdr, 0, sizeof(hdr));\n\n\thdr.ver  = RTP_VERSION;\n\thdr.seq  = seq;\n\thdr.ssrc = SSRC;\n\n\tmb->pos = mb->end = 0;\n\terr  = rtp_hdr_encode(mb, &hdr);\n\terr |= mbuf_write_mem(mb, fixed_payload, sizeof(fixed_payload));\n\tif (err)\n\t\treturn err;\n\n\tlen = mb->end;\n\n\tmb->pos = 0;\n\terr = srtp_encrypt(srtp, mb);\n\tif (err)\n\t\treturn err;\n\n\tTEST_EQUALS(0, mb->pos);\n\tTEST_ASSERT(mb->end > len);\n\n out:\n\treturn err;\n}\n\n\nstatic int recv_srtp_packet(struct srtp *srtp, struct mbuf *mb)\n{\n\tconst size_t len = mb->end;\n\tint err = 0;\n\n\tmb->pos = 0;\n\terr = srtp_decrypt(srtp, mb);\n\tif (err)\n\t\treturn err;\n\n\tTEST_EQUALS(0, mb->pos);\n\tTEST_ASSERT(mb->end < len);\n\tTEST_MEMCMP(fixed_payload, sizeof(fixed_payload),\n\t\t    mb->buf + 12, mb->end - 12);\n out:\n\treturn err;\n}\n\n\nstatic int test_srtp_replay(enum srtp_suite suite)\n{\n\tstatic const uint8_t key[32+14] = {\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t};\n\tstruct srtp *ctx = NULL;\n\tstruct mbuf *mb = NULL;\n\tconst size_t key_len = get_keylen(suite);\n\tconst size_t salt_len = get_saltlen(suite);\n\tint e, err = 0;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr  = srtp_alloc(&ctx, suite, key, key_len + salt_len, 0);\n\tif (err)\n\t\tgoto out;\n\n\t/* send/receive one RTP packet first */\n\terr = send_rtp_packet(ctx, mb, 42);\n\tif (err)\n\t\tgoto out;\n\n\terr = srtp_decrypt(ctx, mb);\n\tif (err)\n\t\tgoto out;\n\n\t/* then send/receive the same packet again,\n\t   expect replay protection */\n\terr = send_rtp_packet(ctx, mb, 42);\n\tif (err)\n\t\tgoto out;\n\n\te = srtp_decrypt(ctx, mb);\n\tTEST_EQUALS(EALREADY, e);\n\n out:\n\tmem_deref(ctx);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic int test_seq_loop(const uint16_t *seqv, size_t seqn)\n{\n\tstatic const uint8_t key[16+14] = {\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t};\n\tstruct srtp *srtp_tx = NULL, *srtp_rx = NULL;\n\tstruct mbuf *mb;\n\tsize_t i;\n\tint err;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\t/* note: we must use two separate SRTP instances here,\n\t   since the SSRC is the same */\n\terr  = srtp_alloc(&srtp_tx, SRTP_AES_CM_128_HMAC_SHA1_32,\n\t\t\t  key, sizeof(key), 0);\n\terr |= srtp_alloc(&srtp_rx, SRTP_AES_CM_128_HMAC_SHA1_32,\n\t\t\t  key, sizeof(key), 0);\n\tif (err)\n\t\tgoto out;\n\n\tfor (i=0; i<seqn; i++) {\n\n\t\terr = send_rtp_packet(srtp_tx, mb, seqv[i]);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\t/* mb now contains the SRTP packet */\n\n\t\terr = recv_srtp_packet(srtp_rx, mb);\n\t\tif (err)\n\t\t\tbreak;\n\t}\n\n out:\n\tmem_deref(srtp_tx);\n\tmem_deref(srtp_rx);\n\tmem_deref(mb);\n\n\t/* for reordered packets that arrive too late */\n\tif (err == ETIMEDOUT)\n\t\terr = 0;\n\n\treturn err;\n}\n\n\nstatic int test_srtp_reordering_and_wrap(void)\n{\n\tstatic const uint16_t seqv1[] = {0, 1, 2};\n\tstatic const uint16_t seqv2[] = {0, 2, 1};\n\tstatic const uint16_t seqv3[] = {65534, 65535, 0, 1};\n\tstatic const uint16_t seqv4[] = {65534, 0, 1};\n\tstatic const uint16_t seqv5[] = {65534, 1, 2};\n\tstatic const uint16_t seqv6[] = {65534, 1, 2, 65535};\n\tint err = 0;\n\n\terr  = test_seq_loop(seqv1, RE_ARRAY_SIZE(seqv1));\n\terr |= test_seq_loop(seqv2, RE_ARRAY_SIZE(seqv2));\n\terr |= test_seq_loop(seqv3, RE_ARRAY_SIZE(seqv3));\n\terr |= test_seq_loop(seqv4, RE_ARRAY_SIZE(seqv4));\n\terr |= test_seq_loop(seqv5, RE_ARRAY_SIZE(seqv5));\n\terr |= test_seq_loop(seqv6, RE_ARRAY_SIZE(seqv6));\n\n\treturn err;\n}\n\n\n/* verify that we dont crash on random input */\nstatic int test_srtp_random(enum srtp_suite suite)\n{\n\tstruct rtp_header hdr;\n\tstruct srtp *ctx = NULL;\n\tstruct mbuf *mb = NULL;\n\tconst size_t key_len = get_keylen(suite);\n\tconst size_t salt_len = get_saltlen(suite);\n\tsize_t sz, i;\n\tint err = 0;\n\n\tstatic const uint8_t master_key[16+16+14] = {\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t};\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr  = srtp_alloc(&ctx, suite, master_key, key_len + salt_len, 0);\n\tif (err)\n\t\tgoto out;\n\n\tmemset(&hdr, 0, sizeof(hdr));\n\n\thdr.ver  = RTP_VERSION;\n\thdr.seq  = 1234;\n\thdr.ssrc = SSRC;\n\n\terr = rtp_hdr_encode(mb, &hdr);\n\tif (err)\n\t\tgoto out;\n\n\terr = mbuf_fill(mb, 0xd5, 32);\n\tif (err)\n\t\tgoto out;\n\n\tsz = mb->end;\n\n\tfor (i=0; i<sz; i++) {\n\n\t\tmb->pos = 0; mb->end = i;\n\t\t(void)srtp_encrypt(ctx, mb);\n\n\t\tmb->pos = 0; mb->end = i;\n\t\t(void)srtp_decrypt(ctx, mb);\n\t}\n\n out:\n\tmem_deref(ctx);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/* verify that we dont crash on random input */\nstatic int test_srtcp_random(enum srtp_suite suite)\n{\n\tstruct srtp *ctx = NULL;\n\tstruct mbuf *mb = NULL;\n\tconst size_t key_len = get_keylen(suite);\n\tconst size_t salt_len = get_saltlen(suite);\n\tconst uint32_t srcv[2] = {0x12345678, 0x00abcdef};\n\tsize_t sz, i;\n\tint err = 0;\n\n\tstatic const uint8_t master_key[16+16+14] = {\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t};\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr  = srtp_alloc(&ctx, suite, master_key, key_len + salt_len, 0);\n\tif (err)\n\t\tgoto out;\n\n\terr = rtcp_encode(mb, RTCP_BYE, 2, srcv, \"ciao\");\n\tif (err)\n\t\tgoto out;\n\n\terr = mbuf_fill(mb, 0xd5, 32);\n\tif (err)\n\t\tgoto out;\n\n\tsz = mb->end;\n\n\tfor (i=0; i<sz; i++) {\n\n\t\tmb->pos = 0;\n\t\tmb->end = i;\n\t\t(void)srtcp_encrypt(ctx, mb);\n\n\t\tmb->pos = 0;\n\t\tmb->end = i;\n\t\t(void)srtcp_decrypt(ctx, mb);\n\t}\n\n out:\n\tmem_deref(ctx);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic int test_srtp_unauth(enum srtp_suite suite)\n{\n\tstruct srtp *srtp_tx, *srtp_rx;\n\tstruct mbuf *mb = NULL;\n\tconst size_t key_len = get_keylen(suite);\n\tconst size_t salt_len = get_saltlen(suite);\n\tint err = 0;\n\n\tstatic const uint8_t master_key[32 + SALT_LEN_CTR] = {\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t};\n\n\tmb = mbuf_alloc(32);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr  = srtp_alloc(&srtp_tx, suite, master_key, key_len+salt_len, 0);\n\terr |= srtp_alloc(&srtp_rx, suite, master_key, key_len+salt_len, 0);\n\tif (err)\n\t\tgoto out;\n\n\terr = send_rtp_packet(srtp_tx, mb, 3);\n\tif (err)\n\t\tgoto out;\n\n\t/* flip bits in the auth-tag to force authentication error */\n\tmb->buf[mb->end - 1] ^= 0x55;\n\n\terr = recv_srtp_packet(srtp_rx, mb);\n\n\tTEST_EQUALS(EAUTH, err);\n\n\terr = 0;\n\n out:\n\tmem_deref(srtp_tx);\n\tmem_deref(srtp_rx);\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\n/*\n * Special test for Unencrypted SRTCP. This is a special case in\n * SDES, See RFC 4568 section 6.3.2\n */\nstatic int test_unencrypted_srtcp(void)\n{\n\tstruct srtp *srtp = NULL;\n\tstruct mbuf *mb1 = NULL, *mb2 = NULL;\n\tenum srtp_suite suite = SRTP_AES_CM_128_HMAC_SHA1_32;\n\tconst size_t tag_len = get_taglen(suite);\n\tsize_t end;\n\tuint32_t v;\n\tbool ep;\n\tint err = 0;\n\n\tconst uint32_t srcv[2] = {0x12345678, 0x00abcdef};\n\tstatic const uint8_t master_key[16+14] = {\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t\t0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,\n\t};\n\n\tmb1 = mbuf_alloc(1024);\n\tmb2 = mbuf_alloc(1024);\n\tif (!mb1 || !mb2) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr  = srtp_alloc(&srtp, suite, master_key, 16 + SALT_LEN_CTR,\n\t\t\t  SRTP_UNENCRYPTED_SRTCP);\n\tif (err)\n\t\tgoto out;\n\n\terr  = rtcp_encode(mb1, RTCP_BYE, 2, srcv, \"ciao\");\n\terr |= rtcp_encode(mb2, RTCP_BYE, 2, srcv, \"ciao\");\n\tif (err)\n\t\tgoto out;\n\n\tend = mb1->end;\n\n\t/* tx */\n\tmb1->pos = 0;\n\terr = srtcp_encrypt(srtp, mb1);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(0, mb1->pos);\n\n\tmb1->pos = end;\n\tv = ntohl(mbuf_read_u32(mb1));\n\tep = (v >> 31) & 1;\n\n\tmb1->pos = 0;\n\n\t/* verify that RTCP packet is not encrypted */\n\tTEST_ASSERT(ep == false);\n\tTEST_MEMCMP(mb2->buf, mb2->end, mb1->buf, mb1->end - 4 - tag_len);\n\n\t/* rx */\n\terr = srtcp_decrypt(srtp, mb1);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(0, mb1->pos);\n\tTEST_MEMCMP(mb2->buf, mb2->end, mb1->buf, mb1->end);\n\n out:\n\tmem_deref(srtp);\n\tmem_deref(mb1);\n\tmem_deref(mb2);\n\n\treturn err;\n}\n\n\nstatic bool have_srtp(void)\n{\n\tstatic const uint8_t nullkey[30];\n\tstruct srtp *srtp = NULL;\n\tint err;\n\n\terr = srtp_alloc(&srtp, SRTP_AES_CM_128_HMAC_SHA1_32,\n\t\t\t nullkey, sizeof(nullkey), 0);\n\n\tmem_deref(srtp);\n\n\treturn err != ENOSYS;\n}\n\n\n/*\n * test low-level code first, then high-level at the end\n */\nint test_srtp(void)\n{\n\tint err = 0;\n\n\t/* XXX: find a better solution for optional SRTP.\n\t   perhaps only register this test if SRTP is available? */\n\tif (!have_srtp()) {\n\t\t(void)re_printf(\"skipping SRTP test\\n\");\n\t\treturn ESKIPPED;\n\t}\n\n\terr  = test_srtp_aescm128();\n\tTEST_ERR(err);\n\n\terr = test_srtp_aescm256();\n\tTEST_ERR(err);\n\n\terr = test_srtp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_32, 3);\n\tTEST_ERR(err);\n\n\terr = test_srtp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_80, 3);\n\tTEST_ERR(err);\n\n\terr = test_srtp_loop(0, SRTP_AES_256_CM_HMAC_SHA1_32, 3);\n\tTEST_ERR(err);\n\n\terr = test_srtp_loop(0, SRTP_AES_256_CM_HMAC_SHA1_80, 3);\n\tTEST_ERR(err);\n\n\terr = test_srtp_loop(4, SRTP_AES_CM_128_HMAC_SHA1_32, 3);\n\tTEST_ERR(err);\n\n\terr = test_srtp_loop(4, SRTP_AES_CM_128_HMAC_SHA1_80, 3);\n\tTEST_ERR(err);\n\n\terr = test_srtp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_32, 65530);\n\tTEST_ERR(err);\n\n\terr = test_srtp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_80, 65530);\n\tTEST_ERR(err);\n\n\terr  = test_srtp_libsrtp();\n\tTEST_ERR(err);\n\n\terr = test_srtp_replay(SRTP_AES_CM_128_HMAC_SHA1_32);\n\tTEST_ERR(err);\n\n\terr = test_srtp_reordering_and_wrap();\n\tTEST_ERR(err);\n\n\terr = test_srtp_unauth(SRTP_AES_CM_128_HMAC_SHA1_32);\n\tTEST_ERR(err);\n\n\terr = test_srtp_random(SRTP_AES_CM_128_HMAC_SHA1_32);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\nint test_srtcp(void)\n{\n\tint err = 0;\n\n\tif (!have_srtp()) {\n\t\t(void)re_printf(\"skipping SRTCP test\\n\");\n\t\treturn ESKIPPED;\n\t}\n\n\terr = test_srtcp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_32, RTCP_BYE);\n\tTEST_ERR(err);\n\n\terr = test_srtcp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_80, RTCP_BYE);\n\tTEST_ERR(err);\n\n\terr = test_srtcp_loop(0, SRTP_AES_256_CM_HMAC_SHA1_32, RTCP_BYE);\n\tTEST_ERR(err);\n\n\terr = test_srtcp_loop(0, SRTP_AES_256_CM_HMAC_SHA1_80, RTCP_BYE);\n\tTEST_ERR(err);\n\n\terr = test_srtcp_loop(4, SRTP_AES_CM_128_HMAC_SHA1_32, RTCP_BYE);\n\tTEST_ERR(err);\n\n\terr = test_srtcp_loop(4, SRTP_AES_CM_128_HMAC_SHA1_80, RTCP_BYE);\n\tTEST_ERR(err);\n\n\terr = test_srtcp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_32, RTCP_RR);\n\tTEST_ERR(err);\n\n\terr = test_srtcp_libsrtp();\n\tTEST_ERR(err);\n\n\terr = test_unencrypted_srtcp();\n\tTEST_ERR(err);\n\n\terr = test_srtcp_random(SRTP_AES_CM_128_HMAC_SHA1_32);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\nint test_srtp_gcm(void)\n{\n\tint err;\n\n\tif (!have_srtp()) {\n\t\tre_printf(\"skipping SRTP GCM test\\n\");\n\t\treturn ESKIPPED;\n\t}\n\n\terr = test_srtp_loop(0, SRTP_AES_128_GCM, 3);\n\tTEST_ERR(err);\n\n\terr = test_srtp_loop(0, SRTP_AES_256_GCM, 3);\n\tTEST_ERR(err);\n\n\terr = test_srtp_loop(0, SRTP_AES_256_GCM, 65530);\n\tTEST_ERR(err);\n\n\terr = test_srtp_unauth(SRTP_AES_256_GCM);\n\tTEST_ERR(err);\n\n\terr = test_srtp_replay(SRTP_AES_128_GCM);\n\tTEST_ERR(err);\n\n\terr = test_srtp_random(SRTP_AES_128_GCM);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\nint test_srtcp_gcm(void)\n{\n\tint err;\n\n\tif (!have_srtp()) {\n\t\tre_printf(\"skipping SRTCP GCM test\\n\");\n\t\treturn ESKIPPED;\n\t}\n\n\terr = test_srtcp_loop(0, SRTP_AES_128_GCM, RTCP_BYE);\n\tTEST_ERR(err);\n\n\terr = test_srtcp_loop(0, SRTP_AES_256_GCM, RTCP_BYE);\n\tTEST_ERR(err);\n\n\terr = test_srtcp_loop(4, SRTP_AES_128_GCM, RTCP_BYE);\n\tTEST_ERR(err);\n\n\terr = test_srtcp_loop(0, SRTP_AES_128_GCM, RTCP_RR);\n\tTEST_ERR(err);\n\n\terr = test_srtcp_random(SRTP_AES_128_GCM);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/stun.c",
    "content": "/**\n * @file stun.c STUN Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_stun\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define NATTED (true)\n\n\n/*\n * Test vectors from RFC 5769\n */\n\nstatic const uint8_t tid[] =\n\t\"\\xb7\\xe7\\xa7\\x01\\xbc\\x34\\xd6\\x86\\xfa\\x87\\xdf\\xae\";\nstatic const char *username = \"evtj:h6vY\";\nstatic const struct pl password = PL(\"VOkJxbRl1RmTxUk/WvJxBt\");\n\nstatic const unsigned char req[] =\n     \"\\x00\\x01\\x00\\x58\"\n     \"\\x21\\x12\\xa4\\x42\"\n     \"\\xb7\\xe7\\xa7\\x01\\xbc\\x34\\xd6\\x86\\xfa\\x87\\xdf\\xae\"\n     \"\\x80\\x22\\x00\\x10\"\n       \"STUN test client\"\n     \"\\x00\\x24\\x00\\x04\"\n       \"\\x6e\\x00\\x01\\xff\"\n     \"\\x80\\x29\\x00\\x08\"\n       \"\\x93\\x2f\\xf9\\xb1\\x51\\x26\\x3b\\x36\"\n     \"\\x00\\x06\\x00\\x09\"\n       \"\\x65\\x76\\x74\\x6a\\x3a\\x68\\x36\\x76\\x59\\x20\\x20\\x20\"\n     \"\\x00\\x08\\x00\\x14\"\n       \"\\x9a\\xea\\xa7\\x0c\\xbf\\xd8\\xcb\\x56\\x78\\x1e\\xf2\\xb5\"\n       \"\\xb2\\xd3\\xf2\\x49\\xc1\\xb5\\x71\\xa2\"\n     \"\\x80\\x28\\x00\\x04\"\n       \"\\xe5\\x7a\\x3b\\xcf\";\n\nstatic const unsigned char respv4[] =\n     \"\\x01\\x01\\x00\\x3c\"\n     \"\\x21\\x12\\xa4\\x42\"\n     \"\\xb7\\xe7\\xa7\\x01\\xbc\\x34\\xd6\\x86\\xfa\\x87\\xdf\\xae\"\n     \"\\x80\\x22\\x00\\x0b\"\n       \"\\x74\\x65\\x73\\x74\\x20\\x76\\x65\\x63\\x74\\x6f\\x72\\x20\"\n     \"\\x00\\x20\\x00\\x08\"\n       \"\\x00\\x01\\xa1\\x47\\xe1\\x12\\xa6\\x43\"\n     \"\\x00\\x08\\x00\\x14\"\n       \"\\x2b\\x91\\xf5\\x99\\xfd\\x9e\\x90\\xc3\\x8c\\x74\\x89\\xf9\"\n       \"\\x2a\\xf9\\xba\\x53\\xf0\\x6b\\xe7\\xd7\"\n     \"\\x80\\x28\\x00\\x04\"\n       \"\\xc0\\x7d\\x4c\\x96\";\n\nstatic const unsigned char respv6[] =\n     \"\\x01\\x01\\x00\\x48\"\n     \"\\x21\\x12\\xa4\\x42\"\n     \"\\xb7\\xe7\\xa7\\x01\\xbc\\x34\\xd6\\x86\\xfa\\x87\\xdf\\xae\"\n     \"\\x80\\x22\\x00\\x0b\"\n       \"\\x74\\x65\\x73\\x74\\x20\\x76\\x65\\x63\\x74\\x6f\\x72\\x20\"\n     \"\\x00\\x20\\x00\\x14\"\n       \"\\x00\\x02\\xa1\\x47\"\n       \"\\x01\\x13\\xa9\\xfa\\xa5\\xd3\\xf1\\x79\"\n       \"\\xbc\\x25\\xf4\\xb5\\xbe\\xd2\\xb9\\xd9\"\n     \"\\x00\\x08\\x00\\x14\"\n       \"\\xa3\\x82\\x95\\x4e\\x4b\\xe6\\x7b\\xf1\\x17\\x84\\xc9\\x7c\"\n       \"\\x82\\x92\\xc2\\x75\\xbf\\xe3\\xed\\x41\"\n     \"\\x80\\x28\\x00\\x04\"\n       \"\\xc8\\xfb\\x0b\\x4c\";\n\n\nstatic const uint32_t ice_prio = 0x6e0001ff;\nstatic const uint64_t ice_contr = 0x932ff9b151263b36ULL;\nstatic const char *client_sw = \"STUN test client\";\nstatic const char *server_sw = \"test vector\";\n\n\nint test_stun_req(void)\n{\n\tstruct stun_msg *msg = NULL;\n\tstruct mbuf *mb;\n\tstruct stun_attr *attr;\n\tint err;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_REQUEST,\n\t\t\t      tid, NULL,\n\t\t\t      (uint8_t *)password.p, password.l, true,\n\t\t\t      0x20, 4,\n\t\t\t      STUN_ATTR_SOFTWARE, client_sw,\n\t\t\t      STUN_ATTR_PRIORITY, &ice_prio,\n\t\t\t      STUN_ATTR_CONTROLLED, &ice_contr,\n\t\t\t      STUN_ATTR_USERNAME, username);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_MEMCMP(req, sizeof(req)-1, mb->buf, mb->end);\n\n\t/* Decode STUN message */\n\tmb->pos = 0;\n\terr = stun_msg_decode(&msg, mb, NULL);\n\tif (err)\n\t\tgoto out;\n\n\tif (STUN_CLASS_REQUEST != stun_msg_class(msg))\n\t\tgoto bad;\n\n\tif (STUN_METHOD_BINDING != stun_msg_method(msg))\n\t\tgoto out;\n\n\terr = stun_msg_chk_mi(msg, (uint8_t *)password.p, password.l);\n\tif (err)\n\t\tgoto out;\n\n\terr = stun_msg_chk_fingerprint(msg);\n\tif (err)\n\t\tgoto out;\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_PRIORITY);\n\tif (!attr || ice_prio != attr->v.priority)\n\t\tgoto bad;\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_CONTROLLED);\n\tif (!attr || ice_contr != attr->v.controlled)\n\t\tgoto bad;\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_USERNAME);\n\tif (!attr || strcmp(username, attr->v.username))\n\t\tgoto bad;\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_SOFTWARE);\n\tif (!attr || strcmp(client_sw, attr->v.software))\n\t\tgoto bad;\n\n\tgoto out;\n\n bad:\n\terr = EBADMSG;\n\n out:\n\tmem_deref(msg);\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstatic int test_resp(const struct pl *resp, const struct sa *addr)\n{\n\tstruct stun_msg *msg = NULL;\n\tstruct stun_attr *attr;\n\tstruct mbuf *mb = NULL;\n\tint err;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_SUCCESS_RESP,\n\t\t\t      tid, NULL,\n\t\t\t      (uint8_t *)password.p, password.l, true,\n\t\t\t      0x20, 2,\n                              STUN_ATTR_SOFTWARE, server_sw,\n\t\t\t      STUN_ATTR_XOR_MAPPED_ADDR, addr);\n\tif (err)\n\t\tgoto out;\n\n\tif (resp->l != mb->end ||\n\t    0 != memcmp(mb->buf, resp->p, mb->end)) {\n\t\terr = EBADMSG;\n\t\tDEBUG_WARNING(\"compare failed (%J)\\n\", addr);\n\t\t(void)re_printf(\"msg: [%02w]\\n\", mb->buf, mb->end);\n\t\t(void)re_printf(\"ref: [%02w]\\n\", resp->p, resp->l);\n\t\tgoto out;\n\t}\n\n\t/* Decode STUN message */\n\tmb->pos = 0;\n\terr = stun_msg_decode(&msg, mb, NULL);\n\tif (err)\n\t\tgoto out;\n\n\tif (STUN_CLASS_SUCCESS_RESP != stun_msg_class(msg))\n\t\tgoto bad;\n\n\tif (STUN_METHOD_BINDING != stun_msg_method(msg))\n\t\tgoto bad;\n\n\terr = stun_msg_chk_mi(msg, (uint8_t *)password.p, password.l);\n\tif (err)\n\t\tgoto out;\n\n\terr = stun_msg_chk_fingerprint(msg);\n\tif (err)\n\t\tgoto out;\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);\n\tif (!attr || !sa_cmp(&attr->v.xor_mapped_addr, addr, SA_ALL))\n\t\tgoto bad;\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_SOFTWARE);\n\tif (!attr || strcmp(server_sw, attr->v.software))\n\t\tgoto bad;\n\n\tgoto out;\n\n bad:\n\terr = EBADMSG;\n\n out:\n\tmem_deref(msg);\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nint test_stun_resp(void)\n{\n\tstruct sa maddr;\n\tstruct pl resp;\n\tint err;\n\n\tresp.p = (char *)respv4;\n\tresp.l = sizeof(respv4) - 1;\n\terr = sa_set_str(&maddr, \"192.0.2.1\", 32853);\n\tif (err)\n\t\treturn err;\n\terr = test_resp(&resp, &maddr);\n\tif (err)\n\t\treturn err;\n\n\tresp.p = (char *)respv6;\n\tresp.l = sizeof(respv6) - 1;\n\terr = sa_set_str(&maddr, \"2001:db8:1234:5678:11:2233:4455:6677\",\n\t\t\t 32853);\n\tif (err)\n\t\treturn err;\n\terr = test_resp(&resp, &maddr);\n\n\treturn err;\n}\n\n\nstatic const unsigned char reqltc[] =\n     \"\\x00\\x01\\x00\\x60\"\n     \"\\x21\\x12\\xa4\\x42\"\n     \"\\x78\\xad\\x34\\x33\\xc6\\xad\\x72\\xc0\\x29\\xda\\x41\\x2e\"\n     \"\\x00\\x06\\x00\\x12\"\n       \"\\xe3\\x83\\x9e\\xe3\\x83\\x88\\xe3\\x83\\xaa\\xe3\\x83\\x83\"\n       \"\\xe3\\x82\\xaf\\xe3\\x82\\xb9\\x00\\x00\"\n     \"\\x00\\x15\\x00\\x1c\"\n       \"\\x66\\x2f\\x2f\\x34\\x39\\x39\\x6b\\x39\\x35\\x34\\x64\\x36\"\n       \"\\x4f\\x4c\\x33\\x34\\x6f\\x4c\\x39\\x46\\x53\\x54\\x76\\x79\"\n       \"\\x36\\x34\\x73\\x41\"\n     \"\\x00\\x14\\x00\\x0b\"\n       \"\\x65\\x78\\x61\\x6d\\x70\\x6c\\x65\\x2e\\x6f\\x72\\x67\\x00\"\n     \"\\x00\\x08\\x00\\x14\"\n       \"\\xf6\\x70\\x24\\x65\\x6d\\xd6\\x4a\\x3e\\x02\\xb8\\xe0\\x71\"\n       \"\\x2e\\x85\\xc9\\xa2\\x8c\\xa8\\x96\\x66\";\n\nstatic const uint8_t tid_ltc[] =\n\t\"\\x78\\xad\\x34\\x33\\xc6\\xad\\x72\\xc0\\x29\\xda\\x41\\x2e\";\n/* Username:  \"<U+30DE><U+30C8><U+30EA><U+30C3><U+30AF><U+30B9>\"\n   (without quotes) unaffected by SASLprep[RFC4013] processing */\nstatic const char *username_ltc =\n\t\"\\xe3\\x83\\x9e\\xe3\\x83\\x88\\xe3\\x83\"\n\t\"\\xaa\\xe3\\x83\\x83\\xe3\\x82\\xaf\\xe3\"\n\t\"\\x82\\xb9\";\n/* Password:  \"The<U+00AD>M<U+00AA>tr<U+2168>\"\" resp \"TheMatrIX\" (without\n   quotes) before resp after SASLprep processing */\nstatic const char *password_ltc = \"TheMatrIX\";\nstatic const char *nonce_ltc    = \"f//499k954d6OL34oL9FSTvy64sA\";\nstatic const char *realm_ltc    = \"example.org\";\n\n\nint test_stun_reqltc(void)\n{\n\tstruct stun_msg *msg = NULL;\n\tstruct stun_attr *attr;\n\tstruct mbuf *mb;\n\tuint8_t md5_hash[MD5_SIZE];\n\tint r, err;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\t/* use long-term credentials */\n\terr = md5_printf(md5_hash, \"%s:%s:%s\", username_ltc, realm_ltc,\n\t\t\t password_ltc);\n\tif (err)\n\t\tgoto out;\n\n\terr = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_REQUEST,\n\t\t\t      tid_ltc, NULL,\n\t\t\t      md5_hash, sizeof(md5_hash),\n\t\t\t      false, 0x00, 3,\n\t\t\t      STUN_ATTR_USERNAME, username_ltc,\n\t\t\t      STUN_ATTR_NONCE, nonce_ltc,\n\t\t\t      STUN_ATTR_REALM, realm_ltc);\n\tif (err)\n\t\tgoto out;\n\n\tr = memcmp(mb->buf, reqltc, mb->end);\n\tif ((sizeof(reqltc)-1) != mb->end || 0 != r) {\n\t\terr = EBADMSG;\n\t\tDEBUG_WARNING(\"compare failed (r=%d)\\n\", r);\n\t\t(void)re_printf(\"msg: [%02w]\\n\", mb->buf, mb->end);\n\t\t(void)re_printf(\"ref: [%02w]\\n\", reqltc, sizeof(reqltc)-1);\n\t\tgoto out;\n\t}\n\n\t/* Decode STUN message */\n\tmb->pos = 0;\n\terr = stun_msg_decode(&msg, mb, NULL);\n\tif (err)\n\t\tgoto out;\n\n\tif (STUN_CLASS_REQUEST != stun_msg_class(msg))\n\t\tgoto bad;\n\n\tif (STUN_METHOD_BINDING != stun_msg_method(msg))\n\t\tgoto bad;\n\n\terr = stun_msg_chk_mi(msg, md5_hash, sizeof(md5_hash));\n\tif (err)\n\t\tgoto out;\n\n\tif (EPROTO != stun_msg_chk_fingerprint(msg))\n\t\tgoto bad;\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_USERNAME);\n\tif (!attr || strcmp(username_ltc, attr->v.username))\n\t\tgoto bad;\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_NONCE);\n\tif (!attr || strcmp(nonce_ltc, attr->v.nonce))\n\t\tgoto bad;\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_REALM);\n\tif (!attr || strcmp(realm_ltc, attr->v.realm))\n\t\tgoto bad;\n\n\tgoto out;\n\n bad:\n\terr = EBADMSG;\n\n out:\n\tmem_deref(msg);\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstruct test {\n\tstruct stun *stun;\n\tstruct udp_sock *us;\n\tstruct sa mapped_addr;\n\tsize_t n_resp;\n\tint err;\n};\n\n\nstatic void stun_resp_handler(int err, uint16_t scode, const char *reason,\n\t\t\t      const struct stun_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\tstruct stun_attr *attr;\n\t(void)reason;\n\n\tif (err)\n\t\tgoto out;\n\n\t++test->n_resp;\n\n\t/* verify STUN response */\n\tASSERT_EQ(0, scode);\n\tTEST_EQUALS(0x0101, stun_msg_type(msg));\n\tTEST_EQUALS(STUN_CLASS_SUCCESS_RESP, stun_msg_class(msg));\n\tTEST_EQUALS(STUN_METHOD_BINDING, stun_msg_method(msg));\n\tTEST_EQUALS(0, stun_msg_chk_fingerprint(msg));\n\n\tattr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);\n\tTEST_ASSERT(attr != NULL);\n\n\ttest->mapped_addr = attr->v.sa;\n\n out:\n\tif (err)\n\t\ttest->err = err;\n\n\t/* done */\n\tre_cancel();\n}\n\n\nstatic void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct test *test = arg;\n\t(void)src;\n\n\t(void)stun_recv(test->stun, mb);\n}\n\n\nstatic int test_stun_request(int proto, bool natted, const char *laddr_str)\n{\n\tstruct stunserver *srv = NULL;\n\tstruct stun_ctrans *ct = NULL;\n\tstruct nat *nat = NULL;\n\tstruct test test;\n\tstruct sa laddr, public_addr;\n\tint err;\n\n\tmemset(&test, 0, sizeof(test));\n\n\terr = stunserver_alloc(&srv, laddr_str);\n\tif (err)\n\t\tgoto out;\n\n\terr = stun_alloc(&test.stun, NULL, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\tif (proto == IPPROTO_UDP) {\n\t\terr = sa_set_str(&laddr, laddr_str, 0);\n\t\tTEST_ERR(err);\n\n\t\terr = udp_listen(&test.us, &laddr, udp_recv_handler, &test);\n\t\tif (err)\n\t\t\tgoto out;\n\t\terr = udp_local_get(test.us, &laddr);\n\t\tTEST_ERR(err);\n\t}\n\n\tif (natted) {\n\t\terr = sa_set_str(&public_addr, \"4.5.6.7\", 0);\n\t\tTEST_ERR(err);\n\n\t\terr = nat_alloc(&nat, NAT_INBOUND_SNAT, srv->us, &public_addr);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tsa_set_port(&public_addr, sa_port(&laddr));\n\t}\n\telse if (proto == IPPROTO_UDP) {\n\t\tpublic_addr = laddr;\n\t}\n\n\terr = stun_request(&ct, test.stun, proto, test.us,\n\t\t\t   stunserver_addr(srv, proto), 0,\n\t\t\t   STUN_METHOD_BINDING, NULL, 0, true,\n\t\t\t   stun_resp_handler, &test, 0);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_ASSERT(ct != NULL);\n\n\terr = re_main_timeout(100);\n\tif (err)\n\t\tgoto out;\n\n\tif (srv->err) {\n\t\terr = srv->err;\n\t\tgoto out;\n\t}\n\tif (test.err) {\n\t\terr = test.err;\n\t\tgoto out;\n\t}\n\n\t/* verify results */\n\tTEST_ASSERT(srv->nrecv >= 1);\n\tTEST_EQUALS(1, test.n_resp);\n\n\tif (proto == IPPROTO_UDP) {\n\t\tTEST_SACMP(&public_addr, &test.mapped_addr, SA_ALL);\n\t}\n\n out:\n\tmem_deref(test.stun);\n\tmem_deref(test.us);\n\tmem_deref(nat);\n\tmem_deref(srv);\n\n\treturn err;\n}\n\n\nstatic int test_stun_req_attributes(void)\n{\n\tstruct stun_msg *msg = NULL;\n\tstruct mbuf *mb;\n\tstruct stun_attr *attr;\n\tconst uint64_t rsv_token = 0x1100c0ffee;\n\tconst uint32_t lifetime = 3600;\n\tconst uint16_t chan = 0x4000;\n\tconst uint8_t req_addr_fam = AF_INET;\n\tint err;\n\n\tmb = mbuf_alloc(1024);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_REQUEST,\n\t\t\t      tid, NULL, NULL, 0, false,\n\t\t\t      0x00, 4,\n\t\t\t      STUN_ATTR_REQ_ADDR_FAMILY, &req_addr_fam,\n\t\t\t      STUN_ATTR_CHANNEL_NUMBER, &chan,\n\t\t\t      STUN_ATTR_LIFETIME, &lifetime,\n\t\t\t      STUN_ATTR_RSV_TOKEN, &rsv_token);\n\tif (err)\n\t\tgoto out;\n\n\t/* Decode STUN message */\n\tmb->pos = 0;\n\terr = stun_msg_decode(&msg, mb, NULL);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(STUN_CLASS_REQUEST, stun_msg_class(msg));\n\tTEST_EQUALS(STUN_METHOD_BINDING, stun_msg_method(msg));\n\n\t/* verify integer attributes of different sizes */\n\n\t/* 8-bit */\n\tattr = stun_msg_attr(msg, STUN_ATTR_REQ_ADDR_FAMILY);\n\tTEST_ASSERT(attr != NULL);\n\tTEST_EQUALS(req_addr_fam, attr->v.req_addr_family);\n\n\t/* 16-bit */\n\tattr = stun_msg_attr(msg, STUN_ATTR_CHANNEL_NUMBER);\n\tTEST_ASSERT(attr != NULL);\n\tTEST_EQUALS(chan, attr->v.channel_number);\n\n\t/* 32-bit */\n\tattr = stun_msg_attr(msg, STUN_ATTR_LIFETIME);\n\tTEST_ASSERT(attr != NULL);\n\tTEST_EQUALS(lifetime, attr->v.lifetime);\n\n\t/* 64-bit */\n\tattr = stun_msg_attr(msg, STUN_ATTR_RSV_TOKEN);\n\tTEST_ASSERT(attr != NULL);\n\tTEST_EQUALS(rsv_token, attr->v.rsv_token);\n\n out:\n\tmem_deref(msg);\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nstatic void stun_dns_handler(int err, const struct sa *srv, void *arg)\n{\n\tstruct sa *stun_addr = arg;\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"stun_dns_handler error: %m\\n\", err);\n\t\tre_cancel();\n\t\treturn;\n\t}\n\n\tDEBUG_INFO(\"stun dns:  server = %J\\n\", srv);\n\n\t*stun_addr = *srv;\n\tre_cancel();\n}\n\n\nstatic const char *get_loopback(int af)\n{\n\tswitch (af) {\n\n\tcase AF_INET:  return \"127.0.0.1\";\n\tcase AF_INET6: return \"::1\";\n\tdefault:       return NULL;\n\t}\n}\n\n\nstatic int test_stun_discover(int af)\n{\n\tstruct dns_server *srv = NULL;\n\tstruct dnsc *dnsc = NULL;\n\tstruct stun_dns *stun_dns = NULL;\n\tstruct sa stun_addr, exp_addr;\n\tconst char *tld = \"example.com\";\n\tconst char *target = \"stun.example.com\";\n\tconst char *laddr = get_loopback(af);\n\tint err;\n\n\tsa_set_str(&exp_addr, laddr, STUN_PORT);\n\n\terr = dns_server_alloc(&srv, laddr);\n\tTEST_ERR(err);\n\n\tchar srv_name[256] = \"\";\n\tre_snprintf(srv_name, sizeof(srv_name), \"_stun._udp.%s\", tld);\n\n\terr = dns_server_add_srv(srv, srv_name, 0, 0, STUN_PORT, target, 3600);\n\tTEST_ERR(err);\n\n\terr = dns_server_add_srv(srv, srv_name, 0, 0, STUN_PORT, target, 3600);\n\tTEST_ERR(err);\n\n\terr = dns_server_add_a(srv, target, 0x7f000001, 3600);\n\tTEST_ERR(err);\n\n\tconst uint8_t ipv6_lo[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};\n\terr = dns_server_add_aaaa(srv, target, ipv6_lo, 3600);\n\tTEST_ERR(err);\n\n\terr = dnsc_alloc(&dnsc, NULL, &srv->addr, 1);\n\tTEST_ERR(err);\n\n\terr = stun_server_discover(&stun_dns, dnsc, stun_usage_binding,\n\t\t\t\t   stun_proto_udp, af, tld, 0,\n\t\t\t\t   stun_dns_handler, &stun_addr);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(10000);\n\tTEST_ERR(err);\n\n\tTEST_SACMP(&exp_addr, &stun_addr, SA_ALL);\n\n out:\n\tmem_deref(stun_dns);\n\tmem_deref(dnsc);\n\tmem_deref(srv);\n\n\treturn err;\n}\n\n\n/*\n * Send a STUN Binding Request to the mock STUN-Server,\n * and expect a STUN Binding Response.\n */\nint test_stun(void)\n{\n\tint err;\n\n\terr = test_stun_request(IPPROTO_UDP, false, \"127.0.0.1\");\n\tTEST_ERR(err);\n\n\terr = test_stun_request(IPPROTO_UDP, NATTED, \"127.0.0.1\");\n\tTEST_ERR(err);\n\n\terr = test_stun_request(IPPROTO_TCP, false, \"127.0.0.1\");\n\tTEST_ERR(err);\n\n\terr = test_stun_req_attributes();\n\tTEST_ERR(err);\n\n\tif (test_ipv6_supported()) {\n\t\terr = test_stun_request(IPPROTO_UDP, false, \"::1\");\n\t\tTEST_ERR(err);\n\t}\n\n\terr = test_stun_discover(AF_INET);\n\tTEST_ERR(err);\n\n\tif (test_ipv6_supported()) {\n\t\terr = test_stun_discover(AF_INET6);\n\t\tTEST_ERR(err);\n\t}\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/sys.c",
    "content": "/**\n * @file sys.c System Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <ctype.h>\n#include <string.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_sys\"\n#define DEBUG_LEVEL 4\n#include <re_dbg.h>\n\n\nint test_sys_endian(void)\n{\n\tuint16_t s_le, s_ho;\n\tuint8_t *s = (uint8_t *)&s_le;\n\tuint32_t l_le, l_ho;\n\tuint8_t *l = (uint8_t *)&l_le;\n\tuint64_t ll0, ll1 = 0x0102030405060708ULL;\n\n\t/* Little endian: LSB first - 0x1234\n\t *\n\t * 0x0000: 0x34\n\t * 0x0001: 0x12\n\t */\n\n\ts[0] = 0x34;\n\ts[1] = 0x12;\n\n\ts_ho = sys_ltohs(s_le);\n\tif (0x1234 != s_ho) {\n\t\tDEBUG_WARNING(\"endian short: 0x%04x\\n\", s_ho);\n\t\treturn EINVAL;\n\t}\n\n\tif (s_le != sys_htols(s_ho)) {\n\t\tDEBUG_WARNING(\"sys_htols failed: 0x%04x\\n\", sys_htols(s_ho));\n\t\treturn EINVAL;\n\t}\n\n\t/* 0x12345678\n\t *\n\t * 0x0000: 0x78\n\t * 0x0001: 0x56\n\t * 0x0002: 0x34\n\t * 0x0003: 0x12\n\t */\n\n\tl[0] = 0x78;\n\tl[1] = 0x56;\n\tl[2] = 0x34;\n\tl[3] = 0x12;\n\n\tl_ho = sys_ltohl(l_le);\n\tif (0x12345678 != l_ho) {\n\t\tDEBUG_WARNING(\"endian long: 0x%08x\\n\", l_ho);\n\t\treturn EINVAL;\n\t}\n\n\tif (l_le != sys_htoll(l_ho)) {\n\t\tDEBUG_WARNING(\"sys_htoll failed: 0x%08x\\n\", sys_htoll(l_ho));\n\t\treturn EINVAL;\n\t}\n\n\t/* Test 64-bit */\n\tll0 = sys_ntohll(sys_htonll(ll1));\n\n\tif (ll0 != ll1) {\n\t\tDEBUG_WARNING(\"endian long-long: 0x%llx\\n\", ll0);\n\t\treturn EINVAL;\n\t}\n\n\treturn 0;\n}\n\n\nint test_sys_rand(void)\n{\n\tchar str[64];\n\tuint8_t buf[64];\n\tsize_t i;\n\tint err = 0;\n\n\tvolatile uint16_t u16 = rand_u16();\n\tvolatile uint32_t u32 = rand_u32();\n\tvolatile uint64_t u64 = rand_u64();\n\tchar ch      = rand_char();\n\n\t(void)u16;\n\t(void)u32;\n\t(void)u64;\n\n\tTEST_ASSERT(ch > 0);\n\tTEST_ASSERT(isprint(ch));\n\n\trand_str(str, sizeof(str));\n\trand_bytes(buf, sizeof(buf));\n\n\tfor (i = 0; i < (sizeof(str)-1); i++) {\n\t\tTEST_ASSERT(str[i] > 0);\n\t\tTEST_ASSERT(isprint(str[i]));\n\t}\n\n out:\n\treturn err;\n}\n\n\nint test_sys_fs_isdir(void)\n{\n\tint err = 0;\n\tbool ret;\n\tchar path[256];\n\tchar file[256];\n\tchar *wpath = \"/some/path/to/nothing\";\n\n\tre_snprintf(path, sizeof(path), \"%s\", test_datapath());\n\tre_snprintf(file, sizeof(file), \"%s/menu.json\", test_datapath());\n\n\tret = fs_isdir(path);\n\tTEST_EQUALS(true, ret);\n\n\tret = fs_isdir(NULL);\n\tTEST_EQUALS(false, ret);\n\n\tret = fs_isdir(wpath);\n\tTEST_EQUALS(false, ret);\n\n\tret = fs_isdir(file);\n\tTEST_EQUALS(false, ret);\n\n out:\n\treturn err;\n}\n\n\nint test_sys_fs_isfile(void)\n{\n\tint err = 0;\n\tbool ret;\n\tchar path[256];\n\tchar file[256];\n\tchar *wpath = \"/some/path/to/nothing\";\n\n\tre_snprintf(path, sizeof(path), \"%s\", test_datapath());\n\tre_snprintf(file, sizeof(file), \"%s/menu.json\", test_datapath());\n\n\tret = fs_isfile(file);\n\tTEST_EQUALS(true, ret);\n\n\tret = fs_isfile(NULL);\n\tTEST_EQUALS(false, ret);\n\n\tret = fs_isfile(wpath);\n\tTEST_EQUALS(false, ret);\n\n\tret = fs_isfile(path);\n\tTEST_EQUALS(false, ret);\n\n out:\n\treturn err;\n}\n\n\nint test_sys_fs_fopen(void)\n{\n\tchar filename[256];\n\tFILE *file;\n\tint err;\n\n\t/* Use a unique filename to avoid clash when running\n\t * multiple instances of test\n\t */\n\tre_snprintf(filename, sizeof(filename),\n\t\t\"%s/retest_fs_fopen-%llu\", test_datapath(), rand_u64());\n\n\terr = fs_fopen(&file, filename, \"w+\");\n\tTEST_ERR(err);\n\tTEST_EQUALS(true, fs_isfile(filename));\n\n\terr = fclose(file);\n\tTEST_ERR(err);\n\n\t/* Try reopen */\n\terr = fs_fopen(&file, filename, \"w+\");\n\tTEST_ERR(err);\n\n\terr = fclose(file);\n\tTEST_ERR(err);\n\n#ifdef WIN32\n\t(void)_unlink(filename);\n#else\n\t(void)unlink(filename);\n#endif\n\nout:\n\treturn err;\n}\n\n\nint test_sys_getenv(void)\n{\n\tint err = 0;\n\tchar *env = NULL;\n\n#ifdef WIN32\n\terr = sys_getenv(&env, \"HOMEPATH\");\n#else\n\terr = sys_getenv(&env, \"HOME\");\n#endif\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(true, str_isset(env));\n\tmem_deref(env);\n\n\terr = sys_getenv(&env, \"DOESNOTEXIST\");\n\tTEST_EQUALS(ENODATA, err);\n\terr = 0;\n\nout:\n\treturn err;\n}\n\n\nint test_sys_fs_gethome(void)\n{\n\tchar path[256];\n\tint err = fs_gethome(path, sizeof(path));\n\tTEST_ERR(err);\n\tTEST_EQUALS(true, str_isset(path));\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/tcp.c",
    "content": "/**\n * @file tcp.c  TCP testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"tcptest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/*\n *               .------.                            .------.\n *               |Client|                            |Server|\n *               '------'                            '------'\n *\n *                                                       <------ tcp_listen()\n * tcp_connect() --->\n *                   ------------- TCP [SYN] ----------->\n *                                                       -----> tcp_conn_h\n *\n *                                                       <----- tcp_accept()\n *                   <-------- TCP [SYN, ACK] ----------\n *                   --------- TCP [ACK] -------------->\n * tcp_estab_h   <---\n *\n * tcp_send()    ===>\n *                    ======= TCP [PSH, ACK] ==========>\n *                   <======= TCP [ACK] ===============\n *                                                       =====> tcp_recv_h\n */\n\nstruct tcp_test {\n\tstruct tcp_sock *ts;\n\tstruct tcp_conn *tc;\n\tstruct tcp_conn *tc2;\n\tint err;\n};\n\n\nstatic const char *ping = \"ping from client to server\\n\";\nstatic const char *pong = \"pong from server 2 client\\n\";\n\n\nstatic void destructor(void *arg)\n{\n\tstruct tcp_test *tt = arg;\n\n\tmem_deref(tt->tc2);\n\tmem_deref(tt->tc);\n\tmem_deref(tt->ts);\n}\n\n\nstatic void abort_test(struct tcp_test *tt, int err)\n{\n\tif (err) {\n\t\ttt->err = err;\n\t}\n\n\tre_cancel();\n}\n\n\nstatic int send_data(struct tcp_conn *tc, const char *data)\n{\n\tstruct mbuf mb;\n\tint err;\n\n\tmbuf_init(&mb);\n\n\terr = mbuf_write_str(&mb, data);\n\tif (err)\n\t\tgoto out;\n\n\tmb.pos = 0;\n\terr = tcp_send(tc, &mb);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tmbuf_reset(&mb);\n\treturn err;\n}\n\n\nstatic bool mbuf_compare(const struct mbuf *mb, const char *str)\n{\n\tif (mbuf_get_left(mb) != strlen(str)) {\n\t\tDEBUG_WARNING(\"compare: mbuf=%u str=%u (bytes)\\n\",\n\t\t\t      mbuf_get_left(mb), strlen(str));\n\t\treturn false;\n\t}\n\n\tif (0 != memcmp(mbuf_buf(mb), str, strlen(str))) {\n\t\tDEBUG_WARNING(\"compare: mbuf=[%b] str=[%s]\\n\",\n\t\t\t      mbuf_buf(mb), mbuf_get_left(mb), str);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\nstatic void tcp_server_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct tcp_test *tt = arg;\n\tint err;\n\n\tDEBUG_INFO(\"Server: TCP Receive data (%u bytes)\\n\",\n\t\t   mbuf_get_left(mb));\n\n\tif (!mbuf_compare(mb, ping)) {\n\t\tabort_test(tt, EBADMSG);\n\t\treturn;\n\t}\n\n\terr = send_data(tt->tc2, pong);\n\tif (err)\n\t\tabort_test(tt, err);\n}\n\n\nstatic void tcp_server_close_handler(int err, void *arg)\n{\n\tstruct tcp_test *tt = arg;\n\tabort_test(tt, err);\n}\n\n\nstatic void tcp_server_conn_handler(const struct sa *peer, void *arg)\n{\n\tstruct tcp_test *tt = arg;\n\tint err;\n\n\t(void)peer;\n\n\tDEBUG_INFO(\"Server: Incoming CONNECT from %J\\n\", peer);\n\n\terr = tcp_accept(&tt->tc2, tt->ts, NULL, tcp_server_recv_handler,\n\t\t\t tcp_server_close_handler, tt);\n\tif (err) {\n\t\tabort_test(tt, err);\n\t\treturn;\n\t}\n}\n\n\nstatic void tcp_client_estab_handler(void *arg)\n{\n\tstruct tcp_test *tt = arg;\n\tint err;\n\n\tDEBUG_INFO(\"Client: TCP Established\\n\");\n\n\terr = send_data(tt->tc, ping);\n\tif (err)\n\t\tabort_test(tt, err);\n}\n\n\nstatic void tcp_client_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct tcp_test *tt = arg;\n\n\tDEBUG_INFO(\"Client: TCP receive: %u bytes\\n\", mbuf_get_left(mb));\n\n\tif (!mbuf_compare(mb, pong)) {\n\t\tabort_test(tt, EBADMSG);\n\t\treturn;\n\t}\n\n\tabort_test(tt, 0);\n}\n\n\nstatic void tcp_client_close_handler(int err, void *arg)\n{\n\tstruct tcp_test *tt = arg;\n\tDEBUG_NOTICE(\"Client: TCP Close (%m)\\n\", err);\n\n\tabort_test(tt, err);\n}\n\n\nint test_tcp(void)\n{\n\tstruct tcp_test *tt;\n\tstruct sa srv;\n\tint err;\n\n\ttt = mem_zalloc(sizeof(*tt), destructor);\n\tif (!tt)\n\t\treturn ENOMEM;\n\n\terr = sa_set_str(&srv, \"127.0.0.1\", 0);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_listen(&tt->ts, &srv, tcp_server_conn_handler, tt);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_local_get(tt->ts, &srv);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_connect(&tt->tc, &srv, tcp_client_estab_handler,\n\t\t\t  tcp_client_recv_handler, tcp_client_close_handler,\n\t\t\t  tt);\n\tif (err)\n\t\tgoto out;\n\n\terr = re_main_timeout(500);\n\tif (err)\n\t\tgoto out;\n\n\tif (tt->err)\n\t\terr = tt->err;\n\n out:\n\tmem_deref(tt);\n\n\treturn err;\n}\n\n\n#if !defined(WIN32)\nstatic int tcp_tos(const char *addr)\n{\n\tstruct tcp_test *tt;\n\tstruct sa srv;\n\tint err;\n\n\ttt = mem_zalloc(sizeof(*tt), destructor);\n\tif (!tt)\n\t\treturn ENOMEM;\n\n\terr = sa_set_str(&srv, addr, 0);\n\tTEST_ERR(err);\n\n\terr = tcp_listen(&tt->ts, &srv, tcp_server_conn_handler, tt);\n\tTEST_ERR(err);\n\n\terr  = tcp_settos(tt->ts, 184);\n\tTEST_ERR(err);\n\n\terr = tcp_local_get(tt->ts, &srv);\n\tTEST_ERR(err);\n\n\terr = tcp_connect(&tt->tc, &srv, tcp_client_estab_handler,\n\t\t\t  tcp_client_recv_handler, tcp_client_close_handler,\n\t\t\t  tt);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(500);\n\tTEST_ERR(err);\n\n\tif (tt->err)\n\t\terr = tt->err;\n\n out:\n\tmem_deref(tt);\n\n\treturn err;\n}\n\n\nint test_tcp_tos(void)\n{\n\tint err;\n\n\terr = tcp_tos(\"127.0.0.1\");\n\tTEST_ERR(err);\n\n\tif (test_ipv6_supported()) {\n\t\terr = tcp_tos(\"::1\");\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\treturn err;\n}\n#else\n/* Outcome of the TOS test on Windows would be dependent on the\n * DisableUserTOSSetting Windows registry setting. */\nint test_tcp_tos(void)\n{\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "test/telev.c",
    "content": "/**\n * @file telev.c  Testcode for Telephone-event\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\nint test_telev(void)\n{\n\tstatic const char digits[] = \"1234567890ABCD*#\";\n\tstruct telev *tlv = NULL;\n\tstruct mbuf *mb;\n\tbool marker, expect_end = false;\n\tchar digit;\n\tsize_t i;\n\tint err;\n\n\tmb = mbuf_alloc(512);\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\terr = telev_alloc(&tlv, 1);\n\tif (err)\n\t\tgoto out;\n\n\t/* Encode all digits */\n\tfor (i=0; i<strlen(digits) && !err; i++) {\n\t\tdigit = telev_digit2code(digits[i]);\n\t\terr |= telev_send(tlv, digit, false);\n\t\terr |= telev_send(tlv, digit, true);\n\t}\n\tif (err)\n\t\tgoto out;\n\n\twhile (0 == telev_poll(tlv, &marker, mb))\n\t\t;\n\n\t/* Decode all digits */\n\tmb->pos = 0;\n\ti = 0;\n\twhile (mbuf_get_left(mb) && i<strlen(digits)) {\n\t\tint event;\n\t\tbool end;\n\n\t\tif (telev_recv(tlv, mb, &event, &end))\n\t\t\tcontinue;\n\n\t\tdigit = telev_code2digit(event);\n\n\t\tif (digits[i] != digit) {\n\t\t\t(void)re_fprintf(stderr, \"telev: expect %c, got %c\\n\",\n\t\t\t\t\t digits[i], digit);\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!expect_end != !end) {\n\t\t\terr = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\texpect_end = !end;\n\t\tif (end)\n\t\t\t++i;\n\t}\n\n\tif (i != strlen(digits))\n\t\terr = EBADMSG;\n\n out:\n\tmem_deref(tlv);\n\tmem_deref(mb);\n\treturn err;\n}\n"
  },
  {
    "path": "test/test.c",
    "content": "/**\n * @file test.c  Regression testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#define _DEFAULT_SOURCE 1\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#ifdef HAVE_IO_H\n#include <io.h>\n#endif\n#include <string.h>\n#include <stdlib.h>\n#ifdef HAVE_SYS_TIME_H\n#include <sys/time.h>\n#endif\n#include <math.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#ifdef WIN32\n#define open _open\n#define read _read\n#define write _write\n#define close _close\n#define dup _dup\n#define dup2 _dup2\n#define fileno _fileno\n#endif\n\n\ntypedef int (test_exec_h)(void);\n\nstruct test {\n\ttest_exec_h *exec;\n\tconst char *name;\n};\n\n#define TEST(a) {a, #a}\n\nstatic const struct test tests[] = {\n\tTEST(test_aac),\n\tTEST(test_aes),\n\tTEST(test_aes_gcm),\n\tTEST(test_async),\n\tTEST(test_au),\n\tTEST(test_aubuf),\n\tTEST(test_aulength),\n\tTEST(test_aulevel),\n\tTEST(test_auposition),\n\tTEST(test_auresamp),\n\tTEST(test_av1),\n\tTEST(test_base64),\n\tTEST(test_bfcp),\n\tTEST(test_bfcp_bin),\n\tTEST(test_bfcp_tcp),\n\tTEST(test_bfcp_udp),\n\tTEST(test_btrace),\n\tTEST(test_conf),\n\tTEST(test_crc32),\n\tTEST(test_dbg),\n\tTEST(test_dd),\n\tTEST(test_dns_dname),\n\tTEST(test_dns_hdr),\n\tTEST(test_dns_proto),\n\tTEST(test_dns_reg),\n\tTEST(test_dns_rr),\n\tTEST(test_dns_rr_dup),\n\tTEST(test_dsp),\n#ifdef USE_TLS\n\tTEST(test_dtls),\n\tTEST(test_dtls_srtp),\n#endif\n\tTEST(test_dtmf),\n\tTEST(test_fir),\n\tTEST(test_fmt_gmtime),\n\tTEST(test_fmt_hexdump),\n\tTEST(test_fmt_human_time),\n\tTEST(test_fmt_param),\n\tTEST(test_fmt_pl),\n\tTEST(test_fmt_pl_alloc_dup),\n\tTEST(test_fmt_pl_alloc_str),\n\tTEST(test_fmt_pl_float),\n\tTEST(test_fmt_pl_i32),\n\tTEST(test_fmt_pl_i64),\n\tTEST(test_fmt_pl_u32),\n\tTEST(test_fmt_pl_u64),\n\tTEST(test_fmt_pl_x3264),\n\tTEST(test_fmt_print),\n\tTEST(test_fmt_regex),\n\tTEST(test_fmt_snprintf),\n\tTEST(test_fmt_str),\n\tTEST(test_fmt_str_bool),\n\tTEST(test_fmt_str_error),\n\tTEST(test_fmt_str_itoa),\n\tTEST(test_fmt_str_wchar),\n\tTEST(test_fmt_timestamp),\n\tTEST(test_fmt_unicode),\n\tTEST(test_fmt_unicode_decode),\n\tTEST(test_g711_alaw),\n\tTEST(test_g711_ulaw),\n\tTEST(test_h264),\n\tTEST(test_h264_packet),\n\tTEST(test_h264_sps),\n\tTEST(test_h265),\n\tTEST(test_h265_packet),\n\tTEST(test_hash),\n\tTEST(test_hmac_sha1),\n\tTEST(test_hmac_sha256),\n\tTEST(test_http),\n\tTEST(test_http_conn),\n\tTEST(test_http_conn_large_body),\n\tTEST(test_http_large_body),\n\tTEST(test_http_loop),\n\tTEST(test_http_request_addr),\n#ifdef USE_TLS\n\tTEST(test_https_request_addr),\n#endif\n#ifdef USE_TLS\n\tTEST(test_https_loop),\n\tTEST(test_http_client_set_tls),\n\tTEST(test_https_large_body),\n#endif\n#ifdef HAVE_TLS1_3_POST_HANDSHAKE_AUTH\n\tTEST(test_https_conn_post_handshake),\n#endif\n\tTEST(test_httpauth_chall),\n\tTEST(test_httpauth_resp),\n\tTEST(test_httpauth_basic_request),\n\tTEST(test_httpauth_digest_request),\n\tTEST(test_httpauth_digest_response),\n\tTEST(test_httpauth_digest_verification),\n\tTEST(test_ice_cand),\n\tTEST(test_ice_loop),\n\tTEST(test_json),\n\tTEST(test_json_file),\n\tTEST(test_json_unicode),\n\tTEST(test_json_bad),\n\tTEST(test_json_array),\n\tTEST(test_list),\n\tTEST(test_list_flush),\n\tTEST(test_list_ref),\n\tTEST(test_list_sort),\n\tTEST(test_mbuf),\n\tTEST(test_md5),\n\tTEST(test_mem),\n\tTEST(test_mem_pool),\n\tTEST(test_mem_reallocarray),\n\tTEST(test_mem_secure),\n\tTEST(test_net_if),\n\tTEST(test_mqueue),\n\tTEST(test_odict),\n\tTEST(test_odict_array),\n\tTEST(test_odict_pl),\n\tTEST(test_pcp),\n\tTEST(test_remain),\n\tTEST(test_re_assert_se),\n\tTEST(test_rtmp_play),\n\tTEST(test_rtmp_publish),\n#ifdef USE_TLS\n\tTEST(test_rtmps_publish),\n#endif\n\tTEST(test_rtp),\n\tTEST(test_rtpext),\n\tTEST(test_rtcp_encode),\n\tTEST(test_rtcp_encode_afb),\n\tTEST(test_rtcp_decode),\n\tTEST(test_rtcp_decode_badmsg),\n\tTEST(test_rtcp_packetloss),\n\tTEST(test_rtcp_twcc),\n\tTEST(test_rtcp_xr),\n\tTEST(test_rtcp_loop),\n\tTEST(test_sa_class),\n\tTEST(test_sa_cmp),\n\tTEST(test_sa_decode),\n\tTEST(test_sa_ntop),\n\tTEST(test_sa_pton),\n\tTEST(test_sa_pton_linklocal),\n\tTEST(test_sdp_all),\n\tTEST(test_sdp_bfcp),\n\tTEST(test_sdp_parse),\n\tTEST(test_sdp_oa),\n\tTEST(test_sdp_extmap),\n\tTEST(test_sdp_disabled_rejected),\n\tTEST(test_sdp_interop),\n\tTEST(test_sha1),\n\tTEST(test_sip_addr),\n\tTEST(test_sip_apply),\n\tTEST(test_sip_auth),\n\tTEST(test_sip_drequestf),\n\tTEST(test_sip_dns),\n\tTEST(test_sip_hdr),\n\tTEST(test_sip_param),\n\tTEST(test_sip_parse),\n\tTEST(test_sip_via),\n#ifdef USE_TLS\n\tTEST(test_sip_transp_add_client_cert),\n#endif\n\tTEST(test_fmt_trim),\n\tTEST(test_sipevent),\n\tTEST(test_sipreg_tcp),\n#ifdef USE_TLS\n\tTEST(test_sipreg_tls),\n#endif\n\tTEST(test_sipreg_udp),\n\tTEST(test_sipsess),\n\tTEST(test_sipsess_100rel_420),\n\tTEST(test_sipsess_100rel_421),\n\tTEST(test_sipsess_100rel_answer_not_allowed),\n\tTEST(test_sipsess_100rel_caller_require),\n\tTEST(test_sipsess_100rel_supported),\n\tTEST(test_sipsess_blind_transfer),\n\tTEST(test_sipsess_reject),\n\tTEST(test_sipsess_update_no_sdp),\n\tTEST(test_sipsess_update_uac),\n\tTEST(test_sipsess_update_uas),\n\tTEST(test_srtcp),\n\tTEST(test_srtcp_gcm),\n\tTEST(test_srtp),\n\tTEST(test_srtp_gcm),\n\tTEST(test_stun),\n\tTEST(test_stun_req),\n\tTEST(test_stun_reqltc),\n\tTEST(test_stun_resp),\n\tTEST(test_sys_endian),\n\tTEST(test_sys_fs_fopen),\n\tTEST(test_sys_fs_gethome),\n\tTEST(test_sys_fs_isdir),\n\tTEST(test_sys_fs_isfile),\n\tTEST(test_sys_getenv),\n\tTEST(test_sys_rand),\n\tTEST(test_tcp),\n\tTEST(test_tcp_tos),\n\tTEST(test_telev),\n\tTEST(test_text2pcap),\n#ifdef USE_TLS\n\tTEST(test_tls),\n\tTEST(test_tls_ec),\n\tTEST(test_tls_selfsigned),\n\tTEST(test_tls_certificate),\n\tTEST(test_tls_false_cafile_path),\n\tTEST(test_tls_cli_conn_change_cert),\n\tTEST(test_tls_session_reuse_tls_v12),\n\tTEST(test_tls_sni),\n#endif\n\tTEST(test_thread),\n\tTEST(test_thread_tss),\n\tTEST(test_trace),\n\tTEST(test_trice_cand),\n\tTEST(test_trice_candpair),\n\tTEST(test_trice_checklist),\n\tTEST(test_trice_loop),\n\tTEST(test_try_into),\n\tTEST(test_turn),\n\tTEST(test_turn_tcp),\n\tTEST(test_udp),\n\tTEST(test_udp_tos),\n\tTEST(test_unixsock),\n\tTEST(test_uri),\n\tTEST(test_uri_encode),\n\tTEST(test_uri_escape),\n\tTEST(test_uri_headers),\n\tTEST(test_uri_params_headers),\n\tTEST(test_uri_user),\n\tTEST(test_vid),\n\tTEST(test_vidconv),\n\tTEST(test_vidconv_pixel_formats),\n\tTEST(test_vidconv_scaling),\n\tTEST(test_websock),\n\n#ifdef USE_TLS\n\t/* combination tests: */\n\tTEST(test_dtls_turn),\n#endif\n};\n\n\nstatic const struct test tests_integration[] = {\n\tTEST(test_dns_cache_http_integration),\n\tTEST(test_dns_http_integration),\n\tTEST(test_dns_integration),\n\tTEST(test_dns_nameservers),\n\tTEST(test_net_dst_source_addr_get),\n\tTEST(test_rtp_listen),\n\tTEST(test_sip_drequestf_network),\n\tTEST(test_sipevent_network),\n\tTEST(test_sipreg_tcp),\n#ifdef USE_TLS\n\tTEST(test_sipreg_tls),\n#endif\n\tTEST(test_sipreg_udp),\n\tTEST(test_tmr_jiffies),\n\tTEST(test_tmr_jiffies_usec),\n\tTEST(test_turn_thread),\n\tTEST(test_thread_cnd_timedwait),\n\tTEST(test_cplusplus),\n};\n\n\n#ifdef DATA_PATH\nstatic char datapath[256] = DATA_PATH;\n#else\nstatic char datapath[256] = \"./test/data\";\n#endif\n\n\nstatic uint32_t timeout_override;\nstatic int dup_stdout;\nstatic int dup_stderr;\n\nenum test_mode test_mode = TEST_NONE;\n\n\nstatic void hide_output(void)\n{\n\tdup_stdout = dup(fileno(stdout));\n\tdup_stderr = dup(fileno(stderr));\n\n#ifdef WIN32\n\tint mode = _S_IREAD | _S_IWRITE;\n#else\n\tmode_t mode = S_IWUSR | S_IRUSR;\n#endif\n\n\tint fd_out = open(\"stdout.out\", O_WRONLY | O_CREAT, mode);\n\tif (fd_out < 0)\n\t\treturn;\n\t(void)dup2(fd_out, fileno(stdout));\n\n\tint fd_err = open(\"stderr.out\", O_WRONLY | O_CREAT, mode);\n\tif (fd_err < 0)\n\t\treturn;\n\t(void)dup2(fd_err, fileno(stderr));\n}\n\n\nstatic void restore_output(int err)\n{\n\tFILE *f = NULL;\n\tchar line[1024];\n\n\tfflush(stdout);\n\tfflush(stderr);\n\n\t/* Restore stdout/stderr */\n\t(void)dup2(dup_stdout, fileno(stdout));\n\t(void)dup2(dup_stderr, fileno(stderr));\n\n\tif (!err)\n\t\tgoto out;\n\n\tf = fopen(\"stdout.out\", \"r\");\n\tif (!f)\n\t\tgoto out;\n\n\twhile (fgets(line, sizeof(line), f)) {\n\t\tre_fprintf(stdout, \"%s\", line);\n\t}\n\t(void)fclose(f);\n\n\n\tf = fopen(\"stderr.out\", \"r\");\n\tif (!f)\n\t\tgoto out;\n\n\twhile (fgets(line, sizeof(line), f)) {\n\t\tre_fprintf(stderr, \"%s\", line);\n\t}\n\t(void)fclose(f);\n\nout:\n#ifdef WIN32\n\t(void)_unlink(\"stdout.out\");\n\t(void)_unlink(\"stderr.out\");\n#else\n\t(void)unlink(\"stdout.out\");\n\t(void)unlink(\"stderr.out\");\n#endif\n}\n\n\nstatic const struct test *find_test(const char *name)\n{\n\tsize_t i;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(tests); i++) {\n\n\t\tif (0 == str_casecmp(name, tests[i].name))\n\t\t\treturn &tests[i];\n\t}\n\n\treturn NULL;\n}\n\n\nstatic const struct test *find_test_int(const char *name)\n{\n\tfor (size_t i=0; i<RE_ARRAY_SIZE(tests_integration); i++) {\n\n\t\tif (0 == str_casecmp(name, tests_integration[i].name))\n\t\t\treturn &tests_integration[i];\n\t}\n\n\treturn NULL;\n}\n\n\nstatic int test_exec(const struct test *test)\n{\n\tif (!test)\n\t\treturn EINVAL;\n\n\tstruct memstat mstat_before;\n\tstruct memstat mstat_after;\n\n\tmem_get_stat(&mstat_before);\n\n\tint err = test->exec();\n\tre_fhs_flush();\n\n\tmem_get_stat(&mstat_after);\n\n\tif (mstat_after.blocks_cur > mstat_before.blocks_cur) {\n\t\tmem_debug_tail((uint32_t)(mstat_after.blocks_cur -\n\t\t\t\t\t  mstat_before.blocks_cur));\n\t\tre_assert(false && \"Test leaks memory blocks\");\n\t}\n\n\tif (mstat_after.bytes_cur > mstat_before.bytes_cur) {\n\t\tmem_debug();\n\t\tre_assert(false && \"Test leaks memory bytes\");\n\t}\n\n\treturn err;\n}\n\n\n/**\n * Run a single testcase in OOM (Out-of-memory) mode.\n *\n * Start with 0 blocks free, and increment by 1 until the test passes.\n *\n *\n *  Blocks\n *  Free\n *\n *    /'\\\n *     |\n *   5 |           #\n *     |         # #\n *     |       # # #\n *     |     # # # #\n *   1 |   # # # # #\n *     '--------------> time\n */\nstatic int testcase_oom(const struct test *test, int levels, bool verbose)\n{\n\tint i;\n\tint err = 0;\n\n\tif (verbose)\n\t\t(void)re_fprintf(stderr, \"  %-26s: \", test->name);\n\n\t/* All memory levels */\n\tfor (i=0; i<levels; i++) {\n\n\t\tmem_threshold_set(i);\n\n\t\terr = test_exec(test);\n\t\tif (err == 0) {\n\t\t\t/* success, stop now */\n\t\t\tbreak;\n\t\t}\n\t\telse if (err == ENOMEM) {\n\t\t\t/* OOM, as expected */\n\t\t\terr = 0;\n\t\t}\n\t\telse if (err == ETIMEDOUT) {\n\t\t\t/* test timed out, stop now */\n\t\t\terr = 0;\n\t\t\tgoto out;\n\t\t}\n\t\telse if (err == ENOSYS) {\n\t\t\terr = 0;\n\t\t\tbreak;\n\t\t}\n\t\telse if (err == ESKIPPED) {\n\t\t\terr = 0;\n\t\t\tbreak;\n\t\t}\n\t\telse {\n\t\t\tDEBUG_WARNING(\"oom: %s: unexpected error code at\"\n\t\t\t\t      \" %d blocks free (%m)\\n\",\n\t\t\t\t      test->name, i, err);\n\t\t\tgoto out;\n\t\t}\n\t}\n\n out:\n\tif (verbose)\n\t\t(void)re_fprintf(stderr, \"oom max %d\\n\", i);\n\n\treturn err;\n}\n\n\nint test_oom(const char *name, bool verbose)\n{\n\tsize_t i;\n\tconst int levels = 128;\n\tint err = 0;\n\n\ttest_mode = TEST_MEMORY;\n\n\tif (!verbose)\n\t\thide_output();\n\n\t(void)re_fprintf(stderr, \"oom tests %u levels: \\n\", levels);\n\n\tif (name) {\n\t\tconst struct test *test = find_test(name);\n\t\tif (!test) {\n\t\t\t(void)re_fprintf(stderr, \"no such test: %s\\n\", name);\n\t\t\terr = ENOENT;\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = testcase_oom(test, levels, verbose);\n\t}\n\telse {\n\t\t/* All test cases */\n\t\tfor (i=0; i<RE_ARRAY_SIZE(tests); i++) {\n\t\t\terr = testcase_oom(&tests[i], levels, verbose);\n\t\t\tif (err)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tmem_threshold_set(-1);\n\nout:\n\tif (!verbose)\n\t\trestore_output(err);\n\n\tif (err) {\n\t\tDEBUG_WARNING(\"oom: %m\\n\", err);\n\t}\n\telse {\n\t\t(void)re_fprintf(stderr, \"\\x1b[32mOK\\x1b[;m\\t\\n\");\n\t}\n\n\treturn err;\n}\n\n\nstatic int test_unit(const char *name, bool verbose)\n{\n\tsize_t skipv[RE_ARRAY_SIZE(tests)] = {0};\n\tsize_t i;\n\tint err = 0;\n\n\tif (!verbose)\n\t\thide_output();\n\n\tif (name) {\n\t\tconst struct test *test = find_test(name);\n\t\tif (!test) {\n\t\t\t(void)re_fprintf(stderr, \"no such test: %s\\n\", name);\n\t\t\terr = ENOENT;\n\t\t\tgoto out;\n\t\t}\n\n\t\terr = test_exec(test);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"%s: test failed (%m)\\n\", name, err);\n\t\t\tgoto out;\n\t\t}\n\t}\n\telse {\n\t\tunsigned n_skipped = 0;\n\n\t\tfor (i=0; i<RE_ARRAY_SIZE(tests); i++) {\n\n\t\t\tif (verbose) {\n\t\t\t\tre_printf(\"test %zu -- %s\\n\",\n\t\t\t\t\t  i, tests[i].name);\n\t\t\t}\n\n\t\t\terr = tests[i].exec();\n\t\t\tif (err) {\n\t\t\t\tif (err == ESKIPPED || err == ENOSYS) {\n\n\t\t\t\t\tskipv[n_skipped] = i;\n\n\t\t\t\t\t++n_skipped;\n\t\t\t\t\terr = 0;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tDEBUG_WARNING(\"%s: test failed (%m)\\n\",\n\t\t\t\t\t      tests[i].name, err);\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t}\n\n\t\tif (n_skipped) {\n\t\t\tre_fprintf(stderr, \"skipped:%u\\n\", n_skipped);\n\n\t\t\t/* show any skipped testcase */\n\t\t\tfor (i=0; i<n_skipped; i++) {\n\t\t\t\tsize_t ix = skipv[i];\n\t\t\t\tre_fprintf(stderr, \"skip %s\\n\",\n\t\t\t\t\t   tests[ix].name );\n\t\t\t}\n\t\t}\n\t}\n\nout:\n\tif (!verbose)\n\t\trestore_output(err);\n\n\treturn err;\n}\n\n\n/* baseunits here is [usec] (micro-seconds) */\nstatic int testcase_perf(const struct test *test, double *usec_avgp)\n{\n#define DRYRUN_MIN        2\n#define DRYRUN_MAX      100\n#define DRYRUN_USEC 10*1000\n\n#define REPEATS_MIN         3\n#define REPEATS_MAX      1000\n#define REPEATS_USEC 100*1000\n\n\tuint64_t usec_start, usec_stop;\n\tdouble usec_avg;\n\tsize_t i, n;\n\tint err = 0;\n\n\t/* dry run */\n\tusec_start = tmr_jiffies_usec();\n\tfor (i = 1; i <= DRYRUN_MAX; i++) {\n\n\t\terr = test_exec(test);\n\t\tif (err)\n\t\t\treturn err;\n\n\t\tusec_stop = tmr_jiffies_usec();\n\n\t\tif ((usec_stop - usec_start) > DRYRUN_USEC)\n\t\t\tbreak;\n\t}\n\n\tusec_avg = 1.0 * (usec_stop - usec_start) / (double)i;\n\n\tn = usec_avg ? (size_t)(REPEATS_USEC / usec_avg) : 0;\n\tn = min(REPEATS_MAX, max(n, REPEATS_MIN));\n\n\t/* now for the real measurement */\n\tusec_start = tmr_jiffies_usec();\n\tfor (i=0; i<n; i++) {\n\t\terr = test_exec(test);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\tusec_stop = tmr_jiffies_usec();\n\n\tif (usec_stop <= usec_start) {\n\t\tDEBUG_WARNING(\"perf: cannot measure, test is too fast\\n\");\n\t\treturn EINVAL;\n\t}\n\n\tusec_avg = (1.0 * (usec_stop - usec_start)) / i;\n\n\tif (usec_avgp)\n\t\t*usec_avgp = usec_avg;\n\n\tre_printf(\"%-32s:  %10.2f usec  [%6zu repeats]\\n\",\n\t\t  test->name, usec_avg, i);\n\n\treturn 0;\n}\n\n\nstruct timing {\n\tconst struct test *test;\n\tuint64_t nsec_avg;\n};\n\n\n/*\n * The comparison function must return an integer less than, equal to,\n * or greater than zero if the first argument  is  considered to  be\n * respectively  less  than,  equal  to, or greater than the second.\n *\n * If two members compare as equal, their order in the sorted array\n * is undefined.\n */\nstatic int timing_cmp(const void *p1, const void *p2)\n{\n\tconst struct timing *v1 = p1;\n\tconst struct timing *v2 = p2;\n\n\tif (v1->nsec_avg < v2->nsec_avg)\n\t\treturn 1;\n\telse if (v1->nsec_avg > v2->nsec_avg)\n\t\treturn -1;\n\telse\n\t\treturn 0;\n}\n\n\nint test_perf(const char *name, bool verbose)\n{\n\tint err = 0;\n\tunsigned i;\n\t(void)verbose;\n\n\ttest_mode = TEST_PERF;\n\n\tif (name) {\n\t\tconst struct test *test;\n\n\t\ttest = find_test(name);\n\t\tif (!test) {\n\t\t\t(void)re_fprintf(stderr, \"no such test: %s\\n\", name);\n\t\t\treturn ENOENT;\n\t\t}\n\n\t\terr = testcase_perf(test, NULL);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\telse {\n\t\tstruct timing timingv[RE_ARRAY_SIZE(tests)];\n\n\t\tmemset(&timingv, 0, sizeof(timingv));\n\n\t\t/* All test cases */\n\t\tfor (i=0; i<RE_ARRAY_SIZE(tests); i++) {\n\n\t\t\tstruct timing *tim = &timingv[i];\n\t\t\tdouble usec_avg;\n\n\t\t\tif (!verbose)\n\t\t\t\thide_output();\n\n\t\t\ttim->test = &tests[i];\n\n\t\t\terr = testcase_perf(&tests[i],\n\t\t\t\t\t    &usec_avg);\n\n\t\t\tif (!verbose)\n\t\t\t\trestore_output(err);\n\n\t\t\tif (err) {\n\t\t\t\tif (err == ESKIPPED || err == ENOSYS) {\n\t\t\t\t\tre_printf(\"skipped: %s\\n\",\n\t\t\t\t\t\t  tests[i].name);\n\t\t\t\t\ttim->test = NULL;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tDEBUG_WARNING(\"perf: %s failed (%m)\\n\",\n\t\t\t\t\t      tests[i].name, err);\n\t\t\t\treturn err;\n\t\t\t}\n\n\t\t\ttim->nsec_avg = (uint64_t)(1000.0 * usec_avg);\n\t\t}\n\n\t\t/* sort the timing table by average time */\n\t\tqsort(timingv, RE_ARRAY_SIZE(timingv), sizeof(timingv[0]),\n\t\t      timing_cmp);\n\n\t\tre_fprintf(stderr,\n\t\t\t   \"\\nsorted by average timing (slowest on top):\\n\");\n\n\t\tfor (i=0; i<RE_ARRAY_SIZE(timingv); i++) {\n\n\t\t\tstruct timing *tim = &timingv[i];\n\t\t\tdouble usec_avg = tim->nsec_avg / 1000.0;\n\n\t\t\tif (!tim->test)\n\t\t\t\tcontinue;\n\n\t\t\tre_fprintf(stderr, \"%-34s: %10.2f usec\\n\",\n\t\t\t\t   tim->test->name, usec_avg);\n\t\t}\n\t\tre_fprintf(stderr, \"\\n\");\n\t}\n\n\treturn err;\n}\n\n\nint test_reg(const char *name, bool verbose)\n{\n\tint err;\n\n\ttest_mode = TEST_REGULAR;\n\n\ttimeout_override = 10000;\n\n\t(void)re_fprintf(stderr, \"regular tests:       \");\n\terr = test_unit(name, verbose);\n\tif (err)\n\t\treturn err;\n\t(void)re_fprintf(stderr, \"\\x1b[32mOK\\x1b[;m\\n\");\n\n\ttimeout_override = 0;\n\n\treturn 0;\n}\n\n\nstruct thread {\n\tconst struct test *test;\n\tthrd_t tid;\n\tint err;\n};\n\n\nstatic int thread_handler(void *arg)\n{\n\tstruct thread *thr = arg;\n\tint err;\n\n\terr = re_thread_init();\n\tif (err) {\n\t\tDEBUG_WARNING(\"thread: re_thread_init failed %m\\n\", err);\n\t\tthr->err = err;\n\t\treturn 0;\n\t}\n\n\terr = thr->test->exec();\n\tif (err) {\n\t\tif (err == ESKIPPED) {\n\t\t\terr = 0;\n\t\t}\n\t\telse {\n\t\t\tDEBUG_WARNING(\"%s: test failed (%m)\\n\",\n\t\t\t\t\tthr->test->name, err);\n\t\t}\n\t}\n\n\tre_thread_close();\n\n\t/* safe to write it, main thread is waiting for us */\n\tthr->err = err;\n\n\treturn 0;\n}\n\n\n/* Run all test-cases in multiple threads */\nint test_multithread(void)\n{\n#define NUM_REPEAT 2\n#define NUM_TOTAL  (NUM_REPEAT * RE_ARRAY_SIZE(tests))\n\n\tstruct thread threadv[NUM_TOTAL];\n\tsize_t test_index=0;\n\tsize_t i;\n\tint err = 0;\n\n\ttest_mode = TEST_THREAD;\n\n\ttimeout_override = 20000;\n\n\tmemset(threadv, 0, sizeof(threadv));\n\n\t(void)re_fprintf(stderr, \"multithread: %zu tests\"\n\t\t\t \" with %d repeats (total %zu threads): \",\n\t\t\t RE_ARRAY_SIZE(tests), NUM_REPEAT, NUM_TOTAL);\n\n\tfor (i=0; i<RE_ARRAY_SIZE(threadv); i++) {\n\n\t\tsize_t ti = (test_index++ % RE_ARRAY_SIZE(tests));\n\n\t\tthreadv[i].test = &tests[ti];\n\t\tthreadv[i].err = -1;           /* error not set */\n\n\t\terr = thrd_success != thrd_create(&threadv[i].tid,\n\t\t\t\t\t\t  thread_handler,\n\t\t\t\t\t\t  (void *)&threadv[i]);\n\t\tif (err) {\n\t\t\terr = EAGAIN;\n\t\t\tDEBUG_WARNING(\"thread_create failed (%m)\\n\", err);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tfor (i=0; i<RE_ARRAY_SIZE(threadv); i++) {\n\n\t\tthrd_join(threadv[i].tid, NULL);\n\t}\n\n\tfor (i=0; i<RE_ARRAY_SIZE(threadv); i++) {\n\n\t\tif (threadv[i].err != 0) {\n\t\t\tre_printf(\"%zu failed: %-30s  [%d] [%m]\\n\", i,\n\t\t\t\t  threadv[i].test->name,\n\t\t\t\t  threadv[i].err, threadv[i].err);\n\t\t\terr = threadv[i].err;\n\t\t}\n\t}\n\n\tif (err)\n\t\treturn err;\n\t(void)re_fprintf(stderr, \"\\x1b[32mOK\\x1b[;m\\n\");\n\n\ttimeout_override = 0;\n\n\treturn 0;\n}\n\n\nvoid test_listcases(void)\n{\n\tsize_t i, n, nh;\n\n\tn = RE_ARRAY_SIZE(tests);\n\tnh = (n+1)/2;\n\n\t(void)re_printf(\"\\n%zu test cases:\\n\", n);\n\n\tfor (i=0; i<nh; i++) {\n\n\t\tsize_t ih = i + nh;\n\n\t\tre_printf(\"    %-32s\", tests[i].name);\n\n\t\tif (ih < n)\n\t\t\tre_printf(\"    %s\", tests[ih].name);\n\n\t\tre_printf(\"\\n\");\n\t}\n\n\t(void)re_printf(\"\\n\");\n}\n\n\nbool test_cmp_double(double a, double b, double precision)\n{\n\treturn fabs(a - b) < precision;\n}\n\n\nvoid test_hexdump_dual(FILE *f,\n\t\t       const void *ep, size_t elen,\n\t\t       const void *ap, size_t alen)\n{\n\tconst uint8_t *ebuf = ep;\n\tconst uint8_t *abuf = ap;\n\tsize_t i, j, len;\n#define WIDTH 8\n\n\tif (!f || !ep || !ap)\n\t\treturn;\n\n\tlen = max(elen, alen);\n\n\t(void)re_fprintf(f, \"\\nOffset:   Expected (%zu bytes):    \"\n\t\t\t \"   Actual (%zu bytes):\\n\", elen, alen);\n\n\tfor (i=0; i < len; i += WIDTH) {\n\n\t\t(void)re_fprintf(f, \"0x%04zx   \", i);\n\n\t\tfor (j=0; j<WIDTH; j++) {\n\t\t\tconst size_t pos = i+j;\n\t\t\tif (pos < elen) {\n\t\t\t\tbool wrong = pos >= alen;\n\n\t\t\t\tif (wrong)\n\t\t\t\t\t(void)re_fprintf(f, \"\\x1b[35m\");\n\t\t\t\t(void)re_fprintf(f, \" %02x\", ebuf[pos]);\n\t\t\t\tif (wrong)\n\t\t\t\t\t(void)re_fprintf(f, \"\\x1b[;m\");\n\t\t\t}\n\t\t\telse\n\t\t\t\t(void)re_fprintf(f, \"   \");\n\t\t}\n\n\t\t(void)re_fprintf(f, \"    \");\n\n\t\tfor (j=0; j<WIDTH; j++) {\n\t\t\tconst size_t pos = i+j;\n\t\t\tif (pos < alen) {\n\t\t\t\tbool wrong;\n\n\t\t\t\tif (pos < elen)\n\t\t\t\t\twrong = ebuf[pos] != abuf[pos];\n\t\t\t\telse\n\t\t\t\t\twrong = true;\n\n\t\t\t\tif (wrong)\n\t\t\t\t\t(void)re_fprintf(f, \"\\x1b[33m\");\n\t\t\t\t(void)re_fprintf(f, \" %02x\", abuf[pos]);\n\t\t\t\tif (wrong)\n\t\t\t\t\t(void)re_fprintf(f, \"\\x1b[;m\");\n\t\t\t}\n\t\t\telse\n\t\t\t\t(void)re_fprintf(f, \"   \");\n\t\t}\n\n\t\t(void)re_fprintf(f, \"\\n\");\n\t}\n\n\t(void)re_fprintf(f, \"\\n\");\n}\n\n\nstatic void oom_watchdog_timeout(void *arg)\n{\n\tint *err = arg;\n\n\t*err = ETIMEDOUT;\n\n\tre_cancel();\n}\n\n\nstatic void signal_handler(int sig)\n{\n\tre_fprintf(stderr, \"test interrupted by signal %d\\n\", sig);\n\tre_cancel();\n}\n\n\nint re_main_timeout(uint32_t timeout_ms)\n{\n\tstruct tmr tmr;\n\tint err = 0;\n\n\ttmr_init(&tmr);\n\n\tif (timeout_override != 0)\n\t\ttimeout_ms = timeout_override;\n\n#ifdef TEST_TIMEOUT\n\ttimeout_ms = TEST_TIMEOUT;\n#endif\n\n\ttmr_start(&tmr, timeout_ms, oom_watchdog_timeout, &err);\n\t(void)re_main(signal_handler);\n\n\ttmr_cancel(&tmr);\n\treturn err;\n}\n\n\nint test_load_file(struct mbuf *mb, const char *filename)\n{\n\tint err = 0, fd = open(filename, O_RDONLY);\n\tif (fd < 0)\n\t\treturn errno;\n\n\tfor (;;) {\n\t\tuint8_t buf[1024];\n\n\t\tconst ssize_t n = read(fd, (void *)buf, sizeof(buf));\n\t\tif (n < 0) {\n\t\t\terr = errno;\n\t\t\tbreak;\n\t\t}\n\t\telse if (n == 0)\n\t\t\tbreak;\n\n\t\terr = mbuf_write_mem(mb, buf, n);\n\t\tif (err)\n\t\t\tbreak;\n\t}\n\n\t(void)close(fd);\n\n\treturn err;\n}\n\n\nint test_write_file(struct mbuf *mb, const char *filename)\n{\n\tint err = 0, fd = open(filename, O_CREAT | O_WRONLY, 0644);\n\tif (fd < 0)\n\t\treturn errno;\n\n\tfor (;;) {\n\t\tuint8_t buf[1024];\n\t\tsize_t count;\n\n\t\tcount = min(sizeof(buf), mbuf_get_left(mb));\n\t\tif (count == 0)\n\t\t\tbreak;\n\n\t\terr = mbuf_read_mem(mb, buf, count);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tssize_t n = write(fd, (void *)buf, (unsigned int)count);\n\t\tif (n < 0) {\n\t\t\terr = errno;\n\t\t\tbreak;\n\t\t}\n\t\telse if (n == 0)\n\t\t\tbreak;\n\n\t}\n\n\t(void)close(fd);\n\n\treturn err;\n}\n\n\nvoid test_set_datapath(const char *path)\n{\n\tstr_ncpy(datapath, path, sizeof(datapath));\n}\n\n\nconst char *test_datapath(void)\n{\n\treturn datapath;\n}\n\n\nint test_integration(const char *name, bool verbose)\n{\n\tsize_t i;\n\tint err = 0;\n\tconst struct test *test;\n\t(void) verbose;\n\n\t(void)re_fprintf(stderr, \"integration tests\\n\");\n\n\tif (name) {\n\t\ttest = find_test_int(name);\n\t\tif (!test) {\n\t\t\t(void)re_fprintf(stderr, \"no such test: %s\\n\", name);\n\t\t\treturn ENOENT;\n\t\t}\n\n\t\tif (!test->name)\n\t\t\treturn EINVAL;\n\n\t\t(void)re_fprintf(stderr, \"  %-24s: \", test->name);\n\n\t\tif (test->exec)\n\t\t\terr = test->exec();\n\n\t\tif (err)\n\t\t\tDEBUG_WARNING(\"  %-24s: NOK: %m\\n\", test->name, err);\n\t\telse\n\t\t\t(void)re_fprintf(stderr, \"\\x1b[32mOK\\x1b[;m\\n\");\n\n\t\treturn err;\n\t}\n\n\tfor (i=0; i<RE_ARRAY_SIZE(tests_integration); i++) {\n\n\t\ttest = &tests_integration[i];\n\t\tif (str_isset(name) && test->name)\n\t\t\tcontinue;\n\n\t\t(void)re_fprintf(stderr, \"  %-32s: \", test->name);\n\n\t\tif (test->exec)\n\t\t\terr = test->exec();\n\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"  %-24s: NOK: %m\\n\", test->name, err);\n\t\t\tbreak;\n\t\t}\n\t\telse {\n\t\t\t(void)re_fprintf(stderr, \"\\x1b[32mOK\\x1b[;m\\t\\n\");\n\t\t}\n\t}\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/test.h",
    "content": "/**\n * @file test.h  Interface to regression testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\nenum test_mode {\n\tTEST_NONE,\n\tTEST_REGULAR,\n\tTEST_MEMORY,\n\tTEST_PERF,\n\tTEST_THREAD\n};\n\n/*\n * Global test mode\n */\nextern enum test_mode test_mode;\n\n/*\n * Special negative error code for a skipped test\n */\n#define ESKIPPED (-1000)\n\n#define TEST_EINVAL(func, ...)\t\t\t\t\t\\\n\terr = func(__VA_ARGS__);\t\t\t\t\\\n\tif (err != EINVAL)\t\t\t\t\t\\\n\t\tgoto out;\n\n#define TEST_EQUALS(expected, actual)\t\t\t\t\\\n\tif ((expected) != (actual)) {\t\t\t\t\\\n\t\t(void)re_fprintf(stderr, \"\\n\");\t\t\t\\\n\t\tDEBUG_WARNING(\"TEST_EQUALS: %s:%u: %s():\"\t\\\n\t\t\t      \" expected=%d(0x%x), actual=%d(0x%x)\\n\",\t\\\n\t\t\t      __FILE__, __LINE__, __func__,\t\\\n\t\t\t      (expected), (expected),\t\t\\\n\t\t\t      (actual), (actual));\t\t\\\n\t\terr = EINVAL;\t\t\t\t\t\\\n\t\tgoto out;\t\t\t\t\t\\\n\t}\n\n#define TEST_NOT_EQUALS(expected, actual)\t\t\t\\\n\tif ((expected) == (actual)) {\t\t\t\t\\\n\t\t(void)re_fprintf(stderr, \"\\n\");\t\t\t\\\n\t\tDEBUG_WARNING(\"TEST_NOT_EQUALS: %s:%u:\"\t\t\\\n\t\t\t      \" expected=%d != actual=%d\\n\",\t\\\n\t\t\t      __FILE__, __LINE__,\t\t\\\n\t\t\t      (expected), (actual));\t\t\\\n\t\terr = EINVAL;\t\t\t\t\t\\\n\t\tgoto out;\t\t\t\t\t\\\n\t}\n\n#define TEST_MEMCMP(expected, expn, actual, actn)\t\t\t\\\n\tif (expn != actn ||\t\t\t\t\t\t\\\n\t    0 != memcmp((expected), (actual), (expn))) {\t\t\\\n\t\t(void)re_fprintf(stderr, \"\\n\");\t\t\t\t\\\n\t\tDEBUG_WARNING(\"TEST_MEMCMP: %s:%u:\"\t\t\t\\\n\t\t\t      \" %s(): failed\\n\",\t\t\t\\\n\t\t\t      __FILE__, __LINE__, __func__);\t\t\\\n\t\ttest_hexdump_dual(stderr,\t\t\t\t\\\n\t\t\t\t  expected, expn,\t\t\t\\\n\t\t\t\t  actual, actn);\t\t\t\\\n\t\terr = EINVAL;\t\t\t\t\t\t\\\n\t\tgoto out;\t\t\t\t\t\t\\\n\t}\n\n#define TEST_STRCMP(expected, expn, actual, actn)\t\t\t\\\n\tif (expn != actn ||\t\t\t\t\t\t\\\n\t    0 != memcmp((expected), (actual), (expn))) {\t\t\\\n\t\t(void)re_fprintf(stderr, \"\\n\");\t\t\t\t\\\n\t\tDEBUG_WARNING(\"TEST_STRCMP: %s:%u:\"\t\t\t\\\n\t\t\t      \" failed\\n\",\t\t\t\t\\\n\t\t\t      __FILE__, __LINE__);\t\t\t\\\n\t\t(void)re_fprintf(stderr,\t\t\t\t\\\n\t\t\t\t \"expected string: (%zu bytes)\\n\"\t\\\n\t\t\t\t \"\\\"%b\\\"\\n\",\t\t\t\t\\\n\t\t\t\t (size_t)(expn),\t\t\t\\\n\t\t\t\t (expected), (size_t)(expn));\t\t\\\n\t\t(void)re_fprintf(stderr,\t\t\t\t\\\n\t\t\t\t \"actual string: (%zu bytes)\\n\"\t\t\\\n\t\t\t\t \"\\\"%b\\\"\\n\",\t\t\t\t\\\n\t\t\t\t (size_t)(actn),\t\t\t\\\n\t\t\t\t (actual), (size_t)(actn));\t\t\\\n\t\terr = EINVAL;\t\t\t\t\t\t\\\n\t\tgoto out;\t\t\t\t\t\t\\\n\t}\n\n#define TEST_ASSERT(actual)\t\t\t\t\t\t\\\n\tif (!(actual)) {\t\t\t\t\t\t\\\n\t\t(void)re_fprintf(stderr, \"\\n\");\t\t\t\t\\\n\t\tDEBUG_WARNING(\"TEST_ASSERT: %s:%u:\"\t\t\t\\\n\t\t\t      \" actual=%d\\n\",\t\t\t\t\\\n\t\t\t      __FILE__, __LINE__,\t\t\t\\\n\t\t\t      (actual));\t\t\t\t\\\n\t\terr = EINVAL;\t\t\t\t\t\t\\\n\t\tgoto out;\t\t\t\t\t\t\\\n\t}\n\n#define TEST_ERR(err)\t\t\t\t\t\t\t\\\n\tif ((err)) {\t\t\t\t\t\t\t\\\n\t\t(void)re_fprintf(stderr, \"\\n\");\t\t\t\t\\\n\t\tDEBUG_WARNING(\"TEST_ERR: %s:%u:\"\t\t\t\\\n\t\t\t      \" (%m)\\n\",\t\t\t\t\\\n\t\t\t      __FILE__, __LINE__,\t\t\t\\\n\t\t\t      (err));\t\t\t\t\t\\\n\t\tgoto out;\t\t\t\t\t\t\\\n\t}\n\n#define TEST_SACMP(expect, actual, flags)\t\t\t\t\\\n\tif (!sa_cmp((expect), (actual), (flags))) {\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\t(void)re_fprintf(stderr, \"\\n\");\t\t\t\t\\\n\t\tDEBUG_WARNING(\"TEST_SACMP: %s:%u:\"\t\t\t\\\n\t\t\t      \" %s(): failed\\n\",\t\t\t\\\n\t\t\t      __FILE__, __LINE__, __func__);\t\t\\\n\t\tDEBUG_WARNING(\"expected: %J\\n\", (expect));\t\t\\\n\t\tDEBUG_WARNING(\"actual:   %J\\n\", (actual));\t\t\\\n\t\terr = EADDRNOTAVAIL;\t\t\t\t\t\\\n\t\tgoto out;\t\t\t\t\t\t\\\n\t}\n\n/*\n * NOTE: try to reuse macros from Gtest.\n */\n\n#define ASSERT_EQ(expected, actual)\t\t\t\t\t\\\n\tif ((expected) != (actual)) {\t\t\t\t\t\\\n\t\tDEBUG_WARNING(\"ASSERT_EQ: %s:%u: %s():\"\t\t\t\\\n\t\t\t      \" expected=%d(0x%x), actual=%d(0x%x)\\n\",\t\\\n\t\t\t      __FILE__, __LINE__, __func__,\t\t\\\n\t\t\t      (expected), (expected),\t\t\t\\\n\t\t\t      (actual), (actual));\t\t\t\\\n\t\terr = EINVAL;\t\t\t\t\t\t\\\n\t\tgoto out;\t\t\t\t\t\t\\\n\t}\n\n#define ASSERT_DOUBLE_EQ(expected, actual, prec)\t\t\t\\\n\tif (!test_cmp_double((expected), (actual), (prec))) {\t\t\\\n\t\tDEBUG_WARNING(\"selftest: ASSERT_DOUBLE_EQ: %s:%u:\"\t\\\n\t\t\t\" expected=%f, actual=%f\\n\",\t\t\t\\\n\t\t\t__FILE__, __LINE__,\t\t\t\t\\\n\t\t\t(double)(expected), (double)(actual));\t\t\\\n\t\terr = EINVAL;\t\t\t\t\t\t\\\n\t\tgoto out;\t\t\t\t\t\t\\\n\t}\n\n#define ASSERT_TRUE(cond)\t\t\t\t\t\\\n\tif (!(cond)) {\t\t\t\t\t\t\\\n\t\tDEBUG_WARNING(\"ASSERT_TRUE: %s:%u:\\n\",\t\t\\\n\t\t\t      __FILE__, __LINE__);\t\t\\\n\t\terr = EINVAL;\t\t\t\t\t\\\n\t\tgoto out;\t\t\t\t\t\\\n\t}\n\n\n/* Module API */\nint test_aac(void);\nint test_aes(void);\nint test_aes_gcm(void);\nint test_au(void);\nint test_aubuf(void);\nint test_aulevel(void);\nint test_aulength(void);\nint test_auposition(void);\nint test_auresamp(void);\nint test_async(void);\nint test_av1(void);\nint test_dd(void);\nint test_base64(void);\nint test_bfcp(void);\nint test_bfcp_bin(void);\nint test_bfcp_udp(void);\nint test_bfcp_tcp(void);\nint test_btrace(void);\nint test_conf(void);\nint test_crc32(void);\nint test_dbg(void);\nint test_dns_dname(void);\nint test_dns_hdr(void);\nint test_dns_integration(void);\nint test_dns_nameservers(void);\nint test_dns_proto(void);\nint test_dns_reg(void);\nint test_dns_rr(void);\nint test_dns_rr_dup(void);\nint test_dsp(void);\nint test_dtmf(void);\nint test_fir(void);\nint test_fmt_gmtime(void);\nint test_fmt_hexdump(void);\nint test_fmt_human_time(void);\nint test_fmt_param(void);\nint test_fmt_pl(void);\nint test_fmt_pl_alloc_dup(void);\nint test_fmt_pl_alloc_str(void);\nint test_fmt_pl_float(void);\nint test_fmt_pl_i32(void);\nint test_fmt_pl_i64(void);\nint test_fmt_pl_u32(void);\nint test_fmt_pl_u64(void);\nint test_fmt_pl_x3264(void);\nint test_fmt_print(void);\nint test_fmt_regex(void);\nint test_fmt_snprintf(void);\nint test_fmt_str(void);\nint test_fmt_str_bool(void);\nint test_fmt_str_error(void);\nint test_fmt_str_itoa(void);\nint test_fmt_str_wchar(void);\nint test_fmt_timestamp(void);\nint test_fmt_trim(void);\nint test_fmt_unicode(void);\nint test_fmt_unicode_decode(void);\nint test_g711_alaw(void);\nint test_g711_ulaw(void);\nint test_h264(void);\nint test_h264_sps(void);\nint test_h264_packet(void);\nint test_h265(void);\nint test_h265_packet(void);\nint test_hash(void);\nint test_hmac_sha1(void);\nint test_hmac_sha256(void);\nint test_http(void);\nint test_http_loop(void);\nint test_http_large_body(void);\nint test_http_conn(void);\nint test_http_conn_large_body(void);\nint test_dns_http_integration(void);\nint test_dns_cache_http_integration(void);\nint test_http_request_addr(void);\n#ifdef USE_TLS\nint test_https_request_addr(void);\n#endif\n#ifdef USE_TLS\nint test_https_loop(void);\nint test_http_client_set_tls(void);\nint test_https_large_body(void);\n#endif\n#ifdef HAVE_TLS1_3_POST_HANDSHAKE_AUTH\nint test_https_conn_post_handshake(void);\n#endif\nint test_httpauth_chall(void);\nint test_httpauth_resp(void);\nint test_httpauth_basic_request(void);\nint test_httpauth_digest_request(void);\nint test_httpauth_digest_response(void);\nint test_httpauth_digest_verification(void);\nint test_ice_loop(void);\nint test_ice_cand(void);\nint test_json(void);\nint test_json_bad(void);\nint test_json_file(void);\nint test_json_unicode(void);\nint test_json_array(void);\nint test_list(void);\nint test_list_flush(void);\nint test_list_ref(void);\nint test_list_sort(void);\nint test_mbuf(void);\nint test_md5(void);\nint test_mem(void);\nint test_mem_pool(void);\nint test_mem_reallocarray(void);\nint test_mem_secure(void);\nint test_mqueue(void);\nint test_net_if(void);\nint test_net_dst_source_addr_get(void);\nint test_odict(void);\nint test_odict_array(void);\nint test_odict_pl(void);\nint test_pcp(void);\nint test_trice_cand(void);\nint test_trice_candpair(void);\nint test_trice_checklist(void);\nint test_trice_loop(void);\nint test_remain(void);\nint test_re_assert_se(void);\nint test_rtmp_play(void);\nint test_rtmp_publish(void);\n#ifdef USE_TLS\nint test_rtmps_publish(void);\n#endif\nint test_rtp(void);\nint test_rtp_listen(void);\nint test_rtpext(void);\nint test_rtcp_encode(void);\nint test_rtcp_encode_afb(void);\nint test_rtcp_decode(void);\nint test_rtcp_decode_badmsg(void);\nint test_rtcp_packetloss(void);\nint test_rtcp_twcc(void);\nint test_rtcp_xr(void);\nint test_rtcp_loop(void);\nint test_sa_class(void);\nint test_sa_cmp(void);\nint test_sa_decode(void);\nint test_sa_ntop(void);\nint test_sa_pton(void);\nint test_sa_pton_linklocal(void);\nint test_sdp_all(void);\nint test_sdp_bfcp(void);\nint test_sdp_parse(void);\nint test_sdp_oa(void);\nint test_sdp_extmap(void);\nint test_sdp_disabled_rejected(void);\nint test_sdp_interop(void);\nint test_sha1(void);\nint test_sip_addr(void);\nint test_sip_auth(void);\nint test_sip_drequestf(void);\nint test_sip_apply(void);\nint test_sip_hdr(void);\nint test_sip_msg(void);\nint test_sip_param(void);\nint test_sip_parse(void);\nint test_sip_via(void);\nint test_sip_dns(void);\n#ifdef USE_TLS\nint test_sip_transp_add_client_cert(void);\n#endif\nint test_sipevent(void);\nint test_sipreg_udp(void);\nint test_sipreg_tcp(void);\n#ifdef USE_TLS\nint test_sipreg_tls(void);\n#endif\nint test_sipsess(void);\nint test_sipsess_reject(void);\nint test_sipsess_blind_transfer(void);\nint test_sipsess_100rel_caller_require(void);\nint test_sipsess_100rel_supported(void);\nint test_sipsess_100rel_answer_not_allowed(void);\nint test_sipsess_100rel_420(void);\nint test_sipsess_100rel_421(void);\nint test_sipsess_update_uac(void);\nint test_sipsess_update_uas(void);\nint test_sipsess_update_no_sdp(void);\nint test_srtp(void);\nint test_srtcp(void);\nint test_srtp_gcm(void);\nint test_srtcp_gcm(void);\nint test_stun_req(void);\nint test_stun_resp(void);\nint test_stun_reqltc(void);\nint test_stun(void);\nint test_sys_endian(void);\nint test_sys_rand(void);\nint test_sys_fs_fopen(void);\nint test_sys_fs_gethome(void);\nint test_sys_fs_isdir(void);\nint test_sys_fs_isfile(void);\nint test_sys_getenv(void);\nint test_tcp(void);\nint test_tcp_tos(void);\nint test_telev(void);\nint test_text2pcap(void);\nint test_thread(void);\nint test_thread_cnd_timedwait(void);\nint test_thread_tss(void);\nint test_tmr_jiffies(void);\nint test_tmr_jiffies_usec(void);\nint test_try_into(void);\nint test_turn(void);\nint test_turn_tcp(void);\nint test_turn_thread(void);\nint test_udp(void);\nint test_udp_tos(void);\nint test_unixsock(void);\nint test_uri(void);\nint test_uri_encode(void);\nint test_uri_headers(void);\nint test_uri_user(void);\nint test_uri_params_headers(void);\nint test_uri_escape(void);\nint test_vid(void);\nint test_vidconv(void);\nint test_vidconv_scaling(void);\nint test_vidconv_pixel_formats(void);\nint test_websock(void);\nint test_trace(void);\n#ifdef USE_TLS\nint test_dtls(void);\nint test_dtls_srtp(void);\nint test_tls(void);\nint test_tls_ec(void);\nint test_tls_selfsigned(void);\nint test_tls_certificate(void);\nint test_tls_false_cafile_path(void);\nint test_tls_cli_conn_change_cert(void);\nint test_tls_session_reuse_tls_v12(void);\nint test_tls_session_reuse(void);\nint test_tls_sni(void);\n#endif\n\n#ifdef USE_TLS\nint test_dtls_turn(void);\n#endif\n\n\n#ifdef USE_TLS\nextern const char test_certificate_ecdsa[];\n#endif\n\n/* Integration tests */\nint  test_integration(const char *name, bool verbose);\nint  test_sipevent_network(void);\nint  test_sip_drequestf_network(void);\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint  test_cplusplus(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n/* High-level API */\nint  test_reg(const char *name, bool verbose);\nint  test_oom(const char *name, bool verbose);\nint  test_perf(const char *name, bool verbose);\nint  test_multithread(void);\nvoid test_listcases(void);\n\n\nvoid test_hexdump_dual(FILE *f,\n\t\t       const void *ep, size_t elen,\n\t\t       const void *ap, size_t alen);\nbool test_cmp_double(double a, double b, double precision);\nint re_main_timeout(uint32_t timeout_ms);\nint test_load_file(struct mbuf *mb, const char *filename);\nint test_write_file(struct mbuf *mb, const char *filename);\nvoid test_set_datapath(const char *path);\nconst char *test_datapath(void);\nbool test_ipv6_supported(void);\n\n\n/*\n * Mock objects\n */\n\n\nstruct stunserver {\n\tstruct udp_sock *us;\n\tstruct tcp_sock *ts;\n\tstruct tcp_conn *tc;\n\tstruct mbuf *mb;\n\tstruct sa laddr;\n\tstruct sa laddr_tcp;\n\tstruct sa paddr;\n\tuint32_t nrecv;\n\tint err;\n};\n\nint stunserver_alloc(struct stunserver **stunp, const char *laddr);\nconst struct sa *stunserver_addr(const struct stunserver *stun, int proto);\n\n\nstruct turnserver {\n\tstruct udp_sock *us;\n\tstruct sa laddr;\n\tstruct tcp_sock *ts;\n\tstruct sa laddr_tcp;\n\tstruct tcp_conn *tc;\n\tstruct sa paddr;\n\tstruct mbuf *mb;\n\tstruct udp_sock *us_relay;\n\tstruct sa cli;\n\tstruct sa relay;\n\tchar addr[64];\n\tconst char *auth_realm;\n\tuint64_t auth_secret;\n\tuint16_t error_scode;\n\n\tstruct channel {\n\t\tuint16_t nr;\n\t\tstruct sa peer;\n\t} chanv[4];\n\tsize_t chanc;\n\n\tstruct sa permv[4];\n\tsize_t permc;\n\n\tsize_t n_allocate;\n\tsize_t n_createperm;\n\tsize_t n_chanbind;\n\tsize_t n_send;\n\tsize_t n_raw;\n\tsize_t n_recv;\n};\n\nint turnserver_alloc(struct turnserver **turnp, const char *addr);\nvoid turnserver_force_error(struct turnserver *turn, uint16_t scode);\n\n\nenum natbox_type {\n\tNAT_INBOUND_SNAT,  /* NOTE: must be installed on receiving socket */\n\tNAT_FIREWALL,\n};\n\n/**\n * A simple NAT-box that can be hooked onto a UDP-socket.\n *\n * The NAT behaviour is port-preserving and will rewrite the source\n * IP-address to the public address.\n */\nstruct nat {\n\tenum natbox_type type;\n\tstruct sa public_addr;\n\tstruct udp_helper *uh;\n\tstruct udp_sock *us;\n\tstruct sa bindingv[16];\n\tsize_t bindingc;\n};\n\nint nat_alloc(struct nat **natp, enum natbox_type type,\n\t      struct udp_sock *us, const struct sa *public_addr);\n\n\n/*\n * SIP Server\n */\n\nstruct sip_server {\n\tstruct sip *sip;\n\tstruct sip_lsnr *lsnr;\n\tbool terminate;\n\n\tunsigned n_register_req;\n\tunsigned n_options_req;\n\tstruct sip_msg *sip_msgs[16];\n};\n\nint sip_server_alloc(struct sip_server **srvp);\nint sip_server_uri(struct sip_server *srv, char *uri, size_t sz,\n\t\t   enum sip_transp tp);\n\n\n/*\n * Mock DNS-Server\n */\n\nstruct dns_server {\n\tstruct udp_sock *us;\n\tstruct tcp_sock *ts;\n\tstruct sa addr;\n\tstruct sa addr_tcp;\n\tstruct list rrl;\n\n\t/* per TCP-connection: */\n\tstruct tcp_conn *tc;\n\tstruct mbuf *mb;\n\tuint16_t flen;\n};\n\nint dns_server_alloc(struct dns_server **srvp, const char *laddr);\nint dns_server_add_a(struct dns_server *srv, const char *name, uint32_t addr,\n\t\t     int64_t ttl);\nint dns_server_add_aaaa(struct dns_server *srv, const char *name,\n\t\t\tconst uint8_t *addr, int64_t ttl);\nint dns_server_add_srv(struct dns_server *srv, const char *name,\n\t\t       uint16_t pri, uint16_t weight, uint16_t port,\n\t\t       const char *target, int64_t ttl);\nvoid dns_server_flush(struct dns_server *srv);\n"
  },
  {
    "path": "test/thread.c",
    "content": "/**\n * @file src/thread.c re threads\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n\n#include <time.h>\n#include <re.h>\n#include \"test.h\"\n\n#define DEBUG_MODULE \"thread\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic int thread_equal(void *thrd)\n{\n\tthrd_t t = *(thrd_t *)thrd;\n\n\tif (thrd_equal(t, thrd_current()))\n\t\treturn 0;\n\n\treturn EINVAL;\n}\n\n\nstatic int thread(void *id)\n{\n\tint n = *(int *)id;\n\n\tif (n != 42)\n\t\treturn thrd_error;\n\n\treturn EPROTO;\n}\n\n\nint test_thread(void)\n{\n\tthrd_t thr;\n\tthrd_t thr_main = thrd_current();\n\tint err;\n\tint id;\n\n\terr = thread_create_name(&thr, \"test1\", NULL, NULL);\n\tTEST_EQUALS(EINVAL, err);\n\n\tid  = 23;\n\terr = thread_create_name(&thr, \"test2\", thread, (void *)&id);\n\tTEST_ERR(err);\n\tthrd_join(thr, &err);\n\tTEST_EQUALS(thrd_error, err);\n\n\tid  = 42;\n\terr = thread_create_name(&thr, \"test3\", thread, (void *)&id);\n\tTEST_ERR(err);\n\tthrd_join(thr, &err);\n\tTEST_EQUALS(EPROTO, err);\n\n\terr = thread_create_name(&thr, \"test_not_equal\", thread_equal,\n\t\t\t\t &thr_main);\n\tTEST_ERR(err);\n\tthrd_join(thr, &err);\n\tTEST_EQUALS(EINVAL, err);\n\n\terr = thread_create_name(&thr, \"test_equal\", thread_equal, &thr);\n\tTEST_ERR(err);\n\tTEST_EQUALS(0, thrd_equal(thr, thrd_current()));\n\tthrd_join(thr, &err);\n\tTEST_EQUALS(0, err);\n\n\terr = 0;\n\nout:\n\treturn err;\n}\n\n\nint test_thread_cnd_timedwait(void)\n{\n\tcnd_t cnd;\n\tmtx_t mtx;\n\tstruct timespec tp;\n\tint err = 0;\n\n\tcnd_init(&cnd);\n\tmtx_init(&mtx, mtx_plain);\n\n\terr = tmr_timespec_get(&tp, 100);\n\tTEST_ERR(err);\n\n\tmtx_lock(&mtx);\n\n\tuint64_t start = tmr_jiffies();\n\tint ret = cnd_timedwait(&cnd, &mtx, &tp);\n\tTEST_EQUALS(thrd_timedout, ret);\n\tuint64_t end = tmr_jiffies();\n\n\t/* This tests can fail if a spurious wake-up occurs */\n\tif (end - start < 100) {\n\t\tDEBUG_WARNING(\"cnd_timedwait: early wake-up!\\n\");\n\t\tgoto out;\n\t}\n\n\tif (end - start > 500) {\n\t\terr = ETIMEDOUT;\n\t\tTEST_ERR(err);\n\t}\n\nout:\n\tmtx_unlock(&mtx);\n\treturn err;\n}\n\n\nint test_thread_tss(void)\n{\n\tint err = 0;\n\tint val = 1234;\n\ttss_t key;\n\n\tTEST_EQUALS(thrd_success, tss_create(&key, NULL));\n\n\tTEST_EQUALS(thrd_success, tss_set(key, &val));\n\n\tTEST_EQUALS(&val, tss_get(key));\n\n\tTEST_EQUALS(thrd_success, tss_set(key, NULL));\n\n\tTEST_EQUALS(NULL, tss_get(key));\n\nout:\n\ttss_delete(key);\n\treturn err;\n}\n"
  },
  {
    "path": "test/tls.c",
    "content": "/**\n * @file tls.c  TLS testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <openssl/ssl.h>\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"tlstest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstruct tls_test {\n\tstruct tls *tls;\n\tstruct tls *tls2;\n\tstruct tls_conn *sc_cli;\n\tstruct tls_conn *sc_srv;\n\tstruct tcp_sock *ts;\n\tstruct tcp_conn *tc_cli;\n\tstruct tcp_conn *tc_srv;\n\tenum tls_keytype keytype;\n\tbool estab_cli;\n\tbool estab_srv;\n\tbool send_done_cli;\n\tsize_t recv_cli;\n\tsize_t recv_srv;\n\tint err;\n};\n\n\nstatic const char *payload = \"0123456789\";\n\n\nstatic void check(struct tls_test *tt, int err)\n{\n\tif (tt->err == 0)\n\t\ttt->err = err;\n\n\tif (tt->err)\n\t\tre_cancel();\n}\n\n\nstatic void can_send(struct tls_test *tt)\n{\n\tstruct mbuf *mb;\n\tint err = 0;\n\n\tif (!tt->estab_cli || !tt->estab_srv || tt->send_done_cli)\n\t\treturn;\n\n\tmb = mbuf_alloc(256);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = mbuf_write_str(mb, payload);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = 0;\n\terr = tcp_send(tt->tc_cli, mb);\n\n\tif (!err)\n\t\ttt->send_done_cli = true;\n\n out:\n\tmem_deref(mb);\n\n\tcheck(tt, err);\n}\n\n\nstatic void client_estab_handler(void *arg)\n{\n\tstruct tls_test *tt = arg;\n\tint err = 0;\n\n\ttt->estab_cli = true;\n\tcan_send(tt);\n\n\tcheck(tt, err);\n}\n\n\nstatic void client_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct tls_test *tt = arg;\n\tint err = 0;\n\n\tif (!tt->estab_cli) {\n\t\t(void)re_fprintf(stderr, \"unexpected data received\"\n\t\t\t\t \" on client [%02w]\\n\",\n\t\t\t\t mbuf_buf(mb), mbuf_get_left(mb));\n\t\tcheck(tt, EPROTO);\n\t}\n\n\t++tt->recv_cli;\n\n\tTEST_MEMCMP(payload, strlen(payload),\n\t\t\tmbuf_buf(mb), mbuf_get_left(mb));\n\n out:\n\tcheck(tt, err);\n\n\t/* We are done */\n\tre_cancel();\n}\n\n\nstatic void client_close_handler(int err, void *arg)\n{\n\tstruct tls_test *tt = arg;\n\n\tif (!tt->estab_cli)\n\t\tcheck(tt, err);\n}\n\n\nstatic void server_estab_handler(void *arg)\n{\n\tstruct tls_test *tt = arg;\n\ttt->estab_srv = true;\n\tcan_send(tt);\n}\n\n\nstatic void server_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct tls_test *tt = arg;\n\tint err = 0;\n\n\tif (!tt->estab_srv) {\n\t\tcheck(tt, EPROTO);\n\t\treturn;\n\t}\n\n\t++tt->recv_srv;\n\n\tTEST_MEMCMP(payload, strlen(payload),\n\t\t    mbuf_buf(mb), mbuf_get_left(mb));\n\n\t/* echo */\n\terr = tcp_send(tt->tc_srv, mb);\n\tif (err) {\n\t\tDEBUG_WARNING(\"server: tcp_send error (%m)\\n\", err);\n\t}\n\n out:\n\tcheck(tt, err);\n}\n\n\nstatic void server_close_handler(int err, void *arg)\n{\n\tstruct tls_test *tt = arg;\n\n\tif (!tt->estab_cli)\n\t\tcheck(tt, err);\n}\n\n\nstatic void server_conn_handler(const struct sa *peer, void *arg)\n{\n\tstruct tls_test *tt = arg;\n\tint err;\n\t(void)peer;\n\n\terr = tcp_accept(&tt->tc_srv, tt->ts, server_estab_handler,\n\t\t\t server_recv_handler, server_close_handler, tt);\n\tcheck(tt, err);\n\n\terr = tls_start_tcp(&tt->sc_srv, tt->tls, tt->tc_srv, 0);\n\tcheck(tt, err);\n}\n\n\nstatic int test_tls_base(enum tls_keytype keytype, bool add_ca, int exp_verr,\n\tbool test_sess_reuse, int forced_version)\n{\n\tstruct tls_test tt;\n\tstruct sa srv;\n\tint err, verr;\n\tunsigned long int i, rounds = 1 + (unsigned long int) test_sess_reuse;\n\n\tmemset(&tt, 0, sizeof(tt));\n\n\ttt.keytype = keytype;\n\n\terr = sa_set_str(&srv, \"127.0.0.1\", 0);\n\tif (err)\n\t\tgoto out;\n\n\terr = tls_alloc(&tt.tls, TLS_METHOD_SSLV23, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\tif (forced_version >= 0) {\n\t\tTEST_EQUALS(0,\n\t\t\ttls_set_min_proto_version(tt.tls, forced_version));\n\t\tTEST_EQUALS(0,\n\t\t\ttls_set_max_proto_version(tt.tls, forced_version));\n\t}\n\n\tswitch (keytype) {\n\n\tcase TLS_KEYTYPE_EC:\n\t\terr = tls_set_certificate(tt.tls, test_certificate_ecdsa,\n\t\t\t\t\t  strlen(test_certificate_ecdsa));\n\t\tif (err)\n\t\t\tgoto out;\n\t\tbreak;\n\n\tdefault:\n\t\terr = EINVAL;\n\t\tgoto out;\n\t}\n\n\tif (add_ca) {\n\t\tchar cafile[256];\n\n\t\tre_snprintf(cafile, sizeof(cafile), \"%s/server-ecdsa.pem\",\n\t\t\t    test_datapath());\n\n\t\terr = tls_add_ca(tt.tls, cafile);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = tcp_listen(&tt.ts, &srv, server_conn_handler, &tt);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_sock_local_get(tt.ts, &srv);\n\tif (err)\n\t\tgoto out;\n\n\terr = tls_set_session_reuse(tt.tls, test_sess_reuse);\n\tif (err)\n\t\tgoto out;\n\n\tfor (i = 0; i < rounds; i++) {\n\t\ttt.send_done_cli = false;\n\t\terr = tcp_connect(&tt.tc_cli, &srv, client_estab_handler,\n\t\t\tclient_recv_handler, client_close_handler, &tt);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = tls_start_tcp(&tt.sc_cli, tt.tls, tt.tc_cli, 0);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (exp_verr == 0) {\n\n\t\t\terr = tls_set_verify_server(tt.sc_cli, \"127.0.0.1\");\n\t\t\tif (err)\n\t\t\t\tgoto out;\n\t\t}\n\n\t\terr = re_main_timeout(800);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (tt.err) {\n\t\t\terr = tt.err;\n\t\t\tgoto out;\n\t\t}\n\n\t\tTEST_EQUALS(true, tt.estab_cli);\n\t\tTEST_EQUALS(true, tt.estab_srv);\n\t\tTEST_EQUALS(1, tt.recv_cli);\n\t\tTEST_EQUALS(1+i, tt.recv_srv);\n\n\t\tverr = tls_peer_verify(tt.sc_cli);\n\t\tTEST_EQUALS(exp_verr, verr);\n\n\t\tif (test_sess_reuse) {\n\t\t\tTEST_EQUALS(i == 0 ? false : true,\n\t\t\t\ttls_session_reused(tt.sc_cli));\n\t\t}\n\n\t\ttt.sc_cli = mem_deref(tt.sc_cli);\n\t\ttt.sc_srv = mem_deref(tt.sc_srv);\n\t\ttt.tc_cli = mem_deref(tt.tc_cli);\n\t\ttt.tc_srv = mem_deref(tt.tc_srv);\n\t\ttt.estab_cli = false;\n\t\ttt.estab_srv = false;\n\t\ttt.recv_cli = 0;\n\t}\n\n out:\n\t/* NOTE: close context first */\n\tmem_deref(tt.tls);\n\n\tmem_deref(tt.sc_cli);\n\tmem_deref(tt.sc_srv);\n\tmem_deref(tt.tc_cli);\n\tmem_deref(tt.tc_srv);\n\tmem_deref(tt.ts);\n\n\treturn err;\n}\n\n\nint test_tls_session_reuse_tls_v12(void)\n{\n\treturn test_tls_base(TLS_KEYTYPE_EC, false, EAUTH, true,\n\t\tTLS1_2_VERSION);\n}\n\n\n/* TLS v1.3 session reuse is not yet supported by libre */\nint test_tls_session_reuse(void)\n{\n\treturn test_tls_base(TLS_KEYTYPE_EC, false, EAUTH, true, -1);\n}\n\n\nint test_tls(void)\n{\n\treturn test_tls_base(TLS_KEYTYPE_EC, false, EAUTH, false, -1);\n}\n\n\nint test_tls_ec(void)\n{\n\tint err;\n\n\terr = test_tls_base(TLS_KEYTYPE_EC, false, EAUTH, false, -1);\n\tTEST_ERR(err);\n\n\terr = test_tls_base(TLS_KEYTYPE_EC, true, 0, false, -1);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\nint test_tls_selfsigned(void)\n{\n\tstruct tls *tls = NULL;\n\tuint8_t fp[32];\n\tint err;\n\n\terr = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\terr = tls_set_selfsigned_ec(tls, \"re_ec@test\", \"unknown\");\n\tTEST_EQUALS(err, ENOTSUP);\n\n\terr = tls_set_selfsigned_ec(tls, \"re_ec@test\", \"prime256v1\");\n\tTEST_ERR(err);\n\n\t/* verify fingerprint of the self-signed certificate */\n\terr = tls_fingerprint(tls, TLS_FINGERPRINT_SHA256, fp, sizeof(fp));\n\tTEST_ERR(err);\n\n out:\n\tmem_deref(tls);\n\treturn err;\n}\n\n\nint test_tls_certificate(void)\n{\n\tstruct tls *tls = NULL;\n\tre_nonstring static const uint8_t test_fingerprint[32] =\n\t\t\"\\x50\\x5d\\x95\\x2b\\xef\\x5b\\x6f\\x7f\"\n\t\t\"\\x2b\\x4a\\xa8\\x1b\\xdd\\xe1\\x99\\xfd\"\n\t\t\"\\x4e\\xb5\\xc1\\x04\\xe7\\x67\\xa7\\x48\"\n\t\t\"\\xb1\\xf1\\x66\\x35\\x98\\xdc\\x84\\xc6\";\n\tuint8_t fp[32];\n\tstruct mbuf *mb = NULL;\n\tint err;\n\n\terr = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\tmb = mbuf_alloc(20);\n\tif (!mb) {\n\t\terr = ENOMEM;\n\t\tgoto out;\n\t}\n\n\terr = tls_get_subject(tls, mb);\n\tTEST_EQUALS(ENOENT, err);\n\n\terr = tls_set_certificate(tls, test_certificate_ecdsa,\n\t\t\t\t  strlen(test_certificate_ecdsa));\n\tTEST_EQUALS(0, err);\n\n\t/* verify fingerprint of the certificate */\n\terr = tls_fingerprint(tls, TLS_FINGERPRINT_SHA256, fp, sizeof(fp));\n\tTEST_ERR(err);\n\n\tTEST_MEMCMP(test_fingerprint, sizeof(test_fingerprint),\n\t\t    fp, sizeof(fp));\n\n\terr = tls_get_subject(tls, mb);\n\tTEST_ERR(err);\n\n out:\n\tmem_deref(tls);\n\tmem_deref(mb);\n\treturn err;\n}\n\n\nint test_tls_false_cafile_path(void)\n{\n\tint err = 0;\n\tstruct tls *tls = NULL;\n\tconst char *cafile_wrong = \"/some/path/to/wrong/file.crt\";\n\tconst char *capath_wrong = \"/some/path/to/nothing\";\n\n\terr = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\terr = tls_add_cafile_path(tls, NULL, NULL);\n\tTEST_EQUALS(EINVAL, err);\n\n\terr = tls_add_cafile_path(tls, cafile_wrong, NULL);\n\tTEST_EQUALS(ENOENT, err);\n\n\terr = tls_add_cafile_path(tls, NULL, capath_wrong);\n\tTEST_EQUALS(ENOTDIR, err);\n\n\terr = tls_add_cafile_path(tls, cafile_wrong, capath_wrong);\n\tTEST_EQUALS(ENOTDIR, err);\n\n\terr = 0;\n\n  out:\n\tmem_deref(tls);\n\treturn err;\n}\n\n\nint test_tls_cli_conn_change_cert(void)\n{\n\tstruct tls_test tt;\n\tstruct sa srv;\n\tint err;\n\tchar clientcert[256];\n\tchar clientcert_cn[256];\n\tchar *exp_clientcert_cn = \"Mr Retest Client Cert\";\n\n\tmemset(&tt, 0, sizeof(tt));\n\n\ttt.keytype = TLS_KEYTYPE_EC;\n\n\terr = sa_set_str(&srv, \"127.0.0.1\", 0);\n\tif (err)\n\t\tgoto out;\n\n\terr = tls_alloc(&tt.tls, TLS_METHOD_SSLV23, NULL, NULL);\n\tif (err)\n\t\tgoto out;\n\n\ttls_set_verify_client_trust_all(tt.tls);\n\n\terr = tls_set_certificate(tt.tls, test_certificate_ecdsa,\n\t\tstrlen(test_certificate_ecdsa));\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_listen(&tt.ts, &srv, server_conn_handler, &tt);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_sock_local_get(tt.ts, &srv);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_connect(&tt.tc_cli, &srv, client_estab_handler,\n\t\t\t  client_recv_handler, client_close_handler, &tt);\n\tif (err)\n\t\tgoto out;\n\n\terr = tls_start_tcp(&tt.sc_cli, tt.tls, tt.tc_cli, 0);\n\tif (err)\n\t\tgoto out;\n\n\t/* actual test cases*/\n\terr = tls_conn_change_cert(tt.sc_cli, NULL);\n\tTEST_EQUALS(EINVAL, err);\n\n\terr = tls_conn_change_cert(NULL, clientcert);\n\tTEST_EQUALS(EINVAL, err);\n\n\tmemset(clientcert, 0, sizeof(clientcert));\n\t(void)re_snprintf(clientcert, sizeof(clientcert),\n\t\t\"%s/not_a_file.pem\", test_datapath());\n\n\tint ret = tls_conn_change_cert(tt.sc_cli, clientcert);\n\tASSERT_TRUE(ret==EINVAL || ret==ENOENT || ret==ENOSYS);\n\n\tif (ret == ENOSYS) {\n\t\terr = 0;\n\t\tgoto out;\n\t}\n\n\tmemset(clientcert, 0, sizeof(clientcert));\n\t(void)re_snprintf(clientcert, sizeof(clientcert),\n\t\t\"%s/client_wrongkey.pem\", test_datapath());\n\n\terr = tls_conn_change_cert(tt.sc_cli, clientcert);\n\tTEST_EQUALS(EKEYREJECTED, err);\n\n\tmemset(clientcert, 0, sizeof(clientcert));\n\t(void)re_snprintf(clientcert, sizeof(clientcert), \"%s/client.pem\",\n\t\ttest_datapath());\n\n\terr = tls_conn_change_cert(tt.sc_cli, clientcert);\n\tif (err)\n\t\tgoto out;\n\n\terr = re_main_timeout(800);\n\tif (err)\n\t\tgoto out;\n\n\terr = tls_peer_common_name(tt.sc_srv, clientcert_cn,\n\t\tsizeof(clientcert_cn));\n\tif (err) {\n\t\tif (!tt.sc_srv) {\n\t\t\tTEST_EQUALS(EINVAL, err);\n\t\t\terr = 0;\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\tTEST_STRCMP(exp_clientcert_cn, strlen(exp_clientcert_cn),\n\t\tclientcert_cn, strlen(clientcert_cn));\n\n\tif (tt.err) {\n\t\terr = tt.err;\n\t\tgoto out;\n\t}\n\n out:\n\t/* NOTE: close context first */\n\tmem_deref(tt.tls);\n\n\tmem_deref(tt.sc_cli);\n\tmem_deref(tt.sc_srv);\n\tmem_deref(tt.tc_cli);\n\tmem_deref(tt.tc_srv);\n\tmem_deref(tt.ts);\n\n\treturn err;\n}\n\n\n/**\n * SNI Test\n *\n * - UAS opens a TLS connection to UAC by sending a client hello with SNI\n * - UAC chooses a certificate with matching SNI\n * - UAC verifies the chain and common name of the UAS\n * - UAS verifies the chain and common name of the UAC\n *\n * @return 0 if success, otherwise errorcode\n */\nint test_tls_sni(void)\n{\n\tstruct tls_test tt;\n\tstruct sa srv;\n\tint err;\n\tconst char *dp = test_datapath();\n\tchar path[256];\n\n\tmemset(&tt, 0, sizeof(tt));\n\n\terr = sa_set_str(&srv, \"127.0.0.1\", 0);\n\tTEST_ERR(err);\n\n\t/* UAC global not matching cert (test checks that it is not used) */\n\tre_snprintf(path, sizeof(path), \"%s/client.pem\", dp);\n\terr = tls_alloc(&tt.tls, TLS_METHOD_SSLV23, path, NULL);\n\tTEST_ERR(err);\n\n\t/* UAS cert + intermediate CA */\n\tre_snprintf(path, sizeof(path), \"%s/sni/server-interm.pem\", dp);\n\terr = tls_alloc(&tt.tls2, TLS_METHOD_SSLV23, path, NULL);\n\tTEST_ERR(err);\n\n\t/* set root CA at UAC and UAS */\n\tre_snprintf(path, sizeof(path), \"%s/sni/root-ca.pem\", dp);\n\terr  = tls_add_ca(tt.tls, path);\n\terr |= tls_add_ca(tt.tls2, path);\n\tTEST_ERR(err);\n\n\t/* UAC cert + intermediate CA */\n\tre_snprintf(path, sizeof(path), \"%s/sni/client-interm.pem\", dp);\n\terr = tls_add_certf(tt.tls, path, \"retest.server.org\");\n\tTEST_ERR(err);\n\n\t/* UAC listens (as TLS server)*/\n\terr = tcp_listen(&tt.ts, &srv, server_conn_handler, &tt);\n\tTEST_ERR(err);\n\n\terr = tcp_sock_local_get(tt.ts, &srv);\n\tTEST_ERR(err);\n\n\t/* UAS connects to UAC (as TLS client) */\n\terr = tcp_connect(&tt.tc_cli, &srv, client_estab_handler,\n\t\t\t  client_recv_handler, client_close_handler, &tt);\n\tTEST_ERR(err);\n\n\terr = tls_start_tcp(&tt.sc_cli, tt.tls2, tt.tc_cli, 0);\n\tTEST_ERR(err);\n\n\terr = tls_set_verify_server(tt.sc_cli, \"retest.client.org\");\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(800);\n\tTEST_ERR(err);\n\n\tif (tt.err) {\n\t\terr = tt.err;\n\t\tgoto out;\n\t}\n\n\tASSERT_TRUE(tt.estab_cli);\n\tASSERT_TRUE(tt.estab_srv);\n\tASSERT_EQ(1, tt.recv_cli);\n\tASSERT_EQ(1, tt.recv_srv);\n\n\terr = tls_peer_verify(tt.sc_cli);\n\tASSERT_EQ(0, err);\n\n out:\n\t/* NOTE: close context first */\n\tmem_deref(tt.tls);\n\tmem_deref(tt.tls2);\n\tmem_deref(tt.sc_cli);\n\tmem_deref(tt.sc_srv);\n\tmem_deref(tt.tc_cli);\n\tmem_deref(tt.tc_srv);\n\tmem_deref(tt.ts);\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/tmr.c",
    "content": "/**\n * @file tmr.c  Timers testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"testtmr\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_tmr_jiffies(void)\n{\n\tuint64_t tmr_start, tmr_end, diff;\n\tint err = 0;\n\n\ttmr_start = tmr_jiffies();\n\tsys_msleep(1);\n\ttmr_end = tmr_jiffies();\n\tdiff = tmr_end - tmr_start;\n\n\tTEST_ASSERT(diff >= 1);\n\tTEST_ASSERT(diff < 50);\n\nout:\n\treturn err;\n}\n\n\nint test_tmr_jiffies_usec(void)\n{\n\tuint64_t tmr_start, diff;\n\tint i;\n\tint err = 0;\n\n\ttmr_start = tmr_jiffies_usec();\n\tdiff = 0;\n\tfor (i = 0; i < 100000 && !diff; i++)\n\t\tdiff = tmr_jiffies_usec() - tmr_start;\n\n\tTEST_ASSERT(diff >= 1);\n\tTEST_ASSERT(diff < 1000);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/trace.c",
    "content": "/**\n * @file trace.c  Trace testcode\n */\n\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include <re.h>\n#include \"test.h\"\n\n#define DEBUG_MODULE \"test_trace\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\nstatic void test_loop(int count)\n{\n\tint i;\n\n\tfor (i=0; i < count; i++) {\n\t\tRE_TRACE_INSTANT_I(\"test\", \"Instant\", i);\n\t}\n}\n\nint test_trace(void)\n{\n\tint err;\n\n\tif (test_mode == TEST_THREAD)\n\t\treturn ESKIPPED;\n\n\terr = re_trace_init(\"test_trace.json\");\n\tTEST_ERR(err);\n\n\tRE_TRACE_PROCESS_NAME(\"retest\");\n\tRE_TRACE_THREAD_NAME(\"test_trace\");\n\tRE_TRACE_BEGIN(\"test\", \"Test Loop Start\");\n\n\ttest_loop(100);\n\n\tRE_TRACE_BEGIN(\"test\", \"Flush\");\n\terr = re_trace_flush();\n\tTEST_ERR(err);\n\n\tRE_TRACE_END(\"test\", \"Flush\");\n\n\ttest_loop(25);\n\n\tRE_TRACE_BEGIN_FUNC();\n\n\terr = re_trace_flush();\n\tTEST_ERR(err);\n\n\tRE_TRACE_END_FUNC();\n\n\tRE_TRACE_END(\"test\", \"Test Loop End\");\n\n\terr = re_trace_close();\n\tTEST_ERR(err);\n\n\t/* Test TRACE after close - should do nothing */\n\tRE_TRACE_BEGIN(\"test\", \"test after close\");\n\n#ifdef WIN32\n\t(void)_unlink(\"test_trace.json\");\n#else\n\t(void)unlink(\"test_trace.json\");\n#endif\n\nout:\n\tif (err)\n\t\tre_trace_close();\n\treturn err;\n}\n"
  },
  {
    "path": "test/trice.c",
    "content": "/**\n * @file trice.c Trickle-ICE Testcode\n *\n * Copyright (C) 2010 - 2022 Alfred E. Heggestad\n */\n\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_trice\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#define DEBUG 0\n#define COMPID 1\n\n\nstruct fixture {\n\tstruct trice *icem;\n\tstruct sa laddr;\n\tbool controlling;\n\tchar lufrag[8];\n\tchar lpwd[24];\n\tchar rufrag[8];\n\tchar rpwd[24];\n\n\tstruct trice *icem2;\n\n\t/* result: */\n\tint err;\n\n\tunsigned n_expected_estabh;\n\tbool cancel_on_both;\n\n\t/* counters: */\n\tunsigned n_estabh;\n\tunsigned n_failh;\n\tunsigned n_estabh2;\n\tunsigned n_failh2;\n\n\t/* NAT */\n\tstruct nat *nat;\n\tstruct nat *nat2;\n};\n\n\n/*\n * Helper macros\n */\n\n\n#define FIXTURE_INIT\t\t\t\t\\\n\tstruct fixture _f, *f = &_f;\t\t\\\n\tint err = fixture_init(f);\t\t\\\n\tif (err)\t\t\t\t\\\n\t\tgoto out;\t\t\t\\\n\n\n/* todo: 'addr' used as 'base_addr' (hack) */\n#define ADD_LOCAL_SRFLX_CANDIDATE(proto, prio, addr)\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tdo {\t\t\t\t\t\t\t\t\\\n\t\tstruct ice_lcand *_lcand;\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\terr = trice_lcand_add(&_lcand, f->icem,\t\\\n\t\t\t\t      COMPID, (proto),\t\t\t\\\n\t\t\t\t      (prio), (addr), (addr),\t\t\\\n\t\t\t\t      ICE_CAND_TYPE_SRFLX, (addr),\t\\\n\t\t\t\t      0, NULL, 0);\t\t\t\\\n\t\tif (err) goto out;\t\t\t\t\t\\\n\t\tTEST_ASSERT(_lcand != NULL);\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t} while (0);\n\n#define add_local_udp_candidate_use(addr) \\\n\t\t\t\t\t\t   \\\n\tdo {\t\t\t\t\t\t\t\t\\\n\t\tstruct ice_lcand *_lcand;\t\t\t\t\\\n\t\tuint32_t _prio;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\t_prio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, 1);\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\terr = trice_lcand_add(&_lcand, f->icem, 1,\t\\\n\t\t\t\t      IPPROTO_UDP, _prio,\t\t\\\n\t\t\t\t      addr, NULL,\t\t\t\\\n\t\t\t\t      ICE_CAND_TYPE_HOST, NULL,\t\t\\\n\t\t\t\t      0, NULL, 0);\t\t\t\\\n\t\tif (err) goto out;\t\t\t\t\t\\\n\t\tTEST_ASSERT(_lcand != NULL);\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t} while (0);\n\n#define add_local_tcp_candidate_use(addr, tcptype) \\\n\t\t\t\t\t\t   \\\n\tdo {\t\t\t\t\t\t\t\t\\\n\t\tstruct ice_lcand *_lcand;\t\t\t\t\\\n\t\tuint32_t _prio;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\t_prio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, 1);\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\terr = trice_lcand_add(&_lcand, f->icem, 1,\t\\\n\t\t\t\t      IPPROTO_TCP, _prio,\t\t\\\n\t\t\t\t      addr, NULL,\t\t\t\\\n\t\t\t\t      ICE_CAND_TYPE_HOST, NULL,\t\t\\\n\t\t\t\t      tcptype, NULL, 0);\t\t\\\n\t\tif (err) goto out;\t\t\t\t\t\\\n\t\tTEST_ASSERT(_lcand != NULL);\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t} while (0);\n\n\n#define add_local_tcp_candidate_use2(addr, tcptype) \\\n\t\t\t\t\t\t   \\\n\tdo {\t\t\t\t\t\t\t\t\\\n\t\tstruct ice_lcand *_lcand;\t\t\t\t\\\n\t\tuint32_t _prio;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\t_prio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, 1);\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\terr = trice_lcand_add(&_lcand, f->icem2, 1,\t\\\n\t\t\t\t      IPPROTO_TCP, _prio,\t\t\\\n\t\t\t\t      addr, NULL,\t\t\t\\\n\t\t\t\t      ICE_CAND_TYPE_HOST, NULL,\t\t\\\n\t\t\t\t      tcptype, NULL, 0);\t\t\\\n\t\tif (err) goto out;\t\t\t\t\t\\\n\t\tTEST_ASSERT(_lcand != NULL);\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t} while (0);\n\n\n#define ADD_REMOTE_HOST_CANDIDATE(addr)\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\tdo {\t\t\t\t\t\t\t\t\\\n\t\tuint32_t _prio;\t\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\t_prio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, 1);\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t\terr = trice_rcand_add(NULL, f->icem,\t\t\\\n\t\t\t\t\t      1, \"FND\",\t\t\t\\\n\t\t\t\t\t      IPPROTO_UDP,\t\t\\\n\t\t\t\t\t      _prio,\t\t\t\\\n\t\t\t\t\t      addr,\t\t\t\\\n\t\t\t\t\t      ICE_CAND_TYPE_HOST, 0);\t\\\n\t\tif (err) goto out;\t\t\t\t\t\\\n\t\t\t\t\t\t\t\t\t\\\n\t} while (0);\n\n#define CHECKLIST_START(fix)\t\t\t\t\t\\\n\terr = trice_checklist_start((fix)->icem, NULL, 1,\t\\\n\t\t\t\t   ice_estab_handler,\t\t\\\n\t\t\t\t   ice_failed_handler, (fix));\t\\\n\tTEST_ERR(err);\t\t\t\t\t\t\\\n\n\nstatic void fixture_abort(struct fixture *f, int err);\n\n\nstatic bool verify_sorted(const struct list *pairl)\n{\n\tstruct le *le;\n\tuint64_t pprio = 0;\n\n\tif (!pairl)\n\t\treturn false;\n\n\tfor (le = list_head(pairl); le; le = le->next) {\n\n\t\tstruct ice_candpair *pair = le->data;\n\n\t\tif (!pprio) {\n\t\t\tpprio = pair->pprio;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (pair->pprio > pprio) {\n\t\t\tDEBUG_WARNING(\"unsorted list: %llu > %llu\\n\",\n\t\t\t\t      pair->pprio, pprio);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\nstatic bool are_both_established(const struct fixture *f)\n{\n\tif (!f)\n\t\treturn false;\n\treturn f->n_estabh > 0 && f->n_estabh2 > 0;\n}\n\n\nstatic void ice_estab_handler(struct ice_candpair *pair,\n\t\t\t      const struct stun_msg *msg, void *arg)\n{\n\tstruct fixture *f = arg;\n\tint err = 0;\n\n\t++f->n_estabh;\n\n\t/* TODO: save candidate-pairs, and compare in the test */\n\n\tTEST_ASSERT(msg != NULL);\n\tTEST_ASSERT(pair != NULL);\n\tTEST_ASSERT(pair->lcand != NULL);\n\tTEST_ASSERT(pair->rcand != NULL);\n\tTEST_ASSERT(pair->valid);\n\tTEST_EQUALS(ICE_CANDPAIR_SUCCEEDED, pair->state);\n\tTEST_ERR(pair->err);\n\tTEST_EQUALS(0, pair->scode);\n\n\tTEST_ASSERT((ICE_CAND_TYPE_HOST == pair->rcand->attr.type) ||\n\t\t    (ICE_CAND_TYPE_PRFLX == pair->rcand->attr.type));\n\n\t/* exit criteria */\n\tif (f->n_expected_estabh && f->n_estabh >= f->n_expected_estabh) {\n\t\tfixture_abort(f, 0);\n\t}\n\n\tif (f->cancel_on_both && are_both_established(f)) {\n\t\tfixture_abort(f, 0);\n\t}\n\n out:\n\tif (err)\n\t\tfixture_abort(f, err);\n}\n\n\nstatic void ice_failed_handler(int err, uint16_t scode,\n\t\t\t       struct ice_candpair *pair, void *arg)\n{\n\tstruct fixture *f = arg;\n\t(void)err;\n\t(void)scode;\n\t(void)pair;\n\n\t++f->n_failh;\n\n\tif (trice_checklist_iscompleted(f->icem)) {\n\t\tre_cancel();\n\t}\n}\n\n\nstatic void ice_estab_handler2(struct ice_candpair *pair,\n\t\t\t       const struct stun_msg *msg, void *arg)\n{\n\tstruct fixture *f = arg;\n\t(void)pair;\n\t(void)msg;\n\n\t++f->n_estabh2;\n\n\tif (f->cancel_on_both && are_both_established(f)) {\n\t\tfixture_abort(f, 0);\n\t}\n}\n\n\nstatic void ice_failed_handler2(int err, uint16_t scode,\n\t\t\t\tstruct ice_candpair *pair, void *arg)\n{\n\tstruct fixture *f = arg;\n\t(void)err;\n\t(void)scode;\n\t(void)pair;\n\n\tre_printf(\"  ~ ice2 closed (%m)\\n\", err);\n\n\t++f->n_failh2;\n\n#if 0\n\tre_cancel();\n#endif\n}\n\n\nstatic int fixture_init(struct fixture *f)\n{\n\tconst struct trice_conf conf = {\n\t\t.debug = DEBUG\n\t};\n\tint err;\n\n\tif (!f)\n\t\treturn EINVAL;\n\n\tmemset(f, 0, sizeof(*f));\n\n\tf->controlling = true;\n\n\trand_str(f->lufrag, sizeof(f->lufrag));\n\trand_str(f->lpwd, sizeof(f->lpwd));\n\trand_str(f->rufrag, sizeof(f->rufrag));\n\trand_str(f->rpwd, sizeof(f->rpwd));\n\n\terr = trice_alloc(&f->icem, &conf,\n\t\t  f->controlling ? ICE_ROLE_CONTROLLING : ICE_ROLE_CONTROLLED,\n\t\t\t  f->lufrag, f->lpwd);\n\tTEST_ERR(err);\n\n\tTEST_ASSERT(f->icem != NULL);\n\n\terr = trice_set_remote_ufrag(f->icem, f->rufrag);\n\tTEST_ERR(err);\n\n\terr = trice_set_remote_pwd(f->icem, f->rpwd);\n\tTEST_ERR(err);\n\n\terr = sa_set_str(&f->laddr, \"127.0.0.1\", 0);\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n\n\nstatic void fixture_close(struct fixture *f)\n{\n\tif (!f)\n\t\treturn;\n\n\tf->nat = mem_deref(f->nat);\n\tf->nat2 = mem_deref(f->nat2);\n\n\tf->icem2 = mem_deref(f->icem2);\n\tf->icem = mem_deref(f->icem);\n}\n\n\nstatic void fixture_abort(struct fixture *f, int err)\n{\n\tf->err = err;\n\tre_cancel();\n}\n\n\n/* ... TEST CASES ... */\n\n\nstatic int candidate_local_udp(void)\n{\n\tstruct ice_lcand *lcand;\n\tFIXTURE_INIT;\n\n\terr = trice_lcand_add(&lcand, f->icem, 1, IPPROTO_UDP,\n\t\t\t      1234, &f->laddr, NULL,\n\t\t\t      ICE_CAND_TYPE_HOST, NULL, 0, NULL, 0);\n\tif (err)\n\t\tgoto out;\n\n\t/* verify the new local candidate */\n\tTEST_ASSERT(lcand != NULL);\n\tTEST_ASSERT(str_isset(lcand->attr.foundation));\n\tTEST_EQUALS(1, lcand->attr.compid);\n\tTEST_EQUALS(IPPROTO_UDP, lcand->attr.proto);\n\tTEST_EQUALS(1234, lcand->attr.prio);\n\tTEST_SACMP(&f->laddr, &lcand->attr.addr, SA_ADDR);\n\tTEST_ASSERT(sa_isset(&lcand->attr.addr, SA_PORT));\n\tTEST_EQUALS(ICE_CAND_TYPE_HOST, lcand->attr.type);\n\n\tTEST_ASSERT(list_contains(trice_lcandl(f->icem), &lcand->le));\n\tTEST_ASSERT(lcand->icem == f->icem);\n\tTEST_ASSERT(lcand->us != NULL);\n\tTEST_ASSERT(lcand->uh != NULL);\n\tTEST_ASSERT(lcand->ts == NULL);\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nstatic int candidate_local_tcp(enum ice_tcptype tcptype)\n{\n\tstruct ice_lcand *lcand;\n\tFIXTURE_INIT;\n\n\terr = trice_lcand_add(&lcand, f->icem, 1, IPPROTO_TCP,\n\t\t\t      1234, &f->laddr, NULL,\n\t\t\t      ICE_CAND_TYPE_HOST, NULL, tcptype, NULL, 0);\n\tif (err)\n\t\tgoto out;\n\n\t/* verify the new local candidate */\n\tTEST_ASSERT(lcand != NULL);\n\tTEST_ASSERT(str_isset(lcand->attr.foundation));\n\tTEST_EQUALS(1, lcand->attr.compid);\n\tTEST_EQUALS(IPPROTO_TCP, lcand->attr.proto);\n\tTEST_EQUALS(1234, lcand->attr.prio);\n\tTEST_SACMP(&f->laddr, &lcand->attr.addr, SA_ADDR);\n\tif (tcptype == ICE_TCP_ACTIVE) {\n\t\tTEST_ASSERT(!sa_isset(&lcand->attr.addr, SA_PORT));\n\t}\n\telse {\n\t\tTEST_ASSERT(sa_isset(&lcand->attr.addr, SA_PORT));\n\t}\n\tTEST_EQUALS(ICE_CAND_TYPE_HOST, lcand->attr.type);\n\n\tTEST_ASSERT(list_contains(trice_lcandl(f->icem), &lcand->le));\n\tTEST_ASSERT(lcand->icem == f->icem);\n\tTEST_ASSERT(lcand->us == NULL);\n\tTEST_ASSERT(lcand->uh == NULL);\n\tif (tcptype == ICE_TCP_ACTIVE) {\n\t\tTEST_ASSERT(lcand->ts == NULL);\n\t}\n\telse {\n\t\tTEST_ASSERT(lcand->ts != NULL);\n\t}\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nstatic int candidate_add_5_local(int proto)\n{\n\tint i;\n\tFIXTURE_INIT;\n\n\tfor (i=0; i<5; i++) {\n\t\tstruct sa addr;\n\t\tchar buf[64];\n\n\t\tre_snprintf(buf, sizeof(buf), \"10.0.0.%u\", i+1);\n\n\t\tsa_set_str(&addr, buf, 1000+i);\n\n\t\tADD_LOCAL_SRFLX_CANDIDATE(proto, 0, &addr)\n\t}\n\n\tTEST_EQUALS(5, list_count(trice_lcandl(f->icem)));\n\tTEST_EQUALS(0, list_count(trice_rcandl(f->icem)));\n\tTEST_EQUALS(0, list_count(trice_checkl(f->icem)));\n\tTEST_EQUALS(0, list_count(trice_validl(f->icem)));\n\n\tTEST_EQUALS(0, f->n_estabh);\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nstatic int candidate_find_local_candidate(void)\n{\n\tstruct sa addr;\n\tstruct ice_lcand *cand;\n\tFIXTURE_INIT;\n\n\tsa_set_str(&addr, \"1.2.3.4\", 1234);\n\n\t/* should not exist now */\n\tcand = trice_lcand_find(f->icem, -1, 1, IPPROTO_UDP, &addr);\n\tTEST_ASSERT(cand == NULL);\n\n\tADD_LOCAL_SRFLX_CANDIDATE(IPPROTO_UDP, 0x7e0000ff, &addr);\n\n\tcand = trice_lcand_find(f->icem, -1, 1, IPPROTO_UDP, &addr);\n\tTEST_ASSERT(cand != NULL);\n\n\tTEST_EQUALS(ICE_CAND_TYPE_SRFLX, cand->attr.type);\n\tTEST_EQUALS(0x7e0000ff, cand->attr.prio);\n\tTEST_ASSERT(str_isset(cand->attr.foundation));\n\tTEST_EQUALS(1, cand->attr.compid);\n\tTEST_SACMP(&addr, &cand->attr.addr, SA_ALL);\n\tTEST_EQUALS(IPPROTO_UDP, cand->attr.proto);\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nstatic int candidate_add_5_remote_candidates(void)\n{\n\tint i;\n\tFIXTURE_INIT;\n\n\tfor (i=0; i<5; i++) {\n\t\tstruct sa addr;\n\t\tchar buf[64];\n\n\t\tre_snprintf(buf, sizeof(buf), \"10.0.0.%u\", i+1);\n\n\t\tsa_set_str(&addr, buf, 1234);\n\n\t\tADD_REMOTE_HOST_CANDIDATE(&addr);\n\t}\n\n\tTEST_EQUALS(0, list_count(trice_lcandl(f->icem)));\n\tTEST_EQUALS(5, list_count(trice_rcandl(f->icem)));\n\tTEST_EQUALS(0, list_count(trice_checkl(f->icem)));\n\tTEST_EQUALS(0, list_count(trice_validl(f->icem)));\n\n\tTEST_EQUALS(0, f->n_estabh);\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nstatic int candidate_find_remote_candidate(void)\n{\n\tstruct sa addr;\n\tstruct ice_rcand *cand;\n\tFIXTURE_INIT;\n\n\tsa_set_str(&addr, \"1.2.3.4\", 1234);\n\n\t/* should not exist now */\n\tcand = trice_rcand_find(f->icem, 1, IPPROTO_UDP, &addr);\n\tTEST_ASSERT(cand == NULL);\n\n\tADD_REMOTE_HOST_CANDIDATE(&addr);\n\n\tcand = trice_rcand_find(f->icem, 1, IPPROTO_UDP, &addr);\n\tTEST_ASSERT(cand != NULL);\n\n\tTEST_EQUALS(ICE_CAND_TYPE_HOST, cand->attr.type);\n\tTEST_EQUALS(0x7e0000ff, cand->attr.prio);\n\tTEST_ASSERT(str_isset(cand->attr.foundation));\n\tTEST_EQUALS(1, cand->attr.compid);\n\tTEST_SACMP(&addr, &cand->attr.addr, SA_ALL);\n\tTEST_EQUALS(IPPROTO_UDP, cand->attr.proto);\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nstatic int candidate_add_2_local_and_2_remote_candidates(void)\n{\n\tstruct sa laddr, raddr;\n\tint i;\n\tFIXTURE_INIT;\n\n\tsa_set_str(&laddr, \"10.0.0.1\", 0);\n\tsa_set_str(&raddr, \"10.0.0.2\", 0);\n\n\tfor (i=0; i<2; i++) {\n\n\t\tsa_set_port(&laddr, 10000+i);\n\t\tsa_set_port(&raddr, 20000+i);\n\n\t\tADD_LOCAL_SRFLX_CANDIDATE(IPPROTO_UDP, 1234, &laddr)\n\n\t\tADD_REMOTE_HOST_CANDIDATE(&raddr);\n\t}\n\n\tTEST_EQUALS(2, list_count(trice_lcandl(f->icem)));\n\tTEST_EQUALS(2, list_count(trice_rcandl(f->icem)));\n\tTEST_EQUALS(4, list_count(trice_checkl(f->icem)));\n\tTEST_EQUALS(0, list_count(trice_validl(f->icem)));\n\n\tTEST_EQUALS(0, f->n_estabh);\n\n\tTEST_ASSERT(verify_sorted(trice_checkl(f->icem)));\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nstatic int candidate_2_local_duplicates(int proto,\n\t\t\t\t\tuint32_t prio1, uint32_t prio2)\n{\n\tstruct sa laddr;\n\tstruct ice_lcand *lcand;\n\tFIXTURE_INIT;\n\n\tsa_set_str(&laddr, \"10.0.0.3\", 1002);\n\n\tTEST_EQUALS(0, list_count(trice_lcandl(f->icem)));\n\n\t/* add one with Low Priority */\n\tADD_LOCAL_SRFLX_CANDIDATE(proto, prio1, &laddr);\n\n\tTEST_EQUALS(1, list_count(trice_lcandl(f->icem)));\n\n\t/* add one with High Priority */\n\tADD_LOCAL_SRFLX_CANDIDATE(proto, prio2, &laddr);\n\n\tTEST_EQUALS(1, list_count(trice_lcandl(f->icem)));\n\n\t/* verify that local candidate has the HIGH prio */\n\tlcand = trice_lcand_find(f->icem, -1, 1, proto, &laddr);\n\tTEST_ASSERT(lcand != NULL);\n\tTEST_EQUALS(max(prio1, prio2), lcand->attr.prio);\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nstatic int candidate_local_host_and_srflx_with_base(void)\n{\n\tstruct fixture f;\n\tstruct sa laddr, srflx;\n\tstruct ice_lcand *lcand;\n\tint err = 0;\n\n\terr = fixture_init(&f);\n\tif (err)\n\t\tgoto out;\n\n\tsa_set_str(&laddr, \"127.0.0.1\", 0);\n\tsa_set_str(&srflx, \"46.45.1.1\", 1002);\n\n\terr = trice_lcand_add(&lcand, f.icem, COMPID, IPPROTO_UDP,\n\t\t\t      1234, &laddr, NULL,\n\t\t\t      ICE_CAND_TYPE_HOST, NULL, 0, NULL, 0);\n\tTEST_ERR(err);\n\tTEST_ASSERT(lcand != NULL);\n\n\tladdr = lcand->attr.addr;\n\n\terr = trice_lcand_add(NULL, f.icem, COMPID, IPPROTO_UDP,\n\t\t\t      1234, &srflx, &laddr,\n\t\t\t      ICE_CAND_TYPE_SRFLX, &laddr, 0, NULL, 0);\n\tTEST_ERR(err);\n\n\tTEST_EQUALS(2, list_count(trice_lcandl(f.icem)));\n\n\t/* verify */\n\tlcand = trice_lcand_find(f.icem, ICE_CAND_TYPE_HOST, COMPID,\n\t\t\t\t IPPROTO_UDP, &lcand->attr.addr);\n\tTEST_ASSERT(lcand != NULL);\n\tTEST_EQUALS(ICE_CAND_TYPE_HOST, lcand->attr.type);\n\tTEST_SACMP(&laddr, &lcand->attr.addr, SA_ALL);\n\n\tlcand = trice_lcand_find(f.icem, ICE_CAND_TYPE_SRFLX, COMPID,\n\t\t\t\t IPPROTO_UDP, &srflx);\n\tTEST_ASSERT(lcand != NULL);\n\tTEST_EQUALS(ICE_CAND_TYPE_SRFLX, lcand->attr.type);\n\tTEST_SACMP(&srflx, &lcand->attr.addr, SA_ALL);\n\tTEST_SACMP(&laddr, &lcand->base_addr, SA_ALL);\n\n out:\n\tfixture_close(&f);\n\treturn err;\n}\n\n\n/* 4.1.3.  Eliminating Redundant Candidates */\nstatic int candidate_verify_redundant_with_public_ip(void)\n{\n\tstruct sa laddr, raddr;\n\tstruct ice_lcand *lcand;\n\tuint32_t prio;\n\tFIXTURE_INIT;\n\n\tsa_set_str(&laddr, \"127.0.0.1\", 0);\n\tsa_set_str(&raddr, \"10.0.0.4\", 1002);\n\n\tprio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, COMPID);\n\terr = trice_lcand_add(&lcand, f->icem, COMPID, IPPROTO_UDP,\n\t\t\t      prio, &laddr, NULL,\n\t\t\t      ICE_CAND_TYPE_HOST, NULL, 0,\n\t\t\t      NULL, 0);\n\tTEST_ERR(err);\n\tTEST_ASSERT(lcand != NULL);\n\n\tladdr = lcand->attr.addr;\n\n\tprio = ice_cand_calc_prio(ICE_CAND_TYPE_SRFLX, 0, COMPID);\n\terr = trice_lcand_add(NULL, f->icem, COMPID, IPPROTO_UDP,\n\t\t\t      prio,\n\t\t\t      &lcand->attr.addr, &lcand->attr.addr,\n\t\t\t      ICE_CAND_TYPE_SRFLX,\n\t\t\t      &lcand->attr.addr,\n\t\t\t      0, NULL, 0);\n\tTEST_ERR(err);\n\n\tADD_REMOTE_HOST_CANDIDATE(&raddr);\n\n\tTEST_EQUALS(1, list_count(trice_lcandl(f->icem)));\n\tTEST_EQUALS(1, list_count(trice_rcandl(f->icem)));\n\tTEST_EQUALS(1, list_count(trice_checkl(f->icem)));\n\tTEST_EQUALS(0, list_count(trice_validl(f->icem)));\n\n\t/* verify the local candidate */\n\tlcand = list_ledata(list_head(trice_lcandl(f->icem)));\n\tTEST_EQUALS(ICE_CAND_TYPE_HOST, lcand->attr.type);\n\tTEST_SACMP(&laddr, &lcand->attr.addr, SA_ALL);\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\n/* ... testcases for candidate pairs ... */\n\n\nstatic int candpair_add_1_local_and_1_remote_candidate_and_create_pair(void)\n{\n\tstruct sa addr;\n\tFIXTURE_INIT;\n\n\tsa_set_str(&addr, \"10.0.0.5\", 1000);\n\n\tADD_LOCAL_SRFLX_CANDIDATE(IPPROTO_UDP, 1234, &addr);\n\n\tADD_REMOTE_HOST_CANDIDATE(&addr);\n\n\t/* the checklist is formatted automatically */\n\n\tTEST_EQUALS(1, list_count(trice_lcandl(f->icem)));\n\tTEST_EQUALS(1, list_count(trice_rcandl(f->icem)));\n\tTEST_EQUALS(1, list_count(trice_checkl(f->icem)));\n\tTEST_EQUALS(0, list_count(trice_validl(f->icem)));\n\n\tTEST_EQUALS(0, f->n_estabh);\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nstatic int candpair_combine_ipv4_ipv6_udp_tcp(void)\n{\n\tstruct sa addr, addr6;\n\tFIXTURE_INIT;\n\n\tsa_set_str(&addr, \"10.0.0.6\", 1000);\n\tsa_set_str(&addr6, \"::1\", 6000);\n\n\terr |= trice_lcand_add(0, f->icem, 1, IPPROTO_UDP, 1234,\n\t\t\t       &addr, &addr, ICE_CAND_TYPE_SRFLX, &addr, 0,\n\t\t\t       NULL, 0);\n\terr |= trice_lcand_add(0, f->icem, 1, IPPROTO_TCP, 1234,\n\t\t\t       &addr, &addr, ICE_CAND_TYPE_SRFLX, &addr,\n\t\t\t       ICE_TCP_ACTIVE,\n\t\t\t       NULL, 0);\n\terr |= trice_lcand_add(0, f->icem, 1, IPPROTO_UDP, 1234,\n\t\t\t       &addr6, &addr6, ICE_CAND_TYPE_SRFLX, &addr6,\n\t\t\t       0, NULL, 0);\n\terr |= trice_lcand_add(0, f->icem, 1, IPPROTO_TCP, 1234,\n\t\t\t       &addr6, &addr6, ICE_CAND_TYPE_SRFLX, &addr6,\n\t\t\t       ICE_TCP_ACTIVE,\n\t\t\t       NULL, 0);\n\tTEST_ERR(err);\n\n\tADD_REMOTE_HOST_CANDIDATE(&addr);\n\terr |= trice_rcand_add(NULL, f->icem, 1,\n\t\t\t\t\t \"FND\", IPPROTO_TCP, 1234,\n\t\t\t\t\t &addr, ICE_CAND_TYPE_HOST,\n\t\t\t\t\t ICE_TCP_PASSIVE);\n\tif (err) goto out;\n\n\tADD_REMOTE_HOST_CANDIDATE(&addr6);\n\terr |= trice_rcand_add(NULL, f->icem, 1,\n\t\t\t\t\t \"FND\", IPPROTO_TCP, 1234,\n\t\t\t\t\t &addr6, ICE_CAND_TYPE_HOST,\n\t\t\t\t\t ICE_TCP_PASSIVE);\n\tif (err) goto out;\n\n\tTEST_EQUALS(4, list_count(trice_lcandl(f->icem)));\n\tTEST_EQUALS(4, list_count(trice_rcandl(f->icem)));\n\tTEST_EQUALS(4, list_count(trice_checkl(f->icem)));\n\tTEST_EQUALS(0, list_count(trice_validl(f->icem)));\n\n\tTEST_EQUALS(0, f->n_estabh);\n\tTEST_EQUALS(0, f->n_failh);\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nstatic int candpair_add_many_verify_sorted(void)\n{\n\tstruct fixture f;\n\tstruct sa laddr, raddr;\n\tint i, err = 0;\n\n\terr = fixture_init(&f);\n\tif (err)\n\t\tgoto out;\n\n\tsa_set_str(&laddr, \"10.0.0.7\", 0);\n\tsa_set_str(&raddr, \"10.0.0.8\", 0);\n\n\tfor (i=0; i<4; i++) {\n\n\t\tuint8_t compid = 1 + i%2;\n\n\t\tsa_set_port(&laddr, 10000+i);\n\t\tsa_set_port(&raddr, 20000+i);\n\n\t\terr = trice_lcand_add(0, f.icem, compid, IPPROTO_UDP,\n\t\t\t\t      i*1000, &laddr, &laddr,\n\t\t\t\t      ICE_CAND_TYPE_SRFLX, &laddr, 0,\n\t\t\t\t      NULL, 0);\n\t\tTEST_ERR(err);\n\n\t\terr = trice_rcand_add(0, f.icem, compid, \"FND\",\n\t\t\t\t      IPPROTO_UDP, i*2000,\n\t\t\t\t      &raddr, ICE_CAND_TYPE_HOST, 0);\n\t\tTEST_ERR(err);\n\t}\n\n\tTEST_EQUALS(4, list_count(trice_lcandl(f.icem)));\n\tTEST_EQUALS(4, list_count(trice_rcandl(f.icem)));\n\tTEST_EQUALS(8, list_count(trice_checkl(f.icem)));\n\tTEST_EQUALS(0, list_count(trice_validl(f.icem)));\n\n\tTEST_ASSERT(verify_sorted(trice_checkl(f.icem)));\n\n out:\n\tfixture_close(&f);\n\treturn err;\n}\n\n\nstatic int candpair_test_pruning(void)\n{\n\tstruct sa srflx_addr, remote_addr;\n\tstruct ice_lcand *lcand;\n\tuint32_t prio;\n\tFIXTURE_INIT;\n\n\terr |= sa_set_str(&srflx_addr, \"95.1.2.3\", 50000);\n\terr |= sa_set_str(&remote_addr, \"10.0.0.9\", 10000);\n\tTEST_ERR(err);\n\n\tprio = ice_cand_calc_prio(ICE_CAND_TYPE_SRFLX, 0, COMPID);\n\n\tadd_local_udp_candidate_use(&f->laddr);\n\n\tlcand = trice_lcand_find(f->icem, -1, COMPID,\n\t\t\t\t IPPROTO_UDP, NULL);\n\tTEST_ASSERT(lcand != NULL);\n\n\terr = trice_lcand_add(&lcand, f->icem, COMPID, IPPROTO_UDP,\n\t\t\t      prio, &srflx_addr, &lcand->attr.addr,\n\t\t\t      ICE_CAND_TYPE_SRFLX, &lcand->attr.addr,\n\t\t\t      0, NULL, 0);\n\tTEST_ERR(err);\n\tTEST_ASSERT(lcand != NULL);\n\n\tADD_REMOTE_HOST_CANDIDATE(&remote_addr);\n\n\t/* verify that SRFLX candpair was pruned\n\t */\n\tTEST_EQUALS(2, list_count(trice_lcandl(f->icem)));\n\tTEST_EQUALS(1, list_count(trice_rcandl(f->icem)));\n\tTEST_EQUALS(1, list_count(trice_checkl(f->icem)));\n\tTEST_EQUALS(0, list_count(trice_validl(f->icem)));\n\n out:\n\tfixture_close(f);\n\treturn err;\n}\n\n\nstatic int checklist_verify_states(void)\n{\n\tstruct fixture f;\n\tint err = 0;\n\n\terr = fixture_init(&f);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_EQUALS(false, trice_checklist_isrunning(f.icem));\n\n\t/* Start -- Running */\n\tCHECKLIST_START(&f);\n\tTEST_EQUALS(true, trice_checklist_isrunning(f.icem));\n\n\t/* Stop */\n\ttrice_checklist_stop(f.icem);\n\tTEST_EQUALS(false, trice_checklist_isrunning(f.icem));\n\n out:\n\tfixture_close(&f);\n\treturn err;\n}\n\n\nstatic int exchange_candidates(struct trice *dst, const struct trice *src)\n{\n\tstruct le *le;\n\tint err = 0;\n\n\tfor (le = list_head(trice_lcandl(src)); le; le = le->next) {\n\n\t\tstruct ice_cand_attr *cand = le->data;\n\n\t\terr = trice_rcand_add(NULL, dst, cand->compid,\n\t\t\t\t      cand->foundation, cand->proto,\n\t\t\t\t      cand->prio, &cand->addr,\n\t\t\t\t      cand->type, cand->tcptype);\n\t\tif (err)\n\t\t\treturn err;\n\t}\n\n\treturn err;\n}\n\n\nstatic int checklist_tcp_simple(enum ice_tcptype tcptype)\n{\n\tstruct le *le;\n\tchar *buf = NULL;\n\tFIXTURE_INIT;\n\n#if 0\n\ttrice_conf(f.icem)->debug = true;\n\ttrice_conf(f.icem)->trace = true;\n#endif\n\n\terr = trice_alloc(&f->icem2, NULL,\n\t\t  !f->controlling ? ICE_ROLE_CONTROLLING : ICE_ROLE_CONTROLLED,\n\t\t\t  f->rufrag, f->rpwd);\n\tTEST_ERR(err);\n\n\terr |= trice_set_remote_ufrag(f->icem2, f->lufrag);\n\terr |= trice_set_remote_pwd(f->icem2, f->lpwd);\n\tTEST_ERR(err);\n\n\tadd_local_tcp_candidate_use(&f->laddr, tcptype);\n\tadd_local_tcp_candidate_use2(&f->laddr, ice_tcptype_reverse(tcptype));\n\n\terr  = exchange_candidates(f->icem, f->icem2);\n\terr |= exchange_candidates(f->icem2, f->icem);\n\tTEST_ERR(err);\n\n\tCHECKLIST_START(f);\n\n\terr = trice_checklist_start(f->icem2, NULL, 1,\n\t\t\t\t   ice_estab_handler2, ice_failed_handler2, f);\n\tTEST_ERR(err);\n\n\tf->n_expected_estabh = 1;\n\n\terr = re_main_timeout(1000);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_ERR(f->err);\n\n\terr = re_sdprintf(&buf, \"%H\", trice_debug, f->icem);\n\tTEST_ERR(err);\n\tASSERT_TRUE(str_isset(buf));\n\n\tTEST_ASSERT(f->n_estabh > 0);\n\n\tTEST_ASSERT(list_count(trice_lcandl(f->icem)) >= 1);\n\tTEST_ASSERT(list_count(trice_rcandl(f->icem)) >= 1);\n\tTEST_EQUALS(1, list_count(trice_validl(f->icem)));\n\n\tfor (le = list_head(trice_validl(f->icem)); le; le = le->next) {\n\t\tstruct ice_candpair *pair = le->data;\n\t\tstruct ice_lcand *lcand = pair->lcand;\n\n\t\tTEST_ASSERT(pair->valid);\n\n\t\tTEST_EQUALS(ICE_CANDPAIR_SUCCEEDED, pair->state);\n\t\tTEST_EQUALS(0, pair->err);\n\t\tTEST_EQUALS(0, pair->scode);\n\n\t\tTEST_EQUALS(IPPROTO_TCP, lcand->attr.proto);\n\t\tTEST_EQUALS(tcptype, lcand->attr.tcptype);\n\n\t\tTEST_EQUALS(IPPROTO_TCP, pair->rcand->attr.proto);\n\t\tTEST_EQUALS(ice_tcptype_reverse(tcptype),\n\t\t\t    pair->rcand->attr.tcptype);\n\t}\n\n out:\n\tfixture_close(f);\n\tmem_deref(buf);\n\treturn err;\n}\n\n\nint test_trice_cand(void)\n{\n\tint err = 0;\n\n\terr |= candidate_local_udp();\n\terr |= candidate_local_tcp(ICE_TCP_ACTIVE);\n\terr |= candidate_local_tcp(ICE_TCP_PASSIVE);\n\terr |= candidate_local_tcp(ICE_TCP_SO);\n\terr |= candidate_add_5_local(IPPROTO_UDP);\n\terr |= candidate_add_5_local(IPPROTO_TCP);\n\terr |= candidate_find_local_candidate();\n\terr |= candidate_add_5_remote_candidates();\n\terr |= candidate_find_remote_candidate();\n\terr |= candidate_add_2_local_and_2_remote_candidates();\n\terr |= candidate_2_local_duplicates(IPPROTO_UDP, 100, 200);\n\terr |= candidate_2_local_duplicates(IPPROTO_UDP, 200, 100);\n#if 0\n\terr |= candidate_2_local_duplicates(IPPROTO_TCP, 100, 200);\n#endif\n\terr |= candidate_local_host_and_srflx_with_base();\n\terr |= candidate_verify_redundant_with_public_ip();\n\n\treturn err;\n}\n\n\nint test_trice_candpair(void)\n{\n\tint err = 0;\n\n\terr |= candpair_add_1_local_and_1_remote_candidate_and_create_pair();\n\terr |= candpair_combine_ipv4_ipv6_udp_tcp();\n\terr |= candpair_add_many_verify_sorted();\n\terr |= candpair_test_pruning();\n\n\treturn err;\n}\n\n\nint test_trice_checklist(void)\n{\n\tint err = 0;\n\n\terr |= checklist_verify_states();\n\tTEST_ERR(err);\n\terr |= checklist_tcp_simple(ICE_TCP_ACTIVE);\n\tTEST_ERR(err);\n\terr |= checklist_tcp_simple(ICE_TCP_PASSIVE);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\nstatic int checklist_udp_loop(bool fw_a, bool fw_b)\n{\n\tstruct ice_lcand *lcand, *lcand2;\n\tstruct sa laddr2;\n\tuint32_t prio;\n\tchar *buf = NULL;\n\tFIXTURE_INIT;\n\n#if 0\n\ttrice_conf(f->icem)->debug = true;\n\ttrice_conf(f->icem)->trace = true;\n#endif\n\n\tsa_set_str(&laddr2, \"127.0.0.1\", 0);\n\n\terr = trice_alloc(&f->icem2, NULL,\n\t\t  !f->controlling ? ICE_ROLE_CONTROLLING : ICE_ROLE_CONTROLLED,\n\t\t\t  f->rufrag, f->rpwd);\n\tTEST_ERR(err);\n\n\terr |= trice_set_remote_ufrag(f->icem2, f->lufrag);\n\terr |= trice_set_remote_pwd(f->icem2, f->lpwd);\n\tTEST_ERR(err);\n\n\t/* add local HOST candidates */\n\n\tadd_local_udp_candidate_use(&f->laddr);\n\n\tlcand = trice_lcand_find2(f->icem,\n\t\t\t\t  ICE_CAND_TYPE_HOST, AF_INET);\n\tTEST_ASSERT(lcand != NULL);\n\n\t/* install NAT/Firewall */\n\tif (fw_a) {\n\t\terr = nat_alloc(&f->nat, NAT_FIREWALL, lcand->us, NULL);\n\t\tif (err) {\n\t\t\tre_printf(\"nat failed\\n\");\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\tprio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, COMPID);\n\terr = trice_lcand_add(&lcand2, f->icem2, COMPID, IPPROTO_UDP,\n\t\t\t      prio, &laddr2, NULL,\n\t\t\t      ICE_CAND_TYPE_HOST, NULL, 0, NULL, 0);\n\tif (err)\n\t\tgoto out;\n\n\t/* install NAT/Firewall */\n\tif (fw_b) {\n\t\terr = nat_alloc(&f->nat2, NAT_FIREWALL, lcand2->us, NULL);\n\t\tif (err) {\n\t\t\tre_printf(\"nat failed\\n\");\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\terr  = exchange_candidates(f->icem, f->icem2);\n\terr |= exchange_candidates(f->icem2, f->icem);\n\tTEST_ERR(err);\n\n\tf->cancel_on_both = true;\n\n\tCHECKLIST_START(f);\n\n\t/* NOTE: slow checklist */\n\terr = trice_checklist_start(f->icem2, NULL, 10,\n\t\t\t\t   ice_estab_handler2, ice_failed_handler2, f);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(2000);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_ERR(f->err);\n\n\terr = re_sdprintf(&buf, \"%H\", trice_debug, f->icem);\n\tTEST_ERR(err);\n\tASSERT_TRUE(str_isset(buf));\n\n\tTEST_ASSERT(f->n_estabh > 0);\n\n\tTEST_ASSERT(list_count(trice_lcandl(f->icem)) >= 1);\n\tTEST_ASSERT(list_count(trice_rcandl(f->icem)) >= 1);\n\tTEST_EQUALS(1, list_count(trice_validl(f->icem)));\n\n\tif (fw_a) {\n\t\tTEST_ASSERT(f->nat->bindingc >= 1);\n\t}\n\n out:\n\tfixture_close(f);\n\tmem_deref(buf);\n\treturn err;\n}\n\n\nint test_trice_loop(void)\n{\n\tint err = 0;\n\n\terr = checklist_udp_loop(0, 0);\n\tTEST_ERR(err);\n\n\terr = checklist_udp_loop(0, 1);\n\tTEST_ERR(err);\n\n\terr = checklist_udp_loop(1, 0);\n\tTEST_ERR(err);\n\n\terr = checklist_udp_loop(1, 1);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/turn.c",
    "content": "/**\n * @file turn.c  TURN testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re_atomic.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_turn\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nenum rx_state {\n\tRX_NULL = 0,\n\tRX_DETACH,\n\tRX_ATTACH,\n\tRX_READY,\n\tRX_CLOSE\n};\n\n\nstruct turntest {\n\tstruct turnc *turnc;\n\tstruct turnserver *turnsrv;\n\tstruct udp_sock *us_cli;\n\tstruct udp_sock *us_peer;\n\tstruct tcp_conn *tc;\n\tstruct sa cli;\n\tstruct sa peer;\n\tstruct mbuf *mb;\n\tstruct tmr tmr;\n\tuint32_t lifetime;\n\tenum rx_state rx_state;\n\tthrd_t thr;\n\tmtx_t *mtx;\n\tint proto;\n\tint err;\n\tbool use_chan;\n\n\tsize_t n_alloc_resp;\n\tsize_t n_perm_resp;\n\tsize_t n_chan_resp;\n\tsize_t n_peer_recv;\n};\n\n\nstatic const char *test_payload = \"guten tag Herr TURN server\";\n\n\nstatic void destructor(void *arg)\n{\n\tstruct turntest *tt = arg;\n\n\t/* NOTE: must be derefed before udp socket */\n\tmem_deref(tt->turnc);\n\n\tmem_deref(tt->us_cli);\n\tmem_deref(tt->us_peer);\n\tmem_deref(tt->tc);\n\tmem_deref(tt->mb);\n\tmem_deref(tt->turnsrv);\n\tmem_deref(tt->mtx);\n}\n\n\nstatic bool is_complete(struct turntest *tt)\n{\n\treturn (tt->use_chan ? tt->n_chan_resp >= 1 : tt->n_perm_resp >= 1)\n\t\t&& tt->n_peer_recv >= 2;\n}\n\n\nstatic void complete_test(struct turntest *tt, int err)\n{\n\ttt->err = err;\n\tre_cancel();\n}\n\n\n/* send a UDP payload via TURN-Server to a UDP-peer */\nstatic int send_payload(struct turntest *tt, size_t offset,\n\t\t\tconst struct sa *dst, const char *str)\n{\n\tstruct mbuf *mb = mbuf_alloc(offset + str_len(str));\n\tint err;\n\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\tmb->pos = offset;\n\n\terr = mbuf_write_str(mb, str);\n\tif (err)\n\t\tgoto out;\n\n\tmb->pos = offset;\n\n\tswitch (tt->proto) {\n\n\tcase IPPROTO_UDP:\n\t\terr = turnc_send(tt->turnc, dst, mb);\n\t\tbreak;\n\n\tcase IPPROTO_TCP:\n\t\terr = turnc_send(tt->turnc, dst, mb);\n\t\tbreak;\n\t}\n\n out:\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic void turnc_perm_handler(void *arg)\n{\n\tstruct turntest *tt = arg;\n\n\t++tt->n_perm_resp;\n\n\t/* Headroom for IPv4/IPv6 STUN headers */\n\tconst size_t offset = 48;\n\n\tint err = send_payload(tt, offset, &tt->peer, test_payload);\n\tif (err) {\n\t\tDEBUG_WARNING(\"failed to send payload (%m)\\n\", err);\n\t\tcomplete_test(tt, err);\n\t}\n}\n\n\nstatic void turnc_chan_handler(void *arg)\n{\n\tstruct turntest *tt = arg;\n\n\t++tt->n_chan_resp;\n\n\tint err = send_payload(tt, 4, &tt->peer, test_payload);\n\tif (err) {\n\t\tDEBUG_WARNING(\"failed to send payload (%m)\\n\", err);\n\t}\n\n\tif (err)\n\t\tcomplete_test(tt, err);\n}\n\n\nstatic void turnc_handler(int err, uint16_t scode, const char *reason,\n\t\t\t  const struct sa *relay_addr,\n\t\t\t  const struct sa *mapped_addr,\n\t\t\t  const struct stun_msg *msg,\n\t\t\t  void *arg)\n{\n\tstruct turntest *tt = arg;\n\t(void)reason;\n\t(void)msg;\n\n\t++tt->n_alloc_resp;\n\n\tif (err) {\n\t\tcomplete_test(tt, err);\n\t\treturn;\n\t}\n\n\tif (scode) {\n\t\tcomplete_test(tt, EPROTO);\n\t\treturn;\n\t}\n\n\tTEST_SACMP(&tt->turnsrv->relay, relay_addr, SA_ALL);\n\tTEST_SACMP(&tt->cli, mapped_addr, SA_ALL);\n\n\tif (tt->use_chan) {\n\n\t\terr = turnc_add_chan(tt->turnc, &tt->peer,\n\t\t\t\t     turnc_chan_handler, tt);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = turnc_add_chan(tt->turnc, &tt->peer,\n\t\t\t\t     turnc_chan_handler, tt);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\telse {\n\t\tturnserver_force_error(tt->turnsrv, 401);\n\n\t\t/* Permission is needed for sending data */\n\t\terr = turnc_add_perm(tt->turnc, &tt->peer,\n\t\t\t\t     turnc_perm_handler, tt);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = turnc_add_perm(tt->turnc, &tt->peer,\n\t\t\t\t     turnc_perm_handler, tt);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n out:\n\tif (err)\n\t\tcomplete_test(tt, err);\n}\n\n\nstatic void peer_udp_recv(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct turntest *tt = arg;\n\tint err = 0;\n\t(void)src;\n\n\t++tt->n_peer_recv;\n\n\terr = udp_send(tt->us_peer, src, mb);\n\tTEST_ERR(err);\n\n\tTEST_MEMCMP(test_payload, strlen(test_payload),\n\t\t    mbuf_buf(mb), mbuf_get_left(mb));\n\n\tmtx_lock(tt->mtx);\n\tif (tt->rx_state > RX_NULL) {\n\t\tmtx_unlock(tt->mtx);\n\t\treturn;\n\t}\n\tmtx_unlock(tt->mtx);\n\n out:\n\tif (err || is_complete(tt))\n\t\tcomplete_test(tt, err);\n}\n\n\nstatic void cli_udp_recv(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct turntest *tt = arg;\n\t(void)src;\n\n\tmtx_lock(tt->mtx);\n\tif (tt->rx_state == RX_DETACH) {\n\t\tudp_thread_detach(tt->us_cli);\n\t\ttt->rx_state = RX_ATTACH;\n\t}\n\tmtx_unlock(tt->mtx);\n\n\tudp_send(tt->us_cli, src, mb);\n}\n\n\nstatic void data_handler(struct turntest *tt, struct sa *src, struct mbuf *mb)\n{\n\t/* echo data */\n\tturnc_send(tt->turnc, src, mb);\n}\n\n\nstatic void tcp_estab_handler(void *arg)\n{\n\tstruct turntest *tt = arg;\n\tint err;\n\n\terr = tcp_conn_local_get(tt->tc, &tt->cli);\n\tif (err)\n\t\tgoto out;\n\n\terr = turnc_alloc(&tt->turnc, NULL, IPPROTO_TCP, tt->tc,\n\t\t\t  0, &tt->turnsrv->laddr_tcp,\n\t\t\t  \"username\", \"password\", tt->lifetime,\n\t\t\t  turnc_handler, tt);\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tcomplete_test(tt, err);\n}\n\n\nstatic void tcp_recv_handler(struct mbuf *mb, void *arg)\n{\n\tstruct turntest *tl = arg;\n\tint err = 0;\n\n\tif (tl->mb) {\n\t\tsize_t pos;\n\n\t\tpos = tl->mb->pos;\n\n\t\ttl->mb->pos = tl->mb->end;\n\n\t\terr = mbuf_write_mem(tl->mb, mbuf_buf(mb),mbuf_get_left(mb));\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\ttl->mb->pos = pos;\n\t}\n\telse {\n\t\ttl->mb = mem_ref(mb);\n\t}\n\n\tfor (;;) {\n\n\t\tsize_t len, pos, end;\n\t\tstruct sa src;\n\t\tuint16_t typ;\n\n\t\tif (mbuf_get_left(tl->mb) < 4)\n\t\t\tbreak;\n\n\t\ttyp = ntohs(mbuf_read_u16(tl->mb));\n\t\tlen = ntohs(mbuf_read_u16(tl->mb));\n\n\t\tif (typ < 0x4000)\n\t\t\tlen += STUN_HEADER_SIZE;\n\t\telse if (typ < 0x8000)\n\t\t\tlen += 4;\n\t\telse {\n\t\t\terr = EBADMSG;\n\t\t\tgoto out;\n\t\t}\n\n\t\ttl->mb->pos -= 4;\n\n\t\tif (mbuf_get_left(tl->mb) < len)\n\t\t\tbreak;\n\n\t\tpos = tl->mb->pos;\n\t\tend = tl->mb->end;\n\n\t\ttl->mb->end = pos + len;\n\n\t\terr = turnc_recv(tl->turnc, &src, tl->mb);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tif (mbuf_get_left(tl->mb))\n\t\t\tdata_handler(tl, &src, tl->mb);\n\n\t\t/* 4 byte alignment */\n\t\twhile (len & 0x03)\n\t\t\t++len;\n\n\t\ttl->mb->pos = pos + len;\n\t\ttl->mb->end = end;\n\n\t\tif (tl->mb->pos >= tl->mb->end) {\n\t\t\ttl->mb = mem_deref(tl->mb);\n\t\t\tbreak;\n\t\t}\n\t}\n\n out:\n\tif (err)\n\t\tcomplete_test(tl, err);\n}\n\n\nstatic void tcp_close_handler(int err, void *arg)\n{\n\tstruct turntest *tt = arg;\n\n\tif (err == ECONNRESET) {\n\t\tre_printf(\"translate ECONNRESET -> ENOMEM\\n\");\n\t\terr = ENOMEM;\n\t}\n\n\tif (err)\n\t\tcomplete_test(tt, err);\n}\n\n\nstatic int turntest_alloc(struct turntest **ttp, int proto, uint32_t lifetime,\n\t\t\t  const char *addr)\n{\n\tstruct turntest *tt;\n\tstruct sa laddr;\n\tint err;\n\n\ttt = mem_zalloc(sizeof(*tt), NULL);\n\tif (!tt)\n\t\treturn ENOMEM;\n\n\terr = mutex_alloc(&tt->mtx);\n\tif (err)\n\t\tgoto out;\n\n\tmem_destructor(tt, destructor);\n\n\ttt->proto    = proto;\n\ttt->lifetime = lifetime;\n\n\terr  = sa_set_str(&laddr, addr, 0);\n\tif (err)\n\t\tgoto out;\n\n\tif (proto == IPPROTO_UDP) {\n\t\terr |= udp_listen(&tt->us_cli, &laddr, cli_udp_recv, tt);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\terr = udp_local_get(tt->us_cli, &tt->cli);\n\t\tif (err)\n\t\t\tgoto out;\n\t}\n\n\terr = udp_listen(&tt->us_peer, &laddr, peer_udp_recv, tt);\n\tif (err)\n\t\tgoto out;\n\n\terr = udp_local_get(tt->us_peer, &tt->peer);\n\tif (err)\n\t\tgoto out;\n\n\terr = turnserver_alloc(&tt->turnsrv, addr);\n\tif (err)\n\t\tgoto out;\n\n\tswitch (proto) {\n\n\tcase IPPROTO_UDP:\n\t\terr = turnc_alloc(&tt->turnc, NULL, proto, tt->us_cli,\n\t\t\t\t  0, &tt->turnsrv->laddr,\n\t\t\t\t  \"username\", \"password\", lifetime,\n\t\t\t\t  turnc_handler, tt);\n\t\tbreak;\n\n\tcase IPPROTO_TCP:\n\t\terr = tcp_connect(&tt->tc, &tt->turnsrv->laddr_tcp,\n\t\t\t\t  tcp_estab_handler, tcp_recv_handler,\n\t\t\t\t  tcp_close_handler, tt);\n\t\tbreak;\n\t}\n\tif (err)\n\t\tgoto out;\n\n out:\n\tif (err)\n\t\tmem_deref(tt);\n\telse if (ttp)\n\t\t*ttp = tt;\n\n\treturn err;\n}\n\n\nstatic int test_turn_param(const char *addr, bool use_chan)\n{\n\tstruct turntest *tt;\n\tint err;\n\n\terr = turntest_alloc(&tt, IPPROTO_UDP, 600, addr);\n\tif (err)\n\t\treturn err;\n\n\ttt->use_chan = use_chan;\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\terr = tt->err;\n\tTEST_ERR(err);\n\n\t/* verify results after test is complete */\n\n\tTEST_EQUALS(1, tt->n_alloc_resp);\n\n\tif (use_chan) {\n\t\tTEST_EQUALS(0, tt->n_perm_resp);\n\t\tTEST_EQUALS(1, tt->n_chan_resp);\n\t}\n\telse {\n\t\tTEST_EQUALS(1, tt->n_perm_resp);\n\t\tTEST_EQUALS(0, tt->n_chan_resp);\n\t}\n\n\tTEST_ASSERT(tt->turnsrv->n_allocate >= 1);\n\tTEST_EQUALS(2, tt->n_peer_recv);\n\n\tif (use_chan) {\n\t\tTEST_ASSERT(tt->turnsrv->n_chanbind >= 1);\n\t\tTEST_ASSERT(tt->turnsrv->n_raw >= 1);\n\t\tTEST_EQUALS(0, tt->turnsrv->n_send);\n\t}\n\telse {\n\t\tTEST_EQUALS(0, tt->turnsrv->n_chanbind);\n\t\tTEST_EQUALS(0, tt->turnsrv->n_raw);\n\t\tTEST_EQUALS(2, tt->turnsrv->n_send);\n\t}\n\n out:\n\tmem_deref(tt);\n\n\treturn err;\n}\n\n\nint test_turn(void)\n{\n\tint err = test_turn_param(\"127.0.0.1\", false);\n\tTEST_ERR(err);\n\n\terr = test_turn_param(\"127.0.0.1\", true);\n\tTEST_ERR(err);\n\n\tif (test_ipv6_supported()) {\n\t\terr = test_turn_param(\"::1\", false);\n\t\tTEST_ERR(err);\n\n\t\terr = test_turn_param(\"::1\", true);\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic int test_turn_param_tcp(const char *addr, bool use_chan)\n{\n\tstruct turntest *tt;\n\tint err;\n\n\terr = turntest_alloc(&tt, IPPROTO_TCP, 600, addr);\n\tif (err)\n\t\treturn err;\n\n\ttt->use_chan = use_chan;\n\n\terr = re_main_timeout(200);\n\tTEST_ERR(err);\n\n\terr = tt->err;\n\tTEST_ERR(err);\n\n\t/* verify results after test is complete */\n\n\tTEST_EQUALS(1, tt->n_alloc_resp);\n\n\tif (use_chan) {\n\t\tTEST_EQUALS(0, tt->n_perm_resp);\n\t\tTEST_EQUALS(1, tt->n_chan_resp);\n\t}\n\telse {\n\t\tTEST_EQUALS(1, tt->n_perm_resp);\n\t\tTEST_EQUALS(0, tt->n_chan_resp);\n\t}\n\n\tTEST_ASSERT(tt->turnsrv->n_allocate >= 1);\n\tTEST_EQUALS(2, tt->n_peer_recv);\n\n\tif (use_chan) {\n\t\tTEST_ASSERT(tt->turnsrv->n_chanbind >= 1);\n\t\tTEST_ASSERT(tt->turnsrv->n_raw >= 1);\n\t\tTEST_EQUALS(0, tt->turnsrv->n_send);\n\t}\n\telse {\n\t\tTEST_EQUALS(0, tt->turnsrv->n_chanbind);\n\t\tTEST_EQUALS(0, tt->turnsrv->n_raw);\n\t\tTEST_EQUALS(2, tt->turnsrv->n_send);\n\t}\n\n out:\n\tmem_deref(tt);\n\n\treturn err;\n}\n\n\nint test_turn_tcp(void)\n{\n\tint err;\n\n\terr = test_turn_param_tcp(\"127.0.0.1\", false);\n\tTEST_ERR(err);\n\n\terr = test_turn_param_tcp(\"127.0.0.1\", true);\n\tTEST_ERR(err);\n\n\tif (test_ipv6_supported()) {\n\t\terr = test_turn_param_tcp(\"::1\", false);\n\t\tTEST_ERR(err);\n\n\t\terr = test_turn_param_tcp(\"::1\", true);\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic void tmr_handler(void *arg)\n{\n\tstruct turntest *tt = arg;\n\n\tmtx_lock(tt->mtx);\n\tif (tt->rx_state == RX_CLOSE)\n\t\tre_cancel();\n\n\tif (tt->rx_state == RX_ATTACH) {\n\t\tudp_thread_attach(tt->us_cli);\n\t\ttt->rx_state = RX_READY;\n\t}\n\tmtx_unlock(tt->mtx);\n\n\ttmr_start(&tt->tmr, 0, tmr_handler, tt);\n}\n\n\nstatic int turn_thread(void *arg)\n{\n\tstruct turntest *tt = arg;\n\tint err;\n\n\terr = re_thread_init();\n\tif (err)\n\t\treturn err;\n\n\ttmr_init(&tt->tmr);\n\ttmr_start(&tt->tmr, 0, tmr_handler, tt);\n\n\terr = re_main(NULL);\n\tTEST_ERR(err);\n\n\ttmr_cancel(&tt->tmr);\n\nout:\n\tre_thread_close();\n\treturn err;\n}\n\n\nint test_turn_thread(void)\n{\n\tstruct turntest *tt;\n\tint err;\n\n\terr = turntest_alloc(&tt, IPPROTO_UDP, 0, \"127.0.0.1\");\n\tif (err)\n\t\treturn err;\n\n\ttt->rx_state = RX_DETACH;\n\n\terr = thread_create_name(&tt->thr, \"test_turn_thread\",\n\t\t\t\t turn_thread, tt);\n\tTEST_ERR(err);\n\n\tre_main_timeout(500);\n\n\tmtx_lock(tt->mtx);\n\ttt->rx_state = RX_CLOSE;\n\tmtx_unlock(tt->mtx);\n\n\tthrd_join(tt->thr, &err);\n\tTEST_ERR(err);\n\n\terr = tt->err;\n\tTEST_ERR(err);\n\nout:\n\tmem_deref(tt);\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/types.c",
    "content": "/**\n * @file types.c Types Testcode\n *\n */\n#include <re.h>\n#include \"test.h\"\n\nint test_re_assert_se(void)\n{\n\tint err;\n\n\tre_assert(true);\n\tre_assert_se(!(err = 0));\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/udp.c",
    "content": "/**\n * @file udp.c  UDP testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"udptest\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstruct udp_test {\n\tstruct udp_sock *usc;\n\tstruct udp_sock *uss;\n\tstruct udp_helper *uh;\n\tstruct sa cli;\n\tstruct sa srv;\n\tint tindex;\n\tint err;\n};\n\n\nstatic const char *data0 = \"data from client to server\";\n\n\nstatic void destructor(void *arg)\n{\n\tstruct udp_test *ut = arg;\n\tmem_deref(ut->uh);\n\tmem_deref(ut->usc);\n\tmem_deref(ut->uss);\n}\n\n\nstatic int send_data(struct udp_sock *us, const struct sa *peer,\n\t\t     const char *data)\n{\n\tstruct mbuf *mb = mbuf_alloc(strlen(data) + 1);\n\tint err;\n\tif (!mb)\n\t\treturn ENOMEM;\n\n\t(void)mbuf_write_str(mb, data);\n\tmb->pos = 0;\n\n\terr = udp_send(us, peer, mb);\n\n\tmem_deref(mb);\n\n\treturn err;\n}\n\n\nstatic bool mbuf_compare(const struct mbuf *mb, const char *str)\n{\n\tif (mbuf_get_left(mb) != strlen(str))\n\t\treturn false;\n\n\tif (0 != memcmp(mbuf_buf(mb), str, strlen(str)))\n\t\treturn false;\n\n\treturn true;\n}\n\n\nstatic void udp_recv_client(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct udp_test *ut = arg;\n\n\tswitch (ut->tindex++) {\n\n\tcase 0:\n\t\tif (!mbuf_compare(mb, data0)) {\n\t\t\tut->err = EBADMSG;\n\t\t\tbreak;\n\t\t}\n\t\tif (!sa_cmp(src, &ut->srv, SA_ALL)) {\n\t\t\tut->err = EPROTO;\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\n\tdefault:\n\t\tut->err = ERANGE;\n\t\tbreak;\n\t}\n\n\tif (ut->tindex >= 1)\n\t\tre_cancel();\n}\n\n\n/* Echo server */\nstatic void udp_recv_server(const struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct udp_test *ut = arg;\n\tint err;\n\n\terr = udp_send(ut->uss, src, mb);\n\tif (err)\n\t\tut->err = err;\n\n\t/* Receive a UDP Datagram on this UDP socket */\n\tudp_recv_packet(ut->usc, &ut->srv, mb);\n}\n\n\nstatic bool udp_helper_send(int *err, struct sa *dst,\n\t\t\t    struct mbuf *mb, void *arg)\n{\n\tstruct udp_test *ut = arg;\n\tconst size_t pos = mb->pos;\n\n\tif (!sa_cmp(dst, &ut->srv, SA_ALL)) {\n\t\t*err = EPROTO;\n\t\treturn false;\n\t}\n\n\tif (!mbuf_compare(mb, data0)) {\n\t\t*err = EBADMSG;\n\t\treturn false;\n\t}\n\n\t/* Append a fake protocol trailer */\n\tmb->pos = mb->end;\n\t*err = mbuf_write_str(mb, \"ABCD\");\n\n\tmb->pos = pos;\n\n\treturn false;\n}\n\n\nstatic bool udp_helper_recv(struct sa *src, struct mbuf *mb, void *arg)\n{\n\tstruct udp_test *ut = arg;\n\n\tif (!sa_cmp(src, &ut->srv, SA_ALL))\n\t\tut->err = EPROTO;\n\n\tmb->end -= 4;\n\n\tif (!mbuf_compare(mb, data0))\n\t\tut->err = EBADMSG;\n\n\treturn false;\n}\n\n\nstatic int test_udp_param(const char *addr, const char *mcast)\n{\n\tstruct udp_sock *uss2;\n\tstruct udp_test *ut;\n\tstruct sa group;\n\tint layer = 0;\n\tint err;\n\n\tut = mem_zalloc(sizeof(*ut), destructor);\n\tif (!ut)\n\t\treturn ENOMEM;\n\n\terr  = sa_set_str(&ut->cli, addr, 0);\n\terr |= sa_set_str(&ut->srv, addr, 0);\n\tif (err)\n\t\tgoto out;\n\n\terr  = udp_listen(&ut->usc, &ut->cli, udp_recv_client, ut);\n\terr |= udp_listen(&ut->uss, &ut->srv, udp_recv_server, ut);\n\tif (err)\n\t\tgoto out;\n\n\tif (mcast) {\n\t\tsa_set_str(&group, mcast, 0);\n\n\t\terr = udp_multicast_join(ut->usc, &group);\n\t\tTEST_ERR(err);\n\t\terr = udp_multicast_join(ut->uss, &group);\n\t\tTEST_ERR(err);\n\t}\n\n\tudp_rxsz_set(ut->usc, 65536);\n\tudp_rxsz_set(ut->uss, 65536);\n\n\terr = udp_sockbuf_set(ut->usc, 65536);\n\tTEST_ERR(err);\n\terr = udp_sockbuf_set(ut->uss, 65536);\n\tTEST_ERR(err);\n\n\tudp_rxbuf_presz_set(ut->uss, 16);\n\n\terr  = udp_local_get(ut->usc, &ut->cli);\n\terr |= udp_local_get(ut->uss, &ut->srv);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_ASSERT(NULL == udp_helper_find(ut->usc, layer));\n\n\terr = udp_register_helper(&ut->uh, ut->usc, layer,\n\t\t\t\t  udp_helper_send, udp_helper_recv, ut);\n\tif (err)\n\t\tgoto out;\n\n\tTEST_ASSERT(NULL != udp_helper_find(ut->usc, layer));\n\n\t/* expect failure */\n\tif (!udp_listen(&uss2, &ut->srv, udp_recv_client, ut)) {\n\t\terr = EBUSY;\n\t\tgoto out;\n\t}\n\n\t/* Send from connected client UDP socket */\n\terr = udp_connect(ut->usc, &ut->srv);\n\tif (err)\n\t\tgoto out;\n\n\t/* Start test */\n\terr = send_data(ut->usc, &ut->srv, data0);\n\tif (err)\n\t\tgoto out;\n\n\terr = re_main_timeout(100);\n\tif (err)\n\t\tgoto out;\n\n\tif (ut->err)\n\t\terr = ut->err;\n\n\tif (mcast) {\n\t\tudp_multicast_leave(ut->usc, &group);\n\t\tudp_multicast_leave(ut->uss, &group);\n\t}\n\n out:\n\tmem_deref(ut);\n\n\treturn err;\n}\n\n\nint test_udp(void)\n{\n\tint err = test_udp_param(\"127.0.0.1\", NULL);\n\tTEST_ERR(err);\n\n\terr = test_udp_param(\"127.0.0.1\", \"224.0.1.194\");\n\tTEST_ERR(err);\n\n\tif (test_ipv6_supported()) {\n\t\terr = test_udp_param(\"::1\", NULL);\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\treturn err;\n}\n\n\n#if !defined(WIN32)\nstatic int udp_tos(const char *addr)\n{\n\tstruct udp_test *ut;\n\tint layer = 0;\n\tint err;\n\n\tut = mem_zalloc(sizeof(*ut), destructor);\n\tif (!ut)\n\t\treturn ENOMEM;\n\n\terr  = sa_set_str(&ut->cli, addr, 0);\n\terr |= sa_set_str(&ut->srv, addr, 0);\n\tTEST_ERR(err);\n\n\terr  = udp_listen(&ut->usc, &ut->cli, udp_recv_client, ut);\n\terr |= udp_listen(&ut->uss, &ut->srv, udp_recv_server, ut);\n\tTEST_ERR(err);\n\n\terr  = udp_settos(ut->usc, 184);\n\terr |= udp_settos(ut->uss, 120);\n\tTEST_ERR(err);\n\n\terr  = udp_local_get(ut->usc, &ut->cli);\n\terr |= udp_local_get(ut->uss, &ut->srv);\n\tTEST_ERR(err);\n\n\terr = udp_register_helper(&ut->uh, ut->usc, layer,\n\t\t\t\t  udp_helper_send, udp_helper_recv, ut);\n\tTEST_ERR(err);\n\n\t/* Send from connected client UDP socket */\n\terr = udp_connect(ut->usc, &ut->srv);\n\tTEST_ERR(err);\n\n\t/* Start test */\n\terr = send_data(ut->usc, &ut->srv, data0);\n\tTEST_ERR(err);\n\n\terr = re_main_timeout(100);\n\tTEST_ERR(err);\n\n\tif (ut->err)\n\t\terr = ut->err;\n\n out:\n\tmem_deref(ut);\n\n\treturn err;\n}\n\n\nint test_udp_tos(void)\n{\n\tint err;\n\n\terr = udp_tos(\"127.0.0.1\");\n\tTEST_ERR(err);\n\n\tif (test_ipv6_supported()) {\n\t\terr = udp_tos(\"::1\");\n\t\tTEST_ERR(err);\n\t}\n\n out:\n\treturn err;\n}\n#else\n/* Outcome of the TOS test on Windows would be dependent on the\n * DisableUserTOSSetting Windows registry setting. */\nint test_udp_tos(void)\n{\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "test/unixsock.c",
    "content": "/**\n * @file src/unixsock.c Unix domain sockets\n *\n * Copyright (C) 2022 Sebastian Reimers\n */\n\n#ifdef HAVE_UNISTD_H\n#include <unistd.h>\n#endif\n#include <re.h>\n#include \"test.h\"\n#ifdef WIN32\n#define unlink _unlink\n#endif\n\n#define DEBUG_MODULE \"unixsock\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n#if HAVE_UNIXSOCK\nstatic void http_req_handler(struct http_conn *conn,\n\t\t\t     const struct http_msg *msg, void *arg)\n{\n\t(void)conn;\n\t(void)msg;\n\t(void)arg;\n}\n#endif\n\n\nint test_unixsock(void)\n{\n#if HAVE_UNIXSOCK\n\tstruct sa srv;\n\tre_sock_t fd = RE_BAD_SOCK;\n\tstruct http_sock *sock;\n\tint err;\n\tchar filename[32];\n\tchar socket[128];\n\n\trand_str(filename, sizeof(filename));\n\tre_snprintf(socket, sizeof(socket), \"unix:http_%s.sock\", filename);\n\n\terr = sa_set_str(&srv, socket, 0);\n\tTEST_ERR(err);\n\n\terr = unixsock_listen_fd(&fd, &srv);\n\tTEST_ERR(err);\n\n\terr = http_listen_fd(&sock, fd, http_req_handler, NULL);\n\tTEST_ERR(err);\n\n\tmem_deref(sock);\n\n\terr = unlink(&socket[5]);\n\tTEST_ERR(err);\n\n\tint error = unixsock_listen_fd(NULL, NULL);\n\tASSERT_EQ(EINVAL, error);\n\n\tre_sock_t fd_dummy = RE_BAD_SOCK;\n\tstruct sa sa_dummy;\n\tsa_set_str(&sa_dummy, \"1.2.3.4\", 1234);\n\terror = unixsock_listen_fd(&fd_dummy, &sa_dummy);\n\tASSERT_EQ(EINVAL, error);\n\nout:\n\tif (err)\n\t\t(void)unlink(&socket[5]);\n\treturn err;\n#else\n\tint err = unixsock_listen_fd(NULL, NULL);\n\tTEST_EQUALS(ENOTSUP, err);\n\n\treturn 0;\nout:\n\treturn err;\n#endif\n}\n"
  },
  {
    "path": "test/uri.c",
    "content": "/**\n * @file uri.c URI Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"testuri\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nint test_uri(void)\n{\n\tconst char *uriv[] = {\n\t\t\"sip:user@host:5060;transport=udp\",\n\t\t\"sip:user@host:5060\",\n\t\t\"sip:host:5060\",\n\t\t\"sip:host\",\n\t\t\"sip:user@81.187.91.2:28481\",\n\t\t\"sip:user@81.187.91.2:28481\",\n\t\t\"sip:81.187.91.2:28481\",\n\t\t\"sips:81.187.91.2:28481\",\n\t\t\"sip:192.168.43.83:443/wss/;transport=wss\",\n\n\t\t/* RFC 3261 */\n\t\t\"sip:alice@atlanta.com\",\n\t\t\"sip:alice@atlanta.com;transport=tcp\",\n\t\t\"sips:alice@atlanta.com?subject=project%20&priority=urgent\",\n\t\t\"sip:+1-212-555-1212@gateway.com;user=phone\",\n\t\t\"sips:1212@gateway.com\",\n\t\t\"sip:alice@192.0.2.4\",\n\t\t\"sip:atlanta.com;method=REGISTER?to=alice%40atlanta.com\",\n\t\t\"sip:alice;day=tuesday@atlanta.com\",\n\n\t\t/* IPv6 */\n\t\t\"sip:[::1];transport=udp\",\n\t\t\"sip:[::1]:1234;transport=udp\",\n\t\t\"sip:user@[::1];transport=udp\",\n\t\t\"sip:user@[::1]:1234;transport=udp\",\n\t\t\"sip:user@[::1]:1234;transport=udp?foo=bar\",\n\n\t\t/* draft-ietf-sipping-ipv6-torture-tests-00 */\n\t\t\"sip:[2001:db8::10]\",\n\t\t\"sip:[2001:db8::10:5070]\",\n\t\t\"sip:[2001:db8::10]:5070\",\n\t\t\"sip:user@[2001:db8::10]\",\n\t};\n\tstruct uri uri;\n\tstruct mbuf mb;\n\tint err = EINVAL;\n\tsize_t i;\n\n\tmbuf_init(&mb);\n\n\tfor (i=0; i<RE_ARRAY_SIZE(uriv); i++) {\n\t\tstruct pl pl0, pl;\n\n\t\t/* Decode */\n\t\tpl_set_str(&pl0, uriv[i]);\n\t\terr = uri_decode(&uri, &pl0);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"uri: uri_decode() failed (%s) (%m)\\n\",\n\t\t\t\t      uriv[i], err);\n\t\t\tgoto out;\n\t\t}\n\n\t\t/* Encode */\n\t\tmbuf_reset(&mb);\n\t\terr = mbuf_printf(&mb, \"%H\", uri_encode, &uri);\n\t\tif (err) {\n\t\t\tgoto out;\n\t\t}\n\n\t\t/* Compare */\n\t\tpl.p = (const char *)mb.buf;\n\t\tpl.l = mb.end;\n\t\terr = pl_cmp(&pl, &pl0);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"uri comp: ref=(%s), gen=(%r) (%m)\\n\",\n\t\t\t\t      uriv[i], &pl, err);\n\t\t\tgoto out;\n\t\t}\n\t}\n\n\tconst struct pl uri_pass = PL(\"sip:user:pass@host\");\n\n\terr = uri_decode(&uri, &uri_pass);\n\tTEST_ERR(err);\n\n\tTEST_STRCMP(\"user\", 4, uri.user.p, uri.user.l);\n\tTEST_STRCMP(\"host\", 4, uri.host.p, uri.host.l);\n\n out:\n\tmbuf_reset(&mb);\n\treturn err;\n}\n\n\nint test_uri_encode(void)\n{\n\tconst struct {\n\t\tstruct uri uri;\n\t\tconst char *enc;\n\t} uriv[] = {\n\t\t{{PL(\"sip\"),\n\t\t  PL(\"user\"),\n\t\t  PL(\"host\"), 0,\n\t\t  5060,\n\t\t  PL(\"\"),\n\t\t  PL(\";transport=udp\"),\n\t\t  PL(\"\")},\n\t\t \"sip:user@host:5060;transport=udp\"\n\t\t},\n\t\t{{PL(\"sip\"),\n\t\t  PL(\"user\"),\n\t\t  PL(\"host\"), 0,\n\t\t  443,\n\t\t  PL(\"/wss/\"),\n\t\t  PL(\";transport=wss\"),\n\t\t  PL(\"\")},\n\t\t \"sip:user@host:443/wss/;transport=wss\"\n\t\t},\n\t\t{{PL(\"sip\"),\n\t\t  PL(\"user\"),\n\t\t  PL(\"::1\"), AF_INET6,\n\t\t  1234,\n\t\t  PL(\"\"),\n\t\t  PL(\";transport=udp\"),\n\t\t  PL(\"\")},\n\t\t \"sip:user@[::1]:1234;transport=udp\"\n\t\t}\n\t};\n\tstruct mbuf mb;\n\tint err = EINVAL;\n\tsize_t i;\n\n\tmbuf_init(&mb);\n\n\tfor (i=0; i<RE_ARRAY_SIZE(uriv); i++) {\n\t\tstruct pl pl;\n\n\t\t/* Encode */\n\t\tmb.pos = 0;\n\t\tmb.end = 0;\n\t\terr = mbuf_printf(&mb, \"%H\", uri_encode, &uriv[i].uri);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\t/* Compare */\n\t\tpl.p = (const char *)mb.buf;\n\t\tpl.l = mb.end;\n\t\terr = pl_strcmp(&pl, uriv[i].enc);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"uri enc: ref=(%s), gen=(%r) (%m)\\n\",\n\t\t\t\t      uriv[i].enc, &pl, err);\n\t\t\tgoto out;\n\t\t}\n\t}\n\n out:\n\tmbuf_reset(&mb);\n\treturn err;\n}\n\n\nint test_uri_user(void)\n{\n\tconst struct {\n\t\tconst char *enc;\n\t\tconst char *dec;\n\t} uriv[] = {\n\t\t{\"alice%40atlanta.com\", \"alice@atlanta.com\"},\n\t\t{\"project%20x\", \"project x\"},\n\t\t{\"*21%23\", \"*21#\"}\n\t};\n\tstruct mbuf mbe, mbd;\n\tint err = EINVAL;\n\tsize_t i;\n\n\tmbuf_init(&mbd);\n\tmbuf_init(&mbe);\n\n\tfor (i=0; i<RE_ARRAY_SIZE(uriv); i++) {\n\t\tstruct pl ple, pld, pl;\n\n\t\t/* Decode and compare */\n\t\tpl_set_str(&ple, uriv[i].enc);\n\t\tmbuf_reset(&mbd);\n\t\terr = mbuf_printf(&mbd, \"%H\", uri_user_unescape, &ple);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tpl.p = (const char *)mbd.buf;\n\t\tpl.l = mbd.end;\n\t\terr = pl_strcmp(&pl, uriv[i].dec);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"uri dec comp: ref=(%s), gen=(%r)\\n\",\n\t\t\t\t      uriv[i].dec, &pl);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Encode and compare */\n\t\tpl_set_str(&pld, uriv[i].dec);\n\t\tmbuf_reset(&mbe);\n\t\terr = mbuf_printf(&mbe, \"%H\", uri_user_escape, &pld);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\t/* Compare */\n\t\tpl.p = (const char *)mbe.buf;\n\t\tpl.l = mbe.end;\n\t\terr = pl_strcmp(&pl, uriv[i].enc);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"uri enc comp: ref=(%s), gen=(%r)\\n\",\n\t\t\t\t      uriv[i].enc, &pl);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tmbuf_reset(&mbe);\n\tmbuf_reset(&mbd);\n\treturn err;\n}\n\n\nint test_uri_headers(void)\n{\n\tconst struct {\n\t\tstruct pl enc;\n\t\tstruct pl dec;\n\t} uriv[] = {\n\t\t{PL(\"alice%40atlanta.com\"),\n\t\t PL(\"alice@atlanta.com\")\n\t\t},\n\t\t{PL(\"project%20x\"),\n\t\t PL(\"project x\")\n\t\t},\n\t\t{PL(\"%3Chttp://www.foo.com%3E\"),\n\t\t PL(\"<http://www.foo.com>\")\n\t\t}\n\t};\n\tstruct mbuf mbe, mbd;\n\tint err = EINVAL;\n\tsize_t i;\n\n\tmbuf_init(&mbd);\n\tmbuf_init(&mbe);\n\n\tfor (i=0; i<RE_ARRAY_SIZE(uriv); i++) {\n\t\tstruct pl pl;\n\n\t\t/* Decode and compare */\n\t\tmbuf_reset(&mbd);\n\t\terr = mbuf_printf(&mbd, \"%H\", uri_header_unescape,\n\t\t\t\t  &uriv[i].enc);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tpl.p = (const char *)mbd.buf;\n\t\tpl.l = mbd.end;\n\t\terr = pl_cmp(&pl, &uriv[i].dec);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"uri dec comp: ref=(%r), gen=(%r)\\n\",\n\t\t\t\t      &uriv[i].dec, &pl);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Encode and compare */\n\t\tmbuf_reset(&mbe);\n\t\terr = mbuf_printf(&mbe, \"%H\", uri_header_escape, &uriv[i].dec);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\t/* Compare */\n\t\tpl.p = (const char *)mbe.buf;\n\t\tpl.l = mbe.end;\n\t\terr = pl_cmp(&pl, &uriv[i].enc);\n\t\tif (err) {\n\t\t\tDEBUG_WARNING(\"uri enc comp: ref=(%r), gen=(%r)\\n\",\n\t\t\t\t      &uriv[i].enc, &pl);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tmbuf_reset(&mbe);\n\tmbuf_reset(&mbd);\n\treturn err;\n}\n\n\nstatic int uri_param_handler(const struct pl *name, const struct pl *val,\n\t\t\t     void *arg)\n{\n\tuint32_t *n = arg;\n\tint err;\n\n\tswitch ((*n)++) {\n\n\tcase 0:\n\t\terr  = pl_strcmp(name, \"rport\");\n\t\tbreak;\n\n\tcase 1:\n\t\terr  = pl_strcmp(name, \"transport\");\n\t\terr |= pl_strcmp(val, \"udp\");\n\t\tbreak;\n\n\tdefault:\n\t\treturn EINVAL;\n\t}\n\n\treturn err;\n}\n\n\nstatic int uri_header_handler(const struct pl *name, const struct pl *val,\n\t\t\t     void *arg)\n{\n\tuint32_t *n = arg;\n\tint err;\n\n\tswitch ((*n)++) {\n\n\tcase 0:\n\t\terr  = pl_strcmp(name, \"Subject\");\n\t\terr |= pl_strcmp(val, \"Urgent\");\n\t\tbreak;\n\n\tcase 1:\n\t\terr  = pl_strcmp(name, \"bar\");\n\t\terr |= pl_strcmp(val, \"2\");\n\t\tbreak;\n\n\tdefault:\n\t\treturn EINVAL;\n\t}\n\n\treturn err;\n}\n\n\nint test_uri_params_headers(void)\n{\n\tconst char *paramv[] = {\n\t\t\";rport;transport=udp\"\n\t};\n\tconst char *headerv[] = {\n\t\t\"?Subject=Urgent&bar=2\"\n\t};\n\tint err;\n\tuint32_t i;\n\n\terr = ENOENT;\n\tfor (i=0; i<RE_ARRAY_SIZE(paramv); i++) {\n\t\tstatic const struct pl transp = PL(\"transport\");\n\t\tstruct pl pl, val;\n\t\tuint32_t n = 0;\n\n\t\tpl_set_str(&pl, paramv[i]);\n\t\terr = uri_param_get(&pl, &transp, &val);\n\t\tif (err)\n\t\t\tbreak;\n\t\terr = pl_strcmp(&val, \"udp\");\n\t\tif (err)\n\t\t\tbreak;\n\t\terr = uri_params_apply(&pl, uri_param_handler, &n);\n\t\tif (err)\n\t\t\tbreak;\n\t\tif (!n)\n\t\t\terr = ENOENT;\n\t}\n\tif (err)\n\t\tgoto out;\n\n\terr = ENOENT;\n\tfor (i=0; i<RE_ARRAY_SIZE(headerv); i++) {\n\t\tstatic const struct pl subj = PL(\"Subject\");\n\t\tstruct pl pl, val;\n\t\tuint32_t n = 0;\n\n\t\tpl_set_str(&pl, headerv[i]);\n\n\t\terr = uri_header_get(&pl, &subj, &val);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = pl_strcmp(&val, \"Urgent\");\n\t\tif (err)\n\t\t\tbreak;\n\n\t\terr = uri_headers_apply(&pl, uri_header_handler, &n);\n\t\tif (err)\n\t\t\tbreak;\n\t\tif (!n)\n\t\t\terr = ENOENT;\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic int devnull_print_handler(const char *p, size_t size, void *arg)\n{\n\t(void)p;\n\t(void)size;\n\t(void)arg;\n\treturn 0;\n}\n\n\nint test_uri_escape(void)\n{\n\tstruct re_printf pf_devnull = {devnull_print_handler, NULL};\n\tconst struct pl pl1 = PL(\"%\");\n\tconst struct pl pl2 = PL(\"%0\");\n\tint e, err = 0;\n\n\t/* silence warnings */\n\tdbg_init(DBG_ERR, 0);\n\n\te = uri_user_unescape(&pf_devnull, &pl1);\n\tTEST_EQUALS(EBADMSG, e);\n\n\te = uri_user_unescape(&pf_devnull, &pl2);\n\tTEST_EQUALS(EBADMSG, e);\n\n out:\n\tdbg_init(DBG_WARNING, 0);\n\n\treturn err;\n}\n"
  },
  {
    "path": "test/vid.c",
    "content": "/**\n * @file vid.c Video Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"vid\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstatic const enum vidfmt fmtv[VID_FMT_N] = {\n\tVID_FMT_YUV420P,\n\tVID_FMT_YUYV422,\n\tVID_FMT_UYVY422,\n\tVID_FMT_RGB32,\n\tVID_FMT_ARGB,\n\tVID_FMT_NV12,\n\tVID_FMT_NV21,\n\tVID_FMT_YUV444P,\n\tVID_FMT_YUV422P,\n};\n\n\nstatic int test_vidsz_cmp(void)\n{\n\tstruct vidsz a = {64, 64}, b = {64, 64}, c = {32, 32};\n\tint err = 0;\n\n\tTEST_ASSERT(!vidsz_cmp(NULL, NULL));\n\tTEST_ASSERT( vidsz_cmp(&a, &a));\n\tTEST_ASSERT( vidsz_cmp(&a, &b));\n\tTEST_ASSERT(!vidsz_cmp(&a, &c));\n\n out:\n\treturn err;\n}\n\n\nstatic int test_vidrect_cmp(void)\n{\n\tstruct vidrect a = {10, 10, 30, 30};\n\tstruct vidrect b = {10, 10, 30, 30};\n\tstruct vidrect c = {10, 10, 40, 40};\n\tstruct vidrect d = {20, 20, 30, 30};\n\tint err = 0;\n\n\tTEST_ASSERT(!vidrect_cmp(NULL, NULL));\n\tTEST_ASSERT( vidrect_cmp(&a, &a));\n\tTEST_ASSERT( vidrect_cmp(&a, &b));\n\tTEST_ASSERT(!vidrect_cmp(&a, &c));\n\tTEST_ASSERT(!vidrect_cmp(&a, &d));\n\n out:\n\treturn err;\n}\n\n\nstatic int test_vidframe_size(void)\n{\n\tstatic const struct vidsz vidsz = {32, 32};\n\tsize_t i;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(fmtv); i++) {\n\n\t\tsize_t sz = vidframe_size(fmtv[i], &vidsz);\n\n\t\tif (sz == 0) {\n\t\t\tDEBUG_WARNING(\"unexpected zero size for format %s\\n\",\n\t\t\t\t      vidfmt_name(fmtv[i]));\n\t\t\treturn EINVAL;\n\t\t}\n\n\t\tconst char *name = vidfmt_name(fmtv[i]);\n\t\tASSERT_TRUE(str_isset(name));\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic int test_vidframe_alloc(void)\n{\n\tstatic const struct vidsz vidsz = {32, 32};\n\tstruct vidframe *vf = NULL;\n\tsize_t i;\n\tint err = ENOENT;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(fmtv); i++) {\n\n\t\terr = vidframe_alloc(&vf, fmtv[i], &vidsz);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tTEST_ASSERT(vidframe_isvalid(vf));\n\n\t\tTEST_NOT_EQUALS(0, vf->linesize[0]);\n\t\tTEST_ASSERT(vidsz_cmp(&vidsz, &vf->size));\n\t\tTEST_EQUALS(fmtv[i], vf->fmt);\n\n\t\tvf = mem_deref(vf);\n\t}\n\n out:\n\tmem_deref(vf);\n\treturn err;\n}\n\n\n/*\n * Create one RGB32 pixel in native endianness\n */\n#define RGB32(r, g, b)  (r)<<16 | (g)<<8 | (b)\n\n\n/*\n * Test a RGB32 Video-frame with 2 x 2 pixels and 3 RGB pixel\n *\n *    .--+----\n *    |RG|\n *    |B |\n *    +--+----\n *    |\n *    |\n */\nstatic int test_vidframe_rgb32_2x2_red(void)\n{\n\tstruct vidframe vf;\n\tstruct vidsz sz = {2, 2};\n\tuint8_t buf[2*4 + 2*4];\n\tconst uint32_t pix[2][2] = {\n\n\t\t{ RGB32(255U, 0, 0), RGB32(0, 255U, 0) },\n\t\t{ RGB32(0, 0, 255U), RGB32(0,    0, 0) }\n\t};\n\tint err = 0;\n\n\tmemset(buf, 0, sizeof(buf));\n\tvidframe_init_buf(&vf, VID_FMT_RGB32, &sz, buf);\n\n\tTEST_EQUALS(buf,  vf.data[0]);\n\tTEST_EQUALS(NULL, vf.data[1]);\n\tTEST_EQUALS(NULL, vf.data[2]);\n\tTEST_EQUALS(NULL, vf.data[3]);\n\n\tTEST_EQUALS(8, vf.linesize[0]);\n\tTEST_EQUALS(0, vf.linesize[1]);\n\tTEST_EQUALS(0, vf.linesize[2]);\n\tTEST_EQUALS(0, vf.linesize[3]);\n\n\tTEST_EQUALS(2, vf.size.w);\n\tTEST_EQUALS(2, vf.size.h);\n\n\tTEST_EQUALS(VID_FMT_RGB32, vf.fmt);\n\n\tvidframe_draw_point(&vf, 0, 0, 255,   0,   0);\n\tvidframe_draw_point(&vf, 1, 0,   0, 255,   0);\n\tvidframe_draw_point(&vf, 0, 1,   0,   0, 255);\n\tvidframe_draw_point(&vf, 1, 1,   0,   0,   0);\n\n\tTEST_MEMCMP(pix[0], sizeof(pix[0]), vf.data[0], 8);\n\tTEST_MEMCMP(pix[1], sizeof(pix[1]), vf.data[0] + vf.linesize[0], 8);\n\n out:\n\treturn err;\n}\n\n\nstatic int test_vidframe_yuv_2x2_white(enum vidfmt fmt, unsigned chroma)\n{\n\tstruct vidframe *vf;\n\tstruct vidsz sz = {2, 2};\n\tconst uint8_t ypix[4] = {235, 235, 235, 235};\n\tconst uint8_t uvpix[4] = {128, 128, 128, 128};\n\tconst unsigned chroma_sq = chroma * chroma;\n\tint err;\n\n\terr = vidframe_alloc(&vf, fmt, &sz);\n\tif (err)\n\t\treturn err;\n\n\tvidframe_fill(vf, 255, 255, 255);\n\n\tTEST_NOT_EQUALS(NULL, vf->data[0]);\n\tTEST_NOT_EQUALS(NULL, vf->data[1]);\n\tTEST_NOT_EQUALS(NULL, vf->data[2]);\n\tTEST_EQUALS(NULL, vf->data[3]);\n\n\tTEST_EQUALS(2, vf->linesize[0]);\n\tTEST_EQUALS(chroma, vf->linesize[1]);\n\tTEST_EQUALS(chroma, vf->linesize[2]);\n\tTEST_EQUALS(0, vf->linesize[3]);\n\n\tTEST_EQUALS(2, vf->size.w);\n\tTEST_EQUALS(2, vf->size.h);\n\n\tTEST_EQUALS(fmt, vf->fmt);\n\n\tTEST_ASSERT(chroma_sq <= sizeof(uvpix));\n\tTEST_MEMCMP(ypix, sizeof(ypix), vf->data[0], 4);\n\tTEST_MEMCMP(uvpix, chroma_sq, vf->data[1], chroma_sq);\n\tTEST_MEMCMP(uvpix, chroma_sq, vf->data[2], chroma_sq);\n\n out:\n\tmem_deref(vf);\n\n\treturn err;\n}\n\n\nstatic int test_vid_draw(void)\n{\n\tstatic const struct vidsz vidsz = {320, 240};\n\tstruct vidframe *vf = NULL, *vf2 = NULL;\n\tint err = 0;\n\n\tstatic const enum vidfmt drawfmtv[] = {\n\t\tVID_FMT_YUV420P,\n\t\tVID_FMT_NV12,\n\t\tVID_FMT_NV21,\n\t\tVID_FMT_YUV444P,\n\t\tVID_FMT_YUV422P,\n\t};\n\n\tfor (size_t i=0; i<RE_ARRAY_SIZE(drawfmtv); i++) {\n\n\t\terr  = vidframe_alloc(&vf,  drawfmtv[i], &vidsz);\n\t\terr |= vidframe_alloc(&vf2, drawfmtv[i], &vidsz);\n\t\tif (err)\n\t\t\tbreak;\n\n\t\tif (vf->fmt == VID_FMT_YUV422P) {\n\n\t\t\tASSERT_EQ(320, vf->linesize[0]);\n\t\t\tASSERT_EQ(160, vf->linesize[1]);\n\t\t\tASSERT_EQ(160, vf->linesize[2]);\n\t\t\tASSERT_EQ(  0, vf->linesize[3]);\n\t\t}\n\n\t\tfor (unsigned x=0; x<vidsz.w; x++)\n\t\t\tfor (unsigned y=0; y<vidsz.h; y++)\n\t\t\t\tvidframe_draw_point(vf, x, y, 127, 127, 127);\n\n\t\tvidframe_fill(vf, 255, 255, 255);\n\n\t\tvidframe_copy(vf2, vf);\n\n\t\tvf2 = mem_deref(vf2);\n\t\tvf  = mem_deref(vf);\n\t}\n\n out:\n\tmem_deref(vf2);\n\tmem_deref(vf);\n\n\treturn err;\n}\n\n\nint test_vid(void)\n{\n\tint err;\n\n\terr = test_vidsz_cmp();\n\tTEST_ERR(err);\n\n\terr = test_vidrect_cmp();\n\tTEST_ERR(err);\n\n\terr = test_vidframe_size();\n\tTEST_ERR(err);\n\n\terr = test_vidframe_alloc();\n\tTEST_ERR(err);\n\n\terr = test_vidframe_rgb32_2x2_red();\n\tTEST_ERR(err);\n\n\terr = test_vidframe_yuv_2x2_white(VID_FMT_YUV420P, 1);\n\tTEST_ERR(err);\n\n\terr = test_vidframe_yuv_2x2_white(VID_FMT_YUV444P, 2);\n\tTEST_ERR(err);\n\n\terr = test_vid_draw();\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/vidconv.c",
    "content": "/**\n * @file vidconv.c Video conversion Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n#include <string.h>\n#include <re.h>\n#include <rem.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"vidconv\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\n/*\n * http://en.wikipedia.org/wiki/YCbCr\n *\n * ITU-R BT.601 conversion\n *\n * Digital YCbCr can derived from digital R'dG'dB'd\n * (8 bits per sample, each using the full range with\n * zero representing black and 255 representing white)\n * according to the following equations:\n */\n\n\n#if 0\nstatic double rgb2y_ref(int r, int g, int b)\n{\n\treturn 16.0 + (65.738*r)/256 + (129.057*g)/256 + (25.064*b)/256;\n}\n\nstatic double rgb2u_ref(int r, int g, int b)\n{\n\treturn 128 - 37.945*r/256 - 74.494*g/256 + 112.439*b/256;\n}\n\nstatic double rgb2v_ref(int r, int g, int b)\n{\n\treturn 128 + 112.439*r/256 - 94.154*g/256 - 18.285*b/256;\n}\n#endif\n\n\n#define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X)\n\n\n/* RGB -> YUV */\n#define RGB2Y(R, G, B) \\\n\tCLIP(( (  66 * (R) + 129 * (G) +  25 * (B) + 128) >> 8) +  16)\n#define RGB2U(R, G, B) \\\n\tCLIP(( ( -38 * (R) -  74 * (G) + 112 * (B) + 128) >> 8) + 128)\n#define RGB2V(R, G, B) \\\n\tCLIP(( ( 112 * (R) -  94 * (G) -  18 * (B) + 128) >> 8) + 128)\n\n\n#define test_vid_rgb2yuv_color(r, g, b)\t\t\t\\\n\t\t\t\t\t\t\t\\\n\tTEST_EQUALS(RGB2Y(r, g, b), rgb2y(r, g, b));\t\\\n\tTEST_EQUALS(RGB2U(r, g, b), rgb2u(r, g, b));\t\\\n\tTEST_EQUALS(RGB2V(r, g, b), rgb2v(r, g, b));\n\n\nstatic int test_vid_rgb2yuv(void)\n{\n\tint r, g, b;\n\tint err = 0;\n\n\t/* combine 2 color components */\n\tfor (r=0; r<256; r++) {\n\t\tfor (g=0; g<256; g++) {\n\t\t\ttest_vid_rgb2yuv_color(r, g, 0);\n\t\t}\n\t}\n\n\tfor (r=0; r<256; r++) {\n\t\tfor (b=0; b<256; b++) {\n\t\t\ttest_vid_rgb2yuv_color(r, 0, b);\n\t\t}\n\t}\n\n\tfor (g=0; g<256; g++) {\n\t\tfor (b=0; b<256; b++) {\n\t\t\ttest_vid_rgb2yuv_color(0, g, b);\n\t\t}\n\t}\n\n out:\n\treturn err;\n}\n\n\nstatic bool vidframe_cmp(const struct vidframe *a, const struct vidframe *b)\n{\n\tint i;\n\n\tif (!a || !b)\n\t\treturn false;\n\n\tif (a->fmt != b->fmt)\n\t\treturn false;\n\n\tfor (i=0; i<4; i++) {\n\n\t\tif (a->linesize[i] != b->linesize[i])\n\t\t\treturn false;\n\n\t\tif (!a->data[i] != !b->data[i])\n\t\t\treturn false;\n\n\t\tif (a->data[i] && b->data[i]) {\n\n\t\t\tif (memcmp(a->data[i], b->data[i], a->linesize[i]))\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn vidsz_cmp(&a->size, &b->size);\n}\n\n\nstatic void vidframe_dump(const struct vidframe *f)\n{\n\tint i;\n\n\tif (!f)\n\t\treturn;\n\n\t(void)re_printf(\"vidframe %u x %u:\\n\", f->size.w, f->size.h);\n\n\tfor (i=0; i<4; i++) {\n\n\t\tif (f->linesize[i] && f->data[i]) {\n\n\t\t\t(void)re_printf(\"%d: %u bytes [%w]\\n\",\n\t\t\t\t\ti, f->linesize[i], f->data[i],\n\t\t\t\t\t(size_t)min(f->linesize[i], 16));\n\t\t}\n\t}\n}\n\n\nstatic void write_pattern(uint8_t *buf, size_t len)\n{\n\tsize_t i;\n\n\tfor (i=0; i<len; i++)\n\t\tbuf[i] = (uint8_t)i;\n}\n\n\n/**\n * Test vidconv module by scaling a random image up and then down.\n * The two images should then be pixel accurate.\n */\nstatic int test_vidconv_scaling_base(enum vidfmt via_fmt)\n{\n\tenum { WIDTH = 40, HEIGHT = 30, SCALE = 2 };\n\tstruct vidframe *f0 = NULL, *f1 = NULL, *f2 = NULL;\n\tconst struct vidsz size0 = {WIDTH, HEIGHT};\n\tconst struct vidsz size1 = {WIDTH*SCALE, HEIGHT*SCALE};\n\tstruct vidrect rect1 = {0, 0, WIDTH*SCALE, HEIGHT*SCALE};\n\tstruct vidrect rect2 = {0, 0, WIDTH, HEIGHT};\n\tint i, err = 0;\n\n\terr |= vidframe_alloc(&f0, VID_FMT_YUV420P, &size0);\n\terr |= vidframe_alloc(&f1, via_fmt, &size1);\n\terr |= vidframe_alloc(&f2, VID_FMT_YUV420P, &size0);\n\tif (err)\n\t\tgoto out;\n\n\t/* generate a random image */\n\tfor (i=0; i<4; i++) {\n\n\t\tif (f0->data[i])\n\t\t\twrite_pattern(f0->data[i], f0->linesize[i]);\n\t}\n\n\tvidconv(f1, f0, &rect1);\n\tvidconv(f2, f1, &rect2);\n\n\tif (!vidframe_cmp(f2, f0)) {\n\n\t\tvidframe_dump(f0);\n\t\tvidframe_dump(f2);\n\n\t\terr = EBADMSG;\n\t\tgoto out;\n\t}\n\n out:\n\tmem_deref(f2);\n\tmem_deref(f1);\n\tmem_deref(f0);\n\n\treturn err;\n}\n\n\n/*\n * verify that pixel conversion between different planar and packed\n * pixel formats is working\n */\nint test_vidconv_pixel_formats(void)\n{\n\tstruct plane {\n\t\tsize_t sz;\n\t\tconst char *data;\n\t};\n\tstatic const struct test {\n\t\tenum vidfmt src_fmt;\n\t\tstruct plane src_planev[3];\n\t\tenum vidfmt dst_fmt;\n\t\tstruct plane dst_planev[3];\n\t} testv [] = {\n\n\t\t/* UYVY422 to YUV420P */\n\t\t{\n\t\t\tVID_FMT_UYVY422,\n\t\t\t{ {32,\n\t\t\t   \"\\x20\\x00\\x30\\x01\"\n\t\t\t   \"\\x21\\x02\\x31\\x03\"\n\t\t\t   \"\\x20\\x04\\x30\\x05\"\n\t\t\t   \"\\x21\\x06\\x31\\x07\"\n\t\t\t   \"\\x22\\x08\\x32\\x09\"\n\t\t\t   \"\\x23\\x0a\\x33\\x0b\"\n\t\t\t   \"\\x22\\x0c\\x32\\x0d\"\n\t\t\t   \"\\x23\\x0e\\x33\\x0f\"},\n\t\t\t  {0,0},\n\t\t\t  {0,0}\n\t\t\t},\n\n\t\t\tVID_FMT_YUV420P,\n\t\t\t{ {16,\n\t\t\t   \"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\"\n\t\t\t   \"\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\"},\n\t\t\t  {4, \"\\x20\\x21\\x22\\x23\"},\n\t\t\t  {4, \"\\x30\\x31\\x32\\x33\"}\n\t\t\t},\n\t\t},\n\n\t\t/* NV12 to YUV420P */\n\t\t{\n\t\t\tVID_FMT_NV12,\n\t\t\t{ {16,\n\t\t\t   \"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\"\n\t\t\t   \"\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\"},\n\t\t\t  {8, \"\\x20\\x30\\x21\\x31\\x22\\x32\\x23\\x33\"},\n\t\t\t  {0,0}\n\t\t\t},\n\n\t\t\tVID_FMT_YUV420P,\n\t\t\t{ {16,\n\t\t\t   \"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\"\n\t\t\t   \"\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\"},\n\t\t\t  {4, \"\\x20\\x21\\x22\\x23\"},\n\t\t\t  {4, \"\\x30\\x31\\x32\\x33\"}\n\t\t\t},\n\t\t},\n\n\t\t/* YUV420P to NV12 */\n\t\t{\n\t\t\tVID_FMT_YUV420P,\n\t\t\t{ {16,\n\t\t\t   \"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\"\n\t\t\t   \"\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\"},\n\t\t\t  {4, \"\\x20\\x21\\x22\\x23\"},\n\t\t\t  {4, \"\\x30\\x31\\x32\\x33\"}\n\t\t\t},\n\n\t\t\tVID_FMT_NV12,\n\t\t\t{ {16,\n\t\t\t   \"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\"\n\t\t\t   \"\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\"},\n\t\t\t  {8, \"\\x20\\x30\\x21\\x31\\x22\\x32\\x23\\x33\"},\n\t\t\t  {0,0}\n\t\t\t},\n\t\t},\n\n#if 0\n\t\t/* RGB32 to YUV444P */\n\t\t{\n\t\t\tVID_FMT_RGB32,\n\t\t\t{ { (16*4),\n\t\t\t    \"\\x00\\x00\\x00\\x00\" \"\\x00\\x00\\x00\\x00\" /* black */\n\t\t\t    \"\\xff\\x00\\x00\\x00\" \"\\xff\\x00\\x00\\x00\" /* red */\n\t\t\t    \"\\x00\\xff\\x00\\x00\" \"\\x00\\xff\\x00\\x00\" /* green */\n\t\t\t    \"\\x00\\x00\\xff\\x00\" \"\\x00\\x00\\xff\\x00\" /* blue */\n\t\t\t    \"\\xff\\x00\\xff\\x00\" \"\\xff\\x00\\xff\\x00\"\n\t\t\t    \"\\x00\\xff\\xff\\x00\" \"\\x00\\xff\\xff\\x00\"\n\t\t\t    \"\\xff\\xff\\x00\\x00\" \"\\xff\\xff\\x00\\x00\"\n\t\t\t    \"\\xff\\xff\\xff\\xff\" \"\\xff\\xff\\xff\\xff\"},/* white */\n\n\t\t\t  {0, \"\"},\n\t\t\t  {0, \"\"}\n\t\t\t},\n\n\t\t\tVID_FMT_YUV444P,\n\t\t\t{ {16,\n\t\t\t   \"\\x10\\x10\\x29\\x29\" \"\\x90\\x90\\x52\\x52\"\n\t\t\t   \"\\x6b\\x6b\\xd2\\xd2\" \"\\xa9\\xa9\\xeb\\xeb\"},\n\t\t\t  {16,\n\t\t\t   \"\\x80\\x80\\xf0\\xf0\" \"\\x36\\x36\\x5a\\x5a\"\n\t\t\t   \"\\xca\\xca\\x10\\x10\" \"\\xa6\\xa6\\x80\\x80\"},\n\t\t\t  {16,\n\t\t\t   \"\\x80\\x80\\x6e\\x6e\" \"\\x22\\x22\\xf0\\xf0\"\n\t\t\t   \"\\xde\\xde\\x92\\x92\" \"\\x10\\x10\\x80\\x80\"},\n\t\t\t},\n\t\t},\n#endif\n\n\t};\n\tstruct vidframe *fsrc = NULL, *fdst = NULL;\n\tconst struct vidsz sz = {4, 4};\n\tunsigned i, p;\n\tint err = 0;\n\n\tfor (i=0; i<RE_ARRAY_SIZE(testv); i++) {\n\n\t\tconst struct test *test = &testv[i];\n\n#if 0\n\t\tre_printf(\"test[%u] %s to %s\\n\", i,\n\t\t\t  vidfmt_name(test->src_fmt),\n\t\t\t  vidfmt_name(test->dst_fmt));\n#endif\n\n\t\terr |= vidframe_alloc(&fsrc, test->src_fmt, &sz);\n\t\terr |= vidframe_alloc(&fdst, test->dst_fmt, &sz);\n\t\tif (err)\n\t\t\tgoto out;\n\n\t\tfor (p=0; p<3; p++) {\n\t\t\tif (test->src_planev[p].sz) {\n\t\t\t\tmemcpy(fsrc->data[p],\n\t\t\t\t       test->src_planev[p].data,\n\t\t\t\t       test->src_planev[p].sz);\n\t\t\t}\n\t\t}\n\n\t\tvidconv(fdst, fsrc, 0);\n\n\t\tfor (p=0; p<3; p++) {\n\n\t\t\tsize_t size = test->dst_planev[p].sz;\n\n\t\t\tif (!test->dst_planev[p].data)\n\t\t\t\tcontinue;\n\n\t\t\tTEST_MEMCMP(test->dst_planev[p].data,\n\t\t\t\t    test->dst_planev[p].sz,\n\t\t\t\t    fdst->data[p],\n\t\t\t\t    size);\n\t\t}\n\n\t\tfdst = mem_deref(fdst);\n\t\tfsrc = mem_deref(fsrc);\n\t}\n\n out:\n\tmem_deref(fsrc);\n\tmem_deref(fdst);\n\n\treturn err;\n}\n\n\nstatic int test_vidconv_center(void)\n{\n\tint err = 0;\n\tstruct vidframe *dst = NULL;\n\tstruct vidframe *src = NULL;\n\n\tstruct test {\n\t\tstruct vidrect r;\n\t\tstruct vidsz src_sz;\n\t\tstruct vidsz dst_sz;\n\t} testv[] = {\n\t\t{.r\t = {.x = 0, .y = 0, .w = 960, .h = 1080},\n\t\t .src_sz = {.w = 320, .h = 180},\n\t\t .dst_sz = {.w = 1920, .h = 1080}},\n\t\t{.r\t = {.x = 0, .y = 0, .w = 960, .h = 1080},\n\t\t .src_sz = {.w = 180, .h = 320},\n\t\t .dst_sz = {.w = 1920, .h = 1080}},\n\t\t{.r\t = {.x = 0, .y = 0, .w = 960, .h = 1080},\n\t\t .src_sz = {.w = 1920, .h = 1080},\n\t\t .dst_sz = {.w = 1920, .h = 1080}},\n\t\t{.r\t = {.x = 0, .y = 0, .w = 960, .h = 1080},\n\t\t .src_sz = {.w = 1080, .h = 1920},\n\t\t .dst_sz = {.w = 1920, .h = 1080}},\n\t\t{.r\t = {.x = 0, .y = 0, .w = 640, .h = 720},\n\t\t .src_sz = {.w = 1920, .h = 1080},\n\t\t .dst_sz = {.w = 1280, .h = 720}},\n\t\t{.r\t = {.x = 960, .y = 0, .w = 960, .h = 1080},\n\t\t .src_sz = {.w = 1024, .h = 768}, /* 4:3 */\n\t\t .dst_sz = {.w = 1920, .h = 1080}},\n\t\t{.r\t = {.x = 0, .y = 0, .w = 960, .h = 1080},\n\t\t .src_sz = {.w = 320, .h = 320}, /* square */\n\t\t .dst_sz = {.w = 1920, .h = 1080}},\n\t\t{.r\t = {.x = 0, .y = 0, .w = 960, .h = 1080},\n\t\t .src_sz = {.w = 1078, .h = 1080}, /* yoffs underflow */\n\t\t .dst_sz = {.w = 1920, .h = 1080}},\n\t\t{.r\t = {.x = 0, .y = 0, .w = 960, .h = 900},\n\t\t .src_sz = {.w = 1080, .h = 1080}, /* xoffs underflow */\n\t\t .dst_sz = {.w = 1920, .h = 1296}},\n\t};\n\n\tfor (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) {\n\t\tstruct test *test    = &testv[i];\n\n\t\terr = vidframe_alloc(&src, VID_FMT_YUV420P, &test->src_sz);\n\t\terr |= vidframe_alloc(&dst, VID_FMT_YUV420P, &test->dst_sz);\n\t\tTEST_ERR(err);\n\n\t\tvidconv_center(dst, src, &test->r);\n\n\t\tsrc = mem_deref(src);\n\t\tdst = mem_deref(dst);\n\t}\n\nout:\n\tsrc = mem_deref(src);\n\tdst = mem_deref(dst);\n\n\treturn err;\n}\n\n\nint test_vidconv(void)\n{\n\tint err;\n\n\terr = test_vid_rgb2yuv();\n\tTEST_ERR(err);\n\n\terr = test_vidconv_center();\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n\n\nint test_vidconv_scaling(void)\n{\n\tint err;\n\n\terr = test_vidconv_scaling_base(VID_FMT_YUV420P);\n\tTEST_ERR(err);\n\n\terr = test_vidconv_scaling_base(VID_FMT_NV12);\n\tTEST_ERR(err);\n\nout:\n\treturn err;\n}\n"
  },
  {
    "path": "test/websock.c",
    "content": "/**\n * @file websock.c Websockets Testcode\n *\n * Copyright (C) 2010 Creytiv.com\n */\n\n#include <string.h>\n#include <re.h>\n#include \"test.h\"\n\n\n#define DEBUG_MODULE \"test_websock\"\n#define DEBUG_LEVEL 5\n#include <re_dbg.h>\n\n\nstruct test {\n\tstruct websock *ws;\n\tstruct websock_conn *wc_cli;\n\tstruct websock_conn *wc_srv;\n\tconst char *proto;\n\tuint32_t n_estab_cli;\n\tuint32_t n_recv_cli;\n\tuint32_t n_recv_srv;\n\tint err;\n};\n\nstatic const char test_payload[]     =\n\t\"0123456789abcdef\"\n\t\"0123456789abcdef\"\n\t\"0123456789abcdef\"\n\t\"0123456789abcdef\"\n\t\"0123456789abcdef\"\n\t\"0123456789abcdef\"\n\t\"0123456789abcdef\"\n\t\"0123456789abcdef\";\nstatic const char custom_useragent[] = \"Retest v0.1\";\n\n\nstatic void abort_test(struct test *t, int err)\n{\n\tt->err = err;\n\tre_cancel();\n}\n\n\nstatic void done(struct test *t)\n{\n\tt->wc_cli = mem_deref(t->wc_cli);\n\tt->wc_srv = mem_deref(t->wc_srv);\n\n\twebsock_shutdown(t->ws);\n}\n\n\nstatic void srv_websock_recv_handler(const struct websock_hdr *hdr,\n\t\t\t\t     struct mbuf *mb, void *arg)\n{\n\tstruct test *test = arg;\n\tint err;\n\n\ttest->n_recv_srv++;\n\n\t/* ECHO */\n\terr = websock_send(test->wc_srv, hdr->opcode,\n\t\t\t   \"%b\", mbuf_buf(mb), mbuf_get_left(mb));\n\tif (err)\n\t\tabort_test(test, err);\n}\n\n\nstatic void srv_websock_close_handler(int err, void *arg)\n{\n\tstruct test *test = arg;\n\t(void)test;\n\t(void)err;\n}\n\n\nstatic void websock_shutdown_handler(void *arg)\n{\n\tabort_test(arg, 0);\n}\n\n\nstatic void http_req_handler(struct http_conn *conn,\n\t\t\t     const struct http_msg *msg, void *arg)\n{\n\tstruct test *test = arg;\n\tint err;\n\n\tTEST_ASSERT(http_msg_hdr_has_value(msg, HTTP_HDR_USER_AGENT,\n\t\t\t\t\t   custom_useragent));\n\n\tif (test->proto) {\n\t\tTEST_ASSERT(http_msg_xhdr_has_value(msg,\n\t\t\t\t\t\t    \"Sec-WebSocket-Protocol\",\n\t\t\t\t\t\t    test->proto));\n\t}\n\n\tunsigned kaint = 1;\n\n\tif (test->proto) {\n\t\terr = websock_accept_proto(&test->wc_srv, test->proto,\n\t\t\t\t\t   test->ws, conn, msg, kaint,\n\t\t\t\t\t   srv_websock_recv_handler,\n\t\t\t\t\t   srv_websock_close_handler, test);\n\t}\n\telse {\n\t\terr = websock_accept(&test->wc_srv, test->ws, conn, msg, kaint,\n\t\t\t\t     srv_websock_recv_handler,\n\t\t\t\t     srv_websock_close_handler, test);\n\t}\n\n out:\n\tif (err)\n\t\tabort_test(test, err);\n}\n\n\nstatic void cli_websock_estab_handler(void *arg)\n{\n\tstruct test *test = arg;\n\tint err;\n\n\tASSERT_TRUE(NULL != websock_tcp(test->wc_cli));\n\n\ttest->n_estab_cli++;\n\n\terr = websock_send(test->wc_cli, WEBSOCK_TEXT, test_payload);\n\n out:\n\tif (err)\n\t\tabort_test(test, err);\n}\n\n\nstatic void cli_websock_recv_handler(const struct websock_hdr *hdr,\n\t\t\t\t     struct mbuf *mb, void *arg)\n{\n\tstruct test *test = arg;\n\tint err = 0;\n\n\ttest->n_recv_cli++;\n\n\tTEST_EQUALS(WEBSOCK_TEXT, hdr->opcode);\n\n\tTEST_STRCMP(test_payload, strlen(test_payload),\n\t\t    mbuf_buf(mb), mbuf_get_left(mb));\n\n\tdone(test);\n\n out:\n\tif (err)\n\t\tabort_test(test, err);\n}\n\n\nstatic void cli_websock_close_handler(int err, void *arg)\n{\n\tstruct test *test = arg;\n\t(void)test;\n\t(void)err;\n\n\t/* translate error code */\n\tif (err) {\n\t\tabort_test(test, ENOMEM);\n\t}\n}\n\n\nstatic int test_websock_loop(const char *proto)\n{\n\tstruct http_sock *httpsock = NULL;\n\tstruct http_cli *http_cli = NULL;\n\tstruct dnsc *dnsc = NULL;\n\tstruct sa srv, dns;\n\tstruct test test;\n\tchar uri[256];\n\tint err = 0;\n\n\tmemset(&test, 0, sizeof(test));\n\n\ttest.proto = proto;\n\n\terr |= sa_set_str(&srv, \"127.0.0.1\", 0);\n\terr |= sa_set_str(&dns, \"127.0.0.1\", 53);    /* note: unused */\n\tif (err)\n\t\tgoto out;\n\n\terr = http_listen(&httpsock, &srv, http_req_handler, &test);\n\tif (err)\n\t\tgoto out;\n\n\terr = tcp_sock_local_get(http_sock_tcp(httpsock), &srv);\n\tif (err)\n\t\tgoto out;\n\n\terr = dnsc_alloc(&dnsc, NULL, &dns, 1);\n\tif (err)\n\t\tgoto out;\n\n\terr = http_client_alloc(&http_cli, dnsc);\n\tif (err)\n\t\tgoto out;\n\n\terr = websock_alloc(&test.ws, websock_shutdown_handler, &test);\n\tif (err)\n\t\tgoto out;\n\n\t(void)re_snprintf(uri, sizeof(uri),\n\t\t\t  \"http://127.0.0.1:%u/\", sa_port(&srv));\n\tunsigned kaint = 1;\n\n\tif (proto) {\n\t\terr = websock_connect_proto(&test.wc_cli, proto, test.ws,\n\t\t\t\t\t    http_cli, uri, kaint,\n\t\t\t\t\t    cli_websock_estab_handler,\n\t\t\t\t\t    cli_websock_recv_handler,\n\t\t\t\t\t    cli_websock_close_handler, &test,\n\t\t\t\t\t    \"User-Agent: %s\\r\\n\",\n\t\t\t\t\t    custom_useragent);\n\t}\n\telse {\n\t\terr = websock_connect(&test.wc_cli, test.ws,\n\t\t\t\t      http_cli, uri, kaint,\n\t\t\t\t      cli_websock_estab_handler,\n\t\t\t\t      cli_websock_recv_handler,\n\t\t\t\t      cli_websock_close_handler, &test,\n\t\t\t\t      \"User-Agent: %s\\r\\n\", custom_useragent);\n\t}\n\n\tif (err)\n\t\tgoto out;\n\n\terr = re_main_timeout(500);\n\tif (err)\n\t\tgoto out;\n\n\tif (test.err) {\n\t\terr = test.err;\n\t\tgoto out;\n\t}\n\n\t/* verify results after traffic is successfully done */\n\tTEST_EQUALS(1, test.n_estab_cli);\n\tTEST_EQUALS(1, test.n_recv_cli);\n\tTEST_EQUALS(1, test.n_recv_srv);\n\n out:\n\tmem_deref(httpsock);\n\tmem_deref(test.wc_cli);\n\tmem_deref(test.ws);\n\tmem_deref(test.wc_srv);\n\tmem_deref(http_cli);\n\tmem_deref(dnsc);\n\n\treturn err;\n}\n\n\nint test_websock(void)\n{\n\tint err = 0;\n\n\terr = test_websock_loop(NULL);\n\tTEST_ERR(err);\n\n\terr = test_websock_loop(\"test\");\n\tTEST_ERR(err);\n\n out:\n\treturn err;\n}\n"
  },
  {
    "path": "tools/genfir.py",
    "content": "#!/usr/bin/python\n#\n# Copyright (C) 2025 Alfred E. Heggestad\n#\n\n''' FIR filter design and generate C-table '''\n\nimport scipy.signal\n\nTAPS = 31\nCUTOFF = 8000.0   # Hz\nSRATE = 16000.0   # Hz\n\ncutoff = CUTOFF / SRATE\n\n\ncoeffs = scipy.signal.firwin(TAPS, cutoff)\n\n\nprint(\"/*\")\nprint(\" * FIR filter with cutoff %dHz, samplerate %dHz\" % (CUTOFF, SRATE))\nprint(\" */\")\nprint(\"static const int16_t fir_lowpass[%d] = {\" % (TAPS))\n\ni = 0\n\nfor c in coeffs:\n    v = int(c * 32768.0)\n\n    print(\" %5d,\" % (v), end=\"\")\n\n    i += 1\n    if not i % 8:\n        print(\"\")\n\nprint(\"\")\nprint(\"};\")\n"
  }
]