diff --git a/examples/dtrace/.#binds.d b/examples/dtrace/.#binds.d deleted file mode 120000 index bd615d0..0000000 --- a/examples/dtrace/.#binds.d +++ /dev/null @@ -1 +0,0 @@ -mark@bluesnoop.local.63414 \ No newline at end of file diff --git a/examples/dtrace/bindloop.js b/examples/dtrace/bindloop.js deleted file mode 100644 index 01fc254..0000000 --- a/examples/dtrace/bindloop.js +++ /dev/null @@ -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(); - }); -} diff --git a/examples/dtrace/binds.d b/examples/dtrace/binds.d deleted file mode 100644 index 42cd7e1..0000000 --- a/examples/dtrace/binds.d +++ /dev/null @@ -1,12 +0,0 @@ -ldapjs*::bind:entry -{ - /*self->start = timestamp;*/ -} -ldapjs*:::return -{ - /* - /self->start/ - @ = quantize(timestamp - self->start); - self->start = 0;" - */ -} diff --git a/examples/in_memory.js b/examples/in_memory.js deleted file mode 100644 index 8d911a9..0000000 --- a/examples/in_memory.js +++ /dev/null @@ -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); -}); diff --git a/lib/dn.js b/lib/dn.js index 8cd7a77..45b7cb1 100644 --- a/lib/dn.js +++ b/lib/dn.js @@ -21,7 +21,17 @@ function isWhitespace(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() { var self = this; diff --git a/lib/dtrace.js b/lib/dtrace.js index 7b78bce..15498aa 100644 --- a/lib/dtrace.js +++ b/lib/dtrace.js @@ -9,67 +9,64 @@ var dtrace = require('dtrace-provider'); var SERVER_PROVIDER; /* Args: - * 0 -> remoteIP - * 1 -> bindDN - * 2 -> req.dn - * 3..5 -> op specific + * 0 -> RequestId (::) + * 1 -> remoteIP + * 2 -> bindDN + * 3 -> req.dn + * 4,5 -> op specific */ var SERVER_PROBES = { - // 3: attributes.length - add: ['char *', 'char *', 'char *', 'int'], + // 4: attributes.length + add: ['char *', 'char *', 'char *', 'char *', 'int'], - bind: ['char *', 'char *', 'char *'], + bind: ['char *', 'char *', 'char *', 'char *'], - // 3: attribute, 4: value - compare: ['char *', 'char *', 'char *', 'char *', 'char *'], + // 4: attribute, 5: value + compare: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'], - 'delete': ['char *', 'char *', 'char *'], + 'delete': ['char *', 'char *', 'char *', 'char *'], - // 3: requestName, 4: requestValue - exop: ['char *', 'char *', 'char *', 'char *', 'char *'], + // 4: requestName, 5: requestValue + exop: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'], - // 3: changes.length - modify: ['char *', 'char *', 'char *', 'int'], + // 4: changes.length + modify: ['char *', 'char *', 'char *', 'char *', 'int'], - // 3: newRdn, 4: deleteOldRdn, 5: newSuperior - modifyDN: ['char *', 'char *', 'char *', 'char *', 'int', 'char *'], + // 4: newRdn, 5: newSuperior + modifyDN: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'], - // 3: filter, 4: scope, 5: attributes.length - search: ['char *', 'char *', 'char *', 'char *', 'char *', 'int'], + // 4: filter, 5: scope + search: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'], - unbind: ['char *', 'char *', 'char *'], + unbind: ['char *', 'char *', 'char *', 'char *'], // remote IP connection: ['char *'], - // statusCode, matchedDN, error message, remoteAddress, bindDN, req.dn - result: ['int', 'char *', 'char *', 'char *', 'char *', 'char *'] + request: ['char *', 'char *', 'char *', 'char *'], + + // requestId, remoteIp, bindDN, request.dn, statusCode, errorMessage + response: ['char *', 'char *', 'char *', 'char *', 'int', 'char *'] }; ///--- 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() { - 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; + SERVER_PROVIDER.enable(); } -}; + return SERVER_PROVIDER; +}(); + diff --git a/lib/messages/result.js b/lib/messages/result.js index 8f72785..1d31e4e 100644 --- a/lib/messages/result.js +++ b/lib/messages/result.js @@ -8,7 +8,7 @@ var asn1 = require('asn1'); var LDAPMessage = require('./message'); var Protocol = require('../protocol'); -var dtrace = require('../dtrace').serverProvider(); +var dtrace = require('../dtrace'); ///--- Globals @@ -69,13 +69,17 @@ LDAPResult.prototype.end = function(status) { try { var self = this; this.connection.write(ber); - dtrace.fire('result', function() { - return [self.status, - (self.matchedDN || ''), - (self.errorMessage || ''), - (self.connection ? self.connection.remoteAddress : ''), - (self.connection ? self.connection.ldap.bindDN.toString() : ''), - (self.requestDN ? self.requestDN.toString() : '')]; + + dtrace.fire('response', function() { + var c = self.connection || {ldap: {bindDN: ''}}; + return [ + (self.logId || ''), + (c.remoteAddress || ''), + c.ldap.bindDN.toString(), + (self.requestDN ? self.requestDN.toString() : ''), + self.status, + self.errorMessage + ]; }); } catch (e) { this.log.warn('%s failure to write message %j: %s', diff --git a/lib/messages/unbind_request.js b/lib/messages/unbind_request.js index 98a9d18..79f4a1f 100644 --- a/lib/messages/unbind_request.js +++ b/lib/messages/unbind_request.js @@ -8,7 +8,7 @@ var asn1 = require('asn1'); var LDAPMessage = require('./message'); var LDAPResult = require('./result'); -var DN = require('../dn').DN; +var dn = require('../dn'); var Protocol = require('../protocol'); @@ -17,6 +17,8 @@ var Protocol = require('../protocol'); var Ber = asn1.Ber; +var DN = dn.DN; +var RDN = dn.RDN; ///--- API @@ -32,8 +34,14 @@ function UnbindRequest(options) { options.protocolOp = Protocol.LDAP_REQ_UNBIND; LDAPMessage.call(this, options); + var self = this; 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); module.exports = UnbindRequest; diff --git a/lib/server.js b/lib/server.js index e18fc4d..5db5ec6 100644 --- a/lib/server.js +++ b/lib/server.js @@ -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 @@ -216,7 +235,6 @@ function Server(options) { EventEmitter.call(this, options); - this.dtrace = dtrace.serverProvider(); this.log = options.log4js.getLogger('Server'); var log = this.log; @@ -228,8 +246,8 @@ function Server(options) { c.remotePort = c.fd; } - var rdn = new dn.RDN(); - rdn.cn = 'anonymous' + var rdn = new dn.RDN({cn: 'anonymous'}); + c.ldap = { id: c.remoteAddress + ':' + c.remotePort, config: options, @@ -268,7 +286,8 @@ function Server(options) { setupConnection(c); if (log.isTraceEnabled()) 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({ log4js: options.log4js @@ -280,6 +299,15 @@ function Server(options) { if (log.isDebugEnabled()) 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); if (!res) { log.warn('Unimplemented server method: %s', req.type); @@ -288,6 +316,7 @@ function Server(options) { } res.connection = c; + res.logId = req.logId; res.requestDN = req.dn; var chain = self._getHandlerChain(req); @@ -396,17 +425,11 @@ module.exports = Server; * @throws {TypeError} on bad input */ Server.prototype.add = function(name) { - var self = this; - var args = Array.prototype.slice.call(arguments, 1); - args.unshift(function(req, res, next) { - self.dtrace.fire('add', function() { - var c = req.connection; - return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString(), - req.attributes.length]; - }); - return next(); - }); + args.unshift(getDTraceHander('add', + function(req, res) { + return req.attributes.length; + })); return this._mount(Protocol.LDAP_REQ_ADD, name, args); }; @@ -423,16 +446,8 @@ Server.prototype.add = function(name) { * @throws {TypeError} on bad input */ Server.prototype.bind = function(name) { - var self = this; - var args = Array.prototype.slice.call(arguments, 1); - args.unshift(function(req, res, next) { - self.dtrace.fire('bind', function() { - var c = req.connection; - return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString()]; - }); - return next(); - }); + args.unshift(getDTraceHander('bind')); return this._mount(Protocol.LDAP_REQ_BIND, name, args); }; @@ -449,17 +464,14 @@ Server.prototype.bind = function(name) { * @throws {TypeError} on bad input */ Server.prototype.compare = function(name) { - var self = this; - var args = Array.prototype.slice.call(arguments, 1); - args.unshift(function(req, res, next) { - self.dtrace.fire('compare', function() { - var c = req.connection; - return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString(), - req.attribute, req.value]; - }); - return next(); - }); + args.unshift(getDTraceHander('compare', + function(req, res) { + return req.attribute; + }, + function(req, res) { + return req.value; + })); return this._mount(Protocol.LDAP_REQ_COMPARE, name, args); }; @@ -476,16 +488,8 @@ Server.prototype.compare = function(name) { * @throws {TypeError} on bad input */ Server.prototype.del = function(name) { - var self = this; - var args = Array.prototype.slice.call(arguments, 1); - args.unshift(function(req, res, next) { - self.dtrace.fire('delete', function() { - var c = req.connection; - return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString()]; - }); - return next(); - }); + args.unshift(getDTraceHander('delete')); return this._mount(Protocol.LDAP_REQ_DELETE, name, args); }; @@ -502,17 +506,14 @@ Server.prototype.del = function(name) { * @throws {TypeError} on bad input. */ Server.prototype.exop = function(name) { - var self = this; - var args = Array.prototype.slice.call(arguments, 1); - args.unshift(function(req, res, next) { - self.dtrace.fire('exop', function() { - var c = req.connection; - return [c.remoteAddress, c.ldap.bindDN.toString(), req.name, - req.name, req.value]; - }); - return next(); - }); + args.unshift(getDTraceHander('exop', + function(req, res) { + return req.name; + }, + function(req, res) { + return req.value; + })); return this._mount(Protocol.LDAP_REQ_EXTENSION, name, args, true); }; @@ -529,17 +530,11 @@ Server.prototype.exop = function(name) { * @throws {TypeError} on bad input */ Server.prototype.modify = function(name) { - var self = this; - var args = Array.prototype.slice.call(arguments, 1); - args.unshift(function(req, res, next) { - self.dtrace.fire('modify', function() { - var c = req.connection; - return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString(), - req.changes.length]; - }); - return next(); - }); + args.unshift(getDTraceHander('modify', + function(req, res) { + return req.changes.length; + })); return this._mount(Protocol.LDAP_REQ_MODIFY, name, args); }; @@ -556,18 +551,15 @@ Server.prototype.modify = function(name) { * @throws {TypeError} on bad input */ Server.prototype.modifyDN = function(name) { - var self = this; - var args = Array.prototype.slice.call(arguments, 1); - args.unshift(function(req, res, next) { - self.dtrace.fire('modifyDN', function() { - var c = req.connection; - return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString(), - req.newRdn.toString(), (req.deleteOldRdn ? 1 : 0), - (req.newSuperior ? req.newSuperior.toString() : '')]; - }); - return next(); - }); + args.unshift(getDTraceHander('modifyDN', + function(req, res) { + return req.newRdn.toString(); + }, + function(req, res) { + return (req.newSuperior ? + req.newSuperior.toString() : ''); + })); return this._mount(Protocol.LDAP_REQ_MODRDN, name, args); }; @@ -584,18 +576,14 @@ Server.prototype.modifyDN = function(name) { * @throws {TypeError} on bad input */ Server.prototype.search = function(name) { - var self = this; - var args = Array.prototype.slice.call(arguments, 1); - args.unshift(function(req, res, next) { - self.dtrace.fire('search', function() { - var c = req.connection; - return [c.remoteAddress, c.ldap.bindDN.toString(), req.dn.toString(), - req.filter.toString(), req.scope, req.attributes.length]; - }); - return next(); - }); - + args.unshift(getDTraceHander('search', + function(req, res) { + return req.scope; + }, + function(req, res) { + return req.filter.toString(); + })); return this._mount(Protocol.LDAP_REQ_SEARCH, name, args); }; @@ -611,15 +599,8 @@ Server.prototype.search = function(name) { * @throws {TypeError} on bad input */ Server.prototype.unbind = function() { - var self = this; - var args = Array.prototype.slice.call(arguments, 0); - args.unshift(function(req, res, next) { - self.dtrace.fire('unbind', function() { - return [c.remoteAddress, c.ldap.bindDN.toString(), c.dn.toString()]; - }); - return next(); - }); + args.unshift(getDTraceHander('unbind')); return this._mount(Protocol.LDAP_REQ_UNBIND, 'unbind', args, true); };