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

bind to correct port when doing udp broadcast/multicast (#1686)

* bind to correct port when doing broadcast/multicast

to allow better re-use of ports.

* allow udp multicast to work out if ip address

makes life easier for mortals

* udp also handle bind to ipv6 multicast if

tidy prompts to suit new function

* udp node, add face to debug log for multicast if known
This commit is contained in:
Dave Conway-Jones 2018-05-01 12:43:51 +01:00 committed by Nick O'Leary
parent e691351976
commit 94cb03f4b5
5 changed files with 94 additions and 41 deletions

View File

@ -29,7 +29,7 @@
</div>
<div class="form-row node-input-iface">
<label for="node-input-iface"><i class="fa fa-random"></i> <span data-i18n="udp.label.interface"></span></label>
<input type="text" id="node-input-iface" data-i18n="[placeholder]udp.label.interfaceprompt">
<input type="text" id="node-input-iface" data-i18n="[placeholder]udp.placeholder.interfaceprompt">
</div>
<div class="form-row">
<label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.onport"></span></label>

View File

@ -16,6 +16,7 @@
module.exports = function(RED) {
"use strict";
var os = require('os');
var dgram = require('dgram');
var udpInputPortsInUse = {};
@ -30,6 +31,29 @@ module.exports = function(RED) {
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;
@ -39,7 +63,7 @@ module.exports = function(RED) {
udpInputPortsInUse[this.port] = server;
}
else {
node.warn(RED._("udp.errors.alreadyused",node.port));
node.warn(RED._("udp.errors.alreadyused",{port:node.port}));
server = udpInputPortsInUse[this.port]; // re-use existing
}
@ -121,12 +145,38 @@ module.exports = function(RED) {
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 sock;
if (udpInputPortsInUse[this.outport || this.port]) {
sock = udpInputPortsInUse[this.outport || this.port];
var p = this.port;
if (node.multicast != "false") { p = this.outport||"0"; }
if (udpInputPortsInUse[p]) {
sock = udpInputPortsInUse[p];
node.log(RED._("udp.status.re-use",{outport:node.outport,host:node.addr,port:node.port}));
}
else {
sock = dgram.createSocket(opts); // default to udp4
@ -136,36 +186,35 @@ module.exports = function(RED) {
// prevent it going to the global error handler and shutting node-red
// down.
});
udpInputPortsInUse[this.outport || this.port] = sock;
}
udpInputPortsInUse[p] = sock;
if (node.multicast != "false") {
if (node.outport === "") { node.outport = node.port; }
sock.bind(node.outport, function() { // have to bind before you can enable broadcast...
sock.setBroadcast(true); // turn on broadcast
if (node.multicast == "multi") {
try {
sock.setMulticastTTL(128);
sock.addMembership(node.addr,node.iface); // Add to the multicast group
node.log(RED._("udp.status.mc-ready",{outport:node.outport,host:node.addr,port:node.port}));
} 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}));
if (node.multicast != "false") {
sock.bind(node.outport, function() { // have to bind before you can enable broadcast...
sock.setBroadcast(true); // turn on broadcast
if (node.multicast == "multi") {
try {
sock.setMulticastTTL(128);
sock.addMembership(node.addr,node.iface); // Add to the multicast group
node.log(RED._("udp.status.mc-ready",{iface:node.iface,outport:node.outport,host:node.addr,port:node.port}));
} 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}));
}
}
} else {
node.log(RED._("udp.status.bc-ready",{outport:node.outport,host:node.addr,port:node.port}));
}
} else {
node.log(RED._("udp.status.bc-ready",{outport:node.outport,host:node.addr,port:node.port}));
}
});
} else if ((node.outport !== "") && (!udpInputPortsInUse[node.outport])) {
sock.bind(node.outport);
node.log(RED._("udp.status.ready",{outport:node.outport,host:node.addr,port:node.port}));
} else {
node.log(RED._("udp.status.ready-nolocal",{host:node.addr,port:node.port}));
});
} else if ((node.outport !== "") && (!udpInputPortsInUse[node.outport])) {
sock.bind(node.outport);
node.log(RED._("udp.status.ready",{outport:node.outport,host:node.addr,port:node.port}));
} else {
node.log(RED._("udp.status.ready-nolocal",{host:node.addr,port:node.port}));
}
}
node.on("input", function(msg) {
@ -198,8 +247,8 @@ module.exports = function(RED) {
});
node.on("close", function() {
if (udpInputPortsInUse.hasOwnProperty(node.outport || node.port)) {
delete udpInputPortsInUse[node.outport || node.port];
if (udpInputPortsInUse.hasOwnProperty(p)) {
delete udpInputPortsInUse[p];
}
try {
sock.close();

View File

@ -495,15 +495,15 @@
"using": "using",
"output": "Output",
"group": "Group",
"interface": "Local IP",
"interfaceprompt": "(optional) local ip address to bind to",
"interface": "Local IF",
"send": "Send a",
"toport": "to port",
"address": "Address",
"decode-base64": "Decode Base64 encoded payload?"
},
"placeholder": {
"interface": "(optional) ip address of eth0",
"interface": "(optional) local interface or address to bind to",
"interfaceprompt": "(optional) local interface or address to bind to",
"address": "destination ip"
},
"udpmsgs": "udp messages",
@ -531,10 +531,11 @@
"mc-group": "udp multicast group __group__",
"listener-stopped": "udp listener stopped",
"output-stopped": "udp output stopped",
"mc-ready": "udp multicast ready: __outport__ -> __host__:__port__",
"mc-ready": "udp multicast ready: __iface__:__outport__ -> __host__:__port__",
"bc-ready": "udp broadcast ready: __outport__ -> __host__:__port__",
"ready": "udp ready: __outport__ -> __host__:__port__",
"ready-nolocal": "udp ready: __host__:__port__"
"ready-nolocal": "udp ready: __host__:__port__",
"re-use": "udp re-use socket: __outport__ -> __host__:__port__"
},
"errors": {
"access-error": "UDP access error, you may need root access for ports below 1024",
@ -544,7 +545,8 @@
"ip-notset": "udp: ip address not set",
"port-notset": "udp: port not set",
"port-invalid": "udp: port number not valid",
"alreadyused": "udp: port already in use"
"alreadyused": "udp: port __port__ already in use",
"ifnotfound": "udp: interface __iface__ not found"
}
},
"switch": {

View File

@ -526,7 +526,8 @@
"mc-ready": "udpードはマルチキャストの準備ができています: __outport__ -> __host__:__port__",
"bc-ready": "udpードはブロードキャストの準備ができています: __outport__ -> __host__:__port__",
"ready": "udpードは準備ができています: __outport__ -> __host__:__port__",
"ready-nolocal": "udpードは準備ができています: __host__:__port__"
"ready-nolocal": "udpードは準備ができています: __host__:__port__",
"re-use": "udp再利用ソケット: __outport__ -> __host__:__port__"
},
"errors": {
"access-error": "UDP接続エラー 管理者権限で1024未満のポート番号にアクセスできる必要があります",

View File

@ -525,7 +525,8 @@
"mc-ready": "udp 组播已准备好: __outport__ -> __host__:__port__",
"bc-ready": "udp 广播已准备好: __outport__ -> __host__:__port__",
"ready": "udp 已准备好: __outport__ -> __host__:__port__",
"ready-nolocal": "udp 已准备好: __host__:__port__"
"ready-nolocal": "udp 已准备好: __host__:__port__",
"re-use": "udp 重用套接字: __outport__ -> __host__:__port__"
},
"errors": {
"access-error": "UDP 访问错误, 你可能需要root权限才能接入1024以下的端口",