Skip to content

Conversation

@Nardol
Copy link
Contributor

@Nardol Nardol commented Dec 12, 2025

Fixes #1987

Context
The Android client could not connect to an IPv6-only TeamTalk server. On some Android devices/networks, name resolution via getaddrinfo() can also fail transiently (EAI_AGAIN), making connections unreliable even when IPv6 connectivity is available.

What this changes

  • Fix UDP socket creation to match the resolved address family (AF_INET/AF_INET6) instead of always forcing IPv4.
  • Make native resolution more robust on Android: retry on EAI_AGAIN, avoid problematic getaddrinfo() flags/constraints, and handle numeric IPv4/IPv6 literals without relying on the platform resolver.
  • On Android, prefer DnsResolver (system ordering) and only fall back to UDP/53 DNS as a last resort (resolver timeouts/failures), with caching and logs limited to the fallback path.

Compatibility / Risk

  • Keeps IPv4-only and dual-stack working.
  • The UDP/53 fallback is intentionally last-resort (unencrypted) to avoid total failure when the system/NDK resolver is flaky.

Testing

  • Verified on device: connects to IPv6-only server, dual-stack hostname (respects system preference), and IPv4-only server.

@Nardol Nardol force-pushed the android-ipv6-udp branch 2 times, most recently from 062df2a to d8224e4 Compare December 12, 2025 22:36
@hwangsihu hwangsihu requested a review from Copilot December 13, 2025 06:35
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Fix native UDP socket family for IPv6 (Android)
- Make address resolution robust on Android (IPv6 literals + getaddrinfo quirks)
- Add Android-side DNS fallback for Private DNS resolver timeouts (udp/53)
- Respect system address ordering and cache DNS results

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>
@Nardol
Copy link
Contributor Author

Nardol commented Dec 14, 2025

Copilot Review suggestions applied, except hostname and port are two different fields and a TeamTalk server can be on a local network.

@bear101
Copy link
Contributor

bear101 commented Dec 15, 2025

Is there a way to avoid changing files in TeamTalk DLL? It appears strange that it works on all other platform than Android.

Is it possible to make the DNS lookup in TeamTalkAndroid and always pass an IP-address (instead of a hostname) to TT_Connect()?

@Nardol
Copy link
Contributor Author

Nardol commented Dec 15, 2025

I tried an Android-only approach (no changes in TeamTalk DLL / TeamTalkLib): resolve hostnames in TeamTalkAndroid (DnsResolver on API 29+, fallback to InetAddress) and always pass IP literals to TT_Connect().
Result: even when we successfully resolve and pass an IPv6 literal, the connection still fails on Android.
Example logs:

DNS: Candidates for ipv6.hostname.tld -> [2001:xxxx:yyyy:zzzz::]
connect() to 2001:xxxx:yyyy:zzzz:: ... -> false

And even when entering the IPv6 literal directly:

connect(): host=2001:xxxx:yyyy:zzzz:: candidates=[2001:xxxx:yyyy:zzzz::]
connect() ... -> false

So this is not just a hostname/DNS issue. The Android native layer (JNI / underlying socket/connect path) appears to fail for IPv6, meaning an Android-only DNS workaround cannot fix #1987 on its own.

I pushed a branch called android-ipv6-java-only to test.

Proposed compromise:
We could keep the Android-side hostname resolution as a fallback to improve dual-stack hostname handling (and avoid DNS issues/timeouts), but it won’t fix IPv6-only/IPv6 literal connects.
The actual fix should be in the native layer so that TT_Connect() works with IPv6 literals on Android (proper IPv6 parsing + socket connect), ideally with additional Android-native logging (errno/family/address) to validate the fix.

const auto host_utf8_full = UnicodeToUtf8(host);
const char* host_utf8_cstr = host_utf8_full.c_str();
std::string host_utf8(host_utf8_cstr != nullptr ? host_utf8_cstr : "");
if (!host_utf8.empty() && host_utf8.front() == '[' && host_utf8.back() == ']')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting IPv6-address in brackets seems to be a Java-thing. The stripping should be done in Java, so host is just an IP-address. Should I look into fixing this or can you?

if (curr->ai_family == AF_INET6)
{
addr.in6_.sin6_port = encode ? ACE_NTOHS(port) : port;
addr.in6_.sin6_port = encode ? ACE_HTONS(port) : port;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

encode is always true, so it can be deleted.

{
errno = ADDRINFOERROR;
#if defined(ANDROID)
__android_log_print(ANDROID_LOG_WARN, "bearware-native",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use MYTRACE instead. It goes to Android log.

const int ADDRINFOERROR = ACE_OS::getaddrinfo(UnicodeToUtf8(host).c_str(), nullptr, &hints, &res);
// Android's resolver may return EAI_AGAIN transiently even when DNS works
// (e.g. during network transitions). Retry a few times before giving up.
int ADDRINFOERROR = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uppercase is for constants. ADDRINFOERROR is not a constant.

import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.net.DatagramPacket;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the DNS code to a separate class in utils. It is unrelated to TeamTalkService.

bear101 added a commit that referenced this pull request Dec 30, 2025
This change addresses the issue #3045 where getaddrinfo() may return
EAI_AGAIN on Android. By continuously calling TeamTalk5.connect() in
TeamTalkService it has the same effect as calling getaddrinfo()
multiple times.

Hopefully this change resolves #3045.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TeamTalk for Android cannot connect to IPv6 servers

2 participants