From 3f4a2f609368b7bbc821d60c30f4c5d19c66f193 Mon Sep 17 00:00:00 2001
From: David Cermak <cermak@espressif.com>
Date: Mon, 18 Nov 2024 07:19:24 +0100
Subject: [PATCH 1/5] fix(mdns): Rust experimenatation

---
 components/mdns/CMakeLists.txt           | 2 +-
 components/mdns/mdns.c                   | 9 +++++++--
 components/mdns/mdns_networking_socket.c | 2 +-
 3 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/components/mdns/CMakeLists.txt b/components/mdns/CMakeLists.txt
index e0b2a4d9e0..915b9bd00e 100644
--- a/components/mdns/CMakeLists.txt
+++ b/components/mdns/CMakeLists.txt
@@ -14,7 +14,7 @@ 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_NETWORKING} ${MDNS_CONSOLE})
+    set(srcs ${MDNS_NETWORKING} ${MDNS_CONSOLE})
 else()
     set(dependencies lwip console esp_netif)
     set(private_dependencies esp_timer esp_wifi)
diff --git a/components/mdns/mdns.c b/components/mdns/mdns.c
index 113a1e999b..ca9d87ddbe 100644
--- a/components/mdns/mdns.c
+++ b/components/mdns/mdns.c
@@ -176,7 +176,7 @@ static inline esp_netif_t *esp_netif_from_preset_if(mdns_predef_if_t predef_if)
  * @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_get_esp_netif(mdns_if_t tcpip_if)
+esp_netif_t *_mdns_get_esp_netif23(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) {
@@ -347,7 +347,7 @@ static bool _mdns_can_add_more_services(void)
     return true;
 }
 
-esp_err_t _mdns_send_rx_action(mdns_rx_packet_t *packet)
+esp_err_t _mdns_send_rx_action23(mdns_rx_packet_t *packet)
 {
     mdns_action_t *action = NULL;
 
@@ -1544,6 +1544,11 @@ static void _mdns_dispatch_tx_packet(mdns_tx_packet_t *p)
     mdns_debug_packet(packet, index);
 #endif
 
+    ESP_LOG_BUFFER_HEXDUMP(TAG, packet, index, ESP_LOG_INFO);
+    for (int i = 0; i < index; ++i) {
+        printf("0x%02x, ", packet[i]);
+    }
+    printf("\n");
     _mdns_udp_pcb_write(p->tcpip_if, p->ip_protocol, &p->dst, p->port, packet, index);
 }
 
diff --git a/components/mdns/mdns_networking_socket.c b/components/mdns/mdns_networking_socket.c
index a99a9cc2d9..d359a353cf 100644
--- a/components/mdns/mdns_networking_socket.c
+++ b/components/mdns/mdns_networking_socket.c
@@ -206,7 +206,7 @@ size_t _mdns_udp_pcb_write(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol, c
         ESP_LOGE(TAG, "espaddr_to_inet() failed: Mismatch of IP protocols");
         return 0;
     }
-    ESP_LOGD(TAG, "[sock=%d]: Sending to IP %s port %d", sock, get_string_address(&in_addr), port);
+    ESP_LOGI(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));

From b983fe48eb2064fe6f4101c1134b74b0cbb72112 Mon Sep 17 00:00:00 2001
From: David Cermak <cermak@espressif.com>
Date: Fri, 22 Nov 2024 16:47:06 +0100
Subject: [PATCH 2/5] feat(mdns): WIP support for rust API

---
 .github/workflows/mdns__build-target-test.yml |  84 ------
 .github/workflows/mdns__host-tests.yml        |  64 -----
 .github/workflows/mdns__rust.yml              |  34 +++
 components/mdns/CMakeLists.txt                |   2 +-
 components/mdns/Cargo.toml                    |  13 +
 components/mdns/build.rs                      | 104 +++++++
 .../mdns/examples/simple_query/CMakeLists.txt |  16 ++
 .../examples/simple_query/main/CMakeLists.txt |   4 +
 .../simple_query/main/Kconfig.projbuild       |  21 ++
 .../simple_query/main/esp_system_linux2.c     |  46 ++++
 .../simple_query/main/idf_component.yml       |   7 +
 .../mdns/examples/simple_query/main/main.c    | 101 +++++++
 .../examples/simple_query/sdkconfig.defaults  |   7 +
 components/mdns/examples/usage.rs             |  58 ++++
 components/mdns/mdns_stub.c                   |  76 ++++++
 components/mdns/src/lib.rs                    | 258 ++++++++++++++++++
 16 files changed, 746 insertions(+), 149 deletions(-)
 delete mode 100644 .github/workflows/mdns__build-target-test.yml
 delete mode 100644 .github/workflows/mdns__host-tests.yml
 create mode 100644 .github/workflows/mdns__rust.yml
 create mode 100644 components/mdns/Cargo.toml
 create mode 100644 components/mdns/build.rs
 create mode 100644 components/mdns/examples/simple_query/CMakeLists.txt
 create mode 100644 components/mdns/examples/simple_query/main/CMakeLists.txt
 create mode 100644 components/mdns/examples/simple_query/main/Kconfig.projbuild
 create mode 100644 components/mdns/examples/simple_query/main/esp_system_linux2.c
 create mode 100644 components/mdns/examples/simple_query/main/idf_component.yml
 create mode 100644 components/mdns/examples/simple_query/main/main.c
 create mode 100644 components/mdns/examples/simple_query/sdkconfig.defaults
 create mode 100644 components/mdns/examples/usage.rs
 create mode 100644 components/mdns/mdns_stub.c
 create mode 100644 components/mdns/src/lib.rs

diff --git a/.github/workflows/mdns__build-target-test.yml b/.github/workflows/mdns__build-target-test.yml
deleted file mode 100644
index d222307c79..0000000000
--- a/.github/workflows/mdns__build-target-test.yml
+++ /dev/null
@@ -1,84 +0,0 @@
-name: "mdns: build/target-tests"
-
-on:
-  push:
-    branches:
-      - master
-  pull_request:
-    types: [opened, synchronize, reopened, labeled]
-
-jobs:
-  build_mdns:
-    if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
-    name: Build
-    strategy:
-      matrix:
-        idf_ver: ["latest", "release-v5.0", "release-v5.2", "release-v5.3"]
-        test: [ { app: example, path: "examples/query_advertise" }, { app: unit_test, path: "tests/unit_test" }, { app: test_app, path: "tests/test_apps" } ]
-    runs-on: ubuntu-22.04
-    container: espressif/idf:${{ matrix.idf_ver }}
-    steps:
-      - name: Checkout esp-protocols
-        uses: actions/checkout@v4
-      - name: Build ${{ matrix.test.app }} with IDF-${{ matrix.idf_ver }}
-        shell: bash
-        run: |
-          . ${IDF_PATH}/export.sh
-          python -m pip install idf-build-apps
-          # Build default configs for all targets
-          python ./ci/build_apps.py components/mdns/${{ matrix.test.path }} -r default -d
-          # Build specific configs for test targets
-          python ./ci/build_apps.py components/mdns/${{ matrix.test.path }}
-          cd components/mdns/${{ matrix.test.path }}
-          for dir in `ls -d build_esp32_*`; do
-          $GITHUB_WORKSPACE/ci/clean_build_artifacts.sh `pwd`/$dir
-          zip -qur artifacts.zip $dir
-          done
-      - uses: actions/upload-artifact@v4
-        with:
-          name: mdns_bin_esp32_${{ matrix.idf_ver }}_${{ matrix.test.app }}
-          path: components/mdns/${{ matrix.test.path }}/artifacts.zip
-          if-no-files-found: error
-
-  target_tests_mdns:
-    # Skip running on forks since it won't have access to secrets
-    if: |
-      github.repository == 'espressif/esp-protocols' &&
-      ( contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push' )
-    name: Target Example and Unit tests
-    strategy:
-      matrix:
-        idf_ver: ["latest"]
-        idf_target: ["esp32"]
-        test: [ { app: example, path: "examples/query_advertise" }, { app: unit_test, path: "tests/unit_test" }, { app: test_app, path: "tests/test_apps" } ]
-    needs: build_mdns
-    runs-on:
-      - self-hosted
-      - ESP32-ETHERNET-KIT
-    steps:
-      - name: Clear repository
-        run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
-      - uses: actions/checkout@v4
-      - uses: actions/download-artifact@v4
-        with:
-          name: mdns_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }}
-          path: components/mdns/${{ matrix.test.path }}/ci/
-      - name: Install Python packages
-        env:
-          PIP_EXTRA_INDEX_URL: "https://www.piwheels.org/simple"
-        run: |
-            sudo apt-get install -y dnsutils
-      - name: Run ${{ matrix.test.app }} application on ${{ matrix.idf_target }}
-        working-directory: components/mdns/${{ matrix.test.path }}
-        run: |
-            unzip ci/artifacts.zip -d ci
-            for dir in `ls -d ci/build_*`; do
-            rm -rf build sdkconfig.defaults
-            mv $dir build
-            python -m pytest --log-cli-level DEBUG --junit-xml=./results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=${{ matrix.idf_target }}
-            done
-      - uses: actions/upload-artifact@v4
-        if: always()
-        with:
-          name: results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}.xml
-          path: components/mdns/${{ matrix.test.path }}/*.xml
diff --git a/.github/workflows/mdns__host-tests.yml b/.github/workflows/mdns__host-tests.yml
deleted file mode 100644
index 5ef6c8adee..0000000000
--- a/.github/workflows/mdns__host-tests.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-name: "mdns: host-tests"
-
-on:
-  push:
-    branches:
-      - master
-  pull_request:
-    types: [opened, synchronize, reopened, labeled]
-
-jobs:
-  host_test_mdns:
-    if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
-    name: Host test build
-    runs-on: ubuntu-22.04
-    container: espressif/idf:release-v5.3
-
-    steps:
-      - name: Checkout esp-protocols
-        uses: actions/checkout@v4
-        with:
-          path: protocols
-
-      - name: Build and Test
-        shell: bash
-        run: |
-          . ${IDF_PATH}/export.sh
-          python -m pip install idf-build-apps dnspython pytest pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf
-          cd $GITHUB_WORKSPACE/protocols
-          # 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
-          # 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
-          # Next we run the pytest (using the console app)
-          pytest
-
-  build_afl_host_test_mdns:
-    if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
-    name: Build AFL host test
-    strategy:
-      matrix:
-        idf_ver: ["latest"]
-        idf_target: ["esp32"]
-
-    runs-on: ubuntu-22.04
-    container: espressif/idf:${{ matrix.idf_ver }}
-    steps:
-      - name: Checkout esp-protocols
-        uses: actions/checkout@v4
-        with:
-          path: esp-protocols
-      - name: Install Necessary Libs
-        run: |
-          apt-get update -y
-          apt-get install -y libbsd-dev
-      - name: Build ${{ matrix.example }} with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
-        env:
-          IDF_TARGET: ${{ matrix.idf_target }}
-        shell: bash
-        run: |
-          . ${IDF_PATH}/export.sh
-          cd $GITHUB_WORKSPACE/esp-protocols/components/mdns/tests/test_afl_fuzz_host/
-          make INSTR=off
diff --git a/.github/workflows/mdns__rust.yml b/.github/workflows/mdns__rust.yml
new file mode 100644
index 0000000000..a32d0ab88c
--- /dev/null
+++ b/.github/workflows/mdns__rust.yml
@@ -0,0 +1,34 @@
+name: "mdns: rust-tests"
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    types: [opened, synchronize, reopened, labeled]
+
+jobs:
+  host_test_mdns:
+    if: contains(github.event.pull_request.labels.*.name, 'mdns') || github.event_name == 'push'
+    name: Host test build
+    runs-on: ubuntu-22.04
+    container: espressif/idf:latest
+
+    steps:
+      - name: Checkout esp-protocols
+        uses: actions/checkout@v4
+
+      - name: Build and Test
+        shell: bash
+        run: |
+          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
+          # Add Rust to the current PATH
+          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
+          . "$HOME/.cargo/env"
+          rustc --version
+          cargo --version
+          . ${IDF_PATH}/export.sh
+          cd components/mdns/examples/simple_query/
+          idf.py build
+          cd ../..
+          COMPILE_COMMANDS_DIR=examples/simple_query/build/ cargo run --example usage
diff --git a/components/mdns/CMakeLists.txt b/components/mdns/CMakeLists.txt
index 915b9bd00e..3a3b4c054b 100644
--- a/components/mdns/CMakeLists.txt
+++ b/components/mdns/CMakeLists.txt
@@ -14,7 +14,7 @@ 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_NETWORKING} ${MDNS_CONSOLE})
+    set(srcs "mdns_stub.c" ${MDNS_NETWORKING} ${MDNS_CONSOLE})
 else()
     set(dependencies lwip console esp_netif)
     set(private_dependencies esp_timer esp_wifi)
diff --git a/components/mdns/Cargo.toml b/components/mdns/Cargo.toml
new file mode 100644
index 0000000000..09a10e7458
--- /dev/null
+++ b/components/mdns/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "mdns"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+libc = "0.2"
+dns-parser = "0.8"
+
+[build-dependencies]
+cc = "1.0"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
diff --git a/components/mdns/build.rs b/components/mdns/build.rs
new file mode 100644
index 0000000000..8647aa0c02
--- /dev/null
+++ b/components/mdns/build.rs
@@ -0,0 +1,104 @@
+use std::env;
+use std::fs;
+use std::path::{Path, PathBuf};
+use serde::Deserialize;
+
+#[derive(Debug, Deserialize)]
+struct CompileCommand {
+    directory: String,
+    command: String,
+    file: String,
+}
+
+fn main() {
+    // Get the directory for compile_commands.json from an environment variable
+    let compile_commands_dir = env::var("COMPILE_COMMANDS_DIR")
+        .unwrap_or_else(|_| ".".to_string()); // Default to current directory
+
+    // Construct the path to the compile_commands.json file
+    let compile_commands_path = Path::new(&compile_commands_dir).join("compile_commands.json");
+
+    // Parse compile_commands.json
+    let compile_commands: Vec<CompileCommand> = {
+        let data = fs::read_to_string(&compile_commands_path)
+            .expect("Failed to read compile_commands.json");
+        serde_json::from_str(&data)
+            .expect("Failed to parse compile_commands.json")
+    };
+
+    // Directory of compile_commands.json, used to resolve relative paths
+    let base_dir = compile_commands_path
+        .parent()
+        .expect("Failed to get base directory of compile_commands.json");
+
+    // List of C files to compile (only base names)
+    let files_to_compile = vec![
+        "mdns_networking_socket.c",
+        "log_write.c",
+        "log_timestamp.c",
+        "esp_netif_linux.c",
+        "freertos_linux.c",
+        "tag_log_level.c",
+        "log_linked_list.c",
+        "log_lock.c",
+        "log_level.c",
+        "log_binary_heap.c",
+        "esp_system_linux2.c",
+        "heap_caps_linux.c",
+        "mdns_stub.c",
+        "log_buffers.c",
+        "util.c"
+    ];
+
+    // Initialize the build
+    let mut build = cc::Build::new();
+
+for file in &files_to_compile {
+    // Extract the base name from `file` for comparison
+    let target_base_name = Path::new(file)
+        .file_name()
+        .expect("Failed to extract base name from target file")
+        .to_str()
+        .expect("Target file name is not valid UTF-8");
+
+    // Find the entry in compile_commands.json by matching the base name
+    let cmd = compile_commands.iter()
+        .find(|entry| {
+            let full_path = Path::new(&entry.directory).join(&entry.file); // Resolve relative paths
+            if let Some(base_name) = full_path.file_name().and_then(|name| name.to_str()) {
+//                 println!("Checking file: {} against {}", base_name, target_base_name); // Debug information
+                base_name == target_base_name
+            } else {
+                false
+            }
+        })
+        .unwrap_or_else(|| panic!("{} not found in compile_commands.json", target_base_name));
+
+    // Add the file to the build
+    build.file(&cmd.file);
+
+    // Parse flags and include paths from the command
+    for part in cmd.command.split_whitespace() {
+        if part.starts_with("-I") {
+            // Handle include directories
+            let include_path = &part[2..];
+            let full_include_path = if Path::new(include_path).is_relative() {
+                base_dir.join(include_path).canonicalize()
+                    .expect("Failed to resolve relative include path")
+            } else {
+                PathBuf::from(include_path)
+            };
+            build.include(full_include_path);
+        } else if part.starts_with("-D") || part.starts_with("-std") {
+            // Add other compilation flags
+            build.flag(part);
+        }
+    }
+}
+
+
+
+
+    // Compile with the gathered information
+    build.compile("mdns");
+}
diff --git a/components/mdns/examples/simple_query/CMakeLists.txt b/components/mdns/examples/simple_query/CMakeLists.txt
new file mode 100644
index 0000000000..a244a7cabb
--- /dev/null
+++ b/components/mdns/examples/simple_query/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.5)
+
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+if(${IDF_TARGET} STREQUAL "linux")
+    set(EXTRA_COMPONENT_DIRS "../../../../common_components/linux_compat" "../../tests/host_test/components/")
+    set(COMPONENTS main)
+endif()
+
+project(mdns_host)
+
+# Enable sanitizers only without console (we'd see some leaks on argtable when console exits)
+if(NOT CONFIG_TEST_CONSOLE AND CONFIG_IDF_TARGET_LINUX)
+idf_component_get_property(mdns mdns COMPONENT_LIB)
+target_link_options(${mdns} INTERFACE -fsanitize=address -fsanitize=undefined)
+endif()
diff --git a/components/mdns/examples/simple_query/main/CMakeLists.txt b/components/mdns/examples/simple_query/main/CMakeLists.txt
new file mode 100644
index 0000000000..31eb27ed13
--- /dev/null
+++ b/components/mdns/examples/simple_query/main/CMakeLists.txt
@@ -0,0 +1,4 @@
+idf_component_register(SRCS "main.c" "esp_system_linux2.c"
+                    INCLUDE_DIRS
+                    "."
+                    REQUIRES mdns console nvs_flash)
diff --git a/components/mdns/examples/simple_query/main/Kconfig.projbuild b/components/mdns/examples/simple_query/main/Kconfig.projbuild
new file mode 100644
index 0000000000..9bf4bba235
--- /dev/null
+++ b/components/mdns/examples/simple_query/main/Kconfig.projbuild
@@ -0,0 +1,21 @@
+menu "Test Configuration"
+
+    config TEST_HOSTNAME
+        string "mDNS Hostname"
+        default "esp32-mdns"
+        help
+            mDNS Hostname for example to use
+
+    config TEST_NETIF_NAME
+        string "Network interface name"
+        default "eth2"
+        help
+            Name/ID if the network interface on which we run the mDNS host test
+
+    config TEST_CONSOLE
+        bool "Start console"
+        default n
+        help
+            Test uses esp_console for interactive testing.
+
+endmenu
diff --git a/components/mdns/examples/simple_query/main/esp_system_linux2.c b/components/mdns/examples/simple_query/main/esp_system_linux2.c
new file mode 100644
index 0000000000..25ee652382
--- /dev/null
+++ b/components/mdns/examples/simple_query/main/esp_system_linux2.c
@@ -0,0 +1,46 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/*
+ * All functions presented here are stubs for the POSIX/Linux implementation of FReeRTOS.
+ * They are meant to allow to compile, but they DO NOT return any meaningful value.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include "esp_private/system_internal.h"
+#include "esp_heap_caps.h"
+
+// dummy, we should never get here on Linux
+void esp_restart_noos_dig(void)
+{
+    abort();
+}
+
+uint32_t esp_get_free_heap_size(void)
+{
+    return heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
+}
+
+uint32_t esp_get_free_internal_heap_size(void)
+{
+    return heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
+}
+
+uint32_t esp_get_minimum_free_heap_size(void)
+{
+    return heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
+}
+
+const char *esp_get_idf_version(void)
+{
+    return "IDF_VER";
+}
+
+void __attribute__((noreturn)) esp_system_abort(const char *details)
+{
+    exit(1);
+}
diff --git a/components/mdns/examples/simple_query/main/idf_component.yml b/components/mdns/examples/simple_query/main/idf_component.yml
new file mode 100644
index 0000000000..e2d4fe9ba2
--- /dev/null
+++ b/components/mdns/examples/simple_query/main/idf_component.yml
@@ -0,0 +1,7 @@
+dependencies:
+  idf: ">=5.0"
+  espressif/mdns:
+    version: "^1.0.0"
+    override_path: "../../.."
+  protocol_examples_common:
+    path: ${IDF_PATH}/examples/common_components/protocol_examples_common
diff --git a/components/mdns/examples/simple_query/main/main.c b/components/mdns/examples/simple_query/main/main.c
new file mode 100644
index 0000000000..1678678eef
--- /dev/null
+++ b/components/mdns/examples/simple_query/main/main.c
@@ -0,0 +1,101 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include <stdio.h>
+#include "esp_log.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_console.h"
+#include "mdns.h"
+
+static const char *TAG = "mdns-test";
+
+static esp_netif_t *s_netif;
+
+static void mdns_test_app(esp_netif_t *interface);
+
+#ifndef CONFIG_IDF_TARGET_LINUX
+#include "protocol_examples_common.h"
+#include "esp_event.h"
+#include "nvs_flash.h"
+
+/**
+ * @brief This is an entry point for the real target device,
+ * need to init few components and connect to a network interface
+ */
+void app_main(void)
+{
+    ESP_ERROR_CHECK(nvs_flash_init());
+    ESP_ERROR_CHECK(esp_netif_init());
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+    ESP_ERROR_CHECK(example_connect());
+
+    mdns_test_app(EXAMPLE_INTERFACE);
+
+    ESP_ERROR_CHECK(example_disconnect());
+}
+#else
+
+/**
+ * @brief This is an entry point for the linux target (simulator on host)
+ * need to create a dummy WiFi station and use it as mdns network interface
+ */
+int main(int argc, char *argv[])
+{
+    setvbuf(stdout, NULL, _IONBF, 0);
+    const esp_netif_inherent_config_t base_cg = { .if_key = "WIFI_STA_DEF", .if_desc = CONFIG_TEST_NETIF_NAME };
+    esp_netif_config_t cfg = { .base = &base_cg  };
+    s_netif = esp_netif_new(&cfg);
+
+    mdns_test_app(s_netif);
+
+    esp_netif_destroy(s_netif);
+    return 0;
+}
+#endif
+
+typedef size_t mdns_if_t;
+
+typedef struct {
+    mdns_if_t tcpip_if;
+    mdns_ip_protocol_t ip_protocol;
+    struct pbuf *pb;
+    esp_ip_addr_t src;
+    esp_ip_addr_t dest;
+    uint16_t src_port;
+    uint8_t multicast;
+} mdns_rx_packet_t;
+
+struct pbuf  {
+    struct pbuf *next;
+    void *payload;
+    size_t tot_len;
+    size_t len;
+};
+
+esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
+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);
+esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
+void _mdns_packet_free(mdns_rx_packet_t *packet);
+
+
+static void mdns_test_app(esp_netif_t *interface)
+{
+    uint8_t query_packet[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x64, 0x61, 0x76, 0x69, 0x64, 0x2d, 0x77, 0x6f, 0x72, 0x6b, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x01, 0x00, 0x01};
+    esp_ip_addr_t ip = ESP_IP4ADDR_INIT(224, 0, 0, 251);
+
+//    ESP_ERROR_CHECK(mdns_init());
+//    ESP_ERROR_CHECK(mdns_register_netif(interface));
+//    ESP_ERROR_CHECK(mdns_netif_action(interface, MDNS_EVENT_ENABLE_IP4));
+    esp_err_t err = _mdns_pcb_init(0, MDNS_IP_PROTOCOL_V4);
+    ESP_LOGI(TAG, "err = %d", err);
+    size_t len = _mdns_udp_pcb_write(0, MDNS_IP_PROTOCOL_V4, &ip, 5353, query_packet, sizeof(query_packet));
+    ESP_LOGI(TAG, "len = %d", (int)len);
+//    query_mdns_host("david-work");
+    vTaskDelay(pdMS_TO_TICKS(1000));
+//    mdns_free();
+    _mdns_pcb_deinit(0, MDNS_IP_PROTOCOL_V4);
+    ESP_LOGI(TAG, "Exit");
+}
diff --git a/components/mdns/examples/simple_query/sdkconfig.defaults b/components/mdns/examples/simple_query/sdkconfig.defaults
new file mode 100644
index 0000000000..dc79fbcad8
--- /dev/null
+++ b/components/mdns/examples/simple_query/sdkconfig.defaults
@@ -0,0 +1,7 @@
+CONFIG_IDF_TARGET="linux"
+CONFIG_TEST_NETIF_NAME="eth0"
+CONFIG_ESP_EVENT_POST_FROM_ISR=n
+CONFIG_MDNS_NETWORKING_SOCKET=y
+CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES=y
+CONFIG_MDNS_PREDEF_NETIF_STA=n
+CONFIG_MDNS_PREDEF_NETIF_AP=n
diff --git a/components/mdns/examples/usage.rs b/components/mdns/examples/usage.rs
new file mode 100644
index 0000000000..68edcfd6c0
--- /dev/null
+++ b/components/mdns/examples/usage.rs
@@ -0,0 +1,58 @@
+// examples/basic_usage.rs
+
+use mdns::*;
+use std::thread;
+use std::time::Duration;
+
+pub fn test_mdns() {
+    let ip4 = EspIpAddr {
+        u_addr: EspIpUnion {
+            ip4: EspIp4Addr {
+                addr: u32::from_le_bytes([224, 0, 0, 251]), // Convert 224.0.0.251 to big-endian
+            },
+        },
+        addr_type: ESP_IPADDR_TYPE_V4,
+    };
+
+//     let query_packet: [u8; 34] = [
+//         0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x64, 0x61,
+//         0x76, 0x69, 0x64, 0x2d, 0x77, 0x6f, 0x72, 0x6b, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00,
+//         0x00, 0x01, 0x00, 0x01,
+//     ];
+
+    if let Err(err) = mdns_pcb_init_rust(MdnsIf::Netif0, MdnsIpProtocol::Ip4) {
+        eprintln!("Failed to initialize mDNS PCB: {}", err);
+        return;
+    }
+
+    let query_packet = create_mdns_query();
+    println!("{:?}", query_packet);
+
+    let len = mdns_udp_pcb_write_rust(MdnsIf::Netif0, MdnsIpProtocol::Ip4, ip4, 5353, &query_packet);
+    println!("Bytes sent: {}", len);
+
+    thread::sleep(Duration::from_millis(500));
+
+    if let Err(err) = mdns_pcb_deinit_rust(MdnsIf::Netif0, MdnsIpProtocol::Ip4) {
+        eprintln!("Failed to deinitialize mDNS PCB: {}", err);
+    }
+}
+
+fn main() {
+    // Initialize mDNS
+    mdns::mdns_init();
+
+//     // Query for a specific host
+//     mdns::mdns_query_host_rust("example.local");
+
+    // Deinitialize mDNS
+    mdns::mdns_deinit();
+
+    test_mdns();
+
+//     let result = mdns::mdns_pcb_init_rust(mdns::MdnsIf::Netif0, mdns::MdnsIpProtocol::Ip4);
+//     match result {
+//         Ok(_) => println!("mdns_pcb_init succeeded"),
+//         Err(err) => eprintln!("mdns_pcb_init failed with error code: {}", err),
+//     }
+}
diff --git a/components/mdns/mdns_stub.c b/components/mdns/mdns_stub.c
new file mode 100644
index 0000000000..1bab517f75
--- /dev/null
+++ b/components/mdns/mdns_stub.c
@@ -0,0 +1,76 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include <stdio.h>
+#include "esp_log.h"
+#include "esp_console.h"
+#include "mdns.h"
+
+
+static const char *TAG = "mdns-stub";
+typedef size_t mdns_if_t;
+
+typedef struct {
+    mdns_if_t tcpip_if;
+    mdns_ip_protocol_t ip_protocol;
+    struct pbuf *pb;
+    esp_ip_addr_t src;
+    esp_ip_addr_t dest;
+    uint16_t src_port;
+    uint8_t multicast;
+} mdns_rx_packet_t;
+
+struct pbuf  {
+    struct pbuf *next;
+    void *payload;
+    size_t tot_len;
+    size_t len;
+};
+
+esp_err_t _mdns_pcb_init(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
+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);
+esp_err_t _mdns_pcb_deinit(mdns_if_t tcpip_if, mdns_ip_protocol_t ip_protocol);
+void _mdns_packet_free(mdns_rx_packet_t *packet);
+
+typedef void (*callback_t)(const uint8_t *, size_t len);
+
+static callback_t rust_callback = NULL;
+
+
+
+void set_callback(callback_t callback)
+{
+    rust_callback = callback;
+}
+
+void set_callback2()
+{
+    ESP_LOGI(TAG, "set_callback2!");
+}
+
+
+esp_err_t _mdns_send_rx_action(mdns_rx_packet_t *packet)
+{
+    ESP_LOGI(TAG, "Received packet!");
+    ESP_LOG_BUFFER_HEXDUMP(TAG, packet->pb->payload, packet->pb->tot_len, ESP_LOG_INFO);
+    if (rust_callback) {
+        rust_callback(packet->pb->payload, packet->pb->tot_len);
+    }
+    _mdns_packet_free(packet);
+    return ESP_OK;
+}
+
+esp_netif_t *g_netif = NULL;
+
+
+esp_netif_t *_mdns_get_esp_netif(mdns_if_t tcpip_if)
+{
+    if (g_netif == NULL) {
+        const esp_netif_inherent_config_t base_cg = { .if_key = "WIFI_STA_DEF", .if_desc = CONFIG_TEST_NETIF_NAME };
+        esp_netif_config_t cfg = { .base = &base_cg  };
+        g_netif = esp_netif_new(&cfg);
+    }
+    return g_netif;
+}
diff --git a/components/mdns/src/lib.rs b/components/mdns/src/lib.rs
new file mode 100644
index 0000000000..327cef0226
--- /dev/null
+++ b/components/mdns/src/lib.rs
@@ -0,0 +1,258 @@
+// src/lib.rs
+
+extern crate libc;
+// extern crate trust_dns;
+// use crate trust_dns;
+use std::fmt;
+use std::os::raw::c_char;
+use std::ffi::CStr;
+use std::fmt::Write;  // For formatting strings
+// use trust_dns_client::op::{Message, Query};
+// use trust_dns_client::rr::{RecordType, Name};
+// use trust_dns_client::serialize::binary::{BinDecodable, BinEncoder};
+use std::net::Ipv4Addr;
+use dns_parser::{Builder, QueryClass, QueryType, Packet};
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct EspIp4Addr {
+    pub addr: u32, // IPv4 address as a 32-bit integer
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct EspIp6Addr {
+    pub addr: [u32; 4], // IPv6 address as an array of 4 32-bit integers
+    pub zone: u8,       // Zone ID
+}
+
+#[repr(C)]
+pub union EspIpUnion {
+    pub ip4: EspIp4Addr,
+    pub ip6: EspIp6Addr,
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct EspIpAddr {
+    pub u_addr: EspIpUnion, // Union containing IPv4 or IPv6 address
+    pub addr_type: u8,
+}
+
+// Manual implementation of Debug for the union
+impl fmt::Debug for EspIpUnion {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // Safely access and format the union's members for debugging
+        unsafe {
+            write!(f, "EspIpUnion {{ ip4: {:?}, ip6: {:?} }}", self.ip4, self.ip6)
+        }
+    }
+}
+
+// Manual implementation of Clone for the union
+impl Clone for EspIpUnion {
+    fn clone(&self) -> Self {
+        // Safety: Assuming the union contains valid data in either `ip4` or `ip6`
+        unsafe {
+            EspIpUnion {
+                ip4: self.ip4.clone(),
+            }
+        }
+    }
+}
+
+// Manual implementation of Copy for the union
+impl Copy for EspIpUnion {}
+
+// // Other structs remain the same
+// #[repr(C)]
+// #[derive(Debug, Clone, Copy)]
+// pub struct EspIp4Addr {
+//     addr: u32,
+// }
+//
+// #[repr(C)]
+// #[derive(Debug, Clone, Copy)]
+// pub struct EspIp6Addr {
+//     addr: [u32; 4],
+//     zone: u8,
+// }
+//
+// #[repr(C)]
+// #[derive(Debug, Clone, Copy)]
+// pub struct EspIpAddr {
+//     u_addr: EspIpUnion, // Union containing IPv4 or IPv6 address
+//     addr_type: u8,
+// }
+// Address type definitions
+pub const ESP_IPADDR_TYPE_V4: u8 = 0;
+pub const ESP_IPADDR_TYPE_V6: u8 = 6;
+pub const ESP_IPADDR_TYPE_ANY: u8 = 46;
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub enum MdnsIf {
+    Netif0 = 0,
+    // Add more as needed based on the actual C enum definition
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub enum MdnsIpProtocol {
+    Ip4 = 0,
+    Ip6 = 1,
+}
+
+type EspErr = i32;
+
+extern "C" {
+    fn _mdns_pcb_init(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> EspErr;
+    fn _mdns_udp_pcb_write(
+        tcpip_if: MdnsIf,
+        ip_protocol: MdnsIpProtocol,
+        ip: *const EspIpAddr,
+        port: u16,
+        data: *const u8,
+        len: usize,
+    ) -> usize;
+    fn _mdns_pcb_deinit(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> EspErr;
+    fn set_callback(callback: extern "C" fn(*const u8, usize));
+
+//     fn set_callback2();
+}
+
+extern "C" fn rust_callback(data: *const u8, len: usize)
+{
+    println!("Received len: {}", len);
+    unsafe {
+        // Ensure that the data pointer is valid
+        if !data.is_null() {
+        // Create a Vec<u8> from the raw pointer and length
+        let data_vec = std::slice::from_raw_parts(data, len).to_vec();
+
+        // Now call the safe parser function with the Vec<u8>
+        parse_dns_response(&data_vec);        }
+    }
+}
+
+fn parse_dns_response(data: &[u8]) {
+    // Safe handling of the slice
+    println!("Parsing DNS response with length: {}", data.len());
+
+    parse_dns_response2(data);
+    // Process the data (this will be safe, as `data` is a slice)
+    // Example: You could convert the slice to a string, inspect it, or pass it to a DNS library
+}
+
+fn parse_dns_response2(data: &[u8]) -> Result<(), String> {
+    println!("Parsing DNS response with length 2 : {}", data.len());
+//     use dns_parser::Packet;
+    let packet = Packet::parse(&data).unwrap();
+    for answer in packet.answers {
+        println!("{:?}", answer);
+    }
+//     match Message::from_vec(data) {
+//         Ok(msg) => {
+//             // Successful parsing
+//             println!("Parsed DNS message successfully.");
+//         }
+//         Err(e) => {
+//             // Detailed error message
+//             eprintln!("Error parsing DNS message: {}", e);
+//         }
+//     }
+    // Parse the response message
+//     let msg = Message::from_vec(data).map_err(|e| e.to_string())?;
+//     println!("Type: {}", msg.op_code().to_string());
+//     // Check if the message is a response (opcode is Response)
+//     if msg.op_code() != trust_dns_client::op::OpCode::Status {
+//         return Err("Not a response message".to_string());
+//     }
+//
+//     // Display the answer section (which should contain A record)
+//     for answer in msg.answers() {
+//         println!("Non-IP answer: {:?}", answer);
+//         if let Some(ipv4_addr) = answer.rdata().to_ip_addr() {
+//             println!("Resolved IP address: {}", ipv4_addr);
+//         } else {
+//             println!("Non-IP answer: {:?}", answer);
+//         }
+//     }
+
+    Ok(())
+}
+
+use std::ffi::CString;
+
+
+pub fn mdns_init() {
+    println!("mdns_init called");
+}
+
+pub fn mdns_deinit() {
+    println!("mdns_deinit called");
+}
+
+pub fn create_mdns_query() -> Vec<u8> {
+    let query_name = "david-work.local"; // The domain you want to query
+    let query_type = QueryType::A;       // Type A query for IPv4 address
+    let query_class = QueryClass::IN;    // Class IN (Internet)
+
+    // Create a new query with ID and recursion setting
+    let mut builder = Builder::new_query(12345, true);
+
+    // Add the question for "david-work.local"
+    builder.add_question(query_name, false, query_type, query_class);
+
+    // Build and return the query packet
+    builder.build().unwrap_or_else(|x| x)
+}
+
+pub fn mdns_query_host_rust(name: &str) {
+    let c_name = CString::new(name).expect("Failed to create CString");
+//     unsafe {
+//         mdns_query_host(c_name.as_ptr());
+//     }
+}
+
+pub fn mdns_pcb_init_rust(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> Result<(), EspErr> {
+    unsafe {
+        set_callback(rust_callback);
+//         set_callback2();
+    }
+
+    let err = unsafe { _mdns_pcb_init(tcpip_if, ip_protocol) };
+    if err == 0 {
+        Ok(())
+    } else {
+        Err(err)
+    }
+}
+
+pub fn mdns_udp_pcb_write_rust(
+    tcpip_if: MdnsIf,
+    ip_protocol: MdnsIpProtocol,
+    ip: EspIpAddr,
+    port: u16,
+    data: &[u8],
+) -> usize {
+    unsafe {
+        _mdns_udp_pcb_write(
+            tcpip_if,
+            ip_protocol,
+            &ip as *const EspIpAddr,
+            port,
+            data.as_ptr(),
+            data.len(),
+        )
+    }
+}
+
+pub fn mdns_pcb_deinit_rust(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> Result<(), EspErr> {
+    let err = unsafe { _mdns_pcb_deinit(tcpip_if, ip_protocol) };
+    if err == 0 {
+        Ok(())
+    } else {
+        Err(err)
+    }
+}

From 8f607c6829c8282f52e8e4b7387f279c8c3560be Mon Sep 17 00:00:00 2001
From: David Cermak <cermak@espressif.com>
Date: Wed, 27 Nov 2024 18:27:15 +0100
Subject: [PATCH 3/5] feat(mdns): Add default and ffi build features

---
 .github/workflows/mdns__rust.yml       |   3 +
 components/mdns/CMakeLists.txt         |   1 +
 components/mdns/Cargo.toml             |   6 +
 components/mdns/build.rs               |  17 ++-
 components/mdns/examples/usage.rs      |  25 ++--
 components/mdns/mdns.c                 |   4 +-
 components/mdns/src/lib.rs             | 170 ++++++++++------------
 components/mdns/src/service/ffi.rs     | 190 +++++++++++++++++++++++++
 components/mdns/src/service/mod.rs     |   9 ++
 components/mdns/src/service/native.rs  | 161 +++++++++++++++++++++
 components/mdns/src/service/service.rs |  12 ++
 11 files changed, 494 insertions(+), 104 deletions(-)
 create mode 100644 components/mdns/src/service/ffi.rs
 create mode 100644 components/mdns/src/service/mod.rs
 create mode 100644 components/mdns/src/service/native.rs
 create mode 100644 components/mdns/src/service/service.rs

diff --git a/.github/workflows/mdns__rust.yml b/.github/workflows/mdns__rust.yml
index a32d0ab88c..3124c5c96a 100644
--- a/.github/workflows/mdns__rust.yml
+++ b/.github/workflows/mdns__rust.yml
@@ -31,4 +31,7 @@ jobs:
           cd components/mdns/examples/simple_query/
           idf.py build
           cd ../..
+          # FFI build
           COMPILE_COMMANDS_DIR=examples/simple_query/build/ cargo run --example usage
+          # Default build
+          cargo run --example usage
diff --git a/components/mdns/CMakeLists.txt b/components/mdns/CMakeLists.txt
index 3a3b4c054b..60a13fd709 100644
--- a/components/mdns/CMakeLists.txt
+++ b/components/mdns/CMakeLists.txt
@@ -15,6 +15,7 @@ if(${target} STREQUAL "linux")
     set(dependencies esp_netif_linux esp_event)
     set(private_dependencies esp_timer console esp_system)
     set(srcs "mdns_stub.c" ${MDNS_NETWORKING} ${MDNS_CONSOLE})
+    # set(srcs "mdns.c" ${MDNS_NETWORKING} ${MDNS_CONSOLE})
 else()
     set(dependencies lwip console esp_netif)
     set(private_dependencies esp_timer esp_wifi)
diff --git a/components/mdns/Cargo.toml b/components/mdns/Cargo.toml
index 09a10e7458..420704dca5 100644
--- a/components/mdns/Cargo.toml
+++ b/components/mdns/Cargo.toml
@@ -6,8 +6,14 @@ edition = "2021"
 [dependencies]
 libc = "0.2"
 dns-parser = "0.8"
+socket2 = "*"
+nix = "0.26"
+lazy_static = "*"
 
 [build-dependencies]
 cc = "1.0"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
+
+[features]
+ffi = []
diff --git a/components/mdns/build.rs b/components/mdns/build.rs
index 8647aa0c02..32c6e2893e 100644
--- a/components/mdns/build.rs
+++ b/components/mdns/build.rs
@@ -11,10 +11,23 @@ struct CompileCommand {
 }
 
 fn main() {
+
+    println!("cargo:rerun-if-env-changed=COMPILE_COMMANDS_DIR");
     // Get the directory for compile_commands.json from an environment variable
-    let compile_commands_dir = env::var("COMPILE_COMMANDS_DIR")
-        .unwrap_or_else(|_| ".".to_string()); // Default to current directory
+    let compile_commands_dir = match env::var("COMPILE_COMMANDS_DIR") {
+        Ok(dir) => dir,
+        Err(_) => {
+            // If the environment variable is not defined, return early
+            println!("COMPILE_COMMANDS_DIR not set, skipping custom build.");
+            // this is a native build
+            // println!("cargo:rustc-cfg=native");
+            return;
+        }
+    };
 
+    // building with FFI of mdns_networking
+    println!("COMPILE_COMMANDS_DIR set, enabling FFI feature.");
+    println!("cargo:rustc-cfg=feature=\"ffi\"");
     // Construct the path to the compile_commands.json file
     let compile_commands_path = Path::new(&compile_commands_dir).join("compile_commands.json");
 
diff --git a/components/mdns/examples/usage.rs b/components/mdns/examples/usage.rs
index 68edcfd6c0..e82d998441 100644
--- a/components/mdns/examples/usage.rs
+++ b/components/mdns/examples/usage.rs
@@ -3,12 +3,16 @@
 use mdns::*;
 use std::thread;
 use std::time::Duration;
+use std::net::{UdpSocket, Ipv4Addr};
+use socket2::{Socket, Domain, Type, Protocol};
 
-pub fn test_mdns() {
+
+
+fn test_mdns() {
     let ip4 = EspIpAddr {
         u_addr: EspIpUnion {
             ip4: EspIp4Addr {
-                addr: u32::from_le_bytes([224, 0, 0, 251]), // Convert 224.0.0.251 to big-endian
+                addr: u32::from_le_bytes([224, 0, 0, 251]),
             },
         },
         addr_type: ESP_IPADDR_TYPE_V4,
@@ -25,11 +29,13 @@ pub fn test_mdns() {
         return;
     }
 
-    let query_packet = create_mdns_query();
-    println!("{:?}", query_packet);
+    // let query_packet = create_mdns_query();
+    // println!("{:?}", query_packet);
+
+
 
-    let len = mdns_udp_pcb_write_rust(MdnsIf::Netif0, MdnsIpProtocol::Ip4, ip4, 5353, &query_packet);
-    println!("Bytes sent: {}", len);
+    // let len = mdns_udp_pcb_write_rust(MdnsIf::Netif0, MdnsIpProtocol::Ip4, ip4, 5353, &query_packet);
+    // println!("Bytes sent: {}", len);
 
     thread::sleep(Duration::from_millis(500));
 
@@ -38,17 +44,20 @@ pub fn test_mdns() {
     }
 }
 
+
 fn main() {
     // Initialize mDNS
     mdns::mdns_init();
 
 //     // Query for a specific host
 //     mdns::mdns_query_host_rust("example.local");
-
+    mdns::mdns_query("david-work.local");
+    thread::sleep(Duration::from_millis(500));
     // Deinitialize mDNS
     mdns::mdns_deinit();
 
-    test_mdns();
+
+    // test_mdns();
 
 //     let result = mdns::mdns_pcb_init_rust(mdns::MdnsIf::Netif0, mdns::MdnsIpProtocol::Ip4);
 //     match result {
diff --git a/components/mdns/mdns.c b/components/mdns/mdns.c
index ca9d87ddbe..c59648ee76 100644
--- a/components/mdns/mdns.c
+++ b/components/mdns/mdns.c
@@ -176,7 +176,7 @@ static inline esp_netif_t *esp_netif_from_preset_if(mdns_predef_if_t predef_if)
  * @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_get_esp_netif23(mdns_if_t tcpip_if)
+esp_netif_t *_mdns_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) {
@@ -347,7 +347,7 @@ static bool _mdns_can_add_more_services(void)
     return true;
 }
 
-esp_err_t _mdns_send_rx_action23(mdns_rx_packet_t *packet)
+esp_err_t _mdns_send_rx_action(mdns_rx_packet_t *packet)
 {
     mdns_action_t *action = NULL;
 
diff --git a/components/mdns/src/lib.rs b/components/mdns/src/lib.rs
index 327cef0226..4e30c53ac6 100644
--- a/components/mdns/src/lib.rs
+++ b/components/mdns/src/lib.rs
@@ -1,4 +1,6 @@
 // src/lib.rs
+mod service;
+use service::{Service, NativeService, CService};
 
 extern crate libc;
 // extern crate trust_dns;
@@ -13,6 +15,29 @@ use std::fmt::Write;  // For formatting strings
 use std::net::Ipv4Addr;
 use dns_parser::{Builder, QueryClass, QueryType, Packet};
 
+
+
+#[cfg(not(feature = "ffi"))]
+fn build_info() {
+    println!("Default build");
+}
+
+#[cfg(not(feature = "ffi"))]
+fn create_service(cb: fn(&[u8])) -> Box<dyn Service> {
+    NativeService::init(cb)
+}
+
+
+#[cfg(feature = "ffi")]
+fn build_info() {
+    println!("FFI build");
+}
+
+#[cfg(feature = "ffi")]
+fn create_service(cb: fn(&[u8])) -> Box<dyn Service> {
+    CService::init(cb)
+}
+
 #[repr(C)]
 #[derive(Debug, Clone, Copy)]
 pub struct EspIp4Addr {
@@ -64,26 +89,6 @@ impl Clone for EspIpUnion {
 // Manual implementation of Copy for the union
 impl Copy for EspIpUnion {}
 
-// // Other structs remain the same
-// #[repr(C)]
-// #[derive(Debug, Clone, Copy)]
-// pub struct EspIp4Addr {
-//     addr: u32,
-// }
-//
-// #[repr(C)]
-// #[derive(Debug, Clone, Copy)]
-// pub struct EspIp6Addr {
-//     addr: [u32; 4],
-//     zone: u8,
-// }
-//
-// #[repr(C)]
-// #[derive(Debug, Clone, Copy)]
-// pub struct EspIpAddr {
-//     u_addr: EspIpUnion, // Union containing IPv4 or IPv6 address
-//     addr_type: u8,
-// }
 // Address type definitions
 pub const ESP_IPADDR_TYPE_V4: u8 = 0;
 pub const ESP_IPADDR_TYPE_V6: u8 = 6;
@@ -117,8 +122,6 @@ extern "C" {
     ) -> usize;
     fn _mdns_pcb_deinit(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> EspErr;
     fn set_callback(callback: extern "C" fn(*const u8, usize));
-
-//     fn set_callback2();
 }
 
 extern "C" fn rust_callback(data: *const u8, len: usize)
@@ -127,92 +130,92 @@ extern "C" fn rust_callback(data: *const u8, len: usize)
     unsafe {
         // Ensure that the data pointer is valid
         if !data.is_null() {
-        // Create a Vec<u8> from the raw pointer and length
-        let data_vec = std::slice::from_raw_parts(data, len).to_vec();
+            // Create a Vec<u8> from the raw pointer and length
+            let data_vec = std::slice::from_raw_parts(data, len).to_vec();
 
-        // Now call the safe parser function with the Vec<u8>
-        parse_dns_response(&data_vec);        }
+            // Now call the safe parser function with the Vec<u8>
+            parse_dns_response(&data_vec).unwrap();
+        }
     }
 }
 
-fn parse_dns_response(data: &[u8]) {
-    // Safe handling of the slice
-    println!("Parsing DNS response with length: {}", data.len());
-
-    parse_dns_response2(data);
-    // Process the data (this will be safe, as `data` is a slice)
-    // Example: You could convert the slice to a string, inspect it, or pass it to a DNS library
-}
-
-fn parse_dns_response2(data: &[u8]) -> Result<(), String> {
+fn parse_dns_response(data: &[u8]) -> Result<(), String> {
     println!("Parsing DNS response with length 2 : {}", data.len());
-//     use dns_parser::Packet;
     let packet = Packet::parse(&data).unwrap();
     for answer in packet.answers {
         println!("{:?}", answer);
     }
-//     match Message::from_vec(data) {
-//         Ok(msg) => {
-//             // Successful parsing
-//             println!("Parsed DNS message successfully.");
-//         }
-//         Err(e) => {
-//             // Detailed error message
-//             eprintln!("Error parsing DNS message: {}", e);
-//         }
-//     }
-    // Parse the response message
-//     let msg = Message::from_vec(data).map_err(|e| e.to_string())?;
-//     println!("Type: {}", msg.op_code().to_string());
-//     // Check if the message is a response (opcode is Response)
-//     if msg.op_code() != trust_dns_client::op::OpCode::Status {
-//         return Err("Not a response message".to_string());
-//     }
-//
-//     // Display the answer section (which should contain A record)
-//     for answer in msg.answers() {
-//         println!("Non-IP answer: {:?}", answer);
-//         if let Some(ipv4_addr) = answer.rdata().to_ip_addr() {
-//             println!("Resolved IP address: {}", ipv4_addr);
-//         } else {
-//             println!("Non-IP answer: {:?}", answer);
-//         }
-//     }
-
+    for question in packet.questions {
+        println!("{:?}", question);
+    }
     Ok(())
 }
 
 use std::ffi::CString;
+use std::thread;
+use std::time::Duration;
+use lazy_static::lazy_static;
+use std::sync::{Arc, Mutex};
 
+lazy_static! {
+    static ref SERVICE: Arc<Mutex<Option<Box<dyn Service>>>> = Arc::new(Mutex::new(None));
+}
+
+fn read_cb(vec: &[u8]) {
+    println!("Received {:?}", vec);
+    parse_dns_response(vec).unwrap();
+}
 
 pub fn mdns_init() {
+    build_info();
+    let mut service_guard = SERVICE.lock().unwrap();
+    if service_guard.is_none() {
+        // Initialize the service only if it hasn't been initialized
+        *service_guard = Some(create_service(read_cb));
+    }
+    // let service: Box<dyn Service> = create_service(read_cb);
+    // service.action1();
+    // let packet: [u8; 34] = [
+    //     0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x64, 0x61,
+    //     0x76, 0x69, 0x64, 0x2d, 0x77, 0x6f, 0x72, 0x6b, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00,
+    //     0x00, 0x01, 0x00, 0x01,
+    // ];
+    // service.send(packet.to_vec());
+    // thread::sleep(Duration::from_millis(500));
+    // service.deinit();
     println!("mdns_init called");
 }
 
 pub fn mdns_deinit() {
+    let mut service_guard = SERVICE.lock().unwrap();
+    if let Some(service) = service_guard.take() {
+        service.deinit();
+    }
     println!("mdns_deinit called");
 }
 
-pub fn create_mdns_query() -> Vec<u8> {
-    let query_name = "david-work.local"; // The domain you want to query
+fn create_a_query(name: &str) -> Vec<u8> {
     let query_type = QueryType::A;       // Type A query for IPv4 address
     let query_class = QueryClass::IN;    // Class IN (Internet)
 
     // Create a new query with ID and recursion setting
-    let mut builder = Builder::new_query(12345, true);
+    let mut builder = Builder::new_query(0x5555, true);
 
     // Add the question for "david-work.local"
-    builder.add_question(query_name, false, query_type, query_class);
+    builder.add_question(name, false, query_type, query_class);
 
     // Build and return the query packet
     builder.build().unwrap_or_else(|x| x)
 }
 
-pub fn mdns_query_host_rust(name: &str) {
-    let c_name = CString::new(name).expect("Failed to create CString");
-//     unsafe {
-//         mdns_query_host(c_name.as_ptr());
-//     }
+pub fn mdns_query(name: &str) {
+    let service_guard = SERVICE.lock().unwrap();
+    if let Some(service) = &*service_guard {
+        let packet = create_a_query(name);
+        service.send(packet);
+    } else {
+        println!("Service not initialized");
+    }
 }
 
 pub fn mdns_pcb_init_rust(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> Result<(), EspErr> {
@@ -229,24 +232,7 @@ pub fn mdns_pcb_init_rust(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> Resu
     }
 }
 
-pub fn mdns_udp_pcb_write_rust(
-    tcpip_if: MdnsIf,
-    ip_protocol: MdnsIpProtocol,
-    ip: EspIpAddr,
-    port: u16,
-    data: &[u8],
-) -> usize {
-    unsafe {
-        _mdns_udp_pcb_write(
-            tcpip_if,
-            ip_protocol,
-            &ip as *const EspIpAddr,
-            port,
-            data.as_ptr(),
-            data.len(),
-        )
-    }
-}
+
 
 pub fn mdns_pcb_deinit_rust(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> Result<(), EspErr> {
     let err = unsafe { _mdns_pcb_deinit(tcpip_if, ip_protocol) };
diff --git a/components/mdns/src/service/ffi.rs b/components/mdns/src/service/ffi.rs
new file mode 100644
index 0000000000..d9f1390fbc
--- /dev/null
+++ b/components/mdns/src/service/ffi.rs
@@ -0,0 +1,190 @@
+use libc::AT_NULL;
+
+use super::service::Service;
+use std::fmt;
+
+pub struct CService
+{
+    callback: fn(&[u8])
+}
+
+impl CService {
+    fn new(cb: fn(&[u8])) -> Self {
+        CService {
+            callback: cb
+        }
+    }
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct EspIp4Addr {
+    pub addr: u32, // IPv4 address as a 32-bit integer
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct EspIp6Addr {
+    pub addr: [u32; 4], // IPv6 address as an array of 4 32-bit integers
+    pub zone: u8,       // Zone ID
+}
+
+#[repr(C)]
+pub union EspIpUnion {
+    pub ip4: EspIp4Addr,
+    pub ip6: EspIp6Addr,
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct EspIpAddr {
+    pub u_addr: EspIpUnion, // Union containing IPv4 or IPv6 address
+    pub addr_type: u8,
+}
+
+// Manual implementation of Debug for the union
+impl fmt::Debug for EspIpUnion {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // Safely access and format the union's members for debugging
+        unsafe {
+            write!(f, "EspIpUnion {{ ip4: {:?}, ip6: {:?} }}", self.ip4, self.ip6)
+        }
+    }
+}
+
+// Manual implementation of Clone for the union
+impl Clone for EspIpUnion {
+    fn clone(&self) -> Self {
+        // Safety: Assuming the union contains valid data in either `ip4` or `ip6`
+        unsafe {
+            EspIpUnion {
+                ip4: self.ip4.clone(),
+            }
+        }
+    }
+}
+
+// Manual implementation of Copy for the union
+impl Copy for EspIpUnion {}
+
+// Address type definitions
+pub const ESP_IPADDR_TYPE_V4: u8 = 0;
+pub const ESP_IPADDR_TYPE_V6: u8 = 6;
+pub const ESP_IPADDR_TYPE_ANY: u8 = 46;
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub enum MdnsIf {
+    Netif0 = 0,
+    // Add more as needed based on the actual C enum definition
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub enum MdnsIpProtocol {
+    Ip4 = 0,
+    Ip6 = 1,
+}
+
+type EspErr = i32;
+
+extern "C" {
+    fn _mdns_pcb_init(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> EspErr;
+    fn _mdns_udp_pcb_write(
+        tcpip_if: MdnsIf,
+        ip_protocol: MdnsIpProtocol,
+        ip: *const EspIpAddr,
+        port: u16,
+        data: *const u8,
+        len: usize,
+    ) -> usize;
+    fn _mdns_pcb_deinit(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> EspErr;
+    fn set_callback(callback: extern "C" fn(*const u8, usize));
+}
+
+static mut CALLBACK: Option<fn(&[u8])> = None;
+
+extern "C" fn rust_callback(data: *const u8, len: usize)
+{
+    println!("Received len: {}", len);
+    unsafe {
+        // Ensure that the data pointer is valid
+        if !data.is_null() {
+            // Create a Vec<u8> from the raw pointer and length
+            let data_vec = std::slice::from_raw_parts(data, len).to_vec();
+            if let Some(cb) = CALLBACK {
+                cb(&data_vec);
+            }
+            // Now call the safe parser function with the Vec<u8>
+            // parse_dns_response(&data_vec);
+        }
+    }
+}
+
+
+pub fn mdns_udp_pcb_write_rust(
+    tcpip_if: MdnsIf,
+    ip_protocol: MdnsIpProtocol,
+    ip: EspIpAddr,
+    port: u16,
+    data: &[u8],
+) -> usize {
+    unsafe {
+        _mdns_udp_pcb_write(
+            tcpip_if,
+            ip_protocol,
+            &ip as *const EspIpAddr,
+            port,
+            data.as_ptr(),
+            data.len(),
+        )
+    }
+}
+
+impl Service for CService {
+
+
+    fn init(cb:fn(&[u8])) -> Box<Self> {
+        println!("FfiHousekeeper: Initializing.");
+        unsafe {
+            set_callback(rust_callback);
+            CALLBACK = Some(cb);
+        }
+        let _ = unsafe { _mdns_pcb_init(MdnsIf::Netif0, MdnsIpProtocol::Ip4) };
+
+
+        Box::new(CService::new(cb))
+    }
+
+    fn send(&self, packet: Vec<u8>) {
+        let ip4 = EspIpAddr {
+            u_addr: EspIpUnion {
+                ip4: EspIp4Addr {
+                    addr: u32::from_le_bytes([224, 0, 0, 251]),
+                },
+            },
+            addr_type: ESP_IPADDR_TYPE_V4,
+        };
+        println!("FfiHousekeeper: Sending");
+        let len = mdns_udp_pcb_write_rust(MdnsIf::Netif0, MdnsIpProtocol::Ip4, ip4, 5353, &packet);
+        println!("Bytes sent: {}", len);
+    }
+
+    fn action1(&self) {
+        println!("FfiHousekeeper: Performing Action1.");
+    }
+
+    fn action2(&self) {
+        println!("FfiHousekeeper: Performing Action2.");
+    }
+
+    fn action3(&self) {
+        println!("FfiHousekeeper: Performing Action3.");
+    }
+
+    fn deinit(self: Box<Self>) {
+        let _ = unsafe { _mdns_pcb_deinit(MdnsIf::Netif0, MdnsIpProtocol::Ip4) };
+        println!("FfiHousekeeper: Deinitializing.");
+    }
+
+}
diff --git a/components/mdns/src/service/mod.rs b/components/mdns/src/service/mod.rs
new file mode 100644
index 0000000000..9d64845a64
--- /dev/null
+++ b/components/mdns/src/service/mod.rs
@@ -0,0 +1,9 @@
+mod service; // Import the trait definition
+mod native; // Thread-based implementation
+mod ffi; // FFI-based implementation
+
+pub use service::Service; // Expose the trait
+pub use native::NativeService;
+pub use ffi::CService;
+// pub use thread::ThreadHousekeeper; // Expose the thread-based implementation
+// pub use ffi::FfiHousekeeper; // Expose the FFI-based implementation
diff --git a/components/mdns/src/service/native.rs b/components/mdns/src/service/native.rs
new file mode 100644
index 0000000000..04c9ebfb24
--- /dev/null
+++ b/components/mdns/src/service/native.rs
@@ -0,0 +1,161 @@
+use super::service::Service;
+use std::thread;
+
+use std::net::{UdpSocket, Ipv4Addr};
+use dns_parser::rdata::null;
+use socket2::{Socket, Domain, Type, Protocol};
+use nix::unistd::{pipe, read, write, close};
+use nix::sys::select::{select, FdSet};
+use nix::sys::time::TimeVal;
+use std::os::fd::{AsRawFd, FromRawFd};
+use std::mem::MaybeUninit;
+
+enum Action {
+    Action1,
+    Action2,
+    Action3,
+}
+
+pub struct NativeService
+{
+    handle: Option<thread::JoinHandle<()>>,
+    socket: UdpSocket,
+    write_fd: i32,
+    callback: fn(&[u8]),
+}
+
+fn create_multicast_socket() -> UdpSocket {
+    let addr: std::net::SocketAddr = "0.0.0.0:5353".parse().unwrap();
+
+    let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP)).unwrap();
+    socket.set_reuse_address(true).unwrap();
+    socket.bind(&addr.into()).unwrap();
+
+    let multicast_addr = Ipv4Addr::new(224, 0, 0, 251);
+    let interface = Ipv4Addr::new(0, 0, 0, 0);
+    socket.join_multicast_v4(&multicast_addr, &interface).unwrap();
+
+    socket.into()
+}
+
+impl NativeService
+{
+    fn new(cb: fn(&[u8])) -> Self {
+        let socket = create_multicast_socket();
+        let (read_fd, w_fd) = pipe().expect("Failed to create pipe");
+        let local_cb = cb;
+        let local_socket = socket.try_clone().unwrap();
+
+        let handle = thread::spawn(move || {
+
+            loop {
+                let socket_fd = local_socket.as_raw_fd();
+                let mut read_fds = FdSet::new();
+                read_fds.insert(socket_fd);
+                read_fds.insert(read_fd);
+
+
+                let mut timeout = TimeVal::new(0, 100_000);
+
+                match select(read_fd.max(socket_fd) + 1, Some(&mut read_fds), None, None, Some(&mut timeout)) {
+                    Ok(0) => println!("ThreadHousekeeper: Performing housekeeping tasks"),
+                    Ok(_) => {
+                        if read_fds.contains(socket_fd) {
+                            // let mut buf: [MaybeUninit<u8>; 1500] = unsafe { MaybeUninit::uninit().assume_init() };
+                            // let mut buf: [u8; 1500];
+                            let mut buf = vec![0u8; 1500]; // Create a buffer using a vector
+                            // let sock =  unsafe { Socket::from_raw_fd(socket_fd) };
+                            match local_socket.recv_from(&mut buf) {
+                                Ok((size, addr)) => {
+                                    // Convert the buffer from MaybeUninit to a regular slice of u8
+                                    // let buf = unsafe {
+                                    //     std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, size)
+                                    // };
+                                    // println!("Received {} bytes from {:?}: {:?}", size, addr, buf);
+                                    local_cb(&buf[..size]);
+                                }
+                                Err(e) => println!("Error reading from socket: {:?}", e),
+                            }
+                        }
+
+                        if read_fds.contains(read_fd) {
+                            let mut buf = [0u8; 10];
+                            match read(read_fd, &mut buf) {
+                                Ok(size) => {
+                                    println!("{}", size);
+                                    if size == 0 {
+                                        break;
+                                    }
+                                }
+                                Err(e) => println!("Error reading from socket: {:?}", e),
+                            }
+                        }
+                    }
+                    Err(e) => {
+                        println!("Error in select(): {:?}", e);
+                        break;
+                    }
+                }
+            }
+
+            // close(read_fd).expect("Failed to close read end of pipe");
+        });
+
+        NativeService {
+            handle: Some(handle),
+            socket,
+            write_fd: w_fd,
+            callback: cb
+        }
+    }
+}
+
+impl Service for NativeService {
+
+    fn init(cb: fn(&[u8])) -> Box<Self> {
+        println!("Native: Initializing.");
+        Box::new(NativeService::new(cb))
+    }
+
+    fn send(&self, packet: Vec<u8>) {
+        let destination: std::net::SocketAddr = "224.0.0.251:5353".parse().unwrap();
+        self.socket.send_to(&packet, &destination)
+                    .expect("Failed to send mDNS query");
+    }
+
+    fn action1(&self) {
+        let buf: [u8; 1] = [0x01];
+        // self.socket.send(&buf).unwrap();
+        write(self.write_fd, &buf).unwrap();
+        // if let Some(tx) = &self.tx {
+        //     tx.send(Action::Action1).unwrap();
+        // }
+    }
+
+    fn action2(&self) {
+        // if let Some(tx) = &self.tx {
+        //     tx.send(Action::Action2).unwrap();
+        // }
+    }
+
+    fn action3(&self) {
+        // if let Some(tx) = &self.tx {
+        //     tx.send(Action::Action3).unwrap();
+        // }
+    }
+
+    fn deinit(self: Box<Self>) {
+        println!("DEINIT called");
+        // if let Some(tx) = self.tx {
+        //     drop(tx);
+        // }
+        close(self.write_fd).unwrap();
+
+        if let Some(handle) = self.handle {
+            handle.join().unwrap();
+            println!("NativeService: Deinitialized");
+        }
+        println!("DEINIT done...");
+
+    }
+}
diff --git a/components/mdns/src/service/service.rs b/components/mdns/src/service/service.rs
new file mode 100644
index 0000000000..8653611a1c
--- /dev/null
+++ b/components/mdns/src/service/service.rs
@@ -0,0 +1,12 @@
+pub trait Service: Send + Sync {
+    fn init(cb: fn(&[u8])) -> Box<Self>
+    where
+        Self: Sized;
+
+    fn send(&self, packet: Vec<u8>);
+    fn action1(&self);
+    fn action2(&self);
+    fn action3(&self);
+
+    fn deinit(self: Box<Self>);
+}

From 180510343bfbb538b40405a175dba7b67f0fa68c Mon Sep 17 00:00:00 2001
From: David Cermak <cermak@espressif.com>
Date: Fri, 6 Dec 2024 08:57:09 +0100
Subject: [PATCH 4/5] feat(mdns): Start implementing querier

---
 components/mdns/examples/usage.rs     |  57 +-----
 components/mdns/src/lib.rs            | 273 +++++++++++---------------
 components/mdns/src/service/native.rs |   4 +-
 3 files changed, 124 insertions(+), 210 deletions(-)

diff --git a/components/mdns/examples/usage.rs b/components/mdns/examples/usage.rs
index e82d998441..49fa9e9775 100644
--- a/components/mdns/examples/usage.rs
+++ b/components/mdns/examples/usage.rs
@@ -3,65 +3,14 @@
 use mdns::*;
 use std::thread;
 use std::time::Duration;
-use std::net::{UdpSocket, Ipv4Addr};
-use socket2::{Socket, Domain, Type, Protocol};
-
-
-
-fn test_mdns() {
-    let ip4 = EspIpAddr {
-        u_addr: EspIpUnion {
-            ip4: EspIp4Addr {
-                addr: u32::from_le_bytes([224, 0, 0, 251]),
-            },
-        },
-        addr_type: ESP_IPADDR_TYPE_V4,
-    };
-
-//     let query_packet: [u8; 34] = [
-//         0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x64, 0x61,
-//         0x76, 0x69, 0x64, 0x2d, 0x77, 0x6f, 0x72, 0x6b, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00,
-//         0x00, 0x01, 0x00, 0x01,
-//     ];
-
-    if let Err(err) = mdns_pcb_init_rust(MdnsIf::Netif0, MdnsIpProtocol::Ip4) {
-        eprintln!("Failed to initialize mDNS PCB: {}", err);
-        return;
-    }
-
-    // let query_packet = create_mdns_query();
-    // println!("{:?}", query_packet);
-
-
-
-    // let len = mdns_udp_pcb_write_rust(MdnsIf::Netif0, MdnsIpProtocol::Ip4, ip4, 5353, &query_packet);
-    // println!("Bytes sent: {}", len);
-
-    thread::sleep(Duration::from_millis(500));
-
-    if let Err(err) = mdns_pcb_deinit_rust(MdnsIf::Netif0, MdnsIpProtocol::Ip4) {
-        eprintln!("Failed to deinitialize mDNS PCB: {}", err);
-    }
-}
 
 
 fn main() {
     // Initialize mDNS
-    mdns::mdns_init();
+    mdns_init();
 
-//     // Query for a specific host
-//     mdns::mdns_query_host_rust("example.local");
-    mdns::mdns_query("david-work.local");
+    mdns_query("david-work.local");
     thread::sleep(Duration::from_millis(500));
     // Deinitialize mDNS
-    mdns::mdns_deinit();
-
-
-    // test_mdns();
-
-//     let result = mdns::mdns_pcb_init_rust(mdns::MdnsIf::Netif0, mdns::MdnsIpProtocol::Ip4);
-//     match result {
-//         Ok(_) => println!("mdns_pcb_init succeeded"),
-//         Err(err) => eprintln!("mdns_pcb_init failed with error code: {}", err),
-//     }
+    mdns_deinit();
 }
diff --git a/components/mdns/src/lib.rs b/components/mdns/src/lib.rs
index 4e30c53ac6..4b8c5e6e2e 100644
--- a/components/mdns/src/lib.rs
+++ b/components/mdns/src/lib.rs
@@ -1,20 +1,10 @@
 // src/lib.rs
 mod service;
 use service::{Service, NativeService, CService};
-
-extern crate libc;
-// extern crate trust_dns;
-// use crate trust_dns;
-use std::fmt;
-use std::os::raw::c_char;
-use std::ffi::CStr;
-use std::fmt::Write;  // For formatting strings
-// use trust_dns_client::op::{Message, Query};
-// use trust_dns_client::rr::{RecordType, Name};
-// use trust_dns_client::serialize::binary::{BinDecodable, BinEncoder};
-use std::net::Ipv4Addr;
+use lazy_static::lazy_static;
+use std::sync::{Arc, Mutex};
 use dns_parser::{Builder, QueryClass, QueryType, Packet};
-
+use std::time::{Duration, Instant};
 
 
 #[cfg(not(feature = "ffi"))]
@@ -27,7 +17,6 @@ fn create_service(cb: fn(&[u8])) -> Box<dyn Service> {
     NativeService::init(cb)
 }
 
-
 #[cfg(feature = "ffi")]
 fn build_info() {
     println!("FFI build");
@@ -38,127 +27,135 @@ fn create_service(cb: fn(&[u8])) -> Box<dyn Service> {
     CService::init(cb)
 }
 
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-pub struct EspIp4Addr {
-    pub addr: u32, // IPv4 address as a 32-bit integer
+fn parse_dns_response(data: &[u8]) -> Result<(), String> {
+    println!("Parsing DNS response with length 2 : {}", data.len());
+    let packet = Packet::parse(&data).unwrap();
+    for answer in packet.answers {
+        println!("{:?}", answer);
+    }
+    for question in packet.questions {
+        println!("{:?}", question);
+    }
+    Ok(())
 }
 
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-pub struct EspIp6Addr {
-    pub addr: [u32; 4], // IPv6 address as an array of 4 32-bit integers
-    pub zone: u8,       // Zone ID
-}
 
-#[repr(C)]
-pub union EspIpUnion {
-    pub ip4: EspIp4Addr,
-    pub ip6: EspIp6Addr,
+// pub trait Querier: Send + Sync {
+//     fn init() -> Box<Self>
+//     where
+//         Self: Sized;
+
+//     fn deinit(self: Box<Self>);
+// }
+
+
+#[derive(Debug)]
+pub struct Query {
+    pub name: String,
+    pub service: String,
+    pub proto: String,
+    pub query_type: QueryType,
+    pub unicast: bool,
+    pub timeout: Duration,
+    pub added_at: Instant, // To track when the query was added
 }
 
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-pub struct EspIpAddr {
-    pub u_addr: EspIpUnion, // Union containing IPv4 or IPv6 address
-    pub addr_type: u8,
+pub struct Querier {
+    queries: Vec<Query>,
 }
 
-// Manual implementation of Debug for the union
-impl fmt::Debug for EspIpUnion {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // Safely access and format the union's members for debugging
-        unsafe {
-            write!(f, "EspIpUnion {{ ip4: {:?}, ip6: {:?} }}", self.ip4, self.ip6)
-        }
-    }
+fn create_querier() -> Box<dyn Querier> {
+    NativeService::init(cb)
 }
 
-// Manual implementation of Clone for the union
-impl Clone for EspIpUnion {
-    fn clone(&self) -> Self {
-        // Safety: Assuming the union contains valid data in either `ip4` or `ip6`
-        unsafe {
-            EspIpUnion {
-                ip4: self.ip4.clone(),
-            }
+impl Querier {
+    pub fn new() -> Self {
+        Self {
+            queries: Vec::new(),
         }
     }
-}
 
-// Manual implementation of Copy for the union
-impl Copy for EspIpUnion {}
-
-// Address type definitions
-pub const ESP_IPADDR_TYPE_V4: u8 = 0;
-pub const ESP_IPADDR_TYPE_V6: u8 = 6;
-pub const ESP_IPADDR_TYPE_ANY: u8 = 46;
-
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-pub enum MdnsIf {
-    Netif0 = 0,
-    // Add more as needed based on the actual C enum definition
-}
-
-#[repr(C)]
-#[derive(Debug, Clone, Copy)]
-pub enum MdnsIpProtocol {
-    Ip4 = 0,
-    Ip6 = 1,
-}
+    pub fn init(&mut self) {
+        println!("Querier initialized");
+    }
 
-type EspErr = i32;
+    pub fn deinit(&mut self) {
+        self.queries.clear();
+        println!("Querier deinitialized");
+    }
 
-extern "C" {
-    fn _mdns_pcb_init(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> EspErr;
-    fn _mdns_udp_pcb_write(
-        tcpip_if: MdnsIf,
-        ip_protocol: MdnsIpProtocol,
-        ip: *const EspIpAddr,
-        port: u16,
-        data: *const u8,
-        len: usize,
-    ) -> usize;
-    fn _mdns_pcb_deinit(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> EspErr;
-    fn set_callback(callback: extern "C" fn(*const u8, usize));
-}
+    pub fn add(
+        &mut self,
+        name: String,
+        service: String,
+        proto: String,
+        query_type: QueryType,
+        unicast: bool,
+        timeout: Duration,
+        // semaphore: Option<tokio::sync::Semaphore>,
+    ) -> usize {
+        let query = Query {
+            name,
+            service,
+            proto,
+            query_type,
+            unicast,
+            timeout,
+            // semaphore,
+            added_at: Instant::now(),
+        };
+        self.queries.push(query);
+        self.queries.len() - 1 // Return the ID (index of the query)
+    }
 
-extern "C" fn rust_callback(data: *const u8, len: usize)
-{
-    println!("Received len: {}", len);
-    unsafe {
-        // Ensure that the data pointer is valid
-        if !data.is_null() {
-            // Create a Vec<u8> from the raw pointer and length
-            let data_vec = std::slice::from_raw_parts(data, len).to_vec();
+    pub fn process(&mut self) {
+        let now = Instant::now();
+        self.queries.retain(|query| {
+            let elapsed = now.duration_since(query.added_at);
+            if elapsed > query.timeout {
+                println!("Query timed out: {:?}", query);
+                // if let Some(semaphore) = &query.semaphore {
+                //     semaphore.add_permits(1); // Release semaphore if waiting
+                // }
+                false // Remove the query
+            } else {
+                println!("Processing query: {:?}", query);
+                // Implement retry logic here if needed
+                true // Keep the query
+            }
+        });
+    }
 
-            // Now call the safe parser function with the Vec<u8>
-            parse_dns_response(&data_vec).unwrap();
+    pub async fn wait(&mut self, id: usize) -> Result<(), &'static str> {
+        if let Some(query) = self.queries.get_mut(id) {
+            Ok(())
+            // if let Some(semaphore) = &query.semaphore {
+            //     semaphore.acquire().await.unwrap(); // Block until the semaphore is released
+            //     Ok(())
+            // } else {
+            //     Err("No semaphore set for this query")
+            // }
+        } else {
+            Err("Invalid query ID")
         }
     }
 }
 
-fn parse_dns_response(data: &[u8]) -> Result<(), String> {
-    println!("Parsing DNS response with length 2 : {}", data.len());
-    let packet = Packet::parse(&data).unwrap();
-    for answer in packet.answers {
-        println!("{:?}", answer);
-    }
-    for question in packet.questions {
-        println!("{:?}", question);
-    }
-    Ok(())
+struct Objects {
+    service: Option<Box<dyn Service>>,
+    // responder: Option<Box<dyn Responder>>,
+    querier: Option<Querier>,
 }
 
-use std::ffi::CString;
-use std::thread;
-use std::time::Duration;
-use lazy_static::lazy_static;
-use std::sync::{Arc, Mutex};
+// lazy_static! {
+//     static ref SERVICE: Arc<Mutex<Option<Box<dyn Service>>>> = Arc::new(Mutex::new(None));
+// }
 
 lazy_static! {
-    static ref SERVICE: Arc<Mutex<Option<Box<dyn Service>>>> = Arc::new(Mutex::new(None));
+    static ref SERVER: Arc<Mutex<Objects>> = Arc::new(Mutex::new(Objects {
+        service: None,
+        querier: None,
+    }));
 }
 
 fn read_cb(vec: &[u8]) {
@@ -168,27 +165,22 @@ fn read_cb(vec: &[u8]) {
 
 pub fn mdns_init() {
     build_info();
-    let mut service_guard = SERVICE.lock().unwrap();
-    if service_guard.is_none() {
+    let mut service_guard = SERVER.lock().unwrap();
+    if service_guard.service.is_none() {
         // Initialize the service only if it hasn't been initialized
-        *service_guard = Some(create_service(read_cb));
+        service_guard.service = Some(create_service(read_cb));
     }
-    // let service: Box<dyn Service> = create_service(read_cb);
-    // service.action1();
-    // let packet: [u8; 34] = [
-    //     0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x64, 0x61,
-    //     0x76, 0x69, 0x64, 0x2d, 0x77, 0x6f, 0x72, 0x6b, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00,
-    //     0x00, 0x01, 0x00, 0x01,
-    // ];
-    // service.send(packet.to_vec());
-    // thread::sleep(Duration::from_millis(500));
-    // service.deinit();
+    if service_guard.querier.is_none() {
+        // Initialize the service only if it hasn't been initialized
+        service_guard.querier = Some(init());
+    }
+
     println!("mdns_init called");
 }
 
 pub fn mdns_deinit() {
-    let mut service_guard = SERVICE.lock().unwrap();
-    if let Some(service) = service_guard.take() {
+    let mut service_guard = SERVER.lock().unwrap();
+    if let Some(service) = service_guard.service.take() {
         service.deinit();
     }
     println!("mdns_deinit called");
@@ -209,36 +201,11 @@ fn create_a_query(name: &str) -> Vec<u8> {
 }
 
 pub fn mdns_query(name: &str) {
-    let service_guard = SERVICE.lock().unwrap();
-    if let Some(service) = &*service_guard {
+    let service_guard = SERVER.lock().unwrap();
+    if let Some(service) = &service_guard.service {
         let packet = create_a_query(name);
         service.send(packet);
     } else {
         println!("Service not initialized");
     }
 }
-
-pub fn mdns_pcb_init_rust(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> Result<(), EspErr> {
-    unsafe {
-        set_callback(rust_callback);
-//         set_callback2();
-    }
-
-    let err = unsafe { _mdns_pcb_init(tcpip_if, ip_protocol) };
-    if err == 0 {
-        Ok(())
-    } else {
-        Err(err)
-    }
-}
-
-
-
-pub fn mdns_pcb_deinit_rust(tcpip_if: MdnsIf, ip_protocol: MdnsIpProtocol) -> Result<(), EspErr> {
-    let err = unsafe { _mdns_pcb_deinit(tcpip_if, ip_protocol) };
-    if err == 0 {
-        Ok(())
-    } else {
-        Err(err)
-    }
-}
diff --git a/components/mdns/src/service/native.rs b/components/mdns/src/service/native.rs
index 04c9ebfb24..da530836c4 100644
--- a/components/mdns/src/service/native.rs
+++ b/components/mdns/src/service/native.rs
@@ -2,13 +2,11 @@ use super::service::Service;
 use std::thread;
 
 use std::net::{UdpSocket, Ipv4Addr};
-use dns_parser::rdata::null;
 use socket2::{Socket, Domain, Type, Protocol};
 use nix::unistd::{pipe, read, write, close};
 use nix::sys::select::{select, FdSet};
 use nix::sys::time::TimeVal;
-use std::os::fd::{AsRawFd, FromRawFd};
-use std::mem::MaybeUninit;
+use std::os::fd::AsRawFd;
 
 enum Action {
     Action1,

From 07b53fc021a0dbca79314154460017f10718904e Mon Sep 17 00:00:00 2001
From: David Cermak <cermak@espressif.com>
Date: Fri, 13 Dec 2024 17:32:57 +0100
Subject: [PATCH 5/5] fix(mdns): Querier with cond-var WIP

---
 components/mdns/examples/usage.rs     |   5 +-
 components/mdns/src/lib.rs            | 266 +++++++++++---------------
 components/mdns/src/querier.rs        | 148 ++++++++++++++
 components/mdns/src/service/native.rs |   9 +-
 4 files changed, 266 insertions(+), 162 deletions(-)
 create mode 100644 components/mdns/src/querier.rs

diff --git a/components/mdns/examples/usage.rs b/components/mdns/examples/usage.rs
index 49fa9e9775..366daed45c 100644
--- a/components/mdns/examples/usage.rs
+++ b/components/mdns/examples/usage.rs
@@ -1,16 +1,17 @@
 // examples/basic_usage.rs
 
+// use std::process::Termination;
 use mdns::*;
 use std::thread;
 use std::time::Duration;
-
+// use libc::__c_anonymous_xsk_tx_metadata_union;
 
 fn main() {
     // Initialize mDNS
     mdns_init();
 
     mdns_query("david-work.local");
-    thread::sleep(Duration::from_millis(500));
+    thread::sleep(Duration::from_millis(1500));
     // Deinitialize mDNS
     mdns_deinit();
 }
diff --git a/components/mdns/src/lib.rs b/components/mdns/src/lib.rs
index 4b8c5e6e2e..67c1cdc8f7 100644
--- a/components/mdns/src/lib.rs
+++ b/components/mdns/src/lib.rs
@@ -1,180 +1,93 @@
-// src/lib.rs
 mod service;
+mod querier;
+
 use service::{Service, NativeService, CService};
+use querier::Querier;
+
 use lazy_static::lazy_static;
 use std::sync::{Arc, Mutex};
 use dns_parser::{Builder, QueryClass, QueryType, Packet};
-use std::time::{Duration, Instant};
-
+use std::time::Duration;
 
-#[cfg(not(feature = "ffi"))]
-fn build_info() {
-    println!("Default build");
+lazy_static! {
+    static ref SERVER: Arc<Mutex<Objects>> = Arc::new(Mutex::new(Objects {
+        service: None,
+        querier: None,
+    }));
 }
 
-#[cfg(not(feature = "ffi"))]
-fn create_service(cb: fn(&[u8])) -> Box<dyn Service> {
-    NativeService::init(cb)
+struct Objects {
+    service: Option<Box<dyn Service>>,
+    querier: Option<Querier>,
 }
 
-#[cfg(feature = "ffi")]
 fn build_info() {
-    println!("FFI build");
+    #[cfg(not(feature = "ffi"))]
+    {
+        println!("Default build");
+    }
+    #[cfg(feature = "ffi")]
+    {
+        println!("FFI build");
+    }
 }
 
-#[cfg(feature = "ffi")]
 fn create_service(cb: fn(&[u8])) -> Box<dyn Service> {
-    CService::init(cb)
+    #[cfg(not(feature = "ffi"))]
+    {
+        NativeService::init(cb)
+    }
+    #[cfg(feature = "ffi")]
+    {
+        CService::init(cb)
+    }
+}
+
+fn read_cb(vec: &[u8]) {
+    if vec.len() == 0 {
+        let mut service_guard = SERVER.lock().unwrap();
+        if let Some(querier) = &mut service_guard.querier {
+            println!("querier process {:?}", vec);
+            let packet = querier.process();
+            if packet.is_some() {
+                if let Some(service) = &service_guard.service {
+                    service.send(packet.unwrap());
+                }
+            }
+        }
+    } else {
+        println!("Received {:?}", vec);
+        let mut service_guard = SERVER.lock().unwrap();
+        if let Some(querier) = &mut service_guard.querier {
+            querier.parse(&vec).expect("Failed to parse..");
+        }
+        // parse_dns_response(vec).unwrap();
+    }
 }
 
 fn parse_dns_response(data: &[u8]) -> Result<(), String> {
     println!("Parsing DNS response with length 2 : {}", data.len());
-    let packet = Packet::parse(&data).unwrap();
+    let packet = Packet::parse(data).unwrap();
     for answer in packet.answers {
+        println!("ANSWER:");
         println!("{:?}", answer);
     }
     for question in packet.questions {
+        println!("QUESTION:");
         println!("{:?}", question);
     }
     Ok(())
 }
 
-
-// pub trait Querier: Send + Sync {
-//     fn init() -> Box<Self>
-//     where
-//         Self: Sized;
-
-//     fn deinit(self: Box<Self>);
-// }
-
-
-#[derive(Debug)]
-pub struct Query {
-    pub name: String,
-    pub service: String,
-    pub proto: String,
-    pub query_type: QueryType,
-    pub unicast: bool,
-    pub timeout: Duration,
-    pub added_at: Instant, // To track when the query was added
-}
-
-pub struct Querier {
-    queries: Vec<Query>,
-}
-
-fn create_querier() -> Box<dyn Querier> {
-    NativeService::init(cb)
-}
-
-impl Querier {
-    pub fn new() -> Self {
-        Self {
-            queries: Vec::new(),
-        }
-    }
-
-    pub fn init(&mut self) {
-        println!("Querier initialized");
-    }
-
-    pub fn deinit(&mut self) {
-        self.queries.clear();
-        println!("Querier deinitialized");
-    }
-
-    pub fn add(
-        &mut self,
-        name: String,
-        service: String,
-        proto: String,
-        query_type: QueryType,
-        unicast: bool,
-        timeout: Duration,
-        // semaphore: Option<tokio::sync::Semaphore>,
-    ) -> usize {
-        let query = Query {
-            name,
-            service,
-            proto,
-            query_type,
-            unicast,
-            timeout,
-            // semaphore,
-            added_at: Instant::now(),
-        };
-        self.queries.push(query);
-        self.queries.len() - 1 // Return the ID (index of the query)
-    }
-
-    pub fn process(&mut self) {
-        let now = Instant::now();
-        self.queries.retain(|query| {
-            let elapsed = now.duration_since(query.added_at);
-            if elapsed > query.timeout {
-                println!("Query timed out: {:?}", query);
-                // if let Some(semaphore) = &query.semaphore {
-                //     semaphore.add_permits(1); // Release semaphore if waiting
-                // }
-                false // Remove the query
-            } else {
-                println!("Processing query: {:?}", query);
-                // Implement retry logic here if needed
-                true // Keep the query
-            }
-        });
-    }
-
-    pub async fn wait(&mut self, id: usize) -> Result<(), &'static str> {
-        if let Some(query) = self.queries.get_mut(id) {
-            Ok(())
-            // if let Some(semaphore) = &query.semaphore {
-            //     semaphore.acquire().await.unwrap(); // Block until the semaphore is released
-            //     Ok(())
-            // } else {
-            //     Err("No semaphore set for this query")
-            // }
-        } else {
-            Err("Invalid query ID")
-        }
-    }
-}
-
-struct Objects {
-    service: Option<Box<dyn Service>>,
-    // responder: Option<Box<dyn Responder>>,
-    querier: Option<Querier>,
-}
-
-// lazy_static! {
-//     static ref SERVICE: Arc<Mutex<Option<Box<dyn Service>>>> = Arc::new(Mutex::new(None));
-// }
-
-lazy_static! {
-    static ref SERVER: Arc<Mutex<Objects>> = Arc::new(Mutex::new(Objects {
-        service: None,
-        querier: None,
-    }));
-}
-
-fn read_cb(vec: &[u8]) {
-    println!("Received {:?}", vec);
-    parse_dns_response(vec).unwrap();
-}
-
 pub fn mdns_init() {
     build_info();
     let mut service_guard = SERVER.lock().unwrap();
     if service_guard.service.is_none() {
-        // Initialize the service only if it hasn't been initialized
         service_guard.service = Some(create_service(read_cb));
     }
     if service_guard.querier.is_none() {
-        // Initialize the service only if it hasn't been initialized
-        service_guard.querier = Some(init());
+        service_guard.querier = Some(Querier::new());
     }
-
     println!("mdns_init called");
 }
 
@@ -186,26 +99,63 @@ pub fn mdns_deinit() {
     println!("mdns_deinit called");
 }
 
-fn create_a_query(name: &str) -> Vec<u8> {
-    let query_type = QueryType::A;       // Type A query for IPv4 address
-    let query_class = QueryClass::IN;    // Class IN (Internet)
-
-    // Create a new query with ID and recursion setting
-    let mut builder = Builder::new_query(0x5555, true);
+/*
+pub fn mdns_query(name: &str) {
+    let mut service_guard = SERVER.lock().unwrap();
 
-    // Add the question for "david-work.local"
-    builder.add_question(name, false, query_type, query_class);
+    if let Some(querier) = &mut service_guard.querier {
+        let timeout = Duration::from_secs(5);
+        let query_id = querier.add(
+            name.to_string(),
+            "".to_string(),
+            "_http._tcp".to_string(),
+            QueryType::A,
+            false,
+            timeout,
+        );
+        querier.wait(query_id).await.unwrap();
+        println!("Query added with ID: {}", query_id);
+    }
 
-    // Build and return the query packet
-    builder.build().unwrap_or_else(|x| x)
 }
 
+ */
+
 pub fn mdns_query(name: &str) {
-    let service_guard = SERVER.lock().unwrap();
-    if let Some(service) = &service_guard.service {
-        let packet = create_a_query(name);
-        service.send(packet);
-    } else {
-        println!("Service not initialized");
+    // Lock the server to access the querier
+    let query_id;
+    let querier_cvar;
+
+    {
+        let mut service_guard = SERVER.lock().unwrap();
+        if let Some(querier) = &mut service_guard.querier {
+            let timeout = Duration::from_secs(1);
+            query_id = querier.add(
+                name.to_string(),
+                "".to_string(),
+                "_http._tcp".to_string(),
+                QueryType::A,
+                false,
+                timeout,
+            );
+            querier_cvar = querier.completed_queries.clone(); // Clone the Arc<Mutex> pair
+        } else {
+            println!("No querier available");
+            return;
+        }
+    } // Release the SERVER lock here
+
+    // Wait for the query to complete
+    let (lock, cvar) = &*querier_cvar;
+    let mut completed = lock.lock().unwrap();
+    while !completed.get(&query_id).copied().unwrap_or(false) {
+        let result = cvar.wait_timeout(completed, Duration::from_secs(5)).unwrap();
+        completed = result.0; // Update the lock guard
+        if result.1.timed_out() {
+            println!("Query timed out: ID {}", query_id);
+            return;
+        }
     }
+
+    println!("Query completed!!! ID {}", query_id);
 }
diff --git a/components/mdns/src/querier.rs b/components/mdns/src/querier.rs
new file mode 100644
index 0000000000..6ff3e769a0
--- /dev/null
+++ b/components/mdns/src/querier.rs
@@ -0,0 +1,148 @@
+use dns_parser::{Builder, QueryClass, QueryType, Packet};
+use std::sync::{Arc, Condvar, Mutex};
+use std::collections::HashMap;
+use std::time::{Duration, Instant};
+
+#[derive(Debug)]
+pub struct Query {
+    pub name: String,
+    pub service: String,
+    pub proto: String,
+    pub query_type: QueryType,
+    pub unicast: bool,
+    pub timeout: Duration,
+    pub added_at: Instant,
+    pub packet: Vec<u8>,
+    pub id: usize,
+}
+
+pub struct Querier {
+    queries: Vec<Query>,
+    pub(crate) completed_queries: Arc<(Mutex<HashMap<usize, bool>>, Condvar)>, // Shared state for query completion
+}
+
+impl Querier {
+    pub fn new() -> Self {
+        Self {
+            queries: Vec::new(),
+            completed_queries: Arc::new((Mutex::new(HashMap::new()), Condvar::new())),
+        }
+    }
+
+    pub fn add(
+        &mut self,
+        name: String,
+        service: String,
+        proto: String,
+        query_type: QueryType,
+        unicast: bool,
+        timeout: Duration,
+    ) -> usize {
+        let id = self.queries.len();
+        let query = Query {
+            name: name.clone(),
+            service,
+            proto,
+            query_type,
+            unicast,
+            timeout,
+            added_at: Instant::now(),
+            packet: create_a_query(&name),
+            id: id.clone()
+        };
+        self.queries.push(query);
+        self.completed_queries.0.lock().unwrap().insert(id, false); // Mark as incomplete
+        id
+    }
+    pub fn parse(&mut self, data: &[u8]) -> Result<(), String> {
+        println!("Parsing DNS response with length 2 : {}", data.len());
+        let packet = Packet::parse(data).unwrap();
+        for answer in packet.answers {
+            println!("ANSWER:");
+            println!("{:?}", answer);
+            let name = answer.name.to_string();
+            let mut completed_queries = vec![];
+            self.queries.retain(|query| {
+                if query.name == name {
+                    println!("ANSWER: {:?}", answer.data);
+                    completed_queries.push(query.id); // Track for completion
+                    false
+                }
+                else { true }
+            });
+            let (lock, cvar) = &*self.completed_queries;
+            let mut completed = lock.lock().unwrap();
+            for query_id in completed_queries {
+                if let Some(entry) = completed.get_mut(&query_id) {
+                    *entry = true;
+                }
+            }
+            cvar.notify_all();
+
+        }
+        for question in packet.questions {
+            println!("{:?}", question);
+        }
+        Ok(())
+
+    }
+
+    pub fn process(&mut self) -> Option<Vec<u8>> {
+        let now = Instant::now();
+        let mut packet_to_send: Option<Vec<u8>> = None;
+
+        // Collect IDs of timed-out queries to mark them as complete
+        let mut timed_out_queries = vec![];
+        self.queries.retain(|query| {
+            let elapsed = now.duration_since(query.added_at);
+            if elapsed > query.timeout {
+                timed_out_queries.push(query.id); // Track for completion
+                false // Remove the query
+            } else {
+                packet_to_send = Some(query.packet.clone());
+                true // Keep the query
+            }
+        });
+
+        // Mark timed-out queries as complete and notify waiting threads
+        let (lock, cvar) = &*self.completed_queries;
+        let mut completed = lock.lock().unwrap();
+        for query_id in timed_out_queries {
+            if let Some(entry) = completed.get_mut(&query_id) {
+                *entry = true;
+            }
+        }
+        cvar.notify_all();
+        println!("Processing... query");
+
+        packet_to_send
+    }
+    pub fn wait(&self, id: usize) -> Result<(), &'static str> {
+        let (lock, cvar) = &*self.completed_queries;
+
+        // Wait until the query is marked as complete or timeout expires
+        let mut completed = lock.lock().unwrap();
+        while !completed.get(&id).copied().unwrap_or(false) {
+            completed = cvar.wait(completed).unwrap();
+        }
+        Ok(())
+    }
+
+    fn mark_query_as_complete(&self, query: &Query) {
+        let (lock, cvar) = &*self.completed_queries;
+        let mut completed = lock.lock().unwrap();
+        if let Some(entry) = completed.get_mut(&(self.queries.len() - 1)) {
+            *entry = true;
+        }
+        cvar.notify_all();
+    }
+}
+
+fn create_a_query(name: &str) -> Vec<u8> {
+    let query_type = QueryType::A; // Type A query for IPv4 address
+    let query_class = QueryClass::IN; // Class IN (Internet)
+
+    let mut builder = Builder::new_query(0, true);
+    builder.add_question(name, false, query_type, query_class);
+    builder.build().unwrap_or_else(|x| x)
+}
diff --git a/components/mdns/src/service/native.rs b/components/mdns/src/service/native.rs
index da530836c4..458e024911 100644
--- a/components/mdns/src/service/native.rs
+++ b/components/mdns/src/service/native.rs
@@ -7,6 +7,7 @@ use nix::unistd::{pipe, read, write, close};
 use nix::sys::select::{select, FdSet};
 use nix::sys::time::TimeVal;
 use std::os::fd::AsRawFd;
+use std::ptr::null;
 
 enum Action {
     Action1,
@@ -53,10 +54,14 @@ impl NativeService
                 read_fds.insert(read_fd);
 
 
-                let mut timeout = TimeVal::new(0, 100_000);
+                let mut timeout = TimeVal::new(0, 500_000);
 
                 match select(read_fd.max(socket_fd) + 1, Some(&mut read_fds), None, None, Some(&mut timeout)) {
-                    Ok(0) => println!("ThreadHousekeeper: Performing housekeeping tasks"),
+                    Ok(0) => {
+                        println!("ThreadHousekeeper: Performing housekeeping tasks");
+                        let buf = vec![];
+                        local_cb(&buf);
+                    }
                     Ok(_) => {
                         if read_fds.contains(socket_fd) {
                             // let mut buf: [MaybeUninit<u8>; 1500] = unsafe { MaybeUninit::uninit().assume_init() };