I am creating an application acquiring the client's IP address frequently (about per second) in JavaScript.
Because of a possible BAN, I do not want to use services providing the client's IP address such as l2.io if I can.
Also, I do not want to prepare the API providing the client's IP address on my web server if I can.
I found WebRTC helpful to solve the problem and tried writing the function acquiring the client's IP address using WebRTC.
It seems that the function works well but I found the function causing a memory leak.
The following is my code causing a memory leak in Chrome 47.
function getIpAddress() {
return new Promise((resolve, reject) => {
var RTCPeerConnection = window.RTCPeerConnection
|| window.mozRTCPeerConnection
|| window.webkitRTCPeerConnection;
var pc = new RTCPeerConnection({
iceServers: [{urls: "stun:stun.services.mozilla.com"}]
}, {
optional: [{RtpDataChannels: true}]
});
pc.onicecandidate = ice => {
if (pc.iceGatheringState == "complete" && ice.candidate === null) { // waiting for completion
resolve(pc.localDescription.sdp.split("\r\n").filter(line => line.indexOf("c=") == 0)[0].split(" ")[2]);
if (pc.signalingState != "closed") pc.close();
}
};
pc.createDataChannel("");
pc.createOffer(result => {
pc.setLocalDescription(result, function () {}, function () {});
}, function () {});
});
}
// calling getIpAddress many times causes a memory leak
(function loop() {
getIpAddress().then(loop);
})();
Do you know which part of getIpAddress causes a memory leak?
If you do not know, is there another way to acquire the client's IP address without a memory leak?
Note:
I have already tried webrtc-ips but it seems that webrtc-ips also causes a memory leak.
I'm not sure that looping the WebRTC request is planned "by design".
Do you have a memory leak when doing the function only once ?
Normally, you need to catch the IP only once by client.
In my firefox, no memory leak, only a big processor charge because of the loop.
If you can use PHP, acquiring IP address would be very easy.
Do you know which part of getIpAddress causes a memory leak?
It seems that instantiating RTCPeerConnection many times causes a memory leak.
The following code causes a memory leak and may crash Chrome 47 and Firefox 43.
while (true) {
var RTCPeerConnection = window.RTCPeerConnection
|| window.mozRTCPeerConnection
|| window.webkitRTCPeerConnection;
var pc = new RTCPeerConnection({});
pc.close();
}
Related
Firefox and Chrome have implemented WebRTC that allow requests to STUN servers be made that will return the local IP address of the client.
The javascript code below outputs the local IP address of the client:
window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //compatibility for firefox and chrome
var pc = new RTCPeerConnection({iceServers:[]}), noop = function(){};
pc.createDataChannel(""); //create a bogus data channel
pc.createOffer(pc.setLocalDescription.bind(pc), noop); // create offer and set local description
pc.onicecandidate = function(ice){ //listen for candidate events
if(!ice || !ice.candidate || !ice.candidate.candidate) return;
var myIP = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/.exec(ice.candidate.candidate)[1];
console.log('my IP: ', myIP);
pc.onicecandidate = noop;
};
Is it possible to obtain the MAC address also?
No you cannot get the MacAdress with Javascript in the Browser. For very good reasons. It would be a big security risk.
The MAC address uniquely identifies the running computer, cellphone...basically every device, an internal IP does not.
You need to use Electron or Node for that, if you want to do it with Javascript.
Use ARP using any way. You can use OS API or you can spawn command line tool or high level API or library of you favorite programming language.
We are unable get the public IP address of a client machine. It is showing the sdp is undefined while executing the code. Below is the code.
Get current IP in JS (no third party services)
https://github.com/diafygi/webrtc-ips
//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})/
var ip_addr = ip_regex.exec(candidate)[1];
//remove duplicates
if(ip_dups[ip_addr] === undefined)
callback(ip_addr);
ip_dups[ip_addr] = true;
}
//listen for candidate events
pc.onicecandidate = function(ice){
//skip non-candidate events
if(ice.candidate)
handleCandidate(ice.candidate.candidate);
};
//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);});
While executing the code we are getting this error message:
'Cannot read property 'sdp' of null'
The example code you are referencing is outdated and their demo page is no longer working either on recent Chrome versions:
https://diafygi.github.io/webrtc-ips/
Furthermore, it seems to use features that are not supported by some browsers.
I don't know your requirements but it is quite standard to send a request to a server in order to discover the public IP of a client. The server looks at headers (e.g. x-forwarded-for, this depends on the Web server used) and sends it back to the requester.
There also exist services such as Ipregistry (disclaimer: I run the service) that do it for you and return even more interesting information: client IP address, location, currency, threat data, etc.
As of this writing, you cannot leak the private IP address of your users.
However, I found a github repo webrtc-ip which can leak a user's public IP address using WebRTC. This is powerful because you cannot trace it, as nothing is shown in the Networks tab.
Sadly, this leak does not work for private IPs, due to the gradual shift to mDNS (at least for WebRTC), which is completely in this great blog. Regardless, here's a working demo:
https://webrtc-ip.herokuapp.com/
I am not sure if this leaks your true IP address regardless of a proxy, but feel free to test it out.
If you look at the repo you referenced, the issues clearly state that the repo does not work and that the repository is not being maintained.
I am not able to capture ip address of ipad devices through JS code.This works fine in Windows system
Below is the code:
var findIP = new Promise(r => {
var w = window,
a = new(w.RTCPeerConnection || w.mozRTCPeerConnection || w.webkitRTCPeerConnection)({
iceServers: []
}),
b = () => {};
a.createDataChannel("");
a.createOffer(c => a.setLocalDescription(c, b, b), b);
a.onicecandidate = c => {
try {
c.candidate.candidate.match(/([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g).forEach(r)
} catch (e) {}
}
})
findIP.then(function(ip) {
console.log(ip);
})
Under recent versions of IOS, Apple is intentionally obscuring the local IP address with the intention of making it hard to fingerprint a device. This is quite unfortunate, because it would be nice to know the IP address so that i could look for peers on the LAN.
The address that IOS returns os of the type: 9befcb75-3f5e-48f5-9201-744fc7ba406c.local
which is probably some random number that is useless.
Also it was not necessary to parse the candidate field, as there is a property in the candidate record called 'address' that normally has the IPv4 address as a string.
This code works on Mac desktop, and Android okay, but alas is useless on the iPad/iOS devices, which are rather numerous.
here's a javascript function you can use to get IP address:
$.getJSON('http://www.geoplugin.net/json.gp?jsoncallback=?', function (data) {
console.log('ipAddress:', data.geoplugin_request);
});
How can I get the local client IP using WebRTC.
I don't need the REMOTE_ADDR of the client but his local network IP.
I've seen this before on websites like sharedrop.com, it recognizes computer within the same network using WebRTC.
In PHP I do this to get the clients remote IP:
<?php
echo $_SERVER["REMOTE_ADDR"]; // which would return 72.72.72.175
?>
I looked through stackoverflow but every question is answered using the remote addr.
How can I get my local IP (192.168.1.24 for example) with JavaScript instead of the remote addr.
UPDATE
Unfortunately the below answer no longer works as browsers have changed this behavior due to security concerns. See this StackOverflow Q&A for more details.
where I took code from --> Source
You can find a demo at --> Demo
I have modified the source code, reduced the lines, not making any stun requests since you only want Local IP not the Public IP, the below code works in latest Firefox and Chrome:
window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //compatibility for firefox and chrome
var pc = new RTCPeerConnection({iceServers:[]}), noop = function(){};
pc.createDataChannel(""); //create a bogus data channel
pc.createOffer(pc.setLocalDescription.bind(pc), noop); // create offer and set local description
pc.onicecandidate = function(ice){ //listen for candidate events
if(!ice || !ice.candidate || !ice.candidate.candidate) return;
var myIP = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/.exec(ice.candidate.candidate)[1];
console.log('my IP: ', myIP);
pc.onicecandidate = noop;
};
what is happening here is, we are creating a dummy peer connection, and for the remote peer to contact us, we generally exchange ice candidates with each other. And reading the ice candiates we can tell the ip of the user.
You can use this version on modern browser (with Promises and async / await)
// minified onliner, 219b
const ip = await new Promise((s,f,c=new RTCPeerConnection(),k='candidate')=>(c.createDataChannel(''),c.createOffer(o=>c.setLocalDescription(o),f),c.onicecandidate=i=>i&&i[k]&&i[k][k]&&c.close(s(i[k][k].split(' ')[4]))))
Or, un-minified:
// cleaned, 363b
const ip = await new Promise((resolve, reject) => {
const conn = new RTCPeerConnection()
conn.createDataChannel('')
conn.createOffer(offer => conn.setLocalDescription(offer), reject)
conn.onicecandidate = ice => {
if (ice && ice.candidate && ice.candidate.candidate) {
resolve(ice.candidate.candidate.split(' ')[4])
conn.close()
}
}
})
Im trying to create web Workers and post messages to them in cycle:
array.forEach(function (data) {
this.createWorker();
this.workers[this.workersPointer].postMessage({task: 'someTask', data: string});
}, this);
createWorker function:
createWorker: function () {
this.workersPointer++;
var worker = this.workers[this.workersPointer] = new Worker('Worker.js'),
storage = this;
worker.onmessage = function (event) {
if (event.data.error) {
storage[event.data.task + 'Errback'](event.data.error);
}
else {
storage[event.data.task + 'Callback'](event.data.data);
}
};
worker.onerror = function (error) {
storage.workerErrback(error);
};
}
Worker code:
self.addEventListener('message', function (event) {
self.postMessage({
data: data,
error: err,
task: event.data.task
});
}, false);
It works perfectly in Google Chrome. When I'm trying to run it in Firefox, it works only 20 times. Do Firefox web workers have a limit? I can't find information about it on mozilla.org. If there is no limit, what's the problem? Any ideas?
Just did some test of my own. For this, i changed the code a little bit:
Cycle:
for(var i=0;i<200;i++){
this.createWorker();
this.workers[this.workersPointer].postMessage({task: 'someTask', number:i});
};
createWorker function:
this.workers =[];
this.workersPointer = 0;
storage=[];
var createWorker= function () {
workersPointer++;
var myPointer = workersPointer;
var worker = this.workers[this.workersPointer] = new Worker('Worker.js');
worker.onmessage = function (event) {
if (event.data.error) {
alert(event.data.error);
}
else {
document.cookie=event.data.task+"["+myPointer+"]="+event.data.number;
}
};
worker.onerror = function (event) {
alert("Error: " + event.error);
};
}
Worker:
onmessage = function(event) {
postMessage({number:event.data.number*2, task: event.data.task});
};
After i run this, in chrome i got 66 cookies (including a nice blue crash window), in firefox i got 20. So both browsers seem to have worker limitations.
EDIT:
In Opera i get a console message:
Maximum number of Web Worker instances(16) exceeded for this window.
There is a setting in Firefox, called "dom.workers.maxPerDomain" which is by default 20.
However, there might not be any real performance gain in using more workers than you have cores in the computer. With a modern computer today that has hyper threading, I think using around 8 workers would be sufficient. Otherwise you might cause to much context switching that would instead introduce a bottleneck.
It all depends though, what you want to achieve.
For futher reference check out in Firefox
about:config
There's a parameter called :
dom.workers.maxPerDomain
Wich (at least in FF 33) is set to a default value of 20.
gl.
And as noted on this other stackoverflow question:
Each browser has web workers limitations (Firefox has 20, Chrome 60+, Opera 16); however, you can change it in Firefox -> dom.workers.maxPerDomain; as for your actual question, if you can or cannot avoid this limitation, I'm not sure. "Workers (as these background scripts are called herein) are relatively heavy-weight, and are not intended to be used in large numbers." Can you give an exact situation where you would want to use more than 20 workers? – Marius Balaban Nov 26 '12 at 22:34
I also played around with workers and tried to find an optimum for my case (encryption of strings). It was 8 too.
Similar question and discussion: Number of Web Workers Limit