DTrace rework; now supporting a conn/message specific ID so you can track latency

This commit is contained in:
Mark Cavage 2011-08-30 18:12:34 +00:00
parent 7f43ade50b
commit 7aa1bfe586
9 changed files with 142 additions and 374 deletions

View File

@ -1 +0,0 @@
mark@bluesnoop.local.63414

View File

@ -1,18 +0,0 @@
var assert = require('assert');
var ldap = require('../../lib/index');
var client = ldap.createClient({
url: 'ldap://localhost:1389'
});
var finished = 0;
var ITERATIONS = 1024;
for (var i = 0; i < ITERATIONS; i++) {
client.bind('cn=root', 'secret', function(err) {
assert.ifError(err);
if (++finished === ITERATIONS)
client.unbind();
});
}

View File

@ -1,12 +0,0 @@
ldapjs*::bind:entry
{
/*self->start = timestamp;*/
}
ldapjs*:::return
{
/*
/self->start/
@ = quantize(timestamp - self->start);
self->start = 0;"
*/
}

View File

@ -1,201 +0,0 @@
var ldap = require('../lib/index');
///--- Shared handlers
function authorize(req, res, next) {
if (!req.connection.ldap.bindDN.equals('cn=root'))
return next(new ldap.InsufficientAccessRightsError());
return next();
}
///--- Globals
var SUFFIX = 'o=joyent';
var db = {};
//ldap.log4js.setLevel('Trace');
var server = ldap.createServer();
server.bind('cn=root', function(req, res, next) {
if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret')
return next(new ldap.InvalidCredentialsError());
res.end();
return next();
});
server.add(SUFFIX, authorize, function(req, res, next) {
var dn = req.dn.toString();
if (db[dn])
return next(new ldap.EntryAlreadyExistsError(dn));
db[dn] = req.toObject().attributes;
res.end();
return next();
});
server.bind(SUFFIX, function(req, res, next) {
var dn = req.dn.toString();
if (!db[dn])
return next(new ldap.NoSuchObjectError(dn));
if (!dn[dn].userpassword)
return next(new ldap.NoSuchAttributeError('userPassword'));
if (db[dn].userpassword !== req.credentials)
return next(new ldap.InvalidCredentialsError());
res.end();
return next();
});
server.compare(SUFFIX, authorize, function(req, res, next) {
var dn = req.dn.toString();
if (!db[dn])
return next(new ldap.NoSuchObjectError(dn));
if (!db[dn][req.attribute])
return next(new ldap.NoSuchAttributeError(req.attribute));
var matches = false;
var vals = db[dn][req.attribute];
for (var i = 0; i < vals.length; i++) {
if (vals[i] === req.value) {
matches = true;
break;
}
}
res.end(matches);
return next();
});
server.del(SUFFIX, authorize, function(req, res, next) {
var dn = req.dn.toString();
if (!db[dn])
return next(new ldap.NoSuchObjectError(dn));
delete db[dn];
res.end();
return next();
});
server.modify(SUFFIX, authorize, function(req, res, next) {
var dn = req.dn.toString();
if (!req.changes.length)
return next(new ldap.ProtocolError('changes required'));
if (!db[dn])
return next(new ldap.NoSuchObjectError(dn));
var entry = db[dn];
for (var i = 0; i < req.changes.length; i++) {
mod = req.changes[i].modification;
switch (req.changes[i].operation) {
case 'replace':
if (!entry[mod.type])
return next(new ldap.NoSuchAttributeError(mod.type));
if (!mod.vals || !mod.vals.length) {
delete entry[mod.type];
} else {
entry[mod.type] = mod.vals;
}
break;
case 'add':
if (!entry[mod.type]) {
entry[mod.type] = mod.vals;
} else {
mod.vals.forEach(function(v) {
if (entry[mod.type].indexOf(v) === -1)
entry[mod.type].push(v);
});
}
break;
case 'delete':
if (!entry[mod.type])
return next(new ldap.NoSuchAttributeError(mod.type));
delete entry[mod.type];
break;
}
}
res.end();
return next();
});
server.search(SUFFIX, authorize, function(req, res, next) {
var dn = req.dn.toString();
if (!db[dn])
return next(new ldap.NoSuchObjectError(dn));
var scopeCheck;
switch (req.scope) {
case 'base':
if (req.filter.matches(db[dn])) {
res.send({
dn: dn,
attributes: db[dn]
});
}
res.end();
return next();
case 'one':
scopeCheck = function(k) {
if (req.dn.equals(k))
return true;
var parent = ldap.parseDN(k).parent();
return (parent ? parent.equals(req.dn) : false);
};
break;
case 'sub':
scopeCheck = function(k) {
return (req.dn.equals(k) || req.dn.parentOf(k));
};
break;
}
Object.keys(db).forEach(function(key) {
if (!scopeCheck(key))
return;
if (req.filter.matches(db[key])) {
res.send({
dn: key,
attributes: db[key]
});
}
});
res.end();
return next();
});
///--- Fire it up
server.listen(1389, function() {
console.log('LDAP server up at: %s', server.url);
});

View File

@ -21,7 +21,17 @@ function isWhitespace(c) {
return re.test(c); return re.test(c);
} }
function RDN() {} function RDN(obj) {
var self = this;
if (obj) {
Object.keys(obj).forEach(function(k) {
self[k] = obj[k];
});
}
}
RDN.prototype.toString = function() { RDN.prototype.toString = function() {
var self = this; var self = this;

View File

@ -9,67 +9,64 @@ var dtrace = require('dtrace-provider');
var SERVER_PROVIDER; var SERVER_PROVIDER;
/* Args: /* Args:
* 0 -> remoteIP * 0 -> RequestId (<ip>:<src port>:<msgID>)
* 1 -> bindDN * 1 -> remoteIP
* 2 -> req.dn * 2 -> bindDN
* 3..5 -> op specific * 3 -> req.dn
* 4,5 -> op specific
*/ */
var SERVER_PROBES = { var SERVER_PROBES = {
// 3: attributes.length // 4: attributes.length
add: ['char *', 'char *', 'char *', 'int'], add: ['char *', 'char *', 'char *', 'char *', 'int'],
bind: ['char *', 'char *', 'char *'], bind: ['char *', 'char *', 'char *', 'char *'],
// 3: attribute, 4: value // 4: attribute, 5: value
compare: ['char *', 'char *', 'char *', 'char *', 'char *'], compare: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'],
'delete': ['char *', 'char *', 'char *'], 'delete': ['char *', 'char *', 'char *', 'char *'],
// 3: requestName, 4: requestValue // 4: requestName, 5: requestValue
exop: ['char *', 'char *', 'char *', 'char *', 'char *'], exop: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'],
// 3: changes.length // 4: changes.length
modify: ['char *', 'char *', 'char *', 'int'], modify: ['char *', 'char *', 'char *', 'char *', 'int'],
// 3: newRdn, 4: deleteOldRdn, 5: newSuperior // 4: newRdn, 5: newSuperior
modifyDN: ['char *', 'char *', 'char *', 'char *', 'int', 'char *'], modifyDN: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'],
// 3: filter, 4: scope, 5: attributes.length // 4: filter, 5: scope
search: ['char *', 'char *', 'char *', 'char *', 'char *', 'int'], search: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'],
unbind: ['char *', 'char *', 'char *'], unbind: ['char *', 'char *', 'char *', 'char *'],
// remote IP // remote IP
connection: ['char *'], connection: ['char *'],
// statusCode, matchedDN, error message, remoteAddress, bindDN, req.dn request: ['char *', 'char *', 'char *', 'char *'],
result: ['int', 'char *', 'char *', 'char *', 'char *', 'char *']
// requestId, remoteIp, bindDN, request.dn, statusCode, errorMessage
response: ['char *', 'char *', 'char *', 'char *', 'int', 'char *']
}; };
///--- API ///--- API
module.exports = { module.exports = function() {
if (!SERVER_PROVIDER) {
SERVER_PROVIDER = dtrace.createDTraceProvider('ldapjs');
ServerProbes: SERVER_PROBES, Object.keys(SERVER_PROBES).forEach(function(p) {
var args = SERVER_PROBES[p].splice(0);
args.unshift(p);
dtrace.DTraceProvider.prototype.addProbe.apply(SERVER_PROVIDER, args);
});
serverProvider: function() { SERVER_PROVIDER.enable();
if (!SERVER_PROVIDER) {
SERVER_PROVIDER = dtrace.createDTraceProvider('ldapjs');
Object.keys(SERVER_PROBES).forEach(function(p) {
var args = SERVER_PROBES[p].splice(0);
args.unshift(p);
dtrace.DTraceProvider.prototype.addProbe.apply(SERVER_PROVIDER, args);
});
SERVER_PROVIDER.enable();
}
return SERVER_PROVIDER;
} }
}; return SERVER_PROVIDER;
}();

View File

@ -8,7 +8,7 @@ var asn1 = require('asn1');
var LDAPMessage = require('./message'); var LDAPMessage = require('./message');
var Protocol = require('../protocol'); var Protocol = require('../protocol');
var dtrace = require('../dtrace').serverProvider(); var dtrace = require('../dtrace');
///--- Globals ///--- Globals
@ -69,13 +69,17 @@ LDAPResult.prototype.end = function(status) {
try { try {
var self = this; var self = this;
this.connection.write(ber); this.connection.write(ber);
dtrace.fire('result', function() {
return [self.status, dtrace.fire('response', function() {
(self.matchedDN || ''), var c = self.connection || {ldap: {bindDN: ''}};
(self.errorMessage || ''), return [
(self.connection ? self.connection.remoteAddress : ''), (self.logId || ''),
(self.connection ? self.connection.ldap.bindDN.toString() : ''), (c.remoteAddress || ''),
(self.requestDN ? self.requestDN.toString() : '')]; c.ldap.bindDN.toString(),
(self.requestDN ? self.requestDN.toString() : ''),
self.status,
self.errorMessage
];
}); });
} catch (e) { } catch (e) {
this.log.warn('%s failure to write message %j: %s', this.log.warn('%s failure to write message %j: %s',

View File

@ -8,7 +8,7 @@ var asn1 = require('asn1');
var LDAPMessage = require('./message'); var LDAPMessage = require('./message');
var LDAPResult = require('./result'); var LDAPResult = require('./result');
var DN = require('../dn').DN; var dn = require('../dn');
var Protocol = require('../protocol'); var Protocol = require('../protocol');
@ -17,6 +17,8 @@ var Protocol = require('../protocol');
var Ber = asn1.Ber; var Ber = asn1.Ber;
var DN = dn.DN;
var RDN = dn.RDN;
///--- API ///--- API
@ -32,8 +34,14 @@ function UnbindRequest(options) {
options.protocolOp = Protocol.LDAP_REQ_UNBIND; options.protocolOp = Protocol.LDAP_REQ_UNBIND;
LDAPMessage.call(this, options); LDAPMessage.call(this, options);
var self = this;
this.__defineGetter__('type', function() { return 'UnbindRequest'; }); this.__defineGetter__('type', function() { return 'UnbindRequest'; });
this.__defineGetter__('_dn', function() { return new DN([{}]); }); this.__defineGetter__('_dn', function() {
if (self.connection)
return self.connection.ldap.bindDN;
return new DN([new RDN({cn: 'anonymous'})]);
});
} }
util.inherits(UnbindRequest, LDAPMessage); util.inherits(UnbindRequest, LDAPMessage);
module.exports = UnbindRequest; module.exports = UnbindRequest;

View File

@ -174,6 +174,25 @@ function noExOpHandler(req, res, next) {
} }
function getDTraceHander(op, cb1, cb2) {
assert.ok(op);
return function(req, res, next) {
dtrace.fire(op, function() {
var c = req.connection;
return [
req.logId,
c.remoteAddress,
c.ldap.bindDN.toString(),
req.dn.toString(),
cb1 ? cb1(req, res) : undefined,
cb2 ? cb2(req, res) : undefined
];
});
return next();
};
}
///--- API ///--- API
@ -216,7 +235,6 @@ function Server(options) {
EventEmitter.call(this, options); EventEmitter.call(this, options);
this.dtrace = dtrace.serverProvider();
this.log = options.log4js.getLogger('Server'); this.log = options.log4js.getLogger('Server');
var log = this.log; var log = this.log;
@ -228,8 +246,8 @@ function Server(options) {
c.remotePort = c.fd; c.remotePort = c.fd;
} }
var rdn = new dn.RDN(); var rdn = new dn.RDN({cn: 'anonymous'});
rdn.cn = 'anonymous'
c.ldap = { c.ldap = {
id: c.remoteAddress + ':' + c.remotePort, id: c.remoteAddress + ':' + c.remotePort,
config: options, config: options,
@ -268,7 +286,8 @@ function Server(options) {
setupConnection(c); setupConnection(c);
if (log.isTraceEnabled()) if (log.isTraceEnabled())
log.trace('new connection from %s', c.ldap.id); log.trace('new connection from %s', c.ldap.id);
self.dtrace.fire('connection', function() { return [c.remoteAddress]; });
dtrace.fire('connection', function() { return [c.remoteAddress]; });
c.parser = new Parser({ c.parser = new Parser({
log4js: options.log4js log4js: options.log4js
@ -280,6 +299,15 @@ function Server(options) {
if (log.isDebugEnabled()) if (log.isDebugEnabled())
log.debug('%s: message received: req=%j', c.ldap.id, req.json); log.debug('%s: message received: req=%j', c.ldap.id, req.json);
dtrace.fire('request', function() {
return [
req.logId,
c.remoteAddress,
c.ldap.bindDN.toString(),
req.dn.toString()
];
});
var res = getResponse(req); var res = getResponse(req);
if (!res) { if (!res) {
log.warn('Unimplemented server method: %s', req.type); log.warn('Unimplemented server method: %s', req.type);
@ -288,6 +316,7 @@ function Server(options) {
} }
res.connection = c; res.connection = c;
res.logId = req.logId;
res.requestDN = req.dn; res.requestDN = req.dn;
var chain = self._getHandlerChain(req); var chain = self._getHandlerChain(req);
@ -396,17 +425,11 @@ module.exports = Server;
* @throws {TypeError} on bad input * @throws {TypeError} on bad input
*/ */
Server.prototype.add = function(name) { Server.prototype.add = function(name) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1); var args = Array.prototype.slice.call(arguments, 1);
args.unshift(function(req, res, next) { args.unshift(getDTraceHander('add',
self.dtrace.fire('add', function() { function(req, res) {
var c = req.connection; return req.attributes.length;
return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString(), }));
req.attributes.length];
});
return next();
});
return this._mount(Protocol.LDAP_REQ_ADD, name, args); return this._mount(Protocol.LDAP_REQ_ADD, name, args);
}; };
@ -423,16 +446,8 @@ Server.prototype.add = function(name) {
* @throws {TypeError} on bad input * @throws {TypeError} on bad input
*/ */
Server.prototype.bind = function(name) { Server.prototype.bind = function(name) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1); var args = Array.prototype.slice.call(arguments, 1);
args.unshift(function(req, res, next) { args.unshift(getDTraceHander('bind'));
self.dtrace.fire('bind', function() {
var c = req.connection;
return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString()];
});
return next();
});
return this._mount(Protocol.LDAP_REQ_BIND, name, args); return this._mount(Protocol.LDAP_REQ_BIND, name, args);
}; };
@ -449,17 +464,14 @@ Server.prototype.bind = function(name) {
* @throws {TypeError} on bad input * @throws {TypeError} on bad input
*/ */
Server.prototype.compare = function(name) { Server.prototype.compare = function(name) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1); var args = Array.prototype.slice.call(arguments, 1);
args.unshift(function(req, res, next) { args.unshift(getDTraceHander('compare',
self.dtrace.fire('compare', function() { function(req, res) {
var c = req.connection; return req.attribute;
return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString(), },
req.attribute, req.value]; function(req, res) {
}); return req.value;
return next(); }));
});
return this._mount(Protocol.LDAP_REQ_COMPARE, name, args); return this._mount(Protocol.LDAP_REQ_COMPARE, name, args);
}; };
@ -476,16 +488,8 @@ Server.prototype.compare = function(name) {
* @throws {TypeError} on bad input * @throws {TypeError} on bad input
*/ */
Server.prototype.del = function(name) { Server.prototype.del = function(name) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1); var args = Array.prototype.slice.call(arguments, 1);
args.unshift(function(req, res, next) { args.unshift(getDTraceHander('delete'));
self.dtrace.fire('delete', function() {
var c = req.connection;
return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString()];
});
return next();
});
return this._mount(Protocol.LDAP_REQ_DELETE, name, args); return this._mount(Protocol.LDAP_REQ_DELETE, name, args);
}; };
@ -502,17 +506,14 @@ Server.prototype.del = function(name) {
* @throws {TypeError} on bad input. * @throws {TypeError} on bad input.
*/ */
Server.prototype.exop = function(name) { Server.prototype.exop = function(name) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1); var args = Array.prototype.slice.call(arguments, 1);
args.unshift(function(req, res, next) { args.unshift(getDTraceHander('exop',
self.dtrace.fire('exop', function() { function(req, res) {
var c = req.connection; return req.name;
return [c.remoteAddress, c.ldap.bindDN.toString(), req.name, },
req.name, req.value]; function(req, res) {
}); return req.value;
return next(); }));
});
return this._mount(Protocol.LDAP_REQ_EXTENSION, name, args, true); return this._mount(Protocol.LDAP_REQ_EXTENSION, name, args, true);
}; };
@ -529,17 +530,11 @@ Server.prototype.exop = function(name) {
* @throws {TypeError} on bad input * @throws {TypeError} on bad input
*/ */
Server.prototype.modify = function(name) { Server.prototype.modify = function(name) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1); var args = Array.prototype.slice.call(arguments, 1);
args.unshift(function(req, res, next) { args.unshift(getDTraceHander('modify',
self.dtrace.fire('modify', function() { function(req, res) {
var c = req.connection; return req.changes.length;
return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString(), }));
req.changes.length];
});
return next();
});
return this._mount(Protocol.LDAP_REQ_MODIFY, name, args); return this._mount(Protocol.LDAP_REQ_MODIFY, name, args);
}; };
@ -556,18 +551,15 @@ Server.prototype.modify = function(name) {
* @throws {TypeError} on bad input * @throws {TypeError} on bad input
*/ */
Server.prototype.modifyDN = function(name) { Server.prototype.modifyDN = function(name) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1); var args = Array.prototype.slice.call(arguments, 1);
args.unshift(function(req, res, next) { args.unshift(getDTraceHander('modifyDN',
self.dtrace.fire('modifyDN', function() { function(req, res) {
var c = req.connection; return req.newRdn.toString();
return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString(), },
req.newRdn.toString(), (req.deleteOldRdn ? 1 : 0), function(req, res) {
(req.newSuperior ? req.newSuperior.toString() : '')]; return (req.newSuperior ?
}); req.newSuperior.toString() : '');
return next(); }));
});
return this._mount(Protocol.LDAP_REQ_MODRDN, name, args); return this._mount(Protocol.LDAP_REQ_MODRDN, name, args);
}; };
@ -584,18 +576,14 @@ Server.prototype.modifyDN = function(name) {
* @throws {TypeError} on bad input * @throws {TypeError} on bad input
*/ */
Server.prototype.search = function(name) { Server.prototype.search = function(name) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1); var args = Array.prototype.slice.call(arguments, 1);
args.unshift(function(req, res, next) { args.unshift(getDTraceHander('search',
self.dtrace.fire('search', function() { function(req, res) {
var c = req.connection; return req.scope;
return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString(), },
req.filter.toString(), req.scope, req.attributes.length]; function(req, res) {
}); return req.filter.toString();
return next(); }));
});
return this._mount(Protocol.LDAP_REQ_SEARCH, name, args); return this._mount(Protocol.LDAP_REQ_SEARCH, name, args);
}; };
@ -611,15 +599,8 @@ Server.prototype.search = function(name) {
* @throws {TypeError} on bad input * @throws {TypeError} on bad input
*/ */
Server.prototype.unbind = function() { Server.prototype.unbind = function() {
var self = this;
var args = Array.prototype.slice.call(arguments, 0); var args = Array.prototype.slice.call(arguments, 0);
args.unshift(function(req, res, next) { args.unshift(getDTraceHander('unbind'));
self.dtrace.fire('unbind', function() {
return [c.remoteAddress, c.ldap.bindDN.toString(), c.dn.toString()];
});
return next();
});
return this._mount(Protocol.LDAP_REQ_UNBIND, 'unbind', args, true); return this._mount(Protocol.LDAP_REQ_UNBIND, 'unbind', args, true);
}; };