Skip to content

Commit

Permalink
starttls: only upgrade net.Socket (#809)
Browse files Browse the repository at this point in the history
  • Loading branch information
sonnyp committed Jan 2, 2020
1 parent 9137af4 commit bdb1b0e
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 52 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ rules:
operator-linebreak: [error, after, {overrides: {'?': before, ':': 'before'}}]
capitalized-comments:
[
error,
warn,
always,
{ignorePattern: prettier-ignore, ignoreConsecutiveComments: true},
]
Expand Down
25 changes: 4 additions & 21 deletions packages/connection/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,6 @@ const {parseHost, parseService} = require('./lib/util')

const NS_STREAM = 'urn:ietf:params:xml:ns:xmpp-streams'

function socketConnect(socket, ...params) {
return new Promise((resolve, reject) => {
function onError(err) {
socket.removeListener('connect', onConnect)
reject(err)
}

function onConnect(value) {
socket.removeListener('error', onError)
resolve(value)
}

socket.once('error', onError)
socket.once('connect', onConnect)
socket.connect(...params)
})
}

class Connection extends EventEmitter {
constructor(options = {}) {
super()
Expand Down Expand Up @@ -241,9 +223,11 @@ class Connection extends EventEmitter {
*/
async connect(service) {
this._status('connecting', service)
this._attachSocket(new this.Socket())
const socket = new this.Socket()
this._attachSocket(socket)
// The 'connect' status is set by the socket 'connect' listener
return socketConnect(this.socket, this.socketParameters(service))
socket.connect(this.socketParameters(service))
return promise(socket, 'connect')
}

/**
Expand Down Expand Up @@ -397,4 +381,3 @@ Connection.prototype.Socket = null
Connection.prototype.Parser = null

module.exports = Connection
module.exports.socketConnect = socketConnect
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'

const test = require('ava')
const {socketConnect} = require('..')
const {promise} = require('..')
const EventEmitter = require('events')

class Socket extends EventEmitter {
Expand All @@ -17,14 +17,15 @@ class Socket extends EventEmitter {
}
}

test('resolves if "connect" is emitted', async t => {
test('resolves if "event" is emitted', async t => {
const value = {}
const socket = new Socket(function() {
this.emit('connect', value)
})
t.is(socket.listenerCount('error'), 0)
t.is(socket.listenerCount('connect'), 0)
const p = socketConnect(socket, 'foo')
socket.connect()
const p = promise(socket, 'connect')
t.is(socket.listenerCount('error'), 1)
t.is(socket.listenerCount('connect'), 1)
const result = await p
Expand All @@ -33,14 +34,15 @@ test('resolves if "connect" is emitted', async t => {
t.is(socket.listenerCount('connect'), 0)
})

test('rejects if "error" is emitted', t => {
test('rejects if "errorEvent" is emitted', t => {
const error = new Error('foobar')
const socket = new Socket(function() {
this.emit('error', error)
})
t.is(socket.listenerCount('error'), 0)
t.is(socket.listenerCount('connect'), 0)
const p = socketConnect(socket, 'foo')
socket.connect()
const p = promise(socket, 'connect', 'error')
t.is(socket.listenerCount('error'), 1)
t.is(socket.listenerCount('connect'), 1)
return p.catch(err => {
Expand Down
5 changes: 3 additions & 2 deletions packages/resolve/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'

const resolve = require('./resolve')
const {socketConnect} = require('@xmpp/connection')
const {promise} = require('@xmpp/events')

async function fetchURIs(domain) {
return [
Expand Down Expand Up @@ -44,7 +44,8 @@ async function fallbackConnect(entity, uris) {
const socket = new Transport.prototype.Socket()

try {
await socketConnect(socket, params)
socket.connect(params)
await promise(socket, 'connect')
// eslint-disable-next-line no-unused-vars
} catch (err) {
return fallbackConnect(entity, uris)
Expand Down
2 changes: 1 addition & 1 deletion packages/resolve/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"node-fetch": false
},
"dependencies": {
"@xmpp/connection": "^0.9.1",
"@xmpp/events": "^0.9.0",
"@xmpp/xml": "^0.9.1",
"node-fetch": "^2.3.0"
},
Expand Down
30 changes: 12 additions & 18 deletions packages/starttls/client.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'

const xml = require('@xmpp/xml')
const tls = require('tls')
const {canUpgrade, upgrade} = require('./starttls')

/*
* References
Expand All @@ -10,20 +10,7 @@ const tls = require('tls')

const NS = 'urn:ietf:params:xml:ns:xmpp-tls'

function proceed(entity, options = {}) {
return new Promise((resolve, reject) => {
const tlsSocket = tls.connect(
{socket: entity._detachSocket(), host: entity.options.domain, ...options},
err => {
if (err) return reject(err)
entity._attachSocket(tlsSocket)
resolve()
}
)
})
}

async function starttls(entity) {
async function negotiate(entity) {
const element = await entity.sendReceive(xml('starttls', {xmlns: NS}))
if (element.is('proceed', NS)) {
return element
Expand All @@ -33,9 +20,16 @@ async function starttls(entity) {
}

module.exports = function({streamFeatures}) {
return streamFeatures.use('starttls', NS, async ({entity}) => {
await starttls(entity)
await proceed(entity)
return streamFeatures.use('starttls', NS, async ({entity}, next) => {
const {socket} = entity
if (!canUpgrade(socket)) {
return next()
}

await negotiate(entity)
const tlsSocket = await upgrade(socket, {host: entity.options.domain})
entity._attachSocket(tlsSocket)

await entity.restart()
})
}
1 change: 1 addition & 0 deletions packages/starttls/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"STARTTLS"
],
"dependencies": {
"@xmpp/events": "^0.9.0",
"@xmpp/xml": "^0.9.1"
},
"engines": {
Expand Down
20 changes: 20 additions & 0 deletions packages/starttls/starttls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict'

const tls = require('tls')
const net = require('net')
const {promise} = require('@xmpp/events')

function canUpgrade(socket) {
return socket instanceof net.Socket && !(socket instanceof tls.TLSSocket)
}

module.exports.canUpgrade = canUpgrade

async function upgrade(socket, options = {}) {
const tlsSocket = tls.connect({socket, ...options})
await promise(tlsSocket, 'secureConnect')

return tlsSocket
}

module.exports.upgrade = upgrade
13 changes: 13 additions & 0 deletions packages/starttls/starttls.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict'

const test = require('ava')
const tls = require('tls')
const {canUpgrade} = require('./starttls')
const net = require('net')
const WebSocket = require('../websocket/lib/Socket')

test('canUpgrade', t => {
t.is(canUpgrade(new WebSocket()), false)
t.is(canUpgrade(new tls.TLSSocket()), false)
t.is(canUpgrade(new net.Socket()), true)
})
15 changes: 12 additions & 3 deletions packages/starttls/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,18 @@ const {mock, stub} = require('sinon')
const test = require('ava')
const {mockClient, promise, delay} = require('@xmpp/test')
const tls = require('tls')
const net = require('net')
const EventEmitter = require('events')

function mockSocket() {
const socket = new net.Socket()
socket.write = (data, cb) => cb()
return socket
}

test('success', async t => {
const {entity} = mockClient()
entity.socket = mockSocket()
const {socket} = entity
const host = (entity.options.domain = 'foobar')

Expand All @@ -15,9 +24,8 @@ test('success', async t => {
.expects('connect')
.once()
.withArgs({socket, host})
.callsFake((options, callback) => {
process.nextTick(callback)
return {}
.callsFake(() => {
return new EventEmitter()
})

stub(entity, '_attachSocket')
Expand All @@ -43,6 +51,7 @@ test('success', async t => {

test('failure', async t => {
const {entity} = mockClient()
entity.socket = mockSocket()

entity.mockInput(
<features xmlns="http://etherx.jabber.org/streams">
Expand Down
2 changes: 1 addition & 1 deletion test/see-other-host.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test.afterEach(t => {
}
})

test.serial.only('see-other-host', async t => {
test.serial('see-other-host', async t => {
const net = require('net')
const Connection = require('../packages/connection-tcp')
const {promise} = require('../packages/events')
Expand Down

0 comments on commit bdb1b0e

Please sign in to comment.