WebRTC extension FF fix
Thanks to updates in modern FFs handling of WebRTC the webrtcadapter wrapper had to be updated. To ensure this would be picked up, also added WebRTC REST integration test cases. The tests only run if the extension is enabled, which is still OFF by default. See Issue #1134 and #1083
This commit is contained in:
@@ -1,190 +1,327 @@
|
|||||||
var RTCPeerConnection = null;
|
/*
|
||||||
|
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* More information about these options at jshint.com/docs/options */
|
||||||
|
/* jshint browser: true, camelcase: true, curly: true, devel: true,
|
||||||
|
eqeqeq: true, forin: false, globalstrict: true, node: true,
|
||||||
|
quotmark: single, undef: true, unused: strict */
|
||||||
|
/* global mozRTCIceCandidate, mozRTCPeerConnection, Promise,
|
||||||
|
mozRTCSessionDescription, webkitRTCPeerConnection, MediaStreamTrack */
|
||||||
|
/* exported trace,requestUserMedia */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
var getUserMedia = null;
|
var getUserMedia = null;
|
||||||
var attachMediaStream = null;
|
var attachMediaStream = null;
|
||||||
var reattachMediaStream = null;
|
var reattachMediaStream = null;
|
||||||
var webrtcDetectedBrowser = null;
|
var webrtcDetectedBrowser = null;
|
||||||
var webrtcDetectedVersion = null;
|
var webrtcDetectedVersion = null;
|
||||||
|
var webrtcMinimumVersion = null;
|
||||||
|
|
||||||
function maybeFixConfiguration(pcConfig) {
|
function trace(text) {
|
||||||
if (pcConfig === null) {
|
// This function is used for logging.
|
||||||
return;
|
if (text[text.length - 1] === '\n') {
|
||||||
|
text = text.substring(0, text.length - 1);
|
||||||
}
|
}
|
||||||
for (var i = 0; i < pcConfig.iceServers.length; i++) {
|
if (window.performance) {
|
||||||
if (pcConfig.iceServers[i].hasOwnProperty('urls')){
|
var now = (window.performance.now() / 1000).toFixed(3);
|
||||||
if (pcConfig.iceServers[i]['urls'].length > 0) {
|
beef.debug(now + ': ' + text);
|
||||||
// In FF - we just take the FIRST STUN Server
|
} else {
|
||||||
pcConfig.iceServers[i]['url'] = pcConfig.iceServers[i]['urls'][0];
|
beef.debug(text);
|
||||||
} else {
|
|
||||||
pcConfig.iceServers[i]['url'] = pcConfig.iceServers[i]['urls'];
|
|
||||||
}
|
|
||||||
delete pcConfig.iceServers[i]['urls'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (navigator.mozGetUserMedia) {
|
if (navigator.mozGetUserMedia) {
|
||||||
|
|
||||||
webrtcDetectedBrowser = "firefox";
|
webrtcDetectedBrowser = 'firefox';
|
||||||
|
|
||||||
|
// the detected firefox version.
|
||||||
webrtcDetectedVersion =
|
webrtcDetectedVersion =
|
||||||
parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
|
parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
|
||||||
|
|
||||||
|
// the minimum firefox version still supported by adapter.
|
||||||
|
webrtcMinimumVersion = 31;
|
||||||
|
|
||||||
// The RTCPeerConnection object.
|
// The RTCPeerConnection object.
|
||||||
var RTCPeerConnection = function(pcConfig, pcConstraints) {
|
window.RTCPeerConnection = function(pcConfig, pcConstraints) {
|
||||||
// .urls is not supported in FF yet.
|
if (webrtcDetectedVersion < 38) {
|
||||||
maybeFixConfiguration(pcConfig);
|
// .urls is not supported in FF < 38.
|
||||||
return new mozRTCPeerConnection(pcConfig, pcConstraints);
|
// create RTCIceServers with a single url.
|
||||||
}
|
if (pcConfig && pcConfig.iceServers) {
|
||||||
|
var newIceServers = [];
|
||||||
// The RTCSessionDescription object.
|
for (var i = 0; i < pcConfig.iceServers.length; i++) {
|
||||||
RTCSessionDescription = mozRTCSessionDescription;
|
var server = pcConfig.iceServers[i];
|
||||||
|
if (server.hasOwnProperty('urls')) {
|
||||||
// The RTCIceCandidate object.
|
for (var j = 0; j < server.urls.length; j++) {
|
||||||
RTCIceCandidate = mozRTCIceCandidate;
|
var newServer = {
|
||||||
|
url: server.urls[j]
|
||||||
// Get UserMedia (only difference is the prefix).
|
};
|
||||||
// Code from Adam Barth.
|
if (server.urls[j].indexOf('turn') === 0) {
|
||||||
getUserMedia = navigator.mozGetUserMedia.bind(navigator);
|
newServer.username = server.username;
|
||||||
navigator.getUserMedia = getUserMedia;
|
newServer.credential = server.credential;
|
||||||
|
}
|
||||||
// Creates iceServer from the url for FF.
|
newIceServers.push(newServer);
|
||||||
createIceServer = function(url, username, password) {
|
}
|
||||||
var iceServer = null;
|
} else {
|
||||||
var url_parts = url.split(':');
|
newIceServers.push(pcConfig.iceServers[i]);
|
||||||
if (url_parts[0].indexOf('stun') === 0) {
|
}
|
||||||
// Create iceServer with stun url.
|
|
||||||
iceServer = { 'url': url };
|
|
||||||
} else if (url_parts[0].indexOf('turn') === 0) {
|
|
||||||
if (webrtcDetectedVersion < 27) {
|
|
||||||
// Create iceServer with turn url.
|
|
||||||
// Ignore the transport parameter from TURN url for FF version <=27.
|
|
||||||
var turn_url_parts = url.split("?");
|
|
||||||
// Return null for createIceServer if transport=tcp.
|
|
||||||
if (turn_url_parts.length === 1 ||
|
|
||||||
turn_url_parts[1].indexOf('transport=udp') === 0) {
|
|
||||||
iceServer = {'url': turn_url_parts[0],
|
|
||||||
'credential': password,
|
|
||||||
'username': username};
|
|
||||||
}
|
}
|
||||||
} else {
|
pcConfig.iceServers = newIceServers;
|
||||||
// FF 27 and above supports transport parameters in TURN url,
|
|
||||||
// So passing in the full url to create iceServer.
|
|
||||||
iceServer = {'url': url,
|
|
||||||
'credential': password,
|
|
||||||
'username': username};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return iceServer;
|
return new mozRTCPeerConnection(pcConfig, pcConstraints);
|
||||||
};
|
};
|
||||||
|
|
||||||
createIceServers = function(urls, username, password) {
|
// The RTCSessionDescription object.
|
||||||
var iceServers = [];
|
window.RTCSessionDescription = mozRTCSessionDescription;
|
||||||
// Use .url for FireFox.
|
|
||||||
for (i = 0; i < urls.length; i++) {
|
|
||||||
var iceServer = createIceServer(urls[i],
|
|
||||||
username,
|
|
||||||
password);
|
|
||||||
if (iceServer !== null) {
|
|
||||||
iceServers.push(iceServer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return iceServers;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// The RTCIceCandidate object.
|
||||||
|
window.RTCIceCandidate = mozRTCIceCandidate;
|
||||||
|
|
||||||
|
// getUserMedia constraints shim.
|
||||||
|
getUserMedia = (webrtcDetectedVersion < 38) ?
|
||||||
|
function(c, onSuccess, onError) {
|
||||||
|
var constraintsToFF37 = function(c) {
|
||||||
|
if (typeof c !== 'object' || c.require) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
var require = [];
|
||||||
|
Object.keys(c).forEach(function(key) {
|
||||||
|
var r = c[key] = (typeof c[key] === 'object') ?
|
||||||
|
c[key] : {ideal: c[key]};
|
||||||
|
if (r.exact !== undefined) {
|
||||||
|
r.min = r.max = r.exact;
|
||||||
|
delete r.exact;
|
||||||
|
}
|
||||||
|
if (r.min !== undefined || r.max !== undefined) {
|
||||||
|
require.push(key);
|
||||||
|
}
|
||||||
|
if (r.ideal !== undefined) {
|
||||||
|
c.advanced = c.advanced || [];
|
||||||
|
var oc = {};
|
||||||
|
oc[key] = {min: r.ideal, max: r.ideal};
|
||||||
|
c.advanced.push(oc);
|
||||||
|
delete r.ideal;
|
||||||
|
if (!Object.keys(r).length) {
|
||||||
|
delete c[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (require.length) {
|
||||||
|
c.require = require;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
};
|
||||||
|
beef.debug('spec: ' + JSON.stringify(c));
|
||||||
|
c.audio = constraintsToFF37(c.audio);
|
||||||
|
c.video = constraintsToFF37(c.video);
|
||||||
|
beef.debug('ff37: ' + JSON.stringify(c));
|
||||||
|
return navigator.mozGetUserMedia(c, onSuccess, onError);
|
||||||
|
} : navigator.mozGetUserMedia.bind(navigator);
|
||||||
|
|
||||||
|
navigator.getUserMedia = getUserMedia;
|
||||||
|
|
||||||
|
// Shim for mediaDevices on older versions.
|
||||||
|
if (!navigator.mediaDevices) {
|
||||||
|
navigator.mediaDevices = {getUserMedia: requestUserMedia,
|
||||||
|
addEventListener: function() { },
|
||||||
|
removeEventListener: function() { }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
navigator.mediaDevices.enumerateDevices =
|
||||||
|
navigator.mediaDevices.enumerateDevices || function() {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
var infos = [
|
||||||
|
{kind: 'audioinput', deviceId: 'default', label:'', groupId:''},
|
||||||
|
{kind: 'videoinput', deviceId: 'default', label:'', groupId:''}
|
||||||
|
];
|
||||||
|
resolve(infos);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (webrtcDetectedVersion < 41) {
|
||||||
|
// Work around http://bugzil.la/1169665
|
||||||
|
var orgEnumerateDevices =
|
||||||
|
navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
|
||||||
|
navigator.mediaDevices.enumerateDevices = function() {
|
||||||
|
return orgEnumerateDevices().catch(function(e) {
|
||||||
|
if (e.name === 'NotFoundError') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
// Attach a media stream to an element.
|
// Attach a media stream to an element.
|
||||||
attachMediaStream = function(element, stream) {
|
attachMediaStream = function(element, stream) {
|
||||||
beef.debug("Attaching media stream");
|
beef.debug('Attaching media stream');
|
||||||
element.mozSrcObject = stream;
|
element.mozSrcObject = stream;
|
||||||
element.play();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
reattachMediaStream = function(to, from) {
|
reattachMediaStream = function(to, from) {
|
||||||
beef.debug("Reattaching media stream");
|
beef.debug('Reattaching media stream');
|
||||||
to.mozSrcObject = from.mozSrcObject;
|
to.mozSrcObject = from.mozSrcObject;
|
||||||
to.play();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fake get{Video,Audio}Tracks
|
|
||||||
if (!MediaStream.prototype.getVideoTracks) {
|
|
||||||
MediaStream.prototype.getVideoTracks = function() {
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!MediaStream.prototype.getAudioTracks) {
|
|
||||||
MediaStream.prototype.getAudioTracks = function() {
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else if (navigator.webkitGetUserMedia) {
|
} else if (navigator.webkitGetUserMedia) {
|
||||||
|
|
||||||
webrtcDetectedBrowser = "chrome";
|
webrtcDetectedBrowser = 'chrome';
|
||||||
// Temporary fix until crbug/374263 is fixed.
|
|
||||||
// Setting Chrome version to 999, if version is unavailable.
|
|
||||||
var result = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
|
|
||||||
if (result !== null) {
|
|
||||||
webrtcDetectedVersion = parseInt(result[2], 10);
|
|
||||||
} else {
|
|
||||||
webrtcDetectedVersion = 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates iceServer from the url for Chrome M33 and earlier.
|
// the detected chrome version.
|
||||||
createIceServer = function(url, username, password) {
|
webrtcDetectedVersion =
|
||||||
var iceServer = null;
|
parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);
|
||||||
var url_parts = url.split(':');
|
|
||||||
if (url_parts[0].indexOf('stun') === 0) {
|
|
||||||
// Create iceServer with stun url.
|
|
||||||
iceServer = { 'url': url };
|
|
||||||
} else if (url_parts[0].indexOf('turn') === 0) {
|
|
||||||
// Chrome M28 & above uses below TURN format.
|
|
||||||
iceServer = {'url': url,
|
|
||||||
'credential': password,
|
|
||||||
'username': username};
|
|
||||||
}
|
|
||||||
return iceServer;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Creates iceServers from the urls for Chrome M34 and above.
|
// the minimum chrome version still supported by adapter.
|
||||||
createIceServers = function(urls, username, password) {
|
webrtcMinimumVersion = 38;
|
||||||
var iceServers = [];
|
|
||||||
if (webrtcDetectedVersion >= 34) {
|
|
||||||
// .urls is supported since Chrome M34.
|
|
||||||
iceServers = {'urls': urls,
|
|
||||||
'credential': password,
|
|
||||||
'username': username };
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < urls.length; i++) {
|
|
||||||
var iceServer = createIceServer(urls[i],
|
|
||||||
username,
|
|
||||||
password);
|
|
||||||
if (iceServer !== null) {
|
|
||||||
iceServers.push(iceServer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return iceServers;
|
|
||||||
};
|
|
||||||
|
|
||||||
// The RTCPeerConnection object.
|
// The RTCPeerConnection object.
|
||||||
var RTCPeerConnection = function(pcConfig, pcConstraints) {
|
window.RTCPeerConnection = function(pcConfig, pcConstraints) {
|
||||||
// .urls is supported since Chrome M34.
|
var pc = new webkitRTCPeerConnection(pcConfig, pcConstraints);
|
||||||
if (webrtcDetectedVersion < 34) {
|
var origGetStats = pc.getStats.bind(pc);
|
||||||
maybeFixConfiguration(pcConfig);
|
pc.getStats = function(selector, successCallback, errorCallback) { // jshint ignore: line
|
||||||
}
|
// If selector is a function then we are in the old style stats so just
|
||||||
return new webkitRTCPeerConnection(pcConfig, pcConstraints);
|
// pass back the original getStats format to avoid breaking old users.
|
||||||
}
|
if (typeof selector === 'function') {
|
||||||
|
return origGetStats(selector, successCallback);
|
||||||
|
}
|
||||||
|
|
||||||
// Get UserMedia (only difference is the prefix).
|
var fixChromeStats = function(response) {
|
||||||
// Code from Adam Barth.
|
var standardReport = {};
|
||||||
getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
|
var reports = response.result();
|
||||||
|
reports.forEach(function(report) {
|
||||||
|
var standardStats = {
|
||||||
|
id: report.id,
|
||||||
|
timestamp: report.timestamp,
|
||||||
|
type: report.type
|
||||||
|
};
|
||||||
|
report.names().forEach(function(name) {
|
||||||
|
standardStats[name] = report.stat(name);
|
||||||
|
});
|
||||||
|
standardReport[standardStats.id] = standardStats;
|
||||||
|
});
|
||||||
|
|
||||||
|
return standardReport;
|
||||||
|
};
|
||||||
|
var successCallbackWrapper = function(response) {
|
||||||
|
successCallback(fixChromeStats(response));
|
||||||
|
};
|
||||||
|
return origGetStats(successCallbackWrapper, selector);
|
||||||
|
};
|
||||||
|
|
||||||
|
return pc;
|
||||||
|
};
|
||||||
|
|
||||||
|
// add promise support
|
||||||
|
['createOffer', 'createAnswer'].forEach(function(method) {
|
||||||
|
var nativeMethod = webkitRTCPeerConnection.prototype[method];
|
||||||
|
webkitRTCPeerConnection.prototype[method] = function() {
|
||||||
|
var self = this;
|
||||||
|
if (arguments.length < 1 || (arguments.length === 1 &&
|
||||||
|
typeof(arguments[0]) === 'object')) {
|
||||||
|
var opts = arguments.length === 1 ? arguments[0] : undefined;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
nativeMethod.apply(self, [resolve, reject, opts]);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return nativeMethod.apply(this, arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
['setLocalDescription', 'setRemoteDescription',
|
||||||
|
'addIceCandidate'].forEach(function(method) {
|
||||||
|
var nativeMethod = webkitRTCPeerConnection.prototype[method];
|
||||||
|
webkitRTCPeerConnection.prototype[method] = function() {
|
||||||
|
var args = arguments;
|
||||||
|
var self = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
nativeMethod.apply(self, [args[0],
|
||||||
|
function() {
|
||||||
|
resolve();
|
||||||
|
if (args.length >= 2) {
|
||||||
|
args[1].apply(null, []);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function(err) {
|
||||||
|
reject(err);
|
||||||
|
if (args.length >= 3) {
|
||||||
|
args[2].apply(null, [err]);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// getUserMedia constraints shim.
|
||||||
|
getUserMedia = function(c, onSuccess, onError) {
|
||||||
|
var constraintsToChrome = function(c) {
|
||||||
|
if (typeof c !== 'object' || c.mandatory || c.optional) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
var cc = {};
|
||||||
|
Object.keys(c).forEach(function(key) {
|
||||||
|
if (key === 'require' || key === 'advanced') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
|
||||||
|
if (r.exact !== undefined && typeof r.exact === 'number') {
|
||||||
|
r.min = r.max = r.exact;
|
||||||
|
}
|
||||||
|
var oldname = function(prefix, name) {
|
||||||
|
if (prefix) {
|
||||||
|
return prefix + name.charAt(0).toUpperCase() + name.slice(1);
|
||||||
|
}
|
||||||
|
return (name === 'deviceId') ? 'sourceId' : name;
|
||||||
|
};
|
||||||
|
if (r.ideal !== undefined) {
|
||||||
|
cc.optional = cc.optional || [];
|
||||||
|
var oc = {};
|
||||||
|
if (typeof r.ideal === 'number') {
|
||||||
|
oc[oldname('min', key)] = r.ideal;
|
||||||
|
cc.optional.push(oc);
|
||||||
|
oc = {};
|
||||||
|
oc[oldname('max', key)] = r.ideal;
|
||||||
|
cc.optional.push(oc);
|
||||||
|
} else {
|
||||||
|
oc[oldname('', key)] = r.ideal;
|
||||||
|
cc.optional.push(oc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (r.exact !== undefined && typeof r.exact !== 'number') {
|
||||||
|
cc.mandatory = cc.mandatory || {};
|
||||||
|
cc.mandatory[oldname('', key)] = r.exact;
|
||||||
|
} else {
|
||||||
|
['min', 'max'].forEach(function(mix) {
|
||||||
|
if (r[mix] !== undefined) {
|
||||||
|
cc.mandatory = cc.mandatory || {};
|
||||||
|
cc.mandatory[oldname(mix, key)] = r[mix];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (c.advanced) {
|
||||||
|
cc.optional = (cc.optional || []).concat(c.advanced);
|
||||||
|
}
|
||||||
|
return cc;
|
||||||
|
};
|
||||||
|
beef.debug('spec: ' + JSON.stringify(c)); // whitespace for alignment
|
||||||
|
c.audio = constraintsToChrome(c.audio);
|
||||||
|
c.video = constraintsToChrome(c.video);
|
||||||
|
beef.debug('chrome: ' + JSON.stringify(c));
|
||||||
|
return navigator.webkitGetUserMedia(c, onSuccess, onError);
|
||||||
|
};
|
||||||
navigator.getUserMedia = getUserMedia;
|
navigator.getUserMedia = getUserMedia;
|
||||||
|
|
||||||
// Attach a media stream to an element.
|
// Attach a media stream to an element.
|
||||||
attachMediaStream = function(element, stream) {
|
attachMediaStream = function(element, stream) {
|
||||||
if (typeof element.srcObject !== 'undefined') {
|
if (typeof element.srcObject !== 'undefined') {
|
||||||
element.srcObject = stream;
|
element.srcObject = stream;
|
||||||
} else if (typeof element.mozSrcObject !== 'undefined') {
|
|
||||||
element.mozSrcObject = stream;
|
|
||||||
} else if (typeof element.src !== 'undefined') {
|
} else if (typeof element.src !== 'undefined') {
|
||||||
element.src = URL.createObjectURL(stream);
|
element.src = URL.createObjectURL(stream);
|
||||||
} else {
|
} else {
|
||||||
@@ -195,6 +332,78 @@ if (navigator.mozGetUserMedia) {
|
|||||||
reattachMediaStream = function(to, from) {
|
reattachMediaStream = function(to, from) {
|
||||||
to.src = from.src;
|
to.src = from.src;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!navigator.mediaDevices) {
|
||||||
|
navigator.mediaDevices = {getUserMedia: requestUserMedia,
|
||||||
|
enumerateDevices: function() {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
var kinds = {audio: 'audioinput', video: 'videoinput'};
|
||||||
|
return MediaStreamTrack.getSources(function(devices) {
|
||||||
|
resolve(devices.map(function(device) {
|
||||||
|
return {label: device.label,
|
||||||
|
kind: kinds[device.kind],
|
||||||
|
deviceId: device.id,
|
||||||
|
groupId: ''};
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
// in case someone wants to listen for the devicechange event.
|
||||||
|
navigator.mediaDevices.addEventListener = function() { };
|
||||||
|
navigator.mediaDevices.removeEventListener = function() { };
|
||||||
|
}
|
||||||
|
} else if (navigator.mediaDevices && navigator.userAgent.match(
|
||||||
|
/Edge\/(\d+).(\d+)$/)) {
|
||||||
|
webrtcDetectedBrowser = 'edge';
|
||||||
|
|
||||||
|
webrtcDetectedVersion =
|
||||||
|
parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10);
|
||||||
|
|
||||||
|
// the minimum version still supported by adapter.
|
||||||
|
webrtcMinimumVersion = 12;
|
||||||
|
|
||||||
|
attachMediaStream = function(element, stream) {
|
||||||
|
element.srcObject = stream;
|
||||||
|
};
|
||||||
|
reattachMediaStream = function(to, from) {
|
||||||
|
to.srcObject = from.srcObject;
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
beef.debug("Browser does not appear to be WebRTC-capable");
|
// console.log('Browser does not appear to be WebRTC-capable');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the result of getUserMedia as a Promise.
|
||||||
|
function requestUserMedia(constraints) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
getUserMedia(constraints, resolve, reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module !== 'undefined') {
|
||||||
|
module.exports = {
|
||||||
|
RTCPeerConnection: window.RTCPeerConnection,
|
||||||
|
getUserMedia: getUserMedia,
|
||||||
|
attachMediaStream: attachMediaStream,
|
||||||
|
reattachMediaStream: reattachMediaStream,
|
||||||
|
webrtcDetectedBrowser: webrtcDetectedBrowser,
|
||||||
|
webrtcDetectedVersion: webrtcDetectedVersion,
|
||||||
|
webrtcMinimumVersion: webrtcMinimumVersion
|
||||||
|
//requestUserMedia: not exposed on purpose.
|
||||||
|
//trace: not exposed on purpose.
|
||||||
|
};
|
||||||
|
} else if ((typeof require === 'function') && (typeof define === 'function')) {
|
||||||
|
// Expose objects and functions when RequireJS is doing the loading.
|
||||||
|
define([], function() {
|
||||||
|
return {
|
||||||
|
RTCPeerConnection: window.RTCPeerConnection,
|
||||||
|
getUserMedia: getUserMedia,
|
||||||
|
attachMediaStream: attachMediaStream,
|
||||||
|
reattachMediaStream: reattachMediaStream,
|
||||||
|
webrtcDetectedBrowser: webrtcDetectedBrowser,
|
||||||
|
webrtcDetectedVersion: webrtcDetectedVersion,
|
||||||
|
webrtcMinimumVersion: webrtcMinimumVersion
|
||||||
|
//requestUserMedia: not exposed on purpose.
|
||||||
|
//trace: not exposed on purpose.
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,10 +67,12 @@ Beefwebrtc.prototype.initialize = function() {
|
|||||||
|
|
||||||
// Initialise the pcConfig hash with the provided stunservers
|
// Initialise the pcConfig hash with the provided stunservers
|
||||||
var stuns = JSON.parse(this.stunservers);
|
var stuns = JSON.parse(this.stunservers);
|
||||||
this.pcConfig = {"iceServers": [{"urls":stuns}]};
|
this.pcConfig = {"iceServers": [{"urls":stuns, "username":"user",
|
||||||
|
"credential":"pass"}]};
|
||||||
|
|
||||||
// We're not getting the browsers to request their own TURN servers, we're specifying them through BeEF
|
// We're not getting the browsers to request their own TURN servers, we're specifying them through BeEF
|
||||||
this.forceTurn(this.turnjson);
|
// this.forceTurn(this.turnjson);
|
||||||
|
this.turnDone = true;
|
||||||
|
|
||||||
// Caller is always ready to create peerConnection.
|
// Caller is always ready to create peerConnection.
|
||||||
this.signalingReady = this.initiator;
|
this.signalingReady = this.initiator;
|
||||||
@@ -450,6 +452,18 @@ Beefwebrtc.prototype.onCreateSessionDescriptionError = function(error) {
|
|||||||
if (localverbose === true) {beef.debug('Failed to create session description: ' + error.toString());}
|
if (localverbose === true) {beef.debug('Failed to create session description: ' + error.toString());}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the browser successfully sets a remote description
|
||||||
|
Beefwebrtc.prototype.onSetRemoteDescriptionSuccess = function() {
|
||||||
|
var localverbose = false;
|
||||||
|
|
||||||
|
for (var k in beefrtcs) {
|
||||||
|
if (beefrtcs[k].verbose === true) {
|
||||||
|
localverbose = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (localverbose === true) {beef.debug('Set remote session description successfully');}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for messages - which includes signaling from a calling peer - this gets kicked off in maybeStart()
|
// Check for messages - which includes signaling from a calling peer - this gets kicked off in maybeStart()
|
||||||
Beefwebrtc.prototype.calleeStart = function() {
|
Beefwebrtc.prototype.calleeStart = function() {
|
||||||
// Callee starts to process cached offer and other messages.
|
// Callee starts to process cached offer and other messages.
|
||||||
@@ -467,11 +481,55 @@ Beefwebrtc.prototype.processSignalingMessage = function(message) {
|
|||||||
|
|
||||||
if (message.type === 'offer') {
|
if (message.type === 'offer') {
|
||||||
if (this.verbose) {beef.debug("Processing signalling message: OFFER");}
|
if (this.verbose) {beef.debug("Processing signalling message: OFFER");}
|
||||||
this.setRemote(message);
|
if (navigator.mozGetUserMedia) { // Mozilla shim fuckn shit - since the new
|
||||||
this.doAnswer();
|
// version of FF - which no longer works
|
||||||
|
if (this.verbose) {beef.debug("Moz shim here");}
|
||||||
|
globalrtc[this.peerid].setRemoteDescription(
|
||||||
|
new RTCSessionDescription(message),
|
||||||
|
function() {
|
||||||
|
// globalrtc[this.peerid].createAnswer(function(answer) {
|
||||||
|
// globalrtc[this.peerid].setLocalDescription(
|
||||||
|
|
||||||
|
var peerid = null;
|
||||||
|
|
||||||
|
for (var k in beefrtcs) {
|
||||||
|
if (beefrtcs[k].allgood === false) {
|
||||||
|
peerid = beefrtcs[k].peerid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
globalrtc[peerid].createAnswer(function(answer) {
|
||||||
|
globalrtc[peerid].setLocalDescription(
|
||||||
|
new RTCSessionDescription(answer),
|
||||||
|
function() {
|
||||||
|
beefrtcs[peerid].sendSignalMsg(answer);
|
||||||
|
},function(error) {
|
||||||
|
beef.debug("setLocalDescription error: " + error);
|
||||||
|
});
|
||||||
|
},function(error) {
|
||||||
|
beef.debug("createAnswer error: " +error);
|
||||||
|
});
|
||||||
|
},function(error) {
|
||||||
|
beef.debug("setRemoteDescription error: " + error);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.setRemote(message);
|
||||||
|
this.doAnswer();
|
||||||
|
}
|
||||||
} else if (message.type === 'answer') {
|
} else if (message.type === 'answer') {
|
||||||
if (this.verbose) {beef.debug("Processing signalling message: ANSWER");}
|
if (this.verbose) {beef.debug("Processing signalling message: ANSWER");}
|
||||||
this.setRemote(message);
|
if (navigator.mozGetUserMedia) { // terrible moz shim - as for the offer
|
||||||
|
if (this.verbose) {beef.debug("Moz shim here");}
|
||||||
|
globalrtc[this.peerid].setRemoteDescription(
|
||||||
|
new RTCSessionDescription(message),
|
||||||
|
function() {},
|
||||||
|
function(error) {
|
||||||
|
beef.debug("setRemoteDescription error: " + error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setRemote(message);
|
||||||
|
}
|
||||||
} else if (message.type === 'candidate') {
|
} else if (message.type === 'candidate') {
|
||||||
if (this.verbose) {beef.debug("Processing signalling message: CANDIDATE");}
|
if (this.verbose) {beef.debug("Processing signalling message: CANDIDATE");}
|
||||||
var candidate = new RTCIceCandidate({sdpMLineIndex: message.label,
|
var candidate = new RTCIceCandidate({sdpMLineIndex: message.label,
|
||||||
@@ -486,11 +544,11 @@ Beefwebrtc.prototype.processSignalingMessage = function(message) {
|
|||||||
// Used to set the RTC remote session
|
// Used to set the RTC remote session
|
||||||
Beefwebrtc.prototype.setRemote = function(message) {
|
Beefwebrtc.prototype.setRemote = function(message) {
|
||||||
globalrtc[this.peerid].setRemoteDescription(new RTCSessionDescription(message),
|
globalrtc[this.peerid].setRemoteDescription(new RTCSessionDescription(message),
|
||||||
onSetRemoteDescriptionSuccess, this.onSetSessionDescriptionError);
|
this.onSetRemoteDescriptionSuccess, this.onSetSessionDescriptionError);
|
||||||
|
|
||||||
function onSetRemoteDescriptionSuccess() {
|
// function onSetRemoteDescriptionSuccess() {
|
||||||
if (this.verbose) {beef.debug("Set remote session description success.");}
|
// if (this.verbose) {beef.debug("Set remote session description success.");}
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// As part of the processSignalingMessage function, we check for 'offers' from peers. If there's an offer, we answer, as below
|
// As part of the processSignalingMessage function, we check for 'offers' from peers. If there's an offer, we answer, as below
|
||||||
@@ -585,4 +643,4 @@ beef.webrtc = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
beef.regCmp('beef.webrtc');
|
beef.regCmp('beef.webrtc');
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ beef:
|
|||||||
name: 'WebRTC'
|
name: 'WebRTC'
|
||||||
enable: false
|
enable: false
|
||||||
authors: ["xntrik"]
|
authors: ["xntrik"]
|
||||||
stunservers: '["stun:stun.l.google.com:19302","stun:stun1.l.google.com:19302"]'
|
stunservers: '["stun:stun.l.google.com:19302","stun:stun1.l.google.com:19302","turn:numb.viagenie.ca:3478"]'
|
||||||
# stunservers: '["stun:stun.l.google.com:19302"]'
|
# stunservers: '["stun:stun.l.google.com:19302"]'
|
||||||
turnservers: '{"username": "someone%40somewhere.com", "password": "somepass", "uris": ["turn:numb.viagenie.ca:3478?transport=udp","turn:numb.viagenie.ca:3478?transport=tcp"]}'
|
turnservers: '{"username": "someone%40somewhere.com", "password": "somepass", "uris": ["turn:numb.viagenie.ca:3478?transport=udp","turn:numb.viagenie.ca:3478?transport=tcp"]}'
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ module BeEF
|
|||||||
# +++ Example with curl +++
|
# +++ Example with curl +++
|
||||||
# curl -H "Content-type: application/json; charset=UTF-8" -v
|
# curl -H "Content-type: application/json; charset=UTF-8" -v
|
||||||
# -X POST -d '{"from":1,"to":2,"message":"Just a plain message"}'
|
# -X POST -d '{"from":1,"to":2,"message":"Just a plain message"}'
|
||||||
# http://127.0.0.1:3000/api/webrtc/go\?token\=df67654b03d030d97018f85f0284247d7f49c348
|
# http://127.0.0.1:3000/api/webrtc/msg\?token\=df67654b03d030d97018f85f0284247d7f49c348
|
||||||
#
|
#
|
||||||
# Available client-side "message" options and handling:
|
# Available client-side "message" options and handling:
|
||||||
# !gostealth - will put the <to> browser into a stealth mode
|
# !gostealth - will put the <to> browser into a stealth mode
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ RESTAPI_NETWORK = "http://" + ATTACK_DOMAIN + ":3000/api/network"
|
|||||||
RESTAPI_DNS = "http://" + ATTACK_DOMAIN + ":3000/api/dns"
|
RESTAPI_DNS = "http://" + ATTACK_DOMAIN + ":3000/api/dns"
|
||||||
RESTAPI_SENG = "http://" + ATTACK_DOMAIN + ":3000/api/seng"
|
RESTAPI_SENG = "http://" + ATTACK_DOMAIN + ":3000/api/seng"
|
||||||
RESTAPI_ADMIN = "http://" + ATTACK_DOMAIN + ":3000/api/admin"
|
RESTAPI_ADMIN = "http://" + ATTACK_DOMAIN + ":3000/api/admin"
|
||||||
|
RESTAPI_WEBRTC = "http://" + ATTACK_DOMAIN + ":3000/api/webrtc"
|
||||||
|
|||||||
252
test/integration/tc_webrtc_rest.rb
Normal file
252
test/integration/tc_webrtc_rest.rb
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2006-2015 Wade Alcorn - wade@bindshell.net
|
||||||
|
# Browser Exploitation Framework (BeEF) - http://beefproject.com
|
||||||
|
# See the file 'doc/COPYING' for copying permission
|
||||||
|
#
|
||||||
|
require 'test/unit'
|
||||||
|
require 'rest-client'
|
||||||
|
require 'json'
|
||||||
|
require '../common/test_constants'
|
||||||
|
require '../common/beef_test'
|
||||||
|
|
||||||
|
class TC_WebRTCRest < Test::Unit::TestCase
|
||||||
|
|
||||||
|
class << self
|
||||||
|
|
||||||
|
# Login to API before performing any tests - and fetch config too
|
||||||
|
def startup
|
||||||
|
json = {:username => BEEF_USER, :password => BEEF_PASSWD}.to_json
|
||||||
|
@@headers = {:content_type => :json, :accept => :json}
|
||||||
|
|
||||||
|
response = RestClient.post("#{RESTAPI_ADMIN}/login",
|
||||||
|
json,
|
||||||
|
@@headers)
|
||||||
|
|
||||||
|
result = JSON.parse(response.body)
|
||||||
|
@@token = result['token']
|
||||||
|
|
||||||
|
$root_dir = '../../'
|
||||||
|
$:.unshift($root_dir)
|
||||||
|
|
||||||
|
require 'core/loader'
|
||||||
|
|
||||||
|
BeEF::Core::Configuration.new(File.join($root_dir, 'config.yaml'))
|
||||||
|
BeEF::Core::Configuration.instance.load_extensions_config
|
||||||
|
|
||||||
|
@@config = BeEF::Core::Configuration.instance
|
||||||
|
|
||||||
|
@@activated = @@config.get('beef.extension.webrtc.enable') || false
|
||||||
|
|
||||||
|
@@victim1 = BeefTest.new_victim
|
||||||
|
@@victim2 = BeefTest.new_victim
|
||||||
|
|
||||||
|
puts "WebRTC Tests: Sleeping for 8 - waiting for 2 browsers to get hooked"
|
||||||
|
sleep 8.0
|
||||||
|
|
||||||
|
# Fetch last online browsers' ids
|
||||||
|
rest_response = RestClient.get "#{RESTAPI_HOOKS}", {:params => {
|
||||||
|
:token => @@token}}
|
||||||
|
result = JSON.parse(rest_response.body)
|
||||||
|
browsers = result["hooked-browsers"]["online"]
|
||||||
|
browsers.each_with_index do |elem, index|
|
||||||
|
if index == browsers.length - 1
|
||||||
|
@@victim2id = browsers["#{index}"]["id"].to_s
|
||||||
|
end
|
||||||
|
if index == browsers.length - 2
|
||||||
|
@@victim1id = browsers["#{index}"]["id"].to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def shutdown
|
||||||
|
$root_dir = nil
|
||||||
|
@@victim1.driver.browser.close
|
||||||
|
@@victim2.driver.browser.close
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_1_webrtc_check_for_two_hooked_browsers
|
||||||
|
return if not @@activated
|
||||||
|
|
||||||
|
rest_response = nil
|
||||||
|
assert_nothing_raised do
|
||||||
|
rest_response = RestClient.get "#{RESTAPI_HOOKS}", {:params => {
|
||||||
|
:token => @@token}}
|
||||||
|
end
|
||||||
|
check_rest_response(rest_response)
|
||||||
|
result = JSON.parse(rest_response.body)
|
||||||
|
browsers = result["hooked-browsers"]["online"]
|
||||||
|
assert_not_nil browsers
|
||||||
|
assert_operator browsers.length, :>=, 2
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_2_webrtc_establishing_p2p
|
||||||
|
return if not @@activated
|
||||||
|
|
||||||
|
rest_response = nil
|
||||||
|
assert_nothing_raised do
|
||||||
|
rest_response = RestClient.post("#{RESTAPI_WEBRTC}/go?token=#{@@token}",
|
||||||
|
{:from => @@victim1id, :to => @@victim2id, :verbose => "true"}.to_json,
|
||||||
|
@@headers)
|
||||||
|
end
|
||||||
|
check_rest_response(rest_response)
|
||||||
|
result = JSON.parse(rest_response.body)
|
||||||
|
assert_equal true, result["success"]
|
||||||
|
|
||||||
|
sleep 20.0
|
||||||
|
|
||||||
|
rest_response = nil
|
||||||
|
assert_nothing_raised do
|
||||||
|
rest_response = RestClient.get "#{RESTAPI_LOGS}", {:params => {
|
||||||
|
:token => @@token}}
|
||||||
|
end
|
||||||
|
check_rest_response(rest_response)
|
||||||
|
result = JSON.parse(rest_response.body)
|
||||||
|
|
||||||
|
loghitcount = 0
|
||||||
|
result["logs"].reverse.each {|l|
|
||||||
|
# Using free-space matching mode /x below to wrap regex.
|
||||||
|
# therefore need to escape spaces I want to check, hence the \
|
||||||
|
regex = Regexp.new(/Browser:(#{@@victim1id}|#{@@victim2id})\ received\
|
||||||
|
message\ from\ Browser:(#{@@victim1id}|#{@@victim2id})
|
||||||
|
:\ ICE\ Status:\ connected/x)
|
||||||
|
loghitcount += 1 if (not regex.match(l["event"]).nil?) and
|
||||||
|
(l["type"].to_s.eql?("WebRTC"))
|
||||||
|
}
|
||||||
|
assert_equal 2, loghitcount
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_3_webrtc_send_msg # assumes test 2 has run
|
||||||
|
return if not @@activated
|
||||||
|
|
||||||
|
rest_response = nil
|
||||||
|
assert_nothing_raised do
|
||||||
|
rest_response = RestClient.post("#{RESTAPI_WEBRTC}/msg?token=#{@@token}",
|
||||||
|
{:from => @@victim1id, :to => @@victim2id,
|
||||||
|
:message => "RTC test message"}.to_json,
|
||||||
|
@@headers)
|
||||||
|
end
|
||||||
|
check_rest_response(rest_response)
|
||||||
|
result = JSON.parse(rest_response.body)
|
||||||
|
assert_equal true, result["success"]
|
||||||
|
|
||||||
|
sleep 10.0
|
||||||
|
|
||||||
|
rest_response = nil
|
||||||
|
assert_nothing_raised do
|
||||||
|
rest_response = RestClient.get "#{RESTAPI_LOGS}", {:params => {
|
||||||
|
:token => @@token}}
|
||||||
|
end
|
||||||
|
check_rest_response(rest_response)
|
||||||
|
result = JSON.parse(rest_response.body)
|
||||||
|
|
||||||
|
assert_block do
|
||||||
|
result["logs"].reverse.each {|l|
|
||||||
|
# Using free-space matching mode /x below to wrap regex.
|
||||||
|
# therefore need to escape spaces I want to check, hence the \
|
||||||
|
regex = Regexp.new(/Browser:(#{@@victim1id}|#{@@victim2id})\ received\
|
||||||
|
message\ from\ Browser:
|
||||||
|
(#{@@victim1id}|#{@@victim2id})
|
||||||
|
:\ RTC\ test\ message/x)
|
||||||
|
return true if (not regex.match(l["event"]).nil?) and
|
||||||
|
(l["type"].to_s.eql?("WebRTC"))
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_4_webrtc_stealthmode # assumes test 2 has run
|
||||||
|
return if not @@activated
|
||||||
|
|
||||||
|
# Test our two browsers are still online
|
||||||
|
rest_response = nil
|
||||||
|
assert_nothing_raised do
|
||||||
|
rest_response = RestClient.get "#{RESTAPI_HOOKS}", {:params => {
|
||||||
|
:token => @@token}}
|
||||||
|
end
|
||||||
|
check_rest_response(rest_response)
|
||||||
|
result = JSON.parse(rest_response.body)
|
||||||
|
online = result["hooked-browsers"]["online"]
|
||||||
|
assert_block do
|
||||||
|
online.each {|hb|
|
||||||
|
return true if hb[1]["id"].eql?(@@victim1id)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
assert_block do
|
||||||
|
online.each {|hb|
|
||||||
|
return true if hb[1]["id"].eql?(@@victim2id)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Command one of the browsers to go stealth
|
||||||
|
rest_response = nil
|
||||||
|
assert_nothing_raised do
|
||||||
|
rest_response = RestClient.post("#{RESTAPI_WEBRTC}/msg?token=#{@@token}",
|
||||||
|
{:from => @@victim1id, :to => @@victim2id,
|
||||||
|
:message => "!gostealth"}.to_json,
|
||||||
|
@@headers)
|
||||||
|
end
|
||||||
|
check_rest_response(rest_response)
|
||||||
|
result = JSON.parse(rest_response.body)
|
||||||
|
assert_equal true, result["success"]
|
||||||
|
|
||||||
|
sleep 40.0 #Wait until that browser is offline.
|
||||||
|
|
||||||
|
# Test that the browser is now offline
|
||||||
|
rest_response = nil
|
||||||
|
assert_nothing_raised do
|
||||||
|
rest_response = RestClient.get "#{RESTAPI_HOOKS}", {:params => {
|
||||||
|
:token => @@token}}
|
||||||
|
end
|
||||||
|
check_rest_response(rest_response)
|
||||||
|
result = JSON.parse(rest_response.body)
|
||||||
|
offline = result["hooked-browsers"]["offline"]
|
||||||
|
assert_block do
|
||||||
|
offline.each {|hb|
|
||||||
|
return true if hb[1]["id"].eql?(@@victim2id)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Test that we can bring it back online (which implies comms are still ok)
|
||||||
|
rest_response = nil
|
||||||
|
assert_nothing_raised do
|
||||||
|
rest_response = RestClient.post("#{RESTAPI_WEBRTC}/msg?token=#{@@token}",
|
||||||
|
{:from => @@victim1id, :to => @@victim2id,
|
||||||
|
:message => "!endstealth"}.to_json,
|
||||||
|
@@headers)
|
||||||
|
end
|
||||||
|
check_rest_response(rest_response)
|
||||||
|
result = JSON.parse(rest_response.body)
|
||||||
|
assert_equal true, result["success"]
|
||||||
|
|
||||||
|
sleep 10.0 # Wait until browser comes back
|
||||||
|
|
||||||
|
# Test that the browser is now online
|
||||||
|
rest_response = nil
|
||||||
|
assert_nothing_raised do
|
||||||
|
rest_response = RestClient.get "#{RESTAPI_HOOKS}", {:params => {
|
||||||
|
:token => @@token}}
|
||||||
|
end
|
||||||
|
check_rest_response(rest_response)
|
||||||
|
result = JSON.parse(rest_response.body)
|
||||||
|
online = result["hooked-browsers"]["online"]
|
||||||
|
assert_block do
|
||||||
|
online.each {|hb|
|
||||||
|
return true if hb[1]["id"].eql?(@@victim2id)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Standard assertions for verifying response from RESTful API
|
||||||
|
def check_rest_response(response)
|
||||||
|
assert_not_nil(response.body)
|
||||||
|
assert_equal(200, response.code)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -19,6 +19,7 @@ require './tc_jools' # Basic tests for jools
|
|||||||
require './tc_network_rest' # Basic tests for Network extension RESTful API interface
|
require './tc_network_rest' # Basic tests for Network extension RESTful API interface
|
||||||
#require './tc_dns_rest' # Basic tests for DNS RESTful API interface
|
#require './tc_dns_rest' # Basic tests for DNS RESTful API interface
|
||||||
require './tc_social_engineering_rest' # Basic tests for social engineering RESTful API interface
|
require './tc_social_engineering_rest' # Basic tests for social engineering RESTful API interface
|
||||||
|
require './tc_webrtc_rest' # Basic tests for WebRTC extension
|
||||||
|
|
||||||
class TS_BeefIntegrationTests
|
class TS_BeefIntegrationTests
|
||||||
def self.suite
|
def self.suite
|
||||||
@@ -31,6 +32,7 @@ class TS_BeefIntegrationTests
|
|||||||
suite << TC_NetworkRest.suite
|
suite << TC_NetworkRest.suite
|
||||||
#suite << TC_DnsRest.suite
|
#suite << TC_DnsRest.suite
|
||||||
suite << TC_SocialEngineeringRest.suite
|
suite << TC_SocialEngineeringRest.suite
|
||||||
|
suite << TC_WebRTCRest.suite
|
||||||
|
|
||||||
return suite
|
return suite
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user