Skip to content

Commit 7fcf23d

Browse files
Merge pull request #378 from momoAmch/bug/discovery_full_udpBuffer
Bug/discovery full udp buffer
2 parents 370bdc9 + b405976 commit 7fcf23d

File tree

3 files changed

+88
-61
lines changed

3 files changed

+88
-61
lines changed

lib/cam.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,9 @@ Cam.prototype.digestAuth = function(wwwAuthenticate, reqOptions) {
405405
ha1.update([this.username, challenge.realm, this.password].join(':'));
406406

407407
// Sony SRG-XP1 sends qop="auth,auth-int" it means the Server will accept either "auth" or "auth-int". We select "auth"
408-
if (typeof challenge.qop === 'string' && challenge.qop === 'auth,auth-int') challenge.qop = 'auth';
408+
if (typeof challenge.qop === 'string' && challenge.qop === 'auth,auth-int') {
409+
challenge.qop = 'auth';
410+
}
409411

410412
const ha2 = crypto.createHash('md5');
411413
ha2.update([reqOptions.method, reqOptions.path].join(':'));

lib/discovery.js

Lines changed: 82 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ var Discovery = Object.create(new events.EventEmitter());
3535
* @param {string} [options.messageId=GUID] WS-Discovery message id
3636
* @param {string} [options.device=defaultroute] Interface to bind on for discovery ex. `eth0`
3737
* @param {number} [options.listeningPort=null] client will listen to discovery data device sent
38+
* @param {number} [options.bufferSize] buffer size in bytes for discovery responses
3839
* @param {Discovery~ProbeCallback} [callback] timeout callback
3940
* @fires Discovery#device
4041
* @fires Discovery#error
@@ -75,20 +76,21 @@ Discovery.probe = function(options, callback) {
7576
messageID = 'urn:uuid:' + (options.messageId || guid()),
7677
request = Buffer.from(
7778
'<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope" xmlns:dn="http://www.onvif.org/ver10/network/wsdl">' +
78-
'<Header>' +
79-
'<wsa:MessageID xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">' + messageID + '</wsa:MessageID>' +
80-
'<wsa:To xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>' +
81-
'<wsa:Action xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>' +
82-
'</Header>' +
83-
'<Body>' +
84-
'<Probe xmlns="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
85-
'<Types>dn:NetworkVideoTransmitter</Types>' +
86-
'<Scopes />' +
87-
'</Probe>' +
88-
'</Body>' +
89-
'</Envelope>'
79+
'<Header>' +
80+
'<wsa:MessageID xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">' + messageID + '</wsa:MessageID>' +
81+
'<wsa:To xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>' +
82+
'<wsa:Action xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>' +
83+
'</Header>' +
84+
'<Body>' +
85+
'<Probe xmlns="http://schemas.xmlsoap.org/ws/2005/04/discovery" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
86+
'<Types>dn:NetworkVideoTransmitter</Types>' +
87+
'<Scopes />' +
88+
'</Probe>' +
89+
'</Body>' +
90+
'</Envelope>'
9091
),
91-
socket = require('dgram').createSocket('udp4');
92+
// create a socket with reuse address option, in case there is many nodde processes listening on the same port
93+
socket = require('dgram').createSocket('udp4', { reuseAddr: true });
9294

9395
socket.on('error', function(err) {
9496
Discovery.emit('error', err);
@@ -97,73 +99,94 @@ Discovery.probe = function(options, callback) {
9799

98100
const httpOK200 = 200;
99101
const listener = function(msg, rinfo) {
100-
parseSOAPString(msg.toString(), function(err, data, xml, _statusCode) {
101-
// TODO check for matching RelatesTo field and messageId
102-
if (err || !data[0].probeMatches) {
103-
errors.push(err || new Error('Wrong SOAP message from ' + rinfo.address + ':' + rinfo.port, xml));
104-
/**
105-
* Indicates error response from device.
106-
* @event Discovery#error
107-
* @type {string}
108-
*/
109-
Discovery.emit('error', 'Wrong SOAP message from ' + rinfo.address + ':' + rinfo.port, xml);
110-
} else {
111-
data = linerase(data);
102+
setImmediate(() => {
103+
parseSOAPString(msg.toString(), function(err, data, xml, _statusCode) {
104+
// TODO check for matching RelatesTo field and messageId
105+
if (err || !data[0].probeMatches) {
106+
errors.push(err || new Error('Wrong SOAP message from ' + rinfo.address + ':' + rinfo.port, xml));
107+
/**
108+
* Indicates error response from device.
109+
* @event Discovery#error
110+
* @type {string}
111+
*/
112+
Discovery.emit('error', 'Wrong SOAP message from ' + rinfo.address + ':' + rinfo.port, xml);
113+
} else {
114+
data = linerase(data);
112115

113-
// Possible to get multiple matches for the same camera
114-
// when your computer has more than one network adapter in the same subnet
115-
var camAddr = data.probeMatches.probeMatch.endpointReference.address;
116-
if (!cams[camAddr]) {
117-
var cam;
118-
if (options.resolve !== false) {
119-
// Create cam with one of the XAddrs uri
120-
var camUris = data.probeMatches.probeMatch.XAddrs.split(' ').map(url.parse),
121-
camUri = matchXAddr(camUris, rinfo.address);
122-
cam = new Cam({
123-
hostname: camUri.hostname,
124-
port: camUri.port,
125-
path: camUri.path,
126-
urn: camAddr
127-
});
116+
// Possible to get multiple matches for the same camera
117+
// when your computer has more than one network adapter in the same subnet
118+
var camAddr = data.probeMatches.probeMatch.endpointReference.address;
119+
if (!cams[camAddr]) {
120+
var cam;
121+
if (options.resolve !== false) {
122+
// Create cam with one of the XAddrs uri
123+
var camUris = data.probeMatches.probeMatch.XAddrs.split(' ').map(url.parse),
124+
camUri = matchXAddr(camUris, rinfo.address);
125+
cam = new Cam({
126+
hostname: camUri.hostname,
127+
port: camUri.port,
128+
path: camUri.path,
129+
urn: camAddr
130+
});
131+
/**
132+
* All available XAddr fields from discovery
133+
* @name xaddrs
134+
* @memberOf Cam#
135+
* @type {Array.<Url>}
136+
*/
137+
cam.xaddrs = camUris;
138+
} else {
139+
cam = data;
140+
}
141+
cams[camAddr] = cam;
128142
/**
129-
* All available XAddr fields from discovery
130-
* @name xaddrs
131-
* @memberOf Cam#
132-
* @type {Array.<Url>}
133-
*/
134-
cam.xaddrs = camUris;
135-
} else {
136-
cam = data;
143+
* Indicates discovered device.
144+
* @event Discovery#device
145+
* @type {Cam|object}
146+
*/
147+
Discovery.emit('device', cam, rinfo, xml);
137148
}
138-
cams[camAddr] = cam;
139-
/**
140-
* Indicates discovered device.
141-
* @event Discovery#device
142-
* @type {Cam|object}
143-
*/
144-
Discovery.emit('device', cam, rinfo, xml);
145149
}
146-
}
147-
}, httpOK200);
150+
}, httpOK200);
151+
});
148152
};
149153

154+
// Callback function to bind the socket to the interface
155+
const bindingCallback = function(err) {
156+
if (err) {
157+
Discovery.emit('error', err);
158+
callback(err);
159+
} else {
160+
// set buffer size to the buffer size option in bytes
161+
if (options.bufferSize && options.bufferSize > 0) {
162+
socket.setRecvBufferSize(options.bufferSize);
163+
}
164+
socket.send(request, 0, request.length, 3702, '239.255.255.250');
165+
}
166+
};
150167
// If device is specified try to bind to that interface
168+
var shouldBind = false;
151169
if (options.device) {
152170
var interfaces = os.networkInterfaces();
153171
// Try to find the interface based on the device name
154172
if (options.device in interfaces) {
155173
interfaces[options.device].some(function(address) {
156174
// Only use IPv4 addresses
157175
if (address.family === 'IPv4') {
158-
socket.bind(options.listeningPort || null, address.address);
176+
socket.bind(options.listeningPort || null, address.address, bindingCallback);
177+
shouldBind = true;
159178
return true;
160179
}
161180
});
162181
}
163182
}
164183

184+
// If no device is specified, bind to the default port
185+
if (!shouldBind) {
186+
socket.bind(options.listeningPort || null, bindingCallback);
187+
}
188+
165189
socket.on('message', listener);
166-
socket.send(request, 0, request.length, 3702, '239.255.255.250');
167190

168191
setTimeout(function() {
169192
socket.removeListener('message', listener);

lib/ptz.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,9 @@ module.exports = function(Cam) {
383383
// Nov 2025 - skip over 'extension'
384384
configOptions.ptzspaces = [];
385385
for (const key of Object.keys(sp)) {
386-
if (key == 'extension') continue;
386+
if (key == 'extension') {
387+
continue;
388+
}
387389
for (const item of sp[key]) {
388390
let data = {
389391
name: key,

0 commit comments

Comments
 (0)