Reputation: 793
I am trying to figure out how to test whether a STUN/TURN server is alive and properly responding to connections. Ideally this test would be performed from an external machine, just in case the STUN/TURN machine is down for this case should also be reported by the connectivity test.
Has anyone looked into this case in the past? What solutions would be recommended?
Upvotes: 66
Views: 127521
Reputation: 11
To check whether a TURN server is working or not you can use the turn server testing tool.
This testing tool lets you test between TURN server only, STUN server only or both TURN and STUN servers
Upvotes: 1
Reputation: 1414
Here's a cleaned up and modernized version of @mido's answer to check if the server is reachable. This is especially useful for private networks where an online tests fails.
It also prints the types of the candidates to the console.
Function
const checkTURNServer = (turnConfig, timeout = 5000) => {
return new Promise(async (resolve, reject) => {
const pc = new RTCPeerConnection({iceServers: [turnConfig]});
let promiseResolved = false;
// Stop waiting after X milliseconds and display the result
setTimeout(() => {
if(promiseResolved)
return;
promiseResolved = true;
resolve(false);
}, timeout);
// Create a bogus data channel
pc.createDataChannel('');
// Listen for candidates
pc.onicecandidate = ice => {
if(promiseResolved || ice === null || ice.candidate === null)
return;
console.log(ice.candidate.type);
if(ice.candidate.type === 'relay') {
promiseResolved = true;
resolve(true);
}
};
// Create offer and set local description
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
});
};
Usage
checkTURNServer({
urls: ['turn:' + location.host + ':3478', 'turns:' + location.host + ':5349'],
username: "1604441890:myUser",
credential: "myPassword",
credentialType: 'password'
}).then(
active => console.log('Is the TURN server active?', active ? 'Yes :)' : 'Not yet...keep trying ;)')
).catch(
e => console.error(e)
);
Upvotes: 0
Reputation: 3737
The earlier answers work with TURN if you use a username:password authentication mechanism for your coturn server. However, as is the case for BigBlueButton and others using static-auth-secret
as seen in /etc/turnserver.conf
, it is not possible to use https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/.
use-auth-secret
static-auth-secret=XXXX
One way to still test your TURN server is to install turnutils_uclient
with sudo apt install coturn
or your respective package manager. You can then subsequently test it with (replace XXXX and turn.example.com):
turnutils_uclient -T -W XXXX turn.example.com
This should result in the following output (redacted IP addresses 192.168.0.2 as internal client address and 1.2.3.4 as the server address):
0: IPv4. Connected from: 192.168.0.2:50988
0: IPv4. Connected to: 1.2.3.4:3478
0: allocate sent
0: allocate response received:
0: allocate sent
0: allocate response received:
0: success
0: IPv4. Received relay addr: 1.2.3.4:56365
....
4: Total transmit time is 4
4: Total lost packets 0 (0.000000%), total send dropped 0 (0.000000%)
4: Average round trip delay 32.500000 ms; min = 15 ms, max = 56 ms
4: Average jitter 12.600000 ms; min = 0 ms, max = 41 ms
On your TURN server, this should be mirrored in /var/log/coturn.log
.
Upvotes: 17
Reputation: 2532
npm i stun
const stun = require('stun');
stun.request('stun.l.google.com:19302', (err, res) => {
if (err) {
console.error(err);
} else {
const { address } = res.getXorAddress();
console.log('your ip', address);
}
});
Upvotes: 0
Reputation: 25054
Edit: A nice implementation in github.io taken from comment to another answer( choose "relay" in IceTransports value):
following Benjamin Trent's advice, I wrote the below code to test TURN server's connectivity, works on both firefox n chrome:
function checkTURNServer(turnConfig, timeout){
return new Promise(function(resolve, reject){
setTimeout(function(){
if(promiseResolved) return;
resolve(false);
promiseResolved = true;
}, timeout || 5000);
var promiseResolved = false
, myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection //compatibility for firefox and chrome
, pc = new myPeerConnection({iceServers:[turnConfig]})
, noop = function(){};
pc.createDataChannel(""); //create a bogus data channel
pc.createOffer(function(sdp){
if(sdp.sdp.indexOf('typ relay') > -1){ // sometimes sdp contains the ice candidates...
promiseResolved = true;
resolve(true);
}
pc.setLocalDescription(sdp, noop, noop);
}, noop); // create offer and set local description
pc.onicecandidate = function(ice){ //listen for candidate events
if(promiseResolved || !ice || !ice.candidate || !ice.candidate.candidate || !(ice.candidate.candidate.indexOf('typ relay')>-1)) return;
promiseResolved = true;
resolve(true);
};
});
}
example usage:
checkTURNServer({
urls: 'turn:127.0.0.1',
username: 'test',
credential: 'test'
}).then(function(bool){
console.log('is TURN server active? ', bool? 'yes':'no');
}).catch(console.error.bind(console));
You can run the below snippet to check:
var res = id('result');
id('button').onclick = function(){
res.innerHTML = 'Checking TURN Server...';
var url = 'turn:'+id('url').value+':'+id('port').value,
useUDP = id('udp').checked;
url +='?transport=' + (useUDP ? 'udp': 'tcp');
checkTURNServer({
urls: url,
username: id('name').value,
credential: id('pass').value
}, id('time').value).then(function(bool){
if(bool)
res.innerHTML = 'Yep, the TURN server works...';
else
throw new Error('Doesn\'t work');
}).catch(function(e){
console.log(e);
res.innerHTML = 'TURN server does not work.';
});
};
function checkTURNServer(turnConfig, timeout){
console.log('turnConfig: ', turnConfig);
return new Promise(function(resolve, reject){
setTimeout(function(){
if(promiseResolved) return;
resolve(false);
promiseResolved = true;
}, timeout || 5000);
var promiseResolved = false
, myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection //compatibility for firefox and chrome
, pc = new myPeerConnection({iceServers:[turnConfig]})
, noop = function(){};
pc.createDataChannel(""); //create a bogus data channel
pc.createOffer(function(sdp){
if(sdp.sdp.indexOf('typ relay') > -1){ // sometimes sdp contains the ice candidates...
promiseResolved = true;
resolve(true);
}
pc.setLocalDescription(sdp, noop, noop);
}, noop); // create offer and set local description
pc.onicecandidate = function(ice){ //listen for candidate events
if(promiseResolved || !ice || !ice.candidate || !ice.candidate.candidate || !(ice.candidate.candidate.indexOf('typ relay')>-1)) return;
promiseResolved = true;
resolve(true);
};
});
}
function id(val){
return document.getElementById(val);
}
#url{
width: 250px;
}
#port{
width: 70px;
}
<h1>
Test TURN server
</h1>
<div>
TURN URL: <input id='url' placeholder='example.com or xxx.yyy.rrr.ddd' />
Port: <input type='number' value='3478' id='port' placeholder='enter a port number' />
</div>
<div>
Transport: <input type="radio" name="transport" id="tcp" value="tcp" /> TCP
<input type="radio" name="transport" id="udp" value="udp" checked/>UDP
</div>
<div>
Username: <input id="name" placeholder="turn username" />
</div>
<div>
password: <input id="pass" placeholder="turn password" />
</div>
<div>
checking Timeout: <input type='number' id="time" placeholder="wait time before checking timeout" value=5000 />
</div>
<div>
<button id='button'>
Check TURN Server
</button>
</div>
<h4 id='result'></h4>
Upvotes: 60
Reputation: 1662
Version of @mido function to check the TURN and the STUN server both (original rejects stun-servers):
function checkTurnOrStun(turnConfig, timeout){
return new Promise(function(resolve, reject){
setTimeout(function(){
if(promiseResolved){
if (promiseResolved == 'STUN') resolve('STUN');
return;
}
resolve(false);
promiseResolved = true;
}, timeout || 5000);
var promiseResolved = false
, myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection //compatibility for firefox and chrome
, pc = new myPeerConnection({iceServers:[turnConfig]})
, noop = function(){};
pc.createDataChannel(""); //create a bogus data channel
pc.createOffer(function(sdp){
if(sdp.sdp.indexOf('typ relay') > -1){ // sometimes sdp contains the ice candidates...
promiseResolved = 'TURN';
resolve(true);
}
pc.setLocalDescription(sdp, noop, noop);
}, noop); // create offer and set local description
pc.onicecandidate = function(ice){ //listen for candidate events
if( !ice || !ice.candidate || !ice.candidate.candidate) return;
if (ice.candidate.candidate.indexOf('typ relay')!=-1) { promiseResolved = 'TURN'; resolve('TURN'); }
else if (!promiseResolved && (ice.candidate.candidate.indexOf('typ prflx')!=-1 || ice.candidate.candidate.indexOf('typ srflx')!=-1)){
promiseResolved = 'STUN';
if (turnConfig.url.indexOf('turn:')!==0) resolve('STUN');
}
else return;
};
});
}
checkTurnOrStun({"url": "stun:stunserver.org"}).then(function(result){
console.log(
result ? 'YES, Server active as '+result : 'NO, server not active');
}).catch(console.error.bind(console));
checkTurnOrStun({
url: 'turn:numb.viagenie.ca',
credential: 'muazkh',
username: '[email protected]'
}).then(function(result){
console.log(
result ? 'YES, Server active as '+result : 'NO, server not active');
}).catch(console.error.bind(console));
Upvotes: 1
Reputation: 551
If you want to check the stun server constantly you can execute this command with cron :
stunserver=stun1.l.google.com;stunport=19302;listenport=20000;echo -ne "\x00\x01\x00\x00YOGO\x59\x4f\x47\x4fSTACFLOW" | nc -u -p $listenport $stunserver $stunport -w 0;timeout 1 nc -l -u $listenport | head -c 32 | tail -c 4 | hexdump -e '/1 "%u" "."' | grep -o ".*[^.]" && echo yes-no-problem || mail -s "Error in Tun server:$stunserver:$stunport" root@localhost <<< 'Error in Tun server'
Replace root@localhost with your email to get the report.
stunserver=stun1.l.google.com;
stunport=19302;
listenport=20000; # Change freely this port if not available
Add it to cron and execute it every minute.
Upvotes: 6
Reputation: 3285
You could set up a 3rd party monitoring service (we use Monitis) or even your own machine to PING the server every minute from 1 or more locations. However this will only tell you if the server is reachable and not necessarily if the TURN/STUN application server still accepts & responds to TURN/STUN packets.
A server side monitoring library for STUN/TURN would make a great GitHub project.
Upvotes: -1