mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Add TLS config option to TCP client nodes
(not yet when in server mode)
This commit is contained in:
		| @@ -23,9 +23,17 @@ | ||||
|         </select> | ||||
|         <span data-i18n="tcpin.label.port"></span> <input type="text" id="node-input-port" style="width:65px"> | ||||
|     </div> | ||||
|     <div class="form-row hidden" id="node-input-host-row" style="padding-left: 110px;"> | ||||
|     <div class="form-row hidden" id="node-input-host-row" style="padding-left:110px;"> | ||||
|         <span data-i18n="tcpin.label.host"></span> <input type="text" id="node-input-host" placeholder="localhost" style="width: 60%;"> | ||||
|     </div> | ||||
|     <div class="form-row" id="node-input-tls-enable"> | ||||
|         <label> </label> | ||||
|         <input type="checkbox" id="node-input-usetls" style="display: inline-block; width:auto; vertical-align:top;"> | ||||
|         <label for="node-input-usetls" style="width:auto" data-i18n="httpin.use-tls"></label> | ||||
|         <div id="node-row-tls" class="hide"> | ||||
|             <label style="width:auto; margin-left:20px; margin-right:10px;" for="node-input-tls"><span data-i18n="httpin.tls-config"></span></label><input type="text" style="width: 300px" id="node-input-tls"> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row"> | ||||
|         <label><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.output"></span></label> | ||||
| @@ -58,17 +66,18 @@ | ||||
| <script type="text/javascript"> | ||||
|     RED.nodes.registerType('tcp in',{ | ||||
|         category: 'network', | ||||
|         color:"Silver", | ||||
|         color: "Silver", | ||||
|         defaults: { | ||||
|             name: {value:""}, | ||||
|             server: {value:"server",required:true}, | ||||
|             host: {value:"",validate:function(v) { return (this.server == "server")||v.length > 0;} }, | ||||
|             port: {value:"",required:true,validate:RED.validators.number()}, | ||||
|             server: {value:"server", required:true}, | ||||
|             host: {value:"", validate:function(v) { return (this.server == "server")||v.length > 0;} }, | ||||
|             port: {value:"", required:true, validate:RED.validators.number()}, | ||||
|             datamode:{value:"stream"}, | ||||
|             datatype:{value:"buffer"}, | ||||
|             newline:{value:""}, | ||||
|             topic: {value:""}, | ||||
|             base64: {/*deprecated*/ value:false,required:true} | ||||
|             base64: {/*deprecated*/ value:false, required:true}, | ||||
|             tls: {type:"tls-config", value:'', required:false} | ||||
|         }, | ||||
|         inputs:0, | ||||
|         outputs:1, | ||||
| @@ -77,15 +86,17 @@ | ||||
|             return this.name || "tcp:"+(this.host?this.host+":":"")+this.port; | ||||
|         }, | ||||
|         labelStyle: function() { | ||||
|             return this.name?"node_label_italic":""; | ||||
|             return this.name ? "node_label_italic" : ""; | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
|             var updateOptions = function() { | ||||
|                 var sockettype = $("#node-input-server").val(); | ||||
|                 if (sockettype == "client") { | ||||
|                     $("#node-input-host-row").show(); | ||||
|                     $("#node-input-tls-enable").show(); | ||||
|                 } else { | ||||
|                     $("#node-input-host-row").hide(); | ||||
|                     $("#node-input-tls-enable").hide(); | ||||
|                 } | ||||
|                 var datamode = $("#node-input-datamode").val(); | ||||
|                 var datatype = $("#node-input-datatype").val(); | ||||
| @@ -103,6 +114,27 @@ | ||||
|             $("#node-input-server").change(updateOptions); | ||||
|             $("#node-input-datatype").change(updateOptions); | ||||
|             $("#node-input-datamode").change(updateOptions); | ||||
|             function updateTLSOptions() { | ||||
|                 if ($("#node-input-usetls").is(':checked')) { | ||||
|                     $("#node-row-tls").show(); | ||||
|                 } else { | ||||
|                     $("#node-row-tls").hide(); | ||||
|                 } | ||||
|             } | ||||
|             if (this.tls) { | ||||
|                 $('#node-input-usetls').prop('checked', true); | ||||
|             } else { | ||||
|                 $('#node-input-usetls').prop('checked', false); | ||||
|             } | ||||
|             updateTLSOptions(); | ||||
|             $("#node-input-usetls").on("click",function() { | ||||
|                 updateTLSOptions(); | ||||
|             }); | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             if (!$("#node-input-usetls").is(':checked')) { | ||||
|                 $("#node-input-tls").val("_ADD_"); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| </script> | ||||
| @@ -123,6 +155,15 @@ | ||||
|         <span data-i18n="tcpin.label.host"></span> <input type="text" id="node-input-host" style="width: 60%;"> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row" id="node-input-tls-enable"> | ||||
|         <label> </label> | ||||
|         <input type="checkbox" id="node-input-usetls" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|         <label for="node-input-usetls" style="width: auto" data-i18n="httpin.use-tls"></label> | ||||
|         <div id="node-row-tls" class="hide"> | ||||
|             <label style="width: auto; margin-left: 20px; margin-right: 10px;" for="node-input-tls"><span data-i18n="httpin.tls-config"></span></label><input type="text" style="width: 300px" id="node-input-tls"> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row hidden" id="node-input-end-row"> | ||||
|         <label> </label> | ||||
|         <input type="checkbox" id="node-input-end" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
| @@ -144,14 +185,15 @@ | ||||
| <script type="text/javascript"> | ||||
|     RED.nodes.registerType('tcp out',{ | ||||
|         category: 'network', | ||||
|         color:"Silver", | ||||
|         color: "Silver", | ||||
|         defaults: { | ||||
|             name: {value:""}, | ||||
|             host: {value:"",validate:function(v) { return (this.beserver != "client")||v.length > 0;} }, | ||||
|             port: {value:"",validate:function(v) { return (this.beserver == "reply")||RED.validators.number()(v); } }, | ||||
|             beserver: {value:"client",required:true}, | ||||
|             base64: {value:false,required:true}, | ||||
|             end: {value:false,required:true}, | ||||
|             name: {value:""} | ||||
|             beserver: {value:"client", required:true}, | ||||
|             base64: {value:false, required:true}, | ||||
|             end: {value:false, required:true}, | ||||
|             tls: {type:"tls-config", value:'', required:false} | ||||
|         }, | ||||
|         inputs:1, | ||||
|         outputs:0, | ||||
| @@ -170,18 +212,42 @@ | ||||
|                     $("#node-input-port-row").hide(); | ||||
|                     $("#node-input-host-row").hide(); | ||||
|                     $("#node-input-end-row").hide(); | ||||
|                     $("#node-input-tls-enable").hide(); | ||||
|                 } else if (sockettype == "client"){ | ||||
|                     $("#node-input-port-row").show(); | ||||
|                     $("#node-input-host-row").show(); | ||||
|                     $("#node-input-end-row").show(); | ||||
|                     $("#node-input-tls-enable").show(); | ||||
|                 } else { | ||||
|                     $("#node-input-port-row").show(); | ||||
|                     $("#node-input-host-row").hide(); | ||||
|                     $("#node-input-end-row").show(); | ||||
|                     $("#node-input-tls-enable").hide(); | ||||
|                 } | ||||
|             }; | ||||
|             updateOptions(); | ||||
|             $("#node-input-beserver").change(updateOptions); | ||||
|             function updateTLSOptions() { | ||||
|                 if ($("#node-input-usetls").is(':checked')) { | ||||
|                     $("#node-row-tls").show(); | ||||
|                 } else { | ||||
|                     $("#node-row-tls").hide(); | ||||
|                 } | ||||
|             } | ||||
|             if (this.tls) { | ||||
|                 $('#node-input-usetls').prop('checked', true); | ||||
|             } else { | ||||
|                 $('#node-input-usetls').prop('checked', false); | ||||
|             } | ||||
|             updateTLSOptions(); | ||||
|             $("#node-input-usetls").on("click",function() { | ||||
|                 updateTLSOptions(); | ||||
|             }); | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             if (!$("#node-input-usetls").is(':checked')) { | ||||
|                 $("#node-input-tls").val("_ADD_"); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| </script> | ||||
| @@ -194,6 +260,14 @@ | ||||
|         <span data-i18n="tcpin.label.port"></span> | ||||
|         <input type="text" id="node-input-port" style="width:60px"> | ||||
|     </div> | ||||
|     <div class="form-row" id="node-input-tls-enable"> | ||||
|         <label> </label> | ||||
|         <input type="checkbox" id="node-input-usetls" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|         <label for="node-input-usetls" style="width: auto" data-i18n="httpin.use-tls"></label> | ||||
|         <div id="node-row-tls" class="hide"> | ||||
|             <label style="width: auto; margin-left: 20px; margin-right: 10px;" for="node-input-tls"><span data-i18n="httpin.tls-config"></span></label><input type="text" style="width: 300px" id="node-input-tls"> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-out"><i class="fa fa-sign-out"></i> <span data-i18n="tcpin.label.return"></span></label> | ||||
|         <select type="text" id="node-input-ret" style="width:54%;"> | ||||
| @@ -222,14 +296,15 @@ | ||||
| <script type="text/javascript"> | ||||
|     RED.nodes.registerType('tcp request',{ | ||||
|         category: 'network', | ||||
|         color:"Silver", | ||||
|         color: "Silver", | ||||
|         defaults: { | ||||
|             name: {value:""}, | ||||
|             server: {value:""}, | ||||
|             port: {value:"",validate:RED.validators.regex(/^(\d*|)$/)}, | ||||
|             out: {value:"time",required:true}, | ||||
|             port: {value:"", validate:RED.validators.regex(/^(\d*|)$/)}, | ||||
|             out: {value:"time", required:true}, | ||||
|             ret: {value:"buffer"}, | ||||
|             splitc: {value:"0",required:true}, | ||||
|             name: {value:""} | ||||
|             splitc: {value:"0", required:true}, | ||||
|             tls: {type:"tls-config", value:'', required:false} | ||||
|         }, | ||||
|         inputs:1, | ||||
|         outputs:1, | ||||
| @@ -272,6 +347,27 @@ | ||||
|                     $("#node-input-splitc").hide(); | ||||
|                 } | ||||
|             }); | ||||
|             function updateTLSOptions() { | ||||
|                 if ($("#node-input-usetls").is(':checked')) { | ||||
|                     $("#node-row-tls").show(); | ||||
|                 } else { | ||||
|                     $("#node-row-tls").hide(); | ||||
|                 } | ||||
|             } | ||||
|             if (this.tls) { | ||||
|                 $('#node-input-usetls').prop('checked', true); | ||||
|             } else { | ||||
|                 $('#node-input-usetls').prop('checked', false); | ||||
|             } | ||||
|             updateTLSOptions(); | ||||
|             $("#node-input-usetls").on("click",function() { | ||||
|                 updateTLSOptions(); | ||||
|             }); | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             if (!$("#node-input-usetls").is(':checked')) { | ||||
|                 $("#node-input-tls").val("_ADD_"); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| </script> | ||||
|   | ||||
| @@ -16,13 +16,46 @@ | ||||
|  | ||||
| module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|     var reconnectTime = RED.settings.socketReconnectTime||10000; | ||||
|     var socketTimeout = RED.settings.socketTimeout||null; | ||||
|     let reconnectTime = RED.settings.socketReconnectTime || 10000; | ||||
|     let socketTimeout = RED.settings.socketTimeout || null; | ||||
|     const msgQueueSize = RED.settings.tcpMsgQueueSize || 1000; | ||||
|     const Denque = require('denque'); | ||||
|     var net = require('net'); | ||||
|     const net = require('net'); | ||||
|     const tls = require('tls'); | ||||
|  | ||||
|     var connectionPool = {}; | ||||
|     let connectionPool = {}; | ||||
|  | ||||
|     function normalizeConnectArgs(listArgs) { | ||||
|       const args = net._normalizeArgs(listArgs); | ||||
|       const options = args[0]; | ||||
|       const cb = args[1]; | ||||
|  | ||||
|       // If args[0] was options, then normalize dealt with it. | ||||
|       // If args[0] is port, or args[0], args[1] is host, port, we need to | ||||
|       // find the options and merge them in, normalize's options has only | ||||
|       // the host/port/path args that it knows about, not the tls options. | ||||
|       // This means that options.host overrides a host arg. | ||||
|       if (listArgs[1] !== null && typeof listArgs[1] === 'object') { | ||||
|         ObjectAssign(options, listArgs[1]); | ||||
|       } else if (listArgs[2] !== null && typeof listArgs[2] === 'object') { | ||||
|         ObjectAssign(options, listArgs[2]); | ||||
|       } | ||||
|  | ||||
|       return cb ? [options, cb] : [options]; | ||||
|     } | ||||
|  | ||||
|     function getAllowUnauthorized() { | ||||
|       const allowUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0'; | ||||
|  | ||||
|       if (allowUnauthorized) { | ||||
|         process.emitWarning( | ||||
|           'Setting the NODE_TLS_REJECT_UNAUTHORIZED ' + | ||||
|           'environment variable to \'0\' makes TLS connections ' + | ||||
|           'and HTTPS requests insecure by disabling ' + | ||||
|           'certificate verification.'); | ||||
|       } | ||||
|       return allowUnauthorized; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Enqueue `item` in `queue` | ||||
| @@ -60,6 +93,8 @@ module.exports = function(RED) { | ||||
|         this.connected = false; | ||||
|         var node = this; | ||||
|         var count = 0; | ||||
|         var nlstr = (this.newline && !this.newline.trim()) ? false : true; | ||||
|         if (n.tls) { var tlsNode = RED.nodes.getNode(n.tls); } | ||||
|  | ||||
|         if (!node.server) { | ||||
|             var buffer = null; | ||||
| @@ -70,13 +105,26 @@ module.exports = function(RED) { | ||||
|                 node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port})); | ||||
|                 node.status({fill:"grey",shape:"dot",text:"common.status.connecting"}); | ||||
|                 var id = RED.util.generateId(); | ||||
|                 client = net.connect(node.port, node.host, function() { | ||||
|                     buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; | ||||
|                     node.connected = true; | ||||
|                     node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port})); | ||||
|                     node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}}); | ||||
|                 }); | ||||
|                 client.setKeepAlive(true,120000); | ||||
|                 var connOpts = {host: node.host | ||||
|                 }; | ||||
|                 if (n.tls) { | ||||
|                     var connOpts = tlsNode.addTLSOptions({host: node.host}); | ||||
|                     client = tls.connect(node.port, connOpts, function() { | ||||
|                         buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; | ||||
|                         node.connected = true; | ||||
|                         node.log(RED._("status.connected", {host: node.host, port: node.port})); | ||||
|                         node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}}); | ||||
|                     }); | ||||
|                 } | ||||
|                 else { | ||||
|                     client = net.connect(node.port, node.host, function() { | ||||
|                         buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; | ||||
|                         node.connected = true; | ||||
|                         node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port})); | ||||
|                         node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}}); | ||||
|                     }); | ||||
|                 } | ||||
|                 client.setKeepAlive(true, 120000); | ||||
|                 connectionPool[id] = client; | ||||
|  | ||||
|                 client.on('data', function (data) { | ||||
| @@ -89,6 +137,7 @@ module.exports = function(RED) { | ||||
|                             buffer = buffer+data; | ||||
|                             var parts = buffer.split(node.newline); | ||||
|                             for (var i = 0; i<parts.length-1; i+=1) { | ||||
|                                 if (nlstr) { parts[i] += node.newline; } | ||||
|                                 msg = {topic:node.topic, payload:parts[i]}; | ||||
|                                 msg._session = {type:"tcp",id:id}; | ||||
|                                 node.send(msg); | ||||
| @@ -269,8 +318,9 @@ module.exports = function(RED) { | ||||
|         this.closing = false; | ||||
|         this.connected = false; | ||||
|         var node = this; | ||||
|         if (n.tls) { var tlsNode = RED.nodes.getNode(n.tls); } | ||||
|  | ||||
|         if (!node.beserver||node.beserver=="client") { | ||||
|         if (!node.beserver || node.beserver == "client") { | ||||
|             var reconnectTimeout; | ||||
|             var client = null; | ||||
|             var end = false; | ||||
| @@ -278,11 +328,24 @@ module.exports = function(RED) { | ||||
|             var setupTcpClient = function() { | ||||
|                 node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port})); | ||||
|                 node.status({fill:"grey",shape:"dot",text:"common.status.connecting"}); | ||||
|                 client = net.connect(node.port, node.host, function() { | ||||
|                     node.connected = true; | ||||
|                     node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port})); | ||||
|                     node.status({fill:"green",shape:"dot",text:"common.status.connected"}); | ||||
|                 }); | ||||
|                 if (n.tls) { | ||||
|                     // connOpts = tlsNode.addTLSOptions(connOpts); | ||||
|                     // client = tls.connect(connOpts, function() { | ||||
|                     var connOpts = tlsNode.addTLSOptions({host: node.host}); | ||||
|                     client = tls.connect(node.port, connOpts, function() { | ||||
|                         // buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; | ||||
|                         node.connected = true; | ||||
|                         node.log(RED._("status.connected", {host: node.host, port: node.port})); | ||||
|                         node.status({fill:"green",shape:"dot",text:"common.status.connected"}); | ||||
|                     }); | ||||
|                 } | ||||
|                 else { | ||||
|                     client = net.connect(node.port, node.host, function() { | ||||
|                         node.connected = true; | ||||
|                         node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port})); | ||||
|                         node.status({fill:"green",shape:"dot",text:"common.status.connected"}); | ||||
|                     }); | ||||
|                 } | ||||
|                 client.setKeepAlive(true,120000); | ||||
|                 client.on('error', function (err) { | ||||
|                     node.log(RED._("tcpin.errors.error",{error:err.toString()})); | ||||
| @@ -446,6 +509,9 @@ module.exports = function(RED) { | ||||
|         this.out = n.out; | ||||
|         this.ret = n.ret || "buffer"; | ||||
|         this.splitc = n.splitc; | ||||
|         if (n.tls) { | ||||
|             var tlsNode = RED.nodes.getNode(n.tls); | ||||
|         } | ||||
|  | ||||
|         if (this.out === "immed") { this.splitc = -1; this.out = "time"; } | ||||
|         if (this.out !== "char") { this.splitc = Number(this.splitc); } | ||||
| @@ -500,12 +566,49 @@ module.exports = function(RED) { | ||||
|                 } | ||||
|                 else { buf = Buffer.alloc(65536); } // set it to 64k... hopefully big enough for most TCP packets.... but only hopefully | ||||
|  | ||||
|                 clients[connection_id].client = net.Socket(); | ||||
|                 var connOpts = {host: host, port: port}; | ||||
|                 if (n.tls) { | ||||
|                     connOpts = tlsNode.addTLSOptions(connOpts); | ||||
|                     const allowUnauthorized = getAllowUnauthorized(); | ||||
|  | ||||
|                     let options = { | ||||
|                         rejectUnauthorized: !allowUnauthorized, | ||||
|                         ciphers: tls.DEFAULT_CIPHERS, | ||||
|                         checkServerIdentity: tls.checkServerIdentity, | ||||
|                         minDHSize: 1024, | ||||
|                         ...connOpts | ||||
|                     }; | ||||
|  | ||||
|                     if (!options.keepAlive) | ||||
|                     options.singleUse = true; | ||||
|  | ||||
|                     const context = options.secureContext || tls.createSecureContext(options); | ||||
|  | ||||
|                     clients[connection_id].client = new tls.TLSSocket(options.socket, { | ||||
|                         allowHalfOpen: options.allowHalfOpen, | ||||
|                         pipe: !!options.path, | ||||
|                         secureContext: context, | ||||
|                         isServer: false, | ||||
|                         requestCert: false, // true, | ||||
|                         rejectUnauthorized: false, // options.rejectUnauthorized !== false, | ||||
|                         session: options.session, | ||||
|                         ALPNProtocols: options.ALPNProtocols, | ||||
|                         requestOCSP: options.requestOCSP, | ||||
|                         enableTrace: options.enableTrace, | ||||
|                         pskCallback: options.pskCallback, | ||||
|                         highWaterMark: options.highWaterMark, | ||||
|                         onread: options.onread, | ||||
|                         signal: options.signal, | ||||
|                     }); | ||||
|                 } | ||||
|                 else { | ||||
|                     clients[connection_id].client = net.Socket(); | ||||
|                 } | ||||
|                 if (socketTimeout !== null) { clients[connection_id].client.setTimeout(socketTimeout);} | ||||
|  | ||||
|                 if (host && port) { | ||||
|                     clients[connection_id].connecting = true; | ||||
|                     clients[connection_id].client.connect(port, host, function() { | ||||
|                     clients[connection_id].client.connect(connOpts, function() { | ||||
|                         //node.log(RED._("tcpin.errors.client-connected")); | ||||
|                         node.status({fill:"green",shape:"dot",text:"common.status.connected"}); | ||||
|                         if (clients[connection_id] && clients[connection_id].client) { | ||||
| @@ -675,7 +778,13 @@ module.exports = function(RED) { | ||||
|                         //node.warn(RED._("tcpin.errors.connect-timeout")); | ||||
|                         if (clients[connection_id].client) { | ||||
|                             clients[connection_id].connecting = true; | ||||
|                             clients[connection_id].client.connect(port, host, function() { | ||||
|  | ||||
|                             var connOpts = {host: host, port: port}; | ||||
|                             if (n.tls) { | ||||
|                                 connOpts = tlsNode.addTLSOptions(connOpts); | ||||
|                             } | ||||
|  | ||||
|                             clients[connection_id].client.connect(connOpts, function() { | ||||
|                                 clients[connection_id].connected = true; | ||||
|                                 clients[connection_id].connecting = false; | ||||
|                                 node.status({fill:"green",shape:"dot",text:"common.status.connected"}); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user