Is it possible to get user internal IP address by using WebRTC in browser.
Here browserleaks webrtc I can see my Local IP address but this address was extracted on client side via JS script.
Here is minimal JS example how to achieve this
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];
document.querySelector('#output').innerHTML=myIP
//console.log('my IP: ', myIP);
pc.onicecandidate = noop;
};
<div id="output">
</div>
As we can see in code we extract ip from onicecandidate event.
If we will look at WebRTC connection flow we can see that ICE candidates exchanges between peers via signal Channel
So is this possible for STUN/TURN server to get information of internal IP address? (I doubt this is possible, but just to check)
Does specially crafted peer client able to get internal IP address of another peer during connection phase or when it exchange ICE candidates with another peer ?
The question is mostly about security concern
Your flow looks right and secure under certain assumptions:
The browser supports Trickle ICE mechanism (all modern browsers do support)
You don't wait for ice gathering to be complete before sending the SDP
You send the SDP from createOffer/createAnswer method, instead of RTCPeerConnection.localDescription.
If the browser does not support ICE trickle, it's disabled by the code or if you wait for ice gathering to be completed. The candidates will be added also to the local SDP which might still expose your internal IP.
Here is the working snippet for current chrome (v73):
const canvas = document.createElement('canvas');
const track = canvas.captureStream(1).getTracks()[0];
const pc = new RTCPeerConnection();
pc.addTrack(track);
pc.createOffer().then(offer => pc.setLocalDescription(offer))
pc.onicecandidate = () => {};
pc.onicegatheringstatechange = () => {
if(pc.iceGatheringState === 'complete') {
const sdp = pc.localDescription.sdp;
const sdpRows = sdp.split('\n')
const candidates = sdpRows.filter(row => row.indexOf('a=candidate') === 0);
console.log(candidates)
}
}
Related
I can not get postman to connect to the server using websockets.
const port =5001;
io.on("connection", (socket) => {
console.log("Client connected",socket.id);
socket.emit("handshake","connected to backend");
socket.on("test", (data)=>{
console.log("test data is:",data);
socket.emit("test", "server heard you!")
});
}
in postman the request address is:
ws://localhost:5001/socket.io/?transport=websocket
the symptoms are: postman says it's connected. but if I try to send anything - it disconnects after a timeout.
if I set the reconnection attempts to 1, it will automatically reconnect when it disconnects...
but I don't think it's actually connecting - because nothing is happening on the server (no new client connected message)
the format of messages I have also experimented with, to no avail.
42["test","i hear you"]
42[test,i hear you]
["test":"i hear you"]
{"test":"I hear you"}
42{"test":"I hear you"}
{"event":"test","data":"I hear you"}
42{"event":"test","data":"I hear you"}
42["event","test","data","I hear you"]
["event","test","data","I hear you"]
I have inspected the console results, and have not found leads there yet. what could I be missing?
You are using socket.io as WebSocket and that does not work because socket.io is not an implementation of websocket.
From official socket.io documentation:
Socket.IO is NOT a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds additional metadata to each packet. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a plain WebSocket server either.
// WARNING: the client will NOT be able to connect!
const socket = io("ws://echo.websocket.org");
Source: https://socket.io/docs/v4#What-Socket-IO-is-not
Postman v8.10.0 added support for Socket.IO, read more.
Just enter ws://localhost:5001 as the connection URL and hit Connect.
Also, you can configure the client version (default: v3), handshake path (default: /socket.io), and other reconnection configurations in the request settings.
Because you don not add listener. Add listener "handshake" to postman. You will receive message.
This is my code:
io.on('connection', () => {
console.log('user connected');
setInterval(() => {
io.emit('msg', { data: [1, 2, 3] });
}, 5000);
});
Working on socket.io for the first time and trying to get it up and going, I can make the request and I have the server up and going, here is the server in node.
const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
app.get("/",function (req,res){
res.send("Hello you socket loving bastard!");
});
io.on('connection', socket => {
console.log('user connection', socket);
io.emit('You got someone!', {user: "me"});
});
io.on('close', socket => {
console.log(socket);
});
http.listen(9090, () => {
console.log("Node starting on 9090 for websockets!")
});
Using vue-native-websocket I have this ...
Vue.use(Socket, 'ws://localhost:9090/', {
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1500
});
The console in the browser says:
build.js?b408:1 WebSocket connection to 'ws://localhost:9090/' failed: Connection closed before receiving a handshake response
The server says nothing in the console at all, however, it will serve the get request
Well... the issue is that I'm using vue-native-websocket Socket.io is NOT a native websocket handler and adds extra header information which was lacking apparently. I switches to just using ws in node and it works fine.
From the Socket.io docs.
Socket.IO is NOT a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the packet id when a message acknowledgement is needed. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a WebSocket server either.
I have an Apache webserver with a valid SSL certificate. It runs my web application on it. Let's call it Server A.
Then I have a second server running a Node-Js server with a valid SSL certificate. There also socket.IO runs. And this one we call Server B.
A client requests the web application at server A and gets the desired page displayed. If the page is set up at the client, a connection to server B is established via websockets. If another client should change something on the page, it will be adapted for all currently connected clients.
Websockets work as desired. As long as the page is accessed via a computer browser.
If I now go to the website with my smartphone (Iphone 7) via Safari or Chrome (WLAN), no connection to the websocket server (Server B) is established.
Then I set up a small websocket example on http without encryption.
There the websockets work on the smartphone browser.
I hope I could describe my problem understandably. I am very grateful for hints, examples or similar.
// This script run on my Server
const fs = require('fs');
const server = require('https').createServer({
key: fs.readFileSync('myserver.key', 'utf8'),
cert: fs.readFileSync('myserver.cer', 'utf8'),
passphrase: ''
});
let io = require('socket.io')(server);
server.listen(3003);
io.on('connection', function (socket) {
console.log("User Connected connect " + socket.id);
socket.on('disconnect', function () {
console.log("User has close the browser " + socket.id);
});
socket.on('feedback', function (data) {
io.sockets.emit('feedback', data);
});
});
// On Clientsite
socket = io.connect('wss://adressOfServer:3003', {
// secure: true,
transports: ['websocket'],
upgrade: false,
rejectUnauthorized: false
//Here I have already tried many combinations
});
socket.on('connect_error', function (error) {
// alert(error);
});
I am looking into WebRTC but I feel like I'm not understanding the full picture. I'm looking at this demo project in particular: https://github.com/oney/RCTWebRTCDemo/blob/master/main.js
I'm having trouble understanding how I can match 2 clients so that Client A can see Client B's video stream and vice versa.
This is in the demo:
function getLocalStream(isFront, callback) {
MediaStreamTrack.getSources(sourceInfos => {
console.log(sourceInfos);
let videoSourceId;
for (const i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if(sourceInfo.kind == "video" && sourceInfo.facing == (isFront ? "front" : "back")) {
videoSourceId = sourceInfo.id;
}
}
getUserMedia({
audio: true,
video: {
mandatory: {
minWidth: 500, // Provide your own width, height and frame rate here
minHeight: 300,
minFrameRate: 30
},
facingMode: (isFront ? "user" : "environment"),
optional: [{ sourceId: sourceInfos.id }]
}
}, function (stream) {
console.log('dddd', stream);
callback(stream);
}, logError);
});
}
and then it's used like this:
socket.on('connect', function(data) {
console.log('connect');
getLocalStream(true, function(stream) {
localStream = stream;
container.setState({selfViewSrc: stream.toURL()});
container.setState({status: 'ready', info: 'Please enter or create room ID'});
});
});
Questions:
What exactly is MediaStreamTrack.getSources doing? Is this because devices can have multiple video sources (e.g. 3 webcams)?
Doesn't getUserMedia just turn on the client's camera? In the code above isn't the client just viewing a video of himself?
I'd like to know how I can pass client A's URL of some sort to client B so that client B streams the video coming from client A. How do I do this? I'm imagining something like this:
Client A enters, joins room "abc123". Waits for another client to join
Client B enters, also joins room "abc123".
Client A is signaled that Client B has entered the room, so he makes a connection with Client B
Client A and Client B start streaming from their webcam. Client A can see Client B, and Client B can see Client A.
How would I do it using the WebRTC library (you can just assume that the backend server for room matching is created)
The process you are looking for is called JSEP (JavaScript Session Establishment Protocol) and it can be divided in the 3 steps I describe below. These steps start once both clients are in the room and can comunicate through WebSockets, I will use ws as an imaginary WebSocket API for communication between the client and the server and other clients:
1. Invite
During this step, one desinged caller creates and offer and sends it through the server to the other client (callee):
// This is only in Chrome
var pc = new webkitRTCPeerConnection({iceServers:[{url:"stun:stun.l.google.com:19302"}]}, {optional: [{RtpDataChannels: true}]});
// Someone must be chosen to be the caller
// (it can be either latest person who joins the room or the people in it)
ws.on('joined', function() {
var offer = pc.createOffer(function (offer) {
pc.setLocalDescription(offer);
ws.send('offer', offer);
});
});
// The callee receives offer and returns an answer
ws.on('offer', function (offer) {
pc.setRemoteDescription(new RTCSessionDescription(offer));
pc.createAnswer(function(answer) {
pc.setLocalDescription(answer);
ws.send('answer', answer);
}, err => console.log('error'), {});
});
// The caller receives the answer
ws.on('answer', function (answer) {
pc.setRemoteDescription(new RTCSessionDescription(answer));
});
Now both sides are have exchanged SDP packets and are ready to connect to each other.
2. Negotiation (ICE)
ICE candidates are created by each side to find a way to connect to each other, they are pretty much IP addresses where they can be found: localhost, local area network address (192.168.x.x) and external public IP Address (ISP). They are generated automatically by the PC object.
// Both processing them on each end:
ws.on('ICE', candidate => pc.addIceCandidate(new RTCIceCandidate(data)));
// Both sending them:
pc.onicecandidate = candidate => ws.send('ICE', candidate);
After the ICE negotiation, the conexion gets estabished unless you try to connect clients through firewalls on both sides of the connection, p2p communications are NAT traversal but won't work on some scenarios.
3. Data streaming
// Once the connection is established we can start to transfer video,
// audio or data
navigator.getUserMedia(function (stream) {
pc.addStream(stream);
}, err => console.log('Error getting User Media'));
It is a good option to have the stream before making the call and adding it at earlier steps, before creating the offer for the caller and right after receiving the call for the callee, so you don't have to deal with renegotiations. This was a pain a few years ago but it may be better implemented now in WebRTC.
Feel free to check my WebRTC project in GitHub where I create p2p connections in rooms for many participants, it is in GitHub and has a live demo.
MediaStreamTrack.getSources is used to get video devices connected. It seems to be deprecated now. See this stack-overflow question and documentation. Also refer MediaStreamTrack.getSources demo and code.
Yes. getUserMedia is just turning on camera. You can see the demo and code here.
Please refer to this peer connection sample & code here to stream audio and video between users.
Also look at this on Real time communication with WebRTC.
I've set up a text chat service using the PeerJS implementation of WebRTC's data channel. PeerJS provides a basic signalling server for this purpose, but I have tried to replace that with STUN and TURN servers set up through XirSys (recommended by SimpleWebRTC, another WebRTC library). I haven't deployed to the web yet.
Using Node to serve my static files locally, it will work on a local network (when I am sitting next to the person and they navigate to my ip/port in the browser), but will not work when connecting through different access points on the same network (i.e. at work, on opposite ends of the building).
My hypothesis is that it's hitting a firewall, but still directing traffic to PeerJS' signalling server without falling back to the XirSys STUN and TURN servers I've tried to set up. Here's the code I'm working with:
var stun = {};
var turn1 = {};
var turn2 = {};
$.ajax({
type: "POST",
dataType: "json",
url: "https://api.xirsys.com/getIceServers",
data: {
ident: "myusername",
secret: "long-alphanumeric-secret-key",
domain: "www.adomain.com",
application: "anapp",
room: "aroom",
secure: 1
},
success: function (data, status) {
console.log(data);
stun = data.d.iceServers[0];
turn1 = data.d.iceServers[1];
turn2 = data.d.iceServers[2];
},
async: false
});
var conn;
// Connect to PeerJS, have server assign an ID instead of providing one
var peerID = prompt('What would you like your screen name to be?');
var peer = new Peer(
peerID,
{key: 'mypeerjsserverkey', debug: true},
{
config: {'iceServers': [
{url: stun.url},
{url: turn1.url, credential: turn1.credential, username: turn1.username},
{url: turn2.url, credential: turn2.credential, username: turn2.username}
]
}
});
NOTE: My ident, secret, domain, etc. obviously aren't accurately represented here. I don't think that's where my problem is.
Any thoughts?
If you email us a wireshark capture of your STUN/TURN traffic, we should be able to outline where your problem is. Messages sent over signalling are separate but parallel to WebRTC messages. Therefore, if the app is working but the messages are being sent over signalling, then it's possible the configuration of the application isn't correct.
XirSys provides TURN via UDP over TCP through port 80/443, so if the signalling is connecting and flowing, so should the TURN.
Also, looking at your code, if you pass data.d from your getIceServers success handler to the PeerJS config, that should reduce your code quite a bit :-) The ICE string you're reconstructing doesn't need to be broken down.
Regards,
Lee
XirSys CTO