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 new file mode 100644 index 0000000..15498aa --- /dev/null +++ b/lib/dtrace.js @@ -0,0 +1,72 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var dtrace = require('dtrace-provider'); + + + +///--- Globals + +var SERVER_PROVIDER; + +/* Args: + * 0 -> RequestId (::) + * 1 -> remoteIP + * 2 -> bindDN + * 3 -> req.dn + * 4,5 -> op specific + */ +var SERVER_PROBES = { + + // 4: attributes.length + add: ['char *', 'char *', 'char *', 'char *', 'int'], + + bind: ['char *', 'char *', 'char *', 'char *'], + + // 4: attribute, 5: value + compare: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'], + + 'delete': ['char *', 'char *', 'char *', 'char *'], + + // 4: requestName, 5: requestValue + exop: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'], + + // 4: changes.length + modify: ['char *', 'char *', 'char *', 'char *', 'int'], + + // 4: newRdn, 5: newSuperior + modifyDN: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'], + + // 4: filter, 5: scope + search: ['char *', 'char *', 'char *', 'char *', 'char *', 'char *'], + + unbind: ['char *', 'char *', 'char *', 'char *'], + + // remote IP + connection: ['char *'], + + request: ['char *', 'char *', 'char *', 'char *'], + + // requestId, remoteIp, bindDN, request.dn, statusCode, errorMessage + response: ['char *', 'char *', 'char *', 'char *', 'int', 'char *'] +}; + + +///--- API + +module.exports = 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; +}(); + diff --git a/lib/messages/result.js b/lib/messages/result.js index ecc20cc..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'); ///--- Globals @@ -17,6 +17,7 @@ var Ber = asn1.Ber; var BerWriter = asn1.BerWriter; + ///--- API function LDAPResult(options) { @@ -66,11 +67,25 @@ LDAPResult.prototype.end = function(status) { this.log.debug('%s: sending: %j', this.connection.ldap.id, this.json); try { + var self = this; this.connection.write(ber); + + 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', this.connection.ldap.id, this.json, e.toString()); } + }; 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 32f0ef0..5db5ec6 100644 --- a/lib/server.js +++ b/lib/server.js @@ -10,6 +10,7 @@ var asn1 = require('asn1'); var sprintf = require('sprintf').sprintf; var dn = require('./dn'); +var dtrace = require('./dtrace'); var errors = require('./errors'); var Protocol = require('./protocol'); var logStub = require('./log_stub'); @@ -173,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 @@ -226,9 +246,12 @@ function Server(options) { c.remotePort = c.fd; } + var rdn = new dn.RDN({cn: 'anonymous'}); + c.ldap = { id: c.remoteAddress + ':' + c.remotePort, - config: options + config: options, + _bindDN: new DN([rdn]) }; c.addListener('timeout', function() { log.trace('%s timed out', c.ldap.id); @@ -247,7 +270,7 @@ function Server(options) { }); c.ldap.__defineGetter__('bindDN', function() { - return c.ldap._bindDN || new DN([{cn: 'anonymous'}]); + return c.ldap._bindDN; }); c.ldap.__defineSetter__('bindDN', function(val) { if (!(val instanceof DN)) @@ -264,6 +287,8 @@ function Server(options) { if (log.isTraceEnabled()) log.trace('new connection from %s', c.ldap.id); + dtrace.fire('connection', function() { return [c.remoteAddress]; }); + c.parser = new Parser({ log4js: options.log4js }); @@ -274,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); @@ -281,6 +315,10 @@ function Server(options) { return; } + res.connection = c; + res.logId = req.logId; + res.requestDN = req.dn; + var chain = self._getHandlerChain(req); var i = 0; @@ -387,9 +425,13 @@ module.exports = Server; * @throws {TypeError} on bad input */ Server.prototype.add = function(name) { - return this._mount(Protocol.LDAP_REQ_ADD, - name, - Array.prototype.slice.call(arguments, 1)); + var args = Array.prototype.slice.call(arguments, 1); + args.unshift(getDTraceHander('add', + function(req, res) { + return req.attributes.length; + })); + + return this._mount(Protocol.LDAP_REQ_ADD, name, args); }; @@ -404,9 +446,10 @@ Server.prototype.add = function(name) { * @throws {TypeError} on bad input */ Server.prototype.bind = function(name) { - return this._mount(Protocol.LDAP_REQ_BIND, - name, - Array.prototype.slice.call(arguments, 1)); + var args = Array.prototype.slice.call(arguments, 1); + args.unshift(getDTraceHander('bind')); + + return this._mount(Protocol.LDAP_REQ_BIND, name, args); }; @@ -421,9 +464,16 @@ Server.prototype.bind = function(name) { * @throws {TypeError} on bad input */ Server.prototype.compare = function(name) { - return this._mount(Protocol.LDAP_REQ_COMPARE, - name, - Array.prototype.slice.call(arguments, 1)); + var args = Array.prototype.slice.call(arguments, 1); + 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); }; @@ -438,9 +488,10 @@ Server.prototype.compare = function(name) { * @throws {TypeError} on bad input */ Server.prototype.del = function(name) { - return this._mount(Protocol.LDAP_REQ_DELETE, - name, - Array.prototype.slice.call(arguments, 1)); + var args = Array.prototype.slice.call(arguments, 1); + args.unshift(getDTraceHander('delete')); + + return this._mount(Protocol.LDAP_REQ_DELETE, name, args); }; @@ -455,10 +506,16 @@ Server.prototype.del = function(name) { * @throws {TypeError} on bad input. */ Server.prototype.exop = function(name) { - return this._mount(Protocol.LDAP_REQ_EXTENSION, - name, - Array.prototype.slice.call(arguments, 1), - true); + var args = Array.prototype.slice.call(arguments, 1); + 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); }; @@ -473,9 +530,13 @@ Server.prototype.exop = function(name) { * @throws {TypeError} on bad input */ Server.prototype.modify = function(name) { - return this._mount(Protocol.LDAP_REQ_MODIFY, - name, - Array.prototype.slice.call(arguments, 1)); + var args = Array.prototype.slice.call(arguments, 1); + args.unshift(getDTraceHander('modify', + function(req, res) { + return req.changes.length; + })); + + return this._mount(Protocol.LDAP_REQ_MODIFY, name, args); }; @@ -490,9 +551,17 @@ Server.prototype.modify = function(name) { * @throws {TypeError} on bad input */ Server.prototype.modifyDN = function(name) { - return this._mount(Protocol.LDAP_REQ_MODRDN, - name, - Array.prototype.slice.call(arguments, 1)); + var args = Array.prototype.slice.call(arguments, 1); + 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); }; @@ -507,9 +576,16 @@ Server.prototype.modifyDN = function(name) { * @throws {TypeError} on bad input */ Server.prototype.search = function(name) { - return this._mount(Protocol.LDAP_REQ_SEARCH, - name, - Array.prototype.slice.call(arguments, 1)); + var args = Array.prototype.slice.call(arguments, 1); + 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); }; @@ -523,10 +599,10 @@ Server.prototype.search = function(name) { * @throws {TypeError} on bad input */ Server.prototype.unbind = function() { - return this._mount(Protocol.LDAP_REQ_UNBIND, - 'unbind', - Array.prototype.slice.call(arguments, 0), - true); + var args = Array.prototype.slice.call(arguments, 0); + args.unshift(getDTraceHander('unbind')); + + return this._mount(Protocol.LDAP_REQ_UNBIND, 'unbind', args, true); }; diff --git a/package.json b/package.json index 535f697..fa3134c 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dependencies": { "asn1": "~0.1.5", "buffertools": "~1.0.3", + "dtrace-provider": "~0.0.2", "sprintf": "~0.1.1" }, "devDependencies": {