Skip to content

Unintended synchronous DNS resolution? #156

Description

@solexperts-tor

Hey there,

We're using this crate for two of our applications, because it is very robust and the API is very nice to work with. Because we recently had some internal networking issues, we've noticed that a MQTT-client's connect procedure has a synchronous DNS resolution. We're not too familiar with the underlying networking crates, but it seems that the synchronous std::sys::net::lookup_host_string is being used in this crate's connect method.

The asynchronous connect method

#[instrument(skip(self))]
    pub async fn connect(&self, address: &str) -> Result<()> {
        let client_id = self.client_id().await;
        tracing::trace!(client_id = %client_id, address = %address, "MQTT CLIENT - connect() method called");
        tracing::info!(client_id = %client_id, address = %address, "Initiating MQTT connection");

        let result = {
            let connection_guard = self.connection_mutex.lock().await;
            let options = self.inner.read().await.options.clone();
            let result = self.connect_with_options_internal(address, options).await;
            drop(connection_guard);
            result
        };

        match result {
            Ok(connect_result) => {
                tracing::info!(client_id = %client_id, session_present = %connect_result.session_present, "Successfully connected to MQTT broker");
                Ok(())
            }
            Err(e) => {
                tracing::error!(client_id = %client_id, error = %e, "Failed to connect to MQTT broker");
                Err(e)
            }
        }
    }

has a synchronous DNS resolution procedure in let result = self.connect_with_options_internal(address, options).await;, which can be tracked to to_socket_addrs() in

pub(crate) fn resolve_addresses(host: &str, port: u16) -> Result<Vec<std::net::SocketAddr>> {
        let addr_str = format!("{host}:{port}");
        tracing::debug!(addr_str = %addr_str, "🌐 DNS RESOLUTION - Starting address resolution");

        let addrs: Vec<_> = addr_str
            .to_socket_addrs()
            .map_err(|e| {
                tracing::error!(addr_str = %addr_str, error = %e, "🌐 DNS RESOLUTION - Failed to resolve address");
                MqttError::ConnectionError(format!("Failed to resolve address: {e}"))
            })?
            .collect();

        tracing::debug!(addr_str = %addr_str, resolved_count = addrs.len(), "🌐 DNS RESOLUTION - Address resolved successfully");

        if addrs.is_empty() {
            return Err(MqttError::ConnectionError(
                "No valid address found".to_string(),
            ));
        }

        Ok(addrs)
    }

where the synchronous std::sys::net::lookup_host_string method is being used.

It seems as if for example a spawn_blocking block or the tokio::net::lookup_host methods would be valid asynchronous alternatives for the DNS loopup procedure. We could just add the manual DNS lookup before the crate's connect method or wrap the connect method in spawn_blocking, but that seems unintended. Therefore, we were wondering: Is the use of a synchronous DNS lookup method intended in the crate's connect method?

Thanks for all the work on this crate!

Best,

Tom

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions