1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Add setting to disable tcp/udp server (inbound)

This commit is contained in:
Steve-Mcl 2022-10-26 08:59:36 +01:00
parent 7da3773f7f
commit 5a21f026f2
5 changed files with 150 additions and 96 deletions

View File

@ -70,7 +70,16 @@
color: "Silver",
defaults: {
name: {value:""},
server: {value:"server", required:true},
server: {
value: RED.settings.tcpInAllowInboundConnections === false ? "client" : "server",
validate: function(v, opt) {
console.log("validating client/server mode")
if (v === 'server' && RED.settings.tcpInAllowInboundConnections === false) {
return RED._("node-red:tcpin.errors.inbound-disabled");
}
return ["client", "server"].indexOf(v) >= 0
}
},
host: {
value:"",
validate:function(v, opt) {
@ -218,7 +227,16 @@
return RED._("node-red:tcpin.errors.invalid-port");
}
},
beserver: {value:"client", required:true},
beserver: {
value: "client",
validate: function(v, opt) {
console.log("validating client/server mode")
if (v === 'server' && RED.settings.tcpInAllowInboundConnections === false) {
return RED._("node-red:tcpin.errors.inbound-disabled");
}
return ["client", "server", "reply"].indexOf(v) >= 0
}
},
base64: {value:false, required:true},
end: {value:false, required:true},
tls: {type:"tls-config", value:'', required:false,

View File

@ -19,6 +19,7 @@ module.exports = function(RED) {
let reconnectTime = RED.settings.socketReconnectTime || 10000;
let socketTimeout = RED.settings.socketTimeout || null;
const msgQueueSize = RED.settings.tcpMsgQueueSize || 1000;
const allowInbound = RED.settings.tcpInAllowInboundConnections === false ? false : true
const Denque = require('denque');
const net = require('net');
const tls = require('tls');
@ -196,8 +197,7 @@ module.exports = function(RED) {
clearTimeout(reconnectTimeout);
if (!node.connected) { done(); }
});
}
else {
} else if (allowInbound) {
let srv = net;
let connOpts;
if (n.tls) {
@ -308,9 +308,19 @@ module.exports = function(RED) {
});
}
});
} else {
node.warn(RED._("tcpin.errors.inbound-disabled",{host:node.host,port:node.port}));
node.status({fill:"red",shape:"dot",text:"tcpin.errors.inbound-disabled"});
}
}
RED.nodes.registerType("tcp in",TcpIn);
RED.nodes.registerType("tcp in",TcpIn, {
settings: {
tcpInAllowInboundConnections: {
value: true,
exportable: true
}
}
});
function TcpOut(n) {
@ -434,7 +444,7 @@ module.exports = function(RED) {
nodeDone();
});
}
else {
else if (allowInbound) {
const connectedSockets = new Set();
node.status({text:RED._("tcpin.status.connections",{count:0})});
let srv = net;
@ -507,11 +517,13 @@ module.exports = function(RED) {
});
}
});
} else {
node.warn(RED._("tcpin.errors.inbound-disabled",{host:node.host,port:node.port}));
node.status({fill:"red",shape:"dot",text:"tcpin.errors.inbound-disabled"});
}
}
RED.nodes.registerType("tcp out",TcpOut);
function TcpGet(n) {
RED.nodes.createNode(this,n);
this.server = n.server;

View File

@ -18,7 +18,7 @@
<script type="text/html" data-template-name="udp in">
<div class="form-row">
<label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.listen"></span></label>
<select id="node-input-multicast" style='width:70%'>
<select id="node-input-multicast" style="width:70%">
<option value="false" data-i18n="udp.udpmsgs"></option>
<option value="true" data-i18n="udp.mcmsgs"></option>
</select>
@ -68,7 +68,16 @@
validate:RED.validators.number(false)
},
ipv: {value:"udp4"},
multicast: {value:"false"},
multicast: {
value:"false",
validate: function(v, opt) {
console.log("validating client/server mode")
if (RED.settings.udpInAllowInboundConnections === false) {
return RED._("node-red:udp.errors.inbound-disabled");
}
return true
}
},
group: {
value:"",
validate:function(v,opt) {

View File

@ -16,9 +16,10 @@
module.exports = function(RED) {
"use strict";
var os = require('os');
var dgram = require('dgram');
var udpInputPortsInUse = {};
const os = require('os');
const dgram = require('dgram');
const udpInputPortsInUse = {};
const allowInbound = RED.settings.udpInAllowInboundConnections === false ? false : true
// The Input Node
function UDPin(n) {
@ -29,97 +30,102 @@ module.exports = function(RED) {
this.iface = n.iface || null;
this.multicast = n.multicast;
this.ipv = n.ipv || "udp4";
var node = this;
if (node.iface && node.iface.indexOf(".") === -1) {
try {
if ((os.networkInterfaces())[node.iface][0].hasOwnProperty("scopeid")) {
if (node.ipv === "udp4") {
node.iface = (os.networkInterfaces())[node.iface][1].address;
} else {
node.iface = (os.networkInterfaces())[node.iface][0].address;
}
}
else {
if (node.ipv === "udp4") {
node.iface = (os.networkInterfaces())[node.iface][0].address;
} else {
node.iface = (os.networkInterfaces())[node.iface][1].address;
}
}
}
catch(e) {
node.warn(RED._("udp.errors.ifnotfound",{iface:node.iface}));
node.iface = null;
}
}
var opts = {type:node.ipv, reuseAddr:true};
if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
var server;
if (!udpInputPortsInUse.hasOwnProperty(node.port)) {
server = dgram.createSocket(opts); // default to udp4
server.bind(node.port, function() {
if (node.multicast == "true") {
server.setBroadcast(true);
server.setMulticastLoopback(false);
try {
server.setMulticastTTL(128);
server.addMembership(node.group,node.iface);
if (node.iface) { node.status({text:n.iface+" : "+node.iface}); }
node.log(RED._("udp.status.mc-group",{group:node.group}));
} catch (e) {
if (e.errno == "EINVAL") {
node.error(RED._("udp.errors.bad-mcaddress"));
} else if (e.errno == "ENODEV") {
node.error(RED._("udp.errors.interface"));
const node = this;
let server;
if (allowInbound) {
if (node.iface && node.iface.indexOf(".") === -1) {
try {
if ((os.networkInterfaces())[node.iface][0].hasOwnProperty("scopeid")) {
if (node.ipv === "udp4") {
node.iface = (os.networkInterfaces())[node.iface][1].address;
} else {
node.error(RED._("udp.errors.error",{error:e.errno}));
node.iface = (os.networkInterfaces())[node.iface][0].address;
}
}
else {
if (node.ipv === "udp4") {
node.iface = (os.networkInterfaces())[node.iface][0].address;
} else {
node.iface = (os.networkInterfaces())[node.iface][1].address;
}
}
}
catch(e) {
node.warn(RED._("udp.errors.ifnotfound",{iface:node.iface}));
node.iface = null;
}
}
let opts = {type:node.ipv, reuseAddr:true};
if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
if (!udpInputPortsInUse.hasOwnProperty(node.port)) {
server = dgram.createSocket(opts); // default to udp4
server.bind(node.port, function() {
if (node.multicast == "true") {
server.setBroadcast(true);
server.setMulticastLoopback(false);
try {
server.setMulticastTTL(128);
server.addMembership(node.group,node.iface);
if (node.iface) { node.status({text:n.iface+" : "+node.iface}); }
node.log(RED._("udp.status.mc-group",{group:node.group}));
} catch (e) {
if (e.errno == "EINVAL") {
node.error(RED._("udp.errors.bad-mcaddress"));
} else if (e.errno == "ENODEV") {
node.error(RED._("udp.errors.interface"));
} else {
node.error(RED._("udp.errors.error",{error:e.errno}));
}
}
}
});
udpInputPortsInUse[node.port] = server;
}
else {
node.log(RED._("udp.errors.alreadyused",{port:node.port}));
server = udpInputPortsInUse[node.port]; // re-use existing
if (node.iface) { node.status({text:n.iface+" : "+node.iface}); }
}
server.on("error", function (err) {
if ((err.code == "EACCES") && (node.port < 1024)) {
node.error(RED._("udp.errors.access-error"));
} else {
node.error(RED._("udp.errors.error",{error:err.code}));
}
server.close();
});
udpInputPortsInUse[node.port] = server;
server.on('message', function (message, remote) {
let msg;
if (node.datatype =="base64") {
msg = { payload:message.toString('base64'), fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port };
} else if (node.datatype =="utf8") {
msg = { payload:message.toString('utf8'), fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port };
} else {
msg = { payload:message, fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port };
}
node.send(msg);
});
server.on('listening', function () {
const address = server.address();
node.log(RED._("udp.status.listener-at",{host:node.iface||address.address,port:address.port}));
});
} else {
node.warn(RED._("udp.errors.inbound-disabled",{host:node.host,port:node.port}));
node.status({fill:"red",shape:"dot",text:"udp.errors.inbound-disabled"});
}
else {
node.log(RED._("udp.errors.alreadyused",{port:node.port}));
server = udpInputPortsInUse[node.port]; // re-use existing
if (node.iface) { node.status({text:n.iface+" : "+node.iface}); }
}
server.on("error", function (err) {
if ((err.code == "EACCES") && (node.port < 1024)) {
node.error(RED._("udp.errors.access-error"));
} else {
node.error(RED._("udp.errors.error",{error:err.code}));
}
server.close();
});
server.on('message', function (message, remote) {
var msg;
if (node.datatype =="base64") {
msg = { payload:message.toString('base64'), fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port };
} else if (node.datatype =="utf8") {
msg = { payload:message.toString('utf8'), fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port };
} else {
msg = { payload:message, fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port };
}
node.send(msg);
});
server.on('listening', function () {
var address = server.address();
node.log(RED._("udp.status.listener-at",{host:node.iface||address.address,port:address.port}));
});
node.on("close", function() {
try {
if (node.multicast == "true") { server.dropMembership(node.group); }
server.close();
node.log(RED._("udp.status.listener-stopped"));
if (server) {
if (node.multicast == "true") { server.dropMembership(node.group); }
server.close();
node.log(RED._("udp.status.listener-stopped"));
}
} catch (err) {
//node.error(err);
}
@ -133,7 +139,14 @@ module.exports = function(RED) {
RED.httpAdmin.get('/udp-ports/:id', RED.auth.needsPermission('udp-ports.read'), function(req,res) {
res.json(Object.keys(udpInputPortsInUse));
});
RED.nodes.registerType("udp in",UDPin);
RED.nodes.registerType("udp in",UDPin, {
settings: {
udpInAllowInboundConnections: {
value: true,
exportable: true
}
}
});

View File

@ -647,6 +647,7 @@
"connections_plural": "__count__ connections"
},
"errors": {
"inbound-disabled": "inbound connections are disabled",
"connection-lost": "connection lost to __host__:__port__",
"timeout": "timeout closed socket port __port__",
"cannot-listen": "unable to listen on port __port__, error: __error__",
@ -711,6 +712,7 @@
"re-use": "udp re-use socket: __outport__ -> __host__:__port__"
},
"errors": {
"inbound-disabled": "inbound connections are disabled",
"access-error": "UDP access error, you may need root access for ports below 1024",
"error": "error: __error__",
"bad-mcaddress": "Bad Multicast Address",