Reputation: 674
Below is a quote from this GitHub project STUN IP Address requests for WebRTC.
These request results are available to JavaScript, so you can now obtain a users local and public IP addresses in JavaScript.
I did as suggested in the following quote.
Here is the annotated demo function that makes the STUN request. You can copy and paste this into the Firefox or Chrome developer console to run the test.
The result was a script error with output results of undefined and 192.168.x.x. It did correctly detect the internal home IP address of one of my laptops.
The error was:
Uncaught TypeError: Cannot read property '1' of null at handleCandidate (:38:47) at RTCPeerConnection.pc.onicecandidate (:52:13)
The error occurred here:
var ip_addr = ip_regex.exec(candidate)[1];
More data
For the internal network IP case that worked, the candidate value was: candidate:1178812653 1 udp 2113937151 192.168.x.x 52663 typ host generation 0 ufrag syTM network-cost 50.
Corrected update
I had the console.log after the handleCandidate, which was why I did not see the second result. I have updated the code with console.log entries.
The second ice event fails because an IPv6 address is returned instead of the client's public IP address:
2299073356 1 udp 2113932031 2001::9d38:953c:1c28:17c0:xxx:xxx 52281 typ host generation 0 ufrag NQtJ network-cost 50
Question:
Is this approach still viable for detecting the client's public IP address? If it is, do you know what is broken with the GitHub code?
Included Code here:
//get the IP addresses associated with an account
function getIPs(callback){
var ip_dups = {};
//compatibility for firefox and chrome
var RTCPeerConnection = window.RTCPeerConnection
|| window.mozRTCPeerConnection
|| window.webkitRTCPeerConnection;
var useWebKit = !!window.webkitRTCPeerConnection;
//bypass naive webrtc blocking using an iframe
if(!RTCPeerConnection){
//NOTE: you need to have an iframe in the page right above the script tag
//
//<iframe id="iframe" sandbox="allow-same-origin" style="display: none"></iframe>
//<script>...getIPs called in here...
//
var win = iframe.contentWindow;
RTCPeerConnection = win.RTCPeerConnection
|| win.mozRTCPeerConnection
|| win.webkitRTCPeerConnection;
useWebKit = !!win.webkitRTCPeerConnection;
}
//minimal requirements for data connection
var mediaConstraints = {
optional: [{RtpDataChannels: true}]
};
var servers = {iceServers: [{urls: "stun:stun.services.mozilla.com"}]};
//construct a new RTCPeerConnection
var pc = new RTCPeerConnection(servers, mediaConstraints);
function handleCandidate(candidate){
//match just the IP address
var ip_regex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/
console.log("candidate in handler" + candidate);
var ip_addr = ip_regex.exec(candidate)[1];
//remove duplicates
if(ip_dups[ip_addr] === undefined)
callback(ip_addr);
ip_dups[ip_addr] = true;
}
var count = 1;
//listen for candidate events
pc.onicecandidate = function(ice){
console.log("ice event " + count + ": " + ice)
//skip non-candidate events
var propertyCount = 1
if(ice.candidate){
console.log("ice candidate " + count + ": " + ice.candidate.candidate);
handleCandidate(ice.candidate.candidate);
}
count++;
};
//create a bogus data channel
pc.createDataChannel("");
//create an offer sdp
pc.createOffer(function(result){
//trigger the stun server request
pc.setLocalDescription(result, function(){}, function(){});
}, function(){});
//wait for a while to let everything done
setTimeout(function(){
//read candidate info from local description
var lines = pc.localDescription.sdp.split('\n');
lines.forEach(function(line){
if(line.indexOf('a=candidate:') === 0)
handleCandidate(line);
});
}, 1000);
}
//Test: Print the IP addresses into the console
getIPs(function(ip){console.log(ip);});
Upvotes: 0
Views: 3300
Reputation: 5628
Yes, making STUN requests with WebRTC to determine the client's IP address is still a viable approach. To determine what's wrong with the particular code you have posted, try dumping candidate
in handleCandidate()
to see why the ip_regex
regexp is choking:
function handleCandidate(candidate){
console.log(candidate);
...
}
Edit: It looks like the problem was with the STUN server used. I've replaced stun.services.mozilla.com
with stun.l.google.com:19302
and I'm getting my public IP address in the console, alongside the private one.
Upvotes: 1