diff --git a/Debug/amt_heci.js b/Debug/amt_heci.js new file mode 100644 index 0000000..99c7a1f --- /dev/null +++ b/Debug/amt_heci.js @@ -0,0 +1,281 @@ +var Q = require('queue'); + +function amt_heci() { + var emitterUtils = require('events').inherits(this); + emitterUtils.createEvent('error'); + emitterUtils.createEvent('connect'); + + var heci = require('heci'); + + this._amt = heci.create(); + this._amt.BiosVersionLen = 65; + this._amt.UnicodeStringLen = 20; + + this._amt.rq = new Q(); + this._amt.Parent = this; + this._amt.on('error', function (e) { this.Parent.emit('error', e); }); + this._amt.on('connect', function () { + this.Parent.emit('connect'); + this.on('data', function (chunk) { + //console.log("Received: " + chunk.length + " bytes"); + var header = this.Parent.getCommand(chunk); + //console.log("CMD = " + header.Command + " (Status: " + header.Status + ") Response = " + header.IsResponse); + + var user = this.rq.deQueue(); + var params = user.optional; + var callback = user.func; + + params.unshift(header); + callback.apply(this.Parent, params); + }); + }); + this._amt.connect(heci.GUIDS.AMT, { noPipeline: 1 }); + + this.getCommand = function (chunk) { + var command = chunk.length == 0 ? (this._amt.rq.peekQueue().cmd | 0x800000) : chunk.readUInt32LE(4); + var ret = { IsResponse: (command & 0x800000) == 0x800000 ? true : false, Command: (command & 0x7FFFFF), Status: chunk.length != 0 ? chunk.readUInt32LE(12) : -1, Data: chunk.length != 0 ? chunk.slice(16) : null }; + return (ret); + }; + + this.sendCommand = function () { + if (arguments.length < 3 || typeof (arguments[0]) != 'number' || typeof (arguments[1]) != 'object' || typeof (arguments[2]) != 'function') { throw ('invalid parameters'); } + var args = []; + for (var i = 3; i < arguments.length; ++i) { args.push(arguments[i]); } + + this._amt.rq.enQueue({ cmd: arguments[0], func: arguments[2], optional: args }); + + var header = Buffer.from('010100000000000000000000', 'hex'); + header.writeUInt32LE(arguments[0] | 0x04000000, 4); + header.writeUInt32LE(arguments[1] == null ? 0 : arguments[1].length, 8); + + this._amt.write(arguments[1] == null ? header : Buffer.concat([header, arguments[1]])); + } + + this.getVersion = function (callback) { + var optional = []; + for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(26, null, function (header, fn, opt) { + if (header.Status == 0) { + var i, CodeVersion = header.Data, val = { BiosVersion: CodeVersion.slice(0, this._amt.BiosVersionLen), Versions: [] }, v = CodeVersion.slice(this._amt.BiosVersionLen + 4); + for (i = 0; i < CodeVersion.readUInt32LE(this._amt.BiosVersionLen) ; ++i) { + val.Versions[i] = { Description: v.slice(2, v.readUInt16LE(0) + 2).toString(), Version: v.slice(4 + this._amt.UnicodeStringLen, 4 + this._amt.UnicodeStringLen + v.readUInt16LE(2 + this._amt.UnicodeStringLen)).toString() }; + v = v.slice(4 + (2 * this._amt.UnicodeStringLen)); + } + opt.unshift(val); + } else { + opt.unshift(null); + } + fn.apply(this, opt); + }, callback, optional); + }; + + this.getProvisioningState = function (callback) { + var optional = []; + for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(17, null, function (header, fn, opt) { + if (header.Status == 0) { + var result = {}; + result.state = header.Data.readUInt32LE(0); + if (result.state < 3) { result.stateStr = ["PRE", "IN", "POST"][result.state]; } + opt.unshift(result); + } else { + opt.unshift(null); + } + fn.apply(this, opt); + }, callback, optional); + }; + this.getProvisioningMode = function (callback) { + var optional = []; + for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(8, null, function (header, fn, opt) { + if (header.Status == 0) { + var result = {}; + result.mode = header.Data.readUInt32LE(0); + if (result.mode < 4) { result.modeStr = ["NONE", "ENTERPRISE", "SMALL_BUSINESS", "REMOTE_ASSISTANCE"][result.mode]; } + result.legacy = header.Data.readUInt32LE(4) == 0 ? false : true; + opt.unshift(result); + } else { + opt.unshift(null); + } + fn.apply(this, opt); + }, callback, optional); + }; + this.getEHBCState = function (callback) { + var optional = []; + for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(132, null, function (header, fn, opt) { + if (header.Status == 0) { + opt.unshift({ EHBC: header.Data.readUInt32LE(0) != 0 }); + } else { + opt.unshift(null); + } + fn.apply(this, opt); + }, callback, optional); + }; + this.getControlMode = function (callback) { + var optional = []; + for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(107, null, function (header, fn, opt) { + if (header.Status == 0) { + var result = {}; + result.controlMode = header.Data.readUInt32LE(0); + if (result.controlMode < 3) { result.controlModeStr = ["NONE_RPAT", "CLIENT", "ADMIN", "REMOTE_ASSISTANCE"][result.controlMode]; } + opt.unshift(result); + } else { + opt.unshift(null); + } + fn.apply(this, opt); + }, callback, optional); + }; + this.getMACAddresses = function (callback) { + var optional = []; + for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(37, null, function (header, fn, opt) { + if (header.Status == 0) { + opt.unshift({ DedicatedMAC: header.Data.slice(0, 6).toString('hex:'), HostMAC: header.Data.slice(6, 12).toString('hex:') }); + } else { opt.unshift({ DedicatedMAC: null, HostMAC: null }); } + fn.apply(this, opt); + }, callback, optional); + }; + this.getDnsSuffix = function (callback) { + var optional = []; + for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(54, null, function (header, fn, opt) { + if (header.Status == 0) { + var resultLen = header.Data.readUInt16LE(0); + if (resultLen > 0) { opt.unshift(header.Data.slice(2, 2 + resultLen).toString()); } else { opt.unshift(null); } + } else { + opt.unshift(null); + } + fn.apply(this, opt); + }, callback, optional); + }; + this.getHashHandles = function (callback) { + var optional = []; + for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(0x2C, null, function (header, fn, opt) { + var result = []; + if (header.Status == 0) { + var resultLen = header.Data.readUInt32LE(0); + for (var i = 0; i < resultLen; ++i) { + result.push(header.Data.readUInt32LE(4 + (4 * i))); + } + } + opt.unshift(result); + fn.apply(this, opt); + }, callback, optional); + }; + this.getCertHashEntry = function (handle, callback) { + var optional = []; + for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); } + + var data = new Buffer(4); + data.writeUInt32LE(handle, 0); + + this.sendCommand(0x2D, data, function (header, fn, opt) { + if (header.Status == 0) { + var result = {}; + result.isDefault = header.Data.readUInt32LE(0); + result.isActive = header.Data.readUInt32LE(4); + result.hashAlgorithm = header.Data.readUInt8(72); + if (result.hashAlgorithm < 4) { + result.hashAlgorithmStr = ["MD5", "SHA1", "SHA256", "SHA512"][result.hashAlgorithm]; + result.hashAlgorithmSize = [16, 20, 32, 64][result.hashAlgorithm]; + result.certificateHash = header.Data.slice(8, 8 + result.hashAlgorithmSize).toString('hex'); + } + result.name = header.Data.slice(73 + 2, 73 + 2 + header.Data.readUInt16LE(73)).toString(); + opt.unshift(result); + } else { + opt.unshift(null); + } + fn.apply(this, opt); + }, callback, optional); + }; + this.getCertHashEntries = function (callback) { + var optional = []; + for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); } + + this.getHashHandles(function (handles, fn, opt) { + var entries = []; + this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles); + }, callback, optional); + }; + this._getHashEntrySink = function (result, fn, opt, entries, handles) { + entries.push(result); + if (handles.length > 0) { + this.getCertHashEntry(handles.shift(), this._getHashEntrySink, fn, opt, entries, handles); + } else { + opt.unshift(entries); + fn.apply(this, opt); + } + } + this.getLocalSystemAccount = function (callback) { + var optional = []; + for (var i = 1; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(103, Buffer.alloc(40), function (header, fn, opt) { + if (header.Data.length == 68) { opt.unshift({ user: header.Data.slice(0, 34).toString(), pass: header.Data.slice(34, 67).toString(), raw: header.Data }); } else { opt.unshift(null); } + fn.apply(this, opt); + }, callback, optional); + } + this.unprovision = function (mode, callback) { + var optional = []; + for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); } + var data = new Buffer(4); + data.writeUInt32LE(mode, 0); + this.sendCommand(16, data, function (header, fn, opt) { + opt.unshift(header.Status); + fn.apply(this, opt); + }, callback, optional); + } + this.startConfiguration = function () { + var optional = []; + for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(0x29, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional); + } + this.stopConfiguration = function () { + var optional = []; + for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(0x5E, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional); + } + this.openUserInitiatedConnection = function () { + var optional = []; + for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(0x44, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional); + } + this.closeUserInitiatedConnection = function () { + var optional = []; + for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(0x45, data, function (header, fn, opt) { opt.unshift(header.Status); fn.apply(this, opt); }, callback, optional); + } + this.getRemoteAccessConnectionStatus = function () { + var optional = []; + for (var i = 2; i < arguments.length; ++i) { optional.push(arguments[i]); } + this.sendCommand(0x46, data, function (header, fn, opt) { + if (header.Status == 0) { + var hostname = v.slice(14, header.Data.readUInt16LE(12) + 14).toString() + opt.unshift({ status: header.Status, networkStatus: header.Data.readUInt32LE(0), remoteAccessStatus: header.Data.readUInt32LE(4), remoteAccessTrigger: header.Data.readUInt32LE(8), mpsHostname: hostname, raw: header.Data }); + } else { + opt.unshift({ status: header.Status }); + } + fn.apply(this, opt); + }, callback, optional); + } + this.getProtocolVersion = function (callback) { + var optional = []; + for (var i = 1; i < arguments.length; ++i) { opt.push(arguments[i]); } + + heci.doIoctl(heci.IOCTL.HECI_VERSION, Buffer.alloc(5), Buffer.alloc(5), function (status, buffer, self, fn, opt) { + if (status == 0) { + var result = buffer.readUInt8(0).toString() + '.' + buffer.readUInt8(1).toString() + '.' + buffer.readUInt8(2).toString() + '.' + buffer.readUInt16BE(3).toString(); + opt.unshift(result); + fn.apply(self, opt); + } + else { + opt.unshift(null); + fn.apply(self, opt); + } + }, this, callback, optional); + } +} + +module.exports = amt_heci; \ No newline at end of file diff --git a/Debug/amt_test.js b/Debug/amt_test.js new file mode 100644 index 0000000..4569ffd --- /dev/null +++ b/Debug/amt_test.js @@ -0,0 +1,61 @@ +var amt_heci = require('amt_heci'); +var amt = new amt_heci(); + +amt.getProtocolVersion(function (result) +{ + console.log('protocol version = ' + result); +}); +amt.on('error', function (e) { console.log(e);}); +amt.on('connect', function() +{ + console.log("Connected!"); + + this.getVersion(OnVersion); + this.getProvisioningState(OnProvisioningState); + this.getProvisioningMode(OnProvisioningMode); + this.getEHBCState(OnEHBC); + this.getControlMode(OnEHBC); + this.getMACAddresses(OnEHBC); + this.getDnsSuffix(OnDns); + this.getCertHashEntries(OnHashEntries); + //this.getHashHandles(OnGetHashHandles); +}); +function OnGetHashHandles(handles) +{ + console.log(handles.length + " HashHandles"); + for (var i = 0; i < handles.length; ++i) + { + amt.getCertHashEntry(handles[i], OnEHBC); + } +} +function OnHashEntries(entries) +{ + for(var i=0;i 0) +{ + console.log("\nIntegrating Dependencies:") + addOn = ""; + for(i=0;i 0) + { + var len = this.buffer[0].data.length - this.buffer[0].offset; + var offset = this.buffer[0].offset; + + if(len > (size - bytesRead)) + { + // Only reading a subset + list.push(this.buffer[0].data.slice(offset, offset + size - bytesRead)); + this.buffer[0].offset += (size - bytesRead); + bytesRead += (size - bytesRead); + } + else + { + // Reading the entire thing + list.push(this.buffer[0].data.slice(offset)); + bytesRead += len; + this.buffer.shift(); + } + } + this._readCheckImmediate = setImmediate(function (buffered) + { + buffered._readCheckImmediate = undefined; + if(buffered.buffer.length == 0) + { + // drained + buffered.emit('drain'); + } + else + { + // not drained + buffered.emit('readable'); + } + }, this); + return (Buffer.concat(list)); + }; +} + + +function lme_heci() +{ + var emitterUtils = require('events').inherits(this); + emitterUtils.createEvent('error'); + emitterUtils.createEvent('connect'); + + var heci = require('heci'); + this.INITIAL_RXWINDOW_SIZE = 4096; + + this._LME = heci.create(); + this._LME.LMS = this; + this._LME.on('error', function (e) { this.Parent.emit('error', e); }); + this._LME.on('connect', function () + { + console.log('emitting connect'); + this.LMS.emit('connect'); + this.on('data', function (chunk) + { + // this = HECI + var cmd = chunk.readUInt8(0); + + switch(cmd) + { + default: + //console.log('Received ' + chunk.length + ' bytes of data for LMS'); + //console.log('Command = ' + cmd); + break; + case APF_SERVICE_REQUEST: + var nameLen = chunk.readUInt32BE(1); + var name = chunk.slice(5, nameLen + 5); + //console.log("Service Request for: " + name); + if (name == 'pfwd@amt.intel.com' || name == 'auth@amt.intel.com') + { + var outBuffer = Buffer.alloc(5 + nameLen); + outBuffer.writeUInt8(6, 0); + outBuffer.writeUInt32BE(nameLen, 1); + outBuffer.write(name.toString(), 5); + this.write(outBuffer); + //console.log('Answering APF_SERVICE_REQUEST'); + } + else + { + //console.log('UNKNOWN APF_SERVICE_REQUEST'); + } + break; + case APF_GLOBAL_REQUEST: + var nameLen = chunk.readUInt32BE(1); + var name = chunk.slice(5, nameLen + 5).toString(); + + switch(name) + { + case 'tcpip-forward': + var len = chunk.readUInt32BE(nameLen + 6); + var port = chunk.readUInt32BE(nameLen + 10 + len); + //console.log("[" + chunk.length + "/" + len + "] APF_GLOBAL_REQUEST for: " + name + " on port " + port); + if (this[name] == undefined) + { + this[name] = {}; + } + this[name][port] = require('net').createServer(); + this[name][port].HECI = this; + this[name][port].listen({ port: port }); + this[name][port].on('connection', function (socket) + { + //console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort); + this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort); + }); + var outBuffer = Buffer.alloc(5); + outBuffer.writeUInt8(81, 0); + outBuffer.writeUInt32BE(port, 1); + this.write(outBuffer); + break; + case 'cancel-tcpip-forward': + break; + case 'udp-send-to@amt.intel.com': + break; + default: + //console.log("Unknown APF_GLOBAL_REQUEST for: " + name); + break; + } + break; + case APF_CHANNEL_OPEN_CONFIRMATION: + var rChannel = chunk.readUInt32BE(1); + var sChannel = chunk.readUInt32BE(5); + var wSize = chunk.readUInt32BE(9); + //console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize); + if (this.sockets[rChannel] != undefined) + { + this.sockets[rChannel].lme.amtId = sChannel; + this.sockets[rChannel].lme.rxWindow = wSize; + this.sockets[rChannel].lme.txWindow = wSize; + this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED'; + //console.log('LME_CS_CONNECTED'); + this.sockets[rChannel].bufferedStream = new stream_bufferedWrite(); + this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel]; + this.sockets[rChannel].bufferedStream.on('readable', function () + { + if(this.socket.lme.txWindow > 0) + { + var buffer = this.read(this.socket.lme.txWindow); + var packet = Buffer.alloc(9 + buffer.length); + packet.writeUInt8(APF_CHANNEL_DATA, 0); + packet.writeUInt32BE(this.socket.lme.amtId, 1); + packet.writeUInt32BE(buffer.length, 5); + buffer.copy(packet, 9); + this.socket.lme.txWindow -= buffer.length; + this.socket.HECI.write(packet); + } + }); + this.sockets[rChannel].bufferedStream.on('drain', function () + { + this.socket.resume(); + }); + this.sockets[rChannel].on('data', function (chunk) + { + if (!this.bufferedStream.write(chunk)) { this.pause(); } + }); + this.sockets[rChannel].on('end', function () + { + var outBuffer = Buffer.alloc(5); + outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0); + outBuffer.writeUInt32BE(this.lme.amtId, 1); + this.HECI.write(outBuffer); + }); + this.sockets[rChannel].resume(); + } + + break; + case APF_PROTOCOLVERSION: + var major = chunk.readUInt32BE(1); + var minor = chunk.readUInt32BE(5); + var reason = chunk.readUInt32BE(9); + var outBuffer = Buffer.alloc(93); + outBuffer.writeUInt8(192, 0); + outBuffer.writeUInt32BE(1, 1); + outBuffer.writeUInt32BE(0, 5); + outBuffer.writeUInt32BE(reason, 9); + //console.log('Answering PROTOCOL_VERSION'); + this.write(outBuffer); + break; + case APF_CHANNEL_WINDOW_ADJUST: + var rChannelId = chunk.readUInt32BE(1); + var bytesToAdd = chunk.readUInt32BE(5); + if (this.sockets[rChannelId] != undefined) + { + this.sockets[rChannelId].lme.txWindow += bytesToAdd; + if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting()) + { + this.sockets[rChannelId].bufferedStream.emit('readable'); + } + } + else + { + //console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_WINDOW_ADJUST'); + } + break; + case APF_CHANNEL_DATA: + var rChannelId = chunk.readUInt32BE(1); + var dataLen = chunk.readUInt32BE(5); + var data = chunk.slice(9, 9 + dataLen); + if (this.sockets[rChannelId] != undefined) + { + this.sockets[rChannelId].pendingBytes.push(data.length); + this.sockets[rChannelId].write(data, function () + { + var written = this.pendingBytes.shift(); + var outBuffer = Buffer.alloc(9); + outBuffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0); + outBuffer.writeUInt32BE(this.lme.amtId, 1); + outBuffer.writeUInt32BE(written, 5); + this.HECI.write(outBuffer); + }); + } + else + { + //console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_DATA'); + } + break; + case APF_CHANNEL_CLOSE: + var rChannelId = chunk.readUInt32BE(1); + if (this.sockets[rChannelId] != undefined) + { + this.sockets[rChannelId].end(); + var amtId = this.sockets[rChannelId].lme.amtId; + var buffer = Buffer.alloc(5); + delete this.sockets[rChannelId]; + + buffer.writeUInt8(APF_CHANNEL_CLOSE, 0); + buffer.writeUInt32BE(amtId, 1); + this.write(buffer); + } + else + { + //console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_CLOSE'); + } + break; + } + }); + }); + + this.bindDuplexStream = function (duplexStream, remoteFamily, localPort) + { + var socket = duplexStream; + //console.log('New [' + remoteFamily + '] Virtual Connection/' + socket.localPort); + socket.pendingBytes = []; + socket.HECI = this._LME; + socket.LMS = this; + socket.lme = new lme_object(); + socket.lme.Socket = socket; + var buffer = new MemoryStream(); + buffer.writeUInt8(0x5A); + buffer.writeUInt32BE(15); + buffer.write('forwarded-tcpip'); + buffer.writeUInt32BE(socket.lme.ourId); + buffer.writeUInt32BE(this.INITIAL_RXWINDOW_SIZE); + buffer.writeUInt32BE(0xFFFFFFFF); + for (var i = 0; i < 2; ++i) + { + if (remoteFamily == 'IPv6') + { + buffer.writeUInt32BE(3); + buffer.write('::1'); + } + else + { + buffer.writeUInt32BE(9); + buffer.write('127.0.0.1'); + } + + buffer.writeUInt32BE(localPort); + } + this._LME.write(buffer.buffer); + if (this._LME.sockets == undefined) { this._LME.sockets = {}; } + this._LME.sockets[socket.lme.ourId] = socket; + socket.pause(); + }; + + this._LME.connect(heci.GUIDS.LME, { noPipeline: 0 }); +} + +module.exports = lme_heci; diff --git a/Debug/lme_test.js b/Debug/lme_test.js new file mode 100644 index 0000000..3d9371e --- /dev/null +++ b/Debug/lme_test.js @@ -0,0 +1,37 @@ +var lme_heci = require('lme_heci'); +var LMS = new lme_heci(); +var http = require('http'); +var _IntelAMTWebAppETAG = "qYelOYPQpH0ndsSfZOXM"; +var _IntelAmtWebApp = "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"; + +LMS.on('error', function (e) { console.log(e); }); +LMS.on('connect', function () +{ + console.log("Connected!"); + LMS.meshCommander = http.createServer() + LMS.meshCommander.listen(16994); + LMS.meshCommander.on('upgrade', function (req, socket, head) + { + socket.upgradeWebSocket(); + LMS.bindDuplexStream(socket, 'IPv4', 16992); + }); + LMS.meshCommander.on('request', function (imsg, rsp) + { + console.log("WebRequest for " + imsg.url); + switch(imsg.url) + { + case '/': // WebApp + rsp.writeHead(200, 'OK', { Server: 'JSLMS', 'Cache-Control': 'max-age=0, no-cache', 'X-Frame-Options': 'DENY', 'Content-Type': 'text/html', 'Content-Encoding': 'gzip', ETag: _IntelAMTWebAppETAG }); + rsp.write(Buffer.from(_IntelAmtWebApp, 'base64')); + rsp.end(); + break; + case '/scriptblocks.txt': + rsp.statusCode = 404; + rsp.statusMessage = "Not Found"; + rsp.end(); + break; + } + + }); + console.log("Mesh Commander Started on port: 16994"); +}); diff --git a/Debug/newstringtest.js b/Debug/newstringtest.js new file mode 100644 index 0000000..730a09d --- /dev/null +++ b/Debug/newstringtest.js @@ -0,0 +1,18 @@ +var http = require('http'); + +console.log('starting client test'); +console.log('Sending Request'); +var req = http.request({host: '127.0.0.1', port: 9093, protocol: 'ws:'}); + + +req.on('upgrade', function (res, sk, h) +{ + sk.on('ping', function () { console.log('received ping'); }); + sk.on('pong', function () { console.log('received pong'); }); + this.websocket = sk; + + console.log("Upgraded to WebSocket!"); sk.write(JSON.stringify({ a: 'hello' })); +}); +//req.end(); + + diff --git a/Debug/parseXml.js b/Debug/parseXml.js new file mode 100644 index 0000000..7da186e --- /dev/null +++ b/Debug/parseXml.js @@ -0,0 +1,205 @@ +Object.defineProperty(Array.prototype, "peek", +{ + value: function () + { + return (this.length > 0 ? this[this.length - 1] : undefined); + } +}); + + +function _treeBuilder() +{ + this.tree = []; + + this.push = function (element) + { + this.tree.push(element); + }; + this.pop = function () + { + var element = this.tree.pop(); + if(this.tree.length>0) + { + this.tree.peek().childNodes.push(element); + } + return (element); + }; + this.peek = function() + { + return (this.tree.peek()); + } + this.addNamespace = function(prefix, namespace) + { + this.tree.peek().nsTable[prefix] = namespace; + if(this.tree.peek().attributes.length > 0) + { + for(var i = 0; i=0;--i) + { + if(this.tree[i].nsTable[prefix] != undefined) + { + return (this.tree[i].nsTable[prefix]); + } + } + return ('undefined'); + } +} + + +// This is a drop-in replacement to _turnToXml() that works without xml parser dependency. +function _turnToXml(text) +{ + if (text == null) return null; + return ({ childNodes: [_turnToXmlRec(text)], getElementsByTagName: _getElementsByTagName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS }); +} + +function _getElementsByTagNameNS(ns, name) +{ + var ret = []; _xmlTraverseAllRec(this.childNodes, function (node) + { + if (node.localName == name && (node.namespace == ns || ns == '*')) { ret.push(node); } + }); return ret; +} +function _getElementsByTagName(name) +{ + var ret = []; _xmlTraverseAllRec(this.childNodes, function (node) + { + if (node.localName == name) { ret.push(node); } + }); return ret; +} +function _getChildElementsByTagName(name) +{ + var ret = []; + if (this.childNodes != undefined) + { + for (var node in this.childNodes) { + if (this.childNodes[node].localName == name) { ret.push(this.childNodes[node]); } + } + } + return (ret); +} +function _getChildElementsByTagNameNS(ns, name) +{ + var ret = []; + if (this.childNodes != undefined) + { + for (var node in this.childNodes) + { + if (this.childNodes[node].localName == name && (ns == '*' || this.childNodes[node].namespace == ns)) { ret.push(this.childNodes[node]); } + } + } + return (ret); +} + +function _xmlTraverseAllRec(nodes, func) { for (var i in nodes) { func(nodes[i]); if (nodes[i].childNodes) { _xmlTraverseAllRec(nodes[i].childNodes, func); } } } +function _turnToXmlRec(text) +{ + var elementStack = new _treeBuilder(); + var lastElement = null; + + var x1 = text.split('<'), ret = [], element = null, currentElementName = null; + for (var i in x1) + { + var x2 = x1[i].split('>'), x3 = x2[0].split(' '), elementName = x3[0]; + if ((elementName.length > 0) && (elementName[0] != '?')) + { + if (elementName[0] != '/') + { + var localName; + var localname2 = elementName.split(' ')[0].split(':'), localName = (localname2.length > 1) ? localname2[1] : localname2[0]; + var attributes = []; + Object.defineProperty(attributes, "get", + { + value: function () + { + if (arguments.length == 1) + { + for (var a in this) { if (this[a].name == arguments[0]) { return (this[a]); } } + } + else if (arguments.length == 2) + { + for (var a in this) { if (this[a].name == arguments[1] && (arguments[0] == '*' || this[a].namespace == arguments[0])) { return (this[a]); } } + } + else + { + throw ('attributes.get(): Invalid number of parameters'); + } + } + }); + + + elementStack.push({ name: elementName, localName: localName, getChildElementsByTagName: _getChildElementsByTagName, getElementsByTagNameNS: _getElementsByTagNameNS, getChildElementsByTagNameNS: _getChildElementsByTagNameNS, attributes: attributes, childNodes: [], nsTable: {} }); + + // Parse Attributes + if (x3.length > 0) + { + var skip = false; + for (var j in x3) + { + if (x3[j] == '/') + { + // This is an empty Element + elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':'))); + elementStack.peek().textContent = ''; + lastElement = elementStack.pop(); + skip = true; + break; + } + var k = x3[j].indexOf('='); + if (k > 0) + { + var attrName = x3[j].substring(0, k); + var attrValue = x3[j].substring(k + 2, x3[j].length - 1); + var attrNS = elementStack.getNamespace('*'); + + if (attrName == 'xmlns') + { + elementStack.addNamespace('*', attrValue); + attrNS = attrValue; + } + else if (attrName.startsWith('xmlns:')) + { + elementStack.addNamespace(attrName.substring(6), attrValue); + } + else + { + var ax = attrName.split(':'); + if (ax.length == 2) { attrName = ax[1]; attrNS = elementStack.getNamespace(ax[0]); } + } + elementStack.peek().attributes.push({ name: attrName, value: attrValue, namespace: attrNS }); + } + } + if (skip) { continue; } + } + elementStack.peek().namespace = elementStack.peek().name == elementStack.peek().localName ? elementStack.getNamespace('*') : elementStack.getNamespace(elementStack.peek().name.substring(0, elementStack.peek().name.indexOf(':'))); + if (x2[1]) { elementStack.peek().textContent = x2[1]; } + } + else + { + lastElement = elementStack.pop(); + } + } + } + return lastElement; +} + +module.exports = _turnToXml; \ No newline at end of file diff --git a/Debug/serviceManager.js b/Debug/serviceManager.js new file mode 100644 index 0000000..f61589b --- /dev/null +++ b/Debug/serviceManager.js @@ -0,0 +1,243 @@ + + +function parseServiceStatus(token) +{ + var j = {}; + var serviceType = token.Deref(0, 4).IntVal; + j.isFileSystemDriver = ((serviceType & 0x00000002) == 0x00000002); + j.isKernelDriver = ((serviceType & 0x00000001) == 0x00000001); + j.isSharedProcess = ((serviceType & 0x00000020) == 0x00000020); + j.isOwnProcess = ((serviceType & 0x00000010) == 0x00000010); + j.isInteractive = ((serviceType & 0x00000100) == 0x00000100); + switch (token.Deref((1 * 4), 4).IntVal) + { + case 0x00000005: + j.state = 'CONTINUE_PENDING'; + break; + case 0x00000006: + j.state = 'PAUSE_PENDING'; + break; + case 0x00000007: + j.state = 'PAUSED'; + break; + case 0x00000004: + j.state = 'RUNNING'; + break; + case 0x00000002: + j.state = 'START_PENDING'; + break; + case 0x00000003: + j.state = 'STOP_PENDING'; + break; + case 0x00000001: + j.state = 'STOPPED'; + break; + } + var controlsAccepted = token.Deref((2 * 4), 4).IntVal + j.controlsAccepted = []; + if ((controlsAccepted & 0x00000010) == 0x00000010) + { + j.controlsAccepted.push('SERVICE_CONTROL_NETBINDADD'); + j.controlsAccepted.push('SERVICE_CONTROL_NETBINDREMOVE'); + j.controlsAccepted.push('SERVICE_CONTROL_NETBINDENABLE'); + j.controlsAccepted.push('SERVICE_CONTROL_NETBINDDISABLE'); + } + if ((controlsAccepted & 0x00000008) == 0x00000008) { j.controlsAccepted.push('SERVICE_CONTROL_PARAMCHANGE'); } + if ((controlsAccepted & 0x00000002) == 0x00000002) { j.controlsAccepted.push('SERVICE_CONTROL_PAUSE'); j.controlsAccepted.push('SERVICE_CONTROL_CONTINUE'); } + if ((controlsAccepted & 0x00000100) == 0x00000100) { j.controlsAccepted.push('SERVICE_CONTROL_PRESHUTDOWN'); } + if ((controlsAccepted & 0x00000004) == 0x00000004) { j.controlsAccepted.push('SERVICE_CONTROL_SHUTDOWN'); } + if ((controlsAccepted & 0x00000001) == 0x00000001) { j.controlsAccepted.push('SERVICE_CONTROL_STOP'); } + if ((controlsAccepted & 0x00000020) == 0x00000020) { j.controlsAccepted.push('SERVICE_CONTROL_HARDWAREPROFILECHANGE'); } + if ((controlsAccepted & 0x00000040) == 0x00000040) { j.controlsAccepted.push('SERVICE_CONTROL_POWEREVENT'); } + if ((controlsAccepted & 0x00000080) == 0x00000080) { j.controlsAccepted.push('SERVICE_CONTROL_SESSIONCHANGE'); } + j.pid = token.Deref((7 * 4), 4).IntVal + return (j); +} + +function serviceManager() +{ + this.GM = require('_GenericMarshal'); + this.proxy = this.GM.CreateNativeProxy('Advapi32.dll'); + this.proxy.CreateMethod('OpenSCManagerA'); + this.proxy.CreateMethod('EnumServicesStatusExA'); + this.proxy.CreateMethod('OpenServiceA'); + this.proxy.CreateMethod('QueryServiceStatusEx'); + this.proxy.CreateMethod('ControlService'); + this.proxy.CreateMethod('StartServiceA'); + this.proxy.CreateMethod('CloseServiceHandle'); + this.proxy.CreateMethod('CreateServiceA'); + this.proxy.CreateMethod('ChangeServiceConfig2A'); + this.proxy.CreateMethod('DeleteService'); + this.proxy2 = this.GM.CreateNativeProxy('Kernel32.dll'); + this.proxy2.CreateMethod('GetLastError'); + + this.enumerateService = function() + { + var machineName = this.GM.CreatePointer(); + var dbName = this.GM.CreatePointer(); + var handle = this.proxy.OpenSCManagerA(0x00, 0x00, 0x0001 | 0x0004); + + var bytesNeeded = this.GM.CreateVariable(4); + var servicesReturned = this.GM.CreateVariable(4); + var resumeHandle = this.GM.CreateVariable(4); + //var services = this.proxy.CreateVariable(262144); + + var success = this.proxy.EnumServicesStatusExA(handle, 0, 0x00000030, 0x00000003, 0x00, 0x00, bytesNeeded, servicesReturned, resumeHandle, 0x00); + if(bytesNeeded.IntVal <= 0) + { + throw ('error enumerating services'); + } + + var sz = bytesNeeded.IntVal; + var services = this.GM.CreateVariable(sz); + this.proxy.EnumServicesStatusExA(handle, 0, 0x00000030, 0x00000003, services, sz, bytesNeeded, servicesReturned, resumeHandle, 0x00); + console.log("servicesReturned", servicesReturned.IntVal, 'PtrSize = ' + dbName._size); + + var ptrSize = dbName._size; + var blockSize = 36 + (2 * ptrSize); + console.log('blockSize', blockSize); + + var retVal = []; + for (var i = 0; i < servicesReturned.IntVal; ++i) + { + var token = services.Deref(i * blockSize, blockSize); + var j = {}; + j.name = token.Deref(0, ptrSize).Deref().String; + j.displayName = token.Deref(ptrSize, ptrSize).Deref().String; + j.status = parseServiceStatus(token.Deref(2 * ptrSize, 36)); + retVal.push(j); + } + + this.proxy.CloseServiceHandle(handle); + + return (retVal); + } + this.getService = function(name) + { + var serviceName = this.GM.CreateVariable(name); + var ptr = this.GM.CreatePointer(); + var bytesNeeded = this.GM.CreateVariable(ptr._size); + var handle = this.proxy.OpenSCManagerA(0x00, 0x00, 0x0001 | 0x0004 | 0x0020 | 0x0010); + if (handle == 0) { throw ('could not open ServiceManager'); } + var h = this.proxy.OpenServiceA(handle, serviceName, 0x0004 | 0x0020 | 0x0010 | 0x00010000); + if (h != 0) + { + var success = this.proxy.QueryServiceStatusEx(h, 0, 0, 0, bytesNeeded); + var status = this.GM.CreateVariable(bytesNeeded.IntVal); + success = this.proxy.QueryServiceStatusEx(h, 0, status, status._size, bytesNeeded); + if (success != 0) + { + retVal = {}; + retVal.status = parseServiceStatus(status); + retVal._scm = handle; + retVal._service = h; + retVal._GM = this.GM; + retVal._proxy = this.proxy; + require('events').inherits(retVal); + retVal.on('~', function () { this._proxy.CloseServiceHandle(this); this._proxy.CloseServiceHandle(this._scm); }); + retVal.name = name; + retVal.stop = function() + { + if(this.status.state == 'RUNNING') + { + var newstate = this._GM.CreateVariable(36); + var success = this._proxy.ControlService(this._service, 0x00000001, newstate); + if(success == 0) + { + throw (this.name + '.stop() failed'); + } + } + else + { + throw ('cannot call ' + this.name + '.stop(), when current state is: ' + this.status.state); + } + } + retVal.start = function() + { + if(this.status.state == 'STOPPED') + { + var success = this._proxy.StartServiceA(this._service, 0, 0); + if(success == 0) + { + throw (this.name + '.start() failed'); + } + } + else + { + throw ('cannot call ' + this.name + '.start(), when current state is: ' + this.status.state); + } + } + return (retVal); + } + else + { + + } + } + + this.proxy.CloseServiceHandle(handle); + throw ('could not find service: ' + name); + } + this.installService = function(options) + { + var handle = this.proxy.OpenSCManagerA(0x00, 0x00, 0x0002); + if (handle == 0) { throw ('error opening SCManager'); } + var serviceName = this.GM.CreateVariable(options.name); + var displayName = this.GM.CreateVariable(options.displayName); + var allAccess = 0x000F01FF; + var serviceType; + var servicePath = this.GM.CreateVariable(options.servicePath); + + switch(options.startType) + { + case 'BOOT_START': + serviceType = 0x00; + break; + case 'SYSTEM_START': + serviceType = 0x01; + break; + case 'AUTO_START': + serviceType = 0x02; + break; + case 'DEMAND_START': + serviceType = 0x03; + break; + default: + serviceType = 0x04; // Disabled + break; + } + var h = this.proxy.CreateServiceA(handle, serviceName, displayName, allAccess, 0x10 | 0x100, serviceType, 0, servicePath, 0, 0, 0, 0, 0); + if (h == 0) { this.proxy.CloseServiceHandle(handle); throw ('Error Creating Service'); } + if(options.description) + { + console.log(options.description); + + var dscPtr = this.GM.CreatePointer(); + dscPtr.Val = this.GM.CreateVariable(options.description); + + if(this.proxy.ChangeServiceConfig2A(h, 1, dscPtr)==0) + { + this.proxy.CloseServiceHandle(h); + this.proxy.CloseServiceHandle(handle); + throw ('Unable to set description'); + } + } + this.proxy.CloseServiceHandle(h); + this.proxy.CloseServiceHandle(handle); + return (this.getService(options.name)); + } + this.uninstallService = function(name) + { + var service = this.getService(name); + if(service.status.state == 'STOPPED') + { + if (this.proxy.DeleteService(service._service) == 0) { throw ('Uninstall Service for: ' + name + ', failed with error: ' + this.proxy2.GetLastError()); } + } + else + { + throw ('Cannot uninstall service: ' + name + ', because it is: ' + service.status.state); + } + } +} + +module.exports = serviceManager; \ No newline at end of file diff --git a/Debug/upnpcp.js b/Debug/upnpcp.js new file mode 100644 index 0000000..12386d1 --- /dev/null +++ b/Debug/upnpcp.js @@ -0,0 +1,655 @@ + +var parseXml = require('parseXml'); +var http = require('http'); +var dgram = require('dgram'); +var os = require('os'); +var MemoryStream = require('MemoryStream'); +var net = require('net'); + +//var networkMonitor = require('NetworkMonitor'); + +function upnpdevice(descriptionUrl, usn, cp) +{ + var d = descriptionUrl.split('/'); + this.BaseURL = d[0] + '//' + d[2]; + var emitterUtils = require('events').inherits(this); + emitterUtils.createEvent('bye'); + emitterUtils.createEvent('error'); + emitterUtils.createEvent('alive'); + emitterUtils.createEvent('serviceLoaded'); + this.pendingServices = 0; + this.usn = usn; + this.cp = cp; + this.req = http.get(descriptionUrl); + this.req.device = this; + this.req.on('error', function () + { + this.device.emit('error', 'Error fetching Description Document from ' + this.device.BaseURL); + }); + this.req.on('response', function (msg) + { + if (msg.statusCode == 200) + { + msg.device = this.device; + this.device.dd = new MemoryStream(); + this.device.dd.device = this.device; + msg.pipe(this.device.dd); + this.device.dd.on('end', function () + { + upnpdevice_parseXml.apply(this.device, [this.buffer.toString()]); + }); + } + else + { + this.device.emit('error', 'Error (' + msg.statusCode + ') Fetching Description Document from: ' + this.device.BaseURL); + } + }); + this.loadAllServices = function () { this.rootDevice.loadAllServices(); }; + this.makeUrl = function (url) + { + if(url.startsWith('/')) + { + if (this.BaseURL.endsWith('/')) + { + return (this.BaseURL + url.substring(1)); + } + else + { + return (this.BaseURL + url); + } + } + else + { + if (this.BaseURL.endsWith('/')) + { + return (this.BaseURL + url); + } + else + { + return (this.BaseURL + '/' + url); + } + } + }; + this.on('~', upnpdevice_Cleanup); + this.on('serviceLoaded', function (svc) + { + if(--this.pendingServices == 0) + { + // All Services have been loaded + this.cp.emit('device', this); + } + }); + this.getDevice = function (udn) + { + return (this.rootDevice.getDevice(udn)); + }; +} + +function upnpdevice_Cleanup() +{ + console.log('Finalizing: ' + this.rootDevice.friendlyName); +} +function upnpservice(parentDevice, xmlDoc) +{ + var emitterUtils = require('events').inherits(this); + this.device = parentDevice; + + this.serviceType = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'serviceType')[0].textContent; + this.serviceId = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'serviceId')[0].textContent; + this.controlURL = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'controlURL')[0].textContent; + this.eventSubURL = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'eventSubURL')[0].textContent; + this.SCPDURL = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'SCPDURL')[0].textContent; + + if (!this.controlURL.startsWith('http:') && !this.controlURL.startsWith('https:')) { this.controlURL = this.device.rootDevice.makeUrl(this.controlURL); } + if (!this.eventSubURL.startsWith('http:') && !this.eventSubURL.startsWith('https:')) { this.eventSubURL = this.device.rootDevice.makeUrl(this.eventSubURL); } + if (!this.SCPDURL.startsWith('http:') && !this.SCPDURL.startsWith('https:')) { this.SCPDURL = this.device.rootDevice.makeUrl(this.SCPDURL); } + + this.load = function () + { + ++this.device.rootDevice.pendingServices; + this.req = http.get(this.SCPDURL); + this.req.service = this; + this.req.on('error', function () { this.service.device.rootDevice.emit('error', 'Error fetching SCPD from: ' + this.service.SCPDURL); }); + this.req.on('response', function (msg) + { + if (msg.statusCode == 200) + { + msg.service = this.service; + this.service.scpdxml = new MemoryStream(); + this.service.scpdxml.service = this.service; + msg.pipe(this.service.scpdxml); + this.service.scpdxml.on('end', function () + { + try + { + upnpservice_parseScpd.apply(this.service, [this.buffer.toString()]); + } + catch(e) + { + this.service.device.rootDevice.emit('error', 'error parsing SCPD: ' + e); + } + }); + } + else + { + this.service.device.rootDevice.emit('error', 'Error loading SCPD from: ' + this.service.SCPDURL); + } + }); + } + this.getAction = function(name) + { + for(var a in this.actions) + { + if (this.actions[a].name == name) { return (this.actions[a]); } + } + return (undefined); + } +} + +function upnpargument(action, xmlDoc) +{ + this.action = action; + this.name = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'name')[0].textContent; + this.direction = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'direction')[0].textContent; + this.relatedStateVariable = action.service.stateVariables.get(xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'relatedStateVariable')[0].textContent); +} + +function post_response(msg) +{ + if (msg.statusCode != 200) + { + var userArgs = this.args; + if (userArgs.length > 0 && typeof (userArgs[0] == 'function')) + { + var fn = userArgs.shift(); + userArgs.unshift(msg.StatusCode ? msg.StatusCode : msg.url); + fn.apply(this.action, userArgs); + } + return; + } + + var buff = new MemoryStream(); + buff.req = this; + msg.pipe(buff); + buff.on('end', function () + { + var body = {}; + var xml = parseXml(this.buffer.toString()); + var action = this.req.action; + var userArgs = this.req.args; + var actionResponse = xml.getElementsByTagNameNS(action.service.serviceType, action.name + 'Response')[0]; + if(actionResponse) + { + for(var child in actionResponse.childNodes) + { + if(action.arguments.get(actionResponse.childNodes[child].name)) + { + body[actionResponse.childNodes[child].name] = actionResponse.childNodes[child].textContent; + } + } + if(userArgs.length > 0 && typeof(userArgs[0]) == 'function') + { + var fn = userArgs.shift(); + userArgs.unshift(body); + fn.apply(action, userArgs); + } + } + + }); +} + +function upnpaction(service, xmlDoc) +{ + this.pendingPosts = []; + this.arguments = []; Object.defineProperty(this.arguments, "get", { value: function (name) { for (var i in this) { if (this[i].name == name) { return (this[i]); } } return (undefined); } }); + this.service = service; + + this.name = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'name')[0].textContent; + var argumentList = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'argumentList')[0]; + + if (argumentList) + { + var arguments = argumentList.getChildElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'argument'); + for (var i in arguments) + { + this.arguments.push(new upnpargument(this, arguments[i])); + } + } + else + { + //console.log(this.service.scpdxml.buffer.toString()); + } + + this.invoke = function (args) + { + var parameters = ''; + for (var i in this.arguments) + { + if(this.arguments[i].direction == 'in' && args[this.arguments[i].name]) + { + parameters += ('' + args[this.arguments[i].name] + ''); + } + else if(this.arguments.direction == 'in') + { + throw ('missing parameter: [' + this.arguments[i].name + '] when invoking Action: ' + this.name); + } + } + + var controlUri = http.parseUri(this.service.controlURL); + console.log(controlUri); + var headers = { HOST: (controlUri.host + ':' + controlUri.port), SOAPACTION: '"' + this.service.serviceType + '#' + this.name + '"', 'Content-Type': 'text/xml; charset="utf-8"' }; + this.pendingPosts.push(http.request({ protocol: 'http', host: controlUri.host, port: controlUri.port, method: 'POST', path: controlUri.path, headers: headers })); + this.pendingPosts.peek().action = this; + this.pendingPosts.peek().args = []; + for (var i = 1; i < arguments.length; ++i) + { + this.pendingPosts.peek().args.push(arguments[i]); + } + this.pendingPosts.peek().on('response', post_response); + + var txt = '\r\n'; + this.pendingPosts.peek().write(txt); + console.log(txt); + + txt = ''; + this.pendingPosts.peek().write(txt); + console.log(txt); + + if (parameters != '') + { + this.pendingPosts.peek().write(parameters); + console.log(parameters); + } + this.pendingPosts.peek().write(''); + this.pendingPosts.peek().write(''); + + console.log('' + ''); + + this.pendingPosts.peek().end(); + }; + this.invokeLegacy = function (args) + { + var controlUri = http.parseUri(this.service.controlURL); + var parameters = ''; + for (var i in this.arguments) { + if (this.arguments[i].direction == 'in' && args[this.arguments[i].name]) { + parameters += ('' + args[this.arguments[i].name] + ''); + } + else if (this.arguments.direction == 'in') { + throw ('missing parameter: [' + this.arguments[i].name + '] when invoking Action: ' + this.name); + } + } + + this.pendingPosts.push(net.connect({ host: controlUri.host, port: controlUri.port })); + this.pendingPosts.peek().path = controlUri.path; + this.pendingPosts.peek().args = args; + this.pendingPosts.peek().parameters = parameters; + this.pendingPosts.peek().action = this; + this.pendingPosts.peek().headers = { HOST: (controlUri.host + ':' + controlUri.port), SOAPACTION: '"' + this.service.serviceType + '#' + this.name + '"', 'Content-Type': 'text/xml; charset="utf-8"' }; + this.pendingPosts.peek().on('connect', function () + { + console.log('legacy connected'); + this.write('POST ' + this.path + ' HTTP/1.1\r\n'); + var headers = this.headers; + this.write('HOST: ' + headers.HOST + '\r\n'); + this.write('SOAPACTION: ' + headers.SOAPACTION + '\r\n'); + this.write('Content-Type: ' + headers['Content-Type'] + '\r\n'); + + var txt = '\r\n'; + txt += ''; + txt += this.parameters; + txt += ('' + ''); + + var b = Buffer.from(txt); + this.write('Content-Length: ' + b.length + '\r\n\r\n'); + this.write(b); + }); + this.pendingPosts.peek().http = http.createStream(); + this.pendingPosts.peek().pipe(this.pendingPosts.peek().http); + this.pendingPosts.peek().http.on('response', post_response); + }; +} +function upnpvariable(service, xmlDoc) +{ + this.service = service; + this.name = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'name')[0].textContent; + this.dataType = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'dataType')[0].textContent; + this.evented = xmlDoc.attributes.get('sendEvents').value; + if (this.evented == 'yes') + { + this.currentValue = null; + } +} +function upnpservice_parseScpd(scpd) +{ + this.stateVariables = []; Object.defineProperty(this.stateVariables, "get", { value: function (name) { for (var i in this) { if (this[i].name == name) { return (this[i]); } } return (undefined); } }); + this.actions = []; Object.defineProperty(this.actions, "get", { value: function (name) { for (var i in this) { if (this[i].name == name) { return (this[i]); } } return (undefined); } }); + var doc = parseXml(scpd); + + var stateTable = doc.getElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'serviceStateTable')[0]; + var variables = stateTable.getChildElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'stateVariable'); + for (var i in variables) + { + this.stateVariables.push(new upnpvariable(this, variables[i])); + } + + var actionList = doc.getElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'actionList')[0]; + var actions = actionList.getChildElementsByTagNameNS('urn:schemas-upnp-org:service-1-0', 'action'); + for (var i in actions) + { + try + { + this.actions.push(new upnpaction(this, actions[i])); + } + catch(e) + { + this.device.rootDevice.emit('error', 'error parsing SCPD/Action: ' + e); + return; + } + } + + this.subscribe = function () + { + + }; + this.device.rootDevice.emit('serviceLoaded', this); +} + +function upnpdevice_child(rootDevice, xmlDoc) +{ + this.rootDevice = rootDevice; + this.services = []; Object.defineProperty(this.services, "get", { value: function (id) { for (var i in this) { if (this[i].serviceType == id || this[i].serviceId == id) { return (this[i]); } } return (undefined); } }); + this.embeddedDevices = []; Object.defineProperty(this.embeddedDevices, "get", { value: function (id) { for (var i in this) { if (this[i].UDN == id || this[i].deviceType == id) { return (this[i]); } } return (undefined); } }); + + var emitterUtils = require('events').inherits(this); + emitterUtils.createEvent('bye'); + emitterUtils.createEvent('error'); + + this.friendlyName = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'friendlyName')[0].textContent; + this.deviceType = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'deviceType')[0].textContent; + this.UDN = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'UDN')[0].textContent; + this.manufacturer = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'manufacturer')[0].textContent; + + var serviceList = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'serviceList')[0] + for (var i in serviceList.childNodes) + { + if(serviceList.childNodes[i].namespace == 'urn:schemas-upnp-org:device-1-0') + { + this.services.push(new upnpservice(this, serviceList.childNodes[i])); + } + } + + var deviceList = xmlDoc.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'deviceList')[0] + + if (deviceList != null) + { + var devices = deviceList.getChildElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'device') + for (var device in devices) + { + this.embeddedDevices.push(new upnpdevice_child(rootDevice, devices[device])); + } + //console.log(devices); + } + this.loadAllServices = function () { for (var i in this.services) { this.services[i].load();} for (var i in this.embeddedDevices) { this.embeddedDevices[i].loadAllServices(); } }; + this.getDevice = function (udn) + { + if (this.UDN == udn) { return (this); } + for(var ed in this.embeddedDevices) + { + var ret = this.embeddedDevices[ed].getDevice(udn); + if (ret) { return (ret); } + } + return (undefined); + }; + this.getService = function(id) + { + for (var s in this.services) + { + if (this.services[s].serviceId == id) { return (this.services[s]); } + } + return (undefined); + } +} + +function upnpdevice_parseXml(xml) +{ + this.dd = null; + var doc = parseXml(xml); + //var URLBase = doc.getElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'URLBase')[0]; + //if (URLBase != null) { console.log("old base: " + this.BaseURL); this.BaseURL = URLBase.textContent; } + + var root = doc.getElementsByTagNameNS('urn:schemas-upnp-org:device-1-0', 'device')[0]; + if (root != null) + { + this.rootDevice = new upnpdevice_child(this, root); + if (!this.cp.searchString.startsWith('ssdp:') && !this.cp.searchString.startsWith('upnp:') && !this.cp.searchString.startsWith('urn:') && !this.cp.searchString.startsWith('uuid:')) + { + // Friendly Name Search + if(this.rootDevice.friendlyName == this.cp.searchString) + { + //console.log(xml); + this.rootDevice.loadAllServices(); + } + } + else + { + console.log(this.cp.searchString.split(':')[0]); + switch(this.cp.searchString.split(':')[0]) + { + case 'ssdp': + break; + case 'upnp': + this.rootDevice.loadAllServices(); + break; + case 'uuid': + break; + case 'urn': + break; + } + } + //console.log(this.rootDevice.friendlyName); + } +} + +function upnpcp_onSearch(msg, rinfo) +{ + var header = require('http-headers')(msg); + if (header.statusCode != 200) { return; } + var usn = header.headers.usn.split('::')[0]; + + if(this.cp.deviceTable[usn] == null) + { + this.cp.deviceTable[usn] = new upnpdevice(header.headers.location, usn, this.cp); + this.cp.deviceTable[usn].on('error', function (e) { console.log('Removing Device/' + this.usn + ' due to error: ' + e); this.cp.deviceTable[this.usn] = null; }); + this.cp.deviceTable[usn].on('alive', function () { this.cp.emit('device', this); }); + } +} + +function upnpcp(search) +{ + this.searchString = search; + if (!search.startsWith('ssdp:') && !search.startsWith('upnp:') && !search.startsWith('uuid:') && !search.startsWith('urn:')) + { + // Search by Friendly Name + search = 'upnp:rootdevice'; + } + + var MSEARCH = 'M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nST: ' + search + '\r\nMAN: "ssdp:discover"\r\nMX: 5\r\nContent-Length: 0\r\n\r\n'; + var emitterUtils = require('events').inherits(this); + emitterUtils.createEvent('device'); + + this.searchSocket = dgram.createSocket({ type: 'udp4' }); + this.searchSocket.cp = this; + this.searchSocket.bind({ port: 0, address:'0.0.0.0' }); + this.deviceTable = {}; + + this.searchSocket.on('message', upnpcp_onSearch); + + var interfaces = os.networkInterfaces(); + for(var name in interfaces) + { + for (var i in interfaces[name]) + { + if (interfaces[name][i].family == 'IPv4' && interfaces[name][i].status == 'up') + { + console.log('Sending Multicast on: ' + interfaces[name][i].address + ' to: 239.255.255.250:1900'); + this.searchSocket.setMulticastTTL(1); + this.searchSocket.setMulticastLoopback(true); + this.searchSocket.setMulticastInterface(interfaces[name][i].address); + this.searchSocket.send(MSEARCH, 1900, '239.255.255.250'); + } + } + } +} + + +//var testCP = new upnpcp("Samsung CLX-3300 Series (10.128.125.118)"); + +function display_device(dv) +{ + if (!dv) { console.log('No match'); return; } + console.log('FriendlyName/ ' + dv.friendlyName); + console.log(' DeviceType/ ' + dv.deviceType); + console.log(' DeviceUDN/ ' + dv.UDN); + + for (var svc in dv.services) + { + console.log(' ServiceID/ ' + dv.services[svc].serviceId); + } + for (var ed in dv.embeddedDevices) + { + console.log(' Embedded Device: ' + dv.embeddedDevices[ed].friendlyName + ' [' + dv.embeddedDevices[ed].UDN + ']'); + } +} +function display_action(action) +{ + var argString = null; + for (var arg in action.arguments) + { + if (action.arguments[arg].direction == 'in') + { + if (argString) + { + argString += (', ' + action.arguments[arg].name); + } + else + { + argString = action.arguments[arg].name; + } + } + } + console.log(' ' + action.name + '(' + (argString?argString:'') + ')'); +} +function display_actionDetail(action) +{ + if (!action) { console.log('no match'); return; } + + console.log('Action: ' + action.name); + console.log(' Input Parameters:'); + console.log(' {'); + for (var arg in action.arguments) + { + if (action.arguments[arg].direction == 'in') + { + console.log(' [' + action.arguments[arg].relatedStateVariable.dataType + '] ' + action.arguments[arg].name); + } + } + console.log(' }'); + console.log(' Output Parameters:'); + console.log(' {'); + for (var arg in action.arguments) + { + if (action.arguments[arg].direction == 'out') + { + console.log(' [' + action.arguments[arg].relatedStateVariable.dataType + '] ' + action.arguments[arg].name); + } + } + console.log(' }'); +} +function display_service(svc) +{ + if (!svc) { console.log('No match'); return; } + console.log('ServiceID/ ' + svc.serviceId); + console.log('ServiceType/ ' + svc.serviceType); + console.log('Actions:'); + for(var action in svc.actions) + { + display_action(svc.actions[action]); + } +} + +var testCP; + +if (process.argv.length > 1) +{ + if(process.argv[1].startsWith('discover=')) + { + testCP = new upnpcp(process.argv[1].split('=')[1]); + } +} + +if (!testCP) { process.exit(); } +testCP.on('device', function (dv) +{ + var selectedDevice = null; + var selectedService = null; + var selectedAction = null; + + console.log(''); + + + console.log('Device Added: '); + display_device(dv.rootDevice); + console.log(''); + + + for (var arg in process.argv) + { + if(process.argv[arg].startsWith('dv=')) + { + var i = parseInt(process.argv[arg].split('=')[1]); + console.log('Selected Embedded Device: ' + i); + display_device(dv.rootDevice.embeddedDevices[i]); + selectedDevice = dv.rootDevice.embeddedDevices[i]; + } + if(process.argv[arg].startsWith('udn=')) + { + console.log('Selected Device: ' + process.argv[arg].split('=')[1]); + selectedDevice = dv.getDevice(process.argv[arg].split('=')[1]); + display_device(selectedDevice); + } + if(selectedDevice && process.argv[arg].startsWith('serviceId=')) + { + selectedService = selectedDevice.getService(process.argv[arg].split('=')[1]); + display_service(selectedService); + } + if(selectedService && process.argv[arg].startsWith('action=')) + { + selectedAction = selectedService.getAction(process.argv[arg].split('=')[1]); + display_actionDetail(selectedAction); + } + if(selectedAction && process.argv[arg].startsWith('invoke=')) + { + var txt = process.argv[arg].split('=')[1]; + + console.log('Invoking with: ' + txt); + selectedAction.invoke(JSON.parse(process.argv[arg].split('=')[1]), function () + { + console.log('Response: ', arguments[0]); + process.exit(); + }); + } + if (selectedAction && process.argv[arg].startsWith('invokeLegacy=')) { + var txt = process.argv[arg].split('=')[1]; + + console.log('Invoking with: ' + txt); + selectedAction.invokeLegacy(JSON.parse(process.argv[arg].split('=')[1]), function () { + console.log('Response: ', arguments[0]); + process.exit(); + }); + } + } + +}); + + diff --git a/makefile b/makefile index f772324..cf97ca5 100644 --- a/makefile +++ b/makefile @@ -36,10 +36,10 @@ SOURCES += microscript/duktape.c microscript/ILibAsyncSocket_Duktape.c microscri SOURCES += microscript/ILibDuktape_http.c microscript/ILibDuktape_net.c microscript/ILibDuktape_ReadableStream.c microscript/ILibDuktape_WritableStream.c SOURCES += microscript/ILibDuktapeModSearch.c microscript/ILibParsers_Duktape.c microscript/ILibWebClient_Duktape.c microscript/ILibDuktape_WebRTC.c SOURCES += microscript/ILibWebServer_Duktape.c microscript/ILibDuktape_SimpleDataStore.c microscript/ILibDuktape_GenericMarshal.c -SOURCES += microscript/ILibDuktape_ProcessPipe.c microscript/ILibDuktape_fs.c microscript/ILibDuktape_SHA256.c microscript/ILibduktape_EventEmitter.c +SOURCES += microscript/ILibDuktape_fs.c microscript/ILibDuktape_SHA256.c microscript/ILibduktape_EventEmitter.c SOURCES += microscript/ILibDuktape_EncryptionStream.c microscript/ILibDuktape_Polyfills.c microscript/ILibDuktape_Dgram.c SOURCES += microscript/ILibDuktape_ScriptContainer.c microscript/ILibDuktape_MemoryStream.c microscript/ILibDuktape_NetworkMonitor.c -SOURCES += microscript/ILibDuktape_ChildProcess.c +SOURCES += microscript/ILibDuktape_ChildProcess.c microscript/ILibDuktape_HECI.c microscript/ILibDuktape_HttpStream.c # Mesh Agent core SOURCES += meshcore/agentcore.c meshconsole/main.c meshcore/meshinfo.c @@ -192,8 +192,8 @@ ifneq ($(WatchDog),) CWATCHDOG := -DILibChain_WATCHDOG_TIMEOUT=$(WatchDog) endif -ifeq ($(NOSSL),1) -SOURCES += microstack/SHA256.c +ifeq ($(NOTLS),1) +SOURCES += microstack/sha384-512.c microstack/sha224-256.c CFLAGS += -DMICROSTACK_NOTLS LINUXSSL = else @@ -264,18 +264,18 @@ $(LIBNAME): $(OBJECTS) $(SOURCES) # Compile on Raspberry Pi 2/3 with KVM pi: - $(MAKE) EXENAME="meshagent_pi2" CFLAGS="-std=gnu99 -g -Wall -D_POSIX -DMICROSTACK_PROXY -D_LINKVM $(CWEBLOG) $(CWATCHDOG) -fno-strict-aliasing $(INCDIRS) -DMESH_AGENTID=25 -D_NOFSWATCHER -D_NOHECI" LDFLAGS="-Lopenssl/libstatic/linux/pi2 $(LDFLAGS) $(LDEXTRA)" - strip meshagent_pi2 + $(MAKE) EXENAME="meshagent_pi" CFLAGS="-std=gnu99 -g -Wall -D_POSIX -DMICROSTACK_PROXY -D_LINKVM $(CWEBLOG) $(CWATCHDOG) -fno-strict-aliasing $(INCDIRS) -DMESH_AGENTID=25 -D_NOFSWATCHER -D_NOHECI" LDFLAGS="-Lopenssl/libstatic/linux/pi $(LDFLAGS) $(LDEXTRA)" + strip meshagent_pi linux: $(MAKE) EXENAME="$(EXENAME)_$(ARCHNAME)$(EXENAME2)" CFLAGS="-DMESH_AGENTID=$(ARCHID) $(CFLAGS) $(CEXTRA)" LDFLAGS="$(LINUXSSL) $(LDFLAGS) $(LDEXTRA)" $(STRIP) -linux-nossl-64-library: - $(MAKE) LIBNAME="$(EXENAME)_x64.so" ADDITIONALSOURCES="$(KVMSOURCES)" CFLAGS="-Os -fPIC -DMICROSTACK_NOTLS $(CFLAGS) $(CEXTRA)" LDFLAGS="-shared $(LDFLAGS) $(LDEXTRA)" +#linux-nossl-64-library: +# $(MAKE) LIBNAME="$(EXENAME)_x64.so" ADDITIONALSOURCES="$(KVMSOURCES)" CFLAGS="-Os -fPIC -DMICROSTACK_NOTLS $(CFLAGS) $(CEXTRA)" LDFLAGS="-shared $(LDFLAGS) $(LDEXTRA)" -linux-64-library-debug: - $(MAKE) LIBNAME="$(EXENAME)_x64.so" ADDITIONALSOURCES="$(KVMSOURCES)" CFLAGS="-fPIC -D_DEBUG -DMICROSTACK_TLS_DETECT $(CFLAGS) $(CEXTRA)" LDFLAGS="-shared -L../../opentools/MeshManageability/openssl-static/x86-64 -lssl $(LDFLAGS) $(LDEXTRA)" +#linux-64-library-debug: +# $(MAKE) LIBNAME="$(EXENAME)_x64.so" ADDITIONALSOURCES="$(KVMSOURCES)" CFLAGS="-fPIC -D_DEBUG -DMICROSTACK_TLS_DETECT $(CFLAGS) $(CEXTRA)" LDFLAGS="-shared -L../../opentools/MeshManageability/openssl-static/x86-64 -lssl $(LDFLAGS) $(LDEXTRA)" #mac-nossl-64: # $(MAKE) $(MAKEFILE) EXENAME="$(EXENAME)_osx64" CFLAGS="-arch x86_64 -mmacosx-version-min=10.5 -framework IOKit -framework ApplicationServices -framework SystemConfiguration -framework CoreFoundation -fconstant-cfstrings -std=gnu99 -Os -Wall -D_AGENTID=16 -D_POSIX -D_NOHECI -D_DAEMON -DMICROSTACK_PROXY -DMICROSTACK_NOTLS -D__APPLE__ $(CWEBLOG) -fno-strict-aliasing $(INCDIRS)" LDFLAGS="-L. -lpthread -ldl -lz -lutil" diff --git a/meshconsole/MeshConsole.vcxproj b/meshconsole/MeshConsole.vcxproj index 11da617..e9194ab 100644 --- a/meshconsole/MeshConsole.vcxproj +++ b/meshconsole/MeshConsole.vcxproj @@ -24,12 +24,7 @@ - - - - - @@ -39,13 +34,14 @@ + + - @@ -78,19 +74,8 @@ - - - - - - - - - - - @@ -100,12 +85,12 @@ + - diff --git a/meshconsole/MeshConsole.vcxproj.filters b/meshconsole/MeshConsole.vcxproj.filters index 3bafde8..8aa5def 100644 --- a/meshconsole/MeshConsole.vcxproj.filters +++ b/meshconsole/MeshConsole.vcxproj.filters @@ -13,9 +13,6 @@ {ab71a2fa-eafc-44d7-9c35-62f7d60fee6a} - - {aeb98903-4a96-453a-8c67-7a824d947bcc} - @@ -24,9 +21,6 @@ Microscript - - Microscript - Microscript @@ -105,9 +99,6 @@ Meshcore - - Microscript - Microscript @@ -141,36 +132,6 @@ Microscript - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - Microstack @@ -190,14 +151,14 @@ Microscript + + Microscript + Microscript - - Microscript - Microscript @@ -274,9 +235,6 @@ Meshcore - - Microscript - Microscript @@ -310,18 +268,6 @@ Microscript - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - Microstack @@ -343,6 +289,12 @@ Microscript + + Microscript + + + Microscript + diff --git a/meshconsole/main.c b/meshconsole/main.c index abd90c7..47dbf76 100644 --- a/meshconsole/main.c +++ b/meshconsole/main.c @@ -75,7 +75,7 @@ int main(int argc, char **argv) if (argc > 2 && memcmp(argv[1], "-faddr", 6) == 0) { uint64_t addrOffset; - util_hexToBuf(argv[2] + 2, strnlen_s(argv[2], 130) - 2, (char*)&addrOffset); + util_hexToBuf(argv[2] + 2, (int)(strnlen_s(argv[2], 130) - 2), (char*)&addrOffset); ILibChain_DebugOffset(ILibScratchPad, sizeof(ILibScratchPad), addrOffset); printf("%s", ILibScratchPad); return(0); diff --git a/meshcore/KVM/Windows/kvm.c b/meshcore/KVM/Windows/kvm.c index aab30fb..b895770 100644 --- a/meshcore/KVM/Windows/kvm.c +++ b/meshcore/KVM/Windows/kvm.c @@ -257,35 +257,36 @@ void kvm_send_display_list(ILibKVM_WriteHandler writeHandler, void *reserved) // Not looked at the number of screens yet if (SCREEN_COUNT == -1) return; - + char *buffer = ILibMemory_AllocateA((5 + SCREEN_COUNT) * 2); + memset(buffer, 0xFF, ILibMemory_AllocateA_Size(buffer)); // Send the list of possible displays to remote if (SCREEN_COUNT == 0 || SCREEN_COUNT == 1) { // Only one display, send empty - ((unsigned short*)ILibScratchPad2)[0] = (unsigned short)htons((unsigned short)MNG_KVM_GET_DISPLAYS); // Write the type - ((unsigned short*)ILibScratchPad2)[1] = (unsigned short)htons((unsigned short)(10 + (2 * SCREEN_COUNT))); // Write the size - ((unsigned short*)ILibScratchPad2)[2] = (unsigned short)htons((unsigned short)(0)); // Screen Count - ((unsigned short*)ILibScratchPad2)[2] = (unsigned short)htons((unsigned short)(0)); // Selected Screen + ((unsigned short*)buffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_GET_DISPLAYS); // Write the type + ((unsigned short*)buffer)[1] = (unsigned short)htons((unsigned short)(8)); // Write the size + ((unsigned short*)buffer)[2] = (unsigned short)htons((unsigned short)(0)); // Screen Count + ((unsigned short*)buffer)[3] = (unsigned short)htons((unsigned short)(0)); // Selected Screen - writeHandler(ILibScratchPad2, (10 + (2 * SCREEN_COUNT)), reserved); + writeHandler(buffer, 8, reserved); } else { // Many displays - ((unsigned short*)ILibScratchPad2)[0] = (unsigned short)htons((unsigned short)MNG_KVM_GET_DISPLAYS); // Write the type - ((unsigned short*)ILibScratchPad2)[1] = (unsigned short)htons((unsigned short)(10 + (2 * SCREEN_COUNT))); // Write the size - ((unsigned short*)ILibScratchPad2)[2] = (unsigned short)htons((unsigned short)(SCREEN_COUNT + 1)); // Screen Count - ((unsigned short*)ILibScratchPad2)[3] = (unsigned short)htons((unsigned short)(-1)); // Possible Screen (ALL) + ((unsigned short*)buffer)[0] = (unsigned short)htons((unsigned short)MNG_KVM_GET_DISPLAYS); // Write the type + ((unsigned short*)buffer)[1] = (unsigned short)htons((unsigned short)(10 + (2 * SCREEN_COUNT))); // Write the size + ((unsigned short*)buffer)[2] = (unsigned short)htons((unsigned short)(SCREEN_COUNT + 1)); // Screen Count + ((unsigned short*)buffer)[3] = (unsigned short)htons((unsigned short)(-1)); // Possible Screen (ALL) for (i = 0; i < SCREEN_COUNT; i++) { - ((unsigned short*)ILibScratchPad2)[4 + i] = (unsigned short)htons((unsigned short)(i + 1)); // Possible Screen + ((unsigned short*)buffer)[4 + i] = (unsigned short)htons((unsigned short)(i + 1)); // Possible Screen } if (SCREEN_SEL == 0) { - ((unsigned short*)ILibScratchPad2)[4 + i] = (unsigned short)htons((unsigned short)(-1)); // Selected Screen (All) + ((unsigned short*)buffer)[4 + i] = (unsigned short)htons((unsigned short)(-1)); // Selected Screen (All) } else { - ((unsigned short*)ILibScratchPad2)[4 + i] = (unsigned short)htons((unsigned short)(SCREEN_SEL)); // Selected Screen + ((unsigned short*)buffer)[4 + i] = (unsigned short)htons((unsigned short)(SCREEN_SEL)); // Selected Screen } - writeHandler(ILibScratchPad2, (10 + (2 * SCREEN_COUNT)), reserved); + writeHandler(buffer, (10 + (2 * SCREEN_COUNT)), reserved); } } @@ -378,6 +379,7 @@ void CheckDesktopSwitch(int checkres, ILibKVM_WriteHandler writeHandler, void *r if (SCREEN_X != x || SCREEN_Y != y || SCREEN_WIDTH != w || SCREEN_HEIGHT != h || SCALING_FACTOR != SCALING_FACTOR_NEW) { + printf("RESOLUTION CHANGED! (supposedly)\n"); SCREEN_X = x; SCREEN_Y = y; SCREEN_WIDTH = w; @@ -617,6 +619,32 @@ int kvm_server_inputdata(char* block, int blocklen, ILibKVM_WriteHandler writeHa return size; } +typedef struct kvm_data_handler +{ + ILibKVM_WriteHandler handler; + void *reserved; + int len; + char buffer[]; +}kvm_data_handler; + +//void __stdcall kvm_relay_feeddata_ex_APC(ULONG_PTR data) +//{ +// kvm_data_handler *k = (kvm_data_handler*)data; +// +// k->handler(k->buffer, k->len, k->reserved); +// free((void*)data); +//} +//ILibTransport_DoneState kvm_relay_feeddata_ex(char *buf, int len, void *reserved) +//{ +// kvm_data_handler *data = (kvm_data_handler*)ILibMemory_Allocate(sizeof(kvm_data_handler) + len, 0, NULL, NULL); +// data->handler = (ILibKVM_WriteHandler)((void**)reserved)[0]; +// data->reserved = ((void**)reserved)[1]; +// data->len = len; +// memcpy_s(data->buffer, len, buf, len); +// +// QueueUserAPC((PAPCFUNC)kvm_relay_feeddata_ex_APC, kvmthread, (ULONG_PTR)data); +//} + // Feed network data into the KVM. Return the number of bytes consumed. // This method consumes as many input commands as it can. int kvm_relay_feeddata(char* buf, int len, ILibKVM_WriteHandler writeHandler, void *reserved) @@ -633,6 +661,7 @@ int kvm_relay_feeddata(char* buf, int len, ILibKVM_WriteHandler writeHandler, vo #else int len2 = 0; int ptr = 0; + //while ((len2 = kvm_server_inputdata(buf + ptr, len - ptr, kvm_relay_feeddata_ex, (void*[]) {writeHandler, reserved})) != 0) { ptr += len2; } while ((len2 = kvm_server_inputdata(buf + ptr, len - ptr, writeHandler, reserved)) != 0) { ptr += len2; } return ptr; #endif @@ -889,7 +918,7 @@ DWORD WINAPI kvm_server_mainloop(LPVOID parm) // We can't go full speed here, we need to slow this down. height = FRAME_RATE_TIMER; - while (!g_shutdown && height > 0) { if (height > 50) { height -= 50; Sleep(50); } else { Sleep(height); height = 0; } } + while (!g_shutdown && height > 0) { if (height > 50) { height -= 50; Sleep(50); } else { Sleep(height); height = 0; } SleepEx(0, TRUE); } } KVMDEBUG("kvm_server_mainloop / end3", (int)GetCurrentThreadId()); diff --git a/meshcore/agentcore.c b/meshcore/agentcore.c index a2540c3..9c3bce6 100644 --- a/meshcore/agentcore.c +++ b/meshcore/agentcore.c @@ -41,11 +41,6 @@ limitations under the License. #include "microscript/ILibDuktape_ScriptContainer.h" #include "../microstack/ILibIPAddressMonitor.h" - -#ifndef _NOHECI -#include "microlms/lms/ILibLMS.h" -#endif - #ifdef _POSIX #include #endif @@ -114,13 +109,13 @@ typedef struct ScriptContainerSettings typedef struct MeshCommand_BinaryPacket_ServerId { unsigned short command; - char serverId[UTIL_HASHSIZE]; + char serverId[UTIL_SHA384_HASHSIZE]; }MeshCommand_BinaryPacket_ServerId; typedef struct MeshCommand_BinaryPacket_AuthRequest { unsigned short command; - char serverHash[UTIL_HASHSIZE]; - char serverNonce[UTIL_HASHSIZE]; + char serverHash[UTIL_SHA384_HASHSIZE]; + char serverNonce[UTIL_SHA384_HASHSIZE]; }MeshCommand_BinaryPacket_AuthRequest; typedef struct MeshCommand_BinaryPacket_AuthVerify_Header { @@ -159,7 +154,7 @@ typedef struct MeshCommand_BinaryPacket_AuthInfo unsigned int agentId; unsigned int agentVersion; unsigned int platformType; - char MeshID[UTIL_HASHSIZE]; + char MeshID[UTIL_SHA384_HASHSIZE]; unsigned int capabilities; unsigned short hostnameLen; char hostname[]; @@ -168,7 +163,7 @@ typedef struct MeshCommand_BinaryPacket_CoreModule { unsigned short command; unsigned short request; - char coreModuleHash[UTIL_HASHSIZE]; + char coreModuleHash[UTIL_SHA384_HASHSIZE]; char coreModule[]; }MeshCommand_BinaryPacket_CoreModule; #pragma pack(pop) @@ -735,16 +730,20 @@ void ILibDuktape_MeshAgent_RemoteDesktop_EndSink(ILibDuktape_DuplexStream *strea { // Peer disconnected the data channel RemoteDesktop_Ptrs *ptrs = (RemoteDesktop_Ptrs*)user; - duk_push_heapptr(ptrs->ctx, ptrs->MeshAgentObject); // [MeshAgent] - duk_del_prop_string(ptrs->ctx, -1, REMOTE_DESKTOP_STREAM); - duk_pop(ptrs->ctx); // ... + if (ptrs->ctx != NULL) + { + duk_push_heapptr(ptrs->ctx, ptrs->MeshAgentObject); // [MeshAgent] + duk_del_prop_string(ptrs->ctx, -1, REMOTE_DESKTOP_STREAM); + duk_pop(ptrs->ctx); // ... - memset(ptrs, 0, sizeof(RemoteDesktop_Ptrs)); + memset(ptrs, 0, sizeof(RemoteDesktop_Ptrs)); + } kvm_cleanup(); } void ILibDuktape_MeshAgent_RemoteDesktop_PauseSink(ILibDuktape_DuplexStream *sender, void *user) { + //printf("KVM/PAUSE\n"); #ifdef _POSIX ILibProcessPipe_Pipe_Pause(((RemoteDesktop_Ptrs*)user)->kvmPipe); #else @@ -753,6 +752,8 @@ void ILibDuktape_MeshAgent_RemoteDesktop_PauseSink(ILibDuktape_DuplexStream *sen } void ILibDuktape_MeshAgent_RemoteDesktop_ResumeSink(ILibDuktape_DuplexStream *sender, void *user) { + //printf("KVM/RESUME\n"); + #ifdef _POSIX ILibProcessPipe_Pipe_Resume(((RemoteDesktop_Ptrs*)user)->kvmPipe); #else @@ -777,7 +778,7 @@ duk_ret_t ILibDuktape_MeshAgent_RemoteDesktop_Finalizer(duk_context *ctx) } return 0; } -void ILibDuktape_MeshAgent_RemoteDesktop_PipeHook(ILibDuktape_readableStream *stream, void *user) +void ILibDuktape_MeshAgent_RemoteDesktop_PipeHook(ILibDuktape_readableStream *stream, void *wstream, void *user) { #ifdef _LINKVM #ifdef WIN32 @@ -793,6 +794,11 @@ void ILibDuktape_MeshAgent_RemoteDesktop_PipeHook(ILibDuktape_readableStream *st } #endif +int ILibDuktape_MeshAgent_remoteDesktop_unshiftSink(ILibDuktape_DuplexStream *sender, int unshiftBytes, void *user) +{ + return(0); +} + duk_ret_t ILibDuktape_MeshAgent_getRemoteDesktop(duk_context *ctx) { #ifndef _LINKVM @@ -816,6 +822,7 @@ duk_ret_t ILibDuktape_MeshAgent_getRemoteDesktop(duk_context *ctx) duk_pop(ctx); duk_push_object(ctx); // [MeshAgent][RemoteDesktop] + ILibDuktape_WriteID(ctx, "MeshAgent.kvmSession"); duk_dup(ctx, -1); // [MeshAgent][RemoteDesktop][RemoteDesktop] duk_put_prop_string(ctx, -3, REMOTE_DESKTOP_STREAM); // [MeshAgent][RemoteDesktop] duk_push_fixed_buffer(ctx, sizeof(RemoteDesktop_Ptrs)); // [MeshAgent][RemoteDesktop][buffer] @@ -825,7 +832,7 @@ duk_ret_t ILibDuktape_MeshAgent_getRemoteDesktop(duk_context *ctx) ptrs->MeshAgentObject = duk_get_heapptr(ctx, -2); ptrs->ctx = ctx; ptrs->object = duk_get_heapptr(ctx, -1); - ptrs->stream = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_MeshAgent_RemoteDesktop_WriteSink, ILibDuktape_MeshAgent_RemoteDesktop_EndSink, ILibDuktape_MeshAgent_RemoteDesktop_PauseSink, ILibDuktape_MeshAgent_RemoteDesktop_ResumeSink, ptrs); + ptrs->stream = ILibDuktape_DuplexStream_InitEx(ctx, ILibDuktape_MeshAgent_RemoteDesktop_WriteSink, ILibDuktape_MeshAgent_RemoteDesktop_EndSink, ILibDuktape_MeshAgent_RemoteDesktop_PauseSink, ILibDuktape_MeshAgent_RemoteDesktop_ResumeSink, ILibDuktape_MeshAgent_remoteDesktop_unshiftSink, ptrs); ILibDuktape_CreateFinalizer(ctx, ILibDuktape_MeshAgent_RemoteDesktop_Finalizer); ptrs->stream->readableStream->PipeHookHandler = ILibDuktape_MeshAgent_RemoteDesktop_PipeHook; @@ -868,24 +875,7 @@ duk_ret_t ILibDuktape_MeshAgent_ConnectedServer(duk_context *ctx) } return 1; } -#ifndef _NOHECI -duk_ret_t ILibDuktape_MeshAgent_MEInfo(duk_context *ctx) -{ - char *data; - int len = ILibLMS_GetMeInformation(&data, 0); - if (len > 0) - { - duk_push_lstring(ctx, data, len); - duk_json_decode(ctx, -1); - free(data); - } - else - { - duk_push_null(ctx); - } - return 1; -} -#endif + duk_ret_t ILibDuktape_MeshAgent_NetInfo(duk_context *ctx) { char *data; @@ -931,7 +921,24 @@ duk_ret_t ILibDuktape_MeshAgent_ServerUrl(duk_context *ctx) duk_push_string(ctx, agent->serveruri); // [MeshAgent][ptr][uri] return(1); } +duk_ret_t ILibDuktape_MeshAgent_isControlChannelConnected(duk_context *ctx) +{ + duk_push_this(ctx); // [agent] + duk_get_prop_string(ctx, -1, MESH_AGENT_PTR); // [agent][ptr] + MeshAgentHostContainer *agent = (MeshAgentHostContainer*)duk_get_pointer(ctx, -1); + duk_push_boolean(ctx, agent->serverAuthState == 3 ? 1 : 0); + return(1); +} +duk_ret_t ILibDuktape_MeshAgent_eval(duk_context *ctx) +{ + duk_size_t evalStrLen; + char *evalStr = duk_get_lstring(ctx, 0, &evalStrLen); + + printf("eval(): %s\n", evalStr); + duk_eval_string(ctx, evalStr); + return(1); +} void ILibDuktape_MeshAgent_PUSH(duk_context *ctx, void *chain) { MeshAgentHostContainer *agent; @@ -985,6 +992,7 @@ void ILibDuktape_MeshAgent_PUSH(duk_context *ctx, void *chain) ILibDuktape_EventEmitter_CreateEvent(emitter, "Ready", &(ptrs->OnReady)); ILibDuktape_EventEmitter_CreateEvent(emitter, "Connected", &(ptrs->OnConnect)); + ILibDuktape_CreateEventWithGetter(ctx, "isControlChannelConnected", ILibDuktape_MeshAgent_isControlChannelConnected); ILibDuktape_EventEmitter_AddHook(emitter, "Ready", ILibDuktape_MeshAgent_Ready); ILibDuktape_CreateEventWithGetter(ctx, "ConnectedServer", ILibDuktape_MeshAgent_ConnectedServer); ILibDuktape_CreateEventWithGetter(ctx, "ServerUrl", ILibDuktape_MeshAgent_ServerUrl); @@ -1001,15 +1009,9 @@ void ILibDuktape_MeshAgent_PUSH(duk_context *ctx, void *chain) ILibDuktape_CreateReadonlyProperty_int(ctx, "hasKVM", 0); #endif -#ifdef _NOHECI - ILibDuktape_CreateReadonlyProperty_int(ctx, "hasHECI", 0); -#else - ILibDuktape_CreateReadonlyProperty_int(ctx, "hasHECI", 1); - ILibDuktape_CreateEventWithGetter(ctx, "MEInfo", ILibDuktape_MeshAgent_MEInfo); - ILibDuktape_EventEmitter_CreateEventEx(emitter, "lmsNotification"); -#endif ILibDuktape_CreateEventWithGetter(ctx, "NetInfo", ILibDuktape_MeshAgent_NetInfo); ILibDuktape_CreateInstanceMethod(ctx, "ExecPowerState", ILibDuktape_MeshAgent_ExecPowerState, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "eval", ILibDuktape_MeshAgent_eval, 1); Duktape_CreateEnum(ctx, "ContainerPermissions", (char*[]) { "DEFAULT", "NO_AGENT", "NO_MARSHAL", "NO_PROCESS_SPAWNING", "NO_FILE_SYSTEM_ACCESS", "NO_NETWORK_ACCESS" }, (int[]) { 0x00, 0x10000000, 0x08000000, 0x04000000, 0x00000001, 0x00000002 }, 6); } @@ -1265,6 +1267,10 @@ duk_context* ScriptEngine_Stop(MeshAgentHostContainer *agent, char *contextGUID) duk_destroy_heap(oldCtx); agent->meshCoreCtx = newCtx; + if (agent->proxyServer != NULL) + { + memcpy_s(&(ILibDuktape_GetNewGlobalTunnel(agent->meshCoreCtx)->proxyServer), sizeof(struct sockaddr_in6), agent->proxyServer, sizeof(struct sockaddr_in6)); + } return(newCtx); } @@ -1378,15 +1384,15 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge SHA384_Init(&c); util_keyhash2(peer, ILibScratchPad); SHA384_Update(&c, AuthRequest->serverHash, sizeof(AuthRequest->serverHash)); // Server web hash - SHA384_Update(&c, agent->serverNonce, UTIL_HASHSIZE); // Server nonce - SHA384_Update(&c, agent->agentNonce, UTIL_HASHSIZE); // Agent nonce + SHA384_Update(&c, agent->serverNonce, UTIL_SHA384_HASHSIZE); // Server nonce + SHA384_Update(&c, agent->agentNonce, UTIL_SHA384_HASHSIZE); // Agent nonce SHA384_Final((unsigned char*)ILibScratchPad, &c); // Place the signature & send evp_prikey = agent->selfcert.pkey; rsa_prikey = EVP_PKEY_get1_RSA(evp_prikey); signLen = sizeof(ILibScratchPad2) - sizeof(MeshCommand_BinaryPacket_AuthVerify_Header) - rav->certLen; - if (RSA_sign(NID_sha384, (unsigned char*)ILibScratchPad, UTIL_HASHSIZE, (unsigned char*)(rav->data + rav->certLen), (unsigned int*)&signLen, rsa_prikey) == 1) + if (RSA_sign(NID_sha384, (unsigned char*)ILibScratchPad, UTIL_SHA384_HASHSIZE, (unsigned char*)(rav->data + rav->certLen), (unsigned int*)&signLen, rsa_prikey) == 1) { // Signature succesful, send the result to the server ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, (char*)rav, sizeof(MeshCommand_BinaryPacket_AuthVerify_Header) + rav->certLen + signLen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); @@ -1410,7 +1416,7 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge if (cmdLen > (int)(sizeof(MeshCommand_BinaryPacket_AuthVerify_Header) + AuthVerify->certLen)) { - int hashlen = UTIL_HASHSIZE; + int hashlen = UTIL_SHA384_HASHSIZE; SHA512_CTX c; X509* serverCert = NULL; EVP_PKEY *evp_pubkey; @@ -1421,7 +1427,7 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge // Check if this certificate public key hash matches what we want X509_pubkey_digest(serverCert, EVP_sha384(), (unsigned char*)ILibScratchPad, (unsigned int*)&hashlen); // OpenSSL 1.1, SHA384 - if (memcmp(ILibScratchPad, agent->serverHash, UTIL_HASHSIZE) != 0) { + if (memcmp(ILibScratchPad, agent->serverHash, UTIL_SHA384_HASHSIZE) != 0) { X509_pubkey_digest(serverCert, EVP_sha256(), (unsigned char*)ILibScratchPad, (unsigned int*)&hashlen); // OpenSSL 1.1, SHA256 (For older .mshx policy file) if (memcmp(ILibScratchPad, agent->serverHash, 32) != 0) { printf("Server certificate mismatch\r\n"); break; // TODO: Disconnect @@ -1431,15 +1437,15 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge // Compute the authentication hash SHA384_Init(&c); util_keyhash2(peer, ILibScratchPad); - SHA384_Update(&c, ILibScratchPad, UTIL_HASHSIZE); - SHA384_Update(&c, agent->agentNonce, UTIL_HASHSIZE); - SHA384_Update(&c, agent->serverNonce, UTIL_HASHSIZE); + SHA384_Update(&c, ILibScratchPad, UTIL_SHA384_HASHSIZE); + SHA384_Update(&c, agent->agentNonce, UTIL_SHA384_HASHSIZE); + SHA384_Update(&c, agent->serverNonce, UTIL_SHA384_HASHSIZE); SHA384_Final((unsigned char*)ILibScratchPad, &c); // Verify the hash signature using the server certificate evp_pubkey = X509_get_pubkey(serverCert); rsa_pubkey = EVP_PKEY_get1_RSA(evp_pubkey); - if (RSA_verify(NID_sha384, (unsigned char*)ILibScratchPad, UTIL_HASHSIZE, (unsigned char*)AuthVerify->signature, AuthVerify->signatureLen, rsa_pubkey) == 1) + if (RSA_verify(NID_sha384, (unsigned char*)ILibScratchPad, UTIL_SHA384_HASHSIZE, (unsigned char*)AuthVerify->signature, AuthVerify->signatureLen, rsa_pubkey) == 1) { int hostnamelen = (int)strnlen_s(agent->hostname, sizeof(agent->hostname)); // Server signature verified, we are good to go. @@ -1447,7 +1453,7 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge // Send to the server information about this agent (TODO: Replace this with a struct) MeshCommand_BinaryPacket_AuthInfo *info = (MeshCommand_BinaryPacket_AuthInfo*)ILibScratchPad2; - memset(info, 0, sizeof(MeshCommand_BinaryPacket_AuthInfo)); // Required because if hash are SHA256, they will not fully fill the struct. + memset(info, 0, sizeof(MeshCommand_BinaryPacket_AuthInfo)); // Required because if hash are SHA384, they will not fully fill the struct. info->command = htons(MeshCommand_AuthInfo); info->infoVersion = htonl(1); info->agentId = htonl(MESH_AGENTID); @@ -1558,13 +1564,13 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge if (cmdLen > sizeof(MeshCommand_BinaryPacket_CoreModule)) // Setup a new mesh core. { - char *hashref = ILibSimpleDataStore_GetHash(agent->masterDb, "CoreModule"); // Get the reference to the SHA256 hash for the currently running code + char *hashref = ILibSimpleDataStore_GetHash(agent->masterDb, "CoreModule"); // Get the reference to the SHA384 hash for the currently running code if (hashref == NULL || memcmp(hashref, cm->coreModuleHash, sizeof(cm->coreModuleHash)) != 0) { // If server sends us the same core, just do nothing. // Server sent us a new core, start by storing it in the data store ILibSimpleDataStore_PutEx(agent->masterDb, "CoreModule", 10, cm->coreModule, cmdLen - sizeof(MeshCommand_BinaryPacket_CoreModule)); // Store the JavaScript in the data store - hashref = ILibSimpleDataStore_GetHash(agent->masterDb, "CoreModule"); // Get the reference to the SHA256 hash + hashref = ILibSimpleDataStore_GetHash(agent->masterDb, "CoreModule"); // Get the reference to the SHA384 hash if (memcmp(hashref, cm->coreModuleHash, sizeof(cm->coreModuleHash)) != 0) { // Check the hash for sanity // Something went wrong, clear the data store @@ -1577,7 +1583,7 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge // Tell the server we are no longer running a core module MeshCommand_BinaryPacket_CoreModule *rcm = (MeshCommand_BinaryPacket_CoreModule*)ILibScratchPad2; - rcm->command = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA256 hash of the code module + rcm->command = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA384 hash of the code module rcm->request = htons(requestid); // Request id ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, (char*)rcm, 4, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); break; @@ -1601,9 +1607,9 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge // Create the server confirmation message that we are running the new core MeshCommand_BinaryPacket_CoreModule *rcm = (MeshCommand_BinaryPacket_CoreModule*)ILibScratchPad2; - ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA256 hash of the code module + ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA384 hash of the code module ((unsigned short*)ILibScratchPad2)[1] = htons(requestid); // Request id - memcpy_s(ILibScratchPad2 + 4, sizeof(ILibScratchPad2) - 4, hashref, UTIL_HASHSIZE); // SHA256 hash + memcpy_s(ILibScratchPad2 + 4, sizeof(ILibScratchPad2) - 4, hashref, UTIL_SHA384_HASHSIZE); // SHA384 hash // Send the confirmation to the server ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, (char*)rcm, sizeof(MeshCommand_BinaryPacket_CoreModule), ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); @@ -1620,16 +1626,16 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge // Confirm to the server that we are not running any core MeshCommand_BinaryPacket_CoreModule *rcm = (MeshCommand_BinaryPacket_CoreModule*)ILibScratchPad2; - rcm->command = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA256 hash of the code module + rcm->command = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA384 hash of the code module rcm->request = htons(requestid); // Request id ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, (char*)rcm, 4, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); } break; } - case MeshCommand_CoreModuleHash: // Request/return the SHA256 hash of the core module + case MeshCommand_CoreModuleHash: // Request/return the SHA384 hash of the core module { // Tell the server what core module we are running - char *hashref = ILibSimpleDataStore_GetHash(agent->masterDb, "CoreModule"); // Get the reference to the SHA256 hash + char *hashref = ILibSimpleDataStore_GetHash(agent->masterDb, "CoreModule"); // Get the reference to the SHA384 hash int len = 4; // If the agent is running with a local core, ignore this command @@ -1637,9 +1643,9 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge // Confirm to the server what core we are running MeshCommand_BinaryPacket_CoreModule *rcm = (MeshCommand_BinaryPacket_CoreModule*)ILibScratchPad2; - rcm->command = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA256 hash of the code module + rcm->command = htons(MeshCommand_CoreModuleHash); // MeshCommand_CoreModuleHash (11), SHA384 hash of the code module rcm->request = htons(requestid); // Request id - if (hashref != NULL) { memcpy_s(rcm->coreModuleHash, sizeof(rcm->coreModuleHash), hashref, UTIL_HASHSIZE); len = sizeof(MeshCommand_BinaryPacket_CoreModule); } + if (hashref != NULL) { memcpy_s(rcm->coreModuleHash, sizeof(rcm->coreModuleHash), hashref, UTIL_SHA384_HASHSIZE); len = sizeof(MeshCommand_BinaryPacket_CoreModule); } // Send the confirmation to the server ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, (char*)rcm, len, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); @@ -1653,9 +1659,9 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge // This is a request for the hash of the agent binary // Built the response that includes our self hash MeshCommand_BinaryPacket_CoreModule *rcm = (MeshCommand_BinaryPacket_CoreModule*)ILibScratchPad2; - rcm->command = htons(MeshCommand_AgentHash); // MeshCommand_AgentHash (12), SHA256 hash of the agent executable + rcm->command = htons(MeshCommand_AgentHash); // MeshCommand_AgentHash (12), SHA384 hash of the agent executable rcm->request = htons(requestid); // Request id - memcpy_s(rcm->coreModuleHash, sizeof(rcm->coreModuleHash), agent->agentHash, UTIL_HASHSIZE);// SHA256 hash of the agent executable + memcpy_s(rcm->coreModuleHash, sizeof(rcm->coreModuleHash), agent->agentHash, UTIL_SHA384_HASHSIZE);// SHA384 hash of the agent executable // Send the self hash back to the server ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, (char*)rcm, sizeof(MeshCommand_BinaryPacket_CoreModule), ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); @@ -1669,7 +1675,7 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge #else char* updateFilePath = MeshAgent_MakeAbsolutePath(agent->exePath, ".update"); #endif - char updateFileHash[UTIL_HASHSIZE]; + char updateFileHash[UTIL_SHA384_HASHSIZE]; MeshCommand_BinaryPacket_CoreModule *cm = (MeshCommand_BinaryPacket_CoreModule*)cmd; if (cmdLen == 4) { @@ -1717,7 +1723,7 @@ void MeshServer_ProcessCommand(ILibWebClient_StateObject WebStateObject, MeshAge } // Confirm we got a mesh agent update block - ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_AgentUpdateBlock); // MeshCommand_AgentHash (14), SHA256 hash of the agent executable + ((unsigned short*)ILibScratchPad2)[0] = htons(MeshCommand_AgentUpdateBlock); // MeshCommand_AgentHash (14), SHA384 hash of the agent executable ((unsigned short*)ILibScratchPad2)[1] = htons(requestid); // Request id ILibWebClient_WebSocket_Send(WebStateObject, ILibWebClient_WebSocket_DataType_BINARY, ILibScratchPad2, 4, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete); @@ -1740,7 +1746,7 @@ void MeshServer_ControlChannel_PongSink(ILibWebClient_StateObject WebStateObject { #ifdef _REMOTELOGGING MeshAgentHostContainer *agent = (MeshAgentHostContainer*)user; - ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "AgentCore/MeshServer_ControlChannel_IdleTimeout(): Received Poing"); + ILibRemoteLogging_printf(ILibChainGetLogger(agent->chain), ILibRemoteLogging_Modules_Agent_GuardPost | ILibRemoteLogging_Modules_ConsolePrint, ILibRemoteLogging_Flags_VerbosityLevel_1, "AgentCore/MeshServer_ControlChannel_IdleTimeout(): Received Pong"); #endif } void MeshServer_OnResponse(ILibWebClient_StateObject WebStateObject, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebClient_ReceiveStatus recvStatus, void *user1, void *user2, int *PAUSE) @@ -2010,7 +2016,6 @@ void MeshServer_ConnectEx(MeshAgentHostContainer *agent) ILibAddHeaderLine(req, "Host", 4, host, (int)strnlen_s(host, serverUrlLen)); free(path); - free(host); if (meshServer.sin6_family != AF_UNSPEC) { @@ -2018,6 +2023,7 @@ void MeshServer_ConnectEx(MeshAgentHostContainer *agent) reqToken = ILibWebClient_PipelineRequest(agent->httpClientManager, (struct sockaddr*)&meshServer, req, MeshServer_OnResponse, agent, NULL); #ifndef MICROSTACK_NOTLS ILibWebClient_Request_SetHTTPS(reqToken, result == ILibParseUriResult_TLS ? ILibWebClient_RequestToken_USE_HTTPS : ILibWebClient_RequestToken_USE_HTTP); + ILibWebClient_Request_SetSNI(reqToken, host, (int)strnlen_s(host, serverUrlLen)); #endif if ((len = ILibSimpleDataStore_Get(agent->masterDb, "WebProxy", ILibScratchPad, sizeof(ILibScratchPad))) != 0) { @@ -2045,6 +2051,7 @@ void MeshServer_ConnectEx(MeshAgentHostContainer *agent) { ILibDestructPacket(req); } + free(host); } void MeshServer_Connect(MeshAgentHostContainer *agent) { @@ -2214,67 +2221,8 @@ void MeshAgent_RunScriptOnly_Finalizer(duk_context *ctx, void *user) } void MeshAgent_CoreModule_UncaughtException(duk_context *ctx, char *msg, void *user) { + printf("JavaCore UncaughtException: %s\n", msg); } - -#ifndef _NOHECI -void MicroLMS_OnNotificationEx(ILibHashtable sender, void *Key1, char* Key2, int Key2Len, void *Data, void *user) -{ - struct cimAlertIndication *values = (struct cimAlertIndication*)((void**)user)[0]; - char *xml = (char*)((void**)user)[1]; - int xmllen = ((int*)((void**)user)[2])[0]; - - if (Key1 == NULL && ((SCRIPT_ENGINE_ISOLATION*)Data)->reserved != 0) - { - // This is a regular Duktape Context - ILibDuktape_EventEmitter *emitter = MeshAgent_GetEventEmitter_FromCTX((duk_context*)Data); - if (emitter != NULL) - { - duk_push_heapptr(emitter->ctx, emitter->object); // [MeshAgent] - duk_get_prop_string(emitter->ctx, -1, "emit"); // [MeshAgent][emit] - duk_swap_top(emitter->ctx, -2); // [emit][this] - duk_push_string(emitter->ctx, "lmsNotification"); // [emit][this][event] - duk_push_object(emitter->ctx); // [emit][this][event][obj] - duk_push_lstring(emitter->ctx, values->MessageID.data, values->MessageID.dataLen); // [emit][this][event][obj][msgId] - duk_put_prop_string(emitter->ctx, -2, "messageId"); // [emit][this][event][obj] - duk_push_lstring(emitter->ctx, values->IndicationTime.data, values->IndicationTime.dataLen); // [emit][this][event][obj][time] - duk_put_prop_string(emitter->ctx, -2, "indicationTime"); // [emit][this][event][obj] - duk_push_lstring(emitter->ctx, values->MessageArguments.data, values->MessageArguments.dataLen);// [emit][this][event][obj][msg] - duk_put_prop_string(emitter->ctx, -2, "messageArguments"); // [emit][this][event][obj] - duk_push_lstring(emitter->ctx, xml, xmllen); // [emit][this][event][obj][rawXML] - duk_put_prop_string(emitter->ctx, -2, "rawXML"); // [emit][this][event][obj] - if (duk_pcall_method(emitter->ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(emitter->ctx, "MeshAgent.onLmsNotification(): Error "); } - duk_pop(emitter->ctx); // ... - } - } -} -void MicroLMS_OnNotification(void *module, struct cimAlertIndication *values, char* xml, int xmllen) -{ - MeshAgentHostContainer *agent = (MeshAgentHostContainer*)((void**)ILibMemory_GetExtraMemory(module, ILibMemory_ILibLMS_CONTAINERSIZE))[0]; - if (agent->meshCoreCtx != NULL) - { - ILibDuktape_EventEmitter *emitter = MeshAgent_GetEventEmitter_FromCTX(agent->meshCoreCtx); - if (emitter != NULL) - { - duk_push_heapptr(emitter->ctx, emitter->object); // [MeshAgent] - duk_get_prop_string(emitter->ctx, -1, "emit"); // [MeshAgent][emit] - duk_swap_top(emitter->ctx, -2); // [emit][this] - duk_push_string(emitter->ctx, "lmsNotification"); // [emit][this][event] - duk_push_object(emitter->ctx); // [emit][this][event][obj] - duk_push_lstring(emitter->ctx, values->MessageID.data, values->MessageID.dataLen); // [emit][this][event][obj][msgId] - duk_put_prop_string(emitter->ctx, -2, "messageId"); // [emit][this][event][obj] - duk_push_lstring(emitter->ctx, values->IndicationTime.data, values->IndicationTime.dataLen); // [emit][this][event][obj][time] - duk_put_prop_string(emitter->ctx, -2, "indicationTime"); // [emit][this][event][obj] - duk_push_lstring(emitter->ctx, values->MessageArguments.data, values->MessageArguments.dataLen);// [emit][this][event][obj][msg] - duk_put_prop_string(emitter->ctx, -2, "messageArguments"); // [emit][this][event][obj] - duk_push_lstring(emitter->ctx, xml, xmllen); // [emit][this][event][obj][rawXML] - duk_put_prop_string(emitter->ctx, -2, "rawXML"); // [emit][this][event][obj] - if (duk_pcall_method(emitter->ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(emitter->ctx, "MeshAgent.onLmsNotification(): Error "); } - duk_pop(emitter->ctx); // ... - } - } -} -#endif - void MeshAgent_AgentMode_IPAddressChanged_Handler(ILibIPAddressMonitor sender, void *user) { MeshAgentHostContainer *agentHost = (MeshAgentHostContainer*)user; @@ -2395,20 +2343,6 @@ int MeshAgent_AgentMode(MeshAgentHostContainer *agentHost, int paramLen, char ** if (str != NULL) { ILibSimpleDataStore_PutEx(agentHost->masterDb, "WebProxy", 8, str, len); free(str); } else { ILibSimpleDataStore_DeleteEx(agentHost->masterDb, "WebProxy", 8); } } -#ifndef _NOHECI -#ifdef WIN32 - // Check if the LMS service is installed in the service manager - if (GetServiceState(TEXT("LMS")) == 100) { - // If not, setup MicroLMS (Intel AMT interface service) if it's needed - if ((agentHost->microLMS = ILibLMS_CreateEx(agentHost->chain, agentHost->exePath, MicroLMS_OnNotification, sizeof(void*))) != NULL) MSG("MicroLMS activated\r\n"); - } -#else - // Setup MicroLMS (Intel AMT interface service) if it's needed - if ((agentHost->microLMS = ILibLMS_CreateEx(agentHost->chain, agentHost->exePath, MicroLMS_OnNotification, sizeof(void*))) != NULL) MSG("MicroLMS activated\r\n"); -#endif - if (agentHost->microLMS != NULL) { ((void**)ILibMemory_GetExtraMemory(agentHost->microLMS, ILibMemory_ILibLMS_CONTAINERSIZE))[0] = agentHost; } -#endif - // Check to see if we need to import a settings file importSettings(agentHost, MeshAgent_MakeAbsolutePath(agentHost->exePath, ".mshx")); importSettings(agentHost, MeshAgent_MakeAbsolutePath(agentHost->exePath, ".msh")); @@ -2427,11 +2361,11 @@ int MeshAgent_AgentMode(MeshAgentHostContainer *agentHost, int paramLen, char ** #endif { // Save the NodeId - util_tohex(agentHost->g_selfid, UTIL_HASHSIZE, ILibScratchPad); + util_tohex(agentHost->g_selfid, UTIL_SHA384_HASHSIZE, ILibScratchPad); RegSetValueExA(hKey, "NodeId", 0, REG_SZ, ILibScratchPad, (int)strlen(ILibScratchPad)); // Save the AgentHash - util_tohex(agentHost->agentHash, UTIL_HASHSIZE, ILibScratchPad); + util_tohex(agentHost->agentHash, UTIL_SHA384_HASHSIZE, ILibScratchPad); RegSetValueExA(hKey, "AgentHash", 0, REG_SZ, ILibScratchPad, (int)strlen(ILibScratchPad)); // Save a bunch of values in the registry diff --git a/meshcore/agentcore.h b/meshcore/agentcore.h index 2fd9cd0..1c02727 100644 --- a/meshcore/agentcore.h +++ b/meshcore/agentcore.h @@ -17,9 +17,6 @@ limitations under the License. #ifndef __AGENTCORE__ #define __AGENTCORE__ -#ifdef MICROSTACK_NOTLS -#define UTIL_HASHSIZE 32 -#endif typedef char JS_ENGINE_CONTEXT[16]; @@ -49,7 +46,6 @@ typedef char JS_ENGINE_CONTEXT[16]; #include "microscript/ILibDuktapeModSearch.h" #include "microscript/ILibDuktape_GenericMarshal.h" #include "microscript/ILibDuktape_SimpleDataStore.h" -#include "microscript/ILibDuktape_ProcessPipe.h" #include "microscript/ILibDuktape_fs.h" #include "microstack/ILibProcessPipe.h" #include "microstack/ILibCrypto.h" @@ -169,23 +165,25 @@ typedef struct MeshAgentHostContainer int serverIndex; int retryTime; MeshAgentHost_BatteryInfo batteryState; - char meshId[UTIL_HASHSIZE]; + char meshId[UTIL_SHA384_HASHSIZE]; int performSelfUpdate; int disableUpdate; - char agentHash[UTIL_HASHSIZE]; - char serverHash[UTIL_HASHSIZE]; + char agentHash[UTIL_SHA384_HASHSIZE]; + char serverHash[UTIL_SHA384_HASHSIZE]; #ifndef MICROSTACK_NOTLS struct util_cert selfcert; struct util_cert selftlscert; struct util_cert selftlsclientcert; - char serverWebHash[UTIL_HASHSIZE]; - char serverNonce[UTIL_HASHSIZE]; - char agentNonce[UTIL_HASHSIZE]; - int serverAuthState; + char serverWebHash[UTIL_SHA384_HASHSIZE]; #endif + + char serverNonce[UTIL_SHA384_HASHSIZE]; + char agentNonce[UTIL_SHA384_HASHSIZE]; + int serverAuthState; + int controlChannel_idleTimeout_seconds; - char g_selfid[UTIL_HASHSIZE]; + char g_selfid[UTIL_SHA384_HASHSIZE]; void* microLMS; void* multicastDiscovery; char* multicastServerUrl; diff --git a/meshcore/meshdefines.h b/meshcore/meshdefines.h index 95dfe7d..d294782 100644 --- a/meshcore/meshdefines.h +++ b/meshcore/meshdefines.h @@ -17,7 +17,6 @@ limitations under the License. #if !defined(__MeshDefines__) #define __MeshDefined__ -#define UTIL_HASHSIZE 48 #define MESH_AGENT_PORT 16990 //!< Default Mesh Agent Port #define MESH_AGENT_STUN_PORT 16991 //!< Default Mesh Agent STUN Port #define MESH_AGENT_VERSION 1 //!< Used for self-update system. diff --git a/meshservice/MeshService.rc b/meshservice/MeshService.rc index 0a8677f..c1466a2 100644 --- a/meshservice/MeshService.rc +++ b/meshservice/MeshService.rc @@ -61,7 +61,7 @@ IDI_ICON1 ICON "MeshService.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,2,0,3 + FILEVERSION 0,2,1,3 PRODUCTVERSION 0,0,0,0 FILEFLAGSMASK 0x17L #ifdef _DEBUG @@ -78,7 +78,7 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "Mesh Agent Service" - VALUE "FileVersion", "0.2.0.3" + VALUE "FileVersion", "0.2.1.3" VALUE "InternalName", "MeshAgent" VALUE "LegalCopyright", "Apache 2.0 License" VALUE "OriginalFilename", "MeshAgent.exe" diff --git a/meshservice/MeshService.vcxproj b/meshservice/MeshService.vcxproj index cca3471..ae7bfbe 100644 --- a/meshservice/MeshService.vcxproj +++ b/meshservice/MeshService.vcxproj @@ -252,12 +252,7 @@ - - - - - @@ -267,13 +262,14 @@ + + - @@ -308,19 +304,8 @@ - - - - - - - - - - - @@ -330,12 +315,12 @@ + - diff --git a/meshservice/MeshService.vcxproj.filters b/meshservice/MeshService.vcxproj.filters index 3f7f925..303b494 100644 --- a/meshservice/MeshService.vcxproj.filters +++ b/meshservice/MeshService.vcxproj.filters @@ -17,9 +17,6 @@ {074ddd93-a67c-44e2-b7d9-b84549b60522} - - {555848d8-baf6-4cf1-94d6-935c78f6fd48} - @@ -37,9 +34,6 @@ Microscript - - Microscript - Microscript @@ -118,9 +112,6 @@ Meshcore - - Microscript - Microscript @@ -151,18 +142,6 @@ Microscript - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - Microstack @@ -187,6 +166,12 @@ Microscript + + Microscript + + + Microscript + @@ -195,9 +180,6 @@ Microscript - - Microscript - Microscript @@ -279,9 +261,6 @@ Meshcore - - Microscript - Microscript @@ -312,36 +291,6 @@ Microscript - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - - - MicroLMS - Microstack @@ -364,5 +313,8 @@ Microscript + + Microscript + \ No newline at end of file diff --git a/meshservice/ServiceMain.c b/meshservice/ServiceMain.c index 8422c35..3b13fd1 100644 --- a/meshservice/ServiceMain.c +++ b/meshservice/ServiceMain.c @@ -48,9 +48,11 @@ int ClearWindowsFirewall(wchar_t* processname); #define _CRTDBG_MAP_ALLOC #endif -TCHAR* serviceFile = TEXT("Mesh Agent v2"); -TCHAR* serviceName = TEXT("Mesh Agent v2 background service"); +TCHAR* serviceFile = TEXT("Mesh Agent"); +TCHAR* serviceFileOld = TEXT("Mesh Agent v2"); +TCHAR* serviceName = TEXT("Mesh Agent background service"); TCHAR* serviceDesc = TEXT("Remote monitoring and management service."); + SERVICE_STATUS serviceStatus; SERVICE_STATUS_HANDLE serviceStatusHandle = 0; INT_PTR CALLBACK DialogHandler(HWND, UINT, WPARAM, LPARAM); @@ -261,14 +263,14 @@ BOOL InstallService() return r; } -int UninstallService() +int UninstallService(TCHAR* serviceName) { int r = 0; SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CONNECT); if (serviceControlManager) { - SC_HANDLE service = OpenService( serviceControlManager, serviceFile, SERVICE_QUERY_STATUS | DELETE ); + SC_HANDLE service = OpenService( serviceControlManager, serviceName, SERVICE_QUERY_STATUS | DELETE ); if (service) { SERVICE_STATUS serviceStatusEx; @@ -457,7 +459,8 @@ void fullinstall(int uninstallonly, char* proxy, int proxylen, char* tag, int ta // Stop and remove the service StopService(serviceFile); - UninstallService(); + UninstallService(serviceFile); + UninstallService(serviceFileOld); // Get our own executable selfexelen = GetModuleFileNameA(NULL, selfexe, _MAX_PATH); @@ -843,7 +846,8 @@ int main(int argc, char* argv[]) { // Setup the service StopService(serviceFile); - UninstallService(); + UninstallService(serviceFile); + UninstallService(serviceFileOld); if (InstallService() == TRUE) { printf("Mesh agent installed"); } else { printf("Failed to install mesh agent"); } #ifndef _MINCORE @@ -872,7 +876,8 @@ int main(int argc, char* argv[]) StopService(serviceFile); // Remove the service - i = UninstallService(); + UninstallService(serviceFileOld); + i = UninstallService(serviceFile); if (i == 0) { printf("Failed to uninstall mesh agent"); } else if (i == 1) { printf("Mesh agent uninstalled"); } else if (i == 2) { printf("Mesh agent still running"); } @@ -1042,6 +1047,7 @@ int main(int argc, char* argv[]) } else { + UninstallService(serviceFileOld); if (argc > 1) { // See if we need to run as a script engine diff --git a/microscript/ILibDuktapeModSearch.c b/microscript/ILibDuktapeModSearch.c index 62a78e7..f9fb458 100644 --- a/microscript/ILibDuktapeModSearch.c +++ b/microscript/ILibDuktapeModSearch.c @@ -29,7 +29,7 @@ limitations under the License. #include #endif -#define ILibDuktape_ModSearch_ModuleFile "0xFF" +#define ILibDuktape_ModSearch_ModuleFile (void*)0xFF int ILibDuktape_ModSearch_AddModule(duk_context *ctx, char *id, char *module, int moduleLen) { diff --git a/microscript/ILibDuktape_ChildProcess.c b/microscript/ILibDuktape_ChildProcess.c index f1955b9..330e189 100644 --- a/microscript/ILibDuktape_ChildProcess.c +++ b/microscript/ILibDuktape_ChildProcess.c @@ -9,7 +9,6 @@ #include "ILibDuktape_WritableStream.h" #include "ILibDuktape_EventEmitter.h" -#define ILibDuktape_ChildProcess_Manager "\xFF_ChildProcess_Manager" #define ILibDuktape_ChildProcess_Process "\xFF_ChildProcess_Process" #define ILibDuktape_ChildProcess_MemBuf "\xFF_ChildProcess_MemBuf" @@ -122,14 +121,23 @@ ILibDuktape_ChildProcess_SubProcess* ILibDuktape_ChildProcess_SpawnedProcess_PUS ILibDuktape_CreateInstanceMethod(ctx, "kill", ILibDuktape_ChildProcess_Kill, 0); duk_push_object(ctx); + ILibDuktape_WriteID(ctx, "childProcess.stdout"); + duk_dup(ctx, -2); + ILibDuktape_CreateReadonlyProperty(ctx, "parent"); retVal->stdOut = ILibDuktape_ReadableStream_Init(ctx, ILibDuktape_ChildProcess_SubProcess_StdOut_OnPause, ILibDuktape_ChildProcess_SubProcess_StdOut_OnResume, retVal); ILibDuktape_CreateReadonlyProperty(ctx, "stdout"); duk_push_object(ctx); + ILibDuktape_WriteID(ctx, "childProcess.stderr"); + duk_dup(ctx, -2); + ILibDuktape_CreateReadonlyProperty(ctx, "parent"); retVal->stdErr = ILibDuktape_ReadableStream_Init(ctx, ILibDuktape_ChildProcess_SubProcess_StdErr_OnPause, ILibDuktape_ChildProcess_SubProcess_StdErr_OnResume, retVal); ILibDuktape_CreateReadonlyProperty(ctx, "stderr"); duk_push_object(ctx); + ILibDuktape_WriteID(ctx, "childProcess.stdin"); + duk_dup(ctx, -2); + ILibDuktape_CreateReadonlyProperty(ctx, "parent"); retVal->stdIn = ILibDuktape_WritableStream_Init(ctx, ILibDuktape_ChildProcess_SubProcess_StdIn_WriteHandler, ILibDuktape_ChildProcess_SubProcess_StdIn_EndHandler, retVal); ILibDuktape_CreateReadonlyProperty(ctx, "stdin"); @@ -163,12 +171,13 @@ duk_ret_t ILibDuktape_ChildProcess_execFile(duk_context *ctx) int i, x; void *callback = NULL; ILibProcessPipe_Process p = NULL; + ILibProcessPipe_SpawnTypes spawnType = ILibProcessPipe_SpawnTypes_DEFAULT; for (i = 0; i < nargs; ++i) { if (duk_is_array(ctx, i) != 0) { - int arrLen = duk_get_length(ctx, i); + int arrLen = (int)duk_get_length(ctx, i); #ifdef WIN32 args = (char**)_alloca((arrLen + 1) * sizeof(char*)); #else @@ -185,6 +194,11 @@ duk_ret_t ILibDuktape_ChildProcess_execFile(duk_context *ctx) { callback = duk_get_heapptr(ctx, i); } + else if (duk_is_object(ctx, i)) + { + // Options + spawnType = (ILibProcessPipe_SpawnTypes)Duktape_GetIntPropertyValue(ctx, i, "type", (int)ILibProcessPipe_SpawnTypes_DEFAULT); + } } #ifdef WIN32 @@ -206,7 +220,7 @@ duk_ret_t ILibDuktape_ChildProcess_execFile(duk_context *ctx) } #endif - p = ILibProcessPipe_Manager_SpawnProcess(manager, target, args); + p = ILibProcessPipe_Manager_SpawnProcessEx2(manager, target, args, spawnType, 0); if (p == NULL) { return(ILibDuktape_Error(ctx, "child_process.execFile(): Could not exec [%s]", target)); @@ -222,6 +236,16 @@ void ILibDuktape_ChildProcess_PUSH(duk_context *ctx, void *chain) ILibDuktape_CreateFinalizer(ctx, ILibDuktape_ChildProcess_Manager_Finalizer); ILibDuktape_CreateInstanceMethod(ctx, "execFile", ILibDuktape_ChildProcess_execFile, DUK_VARARGS); + duk_push_object(ctx); + duk_push_int(ctx, 0); + duk_put_prop_string(ctx, -2, "DEFAULT"); + duk_push_int(ctx, 1); + duk_put_prop_string(ctx, -2, "USER"); + duk_push_int(ctx, 2); + duk_put_prop_string(ctx, -2, "WINLOGON"); + duk_push_int(ctx, 3); + duk_put_prop_string(ctx, -2, "TERM"); + duk_put_prop_string(ctx, -2, "SpawnTypes"); } void ILibDuktape_ChildProcess_Init(duk_context *ctx) { diff --git a/microscript/ILibDuktape_ChildProcess.h b/microscript/ILibDuktape_ChildProcess.h index 45a9546..d7ad69d 100644 --- a/microscript/ILibDuktape_ChildProcess.h +++ b/microscript/ILibDuktape_ChildProcess.h @@ -2,6 +2,7 @@ #define __ILIBDUKTAPE_CHILDPROCESS__ #include "duktape.h" +#define ILibDuktape_ChildProcess_Manager "\xFF_ChildProcess_Manager" void ILibDuktape_ChildProcess_Init(duk_context *ctx); diff --git a/microscript/ILibDuktape_Dgram.c b/microscript/ILibDuktape_Dgram.c index 5566f55..78a532d 100644 --- a/microscript/ILibDuktape_Dgram.c +++ b/microscript/ILibDuktape_Dgram.c @@ -376,6 +376,29 @@ duk_ret_t ILibDuktape_DGram_setTTL(duk_context *ctx) { return ILibDuktape_Error(ctx, "Not implemented"); } +duk_ret_t ILibDuktape_DGram_setMulticastInterface(duk_context *ctx) +{ + ILibDuktape_DGRAM_DATA *ptrs = ILibDuktape_DGram_GetPTR(ctx); + struct sockaddr_in addr; + char *str = (char*)duk_require_string(ctx, 0); + + memset(&addr, 0, sizeof(struct sockaddr_in)); + ILibInet_pton(AF_INET, str, &(addr.sin_addr)); + + ILibAsyncUDPSocket_SetMulticastInterface(ptrs->mSocket, (struct sockaddr*)&addr); + return(0); +} +duk_ret_t ILibDuktape_Dgram_socket_close(duk_context *ctx) +{ + if (duk_get_top(ctx) > 0 && duk_is_function(ctx, 0)) { ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx), "close", duk_require_heapptr(ctx, 0)); } + + duk_push_this(ctx); // [socket] + duk_get_prop_string(ctx, -1, ILibDuktape_DGRAM_SOCKET_NATIVE); // [socket][ptr] + ILibDuktape_DGRAM_DATA *data = (ILibDuktape_DGRAM_DATA*)Duktape_GetBuffer(ctx, -1, NULL); + ILibAsyncSocket_Disconnect(data->mSocket); + + return(0); +} duk_ret_t ILibDuktape_DGram_createSocket(duk_context *ctx) { ILibDuktape_DGRAM_Config config = ILibDuktape_DGRAM_Config_NONE; @@ -397,6 +420,7 @@ duk_ret_t ILibDuktape_DGram_createSocket(duk_context *ctx) /**************************************************************************************/ duk_push_object(ctx); // [socket] + ILibDuktape_WriteID(ctx, "dgram.socket"); ILibDuktape_CreateFinalizer(ctx, ILibDuktape_Dgram_Finalizer); duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_DGRAM_DATA)); // [socket][native] ptrs = (ILibDuktape_DGRAM_DATA*)Duktape_GetBuffer(ctx, -1, NULL); @@ -420,10 +444,13 @@ duk_ret_t ILibDuktape_DGram_createSocket(duk_context *ctx) ILibDuktape_CreateInstanceMethodWithStringProperty(ctx, ILibDuktape_DGRAM_MULTICAST_MEMBERSHIP_TYPE, "add", "addMembership", ILibDuktape_DGram_multicastMembership, DUK_VARARGS); ILibDuktape_CreateInstanceMethodWithStringProperty(ctx, ILibDuktape_DGRAM_MULTICAST_MEMBERSHIP_TYPE, "remove", "dropMembership", ILibDuktape_DGram_multicastMembership, DUK_VARARGS); + + ILibDuktape_CreateProperty_InstanceMethod(ctx, "close", ILibDuktape_Dgram_socket_close, DUK_VARARGS); ILibDuktape_CreateInstanceMethod(ctx, "send", ILibDuktape_DGram_send, DUK_VARARGS); ILibDuktape_CreateInstanceMethod(ctx, "setBroadcast", ILibDuktape_DGram_setBroadcast, DUK_VARARGS); ILibDuktape_CreateInstanceMethod(ctx, "setMulticastLoopback", ILibDuktape_DGram_setMulticastLoopback, 1); ILibDuktape_CreateInstanceMethod(ctx, "setMulticastTTL", ILibDuktape_DGram_setMulticastTTL, 1); + ILibDuktape_CreateInstanceMethod(ctx, "setMulticastInterface", ILibDuktape_DGram_setMulticastInterface, 1); ILibDuktape_CreateInstanceMethod(ctx, "setTTL", ILibDuktape_DGram_setTTL, 1); return 1; diff --git a/microscript/ILibDuktape_DuplexStream.c b/microscript/ILibDuktape_DuplexStream.c index 6f5ced2..60fa68e 100644 --- a/microscript/ILibDuktape_DuplexStream.c +++ b/microscript/ILibDuktape_DuplexStream.c @@ -18,6 +18,7 @@ limitations under the License. #include "ILibDuktape_Helpers.h" #include "ILibDuktape_DuplexStream.h" +#define ILibDuktape_Transform_Data "\xFF_Transform_Data" #ifdef __DOXY__ /*! @@ -53,7 +54,19 @@ void ILibDuktape_DuplexStream_OnEnd(struct ILibDuktape_WritableStream *stream, v if (ds->OnEnd != NULL) { ((ILibDuktape_DuplexStream_EndHandler)ds->OnEnd)(ds, ds->user); } } -ILibDuktape_DuplexStream * ILibDuktape_DuplexStream_Init(duk_context * ctx, ILibDuktape_DuplexStream_WriteHandler WriteHandler, ILibDuktape_DuplexStream_EndHandler EndHandler, ILibDuktape_DuplexStream_PauseResumeHandler PauseHandler, ILibDuktape_DuplexStream_PauseResumeHandler ResumeHandler, void * user) +int ILibDuktape_DuplexStream_OnUnshift(ILibDuktape_readableStream *sender, int unshiftBytes, void *user) +{ + ILibDuktape_DuplexStream *ds = (ILibDuktape_DuplexStream*)user; + if (ds->unshiftHandler == NULL) + { + return(0); + } + else + { + return(((ILibDuktape_DuplexStream_UnshiftHandler)ds->unshiftHandler)(ds, unshiftBytes, ds->user)); + } +} +ILibDuktape_DuplexStream * ILibDuktape_DuplexStream_InitEx(duk_context * ctx, ILibDuktape_DuplexStream_WriteHandler WriteHandler, ILibDuktape_DuplexStream_EndHandler EndHandler, ILibDuktape_DuplexStream_PauseResumeHandler PauseHandler, ILibDuktape_DuplexStream_PauseResumeHandler ResumeHandler, ILibDuktape_DuplexStream_UnshiftHandler UnshiftHandler, void * user) { ILibDuktape_DuplexStream *retVal; @@ -63,12 +76,94 @@ ILibDuktape_DuplexStream * ILibDuktape_DuplexStream_Init(duk_context * ctx, ILib memset(retVal, 0, sizeof(ILibDuktape_DuplexStream)); retVal->user = user; - retVal->readableStream = ILibDuktape_InitReadableStream(ctx, ILibDuktape_DuplexStream_OnPause, ILibDuktape_DuplexStream_OnResume, retVal); + retVal->readableStream = ILibDuktape_ReadableStream_InitEx(ctx, ILibDuktape_DuplexStream_OnPause, ILibDuktape_DuplexStream_OnResume, UnshiftHandler != NULL ? ILibDuktape_DuplexStream_OnUnshift : NULL, retVal); retVal->writableStream = ILibDuktape_WritableStream_Init(ctx, ILibDuktape_DuplexStream_OnWrite, ILibDuktape_DuplexStream_OnEnd, retVal); retVal->OnEnd = EndHandler; retVal->OnWrite = WriteHandler; retVal->OnPause = PauseHandler; retVal->OnResume = ResumeHandler; retVal->ParentObject = duk_get_heapptr(ctx, -1); + retVal->unshiftHandler = UnshiftHandler; return retVal; } + +ILibTransport_DoneState ILibDuktape_Transform_WriteSink(struct ILibDuktape_WritableStream *stream, char *buffer, int bufferLen, void *user) +{ + ILibDuktape_Transform *TF = (ILibDuktape_Transform*)user; + TF->writerEnded = stream->endBytes; + TF->On_NativeTransform(TF, TF->source->Reserved, stream->endBytes, buffer, bufferLen, TF->user); + return(TF->target->paused == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE); +} +void ILibDuktape_Transform_EndSink(struct ILibDuktape_WritableStream *stream, void *user) +{ + ILibDuktape_Transform *TF = (ILibDuktape_Transform*)user; + if (TF->writerEnded == 0) + { + TF->writerEnded = -1; + TF->On_NativeTransform(TF, 0, -1, NULL, 0, TF->user); + } + ILibDuktape_readableStream_WriteEnd(TF->target); +} +void ILibDuktape_Transform_PauseSink(struct ILibDuktape_readableStream *sender, void *user) +{ + UNREFERENCED_PARAMETER(sender); + UNREFERENCED_PARAMETER(user); + + // NO-OP, because it is handled in the WriteSink +} +void ILibDuktape_Transform_ResumeSink(struct ILibDuktape_readableStream *sender, void *user) +{ + ILibDuktape_Transform *TF = (ILibDuktape_Transform*)user; + ILibDuktape_WritableStream_Ready(TF->source); +} +void ILibDuktape_Transform_ReaderPipeHook(struct ILibDuktape_readableStream *sender, void *wstream, void *user) +{ + ILibDuktape_Transform *TF = (ILibDuktape_Transform*)user; + TF->readerIsPiped = 1; + if (TF->On_NativePipedSink != NULL) { TF->On_NativePipedSink(TF, TF->user); } +} +int ILibDuktape_Transform_UnshiftSink(struct ILibDuktape_readableStream *sender, int unshiftBytes, void *user) +{ + ILibDuktape_Transform *TF = (ILibDuktape_Transform*)user; + return(TF->On_NativeUnshift(TF, unshiftBytes, TF->user)); +} +ILibDuktape_Transform* ILibDuktape_Transform_InitEx(duk_context *ctx, ILibDuktape_TransformStream_TransformHandler transformHandler, ILibDuktape_TransformStream_UnShiftHandler unshiftHandler, ILibDuktape_TransformStream_TargetPipedHandler pipedHandler, void *user) +{ + ILibDuktape_Transform *TF; + + duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_Transform)); // [buffer] + TF = (ILibDuktape_Transform*)Duktape_GetBuffer(ctx, -1, NULL); + duk_put_prop_string(ctx, -2, ILibDuktape_Transform_Data); // ... + + TF->source = ILibDuktape_WritableStream_Init(ctx, ILibDuktape_Transform_WriteSink, ILibDuktape_Transform_EndSink, TF); + if (unshiftHandler != NULL) + { + TF->target = ILibDuktape_ReadableStream_InitEx(ctx, ILibDuktape_Transform_PauseSink, ILibDuktape_Transform_ResumeSink, ILibDuktape_Transform_UnshiftSink, TF); + } + else + { + TF->target = ILibDuktape_ReadableStream_Init(ctx, ILibDuktape_Transform_PauseSink, ILibDuktape_Transform_ResumeSink, TF); + } + TF->target->PipeHookHandler = ILibDuktape_Transform_ReaderPipeHook; + TF->On_NativePipedSink = pipedHandler; + TF->On_NativeTransform = transformHandler; + TF->On_NativeUnshift = unshiftHandler; + TF->user = user; + + return(TF); +} + +ILibDuktape_WritableStream *ILibDuktape_DuplexStream_GetNativeWritable(duk_context *ctx, void *stream) +{ + ILibDuktape_WritableStream *retVal = NULL; + duk_push_heapptr(ctx, stream); // [stream] + if (duk_has_prop_string(ctx, -1, ILibDuktape_WritableStream_WSPTRS)) + { + duk_get_prop_string(ctx, -1, ILibDuktape_WritableStream_WSPTRS); // [stream][ws] + retVal = (ILibDuktape_WritableStream*)Duktape_GetBuffer(ctx, -1, NULL); + if (retVal->JSCreated) { retVal = NULL; } + duk_pop(ctx); // [stream] + } + duk_pop(ctx); // ... + return(retVal); +} \ No newline at end of file diff --git a/microscript/ILibDuktape_DuplexStream.h b/microscript/ILibDuktape_DuplexStream.h index 9e7d5b9..cb6b93e 100644 --- a/microscript/ILibDuktape_DuplexStream.h +++ b/microscript/ILibDuktape_DuplexStream.h @@ -28,6 +28,7 @@ typedef struct ILibDuktape_DuplexStream { ILibDuktape_readableStream *readableStream; ILibDuktape_WritableStream *writableStream; + void *unshiftHandler; void *user; void *ParentObject; void *OnWrite; @@ -36,9 +37,34 @@ typedef struct ILibDuktape_DuplexStream void *OnResume; }ILibDuktape_DuplexStream; +struct ILibDuktape_Transform; +typedef void(*ILibDuktape_TransformStream_TransformHandler)(struct ILibDuktape_Transform *sender, int Reserved, int flush, char *buffer, int bufferLen, void *user); +typedef void(*ILibDuktape_TransformStream_TargetPipedHandler)(struct ILibDuktape_Transform *sender, void *user); +typedef int(*ILibDuktape_TransformStream_UnShiftHandler)(struct ILibDuktape_Transform *sender, int unshiftBytes, void *user); + +typedef struct ILibDuktape_Transform +{ + duk_context *ctx; + ILibDuktape_WritableStream *source; + ILibDuktape_readableStream *target; + int readerIsPiped; + int writerEnded; + + ILibDuktape_TransformStream_UnShiftHandler On_NativeUnshift; + ILibDuktape_TransformStream_TransformHandler On_NativeTransform; + ILibDuktape_TransformStream_TargetPipedHandler On_NativePipedSink; + void *user; +}ILibDuktape_Transform; + + + +ILibDuktape_Transform* ILibDuktape_Transform_InitEx(duk_context *ctx, ILibDuktape_TransformStream_TransformHandler transformHandler, ILibDuktape_TransformStream_UnShiftHandler unshiftHandler, ILibDuktape_TransformStream_TargetPipedHandler pipedHandler, void *user); +#define ILibDuktape_Transform_Init(ctx, transformHandler, pipedHandler, user) ILibDuktape_Transform_InitEx(ctx, transformHandler, NULL, pipedHandler, user) + typedef ILibTransport_DoneState(*ILibDuktape_DuplexStream_WriteHandler)(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user); typedef void(*ILibDuktape_DuplexStream_EndHandler)(ILibDuktape_DuplexStream *stream, void *user); typedef void(*ILibDuktape_DuplexStream_PauseResumeHandler)(ILibDuktape_DuplexStream *sender, void *user); +typedef int(*ILibDuktape_DuplexStream_UnshiftHandler)(ILibDuktape_DuplexStream *sender, int unshiftBytes, void *user); #define ILibDuktape_DuplexStream_Ready(duplexStream) ILibDuktape_WritableStream_Ready((duplexStream)->writableStream) #define ILibDuktape_DuplexStream_WriteData(duplexStream, buffer, bufferLen) ILibDuktape_readableStream_WriteData((duplexStream)->readableStream, buffer, bufferLen) @@ -46,7 +72,7 @@ typedef void(*ILibDuktape_DuplexStream_PauseResumeHandler)(ILibDuktape_DuplexStr #define ILibDuktape_DuplexStream_WriteEnd(duplexStream) ILibDuktape_readableStream_WriteEnd((duplexStream)->readableStream) #define ILibDuktape_DuplexStream_Closed(duplexStream) ILibDuktape_readableStream_Closed((duplexStream)->readableStream) -ILibDuktape_DuplexStream* ILibDuktape_DuplexStream_Init(duk_context *ctx, ILibDuktape_DuplexStream_WriteHandler WriteHandler, ILibDuktape_DuplexStream_EndHandler EndHandler, ILibDuktape_DuplexStream_PauseResumeHandler PauseHandler, ILibDuktape_DuplexStream_PauseResumeHandler ResumeHandler, void *user); - - +ILibDuktape_DuplexStream* ILibDuktape_DuplexStream_InitEx(duk_context *ctx, ILibDuktape_DuplexStream_WriteHandler WriteHandler, ILibDuktape_DuplexStream_EndHandler EndHandler, ILibDuktape_DuplexStream_PauseResumeHandler PauseHandler, ILibDuktape_DuplexStream_PauseResumeHandler ResumeHandler, ILibDuktape_DuplexStream_UnshiftHandler UnshiftHandler, void *user); +#define ILibDuktape_DuplexStream_Init(ctx, WriteHandler, EndHandler, PauseHandler, ResumeHandler, user) ILibDuktape_DuplexStream_InitEx(ctx, WriteHandler, EndHandler, PauseHandler, ResumeHandler, NULL, user) +ILibDuktape_WritableStream *ILibDuktape_DuplexStream_GetNativeWritable(duk_context *ctx, void *stream); #endif diff --git a/microscript/ILibDuktape_EventEmitter.h b/microscript/ILibDuktape_EventEmitter.h index 67b388e..ba74ad7 100644 --- a/microscript/ILibDuktape_EventEmitter.h +++ b/microscript/ILibDuktape_EventEmitter.h @@ -31,11 +31,13 @@ int ILibDuktape_EventEmitter_AddEventHeapptr(ILibDuktape_EventEmitter *emitter, int ILibDuktape_EventEmitter_AddSink(ILibDuktape_EventEmitter *emitter, char *eventName, ILibDuktape_EventEmitter_Handler handler); // Add Native Event Handler int ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter *emitter, char *eventName, void *heapptr); // Add native event handler 'once' int ILibDuktape_EventEmitter_AddOnceEx(ILibDuktape_EventEmitter *emitter, char *eventName, duk_c_function func, duk_idx_t funcArgs); -int ILibDuktape_EventEmitter_AddOnceEx2(duk_context *ctx, duk_idx_t idx, char *eventName, duk_c_function func, duk_idx_t funcArgs); -int ILibDuktape_EventEmitter_AddOn(ILibDuktape_EventEmitter *emitter, char *eventName, void *heapptr); // Add native event handler +int ILibDuktape_EventEmitter_AddOnceEx3(duk_context *ctx, duk_idx_t idx, char *eventName, duk_c_function func); +#define ILibDuktape_EventEmitter_AddOnceEx2(ctx, idx, eventName, func, argCount) ILibDuktape_EventEmitter_AddOnceEx3(ctx, idx, eventName, func) +int ILibDuktape_EventEmitter_AddOn(ILibDuktape_EventEmitter *emitter, char *eventName, void *heapptr); // Add native event handler +int ILibDuktape_EventEmitter_AddOnEx(duk_context *ctx, duk_idx_t idx, char *eventName, duk_c_function func); void ILibDuktape_EventEmitter_AddHook(ILibDuktape_EventEmitter *emitter, char *eventName, ILibDuktape_EventEmitter_HookHandler handler); - +void ILibDuktape_EventEmitter_ForwardEvent(duk_context *ctx, duk_idx_t eventSourceIndex, char *sourceEventName, duk_idx_t eventTargetIndex, char *targetEventName); #endif diff --git a/microscript/ILibDuktape_GenericMarshal.c b/microscript/ILibDuktape_GenericMarshal.c index bebf237..2e90721 100644 --- a/microscript/ILibDuktape_GenericMarshal.c +++ b/microscript/ILibDuktape_GenericMarshal.c @@ -41,6 +41,8 @@ limitations under the License. #include #endif +#define ILibDuktape_GenericMarshal_VariableType "\xFF_GenericMarshal_VarType" + typedef PTRSIZE(APICALLTYPE *R0)(); typedef PTRSIZE(APICALLTYPE *R1)(PTRSIZE V1); typedef PTRSIZE(APICALLTYPE *R2)(PTRSIZE V1, PTRSIZE V2); @@ -52,6 +54,17 @@ typedef PTRSIZE(APICALLTYPE *R7)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, typedef PTRSIZE(APICALLTYPE *R8)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8); typedef PTRSIZE(APICALLTYPE *R9)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9); typedef PTRSIZE(APICALLTYPE *R10)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9, PTRSIZE V10); +typedef PTRSIZE(APICALLTYPE *R11)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9, PTRSIZE V10, PTRSIZE V11); +typedef PTRSIZE(APICALLTYPE *R12)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9, PTRSIZE V10, PTRSIZE V11, PTRSIZE V12); +typedef PTRSIZE(APICALLTYPE *R13)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9, PTRSIZE V10, PTRSIZE V11, PTRSIZE V12, PTRSIZE V13); +typedef PTRSIZE(APICALLTYPE *R14)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9, PTRSIZE V10, PTRSIZE V11, PTRSIZE V12, PTRSIZE V13, PTRSIZE V14); +typedef PTRSIZE(APICALLTYPE *R15)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9, PTRSIZE V10, PTRSIZE V11, PTRSIZE V12, PTRSIZE V13, PTRSIZE V14, PTRSIZE V15); +typedef PTRSIZE(APICALLTYPE *R16)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9, PTRSIZE V10, PTRSIZE V11, PTRSIZE V12, PTRSIZE V13, PTRSIZE V14, PTRSIZE V15, PTRSIZE V16); +typedef PTRSIZE(APICALLTYPE *R17)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9, PTRSIZE V10, PTRSIZE V11, PTRSIZE V12, PTRSIZE V13, PTRSIZE V14, PTRSIZE V15, PTRSIZE V16, PTRSIZE V17); +typedef PTRSIZE(APICALLTYPE *R18)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9, PTRSIZE V10, PTRSIZE V11, PTRSIZE V12, PTRSIZE V13, PTRSIZE V14, PTRSIZE V15, PTRSIZE V16, PTRSIZE V17, PTRSIZE V18); +typedef PTRSIZE(APICALLTYPE *R19)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9, PTRSIZE V10, PTRSIZE V11, PTRSIZE V12, PTRSIZE V13, PTRSIZE V14, PTRSIZE V15, PTRSIZE V16, PTRSIZE V17, PTRSIZE V18, PTRSIZE V19); +typedef PTRSIZE(APICALLTYPE *R20)(PTRSIZE V1, PTRSIZE V2, PTRSIZE V3, PTRSIZE V4, PTRSIZE V5, PTRSIZE V6, PTRSIZE V7, PTRSIZE V8, PTRSIZE V9, PTRSIZE V10, PTRSIZE V11, PTRSIZE V12, PTRSIZE V13, PTRSIZE V14, PTRSIZE V15, PTRSIZE V16, PTRSIZE V17, PTRSIZE V18, PTRSIZE V19, PTRSIZE V20); + typedef struct Duktape_GenericMarshal_Proxy { @@ -175,7 +188,6 @@ duk_ret_t ILibDuktape_GenericMarshal_Variable_Val_SET(duk_context *ctx) { void *ptr; int size; - int value = duk_require_int(ctx, 0); duk_push_this(ctx); // [var] duk_get_prop_string(ctx, -1, "_ptr"); // [var][ptr] @@ -183,18 +195,32 @@ duk_ret_t ILibDuktape_GenericMarshal_Variable_Val_SET(duk_context *ctx) duk_get_prop_string(ctx, -2, "_size"); // [var][ptr][size] size = duk_to_int(ctx, -1); - switch (size) + if (duk_is_number(ctx, 0)) { + switch (size) + { case 2: - ((unsigned short*)ptr)[0] = (unsigned short)value; + ((unsigned short*)ptr)[0] = (unsigned short)duk_require_int(ctx, 0); break; case 4: - ((unsigned int*)ptr)[0] = (unsigned int)value; + ((unsigned int*)ptr)[0] = (unsigned int)duk_require_int(ctx, 0); break; default: - duk_push_string(ctx, "UNSUPPORTED VAL SIZE"); + duk_push_string(ctx, "UNSUPPORTED VAL SIZE, with integral type"); duk_throw(ctx); return(DUK_RET_ERROR); + } + } + else if (duk_is_object(ctx, 0) && duk_has_prop_string(ctx, 0, ILibDuktape_GenericMarshal_VariableType)) + { + ((void**)ptr)[0] = Duktape_GetPointerProperty(ctx, 0, "_ptr"); + duk_push_this(ctx); // [var] + duk_dup(ctx, 0); // [var][var] + duk_put_prop_string(ctx, -2, "\xFF_ref"); + } + else + { + return(ILibDuktape_Error(ctx, "Invalid Parameter")); } return 0; } @@ -292,6 +318,18 @@ duk_ret_t ILibDuktape_GenericMarshal_Variable_SetEx(duk_context *ctx) } return 0; } +duk_ret_t ILibDuktape_GenericMarshal_Variable_toBuffer(duk_context *ctx) +{ + duk_push_this(ctx); // [variable] + + void *buffer = Duktape_GetPointerProperty(ctx, -1, "_ptr"); + int bufferLen = Duktape_GetIntPropertyValue(ctx, -1, "_size", 0); + + duk_push_external_buffer(ctx); // [variable][ext] + duk_config_buffer(ctx, -1, buffer, (duk_size_t)bufferLen); + duk_push_buffer_object(ctx, -1, 0, (duk_size_t)bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); + return(1); +} void ILibDuktape_GenericMarshal_Variable_PUSH(duk_context *ctx, void *ptr, int size) { duk_push_object(ctx); // [var] @@ -299,7 +337,8 @@ void ILibDuktape_GenericMarshal_Variable_PUSH(duk_context *ctx, void *ptr, int s duk_put_prop_string(ctx, -2, "_ptr"); // [var] duk_push_int(ctx, size); // [var][size] duk_put_prop_string(ctx, -2, "_size"); // [var] - + duk_push_true(ctx); + duk_put_prop_string(ctx, -2, ILibDuktape_GenericMarshal_VariableType); ILibDuktape_CreateEventWithGetterAndSetterEx(ctx, "Val", ILibDuktape_GenericMarshal_Variable_Val_GET, ILibDuktape_GenericMarshal_Variable_Val_SET); ILibDuktape_CreateEventWithGetterAndSetterWithIntMetaData(ctx, "_VarSize", 4, "IntVal", ILibDuktape_GenericMarshal_Variable_GetEx, ILibDuktape_GenericMarshal_Variable_SetEx); @@ -310,6 +349,8 @@ void ILibDuktape_GenericMarshal_Variable_PUSH(duk_context *ctx, void *ptr, int s ILibDuktape_CreateEventWithGetter(ctx, "AnsiString", ILibDuktape_GenericMarshal_Variable_Val_ASTRING); ILibDuktape_CreateEventWithGetter(ctx, "HexString", ILibDuktape_GenericMarshal_Variable_Val_HSTRING); ILibDuktape_CreateEventWithGetter(ctx, "HexString2", ILibDuktape_GenericMarshal_Variable_Val_HSTRING2); + + ILibDuktape_CreateInstanceMethod(ctx, "toBuffer", ILibDuktape_GenericMarshal_Variable_toBuffer, 0); } duk_ret_t ILibDuktape_GenericMarshal_Variable_Finalizer(duk_context *ctx) { @@ -330,9 +371,30 @@ duk_ret_t ILibDuktape_GenericMarshal_Variable_Finalizer(duk_context *ctx) duk_ret_t ILibDuktape_GenericMarshal_CreateVariable(duk_context *ctx) { char* ptr; - int size = duk_require_int(ctx, 0); + int size; + char *str = NULL; + duk_size_t strLen; + + if (duk_is_number(ctx, 0)) + { + size = duk_require_int(ctx, 0); + } + else if(duk_is_string(ctx, 0)) + { + str = Duktape_GetBuffer(ctx, 0, &strLen); + size = (int)strLen + 1; + } + else + { + return(ILibDuktape_Error(ctx, "_GenericMarshal.CreateVariable(): Invalid Parameter")); + } ptr = (char*)ILibMemory_Allocate(size, 0, NULL, NULL); + if (str != NULL) + { + memcpy_s(ptr, size, str, strLen); + ptr[strLen] = 0; + } ILibDuktape_GenericMarshal_Variable_PUSH(ctx, ptr, size); // [var] ILibDuktape_CreateFinalizer(ctx, ILibDuktape_GenericMarshal_Variable_Finalizer); @@ -567,8 +629,9 @@ duk_ret_t ILibDuktape_GenericMarshal_MethodInvoke(duk_context *ctx) void *fptr = NULL; int parms = duk_get_top(ctx); int i; - PTRSIZE vars[10]; int retVal = -1; + if (parms > 20) { return(ILibDuktape_Error(ctx, "Too many parameters")); } + PTRSIZE *vars = ILibMemory_AllocateA(sizeof(PTRSIZE)*parms); duk_push_current_function(ctx); // [func] duk_get_prop_string(ctx, -1, "_address"); // [func][addr] @@ -639,6 +702,36 @@ duk_ret_t ILibDuktape_GenericMarshal_MethodInvoke(duk_context *ctx) case 10: retVal = (int)((R10)fptr)(vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7], vars[8], vars[9]); break; + case 11: + retVal = (int)((R11)fptr)(vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7], vars[8], vars[9], vars[10]); + break; + case 12: + retVal = (int)((R12)fptr)(vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7], vars[8], vars[9], vars[10], vars[11]); + break; + case 13: + retVal = (int)((R13)fptr)(vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7], vars[8], vars[9], vars[10], vars[11], vars[12]); + break; + case 14: + retVal = (int)((R14)fptr)(vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7], vars[8], vars[9], vars[10], vars[11], vars[12], vars[13]); + break; + case 15: + retVal = (int)((R15)fptr)(vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7], vars[8], vars[9], vars[10], vars[11], vars[12], vars[13], vars[14]); + break; + case 16: + retVal = (int)((R16)fptr)(vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7], vars[8], vars[9], vars[10], vars[11], vars[12], vars[13], vars[14], vars[15]); + break; + case 17: + retVal = (int)((R17)fptr)(vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7], vars[8], vars[9], vars[10], vars[11], vars[12], vars[13], vars[14], vars[15], vars[16]); + break; + case 18: + retVal = (int)((R18)fptr)(vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7], vars[8], vars[9], vars[10], vars[11], vars[12], vars[13], vars[14], vars[15], vars[16], vars[17]); + break; + case 19: + retVal = (int)((R19)fptr)(vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7], vars[8], vars[9], vars[10], vars[11], vars[12], vars[13], vars[14], vars[15], vars[16], vars[17], vars[18]); + break; + case 20: + retVal = (int)((R20)fptr)(vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7], vars[8], vars[9], vars[10], vars[11], vars[12], vars[13], vars[14], vars[15], vars[16], vars[17], vars[18], vars[19]); + break; default: duk_push_string(ctx, "INVALID NUMBER OF PARAMETERS, MAX of 10"); duk_throw(ctx); @@ -726,6 +819,7 @@ duk_ret_t ILibDuktape_GenericMarshal_CreateVariableEx(duk_context *ctx) return 1; } + void ILibDuktape_GenericMarshal_Push(duk_context *ctx, void *chain) { duk_push_object(ctx); // [obj] diff --git a/microscript/ILibDuktape_HECI.c b/microscript/ILibDuktape_HECI.c new file mode 100644 index 0000000..3278c42 --- /dev/null +++ b/microscript/ILibDuktape_HECI.c @@ -0,0 +1,1023 @@ +#include "ILibDuktape_HECI.h" +#include "ILibDuktapeModSearch.h" +#include "ILibDuktape_Helpers.h" +#include "ILibDuktape_DuplexStream.h" +#include "ILibDuktape_EventEmitter.h" +#include "ILibDuktape_ChildProcess.h" +#include "../microstack/ILibParsers.h" +#include "../microstack/ILibProcessPipe.h" +#include "../microstack/ILibRemoteLogging.h" + + +#ifdef WIN32 +#include +#include +#include +#include +DEFINE_GUID(GUID_DEVINTERFACE_HECI, 0xE2D1FF34, 0x3458, 0x49A9, 0x88, 0xDA, 0x8E, 0x69, 0x15, 0xCE, 0x9B, 0xE5); +#elif defined(_POSIX) +typedef struct HECI_client +{ + unsigned int max_msg_length; + unsigned char protocol_version; + unsigned char reserved[3]; +}HECI_client; +typedef struct uuid_le +{ + unsigned char b[16]; +}uuid_le; +struct HECI_CONNECT_client_data +{ + union + { + uuid_le uuid; + HECI_client properties; + }; +}; +#endif + +#define ILibDuktape_HECI_ChainLink "\xFF_HECI_ChainLink" +#define ILibDuktape_HECI_Descriptor "\xFF_HECI_Descriptor" +#define ILibDuktape_HECI_ChildProcess "\xFF_HECI_ChildProcess" +#define ILibDuktape_HECI_Q "\xFF_HECI_Q" +#define ILibDuktape_HECI_IoctlWaitHandle "\xFF_HECI_IoctlWaitHandle" +#define ILibDuktape_HECI_Child "\xFF_HECI_Child" +#define ILibDuktape_HECI_Parent "\xFF_HECI_Parent" +#define ILibDuktape_HECI_MaxBufferSize "\xFF_HECI_MaxBufSize" +#define ILibDuktape_HECI_SessionMemPtr "\xFF_HECI_SessionMemPtr" +#define ILibDuktape_HECI_Session_NoPipeline "\xFF_HECI_Session_NoPipeline" + +#ifdef __DOXY__ +/*! +\implements EventEmitter +\brief JavaScript object interface for HECI calls. require('heci') to use; +*/ +class Heci +{ +public: + /*! + \brief Performs an Ioctl on the HECI device + \param code Ioctl Code to invoke + \param inBuffer \ Input data for Ioctl. Can be null + \param outBuffer \ Optional. Output data from Ioctl. Must be specified if Ioctl code returns data + \param callback Dispatched when a response is received from the HECI device\n + status Success Code. 0 = Success, Error code on failure\n + buffer \ Output Buffer\n + args Optional parameters that were passed in\n + \param args Optional arguments to pass when the callback is called + */ + void doIoctl(code, inBuffer[, outBuffer], callback[, ...args]); +}; +#endif + +typedef struct ILibDuktape_HECI_ioctl_data +{ + duk_context *ctx; + void *heciObject; + void *data; + void *Q; + void *chain; + ILibProcessPipe_Manager pipeManager; +#ifdef WIN32 + OVERLAPPED v; + HANDLE device; + DWORD bytesReceived; +#elif defined(_POSIX) + int device; +#endif + + int code; + char *outBuffer; + void *outBuffer_obj; + duk_size_t outBufferLen; + duk_size_t bufferLen; + char buffer[]; +}ILibDuktape_HECI_ioctl_data; + +typedef struct ILibDuktape_HECI_Session +{ + void *chain; + int noPipelining; + ILibDuktape_DuplexStream *stream; +#ifdef WIN32 + OVERLAPPED v; + OVERLAPPED wv; + ILibProcessPipe_Manager mgr; + HANDLE descriptor; +#else + int descriptor; +#endif + ILibQueue PendingWrites; + duk_size_t bufferSize; + char buffer[]; +}ILibDuktape_HECI_Session; +typedef struct ILibDuktape_HECI_WriteState +{ + ILibDuktape_HECI_Session *session; + int returnIgnored; +#ifndef WIN32 + int bufferOffset; +#endif + int bufferLen; + char buffer[]; +}ILibDuktape_HECI_WriteState; + +typedef struct HECI_chainLink +{ + ILibChain_Link link; + duk_context *ctx; + void *Q; + ILibDuktape_HECI_Session *session; + void *heciObject; + int descriptor; + int paused; +}HECI_chainLink; + +void ILibDuktape_HECI_Push(duk_context *ctx, void *chain); +ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler_Process(ILibDuktape_HECI_Session *session); + +#ifdef WIN32 +HANDLE ILibDuktape_HECI_windowsInit() +{ + PSP_DEVICE_INTERFACE_DETAIL_DATA deviceDetail = NULL; + HDEVINFO hDeviceInfo; + DWORD bufferSize; + SP_DEVICE_INTERFACE_DATA interfaceData; + LONG ii = 0; + HANDLE retVal = NULL; + + // Find all devices that have our interface + hDeviceInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVINTERFACE_HECI, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (hDeviceInfo == INVALID_HANDLE_VALUE) + { + return(NULL); + } + + // Setup the interface data struct + interfaceData.cbSize = sizeof(interfaceData); + for (ii = 0; + SetupDiEnumDeviceInterfaces(hDeviceInfo, NULL, (LPGUID)&GUID_DEVINTERFACE_HECI, ii, &interfaceData); + ++ii) + { + // Found our device instance + if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, NULL, 0, &bufferSize, NULL)) + { + DWORD err = GetLastError(); + if (err != ERROR_INSUFFICIENT_BUFFER) + { + continue; + } + } + + // Allocate a big enough buffer to get detail data + deviceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)ILibMemory_AllocateA(bufferSize); + if (deviceDetail == NULL) { continue; } + + // Setup the device interface struct + deviceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + // Try again to get the device interface detail info + if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, deviceDetail, bufferSize, NULL, NULL)) + { + deviceDetail = NULL; + continue; + } + + break; + } + SetupDiDestroyDeviceInfoList(hDeviceInfo); + + if (deviceDetail == NULL) { return(NULL); } + + retVal = CreateFile(deviceDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (retVal == INVALID_HANDLE_VALUE) { return(NULL); } + + return(retVal); +} +#else +int ILibDuktape_HECI_linuxInit() +{ + int fd, flags; + + if ((fd = open("/dev/mei", O_RDWR)) == -1 && (fd = open("/dev/mei0", O_RDWR)) == -1) + { + return(-1); + } + else + { + flags = fcntl(fd, F_GETFL, 0); + if (fcntl(fd, F_SETFL, O_NONBLOCK | flags) == -1) { printf("Failed to set O_NONBLOCK\n"); close(fd); fd = -1; } + return(fd); + } +} +#endif + +duk_ret_t ILibDuktape_HECI_SessionFinalizer(duk_context *ctx) +{ + return(0); +} + + +void ILibDuktape_HECI_Session_EmitErrorEvent(void *chain, void *session) +{ + if (ILibIsRunningOnChainThread(chain) == 0) { ILibChain_RunOnMicrostackThreadEx(chain, ILibDuktape_HECI_Session_EmitErrorEvent, session); } + ILibDuktape_HECI_Session *s = (ILibDuktape_HECI_Session*)session; + duk_context *ctx = s->stream->readableStream->ctx; + + duk_push_heapptr(ctx, s->stream->ParentObject); // [session] + duk_get_prop_string(ctx, -1, "emit"); // [session][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "error"); // [emit][this][error] + duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error"); // [emit][this][error][err] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); } + duk_pop(ctx); // ... +} +void ILibDuktape_HECI_Session_EmitStreamReady(void *chain, void *session) +{ + if (ILibIsRunningOnChainThread(chain) == 0) { ILibChain_RunOnMicrostackThreadEx(chain, ILibDuktape_HECI_Session_EmitStreamReady, session); } + ILibDuktape_DuplexStream_Ready(((ILibDuktape_HECI_Session*)session)->stream); +} + +#ifdef WIN32 +BOOL ILibDuktape_HECI_Session_WriteHandler_Ready(HANDLE event, void* user) +{ + ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; + DWORD bytesWritten; + + ILibProcessPipe_WaitHandle_Remove(session->mgr, session->wv.hEvent); + + if (session->noPipelining == 0) + { + ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_DeQueue(session->PendingWrites); + free(state); + } + + if (GetOverlappedResult(session->descriptor, &(session->wv), &bytesWritten, FALSE) == 0) + { + // Broken Connection + ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session); + } + else + { + if (session->noPipelining == 0) + { + // Write Completed + ILibDuktape_HECI_Session_WriteHandler_Process(session); + } + } + return(TRUE); +} +#endif + +ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler_Process(ILibDuktape_HECI_Session *session) +{ + ILibTransport_DoneState retVal = ILibTransport_DoneState_ERROR; + int returnIgnored = 0; + +#ifdef WIN32 + DWORD bytesWritten; + BOOL result = TRUE; +#else + size_t bytesWritten; +#endif + + while (ILibQueue_GetCount(session->PendingWrites) > 0) + { + ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_PeekQueue(session->PendingWrites); + returnIgnored = state->returnIgnored; + +#ifdef WIN32 + if ((result = WriteFile(state->session->descriptor, state->buffer, state->bufferLen, &bytesWritten, &(state->session->wv))) == TRUE) + { + if (session->noPipelining == 0) { ILibQueue_DeQueue(state->session->PendingWrites); free(state); } + } + else + { + break; + } +#elif defined(_POSIX) + + bytesWritten = write(state->session->descriptor, state->buffer + state->bufferOffset, state->bufferLen - state->bufferOffset); + if (bytesWritten > 0) + { + state->bufferOffset += bytesWritten; + if (state->bufferOffset == state->bufferLen) { ILibQueue_DeQueue(state->session->PendingWrites); free(state); retVal = session->noPipelining == 0 ? ILibTransport_DoneState_COMPLETE:ILibTransport_DoneState_INCOMPLETE; } + } + else + { + if (errno != EAGAIN) + { + // Error Occured + retVal = ILibTransport_DoneState_ERROR; + ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session); + } + else + { + retVal = ILibTransport_DoneState_INCOMPLETE; + } + break; + } +#endif + + if (session->noPipelining != 0) { break; } + } + +#ifdef WIN32 + if (result == FALSE) + { + if (GetLastError() == ERROR_IO_PENDING) + { + // Not done writing + retVal = ILibTransport_DoneState_INCOMPLETE; + ILibProcessPipe_WaitHandle_Add(session->mgr, session->wv.hEvent, session, ILibDuktape_HECI_Session_WriteHandler_Ready); + } + else + { + // Error Occured + retVal = ILibTransport_DoneState_ERROR; + ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session); + } + } + else + { + if (session->noPipelining == 0) + { + // No more Pending Writes + retVal = ILibTransport_DoneState_COMPLETE; + if (returnIgnored != 0) { ILibDuktape_HECI_Session_EmitStreamReady(session->chain, (void*)session); } + } + else + { + retVal = ILibTransport_DoneState_INCOMPLETE; + } + } +#else + if (ILibQueue_GetCount(session->PendingWrites) == 0 && session->noPipelining == 0) + { + // No more Pending Writes + retVal = ILibTransport_DoneState_COMPLETE; + if (returnIgnored != 0) { ILibDuktape_HECI_Session_EmitStreamReady(session->chain, (void*)session); } + } +#endif + return(retVal); +} + +#ifdef WIN32 +void __stdcall ILibDuktape_HECI_Session_WriteHandler(ULONG_PTR obj) +{ + // This Method is always dispatched from the WindowsRunLoop APC Thread + + ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)obj; + ILibQueue_EnQueue(state->session->PendingWrites, state); + + if (ILibQueue_GetCount(state->session->PendingWrites) == 1) + { + // No Pending Writes, so we can go ahead and send out the first block + ILibDuktape_HECI_Session_WriteHandler_Process(state->session); + } +} +#elif defined(_POSIX) +ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler(void *chain, ILibDuktape_HECI_WriteState* state) +{ + // This Method is always dispatched from the Microstack Thread + ILibQueue_EnQueue(state->session->PendingWrites, state); + + if (ILibQueue_GetCount(state->session->PendingWrites) == 1) + { + return(ILibDuktape_HECI_Session_WriteHandler_Process(state->session)); + } + else + { + return(ILibTransport_DoneState_INCOMPLETE); + } +} +#endif + +ILibTransport_DoneState ILibDuktape_HECI_Session_WriteSink_NoPipeline(void *chain, void *user) +{ + // This is always called from the Microstack Thread + + ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)user; + ILibQueue_EnQueue(state->session->PendingWrites, state); + if (ILibQueue_GetCount(state->session->PendingWrites) == 1) + { + return(ILibDuktape_HECI_Session_WriteHandler_Process(state->session)); + } + else + { + return(ILibTransport_DoneState_INCOMPLETE); + } +} +ILibTransport_DoneState ILibDuktape_HECI_Session_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) +{ + if ((duk_size_t)bufferLen > ((ILibDuktape_HECI_Session*)user)->bufferSize) { return(ILibTransport_DoneState_ERROR); } + + ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; + ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibMemory_Allocate(bufferLen + sizeof(ILibDuktape_HECI_WriteState), 0, NULL, NULL); + state->session = session; + state->bufferLen = bufferLen; + memcpy_s(state->buffer, bufferLen, buffer, bufferLen); + + if (session->noPipelining == 0) + { +#if defined(WIN32) + state->returnIgnored = 1; + QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_Session_WriteHandler, ILibProcessPipe_Manager_GetWorkerThread(session->mgr), (ULONG_PTR)state); +#elif defined(_POSIX) + if (ILibIsRunningOnChainThread(stream->readableStream->chain) != 0) + { + return(ILibDuktape_HECI_Session_WriteHandler(NULL, state)); + } + else + { + state->returnIgnored = 1; + ILibChain_RunOnMicrostackThreadEx(stream->readableStream->chain, (ILibChain_StartEvent)ILibDuktape_HECI_Session_WriteHandler, state); + } +#endif + } + else + { + // We can't pipeline write requests + if (ILibIsRunningOnChainThread(stream->readableStream->chain) != 0) + { + return(ILibDuktape_HECI_Session_WriteSink_NoPipeline(stream->readableStream->chain, state)); + } + else + { + state->returnIgnored = 1; + ILibChain_RunOnMicrostackThreadEx(stream->readableStream->chain, (ILibChain_StartEvent)ILibDuktape_HECI_Session_WriteSink_NoPipeline, state); + } + } + + return(ILibTransport_DoneState_INCOMPLETE); +} +void ILibDuktape_HECI_Session_EndSink(ILibDuktape_DuplexStream *stream, void *user) +{ + +} +void ILibDuktape_HECI_Session_PauseSink(ILibDuktape_DuplexStream *sender, void *user) +{ +#ifdef WIN32 + ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; + + // To Pause, all we need to do is remove our handle + ILibProcessPipe_WaitHandle_Remove(session->mgr, session->v.hEvent); +#else + UNREFERENCED_PARAMETER(sender); + UNREFERENCED_PARAMETER(user); +#endif +} +#ifdef WIN32 +BOOL ILibDuktape_HECI_Session_ReceiveSink(HANDLE event, void* user); +#endif +void ILibDuktape_HECI_Session_ResumeSink_NoPipeline(void *chain, void *user) +{ + // This is always called from the Microstack Thread + ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; + ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_DeQueue(session->PendingWrites); + free(state); + + if (ILibQueue_GetCount(session->PendingWrites) == 0) + { + ILibDuktape_HECI_Session_EmitStreamReady(session->chain, session); + } + else + { + ILibDuktape_HECI_Session_WriteHandler_Process(session); + } +} +void ILibDuktape_HECI_Session_ResumeSink(ILibDuktape_DuplexStream *sender, void *user) +{ + ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; + if (session->noPipelining != 0) + { + ILibChain_RunOnMicrostackThread(sender->readableStream->chain, ILibDuktape_HECI_Session_ResumeSink_NoPipeline, session); + } + +#ifdef WIN32 + DWORD bytesRead; + + // To Resume, we need to ReadFile, then re-add our waithandle + BOOL result = ReadFile(session->descriptor, session->buffer, (DWORD)session->bufferSize, &bytesRead, &(session->v)); + if (result == TRUE || GetLastError() == ERROR_IO_PENDING) + { + ILibProcessPipe_WaitHandle_Add(session->mgr, session->v.hEvent, session, ILibDuktape_HECI_Session_ReceiveSink); + } +#endif +} +#ifdef WIN32 +BOOL ILibDuktape_HECI_Session_ReceiveSink(HANDLE event, void* user) +{ + ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; + DWORD bytesRead; + + do + { + if (GetOverlappedResult(session->descriptor, &(session->v), &bytesRead, FALSE) == FALSE) { break; } + ILibDuktape_DuplexStream_WriteData(session->stream, session->buffer, bytesRead); + } while (session->stream->readableStream->paused == 0 && ReadFile(session->descriptor, session->buffer, (DWORD)session->bufferSize, &bytesRead, &(session->v)) == TRUE); + if (session->stream->readableStream->paused == 0 && GetLastError() != ERROR_IO_PENDING) + { + // Broken Connection + ILibProcessPipe_WaitHandle_Remove(session->mgr, session->v.hEvent); // Remove ourselves from processing loop + ILibDuktape_DuplexStream_WriteEnd(session->stream); + } + return(TRUE); +} +void __stdcall ILibDuktape_HECI_Session_Start(ULONG_PTR obj) +{ + ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)obj; + DWORD bytesRead; + BOOL result = ReadFile(session->descriptor, session->buffer, (DWORD)session->bufferSize, &bytesRead, &(session->v)); + ILibProcessPipe_WaitHandle_Add(session->mgr, session->v.hEvent, session, ILibDuktape_HECI_Session_ReceiveSink); +} +#endif + +duk_ret_t ILibDuktape_HECI_create_OnClientConnect(duk_context *ctx) +{ + int statusCode = duk_require_int(ctx, 0); + ILibDuktape_HECI_Session *session = NULL; + duk_dup(ctx, 2); // [Session] + if (statusCode != 0) + { + duk_get_prop_string(ctx, -1, "emit"); // [session][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "error"); // [emit][this][error] + duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error [%d]", statusCode); // [emit][this][error][err] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); } + duk_pop(ctx); // ... + } + else + { + duk_size_t bufferLen; + char *buffer = (char*)Duktape_GetBuffer(ctx, 1, &bufferLen); + if (bufferLen > 4) + { + duk_push_int(ctx, ((int*)buffer)[0]); + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_MaxBufferSize); // [session] + + duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_HECI_Session) + ((int*)buffer)[0]); // [session][buffer] + session = (ILibDuktape_HECI_Session*)Duktape_GetBuffer(ctx, -1, NULL); + memset(session, 0, sizeof(ILibDuktape_HECI_Session) + ((int*)buffer)[0]); + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_SessionMemPtr); // [session] +#ifdef WIN32 + session->v.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + session->wv.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); +#endif + session->chain = Duktape_GetChain(ctx); + session->bufferSize = (duk_size_t)((int*)buffer)[0]; + session->stream = ILibDuktape_DuplexStream_Init(ctx, + ILibDuktape_HECI_Session_WriteSink, ILibDuktape_HECI_Session_EndSink, + ILibDuktape_HECI_Session_PauseSink, ILibDuktape_HECI_Session_ResumeSink, session); + ILibDuktape_CreateReadonlyProperty_int(ctx, "maxBufferSize", (int)session->bufferSize); + session->PendingWrites = ILibQueue_Create(); + duk_push_current_function(ctx); + session->noPipelining = Duktape_GetIntPropertyValue(ctx, -1, ILibDuktape_HECI_Session_NoPipeline, 0); + duk_pop(ctx); + +#ifdef _POSIX + //printf("Session: %p\n", session); + duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Child); // [session][heci] + duk_get_prop_string(ctx, -1, ILibDuktape_HECI_ChainLink); // [session][heci][link] + HECI_chainLink *link = (HECI_chainLink*)duk_get_pointer(ctx, -1); + link->session = session; + duk_pop_2(ctx); // [session] +#endif + + //printf("NoPipeline: %d\n", session->noPipelining); + } + else + { + // Even tho it was a success, the result buffer is invalid + duk_get_prop_string(ctx, -1, "emit"); // [session][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "error"); // [emit][this][error] + duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error"); // [emit][this][error][err] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); } + duk_pop(ctx); // ... + } + } + + if (session != NULL) + { + // Hookup the Send/Receive logic +#ifdef WIN32 + duk_push_this(ctx); // [HECI] + session->descriptor = (HANDLE)Duktape_GetPointerProperty(ctx, -1, ILibDuktape_HECI_Descriptor); + duk_get_prop_string(ctx, -1, ILibDuktape_HECI_ChildProcess); // [HECI][childProcess] + duk_get_prop_string(ctx, -1, ILibDuktape_ChildProcess_Manager); // [HECI][childProcess][manager] + session->mgr = (ILibProcessPipe_Manager)duk_get_pointer(ctx, -1); + QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_Session_Start, ILibProcessPipe_Manager_GetWorkerThread(session->mgr), (ULONG_PTR)session); +#else + duk_push_this(ctx); // [HECI] + session->descriptor = Duktape_GetIntPropertyValue(ctx, -1, ILibDuktape_HECI_Descriptor, -1); + ILibForceUnBlockChain(session->chain); +#endif + + duk_dup(ctx, 2); + duk_get_prop_string(ctx, -1, "emit"); // [session][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "connect"); // [emit][this][connect] + if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onConnect(): "); } + duk_pop(ctx); // ... + + + } + return(0); +} +duk_ret_t ILibDuktape_HECI_Session_connect(duk_context *ctx) +{ + int i; + int nargs = duk_get_top(ctx); + duk_push_this(ctx); // [Session] + duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Child); // [Session][HECI] + duk_remove(ctx, -2); // [HECI] + + duk_get_prop_string(ctx, -1, "doIoctl"); // [HECI][func] + duk_swap_top(ctx, -2); // [doIoctl][this] + duk_get_prop_string(ctx, -1, "IOCTL"); // [doIoctl][this][IOCTL] + duk_get_prop_string(ctx, -1, "CLIENT_CONNECT"); // [doIoctl][this][IOCTL][CLIENT_CONNECT] + duk_remove(ctx, -2); // [doIoctl][this][CLIENT_CONNECT] + duk_dup(ctx, 0); // [doIoctl][this][CLIENT_CONNECT][guid] + duk_push_fixed_buffer(ctx, 16); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer] + duk_push_c_function(ctx, ILibDuktape_HECI_create_OnClientConnect, DUK_VARARGS); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer][callback] + duk_push_int(ctx, 0); + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Session_NoPipeline); + + for (i = 1; i < nargs; ++i) + { + if (duk_is_function(ctx, i)) { ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx), "connect", duk_require_heapptr(ctx, i)); } + else if (duk_is_object(ctx, i)) + { + int noPipeline = Duktape_GetIntPropertyValue(ctx, i, "noPipeline", 0); + duk_push_int(ctx, noPipeline); + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Session_NoPipeline); + } + } + duk_push_this(ctx); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer][callback][Session] + duk_call_method(ctx, 5); // [retVal] + duk_pop(ctx); // ... + return(0); +} +duk_ret_t ILibDuktape_HECI_create(duk_context *ctx) +{ + duk_push_object(ctx); // [Session] + ILibDuktape_HECI_Push(ctx, NULL); // [Session][HECI] + duk_dup(ctx, -2); // [Session][HECI][Session] + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Parent); // [Session][HECI] + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Child); // [Session] + + ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "error"); + ILibDuktape_CreateProperty_InstanceMethod(ctx, "connect", ILibDuktape_HECI_Session_connect, DUK_VARARGS); + ILibDuktape_CreateFinalizer(ctx, ILibDuktape_HECI_SessionFinalizer); + return(1); +} + + +void ILibDuktape_HECI_IoctlHandler_Dispatch(void *chain, void *user) +{ + ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)user; + duk_size_t count; + int i; + duk_context *ctx = data->ctx; + + duk_push_heapptr(data->ctx, data->data); // [array] + duk_push_heapptr(data->ctx, data->heciObject); // [array][heci] + duk_get_prop_index(data->ctx, -2, 2); // [array][heci][callback] + duk_swap_top(data->ctx, -2); // [array][callback][this] + count = duk_get_length(data->ctx, -3); + duk_push_int(data->ctx, data->code); // [array][callback][this][status] + duk_get_prop_index(data->ctx, -4, 1); // [array][callback][this][status][buffer] + + for (i = 3; i < (int)count; ++i) + { + duk_get_prop_index(data->ctx, -i - 2, i); // [array][callback][this][status][buffer][...args...] + } + if (duk_pcall_method(data->ctx, (duk_idx_t)count - 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(data->ctx, "heci.ioctlHandler_Dispatch.callback(): "); } + duk_pop_2(data->ctx); // ... + + duk_push_heapptr(data->ctx, data->heciObject); // [heci] + ILibDuktape_Push_ObjectStash(data->ctx); // [heci][stash] + duk_del_prop_string(data->ctx, -1, Duktape_GetStashKey(data->data)); // (This will free data internally) + duk_pop_2(ctx); // ... +} +#ifdef WIN32 +void ILibDuktape_HECI_NextIoctl(ILibQueue q); +BOOL ILibDuktape_HECI_IoctlHandler(HANDLE h, void *user) +{ + ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)user; + ILibQueue Q = data->Q; + BOOL result = GetOverlappedResult(data->device, &(data->v), &(data->bytesReceived), FALSE); + ILibQueue_DeQueue(data->Q); + + ILibProcessPipe_WaitHandle_Remove(data->pipeManager, h); + data->code = result == TRUE ? 0 : (int)GetLastError(); + ILibChain_RunOnMicrostackThread(data->chain, ILibDuktape_HECI_IoctlHandler_Dispatch, data); + + if (ILibQueue_GetCount(Q) > 0) + { + void ILibDuktape_HECI_NextIoctl(Q); + } + return(TRUE); +} +void ILibDuktape_HECI_NextIoctl(ILibQueue q) +{ + ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)ILibQueue_PeekQueue(q); + int res; + data->bytesReceived = 0; + + ResetEvent(data->v.hEvent); + res = DeviceIoControl(data->device, (DWORD)data->code, data->buffer, (DWORD)data->bufferLen, data->outBuffer, (DWORD)data->outBufferLen, &(data->bytesReceived), &(data->v)); + ILibProcessPipe_WaitHandle_Add(data->pipeManager, data->v.hEvent, data, ILibDuktape_HECI_IoctlHandler); + +} +void __stdcall ILibDuktape_HECI_apc_AddIoctl(ULONG_PTR obj) +{ + ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)obj; + ILibQueue_EnQueue(data->Q, data); + if (ILibQueue_GetCount(data->Q) == 1) + { + ILibDuktape_HECI_NextIoctl(data->Q); + } +} +#endif +#ifdef _POSIX +void ILibDuktape_HECI_AddIoctl(ILibDuktape_HECI_ioctl_data *data) +{ + ILibQueue_EnQueue(data->Q, data); + if (ILibQueue_GetCount(data->Q) == 1) + { + ILibForceUnBlockChain(data->chain); + } +} +#endif + +duk_ret_t ILibDuktape_HECI_doIoctl(duk_context *ctx) +{ + int code = duk_require_int(ctx, 0); + duk_size_t bufferLen = 0; + char *buffer = duk_is_null(ctx, 1) ? NULL : (char*)Duktape_GetBuffer(ctx, 1, &bufferLen); + int nargs = duk_get_top(ctx); + int i; + ILibQueue Q; + duk_size_t outBufferLen; + char *outBuffer; + int cbx; + + if (duk_is_buffer(ctx, 2) || duk_is_buffer_data(ctx, 2)) + { + outBuffer = (char*)Duktape_GetBuffer(ctx, 2, &outBufferLen); + cbx = 3; + } + else + { + outBuffer = NULL; + outBufferLen = 0; + cbx = 2; + } + +#ifdef _POSIX + if (outBuffer == NULL) + { + outBuffer = buffer; + outBufferLen = bufferLen; + } + else + { + if (bufferLen < outBufferLen) { return(ILibDuktape_Error(ctx, "HECI.doIoctl(): Output Buffer too small")); } + memcpy_s(outBuffer, outBufferLen, buffer, bufferLen); + } +#endif + + duk_require_function(ctx, cbx); + duk_push_this(ctx); // [heci] + duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Q); // [heci][q] + Q = (ILibQueue)duk_get_pointer(ctx, -1); + duk_pop(ctx); // [heci] + + ILibDuktape_Push_ObjectStash(ctx); // [heci][stash] + duk_push_array(ctx); // [heci][stash][array] + ILibDuktape_HECI_ioctl_data *data; + duk_push_fixed_buffer(ctx, bufferLen + sizeof(ILibDuktape_HECI_ioctl_data)); // [heci][stash][array][state] + data = (ILibDuktape_HECI_ioctl_data*)Duktape_GetBuffer(ctx, -1, NULL); + memset(data, 0, sizeof(ILibDuktape_HECI_ioctl_data)); + duk_put_prop_index(ctx, -2, 0); // [heci][stash][array] + if (outBufferLen > 0) + { // [heci][stash][array][buffer] + duk_dup(ctx, 2); + } + else + { + duk_push_null(ctx); // [heci][stash][array][buffer] + } + + duk_put_prop_index(ctx, -2, 1); // [heci][stash][array] + data->ctx = ctx; + + duk_dup(ctx, cbx); // [heci][stash][array][callback] + duk_put_prop_index(ctx, -2, 2); // [heci][stash][array] + +#ifdef WIN32 + duk_get_prop_string(ctx, -3, ILibDuktape_HECI_IoctlWaitHandle); // [heci][stash][array][handle] + data->v.hEvent = (HANDLE)duk_get_pointer(ctx, -1); + duk_pop(ctx); // [heci][stash][array] +#endif + + duk_get_prop_string(ctx, -3, ILibDuktape_HECI_Descriptor); // [heci][stash][array][descriptor] +#ifdef WIN32 + data->device = (HANDLE)duk_get_pointer(ctx, -1); +#elif defined(_POSIX) + data->device = duk_get_int(ctx, -1); +#endif + duk_pop(ctx); // [heci][stash][array] + data->chain = Duktape_GetChain(ctx); + data->Q = Q; + data->code = code; + data->outBuffer = outBuffer; + data->outBufferLen = outBufferLen; + data->heciObject = duk_get_heapptr(ctx, -3); + data->bufferLen = bufferLen; + data->data = duk_get_heapptr(ctx, -1); + memcpy_s(data->buffer, bufferLen, buffer, bufferLen); + + for (i = cbx + 1; i < nargs; ++i) + { + duk_dup(ctx, i); // [heci][stash][array][object] + duk_put_prop_index(ctx, -2, i-1); // [heci][stash][array] + } + duk_put_prop_string(ctx, -2, Duktape_GetStashKey(duk_get_heapptr(ctx, -1))); // [heci][stash] + + +#ifdef WIN32 + duk_get_prop_string(ctx, -2, ILibDuktape_HECI_ChildProcess); // [heci][stash][childProcess] + duk_get_prop_string(ctx, -1, ILibDuktape_ChildProcess_Manager); // [heci][stash][childProcess][manager] + data->pipeManager = (ILibProcessPipe_Manager)duk_get_pointer(ctx, -1); + QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_apc_AddIoctl, ILibProcessPipe_Manager_GetWorkerThread(data->pipeManager), (ULONG_PTR)data); +#elif defined(_POSIX) + ILibDuktape_HECI_AddIoctl(data); +#endif + + return(0); +} +duk_ret_t ILibDuktape_HECI_Finalizer(duk_context *ctx) +{ +#ifdef WIN32 + HANDLE h = Duktape_GetPointerProperty(ctx, 0, ILibDuktape_HECI_IoctlWaitHandle); + if (h != NULL) { CloseHandle(h); } +#endif + + if (duk_has_prop_string(ctx, 0, ILibDuktape_HECI_Q)) + { + duk_get_prop_string(ctx, 0, ILibDuktape_HECI_Q); + ILibQueue_Destroy((ILibQueue)duk_get_pointer(ctx, -1)); + } + +#ifdef _POSIX + if (duk_has_prop_string(ctx, 0, ILibDuktape_HECI_ChainLink)) + { + duk_get_prop_string(ctx, 0, ILibDuktape_HECI_ChainLink); + HECI_chainLink *h = (HECI_chainLink*)duk_get_pointer(ctx, -1); + h->ctx = NULL; + h->heciObject = NULL; + ILibChain_SafeRemove(h->link.ParentChain, h); + } +#endif + + return(0); +} +#ifndef WIN32 +void ILibDuktape_HECI_PreSelect(void* object, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime) +{ + + int result; + HECI_chainLink *h = (HECI_chainLink*)object; + //printf("h = %p, descriptor = %d, paused = %d, session = %p\n", (void*)h, h->descriptor, h->paused, (void*)h->session); + + if (h->descriptor <= 0) { return; } + if (h->paused == 0 && h->session != NULL) { FD_SET(h->descriptor, readset); } + if (h->session != NULL && ILibQueue_GetCount(h->session->PendingWrites) > 0) { FD_SET(h->descriptor, writeset); } + + while (ILibQueue_GetCount(h->Q) > 0 && h->paused == 0) + { + ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)ILibQueue_DeQueue(h->Q); + switch (data->code) + { + case 0x00: + break; + case 0x01: + case 0x02: + case 0x03: + result = ioctl(h->descriptor, _IOC(_IOC_READ | _IOC_WRITE, 'H', data->code, data->outBufferLen), data->outBuffer); + data->code = result ? errno : 0; + ILibDuktape_HECI_IoctlHandler_Dispatch(NULL, data); + break; + default: + break; + } + } +} +void ILibDuktape_HECI_PostSelect(void* object, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset) +{ + HECI_chainLink *h = (HECI_chainLink*)object; + if (h->descriptor <= 0) { return; } + + if (FD_ISSET(h->descriptor, readset)) + { + //printf("session = %p\n", (void*)h->session); + //printf("Attempting to read: %d bytes from %p\n", h->session->bufferSize, (void*)h->session->buffer); + int bytesRead = read(h->descriptor, h->session->buffer, h->session->bufferSize); + if (bytesRead >= 0) + { + ILibDuktape_DuplexStream_WriteData(h->session->stream, h->session->buffer, bytesRead); + } + else + { + ILibDuktape_DuplexStream_WriteEnd(h->session->stream); + } + } + if (FD_ISSET(h->descriptor, writeset)) + { + printf("Writeset\n"); + } +} +void ILibDuktape_HECI_Destroy(void *object) +{ + HECI_chainLink *h = (HECI_chainLink*)object; + if (h->ctx != NULL && h->heciObject != NULL) + { + duk_push_heapptr(h->ctx, h->heciObject); // [heci] + duk_del_prop_string(h->ctx, -1, ILibDuktape_HECI_ChainLink); + duk_pop(h->ctx); // ... + } + close(h->descriptor); +} +#endif +void ILibDuktape_HECI_Push(duk_context *ctx, void *chain) +{ + duk_push_object(ctx); // [HECI] + ILibDuktape_CreateFinalizer(ctx, ILibDuktape_HECI_Finalizer); + +#ifdef WIN32 + HANDLE h = ILibDuktape_HECI_windowsInit(); + if (h == NULL) { duk_push_string(ctx, "error initializing HECI"); duk_throw(ctx); } + duk_push_pointer(ctx, h); // [HECI][HANDLE] + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Descriptor); // [HECI] + + if (duk_peval_string(ctx, "require('child_process');") != 0) // [HECI][child_process] + { + duk_push_string(ctx, "Error instantiating dependency 'child_process'"); duk_throw(ctx); return; + } + + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_ChildProcess); // [HECI] + duk_push_pointer(ctx, CreateEvent(NULL, TRUE, FALSE, NULL)); + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_IoctlWaitHandle); // [HECI] +#elif defined(_POSIX) + int h = ILibDuktape_HECI_linuxInit(); + if (h < 0) { duk_push_string(ctx, "error initializing HECI"); duk_throw(ctx); } + duk_push_int(ctx, h); // [HECI][descriptor] + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Descriptor); // [HECI] + + HECI_chainLink *hlink = ILibMemory_Allocate(sizeof(HECI_chainLink), 0, NULL, NULL); + hlink->ctx = ctx; + hlink->descriptor = h; + hlink->link.PreSelectHandler = ILibDuktape_HECI_PreSelect; + hlink->link.PostSelectHandler = ILibDuktape_HECI_PostSelect; + hlink->link.DestroyHandler = ILibDuktape_HECI_Destroy; + hlink->Q = ILibQueue_Create(); + duk_push_pointer(ctx, hlink); // [HECI][link] + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_ChainLink); // [HECI] + + ILibChain_SafeAdd(Duktape_GetChain(ctx), hlink); +#endif + if (chain != NULL) { ILibDuktape_CreateInstanceMethod(ctx, "create", ILibDuktape_HECI_create, 0); } + ILibDuktape_CreateInstanceMethod(ctx, "doIoctl", ILibDuktape_HECI_doIoctl, DUK_VARARGS); +#ifdef _POSIX + duk_push_pointer(ctx, hlink->Q); // [HECI][Q] +#else + duk_push_pointer(ctx, ILibQueue_Create()); // [HECI][Q] +#endif + duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Q); // [HECI] + duk_push_object(ctx); +#ifdef WIN32 + ILibDuktape_CreateReadonlyProperty_int(ctx, "HECI_VERSION", (int)(CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS))); + ILibDuktape_CreateReadonlyProperty_int(ctx, "CLIENT_CONNECT", (int)(CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS))); +#elif defined(_POSIX) + ILibDuktape_CreateReadonlyProperty_int(ctx, "HECI_VERSION", (int)0x00); + ILibDuktape_CreateReadonlyProperty_int(ctx, "CLIENT_CONNECT", (int)0x01); +#endif + + ILibDuktape_CreateReadonlyProperty(ctx, "IOCTL"); + duk_push_object(ctx); + duk_peval_string(ctx, "Buffer.from('DBA4336776047B4EB3AFBCFC29BEE7A7', 'hex');"); + ILibDuktape_CreateReadonlyProperty(ctx, "LME"); + duk_peval_string(ctx, "Buffer.from('2800F812B7B42D4BACA846E0FF65814C', 'hex');"); + ILibDuktape_CreateReadonlyProperty(ctx, "AMT"); + ILibDuktape_CreateReadonlyProperty(ctx, "GUIDS"); + +} // KLOCKWORK: We are not losing reference to created Event... It is freed in the object finalizer 'ILibDuktape_HECI_Finalizer' +void ILibDuktape_HECI_Init(duk_context *ctx) +{ + ILibDuktape_ModSearch_AddHandler(ctx, "heci", ILibDuktape_HECI_Push); +} \ No newline at end of file diff --git a/microscript/ILibDuktape_HECI.h b/microscript/ILibDuktape_HECI.h new file mode 100644 index 0000000..fa75ba6 --- /dev/null +++ b/microscript/ILibDuktape_HECI.h @@ -0,0 +1,9 @@ +#ifndef __DUKTAPEHECI__ +#define __DUKTAPEHECI__ + +#include "duktape.h" + +void ILibDuktape_HECI_Init(duk_context *ctx); + + +#endif \ No newline at end of file diff --git a/microscript/ILibDuktape_Helpers.c b/microscript/ILibDuktape_Helpers.c index be69b3d..11066ba 100644 --- a/microscript/ILibDuktape_Helpers.c +++ b/microscript/ILibDuktape_Helpers.c @@ -245,6 +245,15 @@ void ILibDuktape_CreateEventWithGetter(duk_context *ctx, char *propName, duk_c_f duk_push_c_function(ctx, getterMethod, 1); // [obj][prop][getFunc] duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_GETTER); // [obj] } +void ILibDuktape_CreateEventWithGetterAndCustomProperty(duk_context *ctx, char *customPropName, char *propName, duk_c_function getterMethod) +{ + duk_push_string(ctx, propName); // [obj][customProp][prop] + duk_push_c_function(ctx, getterMethod, 1); // [obj][customProp][prop][getFunc] + duk_dup(ctx, -3); // [obj][customProp][prop][getFunc][customProp] + duk_put_prop_string(ctx, -2, customPropName); // [obj][customProp][prop][getFunc] + duk_remove(ctx, -3); // [obj][prop][getFunc] + duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_GETTER); // [obj] +} void ILibDuktape_CreateEventWithGetterAndSetterEx(duk_context *ctx, char *propName, duk_c_function getterMethod, duk_c_function setterMethod) { @@ -597,3 +606,127 @@ void *ILibDuktape_Memory_Alloc(duk_context *ctx, duk_size_t size) return(retVal); } +void *ILibDuktape_Memory_AllocEx(duk_context *ctx, duk_idx_t index, duk_size_t size) +{ + char *retVal = NULL; + + duk_dup(ctx, index); // [object] + ILibDuktape_Push_ObjectStash(ctx); // [object][stash] + duk_push_fixed_buffer(ctx, size); // [object][stash][buffer] + retVal = (char*)Duktape_GetBuffer(ctx, -1, NULL); + duk_put_prop_string(ctx, -2, Duktape_GetStashKey(retVal)); // [object][stash] + duk_pop_2(ctx); // ... + return(retVal); +} +void ILibDuktape_ValidatePointer(void *chain, void *ptr) +{ + ILibHashtable_Put(ILibChain_GetBaseHashtable(chain), ptr, NULL, 0, (void*)0xFFFF); +} +void ILibDuktape_InValidatePointer(void *chain, void *ptr) +{ + ILibHashtable_Remove(ILibChain_GetBaseHashtable(chain), ptr, NULL, 0); +} +int ILibDuktape_IsPointerValid(void *chain, void *ptr) +{ + return(ILibHashtable_Get(ILibChain_GetBaseHashtable(chain), ptr, NULL, 0) == NULL ? 0 : 1); +} +void ILibDuktape_PointerValidation_Finalizer(duk_context *ctx, void *obj) +{ + void *chain = Duktape_GetChain(ctx); + ILibDuktape_InValidatePointer(chain, obj); +} +void ILibDuktape_PointerValidation_Init(duk_context *ctx) +{ + void *chain = Duktape_GetChain(ctx); + if (!ILibDuktape_IsPointerValid(chain, duk_get_heapptr(ctx, -1))) + { + // Not set up yet, so set it up + ILibDuktape_ValidatePointer(chain, duk_get_heapptr(ctx, -1)); + ILibDuktape_CreateIndependentFinalizer(ctx, ILibDuktape_PointerValidation_Finalizer); + } +} +duk_ret_t ILibDuktape_Immediate_Sink(duk_context *ctx) +{ + ILibDuktape_ImmediateHandler userCallback = (ILibDuktape_ImmediateHandler)duk_get_pointer(ctx, 0); + void **args = NULL; + int argsLen, i; + + duk_push_this(ctx); // [immediate] + duk_dup(ctx, 1); // [immediate][array] + if ((argsLen = (int)duk_get_length(ctx, -1)) > 0) + { + args = ILibMemory_AllocateA(sizeof(void*)*argsLen); + for (i = 0; i < argsLen; ++i) + { + duk_get_prop_index(ctx, -1, i); // [immediate][array][arg] + args[i] = duk_get_pointer(ctx, -1); + duk_pop(ctx); // [immediate][array] + } + } + + duk_push_heap_stash(ctx); // [immediate][array][stash] + duk_del_prop_string(ctx, -1, Duktape_GetStashKey(duk_get_heapptr(ctx, -3))); + + if (userCallback != NULL) { userCallback(ctx, args, argsLen); } + return(0); +} +void ILibDuktape_Immediate(duk_context *ctx, void ** args, int argsLen, ILibDuktape_ImmediateHandler callback) +{ + int i = 0; + duk_push_global_object(ctx); // [g] + duk_get_prop_string(ctx, -1, "setImmediate"); // [g][setImmediate] + duk_swap_top(ctx, -2); // [setImmediate][this] + duk_push_c_function(ctx, ILibDuktape_Immediate_Sink, DUK_VARARGS); // [setImmediate][this][func] + duk_push_pointer(ctx, callback); // [setImmediate][this][func][userFunc] + duk_push_array(ctx); // [setImmediate][this][func][userFunc][array] + + while (args[i] != NULL && i < argsLen) + { + duk_get_prop_string(ctx, -1, "push"); // [setImmediate][this][func][userFunc][array][push] + duk_dup(ctx, -2); // [setImmediate][this][func][userFunc][array][push][this] + duk_push_pointer(ctx, args[i]); // [setImmediate][this][func][userFunc][array][push][this][val] + if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "ILibDuktape_Immediate => Array.push(): "); } + duk_pop(ctx); // [setImmediate][this][func][userFunc][array] + ++i; + } + + if (duk_pcall_method(ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "ILibDuktape_Immediate => immediate(): "); duk_pop(ctx); return; } + + // [immediate] + duk_push_heap_stash(ctx); // [immediate][stash] + duk_swap_top(ctx, -2); // [stash][immediate] + duk_put_prop_string(ctx, -2, Duktape_GetStashKey(duk_get_heapptr(ctx, -1)));// [stash] + duk_pop(ctx); // ... +} +void ILibDuktape_CreateInstanceMethodWithProperties(duk_context *ctx, char *funcName, duk_c_function funcImpl, duk_idx_t numArgs, unsigned int propertyCount, ...) +{ + unsigned int i; + char *name; + duk_idx_t valueIndex; + + duk_push_c_function(ctx, funcImpl, numArgs); // [func] + + va_list vlist; + va_start(vlist, propertyCount); + for (i = 0; i < propertyCount; ++i) + { + name = va_arg(vlist, char*); + valueIndex = va_arg(vlist, duk_idx_t); + + duk_dup(ctx, valueIndex); // [func][value] + duk_put_prop_string(ctx, -2, name); // [func] + } + va_end(vlist); + + while (propertyCount-- > 0) { duk_remove(ctx, -2); } + + duk_put_prop_string(ctx, -2, funcName); +} +duk_idx_t duk_push_int_ex(duk_context *ctx, duk_int_t val) +{ + return(duk_push_int(ctx, val), duk_get_top_index(ctx)); +} + + + + diff --git a/microscript/ILibDuktape_Helpers.h b/microscript/ILibDuktape_Helpers.h index 070c941..28c17ac 100644 --- a/microscript/ILibDuktape_Helpers.h +++ b/microscript/ILibDuktape_Helpers.h @@ -32,6 +32,10 @@ typedef void(*ILibDuktape_HelperEvent)(duk_context *ctx, void *user); #define ILibDuktape_MeshAgent_Cert_Server "\xFF_selftlscert" #define CONTEXT_GUID_BUFFER "_CONTEXT_GUID" #define ILibDuktape_Context_Chain "\xFF_chainptr" +#define ILibDuktape_OBJID "_ObjectID" + +#define ILibDuktape_CR2HTTP "\xFF_CR2HTTP" +#define ILibDuktape_CR2Options "\xFF_CR2Options" char* Duktape_GetContextGuidHex(duk_context *ctx); void *Duktape_GetChain(duk_context *ctx); @@ -62,6 +66,7 @@ int ILibDuktape_Process_GetExitCode(duk_context *ctx); void ILibDuktape_CreateEventWithGetter(duk_context *ctx, char *propName, duk_c_function getterMethod); void ILibDuktape_CreateEventWithGetterEx(duk_context *ctx, char *propName, void *heapptr); +void ILibDuktape_CreateEventWithGetterAndCustomProperty(duk_context *ctx, char *customPropName, char *propName, duk_c_function getterMethod); void ILibDuktape_CreateEventWithSetter(duk_context *ctx, char *propName, char *propNamePtr, void **hptr); void ILibDuktape_CreateEventWithSetterEx(duk_context *ctx, char *propName, duk_c_function setterMethod); void ILibDuktape_CreateEventWithGetterAndSetter(duk_context *ctx, char *propName, char *propNamePtr, void **hptr, duk_c_function getterMethod); @@ -74,6 +79,9 @@ void ILibDuktape_CreateEventWithGetterAndSetterWithMetaData(duk_context *ctx, ch #define ILibDuktape_CreateInstanceMethodWithBooleanProperty(context, propName, propValue, methodName, funcImpl, numArgs) duk_push_c_function(context, funcImpl, numArgs);duk_push_boolean(context, propValue);duk_put_prop_string(ctx, -2, propName);duk_put_prop_string(ctx, -2, methodName); #define ILibDuktape_CreateInstanceMethodWithIntProperty(context, propName, propValue, methodName, funcImpl, numArgs) duk_push_c_function(context, funcImpl, numArgs);duk_push_int(context, propValue);duk_put_prop_string(ctx, -2, propName);duk_put_prop_string(ctx, -2, methodName); #define ILibDuktape_CreateInstanceMethodWithNumberProperty(context, propName, propValue, methodName, funcImpl, numArgs) duk_push_c_function(context, funcImpl, numArgs);duk_push_number(context, (propValue));duk_put_prop_string(ctx, -2, propName);duk_put_prop_string(ctx, -2, methodName); +void ILibDuktape_CreateInstanceMethodWithProperties(duk_context *ctx, char *funcName, duk_c_function funcImpl, duk_idx_t numArgs, unsigned int propertyCount, ...); +duk_idx_t duk_push_int_ex(duk_context *ctx, duk_int_t val); + void ILibDuktape_CreateProperty_InstanceMethod(duk_context *ctx, char *methodName, duk_c_function impl, duk_idx_t argCount); void ILibDuktape_CreateProperty_InstanceMethodEx(duk_context *ctx, char *methodName, void *funcHeapPtr); void ILibDuktape_CreateReadonlyProperty(duk_context *ctx, char *propName); @@ -81,8 +89,21 @@ void ILibDuktape_CreateReadonlyProperty(duk_context *ctx, char *propName); #define ILibDuktape_CreateFinalizer(context, funcImpl) duk_push_c_function(context, funcImpl, 1); duk_set_finalizer(context, -2); void *ILibDuktape_Memory_Alloc(duk_context *ctx, duk_size_t size); +void *ILibDuktape_Memory_AllocEx(duk_context *ctx, duk_idx_t index, duk_size_t size); void ILibDuktape_Helper_AddHeapFinalizer(duk_context *ctx, ILibDuktape_HelperEvent handler, void *user); void ILibDuktape_Push_ObjectStash(duk_context *ctx); +void ILibDuktape_PointerValidation_Init(duk_context *ctx); +void ILibDuktape_ValidatePointer(void *chain, void *ptr); +void ILibDuktape_InValidatePointer(void *chain, void *ptr); +int ILibDuktape_IsPointerValid(void *chain, void *ptr); +#define ILibDuktape_ValidateHeapPointer(ctx, objIdx) ILibDuktape_ValidatePointer(Duktape_GetChain(ctx), duk_get_heapptr(ctx, objIdx)) +#define ILibDuktape_InValidateHeapPointer(ctx, objIdx) ILibDuktape_InValidatePointer(Duktape_GetChain(ctx), duk_get_heapptr(ctx, objIdx)) + +typedef void(*ILibDuktape_ImmediateHandler)(duk_context *ctx, void ** args, int argsLen); +void ILibDuktape_Immediate(duk_context *ctx, void ** args, int argsLen, ILibDuktape_ImmediateHandler callback); + +#define ILibDuktape_WriteID(ctx, id) duk_push_string(ctx, id);duk_put_prop_string(ctx, -2, ILibDuktape_OBJID) + #endif \ No newline at end of file diff --git a/microscript/ILibDuktape_HttpStream.c b/microscript/ILibDuktape_HttpStream.c new file mode 100644 index 0000000..c648bd1 --- /dev/null +++ b/microscript/ILibDuktape_HttpStream.c @@ -0,0 +1,3614 @@ +#ifdef WIN32 +#include +#include + +#include +#include +#endif + +#include "../microstack/ILibParsers.h" + +#include "duktape.h" +#include "ILibDuktape_Helpers.h" +#include "ILibDuktapeModSearch.h" +#include "ILibDuktape_EventEmitter.h" +#include "ILibDuktape_DuplexStream.h" +#include "ILibDuktape_net.h" + +#include "../microstack/ILibWebClient.h" +#include "../microstack/ILibRemoteLogging.h" + +struct ILibWebClientDataObject; +extern int ILibWebServer_WebSocket_CreateHeader(char* header, unsigned short FLAGS, unsigned short OPCODE, int payloadLength); +extern void ILibWebClient_ResetWCDO(struct ILibWebClientDataObject *wcdo); + +#define ILibDuktape_Agent_SocketJustCreated "\xFF_Agent_SocketJustCreated" +#define ILibDuktape_ClientRequest "\xFF_CR" +#define ILibDuktape_CR_EndCalled "\xFF_CR_EndCalled" +#define ILibDuktape_CR_RequestBuffer "\xFF_CR_RequestBuffer" +#define ILibDuktape_CR2Agent "\xFF_CR2Agent" +#define ILibDuktape_CR2HTTPStream "\xFF_CR2HTTPStream" +#define ILibDuktape_CR2TLSStream "\xFF_CR2TLSStream" +#define ILibDuktape_CR2WS "\xFF_ClientRequest2WS" +#define ILibDuktape_FUNC "\xFF_FUNC" +#define IILibDuktape_HTTP_HoldingQueue "\xFF_HoldingQueue" +#define ILibDuktape_Http_Server_FixedBuffer "\xFF_Http_Server_FixedBuffer" +#define ILibDuktape_Http_Server2NetServer "\xFF_HttpServer2NetServer" +#define ILibduktape_HttpStream2HttpServer "\xFF_HttpStream_2_HttpServer" +#define ILibDuktape_HttpServer_TimeoutCB "\xFF_HttpServer_TimeoutCB" +#define ILibDuktape_HTTP2CR "\xFF_HTTP2ClientRequest" +#define ILibDuktape_HTTP2PipedReadable "\xFF_HTTP2PipedReadable" +#define ILibDuktape_HTTP2PipedWritable "\xFF_HTTP2PipedWritable" +#define ILibDuktape_HTTPStream2Data "\xFF_HTTPStream2Data" +#define ILibDuktape_HTTPStream2HTTP "\xFF_HTTPStream2HTTP" +#define ILibDuktape_IMSG2HttpStream "\xFF_IMSG2HttpStream" +#define ILibDuktape_IMSG2SR "\xFF_IMSG2ServerResponse" +#define ILibDuktape_NS2HttpServer "\xFF_Http_NetServer2HttpServer" +#define ILibDuktape_Options2ClientRequest "\xFF_Options2ClientRequest" +#define ILibDuktape_PipedReadable "\xFF_PipedReadable" +#define ILibDuktape_Socket2AgentStash "\xFF_Socket2AgentStash" +#define ILibDuktape_Socket2Agent "\xFF_Socket2Agent" +#define ILibDuktape_Socket2AgentKey "\xFF_Socket2AgentKey" +#define ILibDuktape_Socket2CR "\xFF_Socket2CR" +#define ILibDuktape_Socket2HttpServer "\xFF_Socket2HttpServer" +#define ILibDuktape_Socket2HttpStream "\xFF_Socket2HttpStream" +#define ILibDuktape_Socket2TLS "\xFF_Socket2TLS" +#define ILibDuktape_SR2HttpStream "\xFF_ServerResponse2HttpStream" +#define ILibDuktape_SR2ImplicitHeaders "\xFF_ServerResponse2ImplicitHeaders" +#define ILibDuktape_SR2State "\xFF_ServerResponse2State" +#define ILibDuktape_SRUSER "\xFF_SRUSER" +#define ILibDuktape_SR2WS "\xFF_Http_ServerResponse2WS" +#define ILibDuktape_TLS2CR "\xFF_TLS2CR" +#define ILibDuktape_WebSocket_Client ((void*)0x01) +#define ILibDuktape_WebSocket_Server ((void*)0x02) +#define ILibDuktape_WebSocket_StatePtr "\xFF_WebSocketState" +#define ILibDuktape_WS2CR "\xFF_WS2ClientRequest" +#define ILibDuktape_WSDEC2WS "\xFF_WSDEC2WS" + +extern void ILibWebServer_Digest_ParseAuthenticationHeader(void* table, char* value, int valueLen); + +typedef struct ILibDuktape_Http_ClientRequest_WriteData +{ + char *buffer; + int noMoreWrites; + int headersFinished; + int contentLengthSpecified; + size_t bufferWriteLen; + size_t bufferLen; +}ILibDuktape_Http_ClientRequest_WriteData; + +typedef struct ILibDuktape_HttpStream_Data +{ + ILibDuktape_DuplexStream *DS; + ILibDuktape_readableStream *bodyStream; + + int bodyStream_unshiftedBytes; + + void *DynamicBuffer; + int connectionCloseSpecified; + int contentLengthSpecified; + void *WCDO; + void *chain; + int ConnectMethod; +}ILibDuktape_HttpStream_Data; + +typedef struct ILibDuktape_HttpStream_ServerResponse_State +{ + duk_context *ctx; + void *chain; + void *writeStream; + void *serverResponse; + ILibDuktape_WritableStream *nativeWriteStream; + int implicitHeaderHandling; + int chunkSupported; + int contentLengthSpecified; +}ILibDuktape_HttpStream_ServerResponse_State; +typedef struct ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State +{ + void *ctx; + void *writeStream; + void *serverResponseObj; + int endBytes; + int chunk; + size_t bufferLen; + char buffer[]; +}ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State; + +typedef struct ILibDuktape_WebSocket_State +{ + void *chain; + int noMasking; + int WebSocketFragmentFlag; // WebSocketFragmentFlag + int WebSocketFragmentFlag_Write; // WebSocketFragmentFlag + int WebSocketDataFrameType; // WebSocketDataFrameType + char* WebSocketFragmentBuffer; // WebSocketFragmentBuffer + int WebSocketFragmentIndex; // WebSocketFragmentIndex; + int WebSocketFragmentBufferSize; // WebSocketFragmentBufferSize; + int WebSocketFragmentMaxBufferSize; // WebSocketFragmentMaxBufferSize; + char WebSocketCloseFrameSent; // WebSocketCloseFrameSent + void *ObjectPtr; // Used to emit Ping/Pong events + duk_context *ctx; // Used to emit Ping/Pong events + + ILibDuktape_DuplexStream *encodedStream; + ILibDuktape_DuplexStream *decodedStream; +}ILibDuktape_WebSocket_State; + +typedef struct ILibDuktape_Http_Server +{ + duk_context *ctx; + +}ILibDuktape_Http_Server; + +typedef struct ILibDuktape_http_ServerResponse +{ + duk_context *ctx; + ILibDuktape_WritableStream *ws; + +}ILibDuktape_http_ServerResponse; + +int ILibDuktape_Headers_IsChunkSupported(ILibHTTPPacket *header) +{ + if (header->VersionLength == 3 && strncmp(header->Version, "1.0", 3) == 0) + { + return(0); + } + else + { + return(1); + } +} +void ILibDuktape_serverResponse_resetHttpStream(duk_context *ctx, void *serverResponse) +{ + // Need to reset HttpStream + duk_push_heapptr(ctx, serverResponse); // [serverResponse] + duk_get_prop_string(ctx, -1, ILibDuktape_SR2HttpStream); // [serverResponse][httpStream] + duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2Data); // [serverResponse][httpStream][data] + ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)Duktape_GetBuffer(ctx, -1, NULL); + + ILibWebClient_FinishedResponse_Server(data->WCDO); + if (data->bodyStream != NULL) + { + ILibDuktape_readableStream_WriteEnd(data->bodyStream); + data->bodyStream = NULL; + } + + duk_pop_n(ctx, 3); // ... +} +void ILibDuktape_Digest_CalculateNonce(duk_context *ctx, void *heapptr, long long expiration, char *opaque, int opaqueLen, char* buffer) +{ + char temp[33]; + if (expiration == 0) + { + char tmp[8]; + util_hexToBuf(opaque, opaqueLen, tmp); + expiration = ((long long*)tmp)[0]; + } + memcpy_s(temp, sizeof(temp), &expiration, 8); + memcpy_s(temp + 8, sizeof(temp) - 8, &heapptr, sizeof(void*)); + + util_md5hex(temp, 8 + sizeof(void*), buffer); +} +duk_ret_t ILibDuktape_HttpStream_http_get(duk_context *ctx) +{ + int nargs = duk_get_top(ctx); + if (duk_is_string(ctx, 0)) + { + // First Param is a string + duk_push_this(ctx); // [http] + duk_get_prop_string(ctx, -1, "parseUri"); // [http][parseUri] + duk_swap_top(ctx, -2); // [parseUri][this] + duk_dup(ctx, 0); // [parseUri][this][uri] + duk_call_method(ctx, 1); // [uri] + duk_push_this(ctx); // [uri][http] + duk_get_prop_string(ctx, -1, "request"); // [uri][http][request] + duk_swap_top(ctx, -2); // [uri][request][this] + duk_push_object(ctx); // [uri][request][this][options] + duk_get_prop_string(ctx, -4, "protocol"); // [uri][request][this][options][protocol] + duk_put_prop_string(ctx, -2, "protocol"); // [uri][request][this][options] + duk_get_prop_string(ctx, -4, "host"); // [uri][request][this][options][host] + duk_put_prop_string(ctx, -2, "host"); // [uri][request][this][options] + duk_get_prop_string(ctx, -4, "port"); // [uri][request][this][options][port] + duk_put_prop_string(ctx, -2, "port"); // [uri][request][this][options] + duk_get_prop_string(ctx, -4, "path"); // [uri][request][this][options][path] + duk_put_prop_string(ctx, -2, "path"); // [uri][request][this][options] + duk_push_string(ctx, "GET"); // [uri][request][this][options][method] + duk_put_prop_string(ctx, -2, "method"); // [uri][request][this][options] + } + else if (duk_is_object(ctx, 0)) + { + duk_push_this(ctx); // [http] + duk_get_prop_string(ctx, -1, "request"); // [http][request] + duk_swap_top(ctx, -2); // [request][this] + duk_dup(ctx, 0); // [request][this][options] + duk_push_string(ctx, "GET"); // [request][this][options][method] + duk_put_prop_string(ctx, -2, "method"); // [request][this][options] + } + else + { + return(ILibDuktape_Error(ctx, "http.get(): invalid parameter type")); + } + + if (nargs > 1 && duk_is_function(ctx, 1)) + { + duk_dup(ctx, 1); // [request][this][options][callback] + duk_call_method(ctx, 2); // [retVal] + } + else + { // [request][this][options] + duk_call_method(ctx, 1); // [retVal] + } + + // [clientRequest] + duk_get_prop_string(ctx, -1, "end"); // [clientRequest][end] + duk_dup(ctx, -2); // [clientRequest][end][this] + duk_call_method(ctx, 0); duk_pop(ctx); // [clientRequest] + + return(1); +} +duk_ret_t ILibDuktape_HttpStream_http_checkIdentity(duk_context *ctx) +{ + int nargs = duk_get_top(ctx); + int i; + + duk_push_current_function(ctx); + duk_get_prop_string(ctx, -1, ILibDuktape_FUNC); // [func] + duk_get_prop_string(ctx, -2, ILibDuktape_ClientRequest); // [func][this] + for (i = 0; i < nargs; ++i) + { + duk_dup(ctx, i); // [func][this][...args] + } + duk_call_method(ctx, nargs); + return(1); +} + +void ILibDuktape_HttpStream_http_ConvertOptionToSend(duk_context *ctx, void *ObjectPtr, void *OptionsPtr) +{ + char *tmp, *buffer = NULL; + duk_size_t len; + size_t bufferLen = 0; + int i; + int expectSpecified = 0; + ILibDuktape_Http_ClientRequest_WriteData *data; + + duk_push_heapptr(ctx, ObjectPtr); // [stream] + duk_push_heapptr(ctx, OptionsPtr); // [stream][Options] + duk_get_prop_string(ctx, -1, ILibDuktape_Options2ClientRequest); // [stream][Options][CR] + duk_get_prop_string(ctx, -1, ILibDuktape_CR_RequestBuffer); // [stream][Options][CR][data] + data = (ILibDuktape_Http_ClientRequest_WriteData*)Duktape_GetBuffer(ctx, -1, NULL); + duk_pop_2(ctx); // [stream][Options] + + char *protocol = Duktape_GetStringPropertyValue(ctx, -1, "protocol", ""); + if (strcmp(protocol, "ws:") == 0 || strcmp(protocol, "wss:") == 0) + { + if (duk_has_prop_string(ctx, -1, "headers")) + { + duk_get_prop_string(ctx, -1, "headers"); // [stream][Options][headers] + } + else + { + duk_push_object(ctx); // [stream][Options][headers] + duk_dup(ctx, -1); // [stream][Options][headers][dup] + duk_put_prop_string(ctx, -3, "headers"); // [stream][Options][headers] + } + char nonce[16]; + char value[26]; + char *enc = value; + + util_random(16, nonce); + ILibBase64Encode((unsigned char*)nonce, 16, (unsigned char**)&enc); + + duk_push_string(ctx, "websocket"); + duk_put_prop_string(ctx, -2, "Upgrade"); + + duk_push_string(ctx, "Upgrade"); + duk_put_prop_string(ctx, -2, "Connection"); + + duk_push_string(ctx, enc); + duk_put_prop_string(ctx, -2, "Sec-WebSocket-Key"); + + duk_push_string(ctx, "13"); + duk_put_prop_string(ctx, -2, "Sec-WebSocket-Version"); + duk_pop(ctx); // [stream][options] + } + + for (i = 0; i < 2; ++i) + { + // measure how big a buffer we'll need + duk_get_prop_string(ctx, -1, "method"); // [stream][options][method] + tmp = (char*)duk_get_lstring(ctx, -1, &len); + if (buffer != NULL) { memcpy_s(buffer + bufferLen, ILibMemory_AllocateA_Size(buffer) - bufferLen, tmp, len); (buffer + bufferLen)[len] = ' '; } + bufferLen += (len + 1); // ('GET ') + duk_pop(ctx); // [stream][options] + + duk_get_prop_string(ctx, -1, "path"); // [stream][options][path] + tmp = (char*)duk_get_lstring(ctx, -1, &len); + if (buffer != NULL) + { + memcpy_s(buffer + bufferLen, ILibMemory_AllocateA_Size(buffer), tmp, len); + memcpy_s(buffer + bufferLen + len, ILibMemory_AllocateA_Size(buffer) - bufferLen - len, " HTTP/1.1\r\n", 11); + } + bufferLen += (len + 11); // ('/path HTTP/1.1\r\n') + duk_pop(ctx); // [stream][options] + + if (duk_has_prop_string(ctx, -1, "headers")) + { + duk_get_prop_string(ctx, -1, "headers"); // [stream][options][headers] + duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); // [stream][options][headers][enumerator] + while (duk_next(ctx, -1, 1)) + { + tmp = (char*)duk_get_lstring(ctx, -2, &len); + if (buffer != NULL) { memcpy_s(buffer + bufferLen, ILibMemory_AllocateA_Size(buffer) - bufferLen, tmp, len); (buffer + bufferLen)[len] = ':'; } + if (len == 6 && strncasecmp(tmp, "expect", 6) == 0) { expectSpecified = 1; } + if (len == 14 && strncasecmp(tmp, "content-length", 14) == 0) { data->contentLengthSpecified = 1; } + bufferLen += (len + 1); // ('key:') + tmp = (char*)duk_get_lstring(ctx, -1, &len); + if (buffer != NULL) { memcpy_s(buffer + bufferLen, ILibMemory_AllocateA_Size(buffer) - bufferLen, tmp, len); (buffer + bufferLen)[len] = '\r'; (buffer + bufferLen)[len + 1] = '\n'; } + bufferLen += (len + 2); // ('value\r\n') + duk_pop_2(ctx); // [stream][options][headers][enumerator] + } + duk_pop_2(ctx); // [stream][options] + } + if (expectSpecified) + { + if (buffer != NULL) { buffer[bufferLen] = '\r'; buffer[bufferLen + 1] = '\n'; } + bufferLen += 2; // (\r\n') + } + if (buffer == NULL) + { + buffer = ILibMemory_AllocateA(bufferLen); + bufferLen = 0; + } + } + + if (expectSpecified) + { + duk_get_prop_string(ctx, -1, ILibDuktape_Options2ClientRequest); // [stream][options][clientRequest] + duk_get_prop_string(ctx, -1, ILibDuktape_CR_RequestBuffer); // [stream][options][clientRequest][buffer] + ((ILibDuktape_Http_ClientRequest_WriteData*)Duktape_GetBuffer(ctx, -1, NULL))->headersFinished = 1; + duk_pop_2(ctx); // [stream][options] + } + + duk_push_external_buffer(ctx); // [stream][options][extBuffer] + duk_config_buffer(ctx, -1, buffer, bufferLen); // [stream][options][extBuffer] + duk_dup(ctx, -3); // [stream][options][extBuffer][stream] + duk_get_prop_string(ctx, -1, "write"); // [stream][options][extBuffer][stream][write] + duk_swap_top(ctx, -2); // [stream][options][extBuffer][write][this] + duk_push_buffer_object(ctx, -3, 0, bufferLen, DUK_BUFOBJ_NODEJS_BUFFER);// [stream][options][extBuffer][write][this][buffer] + + if (duk_pcall_method(ctx, 1) != 0) // [stream][options][extBuffer][retVal] + { + ILibDuktape_Error(ctx, "http.onConnect(): %s", duk_safe_to_string(ctx, -1)); + } + + duk_pop_n(ctx, 4); // ... +} +duk_ret_t ILibDuktape_HttpStream_http_OnTLSConnect(duk_context *ctx) +{ + duk_push_this(ctx); // [TLS] + + // ClientRequest Options => DecryptedTransform + duk_get_prop_string(ctx, -1, ILibDuktape_TLS2CR); // [TLS][ClientRequest] + duk_get_prop_string(ctx, -2, "clear"); // [TLS][ClientRequest][DecryptedTransform] + duk_get_prop_string(ctx, -2, ILibDuktape_CR2Options); // [TLS][ClientRequest][DecryptedTransform][Options] + + ILibDuktape_HttpStream_http_ConvertOptionToSend(ctx, duk_get_heapptr(ctx, -2), duk_get_heapptr(ctx, -1)); + + // ClientRequest => DecryptedTransform + duk_pop(ctx); // [TLS][ClientRequest][DecryptedTransform] + duk_get_prop_string(ctx, -2, "pipe"); // [TLS][ClientRequest][DecryptedTransform][pipe] + duk_dup(ctx, -3); // [TLS][ClientRequest][DecryptedTransform][pipe][this] + duk_dup(ctx, -3); // [TLS][ClientRequest][DecryptedTransform][pipe][this][DecryptedTransform] + duk_push_object(ctx); // [TLS][ClientRequest][DecryptedTransform][pipe][this][DecryptedTransform][options] + duk_push_false(ctx); duk_put_prop_string(ctx, -2, "end"); + if (duk_pcall_method(ctx, 2) != 0) { return(ILibDuktape_Error(ctx, "http.onTlsConnect() => Error calling pipe on Transform Stream: %s", duk_safe_to_string(ctx, -1))); } + duk_pop_2(ctx); // [TLS][ClientRequest] + + // TLS EncryptedTransform => HTTP Stream + duk_get_prop_string(ctx, -2, "encrypted"); // [TLS][ClientRequest][EncryptedTransform] + duk_get_prop_string(ctx, -1, "pipe"); // [TLS][ClientRequest][EncryptedTransform][pipe] + duk_swap_top(ctx, -2); // [TLS][ClientRequest][pipe][this] + duk_get_prop_string(ctx, -3, ILibDuktape_CR2HTTPStream);// [TLS][ClientRequest][pipe][this][http] + if (duk_pcall_method(ctx, 1) != 0) { return(ILibDuktape_Error(ctx, "http.onTlsConnect() => Error calling pipe: %s", duk_safe_to_string(ctx, -1))); } + duk_pop(ctx); // [TLS][ClientRequest] + + duk_get_prop_string(ctx, -1, ILibDuktape_CR2HTTPStream);// [TLS][ClientRequest][http] + duk_get_prop_string(ctx, -1, "pipe"); // [TLS][ClientRequest][http][pipe] + duk_swap_top(ctx, -2); // [TLS][ClientRequest][pipe][this] + duk_get_prop_string(ctx, -4, "clear"); // [TLS][ClientRequest][http][this][decryptedTransform] + if (duk_pcall_method(ctx, 1) != 0) { return(ILibDuktape_Error(ctx, "http.onTlsConnect() => Error calling pipe: %s", duk_safe_to_string(ctx, -1))); } + + return(0); +} +duk_ret_t ILibDuktape_HttpStream_http_onUpgrade(duk_context *ctx) +{ + char *decodedKey; + duk_size_t decodedKeyLen; + char *key; + duk_size_t keyLen; + + duk_get_prop_string(ctx, 0, "headers"); // [headers] + duk_get_prop_string(ctx, -1, "Sec-WebSocket-Accept"); // [headers][key] + key = (char*)Duktape_GetBuffer(ctx, -1, &keyLen); + + decodedKey = ILibMemory_AllocateA(keyLen); + decodedKeyLen = ILibBase64Decode((unsigned char*)key, (int)keyLen, (unsigned char**)&decodedKey); + + // We were upgraded to WebSocket, so we need to create a WebSocket Stream, detach the HTTPStream, and emit the event + // Upstream Readable => X => HttpStream + duk_push_this(ctx); // [HTTPStream] + duk_get_prop_string(ctx, -1, ILibDuktape_HTTP2PipedReadable); // [HTTPStream][readable] + duk_get_prop_string(ctx, -1, "unpipe"); // [HTTPStream][readable][unpipe] + duk_dup(ctx, -2); // [HTTPStream][readable][unpipe][this] + duk_call_method(ctx, 0); // [HTTPStream][readable][...] + duk_pop(ctx); // [HTTPStream][readable] + + duk_push_external_buffer(ctx); // [HTTPStream][readable][ext] + duk_config_buffer(ctx, -1, decodedKey, decodedKeyLen); + + duk_eval_string(ctx, "require('http');"); // [HTTPStream][readable][ext][HTTP] + duk_get_prop_string(ctx, -1, "webSocketStream"); // [HTTPStream][readable][ext][HTTP][wss] + duk_remove(ctx, -2); // [HTTPStream][readable][ext][wss] + duk_push_buffer_object(ctx, -2, 0, decodedKeyLen, DUK_BUFOBJ_NODEJS_BUFFER);// [HTTPStream][readable][ext][wss][buffer] + duk_new(ctx, 1); // [HTTPStream][readable][ext][websocket] + duk_remove(ctx, -2); // [HTTPStream][readable][websocket] + + duk_get_prop_string(ctx, -3, ILibDuktape_HTTP2CR); // [HTTPStream][readable][websocket][clientRequest] + duk_dup(ctx, -2); // [HTTPStream][readable][websocket][clientRequest][websocket] + duk_put_prop_string(ctx, -2, ILibDuktape_CR2WS); // [HTTPStream][readable][websocket][clientRequest] + duk_put_prop_string(ctx, -2, ILibDuktape_WS2CR); // [HTTPStream][readable][websocket] + + // Upstream Readable => WebSocket Encoded + duk_get_prop_string(ctx, -2, "pipe"); // [HTTPStream][readable][websocket][pipe] + duk_dup(ctx, -3); // [HTTPStream][readable][websocket][pipe][this] + duk_get_prop_string(ctx, -3, "encoded"); // [HTTPStream][readable][websocket][pipe][this][WS_ENC] + duk_call_method(ctx, 1); // [HTTPStream][readable][websocket][...] + duk_pop(ctx); // [HTTPStream][readable][websocket] + duk_remove(ctx, -2); // [HTTPStream][websocket] + + if (duk_has_prop_string(ctx, -2, ILibDuktape_HTTP2PipedWritable)) + { + // Web Socket Encoded => Destination Stream + duk_get_prop_string(ctx, -1, "encoded"); // [HTTPStream][websocket][WS_ENC] + duk_get_prop_string(ctx, -1, "pipe"); // [HTTPStream][websocket][WS_ENC][pipe] + duk_swap_top(ctx, -2); // [HTTPStream][websocket][pipe][this] + duk_get_prop_string(ctx, -4, ILibDuktape_HTTP2PipedWritable); // [HTTPStream][websocket][pipe][this][destination] + duk_call_method(ctx, 1); // [HTTPStream][websocket][...] + duk_pop(ctx); // [HTTPStream][websocket] + } + + duk_get_prop_string(ctx, -1, ILibDuktape_WS2CR); // [HTTPStream][websocket][clientRequest] + duk_get_prop_string(ctx, -1, "emit"); // [HTTPStream][websocket][clientRequest][emit] + duk_swap_top(ctx, -2); // [HTTPStream][websocket][emit][this] + duk_push_string(ctx, "upgrade"); // [HTTPStream][websocket][emit][this][upgrade] + duk_dup(ctx, 0); // [HTTPStream][websocket][emit][this][upgrade][imsg] + duk_dup(ctx, -5); // [HTTPStream][websocket][emit][this][upgrade][imsg][websocket] + duk_get_prop_string(ctx, -1, "decoded"); // [HTTPStream][websocket][emit][this][upgrade][imsg][websocket][WS_DEC] + duk_remove(ctx, -2); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC] + duk_push_null(ctx); // [HTTPStream][websocket][emit][this][upgrade][imsg][WS_DEC][null] + duk_call_method(ctx, 4); duk_pop(ctx); // [HTTPStream][websocket] + + return(0); +} +duk_ret_t ILibDuktape_HttpStream_http_endResponseSink(duk_context *ctx) +{ + duk_push_this(ctx); // [imsg] + duk_get_prop_string(ctx, -1, ILibDuktape_IMSG2HttpStream); // [imsg][httpstream] + duk_get_prop_string(ctx, -1, ILibDuktape_HTTP2CR); // [imsg][httpstream][CR] + if (Duktape_GetBooleanProperty(ctx, -2, "connectionCloseSpecified", 0) != 0) + { + // We cant persist this connection, so close the socket. + // Agent is already listening for the 'close' event, so it'll cleanup automatically + duk_get_prop_string(ctx, -1, "socket"); // [imsg][httpstream][CR][socket] + duk_get_prop_string(ctx, -1, "end"); // [imsg][httpstream][CR][socket][end] + duk_swap_top(ctx, -2); // [imsg][httpstream][CR][end][this] + duk_call_method(ctx, 0); + return(0); + } + duk_get_prop_string(ctx, -1, ILibDuktape_CR2Agent); // [imsg][httpstream][CR][Agent] + duk_get_prop_string(ctx, -1, "keepSocketAlive"); // [imsg][httpstream][CR][Agent][keepSocketAlive] + duk_swap_top(ctx, -2); // [imsg][httpstream][CR][keepSocketAlive][this] + duk_get_prop_string(ctx, -3, "socket"); // [imsg][httpstream][CR][keepSocketAlive][this][socket] + duk_call_method(ctx, 1); duk_pop(ctx); // [imsg][httpstream][CR] + return(0); +} +duk_ret_t ILibDuktape_HttpStream_http_responseSink(duk_context *ctx) +{ + duk_push_this(ctx); // [httpstream] + duk_dup(ctx, 0); // [httpstream][imsg] + duk_swap_top(ctx, -2); // [imsg][httpstream] + duk_put_prop_string(ctx, -2, ILibDuktape_IMSG2HttpStream); // [imsg] + + ILibDuktape_EventEmitter_AddOnceEx3(ctx, 0, "end", ILibDuktape_HttpStream_http_endResponseSink); + return(0); +} +duk_ret_t ILibDuktape_HttpStream_http_OnSocketReady(duk_context *ctx) +{ + void *httpStream; + + duk_dup(ctx, 0); // [socket] + duk_push_this(ctx); // [socket][clientRequest] + + if (duk_has_prop_string(ctx, -2, ILibDuktape_Socket2HttpStream)) + { + // HTTP and/or TLS was already setup previously + duk_get_prop_string(ctx, -2, ILibDuktape_Socket2HttpStream); // [socket][clientRequest][HTTPStream] + duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2Data); // [socket][clientRequest][HTTPStream][data] + ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)Duktape_GetBuffer(ctx, -1, NULL); + ILibWebClient_ResetWCDO(data->WCDO); + if (data->bodyStream != NULL) { ILibDuktape_readableStream_WriteEnd(data->bodyStream); data->bodyStream = NULL; } + duk_pop(ctx); // [socket][clientRequest][HTTPStream] + duk_get_prop_string(ctx, -1, ILibDuktape_HTTP2PipedWritable); // [socket][clientRequest][HTTPStream][destination] + duk_get_prop_string(ctx, -3, ILibDuktape_CR2Options); // [socket][clientRequest][HTTPStream][destination][Options] + ILibDuktape_HttpStream_http_ConvertOptionToSend(ctx, duk_get_heapptr(ctx, -2), duk_get_heapptr(ctx, -1)); + + // If HTTP stream was already setup, then we need to pipe the clientRequest to the upstream object, because it wasn't attached yet. + duk_dup(ctx, -4); // [socket][clientRequest][HTTPStream][destination][Options][clientRequest] + duk_get_prop_string(ctx, -1, "pipe"); // [socket][clientRequest][HTTPStream][destination][Options][clientRequest][pipe] + duk_swap_top(ctx, -2); // [socket][clientRequest][HTTPStream][destination][Options][pipe][this] + duk_dup(ctx, -4); // [socket][clientRequest][HTTPStream][destination][Options][pipe][this][destination] + duk_call_method(ctx, 1); + + return(0); + } + + if (duk_peval_string(ctx, "require('http').createStream();") != 0) // [socket][clientRequest][error] + { + // Need to Abort this connection + duk_get_prop_string(ctx, -2, "emit"); // [socket][clientRequest][error][emit] + duk_dup(ctx, -3); // [socket][clientRequest][error][emit][this] + duk_push_string(ctx, "abort"); // [socket][clientRequest][error][emit][this][ebort] + duk_push_string(ctx, duk_safe_to_string(ctx, -3)); // [socket][clientRequest][error][emit][this][abort][errorString] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.onConnect(): "); } + return(0); + } + + httpStream = duk_get_heapptr(ctx, -1); // [socket][clientRequest][httpStream] + duk_dup(ctx, -3); // [socket][clientRequest][httpStream][socket] + duk_dup(ctx, -2); // [socket][clientRequest][httpStream][socket][httpStream] + duk_put_prop_string(ctx, -2, ILibDuktape_Socket2HttpStream); // [socket][clientRequest][httpStream][socket] + duk_pop(ctx); // [socket][clientRequest][httpStream] + duk_dup(ctx, -2); // [socket][clientRequest][httpStream][clientRequest] + duk_put_prop_string(ctx, -2, ILibDuktape_HTTP2CR); // [socket][clientRequest][httpStream] + ILibDuktape_EventEmitter_ForwardEvent(ctx, -1, "response", -2, "response"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -1, "continue", -2, "continue"); + ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "upgrade", ILibDuktape_HttpStream_http_onUpgrade); + ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "response", ILibDuktape_HttpStream_http_responseSink); + + duk_put_prop_string(ctx, -2, ILibDuktape_CR2HTTPStream); // [socket][clientRequest] + + duk_get_prop_string(ctx, -1, ILibDuktape_CR2Options); // [socket][clientRequest][options] + ILibDuktape_HttpStream_http_ConvertOptionToSend(ctx, duk_get_heapptr(ctx, -3), duk_get_heapptr(ctx, -1)); + duk_pop(ctx); // [socket][clientRequest] + + // ClientRequest => Socket + duk_get_prop_string(ctx, -1, "pipe"); // [socket][clientRequest][pipe] + duk_swap_top(ctx, -2); // [socket][pipe][this] + duk_dup(ctx, -3); // [socket][pipe][this][socket] + duk_push_object(ctx); // [socket][pipe][this][socket][options] + duk_push_false(ctx); duk_put_prop_string(ctx, -2, "end"); + if (duk_pcall_method(ctx, 2) != 0) { return(ILibDuktape_Error(ctx, "http.onConnect(): Error Piping with socket ")); } + duk_pop(ctx); // [socket] + + // Socket => HttpStream + duk_get_prop_string(ctx, -1, "pipe"); // [socket][pipe] + duk_dup(ctx, -2); // [socket][pipe][this] + duk_push_heapptr(ctx, httpStream); // [socket][pipe][this][http] + if (duk_pcall_method(ctx, 1) != 0) { return(ILibDuktape_Error(ctx, "http.onConnect(): Error calling pipe ")); } + duk_pop(ctx); // [socket] + + // HttpStream => Socket + duk_push_heapptr(ctx, httpStream); // [socket][http] + duk_get_prop_string(ctx, -1, "pipe"); // [socket][http][pipe] + duk_swap_top(ctx, -2); // [socket][pipe][this] + duk_dup(ctx, -3); // [socket][pipe][this][socket] + if (duk_pcall_method(ctx, 1) != 0) { return(ILibDuktape_Error(ctx, "http.onConnect(): Error calling pipe ")); } + + return(0); +} +duk_ret_t ILibDuktape_HttpStream_http_OnConnectError(duk_context *ctx) +{ + duk_push_this(ctx); // [socket] + if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2CR)) + { + // Socket was created via 'createConnection' specified by the application + duk_get_prop_string(ctx, -1, ILibDuktape_Socket2CR); // [socket][CR] + duk_get_prop_string(ctx, -1, "emit"); // [socket][CR][emit] + duk_swap_top(ctx, -2); // [socket][emit][this] + duk_push_string(ctx, "error"); // [socket][emit][this][error] + duk_dup(ctx, 0); // [socket][emit][this][error][err] + duk_call_method(ctx, 2); + } + else if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2Agent)) + { + // Socket was created via 'http.Agent' + if (duk_has_prop_string(ctx, -1, "\xFF_NET_SOCKET2OPTIONS")) + { + duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [socket][agent] + duk_get_prop_string(ctx, -1, "requests"); // [socket][agent][requests] + duk_get_prop_string(ctx, -2, "getName"); // [socket][agent][requests][getName] + duk_dup(ctx, -3); // [socket][agent][requests][getName][this] + duk_get_prop_string(ctx, -5, "\xFF_NET_SOCKET2OPTIONS"); // [socket][agent][requests][getName][this][options] + duk_call_method(ctx, 1); // [socket][agent][requests][name] + duk_get_prop(ctx, -2); // [socket][agent][requests][Array] + duk_get_prop_string(ctx, -1, "pop"); // [socket][agent][requests][Array][pop] + duk_swap_top(ctx, -2); // [socket][agent][requests][pop][this] + duk_call_method(ctx, 0); // [socket][agent][requests][request] + duk_get_prop_string(ctx, -1, "emit"); // [socket][agent][requests][request][emit] + duk_swap_top(ctx, -2); // [socket][agent][requests][emit][this] + duk_push_string(ctx, "error"); // [socket][agent][requests][emit][this][error] + duk_dup(ctx, 0); // [socket][agent][requests][emit][this][error][err] + duk_call_method(ctx, 2); + } + } + return(0); +} + +//duk_ret_t ILibDuktape_HttpStream_http_proxyData(duk_context *ctx) +//{ +// char *buffer; +// duk_size_t bufferLen; +// +// buffer = (char*)Duktape_GetBuffer(ctx, 0, &bufferLen); +// if (bufferLen > 13 && ILibString_IndexOf(buffer, bufferLen, "\r\n\r\n", 4) > 0) +// { +// if (strncasecmp(buffer + 9, "200", 3) == 0) +// { +// // SUCCESS! +// duk_push_this(ctx); // [socket] +// duk_get_prop_string(ctx, -1, "removeAllListeners"); // [socket][remove] +// duk_dup(ctx, -2); // [socket][remove][this] +// duk_push_string(ctx, "data"); // [socket][remove][this]['data'] +// duk_call_method(ctx, 1); duk_pop(ctx); // [socket] +// +// if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2CR)) +// { +// // Socket was created with passed in createConnection +// duk_get_prop_string(ctx, -1, ILibDuktape_Socket2CR); // [socket][clientRequest] +// duk_get_prop_string(ctx, -1, "emit"); // [socket][clientRequest][emit] +// duk_swap_top(ctx, -2); // [socket][emit][this] +// duk_dup(ctx, -3); // [socket][emit][this][socket] +// if (duk_pcall_method(ctx, 1) != 0) { return(ILibDuktape_Error(ctx, "createConnection().proxyOnConnect(): ")); } +// } +// else +// { +// // Socket was created with Agent +// if (!duk_has_prop_string(ctx, -1, ILibDuktape_Socket2Agent)) +// { +// return(ILibDuktape_Error(ctx, "createConnection().proxyOnConnect(): Internal Error, 'Agent' was not specified")); +// } +// else +// { +// duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [socket][agent] +// duk_get_prop_string(ctx, -1, "keepSocketAlive"); // [socket][agent][keepSocketAlive] +// duk_swap_top(ctx, -2); // [socket][keepSocketAlive][this] +// duk_dup(ctx, -3); // [socket][keepSocketAlive][this][socket] +// if (duk_pcall_method(ctx, 1) != 0) { return(ILibDuktape_Error(ctx, "createConnection().proxyOnConnect(): Error calling Agent.keepSocketAlive [%s]", duk_safe_to_string(ctx, -1))); } +// } +// } +// return(0); +// } +// else +// { +// // FAIL! +// } +// } +// else +// { +// // We don't have the entire response yet +// duk_push_this(ctx); // [socket] +// duk_get_prop_string(ctx, -1, "unshift"); // [socket][unshift] +// duk_swap_top(ctx, -2); // [unshift][this] +// duk_dup(ctx, 0); // [unshift][this][chunk] +// duk_call_method(ctx, 1); +// } +// return(0); +//} + +duk_ret_t ILibDuktape_HttpStream_http_OnConnect(duk_context *ctx) +{ + duk_ret_t retVal = 0; + duk_push_this(ctx); // [socket] + duk_get_prop_string(ctx, -1, ILibDuktape_SOCKET2OPTIONS); // [socket][options] + + + //if (duk_has_prop_string(ctx, -1, "proxy")) + //{ + // duk_get_prop_string(ctx, -1, "proxy"); // [socket][options][proxy] + // duk_size_t remoteHostLen; + // char *remoteHost = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "remoteHost", NULL, &remoteHostLen); + // int remotePort = Duktape_GetIntPropertyValue(ctx, -1, "remotePort", 0); + + // if (remoteHost != NULL && remotePort != 0) + // { + // duk_pop_2(ctx); // [socket] + // ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "data", ILibDuktape_HttpStream_http_proxyData); + + // duk_get_prop_string(ctx, -1, "write"); // [socket][write] + // duk_swap_top(ctx, -2); // [write][this] + + // char *tmp = (char*)ILibMemory_AllocateA((2 * remoteHostLen) + 72); + // sprintf_s(tmp, ILibMemory_AllocateA_Size(tmp), "CONNECT %s:%u HTTP/1.1\r\nProxy-Connection: keep-alive\r\nHost: %s\r\n\r\n", remoteHost, remotePort, remoteHost); + // duk_push_string(ctx, tmp); // [write][this][chunk] + // duk_call_method(ctx, 1); + // return(0); + // } + // else + // { + // duk_pop(ctx); // [socket][options] + // } + //} + + duk_pop(ctx); // [socket] + if (duk_has_prop_string(ctx, -1, ILibDuktape_Socket2CR)) + { + // Socket was created with passed in createConnection + duk_get_prop_string(ctx, -1, ILibDuktape_Socket2CR); // [socket][clientRequest] + duk_get_prop_string(ctx, -1, "emit"); // [socket][clientRequest][emit] + duk_swap_top(ctx, -2); // [socket][emit][this] + duk_dup(ctx, -3); // [socket][emit][this][socket] + if (duk_pcall_method(ctx, 1) != 0) { retVal = ILibDuktape_Error(ctx, "createConnection().onConnect(): "); } + } + else + { + // Socket was created with Agent + if (!duk_has_prop_string(ctx, -1, ILibDuktape_Socket2Agent)) + { + retVal = ILibDuktape_Error(ctx, "createConnection().onConnect(): Internal Error, 'Agent' was not specified"); + } + else + { + duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [socket][agent] + duk_get_prop_string(ctx, -1, "keepSocketAlive"); // [socket][agent][keepSocketAlive] + duk_swap_top(ctx, -2); // [socket][keepSocketAlive][this] + duk_dup(ctx, -3); // [socket][keepSocketAlive][this][socket] + if (duk_pcall_method(ctx, 1) != 0) { retVal = ILibDuktape_Error(ctx, "createConnection().onConnect(): Error calling Agent.keepSocketAlive [%s]", duk_safe_to_string(ctx, -1)); } + } + } + return(retVal); +} + +void ILibDuktape_HttpStream_http_request_transformPiped(struct ILibDuktape_Transform *sender, void *user) +{ + char tmp[100]; + int tmpLen; + + ILibDuktape_Http_ClientRequest_WriteData *data = (ILibDuktape_Http_ClientRequest_WriteData*)user; + if (data->noMoreWrites != 0) + { + // We have the entire request body + data->headersFinished = 1; + tmpLen = sprintf_s(tmp, sizeof(tmp), "Content-Length: %d\r\n\r\n", (int)data->bufferWriteLen); + ILibDuktape_readableStream_WriteData(sender->target, tmp, tmpLen); + if (data->bufferWriteLen > 0) { ILibDuktape_readableStream_WriteData(sender->target, data->buffer, (int)data->bufferWriteLen); } + } + else if(data->bufferWriteLen > 0) + { + if (data->headersFinished) + { + tmpLen = sprintf_s(tmp, sizeof(tmp), "%X\r\n", (unsigned int)data->bufferWriteLen); + } + else + { + data->headersFinished = 1; + tmpLen = sprintf_s(tmp, sizeof(tmp), "Transfer-Encoding: chunked\r\n\r\n%X\r\n", (unsigned int)data->bufferWriteLen); + } + + ILibDuktape_readableStream_WriteData(sender->target, tmp, tmpLen); + ILibDuktape_readableStream_WriteData(sender->target, data->buffer, (int)data->bufferWriteLen); + ILibDuktape_readableStream_WriteData(sender->target, "\r\n", 2); + free(data->buffer); + data->buffer = NULL; + data->bufferLen = data->bufferWriteLen = 0; + } +} +void ILibDuktape_HttpStream_http_request_transform(struct ILibDuktape_Transform *sender, int Reserved, int flush, char *buffer, int bufferLen, void *user) +{ + ILibDuktape_Http_ClientRequest_WriteData *data = (ILibDuktape_Http_ClientRequest_WriteData*)user; + + char tmp[100]; + int tmpLen; + + if (data->headersFinished == 0) + { + // Need to write out the end of the headers + data->headersFinished = 1; + if (flush != 0) + { + tmpLen = sprintf_s(tmp, sizeof(tmp), "Content-Length: %d\r\n\r\n", bufferLen); + ILibDuktape_readableStream_WriteData(sender->target, tmp, tmpLen); + if (bufferLen > 0) + { + ILibDuktape_readableStream_WriteData(sender->target, buffer, bufferLen); + } + } + else + { + tmpLen = sprintf_s(tmp, sizeof(tmp), "Transfer-Encoding: chunked\r\n\r\n%X\r\n", bufferLen); + ILibDuktape_readableStream_WriteData(sender->target, tmp, tmpLen); + if (bufferLen > 0) + { + ILibDuktape_readableStream_WriteData(sender->target, buffer, bufferLen); + } + ILibDuktape_readableStream_WriteData(sender->target, "\r\n", 2); + } + } + else + { + tmpLen = sprintf_s(tmp, sizeof(tmp), "%X\r\n", bufferLen); + ILibDuktape_readableStream_WriteData(sender->target, tmp, tmpLen); + if (bufferLen > 0) + { + ILibDuktape_readableStream_WriteData(sender->target, buffer, bufferLen); + } + ILibDuktape_readableStream_WriteData(sender->target, "\r\n", 2); + } + +} +duk_ret_t ILibDuktape_HttpStream_http_request(duk_context *ctx) +{ + char *proto; + duk_size_t protoLen; + + int nargs = duk_get_top(ctx); + if (duk_is_string(ctx, 0)) + { + // Call 'get' instead, since we already handle that case there... + duk_push_this(ctx); // [http] + duk_get_prop_string(ctx, -1, "get"); // [http][get] + duk_swap_top(ctx, -2); // [get][this] + duk_dup(ctx, 0); // [get][this][uri] + if (nargs > 1 && duk_is_function(ctx, 1)) + { + duk_dup(ctx, 1); // [get][this][uri][callback] + duk_call_method(ctx, 2); // [retVal] + } + else + { + duk_call_method(ctx, 1); // [retVal] + } + return(1); + } + else + { + // Make sure 'host' field is present + duk_dup(ctx, 0); // [options] + duk_get_prop_string(ctx, -1, "protocol"); // [options][protocol] + proto = (char*)Duktape_GetBuffer(ctx, -1, &protoLen); + if ((protoLen == 4 && strncasecmp(proto, "wss:", 4) == 0) || (protoLen == 3 && strncasecmp(proto, "ws:", 3) == 0)) + { + duk_dup(ctx, 0); // [options][protocol][options] + duk_push_false(ctx); // [options][protocol][options][false] + duk_put_prop_string(ctx, -2, "agent"); // [options][protocol][options] + duk_pop(ctx); // [options][protocol] + } + + duk_pop(ctx); // [options] + if (!duk_has_prop_string(ctx, -1, "headers")) + { + duk_push_object(ctx); // [options][headers] + duk_get_prop_string(ctx, -2, "host"); // [options][headers][hostname] + duk_get_prop_string(ctx, -1, "concat"); // [options][headers][hostname][concat] + duk_swap_top(ctx, -2); // [options][headers][concat][this] + duk_push_string(ctx, ":"); // [options][headers][concat][this][:] + duk_get_prop_string(ctx, -5, "port"); // [options][headers][concat][this][:][port] + duk_call_method(ctx, 2); // [options][headers][hostname] + duk_put_prop_string(ctx, -2, "Host"); // [options][headers] + duk_put_prop_string(ctx, -2, "headers"); // [options] + } + duk_get_prop_string(ctx, -1, "headers"); // [options][headers] + if (duk_has_prop_string(ctx, -1, "Expect") && !duk_has_prop_string(ctx, -1, "Transfer-Encoding") && !duk_has_prop_string(ctx, -1, "Content-Length")) + { + return(ILibDuktape_Error(ctx, "http.request(): Cannot specify header 'Expect' without specifying 'Content-Length' or 'Transfer-Encoding'")); + } + duk_pop_2(ctx); // ... + } + + duk_dup(ctx, 0); // [options] + duk_push_object(ctx); // [options][clientRequest] + duk_dup(ctx, -1); // [options][clientRequest][dup] + duk_put_prop_string(ctx, -3, ILibDuktape_Options2ClientRequest);// [options][clientRequest] + duk_remove(ctx, -2); // [clientRequest] + duk_push_this(ctx); // [clientRequest][http] + duk_put_prop_string(ctx, -2, ILibDuktape_CR2HTTP); // [clientRequest] + + duk_push_false(ctx); + duk_put_prop_string(ctx, -2, ILibDuktape_CR_EndCalled); // [clientRequest] + duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_Http_ClientRequest_WriteData)); + ILibDuktape_Http_ClientRequest_WriteData *wdata = (ILibDuktape_Http_ClientRequest_WriteData*)Duktape_GetBuffer(ctx, -1, NULL); + duk_put_prop_string(ctx, -2, ILibDuktape_CR_RequestBuffer); + memset(wdata, 0, sizeof(ILibDuktape_Http_ClientRequest_WriteData)); + + ILibDuktape_Transform_Init(ctx, ILibDuktape_HttpStream_http_request_transform, ILibDuktape_HttpStream_http_request_transformPiped, wdata); + + ILibDuktape_WriteID(ctx, "https.clientRequest"); + ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "abort"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "continue"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "response"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "socket"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "timeout"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "error"); + + ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "socket", ILibDuktape_HttpStream_http_OnSocketReady); + + duk_dup(ctx, 0); // [clientRequest][options] + duk_put_prop_string(ctx, -2, ILibDuktape_CR2Options); // [clientReqeust] + + void *createConnection = NULL; + void *agent = NULL; + + if (duk_has_prop_string(ctx, 0, "createConnection")) + { + createConnection = Duktape_GetHeapptrProperty(ctx, 0, "createConnection"); + } + else + { + if (duk_has_prop_string(ctx, 0, "agent")) + { + duk_get_prop_string(ctx, 0, "agent"); // [clientRequest][Agent] + if (duk_is_boolean(ctx, -1)) + { + if (duk_get_boolean(ctx, -1) == 0) + { + duk_pop(ctx); // [clientRequest] + duk_eval_string(ctx, "require('http').Agent();"); // [clientRequest][tempAgent] + agent = duk_get_heapptr(ctx, -1); + duk_put_prop_string(ctx, -2, ILibDuktape_CR2Agent); // [clientRequest] + } + else + { + duk_pop(ctx); // [clientRequest] + duk_push_this(ctx); // [clientRequest][http] + duk_get_prop_string(ctx, -1, "globalAgent"); // [clientRequest][http][agent] + agent = duk_get_heapptr(ctx, -1); + duk_remove(ctx, -2); // [clientRequest][agent] + duk_put_prop_string(ctx, -2, ILibDuktape_CR2Agent); // [clientRequest] + } + } + else if (duk_is_object(ctx, -1)) + { + agent = duk_get_heapptr(ctx, -1); // [clientRequest][agent] + duk_put_prop_string(ctx, -2, ILibDuktape_CR2Agent); // [clientRequest] + } + else + { + return(ILibDuktape_Error(ctx, "http.request(): Invalid Option Parameter 'Agent'")); + } + } + else + { + duk_push_this(ctx); // [clientRequest][http] + duk_get_prop_string(ctx, -1, "globalAgent"); // [clientRequest][http][agent] + agent = duk_get_heapptr(ctx, -1); + duk_remove(ctx, -2); // [clientRequest][agent] + duk_put_prop_string(ctx, -2, ILibDuktape_CR2Agent); // [clientRequest] + } + } + + // + // Check Proxy + // + ILibDuktape_globalTunnel_data *globalTunnel = ILibDuktape_GetGlobalTunnel(ctx); + if (duk_has_prop_string(ctx, 0, "proxy")) + { + duk_get_prop_string(ctx, 0, "proxy"); // [clientRequest][proxy] + if (duk_is_string(ctx, -1)) { if (strcmp((char*)duk_get_string(ctx, -1), "none") == 0) { globalTunnel = NULL; duk_del_prop_string(ctx, -2, "proxy"); } } + duk_pop(ctx); // [clientRequest] + } + if (globalTunnel != NULL && !duk_has_prop_string(ctx, 0, "proxy")) + { + duk_dup(ctx, 0); // [options] + duk_push_object(ctx); // [options][proxy] + + duk_push_string(ctx, ILibRemoteLogging_ConvertAddress((struct sockaddr*)&(globalTunnel->proxyServer))); + duk_put_prop_string(ctx, -2, "host"); + duk_push_int(ctx, (int)ntohs(globalTunnel->proxyServer.sin6_port)); + duk_put_prop_string(ctx, -2, "port"); // [options][proxy] + duk_put_prop_string(ctx, -2, "proxy"); // [options] + duk_pop(ctx); // ... + } + + if (createConnection != NULL) // [clientRequest] + { + duk_push_heapptr(ctx, createConnection); // [clientRequest][createConnection] + duk_dup(ctx, 0); // [clientRequest][createConnection][options] + duk_call(ctx, 1); // [clientRequest][socket] + duk_dup(ctx, -1); // [clientRequest][socket][clientRequest] + duk_put_prop_string(ctx, -2, ILibDuktape_Socket2CR); // [clientRequest][socket] + ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "connect", ILibDuktape_HttpStream_http_OnConnect); + ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "error", ILibDuktape_HttpStream_http_OnConnectError); + ILibDuktape_CreateReadonlyProperty(ctx, "socket"); // [clientRequest] + } + else if (agent != NULL) // [clientRequest] + { + duk_push_heapptr(ctx, agent); // [clientRequest][agent] + duk_get_prop_string(ctx, -1, "getName"); // [clientRequest][agent][getName] + duk_dup(ctx, -2); // [clientRequest][agent][getName][this] + duk_dup(ctx, 0); // [clientRequest][agent][getName][this][options] + duk_call_method(ctx, 1); // [clientRequest][agent][key] + duk_get_prop_string(ctx, -2, "freeSockets"); // [clientRequest][agent][key][freeSockets] + duk_dup(ctx, -2); // [clientRequest][agent][key][freeSockets][key] + duk_get_prop(ctx, -2); // [clientRequest][agent][key][freeSockets][Array] + if (!duk_is_undefined(ctx, -1)) + { + duk_get_prop_string(ctx, -1, "shift"); // [clientRequest][agent][key][freeSockets][Array][shift] + duk_swap_top(ctx, -2); // [clientRequest][agent][key][freeSockets][shift][this] + duk_call_method(ctx, 0); // [clientRequest][agent][key][freeSockets][socket] + if (!duk_is_undefined(ctx, -1)) + { + duk_remove(ctx, -2); // [clientRequest][agent][key][socket] + duk_get_prop_string(ctx, -3, "reuseSocket"); // [clientRequest][agent][key][socket][reuseSocket] + duk_dup(ctx, -4); // [clientRequest][agent][key][socket][reuseSocket][this] + duk_dup(ctx, -3); // [clientRequest][agent][key][socket][reuseSocket][this][socket] + duk_dup(ctx, -7); // [clientRequest][agent][key][socket][reuseSocket][this][socket][request] + duk_call_method(ctx, 2); // [clientRequest][agent][key][socket][undefined] + duk_pop_n(ctx, 4); // [clientRequest] + agent = NULL; + } + else + { + duk_pop_2(ctx); // [clientRequest][agent][key] + } + } + else + { + duk_pop_2(ctx); // [clientRequest][agent][key] + } + + if (agent != NULL) // [clientRequest][agent][key] + { + // If we are here, it means there was not a freeSocket + + // Let's start by adding ourselves to the Pending Requests Queue + duk_get_prop_string(ctx, -2, "requests"); // [clientRequest][agent][key][requests] + duk_dup(ctx, -2); // [clientRequest][agent][key][requests][key] + if (!duk_has_prop(ctx, -2)) // [clientRequest][agent][key][requests] + { + // No waiting requests, so attach a new queue + duk_dup(ctx, -2); // [clientRequest][agent][key][requests][key] + duk_push_array(ctx); // [clientRequest][agent][key][requests][key][value] + duk_get_prop_string(ctx, -1, "push"); // [clientRequest][agent][key][requests][key][value][push] + duk_dup(ctx, -2); // [clientRequest][agent][key][requests][key][value][push][this] + duk_dup(ctx, -8); // [clientRequest][agent][key][requests][key][value][push][this][clieentRequest] + duk_call_method(ctx, 1); // [clientRequest][agent][key][requests][key][value][retVal] + duk_pop(ctx); // [clientRequest][agent][key][requests][key][value] + duk_put_prop(ctx, -3); // [clientRequest][agent][key][requests] + duk_pop(ctx); // [clientRequest][agent][key] + } + else + { + // There is already a queue here + duk_dup(ctx, -2); // [clientRequest][agent][key][requests][key] + duk_get_prop(ctx, -2); // [clientRequest][agent][key][requests][array] + duk_get_prop_string(ctx, -1, "push"); // [clientRequest][agent][key][requests][array][push] + duk_swap_top(ctx, -2); // [clientRequest][agent][key][requests][push][this] + duk_dup(ctx, -6); // [clientRequest][agent][key][requests][push][this][clientRequest] + duk_call_method(ctx, 1); // [clientRequest][agent][key][requests][retVal] + duk_pop_2(ctx); // [clientRequest][agent][key] + } + + // Let's check to see if there is already a socket in use talking to our same host + duk_get_prop_string(ctx, -2, "sockets"); // [clientRequest][agent][key][sockets] + duk_dup(ctx, -2); // [clientRequest][agent][key][sockets][key] + duk_get_prop(ctx, -2); // [clientRequest][agent][key][sockets][Array] + if (duk_is_undefined(ctx, -1) || duk_get_length(ctx, -1) < (duk_size_t)Duktape_GetIntPropertyValue(ctx, -4, "maxSockets", 0)) + { + // We can create a new socket + duk_pop_3(ctx); // [clientRequest][agent] + duk_dup(ctx, -1); // [clientRequest][agent][agent] + duk_get_prop_string(ctx, -1, "createConnection"); // [clientRequest][agent][agent][createConnection] + duk_swap_top(ctx, -2); // [clientRequest][agent][createConnection][this] + duk_dup(ctx, 0); // [clientRequest][agent][createConnection][this][options] + if (duk_has_prop_string(ctx, -1, "checkClientIdentity")) + { + duk_push_c_function(ctx, ILibDuktape_HttpStream_http_checkIdentity, DUK_VARARGS); // [clientRequest][agent][createConnection][this][options][checkIdentity] + duk_get_prop_string(ctx, -2, "checkClientIdentity"); // [clientRequest][agent][createConnection][this][options][checkIdentity][checkClient] + duk_put_prop_string(ctx, -2, ILibDuktape_FUNC); // [clientRequest][agent][createConnection][this][options][checkIdentity] + duk_dup(ctx, -6); // [clientRequest][agent][createConnection][this][options][checkIdentity][ClientRequest] + duk_put_prop_string(ctx, -2, ILibDuktape_ClientRequest); // [clientRequest][agent][createConnection][this][options][checkIdentity] + duk_put_prop_string(ctx, -2, "checkClientIdentity"); // [clientRequest][agent][createConnection][this][options] + } + + duk_push_c_function(ctx, ILibDuktape_HttpStream_http_OnConnect, DUK_VARARGS); + duk_call_method(ctx, 2); // [clientRequest][agent][socket] + duk_swap_top(ctx, -2); // [clientRequest][socket][agent] + duk_put_prop_string(ctx, -2, ILibDuktape_Socket2Agent); // [clientRequest][socket] + ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "error", ILibDuktape_HttpStream_http_OnConnectError); + duk_pop(ctx); // [clientRequest] + } + else + { + duk_pop_n(ctx, 4); // [clientRequest] + } + } + } + + return(1); +} + +duk_ret_t ILibDuktape_HttpStream_http_server_close(duk_context *ctx) +{ + return(0); +} +duk_ret_t ILibDuktape_HttpStream_http_server_upgradeWebsocket(duk_context *ctx) +{ + char wsguid[] = WEBSOCKET_GUID; + char *key, *keyResult; + int keyResultLen; + duk_size_t keyLen; + SHA_CTX c; + char shavalue[21]; + + duk_push_this(ctx); // [socket] + duk_push_current_function(ctx); // [socket][func] + duk_get_prop_string(ctx, -2, "unpipe"); // [socket][func][unpipe] + duk_dup(ctx, -3); // [socket][func][unpipe][this] + duk_call_method(ctx, 0); duk_pop(ctx); // [socket][func] + + duk_get_prop_string(ctx, -1, "imsg"); // [socket][func][imsg] + duk_get_prop_string(ctx, -1, "headers"); // [socket][func][imsg][headers] + duk_get_prop_string(ctx, -1, "Sec-WebSocket-Key"); // [socket][func][imsg][headers][key] + + key = (char*)Duktape_GetBuffer(ctx, -1, &keyLen); + keyResult = ILibString_Cat(key, (int)keyLen, wsguid, sizeof(wsguid)); + + SHA1_Init(&c); + SHA1_Update(&c, keyResult, strnlen_s(keyResult, sizeof(wsguid) + keyLen)); + SHA1_Final((unsigned char*)shavalue, &c); + shavalue[20] = 0; + free(keyResult); + keyResult = NULL; + + keyResultLen = ILibBase64Encode((unsigned char*)shavalue, 20, (unsigned char**)&keyResult); + + duk_push_this(ctx); // [socket] + duk_get_prop_string(ctx, -1, "write"); // [socket][write] + duk_dup(ctx, -2); // [socket][write][this] + duk_push_string(ctx, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "); + duk_call_method(ctx, 1); duk_pop(ctx); // ... + + duk_push_this(ctx); // [socket] + duk_get_prop_string(ctx, -1, "write"); // [socket][write] + duk_dup(ctx, -2); // [socket][write][this] + duk_push_lstring(ctx, keyResult, keyResultLen); + duk_call_method(ctx, 1); duk_pop(ctx); // ... + + duk_push_this(ctx); // [socket] + duk_get_prop_string(ctx, -1, "write"); // [socket][write] + duk_dup(ctx, -2); // [socket][write][this] + duk_push_string(ctx, "\r\n\r\n"); + duk_call_method(ctx, 1); duk_pop(ctx); // ... + + duk_eval_string(ctx, "require('http');"); // [http] + duk_get_prop_string(ctx, -1, "webSocketStream"); // [http][constructor] + duk_push_lstring(ctx, keyResult, keyResultLen); // [http][constructor][key] + duk_new(ctx, 1); // [http][wss] + + + duk_push_this(ctx); // [http][wss][socket] + duk_get_prop_string(ctx, -1, "pipe"); // [http][wss][socket][pipe] + duk_swap_top(ctx, -2); // [http][wss][pipe][this] + duk_get_prop_string(ctx, -3, "encoded"); // [http][wss][pipe][this][WS_ENC] + duk_call_method(ctx, 1); duk_pop(ctx); // [http][wss] + + duk_get_prop_string(ctx, -1, "encoded"); // [http][wss][WS_ENC] + duk_get_prop_string(ctx, -1, "pipe"); // [http][wss][WS_ENC][pipe] + duk_swap_top(ctx, -2); // [http][wss][pipe][this] + duk_push_this(ctx); // [http][wss][pipe][this][socket] + duk_call_method(ctx, 1); duk_pop(ctx); // [http][wss] + + duk_get_prop_string(ctx, -1, "decoded"); // [http][wss][WS_DEC] + + free(keyResult); + return(1); +} +duk_ret_t ILibDuktape_HttpStream_http_server_onUpgrade_digestWriteUnauth(duk_context *ctx) +{ + int nargs = duk_get_top(ctx), i; + duk_push_current_function(ctx); + duk_get_prop_string(ctx, -1, "imsg"); + duk_get_prop_string(ctx, -1, ILibDuktape_IMSG2SR); // [serverResponse] + duk_get_prop_string(ctx, -1, "Digest_writeUnauthorized"); // [serverResponse][writeUnAuth] + duk_swap_top(ctx, -2); // [writeUnAuth][this] + for (i = 0; i < nargs; ++i) + { + duk_dup(ctx, i); // [writeUnAuth][this][...] + } + duk_call_method(ctx, nargs); + return(0); +} +duk_ret_t ILibDuktape_HttpStream_http_server_onUpgrade(duk_context *ctx) +{ + duk_push_this(ctx); // [HS] + duk_get_prop_string(ctx, -1, ILibduktape_HttpStream2HttpServer); // [HS][server] + duk_get_prop_string(ctx, -1, "emit"); // [HS][server][emit] + duk_swap_top(ctx, -2); // [HS][emit][this] + duk_push_string(ctx, "upgrade"); // [HS][emit][this][upgrade] + duk_dup(ctx, 0); // [HS][emit][this][upgrade][imsg] + duk_get_prop_string(ctx, -5, ILibDuktape_HTTP2PipedReadable); // [HS][emit][this][upgrade][imsg][sck] + duk_push_c_function(ctx, ILibDuktape_HttpStream_http_server_upgradeWebsocket, DUK_VARARGS); // [HS][emit][this][upgrade][imsg][sck][func] + duk_dup(ctx, -3); // [HS][emit][this][upgrade][imsg][sck][func][imsg] + duk_put_prop_string(ctx, -2, "imsg"); // [HS][emit][this][upgrade][imsg][sck][func] + duk_put_prop_string(ctx, -2, "upgradeWebSocket"); // [HS][emit][this][upgrade][imsg][sck] + duk_push_c_function(ctx, ILibDuktape_HttpStream_http_server_onUpgrade_digestWriteUnauth, DUK_VARARGS); + duk_dup(ctx, -3); // [HS][emit][this][upgrade][imsg][sck][func][imsg] + duk_put_prop_string(ctx, -2, "imsg"); // [HS][emit][this][upgrade][imsg][sck][func] + duk_put_prop_string(ctx, -2, "Digest_writeUnauthorized"); // [HS][emit][this][upgrade][imsg][sck] + duk_push_null(ctx); // [HS][emit][this][upgrade][imsg][sck][head] + duk_get_prop_string(ctx, -3, "headers"); + duk_pop(ctx); + duk_call_method(ctx, 4); duk_pop(ctx); // [HS] + return(0); +} +duk_ret_t ILibDuktape_HttpStream_http_server_onConnection_TLSConnect(duk_context *ctx) +{ + //duk_get_prop_string(ctx, -1, ILibDuktape_NS2HttpServer); // [NS][HS] + //duk_get_prop_string(ctx, -1, "emit"); // [NS][HS][emit] + //duk_swap_top(ctx, -2); // [NS][emit][this] + //duk_push_string(ctx, "connection"); // [NS][emit][this][connection] + //duk_dup(ctx, 0); // [NS][emit][this][connection][socket] + //if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.server.onConnection() => Error dispatching connection event "); } + + return(0); +} +duk_ret_t ILibDuktape_HttpStream_http_server_onConnectionTimeout(duk_context *ctx) +{ + void *cb = NULL; + duk_push_this(ctx); // [socket] + duk_get_prop_string(ctx, -1, ILibDuktape_Socket2HttpServer); // [socket][HttpServer] + if ((cb = Duktape_GetHeapptrProperty(ctx, -1, ILibDuktape_HttpServer_TimeoutCB)) != NULL) + { + // Callback was specified, so the callback MUST explictly handle the situation + duk_push_heapptr(ctx, cb); // [socket][HttpServer][func] + duk_swap_top(ctx, -2); // [socket][func][this] + duk_dup(ctx, -3); // [socket][func][this][socket] + duk_call_method(ctx, 1); duk_pop_2(ctx); // ... + } + else + { + // No callback was specified, so the timed out socket MUST be closed. + duk_pop(ctx); // [socket] + duk_get_prop_string(ctx, -1, "end"); // [socket][end] + duk_swap_top(ctx, -2); // [end][this] + duk_call_method(ctx, 0); duk_pop(ctx); // ... + } + return(0); +} +duk_ret_t ILibDuktape_HttpStream_http_server_onConnection(duk_context *ctx) +{ + duk_push_this(ctx); // [NS] + duk_get_prop_string(ctx, -1, ILibDuktape_NS2HttpServer); // [NS][HttpServer] + + // Check to see if we need to set a timeout + duk_get_prop_string(ctx, -1, "timeout"); // [NS][HttpServer][timeout] + if (duk_is_number(ctx, -1)) + { + duk_dup(ctx, 0); // [NS][HttpServer][timeout][socket] + duk_dup(ctx, -3); // [NS][HttpServer][timeout][socket][HttpServer] + duk_put_prop_string(ctx, -2, ILibDuktape_Socket2HttpServer); // [NS][HttpServer][timeout][socket] + duk_get_prop_string(ctx, -1, "setTimeout"); // [NS][HttpServer][timeout][socket][setTimeout] + duk_swap_top(ctx, -2); // [NS][HttpServer][timeout][setTimeout][this] + duk_get_int(ctx, -3); // [NS][HttpServer][timeout][setTimeout][this][value] + duk_push_c_function(ctx, ILibDuktape_HttpStream_http_server_onConnectionTimeout, DUK_VARARGS); // [setTimeout][this][value][callback] + duk_call_method(ctx, 2); duk_pop(ctx); // [NS][HttpServer][timeout] + } + duk_pop_2(ctx); // [NS] + + // Pipe: Socket => HttpStream + duk_dup(ctx, 0); // [NS][socket] + duk_get_prop_string(ctx, -1, "pipe"); // [NS][socket][pipe] + duk_dup(ctx, -2); // [NS][socket][pipe][this] + + duk_eval_string(ctx, "require('http').createStream();"); // [NS][socket][pipe][this][httpStream] + duk_get_prop_string(ctx, -5, ILibDuktape_NS2HttpServer); // [NS][socket][pipe][this][httpStream][httpServer] + duk_dup(ctx, -1); // [NS][socket][pipe][this][httpStream][httpServer][dup] + duk_put_prop_string(ctx, -3, ILibduktape_HttpStream2HttpServer); // [NS][socket][pipe][this][httpStream][httpServer] + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "checkContinue", -1, "checkContinue"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "checkExpectation", -1, "checkExpectation"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "clientError", -1, "clientError"); + //ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "close", -1, "close"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "connect", -1, "connect"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "request", -1, "request"); + ILibDuktape_EventEmitter_AddOnceEx3(ctx, -2, "upgrade", ILibDuktape_HttpStream_http_server_onUpgrade); + duk_pop(ctx); // [NS][socket][pipe][this][httpStream] + duk_call_method(ctx, 1); duk_pop_2(ctx); // [NS] + + duk_get_prop_string(ctx, -1, ILibDuktape_NS2HttpServer); // [NS][HS] + duk_get_prop_string(ctx, -1, "emit"); // [NS][HS][emit] + duk_swap_top(ctx, -2); // [NS][emit][this] + duk_push_string(ctx, "connection"); // [NS][emit][this][connection] + duk_dup(ctx, 0); // [NS][emit][this][connection][socket] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "server.onConnection() => Error dispatching connection event "); } + + return(0); +} +duk_ret_t ILibDuktape_HttpStream_http_server_listen(duk_context *ctx) +{ + int nargs = duk_get_top(ctx); + + duk_push_this(ctx); // [server] + duk_get_prop_string(ctx, -1, ILibDuktape_Http_Server2NetServer); // [server][ns] + + duk_get_prop_string(ctx, -1, "listen"); // [server][ns][listen] + duk_dup(ctx, -2); // [server][ns][listen][this] + if (nargs == 0) + { + // Nothing was specified, convert to Options + duk_push_object(ctx); // [server][ns][listen][this][options] + duk_push_int(ctx, 0); duk_put_prop_string(ctx, -2, "port"); + } + else + { + if (duk_is_object(ctx, 0)) + { + duk_dup(ctx, 0); // [server][ns][listen][this][options] + } + else + { + // Options weren't used, so lets convert it to Options + duk_push_object(ctx); // [server][ns][listen][this][options] + if (duk_is_number(ctx, 0)) + { + duk_dup(ctx, 0); duk_put_prop_string(ctx, -2, "port"); + } + else + { + return(ILibDuktape_Error(ctx, "server.listen(): Unknown parameter ")); + } + } + } + + duk_call_method(ctx, 1); duk_pop(ctx); // [server] + return(1); +} +duk_ret_t ILibDuktape_HttpStream_http_setTimeout(duk_context *ctx) +{ + int nargs = duk_get_top(ctx), i; + int timeout = 120000; + void *callback = NULL; + + for (i = 0; i < nargs; ++i) + { + if (duk_is_number(ctx, i)) { timeout = duk_require_int(ctx, i); } + if (duk_is_function(ctx, i)) { callback = duk_require_heapptr(ctx, i); } + } + + duk_push_this(ctx); // [server] + duk_push_int(ctx, timeout); // [server][timeout] + duk_put_prop_string(ctx, -2, "timeout"); // [server] + if (callback != NULL) + { + duk_push_heapptr(ctx, callback); // [server][cb] + duk_put_prop_string(ctx, -2, ILibDuktape_HttpServer_TimeoutCB); // [server] + } + return(1); +} +duk_ret_t ILibDuktape_HttpStream_http_server_address(duk_context *ctx) +{ + duk_push_this(ctx); // [httpServer] + if (!duk_has_prop_string(ctx, -1, ILibDuktape_Http_Server2NetServer)) { return(ILibDuktape_Error(ctx, "http.server.address(): Cannot call 'address' when listen was not called")); } + + duk_get_prop_string(ctx, -1, ILibDuktape_Http_Server2NetServer); // [httpServer][NS] + duk_get_prop_string(ctx, -1, "address"); // [httpServer][NS][address] + duk_swap_top(ctx, -2); // [httpServer][address][this] + duk_call_method(ctx, 0); // [httpServer][result] + return(1); +} +duk_ret_t ILibDuktape_HttpStream_http_createServer(duk_context *ctx) +{ + ILibDuktape_Http_Server *server; + + int nargs = duk_get_top(ctx); + duk_push_this(ctx); // [http/s] + int isHTTPS = Duktape_GetBooleanProperty(ctx, -1, "isHTTPS", 0); + duk_pop(ctx); + + duk_push_object(ctx); // [server] + duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_Http_Server)); // [server][fxBuffer] + server = (ILibDuktape_Http_Server*)Duktape_GetBuffer(ctx, -1, NULL); + duk_put_prop_string(ctx, -2, ILibDuktape_Http_Server_FixedBuffer); // [server] + + memset(server, 0, sizeof(ILibDuktape_Http_Server)); + server->ctx = ctx; + + ILibDuktape_WriteID(ctx, isHTTPS ? "https.server" : "http.server"); + ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx); + + ILibDuktape_EventEmitter_CreateEventEx(emitter, "checkContinue"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "checkExpectation"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "clientError"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "close"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "connection"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "request"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade"); + + duk_push_undefined(ctx); + duk_put_prop_string(ctx, -2, "timeout"); + + if (nargs > 0 && duk_is_function(ctx, 0)) + { + ILibDuktape_EventEmitter_AddOn(emitter, "request", duk_require_heapptr(ctx, 1)); + } + + ILibDuktape_CreateInstanceMethod(ctx, "close", ILibDuktape_HttpStream_http_server_close, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "listen", ILibDuktape_HttpStream_http_server_listen, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "setTimeout", ILibDuktape_HttpStream_http_setTimeout, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "address", ILibDuktape_HttpStream_http_server_address, 0); + + + // Now let's create a net.server or tls.server + if (isHTTPS) + { + duk_eval_string(ctx, "require('tls');"); // [server][tls] + } + else + { + duk_eval_string(ctx, "require('net');"); // [server][net] + } + duk_get_prop_string(ctx, -1, "createServer"); // [server][nettls][createServer] + duk_swap_top(ctx, -2); // [server][createServer][this] + + if (nargs > 0 && duk_is_object(ctx, 0)) + { + // Options was specified + duk_dup(ctx, 0); // [server][createServer][this][options] + } + duk_push_c_function(ctx, ILibDuktape_HttpStream_http_server_onConnection, DUK_VARARGS); + + duk_call_method(ctx, (nargs > 0 && duk_is_object(ctx, 0)) ? 2 : 1); // [server][netServer] + duk_dup(ctx, -2); // [server][netServer][server] + duk_put_prop_string(ctx, -2, ILibDuktape_NS2HttpServer); // [server][netServer] + duk_put_prop_string(ctx, -2, ILibDuktape_Http_Server2NetServer); // [server] + + return(1); +} + +typedef struct ILibDuktape_HttpStream_DispatchWrite_data +{ + ILibDuktape_HttpStream_Data *httpStream; + int bufferLen; + char buffer[]; +}ILibDuktape_HttpStream_DispatchWrite_data; + +duk_ret_t ILibDuktape_HttpStream_WriteSink_ChainSink_DynamicBuffer_WriteSink(duk_context *ctx) +{ + char *buffer; + duk_size_t bufferLen; + int beginPointer = 0; + int PAUSE = 0; + + ILibDuktape_HttpStream_Data *data; + duk_push_this(ctx); // [DynamicBuffer] + duk_get_prop_string(ctx, -1, "\xFF_HTTP"); + data = (ILibDuktape_HttpStream_Data*)duk_get_pointer(ctx, -1); + + buffer = (char*)Duktape_GetBuffer(ctx, 0, &bufferLen); + ILibWebClient_OnData(NULL, buffer, &beginPointer, (int)bufferLen, NULL, (void**)&(data->WCDO), &PAUSE); + + return(0); +} +duk_ret_t ILibDuktape_HttpStream_WriteSink_ChainSink_DynamicBuffer_EndSink(duk_context *ctx) +{ + return(0); +} +void ILibDuktape_HttpStream_WriteSink_ChainSink(void *chain, void *user) +{ + ILibDuktape_HttpStream_DispatchWrite_data *data = (ILibDuktape_HttpStream_DispatchWrite_data*)user; + duk_context *ctx = data->httpStream->DS->writableStream->ctx; + + if (data->httpStream->DynamicBuffer == NULL) + { + duk_push_heapptr(ctx, data->httpStream->DS->ParentObject); // [httpStream] + if (duk_peval_string(ctx, "require('DynamicBuffer')(4096);") != 0) // [httpStream][DynamicBuffer] + { + ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream.writeSink_chainSink->DynamicBuffer(): "); + duk_pop(ctx); // ... + return; + } + data->httpStream->DynamicBuffer = duk_get_heapptr(ctx, -1); // [httpStream][DynamicBuffer] + ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "data", ILibDuktape_HttpStream_WriteSink_ChainSink_DynamicBuffer_WriteSink); + ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "end", ILibDuktape_HttpStream_WriteSink_ChainSink_DynamicBuffer_EndSink); + duk_push_pointer(ctx, data->httpStream); // [httpStream][DynamicBuffer][ptr] + duk_put_prop_string(ctx, -2, "\xFF_HTTP"); // [httpStream][DynamicBuffer] + duk_put_prop_string(ctx, -2, "\xFF_DynamicBuffer"); // [httpStream] + duk_pop(ctx); // ... + } + + duk_push_external_buffer(ctx); // [extBuffer] + duk_config_buffer(ctx, -1, data->buffer, data->bufferLen); + + duk_push_heapptr(ctx, data->httpStream->DynamicBuffer); // [extBuffer][DynamicBuffer] + duk_get_prop_string(ctx, -1, "write"); // [extBuffer][DynamicBuffer][write] + duk_swap_top(ctx, -2); // [extBuffer][write][this] + duk_push_buffer_object(ctx, -3, 0, data->bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); // [extBuffer][write][this][buffer] + if (duk_pcall_method(ctx, 1) != 0) // [extBuffer][retVal] + { + ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream.WriteSink_ChainSink->DynamicBuffer.Write(): "); + duk_pop(ctx); // [extBuffer] + } + duk_pop(ctx); // ... + free(data); +} +ILibTransport_DoneState ILibDuktape_HttpStream_WriteSink(ILibDuktape_DuplexStream *DS, char *buffer, int bufferLen, void *user) +{ + ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)user; + + if (ILibIsRunningOnChainThread(data->chain) == 0) + { + // We need to context switch, because the event dispatch MUST be on the chain thread + ILibDuktape_HttpStream_DispatchWrite_data *tmp = (ILibDuktape_HttpStream_DispatchWrite_data*)ILibMemory_Allocate(sizeof(ILibDuktape_HttpStream_DispatchWrite_data) + bufferLen, 0, NULL, NULL); + tmp->httpStream = data; + tmp->bufferLen = bufferLen; + memcpy_s(tmp->buffer, bufferLen, buffer, bufferLen); + ILibChain_RunOnMicrostackThread(data->chain, ILibDuktape_HttpStream_WriteSink_ChainSink, tmp); + return(ILibTransport_DoneState_INCOMPLETE); + } + + // We're already on Chain Thread, so we can just directly write + int beginPointer = 0; + int PAUSE = 0; + int MustBuffer = 0; + ILibDuktape_WritableStream *stream = DS->writableStream; + + ILibWebClient_OnData(NULL, buffer, &beginPointer, (int)bufferLen, NULL, (void**)&(data->WCDO), &PAUSE); + + if ((bufferLen - beginPointer) > 0) + { + // Not all the data was consumed, so let's try to push the unprocess data back + if (stream->pipedReadable != NULL) + { + // Make a JavaScript call to readable.unshift() + duk_push_heapptr(stream->ctx, stream->pipedReadable); // [readable] + if (duk_has_prop_string(stream->ctx, -1, "unshift")) + { + duk_push_external_buffer(stream->ctx); // [readable][extBuffer] + duk_config_buffer(stream->ctx, -1, buffer + beginPointer, (int)bufferLen - beginPointer); + duk_swap_top(stream->ctx, -2); // [extBuffer][readable] + duk_get_prop_string(stream->ctx, -1, "unshift"); // [extBuffer][readable][unshift] + duk_swap_top(stream->ctx, -2); // [extBuffer][unshift][this] + duk_push_buffer_object(stream->ctx, -3, 0, (int)bufferLen - beginPointer, DUK_BUFOBJ_NODEJS_BUFFER);// [extBuffer][unshift][this][buffer] + if (duk_pcall_method(stream->ctx, 1) != 0) { MustBuffer = 1; } + duk_pop(stream->ctx); // ... + } + else + { + duk_pop(stream->ctx); + MustBuffer = 1; + } + } + else if (stream->pipedReadable_native != NULL && stream->pipedReadable_native->UnshiftHandler != NULL) + { + if (stream->pipedReadable_native->UnshiftHandler(stream->pipedReadable_native, (int)bufferLen - beginPointer, stream->pipedReadable_native->user) == 0) + { + MustBuffer = 1; + } + } + else + { + MustBuffer = 1; + } + if (MustBuffer != 0) + { + // We couldn't unshift unprocessed bytes, so we have to buffer it for later + // The good news is that we are on the Chain Thread, so we can have JavaScript manage the memory + duk_push_heapptr(stream->ctx, stream->obj); // [HttpStream] + if (duk_has_prop_string(stream->ctx, -1, IILibDuktape_HTTP_HoldingQueue)) + { + duk_get_prop_string(stream->ctx, -1, IILibDuktape_HTTP_HoldingQueue); // [HttpStream][Holding] + } + else + { + duk_push_array(stream->ctx); // [HttpStream][Holding] + duk_dup(stream->ctx, -1); // [HttpStream][Holding][Holding] + duk_put_prop_string(stream->ctx, -3, IILibDuktape_HTTP_HoldingQueue); // [HttpStream][Holding] + } + + duk_get_prop_string(stream->ctx, -1, "push"); // [HttpStream][Holding][push] + duk_swap_top(stream->ctx, -2); // [HttpStream][push][this] + duk_push_fixed_buffer(stream->ctx, (int)bufferLen - beginPointer); // [HttpStream][push][this][buffer] + memcpy_s(Duktape_GetBuffer(stream->ctx, -1, NULL), (int)bufferLen - beginPointer, buffer + beginPointer, (int)bufferLen - beginPointer); + if (duk_pcall_method(stream->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "HttpStream.Write() -> Error calling Array.push() "); } + duk_pop_2(stream->ctx); // ... + + return(ILibTransport_DoneState_INCOMPLETE); + } + else + { + // We successfully unshifted bytes, so we're done here + return(ILibTransport_DoneState_COMPLETE); + } + } + else + { + // Consumed All Data + return(ILibTransport_DoneState_COMPLETE); + } +} +void ILibDuktape_HttpStream_EndSink(ILibDuktape_DuplexStream *stream, void *user) +{ + ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)user; + + if (data->bodyStream != NULL) + { + ILibDuktape_readableStream_WriteEnd(data->bodyStream); + } +} +void ILibDuktape_HttpStream_ServerResponse_WriteImplicitHeaders(void *chain, void *user) +{ + int retVal; + ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State *state = (ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State*)user; + if (chain != NULL && !ILibDuktape_IsPointerValid(chain, state->serverResponseObj)) { free(user); } + + // We are on Microstack Thread, so we can access the JS object, and write the implicit headers + duk_push_heapptr(state->ctx, state->serverResponseObj); // [SR] + duk_get_prop_string(state->ctx, -1, "writeHead"); // [SR][writeHead] + duk_swap_top(state->ctx, -2); // [writeHead][this] + duk_get_prop_string(state->ctx, -1, "statusCode"); // [writeHead][this][statusCode] + duk_get_prop_string(state->ctx, -2, "statusMessage"); // [writeHead][this][statusCode][statusMessage] + duk_get_prop_string(state->ctx, -3, ILibDuktape_SR2ImplicitHeaders); // [writeHead][this][statusCode][statusMessage][headers] + if (state->endBytes >= 0) // -1: Unknown, 0: No Data, >0: Content-Length + { + duk_push_string(state->ctx, "Content-Length"); // [writeHead][this][statusCode][statusMessage][headers][name] + duk_push_int(state->ctx, state->endBytes); // [writeHead][this][statusCode][statusMessage][headers][name][value] + duk_put_prop(state->ctx, -3); // [writeHead][this][statusCode][statusMessage][headers] + } + else + { + if (state->chunk) + { + duk_push_string(state->ctx, "Transfer-Encoding"); // [writeHead][this][statusCode][statusMessage][headers][name] + duk_push_string(state->ctx, "chunked"); // [writeHead][this][statusCode][statusMessage][headers][name][value] + duk_put_prop(state->ctx, -3); // [writeHead][this][statusCode][statusMessage][headers] + } + } + if ((retVal = duk_pcall_method(state->ctx, 3)) != 0) { ILibDuktape_Process_UncaughtExceptionEx(state->ctx, "http.serverResponse.writeImplicitHeaders(): Error "); } + duk_pop(state->ctx); // ... + + if (state->bufferLen > 0 && retVal == 0) + { + duk_push_external_buffer(state->ctx); // [ext] + duk_push_heapptr(state->ctx, state->writeStream); // [ext][stream] + duk_get_prop_string(state->ctx, -1, "write"); // [ext][stream][write] + duk_swap_top(state->ctx, -2); // [ext][write][this] + + if (state->endBytes > 0 || state->chunk == 0) + { + // We can just directly write the data + duk_config_buffer(state->ctx, -3, state->buffer, state->bufferLen); + duk_push_buffer_object(state->ctx, -3, 0, state->bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][write][this][buffer] + retVal = duk_pcall_method(state->ctx, 1); + duk_pop_2(state->ctx); // ... + } + else + { + // We must chunk encode the data + char *tmp = ILibMemory_AllocateA(state->bufferLen + 16); + int i = sprintf_s(tmp, state->bufferLen + 16, "%X\r\n", (unsigned int)state->bufferLen); + memcpy_s(tmp + i, state->bufferLen, state->buffer, state->bufferLen); + i += ((int)state->bufferLen + sprintf_s(tmp + i + state->bufferLen, 16 - i, "\r\n")); + duk_config_buffer(state->ctx, -3, tmp, i); + + duk_push_buffer_object(state->ctx, -3, 0, i, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][write][this][buffer] + retVal = duk_pcall_method(state->ctx, 1); + duk_pop_2(state->ctx); // ... + } + } + + if (retVal == 0 && chain != NULL) + { + // Since we context switched to get here, we must signal that we are done + ILibDuktape_WritableStream *WS = ILibDuktape_DuplexStream_GetNativeWritable(state->ctx, state->serverResponseObj); + if (WS != NULL) + { + ILibDuktape_WritableStream_Ready(WS); + } + } +} +int ILibDuktape_HttpStream_ServerResponse_WriteSink_Flush(struct ILibDuktape_WritableStream *stream, void *user) +{ + ILibDuktape_WritableStream_Ready((struct ILibDuktape_WritableStream *)user); + return(0); +} +duk_ret_t ILibDuktape_HttpStream_ServerResponse_WriteSink_JS_Flushed(duk_context *ctx) +{ + duk_push_this(ctx); // [stream] + duk_get_prop_string(ctx, -1, ILibDuktape_SRUSER); // [stream][ptr] + ILibDuktape_WritableStream *WS = (ILibDuktape_WritableStream*)duk_get_pointer(ctx, -1); + + ILibDuktape_WritableStream_Ready(WS); + return(0); +} +void ILibDuktape_HttpStream_ServerResponse_WriteSink_Chain(void *chain, void *user) +{ + ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State *state = (ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State*)user; + + // We good to go + int noDrain; + duk_push_external_buffer(state->ctx); // [ext] + duk_push_heapptr(state->ctx, state->writeStream); // [ext][stream] + duk_get_prop_string(state->ctx, -1, "write"); // [ext][stream][write] + duk_dup(state->ctx, -2); // [ext][stream][write][this] + if (state->chunk) + { + char tmp[16]; + int tmpLen = sprintf_s(tmp, sizeof(tmp), "%X\r\n", (int)state->bufferLen); + duk_config_buffer(state->ctx, -4, tmp, tmpLen); + duk_push_buffer_object(state->ctx, -4, 0, tmpLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][stream][write][this][buffer] + if (duk_pcall_method(state->ctx, 1) != 0) { duk_pop_2(state->ctx); return; } + duk_pop(state->ctx); // [ext][stream] + duk_get_prop_string(state->ctx, -1, "write"); // [ext][stream][write] + duk_dup(state->ctx, -2); // [ext][stream][write][this] + } + duk_config_buffer(state->ctx, -4, state->buffer, state->bufferLen); + duk_push_buffer_object(state->ctx, -4, 0, state->bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][stream][write][this][buffer] + if (duk_pcall_method(state->ctx, 1) != 0) { duk_pop_2(state->ctx); return; } + noDrain = duk_get_int(state->ctx, -1); + duk_pop(state->ctx); // [ext][stream] + if (state->chunk) + { + char tmp[] = "\r\n"; + duk_get_prop_string(state->ctx, -1, "write"); // [ext][stream][write] + duk_dup(state->ctx, -2); // [ext][stream][write][this] + + duk_config_buffer(state->ctx, -4, tmp, 2); + duk_push_buffer_object(state->ctx, -4, 0, state->bufferLen, DUK_BUFOBJ_NODEJS_BUFFER);// [ext][stream][write][this][buffer] + if (duk_pcall_method(state->ctx, 1) != 0) { duk_pop_2(state->ctx); return; } + noDrain = duk_get_int(state->ctx, -1); + duk_pop(state->ctx); // [ext][stream] + } + if (!noDrain) + { + duk_push_pointer(state->ctx, ILibDuktape_DuplexStream_GetNativeWritable(state->ctx, state->serverResponseObj)); + duk_put_prop_string(state->ctx, -2, ILibDuktape_SRUSER); + ILibDuktape_EventEmitter_AddOnceEx3(state->ctx, -1, "drain", ILibDuktape_HttpStream_ServerResponse_WriteSink_JS_Flushed); + } + else + { + ILibDuktape_WritableStream_Ready(ILibDuktape_DuplexStream_GetNativeWritable(state->ctx, state->serverResponseObj)); + } + free(state); +} +ILibTransport_DoneState ILibDuktape_HttpStream_ServerResponse_WriteSink(struct ILibDuktape_WritableStream *stream, char *buffer, int bufferLen, void *user) +{ + ILibDuktape_HttpStream_ServerResponse_State *state = (ILibDuktape_HttpStream_ServerResponse_State*)user; + if (state->implicitHeaderHandling) + { + state->implicitHeaderHandling = 0; + if (ILibIsRunningOnChainThread(state->chain)) + { + ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State *tmp = (ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State*)ILibMemory_AllocateA(sizeof(ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State) + bufferLen); + memset(tmp, 0, sizeof(ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State)); + tmp->ctx = stream->ctx; + tmp->serverResponseObj = stream->obj; + tmp->writeStream = state->writeStream; + tmp->endBytes = stream->endBytes; + tmp->chunk = state->chunkSupported; + if (bufferLen > 0) { memcpy_s(tmp->buffer, bufferLen, buffer, bufferLen); } + + ILibDuktape_HttpStream_ServerResponse_WriteImplicitHeaders(NULL, &tmp); + return(ILibTransport_DoneState_COMPLETE); + } + else + { + ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State *buffered = (ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State*)ILibMemory_Allocate(sizeof(ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State) + bufferLen, 0, NULL, NULL); + memset(buffered, 0, sizeof(ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State)); + buffered->ctx = stream->ctx; + buffered->serverResponseObj = stream->obj; + buffered->writeStream = state->writeStream; + buffered->bufferLen = bufferLen; + buffered->endBytes = stream->endBytes; + buffered->chunk = state->chunkSupported; + if (bufferLen > 0) { memcpy_s(buffered->buffer, bufferLen, buffer, bufferLen); } + + ILibChain_RunOnMicrostackThreadEx(state->chain, ILibDuktape_HttpStream_ServerResponse_WriteImplicitHeaders, buffered); + return(ILibTransport_DoneState_INCOMPLETE); + } + } + else + { + // Headers were already sent, so we can just send data along + ILibTransport_DoneState retVal; + if (state->nativeWriteStream != NULL) + { + state->nativeWriteStream->OnWriteFlushEx = ILibDuktape_HttpStream_ServerResponse_WriteSink_Flush; + state->nativeWriteStream->OnWriteFlushEx_User = stream; + if (state->chunkSupported && !state->contentLengthSpecified) + { + char tmp[16]; + int tmpLen = sprintf_s(tmp, sizeof(tmp), "%X\r\n", (int)bufferLen); + state->nativeWriteStream->WriteSink(state->nativeWriteStream, tmp, tmpLen, state->nativeWriteStream->WriteSink_User); + } + retVal = state->nativeWriteStream->WriteSink(state->nativeWriteStream, buffer, (int)bufferLen, state->nativeWriteStream->WriteSink_User); + if (state->chunkSupported && !state->contentLengthSpecified) + { + retVal = state->nativeWriteStream->WriteSink(state->nativeWriteStream, "\r\n", 2, state->nativeWriteStream->WriteSink_User); + } + } + else + { + // Upstream is a pure ECMA Script Object + if (ILibIsRunningOnChainThread(state->chain)) + { + // We good to go + int noDrain; + duk_push_external_buffer(stream->ctx); // [ext] + duk_push_heapptr(stream->ctx, state->writeStream); // [ext][stream] + duk_get_prop_string(stream->ctx, -1, "write"); // [ext][stream][write] + duk_dup(stream->ctx, -2); // [ext][stream][write][this] + if (state->chunkSupported && !state->contentLengthSpecified) + { + char tmp[16]; + int tmpLen = sprintf_s(tmp, sizeof(tmp), "%X\r\n", (int)bufferLen); + duk_config_buffer(stream->ctx, -4, tmp, tmpLen); + duk_push_buffer_object(stream->ctx, -4, 0, tmpLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][stream][write][this][buffer] + if (duk_pcall_method(stream->ctx, 1) != 0) { duk_pop_2(stream->ctx); return(ILibTransport_DoneState_ERROR); } + duk_pop(stream->ctx); // [ext][stream] + duk_get_prop_string(stream->ctx, -1, "write"); // [ext][stream][write] + duk_dup(stream->ctx, -2); // [ext][stream][write][this] + } + duk_config_buffer(stream->ctx, -4, buffer, bufferLen); + duk_push_buffer_object(stream->ctx, -4, 0, bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][stream][write][this][buffer] + if (duk_pcall_method(stream->ctx, 1) != 0) { duk_pop_2(stream->ctx); return(ILibTransport_DoneState_ERROR); } + noDrain = duk_get_int(stream->ctx, -1); + duk_pop(stream->ctx); // [ext][stream] + if (state->chunkSupported && !state->contentLengthSpecified) + { + char tmp[] = "\r\n"; + duk_get_prop_string(stream->ctx, -1, "write"); // [ext][stream][write] + duk_dup(stream->ctx, -2); // [ext][stream][write][this] + + duk_config_buffer(stream->ctx, -4, tmp, 2); + duk_push_buffer_object(stream->ctx, -4, 0, bufferLen, DUK_BUFOBJ_NODEJS_BUFFER);// [ext][stream][write][this][buffer] + if (duk_pcall_method(stream->ctx, 1) != 0) { duk_pop_2(stream->ctx); return(ILibTransport_DoneState_ERROR); } + noDrain = duk_get_int(stream->ctx, -1); + duk_pop(stream->ctx); // [ext][stream] + } + if (!noDrain) + { + duk_push_pointer(stream->ctx, stream); + duk_put_prop_string(stream->ctx, -2, ILibDuktape_SRUSER); + ILibDuktape_EventEmitter_AddOnceEx3(stream->ctx, -1, "drain", ILibDuktape_HttpStream_ServerResponse_WriteSink_JS_Flushed); + retVal = ILibTransport_DoneState_INCOMPLETE; + } + else + { + retVal = ILibTransport_DoneState_COMPLETE; + } + } + else + { + // Gotta context switch + ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State *data = (ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State*)ILibMemory_Allocate(sizeof(ILibDuktape_HttpStream_ServerResponse_BufferedImplicit_State) + bufferLen, 0, NULL, NULL); + data->chunk = state->chunkSupported; + data->ctx = stream->ctx; + data->endBytes = stream->endBytes; + data->serverResponseObj = stream->obj; + data->writeStream = state->writeStream; + data->bufferLen = bufferLen; + memcpy_s(data->buffer, bufferLen, buffer, bufferLen); + ILibChain_RunOnMicrostackThreadEx(state->chain, ILibDuktape_HttpStream_ServerResponse_WriteSink_Chain, data); + return(ILibTransport_DoneState_INCOMPLETE); + } + } + return(retVal); + } +} +void ILibDuktape_HttpStream_ServerResponse_EndSink_Chain(void *chain, void *user) +{ + struct ILibDuktape_WritableStream *stream = (struct ILibDuktape_WritableStream*)user; + + duk_push_heapptr(stream->ctx, stream->obj); // [serverResponse] + duk_get_prop_string(stream->ctx, -1, "writeHead"); // [serverResponse][writeHead] + duk_swap_top(stream->ctx, -2); // [writeHead][this] + duk_get_prop_string(stream->ctx, -1, "statusCode"); // [writeHead][this][statusCode] + duk_get_prop_string(stream->ctx, -2, "statusMessage"); // [writeHead][this][statusCode][statusMessage] + duk_get_prop_string(stream->ctx, -3, ILibDuktape_SR2ImplicitHeaders); // [writeHead][this][statusCode][statusMessage][headers] + duk_push_string(stream->ctx, "Content-Length"); // [writeHead][this][statusCode][statusMessage][headers][name] + duk_push_int(stream->ctx, 0); // [writeHead][this][statusCode][statusMessage][headers][name][value] + duk_put_prop(stream->ctx, -3); // [writeHead][this][statusCode][statusMessage][headers] + if (duk_pcall_method(stream->ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "http.serverResponse.end(): Error writing implicit headers "); } + duk_pop(stream->ctx); // ... + + // Need to reset HttpStream + ILibDuktape_serverResponse_resetHttpStream(stream->ctx, stream->obj); +} +void ILibDuktape_HttpStream_ServerResponse_EndSink_ZeroChunk_Chain(void *chain, void *user) +{ + ILibDuktape_HttpStream_ServerResponse_State *state = (ILibDuktape_HttpStream_ServerResponse_State*)user; + + if (state->chunkSupported && !state->contentLengthSpecified) + { + // Send zero size chunk + char tmp[] = "0\r\n\r\n"; + if (state->nativeWriteStream != NULL) + { + state->nativeWriteStream->OnWriteFlushEx = NULL; + state->nativeWriteStream->OnWriteFlushEx_User = NULL; + state->nativeWriteStream->WriteSink(state->nativeWriteStream, tmp, 5, state->nativeWriteStream->WriteSink_User); + } + else + { + duk_push_external_buffer(state->ctx); + duk_config_buffer(state->ctx, -1, tmp, 5); // [ext] + duk_push_heapptr(state->ctx, state->writeStream); // [ext][stream] + duk_get_prop_string(state->ctx, -1, "write"); // [ext][stream][write] + duk_swap_top(state->ctx, -2); // [ext][write][this] + duk_push_buffer_object(state->ctx, -3, 0, 5, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][write][this][buffer] + if (duk_pcall_method(state->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(state->ctx, "http.serverResponse.end(): Error writing upstream "); } + duk_pop_2(state->ctx); // ... + } + } + else + { + if (!state->chunkSupported) + { + // Close the connection + if (state->nativeWriteStream != NULL) + { + state->nativeWriteStream->EndSink(state->nativeWriteStream, state->nativeWriteStream->WriteSink_User); + } + else + { + duk_push_heapptr(state->ctx, state->writeStream); // [stream] + duk_get_prop_string(state->ctx, -1, "end"); // [stream][end] + duk_swap_top(state->ctx, -2); // [end][this] + if (duk_pcall_method(state->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(state->ctx, "http.serverResponse.end(): Error ending upstream "); } + duk_pop(state->ctx); // ... + } + } + } + + // Need to reset HttpStream + ILibDuktape_serverResponse_resetHttpStream(state->ctx, state->serverResponse); +} +void ILibDuktape_HttpStream_ServerResponse_EndSink(struct ILibDuktape_WritableStream *stream, void *user) +{ + ILibDuktape_HttpStream_ServerResponse_State *state = (ILibDuktape_HttpStream_ServerResponse_State*)user; + + if (state->implicitHeaderHandling) + { + if (ILibIsRunningOnChainThread(state->chain)) + { + duk_push_this(stream->ctx); // [serverResponse] + duk_get_prop_string(stream->ctx, -1, "writeHead"); // [serverResponse][writeHead] + duk_swap_top(stream->ctx, -2); // [writeHead][this] + duk_get_prop_string(stream->ctx, -1, "statusCode"); // [writeHead][this][statusCode] + duk_get_prop_string(stream->ctx, -2, "statusMessage"); // [writeHead][this][statusCode][statusMessage] + duk_get_prop_string(stream->ctx, -3, ILibDuktape_SR2ImplicitHeaders); // [writeHead][this][statusCode][statusMessage][headers] + duk_push_string(stream->ctx, "Content-Length"); // [writeHead][this][statusCode][statusMessage][headers][name] + duk_push_int(stream->ctx, 0); // [writeHead][this][statusCode][statusMessage][headers][name][value] + duk_put_prop(stream->ctx, -3); // [writeHead][this][statusCode][statusMessage][headers] + if (duk_pcall_method(stream->ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "http.serverResponse.end(): Error writing implicit headers "); } + duk_pop(stream->ctx); // ... + + // Need to reset HttpStream + ILibDuktape_serverResponse_resetHttpStream(stream->ctx, stream->obj); + } + else + { + // Need to context switch before sending Implicit Headers + ILibChain_RunOnMicrostackThreadEx(state->chain, ILibDuktape_HttpStream_ServerResponse_EndSink_Chain, stream); + } + } + else + { + // Headers already sent... + if (state->nativeWriteStream != NULL) + { + ILibDuktape_HttpStream_ServerResponse_EndSink_ZeroChunk_Chain(state->chain, state); + } + else + { + ILibChain_RunOnMicrostackThread(state->chain, ILibDuktape_HttpStream_ServerResponse_EndSink_ZeroChunk_Chain, state); + } + } +} +duk_ret_t ILibDuktape_HttpStream_ServerResponse_writeHead(duk_context *ctx) +{ + int nargs = duk_get_top(ctx); + int statusCode = duk_require_int(ctx, 0); + char *statusMessage = NULL; + duk_size_t statusMessageLen = 0; + void *headers = NULL; + int i; + int contentLengthSpecified = 0; + + char *buffer = NULL; + duk_size_t bufferLen = 0, len = 0; + if (statusCode < 100 || statusCode > 999) { return(ILibDuktape_Error(ctx, "http.serverResponse.writeHead(): Invalid status code")); } + + for (i = 1; i < nargs; ++i) + { + if (duk_is_string(ctx, i)) { statusMessage = (char*)duk_get_lstring(ctx, i, &statusMessageLen); } + if (duk_is_object(ctx, i)) { headers = duk_require_heapptr(ctx, i); } + } + + if (statusMessage == NULL) + { + switch (statusCode) + { + case 100: + statusMessage = "Continue"; + statusMessageLen = 8; + break; + case 200: + statusMessage = "Bad Request"; + statusMessageLen = 11; + break; + case 401: + statusMessage = "Unauthorized"; + statusMessageLen = 12; + break; + case 404: + statusMessage = "Not Found"; + statusMessageLen = 9; + break; + case 500: + statusMessage = "Internal Server Error"; + statusMessageLen = 21; + break; + default: + statusMessage = "Unspecified"; + statusMessageLen = 11; + break; + } + } + + for (i = 0; i < 2; ++i) + { + if (buffer == NULL) + { + bufferLen = 15 + statusMessageLen; //'HTTP/1.1 XXX statusMessage\r\n' + } + else + { + len += sprintf_s(buffer + len, bufferLen - len, "HTTP/1.1 %d %s\r\n", statusCode, statusMessage); + } + if (headers != NULL) + { + duk_push_heapptr(ctx, headers); // [headers] + duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); // [headers][enum] + while (duk_next(ctx, -1, 1)) // [headers][enum][key][value] + { + char *key, *value; + duk_size_t keyLen, valueLen; + key = (char*)duk_get_lstring(ctx, -2, &keyLen); + if (duk_is_string(ctx, -1)) + { + value = (char*)duk_get_lstring(ctx, -1, &valueLen); + } + else + { + duk_get_prop_string(ctx, -1, "toString"); // [key][value][toString] + duk_swap_top(ctx, -2); // [key][toString][this] + duk_call_method(ctx, 0); // [key][valueString] + value = (char*)duk_get_lstring(ctx, -1, &valueLen); + } + if (buffer == NULL) + { + bufferLen += (keyLen + 2 + valueLen + 2); // key: value\r\n + if (keyLen == 14 && strncasecmp(key, "Content-Length", 14) == 0) { contentLengthSpecified = 1; } + } + else + { + len += sprintf_s(buffer + len, bufferLen - len, "%s: %s\r\n", key, value); + } + duk_pop_2(ctx); // [headers][enum] + } + } + if (buffer == NULL) + { + bufferLen += 2; // End of Headers + } + else + { + len += sprintf_s(buffer + len, bufferLen - len, "\r\n"); + } + if (buffer == NULL) { ++bufferLen; buffer = ILibMemory_AllocateA(bufferLen); } + } + + duk_push_this(ctx); // [SR] + duk_get_prop_string(ctx, -1, ILibDuktape_SR2State); // [SR][state] + ((ILibDuktape_HttpStream_ServerResponse_State*)Duktape_GetBuffer(ctx, -1, NULL))->implicitHeaderHandling = 0; + ((ILibDuktape_HttpStream_ServerResponse_State*)Duktape_GetBuffer(ctx, -1, NULL))->contentLengthSpecified = contentLengthSpecified; + duk_pop(ctx); // [SR] + + + duk_push_external_buffer(ctx); // [SR][ext] + duk_config_buffer(ctx, -1, buffer, bufferLen-1); + duk_get_prop_string(ctx, -2, ILibDuktape_SR2WS); // [SR][ext][WS] + duk_get_prop_string(ctx, -1, "write"); // [SR][ext][WS][write] + duk_swap_top(ctx, -2); // [SR][ext][write][this] + duk_push_buffer_object(ctx, -3, 0, bufferLen-1, DUK_BUFOBJ_NODEJS_BUFFER);// [SR][ext][write][this][buffer] + + duk_call_method(ctx, 1); // [SR][ext][retVal] + return(0); +} +duk_ret_t ILibDuktape_HttpStream_ServerResponse_setHeader(duk_context *ctx) +{ + duk_push_this(ctx); // [SR] + duk_get_prop_string(ctx, -1, ILibDuktape_SR2ImplicitHeaders); // [SR][headers] + duk_dup(ctx, 0); // [SR][headers][name] + duk_dup(ctx, 1); // [SR][headers][name][value] + duk_put_prop(ctx, -3); // [SR][headers] + return(0); +} +duk_ret_t ILibDuktape_HttpStream_ServerResponse_removeHeader(duk_context *ctx) +{ + duk_push_this(ctx); // [SR] + duk_get_prop_string(ctx, -1, ILibDuktape_SR2ImplicitHeaders); // [SR][headers] + duk_dup(ctx, 0); // [SR][headers][name] + duk_del_prop(ctx, -2); // [SR][headers] + return(0); +} +duk_ret_t ILibDuktape_HttpStream_ServerResponse_Digest_SendUnauthorized(duk_context *ctx) +{ + int nargs = duk_get_top(ctx); + long long nonceExpiration = ILibGetUptime() + (long long)(900000); // 15 minutes + char nonce[33]; + char opaque[17]; + + char *realm; + duk_size_t realmLen, htmlLen = 0; + void *hptr; + + if (nargs > 0) + { + duk_get_lstring(ctx, 1, &htmlLen); + } + + + duk_push_this(ctx); // [serverResponse] + duk_get_prop_string(ctx, -1, ILibDuktape_SR2HttpStream); // [serverResponse][httpStream] + duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2HTTP); // [serverResponse][httpStream][http] + hptr = duk_get_heapptr(ctx, -1); + + util_tohex((char*)&nonceExpiration, 8, opaque); + realm = (char*)duk_get_lstring(ctx, 0, &realmLen); + ILibDuktape_Digest_CalculateNonce(ctx, hptr, nonceExpiration, opaque, 16, nonce); + + duk_push_this(ctx); // [serverResponse] + duk_get_prop_string(ctx, -1, "writeHead"); // [serverResponse][writeHead] + duk_swap_top(ctx, -2); // [writeHead][this] + duk_push_int(ctx, 401); // [writeHead][this][401] + duk_push_string(ctx, "Unauthorized"); // [writeHead][this][401][Unauthorized] + duk_push_object(ctx); // [writeHead][this][401][Unauthorized][headers] + duk_push_string(ctx, "WWW-Authenticate"); // [writeHead][this][401][Unauthorized][headers][name] + int wwwLen = sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "Digest realm=\"%s\", nonce=\"%s\", opaque=\"%s\"", realm, nonce, opaque); + duk_push_lstring(ctx, ILibScratchPad, wwwLen); // [writeHead][this][401][Unauthorized][headers][name][value] + duk_put_prop(ctx, -3); // [writeHead][this][401][Unauthorized][headers] + if (htmlLen > 0) + { + duk_push_string(ctx, "Content-Type"); // [writeHead][this][401][Unauthorized][headers][name] + duk_push_string(ctx, "text/html"); // [writeHead][this][401][Unauthorized][headers][name][value] + duk_put_prop(ctx, -3); // [writeHead][this][401][Unauthorized][headers] + } + duk_push_string(ctx, "Content-Length"); // [writeHead][this][401][Unauthorized][headers][name] + duk_push_int(ctx, (int)htmlLen); // [writeHead][this][401][Unauthorized][headers][name][value] + duk_put_prop(ctx, -3); // [writeHead][this][401][Unauthorized][headers] + + duk_call_method(ctx, 3); duk_pop(ctx); // ... + + duk_push_this(ctx); // [serverResponse] + duk_get_prop_string(ctx, -1, "end"); // [serverResponse][end] + duk_swap_top(ctx, -2); // [end][this] + if (htmlLen > 0) + { + duk_dup(ctx, 1); // [end][this][html] + } + duk_call_method(ctx, htmlLen > 0 ? 1 : 0); duk_pop(ctx); // ... + return(0); +} +duk_ret_t ILibDuktape_HttpStream_ServerResponse_writeContinue(duk_context *ctx) +{ + duk_push_this(ctx); // [serverResponse] + duk_get_prop_string(ctx, -1, "writeHead"); // [serverResponse][writeHead] + duk_swap_top(ctx, -2); // [writeHead][this] + duk_push_int(ctx, 100); // [writeHead][this][100] + duk_push_string(ctx, "Continue"); // [writeHead][this][100][continue] + duk_call_method(ctx, 2); + return(0); +} +void ILibDuktape_HttpStream_ServerResponse_PUSH(duk_context *ctx, void* writeStream, ILibHTTPPacket *header, void *httpStream) +{ + ILibDuktape_HttpStream_ServerResponse_State *state; + + duk_push_object(ctx); // [resp] + duk_push_heapptr(ctx, httpStream); // [resp][httpStream] + duk_put_prop_string(ctx, -2, ILibDuktape_SR2HttpStream); // [resp] + ILibDuktape_PointerValidation_Init(ctx); + ILibDuktape_WriteID(ctx, "http.serverResponse"); + duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_HttpStream_ServerResponse_State)); // [resp][state] + state = (ILibDuktape_HttpStream_ServerResponse_State*)Duktape_GetBuffer(ctx, -1, NULL); + duk_put_prop_string(ctx, -2, ILibDuktape_SR2State); // [resp] + memset(state, 0, sizeof(ILibDuktape_HttpStream_ServerResponse_State)); + state->ctx = ctx; + state->serverResponse = duk_get_heapptr(ctx, -1); + state->implicitHeaderHandling = 1; + state->chain = Duktape_GetChain(ctx); + state->writeStream = writeStream; + state->nativeWriteStream = ILibDuktape_DuplexStream_GetNativeWritable(ctx, writeStream); + state->chunkSupported = ILibDuktape_Headers_IsChunkSupported(header); + duk_push_object(ctx); // [resp][implicitHeaders] + duk_put_prop_string(ctx, -2, ILibDuktape_SR2ImplicitHeaders); // [resp] + + duk_push_int(ctx, 200); + duk_put_prop_string(ctx, -2, "statusCode"); + duk_push_string(ctx, "OK"); + duk_put_prop_string(ctx, -2, "statusMessage"); + + duk_push_heapptr(ctx, writeStream); + duk_put_prop_string(ctx, -2, ILibDuktape_SR2WS); + + ILibDuktape_WritableStream_Init(ctx, ILibDuktape_HttpStream_ServerResponse_WriteSink, ILibDuktape_HttpStream_ServerResponse_EndSink, state); + ILibDuktape_CreateInstanceMethod(ctx, "writeHead", ILibDuktape_HttpStream_ServerResponse_writeHead, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "writeContinue", ILibDuktape_HttpStream_ServerResponse_writeContinue, 0); + ILibDuktape_CreateInstanceMethod(ctx, "setHeader", ILibDuktape_HttpStream_ServerResponse_setHeader, 2); + ILibDuktape_CreateInstanceMethod(ctx, "removeHeader", ILibDuktape_HttpStream_ServerResponse_removeHeader, 1); + ILibDuktape_CreateInstanceMethod(ctx, "Digest_writeUnauthorized", ILibDuktape_HttpStream_ServerResponse_Digest_SendUnauthorized, DUK_VARARGS); +} + +int ILibDuktape_Digest_IsCorrectRealmAndNonce(duk_context *ctx, void *IMSG, char* realm, int realmLen) +{ + char* auth; + duk_size_t authLen; + char* userRealm = NULL; + int userRealmLen; + char* nonce = NULL; + char* opaque = NULL; + int opaqueLen, nonceLen; + long long current = ILibGetUptime(); + char expiration[8]; + char calculatedNonce[33]; + void *DigestTable = ILibInitHashTree_CaseInSensitiveEx(ILibMemory_AllocateA(8000)); + void *hptr; + + duk_push_heapptr(ctx, IMSG); // [IMSG] + duk_get_prop_string(ctx, -1, ILibDuktape_IMSG2HttpStream); // [IMSG][httpStream] + duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2HTTP); // [IMSG][httpStream][http] + hptr = duk_get_heapptr(ctx, -1); + duk_pop_2(ctx); // [IMSG] + duk_get_prop_string(ctx, -1, "headers"); // [IMSG][headers] + auth = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "Authorization", "", &authLen); + duk_pop_2(ctx); // ... + + ILibWebServer_Digest_ParseAuthenticationHeader(DigestTable, auth, (int)authLen); + ILibGetEntryEx(DigestTable, "realm", 5, (void**)&userRealm, &userRealmLen); + ILibGetEntryEx(DigestTable, "nonce", 5, (void**)&nonce, &nonceLen); + ILibGetEntryEx(DigestTable, "opaque", 6, (void**)&opaque, &opaqueLen); + + if (opaque != NULL && userRealm != NULL && userRealmLen == realmLen && strncmp(userRealm, realm, realmLen) == 0) + { + // Realm is correct, now check the Nonce & Opaque Values + if (opaqueLen != 16) { return 0; } // Invalid Opaque Block + + util_hexToBuf(opaque, 16, expiration); + if (((long long*)expiration)[0] < current) { return 0; } // Opaque Block Expired + + ILibDuktape_Digest_CalculateNonce(ctx, hptr, ((long long*)expiration)[0], opaque, opaqueLen, calculatedNonce); + return((nonceLen == 32 && strncmp(nonce, calculatedNonce, 32)) == 0 ? 1 : 0); + } + else + { + return 0; + } +} + +duk_ret_t ILibDuktape_HttpStream_IncomingMessage_Digest_IsAuthenticated(duk_context *ctx) +{ + // ILibWebServer_Digest_IsAuthenticated + if (!duk_is_string(ctx, 0)) { return(ILibDuktape_Error(ctx, "IsAuthenticated(): Invalid Parameter/Type")); } + duk_size_t realmLen; + char* realm = (char*)duk_get_lstring(ctx, 0, &realmLen); + int retVal; + + duk_push_this(ctx); // [IMSG] + + retVal = (int)ILibDuktape_Digest_IsCorrectRealmAndNonce(ctx, duk_get_heapptr(ctx, -1) , realm, (int)realmLen); + duk_push_int(ctx, retVal); + return 1; +} +duk_ret_t ILibDuktape_HttpStream_IncomingMessage_Digest_GetUsername(duk_context *ctx) +{ + char *auth; + duk_size_t authLen; + void *DigestTable = ILibInitHashTree_CaseInSensitiveEx(ILibMemory_AllocateA(8000)); + + char *username; + int usernameLen; + + duk_push_this(ctx); // [IMSG] + duk_get_prop_string(ctx, -1, "headers"); // [IMSG][headers] + auth = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "Authorization", "", &authLen); + duk_pop_2(ctx); // ... + + ILibWebServer_Digest_ParseAuthenticationHeader(DigestTable, auth, (int)authLen); + ILibGetEntryEx(DigestTable, "username", 8, (void**)&username, &usernameLen); + + duk_push_lstring(ctx, username, usernameLen); + return(1); +} +duk_ret_t ILibDuktape_HttpStream_IncomingMessage_Digest_ValidatePassword(duk_context *ctx) +{ + int retVal; + char nonce[33]; + char result1[33]; + char result2[33]; + char result3[33]; + char val[16]; + + MD5_CTX mctx; + + char *auth, *username, *password, *opaque, *response, *uri, *realm, *method; + duk_size_t authLen, passwordLen, methodLen; + int usernameLen, opaqueLen, responseLen, uriLen, realmLen; + + void *DigestTable = ILibInitHashTree_CaseInSensitiveEx(ILibMemory_AllocateA(8000)); + void *hptr; + + password = (char*)duk_get_lstring(ctx, 0, &passwordLen); + + duk_push_this(ctx); // [IMSG] + duk_get_prop_string(ctx, -1, ILibDuktape_IMSG2HttpStream); // [IMSG][httpStream] + duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2HTTP); // [IMSG][httpStream][http] + hptr = duk_get_heapptr(ctx, -1); + duk_pop_2(ctx); // [IMSG] + duk_get_prop_string(ctx, -1, "method"); + method = (char*)duk_get_lstring(ctx, -1, &methodLen); + duk_pop(ctx); + + duk_get_prop_string(ctx, -1, "headers"); // [IMSG][headers] + auth = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "Authorization", "", &authLen); + ILibWebServer_Digest_ParseAuthenticationHeader(DigestTable, auth, (int)authLen); + duk_pop_2(ctx); // ... + + ILibGetEntryEx(DigestTable, "username", 8, (void**)&username, &usernameLen); + ILibGetEntryEx(DigestTable, "realm", 5, (void**)&realm, &realmLen); + ILibGetEntryEx(DigestTable, "uri", 3, (void**)&uri, &uriLen); + ILibGetEntryEx(DigestTable, "response", 8, (void**)&response, &responseLen); + ILibGetEntryEx(DigestTable, "opaque", 6, (void**)&opaque, &opaqueLen); + + if (username == NULL || uri == NULL || password == NULL || passwordLen == 0 || response == NULL) + { + duk_push_false(ctx); + return(1); + } + + ILibDuktape_Digest_CalculateNonce(ctx, hptr, 0, opaque, opaqueLen, nonce); + + MD5_Init(&mctx); + MD5_Update(&mctx, username, usernameLen); + MD5_Update(&mctx, ":", 1); + MD5_Update(&mctx, realm, realmLen); + MD5_Update(&mctx, ":", 1); + MD5_Update(&mctx, password, passwordLen); + MD5_Final((unsigned char*)val, &mctx); + util_tohex_lower(val, 16, result1); + + MD5_Init(&mctx); + MD5_Update(&mctx, method, methodLen); + MD5_Update(&mctx, ":", 1); + MD5_Update(&mctx, uri, uriLen); + MD5_Final((unsigned char*)val, &mctx); + util_tohex_lower(val, 16, result2); + + MD5_Init(&mctx); + MD5_Update(&mctx, result1, 32); + MD5_Update(&mctx, ":", 1); + MD5_Update(&mctx, nonce, 32); + MD5_Update(&mctx, ":", 1); + MD5_Update(&mctx, result2, 32); + MD5_Final((unsigned char*)val, &mctx); + util_tohex_lower(val, 16, result3); + + retVal = (responseLen == 32 && strncmp(result3, response, 32)) == 0 ? 1 : 0; + duk_push_int(ctx, retVal); + return(1); +} +void ILibDuktape_HttpStream_IncomingMessage_PUSH(duk_context *ctx, ILibHTTPPacket *header, void *httpstream) +{ + duk_push_object(ctx); // [message] + duk_push_heapptr(ctx, httpstream); // [message][httpStream] + duk_put_prop_string(ctx, -2, ILibDuktape_IMSG2HttpStream); // [message] + duk_push_object(ctx); // [message][headers] + packetheader_field_node *node = header->FirstField; + while (node != NULL) + { + duk_push_lstring(ctx, node->Field, node->FieldLength); // [message][headers][key] + duk_push_lstring(ctx, node->FieldData, node->FieldDataLength); // [message][headers][key][value] + duk_put_prop(ctx, -3); // [message][headers] + node = node->NextField; + } + duk_put_prop_string(ctx, -2, "headers"); + duk_push_lstring(ctx, header->Version, header->VersionLength); // [message][version] + duk_put_prop_string(ctx, -2, "httpVersion"); + + if (header->Directive != NULL) + { + duk_push_lstring(ctx, header->Directive, header->DirectiveLength); // [message][method] + duk_get_prop_string(ctx, -1, "toUpperCase"); // [message][method][toUpper] + duk_swap_top(ctx, -2); // [message][toUpper][this] + duk_call_method(ctx, 0); // [message][method] + ILibDuktape_CreateReadonlyProperty(ctx, "method"); // [message] + + duk_push_lstring(ctx, header->DirectiveObj, header->DirectiveObjLength);// [message][url] + duk_put_prop_string(ctx, -2, "url"); // [message] + } + else + { + duk_push_int(ctx, header->StatusCode); // [message][statusCode] + duk_put_prop_string(ctx, -2, "statusCode"); // [message] + duk_push_lstring(ctx, header->StatusData, header->StatusDataLength); // [message][statusMessage] + duk_put_prop_string(ctx, -2, "statusMessage"); // [message] + } + ILibDuktape_CreateInstanceMethod(ctx, "Digest_IsAuthenticated", ILibDuktape_HttpStream_IncomingMessage_Digest_IsAuthenticated, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "Digest_GetUsername", ILibDuktape_HttpStream_IncomingMessage_Digest_GetUsername, 0); + ILibDuktape_CreateInstanceMethod(ctx, "Digest_ValidatePassword", ILibDuktape_HttpStream_IncomingMessage_Digest_ValidatePassword, 1); +} + +void ILibDuktape_HttpStream_IncomingMessage_PauseSink(ILibDuktape_readableStream* sender, void *user) +{ + +} +void ILibDuktape_HttpStream_IncomingMessage_ResumeSink(ILibDuktape_readableStream* sender, void *user) +{ + +} +int ILibDuktape_HttpStream_IncomingMessage_UnshiftBytes(ILibDuktape_readableStream *sender, int unshiftBytes, void *user) +{ + ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)user; + data->bodyStream_unshiftedBytes = unshiftBytes; + return(unshiftBytes); +} +void ILibDuktape_HttpStream_DispatchEnd(void *chain, void *user) +{ + if (ILibDuktape_IsPointerValid(chain, ((void**)user)[1]) != 0) + { + duk_context *ctx = (duk_context*)((void**)user)[0]; + void *heapPtr = ((void**)user)[1]; + + duk_push_heapptr(ctx, heapPtr); // [httpStream] + duk_get_prop_string(ctx, -1, "emit"); // [httpStream][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "error"); // [emit][this][end] + if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream.onEnd(): "); } + duk_pop(ctx); // ... + } + free(user); +} + +void ILibDuktape_HttpStream_OnReceive(ILibWebClient_StateObject WebStateObject, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebClient_ReceiveStatus recvStatus, void *user1, void *user2, int *PAUSE) +{ + ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)user1; + duk_context *ctx = data->DS->writableStream->ctx; + + if (data->bodyStream != NULL) + { + if (endPointer > 0) { ILibDuktape_readableStream_WriteData(data->bodyStream, bodyBuffer + *beginPointer, endPointer); *beginPointer = endPointer - data->bodyStream_unshiftedBytes; data->bodyStream_unshiftedBytes = 0; } + if (recvStatus == ILibWebClient_ReceiveStatus_Complete) + { + ILibDuktape_readableStream_WriteEnd(data->bodyStream); + data->bodyStream = NULL; + } + return; + } + + duk_push_heapptr(ctx, data->DS->ParentObject); // [httpStream] + duk_get_prop_string(ctx, -1, "emit"); // [httpStream][emit] + duk_swap_top(ctx, -2); // [emit][this] + + if (header == NULL) + { + duk_push_string(ctx, "error"); // [emit][this][error] + if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->error(): "); } + duk_pop(ctx); + return; + } + + if (header->Directive != NULL) + { + // We are a server + + if (header->DirectiveLength == 7 && strncasecmp(header->Directive, "CONNECT", 7) == 0) + { + // Connect + } + else + { + // Check Headers + if (ILibGetHeaderLine(header, "Upgrade", 7) != NULL) + { + duk_push_string(ctx, "upgrade"); // [emit][this][upgrade] + ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][upgrade][imsg] + ILibDuktape_HttpStream_ServerResponse_PUSH(ctx, data->DS->writableStream->pipedReadable, header, data->DS->ParentObject); // [emit][this][request][imsg][rsp] + duk_put_prop_string(ctx, -2, ILibDuktape_IMSG2SR); + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->upgrade(): "); } + duk_pop(ctx); // ... + } + else + { + char *val; + int valLen; + + val = ILibGetHeaderLineEx(header, "Expect", 6, &valLen); + if (val != NULL) + { + if (valLen == 12 && strncasecmp(val, "100-Continue", 12) == 0) + { + duk_push_string(ctx, "checkContinue"); // [emit][this][checkContinue] + ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][checkContinue][imsg] + data->bodyStream = ILibDuktape_ReadableStream_InitEx(ctx, ILibDuktape_HttpStream_IncomingMessage_PauseSink, ILibDuktape_HttpStream_IncomingMessage_ResumeSink, ILibDuktape_HttpStream_IncomingMessage_UnshiftBytes, data); + ILibDuktape_HttpStream_ServerResponse_PUSH(ctx, data->DS->writableStream->pipedReadable, header, data->DS->ParentObject); // [emit][this][checkContinue][imsg][rsp] + duk_dup(ctx, -1); // [emit][this][checkContinue][imsg][rsp][rsp] + duk_insert(ctx, -6); // [rsp][emit][this][checkContinue][imsg][rsp] + if (duk_pcall_method(ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->checkContinue(): "); } // [rsp][hadListener] + else + { + if (!duk_get_boolean(ctx, -1)) + { + // No listener, so we must immediately send '100 Continue' + duk_get_prop_string(ctx, -2, "writeContinue"); // [rsp][hadListener][writeContinue] + duk_dup(ctx, -3); // [rsp][hadListener][writeContinue][this] + duk_call_method(ctx, 0); duk_pop(ctx); // [rsp][hadListener] + } + } + duk_pop_2(ctx); // ... + } + else + { + + } + } + else + { + duk_push_string(ctx, "request"); // [emit][this][request] + ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][request][imsg] + ILibDuktape_HttpStream_ServerResponse_PUSH(ctx, data->DS->writableStream->pipedReadable, header, data->DS->ParentObject); // [emit][this][request][imsg][rsp] + + if (duk_pcall_method(ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->request(): "); } + duk_pop(ctx); + } + } + } + } + else + { + // We are a client + + // First, lets check to see if 'Connection: close' was specified + char *value; + int valueLen; + value = ILibGetHeaderLineEx(header, "connection", 10, &valueLen); + if (value != NULL && valueLen == 5 && strncasecmp(value, "close", 5) == 0) { data->connectionCloseSpecified = 1; } + if (header->VersionLength == 3 && strncmp(header->Version, "1.0", 3) == 0) { if (!(value != NULL && valueLen == 10 && strncasecmp(value, "keep-alive", 10) == 0)) { data->connectionCloseSpecified = 1; } } + + if (data->ConnectMethod != 0) + { + duk_push_string(ctx, "connect"); // [emit][this][connect] + ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][connect][imsg] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->connect(): "); } + duk_pop(ctx); + return; + } + + switch (header->StatusCode) + { + case 100: + duk_push_string(ctx, "continue"); // [emit][this][continue] + if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive(): "); } + duk_pop(ctx); + break; + case 101: + duk_push_string(ctx, "upgrade"); // [emit][this][upgrade] + ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][upgrade][imsg] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->upgrade(): "); } + duk_pop(ctx); + break; + default: + duk_push_string(ctx, "response"); // [emit][this][response] + ILibDuktape_HttpStream_IncomingMessage_PUSH(ctx, header, data->DS->ParentObject); // [emit][this][response][imsg] + data->bodyStream = ILibDuktape_ReadableStream_InitEx(ctx, ILibDuktape_HttpStream_IncomingMessage_PauseSink, ILibDuktape_HttpStream_IncomingMessage_ResumeSink, ILibDuktape_HttpStream_IncomingMessage_UnshiftBytes, data); + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.httpStream.onReceive->response(): "); } + duk_pop(ctx); + break; + } + } + + if (data->bodyStream != NULL && recvStatus == ILibWebClient_ReceiveStatus_Complete) + { + ILibDuktape_readableStream_WriteEnd(data->bodyStream); + } + if (recvStatus == ILibWebClient_ReceiveStatus_Complete) + { + if (ILibIsRunningOnChainThread(data->chain) != 0) + { + // We're on the Chain Thread, so we can directly emit the 'end' event + duk_push_heapptr(ctx, data->DS->ParentObject); // [httpStream] + duk_get_prop_string(ctx, -1, "emit"); // [httpStream][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "end"); // [emit]][this][end] + if (duk_pcall_method(ctx, 1) != 0) ILibDuktape_Process_UncaughtExceptionEx(ctx, "httpStream.onReceive(): Error dispatching 'end': %s", duk_safe_to_string(ctx, -1)); + duk_pop(ctx); // ... + } + else + { + // We're on the wrong thread to dispatch the 'end' event, so we have to context switch + void **tmp = (void**)ILibMemory_Allocate(2 * sizeof(void*), 0, NULL, NULL); + tmp[0] = ctx; + tmp[1] = data->DS->ParentObject; + ILibChain_RunOnMicrostackThread(data->chain, ILibDuktape_HttpStream_DispatchEnd, tmp); + } + } +} +duk_ret_t ILibDuktape_HttpStream_Finalizer(duk_context *ctx) +{ + duk_get_prop_string(ctx, 0, ILibDuktape_HTTPStream2Data); + ILibDuktape_HttpStream_Data *data = (ILibDuktape_HttpStream_Data*)Duktape_GetBuffer(ctx, -1, NULL); + ILibDuktape_InValidatePointer(Duktape_GetChain(ctx), data); + ILibDuktape_InValidateHeapPointer(ctx, 0); + + ILibWebClient_DestroyWebClientDataObject(data->WCDO); + + return(0); +} + +duk_ret_t ILibDuktape_HttpStream_connectionCloseSpecified(duk_context *ctx) +{ + ILibDuktape_HttpStream_Data *data; + + duk_push_this(ctx); // [httpstream] + duk_get_prop_string(ctx, -1, ILibDuktape_HTTPStream2Data); // [httpstream][data] + data = (ILibDuktape_HttpStream_Data*)Duktape_GetBuffer(ctx, -1, NULL); + duk_push_boolean(ctx, data->connectionCloseSpecified); + return(1); +} +void ILibDuktape_HttpStream_piped(struct ILibDuktape_readableStream *sender, void *writableStream, void *user) +{ + duk_push_heapptr(sender->ctx, sender->object); // [httpStream] + duk_push_heapptr(sender->ctx, writableStream); // [httpStream][dest] + duk_put_prop_string(sender->ctx, -2, ILibDuktape_HTTP2PipedWritable); // [httpStream] + duk_pop(sender->ctx); // ... +} +duk_ret_t ILibDuktape_HttpStream_pipeEvent(duk_context *ctx) +{ + duk_push_this(ctx); // [httpStream] + duk_dup(ctx, 0); // [httpStream][readable] + duk_put_prop_string(ctx, -2, ILibDuktape_HTTP2PipedReadable); // [httpStream] + return(0); +} +duk_ret_t ILibduktape_HttpStream_create(duk_context *ctx) +{ + ILibDuktape_HttpStream_Data *data; + duk_push_object(ctx); // [httpStream] + duk_push_this(ctx); // [httpStream][http] + duk_put_prop_string(ctx, -2, ILibDuktape_HTTPStream2HTTP); // [httpStream] + ILibDuktape_WriteID(ctx, "http.httpStream"); + ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx); + duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_HttpStream_Data)); // [httpStream][buffer] + data = (ILibDuktape_HttpStream_Data*)Duktape_GetBuffer(ctx, -1, NULL); + memset(data, 0, sizeof(ILibDuktape_HttpStream_Data)); + duk_put_prop_string(ctx, -2, ILibDuktape_HTTPStream2Data); // [httpStream] + + ILibDuktape_EventEmitter_CreateEventEx(emitter, "end"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "error"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "continue"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "response"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "checkContinue"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "checkExpectation"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "clientError"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "request"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect"); + + data->DS = ILibDuktape_DuplexStream_InitEx(ctx, ILibDuktape_HttpStream_WriteSink, ILibDuktape_HttpStream_EndSink, + NULL, NULL, NULL, data); + data->DS->readableStream->PipeHookHandler = ILibDuktape_HttpStream_piped; + data->WCDO = ILibCreateWebClientEx(ILibDuktape_HttpStream_OnReceive, (ILibAsyncSocket_SocketModule)NULL, data, NULL); + data->chain = Duktape_GetChain(ctx); + + ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "pipe", ILibDuktape_HttpStream_pipeEvent); + + ILibDuktape_CreateEventWithGetter(ctx, "connectionCloseSpecified", ILibDuktape_HttpStream_connectionCloseSpecified); + + ILibDuktape_CreateFinalizer(ctx, ILibDuktape_HttpStream_Finalizer); + ILibDuktape_ValidatePointer(Duktape_GetChain(ctx), data); + ILibDuktape_ValidateHeapPointer(ctx, -1); + return(1); +} +duk_ret_t ILibDuktape_HttpStream_Agent_getName(duk_context *ctx) +{ + char *host = Duktape_GetStringPropertyValue(ctx, 0, "host", "127.0.0.1"); + int port = Duktape_GetIntPropertyValue(ctx, 0, "port", 0); + + sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "%s:%d:%s", host, port, Duktape_GetStringPropertyValue(ctx, 0, "localAddress", "0.0.0.0")); + + duk_push_string(ctx, ILibScratchPad2); + return(1); +} +int ILibDuktape_RemoveObjFromArray(duk_context *ctx, void *arrObj, void *obj) +{ + int retVal = -1; + int len, i; + duk_push_heapptr(ctx, arrObj); // [array] + len = (int)duk_get_length(ctx, -1); + for (i = 0; i < len; ++i) + { + duk_get_prop_index(ctx, -1, i); // [array][obj] + if (duk_get_heapptr(ctx, -1) == obj) + { + duk_pop(ctx); // [array] + duk_get_prop_string(ctx, -1, "splice"); // [array][splice] + duk_dup(ctx, -2); // [array][splice][this] + duk_push_int(ctx, i); // [array][splice][this][i] + duk_push_int(ctx, 1); // [array][splice][this][i][1] + duk_call_method(ctx, 2); duk_pop(ctx); // [array] + break; + } + duk_pop(ctx); // [array] + } + retVal = (int)duk_get_length(ctx, -1); + duk_pop(ctx); // ... + return(retVal); +} +void ILibDuktape_RemoveObjFromTable(duk_context *ctx, duk_idx_t tableIdx, char *key, void *obj) +{ + if (duk_has_prop_string(ctx, tableIdx, key)) + { + duk_get_prop_string(ctx, tableIdx, key); // [Array] + if (ILibDuktape_RemoveObjFromArray(ctx, duk_get_heapptr(ctx, -1), obj) == 0) + { + duk_pop(ctx); // ... + duk_del_prop_string(ctx, tableIdx, key); + } + else + { + duk_pop(ctx); // ... + } + } +} +duk_ret_t ILibDuktape_HttpStream_Agent_socketEndSink(duk_context *ctx) +{ + duk_push_this(ctx); // [socket] + printf("socket has closed: %p\n", duk_get_heapptr(ctx, -1)); + duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [socket][agent] + duk_get_prop_string(ctx, -2, ILibDuktape_Socket2AgentKey); // [socket][agent][key] + char *key = Duktape_GetBuffer(ctx, -1, NULL); + duk_pop(ctx); // [socket][agent[] + + // Check to see if this socket was in the freeSockets table + duk_get_prop_string(ctx, -1, "freeSockets"); // [socket][agent][freeSockets] + ILibDuktape_RemoveObjFromTable(ctx, -1, key, duk_get_heapptr(ctx, 0)); + duk_pop(ctx); // [socket][agent] + + // Check to see if this socket was in the sockets table + duk_get_prop_string(ctx, -1, "sockets"); // [socket][agent][socketsTable] + ILibDuktape_RemoveObjFromTable(ctx, -1, key, duk_get_heapptr(ctx, 0)); + duk_pop(ctx); // [socket][agent] + + // Now that we cleared this socket out of all the tables, we need to check to see if we need to create a new connection + duk_get_prop_string(ctx, -1, "requests"); // [socket][agent][requestTable] + if (duk_has_prop_string(ctx, -1, key)) + { + duk_get_prop_string(ctx, -1, key); // [socket][agent][requestTable][array] + if (duk_get_length(ctx, -1) > 0) + { + // There is at least one request waiting for a socket + duk_get_prop_string(ctx, -3, "sockets"); // [socket][agent][requestTable][array][socketsTable] + duk_get_prop_string(ctx, -1, key); // [socket][agent][requestTable][array][socketsTable][array] + if (duk_is_undefined(ctx, -1) || (duk_get_length(ctx, -1) < (size_t)Duktape_GetIntPropertyValue(ctx, -5, "maxSockets", 1))) + { + // We need to create a new socket + duk_pop_n(ctx, 4); // [socket][agent] + duk_dup(ctx, -1); // [socket][agent][agent] + duk_get_prop_string(ctx, -1, "createConnection"); // [socket][agent][agent][createConnection] + duk_swap_top(ctx, -2); // [socket][agent][createConnection][this] + duk_get_prop_string(ctx, -4, "\xFF_NET_SOCKET2OPTIONS"); // [socket][agent][createConnection][this][options] + duk_call_method(ctx, 1); // [socket][agent][newsocket] + duk_swap_top(ctx, -2); // [socket][newsocket][agent] + duk_put_prop_string(ctx, -2, ILibDuktape_Socket2Agent); // [socket][newsocket] + ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "connect", ILibDuktape_HttpStream_http_OnConnect); + ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "error", ILibDuktape_HttpStream_http_OnConnectError); + } + } + } + + return(0); +} +duk_ret_t ILibDuktape_HttpStream_Agent_keepSocketAlive_timeout(duk_context *ctx) +{ + duk_push_this(ctx); // [socket] + duk_get_prop_string(ctx, -1, "end"); // [socket][end] + duk_swap_top(ctx, -2); // [end][this] + duk_call_method(ctx, 0); + return(0); +} +duk_ret_t ILibDuktape_HttpStream_Agent_keepSocketAlive(duk_context *ctx) +{ + int retVal = 0; + char *remoteAddress = Duktape_GetStringPropertyValue(ctx, 0, "remoteAddress", "127.0.0.1"); + char *remoteHost = Duktape_GetStringPropertyValue(ctx, 0, "remoteHost", remoteAddress); + char *key; + + if (duk_has_prop_string(ctx, 0, ILibDuktape_Socket2AgentKey)) + { + key = (char*)Duktape_GetStringPropertyValue(ctx, 0, ILibDuktape_Socket2AgentKey, NULL); + } + else + { + duk_push_this(ctx); // [Agent] + duk_get_prop_string(ctx, -1, "getName"); // [Agent][getName] + duk_swap_top(ctx, -2); // [getName][this] + duk_push_object(ctx); // [getName][this][options] + duk_push_string(ctx, remoteHost); // [getName][this][options][host] + duk_put_prop_string(ctx, -2, "host"); // [getName][this][options] + duk_get_prop_string(ctx, 0, "remotePort"); // [getName][this][options][port] + duk_put_prop_string(ctx, -2, "port"); // [getName][this][options] + duk_call_method(ctx, 1); // [key] + key = (char*)duk_get_string(ctx, -1); + } + + duk_push_this(ctx); // [key][Agent] + if (duk_has_prop_string(ctx, 0, ILibDuktape_Agent_SocketJustCreated)) + { + duk_del_prop_string(ctx, 0, ILibDuktape_Agent_SocketJustCreated); + } + else if (Duktape_GetBooleanProperty(ctx, -1, "keepAlive", 0) == 0) + { + duk_get_prop_string(ctx, 0, "end"); // [end] + duk_dup(ctx, 0); // [end][this] + duk_call_method(ctx, 0); + return(0); + } + + + duk_get_prop_string(ctx, -1, "requests"); // [key][Agent][requests] + if (duk_has_prop_string(ctx, -1, key)) + { + // Has Key, check the Array + duk_get_prop_string(ctx, -1, key); // [key][Agent][requests][Array] + duk_get_prop_string(ctx, -1, "shift"); // [key][Agent][requests][Array][shift] + duk_swap_top(ctx, -2); // [key][Agent][requests][shift][this] + duk_call_method(ctx, 0); // [key][Agent][requests][request] + if (!duk_is_undefined(ctx, -1)) + { + // There was a request + duk_push_this(ctx); // [key][Agent][requests][request][Agent] + duk_get_prop_string(ctx, -1, "reuseSocket");// [key][Agent][requests][request][Agent][reuseSocket] + duk_swap_top(ctx, -2); // [key][Agent][requests][request][reuseSocket][this] + duk_dup(ctx, 0); // [key][Agent][requests][request][reuseSocket][this][socket] + duk_dup(ctx, -4); // [key][Agent][requests][request][reuseSocket][this][socket][request] + duk_call_method(ctx, 2); // [key][Agent][requests][request][retVal] + retVal = 1; + } + } + if (retVal == 0) + { + // No Requests Found + duk_push_this(ctx); // [Agent] + duk_get_prop_string(ctx, -1, "freeSockets"); // [Agent][table] + if (!duk_has_prop_string(ctx, -1, key)) + { + duk_push_array(ctx); // [Agent][table][Array] + duk_dup(ctx, -1); // [Agent][table][Array][Array] + duk_put_prop_string(ctx, -3, key); // [Agent][table][Array] + } + else + { + duk_get_prop_string(ctx, -1, key); // [Agent][table][Array] + } + duk_get_prop_string(ctx, -1, "push"); // [Agent][table][Array][push] + duk_swap_top(ctx, -2); // [Agent][table][push][this] + duk_dup(ctx, 0); // [AGent][table][push][this][socket] + + duk_get_prop_string(ctx, -1, "setTimeout"); // [AGent][table][push][this][socket][setTimeout] + duk_dup(ctx, -2); // [AGent][table][push][this][socket][setTimeout][this] + duk_get_prop_string(ctx, -7, "keepAliveMsecs"); // [AGent][table][push][this][socket][setTimeout][this][milliseconds] + duk_push_c_function(ctx, ILibDuktape_HttpStream_Agent_keepSocketAlive_timeout, DUK_VARARGS); // [AGent][table][push][this][socket][setTimeout][this][milliseconds][callback] + duk_call_method(ctx, 2); duk_pop(ctx); // [AGent][table][push][this][socket] + + duk_call_method(ctx, 1); // [Agent][table][retVal] + retVal = 1; + } + duk_push_int(ctx, retVal); + return(1); +} +void ILibDuktape_HttpStream_Agent_reuseSocketEx(duk_context *ctx, void ** args, int argsLen) +{ + duk_push_heapptr(ctx, args[1]); // [clientRequest] + duk_push_heapptr(ctx, args[0]); // [clientRequest][socket] + + duk_get_prop_string(ctx, -1, "setTimeout"); // [clientRequest][socket][setTimeout] + duk_dup(ctx, -2); // [clientRequest][socket][setTimeout][this] + duk_push_int(ctx, 0); // [clientRequest][socket][setTimeout][this][0] (Disable Idle Timeout) + duk_call_method(ctx, 1); duk_pop(ctx); // [clientRequest][socket] + + ILibDuktape_CreateReadonlyProperty(ctx, "socket"); // [clientRequest] + duk_get_prop_string(ctx, -1, "emit"); // [clientRequest][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "socket"); // [emit][this][name] + duk_push_heapptr(ctx, args[0]); // [emit][this][name][socket] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "Agent.reuseSocket() => Error emitting 'socket' on clientRequest: %s", duk_safe_to_string(ctx, -1)); } + duk_pop(ctx); // ... +} +duk_ret_t ILibDuktape_HttpStream_Agent_reuseSocket(duk_context *ctx) +{ + // Yield to the next loop, before we emit a 'socket' event, because emitting this event before anyone has the clientRequest object is pointless + ILibDuktape_Immediate(ctx, (void*[]) { duk_get_heapptr(ctx, 0), duk_get_heapptr(ctx, 1) }, 2, ILibDuktape_HttpStream_Agent_reuseSocketEx); + return(0); +} +duk_ret_t ILibDuktape_HttpStream_Agent_createConnection_eventSink(duk_context *ctx) +{ + duk_push_this(ctx); // [socket] + char *key = Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_Socket2AgentKey, ""); + duk_get_prop_string(ctx, -1, ILibDuktape_Socket2Agent); // [socket][agent] + duk_get_prop_string(ctx, -1, "sockets"); // [socket][agent][socketsTable] + ILibDuktape_RemoveObjFromTable(ctx, -1, key, duk_get_heapptr(ctx, -3)); + return(0); +} +duk_ret_t ILibDuktape_HttpStream_Agent_createConnection(duk_context *ctx) +{ + int nargs = duk_get_top(ctx); + int i; + duk_size_t protocolLen; + char *protocol = Duktape_GetStringPropertyValueEx(ctx, 0, "protocol", "http:", &protocolLen); + + duk_push_this(ctx); // [Agent] + if ((protocolLen == 6 && strncasecmp("https:", protocol, 6) == 0) || (protocolLen == 4 && strncasecmp("wss:", protocol, 4) == 0)) + { + duk_eval_string(ctx, "require('tls');"); // [Agent][net] + duk_get_prop_string(ctx, -1, "connect"); // [Agent][net][createConnection] + } + else + { + duk_eval_string(ctx, "require('net');"); // [Agent][net] + duk_get_prop_string(ctx, -1, "createConnection"); // [Agent][net][createConnection] + } + duk_swap_top(ctx, -2); // [Agent][createConnection][this] + for (i = 0; i < nargs; ++i) + { + duk_dup(ctx, i); + } + duk_call_method(ctx, nargs); // [Agent][Socket] + duk_push_true(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_Agent_SocketJustCreated); + duk_get_prop_string(ctx, -2, "getName"); // [Agent][Socket][getName] + duk_dup(ctx, -3); // [Agent][Socket][getName][this] + duk_dup(ctx, 0); // [Agent][Socket][getName][this][options] + duk_call_method(ctx, 1); // [Agent][Socket][key] + duk_put_prop_string(ctx, -2, ILibDuktape_Socket2AgentKey); // [Agent][Socket] + duk_dup(ctx, -2); // [Agent][Socket][Agent] + duk_put_prop_string(ctx, -2, ILibDuktape_Socket2Agent); // [Agent][Socket] + + duk_get_prop_string(ctx, -2, "sockets"); // [Agent][Socket][socketsTable] + duk_get_prop_string(ctx, -2, ILibDuktape_Socket2AgentKey); // [Agent][Socket][socketsTable][key] + if (!duk_has_prop(ctx, -2)) // [Agent][Socket][socketsTable] + { + duk_get_prop_string(ctx, -2, ILibDuktape_Socket2AgentKey); // [Agent][Socket][socketsTable][key] + duk_push_array(ctx); // [Agent][Socket][socketsTable][key][array] + duk_put_prop(ctx, -3); // [Agent][Socket][socketsTable] + } + duk_get_prop_string(ctx, -2, ILibDuktape_Socket2AgentKey); // [Agent][Socket][socketsTable][key] + duk_get_prop(ctx, -2); // [Agent][Socket][socketsTable][array] + duk_get_prop_string(ctx, -1, "push"); // [Agent][Socket][socketsTable][array][push] + duk_swap_top(ctx, -2); // [Agent][Socket][socketsTable][push][this] + duk_dup(ctx, -4); // [Agent][Socket][socketsTable][push][this][socket] + duk_call_method(ctx, 1); duk_pop_2(ctx); // [Agent][Socket] + + ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "error", ILibDuktape_HttpStream_Agent_createConnection_eventSink); + ILibDuktape_EventEmitter_AddOnceEx3(ctx, -1, "close", ILibDuktape_HttpStream_Agent_socketEndSink); + + return(1); +} +duk_ret_t ILibDuktape_HttpStream_Agent_new(duk_context *ctx) +{ + if (duk_get_top(ctx) > 0 && duk_is_object(ctx, 0)) { duk_dup(ctx, 0); } else { duk_push_object(ctx); } + int keepAlive = Duktape_GetBooleanProperty(ctx, -1, "keepAlive", 1); + int keepAliveMsecs = Duktape_GetIntPropertyValue(ctx, -1, "keepAliveMsecs", 15000); + int maxSockets = Duktape_GetIntPropertyValue(ctx, -1, "maxSockets", 1); + int maxFreeSockets = Duktape_GetIntPropertyValue(ctx, -1, "maxFreeSockets", 32); + + duk_push_object(ctx); // [Agent] + ILibDuktape_WriteID(ctx, "http.Agent"); + duk_push_boolean(ctx, (duk_bool_t)keepAlive); // [Agent][keepAlive] + duk_put_prop_string(ctx, -2, "keepAlive"); // [Agent] + duk_push_int(ctx, keepAliveMsecs); // [Agent][keepAliveMsecs] + duk_put_prop_string(ctx, -2, "keepAliveMsecs"); // [Agent] + duk_push_int(ctx, maxSockets); // [Agent][maxSockets] + duk_put_prop_string(ctx, -2, "maxSockets"); // [Agent] + duk_push_int(ctx, maxFreeSockets); // [Agent][maxFreeSockets] + duk_put_prop_string(ctx, -2, "maxFreeSockets"); // [Agent] + + duk_push_object(ctx); // [Agent][freeSockets] + duk_put_prop_string(ctx, -2, "freeSockets"); // [Agent] + duk_push_object(ctx); // [Agent][requests] + duk_put_prop_string(ctx, -2, "requests"); // [Agent] + duk_push_object(ctx); // [Agent][sockets] + duk_put_prop_string(ctx, -2, "sockets"); // [Agent] + + ILibDuktape_CreateInstanceMethod(ctx, "getName", ILibDuktape_HttpStream_Agent_getName, 1); + ILibDuktape_CreateInstanceMethod(ctx, "keepSocketAlive", ILibDuktape_HttpStream_Agent_keepSocketAlive, 1); + ILibDuktape_CreateInstanceMethod(ctx, "reuseSocket", ILibDuktape_HttpStream_Agent_reuseSocket, 2); + ILibDuktape_CreateInstanceMethod(ctx, "createConnection", ILibDuktape_HttpStream_Agent_createConnection, DUK_VARARGS); + + return(1); +} +duk_ret_t ILibDuktape_httpStream_parseUri(duk_context *ctx) +{ + duk_size_t uriLen; + char *uri; + if (!duk_is_string(ctx, 0)) { return(ILibDuktape_Error(ctx, "http.parseUri(): Invalid Parameters")); } + + char *path, *addr; + unsigned short port; + int protocolIndex; + + uri = (char*)duk_get_lstring(ctx, 0, &uriLen); + protocolIndex = 1 + ILibString_IndexOf(uri, (int)uriLen, "://", 3); + if (protocolIndex > 0) + { + ILibParseUriEx(uri, (size_t)uriLen, &addr, &port, &path, NULL); + + duk_push_object(ctx); // [options] + duk_push_lstring(ctx, uri, protocolIndex); // [options][protocol] + duk_put_prop_string(ctx, -2, "protocol"); + duk_push_string(ctx, addr); // [options][host] + duk_put_prop_string(ctx, -2, "host"); + duk_push_int(ctx, port); // [options][port] + duk_put_prop_string(ctx, -2, "port"); // [options] + duk_push_string(ctx, path); // [options][path] + duk_put_prop_string(ctx, -2, "path"); // [options] + duk_push_string(ctx, "GET"); // [options][method] + duk_put_prop_string(ctx, -2, "method"); // [options] + + free(path); + free(addr); + } + else + { + duk_push_null(ctx); + } + return 1; +} + + +ILibTransport_DoneState ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(ILibDuktape_WebSocket_State *state, int opcode, char *_buffer, int _bufferLen, ILibWebClient_WebSocket_FragmentFlags _bufferFragment) +{ + state->noMasking = 1; + char header[10]; + int maskKeyInt; + int headerLen; + unsigned short flags = state->noMasking == 0 ? WEBSOCKET_MASK : 0; + + char *buffer; + int bufferLen; + ILibTransport_DoneState retVal = ILibTransport_DoneState_ERROR; + + ILibWebClient_WebSocket_FragmentFlags bufferFragment; + + buffer = _buffer; + bufferLen = _bufferLen; + bufferFragment = _bufferFragment; + + if (bufferFragment == ILibWebClient_WebSocket_FragmentFlag_Complete) + { + if (state->WebSocketFragmentFlag_Write == 0) + { + // This is a self contained fragment + headerLen = ILibWebServer_WebSocket_CreateHeader(header, flags | WEBSOCKET_FIN, (unsigned short)opcode, bufferLen); + } + else + { + // Termination of an ongoing Fragment + state->WebSocketFragmentFlag_Write = 0; + headerLen = ILibWebServer_WebSocket_CreateHeader(header, flags | WEBSOCKET_FIN, WEBSOCKET_OPCODE_FRAMECONT, bufferLen); + } + } + else + { + if (state->WebSocketFragmentFlag_Write == 0) + { + // Start a new fragment + state->WebSocketFragmentFlag_Write = 1; + headerLen = ILibWebServer_WebSocket_CreateHeader(header, flags, (unsigned short)opcode, bufferLen); + } + else + { + // Continuation of an ongoing fragment + headerLen = ILibWebServer_WebSocket_CreateHeader(header, flags, WEBSOCKET_OPCODE_FRAMECONT, bufferLen); + } + } + + if (flags & WEBSOCKET_MASK) + { + // We have to copy memory anyways to mask, so we might as well copy the extra few bytes and make a single buffer + char *dataFrame = ILibMemory_AllocateA(headerLen + bufferLen); + char *maskKey = (dataFrame + headerLen); + memcpy_s(dataFrame, headerLen, header, headerLen); + + // Mask the payload + util_random(4, maskKey); + maskKeyInt = ((int*)(maskKey))[0]; + if (bufferLen > 0) + { + int x; + for (x = 0; x < (bufferLen >> 2); ++x) { ((int*)(dataFrame+headerLen+4))[x] = ((int*)buffer)[x] ^ (int)maskKeyInt; } // Mask 4 bytes at a time + for (x = (x << 2); x < bufferLen; ++x) { dataFrame[x + headerLen + 4] = buffer[x] ^ maskKey[x % 4]; } // Mask the reminder + } + retVal = ILibDuktape_DuplexStream_WriteData(state->encodedStream, dataFrame, headerLen + 4 + bufferLen) == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE; + } + else + { + // Send payload without masking + if (ILibIsRunningOnChainThread(state->chain) != 0) + { + // We're on the Duktape Thread, so we can just call write multiple times, cuz we won't interleave with JavaScript + ILibDuktape_DuplexStream_WriteData(state->encodedStream, header, headerLen); + retVal = ILibDuktape_DuplexStream_WriteData(state->encodedStream, buffer, bufferLen) == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE; + } + else + { + // We're not on the Duktape Thread, so we need to merge these buffers, to make a single write + char *dataFrame = ILibMemory_AllocateA(headerLen + bufferLen); + + memcpy_s(dataFrame, headerLen, header, headerLen); + memcpy_s(dataFrame + headerLen, bufferLen, buffer, bufferLen); + retVal = ILibDuktape_DuplexStream_WriteData(state->encodedStream, dataFrame, headerLen + bufferLen) == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE; + } + } + + return retVal; +} +ILibTransport_DoneState ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen) +{ + if (stream->writableStream->pipedReadable != NULL || stream->writableStream->pipedReadable_native != NULL) + { + if (stream->writableStream->pipedReadable != NULL) + { + duk_context *ctx = stream->writableStream->ctx; + if (ILibIsRunningOnChainThread(stream->readableStream->chain) != 0) + { + // We can dispatch directly + duk_push_external_buffer(ctx); // [ext] + duk_config_buffer(ctx, -1, buffer, (duk_size_t)bufferLen); + duk_push_heapptr(ctx, stream->writableStream->pipedReadable); // [ext][readable] + duk_get_prop_string(ctx, -1, "unshift"); // [ext][readable][unshift] + duk_swap_top(ctx, -2); // [ext][unshift][this] + duk_push_buffer_object(ctx, -3, 0, (duk_size_t)bufferLen, DUK_BUFOBJ_NODEJS_BUFFER);// [ext][unshift][this][buffer] + if (duk_pcall_method(ctx, 1) != 0) + { + ILibDuktape_Error(ctx, "http.webSocketStream.encoded.write() => Error calling unshift: %s", duk_safe_to_string(ctx, -1)); + return(ILibTransport_DoneState_ERROR); + } + duk_pop_2(ctx); // ... + return(ILibTransport_DoneState_COMPLETE); + } + else + { + return(ILibTransport_DoneState_ERROR); // Something is wrong, if we're called by a JavaScript object on an external thread + } + } + + // We were dispatched directly by a native ReadableStream + if (stream->writableStream->pipedReadable_native->UnshiftHandler == NULL || + stream->writableStream->pipedReadable_native->UnshiftHandler(stream->writableStream->pipedReadable_native, bufferLen, stream->writableStream->pipedReadable_native->user) <= 0) + { + return(ILibTransport_DoneState_ERROR); + } + else + { + return(ILibTransport_DoneState_COMPLETE); + } + } + else + { + // We can't unshift, so we need to buffer. But we won't... + return(ILibTransport_DoneState_ERROR); + } +} + +ILibTransport_DoneState ILibDuktape_httpStream_webSocket_EncodedWriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) +{ + int x; + int i = 2; + int plen; + unsigned short hdr; + char* maskingKey = NULL; + int FIN; + unsigned char OPCODE; + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + + if (bufferLen < 2) + { + // We need at least 2 bytes to read enough of the headers to know how long the frame is + return(ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(stream, buffer, bufferLen)); + } + + hdr = ntohs(((unsigned short*)(buffer))[0]); + FIN = (hdr & WEBSOCKET_FIN) != 0; + OPCODE = (hdr & WEBSOCKET_OPCODE) >> 8; + + plen = (unsigned char)(hdr & WEBSOCKET_PLEN); + if (plen == 126) + { + if (bufferLen < 4) { return(ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(stream, buffer, bufferLen)); } // We need at least 4 bytes to read enough of the headers + plen = (unsigned short)ntohs(((unsigned short*)(buffer))[1]); + i += 2; + } + else if (plen == 127) + { + if (bufferLen < 10) + { + return(ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(stream, buffer, bufferLen)); // We need at least 10 bytes to read enough of the headers + } + else + { + unsigned long long v = ILibNTOHLL(((unsigned long long*)(buffer + 2))[0]); + if (v > 0x7FFFFFFFUL) + { + // this value is too big to store in a 32 bit signed variable, so disconnect the websocket. + return(ILibTransport_DoneState_ERROR); + } + else + { + // this value can be represented with a signed 32 bit variable + plen = (int)v; + i += 8; + } + } + } + + if (bufferLen < (i + plen + ((unsigned char)(hdr & WEBSOCKET_MASK) != 0 ? 4 : 0))) + { + return(ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(stream, buffer, bufferLen)); // Don't have the entire packet + } + + maskingKey = ((unsigned char)(hdr & WEBSOCKET_MASK) == 0) ? NULL : (buffer + i); + if (maskingKey != NULL) + { + // Unmask the data + i += 4; // Move ptr to start of data + + int maskKeyInt = ((int*)(maskingKey))[0]; + if (plen > 0) + { + for (x = 0; x < (plen >> 2); ++x) { ((int*)(buffer+i))[x] = ((int*)(buffer+i))[x] ^ (int)maskKeyInt; } // Mask 4 bytes at a time + for (x = (x << 2); x < plen; ++x) { buffer[x + i] = buffer[x + i] ^ maskingKey[x % 4]; } // Mask the reminder + } + } + + if (OPCODE < 0x8) + { + // NON-CONTROL OP-CODE + // We will try to automatically re-assemble fragments, up to the max buffer size the user specified + if (OPCODE != 0) { state->WebSocketDataFrameType = (int)OPCODE; } // Set the DataFrame Type, so the user can query it + + if (FIN != 0 && state->WebSocketFragmentIndex == 0) + { + // We have an entire fragment, and we didn't save any of it yet... We can just forward it up without copying the buffer + ILibDuktape_DuplexStream_WriteDataEx(state->decodedStream, OPCODE == WEBSOCKET_OPCODE_TEXTFRAME ? 1 : 0, buffer + i, plen); + } + else + { + if (state->WebSocketFragmentIndex + plen >= state->WebSocketFragmentBufferSize) + { + // Need to grow the buffer + state->WebSocketFragmentBufferSize = state->WebSocketFragmentBufferSize * 2; + if ((state->WebSocketFragmentBuffer = (char*)realloc(state->WebSocketFragmentBuffer, state->WebSocketFragmentBufferSize)) == NULL) { ILIBCRITICALEXIT(254); } // MS Static Analyser erroneously reports that this leaks the original memory block + } + + memcpy_s(state->WebSocketFragmentBuffer + state->WebSocketFragmentIndex, state->WebSocketFragmentBufferSize - state->WebSocketFragmentIndex, buffer + i, plen); + state->WebSocketFragmentIndex += plen; + + if (FIN != 0) + { + ILibDuktape_DuplexStream_WriteDataEx(state->decodedStream, OPCODE == WEBSOCKET_OPCODE_TEXTFRAME ? 1 : 0, state->WebSocketFragmentBuffer, state->WebSocketFragmentIndex); + state->WebSocketFragmentIndex = 0; // Reset (We can write to the start of the buffer) + } + } + } + else + { + // CONTROL + switch (OPCODE) + { + case WEBSOCKET_OPCODE_CLOSE: + ILibDuktape_DuplexStream_WriteEnd(state->decodedStream); + if (ILibIsRunningOnChainThread(state->chain) != 0 && state->encodedStream->writableStream->pipedReadable != NULL) + { + duk_push_heapptr(state->ctx, state->encodedStream->writableStream->pipedReadable); // [stream] + duk_get_prop_string(state->ctx, -1, "end"); // [stream][end] + duk_swap_top(state->ctx, -2); // [end][this] + if (duk_pcall_method(state->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(state->ctx, "http.webSocketStream.write(): Error Dispatching 'end' "); } + duk_pop(state->ctx); // ... + } + break; + case WEBSOCKET_OPCODE_PING: + if (ILibIsRunningOnChainThread(state->chain) != 0) + { + duk_push_heapptr(state->ctx, state->decodedStream->ParentObject); // [stream] + duk_get_prop_string(state->ctx, -1, "emit"); // [stream][emit] + duk_swap_top(state->ctx, -2); // [emit][this] + duk_push_string(state->ctx, "ping"); // [emit][this][ping] + + if (duk_pcall_method(state->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(state->ctx, "http.webSocketStream.write(): Error Dispatching Ping "); } + duk_pop(state->ctx); // ... + } + + ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(state, WEBSOCKET_OPCODE_PONG, NULL, 0, ILibWebClient_WebSocket_FragmentFlag_Complete); + break; + case WEBSOCKET_OPCODE_PONG: + if (ILibIsRunningOnChainThread(state->chain) != 0) + { + duk_push_heapptr(state->ctx, state->decodedStream->ParentObject); // [stream] + duk_get_prop_string(state->ctx, -1, "emit"); // [stream][emit] + duk_swap_top(state->ctx, -2); // [emit][this] + duk_push_string(state->ctx, "pong"); // [emit][this][pong] + + if (duk_pcall_method(state->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(state->ctx, "http.webSocketStream.write(): Error Dispatching Pong "); } + duk_pop(state->ctx); // ... + } + break; + } + } + + if (bufferLen > (i + plen)) + { + return(ILibDuktape_httpStream_webSocket_EncodedWriteSink_DispatchUnshift(stream, buffer + i + plen, bufferLen - (i + plen))); // We need at least 10 bytes to read enough of the headers + } + + return(ILibTransport_DoneState_COMPLETE); +} +void ILibDuktape_httpStream_webSocket_EncodedEndSink(ILibDuktape_DuplexStream *stream, void *user) +{ + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + ILibDuktape_DuplexStream_WriteEnd(state->decodedStream); +} +void ILibDuktape_httpStream_webSocket_EncodedPauseSink_Chain(void *chain, void *user) +{ + if (chain != NULL && !ILibDuktape_IsPointerValid(chain, user)) { return; } + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + duk_context *ctx = state->decodedStream->writableStream->ctx; + + duk_push_heapptr(ctx, state->decodedStream->writableStream->pipedReadable); // [readable] + duk_get_prop_string(ctx, -1, "pause"); // [readable][pause] + duk_swap_top(ctx, -2); // [pause][this] + if (duk_pcall_method(ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.webSocketStream.Encoded_Pause(): Error pausing upstream "); } + duk_pop(ctx); // ... +} +void ILibDuktape_httpStream_webSocket_EncodedPauseSink(ILibDuktape_DuplexStream *sender, void *user) +{ + printf("WebSocket.Encoded.Pause();\n"); + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + if (state->decodedStream->writableStream->pipedReadable_native != NULL && state->decodedStream->writableStream->pipedReadable_native->PauseHandler != NULL) + { + state->decodedStream->writableStream->pipedReadable_native->paused = 1; + state->decodedStream->writableStream->pipedReadable_native->PauseHandler(state->decodedStream->writableStream->pipedReadable_native, state->decodedStream->writableStream->pipedReadable_native->user); + } + else + { + if (ILibIsRunningOnChainThread(state->chain)) + { + ILibDuktape_httpStream_webSocket_EncodedPauseSink_Chain(NULL, state); + } + else + { + ILibChain_RunOnMicrostackThreadEx(state->chain, ILibDuktape_httpStream_webSocket_EncodedPauseSink_Chain, state); + } + } +} +void ILibDuktape_httpStream_webSocket_EncodedResumeSink_Chain(void *chain, void *user) +{ + if (chain != NULL && !ILibDuktape_IsPointerValid(chain, user)) { return; } + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + duk_context *ctx = state->decodedStream->writableStream->ctx; + + duk_push_heapptr(ctx, state->decodedStream->writableStream->pipedReadable); // [readable] + duk_get_prop_string(ctx, -1, "resume"); // [readable][resume] + duk_swap_top(ctx, -2); // [resume][this] + if (duk_pcall_method(ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.webSocketStream.Encoded_Resume(): Error resuming upstream "); } + duk_pop(ctx); // ... +} +void ILibDuktape_httpStream_webSocket_EncodedResumeSink(ILibDuktape_DuplexStream *sender, void *user) +{ + printf("WebSocket.Encoded.Resume();\n"); + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + if (state->decodedStream->writableStream->pipedReadable_native != NULL && state->decodedStream->writableStream->pipedReadable_native->ResumeHandler != NULL) + { + state->decodedStream->writableStream->pipedReadable_native->paused = 0; + state->decodedStream->writableStream->pipedReadable_native->ResumeHandler(state->decodedStream->writableStream->pipedReadable_native, state->decodedStream->writableStream->pipedReadable_native->user); + } + else + { + if (ILibIsRunningOnChainThread(state->chain)) + { + ILibDuktape_httpStream_webSocket_EncodedResumeSink_Chain(NULL, state); + } + else + { + ILibChain_RunOnMicrostackThreadEx(state->chain, ILibDuktape_httpStream_webSocket_EncodedResumeSink_Chain, state); + } + } +} +int ILibDuktape_httpStream_webSocket_EncodedUnshiftSink(ILibDuktape_DuplexStream *sender, int unshiftBytes, void *user) +{ + // Block Mode should never need to be unshifted + return(0); +} + +ILibTransport_DoneState ILibDuktape_httpStream_webSocket_DecodedWriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) +{ + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + return(ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(state, stream->writableStream->Reserved == 1 ? ILibWebClient_WebSocket_DataType_TEXT : ILibWebClient_WebSocket_DataType_BINARY, buffer, bufferLen, ILibWebClient_WebSocket_FragmentFlag_Complete)); +} +void ILibDuktape_httpStream_webSocket_DecodedEndSink(ILibDuktape_DuplexStream *stream, void *user) +{ + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(state, WEBSOCKET_OPCODE_CLOSE, NULL, 0, ILibWebClient_WebSocket_FragmentFlag_Complete); +} +void ILibDuktape_httpStream_webSocket_DecodedPauseSink_Chain(void *chain, void *user) +{ + if (chain != NULL && !ILibDuktape_IsPointerValid(chain, user)) { return; } + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + duk_context *ctx = state->encodedStream->writableStream->ctx; + + duk_push_heapptr(ctx, state->encodedStream->writableStream->pipedReadable); // [readable] + duk_get_prop_string(ctx, -1, "pause"); // [readable][pause] + duk_swap_top(ctx, -2); // [pause][this] + if (duk_pcall_method(ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.webSocketStream.Decoded_Pause(): Error pausing upstream "); } + duk_pop(ctx); // ... +} +void ILibDuktape_httpStream_webSocket_DecodedPauseSink(ILibDuktape_DuplexStream *sender, void *user) +{ + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + if (state->encodedStream->writableStream->pipedReadable_native != NULL && state->encodedStream->writableStream->pipedReadable_native->PauseHandler != NULL) + { + state->encodedStream->writableStream->pipedReadable_native->paused = 1; + state->encodedStream->writableStream->pipedReadable_native->PauseHandler(state->encodedStream->writableStream->pipedReadable_native, state->encodedStream->writableStream->pipedReadable_native->user); + } + else + { + if (ILibIsRunningOnChainThread(state->chain)) + { + ILibDuktape_httpStream_webSocket_DecodedPauseSink_Chain(NULL, state); + } + else + { + ILibChain_RunOnMicrostackThreadEx(state->chain, ILibDuktape_httpStream_webSocket_DecodedPauseSink_Chain, state); + } + } +} +void ILibDuktape_httpStream_webSocket_DecodedResumeSink_Chain(void *chain, void *user) +{ + if (chain != NULL && !ILibDuktape_IsPointerValid(chain, user)) { return; } + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + duk_context *ctx = state->encodedStream->writableStream->ctx; + + duk_push_heapptr(ctx, state->encodedStream->writableStream->pipedReadable); // [readable] + duk_get_prop_string(ctx, -1, "resume"); // [readable][resume] + duk_swap_top(ctx, -2); // [resume][this] + if (duk_pcall_method(ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.webSocketStream.Decoded_Resume(): Error resuming upstream "); } + duk_pop(ctx); // ... +} +void ILibDuktape_httpStream_webSocket_DecodedResumeSink(ILibDuktape_DuplexStream *sender, void *user) +{ + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)user; + if (state->encodedStream->writableStream->pipedReadable_native != NULL && state->encodedStream->writableStream->pipedReadable_native->ResumeHandler != NULL) + { + state->encodedStream->writableStream->pipedReadable_native->paused = 0; + state->encodedStream->writableStream->pipedReadable_native->ResumeHandler(state->encodedStream->writableStream->pipedReadable_native, state->encodedStream->writableStream->pipedReadable_native->user); + } + else + { + if (ILibIsRunningOnChainThread(state->chain)) + { + ILibDuktape_httpStream_webSocket_DecodedResumeSink_Chain(NULL, state); + } + else + { + ILibChain_RunOnMicrostackThreadEx(state->chain, ILibDuktape_httpStream_webSocket_DecodedResumeSink_Chain, state); + } + } +} +int ILibDuktape_httpStream_webSocket_DecodedUnshiftSink(ILibDuktape_DuplexStream *sender, int unshiftBytes, void *user) +{ + // Block Mode Data Transfers should never need to be unshifted, otherwise you're doing it wrong + return(0); +} + +duk_ret_t ILibDuktape_httpStream_webSocketStream_finalizer(duk_context *ctx) +{ + void *chain = Duktape_GetChain(ctx); + duk_get_prop_string(ctx, 0, ILibDuktape_WebSocket_StatePtr); + ILibDuktape_InValidatePointer(chain, Duktape_GetBuffer(ctx, -1, NULL)); + return(0); +} +duk_ret_t ILibDuktape_httpStream_webSocketStream_sendPing(duk_context *ctx) +{ + duk_push_this(ctx); // [WS_DEC] + duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS); // [WS_DEC][WS] + duk_get_prop_string(ctx, -1, ILibDuktape_WebSocket_StatePtr); // [WS_DEC][WS][PTR] + + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)Duktape_GetBuffer(ctx, -1, NULL); + ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(state, WEBSOCKET_OPCODE_PING, NULL, 0, ILibWebClient_WebSocket_FragmentFlag_Complete); + return(0); +} +duk_ret_t ILibDuktape_httpStream_webSocketStream_sendPong(duk_context *ctx) +{ + duk_push_this(ctx); // [WS_DEC] + duk_get_prop_string(ctx, -1, ILibDuktape_WSDEC2WS); // [WS_DEC][WS] + duk_get_prop_string(ctx, -1, ILibDuktape_WebSocket_StatePtr); // [WS_DEC][WS][PTR] + + ILibDuktape_WebSocket_State *state = (ILibDuktape_WebSocket_State*)Duktape_GetBuffer(ctx, -1, NULL); + ILibDuktape_httpStream_webSocket_WriteWebSocketPacket(state, WEBSOCKET_OPCODE_PONG, NULL, 0, ILibWebClient_WebSocket_FragmentFlag_Complete); + return(0); +} + +duk_ret_t ILibDuktape_httpStream_webSocketStream_new(duk_context *ctx) +{ + ILibDuktape_WebSocket_State *state; + duk_push_object(ctx); // [WebSocket] + ILibDuktape_WriteID(ctx, "http.WebSocketStream"); + duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_WebSocket_State)); // [WebSocket][data] + state = (ILibDuktape_WebSocket_State*)Duktape_GetBuffer(ctx, -1, NULL); + memset(state, 0, sizeof(ILibDuktape_WebSocket_State)); + duk_put_prop_string(ctx, -2, ILibDuktape_WebSocket_StatePtr); // [WebSocket] + ILibDuktape_ValidatePointer(Duktape_GetChain(ctx), state); + state->ctx = ctx; + state->ObjectPtr = duk_get_heapptr(ctx, -1); + state->chain = Duktape_GetChain(ctx); + + duk_push_object(ctx); // [WebSocket][Encoded] + ILibDuktape_WriteID(ctx, "http.WebSocketStream.encoded"); + state->encodedStream = ILibDuktape_DuplexStream_InitEx(ctx, ILibDuktape_httpStream_webSocket_EncodedWriteSink, ILibDuktape_httpStream_webSocket_EncodedEndSink, ILibDuktape_httpStream_webSocket_EncodedPauseSink, ILibDuktape_httpStream_webSocket_EncodedResumeSink, ILibDuktape_httpStream_webSocket_EncodedUnshiftSink, state); + ILibDuktape_CreateReadonlyProperty(ctx, "encoded"); // [WebSocket] + duk_push_object(ctx); // [WebSocket][Decoded] + ILibDuktape_WriteID(ctx, "http.WebSocketStream.decoded"); + duk_dup(ctx, -2); // [WebSocket][Decoded][WebSocket] + duk_put_prop_string(ctx, -2, ILibDuktape_WSDEC2WS); // [WebSocket][Decoded] + state->decodedStream = ILibDuktape_DuplexStream_InitEx(ctx, ILibDuktape_httpStream_webSocket_DecodedWriteSink, ILibDuktape_httpStream_webSocket_DecodedEndSink, ILibDuktape_httpStream_webSocket_DecodedPauseSink, ILibDuktape_httpStream_webSocket_DecodedResumeSink, ILibDuktape_httpStream_webSocket_DecodedUnshiftSink, state); + ILibDuktape_EventEmitter_CreateEventEx(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), "ping"); + ILibDuktape_EventEmitter_CreateEventEx(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), "pong"); + ILibDuktape_CreateProperty_InstanceMethod(ctx, "ping", ILibDuktape_httpStream_webSocketStream_sendPing, DUK_VARARGS); + ILibDuktape_CreateProperty_InstanceMethod(ctx, "pong", ILibDuktape_httpStream_webSocketStream_sendPong, DUK_VARARGS); + + ILibDuktape_CreateReadonlyProperty(ctx, "decoded"); // [WebSocket] + ILibDuktape_CreateFinalizer(ctx, ILibDuktape_httpStream_webSocketStream_finalizer); + + return(1); +} + +void ILibDuktape_HttpStream_http_PUSH(duk_context *ctx, void *chain) +{ + duk_push_object(ctx); // [http] + ILibDuktape_WriteID(ctx, "http"); + ILibDuktape_CreateInstanceMethod(ctx, "request", ILibDuktape_HttpStream_http_request, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "createServer", ILibDuktape_HttpStream_http_createServer, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "get", ILibDuktape_HttpStream_http_get, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "createStream", ILibduktape_HttpStream_create, 0); + ILibDuktape_CreateInstanceMethod(ctx, "Agent", ILibDuktape_HttpStream_Agent_new, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "parseUri", ILibDuktape_httpStream_parseUri, 1); + ILibDuktape_CreateInstanceMethod(ctx, "webSocketStream", ILibDuktape_httpStream_webSocketStream_new, 1); + + // HTTP Global Agent + duk_push_c_function(ctx, ILibDuktape_HttpStream_Agent_new, DUK_VARARGS); // [http][newAgent] + duk_dup(ctx, -2); // [http][newAgent][this] + duk_call_method(ctx, 0); // [http][Agent] + duk_put_prop_string(ctx, -2, "globalAgent"); // [http][Agent] +} +void ILibDuktape_HttpStream_https_PUSH(duk_context *ctx, void *chain) +{ + ILibDuktape_HttpStream_http_PUSH(ctx, chain); // [https] + ILibDuktape_WriteID(ctx, "https"); + duk_push_boolean(ctx, 1); + ILibDuktape_CreateReadonlyProperty(ctx, "isHTTPS"); // [https] +} +void ILibDuktape_HttpStream_Init(duk_context *ctx) +{ + ILibDuktape_ModSearch_AddHandler(ctx, "http", ILibDuktape_HttpStream_http_PUSH); + ILibDuktape_ModSearch_AddHandler(ctx, "https", ILibDuktape_HttpStream_https_PUSH); +} + + + diff --git a/microscript/ILibDuktape_MemoryStream.c b/microscript/ILibDuktape_MemoryStream.c index 84f6dbb..ca055f7 100644 --- a/microscript/ILibDuktape_MemoryStream.c +++ b/microscript/ILibDuktape_MemoryStream.c @@ -81,6 +81,7 @@ duk_ret_t ILibDuktape_MemoryStream_buffer(duk_context *ctx) duk_push_external_buffer(ctx); duk_config_buffer(ctx, -1, ms->buffer, ms->bufferLen); + duk_push_buffer_object(ctx, -1, 0, ms->bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); return(1); } @@ -93,6 +94,43 @@ duk_ret_t ILibDuktape_MemoryStream_Finalizer(duk_context *ctx) return(0); } +duk_ret_t ILibDuktape_MemoryStream_writeBE(duk_context *ctx) +{ + duk_push_current_function(ctx); + int size = Duktape_GetIntPropertyValue(ctx, -1, "size", 0); + char buffer[16]; + int value = duk_require_int(ctx, 0); + + switch (size) + { + case 1: + buffer[0] = (char)value; + break; + case 2: + ((unsigned short*)buffer)[0] = htons((unsigned short)value); + break; + case 4: + ((unsigned int*)buffer)[0] = htonl((unsigned int)value); + break; + default: + break; + } + + if (size > 0) + { + duk_push_this(ctx); // [ms] + duk_get_prop_string(ctx, -1, "write"); // [ms][write] + duk_swap_top(ctx, -2); // [write][this] + duk_push_external_buffer(ctx); // [write][this][buffer] + duk_config_buffer(ctx, -1, buffer, size); + duk_call_method(ctx, 1); // [retVal] + return(1); + } + else + { + return(ILibDuktape_Error(ctx, "MemoryStream.writeBE() Unknown Error")); + } +} duk_ret_t ILibDuktape_MemoryStream_new(duk_context *ctx) { int initial = duk_get_top(ctx) > 0 ? duk_require_int(ctx, 0) : 4096; @@ -110,6 +148,11 @@ duk_ret_t ILibDuktape_MemoryStream_new(duk_context *ctx) ms->s = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_MemoryStream_OnWrite, ILibDuktape_MemoryStream_OnEnd, NULL, NULL, ms); ILibDuktape_CreateEventWithGetter(ctx, "buffer", ILibDuktape_MemoryStream_buffer); ILibDuktape_CreateFinalizer(ctx, ILibDuktape_MemoryStream_Finalizer); + + ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "size", 4, "writeUInt32BE", ILibDuktape_MemoryStream_writeBE, 1); + ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "size", 2, "writeUInt16BE", ILibDuktape_MemoryStream_writeBE, 1); + ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "size", 1, "writeUInt8", ILibDuktape_MemoryStream_writeBE, 1); + return(1); } void ILibDuktape_MemoryStream_PUSH(duk_context *ctx, void *chain) diff --git a/microscript/ILibDuktape_NetworkMonitor.c b/microscript/ILibDuktape_NetworkMonitor.c index 755a73b..de5d43d 100644 --- a/microscript/ILibDuktape_NetworkMonitor.c +++ b/microscript/ILibDuktape_NetworkMonitor.c @@ -108,14 +108,14 @@ void ILibDuktape_NetworkMonitor_EventSink(ILibIPAddressMonitor sender, void *use } duk_ret_t ILibDuktape_NetworkMonitor_Finalizer(duk_context *ctx) { - if (ILibIsChainBeingDestroyed(Duktape_GetChain(ctx)) != 0) { return(0); } duk_get_prop_string(ctx, 0, ILibDuktape_NetworkMonitor_PTR); ILibDuktape_NetworkMonitor *nm = (ILibDuktape_NetworkMonitor*)Duktape_GetBuffer(ctx, -1, NULL); - ILibChain_SafeRemoveEx(Duktape_GetChain(ctx), nm->addressMonitor); - if (nm->addressTable != NULL) { ILibHashtable_Destroy(nm->addressTable); } + if (ILibIsChainBeingDestroyed(Duktape_GetChain(ctx)) != 0) { return(0); } + ILibChain_SafeRemoveEx(Duktape_GetChain(ctx), nm->addressMonitor); + return(0); } void ILibDuktape_NetworkMonitor_PUSH(duk_context *ctx, void *chain) diff --git a/microscript/ILibDuktape_Polyfills.c b/microscript/ILibDuktape_Polyfills.c index 12d36d7..480fc75 100644 --- a/microscript/ILibDuktape_Polyfills.c +++ b/microscript/ILibDuktape_Polyfills.c @@ -1,11 +1,15 @@ #include "duktape.h" #include "ILibDuktape_Helpers.h" #include "ILibDuktapeModSearch.h" +#include "ILibDuktape_DuplexStream.h" +#include "ILibDuktape_EventEmitter.h" #include "../microstack/ILibParsers.h" #include "../microstack/ILibCrypto.h" #define ILibDuktape_Timer_Ptrs "\xFF_DuktapeTimer_PTRS" +#define ILibDuktape_Queue_Ptr "\xFF_Queue" + duk_ret_t ILibDuktape_Pollyfills_Buffer_slice(duk_context *ctx) { @@ -63,6 +67,13 @@ duk_ret_t ILibDuktape_Polyfills_Buffer_toString(duk_context *ctx) util_tohex(buffer, (int)bufferLen, tmpBuffer); duk_push_string(ctx, tmpBuffer); } + else if (strcmp(cType, "hex:") == 0) + { + duk_push_fixed_buffer(ctx, 1 + (bufferLen * 3)); + tmpBuffer = Duktape_GetBuffer(ctx, -1, NULL); + util_tohex2(buffer, (int)bufferLen, tmpBuffer); + duk_push_string(ctx, tmpBuffer); + } else { duk_push_string(ctx, "buffer.toString(): Unrecognized parameter"); @@ -84,11 +95,10 @@ duk_ret_t ILibDuktape_Polyfills_Buffer_from(duk_context *ctx) if (nargs == 1) { str = (char*)duk_get_lstring(ctx, 0, &strlength); - duk_push_fixed_buffer(ctx, strlength + 1); + duk_push_fixed_buffer(ctx, strlength); buffer = Duktape_GetBuffer(ctx, -1, NULL); - memcpy_s(buffer, strlength + 1, str, strlength); - buffer[strlength] = 0; - duk_push_buffer_object(ctx, -1, 0, strlength+1, DUK_BUFOBJ_ARRAYBUFFER); + memcpy_s(buffer, strlength, str, strlength); + duk_push_buffer_object(ctx, -1, 0, strlength, DUK_BUFOBJ_NODEJS_BUFFER); return(1); } else if(!(nargs == 2 && duk_is_string(ctx, 0) && duk_is_string(ctx, 1))) @@ -107,14 +117,14 @@ duk_ret_t ILibDuktape_Polyfills_Buffer_from(duk_context *ctx) duk_push_fixed_buffer(ctx, ILibBase64DecodeLength((int)strlength)); buffer = Duktape_GetBuffer(ctx, -1, NULL); bufferLen = ILibBase64Decode((unsigned char*)str, (int)strlength, (unsigned char**)&buffer); - duk_push_buffer_object(ctx, -1, 0, bufferLen, DUK_BUFOBJ_ARRAYBUFFER); + duk_push_buffer_object(ctx, -1, 0, bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); } else if (strcmp(encoding, "hex") == 0) { duk_push_fixed_buffer(ctx, strlength / 2); buffer = Duktape_GetBuffer(ctx, -1, NULL); bufferLen = util_hexToBuf(str, (int)strlength, buffer); - duk_push_buffer_object(ctx, -1, 0, bufferLen, DUK_BUFOBJ_ARRAYBUFFER); + duk_push_buffer_object(ctx, -1, 0, bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); } else { @@ -136,31 +146,55 @@ duk_ret_t ILibDuktape_Polyfills_Buffer_readInt32BE(duk_context *ctx) duk_push_int(ctx, ntohl(((int*)(buffer + offset))[0])); return(1); } +duk_ret_t ILibDuktape_Polyfills_Buffer_alloc(duk_context *ctx) +{ + int sz = duk_require_int(ctx, 0); + duk_push_fixed_buffer(ctx, sz); + char *buffer = Duktape_GetBuffer(ctx, -1, NULL); + memset(buffer, 0, sz); + duk_push_buffer_object(ctx, -1, 0, sz, DUK_BUFOBJ_NODEJS_BUFFER); + return(1); +} void ILibDuktape_Polyfills_Buffer(duk_context *ctx) { // Polyfill 'Buffer.slice' - duk_get_prop_string(ctx, -1, "Duktape"); // [g][Duktape] - duk_get_prop_string(ctx, -1, "Buffer"); // [g][Duktape][Buffer] - duk_get_prop_string(ctx, -1, "prototype"); // [g][Duktape][Buffer][prototype] - duk_push_c_function(ctx, ILibDuktape_Pollyfills_Buffer_slice, DUK_VARARGS); // [g][Duktape][Buffer][prototype][func] - duk_put_prop_string(ctx, -2, "slice"); // [g][Duktape][Buffer][prototype] - duk_push_c_function(ctx, ILibDuktape_Polyfills_Buffer_readInt32BE, DUK_VARARGS); - duk_put_prop_string(ctx, -2, "readInt32BE"); - duk_pop_3(ctx); // [g] + duk_get_prop_string(ctx, -1, "Duktape"); // [g][Duktape] + duk_get_prop_string(ctx, -1, "Buffer"); // [g][Duktape][Buffer] + duk_get_prop_string(ctx, -1, "prototype"); // [g][Duktape][Buffer][prototype] + duk_push_c_function(ctx, ILibDuktape_Pollyfills_Buffer_slice, DUK_VARARGS); // [g][Duktape][Buffer][prototype][func] + duk_put_prop_string(ctx, -2, "slice"); // [g][Duktape][Buffer][prototype] + duk_push_c_function(ctx, ILibDuktape_Polyfills_Buffer_readInt32BE, DUK_VARARGS);// [g][Duktape][Buffer][prototype][func] + duk_put_prop_string(ctx, -2, "readInt32BE"); // [g][Duktape][Buffer][prototype] + duk_pop_3(ctx); // [g] // Polyfill 'Buffer.toString() - duk_get_prop_string(ctx, -1, "Duktape"); // [g][Duktape] - duk_get_prop_string(ctx, -1, "Buffer"); // [g][Duktape][Buffer] - duk_get_prop_string(ctx, -1, "prototype"); // [g][Duktape][Buffer][prototype] - duk_push_c_function(ctx, ILibDuktape_Polyfills_Buffer_toString, DUK_VARARGS);// [g][Duktape][Buffer][prototype][func] - duk_put_prop_string(ctx, -2, "toString"); // [g][Duktape][Buffer][prototype] - duk_pop_3(ctx); // [g] - + duk_get_prop_string(ctx, -1, "Duktape"); // [g][Duktape] + duk_get_prop_string(ctx, -1, "Buffer"); // [g][Duktape][Buffer] + duk_get_prop_string(ctx, -1, "prototype"); // [g][Duktape][Buffer][prototype] + duk_push_c_function(ctx, ILibDuktape_Polyfills_Buffer_toString, DUK_VARARGS); // [g][Duktape][Buffer][prototype][func] + duk_put_prop_string(ctx, -2, "toString"); // [g][Duktape][Buffer][prototype] + duk_pop_3(ctx); // [g] + // Polyfill Buffer.from() - duk_get_prop_string(ctx, -1, "Buffer"); // [g][Buffer] - duk_push_c_function(ctx, ILibDuktape_Polyfills_Buffer_from, DUK_VARARGS); // [g][Buffer][func] - duk_put_prop_string(ctx, -2, "from"); // [g][Buffer] - duk_pop(ctx); + duk_get_prop_string(ctx, -1, "Buffer"); // [g][Buffer] + duk_push_c_function(ctx, ILibDuktape_Polyfills_Buffer_from, DUK_VARARGS); // [g][Buffer][func] + duk_put_prop_string(ctx, -2, "from"); // [g][Buffer] + duk_pop(ctx); // [g] + + // Polyfill Buffer.alloc() for Node Buffers) + duk_get_prop_string(ctx, -1, "Buffer"); // [g][Buffer] + duk_push_c_function(ctx, ILibDuktape_Polyfills_Buffer_alloc, DUK_VARARGS); // [g][Buffer][func] + duk_put_prop_string(ctx, -2, "alloc"); // [g][Buffer] + duk_pop(ctx); // [g] + + + // Polyfill Buffer.toString() for Node Buffers + duk_get_prop_string(ctx, -1, "Buffer"); // [g][Buffer] + duk_get_prop_string(ctx, -1, "prototype"); // [g][Buffer][prototype] + duk_push_c_function(ctx, ILibDuktape_Polyfills_Buffer_toString, DUK_VARARGS); // [g][Buffer][prototype][func] + duk_put_prop_string(ctx, -2, "toString"); // [g][Buffer][prototype] + duk_pop_2(ctx); // [g] + } duk_ret_t ILibDuktape_Polyfills_String_startsWith(duk_context *ctx) { @@ -379,6 +413,7 @@ void ILibDuktape_Polyfills_timer_elapsed(void *obj) { ILibDuktape_Timer *ptrs = (ILibDuktape_Timer*)obj; int argCount, i; + duk_context *ctx = ptrs->ctx; if (ptrs->timerType == ILibDuktape_Timer_Type_INTERVAL) { @@ -397,7 +432,7 @@ void ILibDuktape_Polyfills_timer_elapsed(void *obj) } duk_pop(ptrs->ctx); // [func][this][...arg...] if (duk_pcall_method(ptrs->ctx, argCount) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ptrs->ctx, "timers.onElapsed() callback handler"); } - duk_pop(ptrs->ctx); // ... + duk_pop(ctx); // ... } duk_ret_t ILibDuktape_Polyfills_timer_set(duk_context *ctx) { @@ -490,8 +525,245 @@ duk_ret_t ILibDuktape_Polyfills_addModule(duk_context *ctx) } return(0); } +duk_ret_t ILibDuktape_Queue_Finalizer(duk_context *ctx) +{ + duk_get_prop_string(ctx, 0, ILibDuktape_Queue_Ptr); + ILibQueue_Destroy((ILibQueue)duk_get_pointer(ctx, -1)); + return(0); +} +duk_ret_t ILibDuktape_Queue_EnQueue(duk_context *ctx) +{ + ILibQueue Q; + int i; + int nargs = duk_get_top(ctx); + duk_push_this(ctx); // [queue] + duk_get_prop_string(ctx, -1, ILibDuktape_Queue_Ptr); // [queue][ptr] + Q = (ILibQueue)duk_get_pointer(ctx, -1); + duk_pop(ctx); // [queue] + + ILibDuktape_Push_ObjectStash(ctx); // [queue][stash] + duk_push_array(ctx); // [queue][stash][array] + for (i = 0; i < nargs; ++i) + { + duk_dup(ctx, i); // [queue][stash][array][arg] + duk_put_prop_index(ctx, -2, i); // [queue][stash][array] + } + ILibQueue_EnQueue(Q, duk_get_heapptr(ctx, -1)); + duk_put_prop_string(ctx, -2, Duktape_GetStashKey(duk_get_heapptr(ctx, -1))); // [queue][stash] + return(0); +} +duk_ret_t ILibDuktape_Queue_DeQueue(duk_context *ctx) +{ + duk_push_current_function(ctx); + duk_get_prop_string(ctx, -1, "peek"); + int peek = duk_get_int(ctx, -1); + + duk_push_this(ctx); // [Q] + duk_get_prop_string(ctx, -1, ILibDuktape_Queue_Ptr); // [Q][ptr] + ILibQueue Q = (ILibQueue)duk_get_pointer(ctx, -1); + void *h = peek == 0 ? ILibQueue_DeQueue(Q) : ILibQueue_PeekQueue(Q); + if (h == NULL) { return(ILibDuktape_Error(ctx, "Queue is empty")); } + duk_pop(ctx); // [Q] + ILibDuktape_Push_ObjectStash(ctx); // [Q][stash] + duk_push_heapptr(ctx, h); // [Q][stash][array] + int length = (int)duk_get_length(ctx, -1); + int i; + for (i = 0; i < length; ++i) + { + duk_get_prop_index(ctx, -i - 1, i); // [Q][stash][array][args] + } + if (peek == 0) { duk_del_prop_string(ctx, -length - 2, Duktape_GetStashKey(h)); } + return(length); +} + +duk_ret_t ILibDuktape_Queue_new(duk_context *ctx) +{ + duk_push_object(ctx); // [queue] + duk_push_pointer(ctx, ILibQueue_Create()); // [queue][ptr] + duk_put_prop_string(ctx, -2, ILibDuktape_Queue_Ptr); // [queue] + + ILibDuktape_CreateFinalizer(ctx, ILibDuktape_Queue_Finalizer); + ILibDuktape_CreateInstanceMethod(ctx, "enQueue", ILibDuktape_Queue_EnQueue, DUK_VARARGS); + ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "peek", 0, "deQueue", ILibDuktape_Queue_DeQueue, DUK_VARARGS); + ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "peek", 1, "peekQueue", ILibDuktape_Queue_DeQueue, DUK_VARARGS); + + return(1); +} +void ILibDuktape_Queue_Push(duk_context *ctx, void* chain) +{ + duk_push_c_function(ctx, ILibDuktape_Queue_new, 0); +} + +typedef struct ILibDuktape_DynamicBuffer_data +{ + int start; + int end; + int unshiftBytes; + char *buffer; + int bufferLen; +}ILibDuktape_DynamicBuffer_data; + +typedef struct ILibDuktape_DynamicBuffer_ContextSwitchData +{ + void *chain; + void *heapptr; + ILibDuktape_DuplexStream *stream; + ILibDuktape_DynamicBuffer_data *data; + int bufferLen; + char buffer[]; +}ILibDuktape_DynamicBuffer_ContextSwitchData; + +ILibTransport_DoneState ILibDuktape_DynamicBuffer_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user); +void ILibDuktape_DynamicBuffer_WriteSink_ChainThread(void *chain, void *user) +{ + ILibDuktape_DynamicBuffer_ContextSwitchData *data = (ILibDuktape_DynamicBuffer_ContextSwitchData*)user; + if (ILibDuktape_IsPointerValid(chain, data->heapptr) != 0) + { + ILibDuktape_DynamicBuffer_WriteSink(data->stream, data->buffer, data->bufferLen, data->data); + ILibDuktape_DuplexStream_Ready(data->stream); + } + free(user); +} +ILibTransport_DoneState ILibDuktape_DynamicBuffer_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) +{ + ILibDuktape_DynamicBuffer_data *data = (ILibDuktape_DynamicBuffer_data*)user; + if (ILibIsRunningOnChainThread(stream->readableStream->chain) == 0) + { + ILibDuktape_DynamicBuffer_ContextSwitchData *tmp = (ILibDuktape_DynamicBuffer_ContextSwitchData*)ILibMemory_Allocate(sizeof(ILibDuktape_DynamicBuffer_ContextSwitchData) + bufferLen, 0, NULL, NULL); + tmp->chain = stream->readableStream->chain; + tmp->heapptr = stream->ParentObject; + tmp->stream = stream; + tmp->data = data; + tmp->bufferLen = bufferLen; + memcpy_s(tmp->buffer, bufferLen, buffer, bufferLen); + ILibChain_RunOnMicrostackThread(tmp->chain, ILibDuktape_DynamicBuffer_WriteSink_ChainThread, tmp); + return(ILibTransport_DoneState_INCOMPLETE); + } + + + if ((data->bufferLen - data->start - data->end) < bufferLen) + { + if (data->end > 0) + { + // Move the buffer first + memmove_s(data->buffer, data->bufferLen, data->buffer + data->start, data->end); + data->start = 0; + } + if ((data->bufferLen - data->end) < bufferLen) + { + // Need to resize buffer first + int tmpSize = data->bufferLen; + while ((tmpSize - data->end) < bufferLen) + { + tmpSize += 4096; + } + data->buffer = (char*)realloc(data->buffer, tmpSize); + data->bufferLen = tmpSize; + } + } + + + memcpy_s(data->buffer + data->start + data->end, data->bufferLen - data->start - data->end, buffer, bufferLen); + data->end += bufferLen; + + int unshifted = 0; + do + { + duk_push_heapptr(stream->readableStream->ctx, stream->ParentObject); // [ds] + duk_get_prop_string(stream->readableStream->ctx, -1, "emit"); // [ds][emit] + duk_swap_top(stream->readableStream->ctx, -2); // [emit][this] + duk_push_string(stream->readableStream->ctx, "readable"); // [emit][this][readable] + if (duk_pcall_method(stream->readableStream->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->readableStream->ctx, "DynamicBuffer.WriteSink => readable(): "); } + duk_pop(stream->readableStream->ctx); // ... + + ILibDuktape_DuplexStream_WriteData(stream, data->buffer + data->start, data->end); + if (data->unshiftBytes == 0) + { + // All the data was consumed + data->start = data->end = 0; + } + else + { + unshifted = (data->end - data->unshiftBytes); + if (unshifted > 0) + { + data->start += unshifted; + data->end = data->unshiftBytes; + data->unshiftBytes = 0; + } + } + } while (unshifted != 0); + + return(ILibTransport_DoneState_COMPLETE); +} +void ILibDuktape_DynamicBuffer_EndSink(ILibDuktape_DuplexStream *stream, void *user) +{ + ILibDuktape_DuplexStream_WriteEnd(stream); +} +duk_ret_t ILibDuktape_DynamicBuffer_Finalizer(duk_context *ctx) +{ + ILibDuktape_InValidateHeapPointer(ctx, 0); + duk_get_prop_string(ctx, 0, "\xFF_buffer"); + ILibDuktape_DynamicBuffer_data *data = (ILibDuktape_DynamicBuffer_data*)Duktape_GetBuffer(ctx, -1, NULL); + free(data->buffer); + return(0); +} + +int ILibDuktape_DynamicBuffer_unshift(ILibDuktape_DuplexStream *sender, int unshiftBytes, void *user) +{ + ILibDuktape_DynamicBuffer_data *data = (ILibDuktape_DynamicBuffer_data*)user; + data->unshiftBytes = unshiftBytes; + return(unshiftBytes); +} +duk_ret_t ILibDuktape_DynamicBuffer_read(duk_context *ctx) +{ + ILibDuktape_DynamicBuffer_data *data; + duk_push_this(ctx); // [DynamicBuffer] + duk_get_prop_string(ctx, -1, "\xFF_buffer"); // [DynamicBuffer][buffer] + data = (ILibDuktape_DynamicBuffer_data*)Duktape_GetBuffer(ctx, -1, NULL); + duk_push_external_buffer(ctx); // [DynamicBuffer][buffer][extBuffer] + duk_config_buffer(ctx, -1, data->buffer + data->start, data->bufferLen - (data->start + data->end)); + duk_push_buffer_object(ctx, -1, 0, data->bufferLen - (data->start + data->end), DUK_BUFOBJ_NODEJS_BUFFER); + return(1); +} +duk_ret_t ILibDuktape_DynamicBuffer_new(duk_context *ctx) +{ + ILibDuktape_DynamicBuffer_data *data; + int initSize = 4096; + if (duk_get_top(ctx) != 0) + { + initSize = duk_require_int(ctx, 0); + } + + duk_push_object(ctx); // [stream] + duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_DynamicBuffer_data)); + data = (ILibDuktape_DynamicBuffer_data*)Duktape_GetBuffer(ctx, -1, NULL); + memset(data, 0, sizeof(ILibDuktape_DynamicBuffer_data)); + duk_put_prop_string(ctx, -2, "\xFF_buffer"); + + data->bufferLen = initSize; + data->buffer = (char*)malloc(initSize); + + ILibDuktape_DuplexStream_InitEx(ctx, ILibDuktape_DynamicBuffer_WriteSink, ILibDuktape_DynamicBuffer_EndSink, NULL, NULL, ILibDuktape_DynamicBuffer_unshift, data); + ILibDuktape_EventEmitter_CreateEventEx(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), "readable"); + ILibDuktape_CreateInstanceMethod(ctx, "read", ILibDuktape_DynamicBuffer_read, DUK_VARARGS); + ILibDuktape_CreateFinalizer(ctx, ILibDuktape_DynamicBuffer_Finalizer); + ILibDuktape_ValidateHeapPointer(ctx, -1); + + return(1); +} + +void ILibDuktape_DynamicBuffer_Push(duk_context *ctx, void *chain) +{ + duk_push_c_function(ctx, ILibDuktape_DynamicBuffer_new, DUK_VARARGS); +} + + void ILibDuktape_Polyfills_Init(duk_context *ctx) { + ILibDuktape_ModSearch_AddHandler(ctx, "queue", ILibDuktape_Queue_Push); + ILibDuktape_ModSearch_AddHandler(ctx, "DynamicBuffer", ILibDuktape_DynamicBuffer_Push); + // Global Polyfills duk_push_global_object(ctx); // [g] diff --git a/microscript/ILibDuktape_ReadableStream.c b/microscript/ILibDuktape_ReadableStream.c index 329ded8..d034337 100644 --- a/microscript/ILibDuktape_ReadableStream.c +++ b/microscript/ILibDuktape_ReadableStream.c @@ -25,6 +25,7 @@ limitations under the License. #define ILibDuktape_readableStream_WritePipes "\xFF_WritePipes" #define ILibDuktape_readableStream_WritePipes_PTRBUFFER "\xFF_WritePipesPtrBuffer" #define ILibDuktape_readableStream_WritePipes_Stream "\xFF_WritePipes_Stream" +#define ILibDuktape_readableStream_PipeArray "\xFF_RS_PipeArray" #ifdef __DOXY__ /*! @@ -124,40 +125,48 @@ void ILibDuktape_readableStream_WriteData_buffer(ILibDuktape_readableStream *str } tmp->Next = buffered; } - - if (stream->paused == 0) - { - stream->paused = 1; - stream->PauseHandler(stream, stream->user); - } } void ILibDuktape_readableStream_WriteData_OnData_ChainThread(void *chain, void *user) { - ILibDuktape_readableStream *stream = (ILibDuktape_readableStream*)user; + ILibDuktape_readableStream_bufferedData *data = (ILibDuktape_readableStream_bufferedData*)user; + ILibDuktape_readableStream *stream = (ILibDuktape_readableStream*)data->Next; stream->paused = 0; - duk_push_heapptr(stream->ctx, stream->OnData); // [func] - duk_push_heapptr(stream->ctx, stream->object); // [func][this] - if (stream->extBuffer_Reserved == 0) + if (data->Reserved == 0) { - duk_push_heapptr(stream->ctx, stream->extBuffer); // [func][this][buffer] - duk_config_buffer(stream->ctx, -1, stream->extBuffer_buffer, stream->extBuffer_bufferLen); + duk_push_external_buffer(stream->ctx); // [ext] + duk_config_buffer(stream->ctx, -1, data->buffer, data->bufferLen); + } + duk_push_heapptr(stream->ctx, stream->OnData); // [ext][func] + duk_push_heapptr(stream->ctx, stream->object); // [ext][func][this] + if (data->Reserved == 0) + { + duk_push_buffer_object(stream->ctx, -3, 0, data->bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][func][this][buffer] } else { - duk_push_lstring(stream->ctx, stream->extBuffer_buffer, stream->extBuffer_bufferLen); + duk_push_lstring(stream->ctx, data->buffer, data->bufferLen); // [ext][func][this][buffer/string] } - if (duk_pcall_method(stream->ctx, 1) != 0) // [retVal] + if (duk_pcall_method(stream->ctx, 1) != 0) // [...][retVal] { ILibDuktape_Process_UncaughtException(stream->ctx); } - duk_pop(stream->ctx); // ... + if (data->Reserved == 0) + { + duk_pop_2(stream->ctx); // ... + } + else + { + duk_pop(stream->ctx); // ... + } + free(data); if (stream->paused == 0 && stream->ResumeHandler != NULL) { stream->ResumeHandler(stream, stream->user); } } -void ILibDuktape_readableStream_WriteData_Flush(struct ILibDuktape_WritableStream *ws, void *user) +int ILibDuktape_readableStream_WriteData_Flush(struct ILibDuktape_WritableStream *ws, void *user) { ILibDuktape_readableStream *stream = (ILibDuktape_readableStream*)user; - + int unpipeInProgress = 0; + #ifdef WIN32 if(InterlockedDecrement(&(stream->pipe_pendingCount)) == 0) #elif defined(__ATOMIC_SEQ_CST) @@ -169,243 +178,220 @@ void ILibDuktape_readableStream_WriteData_Flush(struct ILibDuktape_WritableStrea if(stream->pipe_pendingCount == 0) #endif { - if (stream->ResumeHandler != NULL) { stream->paused = 0; stream->ResumeHandler(stream, stream->user); } + sem_wait(&(stream->pipeLock)); + stream->pipeInProgress = 0; + unpipeInProgress = stream->unpipeInProgress; + sem_post(&(stream->pipeLock)); + if (unpipeInProgress == 0 && stream->ResumeHandler != NULL && stream->paused != 0) { stream->paused = 0; stream->ResumeHandler(stream, stream->user); } + return(1); } + return(0); } -duk_ret_t ILibDuktape_readableStream_WriteData_Flush_JS(duk_context *ctx) + +duk_ret_t ILibDuktape_readableStream_WriteDataEx_Flush(duk_context *ctx) { - ILibDuktape_readableStream *stream; - duk_push_current_function(ctx); - duk_get_prop_string(ctx, -1, "readable"); - stream = (ILibDuktape_readableStream*)duk_get_pointer(ctx, -1); + duk_get_prop_string(ctx, -1, "\xFF_STREAM"); + ILibDuktape_readableStream *stream = (ILibDuktape_readableStream*)duk_to_pointer(ctx, -1); ILibDuktape_readableStream_WriteData_Flush(NULL, stream); - - return 0; + return(0); } -void ILibDuktape_readableStream_WriteData_ChainThread(void *chain, void *user) + +int ILibDuktape_readableStream_WriteDataEx_Chain_Dispatch(ILibDuktape_readableStream *stream, void *ws, char *buffer, int bufferLen) { - ILibDuktape_readableStream *stream = (ILibDuktape_readableStream*)user; - ILibDuktape_readableStream_nextWriteablePipe *w; - int jsCount = 0; + int retVal = 0; + duk_push_external_buffer(stream->ctx); // [ext] + duk_config_buffer(stream->ctx, -1, buffer, bufferLen); + duk_push_heapptr(stream->ctx, ws); // [ext][ws] + duk_get_prop_string(stream->ctx, -1, "write"); // [ext][ws][write] + duk_swap_top(stream->ctx, -2); // [ext][write][this] + duk_push_buffer_object(stream->ctx, -3, 0, bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); // [ext][write][this][buffer] + duk_push_c_function(stream->ctx, ILibDuktape_readableStream_WriteDataEx_Flush, DUK_VARARGS);// [ext][write][this][buffer][flush] + duk_push_pointer(stream->ctx, stream); // [ext][write][this][buffer][flush][ptr] + duk_put_prop_string(stream->ctx, -2, "\xFF_STREAM"); // [ext][write][this][buffer][flush] + if (duk_pcall_method(stream->ctx, 2) != 0) // [ext][...] + { + ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "readable.write(): Error Piping "); + if (ILibDuktape_readableStream_WriteData_Flush(NULL, stream)) { retVal = 2; } + } + retVal = duk_to_boolean(stream->ctx, -1) ? 1 : 0; + duk_pop_2(stream->ctx); - sem_wait(&(stream->pipeLock)); - w = stream->nextWriteable; - stream->pipe_pendingCount = 0; + return(retVal); +} +void ILibDuktape_readableStream_WriteDataEx_Chain(void *chain, void *user) +{ + ILibDuktape_readableStream_bufferedData *data = (ILibDuktape_readableStream_bufferedData*)user; + ILibDuktape_readableStream *stream = (ILibDuktape_readableStream*)data->Next; + ILibDuktape_readableStream_nextWriteablePipe *w = stream->nextWriteable; while (w != NULL) { - if (w->nativeWritable != NULL || w->writableStream != NULL) { stream->pipe_pendingCount++; } - w = w->next; - } - - w = stream->nextWriteable; - while (w != NULL) - { - if (w->nativeWritable != NULL) + if (w->writableStream != NULL && w->nativeWritable == NULL) { - ILibDuktape_WritableStream *ws = (ILibDuktape_WritableStream*)w->nativeWritable; - switch (ws->WriteSink(ws, stream->extBuffer_buffer, stream->extBuffer_bufferLen, ws->WriteSink_User)) - { - case ILibTransport_DoneState_INCOMPLETE: - ws->OnWriteFlushEx = ILibDuktape_readableStream_WriteData_Flush; - ws->OnWriteFlushEx_User = stream; - break; - case ILibTransport_DoneState_COMPLETE: - ws->OnWriteFlushEx = NULL; - ws->OnWriteFlushEx_User = NULL; -#ifdef WIN32 - InterlockedDecrement(&(stream->pipe_pendingCount)); -#elif defined(__ATOMIC_SEQ_CST) - __atomic_sub_fetch(&(stream->pipe_pendingCount), 1, __ATOMIC_SEQ_CST); -#else - --stream->pipe_pendingCount; -#endif - break; - case ILibTransport_DoneState_ERROR: -#ifdef WIN32 - InterlockedDecrement(&(stream->pipe_pendingCount)); -#elif defined(__ATOMIC_SEQ_CST) - __atomic_sub_fetch(&(stream->pipe_pendingCount), 1, __ATOMIC_SEQ_CST); -#else - --stream->pipe_pendingCount; -#endif - break; - } - } - else if(w->writableStream != NULL) - { - duk_push_heapptr(stream->ctx, w->writableStream); // [stream] - duk_get_prop_string(stream->ctx, -1, "write"); // [stream][func] - duk_swap_top(stream->ctx, -2); // [func][this] - duk_push_heapptr(stream->ctx, stream->extBuffer); // [func][this][chunk] - duk_config_buffer(stream->ctx, -1, stream->extBuffer_buffer, stream->extBuffer_bufferLen); - duk_push_c_function(stream->ctx, ILibDuktape_readableStream_WriteData_Flush_JS, DUK_VARARGS); // [func][this][chunk][callback] - duk_push_pointer(stream->ctx, stream); // [func][this][chunk][callback][user] - duk_put_prop_string(stream->ctx, -2, "readable"); // [func][this][chunk][callback] - if (duk_pcall_method(stream->ctx, 2) != 0) // [retVal] - { - ILibDuktape_Process_UncaughtException(stream->ctx); - } - else - { - jsCount += duk_get_int(stream->ctx, -1); - } - duk_pop(stream->ctx); + if (ILibDuktape_readableStream_WriteDataEx_Chain_Dispatch(stream, w->writableStream, data->buffer, data->bufferLen) == 2) { break; } } w = w->next; } - if (stream->paused != 0 && stream->pipe_pendingCount == 0) - { - sem_post(&(stream->pipeLock)); - if (stream->ResumeHandler != NULL) { stream->paused = 0; stream->ResumeHandler(stream, stream->user); } - } - else - { - sem_post(&(stream->pipeLock)); - } + free(data); } int ILibDuktape_readableStream_WriteDataEx(ILibDuktape_readableStream *stream, int streamReserved, char* buffer, int bufferLen) { ILibDuktape_readableStream_nextWriteablePipe *w; - int nonNativeCount = 0; - int nativeCount = 0; + int dispatchedNonNative = 0; + int noContinue = 0; + int dispatched = 0; + int needPause = 0; + + if (stream == NULL) { return(1); } if (stream->paused != 0) { ILibDuktape_readableStream_WriteData_buffer(stream, streamReserved, buffer, bufferLen); + if (stream->paused == 0 && stream->PauseHandler != NULL) { stream->paused = 1; stream->PauseHandler(stream, stream->user); } return(stream->paused); } if (stream->bypassValue == 0 || stream->bypassValue != streamReserved) { sem_wait(&(stream->pipeLock)); + stream->pipeInProgress = 1; + sem_post(&(stream->pipeLock)); + + w = stream->nextWriteable; + stream->pipe_pendingCount = 0; + while (w != NULL) + { + ++stream->pipe_pendingCount; + w = w->next; + } + dispatched = stream->pipe_pendingCount; w = stream->nextWriteable; while (w != NULL) { - if (w->nativeWritable == 0) { ++nonNativeCount; } - else { ++nativeCount; } - w = w->next; - } - w = stream->nextWriteable; - if (w != NULL) - { - if (nonNativeCount > 0) + if (w->nativeWritable != NULL) { - // There are piped Pure JavaScript objects... We must context switch to Microstack Thread - stream->extBuffer_Reserved = streamReserved; - stream->extBuffer_buffer = buffer; - stream->extBuffer_bufferLen = bufferLen; - sem_post(&(stream->pipeLock)); - if (stream->PauseHandler != NULL) { stream->paused = 1; stream->PauseHandler(stream, stream->user); } - ILibChain_RunOnMicrostackThread(stream->chain, ILibDuktape_readableStream_WriteData_ChainThread, stream); - return(stream->paused); - } - else - { - // All piped objects are native, so we can blast out a send - stream->pipe_pendingCount = nativeCount; - while (w != NULL) + ILibDuktape_WritableStream *ws = (ILibDuktape_WritableStream*)w->nativeWritable; + ws->Reserved = streamReserved; + switch (ws->WriteSink(ws, buffer, bufferLen, ws->WriteSink_User)) { - if (w->nativeWritable != NULL) - { - ILibDuktape_WritableStream *ws = (ILibDuktape_WritableStream*)w->nativeWritable; - ws->Reserved = streamReserved; - switch (ws->WriteSink(ws, buffer, bufferLen, ws->WriteSink_User)) - { - case ILibTransport_DoneState_INCOMPLETE: - ws->OnWriteFlushEx = ILibDuktape_readableStream_WriteData_Flush; - ws->OnWriteFlushEx_User = stream; - break; - case ILibTransport_DoneState_COMPLETE: - ws->OnWriteFlushEx = NULL; - ws->OnWriteFlushEx_User = NULL; -#ifdef WIN32 - InterlockedDecrement(&(stream->pipe_pendingCount)); -#elif defined(__ATOMIC_SEQ_CST) - __atomic_sub_fetch(&(stream->pipe_pendingCount), 1, __ATOMIC_SEQ_CST); -#else - --stream->pipe_pendingCount; -#endif - break; - case ILibTransport_DoneState_ERROR: -#ifdef WIN32 - InterlockedDecrement(&(stream->pipe_pendingCount)); -#elif defined(__ATOMIC_SEQ_CST) - __atomic_sub_fetch(&(stream->pipe_pendingCount), 1, __ATOMIC_SEQ_CST); -#else - --stream->pipe_pendingCount; -#endif - break; - } - } - w = w->next; + case ILibTransport_DoneState_INCOMPLETE: + ws->OnWriteFlushEx = ILibDuktape_readableStream_WriteData_Flush; + ws->OnWriteFlushEx_User = stream; + needPause = 1; + break; + case ILibTransport_DoneState_COMPLETE: + ws->OnWriteFlushEx = NULL; + ws->OnWriteFlushEx_User = NULL; + if (ILibDuktape_readableStream_WriteData_Flush(ws, stream)) { noContinue = 1; } + break; + case ILibTransport_DoneState_ERROR: + if (ILibDuktape_readableStream_WriteData_Flush(ws, stream)) { noContinue = 1; } + break; } - if (stream->pipe_pendingCount == 0) + if (noContinue != 0) { break; } + } + else if (w->writableStream != NULL && dispatchedNonNative == 0) + { + if (ILibIsRunningOnChainThread(stream->chain) == 0) { - sem_post(&(stream->pipeLock)); - return(stream->paused); + ILibDuktape_readableStream_bufferedData *tmp = (ILibDuktape_readableStream_bufferedData*)ILibMemory_Allocate(sizeof(ILibDuktape_readableStream_bufferedData) + bufferLen, 0, NULL, NULL); + tmp->Next = (ILibDuktape_readableStream_bufferedData*)stream; + tmp->Reserved = streamReserved; + tmp->bufferLen = bufferLen; + memcpy_s(tmp->buffer, bufferLen, buffer, bufferLen); + dispatchedNonNative = 1; + needPause = 1; + ILibChain_RunOnMicrostackThreadEx(stream->chain, ILibDuktape_readableStream_WriteDataEx_Chain, tmp); } else { - sem_post(&(stream->pipeLock)); - if (stream->PauseHandler != NULL) { stream->paused = 1; stream->PauseHandler(stream, stream->user); } - return(stream->paused); + // We're running on the Chain Thread, so we can directly dispatch into JS + switch (ILibDuktape_readableStream_WriteDataEx_Chain_Dispatch(stream, w->writableStream, buffer, bufferLen)) + { + case 1: // Need to Pause + needPause = 1; + break; + case 2: // Complete + noContinue = 1; + break; + default: // NOP + break; + } } } - } - else - { - sem_post(&(stream->pipeLock)); + if (noContinue != 0) { break; } + w = w->next; } } - - if (stream->OnData != NULL) + + if (dispatched == 0) { - if (ILibIsRunningOnChainThread(stream->chain)) + sem_wait(&(stream->pipeLock)); + stream->pipeInProgress = 0; + sem_post(&(stream->pipeLock)); + + if (stream->OnData != NULL) { - duk_push_heapptr(stream->ctx, stream->OnData); // [func] - duk_push_heapptr(stream->ctx, stream->object); // [func][this] - if (streamReserved == 0) + if (ILibIsRunningOnChainThread(stream->chain)) { - duk_push_heapptr(stream->ctx, stream->extBuffer); // [func][this][buffer] - duk_config_buffer(stream->ctx, -1, buffer, bufferLen); + if (streamReserved == 0) + { + duk_push_external_buffer(stream->ctx); // [extBuffer] + duk_config_buffer(stream->ctx, -1, buffer, bufferLen); + } + duk_push_heapptr(stream->ctx, stream->OnData); // [func] + duk_push_heapptr(stream->ctx, stream->object); // [func][this] + if (streamReserved == 0) + { + duk_push_buffer_object(stream->ctx, -3, 0, bufferLen, DUK_BUFOBJ_NODEJS_BUFFER); // [extBuffer][func][this][nodeBuffer] + } + else + { + duk_push_lstring(stream->ctx, buffer, bufferLen); // [func][this][string] + } + if (duk_pcall_method(stream->ctx, 1) != 0) // [retVal] + { + ILibDuktape_Process_UncaughtException(stream->ctx); + } + if (streamReserved == 0) + { + duk_pop_2(stream->ctx); + } + else + { + duk_pop(stream->ctx); // ... + } } else { - duk_push_lstring(stream->ctx, buffer, bufferLen); // [func][this][string] + // Need to PAUSE, and context switch to Chain Thread, so we can dispatch into JavaScript + ILibDuktape_readableStream_bufferedData *tmp = (ILibDuktape_readableStream_bufferedData*)ILibMemory_Allocate(sizeof(ILibDuktape_readableStream_bufferedData) + bufferLen, 0, NULL, NULL); + tmp->bufferLen = bufferLen; + tmp->Reserved = streamReserved; + tmp->Next = (ILibDuktape_readableStream_bufferedData*)stream; + memcpy_s(tmp->buffer, bufferLen, buffer, bufferLen); + needPause = 1; + ILibChain_RunOnMicrostackThread(stream->chain, ILibDuktape_readableStream_WriteData_OnData_ChainThread, tmp); } - if (duk_pcall_method(stream->ctx, 1) != 0) // [retVal] - { - ILibDuktape_Process_UncaughtException(stream->ctx); - } - duk_pop(stream->ctx); // ... } - else + else if (stream->PauseHandler != NULL && stream->OnEnd == NULL) { - // Need to PAUSE, and context switch to Chain Thread, so we can dispatch into JavaScript - sem_wait(&(stream->pipeLock)); - stream->extBuffer_Reserved = streamReserved; - stream->extBuffer_buffer = buffer; - stream->extBuffer_bufferLen = bufferLen; - sem_post(&(stream->pipeLock)); - - if (stream->paused == 0 && stream->PauseHandler != NULL) { stream->paused = 1; stream->PauseHandler(stream, stream->user); } - ILibChain_RunOnMicrostackThread(stream->chain, ILibDuktape_readableStream_WriteData_OnData_ChainThread, stream); + // If we get here, it means we are writing data, but nobody is going to be receiving it... + // So we need to buffer the data, so when we are resumed later, we can retry + needPause = 1; + ILibDuktape_readableStream_WriteData_buffer(stream, streamReserved, buffer, bufferLen); + } + else if (stream->OnEnd != NULL) + { + return 0; } } - else if(stream->PauseHandler != NULL && stream->OnEnd == NULL) + if (needPause) { - // If we get here, it means we are writing data, but nobody is going to be receiving it... - // So we need to buffer the data, so when we are resumed later, we can retry - - ILibDuktape_readableStream_WriteData_buffer(stream, streamReserved, buffer, bufferLen); - } - else if (stream->OnEnd != NULL) - { - return 0; + if (stream->paused == 0 && stream->PauseHandler != NULL) { stream->paused = 1; stream->PauseHandler(stream, stream->user); } } return(stream->paused); } @@ -426,7 +412,24 @@ int ILibDuktape_readableStream_WriteEnd(ILibDuktape_readableStream *stream) { ILibDuktape_readableStream_nextWriteablePipe *next; - if (stream->OnEnd != NULL) + if (stream->noPropagateEnd == 0 && stream->nextWriteable != NULL) + { + next = stream->nextWriteable; + while (next != NULL) + { + duk_push_heapptr(stream->ctx, next->writableStream); // [stream] + duk_get_prop_string(stream->ctx, -1, "end"); // [stream][func] + duk_swap_top(stream->ctx, -2); // [func][this] + if (duk_pcall_method(stream->ctx, 0) != 0) // [retVal] + { + ILibDuktape_Process_UncaughtException(stream->ctx); + } + duk_pop(stream->ctx); // ... + next = next->next; + retVal = 0; + } + } + else if (stream->OnEnd != NULL) { duk_context *x = stream->ctx; duk_push_heapptr(stream->ctx, stream->OnEnd); // [func] @@ -438,29 +441,15 @@ int ILibDuktape_readableStream_WriteEnd(ILibDuktape_readableStream *stream) duk_pop(x); // ... retVal = 0; } - next = stream->nextWriteable; - while (next != NULL) - { - duk_push_heapptr(stream->ctx, next->writableStream); // [stream] - duk_get_prop_string(stream->ctx, -1, "end"); // [stream][func] - duk_swap_top(stream->ctx, -2); // [func][this] - if (duk_pcall_method(stream->ctx, 0) != 0) // [retVal] - { - ILibDuktape_Process_UncaughtException(stream->ctx); - } - duk_pop(stream->ctx); // ... - next = next->next; - retVal = 0; - } - } return retVal; } void ILibDuktape_readableStream_Closed(ILibDuktape_readableStream *stream) { + ILibDuktape_readableStream_WriteEnd(stream); if (stream->OnClose != NULL) { - duk_push_heapptr(stream->ctx, stream->OnEnd); // [func] + duk_push_heapptr(stream->ctx, stream->OnClose); // [func] duk_push_heapptr(stream->ctx, stream->object); // [func][this] if (duk_pcall_method(stream->ctx, 0) != 0) // [retVal] { @@ -479,7 +468,14 @@ duk_ret_t ILibDuktape_readableStream_pause(duk_context *ctx) ptr = (ILibDuktape_readableStream*)Duktape_GetBuffer(ctx, -1, NULL); duk_pop(ctx); // [stream] - if (ptr->PauseHandler != NULL) { ptr->paused = 1; ptr->PauseHandler(ptr, ptr->user); } + if (ptr->PauseHandler != NULL) + { + ptr->paused = 1; ptr->PauseHandler(ptr, ptr->user); + } + else + { + return(ILibDuktape_Error(ctx, "Pause Not Supported")); + } return 1; } @@ -523,151 +519,240 @@ duk_ret_t ILibDuktape_readableStream_resume(duk_context *ctx) duk_get_prop_string(ctx, -1, ILibDuktape_readableStream_RSPTRS); // [stream][ptrs] ptr = (ILibDuktape_readableStream*)Duktape_GetBuffer(ctx, -1, NULL); duk_pop(ctx); // [stream] + if (ptr->ResumeHandler == NULL) { return(ILibDuktape_Error(ctx, "Resume not supported")); } + if (ILibDuktape_readableStream_resume_flush(ptr) == 0 && ptr->ResumeHandler != NULL) { ptr->paused = 0; ptr->ResumeHandler(ptr, ptr->user); } return 1; } -void ILibDuktape_readableStream_pipe_resumeFromTimer(void *obj) + +void ILibDuktape_ReadableStream_pipe_ResumeLater(duk_context *ctx, void **args, int argsLen) { - ILibDuktape_readableStream* ptr = (ILibDuktape_readableStream*)((void**)obj)[0]; - if (ILibDuktape_readableStream_resume_flush(ptr) == 0 && ptr->ResumeHandler != NULL) { ptr->paused = 0; ptr->ResumeHandler(ptr, ptr->user); } - free(obj); + ILibDuktape_readableStream *rs = (ILibDuktape_readableStream*)args[0]; + if (ILibDuktape_readableStream_resume_flush(rs) == 0 && rs->ResumeHandler != NULL) { rs->paused = 0; rs->ResumeHandler(rs, rs->user); } + if (rs->PipeHookHandler != NULL) { rs->PipeHookHandler(rs, args[1], rs->user); } } -void ILibDuktape_readableStream_pipe_resumeFromTimer2(void *obj) +void ILibDuktape_readableStream_pipe_later(duk_context *ctx, void **args, int argsLen) { - free(obj); + duk_push_heapptr(ctx, args[0]); // [readable] + duk_get_prop_string(ctx, -1, "pipe"); // [readable][pipe] + duk_swap_top(ctx, -2); // [pipe][this] + duk_push_heapptr(ctx, args[1]); // [pipe][this][writable] + if (argsLen > 2) { duk_push_heapptr(ctx, args[2]); } // [pipe][this][writable][options] + if (duk_pcall_method(ctx, argsLen - 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "readableStream.pipeLater(): "); } + duk_pop(ctx); // ... } duk_ret_t ILibDuktape_readableStream_pipe(duk_context *ctx) { ILibDuktape_readableStream *rstream; - ILibDuktape_readableStream_nextWriteablePipe *w; + ILibDuktape_readableStream_nextWriteablePipe *w, *tmp; int nargs = duk_get_top(ctx); duk_push_this(ctx); // [readable] duk_get_prop_string(ctx, -1, ILibDuktape_readableStream_RSPTRS); // [readable][ptrs] rstream = (ILibDuktape_readableStream*)Duktape_GetBuffer(ctx, -1, NULL); - duk_pop(ctx); // [readable] + duk_pop_2(ctx); // ... - if (nargs > 1 && duk_is_object(ctx, 1)) + sem_wait(&(rstream->pipeLock)); + if (rstream->pipeInProgress != 0) { - rstream->bypassValue = Duktape_GetIntPropertyValue(ctx, 1, "dataTypeSkip", 0); - } + // We must YIELD and try again later, becuase there is an active dispatch going on + duk_push_this(ctx); + ILibDuktape_Immediate(ctx, (void*[]) { duk_get_heapptr(ctx, -1), duk_get_heapptr(ctx, 0), nargs > 1 ? duk_get_heapptr(ctx, 1) : NULL }, 1 + nargs, ILibDuktape_readableStream_pipe_later); - duk_push_object(ctx); // [readable][nextWriteable] - duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_readableStream_nextWriteablePipe)); // [readable][nextWriteable][ptrBuffer] - w = (ILibDuktape_readableStream_nextWriteablePipe*)Duktape_GetBuffer(ctx, -1, NULL); - memset(w, 0, sizeof(ILibDuktape_readableStream_nextWriteablePipe)); - duk_put_prop_string(ctx, -2, ILibDuktape_readableStream_WritePipes_PTRBUFFER); // [readable][nextWriteable] - - - if (duk_has_prop_string(ctx, -2, ILibDuktape_readableStream_WritePipes)) - { - // There are already associated pipes - duk_get_prop_string(ctx, -2, ILibDuktape_readableStream_WritePipes); // [readable][nextWriteable][prevWriteable] - duk_get_prop_string(ctx, -1, ILibDuktape_readableStream_WritePipes_PTRBUFFER); // [readable][nextWriteable][prevWriteable][ptr] - w->next = (ILibDuktape_readableStream_nextWriteablePipe*)Duktape_GetBuffer(ctx, -1, NULL); - duk_pop(ctx); // [readable][nextWriteable][prevWriteable] - duk_put_prop_string(ctx, -2, ILibDuktape_readableStream_WritePipes); // [readable][nextWriteable] - } - - duk_dup(ctx, 0); // [readable][nextWriteable][stream] - if (duk_has_prop_string(ctx, -1, ILibDuktape_WritableStream_WSPTRS)) - { - // This is one of our writable stream implementation... So we can keep everything native - duk_get_prop_string(ctx, -1, ILibDuktape_WritableStream_WSPTRS); // [readable][nextWriteable][stream][buffer] - w->nativeWritable = Duktape_GetBuffer(ctx, -1, NULL); - duk_pop(ctx); // [readable][nextWriteable][stream] - } - w->writableStream = duk_get_heapptr(ctx, -1); - duk_put_prop_string(ctx, -2, ILibDuktape_readableStream_WritePipes_Stream); // [readable][nextWriteable] - - rstream->nextWriteable = w; - - // Save to the readableStream - duk_put_prop_string(ctx, -2, ILibDuktape_readableStream_WritePipes); // [readable] - - duk_dup(ctx, 0); // [readable][writable] - if (duk_has_prop_string(ctx, -1, "emit")) - { - duk_push_string(ctx, "emit"); // [readable][writable][key] - duk_push_string(ctx, "pipe"); // [readable][writable][key][eventName] - duk_dup(ctx, -4); // [readable][writable][key][eventName][readable] - if (duk_pcall_prop(ctx, -4, 2) != 0) // [readable][writable][retVal/err] - { - ILibDuktape_Process_UncaughtException(ctx); - } - duk_pop_2(ctx); // [readable] + duk_dup(ctx, 0); + sem_post(&(rstream->pipeLock)); + return(1); } else { - duk_pop(ctx); - } - - if (rstream->paused != 0) - { - void *chain = Duktape_GetChain(ctx); - if (chain != NULL) + // No Active Dispatch, so while we hold this lock, we can setup/add the pipe + duk_push_heapptr(ctx, rstream->pipeArray); // [pipeArray] + duk_get_prop_string(ctx, -1, "push"); // [pipeArray][push] + duk_swap_top(ctx, -2); // [push][this] + duk_dup(ctx, 0); // [push][this][dest] + ILibDuktape_Push_ObjectStash(ctx); // [push][this][dest][stash] + duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_readableStream_nextWriteablePipe)); // [push][this][dest][stash][buffer] + w = (ILibDuktape_readableStream_nextWriteablePipe*)Duktape_GetBuffer(ctx, -1, NULL); + duk_put_prop_string(ctx, -2, Duktape_GetStashKey(duk_get_heapptr(ctx, -1))); // [push][this][dest][stash] + duk_pop(ctx); // [push][this][dest] + duk_call_method(ctx, 1); duk_pop(ctx); // ... + memset(w, 0, sizeof(ILibDuktape_readableStream_nextWriteablePipe)); + w->writableStream = duk_get_heapptr(ctx, 0); + if (duk_has_prop_string(ctx, 0, ILibDuktape_WritableStream_WSPTRS)) { - // We are paused, so we should yield and resume... We yield, so in case the user tries to chain multiple pipes, it will chain first - void **tmp = (void**)ILibMemory_Allocate(sizeof(void*), 0, NULL, NULL); - tmp[0] = rstream; - ILibLifeTime_AddEx(ILibGetBaseTimer(chain), tmp, 0, ILibDuktape_readableStream_pipe_resumeFromTimer, ILibDuktape_readableStream_pipe_resumeFromTimer2); + // This is one of our writable stream implementation... So we can keep everything native + duk_get_prop_string(ctx, 0, ILibDuktape_WritableStream_WSPTRS); // [wrsPTR] + w->nativeWritable = Duktape_GetBuffer(ctx, -1, NULL); + duk_pop(ctx); // ... + // If JSCreate is non-zero, it means this is actually a JS Stream, not a native one + if (((int*)w->nativeWritable)[0] != 0) { w->nativeWritable = NULL; } + } + + // Now lets lets add this entry to the end of the list, so it can be dispatched without invoking into JS to access the array + if (rstream->nextWriteable == NULL) + { + rstream->nextWriteable = w; } else { - // Oops - duk_push_string(ctx, "ILibParsers_Duktape *MISSING*"); - duk_throw(ctx); - return(DUK_RET_ERROR); + tmp = rstream->nextWriteable; + while (tmp->next != NULL) { tmp = tmp->next; } + tmp->next = w; + w->previous = tmp; } } + if (nargs > 1 && duk_is_object(ctx, 1)) + { + rstream->bypassValue = Duktape_GetIntPropertyValue(ctx, 1, "dataTypeSkip", 0); + rstream->noPropagateEnd = Duktape_GetBooleanProperty(ctx, 1, "end", 1) == 0 ? 1 : 0; + } + sem_post(&(rstream->pipeLock)); - if (rstream->PipeHookHandler != NULL) { rstream->PipeHookHandler(rstream, rstream->user); } + // Now we need to emit a 'pipe' event on the writable that we just attached + duk_push_heapptr(ctx, w->writableStream); // [dest] + duk_get_prop_string(ctx, -1, "emit"); // [dest][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "pipe"); // [emit][this][pipe] + duk_push_this(ctx); // [emit][this][pipe][readable] + duk_call_method(ctx, 2); duk_pop(ctx); // ... + + + if (rstream->paused != 0) + { + // We are paused, so we should yield and resume... We yield, so in case the user tries to chain multiple pipes, it will chain first + ILibDuktape_Immediate(ctx, (void*[]) { rstream, duk_get_heapptr(ctx, 0) }, 1, ILibDuktape_ReadableStream_pipe_ResumeLater); + } + else + { + if (rstream->PipeHookHandler != NULL) { rstream->PipeHookHandler(rstream, duk_get_heapptr(ctx, 0), rstream->user); } + } + + duk_dup(ctx, 0); return 1; } +void ILibDuktape_readableStream_unpipe_later(duk_context *ctx, void ** args, int argsLen) +{ + ILibDuktape_readableStream *data; + ILibDuktape_readableStream_nextWriteablePipe *w; + int i; + duk_size_t arrayLen; + + duk_push_heapptr(ctx, args[0]); // [readable] + duk_get_prop_string(ctx, -1, ILibDuktape_readableStream_RSPTRS); // [readable][ptrs] + data = (ILibDuktape_readableStream*)Duktape_GetBuffer(ctx, -1, NULL); + duk_pop_2(ctx); // ... + + sem_wait(&(data->pipeLock)); + if (data->pipeInProgress != 0) + { + // We must yield, and try again, because there's an active dispatch going on + ILibDuktape_Immediate(ctx, (void*[]) { args[0], args[1] }, argsLen, ILibDuktape_readableStream_unpipe_later); + sem_post(&(data->pipeLock)); + return; + } + else + { + i = 0; + w = data->nextWriteable; + if (argsLen > 1) + { + // Specific stream was specified in 'unpipe' + while (w != NULL) + { + if (w->writableStream == args[1]) + { + // Emit the 'unpipe' event + duk_push_heapptr(ctx, args[1]); // [ws] + duk_get_prop_string(ctx, -1, "emit"); // [ws][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "unpipe"); // [emit][this][unpipe] + duk_push_heapptr(ctx, args[0]); // [emit][this][unpipe][readable] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "readable.unpipe(): "); } + duk_pop(ctx); // ... + + if (w->previous != NULL) + { + w->previous->next = w->next; + } + else + { + data->nextWriteable = w->next; + } + duk_push_heapptr(ctx, data->pipeArray); // [array] + arrayLen = duk_get_length(ctx, -1); + for (i = 0; i < (int)arrayLen; ++i) + { + duk_get_prop_index(ctx, -1, i); // [array][ws] + ILibDuktape_Push_ObjectStash(ctx); // [array][ws][stash] + if (duk_has_prop_string(ctx, -1, Duktape_GetStashKey(args[1]))) + { + // Removing the entry from the Array + duk_pop_2(ctx); // [array] + duk_get_prop_string(ctx, -1, "splice"); // [array][splice] + duk_swap_top(ctx, -2); // [splice][this] + duk_push_int(ctx, i); // [splice][this][i] + duk_push_int(ctx, 1); // [splice][this][i][1] + duk_call_method(ctx, 2); // [undefined] + duk_pop(ctx); // ... + break; + } + duk_pop_2(ctx); // [array] + } + duk_pop(ctx); // ... + break; + } + w = w->next; + } + } + else + { + // 'unpipe' all pipes + w = data->nextWriteable; + while (w != NULL) + { + duk_push_heapptr(ctx, w->writableStream); // [ws] + duk_get_prop_string(ctx, -1, "emit"); // [ws][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "unpipe"); // [emit][this][unpipe] + duk_push_heapptr(ctx, args[0]); // [emit][this][unpipe][readable] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "readable.unpipe(): "); } + duk_pop(ctx); // ... + w = w->next; + } + data->nextWriteable = NULL; + duk_push_heapptr(ctx, args[0]); // [readable] + duk_del_prop_string(ctx, -1, ILibDuktape_readableStream_PipeArray); + duk_push_array(ctx); // [readable][array] + data->pipeArray = duk_get_heapptr(ctx, -1); + duk_put_prop_string(ctx, -2, ILibDuktape_readableStream_PipeArray); // [readable] + duk_pop(ctx); // ... + } + } + data->unpipeInProgress = 0; + sem_post(&(data->pipeLock)); +} duk_ret_t ILibDuktape_readableStream_unpipe(duk_context *ctx) { int nargs = duk_get_top(ctx); ILibDuktape_readableStream *data; - ILibDuktape_readableStream_nextWriteablePipe *w, *prev; - duk_push_this(ctx); // [stream] - duk_get_prop_string(ctx, -1, ILibDuktape_readableStream_RSPTRS); // [stream][ptrs] + duk_push_this(ctx); // [readable] + duk_get_prop_string(ctx, -1, ILibDuktape_readableStream_RSPTRS); // [readable][ptrs] data = (ILibDuktape_readableStream*)Duktape_GetBuffer(ctx, -1, NULL); + duk_pop(ctx); // [readable] - if (nargs == 0) - { - duk_del_prop_string(ctx, -2, ILibDuktape_readableStream_WritePipes); - data->nextWriteable = NULL; - } - else if (data->nextWriteable != NULL) - { - w = data->nextWriteable; - prev = NULL; - while (w != NULL) - { - if (w->writableStream == duk_get_heapptr(ctx, 0)) - { - memset(w, 0, 2 * sizeof(void*)); - if (data->nextWriteable == w) - { - //printf("Unpiping object: %p\n", (void*)w); - data->nextWriteable = w->next; - break; - } - else - { - prev->next = w->next; - break; - } - } - else - { - prev = w; - w = w->next; - } - } - } - + sem_wait(&(data->pipeLock)); + data->unpipeInProgress = 1; + sem_post(&(data->pipeLock)); + + // We need to pause first + duk_push_this(ctx); // [readable] + duk_get_prop_string(ctx, -1, "pause"); // [readable][pause] + duk_dup(ctx, -2); // [readable][pause][this] + duk_call_method(ctx, 0); duk_pop(ctx); // [readable] + + // We must yield, and do this on the next event loop, because we can't unpipe if we're called from a pipe'ed call + ILibDuktape_Immediate(ctx, (void*[]) { duk_get_heapptr(ctx, -1), nargs == 1 ? duk_get_heapptr(ctx, 0) : NULL }, nargs + 1, ILibDuktape_readableStream_unpipe_later); return 0; } duk_ret_t ILibDuktape_readableStream_isPaused(duk_context *ctx) @@ -710,11 +795,31 @@ void ILibDuktape_ReadableStream_PipeLockFinalizer(duk_context *ctx, void *stream sem_destroy(&(ptrs->pipeLock)); duk_pop_2(ctx); } -ILibDuktape_readableStream* ILibDuktape_InitReadableStream(duk_context *ctx, ILibDuktape_readableStream_PauseResumeHandler OnPause, ILibDuktape_readableStream_PauseResumeHandler OnResume, void *user) +duk_ret_t ILibDuktape_ReadableStream_unshift(duk_context *ctx) +{ + ILibDuktape_readableStream *rs; + duk_push_this(ctx); // [stream] + duk_get_prop_string(ctx, -1, ILibDuktape_readableStream_RSPTRS); // [stream][ptrs] + rs = (ILibDuktape_readableStream*)Duktape_GetBuffer(ctx, -1, NULL); + + if (rs->UnshiftHandler == NULL) + { + return(ILibDuktape_Error(ctx, "readable.unshift(): Not Implemented")); + } + else + { + duk_size_t bufferLen; + Duktape_GetBuffer(ctx, 0, &bufferLen); + duk_push_int(ctx, rs->UnshiftHandler(rs, (int)bufferLen, rs->user)); + return(1); + } +} +ILibDuktape_readableStream* ILibDuktape_ReadableStream_InitEx(duk_context *ctx, ILibDuktape_readableStream_PauseResumeHandler OnPause, ILibDuktape_readableStream_PauseResumeHandler OnResume, ILibDuktape_readableStream_UnShiftHandler OnUnshift, void *user) { ILibDuktape_readableStream *retVal; ILibDuktape_EventEmitter *emitter; + ILibDuktape_PointerValidation_Init(ctx); duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_readableStream)); // [obj][buffer] duk_dup(ctx, -1); // [obj][buffer][buffer] duk_put_prop_string(ctx, -3, ILibDuktape_readableStream_RSPTRS); // [obj][buffer] @@ -722,9 +827,10 @@ ILibDuktape_readableStream* ILibDuktape_InitReadableStream(duk_context *ctx, ILi memset(retVal, 0, sizeof(ILibDuktape_readableStream)); duk_pop(ctx); // [obj] - duk_push_external_buffer(ctx); // [obj][extBuffer] - retVal->extBuffer = duk_get_heapptr(ctx, -1); - duk_put_prop_string(ctx, -2, "_extBuffer"); // [obj] + duk_push_array(ctx); // [obj][array] + retVal->pipeArray = duk_get_heapptr(ctx, -1); + duk_put_prop_string(ctx, -2, ILibDuktape_readableStream_PipeArray); // [obj] + retVal->ctx = ctx; retVal->chain = Duktape_GetChain(ctx); @@ -732,6 +838,7 @@ ILibDuktape_readableStream* ILibDuktape_InitReadableStream(duk_context *ctx, ILi retVal->user = user; retVal->PauseHandler = OnPause; retVal->ResumeHandler = OnResume; + retVal->UnshiftHandler = OnUnshift; sem_init(&(retVal->pipeLock), 0, 1); ILibDuktape_CreateIndependentFinalizer(ctx, ILibDuktape_ReadableStream_PipeLockFinalizer); @@ -743,7 +850,8 @@ ILibDuktape_readableStream* ILibDuktape_InitReadableStream(duk_context *ctx, ILi ILibDuktape_CreateInstanceMethod(ctx, "pause", ILibDuktape_readableStream_pause, 0); ILibDuktape_CreateInstanceMethod(ctx, "resume", ILibDuktape_readableStream_resume, 0); ILibDuktape_CreateEventWithGetter(ctx, "pipe", ILibDuktape_readableStream_pipe_getter); - ILibDuktape_CreateInstanceMethod(ctx, "unpipe", ILibDuktape_readableStream_unpipe, DUK_VARARGS); + ILibDuktape_CreateProperty_InstanceMethod(ctx, "unpipe", ILibDuktape_readableStream_unpipe, DUK_VARARGS); ILibDuktape_CreateInstanceMethod(ctx, "isPaused", ILibDuktape_readableStream_isPaused, 0); + ILibDuktape_CreateInstanceMethod(ctx, "unshift", ILibDuktape_ReadableStream_unshift, 1); return retVal; } diff --git a/microscript/ILibDuktape_ReadableStream.h b/microscript/ILibDuktape_ReadableStream.h index 56bab12..d515f8d 100644 --- a/microscript/ILibDuktape_ReadableStream.h +++ b/microscript/ILibDuktape_ReadableStream.h @@ -24,12 +24,14 @@ limitations under the License. struct ILibDuktape_readableStream; typedef void(*ILibDuktape_readableStream_PauseResumeHandler)(struct ILibDuktape_readableStream *sender, void *user); -typedef void(*ILibDuktape_readableStream_MethodHookHandler)(struct ILibDuktape_readableStream *sender, void *user); +typedef void(*ILibDuktape_readableStream_MethodHookHandler)(struct ILibDuktape_readableStream *sender, void *writableStream, void *user); +typedef int(*ILibDuktape_readableStream_UnShiftHandler)(struct ILibDuktape_readableStream *sender, int unshiftBytes, void *user); + typedef struct ILibDuktape_readableStream_nextWriteablePipe { void *writableStream; void *nativeWritable; - struct ILibDuktape_readableStream_nextWriteablePipe *next; + struct ILibDuktape_readableStream_nextWriteablePipe *previous, *next; }ILibDuktape_readableStream_nextWriteablePipe; typedef struct ILibDuktape_readableStream @@ -40,11 +42,9 @@ typedef struct ILibDuktape_readableStream void *OnClose; void *OnData; void *OnEnd; - void *extBuffer; - char *extBuffer_buffer; - int extBuffer_bufferLen, extBuffer_Reserved; void *user; + void *pipeArray; ILibDuktape_readableStream_nextWriteablePipe *nextWriteable; sem_t pipeLock; #if defined(WIN32) @@ -54,16 +54,21 @@ typedef struct ILibDuktape_readableStream #else int pipe_pendingCount; // No Atomic Built-ins... Use a Mutex #endif + int pipeInProgress; + int unpipeInProgress; int bypassValue; + int noPropagateEnd; int paused; void *paused_data; ILibDuktape_readableStream_PauseResumeHandler PauseHandler; ILibDuktape_readableStream_PauseResumeHandler ResumeHandler; ILibDuktape_readableStream_MethodHookHandler PipeHookHandler; + ILibDuktape_readableStream_UnShiftHandler UnshiftHandler; }ILibDuktape_readableStream; -ILibDuktape_readableStream* ILibDuktape_InitReadableStream(duk_context *ctx, ILibDuktape_readableStream_PauseResumeHandler OnPause, ILibDuktape_readableStream_PauseResumeHandler OnResume, void *user); -#define ILibDuktape_ReadableStream_Init(ctx, OnPause, OnResume, user) ILibDuktape_InitReadableStream(ctx, OnPause, OnResume, user) +ILibDuktape_readableStream* ILibDuktape_ReadableStream_InitEx(duk_context *ctx, ILibDuktape_readableStream_PauseResumeHandler OnPause, ILibDuktape_readableStream_PauseResumeHandler OnResume, ILibDuktape_readableStream_UnShiftHandler OnUnshift, void *user); +#define ILibDuktape_InitReadableStream(ctx, OnPause, OnResume, user) ILibDuktape_ReadableStream_InitEx(ctx, OnPause, OnResume, NULL, user) +#define ILibDuktape_ReadableStream_Init(ctx, OnPause, OnResume, user) ILibDuktape_ReadableStream_InitEx(ctx, OnPause, OnResume, NULL, user) #define ILibDuktape_readableStream_SetPauseResumeHandlers(stream, PauseFunc, ResumeFunc, userObj) ((ILibDuktape_readableStream*)stream)->PauseHandler = PauseFunc; ((ILibDuktape_readableStream*)stream)->ResumeHandler = ResumeFunc; ((ILibDuktape_readableStream*)stream)->user = userObj; int ILibDuktape_readableStream_WriteDataEx(ILibDuktape_readableStream *stream, int streamReserved, char* buffer, int bufferLen); diff --git a/microscript/ILibDuktape_ScriptContainer.c b/microscript/ILibDuktape_ScriptContainer.c index 4befe29..d1ed4b2 100644 --- a/microscript/ILibDuktape_ScriptContainer.c +++ b/microscript/ILibDuktape_ScriptContainer.c @@ -40,7 +40,6 @@ limitations under the License. #include "ILibDuktape_EventEmitter.h" #include "ILibDuktape_Helpers.h" #include "../microstack/ILibParsers.h" -#include "../microstack/ILibProcessPipe.h" #include "../microstack/ILibRemoteLogging.h" #include "../microstack/ILibCrypto.h" @@ -49,7 +48,6 @@ limitations under the License. #include "ILibDuktape_WebRTC.h" #include "ILibDuktape_Dgram.h" #include "ILibDuktape_GenericMarshal.h" -#include "ILibDuktape_ProcessPipe.h" #include "ILibDuktape_fs.h" #include "ILibDuktape_Polyfills.h" #include "ILibDuktape_SimpleDataStore.h" @@ -58,6 +56,7 @@ limitations under the License. #include "ILibDuktape_SHA256.h" #include "ILibDuktape_EncryptionStream.h" #include "ILibDuktape_ChildProcess.h" +#include "ILibDuktape_HECI.h" #ifdef _POSIX extern char **environ; @@ -198,11 +197,11 @@ void ILibDuktape_ScriptContainer_CheckEmbedded(char **argv, char **script, int * if (ILibString_EndsWith(argv[0], -1, ".exe", 4) == 0) { sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "%s.exe", argv[0]); - tmpFile = fopen(ILibScratchPad, "rb"); + fopen_s(&tmpFile, ILibScratchPad, "rb"); } else { - tmpFile = fopen(argv[0], "rb"); + fopen_s(&tmpFile, argv[0], "rb"); } #else tmpFile = fopen(argv[0], "rb"); @@ -290,15 +289,23 @@ duk_ret_t ILibDuktape_ScriptContainer_Process_Argv0(duk_context *ctx) // Polyfill process object: duk_ret_t ILibDuktape_ScriptContainer_Process_Argv(duk_context *ctx) { + duk_push_current_function(ctx); + int readOnly = Duktape_GetIntPropertyValue(ctx, -1, "readOnly", 0); + duk_push_this(ctx); // [process] if (duk_has_prop_string(ctx, -1, ILibDuktape_ScriptContainer_Process_ArgArray)) { duk_get_prop_string(ctx, -1, ILibDuktape_ScriptContainer_Process_ArgArray); // [process][array] - duk_dup(ctx, -1); // [process][array][array] + if (readOnly != 0) { duk_dup(ctx, -1); } // [process][array][array] } else { duk_push_array(ctx); // [process][array] + if (readOnly == 0) + { + duk_dup(ctx, -1); // [process][array][array] + duk_put_prop_string(ctx, -3, ILibDuktape_ScriptContainer_Process_ArgArray); // [process][array] + } } return 1; } @@ -404,7 +411,10 @@ void ILibDuktape_ScriptContainer_Process_Init(duk_context *ctx, char **argList) ILibDuktape_EventEmitter_CreateEventEx(emitter, "uncaughtException"); ILibDuktape_CreateEventWithGetter(ctx, "argv0", ILibDuktape_ScriptContainer_Process_Argv0); - ILibDuktape_CreateEventWithGetter(ctx, "argv", ILibDuktape_ScriptContainer_Process_Argv); + duk_push_int(ctx, 1); + ILibDuktape_CreateEventWithGetterAndCustomProperty(ctx, "readOnly", "argv", ILibDuktape_ScriptContainer_Process_Argv); + duk_push_int(ctx, 0); + ILibDuktape_CreateEventWithGetterAndCustomProperty(ctx, "readOnly", "_argv", ILibDuktape_ScriptContainer_Process_Argv); duk_put_prop_string(ctx, -2, "process"); // [g] duk_pop(ctx); // ... @@ -762,7 +772,7 @@ void ILibDuktape_ScriptContainer_OS_Init(duk_context *ctx) { ILibDuktape_ModSearch_AddHandler(ctx, "os", ILibDuktape_ScriptContainer_OS_Push); } - +extern void ILibDuktape_HttpStream_Init(duk_context *ctx); duk_context *ILibDuktape_ScriptContainer_InitializeJavaScriptEngineEx(SCRIPT_ENGINE_SECURITY_FLAGS securityFlags, unsigned int executionTimeout, void *chain, char **argList, ILibSimpleDataStore *db, char *exePath, ILibProcessPipe_Manager pipeManager, ILibDuktape_HelperEvent exitHandler, void *exitUser) { duk_context *ctx = duk_create_heap(ILibDuktape_ScriptContainer_Engine_malloc, ILibDuktape_ScriptContainer_Engine_realloc, ILibDuktape_ScriptContainer_Engine_free, NULL, ILibDuktape_ScriptContainer_Engine_fatal); @@ -800,12 +810,19 @@ duk_context *ILibDuktape_ScriptContainer_InitializeJavaScriptEngineEx(SCRIPT_ENG if ((securityFlags & SCRIPT_ENGINE_NO_NETWORK_ACCESS) == 0) { ILibDuktape_WebRTC_Init(ctx); // WebRTC library (browser api) - ILibDuktape_http_init(ctx, chain); // HTTP library (node api) + ILibDuktape_http_init(ctx, chain); // HTTP-Digest library (node api) ILibDuktape_net_init(ctx, chain); // Network library (node api) ILibDuktape_DGram_Init(ctx); // Datagram Sockets + ILibDuktape_HttpStream_Init(ctx); // HTTP Library (node api) } if ((securityFlags & SCRIPT_ENGINE_NO_GENERIC_MARSHAL_ACCESS) == 0) { ILibDuktape_GenericMarshal_init(ctx); } - if ((securityFlags & SCRIPT_ENGINE_NO_PROCESS_SPAWNING) == 0) { ILibDuktape_ProcessPipe_Init(ctx, chain); ILibDuktape_ChildProcess_Init(ctx); } + if ((securityFlags & SCRIPT_ENGINE_NO_PROCESS_SPAWNING) == 0) + { + ILibDuktape_ChildProcess_Init(ctx); +#ifndef _NOHECI + ILibDuktape_HECI_Init(ctx); +#endif + } if ((securityFlags & SCRIPT_ENGINE_NO_FILE_SYSTEM_ACCESS) == 0) { ILibDuktape_fs_init(ctx); } diff --git a/microscript/ILibDuktape_TlsStream.c b/microscript/ILibDuktape_TlsStream.c new file mode 100644 index 0000000..e1dd424 --- /dev/null +++ b/microscript/ILibDuktape_TlsStream.c @@ -0,0 +1,561 @@ +#include "duktape.h" +#include "ILibDuktape_Helpers.h" +#include "ILibDuktapeModSearch.h" +#include "ILibDuktape_DuplexStream.h" +#include "ILibDuktape_EventEmitter.h" + +#ifndef MICROSTACK_NOTLS +#include +#include +#endif + +#include "../microstack/ILibCrypto.h" + +#define ILibDuktape_TlsStream_Ptr "\xFF_ILibDuktape_TLSSTREAM_PTR" +#define ILibDuktape_TlsStream2Cert "\xFF_TLS_CERT" + +#define MEMORYCHUNKSIZE 4096 +int ILibDuktape_TlsStream_ctx2stream = -1; + +typedef struct ILibDuktape_TlsStream_Data +{ + duk_context *ctx; + void *tlsObject; + void *chain; + int isClient; + ILibDuktape_EventEmitter *emitter; + char *decryptedBuffer; + int decryptedBuffer_mallocSize; + int decryptedBuffer_beginPointer; + int decryptedBuffer_endPointer; + int decryptedBuffer_unshiftedBytes; + int decryptedBuffer_maxSize; + + int rejectUnauthorized; + void *OnVerify; + void *OnServerSNI; + + int ProcessEncryptedBuffer_Active; + + SSL* ssl; + SSL_CTX *ssl_ctx; + BIO *readBio, *writeBio; + BUF_MEM *readBioBuffer, *writeBioBuffer; + int TLSHandshakeCompleted; + ILibDuktape_DuplexStream *clear; + ILibDuktape_DuplexStream *encrypted; + int encrypted_unshiftBytes; +}ILibDuktape_TlsStream_Data; + + +void ILibDuktape_TlsStream_X509_PUSH(duk_context *ctx, X509* cert) +{ + char hash[UTIL_SHA384_HASHSIZE]; + char fingerprint[150]; + + util_keyhash2(cert, hash); + util_tohex2(hash, UTIL_SHA384_HASHSIZE, fingerprint); + + duk_push_object(ctx); // [cert] + duk_push_string(ctx, fingerprint); // [cert][fingerprint] + duk_put_prop_string(ctx, -2, "fingerprint"); // [cert] +} +ILibTransport_DoneState ILibDuktape_TlsStream_ProcessEncryptedBuffer(ILibDuktape_TlsStream_Data *tlsdata) +{ + int j, first = 1; + + if (tlsdata->ProcessEncryptedBuffer_Active == 0) + { + tlsdata->ProcessEncryptedBuffer_Active = 1; + tlsdata->encrypted_unshiftBytes = 0; + BIO_clear_retry_flags(tlsdata->writeBio); + + while ((j = (int)tlsdata->writeBioBuffer->length) > 0 && tlsdata->encrypted->readableStream->paused == 0) + { + if (first == 1) + { + first = 0; + } + else + { + tlsdata->encrypted_unshiftBytes = 0; + BIO_clear_retry_flags(tlsdata->writeBio); + } + + do + { + ILibDuktape_DuplexStream_WriteData(tlsdata->encrypted, tlsdata->writeBioBuffer->data, (int)tlsdata->writeBioBuffer->length); + } while (tlsdata->encrypted_unshiftBytes > 0); + if (tlsdata->encrypted_unshiftBytes == 0) + { + tlsdata->writeBioBuffer->data += (int)tlsdata->writeBioBuffer->length; + tlsdata->writeBioBuffer->length = 0; + } + } + tlsdata->ProcessEncryptedBuffer_Active = 0; + return(tlsdata->writeBioBuffer->length > 0 ? ILibTransport_DoneState_INCOMPLETE : ILibTransport_DoneState_COMPLETE); + } + else + { + return(ILibTransport_DoneState_ERROR); + } +} +ILibTransport_DoneState ILibDuktape_TlsStream_Clear_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) +{ + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)user; + + SSL_write(data->ssl, buffer, bufferLen); + return(ILibDuktape_TlsStream_ProcessEncryptedBuffer(data)); +} +void ILibDuktape_TlsStream_Clear_EndSink(ILibDuktape_DuplexStream *stream, void *user) +{ + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)user; + if (ILibIsRunningOnChainThread(data->chain) != 0 && data->encrypted->writableStream->pipedReadable != NULL) + { + duk_push_heapptr(data->ctx, data->encrypted->writableStream->pipedReadable); // [stream] + duk_get_prop_string(data->ctx, -1, "end"); // [stream][end] + duk_swap_top(data->ctx, -2); // [end][this] + if (duk_pcall_method(data->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(data->ctx, "TlsStream.unencrypted.end(): Error dispatching 'end' upstream "); } + duk_pop(data->ctx); // ... + } +} +void ILibDuktape_TlsStream_Clear_PauseSink_Chain(void *chain, void *user) +{ + if (chain != NULL && !ILibDuktape_IsPointerValid(chain, user)) { return; } + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)user; + + duk_push_heapptr(data->ctx, data->encrypted->writableStream->pipedReadable); // [readable] + duk_get_prop_string(data->ctx, -1, "pause"); // [readable][pause] + duk_swap_top(data->ctx, -2); // [pause][this] + if (duk_pcall_method(data->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(data->ctx, "TlsStream.Decrypted.pause(): Error pausing upstream object "); }; + duk_pop(data->ctx); // ... +} +void ILibDuktape_TlsStream_Clear_PauseSink(ILibDuktape_DuplexStream *sender, void *user) +{ + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)user; + if (data->encrypted->writableStream->pipedReadable_native != NULL && data->encrypted->writableStream->pipedReadable_native->PauseHandler != NULL) + { + data->encrypted->writableStream->pipedReadable_native->paused = 1; + data->encrypted->writableStream->pipedReadable_native->PauseHandler(data->encrypted->writableStream->pipedReadable_native, data->encrypted->writableStream->pipedReadable_native->user); + } + else if (data->encrypted->writableStream->pipedReadable != NULL) + { + if (ILibIsRunningOnChainThread(data->chain) != 0) + { + ILibDuktape_TlsStream_Clear_PauseSink_Chain(NULL, data); + } + else + { + // We're on the wrong thread to resume the upstream object + ILibChain_RunOnMicrostackThreadEx(data->chain, ILibDuktape_TlsStream_Clear_PauseSink_Chain, data); + } + } +} +void ILibDuktape_TlsStream_Clear_ResumeSink_Chain(void *chain, void *user) +{ + if (chain != NULL && !ILibDuktape_IsPointerValid(chain, user)) { return; } + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)user; + + duk_push_heapptr(data->ctx, data->encrypted->writableStream->pipedReadable); // [readable] + duk_get_prop_string(data->ctx, -1, "resume"); // [readable][resume] + duk_swap_top(data->ctx, -2); // [resume][this] + if (duk_pcall_method(data->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(data->ctx, "TlsStream.Decrypted.resume(): Error resuming upstream object "); }; + duk_pop(data->ctx); // ... +} +void ILibDuktape_TlsStream_Clear_ResumeSink(ILibDuktape_DuplexStream *sender, void *user) +{ + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)user; + if (data->encrypted->writableStream->pipedReadable_native != NULL && data->encrypted->writableStream->pipedReadable_native->ResumeHandler != NULL) + { + data->encrypted->writableStream->pipedReadable_native->paused = 0; + data->encrypted->writableStream->pipedReadable_native->ResumeHandler(data->encrypted->writableStream->pipedReadable_native, data->encrypted->writableStream->pipedReadable_native->user); + } + else if (data->encrypted->writableStream->pipedReadable != NULL) + { + if (ILibIsRunningOnChainThread(data->chain) != 0) + { + ILibDuktape_TlsStream_Clear_ResumeSink_Chain(NULL, data); + } + else + { + // We're on the wrong thread to resume the upstream object + ILibChain_RunOnMicrostackThreadEx(data->chain, ILibDuktape_TlsStream_Clear_ResumeSink_Chain, data); + } + } +} +int ILibDuktape_TlsStream_Clear_UnshiftSink(ILibDuktape_DuplexStream *sender, int unshiftBytes, void *user) +{ + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)user; + + if (unshiftBytes == data->decryptedBuffer_endPointer - data->decryptedBuffer_beginPointer) + { + data->decryptedBuffer_unshiftedBytes = -1; + } + else + { + data->decryptedBuffer_beginPointer = data->decryptedBuffer_endPointer - unshiftBytes; + data->decryptedBuffer_unshiftedBytes = unshiftBytes; + } + return(unshiftBytes); +} + +ILibTransport_DoneState ILibDuktape_TlsStream_Encrypted_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) +{ + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)user; + int j; + + //int len = BIO_write(data->writeBio, buffer, bufferLen); + BIO_write(SSL_get_rbio(data->ssl), buffer, bufferLen); + int sslerror; + + if (data->TLSHandshakeCompleted == 0) + { + switch ((sslerror = SSL_do_handshake(data->ssl))) + { + case 0: + // Handshake Failed! + while ((sslerror = ERR_get_error()) != 0) + { + ERR_error_string_n(sslerror, ILibScratchPad, sizeof(ILibScratchPad)); + } + // TODO: We should probably do something + break; + case 1: + data->TLSHandshakeCompleted = 1; + if (ILibIsRunningOnChainThread(data->chain) != 0) + { + // We're on the Duktape Thread + duk_push_heapptr(data->ctx, data->tlsObject); // [TLS] + duk_get_prop_string(data->ctx, -1, "emit"); // [TLS][emit] + duk_swap_top(data->ctx, -2); // [emit][this] + duk_push_string(data->ctx, "connect"); // [emit][this][connect] + if (duk_pcall_method(data->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(data->ctx, "TlsStream.connect(): "); } + duk_pop(data->ctx); // ... + } + else + { + // Need to context switch to the Duktape Thread + } + ILibDuktape_TlsStream_ProcessEncryptedBuffer(data); + break; + default: + // SSL_WANT_READ most likely + sslerror = SSL_get_error(data->ssl, sslerror); + ILibDuktape_TlsStream_ProcessEncryptedBuffer(data); + break; + } + return(ILibTransport_DoneState_COMPLETE); + } + else + { + while ((j = SSL_read(data->ssl, data->decryptedBuffer + data->decryptedBuffer_endPointer, data->decryptedBuffer_mallocSize - data->decryptedBuffer_endPointer)) > 0) + { + // We got new TLS Data + if (j > 0) + { + data->decryptedBuffer_endPointer += j; + if (data->decryptedBuffer_mallocSize - data->decryptedBuffer_endPointer == 0) + { + data->decryptedBuffer_mallocSize = (data->decryptedBuffer_mallocSize + MEMORYCHUNKSIZE < data->decryptedBuffer_maxSize) ? (data->decryptedBuffer_mallocSize + MEMORYCHUNKSIZE) : (data->decryptedBuffer_maxSize == 0 ? (data->decryptedBuffer_mallocSize + MEMORYCHUNKSIZE) : data->decryptedBuffer_maxSize); + if((data->decryptedBuffer = (char*)realloc(data->decryptedBuffer, data->decryptedBuffer_mallocSize)) == NULL) ILIBCRITICALEXIT(254); + } + } + } + if (j < 0) + { + sslerror = SSL_get_error(data->ssl, j); + if (data->writeBioBuffer->length > 0) + { + ILibDuktape_TlsStream_ProcessEncryptedBuffer(data); + } + } + } + + // + // Event data up the stack, to process any data that is available + // + do + { + data->decryptedBuffer_unshiftedBytes = 0; + ILibDuktape_DuplexStream_WriteData(data->clear, data->decryptedBuffer + data->decryptedBuffer_beginPointer, data->decryptedBuffer_endPointer - data->decryptedBuffer_beginPointer); + } while (data->decryptedBuffer_unshiftedBytes > 0); + if (data->decryptedBuffer_unshiftedBytes == 0) { data->decryptedBuffer_beginPointer = data->decryptedBuffer_endPointer = 0; } + + + // + // Check to see if we need to move any data, to maximize buffer space + // + if (data->decryptedBuffer_beginPointer != 0) + { + // + // We can save some cycles by moving the data back to the top + // of the buffer, instead of just allocating more memory. + // + char *temp = data->decryptedBuffer + data->decryptedBuffer_beginPointer; + + memmove_s(data->decryptedBuffer, data->decryptedBuffer_mallocSize, temp, data->decryptedBuffer_endPointer - data->decryptedBuffer_beginPointer); + data->decryptedBuffer_endPointer -= data->decryptedBuffer_beginPointer; + data->decryptedBuffer_beginPointer = 0; + } + + // + // Check to see if we should grow the buffer + // + if (data->decryptedBuffer_mallocSize - data->decryptedBuffer_endPointer < 1024 && (data->decryptedBuffer_maxSize == 0 || data->decryptedBuffer_mallocSize < data->decryptedBuffer_maxSize)) + { + data->decryptedBuffer_mallocSize = (data->decryptedBuffer_mallocSize + MEMORYCHUNKSIZE < data->decryptedBuffer_maxSize) ? (data->decryptedBuffer_mallocSize + MEMORYCHUNKSIZE) : (data->decryptedBuffer_maxSize == 0 ? (data->decryptedBuffer_mallocSize + MEMORYCHUNKSIZE) : data->decryptedBuffer_maxSize); + if ((data->decryptedBuffer = (char*)realloc(data->decryptedBuffer, data->decryptedBuffer_mallocSize)) == NULL) ILIBCRITICALEXIT(254); + } + + return(data->clear->readableStream->paused == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE); +} +void ILibDuktape_TlsStream_Encrypted_EndSink(ILibDuktape_DuplexStream *stream, void *user) +{ +} +void ILibDuktape_TlsStream_Encrypted_PauseSink(ILibDuktape_DuplexStream *sender, void *user) +{ + // Don't need to do anythign, becuase the 'ILibDuktape_TlsStream_ProcessEncryptedBuffer' will exit the processing loop when paused +} +void ILibDuktape_TlsStream_Encrypted_ResumeSink(ILibDuktape_DuplexStream *sender, void *user) +{ + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)user; + ILibDuktape_TlsStream_ProcessEncryptedBuffer(data); +} +int ILibDuktape_TlsStream_Encrypted_UnshiftSink(ILibDuktape_DuplexStream *sender, int unshiftBytes, void *user) +{ + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)user; + + if (unshiftBytes == data->writeBioBuffer->length) + { + data->encrypted_unshiftBytes = -1; + } + else + { + data->writeBioBuffer->data += ((int)data->writeBioBuffer->length - unshiftBytes); + data->writeBioBuffer->length = unshiftBytes; + } + return(unshiftBytes); +} +int ILibDuktape_TlsStream_verify(int preverify_ok, X509_STORE_CTX *ctx) +{ + STACK_OF(X509) *certChain = X509_STORE_CTX_get_chain(ctx); + SSL *ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)SSL_get_ex_data(ssl, ILibDuktape_TlsStream_ctx2stream); + + int i; + int retVal = 0; + + if (data->rejectUnauthorized != 0) { return(preverify_ok); } + if (data->OnVerify == NULL) { return 1; } + + duk_push_heapptr(data->ctx, data->OnVerify); // [func] + duk_push_heapptr(data->ctx, data->emitter->object); // [func][this] + duk_push_array(data->ctx); // [func][this][certs] + for (i = 0; i < sk_X509_num(certChain); ++i) + { + ILibDuktape_TlsStream_X509_PUSH(data->ctx, sk_X509_value(certChain, i)); // [func][this][certs][cert] + duk_put_prop_index(data->ctx, -2, i); // [func][this][certs] + } + retVal = duk_pcall_method(data->ctx, 1) == 0 ? 1 : 0; // [undefined] + duk_pop(data->ctx); // ... + return retVal; +} +duk_ret_t ILibDuktape_TlsStream_Finalizer(duk_context *ctx) +{ + void *chain = Duktape_GetChain(ctx); + duk_get_prop_string(ctx, 0, ILibDuktape_TlsStream_Ptr); + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)Duktape_GetBuffer(ctx, -1, NULL); + struct util_cert *cert = NULL; + + if (data->ssl != NULL) { SSL_free(data->ssl); } + if (data->ssl_ctx != NULL) { SSL_CTX_free(data->ssl_ctx); } + if (data->decryptedBuffer != NULL) { free(data->decryptedBuffer); } + + if (duk_has_prop_string(ctx, 0, ILibDuktape_TlsStream2Cert)) + { + duk_get_prop_string(ctx, 0, ILibDuktape_TlsStream2Cert); + cert = (struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL); + util_freecert(cert); + } + ILibDuktape_InValidatePointer(chain, data); + return(0); +} +duk_ret_t ILibDuktape_TlsStream_serverSNI_cb(duk_context *ctx) +{ + duk_push_current_function(ctx); + duk_get_prop_string(ctx, -1, "\xFF_ptr"); + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)duk_get_pointer(ctx, -1); + + if (duk_has_prop_string(ctx, 1, "pfx") && duk_has_prop_string(ctx, 1, "passphrase")) + { + char *passphrase = Duktape_GetStringPropertyValue(ctx, 1, "passphrase", ""); + char *pfx; + duk_size_t pfxLen; + duk_get_prop_string(ctx, 1, "pfx"); + pfx = (char*)Duktape_GetBuffer(ctx, -1, &pfxLen); + duk_push_heapptr(ctx, data->tlsObject); // [TLS] + duk_push_fixed_buffer(ctx, sizeof(struct util_cert)); // [TLS][cert] + struct util_cert *cert = (struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL); + + if (util_from_p12(pfx, (int)pfxLen, passphrase, cert) == 0) { return(ILibDuktape_Error(ctx, "Error Reading Certificate")); } + SSL_CTX *newCTX = SSL_CTX_new(SSLv23_server_method()); + SSL_CTX_set_options(newCTX, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); + SSL_CTX_set_verify(newCTX, SSL_VERIFY_CLIENT_ONCE, ILibDuktape_TlsStream_verify); /* Ask for authentication */ + SSL_CTX_use_certificate(newCTX, cert->x509); + SSL_CTX_use_PrivateKey(newCTX, cert->pkey); + + SSL_set_SSL_CTX(data->ssl, newCTX); + + duk_get_prop_string(ctx, -2, ILibDuktape_TlsStream2Cert); // [TLS][cert][oldCert] + util_freecert((struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL)); + duk_pop(ctx); // [TLS][cert] + duk_put_prop_string(ctx, -2, ILibDuktape_TlsStream2Cert); // [TLS] + SSL_CTX_free(data->ssl_ctx); + data->ssl_ctx = newCTX; + } + + return(0); +} +static int ILibDuktape_TlsStream_serverSNI_callback(SSL *s, int *ad, void *arg) +{ + const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); + ILibDuktape_TlsStream_Data *data = (ILibDuktape_TlsStream_Data*)SSL_get_ex_data(s, ILibDuktape_TlsStream_ctx2stream); + int retVal = SSL_TLSEXT_ERR_OK; + + duk_push_heapptr(data->ctx, data->OnServerSNI); // [func] + duk_push_heapptr(data->ctx, data->tlsObject); // [func][this] + duk_push_string(data->ctx, servername); // [func][this][servername] + duk_push_c_function(data->ctx, ILibDuktape_TlsStream_serverSNI_cb, 2); // [func][this][servername][cb] + duk_push_pointer(data->ctx, data); // [func][this][servername][cb][ptr] + duk_put_prop_string(data->ctx, -2, "\xFF_ptr"); // [func][this][servername][cb] + if (duk_pcall_method(data->ctx, 2) != 0) + { + ILibDuktape_Process_UncaughtExceptionEx(data->ctx, "TlsStream.serverSNI(): Error dispatching callback "); + retVal = SSL_TLSEXT_ERR_NOACK; + } + duk_pop(data->ctx); // ... + + return(retVal); +} +duk_ret_t ILibDuktape_TlsStream_create(duk_context *ctx) +{ + int nargs = duk_get_top(ctx); + char *sniHost = NULL; + void *chain = Duktape_GetChain(ctx); + int status, requestCert; + duk_push_current_function(ctx); + int isClient = Duktape_GetIntPropertyValue(ctx, -1, "isClient", 0); + ILibDuktape_TlsStream_Data *data; + duk_push_object(ctx); // [TLS] + ILibDuktape_CreateFinalizer(ctx, ILibDuktape_TlsStream_Finalizer); + duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_TlsStream_Data)); // [TLS][struct] + data = (ILibDuktape_TlsStream_Data*)Duktape_GetBuffer(ctx, -1, NULL); + duk_put_prop_string(ctx, -2, ILibDuktape_TlsStream_Ptr); // [TLS] + memset(data, 0, sizeof(ILibDuktape_TlsStream_Data)); + ILibDuktape_ValidatePointer(chain, data); + data->emitter = ILibDuktape_EventEmitter_Create(ctx); + ILibDuktape_EventEmitter_CreateEventEx(data->emitter, "connect"); + data->tlsObject = duk_get_heapptr(ctx, -1); + data->decryptedBuffer_mallocSize = MEMORYCHUNKSIZE; + data->decryptedBuffer = (char*)ILibMemory_Allocate(data->decryptedBuffer_mallocSize, 0, NULL, NULL); + + data->rejectUnauthorized = Duktape_GetIntPropertyValue(ctx, 0, "rejectUnauthorized", 1); + requestCert = Duktape_GetIntPropertyValue(ctx, 0, "requestCert", 0); + data->OnVerify = Duktape_GetHeapptrProperty(ctx, 0, isClient != 0 ? "checkServerIdentity" : "checkClientIdentity"); + + duk_push_object(ctx); // [TLS][clear] + ILibDuktape_WriteID(ctx, "TlsStream.decrypted"); + data->clear = ILibDuktape_DuplexStream_InitEx(ctx, ILibDuktape_TlsStream_Clear_WriteSink, ILibDuktape_TlsStream_Clear_EndSink, + ILibDuktape_TlsStream_Clear_PauseSink, ILibDuktape_TlsStream_Clear_ResumeSink, ILibDuktape_TlsStream_Clear_UnshiftSink, data); + ILibDuktape_CreateReadonlyProperty(ctx, "clear"); // [TLS] + + duk_push_object(ctx); // [TLS][encrypted] + ILibDuktape_WriteID(ctx, "TlsStream.encrypted"); + data->encrypted = ILibDuktape_DuplexStream_InitEx(ctx, ILibDuktape_TlsStream_Encrypted_WriteSink, ILibDuktape_TlsStream_Encrypted_EndSink, + ILibDuktape_TlsStream_Encrypted_PauseSink, ILibDuktape_TlsStream_Encrypted_ResumeSink, ILibDuktape_TlsStream_Encrypted_UnshiftSink, data); + ILibDuktape_CreateReadonlyProperty(ctx, "encrypted"); // [TLS] + + + data->ctx = ctx; + data->chain = Duktape_GetChain(ctx); + data->ssl_ctx = isClient != 0 ? SSL_CTX_new(SSLv23_client_method()) : SSL_CTX_new(SSLv23_server_method()); + SSL_CTX_set_options(data->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); + if (isClient != 0 || requestCert != 0) { SSL_CTX_set_verify(data->ssl_ctx, isClient != 0 ? SSL_VERIFY_PEER : SSL_VERIFY_CLIENT_ONCE, ILibDuktape_TlsStream_verify); /* Ask for authentication */ } + + if (nargs > 0 && duk_is_object(ctx, 0)) + { + sniHost = Duktape_GetStringPropertyValue(ctx, 0, "host", NULL); + data->OnServerSNI = Duktape_GetHeapptrProperty(ctx, 0, "SNICallback"); + if (duk_has_prop_string(ctx, 0, "pfx") && duk_has_prop_string(ctx, 0, "passphrase")) + { + // PFX certificate was passed in thru Options + char *pfx, *passphrase; + duk_size_t pfxLen, passphraseLen; + duk_push_fixed_buffer(ctx, sizeof(struct util_cert)); // [TLS][buff] + struct util_cert *cert = (struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL); + duk_put_prop_string(ctx, -2, ILibDuktape_TlsStream2Cert); // [TLS] + + duk_get_prop_string(ctx, 0, "pfx"); // [pfx] + duk_get_prop_string(ctx, 0, "passphrase"); // [pfx][passphrase] + pfx = (char*)Duktape_GetBuffer(ctx, -2, &pfxLen); + passphrase = (char*)Duktape_GetBuffer(ctx, -1, &passphraseLen); + + if (util_from_p12(pfx, (int)pfxLen, passphrase, cert) == 0) { return(ILibDuktape_Error(ctx, "Error Reading Certificate")); } + SSL_CTX_use_certificate(data->ssl_ctx, cert->x509); + SSL_CTX_use_PrivateKey(data->ssl_ctx, cert->pkey); + duk_pop_2(ctx); + } + } + + data->ssl = SSL_new(data->ssl_ctx); + data->TLSHandshakeCompleted = 0; + data->readBio = BIO_new(BIO_s_mem()); + data->writeBio = BIO_new(BIO_s_mem()); + BIO_get_mem_ptr(data->readBio, &(data->readBioBuffer)); + BIO_get_mem_ptr(data->writeBio, &(data->writeBioBuffer)); + BIO_set_mem_eof_return(data->readBio, -1); + BIO_set_mem_eof_return(data->writeBio, -1); + data->readBioBuffer->length = 0; + SSL_set_bio(data->ssl, data->readBio, data->writeBio); + + if (ILibDuktape_TlsStream_ctx2stream < 0) + { + ILibDuktape_TlsStream_ctx2stream = SSL_get_ex_new_index(0, "ILibDuktape_TlsStream index", NULL, NULL, NULL); + } + SSL_set_ex_data(data->ssl, ILibDuktape_TlsStream_ctx2stream, data); + + if (isClient != 0) + { + if (sniHost != NULL) { SSL_set_tlsext_host_name(data->ssl, sniHost); } + SSL_set_connect_state(data->ssl); + status = SSL_do_handshake(data->ssl); + if (status <= 0) { status = SSL_get_error(data->ssl, status); } + if (status == SSL_ERROR_WANT_READ) + { + ILibDuktape_TlsStream_ProcessEncryptedBuffer(data); + // We're going to drop out now, becuase we need to check for received data + } + } + else + { + if (data->OnServerSNI != NULL) + { + SSL_CTX_set_tlsext_servername_callback(data->ssl_ctx, ILibDuktape_TlsStream_serverSNI_callback); + } + SSL_set_accept_state(data->ssl); // Setup server SSL state + } + + return(1); +} + +void ILibDuktape_TlsStream_PUSH(duk_context *ctx, void *chain) +{ + duk_push_object(ctx); + ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "isClient", 1, "createClient", ILibDuktape_TlsStream_create, DUK_VARARGS); + ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "isClient", 0, "createServer", ILibDuktape_TlsStream_create, DUK_VARARGS); +} +void ILibDuktape_TlsStream_Init(duk_context *ctx) +{ + ILibDuktape_ModSearch_AddHandler(ctx, "TlsStream", ILibDuktape_TlsStream_PUSH); +} + diff --git a/microscript/ILibDuktape_WritableStream.c b/microscript/ILibDuktape_WritableStream.c index 98bfc6e..11a8003 100644 --- a/microscript/ILibDuktape_WritableStream.c +++ b/microscript/ILibDuktape_WritableStream.c @@ -235,6 +235,7 @@ duk_ret_t ILibDuktape_WritableStream_End(duk_context *ctx) duk_dup(ctx, 2); // [stream][flush] duk_put_prop_string(ctx, -2, "_Finish"); // [stream] } + stream->endBytes = (int)bufferLen; if (stream->WriteSink(stream, buffer, (int)bufferLen, stream->WriteSink_User) == ILibTransport_DoneState_INCOMPLETE) { // Incomplete, wait for SendOK @@ -265,11 +266,40 @@ duk_ret_t ILibDuktape_WritableStream_End_Getter(duk_context *ctx) duk_push_c_function(ctx, ILibDuktape_WritableStream_End, DUK_VARARGS); return 1; } +duk_ret_t ILibDuktape_WritableStream_UnPipeSink(duk_context *ctx) +{ + duk_dup(ctx, 0); + duk_push_this(ctx); + //printf("UNPIPE: [%s] => X => [%s]\n", Duktape_GetStringPropertyValue(ctx, -2, ILibDuktape_OBJID, "unknown"), Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "unknown")); + return(0); +} +duk_ret_t ILibDuktape_WritableStream_PipeSink(duk_context *ctx) +{ + ILibDuktape_WritableStream *ws; + duk_push_this(ctx); // [writable] + duk_get_prop_string(ctx, -1, ILibDuktape_WritableStream_WSPTRS); + ws = (ILibDuktape_WritableStream*)Duktape_GetBuffer(ctx, -1, NULL); + + if (duk_has_prop_string(ctx, 0, "\xFF_ReadableStream_PTRS")) + { + duk_get_prop_string(ctx, 0, "\xFF_ReadableStream_PTRS"); // [writable][rs] + ws->pipedReadable_native = (struct ILibDuktape_readableStream*)Duktape_GetBuffer(ctx, -1, NULL); + } + ws->pipedReadable = duk_get_heapptr(ctx, 0); + + duk_dup(ctx, 0); + duk_push_this(ctx); + //printf("PIPE: [%s] => [%s]\n", Duktape_GetStringPropertyValue(ctx, -2, ILibDuktape_OBJID, "unknown"), Duktape_GetStringPropertyValue(ctx, -1, ILibDuktape_OBJID, "unknown")); + + return(0); +} + ILibDuktape_WritableStream* ILibDuktape_WritableStream_Init(duk_context *ctx, ILibDuktape_WritableStream_WriteHandler WriteHandler, ILibDuktape_WritableStream_EndHandler EndHandler, void *user) { ILibDuktape_WritableStream *retVal; ILibDuktape_EventEmitter *emitter; + ILibDuktape_PointerValidation_Init(ctx); duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_WritableStream)); // [obj][buffer] retVal = (ILibDuktape_WritableStream*)Duktape_GetBuffer(ctx, -1, NULL); // [obj][buffer] memset(retVal, 0, sizeof(ILibDuktape_WritableStream)); @@ -283,7 +313,8 @@ ILibDuktape_WritableStream* ILibDuktape_WritableStream_Init(duk_context *ctx, IL retVal->WriteSink_User = user; emitter = ILibDuktape_EventEmitter_Create(ctx); - ILibDuktape_EventEmitter_CreateEvent(emitter, "pipe", &(retVal->OnPipe)); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "pipe"); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "unpipe"); ILibDuktape_EventEmitter_CreateEvent(emitter, "drain", &(retVal->OnDrain)); ILibDuktape_EventEmitter_CreateEvent(emitter, "finish", &(retVal->OnFinish)); ILibDuktape_EventEmitter_CreateEvent(emitter, "error", &(retVal->OnError)); @@ -291,5 +322,7 @@ ILibDuktape_WritableStream* ILibDuktape_WritableStream_Init(duk_context *ctx, IL ILibDuktape_CreateInstanceMethod(ctx, "write", ILibDuktape_WritableStream_Write, DUK_VARARGS); ILibDuktape_CreateEventWithGetter(ctx, "end", ILibDuktape_WritableStream_End_Getter); + ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "pipe", ILibDuktape_WritableStream_PipeSink); + ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "unpipe", ILibDuktape_WritableStream_UnPipeSink); return retVal; } diff --git a/microscript/ILibDuktape_WritableStream.h b/microscript/ILibDuktape_WritableStream.h index 335a386..27863ec 100644 --- a/microscript/ILibDuktape_WritableStream.h +++ b/microscript/ILibDuktape_WritableStream.h @@ -23,16 +23,19 @@ limitations under the License. struct ILibDuktape_WritableStream; typedef ILibTransport_DoneState(*ILibDuktape_WritableStream_WriteHandler)(struct ILibDuktape_WritableStream *stream, char *buffer, int bufferLen, void *user); typedef void(*ILibDuktape_WritableStream_EndHandler)(struct ILibDuktape_WritableStream *stream, void *user); -typedef void(*ILibDuktape_WriteableStream_WriteFlushNative)(struct ILibDuktape_WritableStream *stream, void *user); +typedef int(*ILibDuktape_WriteableStream_WriteFlushNative)(struct ILibDuktape_WritableStream *stream, void *user); typedef void(*ILibDuktape_WritableStream_PipeHandler)(struct ILibDuktape_WritableStream *stream, void *readableSource, void *user); +struct ILibDuktape_readableStream; + typedef struct ILibDuktape_WritableStream { + int JSCreated; duk_context *ctx; void *obj; void *OnDrain; void *OnWriteFlush; - void *OnPipe; + ILibDuktape_WriteableStream_WriteFlushNative OnWriteFlushEx; void *OnWriteFlushEx_User; @@ -42,8 +45,12 @@ typedef struct ILibDuktape_WritableStream ILibDuktape_WritableStream_WriteHandler WriteSink; ILibDuktape_WritableStream_EndHandler EndSink; - ILibDuktape_WritableStream_PipeHandler PipeSink; + + void *pipedReadable; + struct ILibDuktape_readableStream* pipedReadable_native; + void *WriteSink_User; + int endBytes; int Reserved; } ILibDuktape_WritableStream; diff --git a/microscript/ILibDuktape_fs.c b/microscript/ILibDuktape_fs.c index 9204641..8b2b94f 100644 --- a/microscript/ILibDuktape_fs.c +++ b/microscript/ILibDuktape_fs.c @@ -1229,6 +1229,7 @@ duk_ret_t ILibDuktape_fs_readFileSync(duk_context *ctx) duk_push_fixed_buffer(ctx, (duk_size_t)fileLen); ignore_result(fread(Duktape_GetBuffer(ctx, -1, NULL), 1, (size_t)fileLen, f)); fclose(f); + duk_push_buffer_object(ctx, -1, 0, (duk_size_t)fileLen, DUK_BUFOBJ_NODEJS_BUFFER); return(1); } diff --git a/microscript/ILibDuktape_http.c b/microscript/ILibDuktape_http.c index 29ea72d..728ee0f 100644 --- a/microscript/ILibDuktape_http.c +++ b/microscript/ILibDuktape_http.c @@ -14,2537 +14,31 @@ See the License for the specific language governing permissions and limitations under the License. */ -#ifdef WIN32 -#include -#include - -#include -#include -#endif - -#include "microstack/ILibParsers.h" #include "ILibDuktape_http.h" -#include "ILibDuktape_net.h" #include "ILibDuktapeModSearch.h" -#include "microstack/ILibWebServer.h" -#include "microstack/ILibWebClient.h" - -#include "ILibDuktape_ReadableStream.h" #include "ILibDuktape_Helpers.h" -#include "ILibDuktape_WritableStream.h" -#include "ILibDuktape_DuplexStream.h" #include "ILibDuktape_EventEmitter.h" -#include "microstack/ILibCrypto.h" +#include "ILibDuktape_DuplexStream.h" #include "microstack/ILibRemoteLogging.h" +#include "microstack/ILibCrypto.h" -#define HTTP_SERVER_PTR "\xFF_ServerPtr" -#define HTTP_WEBCLIENT_MGR "_RequestManagerPtr" -#define NativeSessionPtr "\xFF_SessionPtr" -#define SessionPtrJS "\xFF_SessionPtr_JS" -#define TLSPTR "\xFF_tlsSettings" -#define TLS_CERT "\xFF_cert" -#define TLS_CERT_NON_LEAF "\xFF_cert_nonleaf" -#define HTTP_DEFAULT_PROTO_KEY "\xFF_defaultProto" -#define HTTP_REQUEST_USER_PTR "\xFF_request_user_ptr" -#define HTTP_REQUEST_TOKEN_PTR "_TokenPtr" -#define HTTP_SOCKET_PTRS "\xFF_socket_ptrs" -#define HTTP_SOCKET_BUFFERPTR "\xFF_socket_bufferptr" -#define HTTP_STREAM_WRAPPER "\xFF_http_StreamWrapper" -#define HTTP_STREAM_WRAPPER_BUFSIZE 4096 -#define HTTP_CLIENTREQUEST_PARAMETER "\xFF_http_clientRequest_parameter" -#define CLIENTREQUEST_HTTP "\xFF_clientRequest_HTTP" -#define CLIENTREQUEST_SOCKET_WCDO "\xFF_clientRequest_SOCKET_WCDO" -#define HTTP_INCOMINGMSG_WebStateObject "\xFF_incomingMessage_WebStateObject" #define DIGEST_USERNAME "\xFF_DigestUsername" #define DIGEST_PASSWORD "\xFF_DigestPassword" -#define DIGEST_WCDO "\xFF_DIGEST_WCDO" #define HTTP_DIGEST "\xFF_HTTP_DIGEST" #define DIGEST_CLIENT_REQUEST "\xFF_DIGEST_CLIENT_REQUEST" #define HTTP_CLIENTREQUEST_DATAPTR "\xFF_CLIENTREQUEST_DATAPTR" #define CLIENTREQUEST_EVENT_NAME "\xFF_CLIENTREQUEST_EVENT_NAME" #define CLIENTREQUEST_IMSG_RSPTR "\xFF_CLIENTREQUEST_IMSG_RSPTR" + +#define DIGEST2CNONCE "\xFF_DIGEST2CNONCE" +#define DIGEST2NC "\xFF_DIGEST2NONCECOUNT" +#define DIGEST2WWWAUTH "\xFF_DIGEST2WWWAUTH" + #define DIGESTCLIENTREQUEST_END_CALLED "\xFF_DIGESTCLIENTREQUEST_END_CALLED" #define DIGESTCLIENTREQUEST_CONTINUE "\xFF_DIGESTCLIENTREQUEST_CONTINUE" #define DIGESTCLIENTREQUEST_TmpBuffer "\xFF_DIGESTCLIENTREQUEST_TmpBuffer" #define DIGESTCLIENTREQUEST_DIGEST "\xFF_DIGESTCLIENTREQUEST_DIGEST" -extern duk_idx_t ILibWebServer_DukTape_Push_ILibWebServerSession(duk_context *ctx, ILibWebServer_Session *session); -void* ILibDuktape_http_request_PUSH_clientRequest(duk_context *ctx, ILibWebClient_RequestToken token, int isWebSocket); - -typedef enum ILibDuktape_http_request_dataTypes -{ - ILibDuktape_http_request_dataType_UNKNOWN = 0, - ILibDuktape_http_request_dataType_request = 1, - ILibDuktape_http_request_dataType_webSocket = 2 -}ILibDuktape_http_request_dataTypes; - -#pragma pack(push, 1) -typedef struct ILibDuktape_http_request_dataType -{ - ILibDuktape_http_request_dataTypes STRUCT_TYPE; -}ILibDuktape_http_request_dataType; - -typedef struct ILibDuktape_http_requestClient_callbacks -{ - ILibDuktape_http_request_dataTypes STRUCT_TYPE; - void *clientRequest; - void *readableStream; - void *requestStream; - void *OnReceive; - void *OnContinue; - void *OnSocket; -#ifndef MICROSTACK_NOTLS - int rejectUnauthorized; - void *checkServerIdentity; -#endif -}ILibDuktape_http_requestClient_callbacks; - -typedef struct ILibDuktape_http_server_ptrs -{ - void *ctx; - void *serverObject; - void *OnResponse; - void *OnUpgrade; - void *OnCheckContinue; - void *OnConnect; -}ILibDuktape_http_server_ptrs; -typedef struct ILibDuktape_http_session_ptrs -{ - ILibDuktape_readableStream *req; - ILibDuktape_WritableStream *res; -}ILibDuktape_http_session_ptrs; -typedef struct ILibDuktape_http_server_websocket_ptrs -{ - ILibDuktape_EventEmitter *emitter; - ILibDuktape_DuplexStream *ds; - ILibWebServer_Session *session; -}ILibDuktape_http_server_websocket_ptrs; - -typedef struct ILibDuktape_WebSocket_Pointers -{ - ILibDuktape_http_request_dataTypes STRUCT_TYPE; - duk_context *ctx; - void *clientRequest_ptr; - void *socket_ptr; - ILibDuktape_DuplexStream *stream; - ILibWebClient_StateObject *wcdo; - int timeout; - void *onTimeout; - void *onPing; - void *onPong; -#ifndef MICROSTACK_NOTLS - int rejectUnauthorized; - void *checkServerIdentity; -#endif -}ILibDuktape_WebSocket_Pointers; -#pragma pack(pop) - - -typedef struct ILibDuktape_http_rawSocket -{ - duk_context *ctx; - void *self; - void *ConnectionToken; - ILibDuktape_EventEmitter *emitter; - ILibDuktape_DuplexStream *stream; -}ILibDuktape_http_rawSocket; - -typedef struct ILibDuktape_http_streamWrapper -{ - duk_context *ctx; - void *self; - ILibDuktape_EventEmitter *emitter; - ILibDuktape_DuplexStream *ds; - ILibWebClient_StateObject *wcdo; - ILibHTTPPacket *impliedHeaders; - ILibDuktape_WritableStream *serverResponse_stream; - void *OnRequest; - void *OnResponse; - int chunkEncoded; - - void *PipedReader; - ILibDuktape_WritableStream *PipedWriter; - char reserved[sizeof(ILibTransport) + sizeof(void*)]; - char hex[16]; - char buffer[HTTP_STREAM_WRAPPER_BUFSIZE]; - int bufferLen; -}ILibDuktape_http_streamWrapper; - - -char * ILibDuktape_http_getDefaultProto(duk_context *ctx) -{ - char *retVal; - duk_push_this(ctx); // [http] - duk_get_prop_string(ctx, -1, HTTP_DEFAULT_PROTO_KEY); // [http][default] - retVal = (char*)duk_get_string(ctx, -1); - duk_pop_2(ctx); // ... - return retVal; -} -#ifndef MICROSTACK_NOTLS -void ILibDuktape_X509_PUSH(duk_context *ctx, X509* cert) -{ - char hash[32]; - char fingerprint[100]; - - util_keyhash2(cert, hash); - util_tohex2(hash, 32, fingerprint); - - duk_push_object(ctx); // [cert] - duk_push_string(ctx, fingerprint); // [cert][fingerprint] - duk_put_prop_string(ctx, -2, "fingerprint"); // [cert] -} -#endif -ILibWebClient_RequestManager ILibDuktape_http_GetRequestManager(duk_context *ctx) -{ - ILibWebClient_RequestManager retVal = NULL; - - duk_push_this(ctx); // [http] - if (duk_has_prop_string(ctx, -1, HTTP_WEBCLIENT_MGR)) - { - duk_get_prop_string(ctx, -1, HTTP_WEBCLIENT_MGR); // [http][mgr] - retVal = (ILibWebClient_RequestManager)duk_get_pointer(ctx, -1); - duk_pop_2(ctx); // ... - } - else - { - duk_get_prop_string(ctx, -1, "chain"); // [http][chain] - duk_get_prop_string(ctx, -2, "RequestPoolSize"); // [http][chain][poolSize] - retVal = ILibCreateWebClient(duk_get_int(ctx, -1), duk_get_pointer(ctx, -2)); - duk_pop_2(ctx); // [http] - duk_push_pointer(ctx, retVal); // [http][mgr] - duk_put_prop_string(ctx, -2, HTTP_WEBCLIENT_MGR); // [http] - duk_pop(ctx); // ... - } - return retVal; -} - -duk_ret_t ILibDuktape_http_serverresponse_end(duk_context *ctx) -{ - ILibWebServer_Session *session; - ILibHTTPPacket *packet; - int statusCode; - char *statusMessage; - duk_size_t statusMessageLen; - - int nargs = duk_get_top(ctx); - duk_push_this(ctx); // [res] - duk_get_prop_string(ctx, -1, NativeSessionPtr); // [res][session] - session = (ILibWebServer_Session*)duk_to_pointer(ctx, -1); // [res][session] - duk_pop(ctx); // [res] - duk_get_prop_string(ctx, -1, "PacketPtr"); // [res][packet] - packet = (ILibHTTPPacket*)duk_to_pointer(ctx, -1); // [res][packet] - duk_pop(ctx); // [res] - duk_get_prop_string(ctx, -1, "statusCode"); // [res][code] - statusCode = duk_to_int(ctx, -1); duk_pop(ctx); // [res] - duk_get_prop_string(ctx, -1, "statusMessage"); // [res][msg] - statusMessage = (char*)duk_get_lstring(ctx, -1, &statusMessageLen); duk_pop(ctx); // [res] - ILibSetStatusCode(packet, statusCode, statusMessage, (int)statusMessageLen); - - ((ILibDuktape_http_session_ptrs*)session->Reserved_Transport.ChainLink.ExtraMemoryPtr)->req = NULL; - - if (nargs == 0) - { - ILibWebServer_Send(session, packet); - return 0; - } - - ILibWebServer_StreamHeader(session, packet); - - if (nargs > 0) - { - char *body; - duk_size_t bodyLen = 0; - - if (duk_is_string(ctx, 0)) - { - body = (char*)duk_get_lstring(ctx, 0, &bodyLen); - } - else - { - body = Duktape_GetBuffer(ctx, 0, &bodyLen); - } - ILibWebServer_StreamBody(session, body, (int)bodyLen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebServer_DoneFlag_Done); - } - return 0; -} -duk_ret_t ILibDuktape_http_serverresponse_setHeader(duk_context *ctx) -{ - ILibHTTPPacket *packet; - if (!duk_is_string(ctx, 0) || !duk_is_string(ctx, 1)) { return(ILibDuktape_Error(ctx, "http.serverresponse.setHeader(): Invalid Parameters")); } - duk_size_t fieldLen, valueLen; - char *field = (char*)duk_get_lstring(ctx, 0, &fieldLen); - char *value = (char*)duk_get_lstring(ctx, 1, &valueLen); - - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, "PacketPtr"); - packet = (ILibHTTPPacket*)duk_to_pointer(ctx, -1); - ILibAddHeaderLine(packet, field, (int)fieldLen, value, (int)valueLen); - return 0; -} - -duk_ret_t ILibDuktape_http_serverresponse_writeHead(duk_context *ctx) -{ - int nargs = duk_get_top(ctx); - int statusCode; - char *statusMessage = nargs > 1 ? (char*)duk_require_string(ctx, 1) : "OK"; - ILibHTTPPacket *packet; - ILibWebServer_Session *session; - - duk_push_this(ctx); // [response] - duk_get_prop_string(ctx, -1, "PacketPtr"); // [response][packet] - packet = (ILibHTTPPacket*)duk_to_pointer(ctx, -1); - duk_del_prop_string(ctx, -2, "PacketPtr"); - duk_get_prop_string(ctx, -2, NativeSessionPtr); // [response][packet][session] - session = (ILibWebServer_Session*)duk_to_pointer(ctx, -1); - - if (nargs < 1) { duk_push_string(ctx, "Missing Status Code"); duk_throw(ctx); return(DUK_RET_ERROR); } - statusCode = duk_require_int(ctx, 0); - ILibSetStatusCode(packet, statusCode, statusMessage, -1); - - if (nargs > 2) - { - duk_enum(ctx, 2, 0); - while (duk_next(ctx, -1, 1)) - { - ILibAddHeaderLine(packet, duk_to_string(ctx, -2), -1, duk_to_string(ctx, -1), -1); // [enum][key][value] - duk_pop_2(ctx); // [enum] - } - } - - ILibWebServer_StreamHeader(session, packet); - return 0; -} - -ILibTransport_DoneState ILibDuktape_http_server_WriteResponse(ILibDuktape_WritableStream *stream, char *buffer, int bufferLen, void *user) -{ - ILibWebServer_Status status = ILibWebServer_ALL_DATA_SENT; - ILibWebServer_Session *session = (ILibWebServer_Session*)user; - duk_push_heapptr(stream->ctx, stream->obj); // [res] - if (duk_has_prop_string(stream->ctx, -1, "PacketPtr")) - { - ILibHTTPPacket *packet; - duk_get_prop_string(stream->ctx, -1, "PacketPtr"); // [res][packet] - packet = (ILibHTTPPacket*)duk_to_pointer(stream->ctx, -1); - duk_pop(stream->ctx); // [res] - duk_del_prop_string(stream->ctx, -1, "PacketPtr"); - duk_get_prop_string(stream->ctx, -1, "statusCode"); // [res][code] - duk_get_prop_string(stream->ctx, -2, "statusMessage"); // [res][code][message] - ILibSetStatusCode(packet, duk_to_int(stream->ctx, -2), (char*)duk_to_string(stream->ctx, -1), -1); - duk_pop_3(stream->ctx); // ... - status = ILibWebServer_StreamHeader(session, packet); - } - if (bufferLen > 0) - { - status = ILibWebServer_StreamBody(session, buffer, bufferLen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebServer_DoneFlag_NotDone); - } - - switch (status) - { - case ILibWebServer_ALL_DATA_SENT: - return(ILibTransport_DoneState_COMPLETE); - case ILibWebServer_NOT_ALL_DATA_SENT_YET: - return(ILibTransport_DoneState_INCOMPLETE); - default: - return(ILibTransport_DoneState_ERROR); - } -} -void ILibDuktape_http_server_EndResponse(ILibDuktape_WritableStream *stream, void *user) -{ - ILibWebServer_Session *session = (ILibWebServer_Session*)user; - ILibDuktape_http_server_WriteResponse(stream, NULL, 0, user); - ILibWebServer_StreamBody(session, NULL, 0, ILibAsyncSocket_MemoryOwnership_USER, ILibWebServer_DoneFlag_Done); -} -duk_ret_t ILibDuktape_http_server_ServerResponse_Finalizer(duk_context *ctx) -{ - if (duk_has_prop_string(ctx, 0, "PacketPtr")) - { - duk_get_prop_string(ctx, 0, "PacketPtr"); - ILibDestructPacket((ILibHTTPPacket*)duk_to_pointer(ctx, -1)); - duk_del_prop_string(ctx, 0, "PacketPtr"); - } - return 0; -} -duk_ret_t ILibDuktape_http_server_ServerResponse_writeContinue(duk_context *ctx) -{ - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, NativeSessionPtr); - ILibWebServer_Session *session = (ILibWebServer_Session*)duk_get_pointer(ctx, -1); - - ILibWebServer_Send_Raw(session, "HTTP/1.1 100 Continue\r\n\r\n", 25, ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_NotDone); - return(0); -} -ILibDuktape_WritableStream* ILibDuktape_http_server_PUSH_ServerResponse(duk_context *ctx, ILibWebServer_Session *session) -{ - ILibHTTPPacket *packet = ILibCreateEmptyPacket(); - ILibSetVersion(packet, "1.1", 3); - - duk_push_object(ctx); // [obj] - duk_push_pointer(ctx, session); // [obj][session] - duk_put_prop_string(ctx, -2, NativeSessionPtr); // [obj] - duk_push_pointer(ctx, packet); // [obj][packet] - duk_put_prop_string(ctx, -2, "PacketPtr"); // [obj] - duk_push_int(ctx, 500); // [obj][statusCode] - duk_put_prop_string(ctx, -2, "statusCode"); // [obj] - duk_push_string(ctx, ""); // [obj][statusMessage] - duk_put_prop_string(ctx, -2, "statusMessage"); // [obj] - - duk_push_c_function(ctx, ILibDuktape_http_server_ServerResponse_Finalizer, 1); // [obj][fin] - duk_set_finalizer(ctx, -2); // [obj] - - duk_push_c_function(ctx, ILibDuktape_http_serverresponse_setHeader, 2); // [obj][func] - duk_put_prop_string(ctx, -2, "setHeader"); // [obj] - - duk_push_c_function(ctx, ILibDuktape_http_serverresponse_writeHead, DUK_VARARGS); // [obj][func] - duk_put_prop_string(ctx, -2, "writeHead"); // [obj] - - ILibDuktape_CreateInstanceMethod(ctx, "writeContinue", ILibDuktape_http_server_ServerResponse_writeContinue, 0); - - return ILibDuktape_WritableStream_Init(ctx, ILibDuktape_http_server_WriteResponse, ILibDuktape_http_server_EndResponse, session); -} - -ILibWebServer_Session* ILibDuktape_http_server_NativeSession_GetSession(duk_context *ctx) -{ - ILibWebServer_Session *retVal = NULL; - duk_push_this(ctx); // [session] - duk_get_prop_string(ctx, -1, NativeSessionPtr); // [session][ptr] - retVal = (ILibWebServer_Session*)duk_get_pointer(ctx, -1); - duk_pop_2(ctx); // ... - return retVal; -} -duk_ret_t ILibDuktape_http_server_NativeSession_Digest_IsAuthenticated(duk_context *ctx) -{ - ILibWebServer_Session *session = ILibDuktape_http_server_NativeSession_GetSession(ctx); - if (!duk_is_string(ctx, 0)) { return(ILibDuktape_Error(ctx, "server.NativeSession.IsAuthenticated(): Invalid Parameters")); } - duk_size_t realmLen; - char *realm = (char*)duk_get_lstring(ctx, 0, &realmLen); - - duk_push_int(ctx, ILibWebServer_Digest_IsAuthenticated(session, realm, (int)realmLen)); - return 1; -} -duk_ret_t ILibDuktape_http_server_NativeSession_Digest_SendUnAuthorized(duk_context *ctx) -{ - ILibWebServer_Session *session = ILibDuktape_http_server_NativeSession_GetSession(ctx); - if (!duk_is_string(ctx, 0) || !duk_is_string(ctx, 1)) { return(ILibDuktape_Error(ctx, "server.NativeSession.SendUnAuthorized(): Invalid Parameters")); } - duk_size_t realmLen, htmlLen; - char *realm = (char*)duk_get_lstring(ctx, 0, &realmLen); - char *html = (char*)duk_get_lstring(ctx, 1, &htmlLen); - ILibDuktape_http_session_ptrs *sessionPtrs = (ILibDuktape_http_session_ptrs*)session->Reserved_Transport.ChainLink.ExtraMemoryPtr; - - ILibWebServer_Digest_SendUnauthorized(session, realm, (int)realmLen, html, (int)htmlLen); - sessionPtrs->req = NULL; - - return 0; -} -duk_ret_t ILibDuktape_http_server_NativeSession_Digest_GetUsername(duk_context *ctx) -{ - ILibWebServer_Session *session = ILibDuktape_http_server_NativeSession_GetSession(ctx); - duk_push_string(ctx, ILibWebServer_Digest_GetUsername(session)); - return 1; -} -duk_ret_t ILibDuktape_http_server_NativeSession_Digest_ValidatePassword(duk_context *ctx) -{ - ILibWebServer_Session *session = ILibDuktape_http_server_NativeSession_GetSession(ctx); - duk_size_t pwdLen; - char *pwd = (char*)duk_get_lstring(ctx, 0, &pwdLen); - if (!duk_is_string(ctx, 0)) { return(ILibDuktape_Error(ctx, "server.NativeSession.ValidatePassword(): Invalid Parameter")); } - - duk_push_int(ctx, ILibWebServer_Digest_ValidatePassword(session, pwd, (int)pwdLen)); - return 1; -} -duk_ret_t ILibDuktape_http_server_NativeSession_WebSocket_GetDataType(duk_context *ctx) -{ - ILibWebServer_WebSocket_DataTypes dType; - ILibWebServer_Session *session; - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, NativeSessionPtr); - session = (ILibWebServer_Session*)duk_get_pointer(ctx, -1); - - dType = ILibWebServer_WebSocket_GetDataType(session); - duk_push_int(ctx, (int)dType); - return(1); -} -duk_ret_t ILibDuktape_http_server_NativeSession_WebSocket_Upgrade(duk_context *ctx) -{ - int nargs = duk_get_top(ctx); - int maxBuffer = nargs > 0 ? (int)duk_require_int(ctx, 0) : 65535; - ILibWebServer_Session *session; - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, NativeSessionPtr); - session = (ILibWebServer_Session*)duk_get_pointer(ctx, -1); - ILibWebServer_UpgradeWebSocket(session, maxBuffer); - return(0); -} -void ILibDuktape_http_server_NativeSession_PUSH(duk_context *ctx, ILibWebServer_Session* session) -{ - if (session->User != NULL) { duk_push_heapptr(ctx, session->User); return; } - - duk_push_object(ctx); // [session] - duk_push_pointer(ctx, session); // [session][ptr] - duk_put_prop_string(ctx, -2, NativeSessionPtr); // [session] - session->User = duk_get_heapptr(ctx, -1); - - ILibDuktape_CreateInstanceMethod(ctx, "Digest_IsAuthenticated", ILibDuktape_http_server_NativeSession_Digest_IsAuthenticated, 1); - ILibDuktape_CreateInstanceMethod(ctx, "Digest_SendUnauthorized", ILibDuktape_http_server_NativeSession_Digest_SendUnAuthorized, 2); - ILibDuktape_CreateInstanceMethod(ctx, "Digest_GetUsername", ILibDuktape_http_server_NativeSession_Digest_GetUsername, 0); - ILibDuktape_CreateInstanceMethod(ctx, "Digest_ValidatePassword", ILibDuktape_http_server_NativeSession_Digest_ValidatePassword, 1); - //ILibDuktape_CreateInstanceMethod(ctx, "WebSocket_GetDataType", ILibDuktape_http_server_NativeSession_WebSocket_GetDataType, 0); - //ILibDuktape_CreateInstanceMethod(ctx, "WebSocket_Upgrade", ILibDuktape_http_server_NativeSession_WebSocket_Upgrade, DUK_VARARGS); - -} - -duk_ret_t ILibDuktape_http_server_NativeSession_Getter(duk_context *ctx) -{ - ILibWebServer_Session *session; - - duk_push_this(ctx); // [incomingMessage] - duk_get_prop_string(ctx, -1, NativeSessionPtr); // [incomingMessage][ptr] - session = (ILibWebServer_Session*)duk_get_pointer(ctx, -1); - duk_pop(ctx); // [incomingMessage] - ILibDuktape_http_server_NativeSession_PUSH(ctx, session); // [incomingMessage][session] - if (!duk_has_prop_string(ctx, -2, SessionPtrJS)) - { - duk_dup(ctx, -1); // [incomingMessage][session][session] - duk_put_prop_string(ctx, -3, SessionPtrJS); // [incomingMessage][session] - } - return 1; -} - - -ILibTransport_DoneState ILibDutkape_http_server_rawSocket_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) -{ - ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user; - return((ILibTransport_DoneState)ILibAsyncSocket_Send(ptrs->ConnectionToken, buffer, bufferLen, ILibAsyncSocket_MemoryOwnership_USER)); -} -void ILibDuktape_http_server_rawSocket_OnData(ILibAsyncSocket_SocketModule socketModule, char* buffer, int *p_beginPointer, int endPointer, ILibAsyncSocket_OnInterrupt* OnInterrupt, void **user, int *PAUSE) -{ - ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user; - buffer += *p_beginPointer; - *p_beginPointer = endPointer; - *PAUSE = ILibDuktape_DuplexStream_WriteData(ptrs->stream, buffer, endPointer); -} -void ILibDuktape_http_server_rawSocket_OnDisconnect(ILibAsyncSocket_SocketModule socketModule, void *user) -{ - ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user; - ILibDuktape_DuplexStream_WriteEnd(ptrs->stream); -} -void ILibDuktape_http_server_rawSocket_OnSendOK(ILibAsyncSocket_SocketModule socketModule, void *user) -{ - ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user; - ILibDuktape_DuplexStream_Ready(ptrs->stream); -} -void ILibDuktape_http_server_rawSocket_EndSink(ILibDuktape_DuplexStream *stream, void *user) -{ - ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user; - ILibAsyncSocket_Disconnect(ptrs->ConnectionToken); -} -void ILibDuktape_http_server_rawSocket_PauseSink(ILibDuktape_DuplexStream *sender, void *user) -{ - ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user; - ILibAsyncSocket_Pause(ptrs->ConnectionToken); -} -void ILibDuktape_http_server_rawSocket_ResumeSink(ILibDuktape_DuplexStream *sender, void *user) -{ - ILibDuktape_http_rawSocket *ptrs = (ILibDuktape_http_rawSocket*)user; - ILibAsyncSocket_Resume(ptrs->ConnectionToken); -} -duk_ret_t ILibDuktape_http_server_IncomingMessage_rawSocket(duk_context *ctx) -{ - void *ConnectionToken; - ILibDuktape_http_rawSocket *ptrs; - - duk_push_this(ctx); // [incomingMessage] - if (!duk_has_prop_string(ctx, -1, NativeSessionPtr)) - { - duk_push_null(ctx); - return 1; - } - - duk_get_prop_string(ctx, -1, NativeSessionPtr); - ConnectionToken = ILibWebServer_Session_GetConnectionToken((ILibWebServer_Session*)duk_get_pointer(ctx, -1)); - if (ConnectionToken == NULL) - { - duk_push_null(ctx); - return 1; - } - - duk_push_object(ctx); // [socket] - duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_http_rawSocket)); // [socket][buffer] - ptrs = (ILibDuktape_http_rawSocket*)Duktape_GetBuffer(ctx, -1, NULL); - memset(ptrs, 0, sizeof(ILibDuktape_http_rawSocket)); - duk_put_prop_string(ctx, -2, HTTP_SOCKET_BUFFERPTR); // [socket] - - ptrs->ctx = ctx; - ptrs->ConnectionToken = ConnectionToken; - ptrs->self = duk_get_heapptr(ctx, -1); - ptrs->emitter = ILibDuktape_EventEmitter_Create(ctx); - ptrs->stream = ILibDuktape_DuplexStream_Init(ctx, ILibDutkape_http_server_rawSocket_WriteSink, ILibDuktape_http_server_rawSocket_EndSink, ILibDuktape_http_server_rawSocket_PauseSink, ILibDuktape_http_server_rawSocket_ResumeSink, ptrs); - ILibAsyncSocket_UpdateCallbacks(ConnectionToken, ILibDuktape_http_server_rawSocket_OnData, NULL, ILibDuktape_http_server_rawSocket_OnDisconnect, ILibDuktape_http_server_rawSocket_OnSendOK); - free(ILibAsyncSocket_GetUser(ConnectionToken)); - ILibAsyncSocket_SetUser(ConnectionToken, ptrs); - return 1; -} -void ILibDuktape_http_server_PUSH_IncomingMessage(duk_context *ctx, ILibHTTPPacket *packet, ILibWebServer_Session *session) -{ - duk_push_object(ctx); // [obj] - duk_push_pointer(ctx, packet); // [obj][packet] - duk_put_prop_string(ctx, -2, "PacketPtr"); // [obj] - //duk_push_external_buffer(ctx); // [obj][buffer] - //duk_put_prop_string(ctx, -2, "_buffer"); // [obj] - - if (session != NULL) - { - duk_push_pointer(ctx, session); // [obj][session] - duk_put_prop_string(ctx, -2, NativeSessionPtr); // [obj] - ILibDuktape_CreateInstanceMethod(ctx, "Digest_IsAuthenticated", ILibDuktape_http_server_NativeSession_Digest_IsAuthenticated, 1); - ILibDuktape_CreateInstanceMethod(ctx, "Digest_SendUnauthorized", ILibDuktape_http_server_NativeSession_Digest_SendUnAuthorized, 2); - ILibDuktape_CreateInstanceMethod(ctx, "Digest_GetUsername", ILibDuktape_http_server_NativeSession_Digest_GetUsername, 0); - ILibDuktape_CreateInstanceMethod(ctx, "Digest_ValidatePassword", ILibDuktape_http_server_NativeSession_Digest_ValidatePassword, 1); - } - if (packet != NULL && packet->Directive != NULL) - { - packet->Directive[packet->DirectiveLength] = 0; - duk_push_string(ctx, packet->Directive); // [obj][method] - duk_put_prop_string(ctx, -2, "method"); - - packet->DirectiveObj[packet->DirectiveObjLength] = 0; - duk_push_string(ctx, packet->DirectiveObj); // [obj][path] - duk_put_prop_string(ctx, -2, "url"); // [obj] - } - else if(packet != NULL) - { - duk_push_int(ctx, packet->StatusCode); - duk_put_prop_string(ctx, -2, "statusCode"); - if (packet->StatusData != NULL) - { - packet->StatusData[packet->StatusDataLength] = 0; - duk_push_string(ctx, packet->StatusData); - duk_put_prop_string(ctx, -2, "statusMessage"); - } - } - - if (packet != NULL) - { - packetheader_field_node *n = packet->FirstField; - duk_push_object(ctx); // [obj][header] - while (n != NULL) - { - duk_push_lstring(ctx, n->Field, n->FieldLength); // [obj][header][fieldName] - duk_push_lstring(ctx, n->FieldData, n->FieldDataLength); // [obj][header][fieldName][fieldValue] - duk_put_prop(ctx, -3); // [obj][header] - n = n->NextField; - } - duk_put_prop_string(ctx, -2, "header"); // [obj] - } - if (session != NULL) { ILibDuktape_CreateEventWithGetter(ctx, "socket", ILibDuktape_http_server_IncomingMessage_rawSocket); } -} - -void ILibDuktape_http_server_Pause(ILibDuktape_readableStream *sender, void *user) -{ - ILibWebServer_Pause((ILibWebServer_Session*)user); -} -void ILibDuktape_http_server_Resume(ILibDuktape_readableStream *sender, void *user) -{ - ILibWebServer_Resume((ILibWebServer_Session*)user); -} -ILibTransport_DoneState ILibDuktape_http_server_webSocket_WriteHandler(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) -{ - return((ILibTransport_DoneState)ILibWebServer_WebSocket_Send(((ILibDuktape_http_server_websocket_ptrs*)user)->session, buffer, bufferLen, stream->writableStream->Reserved == 0 ? ILibWebServer_WebSocket_DataType_BINARY : ILibWebServer_WebSocket_DataType_TEXT, ILibAsyncSocket_MemoryOwnership_USER, ILibWebServer_WebSocket_FragmentFlag_Complete)); -} -void ILibDuktape_http_server_webSocket_EndHandler(ILibDuktape_DuplexStream *stream, void *user) -{ - ILibWebServer_WebSocket_Close(((ILibDuktape_http_server_websocket_ptrs*)user)->session); -} -void ILibDuktape_http_server_webSocket_PauseHandler(ILibDuktape_DuplexStream *sender, void *user) -{ - ILibWebServer_Pause(((ILibDuktape_http_server_websocket_ptrs*)user)->session); -} -void ILibDuktape_http_server_webSocket_ResumeHandler(ILibDuktape_DuplexStream *sender, void *user) -{ - ILibWebServer_Resume(((ILibDuktape_http_server_websocket_ptrs*)user)->session); -} -void ILibDuktape_http_server_webSocket_OnReceive(struct ILibWebServer_Session *sender, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebServer_DoneFlag done) -{ - ILibDuktape_http_server_websocket_ptrs *ptrs = (ILibDuktape_http_server_websocket_ptrs*)((void**)sender->Reserved_Transport.ChainLink.ExtraMemoryPtr)[0]; - *beginPointer = endPointer; - - if (done == ILibWebServer_DoneFlag_Done) - { - ILibDuktape_DuplexStream_WriteEnd(ptrs->ds); - sender->OnDisconnect = NULL; - } - else - { - ILibDuktape_DuplexStream_WriteDataEx(ptrs->ds, ILibWebServer_WebSocket_GetDataType(sender) == ILibWebServer_WebSocket_DataType_TEXT ? 1 : 0, bodyBuffer, endPointer); - } -} -void ILibDuktape_http_server_webSocket_OnDisconnect(struct ILibWebServer_Session *sender) -{ - ILibDuktape_http_server_websocket_ptrs *ptrs = (ILibDuktape_http_server_websocket_ptrs*)((void**)sender->Reserved_Transport.ChainLink.ExtraMemoryPtr)[0]; - ILibDuktape_DuplexStream_WriteEnd(ptrs->ds); - sender->OnDisconnect = NULL; - - duk_push_heapptr(ptrs->emitter->ctx, ptrs->emitter->object); - duk_del_prop_string(ptrs->emitter->ctx, -1, NativeSessionPtr); - duk_pop(ptrs->emitter->ctx); -} -void ILibDuktape_http_server_webSocket_OnSendOK(struct ILibWebServer_Session *sender) -{ - ILibDuktape_http_server_websocket_ptrs *ptrs = (ILibDuktape_http_server_websocket_ptrs*)((void**)sender->Reserved_Transport.ChainLink.ExtraMemoryPtr)[0]; - ILibDuktape_DuplexStream_Ready(ptrs->ds); -} -void ILibDuktape_http_server_IntermediateSocket_Finalizer(duk_context *ctx, void *obj) -{ - duk_push_heapptr(ctx, obj); - if (duk_has_prop_string(ctx, -1, NativeSessionPtr)) - { - duk_get_prop_string(ctx, -1, NativeSessionPtr); - ILibWebServer_Session *session = (ILibWebServer_Session*)duk_get_pointer(ctx, -1); - - session->OnDisconnect = NULL; - session->OnReceive = NULL; - session->OnSendOK = NULL; - - ILibWebServer_DisconnectSession(session); - } -} -duk_ret_t ILibDuktape_http_server_IntermediateSocket_upgradeWebSocket(duk_context *ctx) -{ - ILibWebServer_Session *session; - int bufferSize = duk_get_top(ctx) == 0 ? 65535 : duk_require_int(ctx, 0); - duk_push_this(ctx); // [socket] - duk_get_prop_string(ctx, -1, NativeSessionPtr); // [socket][ptr] - session = (ILibWebServer_Session*)duk_get_pointer(ctx, -1); - duk_pop(ctx); // [socket] - if (ILibWebServer_UpgradeWebSocket(session, bufferSize) != 0) { return(ILibDuktape_Error(ctx, "upgradeWebSocket(): Invalid State")); } - - duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_http_server_websocket_ptrs)); // [socket][buffer] - ILibDuktape_http_server_websocket_ptrs *ptrs = (ILibDuktape_http_server_websocket_ptrs*)Duktape_GetBuffer(ctx, -1, NULL); - memset(ptrs, 0, sizeof(ILibDuktape_http_server_websocket_ptrs)); - duk_put_prop_string(ctx, -2, HTTP_SOCKET_PTRS); // [socket] - - ILibDuktape_CreateIndependentFinalizer(ctx, ILibDuktape_http_server_IntermediateSocket_Finalizer); - - ((void**)session->Reserved_Transport.ChainLink.ExtraMemoryPtr)[0] = ptrs; - ptrs->session = session; - ptrs->emitter = ILibDuktape_EventEmitter_Create(ctx); - ptrs->ds = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_http_server_webSocket_WriteHandler, ILibDuktape_http_server_webSocket_EndHandler, ILibDuktape_http_server_webSocket_PauseHandler, ILibDuktape_http_server_webSocket_ResumeHandler, ptrs); - - // Redirect these events, because it's now a websocket - session->OnDisconnect = ILibDuktape_http_server_webSocket_OnDisconnect; - session->OnReceive = ILibDuktape_http_server_webSocket_OnReceive; - session->OnSendOK = ILibDuktape_http_server_webSocket_OnSendOK; - - return(1); -} -duk_ret_t ILibDuktape_http_server_IntermediateSocket_end(duk_context *ctx) -{ - duk_push_this(ctx); - duk_get_prop_string(ctx, -1, NativeSessionPtr); - ILibWebServer_DisconnectSession((ILibWebServer_Session*)duk_get_pointer(ctx, -1)); - return(0); -} -void ILibDuktape_http_server_PUSH_IntermediateSocket(duk_context *ctx, ILibWebServer_Session *session) -{ - duk_push_object(ctx); // [sock] - duk_push_pointer(ctx, session); // [sock][session] - duk_put_prop_string(ctx, -2, NativeSessionPtr); // [sock] - - ILibDuktape_CreateInstanceMethod(ctx, "upgradeWebSocket", ILibDuktape_http_server_IntermediateSocket_upgradeWebSocket, DUK_VARARGS); - ILibDuktape_CreateProperty_InstanceMethod(ctx, "end", ILibDuktape_http_server_IntermediateSocket_end, 0); -} -void ILibDuktape_http_server_OnReceive(struct ILibWebServer_Session *sender, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebServer_DoneFlag done) -{ - ILibDuktape_http_server_ptrs* serverPtrs = (ILibDuktape_http_server_ptrs*)sender->ParentExtraMemory; - ILibDuktape_http_session_ptrs *sessionPtrs = (ILibDuktape_http_session_ptrs*)sender->Reserved_Transport.ChainLink.ExtraMemoryPtr; - char *headerVal; - int headerValLen; - int r = 0; - - if (ILibGetHeaderLineEx(header, "upgrade", 7, NULL) != NULL) - { - if (serverPtrs->OnUpgrade != NULL) - { - duk_push_heapptr(serverPtrs->ctx, serverPtrs->OnUpgrade); // [func] - duk_push_heapptr(serverPtrs->ctx, serverPtrs->serverObject); // [func][this] - ILibDuktape_http_server_PUSH_IncomingMessage(serverPtrs->ctx, header, sender); // [func][this][incoming] - ILibDuktape_http_server_PUSH_IntermediateSocket(serverPtrs->ctx, sender); // [func][this][incoming][socket] - duk_push_null(serverPtrs->ctx); // [func][this][incoming][socket][head] - - if (duk_pcall_method(serverPtrs->ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(serverPtrs->ctx, "Server.OnUpgrade(): "); } - duk_pop(serverPtrs->ctx); // ... - } - - return; - } - if ((headerVal = ILibGetHeaderLineEx(header, "expect", 6, &headerValLen)) != NULL) - { - if (ILibString_StartsWith(headerVal, headerValLen, "100-", 4)) - { - if (serverPtrs->OnCheckContinue != NULL) - { - duk_push_heapptr(serverPtrs->ctx, serverPtrs->OnCheckContinue); // [func] - duk_push_heapptr(serverPtrs->ctx, serverPtrs->serverObject); // [func][this] - - ILibDuktape_http_server_PUSH_IncomingMessage(serverPtrs->ctx, header, sender); // [func][this][incoming] - sessionPtrs->req = ILibDuktape_InitReadableStream(serverPtrs->ctx, NULL, NULL, NULL); - ILibDuktape_readableStream_SetPauseResumeHandlers(sessionPtrs->req, ILibDuktape_http_server_Pause, ILibDuktape_http_server_Resume, sender); - sessionPtrs->res = ILibDuktape_http_server_PUSH_ServerResponse(serverPtrs->ctx, sender);// [func][this][incoming][res] - if (duk_pcall_method(serverPtrs->ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(serverPtrs->ctx, "httpServer.onCheckContinue(): "); } - duk_pop(serverPtrs->ctx); // ... - } - else - { - // Noboddy is listening for checkContinue, so we must send a 100-Continue manually - ILibWebServer_Send_Raw(sender, "HTTP/1.1 Continue\r\n\r\n", 25, ILibAsyncSocket_MemoryOwnership_STATIC, ILibWebServer_DoneFlag_NotDone); - } - ILibDeleteHeaderLine(header, "expect", 6); - } - } - else - { - // The very first time, Server.request event will be emitted - if (sessionPtrs->req == NULL && serverPtrs->OnResponse != NULL) - { - duk_push_heapptr(serverPtrs->ctx, serverPtrs->OnResponse); // [func] - ILibDuktape_http_server_PUSH_IncomingMessage(serverPtrs->ctx, header, sender); // [func][req] - sessionPtrs->req = ILibDuktape_InitReadableStream(serverPtrs->ctx, NULL, NULL, NULL); - ILibDuktape_readableStream_SetPauseResumeHandlers(sessionPtrs->req, ILibDuktape_http_server_Pause, ILibDuktape_http_server_Resume, sender); - - duk_push_heap_stash(serverPtrs->ctx); // [func][req][stash] - duk_dup(serverPtrs->ctx, -2); // [func][req][stash][req] - duk_put_prop_string(serverPtrs->ctx, -2, Duktape_GetStashKey(sessionPtrs->req->object)); // [func][req][stash] - duk_pop(serverPtrs->ctx); // [func][req] - - sessionPtrs->res = ILibDuktape_http_server_PUSH_ServerResponse(serverPtrs->ctx, sender); // [func][req][res] - if (duk_pcall(serverPtrs->ctx, 2) != 0) // [retVal] - { - ILibDuktape_Process_UncaughtException(serverPtrs->ctx); - } - duk_pop(serverPtrs->ctx); // ... - } - } - // Now we just write to the ReadableStream - if (sessionPtrs->req != NULL) - { - if (endPointer > 0) - { - r += ILibDuktape_readableStream_WriteData(sessionPtrs->req, bodyBuffer + *beginPointer, endPointer - *beginPointer); - } - if (done == ILibWebServer_DoneFlag_Done && r == 0) - { - r += ILibDuktape_readableStream_WriteEnd(sessionPtrs->req); - } - if (done == ILibWebServer_DoneFlag_Done && r == 0) - { - duk_push_heap_stash(serverPtrs->ctx); // [stash] - duk_del_prop_string(serverPtrs->ctx, -1, Duktape_GetStashKey(sessionPtrs->req->object)); - duk_pop(serverPtrs->ctx); // ... - } - } - - if (r == 0) { *beginPointer = endPointer; } - if (done == ILibWebServer_DoneFlag_Done && sender->OnReceive == ILibDuktape_http_server_OnReceive) { sessionPtrs->req = NULL; } -} -void ILibDuktape_http_server_OnSendOK(ILibWebServer_Session *sender) -{ - ILibDuktape_http_session_ptrs *sessionPtrs = (ILibDuktape_http_session_ptrs*)sender->Reserved_Transport.ChainLink.ExtraMemoryPtr; - ILibDuktape_WritableStream_Ready(sessionPtrs->res); -} - -void ILibDuktape_http_server_OnSession(ILibWebServer_Session *session, void *user) -{ - session->OnReceive = ILibDuktape_http_server_OnReceive; - session->OnSendOK = ILibDuktape_http_server_OnSendOK; -} - -#ifndef MICROSTACK_NOTLS -int ILibDuktape_http_server_clientVerify(ILibWebServer_ServerToken sender, int preverify_ok, STACK_OF(X509) *certs, struct sockaddr_in6* address) -{ - int i; - int retVal = 1; - ILibDuktape_http_server_ptrs *ptrs = (ILibDuktape_http_server_ptrs*)((ILibChain_Link*)sender)->ExtraMemoryPtr; - void *OnVerify; - int rejectUnauthorized = 0; - char addr[512]; - - duk_push_heapptr(ptrs->ctx, ptrs->serverObject); // [server] - duk_get_prop_string(ptrs->ctx, -1, TLSPTR); // [server][tlsSettings] - OnVerify = Duktape_GetHeapptrProperty(ptrs->ctx, -1, "checkClientIdentity"); - rejectUnauthorized = Duktape_GetBooleanProperty(ptrs->ctx, -1, "rejectUnauthorized ", 0); - duk_pop_2(ptrs->ctx); // ... - - if (rejectUnauthorized != 0 && preverify_ok == 0) { retVal = 0; } - if (OnVerify != NULL) - { - duk_push_heapptr(ptrs->ctx, OnVerify); // [func] - duk_push_heapptr(ptrs->ctx, ptrs->serverObject); // [func][this] - ILibInet_ntop2((struct sockaddr*)address, addr, sizeof(addr)); - duk_push_string(ptrs->ctx, addr); // [func][this][server] - duk_push_array(ptrs->ctx); // [func][this][server][certs] - for (i = 0; i < sk_X509_num(certs); ++i) - { - ILibDuktape_X509_PUSH(ptrs->ctx, sk_X509_value(certs, i)); // [func][this][server][certs][cert] - duk_put_prop_index(ptrs->ctx, -2, i); // [func][this][server][certs] - } - if (duk_pcall_method(ptrs->ctx, 2) != 0) { retVal = 0; } // [retVal] - duk_pop(ptrs->ctx); // ... - } - return retVal; -} -#endif -duk_ret_t ILibDuktape_http_server_finalizer(duk_context *ctx) -{ - duk_get_prop_string(ctx, 0, HTTP_SERVER_PTR); - ILibWebServer_ServerToken server = (ILibWebServer_ServerToken)duk_get_pointer(ctx, -1); - void *chain = Duktape_GetChain(ctx); - - if (ILibIsChainBeingDestroyed(chain) == 0) - { - // SafeRemove Server here... - ILibChain_SafeRemove(chain, server); - } - - return(0); -} -duk_ret_t ILibDuktape_http_server_listen(duk_context *ctx) -{ - int nargs = duk_get_top(ctx); - int port = duk_require_int(ctx, 0); - //char *host = nargs > 1 ? ((char*)duk_require_string(ctx, 1)) : NULL; - int maxConnections = nargs > 2 ? duk_require_int(ctx, 2) : 5; - void *chain; - ILibWebServer_ServerToken server; - ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx); - - duk_push_this(ctx); // [server] - duk_get_prop_string(ctx, -1, "chain"); // [server][chain] - chain = duk_to_pointer(ctx, -1); - duk_pop(ctx); // [server] - - server = ILibWebServer_Create2(chain, maxConnections, port, ILibDuktape_http_server_OnSession, sizeof(ILibDuktape_http_server_ptrs), NULL); - ((ILibDuktape_http_server_ptrs*)((ILibChain_Link*)server)->ExtraMemoryPtr)->ctx = ctx; - ((ILibDuktape_http_server_ptrs*)((ILibChain_Link*)server)->ExtraMemoryPtr)->serverObject = duk_get_heapptr(ctx, -1); - - ILibDuktape_EventEmitter_AddEventHeapptr(emitter, "request", &(((ILibDuktape_http_server_ptrs*)((ILibChain_Link*)server)->ExtraMemoryPtr)->OnResponse)); - ILibDuktape_EventEmitter_AddEventHeapptr(emitter, "connect", &(((ILibDuktape_http_server_ptrs*)((ILibChain_Link*)server)->ExtraMemoryPtr)->OnConnect)); - ILibDuktape_EventEmitter_AddEventHeapptr(emitter, "upgrade", &(((ILibDuktape_http_server_ptrs*)((ILibChain_Link*)server)->ExtraMemoryPtr)->OnUpgrade)); - ILibDuktape_EventEmitter_AddEventHeapptr(emitter, "checkContinue", &(((ILibDuktape_http_server_ptrs*)((ILibChain_Link*)server)->ExtraMemoryPtr)->OnCheckContinue)); - -#ifndef MICROSTACK_NOTLS - if (duk_has_prop_string(ctx, -1, TLSPTR)) - { - struct util_cert *cert; - struct util_cert *nonleaf = NULL; - - duk_get_prop_string(ctx, -1, TLSPTR); // [server][tlsSettings] - duk_get_prop_string(ctx, -1, TLS_CERT); // [server][tlsSettings][cert] - cert = (struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL); - duk_pop(ctx); // [server][tlsSettings] - if (duk_has_prop_string(ctx, -1, TLS_CERT_NON_LEAF)) - { - duk_get_prop_string(ctx, -1, TLS_CERT_NON_LEAF); // [server][tlsSettings][nonleaf] - nonleaf = (struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL); - duk_pop(ctx); // [server][tlsSettings] - } - - ILibWebServer_EnableHTTPS(server, cert, nonleaf != NULL ? nonleaf->x509 : NULL, Duktape_GetBooleanProperty(ctx, -1, "requestCert", 0), ILibDuktape_http_server_clientVerify); - duk_pop(ctx); // [server] - } -#endif - - duk_push_pointer(ctx, server); // [server][serverPtr] - duk_put_prop_string(ctx, -2, HTTP_SERVER_PTR); // [server] - ILibDuktape_CreateFinalizer(ctx, ILibDuktape_http_server_finalizer); - - return 0; -} - -duk_ret_t ILibDuktape_http_server_tlsSettings_Finalizer(duk_context *ctx) -{ - return 0; -} -duk_ret_t ILibDuktape_http_server_address(duk_context *ctx) -{ - duk_push_this(ctx); - if (!duk_has_prop_string(ctx, -1, HTTP_SERVER_PTR)) - { - return(ILibDuktape_Error(ctx, "http.server.address(): Not listening")); - } - duk_get_prop_string(ctx, -1, HTTP_SERVER_PTR); - ILibAsyncServerSocket_ServerModule s = ILibWebServer_GetServerSocketModule((ILibWebServer_ServerToken)duk_get_pointer(ctx, -1)); - struct sockaddr_in6 local; - memset(&local, 0, sizeof(struct sockaddr_in6)); - - ILibAsyncServerSocket_GetLocal(s, (struct sockaddr*)&local, sizeof(struct sockaddr_in6)); - if (local.sin6_family == AF_UNSPEC) { return(ILibDuktape_Error(ctx, "net.server.address(): call to getsockname() failed")); } - - duk_push_object(ctx); - duk_push_string(ctx, local.sin6_family == AF_INET6 ? "IPv6" : "IPv4"); - duk_put_prop_string(ctx, -2, "family"); - - duk_push_int(ctx, (int)ntohs(local.sin6_port)); - duk_put_prop_string(ctx, -2, "port"); - - duk_push_string(ctx, ILibRemoteLogging_ConvertAddress((struct sockaddr*)&local)); - duk_put_prop_string(ctx, -2, "address"); - - return(1); -} -duk_ret_t ILibDuktape_http_createServer(duk_context *ctx) -{ - int nargs = duk_get_top(ctx); - int i; - - ILibDuktape_EventEmitter *emitter; - - duk_push_this(ctx); // [http] - duk_get_prop_string(ctx, -1, "chain"); // [http][chain] - - duk_push_object(ctx); // [http][chain][server] - duk_swap_top(ctx, -2); // [http][server][chain] - duk_put_prop_string(ctx, -2, "chain"); // [http][server] - - emitter = ILibDuktape_EventEmitter_Create(ctx); - ILibDuktape_EventEmitter_CreateEventEx(emitter, "request"); - ILibDuktape_EventEmitter_CreateEventEx(emitter, "listening"); - ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade"); - ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect"); - ILibDuktape_EventEmitter_CreateEventEx(emitter, "checkContinue"); - - for (i = 0; i < nargs; ++i) - { - if (duk_is_function(ctx, i)) - { - // requestListener - ILibDuktape_EventEmitter_AddOn(emitter, "request", duk_require_heapptr(ctx, i)); - } - else if (duk_is_object(ctx, i)) - { - // options - if (duk_has_prop_string(ctx, 0, "request")) - { - ILibDuktape_EventEmitter_AddOn(emitter, "request", Duktape_GetHeapptrProperty(ctx, 0, "request")); - } - else if (duk_has_prop_string(ctx, i, "MeshAgent")) - { -#ifndef MICROSTACK_NOTLS - struct util_cert *cert; - struct util_cert *nonleaf; - - duk_get_prop_string(ctx, i, "MeshAgent"); // [http][server][MeshAgent] - duk_get_prop_string(ctx, -1, ILibDuktape_MeshAgent_Cert_Server); // [http][server][MeshAgent][cert] - cert = (struct util_cert*)duk_get_pointer(ctx, -1); - duk_get_prop_string(ctx, -2, ILibDuktape_MeshAgent_Cert_NonLeaf); // [http][server][MeshAgent][cert][nonleaf] - nonleaf = (struct util_cert*)duk_get_pointer(ctx, -1); - duk_pop_3(ctx); // [http][server] - duk_push_object(ctx); // [http][server][tlsSettings] - duk_push_external_buffer(ctx); // [http][server][tlsSettings][cert] - duk_config_buffer(ctx, -1, cert, sizeof(struct util_cert)); - duk_put_prop_string(ctx, -2, TLS_CERT); // [http][server][tlsSettings] - duk_push_external_buffer(ctx); // [http][server][tlsSettings][nonleaf] - duk_config_buffer(ctx, -1, nonleaf, sizeof(struct util_cert)); - duk_put_prop_string(ctx, -2, TLS_CERT_NON_LEAF); // [http][server][tlsSettings] - duk_put_prop_string(ctx, -2, TLSPTR); // [http][server] -#else - return(ILibDuktape_Error(ctx, "createServer(): Invalid Argument. MeshAgent only valid with TLS support")); -#endif - } -#ifndef MICROSTACK_NOTLS - else if (duk_has_prop_string(ctx, i, "pfx") && duk_has_prop_string(ctx, i, "passphrase")) - { - struct util_cert *cert; - char *pfx; - duk_size_t pfxLen; - char *passphrase; - - duk_get_prop_string(ctx, i, "pfx"); // [http][server][pfx] - pfx = Duktape_GetBuffer(ctx, -1, &pfxLen); - duk_pop(ctx); // [http][server] - duk_get_prop_string(ctx, i, "passphrase"); // [http][server][passphrase] - passphrase = Duktape_GetBuffer(ctx, -1, NULL); - duk_pop(ctx); // [http][server] - - duk_push_object(ctx); // [http][server][tlsSettings] - duk_push_fixed_buffer(ctx, sizeof(struct util_cert)); // [http][server][tlsSettings][cert] - cert = (struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL); - duk_put_prop_string(ctx, -2, TLS_CERT); // [http][server][tlsSettings] - - if (util_from_p12(pfx, (int)pfxLen, passphrase, cert) == 0) { duk_push_string(ctx, "ERROR reading certificate"); duk_throw(ctx); return(DUK_RET_ERROR); } - else - { - ILibDuktape_CreateFinalizer(ctx, ILibDuktape_http_server_tlsSettings_Finalizer); - duk_put_prop_string(ctx, -2, TLSPTR); // [http][server] - } - } -#endif - - if (duk_has_prop_string(ctx, -1, TLSPTR)) // [http][server] - { - duk_get_prop_string(ctx, -1, TLSPTR); // [http][server][tlsSettings] - duk_push_boolean(ctx, (duk_bool_t)Duktape_GetBooleanProperty(ctx, i, "requestCert", 0)); // [http][server][tlsSettings][requestCert] - duk_put_prop_string(ctx, -2, "requestCert"); // [http][server][tlsSettings] - duk_push_boolean(ctx, (duk_bool_t)Duktape_GetBooleanProperty(ctx, i, "rejectUnauthorized", 0)); // [http][server][tlsSettings][reject] - duk_put_prop_string(ctx, -2, "rejectUnauthorized"); // [http][server][tlsSettings] - duk_push_heapptr(ctx, Duktape_GetHeapptrProperty(ctx, i, "checkClientIdentity")); // [http][server][tlsSettings][checkClient] - duk_put_prop_string(ctx, -2, "checkClientIdentity"); // [http][server][tlsSettings] - duk_put_prop_string(ctx, -2, TLSPTR); // [http][server] - } - - } - } - - duk_push_c_function(ctx, ILibDuktape_http_server_listen, DUK_VARARGS); // [http][server][func] - duk_put_prop_string(ctx, -2, "listen"); // [http][server] - - ILibDuktape_CreateInstanceMethod(ctx, "_address", ILibDuktape_http_server_address, 0); - return 1; -} - -void ILibDuktape_http_request_Pause(ILibDuktape_readableStream* sender, void *user) -{ - ILibWebClient_Pause(user); -} -void ILibDuktape_http_request_Resume(ILibDuktape_readableStream* sender, void *user) -{ - ILibWebClient_Resume(user); -} - -void ILibDuktape_http_request_OnResponse(ILibWebClient_StateObject WebStateObject, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebClient_ReceiveStatus recvStatus, void *user1, void *user2, int *PAUSE) -{ - int r = 0; - duk_context *ctx = (duk_context*)user1; - ILibDuktape_http_requestClient_callbacks *ptrs = (ILibDuktape_http_requestClient_callbacks*)user2; - - if (ptrs == NULL) { return; } - if(ptrs->readableStream != NULL) - { - if (endPointer > 0) - { - r += ILibDuktape_readableStream_WriteData(ptrs->readableStream, bodyBuffer + *beginPointer, endPointer - *beginPointer); - } - if(r == 0 && recvStatus == ILibWebClient_ReceiveStatus_Complete) - { - r += ILibDuktape_readableStream_WriteEnd(ptrs->readableStream); - } - } - else - { - if (InterruptFlag != 0 || header == NULL) - { - if (ctx != NULL) - { - duk_push_heapptr(ctx, ptrs->clientRequest); // [clientRequest] - duk_get_prop_string(ctx, -1, "emit"); // [clientRequest][emit] - duk_swap_top(ctx, -2); // [emit][this] - duk_push_string(ctx, "error"); // [emit][this][error] - duk_push_error_object(ctx, DUK_ERR_ERROR, "Socket was unexpectedly closed"); // [emit][this][error][err] - if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.request.OnError(): "); } - duk_pop(ctx); // ... - } - return; - } - - if ((header->StatusCode == 100 && ptrs->OnContinue != NULL) || ptrs->OnReceive != NULL) - { - ILibDuktape_http_server_PUSH_IncomingMessage(ctx, header, NULL); // [iMsg] - duk_push_pointer(ctx, WebStateObject); - duk_put_prop_string(ctx, -2, HTTP_INCOMINGMSG_WebStateObject); - - ptrs->readableStream = ILibDuktape_InitReadableStream(ctx, NULL, NULL, NULL); - ILibDuktape_readableStream_SetPauseResumeHandlers(ptrs->readableStream, ILibDuktape_http_request_Pause, ILibDuktape_http_request_Resume, WebStateObject); - - duk_push_heapptr(ctx, ptrs->clientRequest); // [iMsg][clientRequest] - duk_swap_top(ctx, -2); // [clientRequest][iMsg] - duk_dup(ctx, -1); // [clientRequest][iMsg][iMsg] - duk_put_prop_string(ctx, -3, "_iMsgPtr"); // [clientRequest][iMsg] - duk_swap_top(ctx, -2); // [iMsg][clientRequest] - if (header->StatusCode == 100 && ptrs->OnContinue != NULL) - { - duk_push_heapptr(ctx, ptrs->OnContinue); // [iMsg][clientRequest][func] - } - else - { - duk_push_heapptr(ctx, ptrs->OnReceive); // [iMsg][clientRequest][func] - } - duk_swap(ctx, -3, -1); // [func][clientRequest/this][iMsg] - if (duk_pcall_method(ctx, 1) != 0) // [retVal] - { - ILibDuktape_Process_UncaughtException(ctx); - } - duk_pop(ctx); // ... - } - if (endPointer > 0) { r += ILibDuktape_readableStream_WriteData(ptrs->readableStream, bodyBuffer + *beginPointer, endPointer - *beginPointer); } - if (r == 0 && recvStatus == ILibWebClient_ReceiveStatus_Complete) { r += ILibDuktape_readableStream_WriteEnd(ptrs->readableStream); } - } - - if (r == 0) { *beginPointer = endPointer; } - if (recvStatus == ILibWebClient_ReceiveStatus_Complete) - { - duk_push_heapptr(ctx, ptrs->clientRequest); // [clientRequest] - duk_del_prop_string(ctx, -1, HTTP_REQUEST_TOKEN_PTR); - duk_del_prop_string(ctx, -1, HTTP_REQUEST_USER_PTR); - ILibDuktape_EventEmitter_RemoveAll(ILibDuktape_EventEmitter_GetEmitter(ctx, -1)); - duk_pop(ctx); // ... - } -} - - -ILibTransport_DoneState ILibDuktape_http_WebSocket_socket_write(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) -{ - ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user; - if (ptrs->wcdo == NULL) - { - return(ILibTransport_DoneState_ERROR); - } - return((ILibTransport_DoneState)ILibWebClient_WebSocket_Send(ptrs->wcdo, ILibWebClient_WebSocket_DataType_BINARY, buffer, bufferLen, ILibAsyncSocket_MemoryOwnership_USER, ILibWebClient_WebSocket_FragmentFlag_Complete)); -} -void ILibDuktape_http_WebSocket_socket_end(ILibDuktape_DuplexStream *stream, void *user) -{ - ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user; - ILibWebClient_StateObject wcdo = ptrs->wcdo; - - ptrs->wcdo = NULL; - if (wcdo != NULL) { ILibWebClient_Disconnect(wcdo); } -} -void ILibDuktape_http_WebSocket_socket_pause(ILibDuktape_DuplexStream *sender, void *user) -{ - ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user; - ILibWebClient_Pause(ptrs->wcdo); -} -void ILibDuktape_http_WebSocket_socket_resume(ILibDuktape_DuplexStream *sender, void *user) -{ - ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user; - ILibWebClient_Resume(ptrs->wcdo); -} -duk_ret_t ILibDuktape_http_WebSocket_socket_finalizer(duk_context *ctx) -{ - ILibDuktape_WebSocket_Pointers *ptrs; - - if (duk_has_prop_string(ctx, 0, HTTP_SOCKET_PTRS)) - { - duk_get_prop_string(ctx, 0, HTTP_SOCKET_PTRS); - ptrs = (ILibDuktape_WebSocket_Pointers*)duk_get_pointer(ctx, -1); - ILibWebClient_Disconnect(ptrs->wcdo); - } - return 0; -} -void ILibDuktape_http_WebSocket_timeoutSink(ILibWebClient_StateObject state, void *user) -{ - ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user; - - if (ptrs->onTimeout != NULL) - { - duk_push_heapptr(ptrs->ctx, ptrs->onTimeout); // [func] - duk_push_heapptr(ptrs->ctx, ptrs->socket_ptr); // [func][this] - if (duk_pcall_method(ptrs->ctx, 0) != 0) { ILibDuktape_Process_UncaughtException(ptrs->ctx); } - duk_pop(ptrs->ctx); - } - if (ptrs->timeout > 0) { ILibWebClient_SetTimeout(ptrs->wcdo, ptrs->timeout, ILibDuktape_http_WebSocket_timeoutSink, ptrs); } -} -duk_ret_t ILibDuktape_http_WebSocket_setTimeout(duk_context *ctx) -{ - ILibDuktape_EventEmitter *emitter; - ILibDuktape_WebSocket_Pointers *ptrs; - int nargs = duk_get_top(ctx); - int milliseconds = duk_require_int(ctx, 0); - - duk_push_this(ctx); // [socket] - duk_get_prop_string(ctx, -1, HTTP_SOCKET_PTRS); // [socket][ptrs] - ptrs = (ILibDuktape_WebSocket_Pointers*)duk_get_pointer(ctx, -1); - - if (milliseconds < 1000) { return(ILibDuktape_Error(ctx, "http/net.socket.setTimeout(): Error, timeout cannot be less than 1 second")); } - ILibWebClient_SetTimeout(ptrs->wcdo, milliseconds / 1000, ILibDuktape_http_WebSocket_timeoutSink, ptrs); - ptrs->timeout = milliseconds / 1000; - if (nargs > 1) - { - emitter = ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx); - ILibDuktape_EventEmitter_AddOnce(emitter, "timeout", duk_require_heapptr(ctx, 1)); - } - - return 0; -} -duk_ret_t ILibDuktape_http_WebSocket_ping(duk_context *ctx) -{ - ILibDuktape_WebSocket_Pointers *ptrs; - - duk_push_this(ctx); // [socket] - duk_get_prop_string(ctx, -1, HTTP_SOCKET_PTRS); // [socket][ptrs] - ptrs = (ILibDuktape_WebSocket_Pointers*)duk_get_pointer(ctx, -1); - - ILibWebClient_WebSocket_Ping(ptrs->wcdo); - - return 0; -} -duk_ret_t ILibDuktape_http_WebSocket_pong(duk_context *ctx) -{ - ILibDuktape_WebSocket_Pointers *ptrs; - - duk_push_this(ctx); // [socket] - duk_get_prop_string(ctx, -1, HTTP_SOCKET_PTRS); // [socket][ptrs] - ptrs = (ILibDuktape_WebSocket_Pointers*)duk_get_pointer(ctx, -1); - - ILibWebClient_WebSocket_Pong(ptrs->wcdo); - return 0; -} -ILibWebClient_WebSocket_PingResponse ILibDuktape_http_WebSocket_pingHandler(ILibWebClient_StateObject state, void *user) -{ - ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user; - ILibWebClient_WebSocket_PingResponse retVal = ILibWebClient_WebSocket_PingResponse_Respond; - - if (ptrs->onPing != NULL) - { - duk_push_heapptr(ptrs->ctx, ptrs->onPing); // [func] - duk_push_heapptr(ptrs->ctx, ptrs->socket_ptr); // [func][this] - if (duk_pcall_method(ptrs->ctx, 0) != 0) { retVal = ILibWebClient_WebSocket_PingResponse_None; } - duk_pop(ptrs->ctx); // ... - } - - return retVal; -} -void ILibDuktape_http_WebSocket_pongHandler(ILibWebClient_StateObject state, void *user) -{ - ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user; - - if (ptrs->onPong != NULL) - { - duk_push_heapptr(ptrs->ctx, ptrs->onPong); // [func] - duk_push_heapptr(ptrs->ctx, ptrs->socket_ptr); // [func][this] - if (duk_pcall_method(ptrs->ctx, 0) != 0) { ILibDuktape_Process_UncaughtException(ptrs->ctx); } - duk_pop(ptrs->ctx); // ... - } -} -void ILibDuktape_http_WebSocket_PUSH_socket(duk_context *ctx, ILibWebClient_StateObject *wcdo, ILibDuktape_WebSocket_Pointers* ptrs) -{ - ILibDuktape_EventEmitter *emitter; - - if (ptrs->socket_ptr == NULL) - { - duk_push_object(ctx); // [socket] - duk_push_pointer(ctx, ptrs); // [socket][ptr] - duk_put_prop_string(ctx, -2, HTTP_SOCKET_PTRS); // [socket] - ptrs->socket_ptr = duk_get_heapptr(ctx, -1); - ptrs->stream = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_http_WebSocket_socket_write, ILibDuktape_http_WebSocket_socket_end, ILibDuktape_http_WebSocket_socket_pause, ILibDuktape_http_WebSocket_socket_resume, ptrs); - ptrs->wcdo = wcdo; - ILibDuktape_CreateFinalizer(ctx, ILibDuktape_http_WebSocket_socket_finalizer); - emitter = ILibDuktape_EventEmitter_Create(ctx); - ILibDuktape_EventEmitter_CreateEvent(emitter, "timeout", &(ptrs->onTimeout)); - ILibDuktape_EventEmitter_CreateEvent(emitter, "ping", &(ptrs->onPing)); - ILibDuktape_EventEmitter_CreateEvent(emitter, "pong", &(ptrs->onPong)); - ILibDuktape_CreateProperty_InstanceMethod(ctx, "ping", ILibDuktape_http_WebSocket_ping, DUK_VARARGS); - ILibDuktape_CreateProperty_InstanceMethod(ctx, "pong", ILibDuktape_http_WebSocket_pong, DUK_VARARGS); - ILibDuktape_CreateInstanceMethod(ctx, "setTimeout", ILibDuktape_http_WebSocket_setTimeout, DUK_VARARGS); - - ILibWebClient_WebSocket_SetPingPongHandler(ptrs->wcdo, ILibDuktape_http_WebSocket_pingHandler, ILibDuktape_http_WebSocket_pongHandler, ptrs); - } - else - { - duk_push_heapptr(ctx, ptrs->socket_ptr); // [socket] - } -} - -void ILibDuktape_http_request_WebSocket_OnResponse(ILibWebClient_StateObject WebStateObject, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebClient_ReceiveStatus recvStatus, void *user1, void *user2, int *PAUSE) -{ - duk_context *ctx = (duk_context*)user1; - ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user2; - - if (ctx == NULL || ptrs == NULL) { return; } - duk_push_heapptr(ctx, ptrs->clientRequest_ptr); - duk_del_prop_string(ctx, -1, HTTP_REQUEST_TOKEN_PTR); - void **user = ILibWebClient_RequestToken_GetUserObjects(ILibWebClient_GetRequestToken_FromStateObject(WebStateObject)); - duk_pop(ctx); - - if (header != NULL && header->StatusCode != 101) - { - duk_push_heapptr(ctx, ptrs->clientRequest_ptr); // [clientRequest] - duk_get_prop_string(ctx, -1, "emit"); // [clientRequest][emit] - duk_swap_top(ctx, -2); // [emit][this] - duk_push_string(ctx, "response"); // [emit][this][response] - - ILibDuktape_http_server_PUSH_IncomingMessage(ctx, header, NULL); // [emit][this][response][iMsg] - duk_push_pointer(ctx, WebStateObject); - duk_put_prop_string(ctx, -2, HTTP_INCOMINGMSG_WebStateObject); // [emit][this][response][iMsg] - if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http_request_WebSocket_OnResponse(): Error dispatching 'response' "); } - duk_pop(ctx); // ... - - *beginPointer = endPointer; - user[1] = NULL; - return; - } - - if (header != NULL && header->StatusCode == 101) - { - switch (recvStatus) - { - case ILibWebClient_ReceiveStatus_Connection_Established: - duk_push_heapptr(ctx, ptrs->clientRequest_ptr); // [clientRequest] - duk_get_prop_string(ctx, -1, "emit"); // [clientRequest][emit] - duk_swap_top(ctx, -2); // [emit][this] - duk_push_string(ctx, "upgrade"); // [emit][this][upgrade] - - ILibDuktape_http_WebSocket_PUSH_socket(ctx, WebStateObject, ptrs); // [emit][this][upgrade][socket] - duk_dup(ctx, -1); // [emit][this][upgrade][socket][socket] - duk_put_prop_string(ctx, -4, "\xFF_socket"); // [emit][this][upgrade][socket] - ILibDuktape_http_server_PUSH_IncomingMessage(ctx, header, NULL); // [emit][this][upgrade][socket][msg] - duk_swap_top(ctx, -2); // [emit][this][upgrade][msg][socket] - duk_push_null(ctx); // [emit][this][upgrade][msg][socket][head] - if (duk_pcall_method(ctx, 4) != 0) // [retVal] - { - ILibDuktape_Process_UncaughtException(ctx); - } - duk_pop(ctx); // ... - - *beginPointer = endPointer; - break; - case ILibWebClient_ReceiveStatus_MoreDataToBeReceived: - if (ptrs->socket_ptr != NULL && ptrs->stream != NULL) - { - ILibDuktape_DuplexStream_WriteData(ptrs->stream, bodyBuffer, endPointer); - } - *beginPointer = endPointer; - break; - default: - // ToDo: See if we need to handle Partial/LastPartial - break; - } - } - if (recvStatus == ILibWebClient_ReceiveStatus_Complete) - { - if (ptrs->socket_ptr != NULL && ptrs->stream != NULL) - { - ILibDuktape_DuplexStream_WriteEnd(ptrs->stream); - } - duk_push_heapptr(ctx, ptrs->clientRequest_ptr); //[clientRequest] - if (header == NULL && ptrs->stream == NULL) - { - duk_get_prop_string(ctx, -1, "emit"); //[clientRequest][emit] - duk_dup(ctx, -2); //[clientRequest][emit][this] - duk_push_string(ctx, "error"); //[clientRequest][emit][this][error] - duk_push_string(ctx, "WebSocket Connection Error"); //[clientRequest][emit][this][error][msg] - if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "Error: http.clientRequet.onError()"); } - duk_pop(ctx); //[clientRequest] - } - duk_del_prop_string(ctx, -1, HTTP_REQUEST_TOKEN_PTR); - duk_del_prop_string(ctx, -1, HTTP_REQUEST_USER_PTR); - duk_pop(ctx); - - if (ptrs->socket_ptr != NULL) - { - duk_push_heapptr(ctx, ptrs->socket_ptr); //[socket] - duk_del_prop_string(ctx, -1, HTTP_SOCKET_PTRS); - ILibDuktape_EventEmitter_RemoveAll(ILibDuktape_EventEmitter_GetEmitter(ctx, -1)); - duk_pop(ctx); - } - if (ptrs->clientRequest_ptr != NULL) - { - duk_push_heapptr(ctx, ptrs->clientRequest_ptr); - ILibDuktape_EventEmitter_RemoveAll(ILibDuktape_EventEmitter_GetEmitter(ctx, -1)); - duk_pop(ctx); - } - user[1] = NULL; - } -} -void ILibDuktape_http_request_OnSendOK(ILibWebClient_StateObject sender, void *user1, void *user2) -{ - //duk_context *ctx = (duk_context*)user1; - ILibDuktape_http_requestClient_callbacks *ptrs = (ILibDuktape_http_requestClient_callbacks*)user2; - - if (ptrs->requestStream != NULL) - { - ILibDuktape_WritableStream_Ready(ptrs->requestStream); - } -} - -#ifndef MICROSTACK_NOTLS -int ILibDuktape_http_request_tls_verify(ILibWebClient_RequestToken sender, int preverify_ok, STACK_OF(X509) *certs, struct sockaddr_in6 *address) -{ - int i; - char addr[512]; - int retVal = 0; - void **user = (void**)ILibWebClient_RequestToken_GetUserObjects(sender); - - duk_context *ctx = (duk_context*)user[0]; - void *clientRequest = NULL; - void *checkServerIdentity = NULL; - int rejectUnauthorised = 0; - - if(((ILibDuktape_http_request_dataType*)user[1])->STRUCT_TYPE == ILibDuktape_http_request_dataType_request) - { - ILibDuktape_http_requestClient_callbacks* ptrs = (ILibDuktape_http_requestClient_callbacks*)user[1]; - clientRequest = ptrs->clientRequest; - checkServerIdentity = ptrs->checkServerIdentity; - rejectUnauthorised = ptrs->rejectUnauthorized; - } - else - { - ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user[1]; - clientRequest = ptrs->clientRequest_ptr; - checkServerIdentity = ptrs->checkServerIdentity; - rejectUnauthorised = ptrs->rejectUnauthorized; - } - - if (rejectUnauthorised != 0 && preverify_ok == 0) { return 0; } - if (checkServerIdentity != NULL) - { - duk_push_heapptr(ctx, checkServerIdentity); // [func] - duk_push_heapptr(ctx, clientRequest); // [func][this] - ILibInet_ntop2((struct sockaddr*)address, addr, sizeof(addr)); - duk_push_string(ctx, addr); // [func][this][server] - duk_push_array(ctx); // [func][this][server][certs] - for (i = 0; i < sk_X509_num(certs); ++i) - { - ILibDuktape_X509_PUSH(ctx, sk_X509_value(certs, i)); // [func][this][server][certs][cert] - duk_put_prop_index(ctx, -2, i); // [func][this][server][certs] - } - - if (duk_pcall_method(ctx, 2) == 0) { retVal = 1; } // [retVal] - duk_pop(ctx); // ... - } - else - { - retVal = 1; - } - return retVal; -} -#endif - -void ILibDuktape_http_webSocket_onSendOk(ILibWebClient_StateObject sender, void *user1, void *user2) -{ - ILibDuktape_WebSocket_Pointers *ptrs = (ILibDuktape_WebSocket_Pointers*)user2; - if (ptrs != NULL && ptrs->ctx != NULL && ptrs->stream != NULL) - { - ILibDuktape_DuplexStream_Ready(ptrs->stream); - } -} -void ILibDuktape_http_request_idleTimeout(ILibAsyncSocket_SocketModule sender, void *user) -{ - ILibWebClient_RequestToken token = ILibWebClient_GetRequestToken_FromStateObject(user); - void ** u = ILibWebClient_RequestToken_GetUserObjects(token); - - if (u[1] != NULL && ((ILibDuktape_http_request_dataType*)u[1])->STRUCT_TYPE == ILibDuktape_http_request_dataType_request && - ((ILibDuktape_http_requestClient_callbacks*)u[1])->clientRequest != NULL) - { - duk_context *ctx = (duk_context*)u[0]; - ILibDuktape_http_requestClient_callbacks *cb = (ILibDuktape_http_requestClient_callbacks*)u[1]; - duk_push_heapptr(ctx, cb->clientRequest); // [clientRequest] - duk_get_prop_string(ctx, -1, "emit"); // [clientRequest][emit] - duk_swap_top(ctx, -2); // [emit][this] - duk_push_string(ctx, "timeout"); // [emit][this][timeout] - if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.clientRequest.onTimeout(): "); } - duk_pop(ctx); - } -} -duk_ret_t ILibDuktape_http_request(duk_context *ctx) -{ - union { int i; void*p; }u; - ILibHTTPPacket *packet; - char *host; - duk_size_t hostLen; - char *key; - int nargs = duk_get_top(ctx); - if (nargs < 1) { duk_push_string(ctx, "Too Few Arguments"); duk_throw(ctx); return(DUK_RET_ERROR); } - int isWebSocket = 0; - ILibWebClient_RequestManager wcm; - struct sockaddr_in6 dest; - ILibWebClient_RequestToken token; - ILibDuktape_globalTunnel_data *globalTunnel = ILibDuktape_GetGlobalTunnel(ctx); - char *path, *method; - duk_size_t pathLen, methodLen; - - char *proto = Duktape_GetStringPropertyValue(ctx, 0, "protocol", ILibDuktape_http_getDefaultProto(ctx)); -#ifndef MICROSTACK_NOTLS - ILibWebClient_RequestToken_HTTPS protocol = (strncmp(proto, "https:", 6) == 0 || strncmp(proto, "wss:", 4) == 0) ? ILibWebClient_RequestToken_USE_HTTPS : ILibWebClient_RequestToken_USE_HTTP; -#else - ILibWebClient_RequestToken_HTTPS protocol = ILibWebClient_RequestToken_USE_HTTP; -#endif - - if (strncmp(proto, "wss:", 4) == 0 || strncmp(proto, "ws:", 3) == 0) { isWebSocket = 1; } - - duk_push_this(ctx); - if (duk_has_prop_string(ctx, -1, HTTP_WEBCLIENT_MGR)) - { - duk_get_prop_string(ctx, -1, HTTP_WEBCLIENT_MGR); - wcm = (ILibWebClient_RequestManager)duk_to_pointer(ctx, -1); - if (protocol == ILibWebClient_RequestToken_USE_HTTPS) - { - ILibWebClient_EnableHTTPS(wcm, NULL, NULL, ILibDuktape_http_request_tls_verify); - } - } - else - { - duk_get_prop_string(ctx, -1, "chain"); // [http][chain] - duk_get_prop_string(ctx, -2, "RequestPoolSize"); // [http][chain][poolSize] - wcm = ILibCreateWebClient(duk_to_int(ctx, -1), duk_to_pointer(ctx, -2)); - duk_pop_2(ctx); // [http] - -#ifndef MICROSTACK_NOTLS - { - char *pfx; - duk_size_t pfxLen; - char *passphrase = Duktape_GetStringPropertyValue(ctx, 0, "passphrase", ""); - - if (duk_has_prop_string(ctx, 0, "pfx") && protocol == ILibWebClient_RequestToken_USE_HTTPS) - { - struct util_cert cert; - - duk_get_prop_string(ctx, 0, "pfx"); // [http][pfx] - pfx = Duktape_GetBuffer(ctx, -1, &pfxLen); - duk_pop(ctx); // [http] - if (util_from_p12(pfx, (int)pfxLen, passphrase, &cert) != 0) - { - duk_push_pointer(ctx, cert.pkey); // [http][pkey] - duk_put_prop_string(ctx, -2, "\xFF_pkey"); // [http] - duk_push_pointer(ctx, cert.x509); // [http][x509] - duk_put_prop_string(ctx, -2, "\xFF_x509"); // [http] - - ILibWebClient_EnableHTTPS(wcm, &cert, NULL, ILibDuktape_http_request_tls_verify); - } - } - else if (duk_has_prop_string(ctx, 0, "MeshAgent") && protocol == ILibWebClient_RequestToken_USE_HTTPS) - { - duk_get_prop_string(ctx, 0, "MeshAgent"); // [http][MeshAgent] - duk_get_prop_string(ctx, -1, ILibDuktape_MeshAgent_Cert_Client); // [http][MeshAgent][clientCert] - duk_get_prop_string(ctx, -2, ILibDuktape_MeshAgent_Cert_NonLeaf); // [http][MeshAgent][clientCert][nonLeafCert] - ILibWebClient_EnableHTTPS(wcm, (struct util_cert*)duk_get_pointer(ctx, -2), ((struct util_cert*)duk_get_pointer(ctx, -1))->x509, ILibDuktape_http_request_tls_verify); - duk_pop_3(ctx); // [http] - } - else if (protocol == ILibWebClient_RequestToken_USE_HTTPS) - { - ILibWebClient_EnableHTTPS(wcm, NULL, NULL, ILibDuktape_http_request_tls_verify); - } - } -#endif - - - duk_push_pointer(ctx, wcm); // [http][wcm] - duk_put_prop_string(ctx, -2, HTTP_WEBCLIENT_MGR); // [http] - } - - if (duk_has_prop_string(ctx, 0, "hostname")) - { - host = Duktape_GetStringPropertyValueEx(ctx, 0, "hostname", "127.0.0.1", &hostLen); - } - else - { - host = Duktape_GetStringPropertyValueEx(ctx, 0, "host", "127.0.0.1", &hostLen); - } - - - if (duk_has_prop_string(ctx, 0, "proxy")) - { - duk_get_prop_string(ctx, 0, "proxy"); - globalTunnel = (ILibDuktape_globalTunnel_data*)ILibScratchPad; - memset(globalTunnel, 0, sizeof(ILibDuktape_globalTunnel_data)); - ILibResolveEx(Duktape_GetStringPropertyValueEx(ctx, -1, "host", "127.0.0.1", NULL), (unsigned short)Duktape_GetIntPropertyValue(ctx, -1, "port", 8080), &(globalTunnel->proxyServer)); - if (globalTunnel->proxyServer.sin6_family == AF_UNSPEC) { return(ILibDuktape_Error(ctx, "http.get(): Cannot resolve proxy host %s", Duktape_GetStringPropertyValueEx(ctx, -1, "host", "127.0.0.1", NULL))); } - } - else if (duk_has_prop_string(ctx, 0, "noProxy")) { globalTunnel = NULL; } - - - packet = ILibCreateEmptyPacket(); - ILibSetVersion(packet, "1.1", 3); - method = Duktape_GetStringPropertyValueEx(ctx, 0, "method", "GET", &methodLen); - path = Duktape_GetStringPropertyValueEx(ctx, 0, "path", "/", &pathLen); - ILibSetDirective(packet, method, (int)methodLen, path, (int)pathLen); - - if (isWebSocket != 0) - { - int len; - char value[32]; - char nonce[16]; - char *enc = value; - util_random(16, nonce); - len = ILibBase64Encode((unsigned char*)nonce, 16, (unsigned char**)&enc); - enc[len] = 0; - u.i = Duktape_GetIntPropertyValue(ctx, 0, "webSocketBufferSize", 65535); - - ILibAddHeaderLine(packet, "Upgrade", -1, "websocket", -1); - ILibAddHeaderLine(packet, "Connection", -1, "Upgrade", -1); - ILibAddHeaderLine(packet, "Sec-WebSocket-Key", -1, enc, -1); - ILibAddHeaderLine(packet, "Sec-WebSocket-Version", -1, "13", -1); - ILibHTTPPacket_Stash_Put(packet, "_WebSocketBufferSize", -1, u.p); - ILibHTTPPacket_Stash_Put(packet, "_WebSocketOnSendOK", -1, ILibDuktape_http_webSocket_onSendOk); - } - - u.i = Duktape_GetIntPropertyValue(ctx, 0, "timeout", 0); - if (u.i > 0) - { - ILibHTTPPacket_Stash_Put(packet, "_idleTimeout", -1, u.p); - ILibHTTPPacket_Stash_Put(packet, "_idleTimeoutHandler", -1, ILibDuktape_http_request_idleTimeout); - } - - if (duk_has_prop_string(ctx, 0, "headers")) - { - duk_get_prop_string(ctx, 0, "headers"); - duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); - while (duk_next(ctx, -1, 1)) - { - key = (char*)duk_to_string(ctx, -2); - ILibAddHeaderLine(packet, key, -1, (char*)duk_to_string(ctx, -1), -1); - duk_pop_2(ctx); - } - } - - if (ILibGetHeaderLine(packet, "host", 4) == NULL) - { - ILibAddHeaderLine(packet, "host", 4, host, (int)hostLen); - } - - memset(&dest, 0, sizeof(struct sockaddr_in6)); - ILibResolveEx(host, (unsigned short)Duktape_GetIntPropertyValue(ctx, 0, "port", protocol == ILibWebClient_RequestToken_USE_HTTP ? 80 : 443), &dest); - if (dest.sin6_family == AF_UNSPEC) - { - duk_push_string(ctx, host); - duk_throw(ctx); - return(DUK_RET_ERROR); - } - - if (isWebSocket == 0) - { - // Make PipelineStreamedRequest - token = ILibWebClient_PipelineStreamedRequest(wcm, (struct sockaddr*)&dest, packet, ILibDuktape_http_request_OnResponse, ILibDuktape_http_request_OnSendOK, ctx, NULL); - if (globalTunnel != NULL) - { - if (ILibHashtable_Get(globalTunnel->exceptionsTable, NULL, host, (int)hostLen) == NULL) - { - ILibWebClient_SetProxyEx(token, &(globalTunnel->proxyServer), (char*)globalTunnel->proxyUser, (char*)globalTunnel->proxyPass); - } - } -#ifndef MICROSTACK_NOTLS - ILibWebClient_Request_SetHTTPS(token, protocol); - ILibDuktape_http_requestClient_callbacks *cb = (ILibDuktape_http_requestClient_callbacks*)ILibDuktape_http_request_PUSH_clientRequest(ctx, token, 0); - cb->rejectUnauthorized = Duktape_GetIntPropertyValue(ctx, 0, "rejectUnauthorized", 0); - cb->checkServerIdentity = Duktape_GetHeapptrProperty(ctx, 0, "checkServerIdentity"); -#else - ILibDuktape_http_request_PUSH_clientRequest(ctx, token, 0); -#endif - - } - else - { - token = ILibWebClient_PipelineRequest(wcm, (struct sockaddr*)&dest, packet, ILibDuktape_http_request_WebSocket_OnResponse, ctx, NULL); -#ifndef MICROSTACK_NOTLS - ILibWebClient_Request_SetHTTPS(token, protocol); -#endif - if (globalTunnel != NULL) - { - if (ILibHashtable_Get(globalTunnel->exceptionsTable, NULL, host, (int)hostLen) == NULL) - { - ILibWebClient_SetProxyEx(token, &(globalTunnel->proxyServer), (char*)globalTunnel->proxyUser, (char*)globalTunnel->proxyPass); - } - } - -#ifndef MICROSTACK_NOTLS - ILibDuktape_WebSocket_Pointers *ptrs = ILibDuktape_http_request_PUSH_clientRequest(ctx, token, 1); - ptrs->rejectUnauthorized = Duktape_GetIntPropertyValue(ctx, 0, "rejectUnauthorized", 0); - ptrs->checkServerIdentity = Duktape_GetHeapptrProperty(ctx, 0, "checkServerIdentity"); -#else - ILibDuktape_http_request_PUSH_clientRequest(ctx, token, 1); -#endif - } - duk_dup(ctx, 0); - duk_put_prop_string(ctx, -2, HTTP_CLIENTREQUEST_PARAMETER); - duk_push_this(ctx); - duk_put_prop_string(ctx, -2, CLIENTREQUEST_HTTP); - - if (nargs > 1) { ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), "response", duk_require_heapptr(ctx, 1)); } - return 1; -} - -ILibTransport_DoneState ILibDuktape_http_request_write(ILibDuktape_WritableStream *stream, char *buffer, int bufferLen, void *user) -{ - ILibWebClient_RequestToken token = (ILibWebClient_RequestToken)user; - ILibTransport_DoneState retVal = ILibTransport_DoneState_COMPLETE; - - if (bufferLen > 0) { retVal = ILibWebClient_StreamRequestBody(token, buffer, bufferLen, ILibAsyncSocket_MemoryOwnership_USER, ILibTransport_DoneState_INCOMPLETE); } - return retVal; -} -void ILibDuktape_http_request_end(ILibDuktape_WritableStream *stream, void *user) -{ - ILibWebClient_RequestToken token = (ILibWebClient_RequestToken)user; - ILibWebClient_StreamRequestBody(token, NULL, 0, ILibAsyncSocket_MemoryOwnership_USER, ILibTransport_DoneState_COMPLETE); -} - -duk_ret_t ILibDuktape_http_clientRequest_upgrade_setter(duk_context *ctx) -{ - duk_push_this(ctx); // [clientRequest] - duk_dup(ctx, 0); // [clientRequest][upgrade] - duk_put_prop_string(ctx, -2, "\xFF_Upgrade"); // [clientRequest] - - return 0; -} -duk_ret_t ILibDuktape_http_request_finalizer(duk_context *ctx) -{ - ILibWebClient_RequestToken token; - void **user; - - if (duk_has_prop_string(ctx, 0, HTTP_REQUEST_USER_PTR)) - { - duk_get_prop_string(ctx, 0, HTTP_REQUEST_USER_PTR); - user = (void**)duk_get_pointer(ctx, -1); - user[1] = NULL; - } - - - if (duk_has_prop_string(ctx, 0, HTTP_CLIENTREQUEST_DATAPTR)) - { - duk_get_prop_string(ctx, 0, HTTP_CLIENTREQUEST_DATAPTR); - duk_size_t bufLen; - char *buf = (char*)Duktape_GetBuffer(ctx, -1, &bufLen); - memset(buf, 0, bufLen); - } - if (duk_has_prop_string(ctx, 0, HTTP_REQUEST_TOKEN_PTR)) - { - duk_get_prop_string(ctx, 0, HTTP_REQUEST_TOKEN_PTR); - token = duk_get_pointer(ctx, -1); - - if (token != NULL) - { - user = (void**)ILibWebClient_RequestToken_GetUserObjects(token); - if (user != NULL) - { - user[0] = NULL; - user[1] = NULL; - } - ILibWebClient_CancelRequest(token); - } - } - - return 0; -} -duk_ret_t ILibDuktape_http_request_no_op(duk_context *ctx) -{ - return 0; -} -void ILibDuktape_http_request_connect(ILibWebClient_RequestToken token) -{ - ILibWebClient_StateObject wcdo = ILibWebClient_GetStateObjectFromRequestToken(token); - void **user = ILibWebClient_RequestToken_GetUserObjects(token); - duk_context *ctx = (duk_context*)user[0]; - ILibDuktape_http_requestClient_callbacks *ptr = (ILibDuktape_http_requestClient_callbacks*)user[1]; - if (ctx != NULL && ptr != NULL && ptr->OnSocket != NULL) - { - duk_push_object(ctx); // [socket] - duk_push_pointer(ctx, wcdo); // [socket][wcdo] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_SOCKET_WCDO); // [socket] - duk_push_heapptr(ctx, ptr->clientRequest); // [socket][clientRequest] - duk_dup(ctx, -2); // [socket][clientRequest][socket] - ILibDuktape_CreateReadonlyProperty(ctx, "socket"); // [socket][clientRequest] - duk_pop(ctx); // [socket] - - duk_push_heapptr(ctx, ptr->clientRequest); // [socket][clientRequest] - ILibDuktape_Push_ObjectStash(ctx); // [socket][clientRequest][stash] - duk_dup(ctx, -3); // [socket][clientRequest][stash][socket] - duk_put_prop_string(ctx, -2, Duktape_GetStashKey(wcdo)); // [socket][clientRequest][stash] - duk_pop_2(ctx); // [socket] - - ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx); - ILibDuktape_EventEmitter_CreateEventEx(emitter, "end"); - duk_push_heapptr(ctx, ptr->OnSocket); // [socket][OnSocket] - duk_push_heapptr(ctx, ptr->clientRequest); // [socket][OnSocket][this] - duk_dup(ctx, -3); // [socket][OnSocket][this][socket] - if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.request_connect.onSocket(): "); } - duk_pop_2(ctx); // ... - } -} -void ILibDuktape_http_request_disconnect(ILibWebClient_RequestToken token) -{ - ILibWebClient_StateObject wcdo = ILibWebClient_GetStateObjectFromRequestToken(token); - char *key = Duktape_GetStashKey(wcdo); - void **user = ILibWebClient_RequestToken_GetUserObjects(token); - if (user == NULL) { return; } - duk_context *ctx = (duk_context*)user[0]; - ILibDuktape_http_requestClient_callbacks *ptr = (ILibDuktape_http_requestClient_callbacks*)user[1]; - if (ctx != NULL && ptr != NULL) - { - duk_push_heapptr(ctx, ptr->clientRequest); // [clientRequest] - ILibDuktape_Push_ObjectStash(ctx); // [clientRequest][stash] - if (duk_has_prop_string(ctx, -1, key)) - { - duk_get_prop_string(ctx, -1, key); // [clientRequest][stash][socket] - duk_get_prop_string(ctx, -1, "emit"); // [clientRequest][stash][socket][emit] - duk_swap_top(ctx, -2); // [clientRequest][stash][emit][this] - duk_push_string(ctx, "end"); // [clientRequest][stash][emit][this][end] - if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "http.request_disconnect(): "); } - duk_pop(ctx); // [clientRequest][stash] - duk_del_prop_string(ctx, -2, key); - } - duk_pop_2(ctx); // ... - } -} -void* ILibDuktape_http_request_PUSH_clientRequest(duk_context *ctx, ILibWebClient_RequestToken token, int isWebSocket) -{ - ILibDuktape_EventEmitter *emitter = NULL; - void **user = ILibWebClient_RequestToken_GetUserObjects(token); - - if (user[1] != NULL && ((ILibDuktape_http_request_dataType*)user[1])->STRUCT_TYPE == ILibDuktape_http_request_dataType_request && - ((ILibDuktape_http_requestClient_callbacks*)user[1])->clientRequest != NULL) - { - duk_push_heapptr(ctx, ((ILibDuktape_http_requestClient_callbacks*)user[1])->clientRequest); - return(user[1]); - } - - if (isWebSocket == 0) - { - ILibWebClient_RequestToken_ConnectionHandler_Set(token, ILibDuktape_http_request_connect, ILibDuktape_http_request_disconnect); - } - - duk_push_object(ctx); // [obj] - duk_push_pointer(ctx, user); - duk_put_prop_string(ctx, -2, HTTP_REQUEST_USER_PTR); - - duk_push_pointer(ctx, token); // [obj][token] - duk_put_prop_string(ctx, -2, HTTP_REQUEST_TOKEN_PTR); // [obj] - duk_push_fixed_buffer(ctx, isWebSocket == 0 ? sizeof(ILibDuktape_http_requestClient_callbacks) : sizeof(ILibDuktape_WebSocket_Pointers)); - user[1] = Duktape_GetBuffer(ctx, -1, NULL); - ((ILibDuktape_http_request_dataType*)user[1])->STRUCT_TYPE = isWebSocket == 0 ? ILibDuktape_http_request_dataType_request : ILibDuktape_http_request_dataType_webSocket; - duk_put_prop_string(ctx, -2, HTTP_CLIENTREQUEST_DATAPTR); - - emitter = ILibDuktape_EventEmitter_Create(ctx); - ILibDuktape_EventEmitter_CreateEventEx(emitter, "upgrade"); - ILibDuktape_EventEmitter_CreateEventEx(emitter, "error"); - - ILibDuktape_CreateFinalizer(ctx, ILibDuktape_http_request_finalizer); - - if (((ILibDuktape_http_request_dataType*)user[1])->STRUCT_TYPE == ILibDuktape_http_request_dataType_request) - { - ILibDuktape_EventEmitter_CreateEvent(emitter, "response", &(((ILibDuktape_http_requestClient_callbacks*)user[1])->OnReceive)); - ILibDuktape_EventEmitter_CreateEvent(emitter, "continue", &(((ILibDuktape_http_requestClient_callbacks*)user[1])->OnContinue)); - ILibDuktape_EventEmitter_CreateEvent(emitter, "socket", &(((ILibDuktape_http_requestClient_callbacks*)user[1])->OnSocket)); - - ILibDuktape_EventEmitter_CreateEventEx(emitter, "timeout"); - ((ILibDuktape_http_requestClient_callbacks*)user[1])->requestStream = ILibDuktape_WritableStream_Init(ctx, ILibDuktape_http_request_write, ILibDuktape_http_request_end, token); - ((ILibDuktape_http_requestClient_callbacks*)user[1])->clientRequest = duk_get_heapptr(ctx, -1); - } - else - { - ILibDuktape_EventEmitter_CreateEventEx(emitter, "response"); - ((ILibDuktape_WebSocket_Pointers*)user[1])->ctx = ctx; - ((ILibDuktape_WebSocket_Pointers*)user[1])->clientRequest_ptr = duk_get_heapptr(ctx, -1); - ILibDuktape_CreateInstanceMethod(ctx, "end", ILibDuktape_http_request_no_op, 0); - } - - return(user[1]); -} - -duk_ret_t ILibDuktape_http_get(duk_context *ctx) -{ - int nargs = duk_get_top(ctx); - ILibWebClient_RequestToken token; - char *host; - unsigned short port; - char *path; - struct sockaddr_in6 dest; - duk_size_t uriLen; - char *uri; - int hostLen; - ILibDuktape_globalTunnel_data *globalTunnel = ILibDuktape_GetGlobalTunnel(ctx); - - if (duk_is_string(ctx, 0)) - { - uri = (char*)duk_get_lstring(ctx, 0, &uriLen); - } - else if (duk_is_object(ctx, 0)) - { - uri = Duktape_GetStringPropertyValueEx(ctx, 0, "uri", "http://127.0.0.1/", &uriLen); - if (duk_has_prop_string(ctx, 0, "proxy")) - { - duk_get_prop_string(ctx, 0, "proxy"); - globalTunnel = (ILibDuktape_globalTunnel_data*)ILibScratchPad; - memset(globalTunnel, 0, sizeof(ILibDuktape_globalTunnel_data)); - ILibResolveEx(Duktape_GetStringPropertyValueEx(ctx, -1, "host", "127.0.0.1", NULL), (unsigned short)Duktape_GetIntPropertyValue(ctx, -1, "port", 8080), &(globalTunnel->proxyServer)); - if (globalTunnel->proxyServer.sin6_family == AF_UNSPEC) { return(ILibDuktape_Error(ctx, "http.get(): Cannot resolve proxy host %s", Duktape_GetStringPropertyValueEx(ctx, -1, "host", "127.0.0.1", NULL))); } - } - else if (duk_has_prop_string(ctx, 0, "noProxy")) { globalTunnel = NULL; } - } - else - { - return(ILibDuktape_Error(ctx, "http.get(): Invalid parameter")); - } - - - ILibHTTPPacket *packet = ILibCreateEmptyPacket(); - -#ifndef MICROSTACK_NOTLS - ILibWebClient_RequestManager manager = ILibDuktape_http_GetRequestManager(ctx); - ILibParseUriResult result = ILibParseUri(uri, &host, &port, &path, &dest); - ILibWebClient_EnableHTTPS(manager, NULL, NULL, ILibDuktape_http_request_tls_verify); -#else - ILibParseUri(uri, &host, &port, &path, &dest); -#endif - - ILibSetVersion(packet, "1.1", 3); - - if (dest.sin6_family == AF_UNSPEC) - { - ILibDestructPacket(packet); - free(host); free(path); - duk_push_string(ctx, "Could not resolve URI"); - duk_throw(ctx); - return(DUK_RET_ERROR); - } - - hostLen = (int)strnlen_s(host, uriLen); - ILibSetDirective(packet, "GET", 3, path, (int)strnlen_s(path, uriLen)); - ILibAddHeaderLine(packet, "Host", 4, host, hostLen); - if (duk_is_object(ctx, 0) && duk_has_prop_string(ctx, 0, "headers")) - { - duk_get_prop_string(ctx, 0, "headers"); - duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); - while (duk_next(ctx, -1, 1)) - { - duk_size_t keyLen, valueLen; - char *key = (char*)duk_to_lstring(ctx, -2, &keyLen), *value = (char*)duk_to_lstring(ctx, -1, &valueLen); - - ILibAddHeaderLine(packet, key, (int)keyLen, value, (int)valueLen); - duk_pop_2(ctx); - } - } - ILibAddHeaderLine(packet, "Content-Length", 14, "0", 1); - - token = ILibWebClient_PipelineRequest(ILibDuktape_http_GetRequestManager(ctx), (struct sockaddr*)&dest, packet, ILibDuktape_http_request_OnResponse, ctx, NULL); - -#ifndef MICROSTACK_NOTLS - ILibWebClient_Request_SetHTTPS(token, result == ILibParseUriResult_TLS ? ILibWebClient_RequestToken_USE_HTTPS : ILibWebClient_RequestToken_USE_HTTP); -#endif - - if (globalTunnel != NULL) - { - if (ILibHashtable_Get(globalTunnel->exceptionsTable, NULL, host, hostLen) == NULL) - { - ILibWebClient_SetProxyEx(token, &(globalTunnel->proxyServer), (char*)globalTunnel->proxyUser, (char*)globalTunnel->proxyPass); - } - } - - free(path); free(host); - ILibDuktape_http_request_PUSH_clientRequest(ctx, token, 0); // [clientRequest] - duk_dup(ctx, 0); // [clientRequest][param] - duk_put_prop_string(ctx, -2, HTTP_CLIENTREQUEST_PARAMETER); // [clientRequest] - duk_push_this(ctx); // [clientRequest][http] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_HTTP); // [clientRequest] - if (nargs > 1) - { - ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter(ctx, -1), "response", duk_get_heapptr(ctx, 1)); - } - - return 1; -} -duk_ret_t ILibDuktape_http_finalizer(duk_context *ctx) -{ - ILibWebClient_RequestManager wcm; - - if (duk_has_prop_string(ctx, 0, HTTP_WEBCLIENT_MGR)) - { - duk_get_prop_string(ctx, 0, HTTP_WEBCLIENT_MGR); - wcm = (ILibWebClient_RequestManager)duk_get_pointer(ctx, -1); - ILibChain_SafeRemove(((ILibChain_Link*)wcm)->ParentChain, wcm); - } - return 0; -} -duk_ret_t ILibDuktape_http_parseUri(duk_context *ctx) -{ - duk_size_t uriLen; - char *uri; - if (!duk_is_string(ctx, 0)) { return(ILibDuktape_Error(ctx, "http.parseUri(): Invalid Parameters")); } - - char *path, *addr; - unsigned short port; - int protocolIndex; - - uri = (char*)duk_get_lstring(ctx, 0, &uriLen); - protocolIndex = 1 + ILibString_IndexOf(uri, (int)uriLen, "://", 3); - if (protocolIndex > 0) - { - ILibParseUriEx(uri, (size_t)uriLen, &addr, &port, &path, NULL); - - duk_push_object(ctx); // [options] - duk_push_lstring(ctx, uri, protocolIndex); // [options][protocol] - duk_put_prop_string(ctx, -2, "protocol"); - duk_push_string(ctx, addr); // [options][host] - duk_put_prop_string(ctx, -2, "host"); - duk_push_int(ctx, port); // [options][port] - duk_put_prop_string(ctx, -2, "port"); // [options] - duk_push_string(ctx, path); // [options][path] - duk_put_prop_string(ctx, -2, "path"); // [options] - duk_push_string(ctx, "GET"); // [options][method] - duk_put_prop_string(ctx, -2, "method"); // [options] - - free(path); - free(addr); - } - else - { - duk_push_null(ctx); - } - return 1; -} - -void ILibDuktape_http_stream_ServerResponse_sendHeaders(ILibDuktape_http_streamWrapper *wrapper, ILibHTTPPacket *headers) -{ - char *data; - int dataLen = ILibGetRawPacket(headers, &data); - - if (wrapper->PipedWriter != NULL) - { - ((ILibDuktape_WritableStream*)wrapper->PipedWriter)->WriteSink(((ILibDuktape_WritableStream*)wrapper->PipedWriter), data, dataLen, ((ILibDuktape_WritableStream*)wrapper->PipedWriter)->WriteSink_User); - } - else - { - duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream] - duk_get_prop_string(wrapper->ctx, -1, "write"); // [stream][func] - duk_swap_top(wrapper->ctx, -2); // [func][this] - duk_push_external_buffer(wrapper->ctx); // [func][this][chunk] - duk_config_buffer(wrapper->ctx, -1, data, dataLen); - if (duk_pcall_method(wrapper->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onWriteHead(): Error "); } - duk_pop(wrapper->ctx); // ... - } - - free(data); -} - -duk_ret_t ILibDuktape_http_stream_ServerResponse_writeHead(duk_context *ctx) -{ - int nargs = duk_get_top(ctx); - ILibHTTPPacket *headers; - ILibDuktape_http_streamWrapper *wrapper; - - int statusCode = 200; - char *statusData = "OK"; - duk_size_t statusDataLen = 2; - - duk_push_this(ctx); // [response] - duk_get_prop_string(ctx, -1, HTTP_STREAM_WRAPPER); // [response][wrapper] - wrapper = (ILibDuktape_http_streamWrapper*)duk_get_pointer(ctx, -1); - headers = wrapper->impliedHeaders; - wrapper->impliedHeaders = NULL; - - statusCode = duk_require_int(ctx, 0); - if (nargs > 1 && duk_is_string(ctx, 1)) - { - statusData = (char*)duk_get_lstring(ctx, 1, &statusDataLen); - } - else - { - switch (statusCode) - { - case 100: - statusData = "Continue"; - statusDataLen = 8; - break; - case 200: - statusData = "OK"; - statusDataLen = 2; - break; - case 400: - statusData = "Bad Request"; - statusDataLen = 11; - break; - case 401: - statusData = "Unauthorized"; - statusDataLen = 12; - break; - case 404: - statusData = "Not Found"; - statusDataLen = 9; - break; - case 500: - statusData = "Internal Server Error"; - statusDataLen = 21; - break; - default: - statusData = "Unspecified"; - statusDataLen = 11; - break; - } - } - ILibSetStatusCode(headers, statusCode, statusData, (int)statusDataLen); - - ILibDuktape_http_stream_ServerResponse_sendHeaders(wrapper, headers); - ILibDestructPacket(headers); - return 0; -} -void ILibDuktape_http_stream_serverResponse_WriteSinkFlushedNative(struct ILibDuktape_WritableStream *stream, void *user) -{ - ILibDuktape_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)user; - ILibDuktape_WritableStream_Ready(wrapper->serverResponse_stream); -} -duk_ret_t ILibDuktape_http_stream_serverResponse_WriteSinkFlushed(duk_context *ctx) -{ - duk_push_current_function(ctx); // [func] - duk_get_prop_string(ctx, -1, "\xFF_USER"); - ILibDuktape_http_stream_serverResponse_WriteSinkFlushedNative(NULL, duk_get_pointer(ctx, -1)); - return 0; -} -ILibTransport_DoneState ILibDuktape_http_stream_serverResponse_WriteSink(struct ILibDuktape_WritableStream *stream, char *buffer, int bufferLen, void *user) -{ - ILibTransport_DoneState retVal = ILibTransport_DoneState_ERROR; - ILibDuktape_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)user; - if (wrapper->impliedHeaders != NULL) - { - // Need to send out headers first - ILibDuktape_http_stream_ServerResponse_sendHeaders(wrapper, wrapper->impliedHeaders); - ILibDestructPacket(wrapper->impliedHeaders); - wrapper->impliedHeaders = NULL; - } - - if (wrapper->PipedWriter != NULL) - { - ILibDuktape_WritableStream *ws = (ILibDuktape_WritableStream*)wrapper->PipedWriter; - ws->OnWriteFlushEx = ILibDuktape_http_stream_serverResponse_WriteSinkFlushedNative; - ws->OnWriteFlushEx_User = wrapper; - if (wrapper->chunkEncoded == 0) - { - return(ws->WriteSink(ws, buffer, bufferLen, ws->WriteSink_User)); - } - else - { - int hexLen = sprintf_s(wrapper->hex, sizeof(wrapper->hex), "%X\r\n", bufferLen); - ws->WriteSink(ws, wrapper->hex, hexLen, ws->WriteSink_User); - ws->WriteSink(ws, buffer, bufferLen, ws->WriteSink_User); - return(ws->WriteSink(ws, "\r\n", 2, ws->WriteSink_User)); - } - } - else - { - if (wrapper->chunkEncoded != 0) - { - int hexLen = sprintf_s(wrapper->hex, sizeof(wrapper->hex), "%X\r\n", bufferLen); - duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream] - duk_get_prop_string(wrapper->ctx, -1, "write"); // [stream][func] - duk_swap_top(wrapper->ctx, -2); // [func][this] - duk_push_external_buffer(wrapper->ctx); // [func][this][chunk] - duk_config_buffer(wrapper->ctx, -1, wrapper->hex, hexLen); - if (duk_pcall_method(wrapper->ctx, 2) != 0) - { - ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onWrite(): Error "); - retVal = ILibTransport_DoneState_ERROR; - duk_pop(wrapper->ctx); // ... - return retVal; - } - duk_pop(wrapper->ctx); // ... - } - duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream] - duk_get_prop_string(wrapper->ctx, -1, "write"); // [stream][func] - duk_swap_top(wrapper->ctx, -2); // [func][this] - duk_push_external_buffer(wrapper->ctx); // [func][this][chunk] - duk_config_buffer(wrapper->ctx, -1, buffer, bufferLen); - if (duk_pcall_method(wrapper->ctx, 1) != 0) - { - ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onWrite(): Error "); - retVal = ILibTransport_DoneState_ERROR; - duk_pop(wrapper->ctx); - return retVal; - } - else - { - retVal = duk_get_boolean(wrapper->ctx, -1) ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE; - } - duk_pop(wrapper->ctx); // ... - - if (wrapper->chunkEncoded != 0) - { - duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream] - duk_get_prop_string(wrapper->ctx, -1, "write"); // [stream][func] - duk_swap_top(wrapper->ctx, -2); // [func][this] - duk_push_external_buffer(wrapper->ctx); // [func][this][chunk] - duk_config_buffer(wrapper->ctx, -1, "\r\n", 2); - duk_push_c_function(wrapper->ctx, ILibDuktape_http_stream_serverResponse_WriteSinkFlushed, 0); // [func][this][chunk][callback] - duk_push_pointer(wrapper->ctx, wrapper); // [func][this][chunk][callback][ptr] - duk_put_prop_string(wrapper->ctx, -2, "\xFF_USER"); // [func][this][chunk][callback] - if (duk_pcall_method(wrapper->ctx, 2) != 0) - { - ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onWrite(): Error "); - retVal = ILibTransport_DoneState_ERROR; - } - else - { - retVal = duk_get_boolean(wrapper->ctx, -1) ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE; - } - duk_pop(wrapper->ctx); - } - } - return retVal; -} -void ILibDuktape_http_stream_serverResponse_EndSink(struct ILibDuktape_WritableStream *stream, void *user) -{ - ILibDuktape_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)user; - - if (wrapper->chunkEncoded == 0) - { - // No choice but to propogate the End up, becuase we aren't chunked - if (wrapper->PipedWriter != NULL) - { - wrapper->PipedWriter->EndSink(wrapper->PipedWriter, wrapper->PipedWriter->WriteSink_User); - } - else - { - duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream] - duk_get_prop_string(wrapper->ctx, -1, "end"); // [stream][func] - duk_swap_top(wrapper->ctx, -2); // [func][this] - if (duk_pcall_method(wrapper->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onEnd(): Error "); } - duk_pop(wrapper->ctx); // ... - } - } - else - { - // Since we're chunked, we can just write a zero length chunk - if (wrapper->PipedWriter != NULL) - { - wrapper->PipedWriter->WriteSink(wrapper->PipedWriter, "0\r\n\r\n", 5, wrapper->PipedWriter->WriteSink_User); - } - else - { - duk_push_heapptr(wrapper->ctx, wrapper->PipedReader); // [stream] - duk_get_prop_string(wrapper->ctx, -1, "write"); // [stream][func] - duk_swap_top(wrapper->ctx, -2); // [func][this] - duk_push_external_buffer(wrapper->ctx); // [func][this][chunk] - duk_config_buffer(wrapper->ctx, -1, "0\r\n\r\n", 5); - if (duk_pcall_method(wrapper->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.onEnd(): Error "); } - duk_pop(wrapper->ctx); // ... - } - } - ILibWebClient_FinishedResponse_Server(wrapper->wcdo); -} -void ILibduktape_http_stream_PUSH_ServerResponse(duk_context *ctx, ILibDuktape_http_streamWrapper *wrapper) -{ - duk_push_object(ctx); // [response] - wrapper->impliedHeaders = ILibCreateEmptyPacket(); - if (wrapper->chunkEncoded == 0) - { - ILibSetVersion(wrapper->impliedHeaders, "1.0", 3); - } - else - { - ILibSetVersion(wrapper->impliedHeaders, "1.1", 3); - ILibAddHeaderLine(wrapper->impliedHeaders, "Transfer-Encoding", 17, "chunked", 7); - } - - ILibSetStatusCode(wrapper->impliedHeaders, 200, "OK", 2); - duk_push_pointer(ctx, wrapper); // [response][wrapper] - duk_put_prop_string(ctx, -2, HTTP_STREAM_WRAPPER); // [response] - - ILibDuktape_CreateInstanceMethod(ctx, "writeHead", ILibDuktape_http_stream_ServerResponse_writeHead, DUK_VARARGS); - wrapper->serverResponse_stream = ILibDuktape_WritableStream_Init(ctx, ILibDuktape_http_stream_serverResponse_WriteSink, ILibDuktape_http_stream_serverResponse_EndSink, wrapper); -} -void ILibDuktape_http_stream_OnReceive(ILibWebClient_StateObject WebStateObject, int InterruptFlag, struct packetheader *header, char *bodyBuffer, int *beginPointer, int endPointer, ILibWebClient_ReceiveStatus recvStatus, void *user1, void *user2, int *PAUSE) -{ - ILibDuktape_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)user1; - if (!(header->VersionLength == 3 && memcmp(header->Version, "1.0", 3) == 0)) { wrapper->chunkEncoded = 1; } - - if (wrapper->OnRequest != NULL) - { - duk_push_heapptr(wrapper->ctx, wrapper->OnRequest); // [func] - duk_push_heapptr(wrapper->ctx, wrapper->self); // [func][this] - ILibDuktape_http_server_PUSH_IncomingMessage(wrapper->ctx, header, NULL); // [func][this][msg] - ILibduktape_http_stream_PUSH_ServerResponse(wrapper->ctx, wrapper); // [func][this][msg][rsp] - if (duk_pcall_method(wrapper->ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(wrapper->ctx, "http.httpStream.OnRequest(); Error "); } - duk_pop(wrapper->ctx); // ... - } -} -duk_ret_t ILibDuktape_http_stream_finalizer(duk_context *ctx) -{ - duk_get_prop_string(ctx, 0, HTTP_STREAM_WRAPPER); - ILibDuktape_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)Duktape_GetBuffer(ctx, -1, NULL); - - if (wrapper->wcdo != NULL) - { - ((int*)(wrapper->reserved + sizeof(ILibTransport)))[0] = ~0; - ILibWebClient_DestroyWebClientDataObject(wrapper->wcdo); - } - return 0; -} -ILibTransport_DoneState ILibDuktape_http_stream_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) -{ - ILibDuktape_http_streamWrapper *wrapper = (ILibDuktape_http_streamWrapper*)user; - ILibAsyncSocket_SocketModule module = (ILibAsyncSocket_SocketModule)wrapper->reserved; - int PAUSE = 0; - int i = 0, consumed = 0; - - if (wrapper->bufferLen == 0) - { - do - { - consumed = 0; - ILibWebClient_OnData(module, buffer + i, &consumed, bufferLen, NULL, (void**)&(wrapper->wcdo), &PAUSE); - i += consumed; - bufferLen -= i; - } while (consumed != 0 && bufferLen != 0 && PAUSE == 0); - if (bufferLen != 0) - { - if (bufferLen > (int)(sizeof(wrapper->buffer) - wrapper->bufferLen)) { return(ILibTransport_DoneState_ERROR); } - if (wrapper->bufferLen + bufferLen > HTTP_STREAM_WRAPPER_BUFSIZE) { return(ILibTransport_DoneState_ERROR); } - memcpy_s(wrapper->buffer + wrapper->bufferLen, sizeof(wrapper->buffer) - wrapper->bufferLen, buffer + i, bufferLen); - wrapper->bufferLen += bufferLen; - } - } - else if (wrapper->bufferLen > 0) - { - if (wrapper->bufferLen + bufferLen > HTTP_STREAM_WRAPPER_BUFSIZE) { return(ILibTransport_DoneState_ERROR); } - memcpy_s(wrapper->buffer + wrapper->bufferLen, sizeof(wrapper->buffer) - wrapper->bufferLen, buffer, bufferLen); - wrapper->bufferLen += bufferLen; - - i = 0; - do - { - consumed = 0; - ILibWebClient_OnData(module, wrapper->buffer + i, &consumed, wrapper->bufferLen, NULL, (void**)&(wrapper->wcdo), &PAUSE); - i += consumed; - wrapper->bufferLen -= i; - } while (consumed != 0 && wrapper->bufferLen != 0 && PAUSE == 0); - if (wrapper->bufferLen != 0) - { - memmove_s(wrapper->buffer, sizeof(wrapper->buffer), wrapper->buffer + i, wrapper->bufferLen); - } - } - - - return(PAUSE == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE); -} -void ILibDuktape_http_stream_EndSink(ILibDuktape_DuplexStream *ds, void *user) -{ - ILibDuktape_http_streamWrapper *stream = (ILibDuktape_http_streamWrapper*)user; - if (stream->PipedWriter != NULL) - { - ((ILibDuktape_WritableStream*)stream->PipedWriter)->EndSink((ILibDuktape_WritableStream*)stream->PipedWriter, ((ILibDuktape_WritableStream*)stream->PipedWriter)->WriteSink_User); - } - else - { - duk_push_heapptr(stream->ctx, stream->PipedReader); // [stream] - duk_get_prop_string(stream->ctx, -1, "end"); // [stream][end] - duk_swap_top(stream->ctx, -2); // [end][this] - if (duk_pcall_method(stream->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "http.httpStream.OnEnd(): Error "); } - duk_pop(stream->ctx); // ... - } -} -void ILibDuktape_http_stream_PauseSink(ILibDuktape_DuplexStream *sender, void *user) -{ - ILibDuktape_http_streamWrapper *stream = (ILibDuktape_http_streamWrapper*)user; - if (stream->PipedWriter != NULL) - { - ILibDuktape_readableStream *rs = (ILibDuktape_readableStream*)stream->PipedReader; - if (rs->PauseHandler != NULL) - { - rs->PauseHandler(rs, rs->user); - } - else - { - duk_push_string(stream->ctx, "net.http.httpStream.OnPause(): Error, Native Readable Stream does not have a PauseHandler"); - ILibDuktape_Process_UncaughtException(stream->ctx); - duk_pop(stream->ctx); - } - } - else - { - duk_push_heapptr(stream->ctx, stream->PipedReader); // [reader] - duk_get_prop_string(stream->ctx, -1, "pause"); // [reader][func] - duk_swap_top(stream->ctx, -2); // [func][this] - if (duk_pcall_method(stream->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "net.http.httpStream.OnPause(): Error "); } - duk_pop(stream->ctx); // ... - } -} -void ILibDuktape_http_stream_ResumeSink(ILibDuktape_DuplexStream *sender, void *user) -{ - ILibDuktape_http_streamWrapper *stream = (ILibDuktape_http_streamWrapper*)user; - if (stream->PipedWriter != NULL) - { - ILibDuktape_readableStream *rs = (ILibDuktape_readableStream*)stream->PipedReader; - if (rs->ResumeHandler != NULL) - { - rs->ResumeHandler(rs, rs->user); - } - else - { - duk_push_string(stream->ctx, "net.http.httpStream.OnResume(): Error, Native Readable Stream does not have a ResumeHandler"); - ILibDuktape_Process_UncaughtException(stream->ctx); - duk_pop(stream->ctx); - } - } - else - { - duk_push_heapptr(stream->ctx, stream->PipedReader); // [reader] - duk_get_prop_string(stream->ctx, -1, "resume"); // [reader][func] - duk_swap_top(stream->ctx, -2); // [func][this] - if (duk_pcall_method(stream->ctx, 0) != 0) { ILibDuktape_Process_UncaughtExceptionEx(stream->ctx, "net.http.httpStream.OnResume(): Error "); } - duk_pop(stream->ctx); // ... - } -} -void ILibDuktape_http_Stream_PipeSink(duk_context *ctx, void *object, char *eventName, void *duk_eventArgs) -{ - ILibDuktape_http_streamWrapper *stream; - duk_push_heapptr(ctx, object); // [stream] - duk_get_prop_string(ctx, -1, HTTP_STREAM_WRAPPER); // [stream][buffer] - stream = (ILibDuktape_http_streamWrapper*)Duktape_GetBuffer(ctx, -1, NULL); - duk_pop_2(ctx); // ... - - duk_push_heapptr(ctx, duk_eventArgs); // [args] - if (duk_get_length(ctx, -1) > 0) - { - duk_get_prop_index(ctx, -1, 0); // [args][pipe] - if (duk_has_prop_string(ctx, -1, ILibDuktape_readableStream_RSPTRS) && duk_has_prop_string(ctx, -1, ILibDuktape_WritableStream_WSPTRS)) - { - duk_get_prop_string(ctx, -1, ILibDuktape_readableStream_RSPTRS); // [args][pipe][rstream] - duk_get_prop_string(ctx, -2, ILibDuktape_WritableStream_WSPTRS); // [args][pipe][rstream][wstream] - stream->PipedReader = Duktape_GetBuffer(ctx, -2, NULL); - stream->PipedWriter = (ILibDuktape_WritableStream*)Duktape_GetBuffer(ctx, -1, NULL); - duk_pop_2(ctx); // [args][pipe] - } - else - { - stream->PipedReader = duk_get_heapptr(ctx, -1); - stream->PipedWriter = NULL; - } - duk_pop(ctx); // [args] - } - duk_pop(ctx); // ... -} -duk_ret_t ILibDuktape_http_createStream(duk_context *ctx) -{ - ILibDuktape_http_streamWrapper *stream; - duk_push_object(ctx); // [stream] - duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_http_streamWrapper)); // [stream][buffer] - stream = (ILibDuktape_http_streamWrapper*)Duktape_GetBuffer(ctx, -1, NULL); - duk_put_prop_string(ctx, -2, HTTP_STREAM_WRAPPER); // [stream] - ILibDuktape_CreateFinalizer(ctx, ILibDuktape_http_stream_finalizer); - - memset(stream, 0, sizeof(ILibDuktape_http_streamWrapper)); - stream->ctx = ctx; - stream->self = duk_get_heapptr(ctx, -1); - stream->emitter = ILibDuktape_EventEmitter_Create(ctx); - ILibDuktape_EventEmitter_CreateEvent(stream->emitter, "request", &(stream->OnRequest)); - ILibDuktape_EventEmitter_CreateEvent(stream->emitter, "response", &(stream->OnResponse)); - stream->wcdo = ILibCreateWebClientEx(ILibDuktape_http_stream_OnReceive, (ILibAsyncSocket_SocketModule)(stream->reserved), stream, NULL); - stream->ds = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_http_stream_WriteSink, ILibDuktape_http_stream_EndSink, ILibDuktape_http_stream_PauseSink, ILibDuktape_http_stream_ResumeSink, stream); - - ILibDuktape_EventEmitter_AddSink(stream->emitter, "pipe", ILibDuktape_http_Stream_PipeSink); - - return 1; -} -void ILibDuktape_http_PUSH_EX(duk_context *ctx, void *chain, int https) -{ - duk_push_object(ctx); // [http] - duk_push_pointer(ctx, chain); // [http][chain] - duk_put_prop_string(ctx, -2, "chain"); // [http] - duk_push_string(ctx, https == 0 ? "http:" : "https:"); - duk_put_prop_string(ctx, -2, HTTP_DEFAULT_PROTO_KEY); // [http] - - ILibDuktape_CreateInstanceMethod(ctx, "parseUri", ILibDuktape_http_parseUri, 1); - ILibDuktape_CreateInstanceMethod(ctx, "createServer", ILibDuktape_http_createServer, DUK_VARARGS); - ILibDuktape_CreateInstanceMethod(ctx, "request", ILibDuktape_http_request, DUK_VARARGS); - ILibDuktape_CreateInstanceMethod(ctx, "get", ILibDuktape_http_get, DUK_VARARGS); - ILibDuktape_CreateInstanceMethod(ctx, "createStream", ILibDuktape_http_createStream, 0); - ILibDuktape_CreateFinalizer(ctx, ILibDuktape_http_finalizer); - - duk_push_int(ctx, 5); // [http][pool] - duk_put_prop_string(ctx, -2, "RequestPoolSize"); // [http] -} -void ILibDuktape_http_PUSH(duk_context *ctx, void *chain) -{ - ILibDuktape_http_PUSH_EX(ctx, chain, 0); -} -void ILibDuktape_https_PUSH(duk_context *ctx, void *chain) -{ - ILibDuktape_http_PUSH_EX(ctx, chain, 1); -} - duk_ret_t ILibDuktape_httpDigest_clientRequest_response2(duk_context *ctx) { ILibHTTPPacket *packet; @@ -2605,7 +99,6 @@ duk_ret_t ILibDuktape_httpDigest_clientRequest_propagateEvent(duk_context *ctx) return(0); } -extern void* ILibWebClient_Digest_GenerateTable(ILibWebClient_StateObject state); void ILibDuktape_httpDigest_clientRequest_IncomingMessage_PauseHandler(ILibDuktape_readableStream *sender, void *user) { duk_push_heapptr(sender->ctx, user); // [imsg] @@ -2624,6 +117,8 @@ void ILibDuktape_httpDigest_clientRequest_IncomingMessage_ResumeHandler(ILibDukt } duk_ret_t ILibDuktape_httpDigest_clientRequest_OnData(duk_context *ctx) { + // http.IncomingMessage.data + duk_push_current_function(ctx); duk_get_prop_string(ctx, -1, CLIENTREQUEST_IMSG_RSPTR); ILibDuktape_readableStream *rs = (ILibDuktape_readableStream*)duk_get_pointer(ctx, -1); @@ -2644,57 +139,101 @@ duk_ret_t ILibDuktape_httpDigest_clientRequest_OnEnd(duk_context *ctx) return(0); } -duk_ret_t ILibDuktape_httpDigest_http_request_socketEvent_end(duk_context *ctx) +extern void ILibWebServer_Digest_ParseAuthenticationHeader(void* table, char* value, int valueLen); +char *ILibDuktape_httpDigest_generateAuthenticationHeader(duk_context *ctx, void *digestObj, void *optionsObj) { - duk_push_this(ctx); // [socket] - duk_get_prop_string(ctx, -1, CLIENTREQUEST_SOCKET_WCDO);// [socket][wcdo] - duk_get_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [socket][wcdo][digest] - duk_get_prop_string(ctx, -1, DIGEST_WCDO); // [socket][wcdo][digest][wcdo] - if (duk_get_pointer(ctx, -1) == duk_get_pointer(ctx, -3)) + int top = duk_get_top(ctx); + int NC; + char *CNONCE; + + char *wwwauth, *username, *password; + char *method, *path; + duk_size_t wwwauthLen; + void *ReservedMemory = ILibMemory_AllocateA(8000); + void *table = ILibInitHashTree_CaseInSensitiveEx(ReservedMemory); + int tmpLen; + char result1[33]; + char result2[33]; + char result3[33]; + + duk_push_heapptr(ctx, digestObj); // [digest] + wwwauth = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, DIGEST2WWWAUTH, NULL, &wwwauthLen); + username = (char*)Duktape_GetStringPropertyValue(ctx, -1, DIGEST_USERNAME, NULL); + password = (char*)Duktape_GetStringPropertyValue(ctx, -1, DIGEST_PASSWORD, NULL); + if (wwwauth == NULL || username == NULL || password == NULL) { duk_pop(ctx); return(NULL); } + + duk_push_heapptr(ctx, optionsObj); // [digest][options] + method = (char*)Duktape_GetStringPropertyValue(ctx, -1, "method", NULL); + path = (char*)Duktape_GetStringPropertyValue(ctx, -1, "path", NULL); + duk_pop(ctx); // [digest] + + ILibWebServer_Digest_ParseAuthenticationHeader(table, wwwauth, (int)wwwauthLen); + char *realm, *nonce, *opaque, *qop; + int realmLen, nonceLen, opaqueLen, qopLen; + + ILibGetEntryEx(table, "realm", 5, (void**)&realm, &realmLen); if (realmLen > 0) { realm[realmLen] = 0; } + ILibGetEntryEx(table, "nonce", 5, (void**)&nonce, &nonceLen); if (nonceLen > 0) { nonce[nonceLen] = 0; } + ILibGetEntryEx(table, "opaque", 6, (void**)&opaque, &opaqueLen); if (opaqueLen > 0) { opaque[opaqueLen] = 0; } + ILibGetEntryEx(table, "qop", 3, (void**)&qop, &qopLen); if (qopLen > 0) { qop[qopLen] = 0; } + + tmpLen = sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "%s:%s:%s", username, realm, password); + util_md5hex(ILibScratchPad2, tmpLen, result1); + + tmpLen = sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "%s:%s", method, path); + util_md5hex(ILibScratchPad2, tmpLen, result2); + + if (qop == NULL) { - duk_del_prop_string(ctx, -2, DIGEST_WCDO); + tmpLen = sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "%s:%s:%s", result1, nonce, result2); } - return(0); -} -duk_ret_t ILibDuktape_httpDigest_http_request_socketEvent(duk_context *ctx) -{ - duk_push_this(ctx); // [clientRequest] - duk_get_prop_string(ctx, -1, DIGEST_CLIENT_REQUEST); // [clientRequest][digestClientRequest] - duk_get_prop_string(ctx, -1, DIGESTCLIENTREQUEST_DIGEST); // [clientRequest][digestClientRequest][digest] - duk_get_prop_string(ctx, 0, CLIENTREQUEST_SOCKET_WCDO); // [clientRequest][digestClientRequest][digest][wcdo] - duk_put_prop_string(ctx, -2, DIGEST_WCDO); // [clientRequest][digestClientRequest][digest] + else + { + CNONCE = (char*)Duktape_GetBuffer(ctx, -1, NULL); + NC = Duktape_GetIntPropertyValue(ctx, -1, DIGEST2NC, 0) + 1; + duk_push_int(ctx, NC); // [digest][NC] + duk_put_prop_string(ctx, -2, DIGEST2NC); // [digest] - duk_dup(ctx, 0); // [clientRequest][digestClientRequest][digest][socket] - duk_swap_top(ctx, -2); // [clientRequest][digestClientRequest][socket][digest] - duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [clientRequest][digestClientRequest][socket] + tmpLen = sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "%s:%s:%08x:%s:%s:%s", result1, nonce, NC, CNONCE, qop, result2); + } + util_md5hex(ILibScratchPad2, tmpLen, result3); + duk_pop(ctx); // ... - ILibDuktape_EventEmitter_AddOnceEx(ILibDuktape_EventEmitter_GetEmitter(ctx, 0), "end", ILibDuktape_httpDigest_http_request_socketEvent_end, 0); - return(0); + tmpLen = sprintf_s(ILibScratchPad2, sizeof(ILibScratchPad2), "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\"", username, realm, nonce, path); + if (opaque != NULL) { tmpLen += sprintf_s(ILibScratchPad2 + tmpLen, sizeof(ILibScratchPad2) - tmpLen, ", opaque=\"%s\"", opaque); } + if (qop != NULL) + { + tmpLen += sprintf_s(ILibScratchPad2 + tmpLen, sizeof(ILibScratchPad2) - tmpLen, ", qop=\"%s\", nc=%08x, cnonce=\"%s\"", qop, NC, CNONCE); + } + + tmpLen += sprintf_s(ILibScratchPad2 + tmpLen, sizeof(ILibScratchPad2) - tmpLen, ", response=\"%s\"", result3); + + if (realmLen > 0) { realm[realmLen] = '"'; } + if (nonceLen > 0) { nonce[nonceLen] = '"'; } + if (opaqueLen > 0) { opaque[opaqueLen] = '"'; } + if (qopLen > 0) { qop[qopLen] = '"'; } + + duk_set_top(ctx, top); + return(ILibScratchPad2); } duk_ret_t ILibDuktape_httpDigest_clientRequest_response(duk_context *ctx) { - ILibHTTPPacket *packet; - ILibWebClient_StateObject wcdo; - char *username, *password; - char *uri = NULL; - void *digestClientPtr; - void *paramPtr = NULL; - void *cr_self; + void *digestClientPtr, *digestObj, *optionsObj; + int statusCode; + + duk_push_this(ctx); + optionsObj = Duktape_GetHeapptrProperty(ctx, -1, ILibDuktape_CR2Options); duk_push_current_function(ctx); duk_get_prop_string(ctx, -1, "digestClientRequest"); digestClientPtr = duk_get_heapptr(ctx, -1); duk_get_prop_string(ctx, -1, "digest"); + digestObj = duk_get_heapptr(ctx, -1); duk_get_prop_string(ctx, -1, DIGEST_USERNAME); - username = (char*)duk_get_string(ctx, -1); duk_get_prop_string(ctx, -2, DIGEST_PASSWORD); - password = (char*)duk_get_string(ctx, -1); - duk_get_prop_string(ctx, 0, "PacketPtr"); - packet = (ILibHTTPPacket*)duk_get_pointer(ctx, -1); - - if (packet->StatusCode == 401) + statusCode = Duktape_GetIntPropertyValue(ctx, 0, "statusCode", -1); + if (statusCode == 401) { duk_push_heapptr(ctx, digestClientPtr); // [digestClientRequest] int endCalledAlready = Duktape_GetBooleanProperty(ctx, -1, DIGESTCLIENTREQUEST_END_CALLED, 0); @@ -2709,121 +248,51 @@ duk_ret_t ILibDuktape_httpDigest_clientRequest_response(duk_context *ctx) duk_pop(ctx); // ... // UnAuthorized, need to retry request with Authorization Headers - duk_get_prop_string(ctx, 0, HTTP_INCOMINGMSG_WebStateObject); - wcdo = (ILibWebClient_StateObject)duk_get_pointer(ctx, -1); + char *auth, *wwwauth; - duk_size_t methodLen, pathLen; - int authLen; - char *method, *path, *auth; - - void *ReservedMemory = ILibMemory_AllocateA(8000); - ILibHTTPPacket *pk = ILibCreateEmptyPacketEx(ReservedMemory); - pk->Version = "1.1"; - pk->VersionLength = 3; - - duk_push_this(ctx); // [clientRequest] - duk_get_prop_string(ctx, -1, HTTP_CLIENTREQUEST_PARAMETER); // [clientRequest][param] - if (duk_is_string(ctx, -1)) + duk_dup(ctx, 0); // [IMSG] + duk_get_prop_string(ctx, -1, "headers"); // [IMSG][headers] + if ((wwwauth = Duktape_GetStringPropertyValue(ctx, -1, "WWW-Authenticate", NULL)) != NULL) { - // Parameter was a uri string - char *tmpHost; - unsigned short tmpPort; - uri = (char*)duk_get_string(ctx, -1); - ILibParseUri(uri, &tmpHost, &tmpPort, &path, NULL); - ILibSetDirective(pk, "GET", 3, path, -1); + duk_push_heapptr(ctx, digestObj); // [IMSG][headers][digest] + duk_push_string(ctx, wwwauth); // [IMSG][headers][digest][www] + duk_put_prop_string(ctx, -2, DIGEST2WWWAUTH); // [IMSG][headers][digest] + duk_pop(ctx); // [IMSG][headers] + } + duk_pop_2(ctx); // ... - free(tmpHost); - free(path); - } - else - { - method = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "method", "GET", &methodLen); - path = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "path", "/", &pathLen); - ILibSetDirective(pk, method, (int)methodLen, path, (int)pathLen); - paramPtr = duk_get_heapptr(ctx, -1); - } + duk_push_this(ctx); // [clientRequest] + auth = ILibDuktape_httpDigest_generateAuthenticationHeader(ctx, digestObj, optionsObj); - ILibWebClient_GenerateAuthenticationHeader(wcdo, pk, username, password); - auth = ILibGetHeaderLineEx(pk, "Authorization", 13, &authLen); - - duk_push_this(ctx); // [clientReqeust] - duk_get_prop_string(ctx, -1, CLIENTREQUEST_HTTP); // [clientReqeust][http] - - if (paramPtr == NULL) - { - duk_get_prop_string(ctx, -1, "get"); // [clientRequest][http][get] - duk_swap_top(ctx, -2); // [clientRequest][get][this] - duk_push_object(ctx); // [clientReqeust][get][this][options] - duk_push_string(ctx, uri); - duk_put_prop_string(ctx, -2, "uri"); - } - else - { - duk_get_prop_string(ctx, -1, "request"); // [clientRequest][http][request] - duk_swap_top(ctx, -2); // [clientRequest][request][this] - duk_push_heapptr(ctx, paramPtr); // [clientRequest][request][this][options] - duk_del_prop_string(ctx, -1, "timeout"); - } + duk_get_prop_string(ctx, -1, ILibDuktape_CR2HTTP); // [clientReqeust][http] + duk_get_prop_string(ctx, -1, "request"); // [clientRequest][http][request] + duk_swap_top(ctx, -2); // [clientRequest][request][this] + duk_get_prop_string(ctx, -3, ILibDuktape_CR2Options); // [clientRequest][request][this][options] if(!duk_has_prop_string(ctx, -1, "headers")) { - duk_push_object(ctx); // [clientReqeust][get][this][options][headers] + duk_push_object(ctx); // [clientReqeust][request][this][options][headers] } else { - duk_get_prop_string(ctx, -1, "headers"); // [clientReqeust][get][this][options][headers] + duk_get_prop_string(ctx, -1, "headers"); // [clientReqeust][request][this][options][headers] } - duk_push_lstring(ctx, auth, authLen); // [clientReqeust][get][this][options][headers][Auth] - duk_put_prop_string(ctx, -2, "Authorization"); // [clientReqeust][get][this][options][headers] - duk_put_prop_string(ctx, -2, "headers"); // [clientReqeust][get][this][options] - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_response2, DUK_VARARGS); // [clientReqeust][get][this][options][callback] - duk_push_heapptr(ctx, digestClientPtr); // [clientReqeust][get][this][options][callback][digestClientRequest] - duk_put_prop_string(ctx, -2, "digestClientRequest"); // [clientReqeust][get][this][options][callback] + duk_push_string(ctx, auth); // [clientReqeust][request][this][options][headers][Auth] + duk_put_prop_string(ctx, -2, "Authorization"); // [clientReqeust][request][this][options][headers] + duk_put_prop_string(ctx, -2, "headers"); // [clientReqeust][request][this][options] + duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_response2, DUK_VARARGS); // [clientReqeust][request][this][options][callback] + duk_push_heapptr(ctx, digestClientPtr); // [clientReqeust][request][this][options][callback][digestClientRequest] + duk_put_prop_string(ctx, -2, "digestClientRequest"); // [clientReqeust][request][this][options][callback] if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "digest_onResponse: Error Invoking http.get"); } duk_push_heapptr(ctx, digestClientPtr); // [clientRequest][digestClientRequest] + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "upgrade", -1, "upgrade"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "error", -1, "error"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "continue", -1, "continue"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "timeout", -1, "timeout"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "drain", -1, "drain"); duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [clientRequest] - ILibDuktape_EventEmitter_AddOnceEx2(ctx, -1, "socket", ILibDuktape_httpDigest_http_request_socketEvent, 1); - - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_propagateEvent, DUK_VARARGS); // [clientReqeust][EventDispatcher] - duk_push_heapptr(ctx, digestClientPtr); // [clientReqeust][EventDispatcher][digestClientRequest] - duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [clientReqeust][EventDispatcher] - duk_push_string(ctx, "upgrade"); // [clientReqeust][EventDispatcher][eventName] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_EVENT_NAME); // [clientReqeust][EventDispatcher] - ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter(ctx, -2), "upgrade", duk_get_heapptr(ctx, -1)); - duk_pop(ctx); // [clientReqeust] - - - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_propagateEvent, DUK_VARARGS); // [clientReqeust][EventDispatcher] - duk_push_heapptr(ctx, digestClientPtr); // [clientReqeust][EventDispatcher][digestClientRequest] - duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [clientReqeust][EventDispatcher] - duk_push_string(ctx, "error"); // [clientReqeust][EventDispatcher][eventName] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_EVENT_NAME); // [clientReqeust][EventDispatcher] - ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter(ctx, -2), "error", duk_get_heapptr(ctx, -1)); - duk_pop(ctx); // [clientReqeust] - - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_propagateEvent, DUK_VARARGS); // [clientReqeust][EventDispatcher] - duk_push_heapptr(ctx, digestClientPtr); // [clientReqeust][EventDispatcher][digestClientRequest] - duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [clientReqeust][EventDispatcher] - duk_push_string(ctx, "continue"); // [clientReqeust][EventDispatcher][eventName] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_EVENT_NAME); // [clientReqeust][EventDispatcher] - ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter(ctx, -2), "continue", duk_get_heapptr(ctx, -1)); - duk_pop(ctx); // [clientReqeust] - - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_propagateEvent, DUK_VARARGS); // [clientReqeust][EventDispatcher] - duk_push_heapptr(ctx, digestClientPtr); // [clientReqeust][EventDispatcher][digestClientRequest] - duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [clientReqeust][EventDispatcher] - duk_push_string(ctx, "timeout"); // [clientReqeust][EventDispatcher][eventName] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_EVENT_NAME); // [clientReqeust][EventDispatcher] - ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter(ctx, -2), "timeout", duk_get_heapptr(ctx, -1)); - duk_pop(ctx); // [clientReqeust] - - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_onDrain, DUK_VARARGS); // [clientReqeust][onDrain] - duk_push_heapptr(ctx, digestClientPtr); // [clientReqeust][onDrain][digestClientRequest] - duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [clientReqeust][onDrain] - ILibDuktape_EventEmitter_AddOn(ILibDuktape_EventEmitter_GetEmitter(ctx, -2), "drain", duk_get_heapptr(ctx, -1)); - duk_pop(ctx); // [clientReqeust] if (endCalledAlready != 0) { @@ -2859,38 +328,14 @@ duk_ret_t ILibDuktape_httpDigest_clientRequest_response(duk_context *ctx) } else { - duk_dup(ctx, 0); - cr_self = duk_get_heapptr(ctx, -1); - duk_pop(ctx); - duk_push_heapptr(ctx, digestClientPtr); // [digestClientRequest] duk_get_prop_string(ctx, -1, "emit"); // [digestClientRequest][emit] duk_swap_top(ctx, -2); // [emit][this] duk_push_string(ctx, "response"); // [emit][this][response] - ILibDuktape_http_server_PUSH_IncomingMessage(ctx, packet, NULL); // [emit][this][response][imsg] - ILibDuktape_readableStream *rs = ILibDuktape_ReadableStream_Init(ctx, ILibDuktape_httpDigest_clientRequest_IncomingMessage_PauseHandler, ILibDuktape_httpDigest_clientRequest_IncomingMessage_ResumeHandler, cr_self); + duk_dup(ctx, 0); // [emit][this][response][IMSG] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "digestClientRequest.onResponse(): "); } duk_pop(ctx); // ... - - duk_dup(ctx, 0); // [imsg] - duk_get_prop_string(ctx, -1, "on"); // [imsg][on] - duk_swap_top(ctx, -2); // [on][this] - duk_push_string(ctx, "data"); // [on][this][data] - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_OnData, DUK_VARARGS); // [on][this][data][func] - duk_push_pointer(ctx, rs); // [on][this][data][func][ptr] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_IMSG_RSPTR); // [on][this][data][func] - if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "digestClientRequst.onResponse(): Error attaching event to clientRequest.on('data'): "); } - duk_pop(ctx); - - duk_dup(ctx, 0); // [imsg] - duk_get_prop_string(ctx, -1, "on"); // [imsg][on] - duk_swap_top(ctx, -2); // [on][this] - duk_push_string(ctx, "end"); // [on][this][end] - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_OnEnd, DUK_VARARGS); // [on][this][end][func] - duk_push_pointer(ctx, rs); // [on][this][end][func][ptr] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_IMSG_RSPTR); // [on][this][end][func] - if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "digestClientRequst.onResponse(): Error attaching event to clientRequest.on('end'): "); } - duk_pop(ctx); } return(0); } @@ -3031,77 +476,46 @@ duk_ret_t ILibDuktape_httpDigest_http_request(duk_context *ctx) int nargs = duk_get_top(ctx); void *clientRequest = NULL; ILibDuktape_EventEmitter *emitter; - ILibDuktape_EventEmitter *crEmitter; - char *username = NULL; - char *password = NULL; + char *auth = NULL; - duk_push_current_function(ctx); // [func] - duk_get_prop_string(ctx, -1, "isGet"); // [func][isGet] - duk_push_this(ctx); // [func][isGet][digest] + duk_push_this(ctx); // [digest] + duk_get_prop_string(ctx, -1, HTTP_DIGEST); // [digest][http] + duk_get_prop_string(ctx, -1, "request"); // [digest][http][request] + duk_swap_top(ctx, -2); // [digest][request][this] - duk_get_prop_string(ctx, -1, DIGEST_USERNAME); // [func][isGet][digest][username] - duk_get_prop_string(ctx, -2, DIGEST_PASSWORD); // [func][isGet][digest][username][password] - username = (char*)duk_get_string(ctx, -2); - password = (char*)duk_get_string(ctx, -1); - duk_pop_2(ctx); // [func][isGet][digest] - - duk_get_prop_string(ctx, -1, HTTP_DIGEST); // [func][isGet][digest][http] - if (duk_get_int(ctx, -3) != 0) + if (duk_is_string(ctx, 0)) { - duk_get_prop_string(ctx, -1, "get"); // [func][isGet][digest][http][get] + duk_get_prop_string(ctx, -1, "parseUri"); // [digest][request][this][parseUri] + duk_dup(ctx, -2); // [digest][request][this][parseUri][this] + duk_dup(ctx, 0); // [digest][request][this][parseUri][this][uri] + duk_call_method(ctx, 1); // [digest][request][this][options] } else { - duk_get_prop_string(ctx, -1, "request");// [func][isGet][digest][http][request] + duk_dup(ctx, 0); // [digest][request][this][options] } - duk_swap_top(ctx, -2); // [func][isGet][digest][get/request][this] - duk_dup(ctx, 0); // [func][isGet][digest][get/request][this][param1] - // - // Check to see if we can insert Auth Headers, in case we already authenticated - // - if (duk_has_prop_string(ctx, -4, DIGEST_WCDO)) + // Before we make the request, let's check to see if we can put in Authorization header right now + if ((auth = ILibDuktape_httpDigest_generateAuthenticationHeader(ctx, duk_get_heapptr(ctx, -4), duk_get_heapptr(ctx, -1))) != NULL) { - void *wcdo = NULL; - ILibHTTPPacket *pk = ILibCreateEmptyPacketEx(ILibMemory_AllocateA(4096)); - duk_size_t methodLen, pathLen; - char *method, *path; - pk->Version = "1.1"; - pk->VersionLength = 3; - method = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "method", "GET", &methodLen); - path = (char*)Duktape_GetStringPropertyValueEx(ctx, -1, "path", "/", &pathLen); - ILibSetDirective(pk, method, (int)methodLen, path, (int)pathLen); - - duk_get_prop_string(ctx, -4, DIGEST_WCDO); - wcdo = duk_get_pointer(ctx, -1); - duk_pop(ctx); - - ILibWebClient_GenerateAuthenticationHeader(wcdo, pk, username, password); - char *auth = ILibGetHeaderLine(pk, "Authorization", 13); - if (auth != NULL) + if (!duk_has_prop_string(ctx, -1, "headers")) { - if (duk_has_prop_string(ctx, -1, "headers")) // [func][isGet][digest][get/request][this][param1] - { - duk_get_prop_string(ctx, -1, "headers"); // [func][isGet][digest][get/request][this][param1][headers] - } - else - { - duk_push_object(ctx); // [func][isGet][digest][get/request][this][param1][headers] - duk_dup(ctx, -1); // [func][isGet][digest][get/request][this][param1][headers][headers] - duk_put_prop_string(ctx, -3, "headers"); // [func][isGet][digest][get/request][this][param1][headers] - } - duk_push_string(ctx, auth); // [func][isGet][digest][get/request][this][param1][headers][auth] - duk_put_prop_string(ctx, -2, "Authorization"); // [func][isGet][digest][get/request][this][param1][headers] - duk_pop(ctx); // [func][isGet][digest][get/request][this][param1] + duk_push_object(ctx); // [digest][request][this][options][headers] + duk_dup(ctx, -1); // [digest][request][this][options][headers][dup] + duk_put_prop_string(ctx, -3, "headers"); // [digest][request][this][options][headers] } + else + { + duk_get_prop_string(ctx, -1, "headers"); // [digest][request][this][options][headers] + } + duk_push_string(ctx, auth); // [digest][request][this][options][headers][auth] + duk_put_prop_string(ctx, -2, "Authorization"); // [digest][request][this][options][headers] + duk_pop(ctx); // [digest][request][this][options] } + duk_call_method(ctx, 1); // [digest][clientRequest] - if (duk_pcall_method(ctx, 1) != 0) { duk_throw(ctx); return(DUK_RET_ERROR); } - // [clientRequest] clientRequest = duk_get_heapptr(ctx, -1); - crEmitter = ILibDuktape_EventEmitter_GetEmitter(ctx, -1); - duk_get_prop_string(ctx, -1, "once"); // [clientRequest][once] duk_swap_top(ctx, -2); // [once][this] duk_push_string(ctx, "response"); // [once][this][response] @@ -3114,6 +528,7 @@ duk_ret_t ILibDuktape_httpDigest_http_request(duk_context *ctx) duk_dup(ctx, -2); // [once][this][response][method][digest-clientRequest][clientRequest][digest-clientRequest] duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [once][this][response][method][digest-clientRequest][clientRequest] duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [once][this][response][method][digest-clientRequest] + emitter = ILibDuktape_EventEmitter_Create(ctx); ILibDuktape_EventEmitter_CreateEventEx(emitter, "response"); ILibDuktape_EventEmitter_CreateEventEx(emitter, "error"); @@ -3122,8 +537,6 @@ duk_ret_t ILibDuktape_httpDigest_http_request(duk_context *ctx) ILibDuktape_EventEmitter_CreateEventEx(emitter, "timeout"); ILibDuktape_EventEmitter_AddOnceEx(emitter, "continue", ILibDuktape_httpDigest_http_request_continueOccured, 0); - ILibDuktape_EventEmitter_AddOnceEx(crEmitter, "socket", ILibDuktape_httpDigest_http_request_socketEvent, 1); - ILibDuktape_WritableStream_Init(ctx, ILibDuktape_httpDigest_http_request_WriteHandler, ILibDuktape_httpDigest_http_request_DoneHandler, NULL); if (nargs > 1 && duk_is_function(ctx, 1)) @@ -3134,47 +547,15 @@ duk_ret_t ILibDuktape_httpDigest_http_request(duk_context *ctx) duk_push_this(ctx); // [once][this][response][method][digest-clientRequest][digest] duk_put_prop_string(ctx, -2, "digest"); // [once][this][response][method][digest-clientRequest] duk_put_prop_string(ctx, -2, "digestClientRequest"); // [once][this][response][method] - if (duk_pcall_method(ctx, 2) != 0) { duk_throw(ctx); return(DUK_RET_ERROR); } - // [clientRequest] - - duk_push_heapptr(emitter->ctx, emitter->object); // [digestClientRequest] - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_propagateEvent, DUK_VARARGS); // [digestClientRequest][EventDispatcher] - duk_dup(ctx, -2); // [digestClientRequest][EventDispatcher][digestClientRequest] - duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [digestClientRequest][EventDispatcher] - duk_push_string(ctx, "upgrade"); // [digestClientRequest][EventDispatcher][eventName] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_EVENT_NAME); // [digestClientRequest][EventDispatcher] - ILibDuktape_EventEmitter_AddOnce(crEmitter, "upgrade", duk_get_heapptr(ctx, -1)); // [digestClientRequest][EventDispatcher] - duk_pop(ctx); // [digestClientRequest] - - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_propagateEvent, DUK_VARARGS); // [digestClientRequest][EventDispatcher] - duk_dup(ctx, -2); // [digestClientRequest][EventDispatcher][digestClientRequest] - duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [digestClientRequest][EventDispatcher] - duk_push_string(ctx, "error"); // [digestClientRequest][EventDispatcher][eventName] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_EVENT_NAME); // [digestClientRequest][EventDispatcher] - ILibDuktape_EventEmitter_AddOnce(crEmitter, "error", duk_get_heapptr(ctx, -1)); // [digestClientRequest][EventDispatcher] - duk_pop(ctx); // [digestClientRequest] - - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_propagateEvent, DUK_VARARGS); // [digestClientRequest][EventDispatcher] - duk_dup(ctx, -2); // [digestClientRequest][EventDispatcher][digestClientRequest] - duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [digestClientRequest][EventDispatcher] - duk_push_string(ctx, "continue"); // [digestClientRequest][EventDispatcher][eventName] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_EVENT_NAME); // [digestClientRequest][EventDispatcher] - ILibDuktape_EventEmitter_AddOnce(crEmitter, "continue", duk_get_heapptr(ctx, -1)); // [digestClientRequest][EventDispatcher] - duk_pop(ctx); // [digestClientRequest] - - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_propagateEvent, DUK_VARARGS); // [digestClientRequest][EventDispatcher] - duk_dup(ctx, -2); // [digestClientRequest][EventDispatcher][digestClientRequest] - duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [digestClientRequest][EventDispatcher] - duk_push_string(ctx, "timeout"); // [digestClientRequest][EventDispatcher][eventName] - duk_put_prop_string(ctx, -2, CLIENTREQUEST_EVENT_NAME); // [digestClientRequest][EventDispatcher] - ILibDuktape_EventEmitter_AddOnce(crEmitter, "timeout", duk_get_heapptr(ctx, -1)); // [digestClientRequest][EventDispatcher] - duk_pop(ctx); // [digestClientRequest] - - duk_push_c_function(ctx, ILibDuktape_httpDigest_clientRequest_onDrain, DUK_VARARGS); // [digestClientRequest][onDrain] - duk_dup(ctx, -2); // [digestClientRequest][onDrain][digestClientRequest] - duk_put_prop_string(ctx, -2, DIGEST_CLIENT_REQUEST); // [digestClientRequest][onDrain] - ILibDuktape_EventEmitter_AddOn(crEmitter, "drain", duk_get_heapptr(ctx, -1)); // [digestClientRequest][onDrain] - duk_pop(ctx); // [digestClientRequest] + if (duk_pcall_method(ctx, 2) != 0) { duk_throw(ctx); return(DUK_RET_ERROR); } // [clientRequest] + + duk_push_heapptr(emitter->ctx, clientRequest); + duk_push_heapptr(emitter->ctx, emitter->object); // [clientRequest][digestClientRequest] + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "upgrade", -1, "upgrade"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "error", -1, "error"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "continue", -1, "continue"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "timeout", -1, "timeout"); + ILibDuktape_EventEmitter_ForwardEvent(ctx, -2, "drain", -1, "drain"); return(1); } @@ -3197,6 +578,11 @@ duk_ret_t ILibduktape_httpDigest_create(duk_context *ctx) duk_put_prop_string(ctx, -2, DIGEST_USERNAME); duk_push_string(ctx, password); duk_put_prop_string(ctx, -2, DIGEST_PASSWORD); + duk_push_fixed_buffer(ctx, 16); + util_randomtext(16, (char*)Duktape_GetBuffer(ctx, -1, NULL)); + duk_put_prop_string(ctx, -2, DIGEST2CNONCE); + duk_push_int(ctx, 0); + duk_put_prop_string(ctx, -2, DIGEST2NC); return(1); } @@ -3250,7 +636,10 @@ duk_ret_t ILibDuktape_httpHeaders(duk_context *ctx) node = packet->FirstField; while (node != NULL) { - duk_push_lstring(ctx, node->Field, node->FieldLength); + duk_push_lstring(ctx, node->Field, node->FieldLength); // [str] + duk_get_prop_string(ctx, -1, "toLowerCase"); // [str][toLower] + duk_swap_top(ctx, -2); // [toLower][this] + duk_call_method(ctx, 0); // [result] duk_push_lstring(ctx, node->FieldData, node->FieldDataLength); duk_put_prop(ctx, -3); node = node->NextField; @@ -3268,8 +657,6 @@ void ILibDuktape_httpHeaders_PUSH(duk_context *ctx, void *chain) } void ILibDuktape_http_init(duk_context * ctx, void * chain) { - ILibDuktape_ModSearch_AddHandler(ctx, "http", ILibDuktape_http_PUSH); - ILibDuktape_ModSearch_AddHandler(ctx, "https", ILibDuktape_https_PUSH); ILibDuktape_ModSearch_AddHandler(ctx, "http-digest", ILibDuktape_httpDigest_PUSH); ILibDuktape_ModSearch_AddHandler(ctx, "http-headers", ILibDuktape_httpHeaders_PUSH); } diff --git a/microscript/ILibDuktape_net.c b/microscript/ILibDuktape_net.c index e08f74c..aa02323 100644 --- a/microscript/ILibDuktape_net.c +++ b/microscript/ILibDuktape_net.c @@ -39,7 +39,12 @@ typedef struct ILibDuktape_net_socket void *OnError; void *OnTimeout; void *OnSetTimeout; + int unshiftBytes; ILibDuktape_EventEmitter *emitter; +#ifndef MICROSTACK_NOTLS + SSL_CTX *ssl_ctx; + SSL *ssl; +#endif }ILibDuktape_net_socket; typedef struct ILibDuktape_net_server @@ -49,7 +54,6 @@ typedef struct ILibDuktape_net_server ILibAsyncServerSocket_ServerModule server; ILibDuktape_EventEmitter *emitter; void *OnClose; - void *OnConnection; void *OnListening; void *OnError; }ILibDuktape_net_server; @@ -61,27 +65,43 @@ typedef struct ILibDuktape_net_server_session ILibDuktape_EventEmitter *emitter; ILibDuktape_DuplexStream *stream; + int unshiftBytes; + void *OnTimeout; }ILibDuktape_net_server_session; +int ILibDuktape_TLS_ctx2socket = -1; +int ILibDuktape_TLS_ctx2server = -1; + +#define ILibDuktape_SecureContext2CertBuffer "\xFF_SecureContext2CertBuffer" +#define ILibDuktape_SecureContext2SSLCTXPTR "\xFF_SecureContext2SSLCTXPTR" #define ILibDuktape_GlobalTunnel_DataPtr "\xFF_GlobalTunnel_DataPtr" #define ILibDuktape_GlobalTunnel_Stash "global-tunnel" #define ILibDuktape_net_Server_buffer "\xFF_FixedBuffer" #define ILibDuktape_net_Server_Session_buffer "\xFF_SessionFixedBuffer" #define ILibDuktape_net_socket_ptr "\xFF_SocketPtr" +#define ILibDuktape_SERVER2ContextTable "\xFF_Server2ContextTable" +#define ILibDuktape_SERVER2OPTIONS "\xFF_ServerToOptions" +#define ILibDuktape_SERVER2LISTENOPTIONS "\xFF_ServerToListenOptions" extern void ILibAsyncServerSocket_RemoveFromChain(ILibAsyncServerSocket_ServerModule serverModule); // Prototypes void ILibDuktape_net_socket_PUSH(duk_context *ctx, ILibAsyncSocket_SocketModule module); #ifndef MICROSTACK_NOTLS -extern void ILibDuktape_X509_PUSH(duk_context *ctx, X509* cert); +duk_ret_t ILibDuktape_tls_server_addContext(duk_context *ctx); #endif + void ILibDuktape_net_socket_OnData(ILibAsyncSocket_SocketModule socketModule, char* buffer, int *p_beginPointer, int endPointer, ILibAsyncSocket_OnInterrupt* OnInterrupt, void **user, int *PAUSE) { ILibDuktape_net_socket *ptrs = (ILibDuktape_net_socket*)((ILibChain_Link*)socketModule)->ExtraMemoryPtr; - if (ILibDuktape_DuplexStream_WriteData((ILibDuktape_DuplexStream*)ptrs->duplexStream, buffer + *p_beginPointer, endPointer - *p_beginPointer) != 0) { *PAUSE = 1; } - else { *p_beginPointer = endPointer; } + if (ILibDuktape_DuplexStream_WriteData((ILibDuktape_DuplexStream*)ptrs->duplexStream, buffer + *p_beginPointer, endPointer - *p_beginPointer) != 0) + { + *PAUSE = 1; + } + + *p_beginPointer = endPointer - ptrs->unshiftBytes; + ptrs->unshiftBytes = 0; } void ILibDuktape_net_socket_OnConnect(ILibAsyncSocket_SocketModule socketModule, int Connected, void *user) { @@ -109,13 +129,24 @@ void ILibDuktape_net_socket_OnConnect(ILibAsyncSocket_SocketModule socketModule, duk_put_prop_string(ptrs->ctx, -2, "remoteFamily"); // [sock] duk_push_int(ptrs->ctx, (int)ntohs(local.sin6_port)); // [sock][remotePort] duk_put_prop_string(ptrs->ctx, -2, "remotePort"); // [sock] - duk_pop(ptrs->ctx); // ... +#ifndef MICROSTACK_NOTLS + if (ptrs->ssl != NULL) + { + duk_push_heapptr(ptrs->ctx, ptrs->object); // [socket] + duk_get_prop_string(ptrs->ctx, -1, "emit"); // [socket][emit] + duk_swap_top(ptrs->ctx, -2); // [emit][this] + duk_push_string(ptrs->ctx, "secureConnect"); // [emit][this][secureConnect] + if (duk_pcall_method(ptrs->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ptrs->ctx, "tls.socket.OnSecureConnect(): "); } + duk_pop(ptrs->ctx); // ... + return; + } +#endif if (ptrs->OnConnect != NULL) { duk_push_heapptr(ptrs->ctx, ptrs->OnConnect); // [func] - ILibDuktape_net_socket_PUSH(ptrs->ctx, socketModule); // [func][this] + duk_push_heapptr(ptrs->ctx, ptrs->object); // [func][this] if (duk_pcall_method(ptrs->ctx, 0) != 0) // [retVal] { ILibDuktape_Process_UncaughtException(ptrs->ctx); @@ -141,14 +172,17 @@ void ILibDuktape_net_socket_OnDisconnect(ILibAsyncSocket_SocketModule socketModu { ILibDuktape_net_socket *ptrs = (ILibDuktape_net_socket*)((ILibChain_Link*)socketModule)->ExtraMemoryPtr; - duk_push_heapptr(ptrs->ctx, ptrs->object); // [sock] - duk_push_string(ptrs->ctx, "0.0.0.0"); // [sock][localAddr] - duk_put_prop_string(ptrs->ctx, -2, "localAddress"); // [sock] - duk_push_undefined(ptrs->ctx); // [sock][remoteAddr] - duk_put_prop_string(ptrs->ctx, -2, "remoteAddress");// [sock] - duk_pop(ptrs->ctx); // ... + if (ILibDuktape_IsPointerValid(ptrs->chain, ptrs->object)) + { + duk_push_heapptr(ptrs->ctx, ptrs->object); // [sock] + duk_push_string(ptrs->ctx, "0.0.0.0"); // [sock][localAddr] + duk_put_prop_string(ptrs->ctx, -2, "localAddress"); // [sock] + duk_push_undefined(ptrs->ctx); // [sock][remoteAddr] + duk_put_prop_string(ptrs->ctx, -2, "remoteAddress");// [sock] + duk_pop(ptrs->ctx); // ... - ILibDuktape_DuplexStream_Closed((ILibDuktape_DuplexStream*)ptrs->duplexStream); + ILibDuktape_DuplexStream_Closed((ILibDuktape_DuplexStream*)ptrs->duplexStream); + } } void ILibDuktape_net_socket_OnSendOK(ILibAsyncSocket_SocketModule socketModule, void *user) { @@ -175,6 +209,17 @@ void ILibDuktape_net_socket_ResumeHandler(ILibDuktape_DuplexStream *sender, void ILibDuktape_net_socket *ptrs = (ILibDuktape_net_socket*)user; ILibAsyncSocket_Resume(ptrs->socketModule); } +duk_ret_t ILibDuktape_net_socket_connect_errorDispatch(duk_context *ctx) +{ + duk_dup(ctx, 0); // [socket] + duk_get_prop_string(ctx, -1, "emit"); // [socket][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "error"); // [emit][this][error] + duk_dup(ctx, 1); // [emit][this][error][err] + duk_call_method(ctx, 2); + duk_pop(ctx); // ... + return(0); +} duk_ret_t ILibDuktape_net_socket_connect(duk_context *ctx) { int nargs = duk_get_top(ctx); @@ -221,14 +266,35 @@ duk_ret_t ILibDuktape_net_socket_connect(duk_context *ctx) } } + duk_push_heapptr(ptrs->ctx, ptrs->object); // [socket] + duk_push_string(ctx, host); // [socket][host] + ILibDuktape_CreateReadonlyProperty(ctx, "remoteHost"); // [socket] + duk_pop(ctx); // ... + ILibResolveEx(host, (unsigned short)port, &dest); - ILibAsyncSocket_ConnectTo(ptrs->socketModule, NULL, (struct sockaddr*)&dest, NULL, ptrs); - - duk_push_heapptr(ptrs->ctx, ptrs->object); // [sockat] - duk_push_true(ptrs->ctx); // [socket][connecting] - duk_put_prop_string(ptrs->ctx, -2, "connecting"); // [socket] - duk_pop(ptrs->ctx); // ... + if (dest.sin6_family == AF_UNSPEC) + { + // Can't resolve... Delay event emit, until next event loop, because if app called net.createConnection(), they don't have the socket yet + duk_push_heapptr(ctx, ptrs->object); // [socket] + duk_push_global_object(ctx); // [socket][g] + duk_get_prop_string(ctx, -1, "setImmediate"); // [socket][g][immediate] + duk_swap_top(ctx, -2); // [socket][immediate][this] + duk_push_c_function(ctx, ILibDuktape_net_socket_connect_errorDispatch, DUK_VARARGS); // [socket][immediate][this][callback] + duk_dup(ctx, -4); // [socket][immediate][this][callback][socket] + duk_push_error_object(ptrs->ctx, DUK_ERR_ERROR, "Cannot Resolve Hostname: %s", host); // [socket][immediate][this][callback][socket][err] + if (duk_pcall_method(ptrs->ctx, 3) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ptrs->ctx, "socket.connect(): "); } + duk_put_prop_string(ptrs->ctx, -2, "\xFF_Immediate"); // [socket] + duk_pop(ptrs->ctx); + } + else + { + ILibAsyncSocket_ConnectTo(ptrs->socketModule, NULL, (struct sockaddr*)&dest, NULL, ptrs); + duk_push_heapptr(ptrs->ctx, ptrs->object); // [sockat] + duk_push_true(ptrs->ctx); // [socket][connecting] + duk_put_prop_string(ptrs->ctx, -2, "connecting"); // [socket] + duk_pop(ptrs->ctx); // ... + } return 0; } @@ -290,13 +356,16 @@ duk_ret_t ILibDuktape_net_socket_setTimeout(duk_context *ctx) ptrs = (ILibDuktape_net_socket*)duk_to_pointer(ctx, -1); duk_pop(ctx); // [socks] - if (timeout < 1000) { return(ILibDuktape_Error(ctx, "net.socket.setTimeout(): Error, timeout must be > 1000ms. Timeout was %d ms", timeout)); } if (nargs > 1 && duk_is_function(ctx, 1)) { ILibDuktape_EventEmitter_AddOnce(ptrs->emitter, "timeout", duk_require_heapptr(ctx, 1)); } - - ILibAsyncSocket_SetTimeout(ptrs->socketModule, timeout / 1000, ILibDuktape_net_socket_timeoutSink); + if (timeout == 0) + { + // Disable + ILibDuktape_EventEmitter_RemoveAllListeners(ptrs->emitter, "timeout"); + } + ILibAsyncSocket_SetTimeoutEx(ptrs->socketModule, timeout, timeout != 0 ? ILibDuktape_net_socket_timeoutSink : NULL); return 0; } duk_ret_t ILibDuktape_net_socket_finalizer(duk_context *ctx) @@ -309,21 +378,32 @@ duk_ret_t ILibDuktape_net_socket_finalizer(duk_context *ctx) if (ptrs->socketModule != NULL) { if (ILibAsyncSocket_IsConnected(ptrs->socketModule) != 0) { ILibAsyncSocket_Disconnect(ptrs->socketModule); } +#ifndef MICROSTACK_NOTLS + if (ptrs->ssl_ctx != NULL) { SSL_CTX_free(ptrs->ssl_ctx); ptrs->ssl_ctx = NULL; } +#endif ILibChain_SafeRemove(chain, ptrs->socketModule); } return 0; } +int ILibDuktape_net_socket_unshift(ILibDuktape_DuplexStream *sender, int unshiftBytes, void *user) +{ + ILibDuktape_net_socket *ptrs = (ILibDuktape_net_socket*)user; + ptrs->unshiftBytes = unshiftBytes; + return(unshiftBytes); +} void ILibDuktape_net_socket_PUSH(duk_context *ctx, ILibAsyncSocket_SocketModule module) { ILibDuktape_net_socket *ptrs = (ILibDuktape_net_socket*)((ILibChain_Link*)module)->ExtraMemoryPtr; - if (ptrs->object != NULL) + if (ptrs != NULL && ptrs->object != NULL) { duk_push_heapptr(ctx, ptrs->object); return; } duk_push_object(ctx); // [obj] + ILibDuktape_WriteID(ctx, "net.socket"); + ILibDuktape_PointerValidation_Init(ctx); ptrs->ctx = ctx; ptrs->chain = ((ILibChain_Link*)module)->ParentChain; ptrs->object = duk_get_heapptr(ctx, -1); @@ -341,7 +421,7 @@ void ILibDuktape_net_socket_PUSH(duk_context *ctx, ILibAsyncSocket_SocketModule duk_put_prop_string(ctx, -2, "remoteAddress"); ptrs->emitter = ILibDuktape_EventEmitter_Create(ctx); - ptrs->duplexStream = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_net_socket_WriteHandler, ILibDuktape_net_socket_EndHandler, ILibDuktape_net_socket_PauseHandler, ILibDuktape_net_socket_ResumeHandler, ptrs); + ptrs->duplexStream = ILibDuktape_DuplexStream_InitEx(ctx, ILibDuktape_net_socket_WriteHandler, ILibDuktape_net_socket_EndHandler, ILibDuktape_net_socket_PauseHandler, ILibDuktape_net_socket_ResumeHandler, ILibDuktape_net_socket_unshift, ptrs); ILibDuktape_EventEmitter_CreateEvent(ptrs->emitter, "close", &(ptrs->OnClose)); ILibDuktape_EventEmitter_CreateEvent(ptrs->emitter, "connect", &(ptrs->OnConnect)); @@ -406,344 +486,13 @@ duk_ret_t ILibDuktape_net_createConnection(duk_context *ctx) duk_push_this(ctx); duk_del_prop_string(ctx, -1, ILibDuktape_net_socket_ptr); duk_push_heapptr(ctx, ptrs->object); - return 1; -} - -#ifndef MICROSTACK_NOTLS -typedef struct ILibDuktape_net_sslStream_ptr -{ - duk_context *ctx; - SSL_CTX *sslctx; - SSL* ssl; - void *sslStream_object; - void *sslStream_en_object; - void *OnVerify; - void *OnConnected; - int handshake; - int rejectUnauthorized; - ILibDuktape_DuplexStream *ds_clear; - ILibDuktape_DuplexStream *ds_encrypted; - int encrypted_processingLoop; - int decrypted_processingLoop; - char encryptedBuffer[4096]; - char decryptedBuffer[4096]; -}ILibDuktape_net_sslStream_ptr; -#define ILibDuktape_net_sslStream_key "\xFF_sslStreamPtr" -int ILibDuktape_net_sslStream_sslIndex = -1; - -void ILibDuktape_net_sslStream_encryptedReadLoop(ILibDuktape_net_sslStream_ptr *ptrs) -{ - int j; - if (ptrs->encrypted_processingLoop == 0) + if (duk_is_object(ctx, 0)) { - ptrs->encrypted_processingLoop = 1; - while (ptrs->ds_encrypted->readableStream->paused == 0 && BIO_ctrl_pending(SSL_get_wbio(ptrs->ssl)) > 0) - { - // Data is pending in the write buffer, send it out - j = BIO_read(SSL_get_wbio(ptrs->ssl), ptrs->encryptedBuffer, sizeof(ptrs->encryptedBuffer)); - ILibDuktape_DuplexStream_WriteData(ptrs->ds_encrypted, ptrs->encryptedBuffer, j); - } - ptrs->encrypted_processingLoop = 0; - } -} -int ILibDuktape_net_sslStream_decryptedReadLoop(ILibDuktape_net_sslStream_ptr *ptrs) -{ - int retVal = 0; - int i = -1; - if (ptrs->decrypted_processingLoop == 0) - { - ptrs->decrypted_processingLoop = 1; - while (ptrs->ds_clear->readableStream->paused == 0 && (i = SSL_read(ptrs->ssl, ptrs->decryptedBuffer, sizeof(ptrs->decryptedBuffer)))>0) - { - // We got new TLS/DTLS data - ILibDuktape_DuplexStream_WriteData(ptrs->ds_clear, ptrs->decryptedBuffer, i); - } - if (i == 0) - { - // Session Closed - retVal = 1; - } - ptrs->decrypted_processingLoop = 0; // Compiler Warning is wrong here... This is to prevent re-entrancy problems, because DuplexStream_WriteData() can end up calling back in - } - return retVal; -} -ILibTransport_DoneState ILibDuktape_net_sslStream_writeSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) -{ - ILibDuktape_net_sslStream_ptr *ptrs = (ILibDuktape_net_sslStream_ptr*)user; - - SSL_write(ptrs->ssl, buffer, bufferLen); - ILibDuktape_net_sslStream_encryptedReadLoop(ptrs); - return(ptrs->ds_encrypted->readableStream->paused == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE); -} -ILibTransport_DoneState ILibDuktape_net_sslStream_en_writeSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) -{ - int err; - ILibDuktape_net_sslStream_ptr *ptrs = (ILibDuktape_net_sslStream_ptr*)user; - ILibTransport_DoneState retVal = ILibTransport_DoneState_ERROR; - - BIO_write(SSL_get_rbio(ptrs->ssl), buffer, bufferLen); - if (ptrs->handshake == 0) - { - switch (SSL_do_handshake(ptrs->ssl)) - { - case 0: - // Handshake Failed! - while ((err = ERR_get_error()) != 0) - { - ERR_error_string_n(err, ILibScratchPad, sizeof(ILibScratchPad)); - } - // TODO: We should probably do something - break; - case 1: - ptrs->handshake = 1; - retVal = ILibTransport_DoneState_COMPLETE; - if (ptrs->OnConnected != NULL) - { - duk_push_heapptr(ptrs->ctx, ptrs->OnConnected); // [func] - duk_push_heapptr(ptrs->ctx, ptrs->sslStream_object); // [func][this] - if (duk_pcall_method(ptrs->ctx, 0) != 0) { ILibDuktape_Process_UncaughtException(ptrs->ctx); } - duk_pop(ptrs->ctx); // ... - } - break; - default: - // SSL_WANT_READ most likely, so do nothing for now - retVal = ILibTransport_DoneState_COMPLETE; - break; - } - } - else - { - retVal = ILibDuktape_net_sslStream_decryptedReadLoop(ptrs) != 0 ? ILibTransport_DoneState_ERROR : (ptrs->ds_clear->readableStream->paused == 0 ? ILibTransport_DoneState_COMPLETE : ILibTransport_DoneState_INCOMPLETE); - } - - ILibDuktape_net_sslStream_encryptedReadLoop(ptrs); - return retVal; -} -void ILibDuktape_net_sslStream_endSink(ILibDuktape_DuplexStream *stream, void *user) -{ - ILibDuktape_net_sslStream_ptr *ptrs = (ILibDuktape_net_sslStream_ptr*)user; - ILibDuktape_DuplexStream_WriteEnd(ptrs->ds_encrypted); -} -void ILibDuktape_net_sslStream_en_endSink(ILibDuktape_DuplexStream *stream, void *user) -{ - ILibDuktape_net_sslStream_ptr *ptrs = (ILibDuktape_net_sslStream_ptr*)user; - ILibDuktape_DuplexStream_WriteEnd(ptrs->ds_clear); -} -void ILibDutkape_net_sslStream_pauseSink(ILibDuktape_DuplexStream *sender, void *user) -{ - -} -void ILibDuktape_net_sslStream_en_pauseSink(ILibDuktape_DuplexStream *sender, void *user) -{ - -} -void ILibDuktape_net_sslStream_resumeSink(ILibDuktape_DuplexStream *sender, void *user) -{ - ILibDuktape_net_sslStream_ptr *ptrs = (ILibDuktape_net_sslStream_ptr*)user; - if (ILibDuktape_net_sslStream_decryptedReadLoop(ptrs) == 0) - { - if (ptrs->ds_clear->readableStream->paused == 0) { ILibDuktape_DuplexStream_Ready(ptrs->ds_encrypted); } - } - else - { - // TLS Session was closed - } -} -void ILibDuktape_net_sslStream_en_resumeSink(ILibDuktape_DuplexStream *sender, void *user) -{ - ILibDuktape_net_sslStream_ptr *ptrs = (ILibDuktape_net_sslStream_ptr*)user; - - ILibDuktape_net_sslStream_encryptedReadLoop(ptrs); - if (ptrs->ds_encrypted->readableStream->paused == 0) { ILibDuktape_DuplexStream_Ready(ptrs->ds_clear); } -} -int ILibDuktape_net_sslStream_verifyServer(int preverify_ok, X509_STORE_CTX *ctx) -{ - STACK_OF(X509) *certChain = X509_STORE_CTX_get_chain(ctx); - SSL *ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - ILibDuktape_net_sslStream_ptr *ptrs = (ILibDuktape_net_sslStream_ptr*)SSL_get_ex_data(ssl, ILibDuktape_net_sslStream_sslIndex); - int i; - int retVal = 0; - - if (ptrs->rejectUnauthorized != 0) { return(preverify_ok); } - else { retVal = 0; } - - if (ptrs->OnVerify == NULL) { return 1; } - - duk_push_heapptr(ptrs->ctx, ptrs->OnVerify); // [func] - duk_push_heapptr(ptrs->ctx, ptrs->sslStream_object); // [func][this] - duk_push_array(ptrs->ctx); // [func][this][certs] - for (i = 0; i < sk_X509_num(certChain); ++i) - { - ILibDuktape_X509_PUSH(ptrs->ctx, sk_X509_value(certChain, i)); // [func][this][certs][cert] - duk_put_prop_index(ptrs->ctx, -2, i); // [func][this][certs] - } - if (duk_pcall_method(ptrs->ctx, 1) != 0) { retVal = 0; } else { retVal = 1; } - return retVal; -} -duk_ret_t ILibDuktape_net_sslStream_Finalizer(duk_context *ctx) -{ - ILibDuktape_net_sslStream_ptr *ptrs; - duk_get_prop_string(ctx, 0, ILibDuktape_net_sslStream_key); - ptrs = (ILibDuktape_net_sslStream_ptr*)Duktape_GetBuffer(ctx, -1, NULL); - - if (ptrs->ssl != NULL) { SSL_free(ptrs->ssl); } - if (ptrs->sslctx != NULL) { SSL_CTX_free(ptrs->sslctx); } - - return 0; -} -duk_ret_t ILibDuktape_net_sslStream_create(duk_context *ctx) -{ - int nargs = duk_get_top(ctx); - ILibDuktape_DuplexStream *ds, *en; - ILibDuktape_net_sslStream_ptr *ptrs; - BIO *read, *write; - int status, i; - int isClient; - struct util_cert *leafCert = NULL; - struct util_cert *nonLeafCert = NULL; - - duk_push_current_function(ctx); - duk_get_prop_string(ctx, -1, "clientMode"); - isClient = duk_get_int(ctx, -1); - - duk_push_object(ctx); // [sslStream] - duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_net_sslStream_ptr)); // [sslStream][buffer] - ptrs = (ILibDuktape_net_sslStream_ptr*)Duktape_GetBuffer(ctx, -1, NULL); - duk_put_prop_string(ctx, -2, ILibDuktape_net_sslStream_key); // [sslStream] - memset(ptrs, 0, sizeof(ILibDuktape_net_sslStream_ptr)); - ILibDuktape_CreateEventWithSetter(ctx, "connected", "\xFF_connected", &(ptrs->OnConnected)); - ILibDuktape_CreateFinalizer(ctx, ILibDuktape_net_sslStream_Finalizer); - ptrs->sslStream_object = duk_get_heapptr(ctx, -1); - - - if (nargs > 1) - { - ptrs->rejectUnauthorized = Duktape_GetIntPropertyValue(ctx, 1, "rejectUnauthorized", 0); - if (duk_has_prop_string(ctx, 1, "verify")) - { - duk_get_prop_string(ctx, 1, "verify"); // [sslStream][OnVerify] - ptrs->OnVerify = duk_get_heapptr(ctx, -1); - duk_put_prop_string(ctx, -2, "\xFF_OnVerify"); // [sslStream] - } - if (duk_has_prop_string(ctx, 1, "MeshAgent")) - { - duk_get_prop_string(ctx, 1, "MeshAgent"); // [sslStream][MeshAgent] - if (isClient == 0) - { - duk_get_prop_string(ctx, -1, ILibDuktape_MeshAgent_Cert_Server); // [sslStream][MeshAgent][cert] - leafCert = (struct util_cert*)duk_get_pointer(ctx, -1); - duk_pop_2(ctx); // [sslStream] - } - else - { - duk_get_prop_string(ctx, -1, ILibDuktape_MeshAgent_Cert_Client); // [sslStream][MeshAgent][clientCert] - duk_get_prop_string(ctx, -2, ILibDuktape_MeshAgent_Cert_NonLeaf); // [sslStream][MeshAgent][clientCert][nonLeafCert] - leafCert = (struct util_cert*)duk_get_pointer(ctx, -2); - nonLeafCert = (struct util_cert*)duk_get_pointer(ctx, -1); - duk_pop_3(ctx); // [sslStream] - } - } - if (duk_has_prop_string(ctx, 1, "pfx") && duk_has_prop_string(ctx, 1, "passphrase")) - { - char *pfx; - duk_size_t pfxLen; - char *pwd; - - duk_get_prop_string(ctx, 1, "pfx"); // [sslStream][pfx] - pfx = Duktape_GetBuffer(ctx, -1, &pfxLen); - duk_get_prop_string(ctx, 1, "passphrase"); // [sslStream][pfx][pwd] - pwd = (char*)duk_get_string(ctx, -1); - util_from_p12(pfx, (int)pfxLen, pwd, leafCert); - - duk_pop_2(ctx); // [sslStream] - } - } - - ds = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_net_sslStream_writeSink, ILibDuktape_net_sslStream_endSink, ILibDutkape_net_sslStream_pauseSink, ILibDuktape_net_sslStream_resumeSink, ptrs); - duk_push_object(ctx); // [sslStream][encryptedStream] - ptrs->sslStream_en_object = duk_get_heapptr(ctx, -1); - en = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_net_sslStream_en_writeSink, ILibDuktape_net_sslStream_en_endSink, ILibDuktape_net_sslStream_en_pauseSink, ILibDuktape_net_sslStream_en_resumeSink, ptrs); - duk_put_prop_string(ctx, -2, "\xFF_internalStream"); // [sslStream] - - ptrs->ds_clear = ds; - ptrs->ds_encrypted = en; - ptrs->ctx = ctx; - ptrs->sslctx = isClient != 0 ? SSL_CTX_new(SSLv23_client_method()) : SSL_CTX_new(SSLv23_server_method()); - SSL_CTX_set_options(ptrs->sslctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); - SSL_CTX_set_verify(ptrs->sslctx, isClient != 0 ? SSL_VERIFY_PEER : SSL_VERIFY_CLIENT_ONCE, ILibDuktape_net_sslStream_verifyServer); // Ask for authentication - - if (leafCert != NULL) - { - SSL_CTX_use_certificate(ptrs->sslctx, leafCert->x509); - SSL_CTX_use_PrivateKey(ptrs->sslctx, leafCert->pkey); - - if (nonLeafCert != NULL) - { - SSL_CTX_add_extra_chain_cert(ptrs->sslctx, X509_dup(nonLeafCert->x509)); - } - } - - ptrs->ssl = SSL_new(ptrs->sslctx); - if (ILibDuktape_net_sslStream_sslIndex < 0) - { - ILibDuktape_net_sslStream_sslIndex = SSL_get_ex_new_index(0, "ILibDuktape_net_sslstream index", NULL, NULL, NULL); - } - SSL_set_ex_data(ptrs->ssl, ILibDuktape_net_sslStream_sslIndex, ptrs); - - duk_dup(ctx, 0); // [input] - duk_get_prop_string(ctx, -1, "pipe"); // [input][pipe] - duk_swap_top(ctx, -2); // [pipe][input/this] - duk_push_heapptr(ctx, ptrs->sslStream_en_object); // [pipe][input/this][stream] - if (duk_pcall_method(ctx, 1) != 0) - { - duk_push_string(ctx, "sslStream: Could not pipe with input stream"); - duk_throw(ctx); - return(DUK_RET_ERROR); - } - duk_pop(ctx); - - duk_push_heapptr(ctx, ptrs->sslStream_en_object); // [stream] - duk_get_prop_string(ctx, -1, "pipe"); // [stream][pipe] - duk_swap_top(ctx, -2); // [pipe][stream/this] - duk_dup(ctx, 0); // [pipe][stream/this][input] - if (duk_pcall_method(ctx, 1) != 0) - { - duk_push_string(ctx, "sslSTream: Could not pipe the input stream"); - duk_throw(ctx); - return(DUK_RET_ERROR); - } - duk_pop(ctx); - - // Set up the memory-buffer BIOs - read = BIO_new(BIO_s_mem()); - write = BIO_new(BIO_s_mem()); - BIO_set_mem_eof_return(read, -1); - BIO_set_mem_eof_return(write, -1); - SSL_set_bio(ptrs->ssl, read, write); - - if (isClient != 0) - { - SSL_set_connect_state(ptrs->ssl); - status = SSL_do_handshake(ptrs->ssl); - if (status <= 0) { status = SSL_get_error(ptrs->ssl, (int)status); } - - if (status == SSL_ERROR_WANT_READ) - { - while (BIO_ctrl_pending(write) > 0) - { - i = BIO_read(write, ptrs->encryptedBuffer, sizeof(ptrs->encryptedBuffer)); - ILibDuktape_DuplexStream_WriteData(ptrs->ds_encrypted, ptrs->encryptedBuffer, i); - } - // We're going to drop out now, becuase we need to check for received data - } - } - else - { - SSL_set_accept_state(ptrs->ssl); + duk_dup(ctx, 0); + duk_put_prop_string(ctx, -2, ILibDuktape_SOCKET2OPTIONS); } return 1; } -#endif ILibTransport_DoneState ILibDuktape_net_server_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) { @@ -779,35 +528,61 @@ duk_ret_t ILibDuktape_net_server_socket_Finalizer(duk_context *ctx) return 0; } +int ILibDuktape_net_server_unshiftSink(ILibDuktape_DuplexStream *sender, int unshiftBytes, void *user) +{ + ILibDuktape_net_server_session *session = (ILibDuktape_net_server_session*)user; + session->unshiftBytes = unshiftBytes; + return(unshiftBytes); +} void ILibDuktape_net_server_OnConnect(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void **user) { ILibDuktape_net_server *ptr = (ILibDuktape_net_server*)((void**)ILibMemory_GetExtraMemory(AsyncServerSocketModule, ILibMemory_ASYNCSERVERSOCKET_CONTAINERSIZE))[0]; ILibDuktape_net_server_session *session; + int isTLS = ILibAsyncSocket_IsUsingTls(ConnectionToken); - if (ptr->OnConnection != NULL) - { - duk_push_heapptr(ptr->ctx, ptr->OnConnection); // [func] - duk_push_heapptr(ptr->ctx, ptr->self); // [func][this] - duk_push_object(ptr->ctx); // [func][this][socket] - ILibDuktape_CreateFinalizer(ptr->ctx, ILibDuktape_net_server_socket_Finalizer); - duk_push_fixed_buffer(ptr->ctx, sizeof(ILibDuktape_net_server_session)); // [func][this][socket][buffer] - session = (ILibDuktape_net_server_session*)Duktape_GetBuffer(ptr->ctx, -1, NULL); - memset(session, 0, sizeof(ILibDuktape_net_server_session)); - duk_put_prop_string(ptr->ctx, -2, ILibDuktape_net_Server_Session_buffer); // [func][this][socket] - *user = session; - session->ctx = ptr->ctx; - session->connection = ConnectionToken; - session->self = duk_get_heapptr(ptr->ctx, -1); - session->emitter = ILibDuktape_EventEmitter_Create(ptr->ctx); + duk_push_heapptr(ptr->ctx, ptr->self); // [server] - ILibDuktape_EventEmitter_CreateEvent(session->emitter, "timeout", &(session->OnTimeout)); + duk_get_prop_string(ptr->ctx, -1, "emit"); // [server][emit] + duk_swap_top(ptr->ctx, -2); // [emit][this] + duk_push_string(ptr->ctx, isTLS ? "secureConnection" : "connection"); // [emit][this][connection] - session->stream = ILibDuktape_DuplexStream_Init(ptr->ctx, ILibDuktape_net_server_WriteSink, ILibDuktape_net_server_EndSink, - ILibDuktape_net_server_PauseSink, ILibDuktape_net_server_ResumeSink, session); + duk_push_object(ptr->ctx); // [emit][this][connection][socket] + ILibDuktape_WriteID(ptr->ctx, isTLS ? "tls.serverSocketConnection" : "net.serverSocketConnection"); + ILibDuktape_CreateFinalizer(ptr->ctx, ILibDuktape_net_server_socket_Finalizer); + duk_push_fixed_buffer(ptr->ctx, sizeof(ILibDuktape_net_server_session)); // [emit][this][connection][socket][buffer] + session = (ILibDuktape_net_server_session*)Duktape_GetBuffer(ptr->ctx, -1, NULL); + memset(session, 0, sizeof(ILibDuktape_net_server_session)); + duk_put_prop_string(ptr->ctx, -2, ILibDuktape_net_Server_Session_buffer); // [emit][this][connection][socket] - if (duk_pcall_method(ptr->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ptr->ctx, "net.server.OnConnect(): Exception"); } - duk_pop(ptr->ctx); // ... - } + struct sockaddr_in6 local; + ILibAsyncSocket_GetLocalInterface(ConnectionToken, (struct sockaddr*)&local); + duk_push_string(ptr->ctx, ILibInet_ntop2((struct sockaddr*)&local, ILibScratchPad, sizeof(ILibScratchPad))); // [emit][this][connection][sock][localAddr] + duk_put_prop_string(ptr->ctx, -2, "localAddress"); // [emit][this][connection][sock] + duk_push_int(ptr->ctx, (int)ntohs(local.sin6_port)); // [emit][this][connection][sock][port] + duk_put_prop_string(ptr->ctx, -2, "localPort"); // [emit][this][connection][sock] + ILibAsyncSocket_GetRemoteInterface(ConnectionToken, (struct sockaddr*)&local); + duk_push_string(ptr->ctx, ILibInet_ntop2((struct sockaddr*)&local, ILibScratchPad, sizeof(ILibScratchPad))); // [emit][this][connection][sock][remoteAddr] + duk_put_prop_string(ptr->ctx, -2, "remoteAddress"); // [emit][this][connection][sock] + duk_push_string(ptr->ctx, local.sin6_family == AF_INET6 ? "IPv6" : "IPv4"); // [emit][this][connection][sock][remoteFamily] + duk_put_prop_string(ptr->ctx, -2, "remoteFamily"); // [emit][this][connection][sock] + duk_push_int(ptr->ctx, (int)ntohs(local.sin6_port)); // [emit][this][connection][sock][remotePort] + duk_put_prop_string(ptr->ctx, -2, "remotePort"); // [emit][this][connection][sock] + + + *user = session; + session->ctx = ptr->ctx; + session->connection = ConnectionToken; + session->self = duk_get_heapptr(ptr->ctx, -1); + session->emitter = ILibDuktape_EventEmitter_Create(ptr->ctx); + + + ILibDuktape_EventEmitter_CreateEvent(session->emitter, "timeout", &(session->OnTimeout)); + + session->stream = ILibDuktape_DuplexStream_InitEx(ptr->ctx, ILibDuktape_net_server_WriteSink, ILibDuktape_net_server_EndSink, + ILibDuktape_net_server_PauseSink, ILibDuktape_net_server_ResumeSink, ILibDuktape_net_server_unshiftSink, session); + + if (duk_pcall_method(ptr->ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ptr->ctx, (isTLS ? "tls.server.OnSecureConnection(): Exception" : "net.server.OnConnect(): Exception")); } + duk_pop(ptr->ctx); // ... } void ILibDuktape_net_server_OnDisconnect(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user) { @@ -818,8 +593,9 @@ void ILibDuktape_net_server_OnReceive(ILibAsyncServerSocket_ServerModule AsyncSe { ILibDuktape_net_server_session *session = (ILibDuktape_net_server_session*)*user; + session->unshiftBytes = 0; ILibDuktape_DuplexStream_WriteData(session->stream, buffer + *p_beginPointer, endPointer); - *p_beginPointer = endPointer; + *p_beginPointer = endPointer - session->unshiftBytes; } void ILibDuktape_net_server_OnInterrupt(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user) { @@ -835,62 +611,103 @@ duk_ret_t ILibDuktape_net_server_listen(duk_context *ctx) ILibDuktape_net_server *server = NULL; int i; - unsigned short port = 80; + unsigned short port = 0; int backlog = 0; struct sockaddr_in6 local; int maxConnections = 10; int initalBufferSize = 4096; - + char *host; memset(&local, 0, sizeof(struct sockaddr_in6)); duk_push_this(ctx); duk_get_prop_string(ctx, -1, ILibDuktape_net_Server_buffer); server = (ILibDuktape_net_server*)Duktape_GetBuffer(ctx, -1, NULL); - if (duk_is_object(ctx, 0)) + if (nargs == 0 || !duk_is_object(ctx, 0)) { - // Options - port = (unsigned short)Duktape_GetIntPropertyValue(ctx, 0, "port", 0); - backlog = Duktape_GetIntPropertyValue(ctx, 0, "backlog", 64); - if (nargs > 1 && duk_is_function(ctx, 1)) + duk_push_this(ctx); // [server] + duk_get_prop_string(ctx, -1, "listen"); // [server][listen] + duk_swap_top(ctx, -2); // [listen][this] + duk_push_object(ctx); // [listen][this][Options] + + // let's call listen again, using an Options object + if (nargs > 0 && duk_is_number(ctx, 0)) { - // Callback - ILibDuktape_EventEmitter_AddOn(server->emitter, "listening", duk_require_heapptr(ctx, 1)); + duk_dup(ctx, 0); duk_put_prop_string(ctx, -2, "port"); // [listen][this][Options] + for (i = 1; i < nargs; ++i) + { + if (duk_is_number(ctx, i)) { duk_dup(ctx, i); duk_put_prop_string(ctx, -2, "backlog"); } + if (duk_is_string(ctx, i)) { duk_dup(ctx, i); duk_put_prop_string(ctx, -2, "host"); } + if (duk_is_function(ctx, i)) { duk_dup(ctx, i); break; } // [listen][this][Options][callback] + } + duk_call_method(ctx, i < nargs ? 2 : 1); + return(1); + } + else + { + duk_call_method(ctx, 1); + return(1); + } + } + + // If we are here, we were called with an Options Object + duk_push_this(ctx); // [server] + duk_dup(ctx, 0); // [server][Options] + duk_put_prop_string(ctx, -2, ILibDuktape_SERVER2LISTENOPTIONS); // [server] + + port = (unsigned short)Duktape_GetIntPropertyValue(ctx, 0, "port", 0); + backlog = Duktape_GetIntPropertyValue(ctx, 0, "backlog", 64); + host = Duktape_GetStringPropertyValue(ctx, 0, "host", NULL); + if (nargs > 1 && duk_is_function(ctx, 1)) + { + // Callback + ILibDuktape_EventEmitter_AddOn(server->emitter, "listening", duk_require_heapptr(ctx, 1)); + } + if (host != NULL) + { + ILibResolveEx(host, port, &local); + if (local.sin6_family == AF_UNSPEC) + { + return(ILibDuktape_Error(ctx, "Socket.listen(): Could not resolve host: '%s'", host)); } } else { - for (i = 0; i < nargs; ++i) - { - if (duk_is_number(ctx, i)) - { - if (i == 0) - { - // Port - port = (unsigned short)duk_get_int(ctx, i); - } - else - { - // Backlog - backlog = duk_get_int(ctx, i); - } - } - if (duk_is_function(ctx, i)) { ILibDuktape_EventEmitter_AddOn(server->emitter, "listening", duk_require_heapptr(ctx, i)); } - if (duk_is_string(ctx, i)) - { - ILibResolveEx((char*)duk_require_string(ctx, i), port, &local); - if (local.sin6_family == AF_UNSPEC) - { - return(ILibDuktape_Error(ctx, "server.listen(): Unknown Host '%s'", duk_require_string(ctx, i))); - } - } - } + local.sin6_family = AF_INET; + local.sin6_port = htons(port); } - server->server = ILibCreateAsyncServerSocketModuleWithMemory(Duktape_GetChain(ctx), maxConnections, port, initalBufferSize, 0, + server->server = ILibCreateAsyncServerSocketModuleWithMemoryEx(Duktape_GetChain(ctx), maxConnections, initalBufferSize, (struct sockaddr*)&local, ILibDuktape_net_server_OnConnect, ILibDuktape_net_server_OnDisconnect, ILibDuktape_net_server_OnReceive, ILibDuktape_net_server_OnInterrupt, ILibDuktape_net_server_OnSendOK, sizeof(void*), sizeof(void*)); + if (server->server == NULL) + { + return(ILibDuktape_Error(ctx, "server.listen(): Failed to bind")); + } + ((void**)ILibMemory_GetExtraMemory(server->server, ILibMemory_ASYNCSERVERSOCKET_CONTAINERSIZE))[0] = server; + ILibAsyncServerSocket_SetTag(server->server, server); +#ifndef MICROSTACK_NOTLS + { + duk_push_this(ctx); // [server] + if (duk_has_prop_string(ctx, -1, "addContext")) + { + duk_get_prop_string(ctx, -1, "addContext"); // [server][addContext] + duk_swap_top(ctx, -2); // [addContext][this] + duk_push_string(ctx, "*"); // [addContext][this][*] + duk_eval_string(ctx, "require('tls');"); // [addContext][this][*][tls] + duk_get_prop_string(ctx, -1, "createSecureContext"); // [addContext][this][*][tls][createSecureContext] + duk_swap_top(ctx, -2); // [addContext][this][*][createSecureContext][this] + duk_get_prop_string(ctx, -4, ILibDuktape_SERVER2OPTIONS); // [addContext][this][*][createSecureContext][this][options] + duk_call_method(ctx, 1); // [addContext][this][*][secureContext] + duk_call_method(ctx, 2); duk_pop(ctx); // ... + } + else + { + duk_pop(ctx); // ... + } + } +#endif if (server->OnListening != NULL) { @@ -943,13 +760,25 @@ duk_ret_t ILibDuktape_net_server_address(duk_context *ctx) return(1); } + duk_ret_t ILibDuktape_net_createServer(duk_context *ctx) { int nargs = duk_get_top(ctx); int i; ILibDuktape_net_server *server; + duk_push_current_function(ctx); + int isTLS = Duktape_GetIntPropertyValue(ctx, -1, "tls", 0); + duk_pop(ctx); + duk_push_object(ctx); // [server] + ILibDuktape_WriteID(ctx, isTLS ? "tls.Server" : "net.Server"); + if (nargs > 0 && duk_is_object(ctx, 0)) + { + duk_dup(ctx, 0); // [server][Options] + duk_put_prop_string(ctx, -2, ILibDuktape_SERVER2OPTIONS); // [server] + } + duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_net_server)); // [server][fbuffer] server = (ILibDuktape_net_server*)Duktape_GetBuffer(ctx, -1, NULL); memset(server, 0, sizeof(ILibDuktape_net_server)); @@ -959,7 +788,17 @@ duk_ret_t ILibDuktape_net_createServer(duk_context *ctx) server->ctx = ctx; server->emitter = ILibDuktape_EventEmitter_Create(ctx); ILibDuktape_EventEmitter_CreateEvent(server->emitter, "close", &(server->OnClose)); - ILibDuktape_EventEmitter_CreateEvent(server->emitter, "connection", &(server->OnConnection)); + ILibDuktape_EventEmitter_CreateEventEx(server->emitter, "connection"); +#ifndef MICROSTACK_NOTLS + if (isTLS) + { + ILibDuktape_EventEmitter_CreateEventEx(server->emitter, "secureConnection"); + ILibDuktape_EventEmitter_CreateEventEx(server->emitter, "tlsClientError"); + ILibDuktape_CreateInstanceMethod(ctx, "addContext", ILibDuktape_tls_server_addContext, 2); + duk_push_object(ctx); duk_put_prop_string(ctx, -2, ILibDuktape_SERVER2ContextTable); + if (ILibDuktape_TLS_ctx2server < 0) { ILibDuktape_TLS_ctx2server = SSL_get_ex_new_index(0, "ILibDuktape_TLS_Server index", NULL, NULL, NULL); } + } +#endif ILibDuktape_EventEmitter_CreateEvent(server->emitter, "error", &(server->OnError)); ILibDuktape_EventEmitter_CreateEvent(server->emitter, "listening", &(server->OnListening)); @@ -972,7 +811,7 @@ duk_ret_t ILibDuktape_net_createServer(duk_context *ctx) if (duk_is_function(ctx, i)) { // Callback - ILibDuktape_EventEmitter_AddOn(server->emitter, "connection", duk_require_heapptr(ctx, i)); + ILibDuktape_EventEmitter_AddOn(server->emitter, isTLS ? "secureConnection" : "connection", duk_require_heapptr(ctx, i)); } if (duk_is_object(ctx, i)) { @@ -985,6 +824,7 @@ duk_ret_t ILibDuktape_net_createServer(duk_context *ctx) void ILibDuktape_net_PUSH_net(duk_context *ctx, void *chain) { duk_push_object(ctx); // [net] + ILibDuktape_WriteID(ctx, "net"); duk_push_pointer(ctx, chain); // [net][chain] duk_put_prop_string(ctx, -2, "chain"); // [net] duk_push_c_function(ctx, ILibDuktape_net_socket_constructor, DUK_VARARGS); // [net][constructor] @@ -993,14 +833,9 @@ void ILibDuktape_net_PUSH_net(duk_context *ctx, void *chain) duk_dup(ctx, -2); // [net][constructor][net] duk_put_prop_string(ctx, -2, "net"); // [net][constructor] duk_put_prop_string(ctx, -2, "socket"); // [net] - ILibDuktape_CreateInstanceMethod(ctx, "createServer", ILibDuktape_net_createServer, DUK_VARARGS); + ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "tls", 0, "createServer", ILibDuktape_net_createServer, DUK_VARARGS); ILibDuktape_CreateInstanceMethod(ctx, "createConnection", ILibDuktape_net_createConnection, DUK_VARARGS); ILibDuktape_CreateInstanceMethod(ctx, "connect", ILibDuktape_net_createConnection, DUK_VARARGS); - -#ifndef MICROSTACK_NOTLS - ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "clientMode", 1, "createClientSslStream", ILibDuktape_net_sslStream_create, DUK_VARARGS); - ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "clientMode", 0, "createServerSslStream", ILibDuktape_net_sslStream_create, DUK_VARARGS); -#endif } duk_ret_t ILibDuktape_globalTunnel_end(duk_context *ctx) { @@ -1099,10 +934,360 @@ ILibDuktape_globalTunnel_data* ILibDuktape_GetGlobalTunnel(duk_context *ctx) return retVal; } + +#ifndef MICROSTACK_NOTLS +SSL_CTX* ILibDuktape_TLS_SecureContext_GetCTX(duk_context *ctx, void *secureContext) +{ + SSL_CTX *retVal = NULL; + + duk_push_heapptr(ctx, secureContext); // [context] + if (duk_has_prop_string(ctx, -1, ILibDuktape_SecureContext2SSLCTXPTR)) + { + retVal = (SSL_CTX*)Duktape_GetPointerProperty(ctx, -1, ILibDuktape_SecureContext2SSLCTXPTR); + } + duk_pop(ctx); // ... + return(retVal); +} +void ILibDuktape_TLS_X509_PUSH(duk_context *ctx, X509* cert) +{ + char hash[UTIL_SHA384_HASHSIZE]; + char fingerprint[150]; + + util_keyhash2(cert, hash); + util_tohex2(hash, UTIL_SHA384_HASHSIZE, fingerprint); + + duk_push_object(ctx); // [cert] + duk_push_string(ctx, fingerprint); // [cert][fingerprint] + duk_put_prop_string(ctx, -2, "fingerprint"); // [cert] +} +int ILibDuktape_TLS_verify(int preverify_ok, X509_STORE_CTX *storectx) +{ + STACK_OF(X509) *certChain = X509_STORE_CTX_get_chain(storectx); + SSL *ssl = (SSL*)X509_STORE_CTX_get_ex_data(storectx, SSL_get_ex_data_X509_STORE_CTX_idx()); + ILibDuktape_net_socket *data = (ILibDuktape_net_socket*)SSL_get_ex_data(ssl, ILibDuktape_TLS_ctx2socket); + + int i; + int retVal = 0; + + duk_push_heapptr(data->ctx, data->object); // [Socket] + duk_get_prop_string(data->ctx, -1, ILibDuktape_SOCKET2OPTIONS); // [Socket][Options] + if (Duktape_GetBooleanProperty(data->ctx, -1, "rejectUnauthorized", 1)) { duk_pop_2(data->ctx); return(preverify_ok); } + void *OnVerify = Duktape_GetHeapptrProperty(data->ctx, -1, "checkServerIdentity"); + + if (OnVerify == NULL) { return(1); } + + duk_push_heapptr(data->ctx, OnVerify); // [func] + duk_push_heapptr(data->ctx, data->object); // [func][this] + duk_push_array(data->ctx); // [func][this][certs] + for (i = 0; i < sk_X509_num(certChain); ++i) + { + ILibDuktape_TLS_X509_PUSH(data->ctx, sk_X509_value(certChain, i)); // [func][this][certs][cert] + duk_put_prop_index(data->ctx, -2, i); // [func][this][certs] + } + retVal = duk_pcall_method(data->ctx, 1) == 0 ? 1 : 0; // [undefined] + duk_pop(data->ctx); // ... + return retVal; +} +int ILibDuktape_TLS_server_verify(int preverify_ok, X509_STORE_CTX *storectx) +{ + STACK_OF(X509) *certChain = X509_STORE_CTX_get_chain(storectx); + SSL *ssl = (SSL*)X509_STORE_CTX_get_ex_data(storectx, SSL_get_ex_data_X509_STORE_CTX_idx()); + ILibDuktape_net_server *data = (ILibDuktape_net_server*)SSL_get_ex_data(ssl, ILibDuktape_TLS_ctx2server); + + int i; + int retVal = 0; + + duk_push_heapptr(data->ctx, data->self); // [Server] + duk_get_prop_string(data->ctx, -1, ILibDuktape_SERVER2OPTIONS); // [Server][Options] + if (Duktape_GetBooleanProperty(data->ctx, -1, "rejectUnauthorized", 1)) { duk_pop_2(data->ctx); return(preverify_ok); } + void *OnVerify = Duktape_GetHeapptrProperty(data->ctx, -1, "checkClientIdentity"); + + if (OnVerify == NULL) { return(1); } + + duk_push_heapptr(data->ctx, OnVerify); // [func] + duk_push_heapptr(data->ctx, data->self); // [func][this] + duk_push_array(data->ctx); // [func][this][certs] + for (i = 0; i < sk_X509_num(certChain); ++i) + { + ILibDuktape_TLS_X509_PUSH(data->ctx, sk_X509_value(certChain, i)); // [func][this][certs][cert] + duk_put_prop_index(data->ctx, -2, i); // [func][this][certs] + } + retVal = duk_pcall_method(data->ctx, 1) == 0 ? 1 : 0; // [undefined] + duk_pop(data->ctx); // ... + return retVal; +} +void ILibDuktape_tls_server_OnSSL(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, void *ConnectionToken, SSL* ctx, void **user) +{ + ILibDuktape_net_server *server = (ILibDuktape_net_server*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); + + if (ctx != NULL && ILibDuktape_TLS_ctx2server) + { + SSL_set_ex_data(ctx, ILibDuktape_TLS_ctx2server, server); + } +} +static int ILibDuktape_tls_server_sniCallback(SSL *s, int *ad, void *arg) +{ + const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); + ILibDuktape_net_server *data = (ILibDuktape_net_server*)SSL_get_ex_data(s, ILibDuktape_TLS_ctx2server); + + duk_push_heapptr(data->ctx, data->self); // [server] + duk_get_prop_string(data->ctx, -1, ILibDuktape_SERVER2ContextTable); // [server][table] + if (duk_has_prop_string(data->ctx, -1, servername)) + { + duk_get_prop_string(data->ctx, -1, servername); // [server][table][secureContext] + SSL_CTX *newCTX = ILibDuktape_TLS_SecureContext_GetCTX(data->ctx, duk_get_heapptr(data->ctx, -1)); + if (newCTX != NULL) + { + SSL_set_SSL_CTX(s, newCTX); + } + duk_pop(data->ctx); // [server][table] + } + duk_pop_2(data->ctx); // ... + return(SSL_TLSEXT_ERR_OK); +} +duk_ret_t ILibDuktape_tls_server_addContext(duk_context *ctx) +{ + duk_size_t hostLen; + char *host = (char*)duk_get_lstring(ctx, 0, &hostLen); + void *context = duk_require_heapptr(ctx, 1); + + duk_push_this(ctx); // [server] + duk_get_prop_string(ctx, -1, ILibDuktape_SERVER2ContextTable); // [server][table] + duk_dup(ctx, 0); // [server][table][host] + duk_dup(ctx, 1); // [server][table][host][context] + duk_put_prop(ctx, -3); // [server][table] + + if (hostLen == 1 && strncasecmp(host, "*", 1) == 0) + { + // Default CTX + SSL_CTX *ssl_ctx = ILibDuktape_TLS_SecureContext_GetCTX(ctx, context); + duk_get_prop_string(ctx, -2, ILibDuktape_SERVER2OPTIONS); // [server][table][options] + if (Duktape_GetBooleanProperty(ctx, -1, "requestCert", 0) || Duktape_GetHeapptrProperty(ctx, -1, "checkClientIdentity")!=NULL) + { + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, ILibDuktape_TLS_server_verify); + } + duk_get_prop_string(ctx, -3, ILibDuktape_net_Server_buffer);// [server][table][options][buffer] + ILibDuktape_net_server *server = (ILibDuktape_net_server*)Duktape_GetBuffer(ctx, -1, NULL); + if (server->server != NULL) + { +#ifdef MICROSTACK_TLS_DETECT + ILibAsyncServerSocket_SetSSL_CTX(server->server, ssl_ctx, 1); +#else + ILibAsyncServerSocket_SetSSL_CTX(server->server, ssl_ctx); +#endif + ILibAsyncServerSocket_SSL_SetSink(server->server, ILibDuktape_tls_server_OnSSL); + } + SSL_CTX_set_tlsext_servername_callback(ssl_ctx, ILibDuktape_tls_server_sniCallback); + } + + return(0); +} +void ILibDuktape_TLS_connect_resolveError(duk_context *ctx, void ** args, int argsLen) +{ + ILibDuktape_net_socket *data = (ILibDuktape_net_socket*)args[0]; + + duk_push_heapptr(ctx, data->object); // [socket] + duk_get_prop_string(ctx, -2, "emit"); // [socket][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_dup(ctx, -3); // [emit][this] + duk_push_string(ctx, "error"); // [emit][this][error] + duk_push_heapptr(ctx, args[1]); // [emit][this][error][err] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "tls.socket.OnError(): "); } + duk_pop(ctx); +} +duk_ret_t ILibDuktape_TLS_connect(duk_context *ctx) +{ + int nargs = duk_get_top(ctx), i; + if (nargs > 0 && duk_is_number(ctx, 0)) + { + // tls.connect(port[, host][, options][, callback]) + // let's convert to the other overload + duk_push_this(ctx); // [TLS] + duk_get_prop_string(ctx, -1, "connect"); // [TLS][connect] + duk_swap_top(ctx, -2); // [connect][this] + for (i = 1; i < nargs; ++i) + { + if (duk_is_object(ctx, i)) + { + duk_dup(ctx, i); // [connect][this][Options] + break; + } + } + if (i == nargs) { duk_push_object(ctx); } // [connect][this][Options] + duk_dup(ctx, 0); // [connect][this][Options][port] + duk_put_prop_string(ctx, -2, "port"); + if (nargs > 1 && duk_is_string(ctx, 1)) + { + duk_dup(ctx, 1); // [connect][this][Options][host] + } + else + { + duk_push_string(ctx, "127.0.0.1"); // [connect][this][Options][host] + } + duk_put_prop_string(ctx, -2, "host"); // [connect][this][Options] + for (i = 1; i < nargs; ++i) + { + if (duk_is_function(ctx, i)) + { + duk_dup(ctx, i); // [connect][this][Options][callback] + break; + } + } + duk_call_method(ctx, i == nargs ? 1 : 2); // [socket] + return(1); + } + + // tls.connect(options[, callback]) + ILibAsyncSocket_SocketModule module = ILibCreateAsyncSocketModuleWithMemory(Duktape_GetChain(ctx), 4096, ILibDuktape_net_socket_OnData, ILibDuktape_net_socket_OnConnect, ILibDuktape_net_socket_OnDisconnect, ILibDuktape_net_socket_OnSendOK, sizeof(ILibDuktape_net_socket)); + ILibDuktape_net_socket *data = (ILibDuktape_net_socket*)((ILibChain_Link*)module)->ExtraMemoryPtr; + + data->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + SSL_CTX_set_options(data->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); + SSL_CTX_set_verify(data->ssl_ctx, SSL_VERIFY_PEER, ILibDuktape_TLS_verify); /* Ask for authentication */ + + if (ILibDuktape_TLS_ctx2socket < 0) + { + ILibDuktape_TLS_ctx2socket = SSL_get_ex_new_index(0, "ILibDuktape_TLS index", NULL, NULL, NULL); + } + + ILibDuktape_net_socket_PUSH(ctx, module); // [socket] + ILibDuktape_WriteID(ctx, "tls.socket"); + duk_dup(ctx, 0); // [socket][options] + duk_put_prop_string(ctx, -2, ILibDuktape_SOCKET2OPTIONS); // [socket] + ILibDuktape_EventEmitter_CreateEventEx(data->emitter, "secureConnect"); + if (nargs > 0 && duk_is_function(ctx, 1)) + { + ILibDuktape_EventEmitter_AddOnce(data->emitter, "secureConnect", duk_require_heapptr(ctx, 1)); + } + + char *host = Duktape_GetStringPropertyValue(ctx, 0, "host", "127.0.0.1"); + int port = Duktape_GetIntPropertyValue(ctx, 0, "port", 0); + struct sockaddr_in6 dest; + struct sockaddr_in6 proxy; + memset(&dest, 0, sizeof(struct sockaddr_in6)); + memset(&proxy, 0, sizeof(struct sockaddr_in6)); + + if (duk_has_prop_string(ctx, 0, "proxy")) + { + duk_get_prop_string(ctx, 0, "proxy"); + ILibResolveEx(Duktape_GetStringPropertyValue(ctx, -1, "host", NULL), (unsigned short)Duktape_GetIntPropertyValue(ctx, -1, "port", 0), &proxy); + duk_pop(ctx); + } + ILibResolveEx(host, (unsigned short)port, &dest); + if (dest.sin6_family == AF_UNSPEC || (duk_has_prop_string(ctx, 0, "proxy") && proxy.sin6_family == AF_UNSPEC)) + { + // Can't resolve... Delay event emit, until next event loop, because if app called net.createConnection(), they don't have the socket yet + duk_push_error_object(ctx, DUK_ERR_ERROR, "tls.socket.connect(): Cannot resolve host '%s'", host); + ILibDuktape_Immediate(ctx, (void*[]) { data, duk_get_heapptr(ctx, -1) }, 2, ILibDuktape_TLS_connect_resolveError); + duk_pop(ctx); // [socket] + } + else + { + if (duk_has_prop_string(ctx, 0, "proxy")) + { + duk_get_prop_string(ctx, 0, "proxy"); + ILibAsyncSocket_ConnectToProxy(data->socketModule, NULL, (struct sockaddr*)&dest, (struct sockaddr*)&proxy, Duktape_GetStringPropertyValue(ctx, -1, "username", NULL), Duktape_GetStringPropertyValue(ctx, -1, "password", NULL), NULL, data); + duk_pop(ctx); + } + else + { + ILibAsyncSocket_ConnectTo(data->socketModule, NULL, (struct sockaddr*)&dest, NULL, data); + } + data->ssl = ILibAsyncSocket_SetSSLContext(data->socketModule, data->ssl_ctx, ILibAsyncSocket_TLS_Mode_Client); + SSL_set_ex_data(data->ssl, ILibDuktape_TLS_ctx2socket, data); + SSL_set_tlsext_host_name(data->ssl, host); + } + return(1); +} +duk_ret_t ILibDuktape_TLS_secureContext_Finalizer(duk_context *ctx) +{ + SSL_CTX_free(ILibDuktape_TLS_SecureContext_GetCTX(ctx, duk_require_heapptr(ctx, 0))); + + duk_get_prop_string(ctx, 0, ILibDuktape_SecureContext2CertBuffer); + struct util_cert *cert = (struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL); + util_freecert(cert); + return(0); +} +duk_ret_t ILibDuktape_TLS_createSecureContext(duk_context *ctx) +{ + duk_push_object(ctx); // [secureContext] + duk_push_fixed_buffer(ctx, sizeof(struct util_cert)); // [secureContext][cert] + struct util_cert *cert = (struct util_cert*)Duktape_GetBuffer(ctx, -1, NULL); + duk_put_prop_string(ctx, -2, ILibDuktape_SecureContext2CertBuffer); // [secureContext] + memset(cert, 0, sizeof(struct util_cert)); + ILibDuktape_CreateFinalizer(ctx, ILibDuktape_TLS_secureContext_Finalizer); + + duk_size_t secureProtocolLen; + char *secureProtocol = (char*)Duktape_GetStringPropertyValueEx(ctx, 0, "secureProtocol", "SSLv23_server_method", &secureProtocolLen); + SSL_CTX *ssl_ctx = NULL; + + if (secureProtocolLen == 20 && strncmp(secureProtocol, "SSLv23_server_method", 20) == 0) + { + ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + SSL_CTX_set_options(ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); + } + else if (secureProtocolLen == 11 && strncmp(secureProtocol, "DTLS_method", 11) == 0) + { + ssl_ctx = SSL_CTX_new(DTLS_method()); + } + else + { + return(ILibDuktape_Error(ctx, "tls.createSecureContext(): secureProtocol[%s] not supported at this time", secureProtocol)); + } + duk_push_pointer(ctx, ssl_ctx); duk_put_prop_string(ctx, -2, ILibDuktape_SecureContext2SSLCTXPTR); + + if (duk_has_prop_string(ctx, 0, "pfx") && duk_has_prop_string(ctx, 0, "passphrase")) + { + duk_get_prop_string(ctx, 0, "pfx"); // [secureContext][pfx] + duk_size_t pfxLen; + char *pfx = (char*)Duktape_GetBuffer(ctx, -1, &pfxLen); + if (util_from_p12(pfx, (int)pfxLen, Duktape_GetStringPropertyValue(ctx, 0, "passphrase", ""), cert) == 0) + { + // Failed to load certificate + return(ILibDuktape_Error(ctx, "tls.createSecureContext(): Invalid passphrase")); + } + duk_pop(ctx); + SSL_CTX_use_certificate(ssl_ctx, cert->x509); + SSL_CTX_use_PrivateKey(ssl_ctx, cert->pkey); + } + + return(1); +} +duk_ret_t ILibDuktape_TLS_generateCertificate(duk_context *ctx) +{ + char *passphrase = (char*)duk_require_string(ctx, 0); + int len; + struct util_cert cert; + char *data; + + len = util_mkCert(NULL, &(cert), 3072, 10000, "localhost", CERTIFICATE_TLS_CLIENT, NULL); + len = util_to_p12(cert, passphrase, &data); + + duk_push_fixed_buffer(ctx, len); + memcpy_s((void*)Duktape_GetBuffer(ctx, -1, NULL), len, data, len); + duk_push_buffer_object(ctx, -1, 0, len, DUK_BUFOBJ_NODEJS_BUFFER); + + util_free(data); + util_freecert(&cert); + return 1; +} +void ILibDuktape_tls_PUSH(duk_context *ctx, void *chain) +{ + duk_push_object(ctx); // [TLS] + ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "tls", 1, "createServer", ILibDuktape_net_createServer, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "connect", ILibDuktape_TLS_connect, DUK_VARARGS); + ILibDuktape_CreateInstanceMethod(ctx, "createSecureContext", ILibDuktape_TLS_createSecureContext, 1); + ILibDuktape_CreateInstanceMethod(ctx, "generateCertificate", ILibDuktape_TLS_generateCertificate, 1); +} +#endif + void ILibDuktape_net_init(duk_context * ctx, void * chain) { ILibDuktape_ModSearch_AddHandler(ctx, "net", ILibDuktape_net_PUSH_net); ILibDuktape_ModSearch_AddHandler(ctx, "global-tunnel", ILibDuktape_globalTunnel_PUSH); +#ifndef MICROSTACK_NOTLS + ILibDuktape_ModSearch_AddHandler(ctx, "tls", ILibDuktape_tls_PUSH); +#endif } diff --git a/microscript/ILibDuktape_net.h b/microscript/ILibDuktape_net.h index 862901f..4049076 100644 --- a/microscript/ILibDuktape_net.h +++ b/microscript/ILibDuktape_net.h @@ -25,6 +25,7 @@ limitations under the License. #include "microstack/ILibParsers.h" +#define ILibDuktape_SOCKET2OPTIONS "\xFF_NET_SOCKET2OPTIONS" void ILibDuktape_net_init(duk_context *ctx, void *chain); typedef struct ILibDuktape_globalTunnel_data diff --git a/microscript/ILibduktape_EventEmitter.c b/microscript/ILibduktape_EventEmitter.c index b575f85..6cc1b76 100644 --- a/microscript/ILibduktape_EventEmitter.c +++ b/microscript/ILibduktape_EventEmitter.c @@ -182,7 +182,7 @@ duk_ret_t ILibDuktape_EventEmitter_emit(duk_context *ctx) duk_pop_2(ctx); // [this] self = duk_get_heapptr(ctx, -1); - if (data->eventTable == NULL) { return 0; } // This probably means the finalizer was already run on the eventEmitter + if (data->eventTable == NULL) { duk_push_false(ctx); return(1); } // This probably means the finalizer was already run on the eventEmitter eventList = ILibHashtable_Get(data->eventTable, NULL, name, (int)nameLen); if (eventList == NULL) { return ILibDuktape_Error(ctx, "EventEmitter.emit(): Event '%s' not found", name); } @@ -245,8 +245,8 @@ duk_ret_t ILibDuktape_EventEmitter_emit(duk_context *ctx) } duk_pop(ctx); // ... } - - return 0; + duk_push_boolean(ctx, i > 1 ? 1 : 0); + return(1); } int ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter *emitter, char *eventName, void *heapptr) { @@ -261,13 +261,13 @@ int ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter *emitter, char *ev return retVal; } -int ILibDuktape_EventEmitter_AddOnceEx2(duk_context *ctx, duk_idx_t idx, char *eventName, duk_c_function func, duk_idx_t funcArgs) +int ILibDuktape_EventEmitter_AddOnceEx3(duk_context *ctx, duk_idx_t idx, char *eventName, duk_c_function func) { int retVal = 1; duk_dup(ctx, idx); // [obj] ILibDuktape_Push_ObjectStash(ctx); // [obj][stash] - duk_push_c_function(ctx, func, funcArgs); // [obj][stash][func] + duk_push_c_function(ctx, func, DUK_VARARGS); // [obj][stash][func] duk_dup(ctx, -1); // [obj][stash][func][func] duk_put_prop_string(ctx, -3, Duktape_GetStashKey(duk_get_heapptr(ctx, -1))); // [obj][stash][func] duk_get_prop_string(ctx, -3, "once"); // [obj][stash][func][once] @@ -316,10 +316,12 @@ duk_ret_t ILibDuktape_EventEmitter_on(duk_context *ctx) ILibDuktape_EventEmitter *data; int once; void *eventList, *node, *dispatcher, **hptr; - int i, count; + int i, count, prepend; duk_push_current_function(ctx); once = Duktape_GetIntPropertyValue(ctx, -1, "once", 0); + prepend = Duktape_GetIntPropertyValue(ctx, -1, "prepend", 0); + duk_push_this(ctx); duk_get_prop_string(ctx, -1, ILibDuktape_EventEmitter_TempObject); @@ -334,7 +336,7 @@ duk_ret_t ILibDuktape_EventEmitter_on(duk_context *ctx) dispatcher = ILibHashtable_Get(data->eventTable, ILibDuktape_EventEmitter_SetterFunc, propName, (int)propNameLen); if (dispatcher == NULL) { return(ILibDuktape_Error(ctx, "EventEmitter.on(): Internal error with Event '%s'", propName)); } - node = ILibLinkedList_AddTail(eventList, callback); + node = prepend ? ILibLinkedList_AddHead(eventList, callback) : ILibLinkedList_AddTail(eventList, callback); ((int*)ILibLinkedList_GetExtendedMemory(node))[0] = once; data->totalListeners[0]++; @@ -462,8 +464,11 @@ ILibDuktape_EventEmitter* ILibDuktape_EventEmitter_Create(duk_context *ctx) retVal->object = duk_get_heapptr(ctx, -1); retVal->eventTable = ILibHashtable_Create(); - ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "once", 1, "once", ILibDuktape_EventEmitter_on, 2); - ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "once", 0, "on", ILibDuktape_EventEmitter_on, 2); + ILibDuktape_CreateInstanceMethodWithProperties(ctx, "once", ILibDuktape_EventEmitter_on, 2, 2, "once", duk_push_int_ex(ctx, 1), "prepend", duk_push_int_ex(ctx, 0)); + ILibDuktape_CreateInstanceMethodWithProperties(ctx, "on", ILibDuktape_EventEmitter_on, 2, 2, "once", duk_push_int_ex(ctx, 0), "prepend", duk_push_int_ex(ctx, 0)); + ILibDuktape_CreateInstanceMethodWithProperties(ctx, "prependOnceListener", ILibDuktape_EventEmitter_on, 2, 2, "once", duk_push_int_ex(ctx, 1), "prepend", duk_push_int_ex(ctx, 1)); + ILibDuktape_CreateInstanceMethodWithProperties(ctx, "prependListener", ILibDuktape_EventEmitter_on, 2, 2, "once", duk_push_int_ex(ctx, 0), "prepend", duk_push_int_ex(ctx, 1)); + ILibDuktape_CreateInstanceMethod(ctx, "removeListener", ILibDuktape_EventEmitter_removeListener, 2); ILibDuktape_CreateInstanceMethod(ctx, "removeAllListeners", ILibDuktape_EventEmitter_removeAllListeners, DUK_VARARGS); ILibDuktape_CreateInstanceMethod(ctx, "emit", ILibDuktape_EventEmitter_emit, DUK_VARARGS); @@ -816,6 +821,15 @@ duk_ret_t ILibDuktape_EventEmitter_Inherits_addMethod(duk_context *ctx) ILibDuktape_CreateProperty_InstanceMethodEx(ctx, (char*)duk_require_string(ctx, 0), duk_require_heapptr(ctx, 1)); return(0); } +duk_ret_t ILibDuktape_EventEmitter_EmitterUtils_Finalizer(duk_context *ctx) +{ + duk_get_prop_string(ctx, 0, "\xFF_MainObject"); // [obj] + duk_get_prop_string(ctx, -1, "emit"); // [obj][emit] + duk_swap_top(ctx, -2); // [emit][this] + duk_push_string(ctx, "~"); // [emit][this][~] + duk_call_method(ctx, 1); + return(0); +} duk_ret_t ILibDuktape_EventEmitter_Inherits(duk_context *ctx) { ILibDuktape_EventEmitter *emitter; @@ -823,12 +837,16 @@ duk_ret_t ILibDuktape_EventEmitter_Inherits(duk_context *ctx) duk_dup(ctx, 0); // [target] emitter = ILibDuktape_EventEmitter_Create(ctx); duk_push_object(ctx); // [target][emitterUtils] + duk_dup(ctx, -2); // [target][emitterUtils][target] + duk_put_prop_string(ctx, -2, "\xFF_MainObject"); // [target][emitterUtils] duk_dup(ctx, -1); // [target][emitterUtils][dup] duk_put_prop_string(ctx, -3, "\xFF_emitterUtils"); // [target][emitterUtils] duk_push_pointer(ctx, emitter); // [target][emitterUtils][ptr] duk_put_prop_string(ctx, -2, "emitter"); // [target][emitterUtils] + ILibDuktape_CreateFinalizer(ctx, ILibDuktape_EventEmitter_EmitterUtils_Finalizer); ILibDuktape_CreateInstanceMethod(ctx, "createEvent", ILibDuktape_EventEmitter_Inherits_createEvent, 1); ILibDuktape_CreateInstanceMethod(ctx, "addMethod", ILibDuktape_EventEmitter_Inherits_addMethod, 2); + ILibDuktape_EventEmitter_CreateEventEx(emitter, "~"); return 1; } void ILibDuktape_EventEmitter_PUSH(duk_context *ctx, void *chain) @@ -840,3 +858,54 @@ void ILibDuktape_EventEmitter_Init(duk_context *ctx) { ILibDuktape_ModSearch_AddHandler(ctx, "events", ILibDuktape_EventEmitter_PUSH); } +duk_ret_t ILibDuktape_EventEmitter_ForwardEvent_Sink(duk_context *ctx) +{ + int nargs = duk_get_top(ctx); + int i; + char *name; + duk_push_current_function(ctx); // [func] + duk_get_prop_string(ctx, -1, "targetObject"); // [func][obj] + duk_get_prop_string(ctx, -1, "emit"); // [func][obj][emit] + duk_swap_top(ctx, -2); // [func][emit][this] + duk_get_prop_string(ctx, -3, "targetName"); // [func][emit][this][name] + name = (char*)duk_get_string(ctx, -1); + + for (i = 0; i < nargs; ++i) + { + duk_dup(ctx, i); // [func][emit][this][name][...args...] + } + + if (duk_pcall_method(ctx, 1 + nargs) != 0) { return(ILibDuktape_Error(ctx, "EventEmitter.ForwardEvent() [%s]: %s", name, duk_safe_to_string(ctx, -1))); } + return(0); +} +void ILibDuktape_EventEmitter_ForwardEvent(duk_context *ctx, duk_idx_t eventSourceIndex, char *sourceEventName, duk_idx_t eventTargetIndex, char *targetEventName) +{ + void *target; + duk_dup(ctx, eventTargetIndex); // [targetObject] + target = duk_get_heapptr(ctx, -1); + duk_pop(ctx); // ... + duk_dup(ctx, eventSourceIndex); // [sourceObject] + duk_get_prop_string(ctx, -1, "on"); // [sourceObject][on] + duk_swap_top(ctx, -2); // [on][this] + duk_push_string(ctx, sourceEventName); // [on][this][name] + duk_push_c_function(ctx, ILibDuktape_EventEmitter_ForwardEvent_Sink, DUK_VARARGS); // [on][this][name][sink] + duk_push_heapptr(ctx, target); // [on][this][name][sink][targetObject] + duk_put_prop_string(ctx, -2, "targetObject"); // [on][this][name][sink] + duk_push_string(ctx, targetEventName); // [on][this][name][sink][targetName] + duk_put_prop_string(ctx, -2, "targetName"); // [on][this][name][sink] + if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "EventEmitter_ForwardEvent(): "); } + duk_pop(ctx); // ... +} +int ILibDuktape_EventEmitter_AddOnEx(duk_context *ctx, duk_idx_t idx, char *eventName, duk_c_function func) +{ + int retVal = 1; + duk_dup(ctx, idx); // [object] + duk_get_prop_string(ctx, -1, "on"); // [object][on] + duk_swap_top(ctx, -2); // [on][this] + duk_push_string(ctx, eventName); // [on][this][name] + duk_push_c_function(ctx, func, DUK_VARARGS); // [on][this][name][func] + if (duk_pcall_method(ctx, 2) != 0) { retVal = 0; } + + duk_pop(ctx); // ... + return(retVal); +} diff --git a/microstack/ILibAsyncServerSocket.c b/microstack/ILibAsyncServerSocket.c index 5579632..6d8d4d8 100644 --- a/microstack/ILibAsyncServerSocket.c +++ b/microstack/ILibAsyncServerSocket.c @@ -621,34 +621,39 @@ void ILibAsyncServerSocket_RemoveFromChain(ILibAsyncServerSocket_ServerModule se */ ILibAsyncServerSocket_ServerModule ILibCreateAsyncServerSocketModuleWithMemory(void *Chain, int MaxConnections, unsigned short PortNumber, int initialBufferSize, int loopbackFlag, ILibAsyncServerSocket_OnConnect OnConnect, ILibAsyncServerSocket_OnDisconnect OnDisconnect, ILibAsyncServerSocket_OnReceive OnReceive, ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK, int ServerUserMappedMemorySize, int SessionUserMappedMemorySize) { - int i; - int ra = 1; - int off = 0; - int receivingAddressLength = sizeof(struct sockaddr_in6); struct sockaddr_in6 localif; - struct sockaddr_in6 localAddress; - struct ILibAsyncServerSocketModule *RetVal; - memset(&localif, 0, sizeof(struct sockaddr_in6)); + if (loopbackFlag != 2 && ILibDetectIPv6Support()) { // Setup the IPv6 any or loopback address, this socket will also work for IPv4 traffic on IPv6 stack localif.sin6_family = AF_INET6; - localif.sin6_addr = (loopbackFlag != 0?in6addr_loopback:in6addr_any); + localif.sin6_addr = (loopbackFlag != 0 ? in6addr_loopback : in6addr_any); localif.sin6_port = htons(PortNumber); } else { // IPv4-only detected localif.sin6_family = AF_INET; -#ifdef WINSOCK2 - ((struct sockaddr_in*)&localif)->sin_addr.S_un.S_addr = htonl((loopbackFlag != 0?INADDR_LOOPBACK:INADDR_ANY)); +#ifdef WIN32 + ((struct sockaddr_in*)&localif)->sin_addr.S_un.S_addr = htonl((loopbackFlag != 0 ? INADDR_LOOPBACK : INADDR_ANY)); #else - ((struct sockaddr_in*)&localif)->sin_addr.s_addr = htonl((loopbackFlag != 0?INADDR_LOOPBACK:INADDR_ANY)); + ((struct sockaddr_in*)&localif)->sin_addr.s_addr = htonl((loopbackFlag != 0 ? INADDR_LOOPBACK : INADDR_ANY)); #endif ((struct sockaddr_in*)&localif)->sin_port = htons(PortNumber); } + return(ILibCreateAsyncServerSocketModuleWithMemoryEx(Chain, MaxConnections, initialBufferSize, (struct sockaddr*)&localif, OnConnect, OnDisconnect, OnReceive, OnInterrupt, OnSendOK, ServerUserMappedMemorySize, SessionUserMappedMemorySize)); +} +ILibAsyncServerSocket_ServerModule ILibCreateAsyncServerSocketModuleWithMemoryEx(void *Chain, int MaxConnections, int initialBufferSize, struct sockaddr *local, ILibAsyncServerSocket_OnConnect OnConnect, ILibAsyncServerSocket_OnDisconnect OnDisconnect, ILibAsyncServerSocket_OnReceive OnReceive, ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK, int ServerUserMappedMemorySize, int SessionUserMappedMemorySize) +{ + int i; + int ra = 1; + int off = 0; + int receivingAddressLength = sizeof(struct sockaddr_in6); + struct ILibAsyncServerSocketModule *RetVal; + struct sockaddr_in6 localAddress; + // Instantiate a new AsyncServer module RetVal = (struct ILibAsyncServerSocketModule*)ILibChain_Link_Allocate(sizeof(struct ILibAsyncServerSocketModule), ServerUserMappedMemorySize); @@ -664,15 +669,14 @@ ILibAsyncServerSocket_ServerModule ILibCreateAsyncServerSocketModuleWithMemory(v RetVal->MaxConnection = MaxConnections; RetVal->AsyncSockets = (void**)malloc(MaxConnections * sizeof(void*)); if (RetVal->AsyncSockets == NULL) { free(RetVal); ILIBMARKPOSITION(253); return NULL; } - RetVal->portNumber = (unsigned short)PortNumber; - RetVal->loopbackFlag = loopbackFlag; - RetVal->initialPortNumber = PortNumber; - + RetVal->portNumber = ntohs(((struct sockaddr_in6*)local)->sin6_port); + RetVal->initialPortNumber = RetVal->portNumber; + // Get our listening socket - if ((RetVal->ListenSocket = socket(localif.sin6_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { free(RetVal->AsyncSockets); free(RetVal); return 0; } + if ((RetVal->ListenSocket = socket(((struct sockaddr_in6*)local)->sin6_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { free(RetVal->AsyncSockets); free(RetVal); return 0; } // Setup the IPv6 & IPv4 support on same socket - if (localif.sin6_family == AF_INET6) if (setsockopt(RetVal->ListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) != 0) ILIBCRITICALERREXIT(253); + if (((struct sockaddr_in6*)local)->sin6_family == AF_INET6) if (setsockopt(RetVal->ListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)) != 0) ILIBCRITICALERREXIT(253); #ifdef SO_NOSIGPIPE setsockopt(RetVal->ListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&ra, sizeof(int)); // Turn off SIGPIPE if writing to disconnected socket @@ -688,9 +692,9 @@ ILibAsyncServerSocket_ServerModule ILibCreateAsyncServerSocketModuleWithMemory(v // Bind the socket #if defined(WIN32) - if (bind(RetVal->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { closesocket(RetVal->ListenSocket); free(RetVal->AsyncSockets); free(RetVal); return 0; } + if (bind(RetVal->ListenSocket, local, INET_SOCKADDR_LENGTH(((struct sockaddr_in6*)local)->sin6_family)) != 0) { closesocket(RetVal->ListenSocket); free(RetVal->AsyncSockets); free(RetVal); return 0; } #else - if (bind(RetVal->ListenSocket, (struct sockaddr*)&localif, INET_SOCKADDR_LENGTH(localif.sin6_family)) != 0) { close(RetVal->ListenSocket); free(RetVal->AsyncSockets); free(RetVal); return 0; } + if (bind(RetVal->ListenSocket, local, INET_SOCKADDR_LENGTH(((struct sockaddr_in6*)local)->sin6_family)) != 0) { close(RetVal->ListenSocket); free(RetVal->AsyncSockets); free(RetVal); return 0; } #endif // Fetch the local port number diff --git a/microstack/ILibAsyncServerSocket.h b/microstack/ILibAsyncServerSocket.h index 3981160..8022f54 100644 --- a/microstack/ILibAsyncServerSocket.h +++ b/microstack/ILibAsyncServerSocket.h @@ -114,6 +114,8 @@ extern const int ILibMemory_ASYNCSERVERSOCKET_CONTAINERSIZE; #define ILibCreateAsyncServerSocketModule(Chain, MaxConnections, PortNumber, initialBufferSize, loopbackFlag, OnConnect, OnDisconnect, OnReceive, OnInterrupt, OnSendOK) ILibCreateAsyncServerSocketModuleWithMemory(Chain, MaxConnections, PortNumber, initialBufferSize, loopbackFlag, OnConnect, OnDisconnect, OnReceive, OnInterrupt, OnSendOK, 0, 0) ILibAsyncServerSocket_ServerModule ILibCreateAsyncServerSocketModuleWithMemory(void *Chain, int MaxConnections, unsigned short PortNumber, int initialBufferSize, int loopbackFlag, ILibAsyncServerSocket_OnConnect OnConnect, ILibAsyncServerSocket_OnDisconnect OnDisconnect, ILibAsyncServerSocket_OnReceive OnReceive, ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK, int ServerUserMappedMemorySize, int SessionUserMappedMemorySize); +ILibAsyncServerSocket_ServerModule ILibCreateAsyncServerSocketModuleWithMemoryEx(void *Chain, int MaxConnections, int initialBufferSize, struct sockaddr* local, ILibAsyncServerSocket_OnConnect OnConnect, ILibAsyncServerSocket_OnDisconnect OnDisconnect, ILibAsyncServerSocket_OnReceive OnReceive, ILibAsyncServerSocket_OnInterrupt OnInterrupt, ILibAsyncServerSocket_OnSendOK OnSendOK, int ServerUserMappedMemorySize, int SessionUserMappedMemorySize); + void *ILibAsyncServerSocket_GetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule); void ILibAsyncServerSocket_SetTag(ILibAsyncServerSocket_ServerModule ILibAsyncSocketModule, void *user); diff --git a/microstack/ILibAsyncSocket.c b/microstack/ILibAsyncSocket.c index 2497db5..e6c2589 100644 --- a/microstack/ILibAsyncSocket.c +++ b/microstack/ILibAsyncSocket.c @@ -1960,7 +1960,7 @@ void ILibAsyncSocket_ModuleOnConnect(ILibAsyncSocket_SocketModule socketModule) \param ssl_ctx SSL_CTX Context object \param server ILibAsyncSocket_TLS_Mode Configuration */ -SSL* ILibAsyncSocket_SetSSLContext(ILibAsyncSocket_SocketModule socketModule, SSL_CTX *ssl_ctx, ILibAsyncSocket_TLS_Mode server) +SSL* ILibAsyncSocket_SetSSLContextEx(ILibAsyncSocket_SocketModule socketModule, SSL_CTX *ssl_ctx, ILibAsyncSocket_TLS_Mode server, char *hostName) { if (socketModule != NULL) { @@ -1992,6 +1992,7 @@ SSL* ILibAsyncSocket_SetSSLContext(ILibAsyncSocket_SocketModule socketModule, SS SSL_set_bio(module->ssl, module->readBio, module->writeBio); if (server == ILibAsyncSocket_TLS_Mode_Client) { + if (hostName != NULL) { SSL_set_tlsext_host_name(module->ssl, hostName); } SSL_set_connect_state(module->ssl); status = SSL_do_handshake(module->ssl); if (status <= 0) { status = SSL_get_error(module->ssl, status); } diff --git a/microstack/ILibAsyncSocket.h b/microstack/ILibAsyncSocket.h index 9afac60..de343b9 100644 --- a/microstack/ILibAsyncSocket.h +++ b/microstack/ILibAsyncSocket.h @@ -212,7 +212,8 @@ typedef enum ILibAsyncSocket_TLS_Mode #endif }ILibAsyncSocket_TLS_Mode; -SSL* ILibAsyncSocket_SetSSLContext(ILibAsyncSocket_SocketModule socketModule, SSL_CTX *ssl_ctx, ILibAsyncSocket_TLS_Mode server); +SSL* ILibAsyncSocket_SetSSLContextEx(ILibAsyncSocket_SocketModule socketModule, SSL_CTX *ssl_ctx, ILibAsyncSocket_TLS_Mode server, char *hostname); +#define ILibAsyncSocket_SetSSLContext(socketModule, ssl_ctx, tlsMode) ILibAsyncSocket_SetSSLContextEx(socketModule, ssl_ctx, tlsMode, NULL) SSL_CTX *ILibAsyncSocket_GetSSLContext(ILibAsyncSocket_SocketModule socketModule); #endif diff --git a/microstack/ILibCrypto.c b/microstack/ILibCrypto.c index a92ef5e..7da71ad 100644 --- a/microstack/ILibCrypto.c +++ b/microstack/ILibCrypto.c @@ -21,6 +21,7 @@ #else #include "md5.h" #include "sha1.h" +#include "microstack/sha.h" #include #endif @@ -561,10 +562,10 @@ int __fastcall util_mkCert(struct util_cert *rootcert, struct util_cert* cert, i X509_NAME *cname = NULL; X509 **x509p = NULL; EVP_PKEY **pkeyp = NULL; - int hashlen = UTIL_HASHSIZE; - char hash[UTIL_HASHSIZE]; + int hashlen = UTIL_SHA384_HASHSIZE; + char hash[UTIL_SHA384_HASHSIZE]; char serial[8]; - char nameStr[(UTIL_HASHSIZE * 2) + 2]; + char nameStr[(UTIL_SHA384_HASHSIZE * 2) + 2]; BIGNUM *oBigNbr; CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); @@ -616,7 +617,7 @@ int __fastcall util_mkCert(struct util_cert *rootcert, struct util_cert* cert, i //util_sha256((char*)x->cert_info->key->public_key->data, x->cert_info->key->public_key->length, hash); // OpenSSL 1.0 X509_pubkey_digest(x, EVP_sha384(), (unsigned char*)hash, (unsigned int*)&hashlen); // OpenSSL 1.1 - util_tohex(hash, UTIL_HASHSIZE, nameStr); + util_tohex(hash, UTIL_SHA384_HASHSIZE, nameStr); X509_NAME_add_entry_by_txt(cname, "CN", MBSTRING_ASC, (unsigned char*)nameStr, -1, -1, 0); } else @@ -682,7 +683,7 @@ err: int __fastcall util_keyhash(struct util_cert cert, char* result) { - int hashlen = UTIL_HASHSIZE; + int hashlen = UTIL_SHA384_HASHSIZE; if (cert.x509 == NULL) return -1; //util_sha256((char*)(cert.x509->cert_info->key->public_key->data), cert.x509->cert_info->key->public_key->length, result); // OpenSSL 1.0 X509_pubkey_digest(cert.x509, EVP_sha384(), (unsigned char*)result,(unsigned int *) &hashlen); // OpenSSL 1.1 @@ -691,7 +692,7 @@ int __fastcall util_keyhash(struct util_cert cert, char* result) int __fastcall util_keyhash2(X509* cert, char* result) { - int hashlen = UTIL_HASHSIZE; + int hashlen = UTIL_SHA384_HASHSIZE; if (cert == NULL) return -1; //util_sha256((char*)(cert->cert_info->key->public_key->data), cert->cert_info->key->public_key->length, result); // OpenSSL 1.0 X509_pubkey_digest(cert, EVP_sha384(), (unsigned char*)result, (unsigned int*)&hashlen); // OpenSSL 1.1 @@ -702,11 +703,11 @@ int __fastcall util_keyhash2(X509* cert, char* result) int __fastcall util_sign(struct util_cert cert, char* data, int datalen, char** signature) { int size = 0; - unsigned int hashsize = UTIL_HASHSIZE; + unsigned int hashsize = UTIL_SHA384_HASHSIZE; BIO *in = NULL; PKCS7 *message = NULL; *signature = NULL; - if (datalen <= UTIL_HASHSIZE) return 0; + if (datalen <= UTIL_SHA384_HASHSIZE) return 0; // Add hash of the certificate to start of data X509_digest(cert.x509, EVP_sha384(), (unsigned char*)data, &hashsize); @@ -730,7 +731,7 @@ int __fastcall util_verify(char* signature, int signlen, struct util_cert* cert, BIO *out = NULL; PKCS7 *message = NULL; char* data2 = NULL; - char hash[UTIL_HASHSIZE]; + char hash[UTIL_SHA256_HASHSIZE]; STACK_OF(X509) *st = NULL; cert->x509 = NULL; @@ -750,7 +751,7 @@ int __fastcall util_verify(char* signature, int signlen, struct util_cert* cert, // If data block contains less than 32 bytes, fail. size = (unsigned int)BIO_get_mem_data(out, &data2); - if (size <= UTIL_HASHSIZE) goto error; + if (size <= UTIL_SHA256_HASHSIZE) goto error; // Copy the data block *data = (char*)malloc(size + 1); @@ -764,11 +765,11 @@ int __fastcall util_verify(char* signature, int signlen, struct util_cert* cert, sk_X509_free(st); // Get a full certificate hash of the signer - r = UTIL_HASHSIZE; + r = UTIL_SHA256_HASHSIZE; X509_digest(cert->x509, EVP_sha256(), (unsigned char*)hash, &r); // Check certificate hash with first 32 bytes of data. - if (memcmp(hash, *data, UTIL_HASHSIZE) != 0) goto error; + if (memcmp(hash, *data, UTIL_SHA256_HASHSIZE) != 0) goto error; // Approved, cleanup and return. BIO_free(out); diff --git a/microstack/ILibCrypto.h b/microstack/ILibCrypto.h index bb84b54..eb0887c 100644 --- a/microstack/ILibCrypto.h +++ b/microstack/ILibCrypto.h @@ -35,18 +35,31 @@ BOOL util_CopyFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName, _I void __fastcall util_random(int length, char* result); void __fastcall util_randomtext(int length, char* result); -#define UTIL_HASHSIZE 48 -#define NONCE_SIZE 48 +#define UTIL_MD5_HASHSIZE 16 +#define UTIL_SHA1_HASHSIZE 20 +#define UTIL_SHA256_HASHSIZE 32 +#define UTIL_SHA384_HASHSIZE 48 +#define UTIL_SHA512_HASHSIZE 64 #ifdef MICROSTACK_NOTLS #include "md5.h" #include "sha1.h" -#include "microstack/SHA256.h" +#include "microstack/SHA.h" -#define SHA256_CTX struct sha256_ctx -#define SHA256_Init(ctx) __sha256_init_ctx(ctx) -#define SHA256_Update(ctx, data, len) __sha256_process_bytes(data, len, ctx) -#define SHA256_Final(md, ctx) __sha256_finish_ctx(ctx, md) +#define SHA256_CTX SHA256Context +#define SHA512_CTX SHA512Context + +#define SHA256_Init(ctx) SHA256Reset (ctx) +#define SHA256_Update(ctx, data, len) SHA256Input(ctx, (uint8_t*)data, len) +#define SHA256_Final(md, ctx) SHA256Result (ctx, md) + +#define SHA384_Init(ctx) SHA384Reset (ctx) +#define SHA384_Update(ctx, data, len) SHA384Input(ctx, (uint8_t*)data, len) +#define SHA384_Final(md, ctx) SHA384Result (ctx, md) + +#define SHA512_Init(ctx) SHA512Reset (ctx) +#define SHA512_Update(ctx, data, len) SHA512Input(ctx, (uint8_t*)data, len) +#define SHA512_Final(md, ctx) SHA512Result (ctx, md) #endif diff --git a/microstack/ILibIPAddressMonitor.c b/microstack/ILibIPAddressMonitor.c index b8cfaf6..1dfb1d5 100644 --- a/microstack/ILibIPAddressMonitor.c +++ b/microstack/ILibIPAddressMonitor.c @@ -41,7 +41,7 @@ void CALLBACK ILibIPAddressMonitor_dispatch( IN DWORD dwFlags ) { - if (dwError == 0) + if (dwError == 0 && lpOverlapped->hEvent != NULL) { _ILibIPAddressMonitor *obj = (_ILibIPAddressMonitor*)lpOverlapped->hEvent; ILibChain_RunOnMicrostackThread(obj->chainLink.ParentChain, ILibIPAddressMonitor_MicrostackThreadDispatch, obj); @@ -60,6 +60,7 @@ void ILibIPAddressMonitor_Destroy(void *object) _ILibIPAddressMonitor *obj = (_ILibIPAddressMonitor*)object; #ifdef WIN32 + obj->reserved.hEvent = NULL; closesocket(obj->mSocket); obj->mSocket = INVALID_SOCKET; #elif defined(_POSIX) diff --git a/microstack/ILibParsers.c b/microstack/ILibParsers.c index e5ba62d..f837804 100644 --- a/microstack/ILibParsers.c +++ b/microstack/ILibParsers.c @@ -5104,9 +5104,17 @@ ILibParseUriResult ILibParseUriEx (const char* URI, size_t URILen, char** Addr, TempStringLength2 = TempStringLength-result2->FirstResult->datalength; if (Path != NULL) { - if ((*Path = (char*)malloc(TempStringLength2 + 1)) == NULL) ILIBCRITICALEXIT(254); - memcpy_s(*Path, TempStringLength2 + 1, TempString + (result2->FirstResult->datalength), TempStringLength2); - (*Path)[TempStringLength2] = '\0'; + if ((*Path = (char*)malloc(TempStringLength2 + 2)) == NULL) ILIBCRITICALEXIT(254); + if (TempStringLength2 == 0) + { + (*Path)[0] = '/'; + (*Path)[1] = '\0'; + } + else + { + memcpy_s(*Path, TempStringLength2 + 2, TempString + (result2->FirstResult->datalength), TempStringLength2); + (*Path)[TempStringLength2] = '\0'; + } } // Parse Port Number @@ -5333,7 +5341,7 @@ void ILibSetDirective(struct packetheader *packet, char* Directive, int Directiv if (packet->ReservedMemory != NULL) { - if (ILibMemory_AllocateA_Size(packet->ReservedMemory) > (DirectiveLength + DirectiveObjLength + 2)) + if (ILibMemory_AllocateA_Size(packet->ReservedMemory) > (unsigned int)(DirectiveLength + DirectiveObjLength + 2)) { packet->Directive = (char*)ILibMemory_AllocateA_Get(packet->ReservedMemory, (size_t)DirectiveLength + 1); packet->DirectiveObj = (char*)ILibMemory_AllocateA_Get(packet->ReservedMemory, (size_t)DirectiveObjLength + 1); diff --git a/microstack/ILibParsers.h b/microstack/ILibParsers.h index d7e695f..4aa6b80 100644 --- a/microstack/ILibParsers.h +++ b/microstack/ILibParsers.h @@ -306,10 +306,10 @@ int ILibIsRunningOnChainThread(void* chain); #else #define ILibMemory_AllocateA(bufferLen) ILibMemory_AllocateA_InitMem(alloca(8+bufferLen+sizeof(void*)), (size_t)(8+bufferLen+sizeof(void*))) #endif - #define ILibMemory_AllocateA_Size(buffer) (((int*)((char*)(buffer)-4))[0]) + #define ILibMemory_AllocateA_Size(buffer) (((unsigned int*)((char*)(buffer)-4))[0]) #define ILibMemory_AllocateA_Next(buffer) (((void**)((char*)(buffer)-4-sizeof(void*)))[0]) #define ILibMemory_AllocateA_Raw(buffer) ((void*)((char*)(buffer)-4-sizeof(void*)-4)) - #define ILibMemory_AllocateA_RawSize(buffer) (((int*)((char*)(buffer)-4-sizeof(void*)-4))[0]) + #define ILibMemory_AllocateA_RawSize(buffer) (((unsigned int*)((char*)(buffer)-4-sizeof(void*)-4))[0]) void* ILibMemory_AllocateA_Get(void *buffer, size_t sz); void* ILibMemory_AllocateA_InitMem(void *buffer, size_t bufferLen); diff --git a/microstack/ILibProcessPipe.c b/microstack/ILibProcessPipe.c index 579140d..94424e9 100644 --- a/microstack/ILibProcessPipe.c +++ b/microstack/ILibProcessPipe.c @@ -162,6 +162,10 @@ typedef struct ILibProcessPipe_WaitHandle void *user; ILibProcessPipe_WaitHandle_Handler callback; }ILibProcessPipe_WaitHandle; +HANDLE ILibProcessPipe_Manager_GetWorkerThread(ILibProcessPipe_Manager mgr) +{ + return(((ILibProcessPipe_Manager_Object*)mgr)->workerThread); +} int ILibProcessPipe_Manager_WindowsWaitHandles_Remove_Comparer(void *source, void *matchWith) { @@ -181,6 +185,7 @@ void __stdcall ILibProcessPipe_WaitHandle_Remove_APC(ULONG_PTR obj) waiter = (ILibProcessPipe_WaitHandle*)ILibLinkedList_GetDataFromNode(node); free(waiter); ILibLinkedList_Remove(node); + SetEvent(manager->updateEvent); } free((void*)obj); } @@ -497,21 +502,36 @@ ILibProcessPipe_PipeObject* ILibProcessPipe_CreatePipe(ILibProcessPipe_Manager m return retVal; } -void ILibProcessPipe_Process_Destroy(ILibProcessPipe_Process_Object *p) +#ifdef WIN32 +void __stdcall ILibProcessPipe_Process_Destroy_WinRunThread(ULONG_PTR obj) { + ILibProcessPipe_Process_Object *p = (ILibProcessPipe_Process_Object*)obj; if (p->exiting != 0) { return; } if (p->stdIn != NULL) { ILibProcessPipe_FreePipe(p->stdIn); } if (p->stdOut != NULL) { ILibProcessPipe_FreePipe(p->stdOut); } if (p->stdErr != NULL) { ILibProcessPipe_FreePipe(p->stdErr); } free(p); } -#ifndef WIN32 -void ILibProcessPipe_Process_BrokenPipeSink(ILibProcessPipe_PipeObject* sender) +#endif +void ILibProcessPipe_Process_Destroy(ILibProcessPipe_Process_Object *p) { - ILibProcessPipe_Process_Object *p = sender->mProcess; +#ifdef WIN32 + // We can't destroy this now, because we're on the MicrostackThread. We must destroy this on the WindowsRunLoop Thread. + QueueUserAPC((PAPCFUNC)ILibProcessPipe_Process_Destroy_WinRunThread, p->parent->workerThread, (ULONG_PTR)p); +#else + if (p->exiting != 0) { return; } + if (p->stdIn != NULL) { ILibProcessPipe_FreePipe(p->stdIn); } + if (p->stdOut != NULL) { ILibProcessPipe_FreePipe(p->stdOut); } + if (p->stdErr != NULL) { ILibProcessPipe_FreePipe(p->stdErr); } + free(p); +#endif +} +#ifndef WIN32 +void ILibProcessPipe_Process_BrokenPipeSink(ILibProcessPipe_Pipe sender) +{ + ILibProcessPipe_Process_Object *p = ((ILibProcessPipe_PipeObject*)sender)->mProcess; int status; - - if (ILibIsRunningOnChainThread(sender->manager->ChainLink.ParentChain) != 0) + if (ILibIsRunningOnChainThread(((ILibProcessPipe_PipeObject*)sender)->manager->ChainLink.ParentChain) != 0) { // This was called from the Reader if (p->exitHandler != NULL) @@ -685,13 +705,14 @@ ILibProcessPipe_Process ILibProcessPipe_Manager_SpawnProcessEx2(ILibProcessPipe_ retVal->stdIn = ILibProcessPipe_Pipe_CreateFromExistingWithExtraMemory(pipeManager, pipe, extraMemorySize); retVal->stdIn->mProcess = retVal; retVal->stdOut = ILibProcessPipe_Pipe_CreateFromExistingWithExtraMemory(pipeManager, pipe, extraMemorySize); + ILibProcessPipe_Pipe_SetBrokenPipeHandler(retVal->stdOut, ILibProcessPipe_Process_BrokenPipeSink); retVal->stdOut->mProcess = retVal; } else { retVal->stdIn = ILibProcessPipe_CreatePipe(pipeManager, 4096, NULL, extraMemorySize); retVal->stdIn->mProcess = retVal; - retVal->stdOut = ILibProcessPipe_CreatePipe(pipeManager, 4096, &ILibProcessPipe_Process_BrokenPipeSink, extraMemorySize); + retVal->stdOut = ILibProcessPipe_CreatePipe(pipeManager, 4096, (ILibProcessPipe_GenericBrokenPipeHandler) ILibProcessPipe_Process_BrokenPipeSink, extraMemorySize); retVal->stdOut->mProcess = retVal; pid = vfork(); } @@ -859,7 +880,6 @@ void ILibProcessPipe_Process_ReadHandler(void* user) #endif ILibLinkedList_Remove(ILibLinkedList_GetNode_Search(pipeObject->manager->ActivePipes, NULL, pipeObject)); - if (pipeObject->brokenPipeHandler != NULL) { ((ILibProcessPipe_GenericBrokenPipeHandler)pipeObject->brokenPipeHandler)(pipeObject); @@ -1240,6 +1260,11 @@ void ILibProcessPipe_Process_PipeHandler_StdIn(void *user1, void *user2) } #ifdef WIN32 +void __stdcall ILibProcessPipe_Process_OnExit_ChainSink_DestroySink(ULONG_PTR obj) +{ + ILibProcessPipe_Process_Object* j = (ILibProcessPipe_Process_Object*)obj; + if (j->exiting == 0) { ILibProcessPipe_Process_Destroy(j); } +} void ILibProcessPipe_Process_OnExit_ChainSink(void *chain, void *user) { ILibProcessPipe_Process_Object* j = (ILibProcessPipe_Process_Object*)user; @@ -1250,8 +1275,9 @@ void ILibProcessPipe_Process_OnExit_ChainSink(void *chain, void *user) j->exiting = 1; j->exitHandler(j, exitCode, j->userObject); j->exiting ^= 1; - - if (j->exiting == 0) { ILibProcessPipe_Process_Destroy(j); } + + // We can't destroy this now, because we're on the MicrostackThread. We must destroy this on the WindowsRunLoop Thread. + QueueUserAPC((PAPCFUNC)ILibProcessPipe_Process_OnExit_ChainSink_DestroySink, j->parent->workerThread, (ULONG_PTR)j); } BOOL ILibProcessPipe_Process_OnExit(HANDLE event, void* user) { diff --git a/microstack/ILibProcessPipe.h b/microstack/ILibProcessPipe.h index 07a3da8..5983a14 100644 --- a/microstack/ILibProcessPipe.h +++ b/microstack/ILibProcessPipe.h @@ -50,6 +50,7 @@ typedef enum ILibProcessPipe_Pipe_ReaderHandleType ILibProcessPipe_Pipe_ReaderHandleType_NotOverLapped = 0, //!< Spawn a I/O processing thread ILibProcessPipe_Pipe_ReaderHandleType_Overlapped = 1 //!< Use Overlapped I/O }ILibProcessPipe_Pipe_ReaderHandleType; +HANDLE ILibProcessPipe_Manager_GetWorkerThread(ILibProcessPipe_Manager mgr); #endif ILibTransport_DoneState ILibProcessPipe_Pipe_Write(ILibProcessPipe_Pipe writePipe, char* buffer, int bufferLen, ILibTransport_MemoryOwnership ownership); diff --git a/microstack/ILibWebClient.c b/microstack/ILibWebClient.c index 03b9ac3..f15ea81 100644 --- a/microstack/ILibWebClient.c +++ b/microstack/ILibWebClient.c @@ -247,6 +247,7 @@ typedef struct ILibWebClientDataObject #ifndef MICROSTACK_NOTLS ILibWebClient_RequestToken_HTTPS requestMode; + char *sniHost; #endif char* CertificateHashPtr; // Points to the certificate hash (next field) if set @@ -342,7 +343,15 @@ void ILibWebClient_DestroyWebRequest(struct ILibWebRequest *wr) if (wr == NULL) return; if (wr != NULL && wr->connectionCloseWasSpecified != 0 && wr->DisconnectSink != NULL) { wr->DisconnectSink(wr->requestToken); } - if (wr->buffered != NULL) { free(wr->buffered); } + if (wr->buffered != NULL) + { + while (wr->buffered != NULL) + { + ILibWebRequest_buffer * rb = wr->buffered->next; + free(wr->buffered); + wr->buffered = rb; + } + } if (wr->streamedState != NULL) { while (ILibQueue_IsEmpty(wr->streamedState->BufferQueue) == 0) @@ -472,6 +481,9 @@ void ILibWebClient_DestroyWebClientDataObject(ILibWebClient_StateObject token) ILibQueue_Destroy(wcdo->RequestQueue); if (wcdo->DigestData != NULL) { free(ILibMemory_AllocateA_Raw(wcdo->DigestData)); } +#ifndef MICROSTACK_NOTLS + if (wcdo->sniHost != NULL) { free(wcdo->sniHost); } +#endif free(wcdo); } @@ -739,8 +751,11 @@ void ILibWebClient_FinishedResponse(ILibAsyncSocket_SocketModule socketModule, s SEM_TRACK(WebClient_TrackLock("ILibWebClient_FinishedResponse", 1, wcdo->Parent);) sem_wait(&(wcdo->Parent->QLock)); wr = (struct ILibWebRequest*)ILibQueue_DeQueue(wcdo->RequestQueue); - wr->connectionCloseWasSpecified = 2; - ILibWebClient_DestroyWebRequest(wr); + if (wr != NULL) + { + wr->connectionCloseWasSpecified = 2; + ILibWebClient_DestroyWebRequest(wr); + } SEM_TRACK(WebClient_TrackUnLock("ILibWebClient_FinishedResponse", 2, wcdo->Parent);) sem_post(&(wcdo->Parent->QLock)); return; @@ -795,12 +810,12 @@ void ILibWebClient_FinishedResponse(ILibAsyncSocket_SocketModule socketModule, s // ILibAsyncSocket_Send(wcdo->SOCK, wr->Buffer[i], wr->BufferLength[i], ILibAsyncSocket_MemoryOwnership_STATIC); } - while (wr->buffered != NULL) + + b = wr->buffered; + while (b != NULL) { - b = wr->buffered->next; - ILibAsyncSocket_Send(wcdo->SOCK, wr->buffered->buffer, wr->buffered->bufferLength, ILibAsyncSocket_MemoryOwnership_USER); - free(wr->buffered); - wr->buffered = b; + ILibAsyncSocket_Send(wcdo->SOCK, b->buffer, b->bufferLength, ILibAsyncSocket_MemoryOwnership_USER); + b = b->next; } } } @@ -1140,7 +1155,7 @@ void ILibWebClient_ProcessChunk(ILibAsyncSocket_SocketModule socketModule, struc wcdo->NeedFlush = 0; ILibWebClient_FinishedResponse_Server(wcdo); } - if (ILibAsyncSocket_IsFree(socketModule)==0) + if (socketModule==NULL || ILibAsyncSocket_IsFree(socketModule)==0) { // // Free the resources associated with this chunk @@ -1556,7 +1571,7 @@ void ILibWebClient_OnData(ILibAsyncSocket_SocketModule socketModule, char* buffe wcdo->source.sin_addr.s_addr = ILibAsyncSocket_GetRemoteInterface(socketModule); wcdo->source.sin_port = htons(ILibAsyncSocket_GetRemotePort(socketModule)); */ - ILibAsyncSocket_GetRemoteInterface(socketModule, (struct sockaddr*)(&wcdo->source)); + if (socketModule != NULL) { ILibAsyncSocket_GetRemoteInterface(socketModule, (struct sockaddr*)(&wcdo->source)); } wcdo->HeaderLength = i + 4; wcdo->WaitForClose = 1; @@ -1654,7 +1669,14 @@ void ILibWebClient_OnData(ILibAsyncSocket_SocketModule socketModule, char* buffe } if (wcdo->header->StatusCode >= 100 && wcdo->header->StatusCode <= 199) { - if (wcdo->header->StatusCode == 101 && wr->requestToken->WebSocketKey != NULL) + if (wr->requestToken == NULL) + { + int zro = 0; + if (wr->OnResponse != NULL) { wr->OnResponse(wcdo, 0, wcdo->header, NULL, &zro, 0, ILibWebClient_ReceiveStatus_Connection_Established, wr->user1, wr->user2, &(wcdo->PAUSE)); } + *p_beginPointer += wcdo->HeaderLength; + return; + } + else if (wcdo->header->StatusCode == 101 && wr->requestToken->WebSocketKey != NULL) { // WebSocket char* skey = ILibGetHeaderLine(wcdo->header, "Sec-WebSocket-Accept", 20); @@ -1812,7 +1834,7 @@ void ILibWebClient_OnData(ILibAsyncSocket_SocketModule socketModule, char* buffe wr->user2, &(wcdo->PAUSE)); } - if (ILibAsyncSocket_IsFree(socketModule)==0) + if (socketModule==NULL || ILibAsyncSocket_IsFree(socketModule)==0) { wcdo->HeaderLength = 0; *p_beginPointer = i+4+zero; @@ -1856,31 +1878,28 @@ void ILibWebClient_OnData(ILibAsyncSocket_SocketModule socketModule, char* buffe wr->user1, wr->user2, &(wcdo->PAUSE)); - if (ILibAsyncSocket_IsFree(socketModule)!=0) + if (wcdo->FinHeader == 0) + { + // + // The user sent a response already, so advance the + // beginPointer and return. + // + // If the user sent a response, and it wasn't in relation to a 100 Continue, then the + // user is in ERROR, because the done flag is not set here, which means that + // the entire request did not get received yet. Simply answering the request + // without receiving the entire request, without closing the socket is in + // VIOLATION of the http specification. + // + *p_beginPointer = i + 4; + return; + } + if (socketModule != NULL && ILibAsyncSocket_IsFree(socketModule)!=0) { // // The user closed the socket, so just return // return; - } - else - { - if (wcdo->FinHeader==0) - { - // - // The user sent a response already, so advance the - // beginPointer and return. - // - // If the user sent a response, and it wasn't in relation to a 100 Continue, then the - // user is in ERROR, because the done flag is not set here, which means that - // the entire request did not get received yet. Simply answering the request - // without receiving the entire request, without closing the socket is in - // VIOLATION of the http specification. - // - *p_beginPointer = i + 4; - return; - } - } + } } @@ -1950,7 +1969,7 @@ void ILibWebClient_OnData(ILibAsyncSocket_SocketModule socketModule, char* buffe wr->user2, &(wcdo->PAUSE)); } - if (ILibAsyncSocket_IsFree(socketModule)==0) + if (socketModule==NULL || ILibAsyncSocket_IsFree(socketModule)==0) { if (wcdo->WaitForClose==0) { @@ -1979,7 +1998,7 @@ void ILibWebClient_OnData(ILibAsyncSocket_SocketModule socketModule, char* buffe } //{{{ <--REMOVE_THIS_FOR_HTTP/1.0_ONLY_SUPPORT }}} } - if (ILibAsyncSocket_IsFree(socketModule)==0) + if (socketModule==NULL || ILibAsyncSocket_IsFree(socketModule)==0) { // // If the user said to pause this connection, do so @@ -2054,12 +2073,11 @@ void ILibWebClient_OnConnect(ILibAsyncSocket_SocketModule socketModule, int Conn ILibAsyncSocket_Send(socketModule, r->Buffer[i], r->BufferLength[i], (enum ILibAsyncSocket_MemoryOwnership)-1); } - while (r->buffered != NULL) + b = r->buffered; + while (b != NULL) { - ILibAsyncSocket_Send(socketModule, r->buffered->buffer, r->buffered->bufferLength, ILibAsyncSocket_MemoryOwnership_USER); - b = r->buffered; - r->buffered = r->buffered->next; - free(b); + ILibAsyncSocket_Send(socketModule, b->buffer, b->bufferLength, ILibAsyncSocket_MemoryOwnership_USER); + b = b->next; } if (r->streamedState != NULL) { @@ -2214,27 +2232,6 @@ void ILibWebClient_OnDisconnectSink(ILibAsyncSocket_SocketModule socketModule, v wr->user2, &(wcdo->PAUSE)); - if (ILibAsyncSocket_IsFree(socketModule) != 0) - { - // - // If the underlying socket is gone, then Finished Response won't clear - // the pending request - // - SEM_TRACK(WebClient_TrackLock("ILibWebClient_OnDisconnect", 5, wcdo->Parent);) - sem_wait(&(wcdo->Parent->QLock)); - wr = (struct ILibWebRequest*)ILibQueue_DeQueue(wcdo->RequestQueue); - SEM_TRACK(WebClient_TrackUnLock("ILibWebClient_OnDisconnect", 6, wcdo->Parent);) - sem_post(&(wcdo->Parent->QLock)); - if (wr != NULL) - { - if (wcdo->IsWebSocket != 0) - { - free(((ILibWebClient_WebSocketState*)wr->Buffer[0])->WebSocketFragmentBuffer); - } - wr->connectionCloseWasSpecified = 2; - ILibWebClient_DestroyWebRequest(wr); - } - } if (wcdo->IsOrphan != 0 || wcdo->IsWebSocket != 0) { ILibWebClient_FinishedResponse(socketModule, wcdo); @@ -2244,20 +2241,6 @@ void ILibWebClient_OnDisconnectSink(ILibAsyncSocket_SocketModule socketModule, v { ILibWebClient_FinishedResponse(socketModule, wcdo); } - //SEM_TRACK(WebClient_TrackLock("ILibWebClient_OnDisconnect", 7, wcdo->Parent);) - //sem_wait(&(wcdo->Parent->QLock)); - //wr = (struct ILibWebRequest*)ILibQueue_PeekQueue(wcdo->RequestQueue); - //SEM_TRACK(WebClient_TrackUnLock("ILibWebClient_OnDisconnect", 8, wcdo->Parent);) - //sem_post(&(wcdo->Parent->QLock)); - //if (wr == NULL) - //{ - // if (wcdo->IsWebSocket != 0) - // { - // // This was a websocket, so we must destroy the WCDO object, because it won't be destroyed anywhere else - // ILibWebClient_DestroyWebClientDataObject(wcdo); - // } - // return; - //} } // Make Another Connection and Continue @@ -2357,7 +2340,7 @@ void ILibWebClient_PreProcess(void* WebClientModule, fd_set *readset, fd_set *wr #ifndef MICROSTACK_NOTLS if (wcm->ssl_ctx != NULL && wcdo->requestMode == ILibWebClient_RequestToken_USE_HTTPS) { - SSL* ssl = ILibAsyncSocket_SetSSLContext(wcdo->SOCK, wcm->ssl_ctx, 0); + SSL* ssl = ILibAsyncSocket_SetSSLContextEx(wcdo->SOCK, wcm->ssl_ctx, 0, wcdo->sniHost); if (ssl != NULL && ILibWebClientDataObjectIndex >= 0) { SSL_set_ex_data(ssl, ILibWebClientDataObjectIndex, wcdo); @@ -3256,7 +3239,7 @@ ILibTransport_DoneState ILibWebClient_StreamRequestBody( if (t != NULL && t->wcdo != NULL) { - wr = (struct ILibWebRequest*)ILibQueue_PeekTail(t->wcdo->RequestQueue); + wr = t->parent; if (t->wcdo->SOCK == NULL || ILibQueue_GetCount(t->wcdo->RequestQueue)>1) { // Connection not established yet, so buffer the data @@ -3583,6 +3566,12 @@ void ILibWebClient_Request_SetHTTPS(ILibWebClient_RequestToken reqToken, ILibWeb { ((struct ILibWebClientDataObject*)ILibWebClient_GetStateObjectFromRequestToken(reqToken))->requestMode = requestMode; } +void ILibWebClient_Request_SetSNI(ILibWebClient_RequestToken reqToken, char *host, int hostLen) +{ + ((struct ILibWebClientDataObject*)ILibWebClient_GetStateObjectFromRequestToken(reqToken))->sniHost = ILibMemory_Allocate(hostLen + 1, 0, NULL, NULL); + memcpy_s(((struct ILibWebClientDataObject*)ILibWebClient_GetStateObjectFromRequestToken(reqToken))->sniHost, hostLen, host, hostLen); + ((struct ILibWebClientDataObject*)ILibWebClient_GetStateObjectFromRequestToken(reqToken))->sniHost[hostLen] = 0; +} #endif int ILibWebClient_GetLocalInterface(void* socketModule, struct sockaddr *localAddress) diff --git a/microstack/ILibWebClient.h b/microstack/ILibWebClient.h index 7c202c9..e3d001c 100644 --- a/microstack/ILibWebClient.h +++ b/microstack/ILibWebClient.h @@ -257,9 +257,7 @@ void* ILibWebClient_GetChainFromWebStateObject(ILibWebClient_StateObject wcdo); typedef enum ILibWebClient_RequestToken_HTTPS { -#ifndef MICROSTACK_NOTLS ILibWebClient_RequestToken_USE_HTTPS = 0, -#endif ILibWebClient_RequestToken_USE_HTTP = 1 }ILibWebClient_RequestToken_HTTPS; @@ -267,6 +265,7 @@ typedef enum ILibWebClient_RequestToken_HTTPS void ILibWebClient_SetTLS(ILibWebClient_RequestManager manager, void *ssl_ctx, ILibWebClient_OnSslConnection OnSslConnection); int ILibWebClient_EnableHTTPS(ILibWebClient_RequestManager manager, struct util_cert* leafCert, X509* nonLeafCert, ILibWebClient_OnHttpsConnection OnHttpsConnection); void ILibWebClient_Request_SetHTTPS(ILibWebClient_RequestToken reqToken, ILibWebClient_RequestToken_HTTPS requestMode); +void ILibWebClient_Request_SetSNI(ILibWebClient_RequestToken reqToken, char *host, int hostLen); #endif // Added methods diff --git a/microstack/ILibWebServer.c b/microstack/ILibWebServer.c index 6449566..5ecfd51 100644 --- a/microstack/ILibWebServer.c +++ b/microstack/ILibWebServer.c @@ -1141,7 +1141,7 @@ int ILibWebServer_Digest_ParseAuthenticationHeader2(char* value, int valueLen, c *token = value; len = ILibTrimString(token, i); - (*token)[len] = 0; + //(*token)[len] = 0; *tokenLen = len; *tokenValue = value + i + 1; @@ -1157,7 +1157,7 @@ int ILibWebServer_Digest_ParseAuthenticationHeader2(char* value, int valueLen, c { len -= 1; } - (*tokenValue)[len] = 0; + //(*tokenValue)[len] = 0; *tokenValueLen = len; return 0; } @@ -1190,7 +1190,7 @@ int ILibWebServer_Digest_IsCorrectRealmAndNonce(struct ILibWebServer_Session *se int userRealmLen; char* nonce = NULL; char* opaque = NULL; - int opaqueLen; + int opaqueLen, nonceLen; long long current; char expiration[8]; char calculatedNonce[33]; @@ -1205,7 +1205,7 @@ int ILibWebServer_Digest_IsCorrectRealmAndNonce(struct ILibWebServer_Session *se ILibWebServer_Digest_ParseAuthenticationHeader(ILibWebServer_Session_GetSystemData(session)->DigestTable, auth, authLen); ILibGetEntryEx(ILibWebServer_Session_GetSystemData(session)->DigestTable, "realm", 5, (void**)&userRealm, &userRealmLen); - nonce = (char*)ILibGetEntry(ILibWebServer_Session_GetSystemData(session)->DigestTable, "nonce", 5); + ILibGetEntryEx(ILibWebServer_Session_GetSystemData(session)->DigestTable, "nonce", 5, (void**)&nonce, &nonceLen); ILibGetEntryEx(ILibWebServer_Session_GetSystemData(session)->DigestTable, "opaque", 6, (void**)&opaque, &opaqueLen); if (opaque != NULL && userRealm != NULL && userRealmLen == realmLen && strncmp(userRealm, realm, realmLen) == 0) @@ -1217,7 +1217,7 @@ int ILibWebServer_Digest_IsCorrectRealmAndNonce(struct ILibWebServer_Session *se if (((long long*)expiration)[0] < current) { return 0; } // Opaque Block Expired ILibWebServer_Digest_CalculateNonce(session, ((long long*)expiration)[0], calculatedNonce); - return(strcmp(nonce, calculatedNonce) == 0 ? 1 : 0); + return((nonceLen == 32 && strncmp(nonce, calculatedNonce, 32)) == 0 ? 1 : 0); } else { @@ -1304,7 +1304,7 @@ ILibExportMethod int ILibWebServer_Digest_ValidatePassword(struct ILibWebServer_ MD5_Final((unsigned char*)val, &mctx); util_tohex_lower(val, 16, result3); - retVal = strcmp(result3, response) == 0 ? 1 : 0; + retVal = (responseLen == 32 && strncmp(result3, response, 32)) == 0 ? 1 : 0; // if (retVal == 0) { ILibWebServer_Digest_SendUnauthorized(session, realm, strlen(realm)); } return retVal; diff --git a/microstack/ILibWrapperWebRTC.c b/microstack/ILibWrapperWebRTC.c index b9f160d..30e0111 100644 --- a/microstack/ILibWrapperWebRTC.c +++ b/microstack/ILibWrapperWebRTC.c @@ -59,8 +59,8 @@ typedef struct struct util_cert selftlscert; struct util_cert selftlsclientcert; SSL_CTX* ctx; - char tlsServerCertThumbprint[32]; - char g_selfid[UTIL_HASHSIZE]; + char tlsServerCertThumbprint[UTIL_SHA256_HASHSIZE]; + char g_selfid[UTIL_SHA256_HASHSIZE]; ILibWebRTC_TURN_ConnectFlags mTurnSetting; struct sockaddr_in6 mTurnServer; diff --git a/microstack/sha-private.h b/microstack/sha-private.h new file mode 100644 index 0000000..2c93e6e --- /dev/null +++ b/microstack/sha-private.h @@ -0,0 +1,61 @@ +/*************************** sha-private.h ***************************/ + +/* +https://github.com/Yubico/yubico-c-client + +Copyright (c) 2006-2013 Yubico AB +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following +disclaimer in the documentation and/or other materials provided +with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/********************** See RFC 4634 for details *********************/ +#ifndef _SHA_PRIVATE__H +#define _SHA_PRIVATE__H +/* +* These definitions are defined in FIPS-180-2, section 4.1. +* Ch() and Maj() are defined identically in sections 4.1.1, +* 4.1.2 and 4.1.3. +* +* The definitions used in FIPS-180-2 are as follows: +*/ + +#ifndef USE_MODIFIED_MACROS +#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +#else /* USE_MODIFIED_MACROS */ +/* +* The following definitions are equivalent and potentially faster. +*/ + +#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z)) +#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) +#endif /* USE_MODIFIED_MACROS */ + +#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z)) + +#endif /* _SHA_PRIVATE__H */ diff --git a/microstack/sha.h b/microstack/sha.h new file mode 100644 index 0000000..ebed869 --- /dev/null +++ b/microstack/sha.h @@ -0,0 +1,303 @@ +/**************************** sha.h ****************************/ + +/* +https://github.com/Yubico/yubico-c-client + +Copyright (c) 2006-2013 Yubico AB +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following +disclaimer in the documentation and/or other materials provided +with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/******************* See RFC 4634 for details ******************/ +#ifndef _SHA_H_ +#define _SHA_H_ + +/* +* Description: +* This file implements the Secure Hash Signature Standard +* algorithms as defined in the National Institute of Standards +* and Technology Federal Information Processing Standards +* Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 +* published on August 1, 2002, and the FIPS PUB 180-2 Change +* Notice published on February 28, 2004. +* +* A combined document showing all algorithms is available at +* http://csrc.nist.gov/publications/fips/ +* fips180-2/fips180-2withchangenotice.pdf +* +* The five hashes are defined in these sizes: +* SHA-1 20 byte / 160 bit +* SHA-224 28 byte / 224 bit +* SHA-256 32 byte / 256 bit +* SHA-384 48 byte / 384 bit +* SHA-512 64 byte / 512 bit +*/ + +#include +/* +* If you do not have the ISO standard stdint.h header file, then you +* must typedef the following: +* name meaning +* uint64_t unsigned 64 bit integer +* uint32_t unsigned 32 bit integer +* uint8_t unsigned 8 bit integer (i.e., unsigned char) +* int_least16_t integer of >= 16 bits +* +*/ + +#ifndef _SHA_enum_ +#define _SHA_enum_ +/* +* All SHA functions return one of these values. +*/ +enum +{ + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError, /* called Input after FinalBits or Result */ + shaBadParam /* passed a bad parameter */ +}; +#endif /* _SHA_enum_ */ + +/* +* These constants hold size information for each of the SHA +* hashing operations +*/ +enum +{ + SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64, + SHA256_Message_Block_Size = 64, SHA384_Message_Block_Size = 128, + SHA512_Message_Block_Size = 128, + USHA_Max_Message_Block_Size = SHA512_Message_Block_Size, + + SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32, + SHA384HashSize = 48, SHA512HashSize = 64, + USHAMaxHashSize = SHA512HashSize, + + SHA1HashSizeBits = 160, SHA224HashSizeBits = 224, + SHA256HashSizeBits = 256, SHA384HashSizeBits = 384, + SHA512HashSizeBits = 512, USHAMaxHashSizeBits = SHA512HashSizeBits +}; + +/* +* These constants are used in the USHA (unified sha) functions. +*/ +typedef enum SHAversion +{ + SHA1, SHA224, SHA256, SHA384, SHA512 +} SHAversion; + +/* +* This structure will hold context information for the SHA-1 +* hashing operation. +*/ +typedef struct SHA1Context +{ + uint32_t Intermediate_Hash[SHA1HashSize / 4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 512-bit message blocks */ + uint8_t Message_Block[SHA1_Message_Block_Size]; + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the digest corrupted? */ +} SHA1Context; + +/* +* This structure will hold context information for the SHA-256 +* hashing operation. +*/ +typedef struct SHA256Context +{ + uint32_t Intermediate_Hash[SHA256HashSize / 4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 512-bit message blocks */ + uint8_t Message_Block[SHA256_Message_Block_Size]; + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the digest corrupted? */ +} SHA256Context; + +/* +* This structure will hold context information for the SHA-512 +* hashing operation. +*/ +typedef struct SHA512Context +{ +#ifdef USE_32BIT_ONLY + uint32_t Intermediate_Hash[SHA512HashSize / 4]; /* Message Digest */ + uint32_t Length[4]; /* Message length in bits */ +#else /* !USE_32BIT_ONLY */ + uint64_t Intermediate_Hash[SHA512HashSize / 8]; /* Message Digest */ + uint64_t Length_Low, Length_High; /* Message length in bits */ +#endif /* USE_32BIT_ONLY */ + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 1024-bit message blocks */ + uint8_t Message_Block[SHA512_Message_Block_Size]; + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the digest corrupted? */ +} SHA512Context; + +/* +* This structure will hold context information for the SHA-224 +* hashing operation. It uses the SHA-256 structure for computation. +*/ +typedef struct SHA256Context SHA224Context; + +/* +* This structure will hold context information for the SHA-384 +* hashing operation. It uses the SHA-512 structure for computation. +*/ +typedef struct SHA512Context SHA384Context; + +/* +* This structure holds context information for all SHA +* hashing operations. +*/ +typedef struct USHAContext +{ + int whichSha; /* which SHA is being used */ + union + { + SHA1Context sha1Context; + SHA224Context sha224Context; + SHA256Context sha256Context; + SHA384Context sha384Context; + SHA512Context sha512Context; + } ctx; +} USHAContext; + +/* +* This structure will hold context information for the HMAC +* keyed hashing operation. +*/ +typedef struct HMACContext +{ + int whichSha; /* which SHA is being used */ + int hashSize; /* hash size of SHA being used */ + int blockSize; /* block size of SHA being used */ + USHAContext shaContext; /* SHA context */ + unsigned char k_opad[USHA_Max_Message_Block_Size]; + /* outer padding - key XORd with opad */ +} HMACContext; + +/* +* Function Prototypes +*/ + +/* SHA-1 */ +extern int SHA1Reset(SHA1Context *); +extern int SHA1Input(SHA1Context *, const uint8_t * bytes, + unsigned int bytecount); +extern int SHA1FinalBits(SHA1Context *, const uint8_t bits, + unsigned int bitcount); +extern int SHA1Result(SHA1Context *, uint8_t Message_Digest[SHA1HashSize]); + +/* SHA-224 */ +extern int SHA224Reset(SHA224Context *); +extern int SHA224Input(SHA224Context *, const uint8_t * bytes, + unsigned int bytecount); +extern int SHA224FinalBits(SHA224Context *, const uint8_t bits, + unsigned int bitcount); +extern int SHA224Result(SHA224Context *, + uint8_t Message_Digest[SHA224HashSize]); + +/* SHA-256 */ +extern int SHA256Reset(SHA256Context *); +extern int SHA256Input(SHA256Context *, const uint8_t * bytes, + unsigned int bytecount); +extern int SHA256FinalBits(SHA256Context *, const uint8_t bits, + unsigned int bitcount); +extern int SHA256Result(SHA256Context *, + uint8_t Message_Digest[SHA256HashSize]); + +/* SHA-384 */ +extern int SHA384Reset(SHA384Context *); +extern int SHA384Input(SHA384Context *, const uint8_t * bytes, + unsigned int bytecount); +extern int SHA384FinalBits(SHA384Context *, const uint8_t bits, + unsigned int bitcount); +extern int SHA384Result(SHA384Context *, + uint8_t Message_Digest[SHA384HashSize]); + +/* SHA-512 */ +extern int SHA512Reset(SHA512Context *); +extern int SHA512Input(SHA512Context *, const uint8_t * bytes, + unsigned int bytecount); +extern int SHA512FinalBits(SHA512Context *, const uint8_t bits, + unsigned int bitcount); +extern int SHA512Result(SHA512Context *, + uint8_t Message_Digest[SHA512HashSize]); + +/* Unified SHA functions, chosen by whichSha */ +extern int USHAReset(USHAContext *, SHAversion whichSha); +extern int USHAInput(USHAContext *, + const uint8_t * bytes, unsigned int bytecount); +extern int USHAFinalBits(USHAContext *, + const uint8_t bits, unsigned int bitcount); +extern int USHAResult(USHAContext *, + uint8_t Message_Digest[USHAMaxHashSize]); +extern int USHABlockSize(enum SHAversion whichSha); +extern int USHAHashSize(enum SHAversion whichSha); +extern int USHAHashSizeBits(enum SHAversion whichSha); + +/* +* HMAC Keyed-Hashing for Message Authentication, RFC2104, +* for all SHAs. +* This interface allows a fixed-length text input to be used. +*/ +extern int hmac(SHAversion whichSha, /* which SHA algorithm to use */ + const unsigned char *text, /* pointer to data stream */ + int text_len, /* length of data stream */ + const unsigned char *key, /* pointer to authentication key */ + int key_len, /* length of authentication key */ + uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */ + + /* + * HMAC Keyed-Hashing for Message Authentication, RFC2104, + * for all SHAs. + * This interface allows any length of text input to be used. + */ +extern int hmacReset(HMACContext * ctx, enum SHAversion whichSha, + const unsigned char *key, int key_len); +extern int hmacInput(HMACContext * ctx, const unsigned char *text, + int text_len); + +extern int hmacFinalBits(HMACContext * ctx, const uint8_t bits, + unsigned int bitcount); +extern int hmacResult(HMACContext * ctx, uint8_t digest[USHAMaxHashSize]); + +#endif /* _SHA_H_ */ diff --git a/microstack/sha224-256.c b/microstack/sha224-256.c new file mode 100644 index 0000000..977f8b6 --- /dev/null +++ b/microstack/sha224-256.c @@ -0,0 +1,636 @@ +/*************************** sha224-256.c ***************************/ +/* +https://github.com/Yubico/yubico-c-client + +Copyright (c) 2006-2013 Yubico AB +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following +disclaimer in the documentation and/or other materials provided +with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/********************* See RFC 4634 for details *********************/ +/* +* Description: +* This file implements the Secure Hash Signature Standard +* algorithms as defined in the National Institute of Standards +* and Technology Federal Information Processing Standards +* Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 +* published on August 1, 2002, and the FIPS PUB 180-2 Change +* Notice published on February 28, 2004. +* +* A combined document showing all algorithms is available at +* http://csrc.nist.gov/publications/fips/ +* fips180-2/fips180-2withchangenotice.pdf +* +* The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit +* message digests for a given data stream. It should take about +* 2**n steps to find a message with the same digest as a given +* message and 2**(n/2) to find any two messages with the same +* digest, when n is the digest size in bits. Therefore, this +* algorithm can serve as a means of providing a +* "fingerprint" for a message. +* +* Portability Issues: +* SHA-224 and SHA-256 are defined in terms of 32-bit "words". +* This code uses (included via "sha.h") to define 32 +* and 8 bit unsigned integer types. If your C compiler does not +* support 32 bit unsigned integers, this code is not +* appropriate. +* +* Caveats: +* SHA-224 and SHA-256 are designed to work with messages less +* than 2^64 bits long. This implementation uses SHA224/256Input() +* to hash the bits that are a multiple of the size of an 8-bit +* character, and then uses SHA224/256FinalBits() to hash the +* final few bits of the input. +*/ + +#include "sha.h" +#include "sha-private.h" +/* Define the SHA shift, rotate left and rotate right macro */ +#define SHA256_SHR(bits,word) ((word) >> (bits)) +#define SHA256_ROTL(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) +#define SHA256_ROTR(bits,word) \ + (((word) >> (bits)) | ((word) << (32-(bits)))) + +/* Define the SHA SIGMA and sigma macros */ +#define SHA256_SIGMA0(word) \ + (SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word)) +#define SHA256_SIGMA1(word) \ + (SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word)) +#define SHA256_sigma0(word) \ + (SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word)) +#define SHA256_sigma1(word) \ + (SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word)) + +/* +* add "length" to the length +*/ +static uint32_t addTemp; +#define SHA224_256AddLength(context, length) \ + (addTemp = (context)->Length_Low, (context)->Corrupted = \ + (((context)->Length_Low += (length)) < addTemp) && \ + (++(context)->Length_High == 0) ? 1 : 0) + +/* Local Function Prototypes */ +static void SHA224_256Finalize(SHA256Context * context, uint8_t Pad_Byte); +static void SHA224_256PadMessage(SHA256Context * context, uint8_t Pad_Byte); +static void SHA224_256ProcessMessageBlock(SHA256Context * context); +static int SHA224_256Reset(SHA256Context * context, uint32_t * H0); +static int SHA224_256ResultN(SHA256Context * context, + uint8_t Message_Digest[], int HashSize); + +/* Initial Hash Values: FIPS-180-2 Change Notice 1 */ +static uint32_t SHA224_H0[SHA256HashSize / 4] = { + 0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939, + 0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4 +}; + +/* Initial Hash Values: FIPS-180-2 section 5.3.2 */ +static uint32_t SHA256_H0[SHA256HashSize / 4] = { + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 +}; + +/* +* SHA224Reset +* +* Description: +* This function will initialize the SHA384Context in preparation +* for computing a new SHA224 message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* +* Returns: +* sha Error Code. +*/ +int +SHA224Reset(SHA224Context * context) +{ + return SHA224_256Reset(context, SHA224_H0); +} + +/* +* SHA224Input +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +* +*/ +int +SHA224Input(SHA224Context * context, const uint8_t * message_array, + unsigned int length) +{ + return SHA256Input(context, message_array, length); +} + +/* +* SHA224FinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +*/ +int +SHA224FinalBits(SHA224Context * context, + const uint8_t message_bits, unsigned int length) +{ + return SHA256FinalBits(context, message_bits, length); +} + +/* +* SHA224Result +* +* Description: +* This function will return the 224-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 28th element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* +* Returns: +* sha Error Code. +*/ +int +SHA224Result(SHA224Context * context, uint8_t Message_Digest[SHA224HashSize]) +{ + return SHA224_256ResultN(context, Message_Digest, SHA224HashSize); +} + +/* +* SHA256Reset +* +* Description: +* This function will initialize the SHA256Context in preparation +* for computing a new SHA256 message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* +* Returns: +* sha Error Code. +*/ +int +SHA256Reset(SHA256Context * context) +{ + return SHA224_256Reset(context, SHA256_H0); +} + +/* +* SHA256Input +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +*/ +int +SHA256Input(SHA256Context * context, const uint8_t * message_array, + unsigned int length) +{ + if (!length) + return shaSuccess; + + if (!context || !message_array) + return shaNull; + + if (context->Computed) + { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + while (length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + if (!SHA224_256AddLength(context, 8) && + (context->Message_Block_Index == SHA256_Message_Block_Size)) + SHA224_256ProcessMessageBlock(context); + + message_array++; + } + + return shaSuccess; + +} + +/* +* SHA256FinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +*/ +int +SHA256FinalBits(SHA256Context * context, + const uint8_t message_bits, unsigned int length) +{ + uint8_t masks[8] = { + /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80, + /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0, + /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8, + /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE + }; + uint8_t markbit[8] = { + /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40, + /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10, + /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04, + /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01 + }; + + if (!length) + return shaSuccess; + + if (!context) + return shaNull; + + if ((context->Computed) || (length >= 8) || (length == 0)) + { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + SHA224_256AddLength(context, length); + SHA224_256Finalize(context, (uint8_t) + ((message_bits & masks[length]) | markbit[length])); + + return shaSuccess; +} + +/* +* SHA256Result +* +* Description: +* This function will return the 256-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 32nd element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* +* Returns: +* sha Error Code. +*/ +int +SHA256Result(SHA256Context * context, uint8_t Message_Digest[]) +{ + return SHA224_256ResultN(context, Message_Digest, SHA256HashSize); +} + +/* +* SHA224_256Finalize +* +* Description: +* This helper function finishes off the digest calculations. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* Pad_Byte: [in] +* The last byte to add to the digest before the 0-padding +* and length. This will contain the last bits of the message +* followed by another single bit. If the message was an +* exact multiple of 8-bits long, Pad_Byte will be 0x80. +* +* Returns: +* sha Error Code. +*/ +static void +SHA224_256Finalize(SHA256Context * context, uint8_t Pad_Byte) +{ + int i; + SHA224_256PadMessage(context, Pad_Byte); + /* message may be sensitive, so clear it out */ + for (i = 0; i < SHA256_Message_Block_Size; ++i) + context->Message_Block[i] = 0; + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; +} + +/* +* SHA224_256PadMessage +* +* Description: +* According to the standard, the message must be padded to an +* even 512 bits. The first padding bit must be a '1'. The +* last 64 bits represent the length of the original message. +* All bits in between should be 0. This helper function will pad +* the message according to those rules by filling the +* Message_Block array accordingly. When it returns, it can be +* assumed that the message digest has been computed. +* +* Parameters: +* context: [in/out] +* The context to pad +* Pad_Byte: [in] +* The last byte to add to the digest before the 0-padding +* and length. This will contain the last bits of the message +* followed by another single bit. If the message was an +* exact multiple of 8-bits long, Pad_Byte will be 0x80. +* +* Returns: +* Nothing. +*/ +static void +SHA224_256PadMessage(SHA256Context * context, uint8_t Pad_Byte) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index >= (SHA256_Message_Block_Size - 8)) + { + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + while (context->Message_Block_Index < SHA256_Message_Block_Size) + context->Message_Block[context->Message_Block_Index++] = 0; + SHA224_256ProcessMessageBlock(context); + } + else + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + + while (context->Message_Block_Index < (SHA256_Message_Block_Size - 8)) + context->Message_Block[context->Message_Block_Index++] = 0; + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (uint8_t)(context->Length_High >> 24); + context->Message_Block[57] = (uint8_t)(context->Length_High >> 16); + context->Message_Block[58] = (uint8_t)(context->Length_High >> 8); + context->Message_Block[59] = (uint8_t)(context->Length_High); + context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24); + context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16); + context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8); + context->Message_Block[63] = (uint8_t)(context->Length_Low); + + SHA224_256ProcessMessageBlock(context); +} + +/* +* SHA224_256ProcessMessageBlock +* +* Description: +* This function will process the next 512 bits of the message +* stored in the Message_Block array. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* +* Returns: +* Nothing. +* +* Comments: +* Many of the variable names in this code, especially the +* single character names, were used because those were the +* names used in the publication. +*/ +static void +SHA224_256ProcessMessageBlock(SHA256Context * context) +{ + /* Constants defined in FIPS-180-2, section 4.2.2 */ + static const uint32_t K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, + 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, + 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, + 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, + 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, + 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, + 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, + 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, + 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + int t, t4; /* Loop counter */ + uint32_t temp1, temp2; /* Temporary word value */ + uint32_t W[64]; /* Word sequence */ + uint32_t A, B, C, D, E, F, G, H; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = t4 = 0; t < 16; t++, t4 += 4) + W[t] = (((uint32_t)context->Message_Block[t4]) << 24) | + (((uint32_t)context->Message_Block[t4 + 1]) << 16) | + (((uint32_t)context->Message_Block[t4 + 2]) << 8) | + (((uint32_t)context->Message_Block[t4 + 3])); + + for (t = 16; t < 64; t++) + W[t] = SHA256_sigma1(W[t - 2]) + W[t - 7] + + SHA256_sigma0(W[t - 15]) + W[t - 16]; + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + F = context->Intermediate_Hash[5]; + G = context->Intermediate_Hash[6]; + H = context->Intermediate_Hash[7]; + + for (t = 0; t < 64; t++) + { + temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E, F, G) + K[t] + W[t]; + temp2 = SHA256_SIGMA0(A) + SHA_Maj(A, B, C); + H = G; + G = F; + F = E; + E = D + temp1; + D = C; + C = B; + B = A; + A = temp1 + temp2; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + context->Intermediate_Hash[5] += F; + context->Intermediate_Hash[6] += G; + context->Intermediate_Hash[7] += H; + + context->Message_Block_Index = 0; +} + +/* +* SHA224_256Reset +* +* Description: +* This helper function will initialize the SHA256Context in +* preparation for computing a new SHA256 message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* H0 +* The initial hash value to use. +* +* Returns: +* sha Error Code. +*/ +static int +SHA224_256Reset(SHA256Context * context, uint32_t * H0) +{ + if (!context) + return shaNull; + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = H0[0]; + context->Intermediate_Hash[1] = H0[1]; + context->Intermediate_Hash[2] = H0[2]; + context->Intermediate_Hash[3] = H0[3]; + context->Intermediate_Hash[4] = H0[4]; + context->Intermediate_Hash[5] = H0[5]; + context->Intermediate_Hash[6] = H0[6]; + context->Intermediate_Hash[7] = H0[7]; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* +* SHA224_256ResultN +* +* Description: +* This helper function will return the 224-bit or 256-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 28th/32nd element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* HashSize: [in] +* The size of the hash, either 28 or 32. +* +* Returns: +* sha Error Code. +*/ +static int +SHA224_256ResultN(SHA256Context * context, + uint8_t Message_Digest[], int HashSize) +{ + int i; + + if (!context || !Message_Digest) + return shaNull; + + if (context->Corrupted) + return context->Corrupted; + + if (!context->Computed) + SHA224_256Finalize(context, 0x80); + + for (i = 0; i < HashSize; ++i) + Message_Digest[i] = (uint8_t) + (context->Intermediate_Hash[i >> 2] >> 8 * (3 - (i & 0x03))); + + return shaSuccess; +} diff --git a/microstack/sha384-512.c b/microstack/sha384-512.c new file mode 100644 index 0000000..6cd8d9c --- /dev/null +++ b/microstack/sha384-512.c @@ -0,0 +1,1090 @@ +/*************************** sha384-512.c ***************************/ + +/* +https://github.com/Yubico/yubico-c-client + +Copyright (c) 2006-2013 Yubico AB +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following +disclaimer in the documentation and/or other materials provided +with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/********************* See RFC 4634 for details *********************/ +/* +* Description: +* This file implements the Secure Hash Signature Standard +* algorithms as defined in the National Institute of Standards +* and Technology Federal Information Processing Standards +* Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 +* published on August 1, 2002, and the FIPS PUB 180-2 Change +* Notice published on February 28, 2004. +* +* A combined document showing all algorithms is available at +* http://csrc.nist.gov/publications/fips/ +* fips180-2/fips180-2withchangenotice.pdf +* +* The SHA-384 and SHA-512 algorithms produce 384-bit and 512-bit +* message digests for a given data stream. It should take about +* 2**n steps to find a message with the same digest as a given +* message and 2**(n/2) to find any two messages with the same +* digest, when n is the digest size in bits. Therefore, this +* algorithm can serve as a means of providing a +* "fingerprint" for a message. +* +* Portability Issues: +* SHA-384 and SHA-512 are defined in terms of 64-bit "words", +* but if USE_32BIT_ONLY is #defined, this code is implemented in +* terms of 32-bit "words". This code uses (included +* via "sha.h") to define the 64, 32 and 8 bit unsigned integer +* types. If your C compiler does not support 64 bit unsigned +* integers, and you do not #define USE_32BIT_ONLY, this code is +* not appropriate. +* +* Caveats: +* SHA-384 and SHA-512 are designed to work with messages less +* than 2^128 bits long. This implementation uses +* SHA384/512Input() to hash the bits that are a multiple of the +* size of an 8-bit character, and then uses SHA384/256FinalBits() +* to hash the final few bits of the input. +* +*/ + +#include "sha.h" +#include "sha-private.h" + +#ifdef USE_32BIT_ONLY +/* +* Define 64-bit arithmetic in terms of 32-bit arithmetic. +* Each 64-bit number is represented in a 2-word array. +* All macros are defined such that the result is the last parameter. +*/ + +/* +* Define shift, rotate left and rotate right functions +*/ +#define SHA512_SHR(bits, word, ret) ( \ + /* (((uint64_t)((word))) >> (bits)) */ \ + (ret)[0] = (((bits) < 32) && ((bits) >= 0)) ? \ + ((word)[0] >> (bits)) : 0, \ + (ret)[1] = ((bits) > 32) ? ((word)[0] >> ((bits) - 32)) : \ + ((bits) == 32) ? (word)[0] : \ + ((bits) >= 0) ? \ + (((word)[0] << (32 - (bits))) | \ + ((word)[1] >> (bits))) : 0 ) + +#define SHA512_SHL(bits, word, ret) ( \ + /* (((uint64_t)(word)) << (bits)) */ \ + (ret)[0] = ((bits) > 32) ? ((word)[1] << ((bits) - 32)) : \ + ((bits) == 32) ? (word)[1] : \ + ((bits) >= 0) ? \ + (((word)[0] << (bits)) | \ + ((word)[1] >> (32 - (bits)))) : \ + 0, \ + (ret)[1] = (((bits) < 32) && ((bits) >= 0)) ? \ + ((word)[1] << (bits)) : 0 ) + +/* +* Define 64-bit OR +*/ +#define SHA512_OR(word1, word2, ret) ( \ + (ret)[0] = (word1)[0] | (word2)[0], \ + (ret)[1] = (word1)[1] | (word2)[1] ) + +/* +* Define 64-bit XOR +*/ +#define SHA512_XOR(word1, word2, ret) ( \ + (ret)[0] = (word1)[0] ^ (word2)[0], \ + (ret)[1] = (word1)[1] ^ (word2)[1] ) + +/* +* Define 64-bit AND +*/ +#define SHA512_AND(word1, word2, ret) ( \ + (ret)[0] = (word1)[0] & (word2)[0], \ + (ret)[1] = (word1)[1] & (word2)[1] ) + +/* +* Define 64-bit TILDA +*/ +#define SHA512_TILDA(word, ret) \ + ( (ret)[0] = ~(word)[0], (ret)[1] = ~(word)[1] ) + +/* +* Define 64-bit ADD +*/ +#define SHA512_ADD(word1, word2, ret) ( \ + (ret)[1] = (word1)[1], (ret)[1] += (word2)[1], \ + (ret)[0] = (word1)[0] + (word2)[0] + ((ret)[1] < (word1)[1]) ) + +/* +* Add the 4word value in word2 to word1. +*/ +static uint32_t ADDTO4_temp, ADDTO4_temp2; +#define SHA512_ADDTO4(word1, word2) ( \ + ADDTO4_temp = (word1)[3], \ + (word1)[3] += (word2)[3], \ + ADDTO4_temp2 = (word1)[2], \ + (word1)[2] += (word2)[2] + ((word1)[3] < ADDTO4_temp), \ + ADDTO4_temp = (word1)[1], \ + (word1)[1] += (word2)[1] + ((word1)[2] < ADDTO4_temp2), \ + (word1)[0] += (word2)[0] + ((word1)[1] < ADDTO4_temp) ) + +/* +* Add the 2word value in word2 to word1. +*/ +static uint32_t ADDTO2_temp; +#define SHA512_ADDTO2(word1, word2) ( \ + ADDTO2_temp = (word1)[1], \ + (word1)[1] += (word2)[1], \ + (word1)[0] += (word2)[0] + ((word1)[1] < ADDTO2_temp) ) + +/* +* SHA rotate ((word >> bits) | (word << (64-bits))) +*/ +static uint32_t ROTR_temp1[2], ROTR_temp2[2]; +#define SHA512_ROTR(bits, word, ret) ( \ + SHA512_SHR((bits), (word), ROTR_temp1), \ + SHA512_SHL(64-(bits), (word), ROTR_temp2), \ + SHA512_OR(ROTR_temp1, ROTR_temp2, (ret)) ) + +/* +* Define the SHA SIGMA and sigma macros +* SHA512_ROTR(28,word) ^ SHA512_ROTR(34,word) ^ SHA512_ROTR(39,word) +*/ +static uint32_t SIGMA0_temp1[2], SIGMA0_temp2[2], +SIGMA0_temp3[2], SIGMA0_temp4[2]; +#define SHA512_SIGMA0(word, ret) ( \ + SHA512_ROTR(28, (word), SIGMA0_temp1), \ + SHA512_ROTR(34, (word), SIGMA0_temp2), \ + SHA512_ROTR(39, (word), SIGMA0_temp3), \ + SHA512_XOR(SIGMA0_temp2, SIGMA0_temp3, SIGMA0_temp4), \ + SHA512_XOR(SIGMA0_temp1, SIGMA0_temp4, (ret)) ) + +/* +* SHA512_ROTR(14,word) ^ SHA512_ROTR(18,word) ^ SHA512_ROTR(41,word) +*/ +static uint32_t SIGMA1_temp1[2], SIGMA1_temp2[2], +SIGMA1_temp3[2], SIGMA1_temp4[2]; +#define SHA512_SIGMA1(word, ret) ( \ + SHA512_ROTR(14, (word), SIGMA1_temp1), \ + SHA512_ROTR(18, (word), SIGMA1_temp2), \ + SHA512_ROTR(41, (word), SIGMA1_temp3), \ + SHA512_XOR(SIGMA1_temp2, SIGMA1_temp3, SIGMA1_temp4), \ + SHA512_XOR(SIGMA1_temp1, SIGMA1_temp4, (ret)) ) + +/* +* (SHA512_ROTR( 1,word) ^ SHA512_ROTR( 8,word) ^ SHA512_SHR( 7,word)) +*/ +static uint32_t sigma0_temp1[2], sigma0_temp2[2], +sigma0_temp3[2], sigma0_temp4[2]; +#define SHA512_sigma0(word, ret) ( \ + SHA512_ROTR( 1, (word), sigma0_temp1), \ + SHA512_ROTR( 8, (word), sigma0_temp2), \ + SHA512_SHR( 7, (word), sigma0_temp3), \ + SHA512_XOR(sigma0_temp2, sigma0_temp3, sigma0_temp4), \ + SHA512_XOR(sigma0_temp1, sigma0_temp4, (ret)) ) + +/* +* (SHA512_ROTR(19,word) ^ SHA512_ROTR(61,word) ^ SHA512_SHR( 6,word)) +*/ +static uint32_t sigma1_temp1[2], sigma1_temp2[2], +sigma1_temp3[2], sigma1_temp4[2]; +#define SHA512_sigma1(word, ret) ( \ + SHA512_ROTR(19, (word), sigma1_temp1), \ + SHA512_ROTR(61, (word), sigma1_temp2), \ + SHA512_SHR( 6, (word), sigma1_temp3), \ + SHA512_XOR(sigma1_temp2, sigma1_temp3, sigma1_temp4), \ + SHA512_XOR(sigma1_temp1, sigma1_temp4, (ret)) ) + +#undef SHA_Ch +#undef SHA_Maj + +#ifndef USE_MODIFIED_MACROS +/* +* These definitions are the ones used in FIPS-180-2, section 4.1.3 +* Ch(x,y,z) ((x & y) ^ (~x & z)) +*/ +static uint32_t Ch_temp1[2], Ch_temp2[2], Ch_temp3[2]; +#define SHA_Ch(x, y, z, ret) ( \ + SHA512_AND(x, y, Ch_temp1), \ + SHA512_TILDA(x, Ch_temp2), \ + SHA512_AND(Ch_temp2, z, Ch_temp3), \ + SHA512_XOR(Ch_temp1, Ch_temp3, (ret)) ) +/* +* Maj(x,y,z) (((x)&(y)) ^ ((x)&(z)) ^ ((y)&(z))) +*/ +static uint32_t Maj_temp1[2], Maj_temp2[2], Maj_temp3[2], Maj_temp4[2]; +#define SHA_Maj(x, y, z, ret) ( \ + SHA512_AND(x, y, Maj_temp1), \ + SHA512_AND(x, z, Maj_temp2), \ + SHA512_AND(y, z, Maj_temp3), \ + SHA512_XOR(Maj_temp2, Maj_temp3, Maj_temp4), \ + SHA512_XOR(Maj_temp1, Maj_temp4, (ret)) ) + +#else /* !USE_32BIT_ONLY */ +/* +* These definitions are potentially faster equivalents for the ones +* used in FIPS-180-2, section 4.1.3. +* ((x & y) ^ (~x & z)) becomes +* ((x & (y ^ z)) ^ z) +*/ +#define SHA_Ch(x, y, z, ret) ( \ + (ret)[0] = (((x)[0] & ((y)[0] ^ (z)[0])) ^ (z)[0]), \ + (ret)[1] = (((x)[1] & ((y)[1] ^ (z)[1])) ^ (z)[1]) ) + +/* +* ((x & y) ^ (x & z) ^ (y & z)) becomes +* ((x & (y | z)) | (y & z)) +*/ +#define SHA_Maj(x, y, z, ret) ( \ + ret[0] = (((x)[0] & ((y)[0] | (z)[0])) | ((y)[0] & (z)[0])), \ + ret[1] = (((x)[1] & ((y)[1] | (z)[1])) | ((y)[1] & (z)[1])) ) +#endif /* USE_MODIFIED_MACROS */ + +/* +* add "length" to the length +*/ +static uint32_t addTemp[4] = { 0, 0, 0, 0 }; + +#define SHA384_512AddLength(context, length) ( \ + addTemp[3] = (length), SHA512_ADDTO4((context)->Length, addTemp), \ + (context)->Corrupted = (((context)->Length[3] == 0) && \ + ((context)->Length[2] == 0) && ((context)->Length[1] == 0) && \ + ((context)->Length[0] < 8)) ? 1 : 0 ) + +/* Local Function Prototypes */ +static void SHA384_512Finalize(SHA512Context * context, uint8_t Pad_Byte); +static void SHA384_512PadMessage(SHA512Context * context, uint8_t Pad_Byte); +static void SHA384_512ProcessMessageBlock(SHA512Context * context); +static int SHA384_512Reset(SHA512Context * context, uint32_t H0[]); +static int SHA384_512ResultN(SHA512Context * context, + uint8_t Message_Digest[], int HashSize); + +/* Initial Hash Values: FIPS-180-2 sections 5.3.3 and 5.3.4 */ +static uint32_t SHA384_H0[SHA512HashSize / 4] = { + 0xCBBB9D5D, 0xC1059ED8, 0x629A292A, 0x367CD507, 0x9159015A, + 0x3070DD17, 0x152FECD8, 0xF70E5939, 0x67332667, 0xFFC00B31, + 0x8EB44A87, 0x68581511, 0xDB0C2E0D, 0x64F98FA7, 0x47B5481D, + 0xBEFA4FA4 +}; + +static uint32_t SHA512_H0[SHA512HashSize / 4] = { + 0x6A09E667, 0xF3BCC908, 0xBB67AE85, 0x84CAA73B, 0x3C6EF372, + 0xFE94F82B, 0xA54FF53A, 0x5F1D36F1, 0x510E527F, 0xADE682D1, + 0x9B05688C, 0x2B3E6C1F, 0x1F83D9AB, 0xFB41BD6B, 0x5BE0CD19, + 0x137E2179 +}; + +#else /* !USE_32BIT_ONLY */ + +/* Define the SHA shift, rotate left and rotate right macro */ +#define SHA512_SHR(bits,word) (((uint64_t)(word)) >> (bits)) +#define SHA512_ROTR(bits,word) ((((uint64_t)(word)) >> (bits)) | \ + (((uint64_t)(word)) << (64-(bits)))) + +/* Define the SHA SIGMA and sigma macros */ +#define SHA512_SIGMA0(word) \ + (SHA512_ROTR(28,word) ^ SHA512_ROTR(34,word) ^ SHA512_ROTR(39,word)) +#define SHA512_SIGMA1(word) \ + (SHA512_ROTR(14,word) ^ SHA512_ROTR(18,word) ^ SHA512_ROTR(41,word)) +#define SHA512_sigma0(word) \ + (SHA512_ROTR( 1,word) ^ SHA512_ROTR( 8,word) ^ SHA512_SHR( 7,word)) +#define SHA512_sigma1(word) \ + (SHA512_ROTR(19,word) ^ SHA512_ROTR(61,word) ^ SHA512_SHR( 6,word)) + +/* +* add "length" to the length +*/ +static uint64_t addTemp; +#define SHA384_512AddLength(context, length) \ + (addTemp = context->Length_Low, context->Corrupted = \ + ((context->Length_Low += length) < addTemp) && \ + (++context->Length_High == 0) ? 1 : 0) + +/* Local Function Prototypes */ +static void SHA384_512Finalize(SHA512Context * context, uint8_t Pad_Byte); +static void SHA384_512PadMessage(SHA512Context * context, uint8_t Pad_Byte); +static void SHA384_512ProcessMessageBlock(SHA512Context * context); +static int SHA384_512Reset(SHA512Context * context, uint64_t H0[]); +static int SHA384_512ResultN(SHA512Context * context, + uint8_t Message_Digest[], int HashSize); + +/* Initial Hash Values: FIPS-180-2 sections 5.3.3 and 5.3.4 */ +static uint64_t SHA384_H0[] = { + 0xCBBB9D5DC1059ED8ll, 0x629A292A367CD507ll, 0x9159015A3070DD17ll, + 0x152FECD8F70E5939ll, 0x67332667FFC00B31ll, 0x8EB44A8768581511ll, + 0xDB0C2E0D64F98FA7ll, 0x47B5481DBEFA4FA4ll +}; + +static uint64_t SHA512_H0[] = { + 0x6A09E667F3BCC908ll, 0xBB67AE8584CAA73Bll, 0x3C6EF372FE94F82Bll, + 0xA54FF53A5F1D36F1ll, 0x510E527FADE682D1ll, 0x9B05688C2B3E6C1Fll, + 0x1F83D9ABFB41BD6Bll, 0x5BE0CD19137E2179ll +}; + +#endif /* USE_32BIT_ONLY */ + +/* +* SHA384Reset +* +* Description: +* This function will initialize the SHA384Context in preparation +* for computing a new SHA384 message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* +* Returns: +* sha Error Code. +* +*/ +int +SHA384Reset(SHA384Context * context) +{ + return SHA384_512Reset(context, SHA384_H0); +} + +/* +* SHA384Input +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +* +*/ +int +SHA384Input(SHA384Context * context, + const uint8_t * message_array, unsigned int length) +{ + return SHA512Input(context, message_array, length); +} + +/* +* SHA384FinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +* +*/ +int +SHA384FinalBits(SHA384Context * context, + const uint8_t message_bits, unsigned int length) +{ + return SHA512FinalBits(context, message_bits, length); +} + +/* +* SHA384Result +* +* Description: +* This function will return the 384-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 48th element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* +* Returns: +* sha Error Code. +* +*/ +int +SHA384Result(SHA384Context * context, uint8_t Message_Digest[SHA384HashSize]) +{ + return SHA384_512ResultN(context, Message_Digest, SHA384HashSize); +} + +/* +* SHA512Reset +* +* Description: +* This function will initialize the SHA512Context in preparation +* for computing a new SHA512 message digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* +* Returns: +* sha Error Code. +* +*/ +int +SHA512Reset(SHA512Context * context) +{ + return SHA384_512Reset(context, SHA512_H0); +} + +/* +* SHA512Input +* +* Description: +* This function accepts an array of octets as the next portion +* of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_array: [in] +* An array of characters representing the next portion of +* the message. +* length: [in] +* The length of the message in message_array +* +* Returns: +* sha Error Code. +* +*/ +int +SHA512Input(SHA512Context * context, + const uint8_t * message_array, unsigned int length) +{ + if (!length) + return shaSuccess; + + if (!context || !message_array) + return shaNull; + + if (context->Computed) + { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + while (length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + if (!SHA384_512AddLength(context, 8) && + (context->Message_Block_Index == SHA512_Message_Block_Size)) + SHA384_512ProcessMessageBlock(context); + + message_array++; + } + + return shaSuccess; +} + +/* +* SHA512FinalBits +* +* Description: +* This function will add in any final bits of the message. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* message_bits: [in] +* The final bits of the message, in the upper portion of the +* byte. (Use 0b###00000 instead of 0b00000### to input the +* three bits ###.) +* length: [in] +* The number of bits in message_bits, between 1 and 7. +* +* Returns: +* sha Error Code. +* +*/ +int +SHA512FinalBits(SHA512Context * context, + const uint8_t message_bits, unsigned int length) +{ + uint8_t masks[8] = { + /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80, + /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0, + /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8, + /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE + }; + uint8_t markbit[8] = { + /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40, + /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10, + /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04, + /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01 + }; + + if (!length) + return shaSuccess; + + if (!context) + return shaNull; + + if ((context->Computed) || (length >= 8) || (length == 0)) + { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + SHA384_512AddLength(context, length); + SHA384_512Finalize(context, (uint8_t) + ((message_bits & masks[length]) | markbit[length])); + + return shaSuccess; +} + +/* +* SHA384_512Finalize +* +* Description: +* This helper function finishes off the digest calculations. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* Pad_Byte: [in] +* The last byte to add to the digest before the 0-padding +* and length. This will contain the last bits of the message +* followed by another single bit. If the message was an +* exact multiple of 8-bits long, Pad_Byte will be 0x80. +* +* Returns: +* sha Error Code. +* +*/ +static void +SHA384_512Finalize(SHA512Context * context, uint8_t Pad_Byte) +{ + int_least16_t i; + SHA384_512PadMessage(context, Pad_Byte); + /* message may be sensitive, clear it out */ + for (i = 0; i < SHA512_Message_Block_Size; ++i) + context->Message_Block[i] = 0; +#ifdef USE_32BIT_ONLY /* and clear length */ + context->Length[0] = context->Length[1] = 0; + context->Length[2] = context->Length[3] = 0; +#else /* !USE_32BIT_ONLY */ + context->Length_Low = 0; + context->Length_High = 0; +#endif /* USE_32BIT_ONLY */ + context->Computed = 1; +} + +/* +* SHA512Result +* +* Description: +* This function will return the 512-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 64th element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* +* Returns: +* sha Error Code. +* +*/ +int +SHA512Result(SHA512Context * context, uint8_t Message_Digest[SHA512HashSize]) +{ + return SHA384_512ResultN(context, Message_Digest, SHA512HashSize); +} + +/* +* SHA384_512PadMessage +* +* Description: +* According to the standard, the message must be padded to an +* even 1024 bits. The first padding bit must be a '1'. The +* last 128 bits represent the length of the original message. +* All bits in between should be 0. This helper function will +* pad the message according to those rules by filling the +* Message_Block array accordingly. When it returns, it can be +* assumed that the message digest has been computed. +* +* Parameters: +* context: [in/out] +* The context to pad +* Pad_Byte: [in] +* The last byte to add to the digest before the 0-padding +* and length. This will contain the last bits of the message +* followed by another single bit. If the message was an +* exact multiple of 8-bits long, Pad_Byte will be 0x80. +* +* Returns: +* Nothing. +* +*/ +static void +SHA384_512PadMessage(SHA512Context * context, uint8_t Pad_Byte) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index >= (SHA512_Message_Block_Size - 16)) + { + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + while (context->Message_Block_Index < SHA512_Message_Block_Size) + context->Message_Block[context->Message_Block_Index++] = 0; + + SHA384_512ProcessMessageBlock(context); + } + else + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + + while (context->Message_Block_Index < (SHA512_Message_Block_Size - 16)) + context->Message_Block[context->Message_Block_Index++] = 0; + + /* + * Store the message length as the last 16 octets + */ +#ifdef USE_32BIT_ONLY + context->Message_Block[112] = (uint8_t)(context->Length[0] >> 24); + context->Message_Block[113] = (uint8_t)(context->Length[0] >> 16); + context->Message_Block[114] = (uint8_t)(context->Length[0] >> 8); + context->Message_Block[115] = (uint8_t)(context->Length[0]); + context->Message_Block[116] = (uint8_t)(context->Length[1] >> 24); + context->Message_Block[117] = (uint8_t)(context->Length[1] >> 16); + context->Message_Block[118] = (uint8_t)(context->Length[1] >> 8); + context->Message_Block[119] = (uint8_t)(context->Length[1]); + + context->Message_Block[120] = (uint8_t)(context->Length[2] >> 24); + context->Message_Block[121] = (uint8_t)(context->Length[2] >> 16); + context->Message_Block[122] = (uint8_t)(context->Length[2] >> 8); + context->Message_Block[123] = (uint8_t)(context->Length[2]); + context->Message_Block[124] = (uint8_t)(context->Length[3] >> 24); + context->Message_Block[125] = (uint8_t)(context->Length[3] >> 16); + context->Message_Block[126] = (uint8_t)(context->Length[3] >> 8); + context->Message_Block[127] = (uint8_t)(context->Length[3]); +#else /* !USE_32BIT_ONLY */ + context->Message_Block[112] = (uint8_t)(context->Length_High >> 56); + context->Message_Block[113] = (uint8_t)(context->Length_High >> 48); + context->Message_Block[114] = (uint8_t)(context->Length_High >> 40); + context->Message_Block[115] = (uint8_t)(context->Length_High >> 32); + context->Message_Block[116] = (uint8_t)(context->Length_High >> 24); + context->Message_Block[117] = (uint8_t)(context->Length_High >> 16); + context->Message_Block[118] = (uint8_t)(context->Length_High >> 8); + context->Message_Block[119] = (uint8_t)(context->Length_High); + + context->Message_Block[120] = (uint8_t)(context->Length_Low >> 56); + context->Message_Block[121] = (uint8_t)(context->Length_Low >> 48); + context->Message_Block[122] = (uint8_t)(context->Length_Low >> 40); + context->Message_Block[123] = (uint8_t)(context->Length_Low >> 32); + context->Message_Block[124] = (uint8_t)(context->Length_Low >> 24); + context->Message_Block[125] = (uint8_t)(context->Length_Low >> 16); + context->Message_Block[126] = (uint8_t)(context->Length_Low >> 8); + context->Message_Block[127] = (uint8_t)(context->Length_Low); +#endif /* USE_32BIT_ONLY */ + + SHA384_512ProcessMessageBlock(context); +} + +/* +* SHA384_512ProcessMessageBlock +* +* Description: +* This helper function will process the next 1024 bits of the +* message stored in the Message_Block array. +* +* Parameters: +* context: [in/out] +* The SHA context to update +* +* Returns: +* Nothing. +* +* Comments: +* Many of the variable names in this code, especially the +* single character names, were used because those were the +* names used in the publication. +* +* +*/ +static void +SHA384_512ProcessMessageBlock(SHA512Context * context) +{ + /* Constants defined in FIPS-180-2, section 4.2.3 */ +#ifdef USE_32BIT_ONLY + static const uint32_t K[80 * 2] = { + 0x428A2F98, 0xD728AE22, 0x71374491, 0x23EF65CD, 0xB5C0FBCF, + 0xEC4D3B2F, 0xE9B5DBA5, 0x8189DBBC, 0x3956C25B, 0xF348B538, + 0x59F111F1, 0xB605D019, 0x923F82A4, 0xAF194F9B, 0xAB1C5ED5, + 0xDA6D8118, 0xD807AA98, 0xA3030242, 0x12835B01, 0x45706FBE, + 0x243185BE, 0x4EE4B28C, 0x550C7DC3, 0xD5FFB4E2, 0x72BE5D74, + 0xF27B896F, 0x80DEB1FE, 0x3B1696B1, 0x9BDC06A7, 0x25C71235, + 0xC19BF174, 0xCF692694, 0xE49B69C1, 0x9EF14AD2, 0xEFBE4786, + 0x384F25E3, 0x0FC19DC6, 0x8B8CD5B5, 0x240CA1CC, 0x77AC9C65, + 0x2DE92C6F, 0x592B0275, 0x4A7484AA, 0x6EA6E483, 0x5CB0A9DC, + 0xBD41FBD4, 0x76F988DA, 0x831153B5, 0x983E5152, 0xEE66DFAB, + 0xA831C66D, 0x2DB43210, 0xB00327C8, 0x98FB213F, 0xBF597FC7, + 0xBEEF0EE4, 0xC6E00BF3, 0x3DA88FC2, 0xD5A79147, 0x930AA725, + 0x06CA6351, 0xE003826F, 0x14292967, 0x0A0E6E70, 0x27B70A85, + 0x46D22FFC, 0x2E1B2138, 0x5C26C926, 0x4D2C6DFC, 0x5AC42AED, + 0x53380D13, 0x9D95B3DF, 0x650A7354, 0x8BAF63DE, 0x766A0ABB, + 0x3C77B2A8, 0x81C2C92E, 0x47EDAEE6, 0x92722C85, 0x1482353B, + 0xA2BFE8A1, 0x4CF10364, 0xA81A664B, 0xBC423001, 0xC24B8B70, + 0xD0F89791, 0xC76C51A3, 0x0654BE30, 0xD192E819, 0xD6EF5218, + 0xD6990624, 0x5565A910, 0xF40E3585, 0x5771202A, 0x106AA070, + 0x32BBD1B8, 0x19A4C116, 0xB8D2D0C8, 0x1E376C08, 0x5141AB53, + 0x2748774C, 0xDF8EEB99, 0x34B0BCB5, 0xE19B48A8, 0x391C0CB3, + 0xC5C95A63, 0x4ED8AA4A, 0xE3418ACB, 0x5B9CCA4F, 0x7763E373, + 0x682E6FF3, 0xD6B2B8A3, 0x748F82EE, 0x5DEFB2FC, 0x78A5636F, + 0x43172F60, 0x84C87814, 0xA1F0AB72, 0x8CC70208, 0x1A6439EC, + 0x90BEFFFA, 0x23631E28, 0xA4506CEB, 0xDE82BDE9, 0xBEF9A3F7, + 0xB2C67915, 0xC67178F2, 0xE372532B, 0xCA273ECE, 0xEA26619C, + 0xD186B8C7, 0x21C0C207, 0xEADA7DD6, 0xCDE0EB1E, 0xF57D4F7F, + 0xEE6ED178, 0x06F067AA, 0x72176FBA, 0x0A637DC5, 0xA2C898A6, + 0x113F9804, 0xBEF90DAE, 0x1B710B35, 0x131C471B, 0x28DB77F5, + 0x23047D84, 0x32CAAB7B, 0x40C72493, 0x3C9EBE0A, 0x15C9BEBC, + 0x431D67C4, 0x9C100D4C, 0x4CC5D4BE, 0xCB3E42B6, 0x597F299C, + 0xFC657E2A, 0x5FCB6FAB, 0x3AD6FAEC, 0x6C44198C, 0x4A475817 + }; + int t, t2, t8; /* Loop counter */ + uint32_t temp1[2], temp2[2], /* Temporary word values */ + temp3[2], temp4[2], temp5[2]; + uint32_t W[2 * 80]; /* Word sequence */ + uint32_t A[2], B[2], C[2], D[2], /* Word buffers */ + E[2], F[2], G[2], H[2]; + + /* Initialize the first 16 words in the array W */ + for (t = t2 = t8 = 0; t < 16; t++, t8 += 8) + { + W[t2++] = ((((uint32_t)context->Message_Block[t8])) << 24) | + ((((uint32_t)context->Message_Block[t8 + 1])) << 16) | + ((((uint32_t)context->Message_Block[t8 + 2])) << 8) | + ((((uint32_t)context->Message_Block[t8 + 3]))); + W[t2++] = ((((uint32_t)context->Message_Block[t8 + 4])) << 24) | + ((((uint32_t)context->Message_Block[t8 + 5])) << 16) | + ((((uint32_t)context->Message_Block[t8 + 6])) << 8) | + ((((uint32_t)context->Message_Block[t8 + 7]))); + } + + for (t = 16; t < 80; t++, t2 += 2) + { + /* W[t] = SHA512_sigma1(W[t-2]) + W[t-7] + + SHA512_sigma0(W[t-15]) + W[t-16]; */ + uint32_t *Wt2 = &W[t2 - 2 * 2]; + uint32_t *Wt7 = &W[t2 - 7 * 2]; + uint32_t *Wt15 = &W[t2 - 15 * 2]; + uint32_t *Wt16 = &W[t2 - 16 * 2]; + SHA512_sigma1(Wt2, temp1); + SHA512_ADD(temp1, Wt7, temp2); + SHA512_sigma0(Wt15, temp1); + SHA512_ADD(temp1, Wt16, temp3); + SHA512_ADD(temp2, temp3, &W[t2]); + } + + A[0] = context->Intermediate_Hash[0]; + A[1] = context->Intermediate_Hash[1]; + B[0] = context->Intermediate_Hash[2]; + B[1] = context->Intermediate_Hash[3]; + C[0] = context->Intermediate_Hash[4]; + C[1] = context->Intermediate_Hash[5]; + D[0] = context->Intermediate_Hash[6]; + D[1] = context->Intermediate_Hash[7]; + E[0] = context->Intermediate_Hash[8]; + E[1] = context->Intermediate_Hash[9]; + F[0] = context->Intermediate_Hash[10]; + F[1] = context->Intermediate_Hash[11]; + G[0] = context->Intermediate_Hash[12]; + G[1] = context->Intermediate_Hash[13]; + H[0] = context->Intermediate_Hash[14]; + H[1] = context->Intermediate_Hash[15]; + + for (t = t2 = 0; t < 80; t++, t2 += 2) + { + /* + * temp1 = H + SHA512_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t]; + */ + SHA512_SIGMA1(E, temp1); + SHA512_ADD(H, temp1, temp2); + SHA_Ch(E, F, G, temp3); + SHA512_ADD(temp2, temp3, temp4); + SHA512_ADD(&K[t2], &W[t2], temp5); + SHA512_ADD(temp4, temp5, temp1); + /* + * temp2 = SHA512_SIGMA0(A) + SHA_Maj(A,B,C); + */ + SHA512_SIGMA0(A, temp3); + SHA_Maj(A, B, C, temp4); + SHA512_ADD(temp3, temp4, temp2); + H[0] = G[0]; + H[1] = G[1]; + G[0] = F[0]; + G[1] = F[1]; + F[0] = E[0]; + F[1] = E[1]; + SHA512_ADD(D, temp1, E); + D[0] = C[0]; + D[1] = C[1]; + C[0] = B[0]; + C[1] = B[1]; + B[0] = A[0]; + B[1] = A[1]; + SHA512_ADD(temp1, temp2, A); + } + + SHA512_ADDTO2(&context->Intermediate_Hash[0], A); + SHA512_ADDTO2(&context->Intermediate_Hash[2], B); + SHA512_ADDTO2(&context->Intermediate_Hash[4], C); + SHA512_ADDTO2(&context->Intermediate_Hash[6], D); + SHA512_ADDTO2(&context->Intermediate_Hash[8], E); + SHA512_ADDTO2(&context->Intermediate_Hash[10], F); + SHA512_ADDTO2(&context->Intermediate_Hash[12], G); + SHA512_ADDTO2(&context->Intermediate_Hash[14], H); + +#else /* !USE_32BIT_ONLY */ + static const uint64_t K[80] = { + 0x428A2F98D728AE22ll, 0x7137449123EF65CDll, 0xB5C0FBCFEC4D3B2Fll, + 0xE9B5DBA58189DBBCll, 0x3956C25BF348B538ll, 0x59F111F1B605D019ll, + 0x923F82A4AF194F9Bll, 0xAB1C5ED5DA6D8118ll, 0xD807AA98A3030242ll, + 0x12835B0145706FBEll, 0x243185BE4EE4B28Cll, 0x550C7DC3D5FFB4E2ll, + 0x72BE5D74F27B896Fll, 0x80DEB1FE3B1696B1ll, 0x9BDC06A725C71235ll, + 0xC19BF174CF692694ll, 0xE49B69C19EF14AD2ll, 0xEFBE4786384F25E3ll, + 0x0FC19DC68B8CD5B5ll, 0x240CA1CC77AC9C65ll, 0x2DE92C6F592B0275ll, + 0x4A7484AA6EA6E483ll, 0x5CB0A9DCBD41FBD4ll, 0x76F988DA831153B5ll, + 0x983E5152EE66DFABll, 0xA831C66D2DB43210ll, 0xB00327C898FB213Fll, + 0xBF597FC7BEEF0EE4ll, 0xC6E00BF33DA88FC2ll, 0xD5A79147930AA725ll, + 0x06CA6351E003826Fll, 0x142929670A0E6E70ll, 0x27B70A8546D22FFCll, + 0x2E1B21385C26C926ll, 0x4D2C6DFC5AC42AEDll, 0x53380D139D95B3DFll, + 0x650A73548BAF63DEll, 0x766A0ABB3C77B2A8ll, 0x81C2C92E47EDAEE6ll, + 0x92722C851482353Bll, 0xA2BFE8A14CF10364ll, 0xA81A664BBC423001ll, + 0xC24B8B70D0F89791ll, 0xC76C51A30654BE30ll, 0xD192E819D6EF5218ll, + 0xD69906245565A910ll, 0xF40E35855771202All, 0x106AA07032BBD1B8ll, + 0x19A4C116B8D2D0C8ll, 0x1E376C085141AB53ll, 0x2748774CDF8EEB99ll, + 0x34B0BCB5E19B48A8ll, 0x391C0CB3C5C95A63ll, 0x4ED8AA4AE3418ACBll, + 0x5B9CCA4F7763E373ll, 0x682E6FF3D6B2B8A3ll, 0x748F82EE5DEFB2FCll, + 0x78A5636F43172F60ll, 0x84C87814A1F0AB72ll, 0x8CC702081A6439ECll, + 0x90BEFFFA23631E28ll, 0xA4506CEBDE82BDE9ll, 0xBEF9A3F7B2C67915ll, + 0xC67178F2E372532Bll, 0xCA273ECEEA26619Cll, 0xD186B8C721C0C207ll, + 0xEADA7DD6CDE0EB1Ell, 0xF57D4F7FEE6ED178ll, 0x06F067AA72176FBAll, + 0x0A637DC5A2C898A6ll, 0x113F9804BEF90DAEll, 0x1B710B35131C471Bll, + 0x28DB77F523047D84ll, 0x32CAAB7B40C72493ll, 0x3C9EBE0A15C9BEBCll, + 0x431D67C49C100D4Cll, 0x4CC5D4BECB3E42B6ll, 0x597F299CFC657E2All, + 0x5FCB6FAB3AD6FAECll, 0x6C44198C4A475817ll + }; + int t, t8; /* Loop counter */ + uint64_t temp1, temp2; /* Temporary word value */ + uint64_t W[80]; /* Word sequence */ + uint64_t A, B, C, D, E, F, G, H; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = t8 = 0; t < 16; t++, t8 += 8) + W[t] = ((uint64_t)(context->Message_Block[t8]) << 56) | + ((uint64_t)(context->Message_Block[t8 + 1]) << 48) | + ((uint64_t)(context->Message_Block[t8 + 2]) << 40) | + ((uint64_t)(context->Message_Block[t8 + 3]) << 32) | + ((uint64_t)(context->Message_Block[t8 + 4]) << 24) | + ((uint64_t)(context->Message_Block[t8 + 5]) << 16) | + ((uint64_t)(context->Message_Block[t8 + 6]) << 8) | + ((uint64_t)(context->Message_Block[t8 + 7])); + + for (t = 16; t < 80; t++) + W[t] = SHA512_sigma1(W[t - 2]) + W[t - 7] + + SHA512_sigma0(W[t - 15]) + W[t - 16]; + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + F = context->Intermediate_Hash[5]; + G = context->Intermediate_Hash[6]; + H = context->Intermediate_Hash[7]; + + for (t = 0; t < 80; t++) + { + temp1 = H + SHA512_SIGMA1(E) + SHA_Ch(E, F, G) + K[t] + W[t]; + temp2 = SHA512_SIGMA0(A) + SHA_Maj(A, B, C); + H = G; + G = F; + F = E; + E = D + temp1; + D = C; + C = B; + B = A; + A = temp1 + temp2; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + context->Intermediate_Hash[5] += F; + context->Intermediate_Hash[6] += G; + context->Intermediate_Hash[7] += H; +#endif /* USE_32BIT_ONLY */ + + context->Message_Block_Index = 0; +} + +/* +* SHA384_512Reset +* +* Description: +* This helper function will initialize the SHA512Context in +* preparation for computing a new SHA384 or SHA512 message +* digest. +* +* Parameters: +* context: [in/out] +* The context to reset. +* H0 +* The initial hash value to use. +* +* Returns: +* sha Error Code. +* +*/ +#ifdef USE_32BIT_ONLY +static int +SHA384_512Reset(SHA512Context * context, uint32_t H0[]) +#else /* !USE_32BIT_ONLY */ +static int +SHA384_512Reset(SHA512Context * context, uint64_t H0[]) +#endif /* USE_32BIT_ONLY */ +{ + int i; + if (!context) + return shaNull; + + context->Message_Block_Index = 0; + +#ifdef USE_32BIT_ONLY + context->Length[0] = context->Length[1] = 0; + context->Length[2] = context->Length[3] = 0; + + for (i = 0; i < SHA512HashSize / 4; i++) + context->Intermediate_Hash[i] = H0[i]; +#else /* !USE_32BIT_ONLY */ + context->Length_High = context->Length_Low = 0; + + for (i = 0; i < SHA512HashSize / 8; i++) + context->Intermediate_Hash[i] = H0[i]; +#endif /* USE_32BIT_ONLY */ + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* +* SHA384_512ResultN +* +* Description: +* This helper function will return the 384-bit or 512-bit message +* digest into the Message_Digest array provided by the caller. +* NOTE: The first octet of hash is stored in the 0th element, +* the last octet of hash in the 48th/64th element. +* +* Parameters: +* context: [in/out] +* The context to use to calculate the SHA hash. +* Message_Digest: [out] +* Where the digest is returned. +* HashSize: [in] +* The size of the hash, either 48 or 64. +* +* Returns: +* sha Error Code. +* +*/ +static int +SHA384_512ResultN(SHA512Context * context, + uint8_t Message_Digest[], int HashSize) +{ + int i; + +#ifdef USE_32BIT_ONLY + int i2; +#endif /* USE_32BIT_ONLY */ + + if (!context || !Message_Digest) + return shaNull; + + if (context->Corrupted) + return context->Corrupted; + + if (!context->Computed) + SHA384_512Finalize(context, 0x80); + +#ifdef USE_32BIT_ONLY + for (i = i2 = 0; i < HashSize;) + { + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 24); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 16); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 8); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2++]); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 24); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 16); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2] >> 8); + Message_Digest[i++] = (uint8_t)(context->Intermediate_Hash[i2++]); + } +#else /* !USE_32BIT_ONLY */ + for (i = 0; i < HashSize; ++i) + Message_Digest[i] = (uint8_t) + (context->Intermediate_Hash[i >> 3] >> 8 * (7 - (i % 8))); +#endif /* USE_32BIT_ONLY */ + + return shaSuccess; +}