-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathstartssl.js
90 lines (79 loc) · 2.79 KB
/
startssl.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
'use strict'
/** startssl module to perform a STARTSSL-type procedure on a stream
* @module startssl
*/
const { createReader, createWriter } = require('awaitify-stream')
const byline = require('byline')
/* STARTSSL null procedure */
const none = (readSocket, writeSocket) => {}
/* STARTSSL SMTP procedure */
const smtp = async (readSocket, writeSocket) => {
let found
let line
line = await readSocket.readAsync()
if (!line.startsWith('220 ')) {
throw new Error(`Unexpected SMTP greeting: ${line}`)
}
await writeSocket.writeAsync('EHLO mail.example.com\r\n')
line = await readSocket.readAsync()
if (!line.startsWith('250-')) {
throw new Error(`Unexpected EHLO response: ${line}`)
}
do {
line = await readSocket.readAsync()
if (line.startsWith('250-STARTTLS')) found = true
} while (line.startsWith('250-'))
if (!line.startsWith('250 ')) {
throw new Error(`Unexpected EHLO response: ${line}`)
}
if (!found) throw new Error('SMTP server does not support STARTTLS')
await writeSocket.writeAsync('STARTTLS\r\n')
line = await readSocket.readAsync()
if (!line.startsWith('220 ')) {
throw new Error(`Unexpected STARTTLS response: ${line}`)
}
}
/* STARTSSL IMAP procedure */
const imap = async (readSocket, writeSocket) => {
let line
line = await readSocket.readAsync()
if (!line.startsWith('* OK')) {
throw new Error(`Unexpected IMAP greeting: ${line}`)
}
await writeSocket.writeAsync('a CAPABILITY\r\n')
line = await readSocket.readAsync()
if (!line.startsWith('* CAPABILITY')) {
throw new Error(`Unexpected IMAP CAPABILITY response: ${line}`)
}
if (!/\bSTARTTLS\b/.test(line)) {
throw new Error('IMAP server does not support STARTTLS')
}
line = await readSocket.readAsync()
if (!line.startsWith('a OK')) {
throw new Error(`Unexpected IMAP CAPABILITY response: ${line}`)
}
await writeSocket.writeAsync('a STARTTLS\r\n')
line = await readSocket.readAsync()
if (!line.startsWith('a OK')) {
throw new Error(`Unexpected IMAP STARTTLS response: ${line}`)
}
}
/** Perform a STARTSSL-type procedure on a stream. The stream will be set
* to 'binary' encoding.
*
* @async
* @param {stream.Duplex} socket - The stream to use.
* @param {string} [protocol='none'] - The protocol to use:
* 'none', 'smtp' or 'imap'.
* @throws {Error} If the protocol is unknown, or the server the stream
* is connected to does not respond as expected, or does not appear to
* support TLS.
*/
module.exports.startssl = async (socket, protocol) => {
socket.setEncoding('binary')
const readSocket = createReader(byline(socket))
const writeSocket = createWriter(socket)
const handler = { none, imap, smtp }[protocol || 'none']
if (!handler) throw new Error(`Unknown protocol ${protocol}`)
return handler(readSocket, writeSocket)
}