UNPKG

dnschain

Version:

A blockchain-based DNS + HTTPS server that fixes HTTPS security, and more!

141 lines (118 loc) 5.36 kB
### dnschain http://dnschain.org Copyright (c) 2014 okTurtles Foundation This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ### module.exports = (dnschain) -> # expose these into our namespace for k of dnschain.globals eval "var #{k} = dnschain.globals.#{k};" # http://tools.ietf.org/html/rfc5246#section-7.4.1 # http://stackoverflow.com/questions/17832592/extract-server-name-indication-sni-from-tls-client-hello class HTTPSUtils categories: { SNI : 0 NO_SNI : 1 NOT_HTTPS : 2 INCOMPLETE : 3 } parseHTTPS: (packet) -> res = {} try res.contentType = packet.readUInt8 0 if res.contentType != 22 then return [@categories.NOT_HTTPS, {}] res.recordVersionMajor = packet.readUInt8 1 if res.recordVersionMajor >= 7 then return [@categories.NOT_HTTPS, {}] res.recordVersionMinor = packet.readUInt8 2 if res.recordVersionMinor >= 7 then return [@categories.NOT_HTTPS, {}] res.recordLength = packet.readUInt16BE 3 res.handshakeType = packet.readUInt8 5 if res.handshakeType != 1 then return [@categories.NOT_HTTPS, {}] res.handshakeLength = packet[6..8] res.handshakeVersion = packet.readUInt16BE 9 res.random = packet[11..42] res.sessionIDlength = packet.readUInt8 43 pos = res.sessionIDlength + 43 + 1 res.cipherSuitesLength = packet.readUInt16BE pos pos += res.cipherSuitesLength + 2 res.compressionMethodsLength = packet.readUInt8 pos pos += res.compressionMethodsLength + 1 res.extensionsLength = packet.readUInt16BE pos pos += 2 extensionsEnd = pos + res.extensionsLength - 1 jump = 0 res.extensions = {} while pos < extensionsEnd ext = {} ext.type = packet.readUInt16BE pos ext.length = packet.readUInt16BE (pos+2) jump = ext.length+4 ext.body = packet[pos..(pos+jump-1)] res.extensions[ext.type] = ext pos += jump if res.extensions["0"]? sniPos = 0 sni = res.extensions["0"] sni.sniType = sni.body.readUInt16BE 0 sni.sniLength = sni.body.readUInt16BE 2 sni.sniList = sni.body.readUInt16BE 4 sni.sniNameType = sni.body.readUInt8 6 sni.sniNameLength = sni.body.readUInt16BE 7 sni.sniName = sni.body[9..(9+sni.sniNameLength)] res.host = sni.sniName.toString "utf8" if res.host.length != sni.sniNameLength then return [@categories.NOT_HTTPS, {}] return [@categories.SNI, res] else return [@categories.NO_SNI, {}] catch ex return [@categories.INCOMPLETE, {}] # Open a TCP socket to a remote host. getStream: (host, port, cb) -> try done = (err, s) -> done = -> cb err, s s = net.createConnection {host, port}, -> done null, s s.on "error", (err) -> s.destroy(); done err s.on "close", -> s.destroy() s.on "timeout", -> s.destroy(); done new Error "getStream timed out" catch err done err # Received raw TCP data in chunked mode and attempt to extract Hello data # after every chunk. Return as soon as the Hello data has been obtained. getClientHello: (c, cb) -> received = [] buf = new Buffer [] done = (err, category, host, buf) -> c.removeAllListeners("data") done = -> cb err, category, host, buf c.on "data", (data) => c.pause() received.push data buf = Buffer.concat received [category, parsed] = @parseHTTPS buf switch category when @categories.SNI done null, category, parsed.host, buf when @categories.NO_SNI done null, category, null, buf when @categories.NOT_HTTPS done new Error "NOT HTTPS", category, null, buf when @categories.INCOMPLETE c.resume() else done new Error "Unimplemented", category, null, buf c.on "timeout", -> c.destroy() done new Error "HTTPS getClientHello timeout" c.on "error", (err) -> c.destroy() done err c.on "close", -> c.destroy() done new Error "HTTPS socket closed"