mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-12-23 07:12:32 +01:00
Compare commits
65 Commits
websocket-
...
eppp-v1.1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0cf91e174 | ||
|
|
2535b3fefc | ||
|
|
ba0ef10038 | ||
|
|
f1e35977e5 | ||
|
|
25735ead9c | ||
|
|
bf9e955514 | ||
|
|
726c41f842 | ||
|
|
c7de9251ed | ||
|
|
0f6235f13e | ||
|
|
6c2c2cd22b | ||
|
|
9e0bcd4b08 | ||
|
|
07b1bcdc34 | ||
|
|
f20a234f65 | ||
|
|
63082b996d | ||
|
|
27d43277d2 | ||
|
|
4d8d25a345 | ||
|
|
bed116d98b | ||
|
|
9ec006a3e4 | ||
|
|
245b5a2ffb | ||
|
|
b9ea0c31ce | ||
|
|
4aa0e4ba49 | ||
|
|
c48c2ebe7e | ||
|
|
0ccaf2c0bb | ||
|
|
ed569d8509 | ||
|
|
9b2b1f680d | ||
|
|
370dfecc15 | ||
|
|
e50c5eb40e | ||
|
|
6f6110e30e | ||
|
|
41f7157ffb | ||
|
|
858f67c280 | ||
|
|
83ffeb0d12 | ||
|
|
6e99202a18 | ||
|
|
fb5279ae88 | ||
|
|
b70cc3fc09 | ||
|
|
911c2dbe9f | ||
|
|
c620855d56 | ||
|
|
cccfdd9315 | ||
|
|
ecc7258093 | ||
|
|
0caea67542 | ||
|
|
11a8567598 | ||
|
|
9b80a7ef7d | ||
|
|
12028ab250 | ||
|
|
68e299a357 | ||
|
|
2681b9b3c6 | ||
|
|
782b7cd119 | ||
|
|
3141d6cab5 | ||
|
|
7b8770e2fc | ||
|
|
6153c0002a | ||
|
|
3d5e11b82f | ||
|
|
eacc3a0aa8 | ||
|
|
2826287d43 | ||
|
|
3bfa00389d | ||
|
|
ace7fca8c6 | ||
|
|
2b2f009a65 | ||
|
|
1444d575f0 | ||
|
|
081eef88cf | ||
|
|
8b0704eaf4 | ||
|
|
4889dd6fcb | ||
|
|
134247d88f | ||
|
|
e772ce673d | ||
|
|
3838485229 | ||
|
|
ba3377b262 | ||
|
|
e5bed394ee | ||
|
|
65b58aa05a | ||
|
|
d1e6708063 |
24
.github/workflows/asio__build-target-test.yml
vendored
24
.github/workflows/asio__build-target-test.yml
vendored
@@ -81,18 +81,24 @@ jobs:
|
||||
with:
|
||||
name: examples_app_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.example }}
|
||||
path: ${{ env.TEST_DIR }}/${{ matrix.example }}/build
|
||||
- name: Install Python packages
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
|
||||
run: |
|
||||
sudo apt-get install -y dnsutils
|
||||
- name: Download Example Test to target ${{ matrix.config }}
|
||||
run: |
|
||||
python -m esptool --chip ${{ matrix.idf_target }} write_flash 0x0 ${{ env.TEST_DIR }}/${{ matrix.example }}/build/flash_image.bin
|
||||
- name: Run Example Test ${{ matrix.example }} on target
|
||||
working-directory: ${{ env.TEST_DIR }}/${{ matrix.example }}
|
||||
run: |
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.config }}.xml --target=${{ matrix.idf_target }}
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
python -m pytest --log-cli-level DEBUG --junit-xml=./examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.config }}.xml --target=${{ matrix.idf_target }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: ifconfig-basic, path: "components/console_cmd_ifconfig/examples"}]
|
||||
include:
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: mqtt_ssl_auth_console, path: "components/console_cmd_mqtt/examples" }]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: ping-basic, path: "components/console_cmd_ping/examples" }]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: wifi-basic, path: "components/console_cmd_wifi/examples" }]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: console_basic, path: "components/console_simple_init/examples" }]
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
2
.github/workflows/eppp__build.yml
vendored
2
.github/workflows/eppp__build.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.5", "release-v5.4", "release-v5.3"]
|
||||
idf_ver: ["latest", "release-v6.0", "release-v5.5", "release-v5.4", "release-v5.3"]
|
||||
test: [ { app: host, path: "examples/host" }, { app: slave, path: "examples/slave" }, { app: test_app, path: "test/test_app" }]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
|
||||
@@ -13,10 +13,7 @@ jobs:
|
||||
name: Build examples
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
|
||||
include:
|
||||
- idf_ver: "latest"
|
||||
warning: "Warning: The smallest app partition is nearly full"
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
env:
|
||||
@@ -27,7 +24,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
- name: Build with IDF-${{ matrix.idf_ver }}
|
||||
env:
|
||||
EXPECTED_WARNING: ${{ matrix.warning }}
|
||||
EXPECTED_WARNING: "Warning: The smallest app partition is nearly full"
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
@@ -75,7 +72,7 @@ jobs:
|
||||
needs: build_all_examples
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.4", "latest"]
|
||||
idf_ver: ["release-v5.5", "latest"]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- modem
|
||||
|
||||
24
.github/workflows/lws_build.yml
vendored
24
.github/workflows/lws_build.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Libwebsockets build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
|
||||
idf_ver: ["release-v5.3", "release-v5.4", "release-v5.5"]
|
||||
test: [ { app: example, path: "examples/client" }]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3", "release-v5.4"]
|
||||
idf_ver: ["release-v5.3", "release-v5.4", "release-v5.5"]
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: example, path: "examples/client" }]
|
||||
runs-on:
|
||||
@@ -65,14 +65,24 @@ jobs:
|
||||
with:
|
||||
name: lws_target_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
|
||||
path: ${{ env.TEST_DIR }}/ci/
|
||||
- name: Install Python packages
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
|
||||
run: |
|
||||
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
|
||||
- name: Run Example Test on target
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
pip install --only-binary cryptography --extra-index-url https://dl.espressif.com/pypi/ -r $GITHUB_WORKSPACE/ci/requirements.txt
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
|
||||
63
.github/workflows/mdns__host-tests.yml
vendored
63
.github/workflows/mdns__host-tests.yml
vendored
@@ -29,6 +29,8 @@ jobs:
|
||||
# Build host tests app (with all configs and targets supported)
|
||||
python ./ci/build_apps.py components/mdns/tests/host_test/
|
||||
cd components/mdns/tests/host_test
|
||||
ls -la
|
||||
ls -ls build*
|
||||
# First run the linux_app and send a quick A query and a reverse query
|
||||
./build_linux_app/mdns_host.elf &
|
||||
python dnsfixture.py A myesp.local --ip_only | xargs python dnsfixture.py X
|
||||
@@ -55,8 +57,11 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd components/mdns/tests/test_afl_fuzz_host/
|
||||
make INSTR=off
|
||||
cd components/mdns/tests/host_unit_test/
|
||||
idf.py reconfigure
|
||||
mkdir build2 && cd build2
|
||||
cmake ..
|
||||
cmake --build .
|
||||
- name: Test no malloc functions
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -69,6 +74,39 @@ jobs:
|
||||
echo "OK"
|
||||
done
|
||||
|
||||
host_unit_test:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
|
||||
name: Unit tests on host
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
- name: Install bsdlib and ruby
|
||||
run: |
|
||||
apt-get update -y
|
||||
apt-get install -y libbsd-dev ruby
|
||||
- name: Build and run unit tests
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
cd components/mdns/tests/host_unit_test/
|
||||
idf.py reconfigure
|
||||
mkdir build2 && cd build2
|
||||
cmake -DUNIT_TESTS=test_receiver ..
|
||||
cmake --build .
|
||||
ctest --extra-verbose
|
||||
cd ..
|
||||
mkdir build3 && cd build3
|
||||
cmake -DUNIT_TESTS=test_sender ..
|
||||
cmake --build .
|
||||
ctest --extra-verbose
|
||||
|
||||
|
||||
fuzz_test:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'mdns-fuzz') || github.event_name == 'push'
|
||||
name: Fuzzer tests for mdns lib
|
||||
@@ -77,7 +115,7 @@ jobs:
|
||||
idf_ver: ["latest"]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: aflplusplus/aflplusplus
|
||||
container: aflplusplus/aflplusplus:v4.34c
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v4
|
||||
@@ -98,13 +136,26 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
export IDF_PATH=$GITHUB_WORKSPACE/idf
|
||||
cd components/mdns/tests/test_afl_fuzz_host/
|
||||
make fuzz
|
||||
cd components/mdns/tests/host_unit_test/
|
||||
pip install dnslib
|
||||
cd input && python generate_cases.py && cd ..
|
||||
cmake -B build2 -S . -G "Ninja" -DCMAKE_C_COMPILER=afl-cc
|
||||
cmake --build build2
|
||||
timeout 10m afl-fuzz -i input -o out -- build2/mdns_host_unit_test || \
|
||||
if [ $? -eq 124 ]; then # timeout exit code
|
||||
if [ -n "$(find out/default/crashes -type f 2>/dev/null)" ]; then
|
||||
echo "Crashes found!";
|
||||
tar -czf out/default/crashes.tar.gz -C out/default crashes;
|
||||
exit 1;
|
||||
fi
|
||||
else
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
- name: Upload Crash Artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fuzz-crashes
|
||||
path: components/mdns/tests/test_afl_fuzz_host/out/default/crashes.tar.gz
|
||||
path: components/mdns/tests/host_unit_test/out/default/crashes.tar.gz
|
||||
if-no-files-found: ignore
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build examples
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4"]
|
||||
idf_ver: ["latest", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
example: ["pppos_client", "modem_console", "modem_tcp_client", "ap_to_pppos", "simple_cmux_client"]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
name: Build tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "latest"]
|
||||
idf_ver: ["latest","release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "release-v5.5", "release-v6.0"]
|
||||
test: ["target", "target_ota", "target_iperf", "target_urc"]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
4
.github/workflows/modem__target-test.yml
vendored
4
.github/workflows/modem__target-test.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Build Target tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_ver: ["latest", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32c3"]
|
||||
test: [ { app: pppd, path: test/target }, { app: pppd_chap_auth, path: test/target }, { app: sim800_c3, path: examples/pppos_client }, { app: sim800_cmux, path: examples/simple_cmux_client } ]
|
||||
include:
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
name: Run Target tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_ver: ["latest", "release-v5.5", "release-v6.0"]
|
||||
idf_target: ["esp32c3"]
|
||||
test: [ { app: pppd, path: test/target }, { app: pppd_chap_auth, path: test/target }, { app: sim800_c3, path: examples/pppos_client }, { app: sim800_cmux, path: examples/simple_cmux_client } ]
|
||||
include:
|
||||
|
||||
48
.github/workflows/mosq__build.yml
vendored
48
.github/workflows/mosq__build.yml
vendored
@@ -13,13 +13,15 @@ jobs:
|
||||
name: Mosquitto build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.5", "release-v5.4", "release-v5.3", "release-v5.2", "release-v5.1"]
|
||||
example: ["broker", "serverless_mqtt"]
|
||||
exclude:
|
||||
# serverless_mqtt is not supported on v5.1 and master (esp-peer dependency)
|
||||
- idf_ver: "release-v5.1"
|
||||
idf_ver: ["latest", "release-v6.0", "release-v5.5", "release-v5.4", "release-v5.3", "release-v5.2", "release-v5.1"]
|
||||
example: ["broker"]
|
||||
include:
|
||||
# serverless_mqtt is not supported on >=v6.0 (esp-peer dependency)
|
||||
- idf_ver: "release-v5.3"
|
||||
example: "serverless_mqtt"
|
||||
- idf_ver: "latest"
|
||||
- idf_ver: "release-v5.4"
|
||||
example: "serverless_mqtt"
|
||||
- idf_ver: "release-v5.5"
|
||||
example: "serverless_mqtt"
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -61,7 +63,7 @@ jobs:
|
||||
needs: build_mosq
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest", "release-v5.3"]
|
||||
idf_ver: ["release-v5.4", "release-v5.5", "release-v6.0", "latest"]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
@@ -77,6 +79,20 @@ jobs:
|
||||
- name: Run Test
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
python -m pip install pytest-embedded-serial-esp pytest-embedded-idf pytest-rerunfailures pytest-timeout pytest-ignore-test-results
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
@@ -120,7 +136,7 @@ jobs:
|
||||
name: Build IDF tests
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_ver: ["release-v5.5"] # TODO: add release-v6.0/latest with esp-mqtt directly
|
||||
idf_target: ["esp32"]
|
||||
test: [ { app: publish, path: "tools/test_apps/protocols/mqtt/publish_connect_test" }]
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -165,7 +181,7 @@ jobs:
|
||||
needs: build_idf_tests_with_mosq
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["latest"]
|
||||
idf_ver: ["release-v5.5"]
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- ESP32-ETHERNET-KIT
|
||||
@@ -180,6 +196,20 @@ jobs:
|
||||
- name: Run Test
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
python -m pip install pytest-embedded-serial-esp pytest-embedded-idf pytest-rerunfailures pytest-timeout pytest-ignore-test-results "paho-mqtt<2" --upgrade
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
|
||||
16
.github/workflows/sockutls_build.yml
vendored
16
.github/workflows/sockutls_build.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install idf-component-manager idf-build-apps --upgrade
|
||||
pip install idf-build-apps --upgrade
|
||||
python ci/build_apps.py ${TEST_DIR}
|
||||
cd ${TEST_DIR}
|
||||
${GITHUB_WORKSPACE}/ci/clean_build_artifacts.sh `pwd`/${TARGET_TEST_DIR}
|
||||
@@ -87,6 +87,20 @@ jobs:
|
||||
- name: Run Test
|
||||
working-directory: ${{ env.TEST_DIR }}
|
||||
run: |
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init --path)"
|
||||
eval "$(pyenv init -)"
|
||||
if ! pyenv versions --bare | grep -q '^3\.12\.6$'; then
|
||||
echo "Installing Python 3.12.6..."
|
||||
pyenv install -s 3.12.6
|
||||
fi
|
||||
if ! pyenv virtualenvs --bare | grep -q '^myenv$'; then
|
||||
echo "Creating pyenv virtualenv 'myenv'..."
|
||||
pyenv virtualenv 3.12.6 myenv
|
||||
fi
|
||||
pyenv activate myenv
|
||||
python --version
|
||||
unzip ci/artifacts.zip -d ci
|
||||
for dir in `ls -d ci/build_*`; do
|
||||
rm -rf build sdkconfig.defaults
|
||||
|
||||
2
.github/workflows/tls_cxx__build.yml
vendored
2
.github/workflows/tls_cxx__build.yml
vendored
@@ -26,5 +26,5 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
. ${IDF_PATH}/export.sh
|
||||
pip install idf-component-manager idf-build-apps --upgrade
|
||||
pip install idf-build-apps --upgrade
|
||||
python ./ci/build_apps.py ./components/mbedtls_cxx/${{ matrix.test.path }} -vv --preserve-all
|
||||
|
||||
@@ -5,3 +5,4 @@ Warning: Deprecated: Option '--flash_freq' is deprecated. Use '--flash-freq' ins
|
||||
Warning: Deprecated: Command 'sign_data' is deprecated. Use 'sign-data' instead.
|
||||
Warning: Deprecated: Command 'extract_public_key' is deprecated. Use 'extract-public-key' instead.
|
||||
warning: unknown kconfig symbol 'EXAMPLE_ETH_PHY_IP101'
|
||||
WARNING: The following Kconfig variables were used in "if" clauses, but not
|
||||
|
||||
@@ -9,4 +9,8 @@ dependencies:
|
||||
override_path: '../console_simple_init'
|
||||
public: true
|
||||
espressif/ethernet_init:
|
||||
version: '>=0.0.7'
|
||||
matches:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
- if: idf_version <6.0
|
||||
version: '==0.3.0'
|
||||
|
||||
@@ -9,3 +9,7 @@ dependencies:
|
||||
version: '>=1.1.0'
|
||||
override_path: '../console_simple_init'
|
||||
public: true
|
||||
espressif/mqtt:
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
|
||||
@@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(eppp): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py eppp_link
|
||||
tag_format: eppp-v$version
|
||||
version: 1.1.3
|
||||
version: 1.1.4
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## [1.1.4](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.4)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed missing freertos deps ([f1e35977](https://github.com/espressif/esp-protocols/commit/f1e35977))
|
||||
- Add optional mqtt dependency ([911c2dbe](https://github.com/espressif/esp-protocols/commit/911c2dbe))
|
||||
|
||||
## [1.1.3](https://github.com/espressif/esp-protocols/commits/eppp-v1.1.3)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/sdio_slave.h"
|
||||
#include "esp_serial_slave_link/essl_sdio.h"
|
||||
#include "eppp_sdio.h"
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/sdio_slave.h"
|
||||
#include "eppp_link.h"
|
||||
#include "eppp_transport.h"
|
||||
|
||||
@@ -5,3 +5,7 @@ dependencies:
|
||||
override_path: ../../..
|
||||
console_cmd_ping:
|
||||
version: '*'
|
||||
espressif/mqtt:
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 1.1.3
|
||||
version: 1.1.4
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/eppp_link
|
||||
description: The component provides a general purpose PPP connectivity, typically used as WiFi-PPP router
|
||||
dependencies:
|
||||
|
||||
@@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(modem): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py esp_modem
|
||||
tag_format: modem-v$version
|
||||
version: 1.4.0
|
||||
version: 2.0.0
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
# Changelog
|
||||
|
||||
## [2.0.0](https://github.com/espressif/esp-protocols/commits/modem-v2.0.0)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- inc headers for AT command definitions are no longer used directly, but pregenerated into *.h(pp) ([Use generated AT command definitions for IDE navigation](https://github.com/espressif/esp-protocols/commit/e2fa1110))
|
||||
|
||||
### Features
|
||||
|
||||
- Add support for multiple connection in AT based example ([2826287d](https://github.com/espressif/esp-protocols/commit/2826287d))
|
||||
- Add enhanced URC observer API ([4889dd6f](https://github.com/espressif/esp-protocols/commit/4889dd6f))
|
||||
- Support esp-modem use without PPP ([858f8570](https://github.com/espressif/esp-protocols/commit/858f8570), [#851](https://github.com/espressif/esp-protocols/issues/851))
|
||||
- Modem simulator based on esp-at ([e5787e3d](https://github.com/espressif/esp-protocols/commit/e5787e3d))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Update tests and examples to use modem-v2.0 ([4aa0e4ba](https://github.com/espressif/esp-protocols/commit/4aa0e4ba))
|
||||
- Replace MQTT client with simple ping command ([0ccaf2c0](https://github.com/espressif/esp-protocols/commit/0ccaf2c0))
|
||||
- Replace MQTT client with simple ping command ([9b2b1f68](https://github.com/espressif/esp-protocols/commit/9b2b1f68))
|
||||
- Update example to use optional mqtt deps ([3141d6ca](https://github.com/espressif/esp-protocols/commit/3141d6ca))
|
||||
- Minor fixed in the test code ([e772ce67](https://github.com/espressif/esp-protocols/commit/e772ce67))
|
||||
- Add missing set_echo() C wrapper ([d1e67080](https://github.com/espressif/esp-protocols/commit/d1e67080), [#926](https://github.com/espressif/esp-protocols/issues/926))
|
||||
- Fix modem console dependencies ([453be4cd](https://github.com/espressif/esp-protocols/commit/453be4cd))
|
||||
- Address build issues ([018ba58e](https://github.com/espressif/esp-protocols/commit/018ba58e))
|
||||
- Fix driver dependency issue on v6.0 ([67c682d9](https://github.com/espressif/esp-protocols/commit/67c682d9))
|
||||
- Fix CI build issues with IDFv6.0 ([15140e04](https://github.com/espressif/esp-protocols/commit/15140e04))
|
||||
- Add support for ESP-AT based tcp-client example ([14d3cb6b](https://github.com/espressif/esp-protocols/commit/14d3cb6b))
|
||||
- Use idf-build-apps for building target tests ([e9d9b3a8](https://github.com/espressif/esp-protocols/commit/e9d9b3a8))
|
||||
- Make MQTT public broker endpoint configurable ([6d541194](https://github.com/espressif/esp-protocols/commit/6d541194))
|
||||
- Fix URC handling in DTE data callback ([93029946](https://github.com/espressif/esp-protocols/commit/93029946))
|
||||
- Use another public broker for examples and tests ([fac2edbe](https://github.com/espressif/esp-protocols/commit/fac2edbe))
|
||||
- Fix incompatible iterator in std::search() in new gcc ([ed0f6334](https://github.com/espressif/esp-protocols/commit/ed0f6334))
|
||||
- Fix autodetect to support ACFC mode in PPP frames ([8b328a69](https://github.com/espressif/esp-protocols/commit/8b328a69), [#801](https://github.com/espressif/esp-protocols/issues/801))
|
||||
- Fix get_network_registration_state() to accept two params ([5f54d907](https://github.com/espressif/esp-protocols/commit/5f54d907), [#826](https://github.com/espressif/esp-protocols/issues/826))
|
||||
- Consume buffer after handled URC ([6eceb28f](https://github.com/espressif/esp-protocols/commit/6eceb28f))
|
||||
- Use generated AT command definitions for IDE navigation ([e2fa1110](https://github.com/espressif/esp-protocols/commit/e2fa1110), !BREAKING)
|
||||
|
||||
## [1.4.0](https://github.com/espressif/esp-protocols/commits/modem-v1.4.0)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.1"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.1"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
|
||||
@@ -3,7 +3,7 @@ dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=4.1.0"
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.0"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
espressif/esp_modem_usb_dte:
|
||||
version: "^1.2.0"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -97,10 +97,21 @@ void wakeup_modem(void)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
command_result handle_urc(uint8_t *data, size_t len)
|
||||
esp_modem::DTE::UrcConsumeInfo handle_enhanced_urc(const esp_modem::DTE::UrcBufferInfo &info)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP("on_read", data, len, ESP_LOG_INFO);
|
||||
return command_result::TIMEOUT;
|
||||
// Log buffer information for debugging
|
||||
ESP_LOGI(TAG, "URC Buffer Info: total_size=%zu, processed_offset=%zu, new_data_size=%zu, command_active=%s",
|
||||
info.buffer_total_size, info.processed_offset, info.new_data_size,
|
||||
info.is_command_active ? "true" : "false");
|
||||
|
||||
// Log the new data content
|
||||
if (info.new_data_size > 0) {
|
||||
ESP_LOG_BUFFER_HEXDUMP("on_read", info.new_data_start, info.new_data_size, ESP_LOG_INFO);
|
||||
}
|
||||
|
||||
// For console example, we just log and don't consume anything
|
||||
// This allows the data to be processed by command handlers
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_NONE, 0};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -381,14 +392,14 @@ extern "C" void app_main(void)
|
||||
CHECK_ERR(dce->reset(), ESP_LOGI(TAG, "OK"));
|
||||
});
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
const ConsoleCommand HandleURC("urc", "toggle urc handling", no_args, [&](ConsoleCommand * c) {
|
||||
const ConsoleCommand HandleURC("urc", "toggle enhanced urc handling", no_args, [&](ConsoleCommand * c) {
|
||||
static int cnt = 0;
|
||||
if (++cnt % 2) {
|
||||
ESP_LOGI(TAG, "Adding URC handler");
|
||||
dce->set_urc(handle_urc);
|
||||
ESP_LOGI(TAG, "Adding enhanced URC handler");
|
||||
dce->set_enhanced_urc(handle_enhanced_urc);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "URC removed");
|
||||
dce->set_urc(nullptr);
|
||||
ESP_LOGI(TAG, "Enhanced URC removed");
|
||||
dce->set_enhanced_urc(nullptr);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.1"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
|
||||
@@ -22,3 +22,18 @@ To enable this mode, please set `EXAMPLE_CUSTOM_TCP_TRANSPORT=y`
|
||||
This configuration could be used with any network library, which is connecting to a localhost endpoint instead of remote one. This example creates a localhost listener which basically mimics the remote endpoint by forwarding the traffic between the library and the TCP/socket layer of the modem (which is already secure if the TLS is used in the network library)
|
||||
|
||||

|
||||
|
||||
### Multi-connection support
|
||||
|
||||
This example supports opening multiple TCP connections concurrently when the modem firmware allows it.
|
||||
|
||||
- ESP-AT: Multi-connection mode is enabled via `AT+CIPMUX=1`. The example assigns a unique link ID per DCE instance and includes the link ID in `CIPSTART/CIPSEND/CIPRECVDATA` commands.
|
||||
- BG96/SIM7600: The example uses module-specific multi-connection syntax (for example `QIOPEN/CIPOPEN` with a connection ID) and tracks link IDs internally.
|
||||
|
||||
How it works:
|
||||
- The `sock_dce` layer creates multiple DCE instances over a shared DTE. A lightweight mutex coordinates access to the UART so only one DCE issues AT commands at a time.
|
||||
- Asynchronous URCs (for example `+IPD`, `+QIURC`, `+CIPRXGET: 1,<cid>`) wake the corresponding DCE which then performs receive operations for its link.
|
||||
|
||||
Usage:
|
||||
- `app_main` starts two DCE tasks to demonstrate concurrent connections. Adjust the number of DCE instances as needed.
|
||||
- For ESP-AT, ensure your firmware supports `CIPMUX=1` and passive receive (`CIPRECVTYPE`).
|
||||
|
||||
@@ -44,6 +44,18 @@ menu "Example Configuration"
|
||||
help
|
||||
Set APN (Access Point Name), a logical name to choose data network
|
||||
|
||||
config EXAMPLE_USE_TLS
|
||||
bool "Use TLS for MQTT broker"
|
||||
default n
|
||||
help
|
||||
Enable TLS for connection to the MQTT broker.
|
||||
|
||||
config EXAMPLE_BROKER_HOST
|
||||
string "MQTT broker host"
|
||||
default "test.mosquitto.org"
|
||||
help
|
||||
Hostname or IP address of the MQTT broker.
|
||||
|
||||
menu "UART Configuration"
|
||||
config EXAMPLE_MODEM_UART_TX_PIN
|
||||
int "TXD Pin Number"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <sys/socket.h>
|
||||
#include "esp_vfs.h"
|
||||
@@ -14,6 +15,29 @@
|
||||
namespace sock_dce {
|
||||
|
||||
constexpr auto const *TAG = "sock_dce";
|
||||
constexpr auto WAIT_TO_IDLE_TIMEOUT = 5000;
|
||||
|
||||
// Definition of the static member variables
|
||||
std::vector<DCE *> DCE::dce_list{};
|
||||
bool DCE::network_init = false;
|
||||
int Responder::s_link_id = 0;
|
||||
SemaphoreHandle_t Responder::s_dte_mutex{};
|
||||
|
||||
// Constructor - add this DCE instance to the static list
|
||||
DCE::DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config)
|
||||
: Module(std::move(dte_arg), config)
|
||||
{
|
||||
dce_list.push_back(this);
|
||||
}
|
||||
|
||||
// Destructor - remove this DCE instance from the static list
|
||||
DCE::~DCE()
|
||||
{
|
||||
auto it = std::find(dce_list.begin(), dce_list.end(), this);
|
||||
if (it != dce_list.end()) {
|
||||
dce_list.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DCE::perform_sock()
|
||||
@@ -61,13 +85,26 @@ bool DCE::perform_sock()
|
||||
|
||||
void DCE::perform_at(uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_VERBOSE);
|
||||
if (state != status::RECEIVING) {
|
||||
std::string_view resp_sv((char *)data, len);
|
||||
at.check_urc(state, resp_sv);
|
||||
if (state == status::IDLE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Trace incoming AT bytes when handling a response; use DEBUG level
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_DEBUG);
|
||||
switch (at.process_data(state, data, len)) {
|
||||
case Responder::ret::OK:
|
||||
// Release DTE access for this link after processing data
|
||||
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
@@ -82,10 +119,14 @@ void DCE::perform_at(uint8_t *data, size_t len)
|
||||
std::string_view response((char *)data, len);
|
||||
switch (at.check_async_replies(state, response)) {
|
||||
case Responder::ret::OK:
|
||||
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
@@ -121,7 +162,7 @@ bool DCE::at_to_sock()
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
ESP_LOGD(TAG, "select read: modem data available %" PRIu64, data);
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
@@ -131,6 +172,10 @@ bool DCE::at_to_sock()
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
// Take DTE mutex before issuing receive on this link
|
||||
ESP_LOGD(TAG, "TAKE RECV %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN RECV %d", at.link_id);
|
||||
state = status::RECEIVING;
|
||||
at.start_receiving(at.get_buf_len());
|
||||
return true;
|
||||
@@ -139,7 +184,7 @@ bool DCE::at_to_sock()
|
||||
bool DCE::sock_to_at()
|
||||
{
|
||||
ESP_LOGD(TAG, "socket read: data available");
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
@@ -149,6 +194,10 @@ bool DCE::sock_to_at()
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
// Take DTE mutex before issuing send on this link
|
||||
ESP_LOGD(TAG, "TAKE SEND %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN SEND %d", at.link_id);
|
||||
state = status::SENDING;
|
||||
int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
|
||||
if (len < 0) {
|
||||
@@ -201,7 +250,7 @@ void DCE::start_listening(int port)
|
||||
}
|
||||
int opt = 1;
|
||||
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
ESP_LOGI(TAG, "Socket created");
|
||||
ESP_LOGD(TAG, "Socket created");
|
||||
struct sockaddr_in addr = { };
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
@@ -213,7 +262,7 @@ void DCE::start_listening(int port)
|
||||
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket bound, port %d", 1883);
|
||||
ESP_LOGD(TAG, "Socket bound, port %d", 1883);
|
||||
err = listen(listen_sock, 1);
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
|
||||
@@ -224,12 +273,12 @@ void DCE::start_listening(int port)
|
||||
|
||||
bool DCE::connect(std::string host, int port)
|
||||
{
|
||||
dte->on_read(nullptr);
|
||||
tcp_close();
|
||||
dte->on_read([this](uint8_t *data, size_t len) {
|
||||
this->perform_at(data, len);
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
});
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
// Take DTE mutex before starting connect for this link
|
||||
ESP_LOGD(TAG, "TAKE CONNECT %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN CONNECT %d", at.link_id);
|
||||
if (!at.start_connecting(host, port)) {
|
||||
ESP_LOGE(TAG, "Unable to start connecting");
|
||||
dte->on_read(nullptr);
|
||||
@@ -241,12 +290,15 @@ bool DCE::connect(std::string host, int port)
|
||||
|
||||
bool DCE::init()
|
||||
{
|
||||
if (network_init) {
|
||||
return true;
|
||||
}
|
||||
network_init = true;
|
||||
Responder::s_dte_mutex = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
|
||||
esp_vfs_eventfd_register(&config);
|
||||
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
|
||||
dte->on_read(nullptr);
|
||||
const int retries = 5;
|
||||
int i = 0;
|
||||
@@ -287,6 +339,10 @@ bool DCE::init()
|
||||
esp_modem::Task::Delay(5000);
|
||||
}
|
||||
ESP_LOGI(TAG, "Got IP %s", ip_addr.c_str());
|
||||
dte->on_read([](uint8_t *data, size_t len) {
|
||||
read_callback(data, len);
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
sock(s), data_ready_fd(ready_fd), dte(dte_arg) {}
|
||||
ret process_data(status state, uint8_t *data, size_t len);
|
||||
ret check_async_replies(status state, std::string_view &response);
|
||||
ret check_urc(status state, std::string_view &response);
|
||||
|
||||
void start_sending(size_t len);
|
||||
void start_receiving(size_t len);
|
||||
@@ -63,13 +64,19 @@ public:
|
||||
return total_len;
|
||||
}
|
||||
|
||||
// Unique link identifier used to target multi-connection AT commands
|
||||
int link_id{s_link_id++};
|
||||
// Shared mutex guarding DTE access across concurrent DCE instances
|
||||
static SemaphoreHandle_t s_dte_mutex;
|
||||
private:
|
||||
static int s_link_id;
|
||||
static constexpr size_t buffer_size = 512;
|
||||
|
||||
bool on_read(char *data, size_t len)
|
||||
{
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
::send(sock, data, len, 0);
|
||||
printf("sending %d\n", len);
|
||||
#else
|
||||
::memcpy(&buffer[actual_read], data, len);
|
||||
actual_read += len;
|
||||
@@ -101,6 +108,8 @@ private:
|
||||
class DCE : public Module {
|
||||
using Module::Module;
|
||||
public:
|
||||
DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config);
|
||||
~DCE();
|
||||
|
||||
/**
|
||||
* @brief Opens network in AT command mode
|
||||
@@ -163,6 +172,10 @@ public:
|
||||
return 0;
|
||||
}
|
||||
at.clear_offsets();
|
||||
// Take DTE mutex before issuing receive on this link
|
||||
ESP_LOGD("TAG", "TAKE RECV %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD("TAG", "TAKEN RECV %d", at.link_id);
|
||||
state = status::RECEIVING;
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
@@ -184,6 +197,10 @@ public:
|
||||
if (!wait_to_idle(timeout_ms)) {
|
||||
return -1;
|
||||
}
|
||||
// Take DTE mutex before issuing send on this link
|
||||
ESP_LOGD("TAG", "TAKE SEND %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD("TAG", "TAKEN SEND %d", at.link_id);
|
||||
state = status::SENDING;
|
||||
memcpy(at.get_buf(), buffer, len_to_send);
|
||||
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);
|
||||
@@ -224,6 +241,14 @@ public:
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static std::vector<DCE *> dce_list;
|
||||
static bool network_init;
|
||||
static void read_callback(uint8_t *data, size_t len)
|
||||
{
|
||||
for (auto dce : dce_list) {
|
||||
dce->perform_at(data, len);
|
||||
}
|
||||
}
|
||||
private:
|
||||
esp_modem::SignalGroup signal;
|
||||
void close_sock();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <sys/socket.h>
|
||||
#include "esp_vfs.h"
|
||||
@@ -14,6 +15,29 @@
|
||||
namespace sock_dce {
|
||||
|
||||
constexpr auto const *TAG = "sock_dce";
|
||||
constexpr auto WAIT_TO_IDLE_TIMEOUT = 5000;
|
||||
|
||||
// Definition of the static member variables
|
||||
std::vector<DCE*> DCE::dce_list{};
|
||||
bool DCE::network_init = false;
|
||||
int Responder::s_link_id = 0;
|
||||
SemaphoreHandle_t Responder::s_dte_mutex{};
|
||||
|
||||
// Constructor - add this DCE instance to the static list
|
||||
DCE::DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config)
|
||||
: Module(std::move(dte_arg), config)
|
||||
{
|
||||
dce_list.push_back(this);
|
||||
}
|
||||
|
||||
// Destructor - remove this DCE instance from the static list
|
||||
DCE::~DCE()
|
||||
{
|
||||
auto it = std::find(dce_list.begin(), dce_list.end(), this);
|
||||
if (it != dce_list.end()) {
|
||||
dce_list.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DCE::perform_sock()
|
||||
@@ -61,13 +85,26 @@ bool DCE::perform_sock()
|
||||
|
||||
void DCE::perform_at(uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_VERBOSE);
|
||||
if (state != status::RECEIVING) {
|
||||
std::string_view resp_sv((char *)data, len);
|
||||
at.check_urc(state, resp_sv);
|
||||
if (state == status::IDLE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Trace incoming AT bytes when handling a response; use DEBUG level
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, data, len, ESP_LOG_DEBUG);
|
||||
switch (at.process_data(state, data, len)) {
|
||||
case Responder::ret::OK:
|
||||
// Release DTE access for this link after processing data
|
||||
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
ESP_LOGD(TAG, "GIVE data %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
@@ -82,10 +119,14 @@ void DCE::perform_at(uint8_t *data, size_t len)
|
||||
std::string_view response((char *)data, len);
|
||||
switch (at.check_async_replies(state, response)) {
|
||||
case Responder::ret::OK:
|
||||
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::IDLE;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
case Responder::ret::FAIL:
|
||||
ESP_LOGD(TAG, "GIVE command %d", at.link_id);
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
state = status::FAILED;
|
||||
signal.set(IDLE);
|
||||
return;
|
||||
@@ -121,7 +162,7 @@ bool DCE::at_to_sock()
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
ESP_LOGD(TAG, "select read: modem data available %" PRIu64, data);
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
@@ -131,6 +172,10 @@ bool DCE::at_to_sock()
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
// Take DTE mutex before issuing receive on this link
|
||||
ESP_LOGD(TAG, "TAKE RECV %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN RECV %d", at.link_id);
|
||||
state = status::RECEIVING;
|
||||
at.start_receiving(at.get_buf_len());
|
||||
return true;
|
||||
@@ -139,7 +184,7 @@ bool DCE::at_to_sock()
|
||||
bool DCE::sock_to_at()
|
||||
{
|
||||
ESP_LOGD(TAG, "socket read: data available");
|
||||
if (!signal.wait(IDLE, 1000)) {
|
||||
if (!signal.wait(IDLE, WAIT_TO_IDLE_TIMEOUT)) {
|
||||
ESP_LOGE(TAG, "Failed to get idle");
|
||||
close_sock();
|
||||
return false;
|
||||
@@ -149,6 +194,10 @@ bool DCE::sock_to_at()
|
||||
close_sock();
|
||||
return false;
|
||||
}
|
||||
// Take DTE mutex before issuing send on this link
|
||||
ESP_LOGD(TAG, "TAKE SEND %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN SEND %d", at.link_id);
|
||||
state = status::SENDING;
|
||||
int len = ::recv(sock, at.get_buf(), at.get_buf_len(), 0);
|
||||
if (len < 0) {
|
||||
@@ -201,7 +250,7 @@ void DCE::start_listening(int port)
|
||||
}
|
||||
int opt = 1;
|
||||
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
ESP_LOGI(TAG, "Socket created");
|
||||
ESP_LOGD(TAG, "Socket created");
|
||||
struct sockaddr_in addr = { };
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
@@ -213,7 +262,7 @@ void DCE::start_listening(int port)
|
||||
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket bound, port %d", 1883);
|
||||
ESP_LOGD(TAG, "Socket bound, port %d", 1883);
|
||||
err = listen(listen_sock, 1);
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
|
||||
@@ -224,12 +273,12 @@ void DCE::start_listening(int port)
|
||||
|
||||
bool DCE::connect(std::string host, int port)
|
||||
{
|
||||
dte->on_read(nullptr);
|
||||
tcp_close();
|
||||
dte->on_read([this](uint8_t *data, size_t len) {
|
||||
this->perform_at(data, len);
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
});
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
// Take DTE mutex before starting connect for this link
|
||||
ESP_LOGD(TAG, "TAKE CONNECT %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD(TAG, "TAKEN CONNECT %d", at.link_id);
|
||||
if (!at.start_connecting(host, port)) {
|
||||
ESP_LOGE(TAG, "Unable to start connecting");
|
||||
dte->on_read(nullptr);
|
||||
@@ -241,12 +290,15 @@ bool DCE::connect(std::string host, int port)
|
||||
|
||||
bool DCE::init()
|
||||
{
|
||||
if (network_init) {
|
||||
return true;
|
||||
}
|
||||
network_init = true;
|
||||
Responder::s_dte_mutex = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(at.s_dte_mutex);
|
||||
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
|
||||
esp_vfs_eventfd_register(&config);
|
||||
|
||||
data_ready_fd = eventfd(0, EFD_SUPPORT_ISR);
|
||||
assert(data_ready_fd > 0);
|
||||
|
||||
dte->on_read(nullptr);
|
||||
const int retries = 5;
|
||||
int i = 0;
|
||||
@@ -287,6 +339,10 @@ bool DCE::init()
|
||||
esp_modem::Task::Delay(5000);
|
||||
}
|
||||
ESP_LOGI(TAG, "Got IP %s", ip_addr.c_str());
|
||||
dte->on_read([](uint8_t *data, size_t len) {
|
||||
read_callback(data, len);
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
sock(s), data_ready_fd(ready_fd), dte(dte_arg) {}
|
||||
ret process_data(status state, uint8_t *data, size_t len);
|
||||
ret check_async_replies(status state, std::string_view &response);
|
||||
ret check_urc(status state, std::string_view &response);
|
||||
|
||||
void start_sending(size_t len);
|
||||
void start_receiving(size_t len);
|
||||
@@ -63,13 +64,19 @@ public:
|
||||
return total_len;
|
||||
}
|
||||
|
||||
// Unique link identifier used to target multi-connection AT commands
|
||||
int link_id{s_link_id++};
|
||||
// Shared mutex guarding DTE access across concurrent DCE instances
|
||||
static SemaphoreHandle_t s_dte_mutex;
|
||||
private:
|
||||
static int s_link_id;
|
||||
static constexpr size_t buffer_size = 512;
|
||||
|
||||
bool on_read(char *data, size_t len)
|
||||
{
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
::send(sock, data, len, 0);
|
||||
printf("sending %d\n", len);
|
||||
#else
|
||||
::memcpy(&buffer[actual_read], data, len);
|
||||
actual_read += len;
|
||||
@@ -101,6 +108,8 @@ private:
|
||||
class DCE : public Module {
|
||||
using Module::Module;
|
||||
public:
|
||||
DCE(std::shared_ptr<esp_modem::DTE> dte_arg, const esp_modem_dce_config *config);
|
||||
~DCE();
|
||||
|
||||
// --- ESP-MODEM command module starts here ---
|
||||
#include "esp_modem_command_declare_helper.inc"
|
||||
@@ -141,6 +150,10 @@ esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
|
||||
return 0;
|
||||
}
|
||||
at.clear_offsets();
|
||||
// Take DTE mutex before issuing receive on this link
|
||||
ESP_LOGD("TAG", "TAKE RECV %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD("TAG", "TAKEN RECV %d", at.link_id);
|
||||
state = status::RECEIVING;
|
||||
uint64_t data;
|
||||
read(data_ready_fd, &data, sizeof(data));
|
||||
@@ -163,6 +176,10 @@ esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
|
||||
if (!wait_to_idle(timeout_ms)) {
|
||||
return -1;
|
||||
}
|
||||
// Take DTE mutex before issuing send on this link
|
||||
ESP_LOGD("TAG", "TAKE SEND %d", at.link_id);
|
||||
xSemaphoreTake(at.s_dte_mutex, portMAX_DELAY);
|
||||
ESP_LOGD("TAG", "TAKEN SEND %d", at.link_id);
|
||||
state = status::SENDING;
|
||||
memcpy(at.get_buf(), buffer, len_to_send);
|
||||
ESP_LOG_BUFFER_HEXDUMP("dce", at.get_buf(), len, ESP_LOG_VERBOSE);
|
||||
@@ -204,6 +221,14 @@ esp_modem::return_type name(ESP_MODEM_COMMAND_PARAMS(__VA_ARGS__));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
static std::vector<DCE*> dce_list;
|
||||
static bool network_init;
|
||||
static void read_callback(uint8_t *data, size_t len)
|
||||
{
|
||||
for (auto dce : dce_list) {
|
||||
dce->perform_at(data, len);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
esp_modem::SignalGroup signal;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.1"
|
||||
override_path: "../../../"
|
||||
version: ^2
|
||||
override_path: ../../../
|
||||
espressif/mbedtls_cxx:
|
||||
version: "*"
|
||||
override_path: "../../../../mbedtls_cxx"
|
||||
version: '*'
|
||||
override_path: ../../../../mbedtls_cxx
|
||||
espressif/mqtt:
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
|
||||
@@ -22,15 +22,26 @@
|
||||
#include "esp_log.h"
|
||||
#include "tcp_transport_mbedtls.h"
|
||||
#include "tcp_transport_at.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define BROKER_URL "test.mosquitto.org"
|
||||
#define USE_TLS CONFIG_EXAMPLE_USE_TLS
|
||||
#define BROKER_HOST CONFIG_EXAMPLE_BROKER_HOST
|
||||
#if USE_TLS
|
||||
#define BROKER_SCHEME "mqtts"
|
||||
#define BROKER_PORT 8883
|
||||
#else
|
||||
#define BROKER_SCHEME "mqtt"
|
||||
#define BROKER_PORT 1883
|
||||
#endif
|
||||
#define BROKER_URL BROKER_SCHEME "://" BROKER_HOST
|
||||
|
||||
|
||||
static const char *TAG = "modem_client";
|
||||
static EventGroupHandle_t event_group = NULL;
|
||||
static const int CONNECT_BIT = BIT0;
|
||||
static const int GOT_DATA_BIT = BIT2;
|
||||
static const int DCE0_DONE = BIT3;
|
||||
static const int DCE1_DONE = BIT4;
|
||||
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
{
|
||||
@@ -73,13 +84,15 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
|
||||
}
|
||||
}
|
||||
|
||||
static void perform(void* ctx);
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
|
||||
/* Init and register system/core components */
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
// Default to INFO; individual modules use DEBUG for verbose tracing
|
||||
esp_log_level_set("*", ESP_LOG_INFO);
|
||||
|
||||
event_group = xEventGroupCreate();
|
||||
|
||||
@@ -104,30 +117,60 @@ extern "C" void app_main(void)
|
||||
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_APN);
|
||||
|
||||
/* create the DCE and initialize network manually (using AT commands) */
|
||||
auto dce = sock_dce::create(&dce_config, std::move(dte));
|
||||
auto dce = sock_dce::create(&dce_config, dte);
|
||||
if (!dce->init()) {
|
||||
ESP_LOGE(TAG, "Failed to setup network");
|
||||
return;
|
||||
}
|
||||
|
||||
esp_mqtt_client_config_t mqtt_config = {};
|
||||
mqtt_config.broker.address.port = BROKER_PORT;
|
||||
mqtt_config.session.message_retransmit_timeout = 10000;
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
mqtt_config.broker.address.uri = "mqtt://127.0.0.1";
|
||||
dce->start_listening(BROKER_PORT);
|
||||
#else
|
||||
mqtt_config.broker.address.uri = "mqtt://" BROKER_URL;
|
||||
esp_transport_handle_t at = esp_transport_at_init(dce.get());
|
||||
esp_transport_handle_t ssl = esp_transport_tls_init(at);
|
||||
xTaskCreate(perform, "perform", 4096, dce.get(), 4, nullptr);
|
||||
|
||||
mqtt_config.network.transport = ssl;
|
||||
/* create another DCE to serve a new connection */
|
||||
auto dce1 = sock_dce::create(&dce_config, dte);
|
||||
if (!dce1->init()) {
|
||||
ESP_LOGE(TAG, "Failed to setup network");
|
||||
return;
|
||||
}
|
||||
xTaskCreate(perform, "perform", 4096, dce1.get(), 4, nullptr);
|
||||
|
||||
xEventGroupWaitBits(event_group, DCE0_DONE | DCE1_DONE, pdFALSE, pdTRUE, portMAX_DELAY);
|
||||
#ifdef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
// this example does never keeps both DCEs running and in tcp-transport option
|
||||
// we don't need a task to run so we exit main and keep DCE's "running"
|
||||
vTaskDelay(portMAX_DELAY);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void perform(void* ctx)
|
||||
{
|
||||
auto dce = static_cast<sock_dce::DCE*>(ctx);
|
||||
char mqtt_client_id[] = "MQTT_CLIENT_0";
|
||||
static int counter = 0;
|
||||
const int id = counter++;
|
||||
mqtt_client_id[sizeof(mqtt_client_id) - 2] += id; // assumes a different client id per each thread
|
||||
esp_mqtt_client_config_t mqtt_config = {};
|
||||
mqtt_config.session.message_retransmit_timeout = 10000;
|
||||
mqtt_config.credentials.client_id = mqtt_client_id;
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
mqtt_config.broker.address.port = BROKER_PORT + id;
|
||||
mqtt_config.broker.address.uri = BROKER_SCHEME "://127.0.0.1";
|
||||
dce->start_listening(BROKER_PORT + id);
|
||||
#else
|
||||
mqtt_config.broker.address.port = BROKER_PORT;
|
||||
mqtt_config.broker.address.uri = BROKER_URL;
|
||||
esp_transport_handle_t at = esp_transport_at_init(dce);
|
||||
#if USE_TLS
|
||||
esp_transport_handle_t ssl = esp_transport_tls_init(at);
|
||||
mqtt_config.network.transport = ssl;
|
||||
#else
|
||||
mqtt_config.network.transport = at;
|
||||
#endif // USE_TLS
|
||||
#endif // CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
|
||||
esp_mqtt_client_register_event(mqtt_client, static_cast<esp_mqtt_event_id_t>(ESP_EVENT_ANY_ID), mqtt_event_handler, nullptr);
|
||||
esp_mqtt_client_start(mqtt_client);
|
||||
#ifndef CONFIG_EXAMPLE_CUSTOM_TCP_TRANSPORT
|
||||
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
|
||||
if (!dce->connect(BROKER_HOST, BROKER_PORT)) {
|
||||
ESP_LOGE(TAG, "Failed to start DCE");
|
||||
return;
|
||||
}
|
||||
@@ -135,18 +178,17 @@ extern "C" void app_main(void)
|
||||
while (dce->perform_sock()) {
|
||||
ESP_LOGV(TAG, "...performing");
|
||||
}
|
||||
ESP_LOGE(TAG, "Loop exit.. retrying");
|
||||
ESP_LOGD(TAG, "Loop exit.. retrying");
|
||||
// handle disconnections errors
|
||||
if (!dce->init()) {
|
||||
ESP_LOGE(TAG, "Failed to reinit network");
|
||||
return;
|
||||
}
|
||||
if (!dce->connect(BROKER_URL, BROKER_PORT)) {
|
||||
if (!dce->connect(BROKER_HOST, BROKER_PORT)) {
|
||||
ESP_LOGI(TAG, "Network reinitialized, retrying");
|
||||
}
|
||||
}
|
||||
#else
|
||||
vTaskDelay(portMAX_DELAY);
|
||||
#endif
|
||||
|
||||
xEventGroupSetBits(event_group, id ? DCE0_DONE : DCE1_DONE);
|
||||
vTaskDelete(nullptr);
|
||||
}
|
||||
|
||||
@@ -109,17 +109,17 @@ void Responder::start_sending(size_t len)
|
||||
{
|
||||
data_to_send = len;
|
||||
send_stat = 0;
|
||||
send_cmd("AT+QISEND=0," + std::to_string(len) + "\r");
|
||||
send_cmd("AT+QISEND=" + std::to_string(link_id) + "," + std::to_string(len) + "\r");
|
||||
}
|
||||
|
||||
void Responder::start_receiving(size_t len)
|
||||
{
|
||||
send_cmd("AT+QIRD=0," + std::to_string(len) + "\r");
|
||||
send_cmd("AT+QIRD=" + std::to_string(link_id) + "," + std::to_string(len) + "\r");
|
||||
}
|
||||
|
||||
bool Responder::start_connecting(std::string host, int port)
|
||||
{
|
||||
send_cmd(R"(AT+QIOPEN=1,0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
|
||||
send_cmd(std::string("AT+QIOPEN=1,") + std::to_string(link_id) + R"(,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -130,16 +130,24 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
auto *recv_data = (char *)data;
|
||||
if (data_to_recv == 0) {
|
||||
const std::string_view head = "+QIRD: ";
|
||||
again:
|
||||
const std::string_view recv_data_view = std::string_view(recv_data, len);
|
||||
auto head_pos_found = recv_data_view.find(head);
|
||||
if (head_pos_found == std::string_view::npos) {
|
||||
return ret::FAIL;
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
auto *head_pos = recv_data + head_pos_found;
|
||||
auto next_nl = (char *)memchr(head_pos + head.size(), '\n', MIN_MESSAGE);
|
||||
|
||||
if (next_nl == nullptr) {
|
||||
if (head_pos + head.size() + 1 < recv_data + len) {
|
||||
// might be that we misinterpreted the URC +QIRD: <>,<>,<> (notification) with the +QIRD: <> (read data)
|
||||
// so we try to find the next +QIRD:
|
||||
recv_data = head_pos + head.size() + 1;
|
||||
goto again;
|
||||
}
|
||||
ESP_LOGD(TAG, "no new line found");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
@@ -151,7 +159,9 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
ESP_LOGD(TAG, "Received: actual len=%d", actual_len);
|
||||
if (actual_len == 0) {
|
||||
ESP_LOGD(TAG, "no data received");
|
||||
return ret::FAIL;
|
||||
data_to_recv = 0;
|
||||
// return OK here, as BG96 would keep unacked data and notifies us with +QIRD: 0
|
||||
return ret::OK;
|
||||
}
|
||||
|
||||
if (actual_len > buffer_size) {
|
||||
@@ -182,6 +192,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
last_pos = (char *)memchr(recv_data + 1 + actual_len, 'O', MIN_MESSAGE);
|
||||
if (last_pos == nullptr || last_pos[1] != 'K') {
|
||||
data_to_recv = 0;
|
||||
ESP_LOGD(TAG, "no OK after data");
|
||||
return ret::FAIL;
|
||||
}
|
||||
}
|
||||
@@ -222,7 +233,7 @@ Responder::ret Responder::send(std::string_view response)
|
||||
{
|
||||
if (send_stat == 3) {
|
||||
if (response.find("SEND OK") != std::string::npos) {
|
||||
send_cmd("AT+QISEND=0,0\r");
|
||||
send_cmd(std::string("AT+QISEND=") + std::to_string(link_id) + ",0\r");
|
||||
send_stat++;
|
||||
return ret::IN_PROGRESS;
|
||||
} else if (response.find("SEND FAIL") != std::string::npos) {
|
||||
@@ -267,7 +278,7 @@ Responder::ret Responder::send(std::string_view response)
|
||||
if (ack < total) {
|
||||
ESP_LOGD(TAG, "all sending data are not ack (missing %d bytes acked)", (total - ack));
|
||||
if (total - ack > 64) {
|
||||
ESP_LOGW(TAG, "Need a pause: missing %d bytes acked", (total - ack));
|
||||
ESP_LOGD(TAG, "Need a pause: missing %d bytes acked", (total - ack));
|
||||
return ret::NEED_MORE_TIME;
|
||||
}
|
||||
}
|
||||
@@ -284,7 +295,8 @@ Responder::ret Responder::send(std::string_view response)
|
||||
|
||||
Responder::ret Responder::connect(std::string_view response)
|
||||
{
|
||||
if (response.find("+QIOPEN: 0,0") != std::string::npos) {
|
||||
std::string open_response = "+QIOPEN: " + std::to_string(link_id) + ",0";
|
||||
if (response.find(open_response) != std::string::npos) {
|
||||
ESP_LOGI(TAG, "Connected!");
|
||||
return ret::OK;
|
||||
}
|
||||
@@ -295,10 +307,9 @@ Responder::ret Responder::connect(std::string_view response)
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
|
||||
Responder::ret Responder::check_urc(status state, std::string_view &response)
|
||||
{
|
||||
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
|
||||
if (response.find("+QIURC: \"recv\",0") != std::string::npos) {
|
||||
if (response.find(std::string("+QIURC: \"recv\",") + std::to_string(link_id)) != std::string::npos) {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
ESP_LOGD(TAG, "Got data on modem!");
|
||||
@@ -309,6 +320,9 @@ Responder::ret Responder::check_async_replies(status state, std::string_view &re
|
||||
response = response.substr(head_pos + head.size());
|
||||
int next_cr = response.find('\r');
|
||||
if (next_cr != std::string::npos) {
|
||||
if (next_cr < 2) {
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
response = response.substr(next_cr - 2, next_cr);
|
||||
if (response.find(",0") != std::string::npos) {
|
||||
ESP_LOGV(TAG, "Receiving done");
|
||||
@@ -318,12 +332,21 @@ Responder::ret Responder::check_async_replies(status state, std::string_view &re
|
||||
ESP_LOGD(TAG, "Got data on modem!");
|
||||
}
|
||||
}
|
||||
} else if (response.find("+QIURC: \"closed\",0") != std::string::npos) {
|
||||
} else if (response.find(std::string("+QIURC: \"closed\",") + std::to_string(link_id)) != std::string::npos) {
|
||||
ESP_LOGE(TAG, "Connection closed");
|
||||
return ret::FAIL;
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
|
||||
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
|
||||
{
|
||||
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
|
||||
if (state == status::SENDING) {
|
||||
return send(response);
|
||||
} else if (state == status::CONNECTING) {
|
||||
}
|
||||
if (state == status::CONNECTING) {
|
||||
return connect(response);
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
@@ -342,7 +365,7 @@ Responder::ret Responder::process_data(status state, uint8_t *data, size_t len)
|
||||
|
||||
status Responder::pending()
|
||||
{
|
||||
send_cmd("AT+QISEND=0,0\r");
|
||||
send_cmd(std::string("AT+QISEND=") + std::to_string(link_id) + ",0\r");
|
||||
return status::SENDING;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,12 +58,19 @@ command_result net_open(CommandableIf *t)
|
||||
}
|
||||
ESP_LOGI(TAG, "WiFi connected successfully");
|
||||
|
||||
// Set passive receive mode (1) for better control
|
||||
ret = set_rx_mode(t, 1);
|
||||
// Enable multiple connections mode
|
||||
ret = dce_commands::generic_command(t, "AT+CIPMUX=1\r\n", "OK", "ERROR", 1000);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGE(TAG, "Failed to set preferred Rx mode");
|
||||
ESP_LOGE(TAG, "Failed to enable multiple connections mode");
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGD(TAG, "Multiple connections mode enabled");
|
||||
|
||||
// Set passive receive mode (1) for better control
|
||||
for (int i = 0; i < 2; i++) {
|
||||
std::string cmd = "AT+CIPRECVTYPE=" + std::to_string(i) + ",1\r\n";
|
||||
dce_commands::generic_command(t, cmd, "OK", "ERROR", 1000);
|
||||
}
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
@@ -78,49 +85,20 @@ command_result net_close(CommandableIf *t)
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result tcp_open(CommandableIf *t, const std::string &host, int port, int timeout)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
|
||||
// Set single connection mode (just in case)
|
||||
auto ret = dce_commands::generic_command(t, "AT+CIPMUX=0\r\n", "OK", "ERROR", 1000);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGW(TAG, "Failed to set single connection mode");
|
||||
}
|
||||
|
||||
// Establish TCP connection
|
||||
std::string tcp_cmd = "AT+CIPSTART=\"TCP\",\"" + host + "\"," + std::to_string(port) + "\r\n";
|
||||
ret = dce_commands::generic_command(t, tcp_cmd, "CONNECT", "ERROR", timeout);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGE(TAG, "Failed to establish TCP connection to %s:%d", host.c_str(), port);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "TCP connection established to %s:%d", host.c_str(), port);
|
||||
return command_result::OK;
|
||||
}
|
||||
|
||||
command_result tcp_close(CommandableIf *t)
|
||||
{
|
||||
return command_result::OK;
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
return dce_commands::generic_command(t, "AT+CIPCLOSE\r\n", "CLOSED", "ERROR", 5000);
|
||||
// Use link ID 0 for closing connection
|
||||
const int link_id = 0;
|
||||
std::string close_cmd = "AT+CIPCLOSE=" + std::to_string(link_id) + "\r\n";
|
||||
|
||||
// In multiple connections mode, response format is: <link ID>,CLOSED
|
||||
std::string expected_response = std::to_string(link_id) + ",CLOSED";
|
||||
|
||||
return dce_commands::generic_command(t, close_cmd, expected_response, "ERROR", 5000);
|
||||
}
|
||||
|
||||
command_result tcp_send(CommandableIf *t, uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
// This function is not used in the current implementation
|
||||
// Data sending is handled by the DCE responder
|
||||
return command_result::FAIL;
|
||||
}
|
||||
|
||||
command_result tcp_recv(CommandableIf *t, uint8_t *data, size_t len, size_t &out_len)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
// This function is not used in the current implementation
|
||||
// Data receiving is handled by the DCE responder
|
||||
return command_result::FAIL;
|
||||
}
|
||||
|
||||
command_result get_ip(CommandableIf *t, std::string &ip)
|
||||
{
|
||||
@@ -150,9 +128,11 @@ command_result get_ip(CommandableIf *t, std::string &ip)
|
||||
|
||||
command_result set_rx_mode(CommandableIf *t, int mode)
|
||||
{
|
||||
ESP_LOGE(TAG, "%s", __func__);
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
// For multiple connections mode, set receive mode for link ID 0
|
||||
const int link_id = 0;
|
||||
// Active mode (0) sends data automatically, Passive mode (1) notifies about data for reading
|
||||
std::string cmd = "AT+CIPRECVTYPE=" + std::to_string(mode) + "\r\n";
|
||||
std::string cmd = "AT+CIPRECVTYPE=" + std::to_string(link_id) + "," + std::to_string(mode) + "\r\n";
|
||||
return dce_commands::generic_command(t, cmd, "OK", "ERROR", 1000);
|
||||
}
|
||||
|
||||
@@ -164,17 +144,20 @@ void Responder::start_sending(size_t len)
|
||||
{
|
||||
data_to_send = len;
|
||||
send_stat = 0;
|
||||
send_cmd("AT+CIPSEND=" + std::to_string(len) + "\r\n");
|
||||
// For multiple connections mode, include link ID
|
||||
send_cmd("AT+CIPSEND=" + std::to_string(link_id) + "," + std::to_string(len) + "\r\n");
|
||||
}
|
||||
|
||||
void Responder::start_receiving(size_t len)
|
||||
{
|
||||
send_cmd("AT+CIPRECVDATA=" + std::to_string(len) + "\r\n");
|
||||
// For multiple connections mode, include link ID
|
||||
send_cmd("AT+CIPRECVDATA=" + std::to_string(link_id) + "," + std::to_string(len) + "\r\n");
|
||||
}
|
||||
|
||||
bool Responder::start_connecting(std::string host, int port)
|
||||
{
|
||||
std::string cmd = "AT+CIPSTART=\"TCP\",\"" + host + "\"," + std::to_string(port) + "\r\n";
|
||||
// For multiple connections mode, include link ID
|
||||
std::string cmd = "AT+CIPSTART=" + std::to_string(link_id) + ",\"TCP\",\"" + host + "\"," + std::to_string(port) + "\r\n";
|
||||
send_cmd(cmd);
|
||||
return true;
|
||||
}
|
||||
@@ -187,16 +170,12 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
|
||||
if (data_to_recv == 0) {
|
||||
const std::string_view head = "+CIPRECVDATA:";
|
||||
|
||||
// Find the response header
|
||||
auto head_pos = std::search(recv_data, recv_data + len, head.data(), head.data() + head.size(), [](char a, char b) {
|
||||
return a == b;
|
||||
});
|
||||
|
||||
if (head_pos == recv_data + len) {
|
||||
return ret::FAIL;
|
||||
const std::string_view recv_data_view(recv_data, len);
|
||||
const auto head_pos_found = recv_data_view.find(head);
|
||||
if (head_pos_found == std::string_view::npos) {
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
const auto *head_pos = recv_data + head_pos_found;
|
||||
// Find the end of the length field
|
||||
auto next_comma = (char *)memchr(head_pos + head.size(), ',', MIN_MESSAGE);
|
||||
if (next_comma == nullptr) {
|
||||
@@ -245,12 +224,25 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
char *ok_pos = nullptr;
|
||||
if (actual_len + 1 + 2 /* OK */ <= len) {
|
||||
ok_pos = (char *)memchr(recv_data + actual_len + 1, 'O', MIN_MESSAGE);
|
||||
if (ok_pos == nullptr || ok_pos[1] != 'K') {
|
||||
if (ok_pos == nullptr) { // || ok_pos[1] != 'K') {
|
||||
data_to_recv = 0;
|
||||
ESP_LOGE(TAG, "Missed 'OK' marker");
|
||||
return ret::OK;
|
||||
return ret::FAIL;
|
||||
}
|
||||
if (ok_pos + 1 < recv_data + len && ok_pos[1] != 'K') {
|
||||
// we ignore the condition when receiving 'O' as the last character in the last batch,
|
||||
// don't wait for the 'K' in the next run, assume the data are valid and let higher layers deal with it.
|
||||
data_to_recv = 0;
|
||||
ESP_LOGE(TAG, "Missed 'OK' marker2");
|
||||
return ret::FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok_pos != nullptr && (char *)data + len - ok_pos - 2 > MIN_MESSAGE) {
|
||||
// check for async replies after the Recv header
|
||||
std::string_view response((char *)ok_pos + 2 /* OK */, (char *)data + len - ok_pos);
|
||||
check_urc(status::RECEIVING, response);
|
||||
}
|
||||
// Reset and prepare for next receive
|
||||
data_to_recv = 0;
|
||||
return ret::OK;
|
||||
@@ -299,7 +291,8 @@ Responder::ret Responder::send(std::string_view response)
|
||||
|
||||
Responder::ret Responder::connect(std::string_view response)
|
||||
{
|
||||
if (response.find("CONNECT") != std::string::npos) {
|
||||
// In multiple connections mode, response format is: <link ID>,CONNECT
|
||||
if (response.find(",CONNECT") != std::string::npos || response.find("CONNECT") != std::string::npos) {
|
||||
ESP_LOGI(TAG, "TCP connected!");
|
||||
return ret::OK;
|
||||
}
|
||||
@@ -309,6 +302,17 @@ Responder::ret Responder::connect(std::string_view response)
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
Responder::ret Responder::check_urc(status state, std::string_view &response)
|
||||
{
|
||||
// Handle data notifications - in multiple connections mode, format is +IPD,<link ID>,<len>
|
||||
std::string expected_urc = "+IPD," + std::to_string(link_id);
|
||||
if (response.find(expected_urc) != std::string::npos) {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
ESP_LOGD(TAG, "Data available notification");
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
|
||||
{
|
||||
@@ -318,24 +322,17 @@ Responder::ret Responder::check_async_replies(status state, std::string_view &re
|
||||
if (response.find("WIFI CONNECTED") != std::string::npos) {
|
||||
ESP_LOGI(TAG, "WiFi connected");
|
||||
} else if (response.find("WIFI DISCONNECTED") != std::string::npos) {
|
||||
ESP_LOGW(TAG, "WiFi disconnected");
|
||||
ESP_LOGD(TAG, "WiFi disconnected");
|
||||
}
|
||||
|
||||
// Handle TCP status messages
|
||||
// Handle TCP status messages (multiple connections format: <link ID>,CONNECT or <link ID>,CLOSED)
|
||||
if (response.find("CONNECT") != std::string::npos && state == status::CONNECTING) {
|
||||
return connect(response);
|
||||
} else if (response.find("CLOSED") != std::string::npos) {
|
||||
ESP_LOGW(TAG, "TCP connection closed");
|
||||
ESP_LOGD(TAG, "TCP connection closed");
|
||||
return ret::FAIL;
|
||||
}
|
||||
|
||||
// Handle data notifications in active mode (if we switch to it later)
|
||||
if (response.find("+IPD,") != std::string::npos) {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
ESP_LOGD(TAG, "Data available notification");
|
||||
}
|
||||
|
||||
if (state == status::SENDING) {
|
||||
return send(response);
|
||||
} else if (state == status::CONNECTING) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -18,13 +18,13 @@ using namespace esp_modem;
|
||||
|
||||
command_result net_open(CommandableIf *term)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
std::string response;
|
||||
auto ret = dce_commands::generic_get_string(term, "AT+NETOPEN?\r", response, 1000);
|
||||
if (ret != command_result::OK) {
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGV(TAG, "%s", response.data() );
|
||||
ESP_LOGV(TAG, "%s", response.data());
|
||||
if (response.find("+NETOPEN: 1") != std::string::npos) {
|
||||
ESP_LOGD(TAG, "Already there");
|
||||
ret = command_result::OK;
|
||||
@@ -42,23 +42,23 @@ command_result net_open(CommandableIf *term)
|
||||
|
||||
command_result net_close(CommandableIf *term)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
return dce_commands::generic_command(term, "AT+NETCLOSE\r", "+NETCLOSE:", "ERROR", 30000);
|
||||
}
|
||||
|
||||
command_result tcp_open(CommandableIf *term, const std::string &host, int port, int timeout)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
auto ret = dce_commands::generic_command(term, "AT+CIPRXGET=1\r", "OK", "ERROR", 50000);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGE(TAG, "Setting Rx mode failed!");
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
std::string ip_open = R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r";
|
||||
ret = dce_commands::generic_command(term, ip_open, "+CIPOPEN: 0,0", "ERROR", timeout);
|
||||
if (ret != command_result::OK) {
|
||||
ESP_LOGE(TAG, "%s Failed", __func__ );
|
||||
ESP_LOGE(TAG, "%s Failed", __func__);
|
||||
return ret;
|
||||
}
|
||||
return command_result::OK;
|
||||
@@ -66,13 +66,13 @@ command_result tcp_open(CommandableIf *term, const std::string &host, int port,
|
||||
|
||||
command_result tcp_close(CommandableIf *term)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
return dce_commands::generic_command(term, "AT+CIPCLOSE=0\r", "+CIPCLOSE:", "ERROR", 10000);
|
||||
}
|
||||
|
||||
command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
std::string send = "AT+CIPSEND=0," + std::to_string(len) + "\r";
|
||||
auto ret = term->command(send, [&](uint8_t *data, size_t len) {
|
||||
std::string_view response((char *)data, len);
|
||||
@@ -86,10 +86,10 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
|
||||
return ret;
|
||||
}
|
||||
ret = command_result::TIMEOUT;
|
||||
ESP_LOGW(TAG, "Before setting...");
|
||||
ESP_LOGD(TAG, "Before setting...");
|
||||
term->on_read([&ret](uint8_t *cmd_data, size_t cmd_len) {
|
||||
std::string_view response((char *)cmd_data, cmd_len);
|
||||
ESP_LOGW(TAG, "CIPSEND response %.*s", static_cast<int>(response.size()), response.data());
|
||||
ESP_LOGD(TAG, "CIPSEND response %.*s", static_cast<int>(response.size()), response.data());
|
||||
|
||||
if (response.find("+CIPSEND:") != std::string::npos) {
|
||||
ret = command_result::OK;
|
||||
@@ -98,7 +98,7 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
ESP_LOGW(TAG, "Before writing...");
|
||||
ESP_LOGD(TAG, "Before writing...");
|
||||
auto written = term->write(data, len);
|
||||
if (written != len) {
|
||||
ESP_LOGE(TAG, "written %d (%d)...", written, len);
|
||||
@@ -107,7 +107,7 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
|
||||
uint8_t ctrl_z = '\x1A';
|
||||
term->write(&ctrl_z, 1);
|
||||
int count = 0;
|
||||
while (ret == command_result::TIMEOUT && count++ < 1000 ) {
|
||||
while (ret == command_result::TIMEOUT && count++ < 1000) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
term->on_read(nullptr);
|
||||
@@ -116,7 +116,7 @@ command_result tcp_send(CommandableIf *term, uint8_t *data, size_t len)
|
||||
|
||||
command_result tcp_recv(CommandableIf *term, uint8_t *data, size_t len, size_t &out_len)
|
||||
{
|
||||
ESP_LOGV(TAG, "%s", __func__ );
|
||||
ESP_LOGV(TAG, "%s", __func__);
|
||||
std::string out;
|
||||
auto ret = dce_commands::generic_get_string(term, "AT+CIPRXGET=4,0\r", out);
|
||||
if (ret != command_result::OK) {
|
||||
@@ -195,17 +195,17 @@ void Responder::start_sending(size_t len)
|
||||
{
|
||||
data_to_send = len;
|
||||
send_stat = 0;
|
||||
send_cmd("AT+CIPSEND=0," + std::to_string(len) + "\r");
|
||||
send_cmd("AT+CIPSEND=" + std::to_string(link_id) + "," + std::to_string(len) + "\r");
|
||||
}
|
||||
|
||||
void Responder::start_receiving(size_t len)
|
||||
{
|
||||
send_cmd("AT+CIPRXGET=2,0," + std::to_string(len) + "\r");
|
||||
send_cmd("AT+CIPRXGET=2," + std::to_string(link_id) + "," + std::to_string(len) + "\r");
|
||||
}
|
||||
|
||||
bool Responder::start_connecting(std::string host, int port)
|
||||
{
|
||||
send_cmd(R"(AT+CIPOPEN=0,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
|
||||
send_cmd(std::string("AT+CIPOPEN=") + std::to_string(link_id) + R"(,"TCP",")" + host + "\"," + std::to_string(port) + "\r");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ Responder::ret Responder::recv(uint8_t *data, size_t len)
|
||||
size_t actual_len = 0;
|
||||
auto *recv_data = (char *)data;
|
||||
if (data_to_recv == 0) {
|
||||
static constexpr std::string_view head = "+CIPRXGET: 2,0,";
|
||||
const std::string head = std::string("+CIPRXGET: 2,") + std::to_string(link_id) + ",";
|
||||
auto head_pos = std::search(recv_data, recv_data + len, head.begin(), head.end());
|
||||
if (head_pos == recv_data + len) {
|
||||
return ret::FAIL;
|
||||
@@ -329,7 +329,8 @@ Responder::ret Responder::send(std::string_view response)
|
||||
|
||||
Responder::ret Responder::connect(std::string_view response)
|
||||
{
|
||||
if (response.find("+CIPOPEN: 0,0") != std::string::npos) {
|
||||
std::string open_response = "+CIPOPEN: " + std::to_string(link_id) + ",0";
|
||||
if (response.find(open_response) != std::string::npos) {
|
||||
ESP_LOGI(TAG, "Connected!");
|
||||
return ret::OK;
|
||||
}
|
||||
@@ -340,14 +341,22 @@ Responder::ret Responder::connect(std::string_view response)
|
||||
return Responder::ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
|
||||
Responder::ret Responder::check_urc(status state, std::string_view &response)
|
||||
{
|
||||
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
|
||||
if (response.find("+CIPRXGET: 1") != std::string::npos) {
|
||||
// 1. When <mode> is set to 1 and the 2-4 mode will take effect.
|
||||
// 2. If AT+CIPRXGET=1, it will report +CIPRXGET: 1,<cid>(multi client) when
|
||||
const std::string expected = std::string("+CIPRXGET: 1,") + std::to_string(link_id);
|
||||
if (response.find(expected) != std::string::npos) {
|
||||
uint64_t data_ready = 1;
|
||||
write(data_ready_fd, &data_ready, sizeof(data_ready));
|
||||
ESP_LOGD(TAG, "Got data on modem!");
|
||||
}
|
||||
return ret::IN_PROGRESS;
|
||||
}
|
||||
|
||||
Responder::ret Responder::check_async_replies(status state, std::string_view &response)
|
||||
{
|
||||
ESP_LOGD(TAG, "response %.*s", static_cast<int>(response.size()), response.data());
|
||||
if (state == status::SENDING) {
|
||||
return send(response);
|
||||
} else if (state == status::CONNECTING) {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=4.1.0"
|
||||
idf: '>=4.1.0'
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.0"
|
||||
override_path: "../../../"
|
||||
version: ^2
|
||||
override_path: ../../../
|
||||
espressif/esp_modem_usb_dte:
|
||||
version: "^1.2.0"
|
||||
version: ^1.2.0
|
||||
rules:
|
||||
- if: "idf_version >=4.4"
|
||||
- if: "target in [esp32s2, esp32s3, esp32p4]"
|
||||
- if: idf_version >=4.4
|
||||
- if: target in [esp32s2, esp32s3, esp32p4]
|
||||
console_cmd_ping:
|
||||
version: '*'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -16,8 +16,10 @@
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ppp.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "esp_modem_api.h"
|
||||
#include "esp_console.h"
|
||||
#include "console_ping.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
@@ -35,7 +37,6 @@ static const char *TAG = "pppos_example";
|
||||
static EventGroupHandle_t event_group = NULL;
|
||||
static const int CONNECT_BIT = BIT0;
|
||||
static const int DISCONNECT_BIT = BIT1;
|
||||
static const int GOT_DATA_BIT = BIT2;
|
||||
static const int USB_DISCONNECTED_BIT = BIT3; // Used only with USB DTE but we define it unconditionally, to avoid too many #ifdefs in the code
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_MODEM_DEVICE_CUSTOM
|
||||
@@ -64,47 +65,6 @@ if ((xEventGroupGetBits(event_group) & USB_DISCONNECTED_BIT) == USB_DISCONNECTED
|
||||
#define CHECK_USB_DISCONNECTION(event_group)
|
||||
#endif
|
||||
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
{
|
||||
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIu32, base, event_id);
|
||||
esp_mqtt_event_handle_t event = event_data;
|
||||
esp_mqtt_client_handle_t client = event->client;
|
||||
int msg_id;
|
||||
switch ((esp_mqtt_event_id_t)event_id) {
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
|
||||
msg_id = esp_mqtt_client_subscribe(client, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, 0);
|
||||
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
|
||||
break;
|
||||
case MQTT_EVENT_SUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
msg_id = esp_mqtt_client_publish(client, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA, 0, 0, 0);
|
||||
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_UNSUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_PUBLISHED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DATA:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
|
||||
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
||||
printf("DATA=%.*s\r\n", event->data_len, event->data);
|
||||
xEventGroupSetBits(event_group, GOT_DATA_BIT);
|
||||
break;
|
||||
case MQTT_EVENT_ERROR:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "MQTT other event id: %d", event->event_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void on_ppp_changed(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
@@ -160,6 +120,11 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &on_ip_event, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, &on_ppp_changed, NULL));
|
||||
|
||||
// Initialize console REPL, register ping and start it
|
||||
ESP_ERROR_CHECK(console_cmd_init());
|
||||
ESP_ERROR_CHECK(console_cmd_ping_register());
|
||||
ESP_ERROR_CHECK(console_cmd_start());
|
||||
|
||||
/* Configure the PPP netif */
|
||||
esp_err_t err;
|
||||
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(CONFIG_EXAMPLE_MODEM_PPP_APN);
|
||||
@@ -251,7 +216,7 @@ void app_main(void)
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT
|
||||
xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
|
||||
xEventGroupClearBits(event_group, CONNECT_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
|
||||
|
||||
err = esp_modem_set_mode(dce, ESP_MODEM_MODE_DETECT);
|
||||
if (err != ESP_OK) {
|
||||
@@ -270,7 +235,7 @@ void app_main(void)
|
||||
}
|
||||
#endif // CONFIG_EXAMPLE_DETECT_MODE_BEFORE_CONNECT
|
||||
|
||||
xEventGroupClearBits(event_group, CONNECT_BIT | GOT_DATA_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
|
||||
xEventGroupClearBits(event_group, CONNECT_BIT | USB_DISCONNECTED_BIT | DISCONNECT_BIT);
|
||||
|
||||
/* Run the modem demo app */
|
||||
#if CONFIG_EXAMPLE_NEED_SIM_PIN == 1
|
||||
@@ -340,15 +305,11 @@ void app_main(void)
|
||||
}
|
||||
|
||||
/* Config MQTT */
|
||||
esp_mqtt_client_config_t mqtt_config = {
|
||||
.broker.address.uri = CONFIG_EXAMPLE_MQTT_BROKER_URI,
|
||||
};
|
||||
esp_mqtt_client_handle_t mqtt_client = esp_mqtt_client_init(&mqtt_config);
|
||||
esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
|
||||
esp_mqtt_client_start(mqtt_client);
|
||||
int ping_ret_val;
|
||||
ESP_ERROR_CHECK(esp_console_run("ping www.espressif.com", &ping_ret_val));
|
||||
ESP_LOGI(TAG, "Ping command finished with return value: %d", ping_ret_val);
|
||||
|
||||
#if CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL
|
||||
xEventGroupWaitBits(event_group, GOT_DATA_BIT, pdTRUE, pdFALSE, portMAX_DELAY);
|
||||
esp_modem_pause_net(dce, true);
|
||||
err = esp_modem_get_signal_quality(dce, &rssi, &ber);
|
||||
if (err != ESP_OK) {
|
||||
@@ -357,14 +318,15 @@ void app_main(void)
|
||||
}
|
||||
ESP_LOGI(TAG, "Signal quality: rssi=%d, ber=%d", rssi, ber);
|
||||
esp_modem_pause_net(dce, false);
|
||||
esp_mqtt_client_publish(mqtt_client, CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA, 0, 0, 0);
|
||||
ESP_ERROR_CHECK(esp_console_run("ping www.espressif.com", &ping_ret_val));
|
||||
ESP_LOGI(TAG, "Ping command finished with return value: %d", ping_ret_val);
|
||||
#endif // CONFIG_EXAMPLE_PAUSE_NETIF_TO_CHECK_SIGNAL
|
||||
|
||||
ESP_LOGI(TAG, "Waiting for MQTT data");
|
||||
xEventGroupWaitBits(event_group, GOT_DATA_BIT | USB_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
|
||||
if (ping_ret_val != 0) {
|
||||
ESP_LOGE(TAG, "Ping command failed with return value: %d", ping_ret_val);
|
||||
}
|
||||
CHECK_USB_DISCONNECTION(event_group);
|
||||
|
||||
esp_mqtt_client_destroy(mqtt_client);
|
||||
err = esp_modem_set_mode(dce, ESP_MODEM_MODE_COMMAND);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_modem_set_mode(ESP_MODEM_MODE_COMMAND) failed with %d", err);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
@@ -8,16 +8,12 @@ def test_pppos_connect(dut):
|
||||
steps:
|
||||
1. initializes connection with SIM800
|
||||
2. checks we get an IP
|
||||
3. checks for the MQTT events
|
||||
3. checks that the ping command works
|
||||
4. checks that the client cleanly disconnects
|
||||
"""
|
||||
# Check the sequence of connecting, publishing, disconnecting
|
||||
dut.expect('Modem Connect to PPP Server', timeout=90)
|
||||
# Check for MQTT connection and the data event
|
||||
dut.expect('MQTT_EVENT_CONNECTED')
|
||||
dut.expect('MQTT_EVENT_DATA')
|
||||
dut.expect('TOPIC=/ci/esp-modem/pppos-client')
|
||||
dut.expect('DATA=esp32-pppos')
|
||||
dut.expect('Ping command finished with return value: 0', timeout=30)
|
||||
# Check that we have disconnected
|
||||
dut.expect('User interrupted event')
|
||||
# And can use commands again
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
idf_component_register(SRCS "simple_cmux_client_main.cpp" "simple_mqtt_client.cpp"
|
||||
idf_component_register(SRCS "simple_cmux_client_main.cpp"
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.1"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
console_cmd_ping:
|
||||
version: '*'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -21,11 +21,13 @@
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "simple_mqtt_client.hpp"
|
||||
#include "esp_vfs_dev.h" // For optional VFS support
|
||||
#include "esp_https_ota.h" // For potential OTA configuration
|
||||
#include "vfs_resource/vfs_create.hpp"
|
||||
#include "SIM7070_gnss.hpp"
|
||||
#include "esp_console.h"
|
||||
#include "console_ping.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
#if defined(CONFIG_EXAMPLE_FLOW_CONTROL_NONE)
|
||||
#define EXAMPLE_FLOW_CONTROL ESP_MODEM_FLOW_CONTROL_NONE
|
||||
@@ -35,18 +37,13 @@
|
||||
#define EXAMPLE_FLOW_CONTROL ESP_MODEM_FLOW_CONTROL_HW
|
||||
#endif
|
||||
|
||||
#define BROKER_URL CONFIG_BROKER_URI
|
||||
|
||||
|
||||
using namespace esp_modem;
|
||||
|
||||
static const char *TAG = "cmux_example";
|
||||
|
||||
class StatusHandler {
|
||||
public:
|
||||
static constexpr auto IP_Event = SignalGroup::bit0;
|
||||
static constexpr auto MQTT_Connect = SignalGroup::bit1;
|
||||
static constexpr auto MQTT_Data = SignalGroup::bit2;
|
||||
static constexpr auto IP_Event = SignalGroup::bit0;
|
||||
|
||||
StatusHandler()
|
||||
{
|
||||
@@ -58,12 +55,6 @@ public:
|
||||
esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, on_event);
|
||||
}
|
||||
|
||||
void handle_mqtt(MqttClient *client)
|
||||
{
|
||||
mqtt_client = client;
|
||||
client->register_handler(ESP_EVENT_ANY_ID, on_event, this);
|
||||
}
|
||||
|
||||
esp_err_t wait_for(decltype(IP_Event) event, int milliseconds)
|
||||
{
|
||||
return signal.wait_any(event, milliseconds);
|
||||
@@ -80,8 +71,6 @@ private:
|
||||
auto *handler = static_cast<StatusHandler *>(arg);
|
||||
if (base == IP_EVENT) {
|
||||
handler->ip_event(event, data);
|
||||
} else {
|
||||
handler->mqtt_event(event, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,27 +81,16 @@ private:
|
||||
ESP_LOGI(TAG, "IP : " IPSTR, IP2STR(&event->ip_info.ip));
|
||||
ESP_LOGI(TAG, "Netmask : " IPSTR, IP2STR(&event->ip_info.netmask));
|
||||
ESP_LOGI(TAG, "Gateway : " IPSTR, IP2STR(&event->ip_info.gw));
|
||||
ip_event_type = static_cast<ip_event_t>(id);
|
||||
signal.set(IP_Event);
|
||||
} else if (id == IP_EVENT_PPP_LOST_IP) {
|
||||
ip_event_type = static_cast<ip_event_t>(id);
|
||||
signal.set(IP_Event);
|
||||
}
|
||||
ip_event_type = static_cast<ip_event_t>(id);
|
||||
}
|
||||
|
||||
void mqtt_event(int32_t event, void *data)
|
||||
{
|
||||
if (mqtt_client && event == mqtt_client->get_event(MqttClient::Event::CONNECT)) {
|
||||
signal.set(MQTT_Connect);
|
||||
} else if (mqtt_client && event == mqtt_client->get_event(MqttClient::Event::DATA)) {
|
||||
ESP_LOGI(TAG, " TOPIC: %s", mqtt_client->get_topic(data).c_str());
|
||||
ESP_LOGI(TAG, " DATA: %s", mqtt_client->get_data(data).c_str());
|
||||
signal.set(MQTT_Data);
|
||||
}
|
||||
}
|
||||
|
||||
esp_modem::SignalGroup signal{};
|
||||
MqttClient *mqtt_client{nullptr};
|
||||
ip_event_t ip_event_type;
|
||||
ip_event_t ip_event_type{};
|
||||
};
|
||||
|
||||
|
||||
@@ -122,6 +100,11 @@ extern "C" void app_main(void)
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
// Initialize console REPL, register ping and start it
|
||||
ESP_ERROR_CHECK(console_cmd_init());
|
||||
ESP_ERROR_CHECK(console_cmd_ping_register());
|
||||
ESP_ERROR_CHECK(console_cmd_start());
|
||||
|
||||
/* Configure and create the DTE */
|
||||
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||
/* setup UART specific configuration based on kconfig options */
|
||||
@@ -175,7 +158,7 @@ extern "C" void app_main(void)
|
||||
#endif
|
||||
assert(dce);
|
||||
|
||||
/* Try to connect to the network and publish an mqtt topic */
|
||||
/* Try to connect to the network */
|
||||
StatusHandler handler;
|
||||
|
||||
if (dte_config.uart_config.flow_control == ESP_MODEM_FLOW_CONTROL_HW) {
|
||||
@@ -224,24 +207,14 @@ extern "C" void app_main(void)
|
||||
} else if (handler.get_ip_event_type() == IP_EVENT_PPP_GOT_IP) {
|
||||
std::cout << "Got IP address" << std::endl;
|
||||
|
||||
/* When connected to network, subscribe and publish some MQTT data */
|
||||
MqttClient mqtt(BROKER_URL);
|
||||
handler.handle_mqtt(&mqtt);
|
||||
mqtt.connect();
|
||||
if (!handler.wait_for(StatusHandler::MQTT_Connect, 60000)) {
|
||||
ESP_LOGE(TAG, "Cannot connect to %s within specified timeout... exiting", BROKER_URL);
|
||||
/* When connected to network, we can ping the internet */
|
||||
int ping_ret_val;
|
||||
ESP_ERROR_CHECK(esp_console_run("ping www.espressif.com", &ping_ret_val));
|
||||
ESP_LOGI(TAG, "Ping command finished with return value: %d", ping_ret_val);
|
||||
if (ping_ret_val != 0) {
|
||||
ESP_LOGE(TAG, "Ping command failed with return value: %d", ping_ret_val);
|
||||
return;
|
||||
}
|
||||
std::cout << "Connected" << std::endl;
|
||||
|
||||
mqtt.subscribe(CONFIG_EXAMPLE_MQTT_TEST_TOPIC);
|
||||
mqtt.publish(CONFIG_EXAMPLE_MQTT_TEST_TOPIC, CONFIG_EXAMPLE_MQTT_TEST_DATA);
|
||||
if (!handler.wait_for(StatusHandler::MQTT_Data, 60000)) {
|
||||
ESP_LOGE(TAG, "Didn't receive published data within specified timeout... exiting");
|
||||
return;
|
||||
}
|
||||
std::cout << "Received MQTT data" << std::endl;
|
||||
|
||||
} else if (handler.get_ip_event_type() == IP_EVENT_PPP_LOST_IP) {
|
||||
ESP_LOGE(TAG, "PPP client has lost connection... exiting");
|
||||
return;
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* PPPoS Client Example
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include "mqtt_client.h"
|
||||
#include "simple_mqtt_client.hpp"
|
||||
|
||||
/**
|
||||
* Reference to the MQTT event base
|
||||
*/
|
||||
ESP_EVENT_DECLARE_BASE(MQTT_EVENTS);
|
||||
|
||||
/**
|
||||
* Thin wrapper around C mqtt_client
|
||||
*/
|
||||
struct MqttClientHandle {
|
||||
explicit MqttClientHandle(const std::string &uri)
|
||||
{
|
||||
esp_mqtt_client_config_t config = { };
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
config.broker.address.uri = uri.c_str();
|
||||
#else
|
||||
config.uri = uri.c_str();
|
||||
#endif
|
||||
client = esp_mqtt_client_init(&config);
|
||||
}
|
||||
|
||||
~MqttClientHandle()
|
||||
{
|
||||
esp_mqtt_client_destroy(client);
|
||||
}
|
||||
|
||||
esp_mqtt_client_handle_t client;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Definitions of MqttClient methods
|
||||
*/
|
||||
MqttClient::MqttClient(const std::string &uri):
|
||||
h(std::unique_ptr<MqttClientHandle>(new MqttClientHandle(uri)))
|
||||
{}
|
||||
|
||||
void MqttClient::connect()
|
||||
{
|
||||
esp_mqtt_client_start(h->client);
|
||||
}
|
||||
|
||||
int32_t MqttClient::get_event(MqttClient::Event ev)
|
||||
{
|
||||
switch (ev) {
|
||||
case Event::CONNECT: {
|
||||
return MQTT_EVENT_CONNECTED;
|
||||
}
|
||||
case Event::DATA:
|
||||
return MQTT_EVENT_DATA;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int MqttClient::publish(const std::string &topic, const std::string &data, int qos)
|
||||
{
|
||||
return esp_mqtt_client_publish(h->client, topic.c_str(), data.c_str(), 0, qos, 0);
|
||||
}
|
||||
|
||||
int MqttClient::subscribe(const std::string &topic, int qos)
|
||||
{
|
||||
return esp_mqtt_client_subscribe(h->client, topic.c_str(), qos);
|
||||
}
|
||||
|
||||
std::string MqttClient::get_topic(void *event_data)
|
||||
{
|
||||
auto event = (esp_mqtt_event_handle_t)event_data;
|
||||
if (event == nullptr || event->client != h->client)
|
||||
return {};
|
||||
|
||||
return std::string(event->topic, event->topic_len);
|
||||
}
|
||||
|
||||
std::string MqttClient::get_data(void *event_data)
|
||||
{
|
||||
auto event = (esp_mqtt_event_handle_t)event_data;
|
||||
if (event == nullptr || event->client != h->client)
|
||||
return {};
|
||||
|
||||
return std::string(event->data, event->data_len);
|
||||
}
|
||||
|
||||
void MqttClient::register_handler(int32_t event_id, esp_event_handler_t event_handler, void *arg)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_mqtt_client_register_event(h->client, MQTT_EVENT_ANY, event_handler, arg));
|
||||
}
|
||||
|
||||
MqttClient::~MqttClient() = default;
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* PPPoS Client Example
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
struct MqttClientHandle;
|
||||
|
||||
/**
|
||||
* @brief Simple MQTT client wrapper
|
||||
*/
|
||||
class MqttClient {
|
||||
public:
|
||||
enum class Event {
|
||||
CONNECT,
|
||||
DATA,
|
||||
};
|
||||
|
||||
explicit MqttClient(const std::string &uri);
|
||||
~MqttClient();
|
||||
|
||||
/**
|
||||
* @brief Start the mqtt-client
|
||||
*/
|
||||
void connect();
|
||||
|
||||
/**
|
||||
* @brief Publish to topic
|
||||
* @param topic Topic to publish
|
||||
* @param data Data to publish
|
||||
* @param qos QoS (0 by default)
|
||||
* @return message id
|
||||
*/
|
||||
int publish(const std::string &topic, const std::string &data, int qos = 0);
|
||||
|
||||
/**
|
||||
* @brief Subscribe to a topic
|
||||
* @param topic Topic to subscribe
|
||||
* @param qos QoS (0 by default)
|
||||
* @return message id
|
||||
*/
|
||||
int subscribe(const std::string &topic, int qos = 0);
|
||||
|
||||
/**
|
||||
* @brief Get topic from event data
|
||||
* @return String topic
|
||||
*/
|
||||
std::string get_topic(void *);
|
||||
|
||||
/**
|
||||
* @brief Get published data from event
|
||||
* @return String representation of the data
|
||||
*/
|
||||
std::string get_data(void *);
|
||||
|
||||
/**
|
||||
* @brief Register MQTT event
|
||||
* @param id Event id
|
||||
* @param event_handler Event handler
|
||||
* @param event_handler_arg Event handler parameters
|
||||
*/
|
||||
void register_handler(int32_t id, esp_event_handler_t event_handler, void *event_handler_arg);
|
||||
|
||||
/**
|
||||
* @brief Convert internal MQTT event to standard ESPEvent
|
||||
* @param ev internal mqtt event
|
||||
* @return corresponding esp_event id
|
||||
*/
|
||||
static int32_t get_event(Event ev);
|
||||
|
||||
private:
|
||||
std::unique_ptr<MqttClientHandle> h;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
@@ -6,21 +6,10 @@ from __future__ import print_function, unicode_literals
|
||||
def test_cmux_connection(dut):
|
||||
"""
|
||||
steps:
|
||||
1. initializes connection with SIM800
|
||||
2. checks we get an IP
|
||||
3. checks for the MQTT events
|
||||
1. checks we're in CMUX mode and get an IP
|
||||
2. checks for ping command
|
||||
"""
|
||||
# Get topic and data from Kconfig
|
||||
topic = ''
|
||||
data = ''
|
||||
try:
|
||||
topic = dut.app.sdkconfig.get('EXAMPLE_MQTT_TEST_TOPIC')
|
||||
data = dut.app.sdkconfig.get('EXAMPLE_MQTT_TEST_DATA')
|
||||
except Exception:
|
||||
print('ENV_TEST_FAILURE: Cannot find broker url in sdkconfig')
|
||||
raise
|
||||
# Check the sequence of connecting, publishing, disconnecting
|
||||
dut.expect('Modem has correctly entered multiplexed')
|
||||
# Check for MQTT connection and the data event
|
||||
dut.expect(f'TOPIC: {topic}')
|
||||
dut.expect(f'DATA: {data}')
|
||||
# Check we're in CMUX mode and get an IP
|
||||
dut.expect('Modem has correctly entered multiplexed command/data mode', timeout=60)
|
||||
# Check for ping command
|
||||
dut.expect('Ping command finished with return value: 0', timeout=30)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: "1.4.0"
|
||||
version: "2.0.0"
|
||||
description: Library for communicating with cellular modems in command and data modes
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/esp_modem
|
||||
issues: https://github.com/espressif/esp-protocols/issues
|
||||
|
||||
@@ -103,6 +103,11 @@ public:
|
||||
{
|
||||
dte->set_urc_cb(on_read_cb);
|
||||
}
|
||||
|
||||
void set_enhanced_urc(esp_modem::DTE::enhanced_urc_cb enhanced_cb)
|
||||
{
|
||||
dte->set_enhanced_urc_cb(enhanced_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -115,6 +115,42 @@ public:
|
||||
{
|
||||
command_cb.urc_handler = std::move(line_cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enhanced URC handler with buffer consumption control
|
||||
* @param buffer_info Information about the current buffer state
|
||||
* @return Information about how much of the buffer to consume
|
||||
*/
|
||||
struct UrcBufferInfo {
|
||||
const uint8_t* buffer_start; // Start of entire buffer
|
||||
size_t buffer_total_size; // Total buffer size
|
||||
size_t processed_offset; // Offset of already processed data
|
||||
size_t new_data_size; // Size of new data since last call
|
||||
const uint8_t* new_data_start; // Pointer to start of new data
|
||||
bool is_command_active; // Whether a command is currently waiting for response
|
||||
};
|
||||
|
||||
enum class UrcConsumeResult {
|
||||
CONSUME_NONE, // Don't consume anything, continue waiting
|
||||
CONSUME_PARTIAL, // Consume only part of the buffer
|
||||
CONSUME_ALL // Consume entire buffer
|
||||
};
|
||||
|
||||
struct UrcConsumeInfo {
|
||||
UrcConsumeResult result;
|
||||
size_t consume_size; // How many bytes to consume (0 = none, SIZE_MAX = all)
|
||||
};
|
||||
|
||||
typedef std::function<UrcConsumeInfo(const UrcBufferInfo &)> enhanced_urc_cb;
|
||||
|
||||
/**
|
||||
* @brief Set enhanced URC callback with buffer consumption control
|
||||
* @param enhanced_cb Enhanced callback that can control buffer consumption
|
||||
*/
|
||||
void set_enhanced_urc_cb(enhanced_urc_cb enhanced_cb)
|
||||
{
|
||||
command_cb.enhanced_urc_handler = std::move(enhanced_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -171,6 +207,33 @@ private:
|
||||
[[nodiscard]] bool exit_cmux(); /*!< Exit of CMUX mode and cleanup */
|
||||
void exit_cmux_internal(); /*!< Cleanup CMUX */
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
/**
|
||||
* @brief Buffer state tracking for enhanced URC processing
|
||||
*/
|
||||
struct BufferState {
|
||||
size_t total_processed = 0; /*!< Total bytes processed by URC handlers */
|
||||
size_t last_urc_processed = 0; /*!< Last offset processed by URC */
|
||||
bool command_waiting = false; /*!< Whether command is waiting for response */
|
||||
size_t command_start_offset = 0; /*!< Where current command response started */
|
||||
} buffer_state;
|
||||
|
||||
/**
|
||||
* @brief Update buffer state when new data arrives
|
||||
* @param new_data_size Size of new data added to buffer
|
||||
*/
|
||||
void update_buffer_state(size_t new_data_size);
|
||||
|
||||
/**
|
||||
* @brief Create URC buffer information for enhanced handlers
|
||||
* @param data Buffer data pointer
|
||||
* @param consumed Already consumed bytes
|
||||
* @param len New data length
|
||||
* @return UrcBufferInfo structure with complete buffer context
|
||||
*/
|
||||
UrcBufferInfo create_urc_info(uint8_t* data, size_t consumed, size_t len);
|
||||
#endif
|
||||
|
||||
Lock internal_lock{}; /*!< Locks DTE operations */
|
||||
unique_buffer buffer; /*!< DTE buffer */
|
||||
std::shared_ptr<CMux> cmux_term; /*!< Primary terminal for this DTE */
|
||||
@@ -216,6 +279,7 @@ private:
|
||||
struct command_cb {
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
got_line_cb urc_handler {}; /*!< URC callback if enabled */
|
||||
enhanced_urc_cb enhanced_urc_handler {}; /*!< Enhanced URC callback with consumption control */
|
||||
#endif
|
||||
static const size_t GOT_LINE = SignalGroup::bit0; /*!< Bit indicating response available */
|
||||
got_line_cb got_line; /*!< Supplied command callback */
|
||||
@@ -223,7 +287,7 @@ private:
|
||||
char separator{}; /*!< Command reply separator (end of line/processing unit) */
|
||||
command_result result{}; /*!< Command return code */
|
||||
SignalGroup signal; /*!< Event group used to signal request-response operations */
|
||||
bool process_line(uint8_t *data, size_t consumed, size_t len); /*!< Lets the processing callback handle one line (processing unit) */
|
||||
bool process_line(uint8_t *data, size_t consumed, size_t len, DTE* dte = nullptr); /*!< Lets the processing callback handle one line (processing unit) */
|
||||
bool wait_for_line(uint32_t time_ms) /*!< Waiting for command processing */
|
||||
{
|
||||
return signal.wait_any(command_cb::GOT_LINE, time_ms);
|
||||
|
||||
@@ -163,6 +163,15 @@ extern "C" esp_err_t esp_modem_read_pin(esp_modem_dce_t *dce_wrap, bool *pin)
|
||||
return command_response_to_esp_err(dce_wrap->dce->read_pin(*pin));
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_set_echo(esp_modem_dce_t *dce_wrap, const bool echo_on)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return command_response_to_esp_err(dce_wrap->dce->set_echo(echo_on));
|
||||
}
|
||||
|
||||
extern "C" esp_err_t esp_modem_sms_txt_mode(esp_modem_dce_t *dce_wrap, bool txt)
|
||||
{
|
||||
if (dce_wrap == nullptr || dce_wrap->dce == nullptr) {
|
||||
|
||||
@@ -65,6 +65,10 @@ void DTE::set_command_callbacks()
|
||||
{
|
||||
primary_term->set_read_cb([this](uint8_t *data, size_t len) {
|
||||
Scoped<Lock> l(command_cb.line_lock);
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
// Update buffer state when new data arrives
|
||||
update_buffer_state(len);
|
||||
#endif
|
||||
#ifndef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
if (command_cb.got_line == nullptr || command_cb.result != command_result::TIMEOUT) {
|
||||
return false; // this line has been processed already (got OK or FAIL previously)
|
||||
@@ -80,7 +84,7 @@ void DTE::set_command_callbacks()
|
||||
std::memcpy(inflatable.current(), data, len);
|
||||
data = inflatable.begin();
|
||||
}
|
||||
if (command_cb.process_line(data, inflatable.consumed, len)) {
|
||||
if (command_cb.process_line(data, inflatable.consumed, len, this)) {
|
||||
return true;
|
||||
}
|
||||
// at this point we're sure that the data processing hasn't finished,
|
||||
@@ -92,7 +96,7 @@ void DTE::set_command_callbacks()
|
||||
inflatable.consumed += len;
|
||||
return false;
|
||||
#else
|
||||
if (command_cb.process_line(data, 0, len)) {
|
||||
if (command_cb.process_line(data, 0, len, this)) {
|
||||
return true;
|
||||
}
|
||||
// cannot inflate and the processing hasn't finishes in the first iteration, but continue
|
||||
@@ -105,7 +109,7 @@ void DTE::set_command_callbacks()
|
||||
if (buffer.size > buffer.consumed) {
|
||||
data = buffer.get();
|
||||
len = primary_term->read(data + buffer.consumed, buffer.size - buffer.consumed);
|
||||
if (command_cb.process_line(data, buffer.consumed, len)) {
|
||||
if (command_cb.process_line(data, buffer.consumed, len, this)) {
|
||||
return true;
|
||||
}
|
||||
buffer.consumed += len;
|
||||
@@ -121,7 +125,7 @@ void DTE::set_command_callbacks()
|
||||
inflatable.grow(inflatable.consumed + len);
|
||||
}
|
||||
len = primary_term->read(inflatable.current(), len);
|
||||
if (command_cb.process_line(inflatable.begin(), inflatable.consumed, len)) {
|
||||
if (command_cb.process_line(inflatable.begin(), inflatable.consumed, len, this)) {
|
||||
return true;
|
||||
}
|
||||
inflatable.consumed += len;
|
||||
@@ -150,10 +154,19 @@ void DTE::set_command_callbacks()
|
||||
command_result DTE::command(const std::string &command, got_line_cb got_line, uint32_t time_ms, const char separator)
|
||||
{
|
||||
Scoped<Lock> l1(internal_lock);
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
// Track command start
|
||||
buffer_state.command_waiting = true;
|
||||
buffer_state.command_start_offset = buffer_state.total_processed;
|
||||
#endif
|
||||
command_cb.set(got_line, separator);
|
||||
primary_term->write((uint8_t *)command.c_str(), command.length());
|
||||
command_cb.wait_for_line(time_ms);
|
||||
command_cb.set(nullptr);
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
// Track command end
|
||||
buffer_state.command_waiting = false;
|
||||
#endif
|
||||
buffer.consumed = 0;
|
||||
#ifdef CONFIG_ESP_MODEM_USE_INFLATABLE_BUFFER_IF_NEEDED
|
||||
inflatable.deflate();
|
||||
@@ -365,18 +378,54 @@ void DTE::on_read(got_line_cb on_read_cb)
|
||||
});
|
||||
}
|
||||
|
||||
bool DTE::command_cb::process_line(uint8_t *data, size_t consumed, size_t len)
|
||||
bool DTE::command_cb::process_line(uint8_t *data, size_t consumed, size_t len, DTE* dte)
|
||||
{
|
||||
// returning true indicates that the processing finished and lower layers can destroy the accumulated buffer
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
bool consume_buffer = false;
|
||||
if (urc_handler) {
|
||||
consume_buffer = urc_handler(data, consumed + len) != command_result::TIMEOUT;
|
||||
// Call enhanced URC handler if registered
|
||||
if (enhanced_urc_handler && dte) {
|
||||
// Create buffer info for enhanced URC handler
|
||||
UrcBufferInfo buffer_info = dte->create_urc_info(data, consumed, len);
|
||||
|
||||
// Call enhanced URC handler
|
||||
UrcConsumeInfo consume_info = enhanced_urc_handler(buffer_info);
|
||||
|
||||
// Handle consumption control
|
||||
switch (consume_info.result) {
|
||||
case UrcConsumeResult::CONSUME_NONE:
|
||||
// Don't consume anything, continue with command processing
|
||||
break;
|
||||
|
||||
case UrcConsumeResult::CONSUME_PARTIAL:
|
||||
// Consume only specified amount
|
||||
dte->buffer_state.last_urc_processed += consume_info.consume_size;
|
||||
// Adjust data pointers for command processing
|
||||
data += consume_info.consume_size;
|
||||
consumed = (consumed + len) - consume_info.consume_size;
|
||||
len = 0;
|
||||
break;
|
||||
|
||||
case UrcConsumeResult::CONSUME_ALL:
|
||||
// Consume entire buffer
|
||||
dte->buffer_state.last_urc_processed = consumed + len;
|
||||
return true; // Signal buffer consumption
|
||||
}
|
||||
}
|
||||
if (result != command_result::TIMEOUT || got_line == nullptr) {
|
||||
return consume_buffer; // this line has been processed already (got OK or FAIL previously)
|
||||
|
||||
// Fallback to legacy URC handler if enhanced handler not set
|
||||
if (urc_handler) {
|
||||
bool consume_buffer = urc_handler(data, consumed + len) != command_result::TIMEOUT;
|
||||
if (result != command_result::TIMEOUT || got_line == nullptr) {
|
||||
return consume_buffer; // this line has been processed already (got OK or FAIL previously)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Continue with normal command processing
|
||||
if (result != command_result::TIMEOUT || got_line == nullptr) {
|
||||
return false; // Command processing continues
|
||||
}
|
||||
|
||||
if (memchr(data + consumed, separator, len)) {
|
||||
result = got_line(data, consumed + len);
|
||||
if (result == command_result::OK || result == command_result::FAIL) {
|
||||
@@ -423,3 +472,22 @@ void DTE::extra_buffer::grow(size_t need_size)
|
||||
*/
|
||||
unique_buffer::unique_buffer(size_t size):
|
||||
data(std::make_unique<uint8_t[]>(size)), size(size), consumed(0) {}
|
||||
|
||||
#ifdef CONFIG_ESP_MODEM_URC_HANDLER
|
||||
void DTE::update_buffer_state(size_t new_data_size)
|
||||
{
|
||||
buffer_state.total_processed += new_data_size;
|
||||
}
|
||||
|
||||
DTE::UrcBufferInfo DTE::create_urc_info(uint8_t* data, size_t consumed, size_t len)
|
||||
{
|
||||
return {
|
||||
.buffer_start = data,
|
||||
.buffer_total_size = consumed + len,
|
||||
.processed_offset = buffer_state.last_urc_processed,
|
||||
.new_data_size = (consumed + len) - buffer_state.last_urc_processed,
|
||||
.new_data_start = data + buffer_state.last_urc_processed,
|
||||
.is_command_active = buffer_state.command_waiting
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -223,9 +223,9 @@ static int ppp_cmd_iperf(int argc, char **argv)
|
||||
cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp",
|
||||
cfg.flag & IPERF_FLAG_SERVER ? "server" : "client",
|
||||
(uint16_t) cfg.source_ip4 & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 8) & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 16) & 0xFF,
|
||||
(uint16_t) (cfg.source_ip4 >> 24) & 0xFF,
|
||||
(uint16_t)(cfg.source_ip4 >> 8) & 0xFF,
|
||||
(uint16_t)(cfg.source_ip4 >> 16) & 0xFF,
|
||||
(uint16_t)(cfg.source_ip4 >> 24) & 0xFF,
|
||||
cfg.sport,
|
||||
cfg.destination_ip4 & 0xFF, (cfg.destination_ip4 >> 8) & 0xFF,
|
||||
(cfg.destination_ip4 >> 16) & 0xFF, (cfg.destination_ip4 >> 24) & 0xFF, cfg.dport,
|
||||
@@ -234,6 +234,18 @@ static int ppp_cmd_iperf(int argc, char **argv)
|
||||
iperf_start(&cfg);
|
||||
return 0;
|
||||
}
|
||||
static int restart(int argc, char **argv)
|
||||
{
|
||||
ESP_LOGI("main", "Restarting");
|
||||
esp_restart();
|
||||
return 0;
|
||||
}
|
||||
static int heap_size(int argc, char **argv)
|
||||
{
|
||||
uint32_t heap_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
|
||||
ESP_LOGI(TAG, "min heap size: %" PRIu32, heap_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_pppd(void)
|
||||
{
|
||||
@@ -286,4 +298,25 @@ void register_pppd(void)
|
||||
.argtable = &iperf_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&iperf_cmd));
|
||||
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "restart",
|
||||
.help = "Restart the program",
|
||||
.hint = NULL,
|
||||
.func = &restart,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||
|
||||
}
|
||||
{
|
||||
const esp_console_cmd_t heap_cmd = {
|
||||
.command = "heap",
|
||||
.help = "Get minimum size of free heap memory that was available during program execution",
|
||||
.hint = NULL,
|
||||
.func = &heap_size,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&heap_cmd));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: "^1.1.0"
|
||||
version: "^2"
|
||||
override_path: "../../.."
|
||||
espressif/iperf-cmd: "^0.1.1"
|
||||
|
||||
@@ -2,7 +2,5 @@
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../.." "../../../mbedtls_cxx")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ota_test)
|
||||
|
||||
14
components/esp_modem/test/target_ota/main/idf_component.yml
Normal file
14
components/esp_modem/test/target_ota/main/idf_component.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_modem:
|
||||
version: '^2'
|
||||
override_path: ../../..
|
||||
espressif/mbedtls_cxx:
|
||||
version: '*'
|
||||
override_path: ../../../../mbedtls_cxx
|
||||
idf:
|
||||
version: '>=4.1.0'
|
||||
espressif/mqtt:
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
@@ -1,4 +1,4 @@
|
||||
CONFIG_TEST_DEVICE_MODEM_GENERIC=y
|
||||
CONFIG_TEST_OTA_URI="https://raw.githubusercontent.com/espressif/esp-protocols/master/components/esp_modem/test/target_ota/bin/blink.bin"
|
||||
CONFIG_TEST_OTA_CA_CERT="MIIEvjCCA6agAwIBAgIQBtjZBNVYQ0b2ii+nVCJ+xDANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaME8xCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxKTAnBgNVBAMTIERpZ2lDZXJ0IFRMUyBSU0EgU0hBMjU2IDIwMjAgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwUuzZUdwvN1PWNvsnO3DZuUfMRNUrUpmRh8sCuxkB+Uu3Ny5CiDt3+PE0J6aqXodgojlEVbbHp9YwlHnLDQNLtKS4VbL8Xlfs7uHyiUDe5pSQWYQYE9XE0nw6Ddng9/n00tnTCJRpt8OmRDtV1F0JuJ9x8piLhMbfyOIJVNvwTRYAIuE//i+p1hJInuWraKImxW8oHzf6VGo1bDtN+I2tIJLYrVJmuzHZ9bjPvXj1hJeRPG/cUJ9WIQDgLGBAfr5yjK7tI4nhyfFK3TUqNaX3sNk+crOU6JWvHgXjkkDKa77SU+kFbnO8lwZV21reacroicgE7XQPUDTITAHk+qZ9QIDAQABo4IBgjCCAX4wEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUt2ui6qiqhIx56rTaD5iyxZV2ufQwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNydDBCBgNVHR8EOzA5MDegNaAzhjFodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3JsMD0GA1UdIAQ2MDQwCwYJYIZIAYb9bAIBMAcGBWeBDAEBMAgGBmeBDAECATAIBgZngQwBAgIwCAYGZ4EMAQIDMA0GCSqGSIb3DQEBCwUAA4IBAQCAMs5eC91uWg0Kr+HWhMvAjvqFcO3aXbMM9yt1QP6FCvrzMXi3cEsaiVi6gL3zax3pfs8LulicWdSQ0/1s/dCYbbdxglvPbQtaCdB73sRD2Cqk3p5BJl+7j5nL3a7hqG+fh/50tx8bIKuxT8b1Z11dmzzp/2n3YWzW2fP9NsarA4h20ksudYbj/NhVfSbCEXffPgK2fPOre3qGNm+499iTcc+G33Mw+nur7SpZyEKEOxEXGlLzyQ4UfaJbcme6ce1XR2bFuAJKZTRei9AqPCCcUZlM51Ke92sRKw2Sfh3oius2FkOH6ipjv3U/697EA7sKPPcw7+uvTPyLNhBzPvOk"
|
||||
CONFIG_TEST_OTA_CA_CERT="MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTflMrY="
|
||||
CONFIG_TEST_OTA_CN="github.com"
|
||||
|
||||
152
components/esp_modem/test/target_urc/README.md
Normal file
152
components/esp_modem/test/target_urc/README.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# ESP Modem Enhanced URC Test
|
||||
|
||||
## Overview
|
||||
|
||||
This test validates the enhanced URC (Unsolicited Result Code) interface with buffer consumption control. It demonstrates the new `set_enhanced_urc()` API that provides granular control over buffer consumption and complete buffer visibility.
|
||||
|
||||
## Test Configuration
|
||||
|
||||
- **Target**: ESP-AT device with HTTP server
|
||||
- **UART**: 115200 baud, 8N1, TX=17, RX=18
|
||||
- **Buffer Size**: 1024 bytes
|
||||
- **Timeout**: 15 seconds
|
||||
|
||||
## Test Cases
|
||||
|
||||
### 1. Enhanced URC Handler Registration
|
||||
- **Objective**: Verify enhanced URC handler can be registered
|
||||
- **Method**: Call `set_enhanced_urc()` with custom handler
|
||||
- **Expected**: Handler receives `UrcBufferInfo` with complete buffer context
|
||||
|
||||
### 2. Buffer Visibility
|
||||
- **Objective**: Verify URC handler receives complete buffer information
|
||||
- **Method**: Log buffer state information in handler
|
||||
- **Expected**: Handler receives `buffer_start`, `buffer_total_size`, `processed_offset`, `new_data_size`, `is_command_active`
|
||||
|
||||
### 3. Granular Consumption Control
|
||||
- **Objective**: Verify handler can consume partial buffer data
|
||||
- **Method**: Process HTTP URCs line-by-line using `CONSUME_PARTIAL`
|
||||
- **Expected**: Each complete line is consumed individually, remaining data preserved
|
||||
|
||||
### 4. Multi-part Response Handling
|
||||
- **Objective**: Verify handling of chunked HTTP responses
|
||||
- **Method**: Process 9 HTTP chunks from ESP-AT server
|
||||
- **Expected**: All chunks processed correctly with proper offset tracking
|
||||
|
||||
### 5. Transfer Completion Detection
|
||||
- **Objective**: Verify detection of transfer completion message
|
||||
- **Method**: Search for "Transfer completed" in buffer
|
||||
- **Expected**: Completion detected and event group signaled
|
||||
|
||||
### 6. Command State Awareness
|
||||
- **Objective**: Verify handler knows command state
|
||||
- **Method**: Check `is_command_active` flag during processing
|
||||
- **Expected**: Flag correctly reflects command state
|
||||
|
||||
## Test Implementation
|
||||
|
||||
The test uses an ESP-AT device running an HTTP server that sends chunked responses. The enhanced URC handler processes each HTTP URC line individually and detects completion.
|
||||
|
||||
### Key Components
|
||||
|
||||
```cpp
|
||||
// Enhanced URC handler registration
|
||||
set_enhanced_urc(handle_enhanced_urc);
|
||||
|
||||
// Handler implementation
|
||||
static esp_modem::DTE::UrcConsumeInfo handle_enhanced_urc(const esp_modem::DTE::UrcBufferInfo& info)
|
||||
{
|
||||
// Process HTTP URCs with granular consumption control
|
||||
if (line.starts_with("+HTTPCGET:")) {
|
||||
// Consume this line only
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_PARTIAL, line_end + 1};
|
||||
}
|
||||
|
||||
// Check for completion
|
||||
if (buffer.find("Transfer completed") != std::string_view::npos) {
|
||||
xEventGroupSetBits(s_event_group, transfer_completed);
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_ALL, 0};
|
||||
}
|
||||
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_NONE, 0};
|
||||
}
|
||||
```
|
||||
|
||||
## Example Output
|
||||
|
||||
### Successful Test Run
|
||||
```
|
||||
I (908) urc_test: Starting Enhanced URC Test
|
||||
I (938) urc_test: Start HTTP server...(0)
|
||||
I (948) urc_test: HTTP GET...(43)
|
||||
I (1228) urc_test: HTTP URC: +HTTPCGET:27,=== Async Response #4 ===
|
||||
I (2778) urc_test: HTTP URC: +HTTPCGET:61,[1/9] [633135 ms] This is a simulated slow server response.
|
||||
I (4288) urc_test: HTTP URC: +HTTPCGET:71,[2/9] [634639 ms] Chunk 1: The ESP-AT HTTP server is demonstrating...
|
||||
I (5788) urc_test: HTTP URC: +HTTPCGET:73,[3/9] [636143 ms] Chunk 2: ...asynchronous chunked transfer encoding...
|
||||
I (7288) urc_test: HTTP URC: +HTTPCGET:72,[4/9] [637647 ms] Chunk 3: ...with artificial delays between chunks...
|
||||
I (8788) urc_test: HTTP URC: +HTTPCGET:74,[5/9] [639151 ms] Chunk 4: ...to simulate real-world network conditions.
|
||||
I (10288) urc_test: HTTP URC: +HTTPCGET:62,[6/9] [640655 ms] Chunk 5: Processing data... please wait...
|
||||
I (11788) urc_test: HTTP URC: +HTTPCGET:63,[7/9] [642159 ms] Chunk 6: Still processing... almost done...
|
||||
I (13288) urc_test: HTTP URC: +HTTPCGET:61,[8/9] [643663 ms] Chunk 7: Final chunk - transfer complete!
|
||||
I (14758) urc_test: HTTP URC: +HTTPCGET:43,[9/9] [645168 ms] === END OF RESPONSE ===
|
||||
I (15258) urc_test: Transfer completed detected in buffer!
|
||||
I (15298) urc_test: Enhanced URC test completed successfully!
|
||||
I (15308) urc_test: The enhanced URC handler successfully processed all HTTP chunks
|
||||
I (15308) urc_test: with granular buffer consumption control
|
||||
```
|
||||
|
||||
### Debug Output (with ESP_LOG_LEVEL_DEBUG)
|
||||
```
|
||||
D (958) urc_test: URC Buffer Info: total_size=43, processed_offset=0, new_data_size=43, command_active=false
|
||||
D (958) urc_test: Buffer content (first 43 chars): AT+HTTPCGET="http://127.0.0.1:8080/async"
|
||||
D (968) urc_test: Other data: AT+HTTPCGET="http://127.0.0.1:8080/async"
|
||||
D (1218) urc_test: URC Buffer Info: total_size=85, processed_offset=43, new_data_size=42, command_active=false
|
||||
D (1228) urc_test: Consuming 40 bytes (line_end=82, processed_offset=43)
|
||||
D (2778) urc_test: Consuming 76 bytes (line_end=158, processed_offset=83)
|
||||
```
|
||||
|
||||
### Failed Test (Timeout)
|
||||
```
|
||||
I (908) urc_test: Starting Enhanced URC Test
|
||||
I (948) urc_test: HTTP GET...(43)
|
||||
E (15385) urc_test: Enhanced URC test timed out
|
||||
I (15385) urc_test: Enhanced URC test done
|
||||
```
|
||||
|
||||
## Test Validation
|
||||
|
||||
### Success Criteria
|
||||
- All 9 HTTP chunks processed successfully
|
||||
- Transfer completion detected within 15 seconds
|
||||
- No buffer arithmetic errors (no negative `new_data_size`)
|
||||
- Proper consumption control (line-by-line processing)
|
||||
|
||||
### Failure Indicators
|
||||
- Test timeout (15 seconds)
|
||||
- Buffer arithmetic errors (negative `new_data_size`)
|
||||
- Missing HTTP chunks
|
||||
- Transfer completion not detected
|
||||
|
||||
## Dependencies
|
||||
|
||||
- ESP-AT device with HTTP server capability
|
||||
- UART connection configured
|
||||
- `CONFIG_ESP_MODEM_URC_HANDLER=y`
|
||||
- FreeRTOS event groups
|
||||
|
||||
## Build and Run
|
||||
|
||||
```bash
|
||||
idf.py build
|
||||
idf.py flash monitor
|
||||
```
|
||||
|
||||
## Comparison with Legacy URC
|
||||
|
||||
| Feature | Legacy URC | Enhanced URC |
|
||||
|---------|------------|--------------|
|
||||
| Buffer Consumption | All or nothing | Granular (partial) |
|
||||
| Buffer Visibility | None | Complete context |
|
||||
| Command State | Unknown | Known (`is_command_active`) |
|
||||
| Processing State | Unknown | Tracked (`processed_offset`) |
|
||||
| Multi-part Support | Limited | Full support |
|
||||
@@ -3,5 +3,5 @@ dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=4.1.0"
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.0"
|
||||
version: "^2"
|
||||
override_path: "../../../"
|
||||
|
||||
@@ -3,7 +3,26 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file urc_test.cpp
|
||||
* @brief Enhanced URC (Unsolicited Result Code) Test
|
||||
*
|
||||
* This test demonstrates the new enhanced URC interface with buffer consumption control.
|
||||
* It tests the following features:
|
||||
*
|
||||
* 1. Enhanced URC Handler Registration: Uses set_enhanced_urc() instead of set_urc()
|
||||
* 2. Buffer Visibility: URC handler receives complete buffer information
|
||||
* 3. Granular Consumption Control: Handler can consume none, partial, or all buffer data
|
||||
* 4. Processing State Awareness: Handler knows what data is new vs. already processed
|
||||
* 5. Command State Awareness: Handler knows if a command is currently active
|
||||
*
|
||||
* The test works with ESP-AT HTTP server that sends chunked responses, demonstrating
|
||||
* how the enhanced URC handler can process multi-part responses with precise control
|
||||
* over buffer consumption.
|
||||
*/
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_netif.h"
|
||||
@@ -67,7 +86,7 @@ public:
|
||||
bool http_get(const std::string &url)
|
||||
{
|
||||
std::string command = "AT+HTTPCGET=\"" + url + "\"\r\n";
|
||||
set_urc(handle_urc);
|
||||
set_enhanced_urc(handle_enhanced_urc);
|
||||
auto ret = dte->write(esp_modem::DTE_Command(command));
|
||||
ESP_LOGI(TAG, "HTTP GET...(%d)", static_cast<int>(ret));
|
||||
return ret > 0;
|
||||
@@ -82,25 +101,88 @@ public:
|
||||
|
||||
static constexpr int transfer_completed = 1;
|
||||
private:
|
||||
static esp_modem::command_result handle_urc(uint8_t *data, size_t len)
|
||||
static esp_modem::DTE::UrcConsumeInfo handle_enhanced_urc(const esp_modem::DTE::UrcBufferInfo &info)
|
||||
{
|
||||
static int start_chunk = 0;
|
||||
static int end_chunk = 0;
|
||||
std::string_view chunk((const char*)data + start_chunk, len - start_chunk);
|
||||
int newline = chunk.find('\n');
|
||||
if (newline == std::string_view::npos) {
|
||||
end_chunk = len; // careful, this grows buffer usage
|
||||
printf(".");
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
// Log buffer information for debugging
|
||||
ESP_LOGD(TAG, "URC Buffer Info: total_size=%zu, processed_offset=%zu, new_data_size=%zu, command_active=%s",
|
||||
info.buffer_total_size, info.processed_offset, info.new_data_size,
|
||||
info.is_command_active ? "true" : "false");
|
||||
|
||||
// Debug: Show buffer content (first 200 chars)
|
||||
if (info.buffer_total_size > 0) {
|
||||
size_t debug_len = std::min(info.buffer_total_size, (size_t)200);
|
||||
ESP_LOGD(TAG, "Buffer content (first %zu chars): %.*s",
|
||||
debug_len, (int)debug_len, (const char*)info.buffer_start);
|
||||
}
|
||||
printf("%.*s\n", newline, (char*)data + start_chunk);
|
||||
start_chunk = end_chunk;
|
||||
// check for the last one
|
||||
constexpr char last_chunk[] = "Transfer completed";
|
||||
if (memmem(data, len, last_chunk, sizeof(last_chunk) - 1) != nullptr) {
|
||||
|
||||
// Create string view of the entire buffer for processing
|
||||
std::string_view buffer((const char*)info.buffer_start, info.buffer_total_size);
|
||||
|
||||
// First, check if we have the completion message anywhere in the buffer
|
||||
if (buffer.find("Transfer completed") != std::string_view::npos) {
|
||||
ESP_LOGI(TAG, "Transfer completed detected in buffer!");
|
||||
xEventGroupSetBits(s_event_group, transfer_completed);
|
||||
// Consume everything
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_ALL, 0};
|
||||
}
|
||||
return esp_modem::command_result::OK;
|
||||
|
||||
// Process from the last processed offset
|
||||
size_t search_start = info.processed_offset;
|
||||
|
||||
// Look for complete lines starting from the processed offset
|
||||
while (search_start < info.buffer_total_size) {
|
||||
size_t line_end = buffer.find('\n', search_start);
|
||||
|
||||
if (line_end == std::string_view::npos) {
|
||||
// No complete line found, wait for more data
|
||||
ESP_LOGD(TAG, "Waiting for more data... (search_start=%zu, total_size=%zu)",
|
||||
search_start, info.buffer_total_size);
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_NONE, 0};
|
||||
}
|
||||
|
||||
// Found a complete line, process it
|
||||
std::string_view line = buffer.substr(search_start, line_end - search_start);
|
||||
|
||||
// Remove carriage return if present
|
||||
if (!line.empty() && line.back() == '\r') {
|
||||
line.remove_suffix(1);
|
||||
}
|
||||
|
||||
// Check if this is an HTTP URC
|
||||
if (line.starts_with("+HTTPCGET:")) {
|
||||
ESP_LOGI(TAG, "HTTP URC: %.*s", (int)line.length(), line.data());
|
||||
|
||||
// Check for transfer completion - look for "Transfer completed" anywhere in the line
|
||||
if (line.find("Transfer completed") != std::string_view::npos) {
|
||||
ESP_LOGI(TAG, "Transfer completed detected!");
|
||||
xEventGroupSetBits(s_event_group, transfer_completed);
|
||||
}
|
||||
|
||||
// Consume this line (including the newline)
|
||||
size_t consume_size = line_end + 1 - info.processed_offset;
|
||||
ESP_LOGD(TAG, "Consuming %zu bytes (line_end=%zu, processed_offset=%zu)",
|
||||
consume_size, line_end, info.processed_offset);
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_PARTIAL, consume_size};
|
||||
|
||||
} else if (line.starts_with("+HTTPCGET")) {
|
||||
// Partial HTTP URC, don't consume yet
|
||||
ESP_LOGD(TAG, "Partial HTTP URC: %.*s", (int)line.length(), line.data());
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_NONE, 0};
|
||||
|
||||
} else if (!line.empty()) {
|
||||
// Other data, log and consume
|
||||
ESP_LOGD(TAG, "Other data: %.*s", (int)line.length(), line.data());
|
||||
size_t consume_size = line_end + 1 - info.processed_offset;
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_PARTIAL, consume_size};
|
||||
}
|
||||
|
||||
// Move to next line
|
||||
search_start = line_end + 1;
|
||||
}
|
||||
|
||||
// Processed all available data
|
||||
ESP_LOGD(TAG, "Processed all available data");
|
||||
return {esp_modem::DTE::UrcConsumeResult::CONSUME_NONE, 0};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -131,8 +213,8 @@ extern "C" void app_main(void)
|
||||
|
||||
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||
dte_config.dte_buffer_size = 1024;
|
||||
dte_config.uart_config.tx_io_num = 18;
|
||||
dte_config.uart_config.rx_io_num = 17;
|
||||
dte_config.uart_config.tx_io_num = 17;
|
||||
dte_config.uart_config.rx_io_num = 18;
|
||||
auto uart_dte = esp_modem::create_uart_dte(&dte_config);
|
||||
if (uart_dte == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to create UART DTE");
|
||||
@@ -144,15 +226,24 @@ extern "C" void app_main(void)
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Starting Enhanced URC Test");
|
||||
ESP_LOGI(TAG, "This test demonstrates the new enhanced URC interface with buffer consumption control");
|
||||
|
||||
dce->start_http_server();
|
||||
|
||||
ESP_LOGI(TAG, "Sending HTTP GET request with enhanced URC handler");
|
||||
dce->http_get("http://127.0.0.1:8080/async");
|
||||
|
||||
EventBits_t bits = xEventGroupWaitBits(s_event_group, 1, pdTRUE, pdFALSE, pdMS_TO_TICKS(15000));
|
||||
if (bits & DCE::transfer_completed) {
|
||||
ESP_LOGI(TAG, "Request finished!");
|
||||
ESP_LOGI(TAG, "Enhanced URC test completed successfully!");
|
||||
ESP_LOGI(TAG, "The enhanced URC handler successfully processed all HTTP chunks");
|
||||
ESP_LOGI(TAG, "with granular buffer consumption control");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Enhanced URC test timed out");
|
||||
}
|
||||
|
||||
dce->sync();
|
||||
vEventGroupDelete(s_event_group);
|
||||
ESP_LOGI(TAG, "Done");
|
||||
ESP_LOGI(TAG, "Enhanced URC test done");
|
||||
}
|
||||
|
||||
@@ -9,3 +9,7 @@ dependencies:
|
||||
# Required IDF version
|
||||
idf:
|
||||
version: ">=5.0"
|
||||
espressif/mqtt:
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
|
||||
@@ -2,4 +2,4 @@ version: "4.3.3"
|
||||
url: https://github.com/espressif/esp-protocols/tree/master/components/libwebsockets
|
||||
description: The component provides a simple ESP-IDF port of libwebsockets client.
|
||||
dependencies:
|
||||
idf: '>=5.3'
|
||||
idf: '>=5.3,<6.0'
|
||||
|
||||
@@ -3,6 +3,6 @@ commitizen:
|
||||
bump_message: 'bump(mdns): $current_version -> $new_version'
|
||||
pre_bump_hooks: python ../../ci/changelog.py mdns
|
||||
tag_format: mdns-v$version
|
||||
version: 1.9.0
|
||||
version: 1.9.1
|
||||
version_files:
|
||||
- idf_component.yml
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## [1.9.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.9.1)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix to use tagged AFL image + minor format fix ([2b2f009a](https://github.com/espressif/esp-protocols/commit/2b2f009a))
|
||||
- Fix unused variable `dcst` warning for wifi-remote chips ([081eef88](https://github.com/espressif/esp-protocols/commit/081eef88))
|
||||
|
||||
## [1.9.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.9.0)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -12,15 +12,18 @@ endif()
|
||||
|
||||
set(MDNS_MEMORY "mdns_mem_caps.c")
|
||||
|
||||
set(MDNS_CORE "mdns_responder.c" "mdns_receive.c" "mdns_utils.c" "mdns_debug.c" "mdns_browser.c" "mdns_send.c" "mdns_netif.c"
|
||||
"mdns_querier.c" "mdns_pcb.c" "mdns_service.c")
|
||||
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
if(${target} STREQUAL "linux")
|
||||
set(dependencies esp_netif_linux esp_event)
|
||||
set(private_dependencies esp_timer console esp_system)
|
||||
set(srcs "mdns.c" ${MDNS_MEMORY} ${MDNS_NETWORKING} ${MDNS_CONSOLE})
|
||||
set(srcs ${MDNS_CORE} ${MDNS_MEMORY} ${MDNS_NETWORKING} ${MDNS_CONSOLE})
|
||||
else()
|
||||
set(dependencies lwip console esp_netif)
|
||||
set(private_dependencies esp_timer esp_wifi)
|
||||
set(srcs "mdns.c" ${MDNS_MEMORY} ${MDNS_NETWORKING} ${MDNS_CONSOLE})
|
||||
set(srcs ${MDNS_CORE} ${MDNS_MEMORY} ${MDNS_NETWORKING} ${MDNS_CONSOLE})
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
|
||||
@@ -142,6 +142,20 @@ menu "mDNS"
|
||||
help
|
||||
Enable for the library to log received and sent mDNS packets to stdout.
|
||||
|
||||
config MDNS_DEBUG_USE_ESP_LOG
|
||||
bool "Route debug prints to ESP_LOG"
|
||||
depends on MDNS_ENABLE_DEBUG_PRINTS
|
||||
default y
|
||||
help
|
||||
Enable this option to route debug prints to ESP_LOG, which allows more flexibility.
|
||||
|
||||
config MDNS_DEBUG_BUFFER_SIZE
|
||||
int "Size of temporary buffer for debug prints"
|
||||
depends on MDNS_DEBUG_USE_ESP_LOG
|
||||
default 1024
|
||||
help
|
||||
This buffer is used to output mDNS packets in debug prints.
|
||||
|
||||
config MDNS_ENABLE_CONSOLE_CLI
|
||||
bool "Enable Command Line Interface on device console"
|
||||
default y
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: "1.9.0"
|
||||
version: "1.9.1"
|
||||
description: "Multicast UDP service used to provide local network service and host discovery."
|
||||
url: "https://github.com/espressif/esp-protocols/tree/master/components/mdns"
|
||||
issues: "https://github.com/espressif/esp-protocols/issues"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
656
components/mdns/mdns_browser.c
Normal file
656
components/mdns/mdns_browser.c
Normal file
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "mdns_private.h"
|
||||
#include "mdns_browser.h"
|
||||
#include "mdns_mem_caps.h"
|
||||
#include "mdns_debug.h"
|
||||
#include "mdns_utils.h"
|
||||
#include "mdns_querier.h"
|
||||
#include "mdns_responder.h"
|
||||
#include "mdns_netif.h"
|
||||
#include "mdns_service.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "mdns_browser";
|
||||
|
||||
static mdns_browse_t *s_browse;
|
||||
|
||||
/**
|
||||
* @brief Browse action
|
||||
*/
|
||||
static esp_err_t send_browse_action(mdns_action_type_t type, mdns_browse_t *browse)
|
||||
{
|
||||
mdns_action_t *action = NULL;
|
||||
|
||||
action = (mdns_action_t *)mdns_mem_malloc(sizeof(mdns_action_t));
|
||||
|
||||
if (!action) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
action->type = type;
|
||||
action->data.browse_add.browse = browse;
|
||||
if (!mdns_priv_queue_action(action)) {
|
||||
mdns_mem_free(action);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free a browse item (Not free the list).
|
||||
*/
|
||||
static void browse_item_free(mdns_browse_t *browse)
|
||||
{
|
||||
mdns_mem_free(browse->service);
|
||||
mdns_mem_free(browse->proto);
|
||||
if (browse->result) {
|
||||
mdns_priv_query_results_free(browse->result);
|
||||
}
|
||||
mdns_mem_free(browse);
|
||||
}
|
||||
|
||||
static void browse_sync(mdns_browse_sync_t *browse_sync)
|
||||
{
|
||||
mdns_browse_t *browse = browse_sync->browse;
|
||||
mdns_browse_result_sync_t *sync_result = browse_sync->sync_result;
|
||||
while (sync_result) {
|
||||
mdns_result_t *result = sync_result->result;
|
||||
DBG_BROWSE_RESULTS(result, browse_sync->browse);
|
||||
browse->notifier(result);
|
||||
if (result->ttl == 0) {
|
||||
queueDetach(mdns_result_t, browse->result, result);
|
||||
// Just free current result
|
||||
result->next = NULL;
|
||||
mdns_query_results_free(result);
|
||||
}
|
||||
sync_result = sync_result->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send PTR query packet to all available interfaces for browsing.
|
||||
*/
|
||||
static void browse_send(mdns_browse_t *browse, mdns_if_t interface)
|
||||
{
|
||||
// Using search once for sending the PTR query
|
||||
mdns_search_once_t search = {0};
|
||||
|
||||
search.instance = NULL;
|
||||
search.service = browse->service;
|
||||
search.proto = browse->proto;
|
||||
search.type = MDNS_TYPE_PTR;
|
||||
search.unicast = false;
|
||||
search.result = NULL;
|
||||
search.next = NULL;
|
||||
|
||||
for (uint8_t protocol_idx = 0; protocol_idx < MDNS_IP_PROTOCOL_MAX; protocol_idx++) {
|
||||
mdns_priv_query_send(&search, interface, (mdns_ip_protocol_t) protocol_idx);
|
||||
}
|
||||
}
|
||||
|
||||
void mdns_priv_browse_send_all(mdns_if_t mdns_if)
|
||||
{
|
||||
mdns_browse_t *browse = s_browse;
|
||||
while (browse) {
|
||||
browse_send(browse, mdns_if);
|
||||
browse = browse->next;
|
||||
}
|
||||
}
|
||||
|
||||
void mdns_priv_browse_free(void)
|
||||
{
|
||||
while (s_browse) {
|
||||
mdns_browse_t *b = s_browse;
|
||||
s_browse = s_browse->next;
|
||||
browse_item_free(b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Mark browse as finished, remove and free it from browse chain
|
||||
*/
|
||||
static void browse_finish(mdns_browse_t *browse)
|
||||
{
|
||||
browse->state = BROWSE_OFF;
|
||||
mdns_browse_t *b = s_browse;
|
||||
mdns_browse_t *target_free = NULL;
|
||||
while (b) {
|
||||
if (strlen(b->service) == strlen(browse->service) && memcmp(b->service, browse->service, strlen(b->service)) == 0 &&
|
||||
strlen(b->proto) == strlen(browse->proto) && memcmp(b->proto, browse->proto, strlen(b->proto)) == 0) {
|
||||
target_free = b;
|
||||
b = b->next;
|
||||
queueDetach(mdns_browse_t, s_browse, target_free);
|
||||
browse_item_free(target_free);
|
||||
} else {
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
browse_item_free(browse);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate new browse structure
|
||||
*/
|
||||
static mdns_browse_t *browse_init(const char *service, const char *proto, mdns_browse_notify_t notifier)
|
||||
{
|
||||
mdns_browse_t *browse = (mdns_browse_t *)mdns_mem_malloc(sizeof(mdns_browse_t));
|
||||
|
||||
if (!browse) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return NULL;
|
||||
}
|
||||
memset(browse, 0, sizeof(mdns_browse_t));
|
||||
|
||||
browse->state = BROWSE_INIT;
|
||||
if (!mdns_utils_str_null_or_empty(service)) {
|
||||
browse->service = mdns_mem_strndup(service, MDNS_NAME_BUF_LEN - 1);
|
||||
if (!browse->service) {
|
||||
browse_item_free(browse);
|
||||
HOOK_MALLOC_FAILED;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mdns_utils_str_null_or_empty(proto)) {
|
||||
browse->proto = mdns_mem_strndup(proto, MDNS_NAME_BUF_LEN - 1);
|
||||
if (!browse->proto) {
|
||||
browse_item_free(browse);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
browse->notifier = notifier;
|
||||
return browse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add new browse to the browse chain
|
||||
*/
|
||||
static void browse_add(mdns_browse_t *browse)
|
||||
{
|
||||
browse->state = BROWSE_RUNNING;
|
||||
mdns_browse_t *queue = s_browse;
|
||||
bool found = false;
|
||||
// looking for this browse in active browses
|
||||
while (queue) {
|
||||
if (strlen(queue->service) == strlen(browse->service) && memcmp(queue->service, browse->service, strlen(queue->service)) == 0 &&
|
||||
strlen(queue->proto) == strlen(browse->proto) && memcmp(queue->proto, browse->proto, strlen(queue->proto)) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
queue = queue->next;
|
||||
}
|
||||
if (!found) {
|
||||
browse->next = s_browse;
|
||||
s_browse = browse;
|
||||
}
|
||||
for (uint8_t interface_idx = 0; interface_idx < MDNS_MAX_INTERFACES; interface_idx++) {
|
||||
browse_send(browse, (mdns_if_t) interface_idx);
|
||||
}
|
||||
if (found) {
|
||||
browse_item_free(browse);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from packet parser to find matching running search
|
||||
*/
|
||||
mdns_browse_t *mdns_priv_browse_find(mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
mdns_browse_t *b = s_browse;
|
||||
// For browse, we only care about the SRV, TXT, A and AAAA
|
||||
if (type != MDNS_TYPE_SRV && type != MDNS_TYPE_A && type != MDNS_TYPE_AAAA && type != MDNS_TYPE_TXT) {
|
||||
return NULL;
|
||||
}
|
||||
mdns_result_t *r = NULL;
|
||||
while (b) {
|
||||
if (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT) {
|
||||
if (strcasecmp(name->service, b->service)
|
||||
|| strcasecmp(name->proto, b->proto)) {
|
||||
b = b->next;
|
||||
continue;
|
||||
}
|
||||
return b;
|
||||
} else if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA) {
|
||||
r = b->result;
|
||||
while (r) {
|
||||
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !mdns_utils_str_null_or_empty(r->hostname) && !strcasecmp(name->host, r->hostname)) {
|
||||
return b;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
b = b->next;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
static void sync_browse_result_link_free(mdns_browse_sync_t *browse_sync)
|
||||
{
|
||||
mdns_browse_result_sync_t *current = browse_sync->sync_result;
|
||||
mdns_browse_result_sync_t *need_free;
|
||||
while (current) {
|
||||
need_free = current;
|
||||
current = current->next;
|
||||
mdns_mem_free(need_free);
|
||||
}
|
||||
mdns_mem_free(browse_sync);
|
||||
}
|
||||
|
||||
void mdns_priv_browse_action(mdns_action_t *action, mdns_action_subtype_t type)
|
||||
{
|
||||
if (type == ACTION_RUN) {
|
||||
switch (action->type) {
|
||||
case ACTION_BROWSE_ADD:
|
||||
browse_add(action->data.browse_add.browse);
|
||||
break;
|
||||
case ACTION_BROWSE_SYNC:
|
||||
browse_sync(action->data.browse_sync.browse_sync);
|
||||
sync_browse_result_link_free(action->data.browse_sync.browse_sync);
|
||||
break;
|
||||
case ACTION_BROWSE_END:
|
||||
browse_finish(action->data.browse_add.browse);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type == ACTION_CLEANUP) {
|
||||
switch (action->type) {
|
||||
case ACTION_BROWSE_ADD:
|
||||
//fallthrough
|
||||
case ACTION_BROWSE_END:
|
||||
browse_item_free(action->data.browse_add.browse);
|
||||
break;
|
||||
case ACTION_BROWSE_SYNC:
|
||||
sync_browse_result_link_free(action->data.browse_sync.browse_sync);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add result to browse, only add when the result is a new one.
|
||||
*/
|
||||
static esp_err_t add_browse_result(mdns_browse_sync_t *sync_browse, mdns_result_t *r)
|
||||
{
|
||||
mdns_browse_result_sync_t *sync_r = sync_browse->sync_result;
|
||||
while (sync_r) {
|
||||
if (sync_r->result == r) {
|
||||
break;
|
||||
}
|
||||
sync_r = sync_r->next;
|
||||
}
|
||||
if (!sync_r) {
|
||||
// Do not find, need to add the result to the list
|
||||
mdns_browse_result_sync_t *new = (mdns_browse_result_sync_t *)mdns_mem_malloc(sizeof(mdns_browse_result_sync_t));
|
||||
|
||||
if (!new) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
new->result = r;
|
||||
new->next = sync_browse->sync_result;
|
||||
sync_browse->sync_result = new;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from parser to add A/AAAA data to search result
|
||||
*/
|
||||
void mdns_priv_browse_result_add_ip(mdns_browse_t *browse, const char *hostname, esp_ip_addr_t *ip,
|
||||
mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl, mdns_browse_sync_t *out_sync_browse)
|
||||
{
|
||||
if (out_sync_browse->browse == NULL) {
|
||||
return;
|
||||
} else {
|
||||
if (out_sync_browse->browse != browse) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mdns_result_t *r = NULL;
|
||||
mdns_ip_addr_t *r_a = NULL;
|
||||
if (browse) {
|
||||
r = browse->result;
|
||||
while (r) {
|
||||
if (r->ip_protocol == ip_protocol) {
|
||||
// Find the target result in browse result.
|
||||
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && !mdns_utils_str_null_or_empty(r->hostname) && !strcasecmp(hostname, r->hostname)) {
|
||||
r_a = r->addr;
|
||||
// Check if the address has already added in result.
|
||||
while (r_a) {
|
||||
#ifdef CONFIG_LWIP_IPV4
|
||||
if (r_a->addr.type == ip->type && r_a->addr.type == ESP_IPADDR_TYPE_V4 && r_a->addr.u_addr.ip4.addr == ip->u_addr.ip4.addr) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_LWIP_IPV6
|
||||
if (r_a->addr.type == ip->type && r_a->addr.type == ESP_IPADDR_TYPE_V6 && !memcmp(r_a->addr.u_addr.ip6.addr, ip->u_addr.ip6.addr, 16)) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
r_a = r_a->next;
|
||||
}
|
||||
if (!r_a) {
|
||||
// The current IP is a new one, add it to the link list.
|
||||
mdns_ip_addr_t *a = NULL;
|
||||
a = mdns_priv_result_addr_create_ip(ip);
|
||||
if (!a) {
|
||||
return;
|
||||
}
|
||||
a->next = r->addr;
|
||||
r->addr = a;
|
||||
if (r->ttl != ttl) {
|
||||
if (r->ttl == 0) {
|
||||
r->ttl = ttl;
|
||||
} else {
|
||||
mdns_priv_query_update_result_ttl(r, ttl);
|
||||
}
|
||||
}
|
||||
if (add_browse_result(out_sync_browse, r) != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_txt_item_in_list(mdns_txt_item_t txt, uint8_t txt_value_len, mdns_txt_item_t *txt_list, uint8_t *txt_value_len_list, size_t txt_count)
|
||||
{
|
||||
for (size_t i = 0; i < txt_count; i++) {
|
||||
if (strcmp(txt.key, txt_list[i].key) == 0) {
|
||||
if (txt_value_len == txt_value_len_list[i] && memcmp(txt.value, txt_list[i].value, txt_value_len) == 0) {
|
||||
return true;
|
||||
} else {
|
||||
// The key value is unique, so there is no need to continue searching.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from parser to add TXT data to search result
|
||||
*/
|
||||
void mdns_priv_browse_result_add_txt(mdns_browse_t *browse, const char *instance, const char *service, const char *proto,
|
||||
mdns_txt_item_t *txt, uint8_t *txt_value_len, size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol,
|
||||
uint32_t ttl, mdns_browse_sync_t *out_sync_browse)
|
||||
{
|
||||
if (out_sync_browse->browse == NULL) {
|
||||
return;
|
||||
} else {
|
||||
if (out_sync_browse->browse != browse) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mdns_result_t *r = browse->result;
|
||||
while (r) {
|
||||
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol &&
|
||||
!mdns_utils_str_null_or_empty(r->instance_name) && !strcasecmp(instance, r->instance_name) &&
|
||||
!mdns_utils_str_null_or_empty(r->service_type) && !strcasecmp(service, r->service_type) &&
|
||||
!mdns_utils_str_null_or_empty(r->proto) && !strcasecmp(proto, r->proto)) {
|
||||
bool should_update = false;
|
||||
if (r->txt) {
|
||||
// Check if txt changed
|
||||
if (txt_count != r->txt_count) {
|
||||
should_update = true;
|
||||
} else {
|
||||
for (size_t txt_index = 0; txt_index < txt_count; txt_index++) {
|
||||
if (!is_txt_item_in_list(txt[txt_index], txt_value_len[txt_index], r->txt, r->txt_value_len, r->txt_count)) {
|
||||
should_update = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the result has a previous txt entry, we delete it and re-add.
|
||||
for (size_t i = 0; i < r->txt_count; i++) {
|
||||
mdns_mem_free((char *)(r->txt[i].key));
|
||||
mdns_mem_free((char *)(r->txt[i].value));
|
||||
}
|
||||
mdns_mem_free(r->txt);
|
||||
mdns_mem_free(r->txt_value_len);
|
||||
}
|
||||
r->txt = txt;
|
||||
r->txt_value_len = txt_value_len;
|
||||
r->txt_count = txt_count;
|
||||
if (r->ttl != ttl) {
|
||||
uint32_t previous_ttl = r->ttl;
|
||||
if (r->ttl == 0) {
|
||||
r->ttl = ttl;
|
||||
} else {
|
||||
mdns_priv_query_update_result_ttl(r, ttl);
|
||||
}
|
||||
if (previous_ttl != r->ttl) {
|
||||
should_update = true;
|
||||
}
|
||||
}
|
||||
if (should_update) {
|
||||
if (add_browse_result(out_sync_browse, r) != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
r = (mdns_result_t *)mdns_mem_malloc(sizeof(mdns_result_t));
|
||||
if (!r) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
goto free_txt;
|
||||
}
|
||||
memset(r, 0, sizeof(mdns_result_t));
|
||||
r->instance_name = mdns_mem_strdup(instance);
|
||||
r->service_type = mdns_mem_strdup(service);
|
||||
r->proto = mdns_mem_strdup(proto);
|
||||
if (!r->instance_name || !r->service_type || !r->proto) {
|
||||
mdns_mem_free(r->instance_name);
|
||||
mdns_mem_free(r->service_type);
|
||||
mdns_mem_free(r->proto);
|
||||
mdns_mem_free(r);
|
||||
return;
|
||||
}
|
||||
r->txt = txt;
|
||||
r->txt_value_len = txt_value_len;
|
||||
r->txt_count = txt_count;
|
||||
r->esp_netif = mdns_priv_get_esp_netif(tcpip_if);
|
||||
r->ip_protocol = ip_protocol;
|
||||
r->ttl = ttl;
|
||||
r->next = browse->result;
|
||||
browse->result = r;
|
||||
add_browse_result(out_sync_browse, r);
|
||||
return;
|
||||
|
||||
free_txt:
|
||||
for (size_t i = 0; i < txt_count; i++) {
|
||||
mdns_mem_free((char *)(txt[i].key));
|
||||
mdns_mem_free((char *)(txt[i].value));
|
||||
}
|
||||
mdns_mem_free(txt);
|
||||
mdns_mem_free(txt_value_len);
|
||||
return;
|
||||
}
|
||||
|
||||
static esp_err_t copy_address_in_previous_result(mdns_result_t *result_list, mdns_result_t *r)
|
||||
{
|
||||
while (result_list) {
|
||||
if (!mdns_utils_str_null_or_empty(result_list->hostname) && !mdns_utils_str_null_or_empty(r->hostname) && !strcasecmp(result_list->hostname, r->hostname) &&
|
||||
result_list->ip_protocol == r->ip_protocol && result_list->addr && !r->addr) {
|
||||
// If there is a same hostname in previous result, we need to copy the address here.
|
||||
r->addr = mdns_utils_copy_address_list(result_list->addr);
|
||||
if (!r->addr) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
result_list = result_list->next;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from parser to add SRV data to search result
|
||||
*/
|
||||
void mdns_priv_browse_result_add_srv(mdns_browse_t *browse, const char *hostname, const char *instance, const char *service, const char *proto,
|
||||
uint16_t port, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl, mdns_browse_sync_t *out_sync_browse)
|
||||
{
|
||||
if (out_sync_browse->browse == NULL) {
|
||||
return;
|
||||
} else {
|
||||
if (out_sync_browse->browse != browse) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mdns_result_t *r = browse->result;
|
||||
while (r) {
|
||||
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol &&
|
||||
!mdns_utils_str_null_or_empty(r->instance_name) && !strcasecmp(instance, r->instance_name) &&
|
||||
!mdns_utils_str_null_or_empty(r->service_type) && !strcasecmp(service, r->service_type) &&
|
||||
!mdns_utils_str_null_or_empty(r->proto) && !strcasecmp(proto, r->proto)) {
|
||||
if (mdns_utils_str_null_or_empty(r->hostname) || strcasecmp(hostname, r->hostname)) {
|
||||
r->hostname = mdns_mem_strdup(hostname);
|
||||
r->port = port;
|
||||
if (!r->hostname) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return;
|
||||
}
|
||||
if (!r->addr) {
|
||||
esp_err_t err = copy_address_in_previous_result(browse->result, r);
|
||||
if (err == ESP_ERR_NO_MEM) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (add_browse_result(out_sync_browse, r) != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (r->ttl != ttl) {
|
||||
uint32_t previous_ttl = r->ttl;
|
||||
if (r->ttl == 0) {
|
||||
r->ttl = ttl;
|
||||
} else {
|
||||
mdns_priv_query_update_result_ttl(r, ttl);
|
||||
}
|
||||
if (previous_ttl != r->ttl) {
|
||||
if (add_browse_result(out_sync_browse, r) != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
r = (mdns_result_t *)mdns_mem_malloc(sizeof(mdns_result_t));
|
||||
if (!r) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
memset(r, 0, sizeof(mdns_result_t));
|
||||
r->hostname = mdns_mem_strdup(hostname);
|
||||
r->instance_name = mdns_mem_strdup(instance);
|
||||
r->service_type = mdns_mem_strdup(service);
|
||||
r->proto = mdns_mem_strdup(proto);
|
||||
if (!r->hostname || !r->instance_name || !r->service_type || !r->proto) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
mdns_mem_free(r->hostname);
|
||||
mdns_mem_free(r->instance_name);
|
||||
mdns_mem_free(r->service_type);
|
||||
mdns_mem_free(r->proto);
|
||||
mdns_mem_free(r);
|
||||
return;
|
||||
}
|
||||
r->port = port;
|
||||
r->esp_netif = mdns_priv_get_esp_netif(tcpip_if);
|
||||
r->ip_protocol = ip_protocol;
|
||||
r->ttl = ttl;
|
||||
r->next = browse->result;
|
||||
browse->result = r;
|
||||
add_browse_result(out_sync_browse, r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Browse sync result
|
||||
*/
|
||||
esp_err_t mdns_priv_browse_sync(mdns_browse_sync_t *browse_sync)
|
||||
{
|
||||
mdns_action_t *action = NULL;
|
||||
|
||||
action = (mdns_action_t *)mdns_mem_malloc(sizeof(mdns_action_t));
|
||||
if (!action) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
action->type = ACTION_BROWSE_SYNC;
|
||||
action->data.browse_sync.browse_sync = browse_sync;
|
||||
if (!mdns_priv_queue_action(action)) {
|
||||
mdns_mem_free(action);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @defgroup MDNS_PUBCLIC_API
|
||||
*/
|
||||
mdns_browse_t *mdns_browse_new(const char *service, const char *proto, mdns_browse_notify_t notifier)
|
||||
{
|
||||
mdns_browse_t *browse = NULL;
|
||||
|
||||
if (mdns_priv_is_server_init() || mdns_utils_str_null_or_empty(service) || mdns_utils_str_null_or_empty(proto)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
browse = browse_init(service, proto, notifier);
|
||||
if (!browse) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (send_browse_action(ACTION_BROWSE_ADD, browse)) {
|
||||
browse_item_free(browse);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return browse;
|
||||
}
|
||||
|
||||
esp_err_t mdns_browse_delete(const char *service, const char *proto)
|
||||
{
|
||||
mdns_browse_t *browse = NULL;
|
||||
|
||||
if (!mdns_priv_is_server_init() || mdns_utils_str_null_or_empty(service) || mdns_utils_str_null_or_empty(proto)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
browse = browse_init(service, proto, NULL);
|
||||
if (!browse) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (send_browse_action(ACTION_BROWSE_END, browse)) {
|
||||
browse_item_free(browse);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
416
components/mdns/mdns_debug.c
Normal file
416
components/mdns/mdns_debug.c
Normal file
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "mdns_private.h"
|
||||
#include "mdns_utils.h"
|
||||
|
||||
#ifdef CONFIG_MDNS_DEBUG_USE_ESP_LOG
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
#define MDNS_DBG_MAX_LINE CONFIG_MDNS_DEBUG_BUFFER_SIZE
|
||||
|
||||
static char s_mdns_dbg_buf[MDNS_DBG_MAX_LINE];
|
||||
static size_t s_mdns_dbg_pos = 0;
|
||||
|
||||
static void mdns_dbg_puts(const char *str)
|
||||
{
|
||||
ESP_LOGI("mdns", "%s", str);
|
||||
}
|
||||
|
||||
static inline void mdns_dbg_flush(void)
|
||||
{
|
||||
if (s_mdns_dbg_pos > 0) {
|
||||
s_mdns_dbg_buf[s_mdns_dbg_pos] = '\0';
|
||||
mdns_dbg_puts(s_mdns_dbg_buf);
|
||||
s_mdns_dbg_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void mdns_dbg_vprintf(const char *fmt, va_list args)
|
||||
{
|
||||
// Try to format directly into the buffer
|
||||
int len = vsnprintf(s_mdns_dbg_buf + s_mdns_dbg_pos,
|
||||
MDNS_DBG_MAX_LINE - s_mdns_dbg_pos,
|
||||
fmt, args);
|
||||
|
||||
if (len < 0) {
|
||||
return; // Error in formatting
|
||||
}
|
||||
|
||||
// Check if the entire formatted string fit in the buffer
|
||||
if (len < (MDNS_DBG_MAX_LINE - s_mdns_dbg_pos)) {
|
||||
// If it fit, just update the position
|
||||
s_mdns_dbg_pos += len;
|
||||
} else {
|
||||
// The formatted string was truncated because it didn't fit
|
||||
// First, flush what we have (the partial string)
|
||||
mdns_dbg_flush();
|
||||
|
||||
// Create a new va_list copy and try again with the full buffer
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
|
||||
// Format again with the entire buffer available
|
||||
len = vsnprintf(s_mdns_dbg_buf, MDNS_DBG_MAX_LINE - 1, fmt, args_copy);
|
||||
va_end(args_copy);
|
||||
|
||||
if (len < 0) {
|
||||
return; // Error
|
||||
}
|
||||
|
||||
// Check if content will be lost (true truncation)
|
||||
if (len >= MDNS_DBG_MAX_LINE - 1) {
|
||||
// This is when actual content will be lost - log a warning
|
||||
ESP_LOGW("mdns", "Message truncated: length (%d) exceeds buffer size (%d). Consider increasing CONFIG_MDNS_DEBUG_BUFFER_SIZE.",
|
||||
len, MDNS_DBG_MAX_LINE - 1);
|
||||
|
||||
// Display what we could fit, then flush and return
|
||||
s_mdns_dbg_pos = MDNS_DBG_MAX_LINE - 1;
|
||||
mdns_dbg_flush();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get here, the whole message fit this time
|
||||
s_mdns_dbg_pos = len;
|
||||
}
|
||||
|
||||
// If buffer is nearly full after this operation, flush it
|
||||
if (s_mdns_dbg_pos >= MDNS_DBG_MAX_LINE - 1) {
|
||||
mdns_dbg_flush();
|
||||
}
|
||||
}
|
||||
|
||||
static void mdns_dbg_printf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
mdns_dbg_vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#define dbg_printf(...) mdns_dbg_printf(__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_printf(...) printf(__VA_ARGS__)
|
||||
#define mdns_dbg_flush()
|
||||
#endif
|
||||
|
||||
void static dbg_packet(const uint8_t *data, size_t len)
|
||||
{
|
||||
static mdns_name_t n;
|
||||
mdns_header_t header;
|
||||
const uint8_t *content = data + MDNS_HEAD_LEN;
|
||||
uint64_t t = xTaskGetTickCount() * portTICK_PERIOD_MS;
|
||||
mdns_name_t *name = &n;
|
||||
memset(name, 0, sizeof(mdns_name_t));
|
||||
|
||||
dbg_printf("Packet[%" PRIu64 "]: ", t);
|
||||
|
||||
header.id = mdns_utils_read_u16(data, MDNS_HEAD_ID_OFFSET);
|
||||
header.flags = mdns_utils_read_u16(data, MDNS_HEAD_FLAGS_OFFSET);
|
||||
header.questions = mdns_utils_read_u16(data, MDNS_HEAD_QUESTIONS_OFFSET);
|
||||
header.answers = mdns_utils_read_u16(data, MDNS_HEAD_ANSWERS_OFFSET);
|
||||
header.servers = mdns_utils_read_u16(data, MDNS_HEAD_SERVERS_OFFSET);
|
||||
header.additional = mdns_utils_read_u16(data, MDNS_HEAD_ADDITIONAL_OFFSET);
|
||||
|
||||
dbg_printf("%s",
|
||||
(header.flags == MDNS_FLAGS_QR_AUTHORITATIVE) ? "AUTHORITATIVE\n" :
|
||||
(header.flags == MDNS_FLAGS_DISTRIBUTED) ? "DISTRIBUTED\n" :
|
||||
(header.flags == 0) ? "\n" : " "
|
||||
);
|
||||
if (header.flags && header.flags != MDNS_FLAGS_QR_AUTHORITATIVE) {
|
||||
dbg_printf("0x%04X\n", header.flags);
|
||||
}
|
||||
|
||||
if (header.questions) {
|
||||
uint8_t qs = header.questions;
|
||||
|
||||
while (qs--) {
|
||||
content = mdns_utils_parse_fqdn(data, content, name, len);
|
||||
if (!content || content + MDNS_CLASS_OFFSET + 1 >= data + len) {
|
||||
header.answers = 0;
|
||||
header.additional = 0;
|
||||
header.servers = 0;
|
||||
dbg_printf("ERROR: parse header questions\n");
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t type = mdns_utils_read_u16(content, MDNS_TYPE_OFFSET);
|
||||
uint16_t mdns_class = mdns_utils_read_u16(content, MDNS_CLASS_OFFSET);
|
||||
bool unicast = !!(mdns_class & 0x8000);
|
||||
mdns_class &= 0x7FFF;
|
||||
content = content + 4;
|
||||
|
||||
dbg_printf(" Q: ");
|
||||
if (unicast) {
|
||||
dbg_printf("*U* ");
|
||||
}
|
||||
if (type == MDNS_TYPE_PTR) {
|
||||
dbg_printf("%s.%s%s.%s.%s. PTR ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain);
|
||||
} else if (type == MDNS_TYPE_SRV) {
|
||||
dbg_printf("%s.%s%s.%s.%s. SRV ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain);
|
||||
} else if (type == MDNS_TYPE_TXT) {
|
||||
dbg_printf("%s.%s%s.%s.%s. TXT ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain);
|
||||
} else if (type == MDNS_TYPE_A) {
|
||||
dbg_printf("%s.%s. A ", name->host, name->domain);
|
||||
} else if (type == MDNS_TYPE_AAAA) {
|
||||
dbg_printf("%s.%s. AAAA ", name->host, name->domain);
|
||||
} else if (type == MDNS_TYPE_NSEC) {
|
||||
dbg_printf("%s.%s%s.%s.%s. NSEC ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain);
|
||||
} else if (type == MDNS_TYPE_ANY) {
|
||||
dbg_printf("%s.%s%s.%s.%s. ANY ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain);
|
||||
} else {
|
||||
dbg_printf("%s.%s%s.%s.%s. %04X ", name->host, name->sub ? "_sub." : "", name->service, name->proto, name->domain, type);
|
||||
}
|
||||
|
||||
if (mdns_class == 0x0001) {
|
||||
dbg_printf("IN");
|
||||
} else {
|
||||
dbg_printf("%04X", mdns_class);
|
||||
}
|
||||
dbg_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (header.answers || header.servers || header.additional) {
|
||||
uint16_t recordIndex = 0;
|
||||
|
||||
while (content < (data + len)) {
|
||||
|
||||
content = mdns_utils_parse_fqdn(data, content, name, len);
|
||||
if (!content) {
|
||||
dbg_printf("ERROR: parse mdns records\n");
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t type = mdns_utils_read_u16(content, MDNS_TYPE_OFFSET);
|
||||
uint16_t mdns_class = mdns_utils_read_u16(content, MDNS_CLASS_OFFSET);
|
||||
uint32_t ttl = mdns_utils_read_u32(content, MDNS_TTL_OFFSET);
|
||||
uint16_t data_len = mdns_utils_read_u16(content, MDNS_LEN_OFFSET);
|
||||
const uint8_t *data_ptr = content + MDNS_DATA_OFFSET;
|
||||
bool flush = !!(mdns_class & 0x8000);
|
||||
mdns_class &= 0x7FFF;
|
||||
|
||||
content = data_ptr + data_len;
|
||||
if (content > (data + len)) {
|
||||
dbg_printf("ERROR: content length overflow\n");
|
||||
break;
|
||||
}
|
||||
|
||||
mdns_parsed_record_type_t record_type = MDNS_ANSWER;
|
||||
|
||||
if (recordIndex >= (header.answers + header.servers)) {
|
||||
record_type = MDNS_EXTRA;
|
||||
} else if (recordIndex >= (header.answers)) {
|
||||
record_type = MDNS_NS;
|
||||
}
|
||||
recordIndex++;
|
||||
|
||||
if (record_type == MDNS_EXTRA) {
|
||||
dbg_printf(" X");
|
||||
} else if (record_type == MDNS_NS) {
|
||||
dbg_printf(" S");
|
||||
} else {
|
||||
dbg_printf(" A");
|
||||
}
|
||||
|
||||
if (type == MDNS_TYPE_PTR) {
|
||||
dbg_printf(": %s%s%s.%s.%s. PTR ", name->host, name->host[0] ? "." : "", name->service, name->proto, name->domain);
|
||||
} else if (type == MDNS_TYPE_SRV) {
|
||||
dbg_printf(": %s.%s.%s.%s. SRV ", name->host, name->service, name->proto, name->domain);
|
||||
} else if (type == MDNS_TYPE_TXT) {
|
||||
dbg_printf(": %s.%s.%s.%s. TXT ", name->host, name->service, name->proto, name->domain);
|
||||
} else if (type == MDNS_TYPE_A) {
|
||||
dbg_printf(": %s.%s. A ", name->host, name->domain);
|
||||
} else if (type == MDNS_TYPE_AAAA) {
|
||||
dbg_printf(": %s.%s. AAAA ", name->host, name->domain);
|
||||
} else if (type == MDNS_TYPE_NSEC) {
|
||||
dbg_printf(": %s.%s.%s.%s. NSEC ", name->host, name->service, name->proto, name->domain);
|
||||
} else if (type == MDNS_TYPE_ANY) {
|
||||
dbg_printf(": %s.%s.%s.%s. ANY ", name->host, name->service, name->proto, name->domain);
|
||||
} else if (type == MDNS_TYPE_OPT) {
|
||||
dbg_printf(": . OPT ");
|
||||
} else {
|
||||
dbg_printf(": %s.%s.%s.%s. %04X ", name->host, name->service, name->proto, name->domain, type);
|
||||
}
|
||||
|
||||
if (mdns_class == 0x0001) {
|
||||
dbg_printf("IN ");
|
||||
} else {
|
||||
dbg_printf("%04X ", mdns_class);
|
||||
}
|
||||
if (flush) {
|
||||
dbg_printf("FLUSH ");
|
||||
}
|
||||
dbg_printf("%" PRIu32, ttl);
|
||||
dbg_printf("[%u] ", data_len);
|
||||
if (type == MDNS_TYPE_PTR) {
|
||||
if (!mdns_utils_parse_fqdn(data, data_ptr, name, len)) {
|
||||
dbg_printf("ERROR: parse PTR\n");
|
||||
continue;
|
||||
}
|
||||
dbg_printf("%s.%s.%s.%s.\n", name->host, name->service, name->proto, name->domain);
|
||||
} else if (type == MDNS_TYPE_SRV) {
|
||||
if (!mdns_utils_parse_fqdn(data, data_ptr + MDNS_SRV_FQDN_OFFSET, name, len)) {
|
||||
dbg_printf("ERROR: parse SRV\n");
|
||||
continue;
|
||||
}
|
||||
uint16_t priority = mdns_utils_read_u16(data_ptr, MDNS_SRV_PRIORITY_OFFSET);
|
||||
uint16_t weight = mdns_utils_read_u16(data_ptr, MDNS_SRV_WEIGHT_OFFSET);
|
||||
uint16_t port = mdns_utils_read_u16(data_ptr, MDNS_SRV_PORT_OFFSET);
|
||||
dbg_printf("%u %u %u %s.%s.\n", priority, weight, port, name->host, name->domain);
|
||||
} else if (type == MDNS_TYPE_TXT) {
|
||||
uint16_t i = 0, y;
|
||||
while (i < data_len) {
|
||||
uint8_t partLen = data_ptr[i++];
|
||||
if ((i + partLen) > data_len) {
|
||||
dbg_printf("ERROR: parse TXT\n");
|
||||
break;
|
||||
}
|
||||
char txt[partLen + 1];
|
||||
for (y = 0; y < partLen; y++) {
|
||||
char d = data_ptr[i++];
|
||||
txt[y] = d;
|
||||
}
|
||||
txt[partLen] = 0;
|
||||
dbg_printf("%s", txt);
|
||||
if (i < data_len) {
|
||||
dbg_printf("; ");
|
||||
}
|
||||
}
|
||||
dbg_printf("\n");
|
||||
} else if (type == MDNS_TYPE_AAAA) {
|
||||
esp_ip6_addr_t ip6;
|
||||
memcpy(&ip6, data_ptr, sizeof(esp_ip6_addr_t));
|
||||
dbg_printf(IPV6STR "\n", IPV62STR(ip6));
|
||||
} else if (type == MDNS_TYPE_A) {
|
||||
esp_ip4_addr_t ip;
|
||||
memcpy(&ip, data_ptr, sizeof(esp_ip4_addr_t));
|
||||
dbg_printf(IPSTR "\n", IP2STR(&ip));
|
||||
} else if (type == MDNS_TYPE_NSEC) {
|
||||
const uint8_t *old_ptr = data_ptr;
|
||||
const uint8_t *new_ptr = mdns_utils_parse_fqdn(data, data_ptr, name, len);
|
||||
if (new_ptr) {
|
||||
dbg_printf("%s.%s.%s.%s. ", name->host, name->service, name->proto, name->domain);
|
||||
size_t diff = new_ptr - old_ptr;
|
||||
data_len -= diff;
|
||||
data_ptr = new_ptr;
|
||||
}
|
||||
size_t i;
|
||||
for (i = 0; i < data_len; i++) {
|
||||
dbg_printf(" %02x", data_ptr[i]);
|
||||
}
|
||||
dbg_printf("\n");
|
||||
} else if (type == MDNS_TYPE_OPT) {
|
||||
uint16_t opCode = mdns_utils_read_u16(data_ptr, 0);
|
||||
uint16_t opLen = mdns_utils_read_u16(data_ptr, 2);
|
||||
dbg_printf(" Code: %04x Data[%u]:", opCode, opLen);
|
||||
size_t i;
|
||||
for (i = 4; i < data_len; i++) {
|
||||
dbg_printf(" %02x", data_ptr[i]);
|
||||
}
|
||||
dbg_printf("\n");
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < data_len; i++) {
|
||||
dbg_printf(" %02x", data_ptr[i]);
|
||||
}
|
||||
dbg_printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
mdns_dbg_flush();
|
||||
}
|
||||
|
||||
void mdns_debug_tx_packet(mdns_tx_packet_t *p, uint8_t packet[MDNS_MAX_PACKET_SIZE], uint16_t index)
|
||||
{
|
||||
dbg_printf("\nTX[%lu][%lu]: ", (unsigned long)p->tcpip_if, (unsigned long)p->ip_protocol);
|
||||
#ifdef CONFIG_LWIP_IPV4
|
||||
if (p->dst.type == ESP_IPADDR_TYPE_V4) {
|
||||
dbg_printf("To: " IPSTR ":%u, ", IP2STR(&p->dst.u_addr.ip4), p->port);
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_LWIP_IPV6
|
||||
if (p->dst.type == ESP_IPADDR_TYPE_V6) {
|
||||
dbg_printf("To: " IPV6STR ":%u, ", IPV62STR(p->dst.u_addr.ip6), p->port);
|
||||
}
|
||||
#endif
|
||||
dbg_packet(packet, index);
|
||||
mdns_dbg_flush();
|
||||
}
|
||||
|
||||
void mdns_debug_rx_packet(mdns_rx_packet_t *packet, const uint8_t* data, uint16_t len)
|
||||
{
|
||||
dbg_printf("\nRX[%lu][%lu]: ", (unsigned long)packet->tcpip_if, (unsigned long)packet->ip_protocol);
|
||||
#ifdef CONFIG_LWIP_IPV4
|
||||
if (packet->src.type == ESP_IPADDR_TYPE_V4) {
|
||||
dbg_printf("From: " IPSTR ":%u, To: " IPSTR ", ", IP2STR(&packet->src.u_addr.ip4), packet->src_port, IP2STR(&packet->dest.u_addr.ip4));
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_LWIP_IPV6
|
||||
if (packet->src.type == ESP_IPADDR_TYPE_V6) {
|
||||
dbg_printf("From: " IPV6STR ":%u, To: " IPV6STR ", ", IPV62STR(packet->src.u_addr.ip6), packet->src_port, IPV62STR(packet->dest.u_addr.ip6));
|
||||
}
|
||||
#endif
|
||||
dbg_packet(data, len);
|
||||
mdns_dbg_flush();
|
||||
}
|
||||
|
||||
static void dbg_printf_result(mdns_result_t *r_t)
|
||||
{
|
||||
mdns_ip_addr_t *r_a = NULL;
|
||||
int addr_count = 0;
|
||||
dbg_printf("result esp_netif: %p\n", r_t->esp_netif);
|
||||
dbg_printf("result ip_protocol: %d\n", r_t->ip_protocol);
|
||||
dbg_printf("result hostname: %s\n", mdns_utils_str_null_or_empty(r_t->hostname) ? "NULL" : r_t->hostname);
|
||||
dbg_printf("result instance_name: %s\n", mdns_utils_str_null_or_empty(r_t->instance_name) ? "NULL" : r_t->instance_name);
|
||||
dbg_printf("result service_type: %s\n", mdns_utils_str_null_or_empty(r_t->service_type) ? "NULL" : r_t->service_type);
|
||||
dbg_printf("result proto: %s\n", mdns_utils_str_null_or_empty(r_t->proto) ? "NULL" : r_t->proto);
|
||||
dbg_printf("result port: %d\n", r_t->port);
|
||||
dbg_printf("result ttl: %" PRIu32 "\n", r_t->ttl);
|
||||
for (int i = 0; i < r_t->txt_count; i++) {
|
||||
dbg_printf("result txt item%d, key: %s, value: %s\n", i, r_t->txt[i].key, r_t->txt[i].value);
|
||||
}
|
||||
r_a = r_t->addr;
|
||||
while (r_a) {
|
||||
#ifdef CONFIG_LWIP_IPV4
|
||||
if (r_a->addr.type == ESP_IPADDR_TYPE_V4) {
|
||||
dbg_printf("Addr%d: " IPSTR "\n", addr_count++, IP2STR(&r_a->addr.u_addr.ip4));
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_LWIP_IPV6
|
||||
if (r_a->addr.type == ESP_IPADDR_TYPE_V6) {
|
||||
dbg_printf("Addr%d: " IPV6STR "\n", addr_count++, IPV62STR(r_a->addr.u_addr.ip6));
|
||||
}
|
||||
#endif
|
||||
r_a = r_a->next;
|
||||
}
|
||||
mdns_dbg_flush();
|
||||
}
|
||||
|
||||
void mdns_debug_printf_browse_result(mdns_result_t *r_t, mdns_browse_t *b_t)
|
||||
{
|
||||
dbg_printf("----------------sync browse %s.%s result---------------\n", b_t->service, b_t->proto);
|
||||
dbg_printf("browse pointer: %p\n", b_t);
|
||||
dbg_printf_result(r_t);
|
||||
mdns_dbg_flush();
|
||||
}
|
||||
|
||||
void mdns_debug_printf_browse_result_all(mdns_result_t *r_t)
|
||||
{
|
||||
int count = 0;
|
||||
while (r_t) {
|
||||
dbg_printf("----------------result %d---------------\n", count++);
|
||||
dbg_printf_result(r_t);
|
||||
r_t = r_t->next;
|
||||
}
|
||||
mdns_dbg_flush();
|
||||
}
|
||||
51
components/mdns/mdns_diagram.md
Normal file
51
components/mdns/mdns_diagram.md
Normal file
@@ -0,0 +1,51 @@
|
||||
```mermaid
|
||||
graph TB
|
||||
%% Housekeeping modules at the top
|
||||
subgraph Housekeeping [Support Modules]
|
||||
Service[mdns_service.c]
|
||||
Utils[mdns_utils.c]
|
||||
MemCaps[mdns_mem_caps.c]
|
||||
Debug[mdns_debug.c]
|
||||
end
|
||||
|
||||
%% Switch to LR direction for the main flow
|
||||
subgraph MainFlow [Main Data Flow]
|
||||
direction LR
|
||||
|
||||
%% Network on left side
|
||||
Network((Network)) <--> Networking
|
||||
|
||||
%% Networking layer
|
||||
subgraph Networking [Networking Layer]
|
||||
LWIP[mdns_networking_lwip.c]
|
||||
Socket[mdns_networking_socket.c]
|
||||
end
|
||||
|
||||
%% Traffic flow
|
||||
Networking --> |Incoming| Receive[mdns_receive.c]
|
||||
Send[mdns_send.c] --> |Outgoing| Networking
|
||||
|
||||
%% Core MDNS components
|
||||
Receive --> Responder[mdns_responder.c]
|
||||
Receive --> Browser[mdns_browser.c]
|
||||
Receive --> Querier[mdns_querier.c]
|
||||
|
||||
Responder --> Send
|
||||
Browser --> Send
|
||||
Querier --> Send
|
||||
|
||||
PCB[mdns_pcb.c] --> Send
|
||||
NetIF[mdns_netif.c]
|
||||
|
||||
%% Users on the right side, aligned vertically
|
||||
Responder --> Advertise((User: Advertising))
|
||||
Querier --> Search((User: Searching))
|
||||
Browser --> Browse((User: Browsing))
|
||||
end
|
||||
|
||||
%% Style user nodes
|
||||
style Advertise fill:#f9f,stroke:#333,stroke-width:2px
|
||||
style Search fill:#f9f,stroke:#333,stroke-width:2px
|
||||
style Browse fill:#f9f,stroke:#333,stroke-width:2px
|
||||
style Housekeeping fill:#e6f7ff,stroke:#333,stroke-width:1px
|
||||
```
|
||||
433
components/mdns/mdns_netif.c
Normal file
433
components/mdns/mdns_netif.c
Normal file
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "mdns.h"
|
||||
#include "mdns_private.h"
|
||||
#include "mdns_mem_caps.h"
|
||||
#include "mdns_utils.h"
|
||||
#include "mdns_debug.h"
|
||||
#include "mdns_browser.h"
|
||||
#include "mdns_netif.h"
|
||||
#include "mdns_pcb.h"
|
||||
#include "mdns_responder.h"
|
||||
#include "mdns_service.h"
|
||||
|
||||
static const char *TAG = "mdns_netif";
|
||||
|
||||
#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
#include "esp_eth.h"
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
#define MDNS_ESP_WIFI_ENABLED CONFIG_SOC_WIFI_SUPPORTED
|
||||
#else
|
||||
#define MDNS_ESP_WIFI_ENABLED (CONFIG_ESP_WIFI_ENABLED || CONFIG_ESP_WIFI_REMOTE_ENABLED)
|
||||
#endif
|
||||
|
||||
#if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP)
|
||||
#include "esp_wifi.h"
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
MDNS_IF_STA = 0,
|
||||
MDNS_IF_AP = 1,
|
||||
MDNS_IF_ETH = 2,
|
||||
} mdns_predef_if_t;
|
||||
|
||||
typedef struct mdns_interfaces mdns_interfaces_t;
|
||||
|
||||
struct mdns_interfaces {
|
||||
const bool predefined;
|
||||
esp_netif_t *netif;
|
||||
const mdns_predef_if_t predef_if;
|
||||
mdns_if_t duplicate;
|
||||
};
|
||||
|
||||
/*
|
||||
* @brief Internal collection of mdns supported interfaces
|
||||
*
|
||||
*/
|
||||
static mdns_interfaces_t s_esp_netifs[MDNS_MAX_INTERFACES] = {
|
||||
#if CONFIG_MDNS_PREDEF_NETIF_STA
|
||||
{ .predefined = true, .netif = NULL, .predef_if = MDNS_IF_STA, .duplicate = MDNS_MAX_INTERFACES },
|
||||
#endif
|
||||
#if CONFIG_MDNS_PREDEF_NETIF_AP
|
||||
{ .predefined = true, .netif = NULL, .predef_if = MDNS_IF_AP, .duplicate = MDNS_MAX_INTERFACES },
|
||||
#endif
|
||||
#if CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
{ .predefined = true, .netif = NULL, .predef_if = MDNS_IF_ETH, .duplicate = MDNS_MAX_INTERFACES },
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper to get either ETH or STA if the other is provided
|
||||
* Used when two interfaces are on the same subnet
|
||||
*/
|
||||
mdns_if_t mdns_priv_netif_get_other_interface(mdns_if_t tcpip_if)
|
||||
{
|
||||
if (tcpip_if < MDNS_MAX_INTERFACES) {
|
||||
return s_esp_netifs[tcpip_if].duplicate;
|
||||
}
|
||||
return MDNS_MAX_INTERFACES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert Predefined interface to the netif id from the internal netif list
|
||||
* @param predef_if Predefined interface enum
|
||||
* @return Ordinal number of internal list of mdns network interface.
|
||||
* Returns MDNS_MAX_INTERFACES if the predefined interface wasn't found in the list
|
||||
*/
|
||||
static mdns_if_t mdns_if_from_preset(mdns_predef_if_t predef_if)
|
||||
{
|
||||
for (int i = 0; i < MDNS_MAX_INTERFACES; ++i) {
|
||||
if (s_esp_netifs[i].predefined && s_esp_netifs[i].predef_if == predef_if) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return MDNS_MAX_INTERFACES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert Predefined interface to esp-netif handle
|
||||
* @param predef_if Predefined interface enum
|
||||
* @return esp_netif pointer from system list of network interfaces
|
||||
*/
|
||||
static inline esp_netif_t *netif_from_preset(mdns_predef_if_t predef_if)
|
||||
{
|
||||
switch (predef_if) {
|
||||
case MDNS_IF_STA:
|
||||
return esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
|
||||
case MDNS_IF_AP:
|
||||
return esp_netif_get_handle_from_ifkey("WIFI_AP_DEF");
|
||||
#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
case MDNS_IF_ETH:
|
||||
return esp_netif_get_handle_from_ifkey("ETH_DEF");
|
||||
#endif
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
esp_netif_t *mdns_priv_get_esp_netif(mdns_if_t tcpip_if)
|
||||
{
|
||||
if (tcpip_if < MDNS_MAX_INTERFACES) {
|
||||
if (s_esp_netifs[tcpip_if].netif == NULL && s_esp_netifs[tcpip_if].predefined) {
|
||||
// If the local copy is NULL and this netif is predefined -> we can find it in the global netif list
|
||||
s_esp_netifs[tcpip_if].netif = netif_from_preset(s_esp_netifs[tcpip_if].predef_if);
|
||||
// failing to find it means that the netif is *not* available -> return NULL
|
||||
}
|
||||
return s_esp_netifs[tcpip_if].netif;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Clean internal mdns interface's pointer
|
||||
*/
|
||||
void mdns_priv_netif_disable(mdns_if_t tcpip_if)
|
||||
{
|
||||
if (tcpip_if < MDNS_MAX_INTERFACES) {
|
||||
s_esp_netifs[tcpip_if].netif = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Convert esp-netif handle to mdns if
|
||||
*/
|
||||
static mdns_if_t get_if_from_netif(esp_netif_t *esp_netif)
|
||||
{
|
||||
for (int i = 0; i < MDNS_MAX_INTERFACES; ++i) {
|
||||
// The predefined netifs in the static array are NULL when firstly calling this function
|
||||
// if IPv4 is disabled. Set these netifs here.
|
||||
if (s_esp_netifs[i].netif == NULL && s_esp_netifs[i].predefined) {
|
||||
s_esp_netifs[i].netif = netif_from_preset(s_esp_netifs[i].predef_if);
|
||||
}
|
||||
if (esp_netif == s_esp_netifs[i].netif) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return MDNS_MAX_INTERFACES;
|
||||
}
|
||||
|
||||
static esp_err_t post_custom_action(mdns_if_t mdns_if, mdns_event_actions_t event_action)
|
||||
{
|
||||
if (!mdns_priv_is_server_init() || mdns_if >= MDNS_MAX_INTERFACES) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
mdns_action_t *action = (mdns_action_t *)mdns_mem_calloc(1, sizeof(mdns_action_t));
|
||||
if (!action) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
action->type = ACTION_SYSTEM_EVENT;
|
||||
action->data.sys_event.event_action = event_action;
|
||||
action->data.sys_event.interface = mdns_if;
|
||||
|
||||
if (!mdns_priv_queue_action(action)) {
|
||||
mdns_mem_free(action);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dispatch interface changes based on system events
|
||||
*/
|
||||
static inline void post_disable_pcb(mdns_predef_if_t preset_if, mdns_ip_protocol_t protocol)
|
||||
{
|
||||
post_custom_action(mdns_if_from_preset(preset_if),
|
||||
protocol == MDNS_IP_PROTOCOL_V4 ? MDNS_EVENT_DISABLE_IP4 : MDNS_EVENT_DISABLE_IP6);
|
||||
}
|
||||
|
||||
static inline void post_enable_pcb(mdns_predef_if_t preset_if, mdns_ip_protocol_t protocol)
|
||||
{
|
||||
post_custom_action(mdns_if_from_preset(preset_if),
|
||||
protocol == MDNS_IP_PROTOCOL_V4 ? MDNS_EVENT_ENABLE_IP4 : MDNS_EVENT_ENABLE_IP6);
|
||||
}
|
||||
|
||||
static inline void post_announce_pcb(mdns_predef_if_t preset_if, mdns_ip_protocol_t protocol)
|
||||
{
|
||||
post_custom_action(mdns_if_from_preset(preset_if),
|
||||
protocol == MDNS_IP_PROTOCOL_V4 ? MDNS_EVENT_ANNOUNCE_IP4 : MDNS_EVENT_ANNOUNCE_IP6);
|
||||
}
|
||||
|
||||
#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
static void handle_system_event_for_preset(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
if (!mdns_priv_is_server_init()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP)
|
||||
if (event_base == WIFI_EVENT) {
|
||||
esp_netif_dhcp_status_t dcst;
|
||||
switch (event_id) {
|
||||
case WIFI_EVENT_STA_CONNECTED:
|
||||
if (!esp_netif_dhcpc_get_status(netif_from_preset(MDNS_IF_STA), &dcst)) {
|
||||
if (dcst == ESP_NETIF_DHCP_STOPPED) {
|
||||
post_enable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V4);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WIFI_EVENT_STA_DISCONNECTED:
|
||||
post_disable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V4);
|
||||
post_disable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V6);
|
||||
break;
|
||||
case WIFI_EVENT_AP_START:
|
||||
post_enable_pcb(MDNS_IF_AP, MDNS_IP_PROTOCOL_V4);
|
||||
break;
|
||||
case WIFI_EVENT_AP_STOP:
|
||||
post_disable_pcb(MDNS_IF_AP, MDNS_IP_PROTOCOL_V4);
|
||||
post_disable_pcb(MDNS_IF_AP, MDNS_IP_PROTOCOL_V6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
if (event_base == ETH_EVENT) {
|
||||
esp_netif_dhcp_status_t dcst;
|
||||
switch (event_id) {
|
||||
case ETHERNET_EVENT_CONNECTED:
|
||||
if (!esp_netif_dhcpc_get_status(netif_from_preset(MDNS_IF_ETH), &dcst)) {
|
||||
if (dcst == ESP_NETIF_DHCP_STOPPED) {
|
||||
post_enable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ETHERNET_EVENT_DISCONNECTED:
|
||||
post_disable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4);
|
||||
post_disable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (event_base == IP_EVENT) {
|
||||
switch (event_id) {
|
||||
case IP_EVENT_STA_GOT_IP:
|
||||
post_enable_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V4);
|
||||
post_announce_pcb(MDNS_IF_STA, MDNS_IP_PROTOCOL_V6);
|
||||
break;
|
||||
#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
case IP_EVENT_ETH_GOT_IP:
|
||||
post_enable_pcb(MDNS_IF_ETH, MDNS_IP_PROTOCOL_V4);
|
||||
break;
|
||||
#endif
|
||||
case IP_EVENT_GOT_IP6: {
|
||||
ip_event_got_ip6_t *event = (ip_event_got_ip6_t *) event_data;
|
||||
mdns_if_t mdns_if = get_if_from_netif(event->esp_netif);
|
||||
if (mdns_if >= MDNS_MAX_INTERFACES) {
|
||||
return;
|
||||
}
|
||||
post_enable_pcb(mdns_if, MDNS_IP_PROTOCOL_V6);
|
||||
post_announce_pcb(mdns_if, MDNS_IP_PROTOCOL_V4);
|
||||
mdns_priv_browse_send_all(mdns_if);
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH */
|
||||
|
||||
static inline void set_default_duplicated_interfaces(void)
|
||||
{
|
||||
mdns_if_t wifi_sta_if = MDNS_MAX_INTERFACES;
|
||||
mdns_if_t eth_if = MDNS_MAX_INTERFACES;
|
||||
for (mdns_if_t i = 0; i < MDNS_MAX_INTERFACES; i++) {
|
||||
if (s_esp_netifs[i].predefined && s_esp_netifs[i].predef_if == MDNS_IF_STA) {
|
||||
wifi_sta_if = i;
|
||||
}
|
||||
if (s_esp_netifs[i].predefined && s_esp_netifs[i].predef_if == MDNS_IF_ETH) {
|
||||
eth_if = i;
|
||||
}
|
||||
}
|
||||
if (wifi_sta_if != MDNS_MAX_INTERFACES && eth_if != MDNS_MAX_INTERFACES) {
|
||||
s_esp_netifs[wifi_sta_if].duplicate = eth_if;
|
||||
s_esp_netifs[eth_if].duplicate = wifi_sta_if;
|
||||
}
|
||||
}
|
||||
|
||||
void mdns_priv_netif_unregister_predefined_handlers(void)
|
||||
{
|
||||
#if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP)
|
||||
esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, handle_system_event_for_preset);
|
||||
#endif
|
||||
#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
esp_event_handler_unregister(IP_EVENT, ESP_EVENT_ANY_ID, handle_system_event_for_preset);
|
||||
#endif
|
||||
#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, handle_system_event_for_preset);
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t mdns_priv_netif_init(void)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
// zero-out local copy of netifs to initiate a fresh search by interface key whenever a netif ptr is needed
|
||||
for (mdns_if_t i = 0; i < MDNS_MAX_INTERFACES; ++i) {
|
||||
s_esp_netifs[i].netif = NULL;
|
||||
}
|
||||
#if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP)
|
||||
if ((err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, handle_system_event_for_preset, NULL)) != ESP_OK) {
|
||||
goto free_event_handlers;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
if ((err = esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, handle_system_event_for_preset, NULL)) != ESP_OK) {
|
||||
goto free_event_handlers;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
if ((err = esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, handle_system_event_for_preset, NULL)) != ESP_OK) {
|
||||
goto free_event_handlers;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
set_default_duplicated_interfaces();
|
||||
#endif
|
||||
|
||||
uint8_t i;
|
||||
#ifdef CONFIG_LWIP_IPV6
|
||||
esp_ip6_addr_t tmp_addr6;
|
||||
#endif
|
||||
#ifdef CONFIG_LWIP_IPV4
|
||||
esp_netif_ip_info_t if_ip_info;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
|
||||
#ifdef CONFIG_LWIP_IPV6
|
||||
if (!esp_netif_get_ip6_linklocal(mdns_priv_get_esp_netif(i), &tmp_addr6) && !mdns_utils_ipv6_address_is_zero(tmp_addr6)) {
|
||||
mdns_priv_pcb_enable(i, MDNS_IP_PROTOCOL_V6);
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_LWIP_IPV4
|
||||
if (!esp_netif_get_ip_info(mdns_priv_get_esp_netif(i), &if_ip_info) && if_ip_info.ip.addr) {
|
||||
mdns_priv_pcb_enable(i, MDNS_IP_PROTOCOL_V4);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return ESP_OK;
|
||||
#if CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP || CONFIG_MDNS_PREDEF_NETIF_ETH
|
||||
free_event_handlers:
|
||||
mdns_priv_netif_unregister_predefined_handlers();
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t mdns_priv_netif_deinit(void)
|
||||
{
|
||||
for (int i = 0; i < MDNS_MAX_INTERFACES; i++) {
|
||||
mdns_priv_pcb_disable(i, MDNS_IP_PROTOCOL_V6);
|
||||
mdns_priv_pcb_disable(i, MDNS_IP_PROTOCOL_V4);
|
||||
s_esp_netifs[i].duplicate = MDNS_MAX_INTERFACES;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Public Methods
|
||||
* */
|
||||
esp_err_t mdns_netif_action(esp_netif_t *esp_netif, mdns_event_actions_t event_action)
|
||||
{
|
||||
return post_custom_action(get_if_from_netif(esp_netif), event_action);
|
||||
}
|
||||
|
||||
esp_err_t mdns_register_netif(esp_netif_t *esp_netif)
|
||||
{
|
||||
if (!mdns_priv_is_server_init()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_err_t err = ESP_ERR_NO_MEM;
|
||||
mdns_priv_service_lock();
|
||||
for (mdns_if_t i = 0; i < MDNS_MAX_INTERFACES; ++i) {
|
||||
if (s_esp_netifs[i].netif == esp_netif) {
|
||||
mdns_priv_service_unlock();
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
for (mdns_if_t i = 0; i < MDNS_MAX_INTERFACES; ++i) {
|
||||
if (!s_esp_netifs[i].predefined && s_esp_netifs[i].netif == NULL) {
|
||||
s_esp_netifs[i].netif = esp_netif;
|
||||
err = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mdns_priv_service_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t mdns_unregister_netif(esp_netif_t *esp_netif)
|
||||
{
|
||||
if (!mdns_priv_is_server_init()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_err_t err = ESP_ERR_NOT_FOUND;
|
||||
mdns_priv_service_lock();
|
||||
for (mdns_if_t i = 0; i < MDNS_MAX_INTERFACES; ++i) {
|
||||
if (!s_esp_netifs[i].predefined && s_esp_netifs[i].netif == esp_netif) {
|
||||
s_esp_netifs[i].netif = NULL;
|
||||
err = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mdns_priv_service_lock();
|
||||
return err;
|
||||
}
|
||||
@@ -16,11 +16,12 @@
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
#include "mdns_networking.h"
|
||||
#include "esp_netif_net_stack.h"
|
||||
#include "mdns_mem_caps.h"
|
||||
#include "mdns_utils.h"
|
||||
#include "mdns_netif.h"
|
||||
#include "mdns_service.h"
|
||||
|
||||
/*
|
||||
* MDNS Server Networking
|
||||
@@ -38,56 +39,75 @@ typedef struct interfaces {
|
||||
|
||||
static interfaces_t s_interfaces[MDNS_MAX_INTERFACES];
|
||||
|
||||
static struct udp_pcb *_pcb_main = NULL;
|
||||
static struct udp_pcb *s_pcb_main = NULL;
|
||||
|
||||
static const char *TAG = "mdns_networking";
|
||||
|
||||
static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport);
|
||||
static void receive(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport);
|
||||
|
||||
static esp_err_t send_rx_action(mdns_rx_packet_t *packet)
|
||||
{
|
||||
mdns_action_t *action = NULL;
|
||||
|
||||
action = (mdns_action_t *)mdns_mem_malloc(sizeof(mdns_action_t));
|
||||
if (!action) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
action->type = ACTION_RX_HANDLE;
|
||||
action->data.rx_handle.packet = packet;
|
||||
if (!mdns_priv_queue_action(action)) {
|
||||
mdns_mem_free(action);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Low level UDP PCB Initialize
|
||||
*/
|
||||
static esp_err_t _udp_pcb_main_init(void)
|
||||
static esp_err_t pcb_init(void)
|
||||
{
|
||||
if (_pcb_main) {
|
||||
if (s_pcb_main) {
|
||||
return ESP_OK;
|
||||
}
|
||||
_pcb_main = udp_new();
|
||||
if (!_pcb_main) {
|
||||
s_pcb_main = udp_new();
|
||||
if (!s_pcb_main) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (udp_bind(_pcb_main, IP_ANY_TYPE, MDNS_SERVICE_PORT) != 0) {
|
||||
udp_remove(_pcb_main);
|
||||
_pcb_main = NULL;
|
||||
if (udp_bind(s_pcb_main, IP_ANY_TYPE, MDNS_SERVICE_PORT) != 0) {
|
||||
udp_remove(s_pcb_main);
|
||||
s_pcb_main = NULL;
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
_pcb_main->mcast_ttl = 255;
|
||||
_pcb_main->remote_port = MDNS_SERVICE_PORT;
|
||||
ip_addr_copy(_pcb_main->remote_ip, *(IP_ANY_TYPE));
|
||||
udp_recv(_pcb_main, &_udp_recv, NULL);
|
||||
s_pcb_main->mcast_ttl = 255;
|
||||
s_pcb_main->remote_port = MDNS_SERVICE_PORT;
|
||||
ip_addr_copy(s_pcb_main->remote_ip, *(IP_ANY_TYPE));
|
||||
udp_recv(s_pcb_main, receive, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Low level UDP PCB Free
|
||||
*/
|
||||
static void _udp_pcb_main_deinit(void)
|
||||
static void pcb_deinit(void)
|
||||
{
|
||||
if (_pcb_main) {
|
||||
udp_recv(_pcb_main, NULL, NULL);
|
||||
udp_disconnect(_pcb_main);
|
||||
udp_remove(_pcb_main);
|
||||
_pcb_main = NULL;
|
||||
if (s_pcb_main) {
|
||||
udp_recv(s_pcb_main, NULL, NULL);
|
||||
udp_disconnect(s_pcb_main);
|
||||
udp_remove(s_pcb_main);
|
||||
s_pcb_main = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Low level UDP Multicast membership control
|
||||
*/
|
||||
static esp_err_t _udp_join_group(mdns_if_t if_inx, mdns_ip_protocol_t ip_protocol, bool join)
|
||||
static esp_err_t join_group(mdns_if_t if_inx, mdns_ip_protocol_t ip_protocol, bool join)
|
||||
{
|
||||
struct netif *netif = NULL;
|
||||
esp_netif_t *tcpip_if = _mdns_get_esp_netif(if_inx);
|
||||
esp_netif_t *tcpip_if = mdns_priv_get_esp_netif(if_inx);
|
||||
|
||||
if (!esp_netif_is_netif_up(tcpip_if)) {
|
||||
// Network interface went down before event propagated, skipping IGMP config
|
||||
@@ -135,7 +155,7 @@ static esp_err_t _udp_join_group(mdns_if_t if_inx, mdns_ip_protocol_t ip_protoco
|
||||
* @brief the receive callback of the raw udp api. Packets are received here
|
||||
*
|
||||
*/
|
||||
static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport)
|
||||
static void receive(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip_addr_t *raddr, uint16_t rport)
|
||||
{
|
||||
|
||||
uint8_t i;
|
||||
@@ -188,7 +208,7 @@ static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip
|
||||
struct netif *netif = NULL;
|
||||
bool found = false;
|
||||
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
|
||||
netif = esp_netif_get_netif_impl(_mdns_get_esp_netif(i));
|
||||
netif = esp_netif_get_netif_impl(mdns_priv_get_esp_netif(i));
|
||||
if (s_interfaces[i].proto && netif && netif == ip_current_input_netif()) {
|
||||
#if LWIP_IPV4
|
||||
if (packet->src.type == IPADDR_TYPE_V4) {
|
||||
@@ -204,7 +224,7 @@ static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip
|
||||
}
|
||||
}
|
||||
|
||||
if (!found || _mdns_send_rx_action(packet) != ESP_OK) {
|
||||
if (!found || send_rx_action(packet) != ESP_OK) {
|
||||
pbuf_free(this_pb);
|
||||
mdns_mem_free(packet);
|
||||
}
|
||||
@@ -212,7 +232,7 @@ static void _udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *pb, const ip
|
||||
|
||||
}
|
||||
|
||||
bool mdns_is_netif_ready(mdns_if_t netif, mdns_ip_protocol_t ip_proto)
|
||||
bool mdns_priv_if_ready(mdns_if_t netif, mdns_ip_protocol_t ip_proto)
|
||||
{
|
||||
return s_interfaces[netif].ready &&
|
||||
s_interfaces[netif].proto & (ip_proto == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
|
||||
@@ -221,12 +241,12 @@ bool mdns_is_netif_ready(mdns_if_t netif, mdns_ip_protocol_t ip_proto)
|
||||
/**
|
||||
* @brief Check if any of the interfaces is up
|
||||
*/
|
||||
static bool _udp_pcb_is_in_use(void)
|
||||
static bool is_any_pcb_in_use(void)
|
||||
{
|
||||
int i, p;
|
||||
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
|
||||
for (p = 0; p < MDNS_IP_PROTOCOL_MAX; p++) {
|
||||
if (mdns_is_netif_ready(i, p)) {
|
||||
if (mdns_priv_if_ready(i, p)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -237,14 +257,14 @@ static bool _udp_pcb_is_in_use(void)
|
||||
/**
|
||||
* @brief Stop PCB Main code
|
||||
*/
|
||||
static void _udp_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
static void pcb_if_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
s_interfaces[tcpip_if].proto &= ~(ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
|
||||
if (s_interfaces[tcpip_if].proto == 0) {
|
||||
s_interfaces[tcpip_if].ready = false;
|
||||
_udp_join_group(tcpip_if, ip_protocol, false);
|
||||
if (!_udp_pcb_is_in_use()) {
|
||||
_udp_pcb_main_deinit();
|
||||
join_group(tcpip_if, ip_protocol, false);
|
||||
if (!is_any_pcb_in_use()) {
|
||||
pcb_deinit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -252,18 +272,18 @@ static void _udp_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
/**
|
||||
* @brief Start PCB Main code
|
||||
*/
|
||||
static esp_err_t _udp_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
static esp_err_t pcb_if_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
if (mdns_is_netif_ready(tcpip_if, ip_protocol)) {
|
||||
if (mdns_priv_if_ready(tcpip_if, ip_protocol)) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_err_t err = _udp_join_group(tcpip_if, ip_protocol, true);
|
||||
esp_err_t err = join_group(tcpip_if, ip_protocol, true);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = _udp_pcb_main_init();
|
||||
err = pcb_init();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
@@ -286,20 +306,20 @@ typedef struct {
|
||||
/**
|
||||
* @brief Start PCB from LwIP thread
|
||||
*/
|
||||
static err_t _mdns_pcb_init_api(struct tcpip_api_call_data *api_call_msg)
|
||||
static err_t pcb_if_init_lwip(struct tcpip_api_call_data *api_call_msg)
|
||||
{
|
||||
mdns_api_call_t *msg = (mdns_api_call_t *)api_call_msg;
|
||||
msg->err = _udp_pcb_init(msg->tcpip_if, msg->ip_protocol) == ESP_OK ? ERR_OK : ERR_IF;
|
||||
return msg->err;
|
||||
msg->err = pcb_if_init(msg->tcpip_if, msg->ip_protocol);
|
||||
return msg->err == ESP_OK ? ERR_OK : ERR_IF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop PCB from LwIP thread
|
||||
*/
|
||||
static err_t _mdns_pcb_deinit_api(struct tcpip_api_call_data *api_call_msg)
|
||||
static err_t pcb_if_deinit_lwip(struct tcpip_api_call_data *api_call_msg)
|
||||
{
|
||||
mdns_api_call_t *msg = (mdns_api_call_t *)api_call_msg;
|
||||
_udp_pcb_deinit(msg->tcpip_if, msg->ip_protocol);
|
||||
pcb_if_deinit(msg->tcpip_if, msg->ip_protocol);
|
||||
msg->err = ESP_OK;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -309,43 +329,43 @@ static err_t _mdns_pcb_deinit_api(struct tcpip_api_call_data *api_call_msg)
|
||||
* - _mdns prefixed
|
||||
* - commented in mdns_networking.h header
|
||||
*/
|
||||
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
esp_err_t mdns_priv_if_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
mdns_api_call_t msg = {
|
||||
.tcpip_if = tcpip_if,
|
||||
.ip_protocol = ip_protocol
|
||||
};
|
||||
tcpip_api_call(_mdns_pcb_init_api, &msg.call);
|
||||
tcpip_api_call(pcb_if_init_lwip, &msg.call);
|
||||
return msg.err;
|
||||
}
|
||||
|
||||
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
esp_err_t mdns_priv_if_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
mdns_api_call_t msg = {
|
||||
.tcpip_if = tcpip_if,
|
||||
.ip_protocol = ip_protocol
|
||||
};
|
||||
tcpip_api_call(_mdns_pcb_deinit_api, &msg.call);
|
||||
tcpip_api_call(pcb_if_deinit_lwip, &msg.call);
|
||||
return msg.err;
|
||||
}
|
||||
|
||||
static err_t _mdns_udp_pcb_write_api(struct tcpip_api_call_data *api_call_msg)
|
||||
static err_t write_if_lwip(struct tcpip_api_call_data *api_call_msg)
|
||||
{
|
||||
void *nif = NULL;
|
||||
mdns_api_call_t *msg = (mdns_api_call_t *)api_call_msg;
|
||||
nif = esp_netif_get_netif_impl(_mdns_get_esp_netif(msg->tcpip_if));
|
||||
if (!nif || !mdns_is_netif_ready(msg->tcpip_if, msg->ip_protocol) || _pcb_main == NULL) {
|
||||
nif = esp_netif_get_netif_impl(mdns_priv_get_esp_netif(msg->tcpip_if));
|
||||
if (!nif || !mdns_priv_if_ready(msg->tcpip_if, msg->ip_protocol) || s_pcb_main == NULL) {
|
||||
pbuf_free(msg->pbt);
|
||||
msg->err = ERR_IF;
|
||||
return ERR_IF;
|
||||
}
|
||||
esp_err_t err = udp_sendto_if(_pcb_main, msg->pbt, msg->ip, msg->port, (struct netif *)nif);
|
||||
err_t err = udp_sendto_if(s_pcb_main, msg->pbt, msg->ip, msg->port, (struct netif *)nif);
|
||||
pbuf_free(msg->pbt);
|
||||
msg->err = err;
|
||||
msg->err = err == ERR_OK ? ESP_OK : ESP_FAIL;
|
||||
return err;
|
||||
}
|
||||
|
||||
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len)
|
||||
size_t mdns_priv_if_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len)
|
||||
{
|
||||
struct pbuf *pbt = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
|
||||
if (pbt == NULL) {
|
||||
@@ -373,7 +393,7 @@ size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, c
|
||||
.ip = &ip_add_copy,
|
||||
.port = port
|
||||
};
|
||||
tcpip_api_call(_mdns_udp_pcb_write_api, &msg.call);
|
||||
tcpip_api_call(write_if_lwip, &msg.call);
|
||||
|
||||
if (msg.err) {
|
||||
return 0;
|
||||
@@ -381,17 +401,17 @@ size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, c
|
||||
return len;
|
||||
}
|
||||
|
||||
void *_mdns_get_packet_data(mdns_rx_packet_t *packet)
|
||||
void *mdns_priv_get_packet_data(mdns_rx_packet_t *packet)
|
||||
{
|
||||
return packet->pb->payload;
|
||||
}
|
||||
|
||||
size_t _mdns_get_packet_len(mdns_rx_packet_t *packet)
|
||||
size_t mdns_priv_get_packet_len(mdns_rx_packet_t *packet)
|
||||
{
|
||||
return packet->pb->len;
|
||||
}
|
||||
|
||||
void _mdns_packet_free(mdns_rx_packet_t *packet)
|
||||
void mdns_priv_packet_free(mdns_rx_packet_t *packet)
|
||||
{
|
||||
pbuf_free(packet->pb);
|
||||
mdns_mem_free(packet);
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#include <sys/param.h>
|
||||
#include "esp_log.h"
|
||||
#include "mdns_mem_caps.h"
|
||||
#include "mdns_utils.h"
|
||||
#include "mdns_netif.h"
|
||||
#include "mdns_service.h"
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_LINUX)
|
||||
#include <sys/ioctl.h>
|
||||
@@ -58,6 +61,26 @@ struct pbuf {
|
||||
#define s6_addr32 un.u32_addr
|
||||
#endif // CONFIG_IDF_TARGET_LINUX
|
||||
|
||||
static esp_err_t send_rx_action(mdns_rx_packet_t *packet)
|
||||
{
|
||||
mdns_action_t *action = NULL;
|
||||
|
||||
action = (mdns_action_t *)mdns_mem_malloc(sizeof(mdns_action_t));
|
||||
if (!action) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
action->type = ACTION_RX_HANDLE;
|
||||
action->data.rx_handle.packet = packet;
|
||||
if (!mdns_priv_queue_action(action)) {
|
||||
mdns_mem_free(action);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static void __attribute__((constructor)) ctor_networking_socket(void)
|
||||
{
|
||||
for (int i = 0; i < sizeof(s_interfaces) / sizeof(s_interfaces[0]); ++i) {
|
||||
@@ -71,29 +94,29 @@ static void delete_socket(int sock)
|
||||
close(sock);
|
||||
}
|
||||
|
||||
bool mdns_is_netif_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
bool mdns_priv_if_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
return s_interfaces[tcpip_if].proto & (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
|
||||
}
|
||||
|
||||
void *_mdns_get_packet_data(mdns_rx_packet_t *packet)
|
||||
void *mdns_priv_get_packet_data(mdns_rx_packet_t *packet)
|
||||
{
|
||||
return packet->pb->payload;
|
||||
}
|
||||
|
||||
size_t _mdns_get_packet_len(mdns_rx_packet_t *packet)
|
||||
size_t mdns_priv_get_packet_len(mdns_rx_packet_t *packet)
|
||||
{
|
||||
return packet->pb->len;
|
||||
}
|
||||
|
||||
void _mdns_packet_free(mdns_rx_packet_t *packet)
|
||||
void mdns_priv_packet_free(mdns_rx_packet_t *packet)
|
||||
{
|
||||
mdns_mem_free(packet->pb->payload);
|
||||
mdns_mem_free(packet->pb);
|
||||
mdns_mem_free(packet);
|
||||
}
|
||||
|
||||
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
esp_err_t mdns_priv_if_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
s_interfaces[tcpip_if].proto &= ~(ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6);
|
||||
if (s_interfaces[tcpip_if].proto == 0) {
|
||||
@@ -192,7 +215,7 @@ static inline size_t espaddr_to_inet(const esp_ip_addr_t *addr, const uint16_t p
|
||||
return ss_addr_len;
|
||||
}
|
||||
|
||||
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len)
|
||||
size_t mdns_priv_if_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len)
|
||||
{
|
||||
if (!(s_interfaces[tcpip_if].proto & (ip_protocol == MDNS_IP_PROTOCOL_V4 ? PROTO_IPV4 : PROTO_IPV6))) {
|
||||
return 0;
|
||||
@@ -210,7 +233,7 @@ size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, c
|
||||
ESP_LOGD(TAG, "[sock=%d]: Sending to IP %s port %d", sock, get_string_address(&in_addr), port);
|
||||
ssize_t actual_len = sendto(sock, data, len, 0, (struct sockaddr *)&in_addr, ss_size);
|
||||
if (actual_len < 0) {
|
||||
ESP_LOGE(TAG, "[sock=%d]: _mdns_udp_pcb_write sendto() has failed\n errno=%d: %s", sock, errno, strerror(errno));
|
||||
ESP_LOGE(TAG, "[sock=%d]: mdns_priv_if_write sendto() has failed\n errno=%d: %s", sock, errno, strerror(errno));
|
||||
}
|
||||
return actual_len;
|
||||
}
|
||||
@@ -325,8 +348,8 @@ void sock_recv_task(void *arg)
|
||||
packet->dest.type = packet->src.type;
|
||||
packet->ip_protocol =
|
||||
packet->src.type == ESP_IPADDR_TYPE_V4 ? MDNS_IP_PROTOCOL_V4 : MDNS_IP_PROTOCOL_V6;
|
||||
if (_mdns_send_rx_action(packet) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "_mdns_send_rx_action failed!");
|
||||
if (send_rx_action(packet) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "send_rx_action failed!");
|
||||
mdns_mem_free(packet->pb->payload);
|
||||
mdns_mem_free(packet->pb);
|
||||
mdns_mem_free(packet);
|
||||
@@ -338,7 +361,7 @@ void sock_recv_task(void *arg)
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void mdns_networking_init(void)
|
||||
static void networking_init(void)
|
||||
{
|
||||
if (s_run_sock_recv_task == false) {
|
||||
s_run_sock_recv_task = true;
|
||||
@@ -352,7 +375,7 @@ static bool create_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
return true;
|
||||
}
|
||||
int sock = s_interfaces[tcpip_if].sock;
|
||||
esp_netif_t *netif = _mdns_get_esp_netif(tcpip_if);
|
||||
esp_netif_t *netif = mdns_priv_get_esp_netif(tcpip_if);
|
||||
if (sock < 0) {
|
||||
sock = create_socket(netif);
|
||||
}
|
||||
@@ -369,14 +392,14 @@ static bool create_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
esp_err_t mdns_priv_if_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
ESP_LOGI(TAG, "_mdns_pcb_init(tcpip_if=%lu, ip_protocol=%lu)", (unsigned long)tcpip_if, (unsigned long)ip_protocol);
|
||||
ESP_LOGI(TAG, "mdns_priv_if_init(tcpip_if=%lu, ip_protocol=%lu)", (unsigned long)tcpip_if, (unsigned long)ip_protocol);
|
||||
if (!create_pcb(tcpip_if, ip_protocol)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
mdns_networking_init();
|
||||
networking_init();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
462
components/mdns/mdns_pcb.c
Normal file
462
components/mdns/mdns_pcb.c
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "mdns_private.h"
|
||||
#include "mdns_networking.h"
|
||||
#include "mdns_pcb.h"
|
||||
#include "mdns_netif.h"
|
||||
#include "mdns_utils.h"
|
||||
#include "mdns_send.h"
|
||||
#include "mdns_mem_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_random.h"
|
||||
#include "mdns_responder.h"
|
||||
|
||||
#define PCB_STATE_IS_PROBING(s) (s->state > PCB_OFF && s->state < PCB_ANNOUNCE_1)
|
||||
#define PCB_STATE_IS_ANNOUNCING(s) (s->state > PCB_PROBE_3 && s->state < PCB_RUNNING)
|
||||
|
||||
typedef enum {
|
||||
PCB_OFF, PCB_DUP, PCB_INIT,
|
||||
PCB_PROBE_1, PCB_PROBE_2, PCB_PROBE_3,
|
||||
PCB_ANNOUNCE_1, PCB_ANNOUNCE_2, PCB_ANNOUNCE_3,
|
||||
PCB_RUNNING
|
||||
} mdns_pcb_state_t;
|
||||
|
||||
typedef struct {
|
||||
mdns_pcb_state_t state;
|
||||
mdns_srv_item_t **probe_services;
|
||||
uint8_t probe_services_len;
|
||||
uint8_t probe_ip;
|
||||
uint8_t probe_running;
|
||||
uint16_t failed_probes;
|
||||
} mdns_pcb_t;
|
||||
|
||||
static const char *TAG = "mdns_pcb";
|
||||
static mdns_pcb_t s_pcbs[MDNS_MAX_INTERFACES][MDNS_IP_PROTOCOL_MAX];
|
||||
|
||||
/**
|
||||
* @brief Send announcement on particular PCB
|
||||
*/
|
||||
void mdns_priv_pcb_announce(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t **services, size_t len, bool include_ip)
|
||||
{
|
||||
mdns_pcb_t *_pcb = &s_pcbs[tcpip_if][ip_protocol];
|
||||
size_t i;
|
||||
if (mdns_priv_if_ready(tcpip_if, ip_protocol)) {
|
||||
if (PCB_STATE_IS_PROBING(_pcb)) {
|
||||
mdns_priv_init_pcb_probe(tcpip_if, ip_protocol, services, len, include_ip);
|
||||
} else if (PCB_STATE_IS_ANNOUNCING(_pcb)) {
|
||||
mdns_tx_packet_t *p = mdns_priv_get_next_packet(tcpip_if, ip_protocol);
|
||||
if (p) {
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!mdns_priv_create_answer(&p->answers, MDNS_TYPE_SDPTR, services[i]->service, NULL, false, false)
|
||||
|| !mdns_priv_create_answer(&p->answers, MDNS_TYPE_PTR, services[i]->service, NULL, false,
|
||||
false)
|
||||
|| !mdns_priv_create_answer(&p->answers, MDNS_TYPE_SRV, services[i]->service, NULL, true,
|
||||
false)
|
||||
|| !mdns_priv_create_answer(&p->answers, MDNS_TYPE_TXT, services[i]->service, NULL, true,
|
||||
false)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (include_ip) {
|
||||
mdns_priv_dealloc_answer(&p->additional, MDNS_TYPE_A, NULL);
|
||||
mdns_priv_dealloc_answer(&p->additional, MDNS_TYPE_AAAA, NULL);
|
||||
mdns_priv_append_host_list_in_services(&p->answers, services, len, true, false);
|
||||
}
|
||||
_pcb->state = PCB_ANNOUNCE_1;
|
||||
}
|
||||
} else if (_pcb->state == PCB_RUNNING) {
|
||||
|
||||
if (mdns_utils_str_null_or_empty(mdns_priv_get_global_hostname())) {
|
||||
return;
|
||||
}
|
||||
|
||||
_pcb->state = PCB_ANNOUNCE_1;
|
||||
mdns_tx_packet_t *p = mdns_priv_create_announce_packet(tcpip_if, ip_protocol, services, len, include_ip);
|
||||
if (p) {
|
||||
mdns_priv_send_after(p, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if interface is duplicate (two interfaces on the same subnet)
|
||||
*/
|
||||
bool mdns_priv_pcb_check_for_duplicates(mdns_if_t tcpip_if)
|
||||
{
|
||||
mdns_if_t ifaces[MDNS_MAX_INTERFACES] = {tcpip_if, mdns_priv_netif_get_other_interface(tcpip_if) };
|
||||
if (ifaces[1] == MDNS_MAX_INTERFACES) {
|
||||
return false;
|
||||
}
|
||||
// check both this netif and the potential duplicate on all protocols
|
||||
// if any of them is in duplicate state, return true
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int proto = 0; proto < MDNS_IP_PROTOCOL_MAX; proto++) {
|
||||
if (s_pcbs[ifaces[i]][(mdns_ip_protocol_t) proto].state == PCB_DUP) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static esp_err_t deinit_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_proto)
|
||||
{
|
||||
esp_err_t err = mdns_priv_if_deinit(tcpip_if, ip_proto);
|
||||
mdns_pcb_t *pcb = &s_pcbs[tcpip_if][ip_proto];
|
||||
if (pcb == NULL || err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
mdns_mem_free(pcb->probe_services);
|
||||
pcb->state = PCB_OFF;
|
||||
pcb->probe_ip = false;
|
||||
pcb->probe_services = NULL;
|
||||
pcb->probe_services_len = 0;
|
||||
pcb->probe_running = false;
|
||||
pcb->failed_probes = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restart the responder on particular PCB
|
||||
*/
|
||||
static void restart_pcb(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
size_t srv_count = 0;
|
||||
mdns_srv_item_t *a = mdns_priv_get_services();
|
||||
while (a) {
|
||||
srv_count++;
|
||||
a = a->next;
|
||||
}
|
||||
if (srv_count == 0) {
|
||||
// proble only IP
|
||||
mdns_priv_init_pcb_probe(tcpip_if, ip_protocol, NULL, 0, true);
|
||||
return;
|
||||
}
|
||||
mdns_srv_item_t *services[srv_count];
|
||||
size_t i = 0;
|
||||
a = mdns_priv_get_services();
|
||||
while (a) {
|
||||
services[i++] = a;
|
||||
a = a->next;
|
||||
}
|
||||
mdns_priv_init_pcb_probe(tcpip_if, ip_protocol, services, srv_count, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable mDNS interface
|
||||
*/
|
||||
void mdns_priv_pcb_disable(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
mdns_priv_netif_disable(tcpip_if);
|
||||
|
||||
if (mdns_priv_if_ready(tcpip_if, ip_protocol)) {
|
||||
mdns_priv_clear_tx_queue_if(tcpip_if, ip_protocol);
|
||||
deinit_pcb(tcpip_if, ip_protocol);
|
||||
mdns_if_t other_if = mdns_priv_netif_get_other_interface(tcpip_if);
|
||||
if (other_if != MDNS_MAX_INTERFACES && s_pcbs[other_if][ip_protocol].state == PCB_DUP) {
|
||||
s_pcbs[other_if][ip_protocol].state = PCB_OFF;
|
||||
mdns_priv_pcb_enable(other_if, ip_protocol);
|
||||
}
|
||||
}
|
||||
s_pcbs[tcpip_if][ip_protocol].state = PCB_OFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable mDNS interface
|
||||
*/
|
||||
void mdns_priv_pcb_enable(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
if (!mdns_priv_if_ready(tcpip_if, ip_protocol)) {
|
||||
if (mdns_priv_if_init(tcpip_if, ip_protocol)) {
|
||||
s_pcbs[tcpip_if][ip_protocol].failed_probes = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
restart_pcb(tcpip_if, ip_protocol);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set interface as duplicate if another is found on the same subnet
|
||||
*/
|
||||
void mdns_priv_pcb_set_duplicate(mdns_if_t tcpip_if)
|
||||
{
|
||||
uint8_t i;
|
||||
mdns_if_t other_if = mdns_priv_netif_get_other_interface(tcpip_if);
|
||||
if (other_if == MDNS_MAX_INTERFACES) {
|
||||
return; // no other interface found
|
||||
}
|
||||
for (i = 0; i < MDNS_IP_PROTOCOL_MAX; i++) {
|
||||
if (mdns_priv_if_ready(other_if, i)) {
|
||||
//stop this interface and mark as dup
|
||||
if (mdns_priv_if_ready(tcpip_if, i)) {
|
||||
mdns_priv_clear_tx_queue_if(tcpip_if, i);
|
||||
deinit_pcb(tcpip_if, i);
|
||||
}
|
||||
s_pcbs[tcpip_if][i].state = PCB_DUP;
|
||||
mdns_priv_pcb_announce(other_if, i, NULL, 0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool mdns_priv_pcb_is_off(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
return s_pcbs[tcpip_if][ip_protocol].state == PCB_OFF;
|
||||
}
|
||||
|
||||
void mdns_priv_pcb_schedule_tx_packet(mdns_tx_packet_t *p)
|
||||
{
|
||||
mdns_pcb_t *pcb = &s_pcbs[p->tcpip_if][p->ip_protocol];
|
||||
mdns_out_question_t *q = NULL;
|
||||
mdns_tx_packet_t *a = NULL;
|
||||
uint32_t send_after = 1000;
|
||||
switch (pcb->state) {
|
||||
case PCB_PROBE_1:
|
||||
q = p->questions;
|
||||
while (q) {
|
||||
q->unicast = false;
|
||||
q = q->next;
|
||||
}
|
||||
//fallthrough
|
||||
case PCB_PROBE_2:
|
||||
mdns_priv_send_after(p, 250);
|
||||
pcb->state = (mdns_pcb_state_t)((uint8_t)(pcb->state) + 1);
|
||||
break;
|
||||
case PCB_PROBE_3:
|
||||
a = mdns_priv_create_announce_from_probe(p);
|
||||
if (!a) {
|
||||
mdns_priv_send_after(p, 250);
|
||||
break;
|
||||
}
|
||||
pcb->probe_running = false;
|
||||
pcb->probe_ip = false;
|
||||
pcb->probe_services_len = 0;
|
||||
pcb->failed_probes = 0;
|
||||
mdns_mem_free(pcb->probe_services);
|
||||
pcb->probe_services = NULL;
|
||||
mdns_priv_free_tx_packet(p);
|
||||
p = a;
|
||||
send_after = 250;
|
||||
//fallthrough
|
||||
case PCB_ANNOUNCE_1:
|
||||
//fallthrough
|
||||
case PCB_ANNOUNCE_2:
|
||||
mdns_priv_send_after(p, send_after);
|
||||
pcb->state = (mdns_pcb_state_t)((uint8_t)(pcb->state) + 1);
|
||||
break;
|
||||
case PCB_ANNOUNCE_3:
|
||||
pcb->state = PCB_RUNNING;
|
||||
mdns_priv_free_tx_packet(p);
|
||||
break;
|
||||
default:
|
||||
mdns_priv_free_tx_packet(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void mdns_priv_pcb_check_probing_services(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_service_t *service, bool removed_answers, bool *should_remove_questions)
|
||||
{
|
||||
mdns_pcb_t *_pcb = &s_pcbs[tcpip_if][ip_protocol];
|
||||
|
||||
if (PCB_STATE_IS_PROBING(_pcb)) {
|
||||
uint8_t i;
|
||||
//check if we are probing this service
|
||||
for (i = 0; i < _pcb->probe_services_len; i++) {
|
||||
mdns_srv_item_t *s = _pcb->probe_services[i];
|
||||
if (s->service == service) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < _pcb->probe_services_len) {
|
||||
if (_pcb->probe_services_len > 1) {
|
||||
uint8_t n;
|
||||
for (n = (i + 1); n < _pcb->probe_services_len; n++) {
|
||||
_pcb->probe_services[n - 1] = _pcb->probe_services[n];
|
||||
}
|
||||
_pcb->probe_services_len--;
|
||||
} else {
|
||||
_pcb->probe_services_len = 0;
|
||||
mdns_mem_free(_pcb->probe_services);
|
||||
_pcb->probe_services = NULL;
|
||||
if (!_pcb->probe_ip) {
|
||||
_pcb->probe_running = false;
|
||||
_pcb->state = PCB_RUNNING;
|
||||
}
|
||||
}
|
||||
*should_remove_questions = true;
|
||||
return;
|
||||
}
|
||||
} else if (PCB_STATE_IS_ANNOUNCING(_pcb)) {
|
||||
//if answers were cleared, set to running
|
||||
if (removed_answers) {
|
||||
_pcb->state = PCB_RUNNING;
|
||||
}
|
||||
}
|
||||
*should_remove_questions = false;
|
||||
}
|
||||
|
||||
void mdns_priv_pcb_deinit(void)
|
||||
{
|
||||
for (int i = 0; i < MDNS_MAX_INTERFACES; i++) {
|
||||
for (int j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) {
|
||||
deinit_pcb(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool mdsn_priv_pcb_is_inited(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
return mdns_priv_if_ready(tcpip_if, ip_protocol) && s_pcbs[tcpip_if][ip_protocol].state > PCB_INIT;
|
||||
}
|
||||
|
||||
bool mdns_priv_pcb_is_duplicate(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
return s_pcbs[tcpip_if][ip_protocol].state == PCB_DUP;
|
||||
}
|
||||
|
||||
bool mdns_priv_pcb_is_probing(mdns_rx_packet_t *packet)
|
||||
{
|
||||
return s_pcbs[packet->tcpip_if][packet->ip_protocol].probe_running;
|
||||
}
|
||||
|
||||
bool mdns_priv_pcb_is_after_probing(mdns_rx_packet_t *packet)
|
||||
{
|
||||
return s_pcbs[packet->tcpip_if][packet->ip_protocol].state > PCB_PROBE_3;
|
||||
}
|
||||
|
||||
void mdns_priv_pcb_set_probe_failed(mdns_rx_packet_t *packet)
|
||||
{
|
||||
s_pcbs[packet->tcpip_if][packet->ip_protocol].failed_probes++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send probe for additional services on particular PCB
|
||||
*/
|
||||
static void init_probe_new_service(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t **services, size_t len, bool probe_ip)
|
||||
{
|
||||
mdns_pcb_t *pcb = &s_pcbs[tcpip_if][ip_protocol];
|
||||
size_t services_final_len = len;
|
||||
|
||||
if (PCB_STATE_IS_PROBING(pcb)) {
|
||||
services_final_len += pcb->probe_services_len;
|
||||
}
|
||||
mdns_srv_item_t **s = NULL;
|
||||
if (services_final_len) {
|
||||
s = (mdns_srv_item_t **)mdns_mem_malloc(sizeof(mdns_srv_item_t *) * services_final_len);
|
||||
if (!s) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < len; i++) {
|
||||
s[i] = services[i];
|
||||
}
|
||||
if (pcb->probe_services) {
|
||||
for (i = 0; i < pcb->probe_services_len; i++) {
|
||||
s[len + i] = pcb->probe_services[i];
|
||||
}
|
||||
mdns_mem_free(pcb->probe_services);
|
||||
}
|
||||
}
|
||||
|
||||
probe_ip = pcb->probe_ip || probe_ip;
|
||||
|
||||
pcb->probe_ip = false;
|
||||
pcb->probe_services = NULL;
|
||||
pcb->probe_services_len = 0;
|
||||
pcb->probe_running = false;
|
||||
|
||||
mdns_tx_packet_t *packet = mdns_priv_create_probe_packet(tcpip_if, ip_protocol, s, services_final_len, true,
|
||||
probe_ip);
|
||||
if (!packet) {
|
||||
mdns_mem_free(s);
|
||||
return;
|
||||
}
|
||||
|
||||
pcb->probe_ip = probe_ip;
|
||||
pcb->probe_services = s;
|
||||
pcb->probe_services_len = services_final_len;
|
||||
pcb->probe_running = true;
|
||||
mdns_priv_send_after(packet, ((pcb->failed_probes > 5) ? 1000 : 120) + (esp_random() & 0x7F));
|
||||
pcb->state = PCB_PROBE_1;
|
||||
}
|
||||
|
||||
void mdns_priv_init_pcb_probe(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t **services, size_t len, bool probe_ip)
|
||||
{
|
||||
mdns_pcb_t *pcb = &s_pcbs[tcpip_if][ip_protocol];
|
||||
|
||||
mdns_priv_clear_tx_queue_if(tcpip_if, ip_protocol);
|
||||
|
||||
if (mdns_utils_str_null_or_empty(mdns_priv_get_global_hostname())) {
|
||||
pcb->state = PCB_RUNNING;
|
||||
return;
|
||||
}
|
||||
|
||||
if (PCB_STATE_IS_PROBING(pcb)) {
|
||||
// Looking for already probing services to resolve duplications
|
||||
mdns_srv_item_t *new_probe_services[len];
|
||||
int new_probe_service_len = 0;
|
||||
bool found;
|
||||
for (size_t j = 0; j < len; ++j) {
|
||||
found = false;
|
||||
for (int i = 0; i < pcb->probe_services_len; ++i) {
|
||||
if (pcb->probe_services[i] == services[j]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
new_probe_services[new_probe_service_len++] = services[j];
|
||||
}
|
||||
}
|
||||
// init probing for newly added services
|
||||
init_probe_new_service(tcpip_if, ip_protocol,
|
||||
new_probe_service_len ? new_probe_services : NULL, new_probe_service_len, probe_ip);
|
||||
} else {
|
||||
// not probing, so init for all services
|
||||
init_probe_new_service(tcpip_if, ip_protocol, services, len, probe_ip);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send by for particular services
|
||||
*/
|
||||
void mdns_priv_pcb_send_bye_service(mdns_srv_item_t **services, size_t len, bool include_ip)
|
||||
{
|
||||
uint8_t i, j;
|
||||
if (mdns_utils_str_null_or_empty(mdns_priv_get_global_hostname())) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
|
||||
for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) {
|
||||
if (mdns_priv_if_ready(i, j) && s_pcbs[i][j].state == PCB_RUNNING) {
|
||||
mdns_priv_send_bye((mdns_if_t) i, (mdns_ip_protocol_t) j, services, len, include_ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mdns_priv_probe_all_pcbs(mdns_srv_item_t **services, size_t len, bool probe_ip, bool clear_old_probe)
|
||||
{
|
||||
uint8_t i, j;
|
||||
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
|
||||
for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) {
|
||||
if (mdns_priv_if_ready(i, j)) {
|
||||
mdns_pcb_t *_pcb = &s_pcbs[i][j];
|
||||
if (clear_old_probe) {
|
||||
mdns_mem_free(_pcb->probe_services);
|
||||
_pcb->probe_services = NULL;
|
||||
_pcb->probe_services_len = 0;
|
||||
_pcb->probe_running = false;
|
||||
}
|
||||
mdns_priv_init_pcb_probe((mdns_if_t) i, (mdns_ip_protocol_t) j, services, len, probe_ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
872
components/mdns/mdns_querier.c
Normal file
872
components/mdns/mdns_querier.c
Normal file
@@ -0,0 +1,872 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "mdns_private.h"
|
||||
#include "mdns_querier.h"
|
||||
#include "mdns_mem_caps.h"
|
||||
#include "mdns_utils.h"
|
||||
#include "mdns_send.h"
|
||||
#include "esp_log.h"
|
||||
#include "mdns_pcb.h"
|
||||
#include "mdns_netif.h"
|
||||
#include "mdns_responder.h"
|
||||
#include "mdns_service.h"
|
||||
|
||||
static const char *TAG = "mdns_querier";
|
||||
static mdns_search_once_t *s_search_once;
|
||||
|
||||
static esp_err_t send_search_action(mdns_action_type_t type, mdns_search_once_t *search);
|
||||
static void search_free(mdns_search_once_t *search);
|
||||
|
||||
void mdns_priv_query_results_free(mdns_result_t *results)
|
||||
{
|
||||
mdns_result_t *r;
|
||||
mdns_ip_addr_t *a;
|
||||
|
||||
while (results) {
|
||||
r = results;
|
||||
|
||||
mdns_mem_free((char *)(r->hostname));
|
||||
mdns_mem_free((char *)(r->instance_name));
|
||||
mdns_mem_free((char *)(r->service_type));
|
||||
mdns_mem_free((char *)(r->proto));
|
||||
|
||||
for (size_t i = 0; i < r->txt_count; i++) {
|
||||
mdns_mem_free((char *)(r->txt[i].key));
|
||||
mdns_mem_free((char *)(r->txt[i].value));
|
||||
}
|
||||
mdns_mem_free(r->txt);
|
||||
mdns_mem_free(r->txt_value_len);
|
||||
|
||||
while (r->addr) {
|
||||
a = r->addr;
|
||||
r->addr = r->addr->next;
|
||||
mdns_mem_free(a);
|
||||
}
|
||||
|
||||
results = results->next;
|
||||
mdns_mem_free(r);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Mark search as finished and remove it from search chain
|
||||
*/
|
||||
static void search_finish(mdns_search_once_t *search)
|
||||
{
|
||||
search->state = SEARCH_OFF;
|
||||
queueDetach(mdns_search_once_t, s_search_once, search);
|
||||
if (search->notifier) {
|
||||
search->notifier(search);
|
||||
}
|
||||
xSemaphoreGive(search->done_semaphore);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add new search to the search chain
|
||||
*/
|
||||
void search_add(mdns_search_once_t *search)
|
||||
{
|
||||
search->next = s_search_once;
|
||||
s_search_once = search;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send search packet to all available interfaces
|
||||
*/
|
||||
static void search_send(mdns_search_once_t *search)
|
||||
{
|
||||
mdns_search_once_t *queue = s_search_once;
|
||||
bool found = false;
|
||||
// looking for this search in active searches
|
||||
while (queue) {
|
||||
if (queue == search) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
queue = queue->next;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// no longer active -> skip sending this search
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t i, j;
|
||||
for (i = 0; i < MDNS_MAX_INTERFACES; i++) {
|
||||
for (j = 0; j < MDNS_IP_PROTOCOL_MAX; j++) {
|
||||
mdns_priv_query_send(search, (mdns_if_t) i, (mdns_ip_protocol_t) j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mdns_priv_query_action(mdns_action_t *action, mdns_action_subtype_t type)
|
||||
{
|
||||
if (type == ACTION_RUN) {
|
||||
switch (action->type) {
|
||||
case ACTION_SEARCH_ADD:
|
||||
search_add(action->data.search_add.search);
|
||||
break;
|
||||
case ACTION_SEARCH_SEND:
|
||||
search_send(action->data.search_add.search);
|
||||
break;
|
||||
case ACTION_SEARCH_END:
|
||||
search_finish(action->data.search_add.search);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (type == ACTION_CLEANUP) {
|
||||
search_free(action->data.search_add.search);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from timer task to run active searches
|
||||
*/
|
||||
void mdns_priv_query_start_stop(void)
|
||||
{
|
||||
mdns_priv_service_lock();
|
||||
mdns_search_once_t *s = s_search_once;
|
||||
uint32_t now = xTaskGetTickCount() * portTICK_PERIOD_MS;
|
||||
if (!s) {
|
||||
mdns_priv_service_unlock();
|
||||
return;
|
||||
}
|
||||
while (s) {
|
||||
if (s->state != SEARCH_OFF) {
|
||||
if (now > (s->started_at + s->timeout)) {
|
||||
s->state = SEARCH_OFF;
|
||||
if (send_search_action(ACTION_SEARCH_END, s) != ESP_OK) {
|
||||
s->state = SEARCH_RUNNING;
|
||||
}
|
||||
} else if (s->state == SEARCH_INIT || (now - s->sent_at) > 1000) {
|
||||
s->state = SEARCH_RUNNING;
|
||||
s->sent_at = now;
|
||||
if (send_search_action(ACTION_SEARCH_SEND, s) != ESP_OK) {
|
||||
s->sent_at -= 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
s = s->next;
|
||||
}
|
||||
mdns_priv_service_unlock();
|
||||
}
|
||||
|
||||
void mdns_priv_query_free(void)
|
||||
{
|
||||
while (s_search_once) {
|
||||
mdns_search_once_t *h = s_search_once;
|
||||
s_search_once = h->next;
|
||||
mdns_mem_free(h->instance);
|
||||
mdns_mem_free(h->service);
|
||||
mdns_mem_free(h->proto);
|
||||
vSemaphoreDelete(h->done_semaphore);
|
||||
if (h->result) {
|
||||
mdns_priv_query_results_free(h->result);
|
||||
}
|
||||
mdns_mem_free(h);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from parser to finish any searches that have reached maximum results
|
||||
*/
|
||||
void mdns_priv_query_done(void)
|
||||
{
|
||||
mdns_search_once_t *search = s_search_once;
|
||||
mdns_search_once_t *s = NULL;
|
||||
while (search) {
|
||||
s = search;
|
||||
search = search->next;
|
||||
if (s->max_results && s->num_results >= s->max_results) {
|
||||
search_finish(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from packet parser to find matching running search
|
||||
*/
|
||||
mdns_search_once_t *mdns_priv_query_find_from(mdns_search_once_t *s, mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
mdns_result_t *r = NULL;
|
||||
while (s) {
|
||||
if (s->state == SEARCH_OFF) {
|
||||
s = s->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == MDNS_TYPE_A || type == MDNS_TYPE_AAAA) {
|
||||
if ((s->type == MDNS_TYPE_ANY && s->service != NULL)
|
||||
|| (s->type != MDNS_TYPE_ANY && s->type != type && s->type != MDNS_TYPE_PTR && s->type != MDNS_TYPE_SRV)) {
|
||||
s = s->next;
|
||||
continue;
|
||||
}
|
||||
if (s->type != MDNS_TYPE_PTR && s->type != MDNS_TYPE_SRV) {
|
||||
if (!strcasecmp(name->host, s->instance)) {
|
||||
return s;
|
||||
}
|
||||
s = s->next;
|
||||
continue;
|
||||
}
|
||||
r = s->result;
|
||||
while (r) {
|
||||
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !mdns_utils_str_null_or_empty(r->hostname) && !strcasecmp(name->host, r->hostname)) {
|
||||
return s;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
s = s->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == MDNS_TYPE_SRV || type == MDNS_TYPE_TXT) {
|
||||
if ((s->type == MDNS_TYPE_ANY && s->service == NULL)
|
||||
|| (s->type != MDNS_TYPE_ANY && s->type != type && s->type != MDNS_TYPE_PTR)) {
|
||||
s = s->next;
|
||||
continue;
|
||||
}
|
||||
if (strcasecmp(name->service, s->service)
|
||||
|| strcasecmp(name->proto, s->proto)) {
|
||||
s = s->next;
|
||||
continue;
|
||||
}
|
||||
if (s->type != MDNS_TYPE_PTR) {
|
||||
if (s->instance && strcasecmp(name->host, s->instance) == 0) {
|
||||
return s;
|
||||
}
|
||||
s = s->next;
|
||||
continue;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
if (type == MDNS_TYPE_PTR && type == s->type && !strcasecmp(name->service, s->service) && !strcasecmp(name->proto, s->proto)) {
|
||||
return s;
|
||||
}
|
||||
|
||||
s = s->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mdns_search_once_t *mdns_priv_query_find(mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
return mdns_priv_query_find_from(s_search_once, name, type, tcpip_if, ip_protocol);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create search packet for particular interface
|
||||
*/
|
||||
static mdns_tx_packet_t *create_search_packet(mdns_search_once_t *search, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
mdns_result_t *r = NULL;
|
||||
mdns_tx_packet_t *packet = mdns_priv_alloc_packet(tcpip_if, ip_protocol);
|
||||
if (!packet) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mdns_out_question_t *q = (mdns_out_question_t *)mdns_mem_malloc(sizeof(mdns_out_question_t));
|
||||
if (!q) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
mdns_priv_free_tx_packet(packet);
|
||||
return NULL;
|
||||
}
|
||||
q->next = NULL;
|
||||
q->unicast = search->unicast;
|
||||
q->type = search->type;
|
||||
q->host = search->instance;
|
||||
q->service = search->service;
|
||||
q->proto = search->proto;
|
||||
q->domain = MDNS_UTILS_DEFAULT_DOMAIN;
|
||||
q->own_dynamic_memory = false;
|
||||
queueToEnd(mdns_out_question_t, packet->questions, q);
|
||||
|
||||
if (search->type == MDNS_TYPE_PTR) {
|
||||
r = search->result;
|
||||
while (r) {
|
||||
//full record on the same interface is available
|
||||
if (r->esp_netif != mdns_priv_get_esp_netif(tcpip_if) || r->ip_protocol != ip_protocol || r->instance_name == NULL || r->hostname == NULL || r->addr == NULL) {
|
||||
r = r->next;
|
||||
continue;
|
||||
}
|
||||
mdns_out_answer_t *a = (mdns_out_answer_t *)mdns_mem_malloc(sizeof(mdns_out_answer_t));
|
||||
if (!a) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
mdns_priv_free_tx_packet(packet);
|
||||
return NULL;
|
||||
}
|
||||
a->type = MDNS_TYPE_PTR;
|
||||
a->service = NULL;
|
||||
a->custom_instance = r->instance_name;
|
||||
a->custom_service = search->service;
|
||||
a->custom_proto = search->proto;
|
||||
a->bye = false;
|
||||
a->flush = false;
|
||||
a->next = NULL;
|
||||
queueToEnd(mdns_out_answer_t, packet->answers, a);
|
||||
r = r->next;
|
||||
}
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Send search packet to particular interface
|
||||
*/
|
||||
void mdns_priv_query_send(mdns_search_once_t *search, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol)
|
||||
{
|
||||
mdns_tx_packet_t *packet = NULL;
|
||||
if (mdsn_priv_pcb_is_inited(tcpip_if, ip_protocol)) {
|
||||
packet = create_search_packet(search, tcpip_if, ip_protocol);
|
||||
if (!packet) {
|
||||
return;
|
||||
}
|
||||
mdns_priv_dispatch_tx_packet(packet);
|
||||
mdns_priv_free_tx_packet(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free search structure (except the results)
|
||||
*/
|
||||
static void search_free(mdns_search_once_t *search)
|
||||
{
|
||||
mdns_mem_free(search->instance);
|
||||
mdns_mem_free(search->service);
|
||||
mdns_mem_free(search->proto);
|
||||
vSemaphoreDelete(search->done_semaphore);
|
||||
mdns_mem_free(search);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate new search structure
|
||||
*/
|
||||
static mdns_search_once_t *search_init(const char *name, const char *service, const char *proto, uint16_t type, bool unicast,
|
||||
uint32_t timeout, uint8_t max_results, mdns_query_notify_t notifier)
|
||||
{
|
||||
mdns_search_once_t *search = (mdns_search_once_t *)mdns_mem_malloc(sizeof(mdns_search_once_t));
|
||||
if (!search) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return NULL;
|
||||
}
|
||||
memset(search, 0, sizeof(mdns_search_once_t));
|
||||
|
||||
search->done_semaphore = xSemaphoreCreateBinary();
|
||||
if (!search->done_semaphore) {
|
||||
mdns_mem_free(search);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!mdns_utils_str_null_or_empty(name)) {
|
||||
search->instance = mdns_mem_strndup(name, MDNS_NAME_BUF_LEN - 1);
|
||||
if (!search->instance) {
|
||||
search_free(search);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mdns_utils_str_null_or_empty(service)) {
|
||||
search->service = mdns_mem_strndup(service, MDNS_NAME_BUF_LEN - 1);
|
||||
if (!search->service) {
|
||||
search_free(search);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mdns_utils_str_null_or_empty(proto)) {
|
||||
search->proto = mdns_mem_strndup(proto, MDNS_NAME_BUF_LEN - 1);
|
||||
if (!search->proto) {
|
||||
search_free(search);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
search->type = type;
|
||||
search->unicast = unicast;
|
||||
search->timeout = timeout;
|
||||
search->num_results = 0;
|
||||
search->max_results = max_results;
|
||||
search->result = NULL;
|
||||
search->state = SEARCH_INIT;
|
||||
search->sent_at = 0;
|
||||
search->started_at = xTaskGetTickCount() * portTICK_PERIOD_MS;
|
||||
search->notifier = notifier;
|
||||
search->next = NULL;
|
||||
|
||||
return search;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Queue search action
|
||||
*/
|
||||
static esp_err_t send_search_action(mdns_action_type_t type, mdns_search_once_t *search)
|
||||
{
|
||||
mdns_action_t *action = NULL;
|
||||
|
||||
action = (mdns_action_t *)mdns_mem_malloc(sizeof(mdns_action_t));
|
||||
if (!action) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
action->type = type;
|
||||
action->data.search_add.search = search;
|
||||
if (!mdns_priv_queue_action(action)) {
|
||||
mdns_mem_free(action);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from parser to add TXT data to search result
|
||||
*/
|
||||
void mdns_priv_query_result_add_txt(mdns_search_once_t *search, mdns_txt_item_t *txt, uint8_t *txt_value_len,
|
||||
size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol,
|
||||
uint32_t ttl)
|
||||
{
|
||||
mdns_result_t *r = search->result;
|
||||
while (r) {
|
||||
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol) {
|
||||
if (r->txt) {
|
||||
goto free_txt;
|
||||
}
|
||||
r->txt = txt;
|
||||
r->txt_value_len = txt_value_len;
|
||||
r->txt_count = txt_count;
|
||||
mdns_priv_query_update_result_ttl(r, ttl);
|
||||
return;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
if (!search->max_results || search->num_results < search->max_results) {
|
||||
r = (mdns_result_t *)mdns_mem_malloc(sizeof(mdns_result_t));
|
||||
if (!r) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
goto free_txt;
|
||||
}
|
||||
|
||||
memset(r, 0, sizeof(mdns_result_t));
|
||||
r->txt = txt;
|
||||
r->txt_value_len = txt_value_len;
|
||||
r->txt_count = txt_count;
|
||||
r->esp_netif = mdns_priv_get_esp_netif(tcpip_if);
|
||||
r->ip_protocol = ip_protocol;
|
||||
r->ttl = ttl;
|
||||
r->next = search->result;
|
||||
search->result = r;
|
||||
search->num_results++;
|
||||
}
|
||||
return;
|
||||
|
||||
free_txt:
|
||||
for (size_t i = 0; i < txt_count; i++) {
|
||||
mdns_mem_free((char *)(txt[i].key));
|
||||
mdns_mem_free((char *)(txt[i].value));
|
||||
}
|
||||
mdns_mem_free(txt);
|
||||
mdns_mem_free(txt_value_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Chain new IP to search result
|
||||
*/
|
||||
static void result_add_ip(mdns_result_t *r, esp_ip_addr_t *ip)
|
||||
{
|
||||
mdns_ip_addr_t *a = r->addr;
|
||||
while (a) {
|
||||
if (a->addr.type == ip->type) {
|
||||
#ifdef CONFIG_LWIP_IPV4
|
||||
if (a->addr.type == ESP_IPADDR_TYPE_V4 && a->addr.u_addr.ip4.addr == ip->u_addr.ip4.addr) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_LWIP_IPV6
|
||||
if (a->addr.type == ESP_IPADDR_TYPE_V6 && !memcmp(a->addr.u_addr.ip6.addr, ip->u_addr.ip6.addr, 16)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
a = mdns_priv_result_addr_create_ip(ip);
|
||||
if (!a) {
|
||||
return;
|
||||
}
|
||||
a->next = r->addr;
|
||||
r->addr = a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from parser to add A/AAAA data to search result
|
||||
*/
|
||||
void mdns_priv_query_result_add_ip(mdns_search_once_t *search, const char *hostname, esp_ip_addr_t *ip,
|
||||
mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl)
|
||||
{
|
||||
mdns_result_t *r = NULL;
|
||||
mdns_ip_addr_t *a = NULL;
|
||||
|
||||
if ((search->type == MDNS_TYPE_A && ip->type == ESP_IPADDR_TYPE_V4)
|
||||
|| (search->type == MDNS_TYPE_AAAA && ip->type == ESP_IPADDR_TYPE_V6)
|
||||
|| search->type == MDNS_TYPE_ANY) {
|
||||
r = search->result;
|
||||
while (r) {
|
||||
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol) {
|
||||
result_add_ip(r, ip);
|
||||
mdns_priv_query_update_result_ttl(r, ttl);
|
||||
return;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
if (!search->max_results || search->num_results < search->max_results) {
|
||||
r = (mdns_result_t *)mdns_mem_malloc(sizeof(mdns_result_t));
|
||||
if (!r) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
memset(r, 0, sizeof(mdns_result_t));
|
||||
|
||||
a = mdns_priv_result_addr_create_ip(ip);
|
||||
if (!a) {
|
||||
mdns_mem_free(r);
|
||||
return;
|
||||
}
|
||||
a->next = r->addr;
|
||||
r->hostname = mdns_mem_strdup(hostname);
|
||||
r->addr = a;
|
||||
r->esp_netif = mdns_priv_get_esp_netif(tcpip_if);
|
||||
r->ip_protocol = ip_protocol;
|
||||
r->next = search->result;
|
||||
r->ttl = ttl;
|
||||
search->result = r;
|
||||
search->num_results++;
|
||||
}
|
||||
} else if (search->type == MDNS_TYPE_PTR || search->type == MDNS_TYPE_SRV) {
|
||||
r = search->result;
|
||||
while (r) {
|
||||
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !mdns_utils_str_null_or_empty(r->hostname) && !strcasecmp(hostname, r->hostname)) {
|
||||
result_add_ip(r, ip);
|
||||
mdns_priv_query_update_result_ttl(r, ttl);
|
||||
break;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from parser to add SRV data to search result
|
||||
*/
|
||||
void mdns_priv_query_result_add_srv(mdns_search_once_t *search, const char *hostname, uint16_t port,
|
||||
mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl)
|
||||
{
|
||||
mdns_result_t *r = search->result;
|
||||
while (r) {
|
||||
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !mdns_utils_str_null_or_empty(r->hostname) && !strcasecmp(hostname, r->hostname)) {
|
||||
mdns_priv_query_update_result_ttl(r, ttl);
|
||||
return;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
if (!search->max_results || search->num_results < search->max_results) {
|
||||
r = (mdns_result_t *)mdns_mem_malloc(sizeof(mdns_result_t));
|
||||
if (!r) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return;
|
||||
}
|
||||
|
||||
memset(r, 0, sizeof(mdns_result_t));
|
||||
r->hostname = mdns_mem_strdup(hostname);
|
||||
if (!r->hostname) {
|
||||
mdns_mem_free(r);
|
||||
return;
|
||||
}
|
||||
if (search->instance) {
|
||||
r->instance_name = mdns_mem_strdup(search->instance);
|
||||
}
|
||||
r->service_type = mdns_mem_strdup(search->service);
|
||||
r->proto = mdns_mem_strdup(search->proto);
|
||||
r->port = port;
|
||||
r->esp_netif = mdns_priv_get_esp_netif(tcpip_if);
|
||||
r->ip_protocol = ip_protocol;
|
||||
r->ttl = ttl;
|
||||
r->next = search->result;
|
||||
search->result = r;
|
||||
search->num_results++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from parser to add PTR data to search result
|
||||
*/
|
||||
mdns_result_t *mdns_priv_query_result_add_ptr(mdns_search_once_t *search, const char *instance,
|
||||
const char *service_type, const char *proto, mdns_if_t tcpip_if,
|
||||
mdns_ip_protocol_t ip_protocol, uint32_t ttl)
|
||||
{
|
||||
mdns_result_t *r = search->result;
|
||||
while (r) {
|
||||
if (r->esp_netif == mdns_priv_get_esp_netif(tcpip_if) && r->ip_protocol == ip_protocol && !mdns_utils_str_null_or_empty(r->instance_name) && !strcasecmp(instance, r->instance_name)) {
|
||||
mdns_priv_query_update_result_ttl(r, ttl);
|
||||
return r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
if (!search->max_results || search->num_results < search->max_results) {
|
||||
r = (mdns_result_t *)mdns_mem_malloc(sizeof(mdns_result_t));
|
||||
if (!r) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(r, 0, sizeof(mdns_result_t));
|
||||
r->instance_name = mdns_mem_strdup(instance);
|
||||
r->service_type = mdns_mem_strdup(service_type);
|
||||
r->proto = mdns_mem_strdup(proto);
|
||||
if (!r->instance_name) {
|
||||
mdns_mem_free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r->esp_netif = mdns_priv_get_esp_netif(tcpip_if);
|
||||
r->ip_protocol = ip_protocol;
|
||||
r->ttl = ttl;
|
||||
r->next = search->result;
|
||||
search->result = r;
|
||||
search->num_results++;
|
||||
return r;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mdns_ip_addr_t *mdns_priv_result_addr_create_ip(esp_ip_addr_t *ip)
|
||||
{
|
||||
mdns_ip_addr_t *a = (mdns_ip_addr_t *)mdns_mem_malloc(sizeof(mdns_ip_addr_t));
|
||||
if (!a) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
return NULL;
|
||||
}
|
||||
memset(a, 0, sizeof(mdns_ip_addr_t));
|
||||
a->addr.type = ip->type;
|
||||
if (ip->type == ESP_IPADDR_TYPE_V6) {
|
||||
memcpy(a->addr.u_addr.ip6.addr, ip->u_addr.ip6.addr, 16);
|
||||
} else {
|
||||
a->addr.u_addr.ip4.addr = ip->u_addr.ip4.addr;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MDNS_PUBLIC_API
|
||||
* */
|
||||
void mdns_query_results_free(mdns_result_t *results)
|
||||
{
|
||||
mdns_priv_service_lock();
|
||||
mdns_priv_query_results_free(results);
|
||||
mdns_priv_service_unlock();
|
||||
}
|
||||
|
||||
esp_err_t mdns_query_async_delete(mdns_search_once_t *search)
|
||||
{
|
||||
if (!search) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (search->state != SEARCH_OFF) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
mdns_priv_service_lock();
|
||||
search_free(search);
|
||||
mdns_priv_service_unlock();
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool mdns_query_async_get_results(mdns_search_once_t *search, uint32_t timeout, mdns_result_t **results, uint8_t *num_results)
|
||||
{
|
||||
if (xSemaphoreTake(search->done_semaphore, pdMS_TO_TICKS(timeout)) == pdTRUE) {
|
||||
if (results) {
|
||||
*results = search->result;
|
||||
}
|
||||
if (num_results) {
|
||||
*num_results = search->num_results;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
mdns_search_once_t *mdns_query_async_new(const char *name, const char *service, const char *proto, uint16_t type,
|
||||
uint32_t timeout, size_t max_results, mdns_query_notify_t notifier)
|
||||
{
|
||||
mdns_search_once_t *search = NULL;
|
||||
|
||||
if (!mdns_priv_is_server_init() || !timeout || mdns_utils_str_null_or_empty(service) != mdns_utils_str_null_or_empty(proto)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
search = search_init(name, service, proto, type, type != MDNS_TYPE_PTR, timeout, max_results, notifier);
|
||||
if (!search) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (send_search_action(ACTION_SEARCH_ADD, search)) {
|
||||
search_free(search);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return search;
|
||||
}
|
||||
|
||||
esp_err_t mdns_query_generic(const char *name, const char *service, const char *proto, uint16_t type, mdns_query_transmission_type_t transmission_type, uint32_t timeout, size_t max_results, mdns_result_t **results)
|
||||
{
|
||||
mdns_search_once_t *search = NULL;
|
||||
|
||||
*results = NULL;
|
||||
|
||||
if (!mdns_priv_is_server_init()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!timeout || mdns_utils_str_null_or_empty(service) != mdns_utils_str_null_or_empty(proto)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
search = search_init(name, service, proto, type, transmission_type == MDNS_QUERY_UNICAST, timeout, max_results,
|
||||
NULL);
|
||||
if (!search) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (send_search_action(ACTION_SEARCH_ADD, search)) {
|
||||
search_free(search);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
xSemaphoreTake(search->done_semaphore, portMAX_DELAY);
|
||||
|
||||
*results = search->result;
|
||||
search_free(search);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t mdns_query(const char *name, const char *service_type, const char *proto, uint16_t type, uint32_t timeout, size_t max_results, mdns_result_t **results)
|
||||
{
|
||||
return mdns_query_generic(name, service_type, proto, type, type != MDNS_TYPE_PTR, timeout, max_results, results);
|
||||
}
|
||||
|
||||
esp_err_t mdns_query_ptr(const char *service, const char *proto, uint32_t timeout, size_t max_results, mdns_result_t **results)
|
||||
{
|
||||
if (mdns_utils_str_null_or_empty(service) || mdns_utils_str_null_or_empty(proto)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return mdns_query(NULL, service, proto, MDNS_TYPE_PTR, timeout, max_results, results);
|
||||
}
|
||||
|
||||
esp_err_t mdns_query_srv(const char *instance, const char *service, const char *proto, uint32_t timeout, mdns_result_t **result)
|
||||
{
|
||||
if (mdns_utils_str_null_or_empty(instance) || mdns_utils_str_null_or_empty(service) || mdns_utils_str_null_or_empty(proto)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return mdns_query(instance, service, proto, MDNS_TYPE_SRV, timeout, 1, result);
|
||||
}
|
||||
|
||||
esp_err_t mdns_query_txt(const char *instance, const char *service, const char *proto, uint32_t timeout, mdns_result_t **result)
|
||||
{
|
||||
if (mdns_utils_str_null_or_empty(instance) || mdns_utils_str_null_or_empty(service) || mdns_utils_str_null_or_empty(proto)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return mdns_query(instance, service, proto, MDNS_TYPE_TXT, timeout, 1, result);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LWIP_IPV4
|
||||
esp_err_t mdns_query_a(const char *name, uint32_t timeout, esp_ip4_addr_t *addr)
|
||||
{
|
||||
mdns_result_t *result = NULL;
|
||||
esp_err_t err;
|
||||
|
||||
if (mdns_utils_str_null_or_empty(name)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (strstr(name, ".local")) {
|
||||
ESP_LOGW(TAG, "Please note that hostname must not contain domain name, as mDNS uses '.local' domain");
|
||||
}
|
||||
|
||||
err = mdns_query(name, NULL, NULL, MDNS_TYPE_A, timeout, 1, &result);
|
||||
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
mdns_ip_addr_t *a = result->addr;
|
||||
while (a) {
|
||||
if (a->addr.type == ESP_IPADDR_TYPE_V4) {
|
||||
addr->addr = a->addr.u_addr.ip4.addr;
|
||||
mdns_query_results_free(result);
|
||||
return ESP_OK;
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
|
||||
mdns_query_results_free(result);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#endif /* CONFIG_LWIP_IPV4 */
|
||||
|
||||
#ifdef CONFIG_LWIP_IPV6
|
||||
esp_err_t mdns_query_aaaa(const char *name, uint32_t timeout, esp_ip6_addr_t *addr)
|
||||
{
|
||||
mdns_result_t *result = NULL;
|
||||
esp_err_t err;
|
||||
|
||||
if (mdns_utils_str_null_or_empty(name)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (strstr(name, ".local")) {
|
||||
ESP_LOGW(TAG, "Please note that hostname must not contain domain name, as mDNS uses '.local' domain");
|
||||
}
|
||||
|
||||
err = mdns_query(name, NULL, NULL, MDNS_TYPE_AAAA, timeout, 1, &result);
|
||||
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
mdns_ip_addr_t *a = result->addr;
|
||||
while (a) {
|
||||
if (a->addr.type == ESP_IPADDR_TYPE_V6) {
|
||||
memcpy(addr->addr, a->addr.u_addr.ip6.addr, 16);
|
||||
mdns_query_results_free(result);
|
||||
return ESP_OK;
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
|
||||
mdns_query_results_free(result);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#endif /* CONFIG_LWIP_IPV6 */
|
||||
1281
components/mdns/mdns_receive.c
Normal file
1281
components/mdns/mdns_receive.c
Normal file
File diff suppressed because it is too large
Load Diff
1559
components/mdns/mdns_responder.c
Normal file
1559
components/mdns/mdns_responder.c
Normal file
File diff suppressed because it is too large
Load Diff
1889
components/mdns/mdns_send.c
Normal file
1889
components/mdns/mdns_send.c
Normal file
File diff suppressed because it is too large
Load Diff
424
components/mdns/mdns_service.c
Normal file
424
components/mdns/mdns_service.c
Normal file
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "mdns_private.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_check.h"
|
||||
#include "mdns.h"
|
||||
#include "mdns_mem_caps.h"
|
||||
#include "mdns_utils.h"
|
||||
#include "mdns_browser.h"
|
||||
#include "mdns_netif.h"
|
||||
#include "mdns_send.h"
|
||||
#include "mdns_receive.h"
|
||||
#include "mdns_querier.h"
|
||||
#include "mdns_pcb.h"
|
||||
#include "mdns_responder.h"
|
||||
|
||||
#define MDNS_SERVICE_STACK_DEPTH CONFIG_MDNS_TASK_STACK_SIZE
|
||||
#define MDNS_TASK_PRIORITY CONFIG_MDNS_TASK_PRIORITY
|
||||
#if (MDNS_TASK_PRIORITY > ESP_TASK_PRIO_MAX)
|
||||
#error "mDNS task priority is higher than ESP_TASK_PRIO_MAX"
|
||||
#elif (MDNS_TASK_PRIORITY > ESP_TASKD_EVENT_PRIO)
|
||||
#warning "mDNS task priority is higher than ESP_TASKD_EVENT_PRIO, mDNS library might not work correctly"
|
||||
#endif
|
||||
#define MDNS_TASK_AFFINITY CONFIG_MDNS_TASK_AFFINITY
|
||||
|
||||
#define MDNS_SERVICE_LOCK() xSemaphoreTake(s_service_semaphore, portMAX_DELAY)
|
||||
#define MDNS_SERVICE_UNLOCK() xSemaphoreGive(s_service_semaphore)
|
||||
|
||||
static volatile TaskHandle_t s_service_task_handle = NULL;
|
||||
static SemaphoreHandle_t s_service_semaphore = NULL;
|
||||
static StackType_t *s_stack_buffer;
|
||||
static QueueHandle_t s_action_queue;
|
||||
static esp_timer_handle_t s_timer_handle;
|
||||
|
||||
static const char *TAG = "mdns_service";
|
||||
|
||||
#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES
|
||||
static inline char nibble_to_hex(int var)
|
||||
{
|
||||
return var > 9 ? var - 10 + 'a' : var + '0';
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Performs interface changes based on system events or custom commands
|
||||
*/
|
||||
static void perform_event_action(mdns_if_t mdns_if, mdns_event_actions_t action)
|
||||
{
|
||||
if (!mdns_priv_is_server_init() || mdns_if >= MDNS_MAX_INTERFACES) {
|
||||
return;
|
||||
}
|
||||
if (action & MDNS_EVENT_ENABLE_IP4) {
|
||||
mdns_priv_pcb_enable(mdns_if, MDNS_IP_PROTOCOL_V4);
|
||||
}
|
||||
if (action & MDNS_EVENT_ENABLE_IP6) {
|
||||
mdns_priv_pcb_enable(mdns_if, MDNS_IP_PROTOCOL_V6);
|
||||
}
|
||||
if (action & MDNS_EVENT_DISABLE_IP4) {
|
||||
mdns_priv_pcb_disable(mdns_if, MDNS_IP_PROTOCOL_V4);
|
||||
}
|
||||
if (action & MDNS_EVENT_DISABLE_IP6) {
|
||||
mdns_priv_pcb_disable(mdns_if, MDNS_IP_PROTOCOL_V6);
|
||||
}
|
||||
if (action & MDNS_EVENT_ANNOUNCE_IP4) {
|
||||
mdns_priv_pcb_announce(mdns_if, MDNS_IP_PROTOCOL_V4, NULL, 0, true);
|
||||
}
|
||||
if (action & MDNS_EVENT_ANNOUNCE_IP6) {
|
||||
mdns_priv_pcb_announce(mdns_if, MDNS_IP_PROTOCOL_V6, NULL, 0, true);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MDNS_RESPOND_REVERSE_QUERIES
|
||||
#ifdef CONFIG_LWIP_IPV4
|
||||
if (action & MDNS_EVENT_IP4_REVERSE_LOOKUP) {
|
||||
esp_netif_ip_info_t if_ip_info;
|
||||
if (esp_netif_get_ip_info(mdns_priv_get_esp_netif(mdns_if), &if_ip_info) == ESP_OK) {
|
||||
esp_ip4_addr_t *ip = &if_ip_info.ip;
|
||||
char *reverse_query_name = NULL;
|
||||
if (asprintf(&reverse_query_name, "%d.%d.%d.%d.in-addr",
|
||||
esp_ip4_addr4_16(ip), esp_ip4_addr3_16(ip),
|
||||
esp_ip4_addr2_16(ip), esp_ip4_addr1_16(ip)) > 0 && reverse_query_name) {
|
||||
ESP_LOGD(TAG, "Registered reverse query: %s.arpa", reverse_query_name);
|
||||
mdns_priv_delegate_hostname_add(reverse_query_name, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_LWIP_IPV4 */
|
||||
#ifdef CONFIG_LWIP_IPV6
|
||||
if (action & MDNS_EVENT_IP6_REVERSE_LOOKUP) {
|
||||
esp_ip6_addr_t addr6;
|
||||
if (!esp_netif_get_ip6_linklocal(mdns_priv_get_esp_netif(mdns_if), &addr6) && !mdns_utils_ipv6_address_is_zero(addr6)) {
|
||||
uint8_t *paddr = (uint8_t *)&addr6.addr;
|
||||
const char sub[] = "ip6";
|
||||
const size_t query_name_size = 4 * sizeof(addr6.addr) /* (2 nibbles + 2 dots)/per byte of IP address */ + sizeof(sub);
|
||||
char *reverse_query_name = mdns_mem_malloc(query_name_size);
|
||||
if (reverse_query_name) {
|
||||
char *ptr = &reverse_query_name[query_name_size]; // point to the end
|
||||
memcpy(ptr - sizeof(sub), sub, sizeof(sub)); // copy the IP sub-domain
|
||||
ptr -= sizeof(sub) + 1; // move before the sub-domain
|
||||
while (reverse_query_name < ptr) { // continue populating reverse query from the end
|
||||
*ptr-- = '.'; // nibble by nibble, until we reach the beginning
|
||||
*ptr-- = nibble_to_hex(((*paddr) >> 4) & 0x0F);
|
||||
*ptr-- = '.';
|
||||
*ptr-- = nibble_to_hex((*paddr) & 0x0F);
|
||||
paddr++;
|
||||
}
|
||||
ESP_LOGD(TAG, "Registered reverse query: %s.arpa", reverse_query_name);
|
||||
mdns_priv_delegate_hostname_add(reverse_query_name, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_LWIP_IPV6 */
|
||||
#endif /* CONFIG_MDNS_RESPOND_REVERSE_QUERIES */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free action data
|
||||
*/
|
||||
static void free_action(mdns_action_t *action)
|
||||
{
|
||||
switch (action->type) {
|
||||
case ACTION_SEARCH_ADD:
|
||||
case ACTION_SEARCH_SEND:
|
||||
case ACTION_SEARCH_END:
|
||||
mdns_priv_query_action(action, ACTION_CLEANUP);
|
||||
break;
|
||||
case ACTION_BROWSE_ADD:
|
||||
case ACTION_BROWSE_END:
|
||||
case ACTION_BROWSE_SYNC:
|
||||
mdns_priv_browse_action(action, ACTION_CLEANUP);
|
||||
break;
|
||||
case ACTION_TX_HANDLE:
|
||||
mdns_priv_send_action(action, ACTION_CLEANUP);
|
||||
break;
|
||||
case ACTION_RX_HANDLE:
|
||||
mdns_priv_receive_action(action, ACTION_CLEANUP);
|
||||
break;
|
||||
case ACTION_HOSTNAME_SET:
|
||||
case ACTION_INSTANCE_SET:
|
||||
case ACTION_DELEGATE_HOSTNAME_SET_ADDR:
|
||||
case ACTION_DELEGATE_HOSTNAME_REMOVE:
|
||||
case ACTION_DELEGATE_HOSTNAME_ADD:
|
||||
mdns_priv_responder_action(action, ACTION_CLEANUP);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mdns_mem_free(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called from service thread to execute given action
|
||||
*/
|
||||
static void execute_action(mdns_action_t *action)
|
||||
{
|
||||
switch (action->type) {
|
||||
case ACTION_SYSTEM_EVENT:
|
||||
perform_event_action(action->data.sys_event.interface, action->data.sys_event.event_action);
|
||||
break;
|
||||
case ACTION_SEARCH_ADD:
|
||||
case ACTION_SEARCH_SEND:
|
||||
case ACTION_SEARCH_END:
|
||||
mdns_priv_query_action(action, ACTION_RUN);
|
||||
break;
|
||||
case ACTION_BROWSE_ADD:
|
||||
case ACTION_BROWSE_SYNC:
|
||||
case ACTION_BROWSE_END:
|
||||
mdns_priv_browse_action(action, ACTION_RUN);
|
||||
break;
|
||||
|
||||
case ACTION_TX_HANDLE:
|
||||
mdns_priv_send_action(action, ACTION_RUN);
|
||||
break;
|
||||
case ACTION_RX_HANDLE:
|
||||
mdns_priv_receive_action(action, ACTION_RUN);
|
||||
break;
|
||||
case ACTION_HOSTNAME_SET:
|
||||
case ACTION_INSTANCE_SET:
|
||||
case ACTION_DELEGATE_HOSTNAME_ADD:
|
||||
case ACTION_DELEGATE_HOSTNAME_SET_ADDR:
|
||||
case ACTION_DELEGATE_HOSTNAME_REMOVE:
|
||||
mdns_priv_responder_action(action, ACTION_RUN);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mdns_mem_free(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief the main MDNS service task. Packets are received and parsed here
|
||||
*/
|
||||
static void service_task(void *pvParameters)
|
||||
{
|
||||
mdns_action_t *a = NULL;
|
||||
for (;;) {
|
||||
if (mdns_priv_is_server_init() && s_action_queue) {
|
||||
if (xQueueReceive(s_action_queue, &a, portMAX_DELAY) == pdTRUE) {
|
||||
assert(a);
|
||||
if (a->type == ACTION_TASK_STOP) {
|
||||
break;
|
||||
}
|
||||
MDNS_SERVICE_LOCK();
|
||||
execute_action(a);
|
||||
MDNS_SERVICE_UNLOCK();
|
||||
}
|
||||
} else {
|
||||
vTaskDelay(500 * portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
s_service_task_handle = NULL;
|
||||
vTaskDelay(portMAX_DELAY);
|
||||
}
|
||||
|
||||
static void timer_cb(void *arg)
|
||||
{
|
||||
mdns_priv_send_packets();
|
||||
mdns_priv_query_start_stop();
|
||||
}
|
||||
|
||||
static esp_err_t start_timer(void)
|
||||
{
|
||||
esp_timer_create_args_t timer_conf = {
|
||||
.callback = timer_cb,
|
||||
.arg = NULL,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "mdns_timer"
|
||||
};
|
||||
esp_err_t err = esp_timer_create(&timer_conf, &(s_timer_handle));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
return esp_timer_start_periodic(s_timer_handle, MDNS_TIMER_PERIOD_US);
|
||||
}
|
||||
|
||||
static esp_err_t stop_timer(void)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
if (s_timer_handle) {
|
||||
err = esp_timer_stop(s_timer_handle);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
err = esp_timer_delete(s_timer_handle);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t create_task_with_caps(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
static StaticTask_t mdns_task_buffer;
|
||||
|
||||
s_stack_buffer = mdns_mem_task_malloc(MDNS_SERVICE_STACK_DEPTH);
|
||||
ESP_GOTO_ON_FALSE(s_stack_buffer != NULL, ESP_FAIL, alloc_failed, TAG, "failed to allocate memory for the mDNS task's stack");
|
||||
|
||||
s_service_task_handle = xTaskCreateStaticPinnedToCore(service_task, "mdns", MDNS_SERVICE_STACK_DEPTH, NULL, MDNS_TASK_PRIORITY, s_stack_buffer, &mdns_task_buffer, MDNS_TASK_AFFINITY);
|
||||
ESP_GOTO_ON_FALSE(s_service_task_handle != NULL, ESP_FAIL, err, TAG, "failed to create task for the mDNS");
|
||||
|
||||
return ret;
|
||||
|
||||
alloc_failed:
|
||||
HOOK_MALLOC_FAILED;
|
||||
err:
|
||||
mdns_mem_task_free(s_stack_buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start the service thread if not running
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
static esp_err_t service_task_start(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (!s_service_semaphore) {
|
||||
s_service_semaphore = xSemaphoreCreateMutex();
|
||||
ESP_RETURN_ON_FALSE(s_service_semaphore != NULL, ESP_FAIL, TAG, "Failed to create the mDNS service lock");
|
||||
}
|
||||
MDNS_SERVICE_LOCK();
|
||||
ESP_GOTO_ON_ERROR(start_timer(), err, TAG, "Failed to start the mDNS service timer");
|
||||
|
||||
if (!s_service_task_handle) {
|
||||
ESP_GOTO_ON_ERROR(create_task_with_caps(), err_stop_timer, TAG, "Failed to start the mDNS service task");
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) && !CONFIG_IDF_TARGET_LINUX
|
||||
StackType_t *mdns_debug_stack_buffer;
|
||||
StaticTask_t *mdns_debug_task_buffer;
|
||||
xTaskGetStaticBuffers(s_service_task_handle, &mdns_debug_stack_buffer, &mdns_debug_task_buffer);
|
||||
ESP_LOGD(TAG, "mdns_debug_stack_buffer:%p mdns_debug_task_buffer:%p\n", mdns_debug_stack_buffer, mdns_debug_task_buffer);
|
||||
#endif // CONFIG_IDF_TARGET_LINUX
|
||||
}
|
||||
MDNS_SERVICE_UNLOCK();
|
||||
return ret;
|
||||
|
||||
err_stop_timer:
|
||||
stop_timer();
|
||||
err:
|
||||
MDNS_SERVICE_UNLOCK();
|
||||
vSemaphoreDelete(s_service_semaphore);
|
||||
s_service_semaphore = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop the service thread
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK
|
||||
*/
|
||||
static esp_err_t service_task_stop(void)
|
||||
{
|
||||
stop_timer();
|
||||
if (s_service_task_handle) {
|
||||
TaskHandle_t task_handle = s_service_task_handle;
|
||||
mdns_action_t action;
|
||||
mdns_action_t *a = &action;
|
||||
action.type = ACTION_TASK_STOP;
|
||||
if (xQueueSend(s_action_queue, &a, (TickType_t)0) != pdPASS) {
|
||||
s_service_task_handle = NULL;
|
||||
}
|
||||
while (s_service_task_handle) {
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
vTaskDelete(task_handle);
|
||||
}
|
||||
vSemaphoreDelete(s_service_semaphore);
|
||||
s_service_semaphore = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void mdns_priv_service_lock(void)
|
||||
{
|
||||
MDNS_SERVICE_LOCK();
|
||||
}
|
||||
|
||||
void mdns_priv_service_unlock(void)
|
||||
{
|
||||
MDNS_SERVICE_UNLOCK();
|
||||
}
|
||||
|
||||
esp_err_t mdns_init(void)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
|
||||
if (mdns_priv_is_server_init()) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (mdns_priv_responder_init() != ESP_OK) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
s_action_queue = xQueueCreate(MDNS_ACTION_QUEUE_LEN, sizeof(mdns_action_t *));
|
||||
if (!s_action_queue) {
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto free_responder;
|
||||
}
|
||||
|
||||
if (mdns_priv_netif_init() != ESP_OK) {
|
||||
err = ESP_FAIL;
|
||||
goto free_queue;
|
||||
}
|
||||
|
||||
if (service_task_start()) {
|
||||
//service start failed!
|
||||
err = ESP_FAIL;
|
||||
goto free_all_and_disable_pcbs;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
free_all_and_disable_pcbs:
|
||||
mdns_priv_netif_deinit();
|
||||
free_queue:
|
||||
vQueueDelete(s_action_queue);
|
||||
free_responder:
|
||||
mdns_priv_responder_free();
|
||||
return err;
|
||||
}
|
||||
|
||||
void mdns_free(void)
|
||||
{
|
||||
if (!mdns_priv_is_server_init()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Unregister handlers before destroying the mdns internals to avoid receiving async events while deinit
|
||||
mdns_priv_netif_unregister_predefined_handlers();
|
||||
|
||||
mdns_service_remove_all();
|
||||
service_task_stop();
|
||||
// at this point, the service task is deleted, so we can destroy the stack size
|
||||
mdns_mem_task_free(s_stack_buffer);
|
||||
mdns_priv_pcb_deinit();
|
||||
if (s_action_queue) {
|
||||
mdns_action_t *c;
|
||||
while (xQueueReceive(s_action_queue, &c, 0) == pdTRUE) {
|
||||
free_action(c);
|
||||
}
|
||||
vQueueDelete(s_action_queue);
|
||||
}
|
||||
mdns_priv_clear_tx_queue();
|
||||
mdns_priv_query_free();
|
||||
mdns_priv_browse_free();
|
||||
mdns_priv_responder_free();
|
||||
}
|
||||
|
||||
bool mdns_priv_queue_action(mdns_action_t *action)
|
||||
{
|
||||
if (xQueueSend(s_action_queue, &action, (TickType_t)0) != pdPASS) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
270
components/mdns/mdns_utils.c
Normal file
270
components/mdns/mdns_utils.c
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "mdns_private.h"
|
||||
#include "mdns_mem_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "mdns_utils.h"
|
||||
#include "mdns_responder.h"
|
||||
|
||||
static const char *TAG = "mdns_utils";
|
||||
static const char *MDNS_DEFAULT_DOMAIN = "local";
|
||||
static const char *MDNS_SUB_STR = "_sub";
|
||||
|
||||
const uint8_t *mdns_utils_read_fqdn(const uint8_t *packet, const uint8_t *start, mdns_name_t *name, char *buf, size_t packet_len)
|
||||
{
|
||||
size_t index = 0;
|
||||
const uint8_t *packet_end = packet + packet_len;
|
||||
while (start + index < packet_end && start[index]) {
|
||||
if (name->parts == 4) {
|
||||
name->invalid = true;
|
||||
}
|
||||
uint8_t len = start[index++];
|
||||
if (len < 0xC0) {
|
||||
if (len > 63) {
|
||||
//length can not be more than 63
|
||||
return NULL;
|
||||
}
|
||||
uint8_t i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (start + index >= packet_end) {
|
||||
return NULL;
|
||||
}
|
||||
buf[i] = start[index++];
|
||||
}
|
||||
buf[len] = '\0';
|
||||
if (name->parts == 1 && buf[0] != '_'
|
||||
&& (strcasecmp(buf, MDNS_DEFAULT_DOMAIN) != 0)
|
||||
&& (strcasecmp(buf, "arpa") != 0)
|
||||
#ifndef CONFIG_MDNS_RESPOND_REVERSE_QUERIES
|
||||
&& (strcasecmp(buf, "ip6") != 0)
|
||||
&& (strcasecmp(buf, "in-addr") != 0)
|
||||
#endif
|
||||
) {
|
||||
strlcat(name->host, ".", sizeof(name->host));
|
||||
strlcat(name->host, buf, sizeof(name->host));
|
||||
} else if (strcasecmp(buf, MDNS_SUB_STR) == 0) {
|
||||
name->sub = 1;
|
||||
} else if (!name->invalid) {
|
||||
char *mdns_name_ptrs[] = {name->host, name->service, name->proto, name->domain};
|
||||
memcpy(mdns_name_ptrs[name->parts++], buf, len + 1);
|
||||
}
|
||||
} else {
|
||||
size_t address = (((uint16_t)len & 0x3F) << 8) | start[index++];
|
||||
if ((packet + address) >= start) {
|
||||
//reference address can not be after where we are
|
||||
return NULL;
|
||||
}
|
||||
if (mdns_utils_read_fqdn(packet, packet + address, name, buf, packet_len)) {
|
||||
return start + index;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return start + index + 1;
|
||||
}
|
||||
|
||||
const uint8_t *mdns_utils_parse_fqdn(const uint8_t *packet, const uint8_t *start, mdns_name_t *name, size_t packet_len)
|
||||
{
|
||||
name->parts = 0;
|
||||
name->sub = 0;
|
||||
name->host[0] = 0;
|
||||
name->service[0] = 0;
|
||||
name->proto[0] = 0;
|
||||
name->domain[0] = 0;
|
||||
name->invalid = false;
|
||||
|
||||
static char buf[MDNS_NAME_BUF_LEN];
|
||||
|
||||
const uint8_t *next_data = (uint8_t *) mdns_utils_read_fqdn(packet, start, name, buf, packet_len);
|
||||
if (!next_data) {
|
||||
return 0;
|
||||
}
|
||||
if (!name->parts || name->invalid) {
|
||||
return next_data;
|
||||
}
|
||||
if (name->parts == 3) {
|
||||
memmove((uint8_t *)name + (MDNS_NAME_BUF_LEN), (uint8_t *)name, 3 * (MDNS_NAME_BUF_LEN));
|
||||
name->host[0] = 0;
|
||||
} else if (name->parts == 2) {
|
||||
memmove((uint8_t *)(name->domain), (uint8_t *)(name->service), (MDNS_NAME_BUF_LEN));
|
||||
name->service[0] = 0;
|
||||
name->proto[0] = 0;
|
||||
}
|
||||
if (strcasecmp(name->domain, MDNS_DEFAULT_DOMAIN) == 0 || strcasecmp(name->domain, "arpa") == 0) {
|
||||
return next_data;
|
||||
}
|
||||
name->invalid = true; // mark the current name invalid, but continue with other question
|
||||
return next_data;
|
||||
}
|
||||
|
||||
bool mdns_utils_hostname_is_ours(const char *hostname)
|
||||
{
|
||||
if (!mdns_utils_str_null_or_empty(mdns_priv_get_global_hostname()) &&
|
||||
strcasecmp(hostname, mdns_priv_get_global_hostname()) == 0) {
|
||||
return true;
|
||||
}
|
||||
mdns_host_item_t *host = mdns_priv_get_hosts();
|
||||
while (host != NULL) {
|
||||
if (strcasecmp(hostname, host->hostname) == 0) {
|
||||
return true;
|
||||
}
|
||||
host = host->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mdns_utils_service_match(const mdns_service_t *srv, const char *service, const char *proto,
|
||||
const char *hostname)
|
||||
{
|
||||
if (!service || !proto || !srv || !srv->hostname) {
|
||||
return false;
|
||||
}
|
||||
return !strcasecmp(srv->service, service) && !strcasecmp(srv->proto, proto) &&
|
||||
(mdns_utils_str_null_or_empty(hostname) || !strcasecmp(srv->hostname, hostname));
|
||||
}
|
||||
|
||||
mdns_srv_item_t *mdns_utils_get_service_item(const char *service, const char *proto, const char *hostname)
|
||||
{
|
||||
mdns_srv_item_t *s = mdns_priv_get_services();
|
||||
while (s) {
|
||||
if (mdns_utils_service_match(s->service, service, proto, hostname)) {
|
||||
return s;
|
||||
}
|
||||
s = s->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mdns_srv_item_t *mdns_utils_get_service_item_instance(const char *instance, const char *service, const char *proto,
|
||||
const char *hostname)
|
||||
{
|
||||
mdns_srv_item_t *s = mdns_priv_get_services();
|
||||
while (s) {
|
||||
if (instance) {
|
||||
if (mdns_utils_service_match_instance(s->service, instance, service, proto, hostname)) {
|
||||
return s;
|
||||
}
|
||||
} else {
|
||||
if (mdns_utils_service_match(s->service, service, proto, hostname)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
s = s->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool mdns_utils_service_match_instance(const mdns_service_t *srv, const char *instance, const char *service,
|
||||
const char *proto, const char *hostname)
|
||||
{
|
||||
// service and proto must be supplied, if not this instance won't match
|
||||
if (!service || !proto) {
|
||||
return false;
|
||||
}
|
||||
// instance==NULL -> mdns_utils_instance_name_match() will check the default instance
|
||||
// hostname==NULL -> matches if instance, service and proto matches
|
||||
return !strcasecmp(srv->service, service) && mdns_utils_instance_name_match(srv->instance, instance) &&
|
||||
!strcasecmp(srv->proto, proto) && (mdns_utils_str_null_or_empty(hostname) || !strcasecmp(srv->hostname, hostname));
|
||||
}
|
||||
|
||||
static const char *get_default_instance_name(void)
|
||||
{
|
||||
const char* instance = mdns_priv_get_instance();
|
||||
if (!mdns_utils_str_null_or_empty(instance)) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
const char* host = mdns_priv_get_global_hostname();
|
||||
if (!mdns_utils_str_null_or_empty(host)) {
|
||||
return host;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the service name of a service
|
||||
*/
|
||||
const char *mdns_utils_get_service_instance_name(const mdns_service_t *service)
|
||||
{
|
||||
if (service && !mdns_utils_str_null_or_empty(service->instance)) {
|
||||
return service->instance;
|
||||
}
|
||||
|
||||
return get_default_instance_name();
|
||||
}
|
||||
|
||||
|
||||
bool mdns_utils_instance_name_match(const char *lhs, const char *rhs)
|
||||
{
|
||||
if (lhs == NULL) {
|
||||
lhs = get_default_instance_name();
|
||||
}
|
||||
if (rhs == NULL) {
|
||||
rhs = get_default_instance_name();
|
||||
}
|
||||
return !strcasecmp(lhs, rhs);
|
||||
}
|
||||
|
||||
|
||||
mdns_ip_addr_t *mdns_utils_copy_address_list(const mdns_ip_addr_t *address_list)
|
||||
{
|
||||
mdns_ip_addr_t *head = NULL;
|
||||
mdns_ip_addr_t *tail = NULL;
|
||||
while (address_list != NULL) {
|
||||
mdns_ip_addr_t *addr = (mdns_ip_addr_t *)mdns_mem_malloc(sizeof(mdns_ip_addr_t));
|
||||
if (addr == NULL) {
|
||||
HOOK_MALLOC_FAILED;
|
||||
mdns_utils_free_address_list(head);
|
||||
return NULL;
|
||||
}
|
||||
addr->addr = address_list->addr;
|
||||
addr->next = NULL;
|
||||
if (head == NULL) {
|
||||
head = addr;
|
||||
tail = addr;
|
||||
} else {
|
||||
tail->next = addr;
|
||||
tail = tail->next;
|
||||
}
|
||||
address_list = address_list->next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
void mdns_utils_free_address_list(mdns_ip_addr_t *address_list)
|
||||
{
|
||||
while (address_list != NULL) {
|
||||
mdns_ip_addr_t *next = address_list->next;
|
||||
mdns_mem_free(address_list);
|
||||
address_list = next;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LWIP_IPV6
|
||||
bool mdns_utils_ipv6_address_is_zero(esp_ip6_addr_t ip6)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t *data = (uint8_t *)ip6.addr;
|
||||
for (i = 0; i < MDNS_UTILS_SIZEOF_IP6_ADDR; i++) {
|
||||
if (data[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_LWIP_IPV6 */
|
||||
|
||||
uint8_t mdns_utils_append_u16(uint8_t *packet, uint16_t *index, uint16_t value)
|
||||
{
|
||||
if ((*index + 1) >= MDNS_MAX_PACKET_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
mdns_utils_append_u8(packet, index, (value >> 8) & 0xFF);
|
||||
mdns_utils_append_u8(packet, index, value & 0xFF);
|
||||
return 2;
|
||||
}
|
||||
78
components/mdns/private_include/mdns_browser.h
Normal file
78
components/mdns/private_include/mdns_browser.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "mdns_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Free browse item queue
|
||||
*
|
||||
* @note Called from mdns_free()
|
||||
*/
|
||||
void mdns_priv_browse_free(void);
|
||||
|
||||
/**
|
||||
* @brief Looks for the name/type in active browse items
|
||||
*
|
||||
* @note Called from the packet parser (mdns_receive.c)
|
||||
*
|
||||
* @return browse results
|
||||
*/
|
||||
mdns_browse_t *mdns_priv_browse_find(mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Send out all browse queries
|
||||
*
|
||||
* @note Called from the network events (mdns_netif.c)
|
||||
* @note Calls (indirectly) search-send from mdns_querier.c, which sends out the query
|
||||
*/
|
||||
void mdns_priv_browse_send_all(mdns_if_t mdns_if);
|
||||
|
||||
/**
|
||||
* @brief Sync browse results
|
||||
*
|
||||
* @note Called from the packet parser
|
||||
* @note Calls mdns_priv_queue_action() from mdns_engine
|
||||
*/
|
||||
esp_err_t mdns_priv_browse_sync(mdns_browse_sync_t *browse_sync);
|
||||
|
||||
/**
|
||||
* @brief Perform action from mdns service queue
|
||||
*
|
||||
* @note Called from the _mdns_service_task() in mdns.c
|
||||
*/
|
||||
void mdns_priv_browse_action(mdns_action_t *action, mdns_action_subtype_t type);
|
||||
|
||||
/**
|
||||
* @brief Add a TXT record to the browse result
|
||||
*
|
||||
* @note Called from the packet parser (mdns_receive.c)
|
||||
*/
|
||||
void mdns_priv_browse_result_add_txt(mdns_browse_t *browse, const char *instance, const char *service, const char *proto,
|
||||
mdns_txt_item_t *txt, uint8_t *txt_value_len, size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol,
|
||||
uint32_t ttl, mdns_browse_sync_t *out_sync_browse);
|
||||
/**
|
||||
* @brief Add an IP record to the browse result
|
||||
*
|
||||
* @note Called from the packet parser (mdns_receive.c)
|
||||
*/
|
||||
void mdns_priv_browse_result_add_ip(mdns_browse_t *browse, const char *hostname, esp_ip_addr_t *ip,
|
||||
mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl, mdns_browse_sync_t *out_sync_browse);
|
||||
/**
|
||||
* @brief Add a SRV record to the browse result
|
||||
*
|
||||
* @note Called from the packet parser (mdns_receive.c)
|
||||
*/
|
||||
void mdns_priv_browse_result_add_srv(mdns_browse_t *browse, const char *hostname, const char *instance, const char *service, const char *proto,
|
||||
uint16_t port, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl, mdns_browse_sync_t *out_sync_browse);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
65
components/mdns/private_include/mdns_debug.h
Normal file
65
components/mdns/private_include/mdns_debug.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "mdns_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MDNS_ENABLE_DEBUG_PRINTS
|
||||
#include "esp_log.h"
|
||||
|
||||
/* Define the debug macros for the mDNS module
|
||||
*/
|
||||
#define DBG_BROWSE_RESULTS(result, browse) mdns_debug_printf_browse_result(result, browse)
|
||||
|
||||
#define DBG_BROWSE_RESULTS_WITH_MSG(result, ...) do { \
|
||||
ESP_LOGD("mdns", __VA_ARGS__); \
|
||||
mdns_debug_printf_browse_result_all(result); \
|
||||
} while(0)
|
||||
|
||||
#define DBG_TX_PACKET(packet, data, len) mdns_debug_tx_packet(packet, data, len)
|
||||
|
||||
#define DBG_RX_PACKET(packet, data, len) mdns_debug_rx_packet(packet, data, len)
|
||||
|
||||
/**
|
||||
* @brief Print the browse results
|
||||
*/
|
||||
void mdns_debug_printf_browse_result(mdns_result_t *r_t, mdns_browse_t *b_t);
|
||||
|
||||
/**
|
||||
* @brief Print all the browse results
|
||||
*/
|
||||
void mdns_debug_printf_browse_result_all(mdns_result_t *r_t);
|
||||
|
||||
/**
|
||||
* @brief Print the tx packet
|
||||
*/
|
||||
void mdns_debug_tx_packet(mdns_tx_packet_t *p, uint8_t packet[MDNS_MAX_PACKET_SIZE], uint16_t index);
|
||||
|
||||
/**
|
||||
* @brief Print the rx packet
|
||||
*/
|
||||
void mdns_debug_rx_packet(mdns_rx_packet_t *packet, const uint8_t* data, uint16_t len);
|
||||
|
||||
#else
|
||||
|
||||
/* Define the dummy debug macros if debugging is OFF
|
||||
*/
|
||||
#define DBG_BROWSE_RESULTS(result, browse)
|
||||
#define DBG_BROWSE_RESULTS_WITH_MSG(result, ...)
|
||||
#define DBG_TX_PACKET(packet, data, len)
|
||||
#define DBG_RX_PACKET(packet, data, len)
|
||||
|
||||
#endif // CONFIG_MDNS_ENABLE_DEBUG_PRINTS
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
67
components/mdns/private_include/mdns_netif.h
Normal file
67
components/mdns/private_include/mdns_netif.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "mdns_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize the mDNS network interfaces
|
||||
*
|
||||
* @note Called from mdns_init() in mdns.c
|
||||
*/
|
||||
esp_err_t mdns_priv_netif_init(void);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the mDNS network interfaces
|
||||
*
|
||||
* @note Called from mdns_init() in mdns.c
|
||||
*/
|
||||
esp_err_t mdns_priv_netif_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Unregister predefined (default) network interfaces
|
||||
*
|
||||
* @note Called from mdns_free() in mdns.c
|
||||
*
|
||||
*/
|
||||
void mdns_priv_netif_unregister_predefined_handlers(void);
|
||||
|
||||
/**
|
||||
* @brief Clean the internal netif for the particular interface
|
||||
*
|
||||
* @note Called from mdns_responder on disabling pcbs
|
||||
*/
|
||||
void mdns_priv_netif_disable(mdns_if_t tcpip_if);
|
||||
|
||||
/**
|
||||
* @brief Returns potentially duplicated interface
|
||||
*
|
||||
* @note Called from multiple places where Rx and Tx packets are processed
|
||||
*/
|
||||
mdns_if_t mdns_priv_netif_get_other_interface(mdns_if_t tcpip_if);
|
||||
|
||||
/**
|
||||
* @brief Gets the actual esp_netif pointer from the internal network interface list
|
||||
*
|
||||
* The supplied ordinal number could
|
||||
* - point to a predef netif -> "STA", "AP", "ETH"
|
||||
* - if no entry in the list (NULL) -> check if the system added this netif
|
||||
* - point to a custom netif -> just return the entry in the list
|
||||
* - users is responsible for the lifetime of this netif (to be valid between mdns-init -> deinit)
|
||||
*
|
||||
* @param tcpip_if Ordinal number of the interface
|
||||
* @return Pointer ot the esp_netif object if the interface is available, NULL otherwise
|
||||
*/
|
||||
esp_netif_t *mdns_priv_get_esp_netif(mdns_if_t tcpip_if);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,10 +1,9 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef ESP_MDNS_NETWORKING_H_
|
||||
#define ESP_MDNS_NETWORKING_H_
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* MDNS Server Networking -- private include
|
||||
@@ -13,23 +12,24 @@
|
||||
#include "mdns.h"
|
||||
#include "mdns_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Queue RX packet action
|
||||
* @brief Check if the netif on the selected interfacce and protocol is ready
|
||||
*/
|
||||
esp_err_t _mdns_send_rx_action(mdns_rx_packet_t *packet);
|
||||
|
||||
bool mdns_is_netif_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
bool mdns_priv_if_ready(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Start PCB
|
||||
*/
|
||||
esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
esp_err_t mdns_priv_if_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Stop PCB
|
||||
*/
|
||||
esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
esp_err_t mdns_priv_if_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief send packet over UDP
|
||||
@@ -40,21 +40,23 @@ esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
*
|
||||
* @return length of sent packet or 0 on error
|
||||
*/
|
||||
size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len);
|
||||
size_t mdns_priv_if_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, const esp_ip_addr_t *ip, uint16_t port, uint8_t *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Gets data pointer to the mDNS packet
|
||||
*/
|
||||
void *_mdns_get_packet_data(mdns_rx_packet_t *packet);
|
||||
void *mdns_priv_get_packet_data(mdns_rx_packet_t *packet);
|
||||
|
||||
/**
|
||||
* @brief Gets data length of c
|
||||
*/
|
||||
size_t _mdns_get_packet_len(mdns_rx_packet_t *packet);
|
||||
size_t mdns_priv_get_packet_len(mdns_rx_packet_t *packet);
|
||||
|
||||
/**
|
||||
* @brief Free the mDNS packet
|
||||
*/
|
||||
void _mdns_packet_free(mdns_rx_packet_t *packet);
|
||||
void mdns_priv_packet_free(mdns_rx_packet_t *packet);
|
||||
|
||||
#endif /* ESP_MDNS_NETWORKING_H_ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
121
components/mdns/private_include/mdns_pcb.h
Normal file
121
components/mdns/private_include/mdns_pcb.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "mdns_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Disable the PCB for the selected interface and protocol
|
||||
* @note Called from the main module (deinit and event handler)
|
||||
*/
|
||||
void mdns_priv_pcb_disable(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Enable the PCB for the selected interface and protocol
|
||||
* @note Called from the main module (deinit and event handler)
|
||||
*/
|
||||
void mdns_priv_pcb_enable(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Set the interface as duplicate
|
||||
* @note Called from the packet parser when checking for collisions
|
||||
*/
|
||||
void mdns_priv_pcb_set_duplicate(mdns_if_t tcpip_if);
|
||||
|
||||
/**
|
||||
* @brief Send announcement on particular PCB
|
||||
* @note Called mainly from mdns.c to send announcements on all interfaces
|
||||
*/
|
||||
void mdns_priv_pcb_announce(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t **services, size_t len, bool include_ip);
|
||||
|
||||
/**
|
||||
* @brief Checks if the PCB is OFF
|
||||
* @note Called from mdns_send.c
|
||||
*/
|
||||
bool mdns_priv_pcb_is_off(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Schedules TX packets for various PCB states of probing and announcing
|
||||
* @note Called from mdns_send.c
|
||||
*/
|
||||
void mdns_priv_pcb_schedule_tx_packet(mdns_tx_packet_t *p);
|
||||
|
||||
/**
|
||||
* @brief Update probing services on certain service removal
|
||||
* @note Called from mdns_send.c upon removing scheduled packets for a service
|
||||
*/
|
||||
void mdns_priv_pcb_check_probing_services(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_service_t *service, bool removed_answers, bool *should_remove_questions);
|
||||
|
||||
/**
|
||||
* @brief Deinit pcbs
|
||||
* @note Called from mdns_free()
|
||||
*/
|
||||
void mdns_priv_pcb_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Checks if the netif and PCB is initialized
|
||||
* @note Called from mdns_send.c
|
||||
*/
|
||||
bool mdsn_priv_pcb_is_inited(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Checks if PCB is duplicated
|
||||
* @note Called from mdns_send.c
|
||||
*/
|
||||
bool mdns_priv_pcb_is_duplicate(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Checks if PCB is probing
|
||||
* @note Called from mdns_receive.c
|
||||
*/
|
||||
bool mdns_priv_pcb_is_probing(mdns_rx_packet_t *packet);
|
||||
|
||||
/**
|
||||
* @brief Set probe failed to PCB
|
||||
* @note Called from mdns_receive.c
|
||||
*/
|
||||
void mdns_priv_pcb_set_probe_failed(mdns_rx_packet_t *packet);
|
||||
|
||||
/**
|
||||
* @brief Checks if PCB completed probing
|
||||
* @note Called from mdns_receive.c
|
||||
*/
|
||||
bool mdns_priv_pcb_is_after_probing(mdns_rx_packet_t *packet);
|
||||
|
||||
/**
|
||||
* @brief Checks if the selected interface has a duplicate
|
||||
* @note Called from mdns_send.c
|
||||
*/
|
||||
bool mdns_priv_pcb_check_for_duplicates(mdns_if_t tcpip_if);
|
||||
|
||||
/**
|
||||
* @brief Sends bye for particular services on particular PCB
|
||||
* @note Called from mdns.c
|
||||
*/
|
||||
void mdns_priv_pcb_send_bye_service(mdns_srv_item_t **services, size_t len, bool include_ip);
|
||||
|
||||
/**
|
||||
* @brief Send probe on all active PCBs
|
||||
*/
|
||||
void mdns_priv_probe_all_pcbs(mdns_srv_item_t **services, size_t len, bool probe_ip, bool clear_old_probe);
|
||||
|
||||
/**
|
||||
* @brief Send probe for particular services on particular PCB
|
||||
*
|
||||
* Tests possible duplication on probing service structure and probes only for new entries.
|
||||
* - If pcb probing then add only non-probing services and restarts probing
|
||||
* - If pcb not probing, run probing for all specified services
|
||||
*/
|
||||
void mdns_priv_init_pcb_probe(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t **services, size_t len, bool probe_ip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -3,8 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef MDNS_PRIVATE_H_
|
||||
#define MDNS_PRIVATE_H_
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "mdns.h"
|
||||
@@ -16,11 +15,6 @@
|
||||
#include "esp_timer.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
#ifdef CONFIG_MDNS_ENABLE_DEBUG_PRINTS
|
||||
#define MDNS_ENABLE_DEBUG
|
||||
#define _mdns_dbg_printf(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/** Number of predefined interfaces */
|
||||
#ifndef CONFIG_MDNS_PREDEF_NETIF_STA
|
||||
#define CONFIG_MDNS_PREDEF_NETIF_STA 0
|
||||
@@ -90,14 +84,6 @@
|
||||
#define MDNS_ANSWER_AAAA_SIZE 16
|
||||
|
||||
#define MDNS_SERVICE_PORT 5353 // UDP port that the server runs on
|
||||
#define MDNS_SERVICE_STACK_DEPTH CONFIG_MDNS_TASK_STACK_SIZE
|
||||
#define MDNS_TASK_PRIORITY CONFIG_MDNS_TASK_PRIORITY
|
||||
#if (MDNS_TASK_PRIORITY > ESP_TASK_PRIO_MAX)
|
||||
#error "mDNS task priority is higher than ESP_TASK_PRIO_MAX"
|
||||
#elif (MDNS_TASK_PRIORITY > ESP_TASKD_EVENT_PRIO)
|
||||
#warning "mDNS task priority is higher than ESP_TASKD_EVENT_PRIO, mDNS library might not work correctly"
|
||||
#endif
|
||||
#define MDNS_TASK_AFFINITY CONFIG_MDNS_TASK_AFFINITY
|
||||
#define MDNS_SERVICE_ADD_TIMEOUT_MS CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS
|
||||
|
||||
#define MDNS_PACKET_QUEUE_LEN 16 // Maximum packets that can be queued for parsing
|
||||
@@ -126,9 +112,6 @@
|
||||
|
||||
#define MDNS_TIMER_PERIOD_US (CONFIG_MDNS_TIMER_PERIOD_MS*1000)
|
||||
|
||||
#define MDNS_SERVICE_LOCK() xSemaphoreTake(_mdns_service_semaphore, portMAX_DELAY)
|
||||
#define MDNS_SERVICE_UNLOCK() xSemaphoreGive(_mdns_service_semaphore)
|
||||
|
||||
#define queueToEnd(type, queue, item) \
|
||||
if (!queue) { \
|
||||
queue = item; \
|
||||
@@ -156,23 +139,12 @@
|
||||
|
||||
#define queueFree(type, queue) while (queue) { type * _q = queue; queue = queue->next; mdns_mem_free(_q); }
|
||||
|
||||
#define PCB_STATE_IS_PROBING(s) (s->state > PCB_OFF && s->state < PCB_ANNOUNCE_1)
|
||||
#define PCB_STATE_IS_ANNOUNCING(s) (s->state > PCB_PROBE_3 && s->state < PCB_RUNNING)
|
||||
#define PCB_STATE_IS_RUNNING(s) (s->state == PCB_RUNNING)
|
||||
|
||||
#ifndef HOOK_MALLOC_FAILED
|
||||
#define HOOK_MALLOC_FAILED ESP_LOGE(TAG, "Cannot allocate memory (line: %d, free heap: %" PRIu32 " bytes)", __LINE__, esp_get_free_heap_size());
|
||||
#define HOOK_MALLOC_FAILED do { ESP_LOGE(TAG, "Cannot allocate memory (%s(%d), free heap: %" PRIu32 " bytes)", __func__, __LINE__, esp_get_free_heap_size()); } while(0)
|
||||
#endif
|
||||
|
||||
typedef size_t mdns_if_t;
|
||||
|
||||
typedef enum {
|
||||
PCB_OFF, PCB_DUP, PCB_INIT,
|
||||
PCB_PROBE_1, PCB_PROBE_2, PCB_PROBE_3,
|
||||
PCB_ANNOUNCE_1, PCB_ANNOUNCE_2, PCB_ANNOUNCE_3,
|
||||
PCB_RUNNING
|
||||
} mdns_pcb_state_t;
|
||||
|
||||
typedef enum {
|
||||
MDNS_ANSWER, MDNS_NS, MDNS_EXTRA
|
||||
} mdns_parsed_record_type_t;
|
||||
@@ -196,6 +168,10 @@ typedef enum {
|
||||
ACTION_MAX
|
||||
} mdns_action_type_t;
|
||||
|
||||
typedef enum {
|
||||
ACTION_RUN,
|
||||
ACTION_CLEANUP,
|
||||
} mdns_action_subtype_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t id;
|
||||
@@ -342,15 +318,6 @@ typedef struct mdns_tx_packet_s {
|
||||
uint16_t id;
|
||||
} mdns_tx_packet_t;
|
||||
|
||||
typedef struct {
|
||||
mdns_pcb_state_t state;
|
||||
mdns_srv_item_t **probe_services;
|
||||
uint8_t probe_services_len;
|
||||
uint8_t probe_ip;
|
||||
uint8_t probe_running;
|
||||
uint16_t failed_probes;
|
||||
} mdns_pcb_t;
|
||||
|
||||
typedef enum {
|
||||
SEARCH_OFF,
|
||||
SEARCH_INIT,
|
||||
@@ -405,21 +372,6 @@ typedef struct mdns_browse_sync {
|
||||
mdns_browse_result_sync_t *sync_result;
|
||||
} mdns_browse_sync_t;
|
||||
|
||||
typedef struct mdns_server_s {
|
||||
struct {
|
||||
mdns_pcb_t pcbs[MDNS_IP_PROTOCOL_MAX];
|
||||
} interfaces[MDNS_MAX_INTERFACES];
|
||||
const char *hostname;
|
||||
const char *instance;
|
||||
mdns_srv_item_t *services;
|
||||
QueueHandle_t action_queue;
|
||||
SemaphoreHandle_t action_sema;
|
||||
mdns_tx_packet_t *tx_queue_head;
|
||||
mdns_search_once_t *search_once;
|
||||
esp_timer_handle_t timer_handle;
|
||||
mdns_browse_t *browse;
|
||||
} mdns_server_t;
|
||||
|
||||
typedef struct {
|
||||
mdns_action_type_t type;
|
||||
union {
|
||||
@@ -452,17 +404,3 @@ typedef struct {
|
||||
} browse_sync;
|
||||
} data;
|
||||
} mdns_action_t;
|
||||
|
||||
/*
|
||||
* @brief Convert mnds if to esp-netif handle
|
||||
*
|
||||
* @param tcpip_if mdns supported interface as internal enum
|
||||
*
|
||||
* @return
|
||||
* - ptr to esp-netif on success
|
||||
* - NULL if no available netif for current interface index
|
||||
*/
|
||||
esp_netif_t *_mdns_get_esp_netif(mdns_if_t tcpip_if);
|
||||
|
||||
|
||||
#endif /* MDNS_PRIVATE_H_ */
|
||||
|
||||
111
components/mdns/private_include/mdns_querier.h
Normal file
111
components/mdns/private_include/mdns_querier.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "mdns_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Start or stop requested queries
|
||||
* @note Called periodically from timer task in mdns.c
|
||||
*/
|
||||
void mdns_priv_query_start_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Free search item queue
|
||||
* @note Called from mdns_free()
|
||||
*/
|
||||
void mdns_priv_query_free(void);
|
||||
|
||||
/**
|
||||
* @brief Free search results
|
||||
* @note Called from multiple modules (browser, querier, core)
|
||||
*/
|
||||
void mdns_priv_query_results_free(mdns_result_t *results);
|
||||
|
||||
/**
|
||||
* @brief Complete the query if max results reached
|
||||
* @note Called from the packet parser
|
||||
*/
|
||||
void mdns_priv_query_done(void);
|
||||
|
||||
/**
|
||||
* @brief Looks for the name/type in active search items
|
||||
*
|
||||
* @note Called from the packet parser (mdns_receive.c)
|
||||
*
|
||||
* @return search results
|
||||
*/
|
||||
mdns_search_once_t *mdns_priv_query_find(mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Looks for the name/type in the search items
|
||||
*
|
||||
* @note Called from the packet parser (mdns_receive.c)
|
||||
*/
|
||||
mdns_search_once_t *mdns_priv_query_find_from(mdns_search_once_t *s, mdns_name_t *name, uint16_t type, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Add TXT item to the search results
|
||||
* @note Called from the packet parser (mdns_receive.c)
|
||||
*/
|
||||
void mdns_priv_query_result_add_txt(mdns_search_once_t *search, mdns_txt_item_t *txt, uint8_t *txt_value_len,
|
||||
size_t txt_count, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol,
|
||||
uint32_t ttl);
|
||||
/**
|
||||
* @brief Add IP (A/AAAA records) to the search results
|
||||
* @note Called from the packet parser (mdns_receive.c)
|
||||
*/
|
||||
void mdns_priv_query_result_add_ip(mdns_search_once_t *search, const char *hostname, esp_ip_addr_t *ip,
|
||||
mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl);
|
||||
|
||||
/**
|
||||
* @brief Add SRV record to the search results
|
||||
* @note Called from the packet parser (mdns_receive.c)
|
||||
*/
|
||||
void mdns_priv_query_result_add_srv(mdns_search_once_t *search, const char *hostname, uint16_t port,
|
||||
mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint32_t ttl);
|
||||
|
||||
/**
|
||||
* @brief Add PTR record to the search results
|
||||
* @note Called from the packet parser (mdns_receive.c)
|
||||
*/
|
||||
mdns_result_t *mdns_priv_query_result_add_ptr(mdns_search_once_t *search, const char *instance,
|
||||
const char *service_type, const char *proto, mdns_if_t tcpip_if,
|
||||
mdns_ip_protocol_t ip_protocol, uint32_t ttl);
|
||||
|
||||
/**
|
||||
* @brief Perform action from mdns service queue
|
||||
*
|
||||
* @note Called from the _mdns_service_task() in mdns.c
|
||||
*/
|
||||
void mdns_priv_query_action(mdns_action_t *action, mdns_action_subtype_t type);
|
||||
|
||||
/**
|
||||
* @brief Create linked IP (copy) from parsed one
|
||||
*/
|
||||
mdns_ip_addr_t *mdns_priv_result_addr_create_ip(esp_ip_addr_t *ip);
|
||||
|
||||
/**
|
||||
* @brief Update TTL results to whichever is smaller
|
||||
*/
|
||||
static inline void mdns_priv_query_update_result_ttl(mdns_result_t *r, uint32_t ttl)
|
||||
{
|
||||
r->ttl = r->ttl < ttl ? r->ttl : ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send a search query packet on the specified interface and protocol
|
||||
*/
|
||||
void mdns_priv_query_send(mdns_search_once_t *search, mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
24
components/mdns/private_include/mdns_receive.h
Normal file
24
components/mdns/private_include/mdns_receive.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "mdns_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Perform action from mdns service queue
|
||||
*
|
||||
* @note Called from the _mdns_service_task() in mdns.c
|
||||
*/
|
||||
void mdns_priv_receive_action(mdns_action_t *action, mdns_action_subtype_t type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
98
components/mdns/private_include/mdns_responder.h
Normal file
98
components/mdns/private_include/mdns_responder.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "mdns_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Perform action from mdns responder
|
||||
*
|
||||
* @note Called from the _mdns_service_task() in mdns.c
|
||||
*/
|
||||
void mdns_priv_responder_action(mdns_action_t *action, mdns_action_subtype_t type);
|
||||
|
||||
/**
|
||||
* @brief Initializes responder storage
|
||||
*/
|
||||
esp_err_t mdns_priv_responder_init(void);
|
||||
|
||||
/**
|
||||
* @brief Frees responder storage
|
||||
*/
|
||||
void mdns_priv_responder_free(void);
|
||||
|
||||
/**
|
||||
* @brief get global (mdns_server->hostname) hostname
|
||||
*/
|
||||
const char *mdns_priv_get_global_hostname(void);
|
||||
|
||||
/**
|
||||
* @brief get service list
|
||||
*/
|
||||
mdns_srv_item_t *mdns_priv_get_services(void);
|
||||
|
||||
/**
|
||||
* @brief get host list
|
||||
*/
|
||||
mdns_host_item_t *mdns_priv_get_hosts(void);
|
||||
|
||||
/**
|
||||
* @brief get self host
|
||||
*/
|
||||
mdns_host_item_t *mdns_priv_get_self_host(void);
|
||||
|
||||
/**
|
||||
* @brief set global hostname
|
||||
*/
|
||||
void mdns_priv_set_global_hostname(const char *hostname);
|
||||
|
||||
/**
|
||||
* @brief get mdns_server->instance
|
||||
*/
|
||||
const char *mdns_priv_get_instance(void);
|
||||
|
||||
/**
|
||||
* @brief set mdns_server->instance
|
||||
*/
|
||||
void mdns_priv_set_instance(const char *instance);
|
||||
|
||||
/**
|
||||
* @brief Returns true if the mdns server is initialized
|
||||
*/
|
||||
bool mdns_priv_is_server_init(void);
|
||||
|
||||
/**
|
||||
* @brief Restart the responder on all services without instance
|
||||
*/
|
||||
void mdns_priv_restart_all_pcbs_no_instance(void);
|
||||
|
||||
/**
|
||||
* @brief Restart the responder on all active PCBs
|
||||
*/
|
||||
void mdns_priv_restart_all_pcbs(void);
|
||||
|
||||
/**
|
||||
* @brief Adds a delegated hostname to the linked list
|
||||
* @param hostname Host name pointer
|
||||
* @param address_list Address list
|
||||
* @return true on success
|
||||
* false if the host wasn't attached (this is our hostname, or alloc failure) so we have to free the structs
|
||||
*/
|
||||
bool mdns_priv_delegate_hostname_add(const char *hostname, mdns_ip_addr_t *address_list);
|
||||
|
||||
/**
|
||||
* @brief Remaps hostname of self service
|
||||
*/
|
||||
void mdns_priv_remap_self_service_hostname(const char *old_hostname, const char *new_hostname);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
131
components/mdns/private_include/mdns_send.h
Normal file
131
components/mdns/private_include/mdns_send.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "mdns_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Free a transmit packet and its associated resources
|
||||
*/
|
||||
void mdns_priv_free_tx_packet(mdns_tx_packet_t *packet);
|
||||
|
||||
/**
|
||||
* @brief Create a probe packet for service discovery
|
||||
*/
|
||||
mdns_tx_packet_t *mdns_priv_create_probe_packet(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t *services[], size_t len, bool first, bool include_ip);
|
||||
|
||||
/**
|
||||
* @brief Allocate and initialize an answer record for mDNS response
|
||||
*/
|
||||
bool mdns_priv_create_answer(mdns_out_answer_t **destination, uint16_t type, mdns_service_t *service,
|
||||
mdns_host_item_t *host, bool flush, bool bye);
|
||||
|
||||
/**
|
||||
* @brief Create an announcement packet for service advertisement
|
||||
*/
|
||||
mdns_tx_packet_t *mdns_priv_create_announce_packet(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t *services[], size_t len, bool include_ip);
|
||||
|
||||
/**
|
||||
* @brief Create an announcement packet from an existing probe packet
|
||||
*/
|
||||
mdns_tx_packet_t *mdns_priv_create_announce_from_probe(mdns_tx_packet_t *probe);
|
||||
|
||||
/**
|
||||
* @brief Dispatch a transmit packet for sending
|
||||
*/
|
||||
void mdns_priv_dispatch_tx_packet(mdns_tx_packet_t *p);
|
||||
|
||||
/**
|
||||
* @brief Send a goodbye message for a service subtype
|
||||
*/
|
||||
void mdns_priv_send_bye_subtype(mdns_srv_item_t *service, const char *instance_name, mdns_subtype_t *remove_subtypes);
|
||||
|
||||
/**
|
||||
* @brief Append host list information to service records
|
||||
*/
|
||||
bool mdns_priv_append_host_list_in_services(mdns_out_answer_t **destination, mdns_srv_item_t *services[], size_t services_len, bool flush, bool bye);
|
||||
|
||||
/**
|
||||
* @brief Clear the transmit queue head for a specific interface and protocol
|
||||
*/
|
||||
void mdns_priv_clear_tx_queue_if(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Clear the global transmit queue head
|
||||
*/
|
||||
void mdns_priv_clear_tx_queue(void);
|
||||
|
||||
/**
|
||||
* @brief Remove scheduled service packets for a given service
|
||||
*/
|
||||
void mdns_priv_remove_scheduled_service_packets(mdns_service_t *service);
|
||||
|
||||
/**
|
||||
* @brief Get the next packet from the PCB queue for a specific interface and protocol
|
||||
*/
|
||||
mdns_tx_packet_t *mdns_priv_get_next_packet(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Deallocate an answer record
|
||||
*/
|
||||
void mdns_priv_dealloc_answer(mdns_out_answer_t **destination, uint16_t type, mdns_srv_item_t *service);
|
||||
|
||||
/**
|
||||
* @brief Remove a scheduled answer for a specific interface, protocol, and service
|
||||
*/
|
||||
void mdns_priv_remove_scheduled_answer(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, uint16_t type, mdns_srv_item_t *service);
|
||||
|
||||
/**
|
||||
* @brief Allocate a new packet with default settings
|
||||
*/
|
||||
mdns_tx_packet_t *mdns_priv_alloc_packet(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
|
||||
|
||||
/**
|
||||
* @brief Send all pending mDNS packets
|
||||
*/
|
||||
void mdns_priv_send_packets(void);
|
||||
|
||||
/**
|
||||
* @brief Execute an mDNS action with specified type
|
||||
*/
|
||||
void mdns_priv_send_action(mdns_action_t *action, mdns_action_subtype_t type);
|
||||
|
||||
/**
|
||||
* @brief Schedule a transmit packet for sending after a specified delay
|
||||
*/
|
||||
void mdns_priv_send_after(mdns_tx_packet_t *packet, uint32_t ms_after);
|
||||
|
||||
/**
|
||||
* @brief Send by for particular services on particular PCB
|
||||
*/
|
||||
void mdns_priv_send_bye(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, mdns_srv_item_t **services, size_t len, bool include_ip);
|
||||
|
||||
/**
|
||||
* @brief Create answer packet to questions from parsed packet
|
||||
*/
|
||||
void mdns_priv_create_answer_from_parsed_packet(mdns_parsed_packet_t *parsed_packet);
|
||||
|
||||
/**
|
||||
* @brief appends one TXT record ("key=value" or "key")
|
||||
*
|
||||
* @param packet MDNS packet
|
||||
* @param index offset in the packet
|
||||
* @param txt one txt record
|
||||
*
|
||||
* @return length of added data: length of the added txt value + 1 on success
|
||||
* 0 if data won't fit the packet
|
||||
* -1 if invalid TXT entry
|
||||
*/
|
||||
int mdns_priv_append_one_txt_record_entry(uint8_t *packet, uint16_t *index, mdns_txt_linked_item_t *txt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
32
components/mdns/private_include/mdns_service.h
Normal file
32
components/mdns/private_include/mdns_service.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "mdns_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Lock mdns service
|
||||
*/
|
||||
void mdns_priv_service_lock(void);
|
||||
|
||||
/**
|
||||
* @brief Unlock mdns service
|
||||
*/
|
||||
void mdns_priv_service_unlock(void);
|
||||
|
||||
/**
|
||||
* @brief Send the given action to the service queue
|
||||
*/
|
||||
bool mdns_priv_queue_action(mdns_action_t *action);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
155
components/mdns/private_include/mdns_utils.h
Normal file
155
components/mdns/private_include/mdns_utils.h
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "mdns_private.h"
|
||||
|
||||
#define MDNS_UTILS_DEFAULT_DOMAIN "local"
|
||||
#define MDNS_UTILS_SIZEOF_IP6_ADDR (MDNS_ANSWER_AAAA_SIZE)
|
||||
|
||||
/**
|
||||
* @brief read uint16_t from a packet
|
||||
* @param packet the packet
|
||||
* @param index index in the packet where the value starts
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
static inline uint16_t mdns_utils_read_u16(const uint8_t *packet, uint16_t index)
|
||||
{
|
||||
return (uint16_t)(packet[index]) << 8 | packet[index + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief read uint32_t from a packet
|
||||
* @param packet the packet
|
||||
* @param index index in the packet where the value starts
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
static inline uint32_t mdns_utils_read_u32(const uint8_t *packet, uint16_t index)
|
||||
{
|
||||
return (uint32_t)(packet[index]) << 24 | (uint32_t)(packet[index + 1]) << 16 | (uint32_t)(packet[index + 2]) << 8 | packet[index + 3];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check for null or empty string
|
||||
*/
|
||||
static inline bool mdns_utils_str_null_or_empty(const char *str)
|
||||
{
|
||||
return (str == NULL || *str == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief reads and formats MDNS FQDN into mdns_name_t structure
|
||||
*
|
||||
* @param packet MDNS packet
|
||||
* @param start Starting point of FQDN
|
||||
* @param name mdns_name_t structure to populate
|
||||
*
|
||||
* @return the address after the parsed FQDN in the packet or NULL on error
|
||||
*/
|
||||
const uint8_t *mdns_utils_parse_fqdn(const uint8_t *packet, const uint8_t *start, mdns_name_t *name, size_t packet_len);
|
||||
|
||||
/**
|
||||
* @brief reads MDNS FQDN into mdns_name_t structure
|
||||
* FQDN is in format: [hostname.|[instance.]_service._proto.]local.
|
||||
*
|
||||
* @param packet MDNS packet
|
||||
* @param start Starting point of FQDN
|
||||
* @param name mdns_name_t structure to populate
|
||||
* @param buf temporary char buffer
|
||||
*
|
||||
* @return the address after the parsed FQDN in the packet or NULL on error
|
||||
*/
|
||||
const uint8_t *mdns_utils_read_fqdn(const uint8_t *packet, const uint8_t *start, mdns_name_t *name, char *buf, size_t packet_len);
|
||||
|
||||
/**
|
||||
* @brief Check if IPv6 address is NULL
|
||||
*/
|
||||
bool mdns_utils_ipv6_address_is_zero(esp_ip6_addr_t ip6);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Checks if the hostname is ours
|
||||
*/
|
||||
bool mdns_utils_hostname_is_ours(const char *hostname);
|
||||
|
||||
/**
|
||||
* @brief Checks if given service matches with arguments
|
||||
*/
|
||||
bool mdns_utils_service_match(const mdns_service_t *srv, const char *service, const char *proto,
|
||||
const char *hostname);
|
||||
|
||||
/**
|
||||
* @brief finds service from given service type
|
||||
* @param server the server
|
||||
* @param service service type to match
|
||||
* @param proto proto to match
|
||||
* @param hostname hostname of the service (if non-null)
|
||||
*
|
||||
* @return the service item if found or NULL on error
|
||||
*/
|
||||
mdns_srv_item_t *mdns_utils_get_service_item(const char *service, const char *proto, const char *hostname);
|
||||
|
||||
/**
|
||||
* @brief finds service from given instance, etc
|
||||
*/
|
||||
mdns_srv_item_t *mdns_utils_get_service_item_instance(const char *instance, const char *service, const char *proto,
|
||||
const char *hostname);
|
||||
|
||||
/**
|
||||
* @brief returns true if the service matches with args
|
||||
*/
|
||||
bool mdns_utils_service_match_instance(const mdns_service_t *srv, const char *instance, const char *service,
|
||||
const char *proto, const char *hostname);
|
||||
|
||||
/**
|
||||
* @brief returns true if the two instances match
|
||||
*/
|
||||
bool mdns_utils_instance_name_match(const char *lhs, const char *rhs);
|
||||
|
||||
/**
|
||||
* @brief Get service instance name
|
||||
*/
|
||||
const char *mdns_utils_get_service_instance_name(const mdns_service_t *service);
|
||||
|
||||
/**
|
||||
* @brief Copy address list
|
||||
*/
|
||||
mdns_ip_addr_t *mdns_utils_copy_address_list(const mdns_ip_addr_t *address_list);
|
||||
|
||||
/**
|
||||
* @brief Free address list
|
||||
*/
|
||||
void mdns_utils_free_address_list(mdns_ip_addr_t *address_list);
|
||||
|
||||
/**
|
||||
* @brief appends uint16_t in a packet, incrementing the index
|
||||
*
|
||||
* @param packet MDNS packet
|
||||
* @param index offset in the packet
|
||||
* @param value the value to set
|
||||
*
|
||||
* @return length of added data: 0 on error or 2 on success
|
||||
*/
|
||||
uint8_t mdns_utils_append_u16(uint8_t *packet, uint16_t *index, uint16_t value);
|
||||
|
||||
/**
|
||||
* @brief appends byte in a packet, incrementing the index
|
||||
*
|
||||
* @param packet MDNS packet
|
||||
* @param index offset in the packet
|
||||
* @param value the value to set
|
||||
*
|
||||
* @return length of added data: 0 on error or 1 on success
|
||||
*/
|
||||
static inline uint8_t mdns_utils_append_u8(uint8_t *packet, uint16_t *index, uint8_t value)
|
||||
{
|
||||
if (*index >= MDNS_MAX_PACKET_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
packet[*index] = value;
|
||||
*index += 1;
|
||||
return 1;
|
||||
}
|
||||
56
components/mdns/refactor_2025.md
Normal file
56
components/mdns/refactor_2025.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# MDNS Component Refactor
|
||||
|
||||
## Introduction
|
||||
|
||||
The MDNS component has been refactored from a single monolithic file `mdns.c` into a set of focused modules with clear responsibilities. This restructuring maintains the same functionality while improving code organization, maintainability, and testability.
|
||||
|
||||
The refactor has been performed in thee major stages, to ensure smooth transition to production while avoiding major changes in untested paths. These stages are:
|
||||
[x] Restructuring into several modules while keeping the actual code changes to minimum
|
||||
[ ] Cover functionality of each module by specifically focused unit tests.
|
||||
[ ] Optimizations on module levels (string and queue operations, esp_timer dependency, socket task improvements, error checking)
|
||||
|
||||
## Module Structure
|
||||
|
||||
### mdns_service.c
|
||||
The central service module that orchestrates the MDNS component operation. It manages the service task, timer, action queue, and system event handling. This module provides the entry points for the public API and coordinates actions between different modules. It's responsible for the general lifecycle of the MDNS service.
|
||||
|
||||
### mdns_responder.c
|
||||
Implements the responder functionality that answers incoming MDNS queries. It manages hostname and service registration, and generates appropriate response packets based on the queried information. The responder tracks all registered services and their associated metadata, handling hostname conflicts and service announcement.
|
||||
|
||||
### mdns_browser.c
|
||||
Handles browse/query functionality, managing browse requests for MDNS services on the network. It provides the infrastructure for adding, tracking, and removing browse requests, and processes incoming service discovery responses. The module manages browsing state and notifies applications when services appear or disappear.
|
||||
|
||||
### mdns_querier.c
|
||||
Handles outgoing query functionality, maintaining state for ongoing queries and processing query responses. It supports one-shot and continuous queries, manages query timeouts, and organizes results. The module provides the infrastructure for applications to discover services and resolve hostnames.
|
||||
|
||||
### mdns_send.c
|
||||
Manages packet generation and transmission. It constructs properly formatted MDNS packets, with correct headers and resource records, and handles the transmission scheduling and queueing of outgoing messages. This module encapsulates knowledge about the MDNS protocol packet structure.
|
||||
|
||||
### mdns_receive.c
|
||||
Processes incoming MDNS packets, parsing and validating their structure. It extracts queries and responses from received packets and dispatches them to the appropriate handling modules. This module focuses on packet parsing and initial classification.
|
||||
|
||||
### mdns_pcb.c
|
||||
Manages protocol control blocks (PCBs) that handle the state machine for MDNS interface operation. It coordinates the probing, announcing, and running states for each network interface and protocol combination. This module is responsible for service conflict detection, duplicate interface handling, and ensuring proper service announcements.
|
||||
|
||||
### mdns_networking_lwip.c and mdns_networking_socket.c
|
||||
These modules provide networking abstraction for different underlying network stacks. They handle multicast group management, packet reception, and transmission over the network interfaces. This abstraction allows the MDNS implementation to work with different networking stacks.
|
||||
|
||||
### mdns_netif.c
|
||||
Manages network interface registration and monitoring. It tracks network interface state changes and notifies the MDNS service when interfaces become available or unavailable, enabling dynamic adaptation to network configuration changes.
|
||||
|
||||
### mdns_utils.c
|
||||
Contains common utility functions used across multiple modules, such as string handling, data structure operations, and debug helpers. This module centralizes shared functionality to avoid code duplication.
|
||||
|
||||
### mdns_mem_caps.c
|
||||
Provides memory allocation functions with capability-based allocation support. It encapsulates memory management operations, making it easier to track and debug memory usage across the MDNS component.
|
||||
|
||||
### mdns_debug.c
|
||||
Contains debugging and logging utilities specific to the MDNS component. It provides structured logging and debug information for troubleshooting complex MDNS operations.
|
||||
|
||||
## Testability Improvements
|
||||
|
||||
The refactored module structure significantly enhances testability by clearly separating different functional areas. Each module can now be tested in isolation with proper mocking of its dependencies. The separation of networking, packet handling, and business logic makes it possible to create focused unit tests without the complexity of managing the entire MDNS stack. Module-specific state is now contained within individual modules rather than distributed throughout a monolithic codebase, making test case setup and verification more straightforward. The refactoring also improves the ability to simulate various network conditions and edge cases by intercepting inter-module communication at well-defined boundaries.
|
||||
|
||||
## Conclusion
|
||||
|
||||
The MDNS component refactoring provides a more maintainable and testable architecture while preserving the existing public API. By breaking down the monolithic implementation into focused modules with clear responsibilities, the codebase becomes easier to understand, extend, and maintain. The refactored structure makes it simpler to identify and fix bugs, implement new features, and adapt to future requirements. This architectural improvement supports long-term evolution of the component while maintaining backward compatibility for existing applications.
|
||||
244
components/mdns/refactoring_details.md
Normal file
244
components/mdns/refactoring_details.md
Normal file
@@ -0,0 +1,244 @@
|
||||
| Original Function | Refactored Function | Concerns |
|
||||
|------------------|--------------------|---------|
|
||||
| [mdns_if_from_preset_if](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L136) | [mdns_if_from_preset](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L87) | The function name was changed from `mdns_if_from_preset_if` to `mdns_if_from_preset`, but the logic remains identical. While this appears to be a systematic naming convention change (supported by the evidence that all call sites in the refactored codebase now use the new name), there's a risk that some call sites in the original codebase might not have been updated, or that external code depending on the original function name could break. The functionality itself is preserved, but the interface change could cause linkage or compilation issues if not all dependencies are updated. |
|
||||
| [esp_netif_from_preset_if](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L151) | [netif_from_preset](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L102) | |
|
||||
| [_mdns_get_esp_netif](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L179) | [mdns_priv_get_esp_netif](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L118) | The function `esp_netif_from_preset_if()` has been replaced with `netif_from_preset()` in the refactored code. While both functions appear to serve the same purpose of looking up predefined network interfaces, this change could introduce subtle bugs if: <br/> 1. The new function has different error handling behavior <br/> 2. The new function returns different values for edge cases (e.g., when interfaces are not available) <br/> 3. The new function has different performance characteristics that could affect timing-sensitive MDNS operations <br/> <br/> The original `esp_netif_from_preset_if()` was an inline function with a switch statement mapping predefined interface types to actual esp_netif instances. If `netif_from_preset()` has a different implementation or doesn't handle all the same `mdns_predef_if_t` enum values, this could break MDNS functionality for predefined interfaces like STA, AP, or ETH when they become available after initialization. |
|
||||
| [_mdns_clean_netif_ptr](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L196) | [mdns_priv_netif_disable](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L134) | |
|
||||
| [_mdns_get_if_from_esp_netif](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L207) | [get_if_from_netif](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L144) | The function `esp_netif_from_preset_if` was renamed to `netif_from_preset` in the refactored code. While both functions appear to serve the same purpose (converting a predefined interface enum to an esp_netif pointer), this change could introduce subtle bugs if the implementation of `netif_from_preset` differs from the original `esp_netif_from_preset_if` function, particularly in error handling, return values for unsupported interface types, or behavioral differences in edge cases. |
|
||||
| [_str_null_or_empty](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L224) | [mdns_utils_str_null_or_empty](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.h#L38) | The refactoring introduced a potential compilation issue. The original function `_str_null_or_empty` was defined as `static inline` within `mdns.c`, making it only visible within that translation unit. The refactored version `mdns_utils_str_null_or_empty` is also `static inline` but in `mdns_utils.h`, which means it needs to be properly included in all files that use it. From the search results, I can see that `mdns_browser.c`, `mdns_debug.c`, and `mdns_responder.c` are using the new function, but I cannot verify if the original `mdns.c` file has been updated to include `mdns_utils.h` and replace all calls to `_str_null_or_empty` with `mdns_utils_str_null_or_empty`. If the original `mdns.c` still contains calls to the old function name without including the new header, this will cause compilation failures. |
|
||||
| [_mdns_mangle_name](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L232) | [mangle_name](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L138) | |
|
||||
| [_mdns_service_match](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L272) | [mdns_utils_service_match](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L121) | The refactored function adds a null check for the 'srv' parameter (!srv), which changes the behavior from the original function. In the original implementation, callers could potentially pass NULL for 'srv', which would cause a segmentation fault when accessing srv->hostname. The refactored version now explicitly checks for NULL and returns false, which may mask underlying bugs in calling code that should be fixed rather than silently handled. Additionally, the function visibility changed from static to public, which could break encapsulation if this was intended to be an internal helper function. |
|
||||
| [_mdns_get_service_item](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L291) | [mdns_utils_get_service_item](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L131) | The refactored function changed from static to non-static visibility and now uses mdns_priv_get_services() instead of directly accessing _mdns_server->services. This could introduce subtle bugs related to: <br/> 1. Thread safety - the original function was likely called within MDNS_SERVICE_LOCK/UNLOCK contexts, but the refactored version's access pattern through mdns_priv_get_services() may not provide the same guarantees <br/> 2. Encapsulation - making the function non-static exposes internal service lookup logic that was previously hidden <br/> 3. Dependency on mdns_utils_service_match having identical behavior to _mdns_service_match, which needs verification <br/> 4. Potential null pointer issues if mdns_priv_get_services() returns NULL when _mdns_server->services would not be NULL |
|
||||
| [_mdns_get_service_item_subtype](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L303) | [get_service_item_subtype](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L39) | |
|
||||
| [mdns_get_host_item](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L321) | [get_host_item](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L240) | The refactored get_host_item function introduces a potential NULL return when the hostname matches the server's hostname or is NULL. In the original implementation, &_mdns_self_host was always returned (a guaranteed non-NULL address), but the refactored version calls mdns_priv_get_self_host() which can return NULL if s_server is NULL. This could break callers that assume non-NULL returns for self-host cases, potentially causing segmentation faults or incorrect behavior in service discovery and host resolution. |
|
||||
| [_mdns_can_add_more_services](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L336) | [can_add_more_services](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L129) | |
|
||||
| [_mdns_send_rx_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L354) | [send_rx_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_networking_lwip.c#L48) | The refactored function 'send_rx_action' replaces the direct xQueueSend call with mdns_priv_queue_action, but the implementation of mdns_priv_queue_action is not visible in the provided context. This introduces a potential subtle bug if mdns_priv_queue_action does not properly handle the queue operation with the same blocking/non-blocking behavior (original used (TickType_t)0 timeout) or if it returns incorrect boolean values that don't match the expected ESP_OK/ESP_ERR_NO_MEM semantics. Additionally, the function was changed from global to static scope, which could break external callers if not properly addressed in the refactoring. |
|
||||
| [_mdns_get_default_instance_name](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L373) | [get_default_instance_name](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L175) | The refactored function introduces potential subtle bugs due to the separation of concerns. Specifically: <br/> 1. The helper functions mdns_priv_get_instance() and mdns_priv_get_global_hostname() may have different NULL-checking behavior than the original direct _mdns_server access <br/> 2. There could be thread safety issues if s_server (the refactored equivalent of _mdns_server) is accessed without proper synchronization <br/> 3. The behavior when s_server is NULL may not exactly match the original behavior, particularly in edge cases <br/> 4. The replacement of _str_null_or_empty with mdns_utils_str_null_or_empty could have different implementations that handle edge cases differently |
|
||||
| [_mdns_get_service_instance_name](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L389) | [mdns_utils_get_service_instance_name](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L192) | The refactored function get_default_instance_name() can return NULL when both mdns_priv_get_instance() and mdns_priv_get_global_hostname() return NULL or empty strings, whereas the original _mdns_get_default_instance_name() appears to always return a non-NULL string. This behavioral change could lead to null pointer dereferences in callers that expect a valid string, as evidenced by the original usage patterns where the return value is used directly in string operations without null checks. |
|
||||
| [_mdns_instance_name_match](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L398) | [mdns_utils_instance_name_match](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L202) | The refactored function `mdns_utils_instance_name_match` calls `get_default_instance_name()` which uses `mdns_priv_get_instance()` internally, while the original function `_mdns_instance_name_match` used `_mdns_get_default_instance_name()` that directly accessed `_mdns_server->instance`. The behavioral equivalence depends on whether `mdns_priv_get_instance()` returns the same value as the original direct server access, particularly in edge cases where the server state might be inconsistent or during initialization/teardown phases. |
|
||||
| [_mdns_service_match_instance](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L409) | [mdns_utils_service_match_instance](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L162) | The refactored function changed from static to public scope (removed static keyword), which could break encapsulation and allow unintended external access to internal service matching logic. Additionally, the helper function `mdns_utils_str_null_or_empty` might have different behavior than the original `_str_null_or_empty` if it was reimplemented during the utility extraction. |
|
||||
| [_mdns_get_service_item_instance](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L422) | [mdns_utils_get_service_item_instance](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L143) | |
|
||||
| [_mdns_read_fqdn](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L452) | [mdns_utils_read_fqdn](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L18) | The refactored function mdns_utils_parse_fqdn uses a static buffer (static char buf[MDNS_NAME_BUF_LEN]) which could cause thread safety issues in a multi-threaded environment or reentrancy problems if the function is called recursively. Additionally, the memory movement operations using memmove in mdns_utils_parse_fqdn appear to be incorrectly calculating the offsets - the destination and source calculations seem to assume the mdns_name_t structure fields are exactly MDNS_NAME_BUF_LEN bytes apart, which may not be correct if the structure has padding or different field sizes. |
|
||||
| [_mdns_set_u16](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L512) | ??? | |
|
||||
| [_mdns_append_u8](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L530) | [mdns_utils_append_u8](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.h#L147) | |
|
||||
| [_mdns_append_u16](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L549) | [mdns_utils_append_u16](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L262) | |
|
||||
| [_mdns_append_u32](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L568) | [append_u32](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L39) | The bounds check in the refactored function uses `(*index + sizeof(uint32_t)) >= MDNS_MAX_PACKET_SIZE` while the original used `(*index + 3) >= MDNS_MAX_PACKET_SIZE`. Although `sizeof(uint32_t)` should typically be 4, this creates a potential portability issue if compiled on a system where `uint32_t` has a different size. Additionally, the refactored function now depends on `mdns_utils_append_u8` which has different error handling - it returns 0 on bounds violation but doesn't prevent the index from being incremented in subsequent calls, potentially causing inconsistent state. |
|
||||
| [_mdns_append_type](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L590) | [append_type](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L61) | The refactored function uses inconsistent function naming: `mdns_utils_append_u16` and `append_u32` (without the `mdns_utils_` prefix). This suggests either a missing utility function implementation or inconsistent refactoring that could lead to compilation errors if `append_u32` is not properly defined in the same context. Additionally, the buffer size calculation changed from hardcoded 10 to using sizeof operations, which while functionally equivalent, introduces potential maintenance complexity. |
|
||||
| [_mdns_append_string_with_len](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L622) | [append_string](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L103) | The refactored function `append_string` introduces a subtle bug by replacing the explicit length parameter with `strlen(string)`. This breaks compatibility with: <br/> 1. Non-null-terminated strings that were previously supported <br/> 2. Binary data that might contain embedded null bytes <br/> 3. Callers who want to control the exact length written (important for DNS label encoding) <br/> 4. Embedded systems use cases where string length might be known without scanning <br/> <br/> Additionally, the return value behavior has changed from returning the number of bytes written (len + 1) to potentially returning 0 on failure, which may break error handling logic in callers expecting the original return value pattern. |
|
||||
| [_mdns_append_string](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L642) | [append_string](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L103) | The refactored function `append_single_str` has a critical semantic change in its return value. While the original `_mdns_append_string` returns the length of added data (string length + 1) on success, `append_single_str` returns the updated packet index. This could break calling code that relies on the original return value semantics for length calculations or error checking. Additionally, `append_single_str` introduces an extra error check for `mdns_utils_append_u8` that wasn't present in the original, which changes the error handling behavior. |
|
||||
| [append_one_txt_record_entry](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L665) | [mdns_priv_append_one_txt_record_entry](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L3399) | The boundary check condition `(*index + len + 1) >= MDNS_MAX_PACKET_SIZE` in both original and refactored code may be incorrect. It should likely use `>` instead of `>=` to prevent buffer overflow when `*index + len + 1` exactly equals `MDNS_MAX_PACKET_SIZE`. This would write one byte beyond the allocated buffer size. The refactoring preserved this potential off-by-one error rather than fixing it. |
|
||||
| [append_single_str](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L686) | [append_single_str](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L430) | The refactored append_single_str function now calls mdns_utils_append_u8 instead of _mdns_append_u8, but the return value check is incorrect. The original _mdns_append_u8 likely returned 0 on failure, but the new mdns_utils_append_u8 returns 1 on success and 0 on failure. However, the refactored code checks "if (!mdns_utils_append_u8(...))" which is correct for the new return semantics, but there's a potential issue: the original function returned the final packet index on success, while the new helper returns a boolean. This could cause issues if any calling code depends on the specific return value semantics beyond just success/failure checking. |
|
||||
| [append_fqdn_dots](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L711) | [append_fqdn_dots](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L455) | The refactored append_fqdn_dots function in Result 1 has a subtle bug: it always appends "arpa" suffix regardless of the input domain, whereas the original function's logic suggests this should only be used for reverse DNS queries. The function also ignores the 'last' parameter completely, which may indicate missing functionality for handling non-reverse DNS queries. Additionally, the hardcoded "arpa" suffix could cause incorrect FQDN formatting for regular forward DNS queries. |
|
||||
| [_mdns_append_fqdn](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L750) | [append_fqdn](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L667) | The refactored function calls `append_string` instead of `_mdns_append_string`, but the search results show no implementation of `append_string` in the refactored codebase. This suggests the function may be missing, which would cause linking errors or runtime failures. Additionally, the recursive call to `append_fqdn` relies on this potentially missing function, creating a dependency chain that may be broken. |
|
||||
| [_mdns_append_ptr_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L823) | [append_ptr_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L781) | |
|
||||
| [_mdns_append_subtype_ptr_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L873) | ??? | |
|
||||
| [_mdns_append_sdptr_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L918) | [append_sdptr_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1038) | |
|
||||
| [_mdns_append_txt_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L968) | [append_txt_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L974) | The refactored function uses renamed helper functions (mdns_utils_get_service_instance_name, append_fqdn, append_type, mdns_priv_append_one_txt_record_entry, set_u16) and constants (MDNS_UTILS_DEFAULT_DOMAIN) without verification that these have identical behavior and error handling as their original counterparts. Specifically, if any of these helper functions now have different buffer bounds checking, error return values, or memory management behavior, it could introduce subtle bugs in packet construction, buffer overflow protection, or error propagation. |
|
||||
| [_mdns_append_srv_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1032) | [append_srv_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L902) | The refactored function replaces direct struct member access `_mdns_server->hostname` with a function call `mdns_priv_get_global_hostname()`. This could introduce subtle bugs if: <br/> 1. The function returns NULL when the original struct member was guaranteed to be non-NULL <br/> 2. The function has different error handling semantics <br/> 3. The function introduces side effects or performance overhead not present in the original <br/> 4. The function's behavior differs in edge cases (e.g., during initialization/shutdown) <br/> <br/> Additionally, the function name changes from `_mdns_append_u16` to `mdns_utils_append_u16` and similar utility function renames could introduce behavioral differences if the implementations are not functionally equivalent. |
|
||||
| [_mdns_append_a_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1105) | [append_a_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1089) | The boundary check condition `(*index + 3) >= MDNS_MAX_PACKET_SIZE` is preserved from the original but appears to be incorrect. When *index equals MDNS_MAX_PACKET_SIZE - 3, the condition evaluates to true (preventing the write), but the code needs to write 4 bytes starting from *index. This means the valid range should be *index <= MDNS_MAX_PACKET_SIZE - 4. The condition should likely be `(*index + 4) > MDNS_MAX_PACKET_SIZE` or `(*index > MDNS_MAX_PACKET_SIZE - 4)` to properly prevent buffer overflow. |
|
||||
| [_mdns_append_aaaa_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1157) | [append_aaaa_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1141) | The refactored function uses renamed utility functions (append_fqdn, append_type, set_u16, mdns_utils_str_null_or_empty) and constants (MDNS_UTILS_DEFAULT_DOMAIN) without verification that they maintain identical behavior to the original implementations. Specifically, if any of these helper functions now have different error handling, boundary checking, or return value semantics, it could cause subtle bugs in AAAA record construction. The domain name change from MDNS_DEFAULT_DOMAIN to MDNS_UTILS_DEFAULT_DOMAIN could also affect DNS resolution if the values differ. |
|
||||
| [_mdns_append_question](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1201) | [append_question](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L734) | The refactored function uses `append_fqdn` and `mdns_utils_append_u16` which may have different error handling behavior than the original `_mdns_append_fqdn` and `_mdns_append_u16` functions. Specifically, the bounds checking in `mdns_utils_append_u16` returns 0 on error, but the original function's behavior is unknown. Additionally, the cumulative length calculation in the refactored function could be incorrect if the utility functions return 0 on error, as it would add 0 to `part_length` instead of properly handling the error case. |
|
||||
| [_mdns_get_other_if](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1242) | [mdns_priv_netif_get_other_interface](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L73) | |
|
||||
| [_mdns_if_is_dup](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1253) | [mdns_priv_pcb_check_for_duplicates](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_pcb.c#L89) | The refactored function uses a different data structure (s_pcbs) and interface lookup mechanism (s_esp_netifs[tcpip_if].duplicate) compared to the original (_mdns_server->interfaces[...].pcbs and _mdns_get_other_if). Without verification that these data structures are properly initialized and contain equivalent state information, there's a risk of accessing uninitialized memory or incorrect duplicate detection. Additionally, the refactored code assumes MDNS_IP_PROTOCOL_MAX equals 2 (covering both IPv4 and IPv6), which may not match the original implementation's explicit protocol checks. |
|
||||
| [_ipv6_address_is_zero](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1273) | [mdns_utils_ipv6_address_is_zero](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L249) | |
|
||||
| [_mdns_append_host_answer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1286) | [append_host_answer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1182) | The refactored append_answer function introduces new host validation logic that checks if the host is valid (either self-host or in the hosts list) before processing. This validation was not present in the original _mdns_append_host_answer function and could potentially block legitimate host answers if the validation logic is incorrect or if there are timing issues with the host list management. Additionally, the return value semantics have changed - the original function returned the count of successfully appended records, while append_answer returns different values (0, 1, 2, count) depending on the context, which could break the calling code's expectations. |
|
||||
| [_mdns_append_reverse_ptr_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1318) | [append_reverse_ptr_record](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L485) | The refactored function uses `mdns_priv_get_self_host()->hostname` without checking if `mdns_priv_get_self_host()` returns NULL. In the original code, `_mdns_self_host.hostname` was accessed directly, suggesting `_mdns_self_host` was a global struct. The new access pattern through a function call introduces a potential null pointer dereference if the function returns NULL when the self host is not properly initialized. |
|
||||
| [_mdns_append_service_ptr_answers](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1350) | [append_service_ptr_answers](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L870) | |
|
||||
| [_mdns_append_answer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1378) | [append_answer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1215) | The refactoring introduces potential behavioral changes in the IPv4 and IPv6 address handling logic. Specifically: <br/> <br/> 1. The original code checked `_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V4].state != PCB_DUP` and `_mdns_server->interfaces[tcpip_if].pcbs[MDNS_IP_PROTOCOL_V6].state != PCB_DUP`, but the refactored code uses `!mdns_priv_pcb_is_duplicate(tcpip_if, MDNS_IP_PROTOCOL_V4)` and `!mdns_priv_pcb_is_duplicate(tcpip_if, MDNS_IP_PROTOCOL_V6)`. The logic appears inverted - the original checks if state is NOT PCB_DUP, while the new function name suggests it checks if IS duplicate. <br/> <br/> 2. The original used `_mdns_if_is_dup(tcpip_if)` while the refactored uses `mdns_priv_pcb_check_for_duplicates(tcpip_if)`. These appear to be different checks with potentially different semantics. <br/> <br/> 3. The IPv6 zero address check changed from `_ipv6_address_is_zero(if_ip6s[i])` to `mdns_utils_ipv6_address_is_zero(if_ip6s[i])`. Without seeing the implementation of both functions, we cannot guarantee identical behavior. <br/> <br/> These changes could affect when additional interface addresses are appended and when IPv6 addresses are considered valid, potentially breaking the duplicate interface handling logic and IPv6 address validation. |
|
||||
| [_mdns_dispatch_tx_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1490) | [mdns_priv_dispatch_tx_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1329) | The refactoring changed the function from static to global visibility (mdns_priv_dispatch_tx_packet), which breaks encapsulation and could allow unintended external calls to this internal packet dispatch function. Additionally, the static buffer 'packet[MDNS_MAX_PACKET_SIZE]' remains, creating potential thread safety issues if multiple threads call this function concurrently, as they would share the same buffer. The debug functionality extraction to DBG_TX_PACKET macro and mdns_debug_tx_packet function appears correct, but without seeing the macro definition, we cannot verify if debug code is properly conditionally compiled. |
|
||||
| [_mdns_free_tx_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1559) | [mdns_priv_free_tx_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L187) | |
|
||||
| [_mdns_schedule_tx_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1588) | [mdns_priv_send_after](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1617) | The refactored function mdns_priv_send_after has changed from static to non-static visibility, which could lead to unintended external access to the internal packet scheduling mechanism. Additionally, the global queue variable has been renamed from _mdns_server->tx_queue_head to s_tx_queue_head, but without seeing the broader context of how s_tx_queue_head is declared and managed, there may be synchronization issues if multiple threads can access this queue concurrently. The original function was tightly coupled with the _mdns_server structure, and this refactoring appears to separate the queue management from the server context without clear evidence of proper synchronization mechanisms being added. |
|
||||
| [_mdns_clear_tx_queue_head](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1611) | [mdns_priv_clear_tx_queue](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L2107) | The refactored function uses a global variable `s_tx_queue_head` instead of accessing it through `_mdns_server->tx_queue_head`. This changes the data structure from a server-specific queue to a global queue, which could introduce race conditions in multi-server scenarios or break the encapsulation of server state. Additionally, the function name change suggests it's now part of a different module (mdns_send.c), which may indicate architectural changes that need verification across the entire codebase. |
|
||||
| [_mdns_clear_pcb_tx_queue_head](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1627) | [mdns_priv_clear_tx_queue_if](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1656) | |
|
||||
| [_mdns_get_next_pcb_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1655) | [mdns_priv_get_next_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L2294) | The refactored function changed from accessing `_mdns_server->tx_queue_head` to `s_tx_queue_head`, which represents a change in global state management. This could introduce subtle bugs if: <br/> 1. `s_tx_queue_head` is not properly initialized or synchronized with the rest of the system <br/> 2. Other parts of the codebase still reference `_mdns_server->tx_queue_head` inconsistently <br/> 3. The queue management operations (add/remove packets) use different queue head variables in different modules <br/> <br/> Additionally, the function visibility changed from static to non-static, which could break encapsulation and allow unintended external access to internal queue operations. |
|
||||
| [_mdns_remove_scheduled_answer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1670) | [mdns_priv_remove_scheduled_answer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L2558) | The refactored function changed from static to non-static scope while maintaining the pattern of creating a local mdns_srv_item_t variable when service is NULL. This could lead to undefined behavior if the function is called from multiple threads or contexts, as the local variable's address is used for comparison in the linked list traversal. Additionally, the global variable access changed from _mdns_server->tx_queue_head to s_tx_queue_head, which may have different synchronization guarantees. |
|
||||
| [_mdns_dealloc_answer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1704) | [mdns_priv_dealloc_answer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L2408) | The refactored function `mdns_priv_dealloc_answer` has a potential bug when `service` parameter is NULL. In the original function, when `service` is NULL, it creates a local `mdns_srv_item_t s` with NULL pointers and uses `service = &s`, then compares `d->service == service->service`. However, `service->service` will be NULL in this case, so it's comparing `d->service == NULL`. This logic is preserved in the refactored version, but the comparison `d->service == service->service` when `service` points to a stack-allocated structure with NULL members could be confusing and error-prone. The function appears to be trying to match answers where the service pointer is NULL, but this pattern is fragile and depends on the caller's understanding of when to pass NULL vs an actual service. |
|
||||
| [_mdns_alloc_answer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1733) | [mdns_priv_create_answer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L1053) | The refactored function `mdns_priv_create_answer` has lost the `static` qualifier that was present in the original `_mdns_alloc_answer` function. This changes the function's visibility from file-scope to global scope, which could lead to unintended external access and potential linkage issues. Additionally, the function name change from `_mdns_alloc_answer` to `mdns_priv_create_answer` suggests it should remain private, but removing the `static` qualifier contradicts this intention. |
|
||||
| [_mdns_alloc_packet_default](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1763) | [mdns_priv_alloc_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L2698) | The function visibility has changed from static to non-static (exported), which could break encapsulation and allow unintended external access to this internal packet allocation function. This may lead to architectural issues where external code could bypass proper MDNS APIs and directly manipulate packet allocation, potentially causing memory management issues or protocol violations. |
|
||||
| [_mdns_create_answer_from_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1789) | [create_answer_from_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L255) | The refactored function uses `mdns_priv_get_self_host()` instead of comparing directly with `&_mdns_self_host`, which could introduce subtle behavioral differences if the function doesn't return the expected reference. Additionally, the MDNS_TYPE_SDPTR case consistently uses `false` for the `send_flush` parameter in both versions, but this might be an existing issue that wasn't addressed during refactoring. |
|
||||
| [_mdns_create_answer_from_hostname](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1824) | [create_answer_from_hostname](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L292) | |
|
||||
| [_mdns_service_match_ptr_question](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1834) | [service_match_ptr_question](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L302) | The refactored function uses renamed helper functions (mdns_utils_service_match and mdns_utils_get_service_instance_name) without verification that they maintain identical behavior to the original _mdns_service_match and _mdns_get_service_instance_name functions. If these helper functions have different error handling, null checking, or return value semantics, it could introduce subtle bugs in service discovery logic. |
|
||||
| [_mdns_create_answer_from_parsed_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L1862) | [mdns_priv_create_answer_from_parsed_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L3276) | The refactored function has changed from static to non-static visibility, which could break encapsulation and allow unintended external access. Additionally, the function `mdns_priv_get_services()` replaces direct access to `mdns_server->services`, but without seeing its implementation, we cannot verify it returns the same service list with proper synchronization. The `get_host_item()` function also replaces `mdns_get_host_item()` - if these have different implementations, it could affect host matching logic and service discovery behavior. |
|
||||
| [_mdns_question_exists](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2009) | [question_exists](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L377) | |
|
||||
| [_mdns_append_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2023) | [append_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L326) | The refactored `append_host_list` function contains a potential linked list traversal bug. The original code in `_mdns_append_host_list` correctly traversed the host list by advancing the pointer after processing each host. However, the refactored version advances `host = host->next` before calling `append_host`, which means: <br/> 1. The first host in the list is skipped entirely <br/> 2. The last iteration will attempt to access `host->next` which could be NULL, leading to potential NULL pointer dereference in `append_host` <br/> 3. This could result in missing host entries in MDNS responses <br/> <br/> The correct pattern should be: <br/> ```c <br/> while (host != NULL) { <br/> if (!append_host(destination, host, flush, bye)) { <br/> return false; <br/> } <br/> host = host->next; <br/> } <br/> ``` |
|
||||
| [_mdns_append_host_list_in_services](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2034) | [mdns_priv_append_host_list_in_services](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L1834) | The refactored function changes the hostname lookup mechanism from `_mdns_server->hostname` to `mdns_priv_get_global_hostname()`. This could introduce subtle bugs if: <br/> 1. The global hostname differs from the server hostname in certain contexts <br/> 2. The `mdns_priv_get_global_hostname()` function has different initialization timing or state dependencies <br/> 3. There are race conditions where the global hostname changes during packet construction <br/> <br/> Additionally, the function dependencies have changed from `mdns_get_host_item` and `_mdns_append_host` to `get_host_item` and `append_host`. Without verifying these functions have identical behavior, there's risk of: <br/> - Different error handling semantics <br/> - Changed memory management patterns <br/> - Modified host lookup logic |
|
||||
| [_mdns_append_host_list](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2053) | [append_host_list](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L356) | The refactored append_host_list function has a critical bug in the while loop logic. The original code correctly processes the current host before advancing to the next one (host = host->next at the end of the loop). The refactored version advances the pointer before processing (host = host->next at the beginning of the loop), which causes it to skip the first host in the list and potentially access invalid memory when the list is empty or has only one element. |
|
||||
| [_mdns_append_host_question](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2071) | [append_host_question](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L391) | The refactored function uses MDNS_UTILS_DEFAULT_DOMAIN instead of MDNS_DEFAULT_DOMAIN, which could be a different constant value. Additionally, it calls question_exists instead of _mdns_question_exists, which might have different implementation behavior. The related function append_host_questions_for_services now uses mdns_priv_get_global_hostname() instead of directly accessing _mdns_server->hostname, which could introduce different behavior if the global hostname retrieval differs from the direct struct access. |
|
||||
| [_mdns_append_host_questions_for_services](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2094) | [append_host_questions_for_services](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L414) | The refactored function introduces a subtle bug in the append_host_question helper function. The original implementation did not check for duplicate questions before adding them to the queue, but the refactored version now includes a question_exists check that frees the allocated memory if a duplicate is found, yet still returns true. This means: <br/> 1. Memory is allocated but potentially leaked if duplicates exist (malloc followed by free without being added to queue) <br/> 2. The function returns true even when the question wasn't actually added to the questions list <br/> 3. This could lead to inconsistent state where callers think questions were added but they weren't <br/> Additionally, the change from direct _mdns_server->hostname access to mdns_priv_get_global_hostname() and from _str_null_or_empty to mdns_utils_str_null_or_empty could introduce behavioral differences if these functions don't have identical implementations. |
|
||||
| [_mdns_create_probe_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2112) | [mdns_priv_create_probe_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L887) | The function `question_exists` (without the `mdns_priv_` prefix) appears to be a different implementation than the original `_mdns_question_exists`. This could introduce subtle bugs if the new function has different behavior for duplicate question detection, potentially causing incorrect packet construction or memory leaks. Additionally, the constant `MDNS_UTILS_DEFAULT_DOMAIN` may have a different value than the original `MDNS_DEFAULT_DOMAIN`, which could affect DNS query formatting and service discovery. |
|
||||
| [_mdns_create_announce_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2166) | [mdns_priv_create_announce_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L1256) | The refactored function uses different underlying helper functions (mdns_priv_alloc_packet, mdns_priv_create_answer, mdns_priv_free_tx_packet, mdns_priv_append_host_list_in_services) compared to the original (_mdns_alloc_packet_default, _mdns_alloc_answer, _mdns_free_tx_packet, _mdns_append_host_list_in_services). While the function signatures appear identical, the inability to find these renamed functions in the search results suggests they may have different implementations or behaviors that could introduce subtle bugs, particularly around memory management, error handling, or protocol compliance. The refactoring assumes functional equivalence of these underlying components without verification. |
|
||||
| [_mdns_create_announce_from_probe](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2196) | [mdns_priv_create_announce_from_probe](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L1417) | The function `mdns_get_host_item` from the original code has been replaced with `get_host_item` without the `mdns_` prefix. This could indicate either an incomplete refactoring where the prefix was accidentally omitted, or a different function with potentially different behavior. If `get_host_item` is not the correct replacement or has different memory management semantics, it could lead to null pointer dereferences or incorrect host item lookups. |
|
||||
| [_mdns_pcb_send_bye](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2236) | [mdns_priv_send_bye](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1505) | The refactored function mdns_priv_pcb_send_bye_service introduces a new global hostname check using mdns_utils_str_null_or_empty(mdns_priv_get_global_hostname()) that wasn't present in the original code. This could prevent goodbye packets from being sent when the global hostname is empty, potentially leaving stale service records in the network. The original implementation would send goodbye packets regardless of hostname state, which may be the intended behavior for proper service cleanup. |
|
||||
| [_mdns_init_pcb_probe_new_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2260) | [init_probe_new_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_pcb.c#L339) | The refactoring introduces a potential stack overflow vulnerability in mdns_priv_init_pcb_probe where a Variable Length Array (VLA) `new_probe_services[len]` is allocated on the stack without bounds checking. If `len` is large, this could cause stack exhaustion. Additionally, the global hostname check in mdns_priv_init_pcb_probe may prematurely set PCB state to RUNNING without probing services, potentially skipping necessary service discovery steps. |
|
||||
| [_mdns_init_pcb_probe_new_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2260) | [mdns_priv_init_pcb_probe](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_pcb.c#L2478) | The refactoring introduces a potential stack overflow vulnerability in mdns_priv_init_pcb_probe where a Variable Length Array (VLA) `new_probe_services[len]` is allocated on the stack without bounds checking. If `len` is large, this could cause stack exhaustion. Additionally, the global hostname check in mdns_priv_init_pcb_probe may prematurely set PCB state to RUNNING without probing services, potentially skipping necessary service discovery steps. |
|
||||
| [_mdns_init_pcb_probe](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2316) | [mdns_priv_init_pcb_probe](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_pcb.c#L2478) | The refactored code introduces a potential stack overflow vulnerability by using a Variable Length Array (VLA) `new_probe_services[len]` without bounds checking. In the original code, the VLA usage was the same, but the refactoring context doesn't show if `len` is properly validated elsewhere. Large `len` values could exhaust the stack, especially in embedded environments. Additionally, the refactored `init_probe_new_service` function has complex memory management logic that could leak memory if `mdns_priv_create_probe_packet` returns NULL, though this appears consistent with the original design. |
|
||||
| [_mdns_restart_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2356) | [restart_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_pcb.c#L127) | The refactored function uses `mdns_priv_get_services()` instead of direct access to `_mdns_server->services`, which changes the data access pattern. While this might be intentional encapsulation, it could introduce subtle bugs if: <br/> 1. `mdns_priv_get_services()` returns a different service list than the original global variable <br/> 2. The function is called multiple times (line 132 and 140) and the service list changes between calls <br/> 3. The service counting logic becomes inconsistent if services are added/removed during execution <br/> <br/> Additionally, the function name changed from `_mdns_restart_pcb` to `restart_pcb`, which may affect visibility and linkage if this was previously a static function. |
|
||||
| [_mdns_send_bye](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2382) | [mdns_priv_pcb_send_bye_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_pcb.c#L429) | The refactored function uses a different PCB state access pattern: original code accessed `_mdns_server->interfaces[i].pcbs[j].state` while refactored code uses `s_pcbs[i][j].state`. This structural change could introduce subtle bugs if the PCB state management or initialization logic differs between the two implementations, potentially leading to incorrect state checks or accessing uninitialized memory. |
|
||||
| [_mdns_send_bye_subtype](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2401) | [mdns_priv_send_bye_subtype](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L1681) | The refactored function changed from static to non-static visibility, which could break encapsulation and allow unintended external access. Additionally, the static buffer 'pkt' remains in the refactored code, creating potential race conditions in multi-threaded environments since multiple instances could overwrite the same buffer. |
|
||||
| [_mdns_announce_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2451) | [mdns_priv_pcb_announce](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_pcb.c#L861) | The refactored function accesses PCB state through a different global structure (s_pcbs[][] instead of _mdns_server->interfaces[].pcbs[]), which could lead to state inconsistencies if the initialization or synchronization of these structures differs. Additionally, the hostname check now uses mdns_priv_get_global_hostname() instead of direct access to _mdns_server->hostname, which may return different values if the global hostname management was modified during refactoring. |
|
||||
| [_mdns_probe_all_pcbs](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2494) | [mdns_priv_probe_all_pcbs](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_pcb.c#L2342) | The refactored function accesses PCB data through `s_pcbs[i][j]` instead of `_mdns_server->interfaces[i].pcbs[j]`, which represents a significant architectural change in data structure organization. This could introduce subtle bugs if: <br/> <br/> 1. The `s_pcbs` array is not properly initialized or synchronized with the original server state <br/> 2. The memory layout or indexing differs between the two data structures <br/> 3. There are race conditions in accessing the global `s_pcbs` vs the server-managed PCB structures <br/> <br/> Additionally, the helper function `mdns_priv_init_pcb_probe` (shown in Result 3) has been substantially refactored with new logic for duplicate service detection and different probing state handling, which may change the probing behavior in edge cases where services are already being probed. |
|
||||
| [_mdns_announce_all_pcbs](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2516) | [announce_all_pcbs](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L150) | The refactored function `announce_all_pcbs` calls `mdns_priv_pcb_announce` which introduces state-dependent behavior that wasn't present in the original implementation. In the PCB_RUNNING state, there's a global hostname check that can cause the function to return early without any announcement if the hostname is null or empty. This could lead to silent failures where services aren't announced when expected, particularly during system initialization or hostname changes. The original implementation would always attempt to announce regardless of PCB state or hostname status. |
|
||||
| [_mdns_send_final_bye](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2529) | [send_final_bye](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L163) | The refactored function send_final_bye calls mdns_priv_pcb_send_bye_service which adds a hostname validation check that wasn't present in the original function. This could prevent bye packets from being sent when the global hostname is empty/null, potentially leaving stale service records in the network. Additionally, the new implementation iterates through multiple interfaces/protocols and checks PCB state, which may change the multicast behavior compared to the original single-call approach. |
|
||||
| [_mdns_send_bye_all_pcbs_no_instance](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2554) | [send_bye_all_pcbs_no_instance](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L188) | The refactored function `send_bye_all_pcbs_no_instance` now calls `mdns_priv_pcb_send_bye_service` which includes an additional global hostname check that wasn't present in the original implementation. This could potentially prevent bye packets from being sent when the global hostname is empty, even though services without instances should still be able to send bye packets independently of the hostname state. This represents a behavioral change that might affect service shutdown behavior. |
|
||||
| [_mdns_restart_all_pcbs_no_instance](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2582) | [mdns_priv_restart_all_pcbs_no_instance](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L213) | |
|
||||
| [_mdns_restart_all_pcbs](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2610) | [mdns_priv_restart_all_pcbs](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L238) | The refactored function changed from static to non-static visibility, which could lead to unintended external access and linkage issues. Additionally, the global variable changed from _mdns_server to s_server, and function calls changed from _mdns_clear_tx_queue_head() to mdns_priv_clear_tx_queue() and _mdns_probe_all_pcbs() to mdns_priv_probe_all_pcbs(). Without verification that these are functionally equivalent, there's risk of behavioral changes or memory management issues. |
|
||||
| [_mdns_allocate_txt](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2643) | [allocate_txt](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L269) | The refactored function `allocate_txt` has a subtle memory leak bug. In the original function, when `mdns_mem_strdup(txt[i].value)` fails, it properly cleans up by freeing both `new_item->key` and `new_item` before breaking. However, in the refactored version, when `mdns_mem_strdup(txt[i].value)` fails, it only frees `(char *)new_item->key` and `new_item`, but any previously allocated items in the linked list (`new_txt`) remain allocated and are not cleaned up. This creates a memory leak where partial allocations are not properly rolled back when allocation failures occur mid-loop. |
|
||||
| [_mdns_free_linked_txt](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2682) | [free_linked_txt](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L308) | The refactored function free_linked_txt casts t->key and t->value to (char *) before freeing, which assumes these fields were allocated as char arrays. While this matches the original behavior, this pattern relies on implementation details of the memory allocator and could be problematic if the allocation types change. The casting suggests potential type safety issues in the memory management design. |
|
||||
| [_mdns_create_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2706) | [create_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L332) | The refactored code introduces a potential resource leak in the error handling path of mdns_service_add_for_host. When create_service succeeds but the subsequent mdns_mem_malloc for the mdns_srv_item_t fails, the function calls free_service(s) but does not remove the service from the global s_server->services list. Additionally, the locking mechanism (mdns_priv_service_lock/unlock) wraps the entire function but the original _mdns_create_service was likely called within an already locked context, potentially creating deadlock risks or inconsistent locking behavior. |
|
||||
| [_mdns_dealloc_scheduled_service_answers](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2762) | [dealloc_scheduled_service_answers](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1742) | The refactored function 'dealloc_scheduled_service_answers' has identical logic to the original but there's a potential issue with the second while loop. In the original function, when d->next is NULL but d itself matches the service, it won't be removed in the second loop. The refactored version preserves this same behavior, which might be intentional but could lead to leftover answers if the first element after the initial removal matches the service but has no next pointer. This appears to be a pre-existing issue carried over from the original implementation. |
|
||||
| [_mdns_remove_scheduled_service_packets](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2787) | [mdns_priv_remove_scheduled_service_packets](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mock_mdns_send.c#L2171) | The refactored function passes the condition `had_answers && q->answers == NULL` to `mdns_priv_pcb_check_probing_services` for the announcing state check, but this condition is evaluated before the deallocation calls. In the original code, this condition was checked after deallocation, which could lead to different behavior if the deallocation functions modify the answers list during execution. This timing difference might cause incorrect PCB state transitions during service removal. |
|
||||
| [_mdns_free_subtype](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2868) | [free_subtype](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L385) | The refactored function `free_subtype` appears to be a direct replacement for `_mdns_free_subtype` with identical logic, but there's a potential memory safety issue: the function casts `subtype->subtype` to `(char *)` before freeing, which suggests the original data might have been declared as `const char *`. This cast could mask const-correctness violations in the codebase. If the subtype string was originally allocated as read-only memory or through string literals, this could lead to undefined behavior when attempting to free it. |
|
||||
| [_mdns_free_service_subtype](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2878) | [free_service_subtype](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L395) | |
|
||||
| [_mdns_free_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2889) | [free_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L406) | The refactored function `free_service` calls `free_service_subtype(service)` which sets `service->subtype = NULL`, but this happens after the service structure has already been freed with `mdns_mem_free(service)`. This creates a use-after-free bug where we're writing to memory that has already been deallocated. In the original code, `_mdns_free_service_subtype` was called before freeing the service structure, which was correct. |
|
||||
| [_mdns_check_srv_collision](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2917) | [check_srv_collision](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L438) | The refactored function hardcodes domain length as 5 bytes when copying MDNS_UTILS_DEFAULT_DOMAIN, but there's no verification that MDNS_UTILS_DEFAULT_DOMAIN is actually exactly 5 bytes. If the domain constant differs in length, this could cause buffer overruns or incorrect collision detection. Additionally, the function uses Variable Length Arrays (VLAs) which could cause stack overflow with very long hostnames, and the uint16_t index variables may overflow if the total data length exceeds 65535 bytes. |
|
||||
| [_mdns_check_txt_collision](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L2974) | [check_txt_collision](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L393) | The refactored function uses mdns_priv_append_one_txt_record_entry instead of append_one_txt_record_entry. While both functions appear to have identical signatures and similar logic, the refactored version is no longer inline and has been moved to a separate compilation unit (mdns_send.c). This could potentially impact performance in the collision detection hot path, and there's a risk that the implementation might differ slightly in edge cases (like handling of empty values or special characters) which could affect the byte-for-byte comparison critical for proper mDNS collision resolution. |
|
||||
| [mdns_pcb_deinit_local](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3016) | [deinit_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_pcb.c#L107) | The NULL check on `pcb` in the refactored function is problematic. In the original code, `_pcb` was obtained via `&_mdns_server->interfaces[tcpip_if].pcbs[ip_proto]`, which would always return a valid address (not NULL) if the array structure exists. In the refactored code, `pcb = &s_pcbs[tcpip_if][ip_proto]` should also always return a valid address. This NULL check could mask actual initialization issues with the `s_pcbs` array and might prevent proper error propagation from `mdns_priv_if_deinit`. The check should either be removed or replaced with a proper array bounds validation. |
|
||||
| [_mdns_dup_interface](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3035) | [mdns_priv_pcb_set_duplicate](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_pcb.c#L186) | The refactored function uses a different data structure (s_pcbs[tcpip_if][i] vs _mdns_server->interfaces[tcpip_if].pcbs[i]) and the state transition to PCB_DUP is performed unconditionally without verifying if the PCB was previously in a valid state. This could lead to inconsistent state management if the PCB was already in PCB_OFF or another terminal state. Additionally, the function is no longer static, which changes its visibility and could affect encapsulation. |
|
||||
| [_mdns_check_a_collision](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3059) | [check_a_collision](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L322) | The refactored function uses different helper functions (mdns_priv_get_esp_netif, mdns_priv_netif_get_other_interface, mdns_priv_pcb_set_duplicate) compared to the original (_mdns_get_esp_netif, _mdns_get_other_if, _mdns_dup_interface). While the function logic appears identical, there's a risk that these renamed helper functions might have different implementations or error handling behaviors that could affect the collision detection logic. Specifically, if mdns_priv_netif_get_other_interface returns different values than _mdns_get_other_if for edge cases, or if mdns_priv_pcb_set_duplicate has different side effects than _mdns_dup_interface, this could introduce subtle behavioral changes in the collision resolution protocol. |
|
||||
| [_mdns_check_aaaa_collision](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3095) | [check_aaaa_collision](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L358) | The refactored function uses renamed helper functions and constants whose implementations cannot be verified from the provided context. Specifically: <br/> 1. `mdns_priv_netif_get_other_interface` replaces `_mdns_get_other_if` - if this function has different behavior in edge cases (like when interfaces are disabled), it could affect collision detection logic <br/> 2. `mdns_priv_pcb_set_duplicate` replaces `_mdns_dup_interface` - if this function performs additional operations or misses some operations from the original, it could impact interface state management <br/> 3. `MDNS_UTILS_SIZEOF_IP6_ADDR` must exactly match the original `_MDNS_SIZEOF_IP6_ADDR` (typically 16 bytes for IPv6 addresses), otherwise memcmp comparisons will be incorrect <br/> <br/> The functional logic appears identical, but without verifying the implementations of these renamed components, there's risk of subtle behavioral changes. |
|
||||
| [_hostname_is_ours](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3127) | [mdns_utils_hostname_is_ours](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L105) | The refactored function mdns_utils_hostname_is_ours accesses global data through mdns_priv_get_global_hostname() and mdns_priv_get_hosts() without any locking mechanism, while the original function was called within MDNS_SERVICE_LOCK/UNLOCK sections. This could lead to race conditions if the global hostname or host list is modified concurrently. The wrapper function mdns_hostname_exists does provide locking, but direct calls to mdns_utils_hostname_is_ours without locking could access inconsistent state. |
|
||||
| [_mdns_delegate_hostname_add](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3150) | [mdns_priv_delegate_hostname_add](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L426) | The refactored function uses s_server->host_list instead of the global _mdns_host_list, but there's no null check for s_server. If s_server is NULL, this will cause a segmentation fault when accessing s_server->host_list. The original function directly used the global variable _mdns_host_list which would always be accessible (though potentially NULL for an empty list). This introduces a new failure mode that wasn't present in the original implementation. |
|
||||
| [free_address_list](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3168) | [mdns_utils_free_address_list](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L239) | |
|
||||
| [_mdns_delegate_hostname_set_address](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3178) | [delegate_hostname_set_address](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L444) | The refactored function delegate_hostname_set_address uses mdns_utils_free_address_list instead of the original free_address_list, but the memory ownership semantics may have changed. In the original code, when ACTION_DELEGATE_HOSTNAME_SET_ADDR action fails, the caller explicitly frees the address_list, suggesting the function takes ownership. The refactored version appears to directly assign the address_list to the host item without clear documentation of ownership transfer, which could lead to double-free or memory leaks if the memory management contract has changed. |
|
||||
| [copy_address_list](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3198) | [mdns_utils_copy_address_list](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L214) | The refactored function introduces a HOOK_MALLOC_FAILED macro call before freeing the partially allocated list. If this hook has side effects (like logging, delays, or state changes), it could potentially affect error handling behavior or timing in memory-constrained scenarios. Additionally, the function now depends on mdns_utils_free_address_list being available and functionally equivalent to the original free_address_list. |
|
||||
| [free_delegated_hostnames](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3222) | [free_delegated_hostnames](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L49) | The refactored function now accesses s_server->host_list instead of the global _mdns_host_list, but there is no NULL check for s_server. If free_delegated_hostnames is called when s_server is NULL (e.g., during shutdown or error conditions), this will cause a segmentation fault. Additionally, the function mdns_priv_responder_free calls free_delegated_hostnames after checking if s_server is NULL, suggesting that s_server could be NULL in some scenarios. |
|
||||
| [_mdns_delegate_hostname_remove](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3235) | [delegate_hostname_remove](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L464) | The refactored function uses different global variables and function names without clear verification that they maintain identical behavior. Specifically: s_server vs _mdns_server, mdns_priv_pcb_send_bye_service vs _mdns_send_bye, mdns_priv_remove_scheduled_service_packets vs _mdns_remove_scheduled_service_packets, and free_service vs _mdns_free_service. The host list management also changed from _mdns_host_list to s_server->host_list. These changes could introduce subtle bugs if the underlying implementations differ, especially around memory management, protocol behavior, or state consistency. |
|
||||
| [_mdns_name_is_discovery](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3282) | [is_discovery](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L28) | |
|
||||
| [_mdns_name_is_selfhosted](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3296) | [is_name_selfhosted](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L495) | The refactored function `is_name_selfhosted` introduces a potential null pointer dereference. The original function accessed `_mdns_server->hostname` directly, while the refactored version calls `mdns_priv_get_global_hostname()` without checking if the returned pointer is valid before passing it to `mdns_utils_str_null_or_empty()`. If `mdns_priv_get_global_hostname()` can return NULL, this could lead to undefined behavior. |
|
||||
| [_mdns_name_is_ours](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3319) | [is_ours](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L60) | The refactored function uses mdns_utils_hostname_is_ours which now checks against multiple hosts from mdns_priv_get_hosts() in addition to the global hostname. This expands the definition of "ours" to include additional hosts, which may break assumptions in the original code that only the global hostname was considered. Additionally, the change from _mdns_server->hostname to mdns_priv_get_global_hostname() could have different semantics if the underlying data structure or initialization timing has changed. |
|
||||
| [_mdns_read_u16](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3385) | [mdns_utils_read_u16](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.h#L18) | |
|
||||
| [_mdns_read_u32](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3397) | [mdns_utils_read_u32](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.h#L30) | |
|
||||
| [_mdns_parse_fqdn](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3411) | [mdns_utils_parse_fqdn](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_utils.c#L71) | The refactoring changed the function scope from static to external (non-static), which could lead to unintended access from other compilation units and potential namespace pollution. Additionally, the static buffer usage in both mdns_utils_parse_fqdn and mdns_utils_read_fqdn creates reentrancy issues in multi-threaded environments or during recursive calls. |
|
||||
| [_mdns_question_matches](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3448) | [question_matches](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L518) | The refactored function uses MDNS_UTILS_DEFAULT_DOMAIN instead of MDNS_DEFAULT_DOMAIN, which could introduce subtle bugs if these constants have different values or if MDNS_UTILS_DEFAULT_DOMAIN is not properly defined in the refactored codebase. Additionally, the function _mdns_get_service_instance_name was renamed to mdns_utils_get_service_instance_name, and any behavioral differences between these functions could affect the matching logic for SRV and TXT record types. |
|
||||
| [_mdns_remove_parsed_question](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3483) | [remove_parsed_question](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L553) | |
|
||||
| [_mdns_txt_items_count_get](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3515) | [get_txt_items_count](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L181) | |
|
||||
| [_mdns_txt_item_name_get_len](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3542) | [get_txt_item_len](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L208) | The refactored code in result_txt_create removed the bounds check "txt_num >= num_items" that was present in the original code. In the original implementation, the condition was "if (name_len < 0 || txt_num >= num_items)" which provided protection against buffer overruns when the actual number of parsed TXT items exceeds the pre-allocated array size. The refactored version only checks "if (name_len < 0)" which could lead to writing beyond the allocated txt array bounds if get_txt_items_count returns an incorrect value or if the TXT data contains more valid items than expected. |
|
||||
| [_mdns_result_txt_create](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3558) | [result_txt_create](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L224) | The refactored function removed the `txt_num >= num_items` check from the invalid item condition, relying solely on the while loop condition `txt_num < num_items`. This creates a potential buffer overrun vulnerability if there are many invalid TXT items (where name_len < 0) followed by valid ones. The original code had double protection by checking both conditions, but the refactored version only checks the array bounds in the loop condition, which could be bypassed if invalid items cause the loop to continue without incrementing txt_num properly. |
|
||||
| [_mdns_strdup_check](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3654) | [strdup_check](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L122) | |
|
||||
| [mdns_parse_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L3672) | [mdns_parse_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_receive.c#L587) | The refactored function has been changed from public to static scope, which could break external callers. Additionally, there are potential subtle bugs in the collision detection logic where the refactored version uses simplified PCB state checking via `mdns_priv_pcb_is_probing(packet)` instead of the original direct structure access `_mdns_server->interfaces[packet->tcpip_if].pcbs[packet->ip_protocol].probe_running`. This abstraction might not handle all edge cases identically, particularly during concurrent probe operations or interface state transitions. |
|
||||
| [_mdns_enable_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4362) | [mdns_priv_pcb_enable](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_pcb.c#L172) | |
|
||||
| [_mdns_disable_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4376) | [mdns_priv_pcb_disable](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_pcb.c#L153) | The refactored function changes the data structure access pattern from `_mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].state` to `s_pcbs[tcpip_if][ip_protocol].state`, which represents a significant architectural change. This could introduce subtle bugs related to: <br/> 1. Memory layout differences affecting state synchronization <br/> 2. Race conditions if the new s_pcbs structure has different locking mechanisms <br/> 3. Potential null pointer dereferences if s_pcbs array initialization differs from the original _mdns_server structure <br/> 4. Consistency issues with the PCB state transitions across the codebase due to the structural change <br/> <br/> Additionally, the function renaming (`mdns_pcb_deinit_local` → `deinit_pcb`, `_mdns_get_other_if` → `mdns_priv_netif_get_other_interface`) suggests broader architectural changes that could affect error handling paths or edge case behavior. |
|
||||
| [nibble_to_hex](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4393) | [nibble_to_hex](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L46) | |
|
||||
| [perform_event_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4402) | [perform_event_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L55) | The refactored function maintains the same logic but introduces potential memory leaks in the reverse DNS lookup code. In the IPv4 reverse lookup, asprintf allocates memory for reverse_query_name but there's no free() call if mdns_priv_delegate_hostname_add succeeds. Similarly, in IPv6 reverse lookup, mdns_mem_malloc allocates memory but there's no corresponding free. The original code had the same issue, but this refactoring missed the opportunity to fix it. |
|
||||
| [post_mdns_disable_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4473) | [post_disable_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L183) | |
|
||||
| [post_mdns_enable_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4478) | [post_enable_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L189) | |
|
||||
| [post_mdns_announce_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4483) | [post_announce_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L195) | |
|
||||
| [mdns_preset_if_handle_system_event](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4489) | [handle_system_event_for_preset](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L202) | The refactored function replaces manual browse iteration with mdns_priv_browse_send_all(), but without seeing the implementation of this function, we cannot verify if it maintains the same iteration behavior as the original _mdns_browse_send() calls. Additionally, the function name changes (esp_netif_from_preset_if to netif_from_preset, etc.) suggest potential semantic differences that need verification. |
|
||||
| [_mdns_search_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4582) | [search_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L343) | The refactored search_free function does not check if the search parameter is NULL before dereferencing its fields. While the original function also lacked this check, the refactoring moved the function to a different context (mdns_querier.c) where the calling patterns might have changed. In C programming, defensive programming practices suggest checking for NULL pointers before dereferencing, especially for cleanup functions that might be called from multiple contexts with potentially invalid parameters. |
|
||||
| [_mdns_search_init](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4594) | [search_init](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L355) | |
|
||||
| [_mdns_search_finish](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4652) | [search_finish](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L59) | The refactored function changes the global variable from `_mdns_server->search_once` to `s_search_once`, but there's no evidence that the ACTION_SEARCH_END case in the action handler was updated to call the new function. This could leave dangling searches that are never properly finished when triggered through the action system, potentially causing resource leaks or synchronization issues with the semaphore. |
|
||||
| [_mdns_search_add](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4665) | [search_add](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L72) | |
|
||||
| [_mdns_search_finish_done](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4674) | [mdns_priv_query_done](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L181) | The refactored function uses a global variable `s_search_once` instead of accessing it through `_mdns_server->search_once`, which changes the data structure access pattern. Additionally, the new `search_finish` function performs different cleanup operations (queue detachment and semaphore signaling) compared to the original `_mdns_search_finish` function, whose implementation is not shown for verification. This could lead to inconsistent search state management or resource cleanup issues. |
|
||||
| [_mdns_result_addr_create_ip](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4690) | [mdns_priv_result_addr_create_ip](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L653) | The function visibility changed from static to non-static, which could lead to unintended usage across the codebase. Additionally, the refactored function is now called from new contexts (result_add_ip and mdns_priv_query_result_add_ip) that weren't in the original codebase, potentially introducing different error handling requirements. While the core implementation remains identical, the architectural changes in how IP addresses are managed could affect resource cleanup patterns in case of failures. |
|
||||
| [_mdns_result_update_ttl](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4707) | [mdns_priv_query_update_result_ttl](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.h#L99) | |
|
||||
| [_mdns_result_add_ip](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4715) | [result_add_ip](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L485) | The refactoring introduces a subtle bug in the memory management logic. In the original code, when _mdns_result_addr_create_ip fails, the function simply returns without affecting the result structure. However, in mdns_priv_query_result_add_ip, when mdns_priv_result_addr_create_ip fails during new result creation, the partially allocated mdns_result_t structure is freed, but the search->result pointer is never updated, leaving the search result list in an inconsistent state. Additionally, the hostname string duplication occurs after IP address creation, which could lead to memory leaks if the IP creation fails but the hostname was already allocated. |
|
||||
| [_mdns_result_add_ip](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4715) | [mdns_priv_query_result_add_ip](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L514) | The refactoring introduces a subtle bug in the memory management logic. In the original code, when _mdns_result_addr_create_ip fails, the function simply returns without affecting the result structure. However, in mdns_priv_query_result_add_ip, when mdns_priv_result_addr_create_ip fails during new result creation, the partially allocated mdns_result_t structure is freed, but the search->result pointer is never updated, leaving the search result list in an inconsistent state. Additionally, the hostname string duplication occurs after IP address creation, which could lead to memory leaks if the IP creation fails but the hostname was already allocated. |
|
||||
| [_mdns_search_result_add_ip](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4744) | [mdns_priv_query_result_add_ip](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L514) | The function 'result_add_ip' is called without the 'mdns_priv_' prefix, which may indicate either a missing namespace prefix or an undefined function. This could lead to linking errors or incorrect behavior if the intended function is not available. All other helper functions were properly prefixed with 'mdns_priv_' or 'mdns_utils_', making this inconsistency potentially problematic. |
|
||||
| [_mdns_search_result_add_ptr](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4802) | [mdns_priv_query_result_add_ptr](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L614) | The refactored function mdns_priv_query_result_add_ptr appears to have a critical bug: callers in the refactored code (mdns_receive.c lines 851-852, 922-923, 1038-1040) do not check the return value for NULL, unlike the original code which explicitly checked for NULL returns (lines 4020-4023, 4132-4135). This could lead to NULL pointer dereferences if the function fails to allocate memory or encounters other errors. The function signature still returns mdns_result_t*, suggesting it should be checked, but the calling code ignores the return value. |
|
||||
| [_mdns_search_result_add_srv](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4844) | [mdns_priv_query_result_add_srv](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L572) | |
|
||||
| [_mdns_search_result_add_txt](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4886) | [mdns_priv_query_result_add_txt](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L435) | The refactoring splits the original unified TXT handling function into two specialized functions (query vs browse) with different signatures. The original parser call site at line 4149 now needs to determine which function to call based on context, but the additional parameters required by mdns_priv_browse_result_add_txt (instance, service, proto) may not be available in all calling contexts where the original function was used. This could lead to either incorrect function selection or missing data when calling the browse version, potentially causing incomplete TXT record processing or memory access issues. |
|
||||
| [_mdns_search_find_from](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L4936) | [mdns_priv_query_find_from](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L197) | The function has been changed from static to non-static linkage, which could expose internal implementation details and create unintended dependencies. Additionally, the utility function replacements (_mdns_get_esp_netif → mdns_priv_get_esp_netif and _str_null_or_empty → mdns_utils_str_null_or_empty) need verification that they maintain identical behavior in all edge cases, particularly for NULL inputs and network interface mapping. |
|
||||
| [_mdns_create_search_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5003) | [create_search_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L269) | The refactored function uses `mdns_priv_alloc_packet` instead of `_mdns_alloc_packet_default`. If `mdns_priv_alloc_packet` doesn't provide the same default packet initialization (like setting default flags, TTL values, or other packet metadata), it could result in malformed mDNS search packets that don't conform to protocol specifications. Additionally, the domain constant change from `MDNS_DEFAULT_DOMAIN` to `MDNS_UTILS_DEFAULT_DOMAIN` could affect DNS query formation if the values differ, potentially causing search queries to use the wrong domain. |
|
||||
| [_mdns_search_send_pcb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5060) | [mdns_priv_query_send](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L327) | The refactored function uses `mdsn_priv_pcb_is_inited(tcpip_if, ip_protocol)` instead of the original complex condition `mdns_is_netif_ready(tcpip_if, ip_protocol) && _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].state > PCB_INIT`. This could introduce subtle bugs if the new function doesn't properly replicate both the network interface readiness check AND the PCB state validation. Additionally, there appears to be a potential typo in the function name "mdsn_priv_pcb_is_inited" (missing 'n' in 'mdns'). |
|
||||
| [_mdns_search_send](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5076) | [search_send](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L81) | The refactored function `mdns_priv_query_send` (which replaces `_mdns_search_send_pcb`) immediately frees the packet after dispatch with `mdns_priv_free_tx_packet(packet)`, while the original implementation's packet lifecycle management is unclear. This could cause issues if packet transmission is asynchronous and the packet needs to remain valid until transmission completes. Additionally, the refactored version introduces early return on packet creation failure, which changes the error handling behavior compared to the original loop structure. |
|
||||
| [_mdns_tx_handle_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5102) | [handle_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1816) | The refactoring splits the original function's logic across multiple functions, but introduces a potential bug in the PCB_PROBE_3 case. In the original code, when creating an announce packet fails, it breaks from the switch statement after scheduling a retry. However, in the refactored version, the variable 'p' is reassigned to 'a' (the new announce packet) in the success case, but this reassignment only affects the local copy in mdns_priv_pcb_schedule_tx_packet. The calling function handle_packet still holds the original 'p' pointer, which could lead to double-free or use-after-free issues if the packet lifecycle isn't properly managed across the function boundaries. |
|
||||
| [_mdns_tx_handle_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5102) | [mdns_priv_pcb_schedule_tx_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_pcb.c#L211) | The refactoring splits the original function's logic across multiple functions, but introduces a potential bug in the PCB_PROBE_3 case. In the original code, when creating an announce packet fails, it breaks from the switch statement after scheduling a retry. However, in the refactored version, the variable 'p' is reassigned to 'a' (the new announce packet) in the success case, but this reassignment only affects the local copy in mdns_priv_pcb_schedule_tx_packet. The calling function handle_packet still holds the original 'p' pointer, which could lead to double-free or use-after-free issues if the packet lifecycle isn't properly managed across the function boundaries. |
|
||||
| [_mdns_remap_self_service_hostname](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5159) | [mdns_priv_remap_self_service_hostname](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L508) | The function scope change from static to non-static could potentially cause linking conflicts if this function is now accessible from other compilation units. Additionally, the global variable change from _mdns_server to s_server requires verification that s_server is properly initialized and equivalent to the original _mdns_server in all execution contexts. |
|
||||
| [_mdns_sync_browse_result_link_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5173) | [sync_browse_result_link_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L236) | The function name changed from `_mdns_sync_browse_result_link_free` to `sync_browse_result_link_free` (removing the leading underscore and "mdns_" prefix). While the core logic remains identical, this naming change could introduce subtle bugs if: <br/> 1. Call sites in the refactored codebase haven't been updated to use the new function name <br/> 2. The function visibility/scope changed due to the naming convention alteration <br/> 3. Other parts of the codebase rely on the specific naming pattern for internal functions <br/> <br/> The original function was called in ACTION_BROWSE_SYNC cases in mdns.c, and if those call sites weren't properly updated to reference the renamed function, it would result in compilation errors or runtime failures due to undefined function references. |
|
||||
| [_mdns_free_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5188) | [free_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L126) | The refactored free_action function delegates cleanup to external functions (mdns_priv_*_action with ACTION_CLEANUP mode) without visible implementation. This introduces a risk that the specific memory cleanup operations performed in the original function (mdns_mem_free for hostnames, _mdns_search_free for searches, free_address_list for address lists) may not be properly handled, potentially leading to memory leaks. The architectural change from direct cleanup to delegation requires verification that all cleanup functions properly handle their respective resource types. |
|
||||
| [_mdns_execute_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5235) | [execute_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_engine.c#L15) | The refactoring introduces several subtle bugs: <br/> <br/> 1. **Missing ACTION_SYSTEM_EVENT handling**: In Result 2 (mdns_engine.c), the ACTION_SYSTEM_EVENT case is completely empty, while the original function called perform_event_action(). This will break system event processing. <br/> <br/> 2. **Missing semaphore operations**: The original ACTION_HOSTNAME_SET case called xSemaphoreGive(_mdns_server->action_sema) after completion, but the refactored version delegates to mdns_priv_responder_action without preserving this critical synchronization. <br/> <br/> 3. **Missing memory cleanup in ACTION_BROWSE_SYNC**: The original function explicitly called _mdns_sync_browse_result_link_free() for ACTION_BROWSE_SYNC, but the refactored version delegates this to mdns_priv_browse_action without clear evidence this cleanup is preserved. <br/> <br/> 4. **Split responsibility confusion**: The introduction of both execute_action and free_action functions suggests the original single responsibility has been split, but it's unclear when each should be called. This could lead to double-free or memory leaks if not properly coordinated. <br/> <br/> 5. **Inconsistent implementations**: Having two different execute_action functions (Result 1 vs Result 2) with different behavior for ACTION_SYSTEM_EVENT suggests either incomplete refactoring or platform-specific variations that need verification. |
|
||||
| [_mdns_execute_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5235) | [free_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L126) | The refactoring introduces several subtle bugs: <br/> <br/> 1. **Missing ACTION_SYSTEM_EVENT handling**: In Result 2 (mdns_engine.c), the ACTION_SYSTEM_EVENT case is completely empty, while the original function called perform_event_action(). This will break system event processing. <br/> <br/> 2. **Missing semaphore operations**: The original ACTION_HOSTNAME_SET case called xSemaphoreGive(_mdns_server->action_sema) after completion, but the refactored version delegates to mdns_priv_responder_action without preserving this critical synchronization. <br/> <br/> 3. **Missing memory cleanup in ACTION_BROWSE_SYNC**: The original function explicitly called _mdns_sync_browse_result_link_free() for ACTION_BROWSE_SYNC, but the refactored version delegates this to mdns_priv_browse_action without clear evidence this cleanup is preserved. <br/> <br/> 4. **Split responsibility confusion**: The introduction of both execute_action and free_action functions suggests the original single responsibility has been split, but it's unclear when each should be called. This could lead to double-free or memory leaks if not properly coordinated. <br/> <br/> 5. **Inconsistent implementations**: Having two different execute_action functions (Result 1 vs Result 2) with different behavior for ACTION_SYSTEM_EVENT suggests either incomplete refactoring or platform-specific variations that need verification. |
|
||||
| [_mdns_send_search_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5322) | [send_search_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L413) | The refactored function replaces direct queue access `xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0)` with a wrapper function `mdns_priv_queue_action(action)`. Without seeing the implementation of `mdns_priv_queue_action`, we cannot verify if it maintains the same non-blocking behavior (0 timeout) and return value semantics. If `mdns_priv_queue_action` uses blocking behavior or has different error handling, it could introduce timing issues or resource management problems in the MDNS search functionality. |
|
||||
| [_mdns_scheduler_run](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5348) | [mdns_priv_send_packets](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_send.c#L1703) | The refactored function uses `mdns_priv_queue_action(action)` instead of directly calling `xQueueSend`. While this appears to be a wrapper function, there's a potential subtle bug: if `mdns_priv_queue_action` has different timeout behavior than the original `(TickType_t)0` (non-blocking), it could introduce blocking behavior that wasn't present in the original implementation. Additionally, the error handling logic assumes `mdns_priv_queue_action` returns false on failure (matching the original `xQueueSend != pdPASS`), but this needs verification to ensure the same cleanup behavior is maintained. |
|
||||
| [_mdns_search_run](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5385) | [mdns_priv_query_start_stop](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L133) | The refactored function mdns_priv_query_start_stop introduces a subtle bug in error handling. In the original code, when _mdns_send_search_action(ACTION_SEARCH_END, s) fails, the search state is restored to SEARCH_RUNNING, allowing retry in the next iteration. However, in the refactored version, when send_search_action(ACTION_SEARCH_END, s) fails, the state is also restored to SEARCH_RUNNING, but the timeout condition (now > (s->started_at + s->timeout)) will remain true in subsequent iterations, causing the search to immediately try to end again in an infinite loop until the send succeeds. This could lead to excessive retries and potential resource exhaustion. |
|
||||
| [_mdns_service_task](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5417) | [service_task](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L200) | The refactored function replaces the direct pointer check `_mdns_server && _mdns_server->action_queue` with a function call `mdns_priv_is_server_init()` and a global variable `s_action_queue`. This introduces potential issues: <br/> 1. The function call `mdns_priv_is_server_init()` may have different timing/side effects compared to the simple pointer check <br/> 2. The global `s_action_queue` is not protected by the same locking mechanism as the original server structure <br/> 3. There's a potential race condition if `s_action_queue` is modified while the service task is running <br/> 4. The separation of server initialization check from queue access could lead to inconsistent state detection |
|
||||
| [_mdns_timer_cb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5439) | [timer_cb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L222) | The refactoring changes the core functionality of the timer callback from scheduler/search operations to packet sending/query operations. While the structural mapping is clear (timer_cb replaces _mdns_timer_cb and start_timer replaces _mdns_start_timer), the functional equivalence cannot be verified without understanding what mdns_priv_send_packets and mdns_priv_query_start_stop do compared to the original _mdns_scheduler_run and _mdns_search_run. This could introduce subtle bugs in MDNS protocol timing, packet scheduling, or service discovery behavior. |
|
||||
| [_mdns_timer_cb](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5439) | [start_timer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L228) | The refactoring changes the core functionality of the timer callback from scheduler/search operations to packet sending/query operations. While the structural mapping is clear (timer_cb replaces _mdns_timer_cb and start_timer replaces _mdns_start_timer), the functional equivalence cannot be verified without understanding what mdns_priv_send_packets and mdns_priv_query_start_stop do compared to the original _mdns_scheduler_run and _mdns_search_run. This could introduce subtle bugs in MDNS protocol timing, packet scheduling, or service discovery behavior. |
|
||||
| [_mdns_start_timer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5445) | [start_timer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L228) | The refactored start_timer function uses a global variable s_timer_handle instead of the original _mdns_server->timer_handle, but there's no evidence that s_timer_handle is properly initialized to NULL. This could lead to double-creation of timers if the function is called multiple times, as the original code would have stored the handle in a server structure that gets reinitialized. Additionally, the callback function was renamed from _mdns_timer_cb to timer_cb without verification that the implementation remains equivalent. |
|
||||
| [_mdns_stop_timer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5460) | [stop_timer](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L243) | |
|
||||
| [_mdns_task_create_with_caps](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5473) | [create_task_with_caps](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L256) | The refactored function introduces a new error handling path with 'alloc_failed' label that calls HOOK_MALLOC_FAILED before proceeding to the 'err' label. This changes the control flow and could potentially cause issues if HOOK_MALLOC_FAILED has side effects or if the order of cleanup operations matters. In the original function, memory allocation failure went directly to the cleanup code, but now there's an intermediate step that might interfere with proper resource management. |
|
||||
| [_mdns_service_task_start](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5498) | [service_task_start](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L283) | |
|
||||
| [_mdns_service_task_stop](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5537) | [service_task_stop](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L320) | The refactored function maintains the same potential resource management issue as the original: if xQueueSend fails, the semaphore gets deleted immediately while the task might still be running and accessing it. However, there's an additional subtle change - the refactored version uses different timer functions (esp_timer_stop/delete vs original _mdns_stop_timer) which could have different error handling behavior. The original _mdns_stop_timer might have handled timer state differently than the new stop_timer function. |
|
||||
| [mdns_post_custom_action_tcpip_if](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5558) | [post_custom_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L159) | The refactored function inverts the queue operation logic: original uses `xQueueSend(...) != pdPASS` for failure case, while refactored uses `!mdns_priv_queue_action(action)` for failure case. This suggests `mdns_priv_queue_action` returns true on success, but without seeing its implementation, we cannot verify if the queue behavior is equivalent. Additionally, the server initialization check changed from direct `!_mdns_server` to `!mdns_priv_is_server_init()` which may have different semantics. |
|
||||
| [set_default_duplicated_interfaces](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5579) | [set_default_duplicated_interfaces](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L285) | The function appears to be identical in both implementations (100% similarity), but there's a potential architectural concern. The refactored codebase contains mdns_priv_pcb_set_duplicate which handles PCB-level duplication with additional operations (clearing TX queues, deinitializing PCBs, setting PCB_DUP state), while the original netif-level duplication logic remains unchanged. This creates a potential inconsistency where netif-level duplication is established but PCB-level duplication handling may not be properly synchronized. The bidirectional relationship between interfaces is managed differently in these two approaches, which could lead to race conditions or inconsistent state between netif and PCB layers. |
|
||||
| [unregister_predefined_handlers](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5597) | [mdns_priv_netif_unregister_predefined_handlers](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L303) | The refactored function uses a different event handler function name: 'handle_system_event_for_preset' instead of 'mdns_preset_if_handle_system_event'. While the conditional compilation logic is identical, this change could introduce subtle bugs if: <br/> 1. The new handler function has different behavior or implementation <br/> 2. The new handler function has a different signature that's not compatible with the event system <br/> 3. The original handler function 'mdns_preset_if_handle_system_event' is still registered elsewhere and not properly cleaned up <br/> 4. The new handler function doesn't exist or has different error handling <br/> <br/> The search shows 'mdns_preset_if_handle_system_event' exists in the original codebase with a specific implementation, but no matches were found for 'handle_system_event_for_preset' in the refactored codebase, suggesting it might be missing or renamed differently. |
|
||||
| [mdns_netif_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5614) | [mdns_netif_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L384) | The refactored post_custom_action function returns ESP_OK even when mdns_priv_queue_action fails (returns false), which occurs when memory allocation for the action fails. This masks memory allocation failures and changes the error behavior from the original implementation. The original mdns_post_custom_action_tcpip_if likely returned ESP_ERR_NO_MEM in this scenario, but the refactored version returns success, potentially hiding critical resource constraints. |
|
||||
| [mdns_register_netif](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5619) | [mdns_register_netif](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L389) | The refactored mdns_unregister_netif function in Result 2 has a critical bug where it calls mdns_priv_service_lock() twice at the end instead of unlocking. This will cause a deadlock situation where the service lock is never released, blocking all subsequent mDNS operations that require the lock. The correct pattern should be mdns_priv_service_unlock() before returning. |
|
||||
| [mdns_unregister_netif](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5645) | [mdns_unregister_netif](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_netif.c#L415) | The refactored mdns_unregister_netif function has a critical bug where it calls mdns_priv_service_lock() twice instead of calling mdns_priv_service_unlock() to release the lock. This will cause a deadlock situation as the service lock is never released, preventing other threads from accessing the shared resource and potentially hanging the system. |
|
||||
| [mdns_init](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5665) | [mdns_init](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L351) | The refactored mdns_init function has a subtle bug in error handling: when mdns_priv_netif_init() fails, the code jumps to free_queue label which only deletes the action queue, but does not delete the semaphore created in mdns_priv_responder_init(). In the original code, both the queue and semaphore were properly cleaned up in the error path. This could lead to a resource leak of the semaphore. |
|
||||
| [mdns_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5763) | [mdns_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_service.c#L391) | The refactored mdns_free function introduces a subtle bug in the cleanup sequence. In the original code, free_delegated_hostnames() was called before mdns_mem_free((char *)_mdns_server->hostname) and mdns_mem_free((char *)_mdns_server->instance). However, in the refactored version, free_delegated_hostnames() is called inside mdns_priv_responder_free() AFTER the hostname and instance have already been freed. This could lead to accessing freed memory if free_delegated_hostnames() references the server's hostname or instance fields. |
|
||||
| [mdns_hostname_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5816) | [mdns_hostname_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L591) | The refactored function replaces direct xQueueSend with mdns_priv_queue_action, but there's a potential synchronization bug. The original code used xQueueSend with 0 timeout (non-blocking) and returned ESP_ERR_NO_MEM on queue full. The new mdns_priv_queue_action function's implementation is unknown - if it uses a different timeout or blocking behavior, it could cause deadlocks or different memory error handling. Additionally, the semaphore take operation (xSemaphoreTake) remains but the queue mechanism changed, which could lead to inconsistent state if the new queue function doesn't properly coordinate with the semaphore signaling. |
|
||||
| [mdns_hostname_get](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5846) | [mdns_hostname_get](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L621) | |
|
||||
| [mdns_delegate_hostname_add](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5864) | [mdns_delegate_hostname_add](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L639) | The refactored function uses mdns_priv_queue_action() instead of xQueueSend() with (TickType_t)0 timeout. If mdns_priv_queue_action() has different queue semantics (e.g., different timeout behavior or error handling), it could introduce subtle bugs in memory management or action queuing reliability. Additionally, the replacement of copy_address_list() with mdns_utils_copy_address_list() requires verification that the memory allocation and cleanup behavior remains identical, especially in error scenarios. |
|
||||
| [mdns_delegate_hostname_remove](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5895) | [mdns_delegate_hostname_remove](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L670) | The refactored function shows inconsistent synchronization behavior compared to similar functions in the same codebase. While mdns_hostname_set and mdns_delegate_hostname_add both call xSemaphoreTake(s_server->action_sema, portMAX_DELAY) after queueing their actions, mdns_delegate_hostname_remove does not. This inconsistency could lead to race conditions where callers of mdns_delegate_hostname_remove assume the operation is complete when it may still be pending in the action queue. The original codebase also lacked this synchronization, but the refactoring introduced this pattern inconsistently across similar functions. |
|
||||
| [mdns_delegate_hostname_set_address](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5924) | [mdns_delegate_hostname_set_address](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L699) | The refactored function removed the semaphore synchronization (xSemaphoreTake) that was present in similar functions (mdns_delegate_hostname_add and mdns_hostname_set), while the queue mechanism changed from direct xQueueSend to mdns_priv_queue_action. This creates inconsistent synchronization behavior across similar API functions and could lead to race conditions if the action handler expects synchronous completion. The original implementation used immediate queue sending (timeout 0) without waiting, but the refactored similar functions now wait for semaphore, suggesting this function may have missed a required synchronization update. |
|
||||
| [mdns_hostname_exists](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5954) | [mdns_hostname_exists](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L729) | |
|
||||
| [mdns_instance_name_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5963) | [mdns_instance_name_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L738) | The refactored function replaces direct xQueueSend with mdns_priv_queue_action, but the timeout behavior may have changed. The original used (TickType_t)0 timeout (non-blocking), while the helper function's timeout semantics are unclear. Additionally, the refactored version lacks the explicit semaphore synchronization present in mdns_hostname_set (xSemaphoreTake), potentially creating inconsistent behavior between similar operations. |
|
||||
| [mdns_service_add_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L5996) | [mdns_service_add_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L767) | The refactored function has a critical bug in the linked list insertion logic. The original code correctly sets up the linked list by assigning the new item to the head of the services list. However, the refactored code has a logical error: it sets `item->next = NULL` and then immediately overwrites it with `item->next = s_server->services`, which is correct, but then assigns `s_server->services = item`. This appears correct at first glance, but there's a subtle issue - the original code properly maintains the linked list structure while the refactored version might have different behavior if the helper functions (like `mdns_utils_get_service_item_instance` and `create_service`) have different implementations than their original counterparts. Additionally, the memory cleanup function changed from `_mdns_free_service` to `free_service` - if these have different implementations, it could lead to memory leaks or corruption in error paths. |
|
||||
| [mdns_service_add](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6038) | [mdns_service_add](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L809) | The refactored mdns_service_add function appears functionally equivalent to the original, but there are potential issues in its dependency chain. The function itself only changed the global variable name from _mdns_server to s_server, but the mdns_service_add_for_host function it calls has undergone significant refactoring with new locking mechanisms, service limit checks, and different error handling patterns. This could introduce subtle bugs like: <br/> 1. Deadlocks if the new mdns_priv_service_lock/unlock functions are not properly balanced <br/> 2. Different service validation behavior through mdns_utils_get_service_item_instance <br/> 3. Changed memory allocation patterns with create_service and HOOK_MALLOC_FAILED <br/> 4. Potential race conditions if the locking scope differs from the original implementation |
|
||||
| [mdns_service_exists](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6047) | [mdns_service_exists](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L818) | |
|
||||
| [mdns_service_exists_with_instance](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6056) | [mdns_service_exists_with_instance](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L827) | |
|
||||
| [_copy_mdns_txt_items](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6066) | [copy_txt_items](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L837) | The error handling cleanup loop condition `y < ret_index + 1` in both original and refactored code appears to have a potential off-by-one error. When `ret_index` represents the number of successfully allocated items, accessing `ret[ret_index]` (which would be the `ret_index + 1`-th element) could read/write beyond the allocated memory bounds if an allocation failure occurs early in the second loop. The condition should likely be `y < ret_index` to only clean up the items that were actually allocated. |
|
||||
| [_copy_delegated_host_address_list](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6119) | [copy_delegated_host_address_list](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L890) | |
|
||||
| [_mdns_lookup_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6131) | [lookup_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_console.c#L1139) | |
|
||||
| [mdns_service_port_set_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6211) | [mdns_service_port_set_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L982) | |
|
||||
| [mdns_service_port_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6229) | [mdns_service_port_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1000) | |
|
||||
| [mdns_service_txt_set_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6237) | [mdns_service_txt_set_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1008) | The refactored function has the same subtle bug as the original: when memory allocation fails (allocate_txt returns NULL), the function returns ESP_ERR_NO_MEM directly without releasing the service lock (mdns_priv_service_unlock). This creates a potential deadlock scenario where the service lock remains held indefinitely after a memory allocation failure. |
|
||||
| [mdns_service_txt_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6267) | [mdns_service_txt_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1038) | The refactored function uses s_server instead of _mdns_server for the server state check. While both appear to serve the same purpose (global MDNS server state), the refactored code introduces a potential NULL pointer dereference in mdns_priv_responder_free() at line 64 where it accesses s_server->action_sema without first checking if s_server is NULL, unlike the original code which consistently checked _mdns_server before accessing its members. |
|
||||
| [mdns_service_txt_item_set_for_host_with_explicit_value_len](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6275) | [mdns_service_txt_item_set_for_host_with_explicit_value_len](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1046) | The refactored function uses renamed helper functions (mdns_utils_str_null_or_empty, mdns_utils_get_service_item_instance, announce_all_pcbs, mdns_priv_service_lock/unlock) and a renamed global variable (s_server). While the core logic appears identical, there's a risk that these renamed functions may have different implementations or behaviors compared to their original counterparts. Specifically, the memory allocation error handling in the out_of_mem label still uses HOOK_MALLOC_FAILED and mdns_mem_free, but if the renamed locking functions don't properly handle error states or if the global s_server initialization differs from _mdns_server, it could lead to subtle bugs like memory leaks, race conditions, or incorrect service discovery behavior. |
|
||||
| [mdns_service_txt_item_set_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6331) | [mdns_service_txt_item_set_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1102) | The refactored function appears identical to the original, but there are two related functions (mdns_service_txt_item_set and mdns_service_txt_item_set_with_explicit_value_len) that now include state checking for s_server, while the original mdns_service_txt_item_set_for_host does not. This creates an inconsistency where some TXT item setting functions check server state while others don't. Additionally, the global variable has been renamed from _mdns_server to s_server, which could indicate broader changes in the codebase that might affect error handling consistency. |
|
||||
| [mdns_service_txt_item_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6339) | [mdns_service_txt_item_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1110) | |
|
||||
| [mdns_service_txt_item_set_with_explicit_value_len](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6348) | [mdns_service_txt_item_set_with_explicit_value_len](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1119) | |
|
||||
| [mdns_service_txt_item_remove_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6357) | [mdns_service_txt_item_remove_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1128) | The refactored function calls `announce_all_pcbs(&s, 1, false)` but there's no evidence that `announce_all_pcbs` exists in the refactored codebase. The search for "announce_all_pcbs" returned no matches, suggesting this function may be missing or renamed differently than expected. This could lead to a compilation error or runtime failure when trying to announce the TXT item removal. |
|
||||
| [mdns_service_txt_item_remove](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6404) | [mdns_service_txt_item_remove](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1175) | The refactored mdns_service_txt_item_remove_for_host function has several potential issues: <br/> 1. The complex ESP_GOTO_ON_FALSE conditional combines multiple error checks (server state, service/proto/key validation) into a single error path, which may mask specific error conditions that were handled separately in the original implementation <br/> 2. The memory management uses mdns_mem_free() instead of the original free() calls, which could indicate inconsistent memory management strategy across the codebase <br/> 3. The announce_all_pcbs(&s, 1, false) call appears to be new functionality that wasn't present in the original TXT removal logic, potentially causing unnecessary network announcements <br/> 4. The error handling with HOOK_MALLOC_FAILED at the end seems misplaced since no memory allocation occurs in the main logic path of this function <br/> 5. The function returns ESP_OK even when no TXT item was found to remove (the function completes without setting an error when the key isn't found) |
|
||||
| [_mdns_service_subtype_remove_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6412) | [service_subtype_remove_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1183) | The refactored mdns_service_subtype_remove_for_host function has a potential memory leak in the error path. When memory allocation fails at remove_subtypes->subtype = mdns_mem_strdup(subtype), the code jumps to out_of_mem label which frees remove_subtypes but does not free the already allocated remove_subtypes structure itself. This creates a scenario where remove_subtypes is allocated but its subtype field allocation fails, leaving the remove_subtypes structure leaked. The original function had no such memory allocation, so this is a new bug introduced during refactoring. |
|
||||
| [_mdns_service_subtype_remove_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6412) | [mdns_service_subtype_remove_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1212) | The refactored mdns_service_subtype_remove_for_host function has a potential memory leak in the error path. When memory allocation fails at remove_subtypes->subtype = mdns_mem_strdup(subtype), the code jumps to out_of_mem label which frees remove_subtypes but does not free the already allocated remove_subtypes structure itself. This creates a scenario where remove_subtypes is allocated but its subtype field allocation fails, leaving the remove_subtypes structure leaked. The original function had no such memory allocation, so this is a new bug introduced during refactoring. |
|
||||
| [mdns_service_subtype_remove_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6441) | [mdns_service_subtype_remove_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1212) | The refactored function uses `free_subtype(remove_subtypes)` instead of `_mdns_free_subtype(remove_subtypes)`. If `free_subtype` doesn't properly handle the case where `remove_subtypes->subtype` is NULL (due to failed mdns_mem_strdup), this could lead to memory leaks or undefined behavior. Additionally, the refactored version calls `free_subtype` even when `mdns_priv_send_bye_subtype` might have taken ownership of the subtypes, potentially causing double-free issues if the ownership semantics changed. |
|
||||
| [_mdns_service_subtype_add_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6474) | [service_subtype_add_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1245) | The refactored function 'mdns_service_subtype_add_multiple_items_for_host' has inconsistent error handling compared to the original. In the original code, when _mdns_service_subtype_add_for_host returns ESP_ERR_INVALID_ARG (subtype already exists), the calling code continues processing other subtypes. However, in the refactored version, any error other than ESP_OK or ESP_ERR_NO_MEM causes immediate termination with goto exit, preventing processing of remaining subtypes and potentially leaving the service in an inconsistent state. Additionally, the memory cleanup rollback only occurs for ESP_ERR_NO_MEM cases, not for other error types. |
|
||||
| [mdns_service_subtype_add_multiple_items_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6498) | [mdns_service_subtype_add_multiple_items_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1269) | The refactored function in Result 1 appears to be the direct replacement, but there are several subtle concerns: <br/> <br/> 1. **Missing validation for num_items > 0**: In the original code, the ESP_GOTO_ON_FALSE check includes `(num_items > 0)`, but in Result 1, this validation appears to be missing from the condition, which could allow calling the function with num_items=0 and bypass proper error handling. <br/> <br/> 2. **Function signature inconsistency**: The refactored function uses parameter name 'service' while the original uses 'service_type' in the header declaration. This could cause compilation issues if the header wasn't updated consistently. <br/> <br/> 3. **Resource management in error path**: The cleanup loop in the error path uses `service_subtype_remove_for_host` which may have different memory management behavior than the original `_mdns_service_subtype_remove_for_host`. If the memory allocation strategy changed (as seen in Result 3), the removal function might not properly handle the new allocation pattern. <br/> <br/> 4. **Potential double-free in update function**: Result 2 shows `free_subtype(goodbye_subtype)` followed by `free_service_subtype(s->service)` - if these functions have overlapping cleanup responsibilities, this could lead to double-free issues. |
|
||||
| [mdns_service_subtype_add_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6535) | [mdns_service_subtype_add_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1306) | The refactoring introduced a subtle bug in the error handling logic. In the original implementation, when _mdns_service_subtype_add_for_host returns ESP_ERR_NO_MEM, the calling function mdns_service_subtype_add_multiple_items_for_host would continue processing other subtypes but eventually roll back all successful additions. However, in the refactored version, service_subtype_add_for_host immediately returns ESP_ERR_NO_MEM without proper cleanup of the allocated subtype_item, causing a memory leak. Additionally, the refactored version uses different error labels (err vs out_of_mem) and the memory cleanup path in service_subtype_add_for_host returns the same error code regardless of which allocation failed, losing error specificity. |
|
||||
| [mdns_service_subtype_add_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6535) | [service_subtype_add_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1245) | The refactoring introduced a subtle bug in the error handling logic. In the original implementation, when _mdns_service_subtype_add_for_host returns ESP_ERR_NO_MEM, the calling function mdns_service_subtype_add_multiple_items_for_host would continue processing other subtypes but eventually roll back all successful additions. However, in the refactored version, service_subtype_add_for_host immediately returns ESP_ERR_NO_MEM without proper cleanup of the allocated subtype_item, causing a memory leak. Additionally, the refactored version uses different error labels (err vs out_of_mem) and the memory cleanup path in service_subtype_add_for_host returns the same error code regardless of which allocation failed, losing error specificity. |
|
||||
| [mdns_service_subtype_add_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6535) | [mdns_service_subtype_add_multiple_items_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1269) | The refactoring introduced a subtle bug in the error handling logic. In the original implementation, when _mdns_service_subtype_add_for_host returns ESP_ERR_NO_MEM, the calling function mdns_service_subtype_add_multiple_items_for_host would continue processing other subtypes but eventually roll back all successful additions. However, in the refactored version, service_subtype_add_for_host immediately returns ESP_ERR_NO_MEM without proper cleanup of the allocated subtype_item, causing a memory leak. Additionally, the refactored version uses different error labels (err vs out_of_mem) and the memory cleanup path in service_subtype_add_for_host returns the same error code regardless of which allocation failed, losing error specificity. |
|
||||
| [_mdns_service_find_subtype_needed_sendbye](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6543) | [service_find_subtype_needed_sendbye](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1314) | |
|
||||
| [mdns_service_subtype_update_multiple_items_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6593) | [mdns_service_subtype_update_multiple_items_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1364) | The refactored function calls free_service_subtype(s->service) which completely clears all subtypes from the service before adding new ones in the loop. This differs from the original behavior where _mdns_free_service_subtype might have been more selective. This could cause temporary service unavailability and potential race conditions where subtypes are briefly unavailable between the free and add operations. Additionally, the refactored version uses different locking functions (mdns_priv_service_lock/unlock vs MDNS_SERVICE_LOCK/UNLOCK) which may have different thread safety guarantees. |
|
||||
| [mdns_service_instance_name_set_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6640) | [mdns_service_instance_name_set_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1411) | |
|
||||
| [mdns_service_instance_name_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6666) | [mdns_service_instance_name_set](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1437) | The refactored function in Result 1 appears to be functionally equivalent to the original, but there's a subtle variable name change from `_mdns_server` to `s_server`. While this is likely just a naming convention change, it could indicate broader architectural changes that might affect other parts of the codebase if the global server variable semantics have changed. The function maintains the same parameter signature and delegation pattern to `mdns_service_instance_name_set_for_host`. <br/> <br/> However, the presence of `mdns_instance_name_set` (Result 2) is concerning as it suggests a parallel API with different semantics - it only takes an instance parameter and implements the functionality directly rather than delegating. This could lead to confusion about which function to use and potential inconsistencies in service instance management. |
|
||||
| [mdns_service_remove_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6674) | [mdns_service_remove_for_host](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1445) | The refactored function calls `free_service(a->service)` and `mdns_mem_free(a)` after removing the service from the linked list, but the original function used `_mdns_free_service(a->service)`. Without seeing the implementation of `free_service` vs `_mdns_free_service`, there's a risk that the refactored version may have different memory management behavior, potentially leading to memory leaks or double-free issues if the implementations differ. |
|
||||
| [mdns_service_remove](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6727) | [mdns_service_remove](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1498) | |
|
||||
| [mdns_service_remove_all](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6735) | [mdns_service_remove_all](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1506) | |
|
||||
| [mdns_query_results_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6763) | [mdns_query_results_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L673) | |
|
||||
| [_mdns_query_results_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6770) | [mdns_priv_query_results_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L25) | The refactoring changes the locking mechanism from MDNS_SERVICE_LOCK/UNLOCK to mdns_priv_service_lock/unlock without clear evidence of functional equivalence. This could introduce subtle synchronization issues, deadlock risks, or race conditions if the new locking functions have different semantics, scope, or implementation than the original ones. The change from static to public function scope (mdns_priv_query_results_free) also increases the risk of improper direct usage bypassing necessary locking. |
|
||||
| [_mdns_query_results_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6770) | [mdns_query_results_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L673) | The refactoring changes the locking mechanism from MDNS_SERVICE_LOCK/UNLOCK to mdns_priv_service_lock/unlock without clear evidence of functional equivalence. This could introduce subtle synchronization issues, deadlock risks, or race conditions if the new locking functions have different semantics, scope, or implementation than the original ones. The change from static to public function scope (mdns_priv_query_results_free) also increases the risk of improper direct usage bypassing necessary locking. |
|
||||
| [mdns_query_async_delete](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6801) | [mdns_query_async_delete](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L680) | |
|
||||
| [mdns_query_async_get_results](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6817) | [mdns_query_async_get_results](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L696) | The function implementation remains identical, but the relocation from mdns.c to mdns_querier.c introduces potential architectural risks. The main concerns are: 1) Header file dependencies - mdns.h must properly declare the function and include necessary types from the new module, 2) Structure definition consistency - mdns_search_once_t must have identical layout in both compilation units, particularly the done_semaphore and result fields, 3) Build system integration - mdns_querier.c must be properly compiled and linked, and 4) Cross-module access - other functions in mdns.c that previously accessed this function directly may now require proper inter-module function calls. |
|
||||
| [mdns_query_async_new](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6831) | [mdns_query_async_new](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L710) | The refactored search_init function now uses uint8_t for max_results parameter instead of size_t, which could cause truncation issues if max_results values exceed 255. This is a potential data loss bug since the original function accepted size_t and could handle larger values. Additionally, the search_init function creates a semaphore that needs verification of proper cleanup in all error paths through search_free. |
|
||||
| [mdns_query_generic](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6853) | [mdns_query_generic](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L732) | The refactored function uses renamed helper functions (mdns_priv_is_server_init, mdns_utils_str_null_or_empty, search_init, send_search_action, search_free) without clear verification that these maintain the exact same behavior and error handling semantics as their original counterparts. Specifically: <br/> <br/> 1. The condition `mdns_utils_str_null_or_empty(service) != mdns_utils_str_null_or_empty(proto)` relies on the renamed string function returning consistent boolean values <br/> 2. The search initialization and action functions may have different memory management or error propagation behaviors <br/> 3. The semaphore wait on `search->done_semaphore` assumes the internal search structure and synchronization mechanism remains unchanged <br/> 4. Without seeing the implementations of these renamed functions, we cannot verify that resource cleanup (search_free) properly handles the results pointer assignment that occurs before freeing <br/> <br/> These renamed functions could introduce subtle behavioral changes in error cases, memory management, or protocol compliance. |
|
||||
| [mdns_query](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6884) | [mdns_query](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L764) | The refactored mdns_query function appears identical to the original, but its dependency on mdns_query_generic has been significantly changed, introducing potential subtle bugs: <br/> <br/> 1. **Timeout handling regression**: The refactored mdns_query_generic uses `portMAX_DELAY` instead of the original timeout parameter, making queries potentially block indefinitely rather than respecting the user-specified timeout. <br/> <br/> 2. **Error code inconsistency**: The refactored version returns `ESP_ERR_NO_MEM` when `send_search_action` fails, but this function could fail for other reasons (network errors, internal state issues) that should return different error codes. <br/> <br/> 3. **Parameter validation change**: The validation logic for service/proto parameters has been altered from the original implementation, potentially allowing invalid parameter combinations that were previously rejected. <br/> <br/> 4. **Function dependency changes**: The refactored code uses `search_init`, `send_search_action`, and `search_free` instead of the original `_mdns_search_init` and internal mechanisms, raising concerns about functional equivalence and proper resource management. <br/> <br/> While the mdns_query wrapper function itself is unchanged, these underlying changes in mdns_query_generic could lead to unexpected blocking behavior, incorrect error reporting, and different parameter validation semantics. |
|
||||
| [mdns_query_ptr](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6889) | [mdns_query_ptr](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L769) | |
|
||||
| [mdns_query_srv](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6898) | [mdns_query_srv](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L778) | |
|
||||
| [mdns_query_txt](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6907) | [mdns_query_txt](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L787) | |
|
||||
| [mdns_lookup_delegated_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6916) | [mdns_lookup_delegated_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1531) | The refactored lookup_service function in mdns_console.c (Result 2) creates a circular dependency: it calls mdns_lookup_delegated_service/mdns_lookup_selfhosted_service, which in turn call the lookup_service function in mdns_responder.c. This appears to be a naming collision where two different functions with the same name exist in different compilation units, which could lead to linker errors or incorrect function resolution depending on the build configuration. |
|
||||
| [mdns_lookup_selfhosted_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6931) | [mdns_lookup_selfhosted_service](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_responder.c#L1546) | The refactored code introduces a potential circular dependency. The function `lookup_service` in mdns_console.c calls `mdns_lookup_selfhosted_service`, which in turn calls `lookup_service` (the internal implementation). This creates infinite recursion. The original code called `_mdns_lookup_service` directly without this circular call pattern. Additionally, the parameter order in the `lookup_service` wrapper function appears inconsistent - it takes a `delegated` parameter but the actual implementation functions don't have this parameter in their signatures. |
|
||||
| [mdns_query_a](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6947) | [mdns_query_a](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L797) | The refactored function changes the validation function from `_str_null_or_empty` to `mdns_utils_str_null_or_empty`. While this appears to be a simple renaming for better organization, there's a risk that the new function might have different behavior (e.g., different empty string definition, different return values, or different error handling) that could affect the input validation logic. The original codebase search didn't find references to `mdns_utils_str_null_or_empty`, suggesting this might be a newly introduced utility function whose implementation should be verified for functional equivalence. |
|
||||
| [mdns_query_aaaa](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L6986) | [mdns_query_aaaa](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_querier.c#L836) | |
|
||||
| [mdns_debug_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7026) | [dbg_packet](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_debug.c#L106) | The refactored function changes the timestamp type from uint32_t to uint64_t but uses the same format string logic. In embedded systems with 32-bit architectures, this could cause issues with printf formatting and stack alignment. Additionally, the static mdns_name_t variable could cause thread-safety issues in multi-threaded environments, and the new mdns_dbg_flush() call at the end might introduce blocking behavior that wasn't present in the original function. |
|
||||
| [_mdns_sync_browse_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7256) | [mdns_priv_browse_sync](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L595) | The refactored function `mdns_priv_browse_sync` hardcodes the action type to `ACTION_BROWSE_SYNC`, removing the `type` parameter from the original function. This could be problematic if the original function was ever called with action types other than `ACTION_BROWSE_SYNC`. While the search shows only `ACTION_BROWSE_SYNC` usage in the original codebase, this change reduces flexibility and could break future extensions that might use different browse sync action types. |
|
||||
| [_mdns_send_browse_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7278) | [send_browse_action](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L26) | The refactored function uses `!mdns_priv_queue_action(action)` instead of the original `xQueueSend(_mdns_server->action_queue, &action, (TickType_t)0) != pdPASS`. This boolean logic inversion suggests that `mdns_priv_queue_action` returns true for success and false for failure, while `xQueueSend` returns `pdPASS` (typically 1) for success and other values for failure. If `mdns_priv_queue_action` doesn't properly replicate the exact queueing semantics (including the zero timeout behavior), this could introduce subtle behavioral changes in the action queueing mechanism, potentially affecting the MDNS browsing functionality under high load conditions. |
|
||||
| [_mdns_browse_item_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7301) | [browse_item_free](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L49) | |
|
||||
| [_mdns_browse_init](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7314) | [browse_init](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L141) | The refactored browse_init function has inconsistent error handling for memory allocation failures. When browse->service allocation fails, HOOK_MALLOC_FAILED is called twice (once for browse malloc and once for service strndup), but when browse->proto allocation fails, HOOK_MALLOC_FAILED is only called once (for browse malloc). This inconsistency could lead to incorrect memory failure tracking or logging. The original function had consistent behavior where HOOK_MALLOC_FAILED was only called for the initial browse allocation failure. |
|
||||
| [mdns_browse_new](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7345) | [mdns_browse_new](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L617) | The refactored mdns_browse_new function has a critical logic error in the server initialization check. The original code returned NULL when `!_mdns_server` (server not initialized), but the refactored code returns NULL when `mdns_priv_is_server_init()` (server IS initialized). This logical inversion would prevent browsing from working when the MDNS server is properly initialized, making the function always return NULL in normal operation. |
|
||||
| [mdns_browse_delete](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7366) | [mdns_browse_delete](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L638) | |
|
||||
| [_mdns_browse_finish](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7389) | [browse_finish](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L119) | The refactored function uses a global variable 's_browse' instead of accessing '_mdns_server->browse'. This structural change could introduce subtle bugs if: <br/> 1. 's_browse' is not properly synchronized with the server state in other parts of the system <br/> 2. There are concurrent access issues since global variables are more prone to race conditions <br/> 3. Other functions that previously operated on '_mdns_server->browse' might not be updated to use 's_browse' consistently <br/> 4. The initialization and lifetime management of 's_browse' might differ from the server-based approach |
|
||||
| [_mdns_browse_add](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7411) | [browse_add](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L176) | The refactored function uses a global variable 's_browse' instead of accessing it through '_mdns_server->browse'. This structural change could introduce subtle bugs if: <br/> 1. 's_browse' is not properly initialized or synchronized with the original server state <br/> 2. Other parts of the system still expect to access browse data through '_mdns_server->browse' <br/> 3. There are thread safety issues since global 's_browse' might not have the same protection as the original server structure <br/> 4. The browse queue management is now split between different data structures, potentially causing inconsistencies |
|
||||
| [_mdns_browse_send](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7440) | [browse_send](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L80) | The refactored function replaces _mdns_search_send_pcb with mdns_priv_query_send, which changes the PCB readiness check from mdns_is_netif_ready(tcpip_if, ip_protocol) && _mdns_server->interfaces[tcpip_if].pcbs[ip_protocol].state > PCB_INIT to mdsn_priv_pcb_is_inited(tcpip_if, ip_protocol). This could change when queries are sent and there's a potential typo in "mdsn_priv_pcb_is_inited" (should likely be "mdns_priv_pcb_is_inited"). Additionally, the packet management in mdns_priv_query_send (create, dispatch, immediate free) may have different error handling characteristics than the original implementation. |
|
||||
| [_mdns_add_browse_result](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7461) | [add_browse_result](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L288) | The function visibility has changed from static to potentially non-static (the refactored code snippet doesn't show the static keyword). This could lead to linkage issues or unintended external access. Additionally, all call sites in the original codebase need to be verified to ensure they now call the renamed function in the new file location (mdns_browser.c vs mdns.c). |
|
||||
| [_mdns_browse_result_add_ip](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7488) | [mdns_priv_browse_result_add_ip](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L315) | The refactored function replaces `_mdns_add_browse_result` with `add_browse_result` without the `_mdns_` prefix, which may indicate a missing namespace or different function implementation. Additionally, the TTL update logic changed from `_mdns_result_update_ttl` to `mdns_priv_query_update_result_ttl`, which could have different behavior for TTL calculations. The search results show no matches for these renamed functions in the refactored codebase, suggesting they may be missing or have different signatures/implementations. |
|
||||
| [_mdns_browse_find_from](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7553) | [mdns_priv_browse_find](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L205) | The refactored function changed from taking a browse list parameter to using a global variable 's_browse', which could introduce thread safety issues and makes the function dependent on global state. Additionally, the original function was called with '_mdns_server->browse' as parameter, but the refactored version directly uses 's_browse' without clear evidence that these are equivalent. This change in data access pattern could lead to incorrect browse list traversal if the global variable is not properly synchronized with the original server state. |
|
||||
| [is_txt_item_in_list](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7583) | [is_txt_item_in_list](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L376) | The refactored function appears to be identical to the original (100% similarity) but has been moved from mdns.c to mdns_browser.c. This change in location could introduce subtle issues if: <br/> 1. The function now has different visibility/scope in the new file <br/> 2. Callers in the original location (mdns.c) cannot access it if it remains static <br/> 3. The function assumes different context or dependencies in the browser component vs responder component <br/> 4. The TXT item comparison logic might need different behavior between browser and responder contexts |
|
||||
| [_mdns_browse_result_add_txt](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7601) | ??? | |
|
||||
| [_mdns_copy_address_in_previous_result](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7699) | [copy_address_in_previous_result](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L492) | |
|
||||
| [_mdns_browse_result_add_srv](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7720) | [mdns_priv_browse_result_add_srv](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L513) | The refactored function calls two helper functions without the expected 'mdns_priv_' prefix: 'copy_address_in_previous_result' and 'add_browse_result'. This naming inconsistency suggests these functions may not be properly declared or implemented in the refactored codebase, potentially leading to linker errors or incorrect function resolution. Additionally, the final explicit 'return' statement was removed, which could affect code clarity but likely doesn't impact functionality. |
|
||||
| [_mdns_browse_sync](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7800) | [browse_sync](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_browser.c#L59) | |
|
||||
| [_debug_printf_result](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7821) | [dbg_printf_result](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_debug.c#L367) | The refactored function dbg_printf_result has been changed from non-static to static, which may break external calls if the function was used outside its compilation unit. Additionally, the addition of mdns_dbg_flush() at the end changes the debug output behavior - it now forces immediate flushing which could impact performance in debug-heavy scenarios or embedded systems with constrained I/O resources. |
|
||||
| [debug_printf_browse_result](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7852) | [mdns_debug_printf_browse_result](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_debug.c#L399) | The refactored function adds an unconditional call to mdns_dbg_flush() which was not present in the original function. This could potentially impact performance in debug mode, especially if called frequently, as it may force immediate output flushing that wasn't happening before. Additionally, the debug macro names have changed from _mdns_dbg_printf/_debug_printf_result to dbg_printf/dbg_printf_result, which could have different formatting or behavior if these macros are not functionally equivalent. |
|
||||
| [debug_printf_browse_result_all](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns.c#L7859) | [mdns_debug_printf_browse_result_all](https://github.com/espressif/esp-protocols/blob/main/components/mdns/mdns_debug.c#L407) | The refactored function adds a call to mdns_dbg_flush() at the end. While this might be intentional to ensure debug output is flushed, it could introduce timing differences in debugging scenarios or potentially block if the flush operation has significant overhead. In embedded systems or real-time environments, this could affect system behavior during debugging sessions. The original function did not have any flush operation, so output buffering behavior may have changed. |
|
||||
@@ -76,6 +76,21 @@ def test_ptr_additional_records_for_service(dig_app):
|
||||
dig_app.check_additional(resp, 'TXT', 'test_service._http._tcp.local', expected=True)
|
||||
|
||||
|
||||
def test_instance_any_answer_records(dig_app):
|
||||
"""Query ANY for the service instance and ensure SRV/TXT are in Answer (Q/A path)."""
|
||||
resp = dig_app.run_query('test_service._http._tcp.local', query_type='ANY')
|
||||
|
||||
# Answer section should contain SRV and TXT records for the instance
|
||||
srv_answers = dig_app.parse_section(resp, 'answer', 'SRV')
|
||||
txt_answers = dig_app.parse_section(resp, 'answer', 'TXT')
|
||||
assert any('test_service._http._tcp.local' in a for a in srv_answers)
|
||||
assert any('test_service._http._tcp.local' in a for a in txt_answers)
|
||||
|
||||
# We should not see a PTR for the generic service name in the Answer section
|
||||
ptr_answers = dig_app.parse_section(resp, 'answer', 'PTR')
|
||||
assert not any('_http._tcp.local' in a for a in ptr_answers)
|
||||
|
||||
|
||||
def test_remove_service(mdns_console, dig_app):
|
||||
mdns_console.send_input('mdns_service_remove _http _tcp')
|
||||
mdns_console.send_input('mdns_service_lookup _http _tcp')
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user