Repository: avahi/avahi (C)

Issue:
returning service types as services
hi, i noticed that avahi returns service types as services. i am filing this issue to find out if this is intended behavior, and to fix behavior in existing avahi-supplied programs.

as an example:

```
mischief@beast:~ $ avahi-browse -at|grep -E ' +_tcp'
+ bridge0 IPv6 _googlezone                                   _tcp                 local
+ bridge0 IPv6 _googlecast                                   _tcp                 local
+ bridge0 IPv6 _apt_proxy                                    _tcp                 local
+ bridge0 IPv6 _home-assistant                               _tcp                 local
+ bridge0 IPv6 _eero                                         _tcp                 local
+ bridge0 IPv6 _alexa                                        _tcp                 local
+ bridge0 IPv6 _sftp-ssh                                     _tcp                 local
+ bridge0 IPv6 _printer                                      _tcp                 local
+ bridge0 IPv6 _ipps                                         _tcp                 local
+ bridge0 IPv6 _ipp                                          _tcp                 local
+ bridge0 IPv6 _ssh                                          _tcp                 local
+ bridge0 IPv6 _workstation                                  _tcp                 local
+ bridge0 IPv6 _matter                                       _tcp                 local
+ bridge0 IPv4 _prometheus-http                              _tcp                 local
+ bridge0 IPv4 _esphomelib                                   _tcp                 local
+ bridge0 IPv4 _googlezone                                   _tcp                 local
...
```

or:
![2024-03-18-222548_648x569_scrot](https://github.com/avahi/avahi/assets/1487996/4f8d8470-dcaa-4e72-8c8d-fb54a9d06a51)

in the second case, avahi-discover can't print any info when an element is clicked in the ui because the service info rendered in the UI is incomplete.

additionally, avahi-discover-standalone will crash because avahi-core is reporting service *types* to the service browser which it cannot handle:
```
$ avahi-discover-standalone

(avahi-discover-standalone:1163318): dbind-WARNING **: 21:11:18.459: AT-SPI: Error retrieving accessibility bus address: org.freedesktop.systemd1.UnitMasked: Unit at-spi-dbus-bus.service is masked.
*** WARNING: Detected another IPv4 mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***
*** WARNING: Detected another IPv6 mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***
Joining mDNS multicast group on interface bridge0.IPv6 with address 2601:646:100:23:ca7f:54ff:fe67:efe4.
New relevant interface bridge0.IPv6 for mDNS.
Joining mDNS multicast group on interface bridge0.IPv4 with address 192.168.0.12.
New relevant interface bridge0.IPv4 for mDNS.
Joining mDNS multicast group on interface lo.IPv6 with address ::1.
New relevant interface lo.IPv6 for mDNS.
Joining mDNS multicast group on interface lo.IPv4 with address 127.0.0.1.
New relevant interface lo.IPv4 for mDNS.
Network interface enumeration completed.
sendmsg() to ff02::fb failed: Network is unreachable
**
ERROR:main.c:149:service_browser_callback: assertion failed: (s->service_type)
Bail out! ERROR:main.c:149:service_browser_callback: assertion failed: (s->service_type)
Aborted (core dumped)
```

my tests were done with 0.8-r7 on gentoo, and i also tested avahi-discover-standalone on 709e60fd67a19bb529045d20c0e6e8443fd20acf.

is it on purpose that the service browser somehow includes service types? or should it only include actual services? if the latter, then i think that avahi-core should be fixed to exclude them from the results. if the former, then i think avahi-discover should be fixed to not show errors, and avahi-browser-standalone should be fixed not to crash.

happy to provide more info if needed.


Discussion: > is it on purpose that the service browser somehow includes service types?

It's a bug. When avahi follows PTR resource records it receives in reply to its `_services._dns-sd._udp.local` query it expects those PTR resource records to point to instance names like `<Instance> . <Service> .local`. Looking at the output of `avahi-browse` it seems in this particular case the `<Instance>` part is missing so avahi thinks that the first label of the service name is the instance and the second label of the service name is the service. Devices shouldn't advertise invalid PTR resource records like that but avahi should be able to handle that better. `avahi-browser-standalone` shouldn't crash either.
> > is it on purpose that the service browser somehow includes service types?
> 
> It's a bug. When avahi follows PTR resource records it receives in reply to its `_services._dns-sd._udp.local` query it expects those PTR resource records to point to instance names like `<Instance> . <Service> .local`. Looking at the output of `avahi-browse` it seems in this particular case the `<Instance>` part is missing so avahi thinks that the first label of the service name is the instance and the second label of the service name is the service. Devices shouldn't advertise invalid PTR resource records like that but avahi should be able to handle that better. `avahi-browser-standalone` shouldn't crash either.


> Devices shouldn't advertise invalid PTR resource records like that

what devices? one of the invalid services is _apt_proxy, served by approx on a debian host. here is the avahi service descriptor on that system:

```
mischief@sokoban:~ $ cat /etc/avahi/services/approx.service 
<?xml version="1.0" standalone='no'?>
<!--
From: Lisandro Damián Nicanor Pérez Meyer <lisandro@debian.org>
X-Source: http://blog.surgut.co.uk/2013/03/avahi-apt-cacher-ng-sbuild.html
 See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=704790 for details.
-->
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
 <name replace-wildcards="yes">approx proxy on %h</name>
 <service protocol="ipv6">
  <type>_apt_proxy._tcp</type>
  <port>9999</port>
 </service>
 <service protocol="ipv4">
  <type>_apt_proxy._tcp</type>
  <port>9999</port>
 </service>
</service-group>
```

this is one of the entries that appears as a service when it is a service type. this would be an avahi<>avahi case. without clarification i am not sure it is right to say "devices" advertise invalid PTR records.

thanks for taking a look.
i agree with you that there are possibly "smart devices" on my network that could be advertising invalid entries. perhaps it is possible that the advertisements from such devices somehow corrupt the avahi service browser cache and result in things like this, but i do not know enough about avahi internals to understand why this would be the case.
> what devices?

It should be possible to find where those PTR resource records come from by running `tcpdump` or `wireshark`. They should advertise resource records pointing services to services (instead of particular instances) like
```
        _wat._tcp.local: type PTR, class IN, _wat._tcp.local                                                                                                  
            Name: _wat._tcp.local                                                                                                                             
            Type: PTR (domain name PoinTeR) (12)                                                                                                              
            .000 0000 0000 0001 = Class: IN (0x0001)                                                                                                          
            0... .... .... .... = Cache flush: False                                                                                                          
            Time to live: 4500 (1 hour, 15 minutes)                                                                                                           
            Data length: 17                                                                                                                                   
            Domain Name: _wat._tcp.local                         
```
A valid PTR resource record would point `_wat._tcp.local ` to something like `Printer._wat._tcp.local`.

> this would be an avahi<>avahi case

I've just checked that service file and avahi announced the service correctly. `_services._dns-sd._udp.local.` pointed to `_apt_proxy._tcp.local.` and `_apt_proxy._tcp.local.` pointed to `approx proxy on H._apt_proxy._tcp.local.`.
```python
>>> p[DNS].an
[<DNSRR  rrname=b'approx proxy on H._apt_proxy._tcp.local.' type=TXT rclass=32769 ttl=4500 rdata=[b''] |>,
 <DNSRR  rrname=b'_apt_proxy._tcp.local.' type=PTR rclass=IN ttl=4500 rdata=b'approx proxy on H._apt_proxy._tcp.local.' |>,
 <DNSRRSRV  rrname=b'approx proxy on H._apt_proxy._tcp.local.' type=SRV rclass=32769 ttl=120 priority=0 weight=0 port=9999 target=b'H.local.' |>,
 <DNSRR  rrname=b'H.local.' type=AAAA rclass=32769 ttl=120 rdata=fe80::7299:e626:f044:3db9 |>,
 <DNSRR  rrname=b'H.local.' type=A rclass=32769 ttl=120 rdata=192.168.56.4 |>,
 <DNSRR  rrname=b'_services._dns-sd._udp.local.' type=PTR rclass=IN ttl=4500 rdata=b'_apt_proxy._tcp.local.' |>]
```
is there a standard avahi command i can run to provide a full dump of what is on my network? i'm happy to provide it to debug this.

for avahi-discover-standalone, i can provide this backtrace from building 709e60fd67a19bb529045d20c0e6e8443fd20acf and running `gdb -nh -batch -ex 'r' -ex 'bt' ./avahi-discover-standalone/.libs/avahi-discover-standalone`:
```
Thread 1 "avahi-discover-" received signal SIGABRT, Aborted.
0x00007ffff6e7608c in ?? () from /usr/lib64/libc.so.6
#0  0x00007ffff6e7608c in ??? () at /usr/lib64/libc.so.6
#1  0x00007ffff6e28112 in raise () at /usr/lib64/libc.so.6
#2  0x00007ffff6e114f2 in abort () at /usr/lib64/libc.so.6
#3  0x00007ffff7043f0e in g_assertion_message (domain=domain@entry=0x0, file=file@entry=0x55555555801a "main.c", line=line@entry=149, func=func@entry=0x5555555581c0 <__func__.1> "service_browser_callback", message=message@entry=0x5555555f1ae0 "assertion failed: (s->service_type)") at ../glib-2.78.3/glib/gtestutils.c:3497
#4  0x00007ffff70a9d5a in g_assertion_message_expr (domain=domain@entry=0x0, file=file@entry=0x55555555801a "main.c", line=line@entry=149, func=func@entry=0x5555555581c0 <__func__.1> "service_browser_callback", expr=expr@entry=0x55555555800a "s->service_type") at ../glib-2.78.3/glib/gtestutils.c:3523
#5  0x0000555555556af1 in service_browser_callback (interface=3, protocol=1, service_name=0x7fffffffc0e0 "_workstation", service_type=0x7fffffffc120 "_tcp", domain_name=0x7fffffffc520 "local", userdata=<optimized out>, flags=<optimized out>, event=<optimized out>, b=<optimized out>) at main.c:149
#6  0x00007ffff7f5ec68 in record_browser_callback (rr=<optimized out>, interface=3, protocol=1, event=AVAHI_BROWSER_NEW, record=0x5555555f19d0, flags=(AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST), userdata=0x5555556597c0) at browse-service.c:79
#7  0x00007ffff7f5bca1 in lookup_multicast_callback (e=<optimized out>, interface=3, protocol=1, event=<optimized out>, flags=<optimized out>, r=0x5555555f19d0, userdata=0x5555556598a0) at browse.c:300
#8  0x00007ffff7f66217 in scan_cache_callback (c=<optimized out>, pattern=<optimized out>, e=<optimized out>, userdata=0x7fffffffca80) at multicast-lookup.c:210
#9  0x00007ffff7f55513 in avahi_cache_walk (c=0x555555634b80, pattern=0x555555659810, cb=cb@entry=0x7ffff7f661d0 <scan_cache_callback>, userdata=userdata@entry=0x7fffffffca80) at cache.c:138
#10 0x00007ffff7f6655b in scan_interface_callback (m=<optimized out>, i=0x555555563bd0, userdata=0x7fffffffca80) at multicast-lookup.c:233
#11 0x00007ffff7f4c8d0 in avahi_interface_monitor_walk (m=0x555555636cf0, interface=-1, protocol=-1, callback=0x7ffff7f66520 <scan_interface_callback>, userdata=0x7fffffffca80) at iface.c:761
#12 0x00007ffff7f66948 in avahi_multicast_lookup_engine_scan_cache (e=0x555555636b00, interface=-1, protocol=-1, key=<optimized out>, callback=callback@entry=0x7ffff7f5bbd0 <lookup_multicast_callback>, userdata=userdata@entry=0x5555556598a0) at multicast-lookup.c:266
#13 0x00007ffff7f5b871 in lookup_scan_cache (l=0x5555556598a0) at browse.c:353
#14 lookup_go (l=0x5555556598a0) at browse.c:384
#15 lookup_go (l=0x5555556598a0) at browse.c:374
#16 0x00007ffff7f5c15a in defer_callback (e=<optimized out>, userdata=0x555555659830) at browse.c:474
#17 0x00007ffff7f4a8c0 in expiration_event (timeout=<optimized out>, userdata=0x5555557c4c90) at timeeventq.c:94
#18 0x00007ffff7f798f0 in start_timeout_callback (t=0x555555649340) at glib-watch.c:252
#19 dispatch_func (source=0x5555556339e0, callback=<optimized out>, userdata=<optimized out>) at glib-watch.c:331
#20 0x00007ffff707be57 in g_main_dispatch (context=context@entry=0x5555555af4a0) at ../glib-2.78.3/glib/gmain.c:3476
#21 0x00007ffff707ed27 in g_main_context_dispatch_unlocked (context=0x5555555af4a0) at ../glib-2.78.3/glib/gmain.c:4284
#22 g_main_context_iterate_unlocked (context=0x5555555af4a0, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib-2.78.3/glib/gmain.c:4349
#23 0x00007ffff707f51f in g_main_loop_run (loop=0x55555565b180) at ../glib-2.78.3/glib/gmain.c:4551
#24 0x00007ffff77f6cf5 in gtk_main () at ../gtk+-3.24.39/gtk/gtkmain.c:1329
#25 0x0000555555556643 in main (argc=<optimized out>, argv=<optimized out>) at main.c:363
```
> is there a standard avahi command i can run to provide a full dump of what is on my network? i'm happy to provide it to debug this.

`pkill -USR1 avahi-daemon` should dump the avahi cache to the journal and should show all the PTR resource records.

(I've already reproduced it by crafting those PTR RR and sending it to avahi) 
attached is `journalctl --since=1:22 -u avahi-daemon`.
[avahi.txt](https://github.com/avahi/avahi/files/14663617/avahi.txt)

i would love the explaination for why this bug exists, as i spent quite a few hours scratching my head at the avahi-core code about this :-)

`avahi_service_name_split` doesn't handle this properly. Instead of rejecting something like `_ssh._tcp.local` it puts `_ssh` in `name`, `_tcp` in `type` and `local` in `domain`. The following test
```diff
--- a/avahi-common/domain-test.c
+++ b/avahi-common/domain-test.c
@@ -134,6 +134,12 @@ int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) {
     assert(!avahi_is_valid_fqdn("::1"));
     assert(!avahi_is_valid_fqdn(".192.168.50.1."));
 
+    res = avahi_service_name_split("_ssh._tcp.local", name, sizeof(name), type, sizeof(type), domain, sizeof(domain));
+    assert(res >= 0);
+    assert(strcmp(name, "_ssh") == 0);
+    assert(strcmp(type, "_tcp") == 0);
+    assert(strcmp(domain, "local") == 0);
+
```
shouldn't pass but it does currently. `res` should be negative.
@mischief could you apply the following patch to see if it works for you
```diff
diff --git a/avahi-common/domain.c b/avahi-common/domain.c
index 9e93018..c92e786 100644
--- a/avahi-common/domain.c
+++ b/avahi-common/domain.c
@@ -578,7 +578,7 @@ 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))
+    if (!avahi_is_valid_service_type_strict(otype))
         return AVAHI_ERR_INVALID_SERVICE_TYPE;
 
     if (!avahi_is_valid_domain_name(odomain))
```
?

(I'd need to make sure that it doesn't break subtypes anywhere, add tests and so on but until then it should hopefully help)
this seems to work - `avahi-browse` now doesn't print service types _as if they were services_.

avahi-discover-standalone still crashes, so i think that is simply a different issue.
Could you show the backtrace with the patch applied and also the cache dump of avahi? The most interesting part would be `service_browser_callback` and its arguments.

it seems there might still be a service type being passed as a name still here. avahi cache dump attached.

[avahi2.txt](https://github.com/avahi/avahi/files/14670320/avahi2.txt)

edit: ignore this, i'm just an idiot because this is running against the old host libs, not the patched libs.

`gdb -nh -batch -ex 'r' -ex 'bt' ./avahi-discover-standalone/.libs/avahi-discover-standalone`:
```
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib64/libthread_db.so.1".
[New Thread 0x7ffff5bbe6c0 (LWP 3668310)]
[New Thread 0x7ffff53bd6c0 (LWP 3668311)]
[New Thread 0x7ffff4bbc6c0 (LWP 3668312)]

(avahi-discover-standalone:3668307): dbind-WARNING **: 10:17:34.761: AT-SPI: Error retrieving accessibility bus address: org.freedesktop.systemd1.UnitMasked: Unit at-spi-dbus-bus.service is masked.

(avahi-discover-standalone:3668307): GLib-GObject-CRITICAL **: 10:17:34.761: invalid (NULL) pointer instance

(avahi-discover-standalone:3668307): GLib-GObject-CRITICAL **: 10:17:34.761: g_signal_connect_data: assertion 'G_TYPE_CHECK_INSTANCE (instance)' failed

(avahi-discover-standalone:3668307): GLib-GObject-CRITICAL **: 10:17:34.761: invalid (NULL) pointer instance

(avahi-discover-standalone:3668307): GLib-GObject-CRITICAL **: 10:17:34.761: g_signal_connect_data: assertion 'G_TYPE_CHECK_INSTANCE (instance)' failed

(avahi-discover-standalone:3668307): Gtk-CRITICAL **: 10:17:34.761: gtk_tree_view_set_model: assertion 'GTK_IS_TREE_VIEW (tree_view)' failed

(avahi-discover-standalone:3668307): Gtk-CRITICAL **: 10:17:34.761: gtk_tree_view_insert_column_with_attributes: assertion 'GTK_IS_TREE_VIEW (tree_view)' failed

(avahi-discover-standalone:3668307): Gtk-CRITICAL **: 10:17:34.761: gtk_tree_view_insert_column_with_attributes: assertion 'GTK_IS_TREE_VIEW (tree_view)' failed

(avahi-discover-standalone:3668307): Gtk-CRITICAL **: 10:17:34.761: gtk_tree_view_get_column: assertion 'GTK_IS_TREE_VIEW (tree_view)' failed

(avahi-discover-standalone:3668307): Gtk-CRITICAL **: 10:17:34.761: gtk_tree_view_column_set_resizable: assertion 'GTK_IS_TREE_VIEW_COLUMN (tree_column)' failed

(avahi-discover-standalone:3668307): Gtk-CRITICAL **: 10:17:34.761: gtk_tree_view_column_set_sizing: assertion 'GTK_IS_TREE_VIEW_COLUMN (tree_column)' failed

(avahi-discover-standalone:3668307): Gtk-CRITICAL **: 10:17:34.761: gtk_tree_view_column_set_expand: assertion 'GTK_IS_TREE_VIEW_COLUMN (tree_column)' failed
*** WARNING: Detected another IPv4 mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***
*** WARNING: Detected another IPv6 mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***
Joining mDNS multicast group on interface bridge0.IPv6 with address 2601:646:100:23:ca7f:54ff:fe67:efe4.
New relevant interface bridge0.IPv6 for mDNS.
Joining mDNS multicast group on interface bridge0.IPv4 with address 192.168.0.12.
New relevant interface bridge0.IPv4 for mDNS.
Joining mDNS multicast group on interface lo.IPv6 with address ::1.
New relevant interface lo.IPv6 for mDNS.
Joining mDNS multicast group on interface lo.IPv4 with address 127.0.0.1.
New relevant interface lo.IPv4 for mDNS.
Network interface enumeration completed.
sendmsg() to ff02::fb failed: Network is unreachable
**
ERROR:main.c:149:service_browser_callback: assertion failed: (s->service_type)
Bail out! ERROR:main.c:149:service_browser_callback: assertion failed: (s->service_type)

Thread 1 "avahi-discover-" received signal SIGABRT, Aborted.
0x00007ffff6e7608c in ?? () from /usr/lib64/libc.so.6
#0  0x00007ffff6e7608c in ??? () at /usr/lib64/libc.so.6
#1  0x00007ffff6e28112 in raise () at /usr/lib64/libc.so.6
#2  0x00007ffff6e114f2 in abort () at /usr/lib64/libc.so.6
#3  0x00007ffff7043f0e in g_assertion_message (domain=domain@entry=0x0, file=file@entry=0x55555555801a "main.c", line=line@entry=149, func=func@entry=0x5555555581c0 <__func__.1> "service_browser_callback", message=message@entry=0x5555557222e0 "assertion failed: (s->service_type)") at ../glib-2.78.3/glib/gtestutils.c:3497
#4  0x00007ffff70a9d5a in g_assertion_message_expr (domain=domain@entry=0x0, file=file@entry=0x55555555801a "main.c", line=line@entry=149, func=func@entry=0x5555555581c0 <__func__.1> "service_browser_callback", expr=expr@entry=0x55555555800a "s->service_type") at ../glib-2.78.3/glib/gtestutils.c:3523
#5  0x0000555555556af1 in service_browser_callback (interface=3, protocol=1, service_name=0x7fffffffc0e0 "_p2p", service_type=0x7fffffffc120 "_udp", domain_name=0x7fffffffc520 "local", userdata=<optimized out>, flags=<optimized out>, event=<optimized out>, b=<optimized out>) at main.c:149
#6  0x00007ffff7f5ec68 in record_browser_callback (rr=<optimized out>, interface=3, protocol=1, event=AVAHI_BROWSER_NEW, record=0x5555556b1010, flags=(AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST), userdata=0x555555721e50) at browse-service.c:79
#7  0x00007ffff7f5bca1 in lookup_multicast_callback (e=<optimized out>, interface=3, protocol=1, event=<optimized out>, flags=<optimized out>, r=0x5555556b1010, userdata=0x555555721f30) at browse.c:300
#8  0x00007ffff7f66217 in scan_cache_callback (c=<optimized out>, pattern=<optimized out>, e=<optimized out>, userdata=0x7fffffffca80) at multicast-lookup.c:210
#9  0x00007ffff7f55513 in avahi_cache_walk (c=0x55555565dd20, pattern=0x555555721ea0, cb=cb@entry=0x7ffff7f661d0 <scan_cache_callback>, userdata=userdata@entry=0x7fffffffca80) at cache.c:138
#10 0x00007ffff7f6655b in scan_interface_callback (m=<optimized out>, i=0x555555563bd0, userdata=0x7fffffffca80) at multicast-lookup.c:233
#11 0x00007ffff7f4c8d0 in avahi_interface_monitor_walk (m=0x5555556365b0, interface=-1, protocol=-1, callback=0x7ffff7f66520 <scan_interface_callback>, userdata=0x7fffffffca80) at iface.c:761
#12 0x00007ffff7f66948 in avahi_multicast_lookup_engine_scan_cache (e=0x555555636580, interface=-1, protocol=-1, key=<optimized out>, callback=callback@entry=0x7ffff7f5bbd0 <lookup_multicast_callback>, userdata=userdata@entry=0x555555721f30) at multicast-lookup.c:266
#13 0x00007ffff7f5b871 in lookup_scan_cache (l=0x555555721f30) at browse.c:353
#14 lookup_go (l=0x555555721f30) at browse.c:384
#15 lookup_go (l=0x555555721f30) at browse.c:374
#16 0x00007ffff7f5c15a in defer_callback (e=<optimized out>, userdata=0x555555721ec0) at browse.c:474
#17 0x00007ffff7f4a8c0 in expiration_event (timeout=<optimized out>, userdata=0x5555555d1420) at timeeventq.c:94
#18 0x00007ffff7f798f0 in start_timeout_callback (t=0x55555566bb80) at glib-watch.c:252
#19 dispatch_func (source=0x55555563a1c0, callback=<optimized out>, userdata=<optimized out>) at glib-watch.c:331
#20 0x00007ffff707be57 in g_main_dispatch (context=context@entry=0x5555555af490) at ../glib-2.78.3/glib/gmain.c:3476
#21 0x00007ffff707ed27 in g_main_context_dispatch_unlocked (context=0x5555555af490) at ../glib-2.78.3/glib/gmain.c:4284
#22 g_main_context_iterate_unlocked (context=0x5555555af490, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib-2.78.3/glib/gmain.c:4349
#23 0x00007ffff707f51f in g_main_loop_run (loop=0x55555565b1b0) at ../glib-2.78.3/glib/gmain.c:4551
#24 0x00007ffff77f6cf5 in gtk_main () at ../gtk+-3.24.39/gtk/gtkmain.c:1329
#25 0x0000555555556643 in main (argc=<optimized out>, argv=<optimized out>) at main.c:363
```
building with your patch appears to make avahi-discover-standalone work now as well, and not crash. :partying_face: 
What's weird is that there are no PTR resource records in the cache dump that can cause this. It looks like something sends them with short TTLs or something like that. Given that they match normal services it could be that some device inspects mDNS packets, downgrade TTLs, change the PTR rdata and then forwards them. It's a mystery.
i think i have narrowed it down to a virtual machine running home assistant os (https://www.home-assistant.io/installation/generic-x86-64). i don't know what implementation of mdns they use, but maybe it is sending bogus.

with the machine powered on, i have these entries now in avahi-browse:
```
$ avahi-browse -ptka|grep -E ";_(tcp|udp)"
+;bridge0;IPv6;_workstation;_tcp;local
+;bridge0;IPv6;_home-assistant;_tcp;local
+;bridge0;IPv6;_apt_proxy;_tcp;local
+;bridge0;IPv6;_sftp-ssh;_tcp;local
+;bridge0;IPv6;_ssh;_tcp;local
+;bridge0;IPv4;_apt_proxy;_tcp;local
+;bridge0;IPv4;_home-assistant;_tcp;local
+;bridge0;IPv4;_sftp-ssh;_tcp;local
+;bridge0;IPv4;_ssh;_tcp;local
```

if i power off the VM, the strange entries disappear.

attached is a pcap of port == 5353 and ip.host == the VM's ip, captured while the vm was booting up and the entries appeared shortly after. 
[ha.pcapng.gz](https://github.com/avahi/avahi/files/14672928/ha.pcapng.gz)

is it because of the spaces/brackets in the domain name the vm is sending?
> is it because of the spaces/brackets in the domain name the vm is sending?

avahi escapes those characters. I took a quick look and I can't see offending PTR resource records there either. I replayed one announcement with `_workstation._tcp` and got
```sh
+;eth1;IPv4;homeassistant\032\091d92dc75e3a3e45788db19f6c184f3c74\093;_workstation._tcp;local
```
Something weird is going on. I'll take a closer look later.
I replayed them all and reproduced the issue. The culprit is
```
        _services._dns-sd._udp.local: type PTR, class IN, _services._dns-sd._udp.local
            Name: _services._dns-sd._udp.local
            Type: PTR (domain name PoinTeR) (12)
            .000 0000 0000 0001 = Class: IN (0x0001)
            0... .... .... .... = Cache flush: False
            Time to live: 120 (2 minutes)
            Data length: 2
            Domain Name: _services._dns-sd._udp.local
```
Whatever it is it violates https://datatracker.ietf.org/doc/html/rfc6763#section-9
> the rdata of each PTR record is the two-
   label <Service> name, plus the same domain, e.g.,
   "_http._tcp.<Domain>".

Anyway with the patch applied I replayed all those announcement again just in case and the issue is gone.
should we PR the patch with service name checking? or should we investigate the underlying cause of breaking the avahi cache?
The patch itself is fully functional. It works and doesn't break subtypes anywhere in the sense that subtypes can still be published/browsed. The problem is that it breaks a unit test related to subtypes I added recently:
```c
    res = avahi_service_name_split("test._hop._sub._ssh._tcp.local", name, sizeof(name), type, sizeof(type), domain, sizeof(domain));
    assert(res >= 0);
```
and I've been trying to figure out how realistic that test case is. `avahi_service_name_split` isn't used by `avahi` itself for that but it's a public API and it's possible that this change can break something somewhere.
@mischief I opened https://github.com/avahi/avahi/pull/582. I tested it with the pcap but could you try it just in case?
I'll go ahead and merge that PR to fully unleash the part of the CI that hasn't been fully hooked up to the repository yet: https://github.com/avahi/avahi/issues/540. If something doesn't work just let me know and I reopen the issue.



Fix (patch):
diff --git a/avahi-common/domain.c b/avahi-common/domain.c
index 9e930188c..3c01d43a5 100644
--- a/avahi-common/domain.c
+++ b/avahi-common/domain.c
@@ -578,7 +578,7 @@ 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))
+    if (!avahi_is_valid_service_type_strict(otype) && !avahi_is_valid_service_subtype(otype))
         return AVAHI_ERR_INVALID_SERVICE_TYPE;
 
     if (!avahi_is_valid_domain_name(odomain))
diff --git a/avahi-core/browse-service-type.c b/avahi-core/browse-service-type.c
index f0d69388f..e57b8da32 100644
--- a/avahi-core/browse-service-type.c
+++ b/avahi-core/browse-service-type.c
@@ -69,6 +69,11 @@ static void record_browser_callback(
             return;
         }
 
+        if (!avahi_is_valid_service_type_strict(type)) {
+            avahi_log_debug("Invalid service '%s'", record->data.ptr.name);
+            return;
+        }
+
         b->callback(b, interface, protocol, event, type, domain, flags, b->userdata);
     } else
         b->callback(b, interface, protocol, event, NULL, b->domain_name, flags, b->userdata);
diff --git a/avahi-core/browse-service.c b/avahi-core/browse-service.c
index e924baeb2..830659b66 100644
--- a/avahi-core/browse-service.c
+++ b/avahi-core/browse-service.c
@@ -73,6 +73,11 @@ static void record_browser_callback(
             return;
         }
 
+        if (!avahi_is_valid_service_type_strict(type)) {
+            avahi_log_debug("Invalid service '%s'", record->data.ptr.name);
+            return;
+        }
+
         b->callback(b, interface, protocol, event, service, type, domain, flags, b->userdata);
 
     } else