@@ -355,7 +355,7 @@ function Client(options) {
355
355
} ) ;
356
356
}
357
357
358
- this . socket = null ;
358
+ this . _socket = null ;
359
359
this . connected = false ;
360
360
this . connect ( ) ;
361
361
}
@@ -792,6 +792,7 @@ Client.prototype.search = function search(base,
792
792
793
793
var self = this ;
794
794
var baseDN = ensureDN ( base , this . strictDN ) ;
795
+
795
796
function sendRequest ( ctrls , emitter , cb ) {
796
797
var req = new SearchRequest ( {
797
798
baseObject : baseDN ,
@@ -834,7 +835,7 @@ Client.prototype.search = function search(base,
834
835
pager . on ( 'search' , sendRequest ) ;
835
836
pager . begin ( ) ;
836
837
} else {
837
- sendRequest ( controls , new EventEmitter , callback ) ;
838
+ sendRequest ( controls , new EventEmitter ( ) , callback ) ;
838
839
}
839
840
} ;
840
841
@@ -859,14 +860,106 @@ Client.prototype.unbind = function unbind(callback) {
859
860
// user-initiated unbind or something else.
860
861
this . unbound = true ;
861
862
862
- if ( ! this . socket )
863
+ if ( ! this . _socket )
863
864
return callback ( ) ;
864
865
865
866
var req = new UnbindRequest ( ) ;
866
867
return this . _send ( req , 'unbind' , null , callback ) ;
867
868
} ;
868
869
869
870
871
+ /**
872
+ * Attempt to secure connection with StartTLS.
873
+ */
874
+ Client . prototype . starttls = function starttls ( options ,
875
+ controls ,
876
+ callback ,
877
+ _bypass ) {
878
+ assert . optionalObject ( options ) ;
879
+ options = options || { } ;
880
+ callback = once ( callback ) ;
881
+ var self = this ;
882
+
883
+ if ( this . _starttls ) {
884
+ return callback ( new Error ( 'STARTTLS already in progress or active' ) ) ;
885
+ }
886
+
887
+ function onSend ( err , emitter ) {
888
+ if ( err ) {
889
+ callback ( err ) ;
890
+ return ;
891
+ }
892
+ /*
893
+ * Now that the request has been sent, block all outgoing messages
894
+ * until an error is received or we successfully complete the setup.
895
+ */
896
+ // TODO: block traffic
897
+ self . _starttls = {
898
+ started : true
899
+ } ;
900
+
901
+ emitter . on ( 'error' , function ( err ) {
902
+ self . _starttls = null ;
903
+ callback ( err ) ;
904
+ } ) ;
905
+ emitter . on ( 'end' , function ( res ) {
906
+ var sock = self . _socket ;
907
+ /*
908
+ * Unplumb socket data during SSL negotiation.
909
+ * This will prevent the LDAP parser from stumbling over the TLS
910
+ * handshake and raising a ruckus.
911
+ */
912
+ sock . removeAllListeners ( 'data' ) ;
913
+
914
+ options . socket = sock ;
915
+ var secure = tls . connect ( options ) ;
916
+ secure . once ( 'secureConnect' , function ( ) {
917
+ /*
918
+ * Wire up 'data' and 'error' handlers like the normal socket.
919
+ * Handling 'end' events isn't necessary since the underlying socket
920
+ * will handle those.
921
+ */
922
+ secure . removeAllListeners ( 'error' ) ;
923
+ secure . on ( 'data' , function onData ( data ) {
924
+ if ( self . log . trace ( ) )
925
+ self . log . trace ( 'data event: %s' , util . inspect ( data ) ) ;
926
+
927
+ self . _tracker . parser . write ( data ) ;
928
+ } ) ;
929
+ secure . on ( 'error' , function ( err ) {
930
+ if ( self . log . trace ( ) )
931
+ self . log . trace ( { err : err } , 'error event: %s' , new Error ( ) . stack ) ;
932
+
933
+ self . emit ( 'error' , err ) ;
934
+ sock . destroy ( ) ;
935
+ } ) ;
936
+ callback ( null ) ;
937
+ } ) ;
938
+ secure . once ( 'error' , function ( err ) {
939
+ // If the SSL negotiation failed, to back to plain mode.
940
+ self . _starttls = null ;
941
+ secure . removeAllListeners ( ) ;
942
+ callback ( err ) ;
943
+ } ) ;
944
+ self . _starttls . success = true ;
945
+ self . _socket = secure ;
946
+ } ) ;
947
+ }
948
+
949
+ var req = new ExtendedRequest ( {
950
+ requestName : '1.3.6.1.4.1.1466.20037' ,
951
+ requestValue : null ,
952
+ controls : controls
953
+ } ) ;
954
+
955
+ return this . _send ( req ,
956
+ [ errors . LDAP_SUCCESS ] ,
957
+ new EventEmitter ( ) ,
958
+ onSend ,
959
+ _bypass ) ;
960
+ } ;
961
+
962
+
870
963
/**
871
964
* Disconnect from the LDAP server and do not allow reconnection.
872
965
*
@@ -889,8 +982,8 @@ Client.prototype.destroy = function destroy(err) {
889
982
} ) ;
890
983
if ( this . connected ) {
891
984
this . unbind ( ) ;
892
- } else if ( this . socket ) {
893
- this . socket . destroy ( ) ;
985
+ } else if ( this . _socket ) {
986
+ this . _socket . destroy ( ) ;
894
987
}
895
988
this . emit ( 'destroy' , err ) ;
896
989
} ;
@@ -906,6 +999,7 @@ Client.prototype.connect = function connect() {
906
999
var self = this ;
907
1000
var log = this . log ;
908
1001
var socket ;
1002
+ var tracker ;
909
1003
910
1004
// Establish basic socket connection
911
1005
function connectSocket ( cb ) {
@@ -930,8 +1024,8 @@ Client.prototype.connect = function connect() {
930
1024
. removeAllListeners ( 'connect' )
931
1025
. removeAllListeners ( 'secureConnect' ) ;
932
1026
933
- socket . ldap . id = nextClientId ( ) + '__' + socket . ldap . id ;
934
- self . log = self . log . child ( { ldap_id : socket . ldap . id } , true ) ;
1027
+ tracker . id = nextClientId ( ) + '__' + tracker . id ;
1028
+ self . log = self . log . child ( { ldap_id : tracker . id } , true ) ;
935
1029
936
1030
// Move on to client setup
937
1031
setupClient ( cb ) ;
@@ -953,7 +1047,7 @@ Client.prototype.connect = function connect() {
953
1047
self . connectTimer = setTimeout ( function onConnectTimeout ( ) {
954
1048
if ( ! socket || ! socket . readable || ! socket . writeable ) {
955
1049
socket . destroy ( ) ;
956
- self . socket = null ;
1050
+ self . _socket = null ;
957
1051
onResult ( new ConnectionError ( 'connection timeout' ) ) ;
958
1052
}
959
1053
} , self . connectTimeout ) ;
@@ -962,7 +1056,7 @@ Client.prototype.connect = function connect() {
962
1056
963
1057
// Initialize socket events and LDAP parser.
964
1058
function initSocket ( ) {
965
- socket . ldap = new MessageTracker ( {
1059
+ tracker = new MessageTracker ( {
966
1060
id : self . url ? self . url . href : self . socketPath ,
967
1061
parser : new Parser ( { log : log } )
968
1062
} ) ;
@@ -979,13 +1073,13 @@ Client.prototype.connect = function connect() {
979
1073
if ( log . trace ( ) )
980
1074
log . trace ( 'data event: %s' , util . inspect ( data ) ) ;
981
1075
982
- socket . ldap . parser . write ( data ) ;
1076
+ tracker . parser . write ( data ) ;
983
1077
} ) ;
984
1078
985
1079
// The "router"
986
- socket . ldap . parser . on ( 'message' , function onMessage ( message ) {
987
- message . connection = socket ;
988
- var callback = socket . ldap . fetch ( message . messageID ) ;
1080
+ tracker . parser . on ( 'message' , function onMessage ( message ) {
1081
+ message . connection = self . _socket ;
1082
+ var callback = tracker . fetch ( message . messageID ) ;
989
1083
990
1084
if ( ! callback ) {
991
1085
log . error ( { message : message . json } , 'unsolicited message' ) ;
@@ -995,9 +1089,9 @@ Client.prototype.connect = function connect() {
995
1089
return callback ( message ) ;
996
1090
} ) ;
997
1091
998
- socket . ldap . parser . on ( 'error' , function onParseError ( err ) {
1092
+ tracker . parser . on ( 'error' , function onParseError ( err ) {
999
1093
self . emit ( 'error' , new VError ( err , 'Parser error for %s' ,
1000
- socket . ldap . id ) ) ;
1094
+ tracker . id ) ) ;
1001
1095
self . connected = false ;
1002
1096
socket . end ( ) ;
1003
1097
} ) ;
@@ -1018,7 +1112,8 @@ Client.prototype.connect = function connect() {
1018
1112
socket . once ( 'end' , bail ) ;
1019
1113
socket . once ( 'timeout' , bail ) ;
1020
1114
1021
- self . socket = socket ;
1115
+ self . _socket = socket ;
1116
+ self . _tracker = tracker ;
1022
1117
1023
1118
// Run any requested setup (such as automatically performing a bind) on
1024
1119
// socket before signalling successful connection.
@@ -1152,14 +1247,15 @@ Client.prototype._flushQueue = function _flushQueue() {
1152
1247
* Clean up socket/parser resources after socket close.
1153
1248
*/
1154
1249
Client . prototype . _onClose = function _onClose ( had_err ) {
1155
- var socket = this . socket ;
1250
+ var socket = this . _socket ;
1251
+ var tracker = this . _tracker ;
1156
1252
socket . removeAllListeners ( 'connect' )
1157
1253
. removeAllListeners ( 'data' )
1158
1254
. removeAllListeners ( 'drain' )
1159
1255
. removeAllListeners ( 'end' )
1160
1256
. removeAllListeners ( 'error' )
1161
1257
. removeAllListeners ( 'timeout' ) ;
1162
- this . socket = null ;
1258
+ this . _socket = null ;
1163
1259
this . connected = false ;
1164
1260
1165
1261
( ( socket . socket ) ? socket . socket : socket ) . removeAllListeners ( 'close' ) ;
@@ -1170,12 +1266,12 @@ Client.prototype._onClose = function _onClose(had_err) {
1170
1266
this . emit ( 'close' , had_err ) ;
1171
1267
// On close we have to walk the outstanding messages and go invoke their
1172
1268
// callback with an error.
1173
- socket . ldap . pending . forEach ( function ( msgid ) {
1174
- var cb = socket . ldap . fetch ( msgid ) ;
1175
- socket . ldap . remove ( msgid ) ;
1269
+ tracker . pending . forEach ( function ( msgid ) {
1270
+ var cb = tracker . fetch ( msgid ) ;
1271
+ tracker . remove ( msgid ) ;
1176
1272
1177
1273
if ( socket . unbindMessageID !== parseInt ( msgid , 10 ) ) {
1178
- return cb ( new ConnectionError ( socket . ldap . id + ' closed' ) ) ;
1274
+ return cb ( new ConnectionError ( tracker . id + ' closed' ) ) ;
1179
1275
} else {
1180
1276
// Unbinds will be communicated as a success since we're closed
1181
1277
var unbind = new UnbindResponse ( { messageID : msgid } ) ;
@@ -1184,8 +1280,9 @@ Client.prototype._onClose = function _onClose(had_err) {
1184
1280
}
1185
1281
} ) ;
1186
1282
1187
- delete socket . ldap . parser ;
1188
- delete socket . ldap ;
1283
+ // Trash any parser or starttls state
1284
+ this . _tracker = null ;
1285
+ delete this . _starttls ;
1189
1286
1190
1287
// Automatically fire reconnect logic if the socket was closed for any reason
1191
1288
// other than a user-initiated unbind.
@@ -1212,8 +1309,8 @@ Client.prototype._updateIdle = function _updateIdle(override) {
1212
1309
var self = this ;
1213
1310
function isIdle ( disable ) {
1214
1311
return ( ( disable !== true ) &&
1215
- ( self . socket && self . connected ) &&
1216
- ( self . socket . ldap . pending . length === 0 ) ) ;
1312
+ ( self . _socket && self . connected ) &&
1313
+ ( self . _tracker . pending . length === 0 ) ) ;
1217
1314
}
1218
1315
if ( isIdle ( override ) ) {
1219
1316
if ( ! this . _idleTimer ) {
@@ -1242,14 +1339,14 @@ Client.prototype._send = function _send(message,
1242
1339
_bypass ) {
1243
1340
assert . ok ( message ) ;
1244
1341
assert . ok ( expect ) ;
1245
- assert . ok ( typeof ( emitter ) !== undefined ) ;
1342
+ assert . optionalObject ( emitter ) ;
1246
1343
assert . ok ( callback ) ;
1247
1344
1248
1345
// Allow connect setup traffic to bypass checks
1249
- if ( _bypass && this . socket && this . socket . writable ) {
1346
+ if ( _bypass && this . _socket && this . _socket . writable ) {
1250
1347
return this . _sendSocket ( message , expect , emitter , callback ) ;
1251
1348
}
1252
- if ( ! this . socket || ! this . connected ) {
1349
+ if ( ! this . _socket || ! this . connected ) {
1253
1350
if ( ! this . queue . enqueue ( message , expect , emitter , callback ) ) {
1254
1351
callback ( new ConnectionError ( 'connection unavailable' ) ) ;
1255
1352
}
@@ -1268,7 +1365,8 @@ Client.prototype._sendSocket = function _sendSocket(message,
1268
1365
expect ,
1269
1366
emitter ,
1270
1367
callback ) {
1271
- var conn = this . socket ;
1368
+ var conn = this . _socket ;
1369
+ var tracker = this . _tracker ;
1272
1370
var log = this . log ;
1273
1371
var self = this ;
1274
1372
var timer = false ;
@@ -1313,7 +1411,7 @@ Client.prototype._sendSocket = function _sendSocket(message,
1313
1411
event = event [ 0 ] . toLowerCase ( ) + event . slice ( 1 ) ;
1314
1412
return _done ( event , msg ) ;
1315
1413
} else {
1316
- conn . ldap . remove ( message . messageID ) ;
1414
+ tracker . remove ( message . messageID ) ;
1317
1415
// Potentially mark client as idle
1318
1416
self . _updateIdle ( ) ;
1319
1417
@@ -1332,7 +1430,7 @@ Client.prototype._sendSocket = function _sendSocket(message,
1332
1430
1333
1431
function onRequestTimeout ( ) {
1334
1432
self . emit ( 'timeout' , message ) ;
1335
- var cb = conn . ldap . fetch ( message . messageID ) ;
1433
+ var cb = tracker . fetch ( message . messageID ) ;
1336
1434
if ( cb ) {
1337
1435
//FIXME: the timed-out request should be abandoned
1338
1436
cb ( new errors . TimeoutError ( 'request timeout (client interrupt)' ) ) ;
@@ -1342,9 +1440,9 @@ Client.prototype._sendSocket = function _sendSocket(message,
1342
1440
function writeCallback ( ) {
1343
1441
if ( expect === 'abandon' ) {
1344
1442
// Mark the messageID specified as abandoned
1345
- conn . ldap . abandon ( message . abandonID ) ;
1443
+ tracker . abandon ( message . abandonID ) ;
1346
1444
// No need to track the abandon request itself
1347
- conn . ldap . remove ( message . id ) ;
1445
+ tracker . remove ( message . id ) ;
1348
1446
return callback ( null ) ;
1349
1447
} else if ( expect === 'unbind' ) {
1350
1448
conn . unbindMessageID = message . id ;
@@ -1363,7 +1461,7 @@ Client.prototype._sendSocket = function _sendSocket(message,
1363
1461
} // end writeCallback()
1364
1462
1365
1463
// Start actually doing something...
1366
- conn . ldap . track ( message , messageCallback ) ;
1464
+ tracker . track ( message , messageCallback ) ;
1367
1465
// Mark client as active
1368
1466
this . _updateIdle ( true ) ;
1369
1467
0 commit comments