Highdown
Highdown

Reputation: 674

STUN IP Address Javascript

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

Answers (1)

jamix
jamix

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

Related Questions