GH-26 rewrite network parser
This commit is contained in:
parent
c9b10ce420
commit
868c46bde4
|
@ -180,10 +180,10 @@ Client.prototype.connect = function(callback) {
|
||||||
if (err)
|
if (err)
|
||||||
c.end();
|
c.end();
|
||||||
|
|
||||||
self.emit('connect')
|
self.emit('connect');
|
||||||
});
|
});
|
||||||
|
|
||||||
self.emit('connect')
|
self.emit('connect');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,9 +216,9 @@ Client.prototype.bind = function(name, credentials, controls, callback, conn) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var req = new BindRequest({
|
var req = new BindRequest({
|
||||||
name: name,
|
name: name || '',
|
||||||
authentication: 'Simple',
|
authentication: 'Simple',
|
||||||
credentials: credentials,
|
credentials: credentials || '',
|
||||||
controls: controls
|
controls: controls
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -830,8 +830,9 @@ Client.prototype._newConnection = function() {
|
||||||
|
|
||||||
c.on('connect', function() {
|
c.on('connect', function() {
|
||||||
if (log.isTraceEnabled())
|
if (log.isTraceEnabled())
|
||||||
log.trace('%s connect event', c.ldap.id); c.ldap.connected = true;
|
log.trace('%s connect event', c.ldap.id);
|
||||||
|
|
||||||
|
c.ldap.connected = true;
|
||||||
c.ldap.id += ':' + c.fd;
|
c.ldap.id += ':' + c.fd;
|
||||||
self.emit('connect', c.ldap.id);
|
self.emit('connect', c.ldap.id);
|
||||||
});
|
});
|
||||||
|
@ -853,7 +854,7 @@ Client.prototype._newConnection = function() {
|
||||||
err = new ConnectionError(c.ldap.id + ' closed');
|
err = new ConnectionError(c.ldap.id + ' closed');
|
||||||
} else {
|
} else {
|
||||||
err = new UnbindResponse({
|
err = new UnbindResponse({
|
||||||
messageID: msgid,
|
messageID: msgid
|
||||||
});
|
});
|
||||||
err.status = 'unbind';
|
err.status = 'unbind';
|
||||||
}
|
}
|
||||||
|
@ -910,5 +911,15 @@ Client.prototype._newConnection = function() {
|
||||||
return callback(message);
|
return callback(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
c.parser.on('error', function(err) {
|
||||||
|
if (log.isTraceEnabled())
|
||||||
|
log.trace('%s error event=%s', c.ldap.id, err ? err.stack : '?');
|
||||||
|
|
||||||
|
if (self.listeners('error').length)
|
||||||
|
self.emit('error', err);
|
||||||
|
|
||||||
|
c.end();
|
||||||
|
});
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
};
|
};
|
||||||
|
|
|
@ -68,9 +68,9 @@ BindRequest.prototype._toBer = function(ber) {
|
||||||
assert.ok(ber);
|
assert.ok(ber);
|
||||||
|
|
||||||
ber.writeInt(this.version);
|
ber.writeInt(this.version);
|
||||||
ber.writeString(this.name.toString());
|
ber.writeString((this.name || '').toString());
|
||||||
// TODO add support for SASL et al
|
// TODO add support for SASL et al
|
||||||
ber.writeString(this.credentials, Ber.Context);
|
ber.writeString((this.credentials || ''), Ber.Context);
|
||||||
|
|
||||||
return ber;
|
return ber;
|
||||||
};
|
};
|
||||||
|
|
|
@ -62,17 +62,14 @@ LDAPMessage.prototype.toString = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
LDAPMessage.prototype.parse = function(data, length) {
|
LDAPMessage.prototype.parse = function(ber) {
|
||||||
if (!data || !Buffer.isBuffer(data))
|
assert.ok(ber);
|
||||||
throw new TypeError('data (buffer) required');
|
|
||||||
|
|
||||||
if (this.log.isTraceEnabled())
|
if (this.log.isTraceEnabled())
|
||||||
this.log.trace('parse: data=%s, len=%d', util.inspect(data), length);
|
this.log.trace('parse: data=%s', util.inspect(ber.buffer));
|
||||||
|
|
||||||
var ber = new BerReader(data);
|
|
||||||
|
|
||||||
// Delegate off to the specific type to parse
|
// Delegate off to the specific type to parse
|
||||||
this._parse(ber, length);
|
this._parse(ber, ber.remain);
|
||||||
|
|
||||||
// Look for controls
|
// Look for controls
|
||||||
if (ber.peek() === 0xa0) {
|
if (ber.peek() === 0xa0) {
|
||||||
|
|
|
@ -55,9 +55,7 @@ function Parser(options) {
|
||||||
|
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
this._reset();
|
this.buffer = null;
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this.log4js = options.log4js;
|
this.log4js = options.log4js;
|
||||||
this.log = this.log4js.getLogger('Parser');
|
this.log = this.log4js.getLogger('Parser');
|
||||||
}
|
}
|
||||||
|
@ -69,83 +67,61 @@ Parser.prototype.write = function(data) {
|
||||||
if (!data || !Buffer.isBuffer(data))
|
if (!data || !Buffer.isBuffer(data))
|
||||||
throw new TypeError('data (buffer) required');
|
throw new TypeError('data (buffer) required');
|
||||||
|
|
||||||
|
var log = this.log;
|
||||||
|
var nextMessage = null;
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (this._buffer)
|
function end() {
|
||||||
data = this._buffer.concat(data);
|
if (nextMessage)
|
||||||
|
return self.write(nextMessage);
|
||||||
|
|
||||||
if (this.log.isTraceEnabled())
|
return true;
|
||||||
this.log.trace('Processing buffer (concat\'d): ' + util.inspect(data));
|
}
|
||||||
|
|
||||||
// If there's more than one message in this buffer
|
self.buffer = (self.buffer ? self.buffer.concat(data) : data);
|
||||||
var extra;
|
|
||||||
|
var ber = new BerReader(self.buffer);
|
||||||
|
if (!ber.readSequence())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ber.remain < ber.length) { // ENOTENOUGH
|
||||||
|
return false;
|
||||||
|
} else if (ber.remain > ber.length) { // ETOOMUCH
|
||||||
|
// This is sort of ugly, but allows us to make miminal copies
|
||||||
|
nextMessage = self.buffer.slice(ber.offset + ber.length);
|
||||||
|
ber._size = ber.offset + ber.length;
|
||||||
|
assert.equal(ber.remain, ber.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're here, ber holds the message, and nextMessage is temporarily
|
||||||
|
// pointing at the next sequence of data (if it exists)
|
||||||
|
self.buffer = null;
|
||||||
|
|
||||||
|
var message = this.getMessage(ber);
|
||||||
|
if (!message)
|
||||||
|
return end();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this._message === null) {
|
message.parse(ber);
|
||||||
var ber = new BerReader(data);
|
|
||||||
if (!this._newMessage(ber))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
data = data.slice(ber.offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.length > this._messageLength) {
|
|
||||||
extra = data.slice(ber.length);
|
|
||||||
data = data.slice(0, ber.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.ok(this._message.parse(data, ber.length));
|
|
||||||
|
|
||||||
var message = this._message;
|
|
||||||
this._reset();
|
|
||||||
this.emit('message', message);
|
this.emit('message', message);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
self.emit('error', e, self._message);
|
this.emit('error', e, message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Another message is already there
|
return end();
|
||||||
if (extra) {
|
|
||||||
if (this.log.isTraceEnabled())
|
|
||||||
this.log.trace('parsing extra bytes: ' + util.inspect(extra));
|
|
||||||
|
|
||||||
return this.write(extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Parser.prototype._newMessage = function(ber) {
|
Parser.prototype.getMessage = function(ber) {
|
||||||
assert.ok(ber);
|
assert.ok(ber);
|
||||||
|
|
||||||
if (this._messageLength === null) {
|
var log = this.log;
|
||||||
if (ber.readSequence() === null) { // not enough data for the length?
|
var self = this;
|
||||||
this._buffer = ber.buffer;
|
|
||||||
if (this.log.isTraceEnabled())
|
|
||||||
this.log.trace('Not enough data for the message header');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this._messageLength = ber.length;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ber.remain < this._messageLength) {
|
|
||||||
if (this.log.isTraceEnabled())
|
|
||||||
this.log.trace('Not enough data for the message');
|
|
||||||
|
|
||||||
this._buffer = ber.buffer;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var messageID = ber.readInt();
|
var messageID = ber.readInt();
|
||||||
var type = ber.readSequence();
|
var type = ber.readSequence();
|
||||||
|
|
||||||
if (this.log.isTraceEnabled())
|
|
||||||
this.log.trace('message id=%d, type=0x%s', messageID, type.toString(16));
|
|
||||||
|
|
||||||
var Message;
|
var Message;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
||||||
|
@ -230,28 +206,23 @@ Parser.prototype._newMessage = function(ber) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
var e = new Error('protocolOp 0x' + type.toString(16) + ' not supported');
|
this.emit('error',
|
||||||
this.emit('error', e, new LDAPResult({
|
new Error('protocolOp 0x' +
|
||||||
messageID: messageID,
|
(type ? type.toString(16) : '??') +
|
||||||
protocolOp: type
|
' not supported'
|
||||||
}));
|
),
|
||||||
this._reset();
|
new LDAPResult({
|
||||||
|
messageID: messageID,
|
||||||
|
protocolOp: type || Protocol.LDAP_REP_EXTENSION
|
||||||
|
}));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
assert.ok(Message);
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this._message = new Message({
|
return new Message({
|
||||||
messageID: messageID,
|
messageID: messageID,
|
||||||
log4js: self.log4js
|
log4js: self.log4js
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Parser.prototype._reset = function() {
|
|
||||||
this._message = null;
|
|
||||||
this._messageLength = null;
|
|
||||||
this._buffer = null;
|
|
||||||
};
|
|
||||||
|
|
|
@ -140,8 +140,8 @@ SearchEntry.prototype._parse = function(ber) {
|
||||||
|
|
||||||
this.objectName = ber.readString();
|
this.objectName = ber.readString();
|
||||||
assert.ok(ber.readSequence());
|
assert.ok(ber.readSequence());
|
||||||
var end = ber.offset + ber.length;
|
|
||||||
|
|
||||||
|
var end = ber.offset + ber.length;
|
||||||
while (ber.offset < end) {
|
while (ber.offset < end) {
|
||||||
var a = new Attribute();
|
var a = new Attribute();
|
||||||
a.parse(ber);
|
a.parse(ber);
|
||||||
|
|
|
@ -395,14 +395,17 @@ function Server(options) {
|
||||||
log.error('Exception happened parsing for %s: %s',
|
log.error('Exception happened parsing for %s: %s',
|
||||||
c.ldap.id, err.stack);
|
c.ldap.id, err.stack);
|
||||||
|
|
||||||
|
|
||||||
|
if (!message)
|
||||||
|
return c.destroy();
|
||||||
|
|
||||||
var res = getResponse(message);
|
var res = getResponse(message);
|
||||||
if (res) {
|
if (!res)
|
||||||
res.status = 0x02; // protocol error
|
return c.destroy();
|
||||||
res.errorMessage = err.toString();
|
|
||||||
c.end(res.toBer());
|
res.status = 0x02; // protocol error
|
||||||
} else {
|
res.errorMessage = err.toString();
|
||||||
c.destroy();
|
c.end(res.toBer());
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
c.on('data', function(data) {
|
c.on('data', function(data) {
|
||||||
|
@ -804,7 +807,7 @@ Server.prototype._getHandlerChain = function(req) {
|
||||||
handlers: [(req.protocolOp !== Protocol.LDAP_REQ_EXTENSION ?
|
handlers: [(req.protocolOp !== Protocol.LDAP_REQ_EXTENSION ?
|
||||||
noSuffixHandler : noExOpHandler)]
|
noSuffixHandler : noExOpHandler)]
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
Server.prototype._mount = function(op, name, argv, notDN) {
|
Server.prototype._mount = function(op, name, argv, notDN) {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "ldapjs",
|
"name": "ldapjs",
|
||||||
"homepage": "http://ldapjs.org",
|
"homepage": "http://ldapjs.org",
|
||||||
"description": "LDAP client and server APIs",
|
"description": "LDAP client and server APIs",
|
||||||
"version": "0.2.8",
|
"version": "0.3.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/mcavage/node-ldapjs.git"
|
"url": "git://github.com/mcavage/node-ldapjs.git"
|
||||||
|
|
|
@ -128,7 +128,8 @@ test('setup', function(t) {
|
||||||
reconnect: false // turn this off for unit testing
|
reconnect: false // turn this off for unit testing
|
||||||
});
|
});
|
||||||
t.ok(client);
|
t.ok(client);
|
||||||
client.log4js.setLevel('Trace');
|
// client.log4js.setLevel('Trace');
|
||||||
|
// server.log4js.setLevel('Trace');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ test('Construct no args', function(t) {
|
||||||
test('Construct args', function(t) {
|
test('Construct args', function(t) {
|
||||||
var f = new ExtensibleFilter({
|
var f = new ExtensibleFilter({
|
||||||
matchType: 'foo',
|
matchType: 'foo',
|
||||||
value: 'bar',
|
value: 'bar'
|
||||||
});
|
});
|
||||||
t.ok(f);
|
t.ok(f);
|
||||||
t.equal(f.matchType, 'foo');
|
t.equal(f.matchType, 'foo');
|
||||||
|
|
|
@ -46,7 +46,7 @@ test('parse', function(t) {
|
||||||
var req = new DeleteRequest();
|
var req = new DeleteRequest();
|
||||||
var reader = new BerReader(ber.buffer);
|
var reader = new BerReader(ber.buffer);
|
||||||
reader.readSequence(0x4a);
|
reader.readSequence(0x4a);
|
||||||
t.ok(req.parse(reader.buffer, reader.length));
|
t.ok(req.parse(reader, reader.length));
|
||||||
t.equal(req.dn.toString(), 'cn=test');
|
t.equal(req.dn.toString(), 'cn=test');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue