Issue with TURN Server Binding Request in libpeer
I have successfully cross-compiled libpeer for QNX and am currently testing video streaming between:
- A QNX-based application running libpeer
- A WebRTC-based web client running on macOS
For NAT traversal, I am using Coturn as my STUN/TURN server.
Observations --> STUN Server Works as Expected When using a STUN server in libpeer:
- ICE candidate collection works correctly.
- The QNX app generates an SDP offer and shares it with the web client.
- After receiving the SDP answer, streaming starts properly.
- The binding request is sent and successfully receives a binding response.
--> TURN Server Issue: Binding Request Fails When using a TURN server in libpeer:
1.ICE candidate collection works correctly, and relay candidates are obtained. 2. The QNX app generates an SDP offer and shares it with the web client. 3. The web client responds with an SDP answer.
Issue: After receiving the SDP answer, the binding request is sent, but it fails.
After some time, the connection state changes to "failed".
I have explicitly configured the web client to use only relay candidates for testing. Despite trying various configurations, the issue persists only when using a TURN server.
Debugging Attempts & Findings Authentication:
- ICE candidate collection successfully authenticates using the provided username, realm, and credentials in peerconfig.
- However, for the binding request, the username is taken from the remote and local ufrag instead of the explicitly provided credentials.
- The request is marked with short-term credentials in the code.
Coturn Server Logs:
- While ICE candidate collection succeeds, the binding request always results in a 401 Unauthorized error.
- This behavior is unexpected since initial authentication is successful, but subsequent binding requests fail.
Questions
- Does libpeer fully support TURN servers for relay candidates, or is this an expected limitation?
- Has libpeer been tested with specific TURN servers? If so, which ones?
- Why does the binding request fail with a 401 error despite successful authentication during ICE candidate collection?
- Are there any known workarounds for handling TURN authentication issues in libpeer?
Any insights or guidance would be greatly appreciated!
TURN's username and password are used in function agent_create_stun_addr. The function is to obtain the relay address through the TURN server and create the corresponding ICE candidate.
Thank you for the response. I’d like to clarify the exact problem I’m facing:
When libpeer (QNX app) and the web client (Mac) both use the same TURN server, it does not work.
However, when libpeer uses a STUN server and the web client uses a TURN server, it works fine.
The issue happens only when libpeer is also using TURN.
Issue Recap ICE candidate collection succeeds in libpeer when using TURN.
The SDP offer is created and shared with the web client, which replies with an SDP answer.
After receiving the SDP answer, the binding request fails, and eventually, the connection state changes to “failed.”
Coturn logs show a 401 Unauthorized error for the binding request, even though initial authentication during ICE candidate collection was successful.
Questions: Has libpeer been tested when both peers (libpeer and web client) are using the same TURN server?
Is there a known issue where libpeer fails at the binding request stage when using a TURN relay?
Could there be a mismatch in how libpeer constructs TURN authentication for the binding request compared to ICE candidate gathering?
I would appreciate any insights or suggestions on this issue. Thank you!
Hi, I’m not encountering the same issue in my environment, but there appears to be some conflicting logic that could be causing the problem. I’ll investigate further and update you.
Request for More Details on Your Setup
Thanks for looking into this! Since you're not encountering the same issue, I’d like to understand your setup better to see if there are any differences.
Are you using the latest code from the main branch, or have you made any modifications?
How did you set up your TURN server?
Which TURN server are you using?
What configuration options did you set?
How are you handling authentication (e.g., long-term vs. short-term credentials)?
Can you share the exact steps you followed for your setup? That would help me replicate your environment and identify what might be different.
I appreciate your help in troubleshooting this. Thanks!
Hi @topworldcoder, thanks for the great help on this project!
I wanted to share my use case and ask for some clarification.
I'm using a TURN server in my setup. When I run the web client in Chrome with the TURN server configured, it gathers ICE candidates including private IP (host), srflx, and relay addresses. On the other side, I'm using libpeer with only a STUN server configured, and it gathers host and srflx candidates.
In this setup, video streaming works fine — the libpeer client sends binding requests to the web client’s relay address, and everything connects and streams as expected.
However, when I flip the setup and configure TURN on the libpeer client (with correct credentials), libpeer fails to bind any of the ICE candidates, and the connection moves to a failed state. The same setup that worked with TURN on the web client doesn’t work with TURN on libpeer.
My questions are:
Is this the expected behavior with libpeer?
If not, have you successfully used libpeer with TURN credentials alone and been able to stream video?
If yes, could you please share any details on TURN server configuration, ICE candidate handling, or anything special you needed to make it work?
Thanks in advance!
Hi, this behavior likely occurs because WebRTC implementations in browsers (e.g., Chrome’s libwebrtc) automatically treat a configured TURN server as a STUN server, thereby generating relay candidates and srflx candidates as well.
In contrast, libpeer follows a stricter policy to differentiate STUN/TURN usage—when only a TURN server is configured, it does not request srflx candidates.
If you need to customize libpeer to fetch srflx candidates even with a single TURN server, modify the following code: In the agent_gather_candidate function, after calling agent_create_turn_addr, add a call to agent_create_stun_addr.
PS. A reply from the author(sepfy) could offer a more definitive clarification. @sepfy
INFO /opt/newdisk/libpeer/src/peer_connection.c 345 ice server: stun:stun.l.google.com:19302 INFO /opt/newdisk/libpeer/src/ports.c 140 Resolved stun.l.google.com -> 74.125.250.129 INFO /opt/newdisk/libpeer/src/agent.c 269 Resolved stun/turn server 74.125.250.129:19302 INFO /opt/newdisk/libpeer/src/mdns.c 157 Resolved 196009dd-8768-479f-8536-714805de327f.local -> 192.168.1.199 INFO /opt/newdisk/libpeer/src/mdns.c 85 invalid length INFO /opt/newdisk/libpeer/src/mdns.c 78 not a mDNS response INFO /opt/newdisk/libpeer/src/mdns.c 167 Failed to resolve hostname WARN /opt/newdisk/libpeer/src/ice.c 125 Failed to resolve mDNS address
Hello, I apologize for the confusion caused by my previous reply. After reviewing several WebRTC implementations and related source codes, I realized that my earlier explanation was incomplete. Please allow me to clarify the correct behavior.
First, it is possible to establish an ICE connection when only one side uses TURN while the other side connects directly without TURN. In such cases, the TURN client must create permissions for the remote peer’s address using CreatePermission requests. After that, the client can probe the connectivity through TURN Send Indications to verify whether the ICE connection is valid. Once the connection path is established, data exchange can proceed via TURN ChannelBind + ChannelData messages. It is also necessary to refresh the TURN allocation periodically and send regular STUN Binding messages to maintain NAT mappings. If the connection is established using the Indication mode, the client should continue sending Indications containing STUN Binding messages. When operating in relay mode, the client also needs to periodically refresh the ChannelBind state to keep the channel active.
Second, it is possible to collect peer reflexive (prflx) candidates, which can help improve ICE connectivity success rates by discovering alternate network paths. There are typically two scenarios for obtaining prflx candidates:
When a TURN Send Indication message is received and its Data attribute contains a valid STUN Binding message, and the Indication also includes an XOR-PEER-ADDRESS attribute, the peer address specified there can be used as a prflx candidate.
When a STUN Binding Request is received directly (not encapsulated inside a TURN Indication) and the source IP/port of that message does not match any existing remote candidate from the peer’s SDP, that address should be recorded as a new prflx candidate.
For more insight, you can analyze existing WebRTC implementations using Wireshark to capture STUN/TURN/ICE traffic. This will help visualize the exact message flow and confirm the connection establishment sequence in detail.
Best of luck with your implementation!