diff --git a/lib/ssh-session.js b/lib/ssh-session.js index c35ffdf..b71365d 100644 --- a/lib/ssh-session.js +++ b/lib/ssh-session.js @@ -1,6 +1,7 @@ let wrtc = require('./webrtc-request'); let Uuid = require('uuid'); let debug = require('debug')('Unifi SSH'); +debug.log = console.log.bind(console); function SSHSession(unifi, mac, uuid, stun, turn, username, password, site, autoclose, webrtc, waiter) { this.unifi = unifi; @@ -80,11 +81,13 @@ SSHSession.prototype.connect = function(connectTimeout, closeCallBack) { this.password = d.password; } } + debug('Building ssh session with params: mac=' + this.mac + ' uuid=' + this.uuid + ' ttl=-1 stun=' + (this.stun instanceof Array && this.stun[0] ? this.stun[0].replace(/stun:/, '') : this.stun) + ' turn=' + (this.turn instanceof Array && this.turn[0] ? this.turn[0].replace(/turn:/, '') : this.turn) + ' username='+this.username + ' password=' + this.password + ' site=' + this.site); return this.unifi.buildSSHSession(this.mac, this.uuid, "-1", (this.stun instanceof Array && this.stun[0] ? this.stun[0].replace(/stun:/, '') : this.stun), (this.turn instanceof Array && this.turn[0] ? this.turn[0].replace(/turn:/, '') : this.turn), this.username, this.password, this.site); }); } firstCall - .then(() => { + .then((r) => { + debug("first call response: " + JSON.stringify(r)); let o = { debug: this.debug }; if (this.waiter) o.waiter = this.waiter; if (this.webrtc) o.webrtc = this.webrtc; @@ -92,12 +95,10 @@ SSHSession.prototype.connect = function(connectTimeout, closeCallBack) { debug('Will open peer connection with stun', this.stun, 'turn', this.turn); this.wrtc.RTCPeerConnection({ iceServers: [{ - urls: this.stun, - url: this.stun + urls: this.stun }, { urls: this.turn, - url: this.turn, username: this.username, credential: this.password } @@ -109,8 +110,8 @@ SSHSession.prototype.connect = function(connectTimeout, closeCallBack) { ] }); // ICE Servers let connStateChange = () => { - debug('CAREFUL, Connection state changed'); let state = this.wrtc.peer.iceConnectionState; + debug('CAREFUL, Connection state changed: ' + state); if (state == 'disconnected' || state == 'failed') { debug('We are notified for session disconnection'); let rej = this.state != "open"; @@ -148,6 +149,17 @@ SSHSession.prototype.connect = function(connectTimeout, closeCallBack) { this.fireQ('onmessage', event); }; }); + this.wrtc.setCallback('onnegotiationneeded', (event) => { + debug("Handling negotiation needed event."); + this.wrtc.createOffer() + .then((offer) => { + debug("Negotiation offer created: " + JSON.stringify(offer)); + return this.wrtc.setLocalDescription(offer); + }) + .catch((err) => { + debug("Error while negotiating: " + err); + }); + }); return this.unifi.getSDPOffer(this.mac, this.uuid, this.site); }) .then((data) => { @@ -159,12 +171,15 @@ SSHSession.prototype.connect = function(connectTimeout, closeCallBack) { }); }) .then((data) => { + debug("Remote description was set: " + JSON.stringify(data) + "; creating answer"); return this.wrtc.createAnswer(data); }) .then((data) => { + debug("Answer was created: " + JSON.stringify(data) + "; setting local description"); return this.wrtc.setLocalDescription(data); }) .then((sdpData) => { + debug("Local description was set: " + JSON.stringify(sdpData) + "; collecting ICE candidates"); return this.wrtc.collectIceCandidates(sdpData); }) .then((data) => { @@ -180,13 +195,16 @@ SSHSession.prototype.connect = function(connectTimeout, closeCallBack) { return x > y; }).shift(); let ip = line.match(/udp\s+\d+\s+(\S+)\s/)[1]; + debug("Executing ssh-sdp-answer with sdp=" + sdp); return this.unifi.sshSDPAnswer(this.mac, this.uuid, /*sdp.replace("c=IN IP4 0.0.0.0", "c=IN IP4 " + ip) */ sdp, this.site); }) - // .then((data) => { - // return this.wrtc.openDataChannel('ssh'); - // }) .then((data) => { - debug('Channel is supposed to be open now. Lets wait'); + debug('ssh-sdp-answer is completed with response: ' + JSON.stringify(data) + '; openning data channel'); + return this.wrtc.openDataChannel('ssh'); + }) + .then((data) => { + debug('Channel is supposed to be open now. Lets wait: ' + JSON.stringify(data)); + debug('Set timeout: ' + (connectTimeout || this.connectTimeout || 15000)); timeoutChannel = setTimeout(() => { debug('Timeout has passed without response, the channel is not open'); this.unifi.closeSSHSession(this.mac, this.uuid, this.site) diff --git a/lib/webrtc-request.js b/lib/webrtc-request.js index 81e1d9f..2c53e2e 100644 --- a/lib/webrtc-request.js +++ b/lib/webrtc-request.js @@ -17,6 +17,7 @@ class WRTC { this._q = {}; this._channel = {}; this.log = require('debug')(this.debugName || 'WRTCRequest'); + this.log.log = console.log.bind(console); if (this.debug) this.log.enabled = true; if (this.webrtc && this.webrtc.on) { this.webrtc.on('error', error => { @@ -124,13 +125,18 @@ class WRTC { } this.log('WEBRTC_SET_REMOTEDESC', desc); let w = new this.webrtc.RTCSessionDescription(desc); + wait(this.waiter).then(() => { this.peer.setRemoteDescription( w, (data) => { // Create Answer - resolve(data /*|| desc*/ ); + this.log('received answer from setting remote description: ' + JSON.stringify(data)); + resolve(data || desc); // data is always null }, - reject + (err) => { + this.log('error while trying to set remote description: ' + err); + reject(err); + } ); }); }); @@ -186,6 +192,11 @@ class WRTC { this.log('Candidate is empty, terminate the gathering'); data.sdp = sdp; return resolve(data); + } + if (candidate.candidate.type === 'relay') { + this.log('Candidate type is relay, terminate the gathering'); + data.sdp = sdp; + return resolve(data); } // TODO: implement reject if (candidate && candidate.candidate) { let cand = candidate.candidate; @@ -448,4 +459,4 @@ class WRTC { } } -module.exports = WRTC; \ No newline at end of file +module.exports = WRTC;