Repository: avahi/avahi (C)

Issue:
avahi-browse -a breaks with "Invalid service type" if a device sends a malformed service
**What happens**
If a device on the network sends a service PTR like "esp32.http.tcp.local" then avahi-browse is broken on every computer on the network:
```
avahi-browse -a
avahi_service_browser_new() failed: Invalid service type
```

avahi-discover-standalone is not affected (it lists an empty line)

**What I expect to happen**
avahi-browse -a lists all found services, maybe an error message for the malformed service

**How to reproduce it**
create a service missing the underscores




Discussion: Automation based on avahi-browse -a broke for me too, on a network where some Bosch wifi-based smart home devices were added.  Presumably they send broken service announcements, however this means avahi-browse -a is fully broken since it does not list the other correct services, it seems to exit as soon as it encounters a broken service record, at a random point in the discovery process.
I think that if one service is invalid avahi_dbus_respond_error() should not be called at:
https://github.com/lathiat/avahi/blob/master/avahi-daemon/dbus-protocol.c#L606

But I'm not sure if avahi_s_service_browser_prepare() should return an error if a service doesn't start with an _
https://github.com/lathiat/avahi/blob/master/avahi-core/browse-service.c#L106

What about:
```
-    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_generic(service_type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+    if(!avahi_is_valid_service_type_generic(service_type)) {
+      service_type="_invalid._tcp";
+    }
```

The device is still not listet with an invalid service (I don't know why) but a broken device doestn't crash the rest of the mDNS entries
> I think that if one service is invalid avahi_dbus_respond_error() should not be called at:
> https://github.com/lathiat/avahi/blob/master/avahi-daemon/dbus-protocol.c#L606
> 
> But I'm not sure if avahi_s_service_browser_prepare() should return an error if a service doesn't start with an _
> https://github.com/lathiat/avahi/blob/master/avahi-core/browse-service.c#L106
> 
> What about:
> 
> ```
> -    AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_generic(service_type), AVAHI_ERR_INVALID_SERVICE_TYPE);
> +    if(!avahi_is_valid_service_type_generic(service_type)) {
> +      service_type="_invalid._tcp";
> +    }
> ```
> 
> The device is still not listet with an invalid service (I don't know why) but a broken device doestn't crash the rest of the mDNS entries

Did you manage to test this? It looks a sensible thing to do in my view. Issue has been around since 2018 and would be lovely if it was fixed in a formal release (and allow peoples scripts to restart) :)
Resolves the issue for me with Fedora's 0.8 RPM from the upcoming F33 release with your suggested fix.
My god I hope this fix comes to Ubuntu some day. This bug has gone unaddressed for **_FIFTEEN YEARS!!!_**

Despite the bug being in Avahi, [Lennart Pottering believes that his code should't be responsible for sanitizing it's inputs, and it is instead the fault of the foreign network device for sending malformed content!](https://avahi.freedesktop.narkive.com/2Vi2UDCe/avahi-daemon-dies-on-certain-hostnames). Brain dead 'logic' like this needs to die a horrible death. It only causes DECADES of suffering for those that rely on this software.
@ferbar's patch has now been integrated into the Arch Linux version of avahi in version 0.8+22+gfd482a7.
On Fedora 36 I too am encountering the OP's issue.

Whilst awaiting the bug fix, can someone suggest a way of finding out which device is sending an "Invalid service type"?  The current error message of:
` avahi_service_browser_new() failed: Invalid service type`
is not very helpful.

Can someone suggest something?
 
how is this still a thing?
Do we have any PR attempting to fix this issue? Some changes are referenced by the comments, but I do not see matching PR. Do you have @ferbar any change you would like to contribute?
@pemensik See #472 



Fix (patch):
diff --git a/avahi-common/domain.c b/avahi-common/domain.c
index c7af116a2..9e930188c 100644
--- a/avahi-common/domain.c
+++ b/avahi-common/domain.c
@@ -501,6 +501,7 @@ int avahi_service_name_split(const char *p, char *name, size_t name_size, char *
         DOMAIN
     } state;
     int type_empty = 1, domain_empty = 1;
+    char *oname, *otype, *odomain;
 
     assert(p);
     assert(type);
@@ -508,6 +509,10 @@ int avahi_service_name_split(const char *p, char *name, size_t name_size, char *
     assert(domain);
     assert(domain_size > 0);
 
+    oname = name;
+    otype = type;
+    odomain = domain;
+
     if (name) {
         assert(name_size > 0);
         *name = 0;
@@ -570,6 +575,15 @@ int avahi_service_name_split(const char *p, char *name, size_t name_size, char *
         }
     }
 
+    if ((oname && !avahi_is_valid_service_name(oname)))
+        return AVAHI_ERR_INVALID_SERVICE_NAME;
+
+    if (!avahi_is_valid_service_type_generic(otype))
+        return AVAHI_ERR_INVALID_SERVICE_TYPE;
+
+    if (!avahi_is_valid_domain_name(odomain))
+        return AVAHI_ERR_INVALID_DOMAIN_NAME;
+
     return 0;
 }
 
diff --git a/avahi-core/browse-service-type.c b/avahi-core/browse-service-type.c
index b1fc7af8f..f0d69388f 100644
--- a/avahi-core/browse-service-type.c
+++ b/avahi-core/browse-service-type.c
@@ -65,7 +65,7 @@ static void record_browser_callback(
         assert(record->key->type == AVAHI_DNS_TYPE_PTR);
 
         if (avahi_service_name_split(record->data.ptr.name, NULL, 0, type, sizeof(type), domain, sizeof(domain)) < 0) {
-            avahi_log_warn("Invalid service type '%s'", record->key->name);
+            avahi_log_debug("Failed to split service name '%s'", record->data.ptr.name);
             return;
         }
 
diff --git a/avahi-core/browse-service.c b/avahi-core/browse-service.c
index 63e0275a5..e924baeb2 100644
--- a/avahi-core/browse-service.c
+++ b/avahi-core/browse-service.c
@@ -69,7 +69,7 @@ static void record_browser_callback(
             flags |= AVAHI_LOOKUP_RESULT_LOCAL;
 
         if (avahi_service_name_split(record->data.ptr.name, service, sizeof(service), type, sizeof(type), domain, sizeof(domain)) < 0) {
-            avahi_log_warn("Failed to split '%s'", record->key->name);
+            avahi_log_debug("Failed to split service name '%s'", record->data.ptr.name);
             return;
         }
 
diff --git a/fuzz/fuzz-packet.c b/fuzz/fuzz-packet.c
index 7757b8ad9..bfc661a27 100644
--- a/fuzz/fuzz-packet.c
+++ b/fuzz/fuzz-packet.c
@@ -53,17 +53,17 @@ bool copy_rrs(AvahiDnsPacket *from, AvahiDnsPacket *to, unsigned idx) {
         if (record->key->type == AVAHI_DNS_TYPE_PTR) {
             char service[AVAHI_LABEL_MAX], type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX];
             char name[AVAHI_DOMAIN_NAME_MAX];
+            int res;
 
-            /* Ideally there should also be assertions to make sure that all the components
-             * can be joined back together successfully but in its current form avahi_service_name_split
-             * can supply invalid arguments rejected by avahi_service_name_join and that's where
-             * issues like https://github.com/lathiat/avahi/issues/212 come from. Let's just exercise
-             * those code paths for now to at least make sure they don't crash. */
-            if (avahi_service_name_split(record->data.ptr.name, service, sizeof(service), type, sizeof(type), domain, sizeof(domain)) >= 0)
-                avahi_service_name_join(name, sizeof(name), service, type, domain);
+            if (avahi_service_name_split(record->data.ptr.name, service, sizeof(service), type, sizeof(type), domain, sizeof(domain)) >= 0) {
+                res = avahi_service_name_join(name, sizeof(name), service, type, domain);
+                assert(res >= 0);
+            }
 
-            if (avahi_service_name_split(record->data.ptr.name, NULL, 0, type, sizeof(type), domain, sizeof(domain)) >= 0)
-                avahi_service_name_join(name, sizeof(name), NULL, type, domain);
+            if (avahi_service_name_split(record->data.ptr.name, NULL, 0, type, sizeof(type), domain, sizeof(domain)) >= 0) {
+                res = avahi_service_name_join(name, sizeof(name), NULL, type, domain);
+                assert(res >= 0);
+            }
         }
 
         res = avahi_dns_packet_append_record(to, record, cache_flush, 0);