zeroconf icon indicating copy to clipboard operation
zeroconf copied to clipboard

IPv4 is sometimes empty in found service entries

Open shangsunset opened this issue 6 years ago • 7 comments

Hi,

I have a question. With provided example code, if I start and stop client.go a couple times, IPv4 array would be empty in found service entry sometimes.

&{{service _foobar._tcp local. } router.home.local. 9000 [txtv=0 lo=1 la=2] 3200 [192.168.1.154] [fe80::ca2:1095:d9ab:c3e3]}

&{{service _foobar._tcp local. } router.home.local. 9000 [txtv=0 lo=1 la=2] 3200 [] [fe80::fe3a:efd5:f3dd:a9b6]}

Is this normal or a bug?

Thank you.

shangsunset avatar Aug 16 '17 03:08 shangsunset

I hit the same issue. The problem (in my case), was the entry with the IPV4 address comes in immediately after the IV6 one, but, since the entry was sent (and is correspondingly in sentEntries), it isn't sent again.

in client.mainloop, the line that prevents seeing the IPV4 entry is:

				if _, ok := sentEntries[k]; ok {
					continue
				}

@grandcat I'm more than willing to work up a patch for this, (this assumes it is something that you'd like to see fixed. FWIW avahi-browse will show both entries separately), but it would probably be best to have some idea of how you'd like to see this handled.

For example, one option is to delete the entry from sentEntries if it is updated. This would result in sending two entries. The IP association loop then becomes:

			for _, answer := range sections {
				switch rr := answer.(type) {
				case *dns.A:
					for k, e := range entries {
						if e.HostName == rr.Hdr.Name {
							entries[k].AddrIPv4 = append(entries[k].AddrIPv4, rr.A)
                                                        delete(sentEntries, k)
						}
					}
				case *dns.AAAA:
					for k, e := range entries {
						if e.HostName == rr.Hdr.Name {
							entries[k].AddrIPv6 = append(entries[k].AddrIPv6, rr.AAAA)
                                                        delete(sentEntries, k)
						}
					}
				}
		}

I'm not sure of a way to wait for both IPV4/6 that isn't brittle. At least if both entries are sent then the user can implement their own wait and merge strategy.

jlitzingerdev avatar Aug 31 '17 18:08 jlitzingerdev

I am aware of this problem. As it is time-critical and dependent on the environment, it might be appear or not.

The idea to tackle this in future would be as follows: client.go would cache received service entries as usual. Instead of sending only a single update to the listening application (via Browse), this channel would also be used to announce an updated service entry, e.g. for an IPv4 entry that came in later. Like this, it is up to the application layer to wait until its needed fields are there.

As it is an API break, I would like to introduce a versioning/vendoring first, so people could stick to the old API. Otherwise, I am open for suggestions.

grandcat avatar Oct 29 '17 20:10 grandcat

FWIW, it's currently possible to do such a wait and combine using zeroconf.SelectIPTraffic(iptype ) in parallel with IPv6 and IPv4 and merging the lists as they come.

I think that it's a surprising behavior from a end-user point of view.

Wouldn't introducing a new method to return a slice of ServiceEntry after a timeout be a better way, hiding the merge operation from users? It's what I (most users?) work with in the end anyway.

sdumetz avatar Jan 24 '18 17:01 sdumetz

Nice workaround :) I am aware of the problem, but returning after a timeout is no reliable solution either. Depending on the network, delays are quite different.

Instead, ServiceEntrys should be updateable in the sense that any updates coming in though mDNS, should be pushed to the client by keeping a ServiceEntry internally and fill in the new values. An additional property should state whether an entry was updated.

grandcat avatar Jan 25 '18 11:01 grandcat

does anybody plan to fix this?

vtolstov avatar Sep 03 '18 07:09 vtolstov

We‘re seeing the same problem in https://github.com/evcc-io/evcc/discussions/1217#discussioncomment-2890670

andig avatar Jun 06 '22 13:06 andig

What is required to reproduce this issue? I'm using examples/register and examples/resolv. Even when I delay the resolv's ip4 query by ~100ms, I always receive the entire response including all IPs/services as advertised by register.

Is it correct to assume, that this issue will only trigger if there are two separate services announced, one for A and one for AAAA?

andig avatar Jun 07 '22 12:06 andig