Welcome to mirror list, hosted at ThFree Co, Russian Federation.

Connection.cs « Novell.Directory.Ldap « Novell.Directory.Ldap « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: e2886ba09ff385dc18e3bf849871db0c140478f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
/******************************************************************************
* The MIT License
* Copyright (c) 2003 Novell Inc.  www.novell.com
* 
* Permission is hereby granted, free of charge, to any person obtaining  a copy
* of this software and associated documentation files (the Software), to deal
* in the Software without restriction, including  without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
* copies of the Software, and to  permit persons to whom the Software is 
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in 
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*******************************************************************************/
//
// Novell.Directory.Ldap.Connection.cs
//
// Author:
//   Sunil Kumar (Sunilk@novell.com)
//
// (C) 2003 Novell, Inc (http://www.novell.com)
//

using System;
using System.Threading;
using Novell.Directory.Ldap.Asn1;
using Novell.Directory.Ldap.Rfc2251;
using Novell.Directory.Ldap.Utilclass;
#if !TARGET_JVM
using Mono.Security.Protocol.Tls;
using Mono.Security.X509.Extensions;
#endif
using Syscert = System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.IO;
using System.Text;
#if !TARGET_JVM
using Mono.Security.X509;
#endif
using System.Text.RegularExpressions;
using System.Globalization;
using System.Reflection;

namespace Novell.Directory.Ldap
{
	public delegate bool CertificateValidationCallback(
		Syscert.X509Certificate certificate,
		int[] certificateErrors);
	
	/// <summary> The class that creates a connection to the Ldap server. After the
	/// connection is made, a thread is created that reads data from the
	/// connection.
	/// 
	/// The application's thread sends a request to the MessageAgent class, which
	/// creates a Message class.  The Message class calls the writeMessage method
	/// of this class to send the request to the server. The application thread
	/// will then query the MessageAgent class for a response.
	/// 
	/// The reader thread multiplexes response messages received from the
	/// server to the appropriate Message class. Each Message class
	/// has its own message queue.
	/// 
	/// Unsolicited messages are process separately, and if the application
	/// has registered a handler, a separate thread is created for that
	/// application's handler to process the message.
	/// 
	/// Note: the reader thread must not be a "selfish" thread, since some
	/// operating systems do not time slice.
	/// 
	/// </summary>
	/*package*/
	sealed class Connection
	{
		public event CertificateValidationCallback OnCertificateValidation;
		public  enum    CertificateProblem  : long
		{
			CertEXPIRED                   = 0x800B0101,
			CertVALIDITYPERIODNESTING     = 0x800B0102,
			CertROLE                      = 0x800B0103,
			CertPATHLENCONST              = 0x800B0104,
			CertCRITICAL                  = 0x800B0105,
			CertPURPOSE                   = 0x800B0106,
			CertISSUERCHAINING            = 0x800B0107,
			CertMALFORMED                 = 0x800B0108,
			CertUNTRUSTEDROOT             = 0x800B0109,
			CertCHAINING                  = 0x800B010A,
			CertREVOKED                   = 0x800B010C,
			CertUNTRUSTEDTESTROOT         = 0x800B010D,
			CertREVOCATION_FAILURE        = 0x800B010E,
			CertCN_NO_MATCH               = 0x800B010F,
			CertWRONG_USAGE               = 0x800B0110,
			CertUNTRUSTEDCA               = 0x800B0112
		}
		private static String GetProblemMessage(CertificateProblem Problem)
		{
			String ProblemMessage = "";
			String ProblemCodeName = CertificateProblem.GetName(typeof(CertificateProblem), Problem);
			if(ProblemCodeName != null)
				ProblemMessage = ProblemMessage + ProblemCodeName;
			else
				ProblemMessage = "Unknown Certificate Problem";
			return ProblemMessage;
		}
 
		private ArrayList handshakeProblemsEncountered = new ArrayList();
		private void  InitBlock()
		{
			writeSemaphore = new System.Object();
			encoder = new LBEREncoder();
			decoder = new LBERDecoder();
			stopReaderMessageID = CONTINUE_READING;
			messages = new MessageVector(5, 5);
			unsolicitedListeners = new System.Collections.ArrayList(3);
		}
		/// <summary>  Indicates whether clones exist for LdapConnection
		/// 
		/// </summary>
		/// <returns> true if clones exist, false otherwise.
		/// </returns>
		internal bool Cloned
		{
			/* package */
			
			get
			{
				return (cloneCount > 0);
			}
			
		}
	


		internal bool Ssl
		{
			get
			{
				return ssl;
			}
			set
			{
				ssl=value;
			}
		}
		/// <summary> gets the host used for this connection</summary>
		internal System.String Host
		{
			/* package */
			
			get
			{
				return host;
			}
			
		}
		/// <summary> gets the port used for this connection</summary>
		internal int Port
		{
			/* package */
			
			get
			{
				return port;
			}
			
		}
		/// <summary> gets the writeSemaphore id used for active bind operation</summary>
		/// <summary> sets the writeSemaphore id used for active bind operation</summary>
		internal int BindSemId
		{
			/* package */
			
			get
			{
				return bindSemaphoreId;
			}
			
			/* package */
			
			set
			{
				bindSemaphoreId = value;
				return ;
			}
			
		}
		/// <summary> checks if the writeSemaphore id used for active bind operation is clear</summary>
		internal bool BindSemIdClear
		{
			/* package */
			
			get
			{
				if (bindSemaphoreId == 0)
				{
					return true;
				}
				return false;
			}
			
		}
		/// <summary> Return whether the application is bound to this connection.
		/// Note: an anonymous bind returns false - not bound
		/// </summary>
		internal bool Bound
		{
			/* package */
			
			get
			{
				if (bindProperties != null)
				{
					// Bound if not anonymous
					return (!bindProperties.Anonymous);
				}
				return false;
			}
			
		}
		/// <summary> Return whether a connection has been made</summary>
		internal bool Connected
		{
			/* package */
			
			get
			{
				return (in_Renamed != null);
			}
			
		}
		/// <summary> 
		/// Sets the authentication credentials in the object
		/// and set flag indicating successful bind.
		/// 
		/// 
		/// 
		/// </summary>
		/// <returns>  The BindProperties object for this connection.
		/// </returns>
		/// <summary> 
		/// Sets the authentication credentials in the object
		/// and set flag indicating successful bind.
		/// 
		/// 
		/// 
		/// </summary>
		/// <param name="bindProps">  The BindProperties object to set.
		/// </param>
		internal BindProperties BindProperties
		{
			/* package */
			
			get
			{
				return bindProperties;
			}
			
			/* package */
			
			set
			{
				bindProperties = value;
				return ;
			}
			
		}
		/// <summary> Gets the current referral active on this connection if created to
		/// follow referrals.
		/// 
		/// </summary>
		/// <returns> the active referral url
		/// </returns>
		/// <summary> Sets the current referral active on this connection if created to
		/// follow referrals.
		/// </summary>
		internal ReferralInfo ActiveReferral
		{

			
			get
			{
				return activeReferral;
			}
			

			
			set
			{
				activeReferral = value;
				return ;
			}
			
		}
		
		/// <summary> Returns the name of this Connection, used for debug only
		/// 
		/// </summary>
		/// <returns> the name of this connection
		/// </returns>
		internal System.String ConnectionName
		{
			/*package*/
			
			get
			{
				return name;
			}
			
		}
		
		private System.Object writeSemaphore;
		private int writeSemaphoreOwner = 0;
		private int writeSemaphoreCount = 0;
		
		// We need a message number for disconnect to grab the semaphore,
		// but may not have one, so we invent a unique one.
		private int ephemeralId = - 1;
		private BindProperties bindProperties = null;
		private int bindSemaphoreId = 0; // 0 is never used by to lock a semaphore
		
		private Thread reader = null; // New thread that reads data from the server.
		private Thread deadReader = null; // Identity of last reader thread
		private System.IO.IOException deadReaderException = null; // Last exception of reader
		
		private LBEREncoder encoder;
		private LBERDecoder decoder;
		
		/*
		* socket is the current socket being used.
		* nonTLSBackup is the backup socket if startTLS is called.
		* if nonTLSBackup is null then startTLS has not been called,
		* or stopTLS has been called to end TLS protection
		*/
		private System.Net.Sockets.Socket sock = null;
		private System.Net.Sockets.TcpClient socket = null;
		private System.Net.Sockets.TcpClient nonTLSBackup = null;
		
		private System.IO.Stream in_Renamed = null;
		private System.IO.Stream out_Renamed = null;
		// When set to true the client connection is up and running
		private bool clientActive = true;
		
		private bool ssl = false;
		
		// Indicates we have received a server shutdown unsolicited notification
		private bool unsolSvrShutDnNotification = false;
		
		//  Ldap message IDs are all positive numbers so we can use negative
		//  numbers as flags.  This are flags assigned to stopReaderMessageID
		//  to tell the reader what state we are in.
		private const int CONTINUE_READING = - 99;
		private const int STOP_READING = - 98;
		
		//  Stops the reader thread when a Message with the passed-in ID is read.
		//  This parameter is set by stopReaderOnReply and stopTLS
		private int stopReaderMessageID;
		
		
		// Place to save message information classes
		private MessageVector messages;
		
		// Connection created to follow referral
		private ReferralInfo activeReferral = null;
		
		// Place to save unsolicited message listeners
		private System.Collections.ArrayList unsolicitedListeners;
		
		// The LdapSocketFactory to be used as the default to create new connections
//		private static LdapSocketFactory socketFactory = null;
		// The LdapSocketFactory used for this connection
//		private LdapSocketFactory mySocketFactory;
		private System.String host = null;
		private int port = 0;
		// Number of clones in addition to original LdapConnection using this
		// connection.
		private int cloneCount = 0;
		// Connection number & name used only for debug
		private System.String name = "";
		private static System.Object nameLock; // protect connNum
		private static int connNum = 0;
		
		// These attributes can be retreived using the getProperty
		// method in LdapConnection.  Future releases might require
		// these to be local variables that can be modified using
		// the setProperty method.
		/* package */
		internal static System.String sdk;
		/* package */
		internal static System.Int32 protocol;
		/* package */
		internal static System.String security = "simple";
		
		/// <summary> Create a new Connection object
		/// 
		/// </summary>
		/// <param name="factory">specifies the factory to use to produce SSL sockets.
		/// </param>
		/* package */
//		internal Connection(LdapSocketFactory factory)
		internal Connection()
		{
			InitBlock();
			return ;
		}
		
		/// <summary> Copy this Connection object.
		/// 
		/// This is not a true clone, but creates a new object encapsulating
		/// part of the connection information from the original object.
		/// The new object will have the same default socket factory,
		/// designated socket factory, host, port, and protocol version
		/// as the original object.
		/// The new object is NOT be connected to the host.
		/// 
		/// </summary>
		/// <returns> a shallow copy of this object
		/// </returns>
		/* package */
		internal System.Object copy()
		{
			Connection c = new Connection();
			c.host = this.host;
			c.port = this.port;
			Novell.Directory.Ldap.Connection.protocol = Connection.protocol;
			return c;
		}
		
		/// <summary> Acquire a simple counting semaphore that synchronizes state affecting
		/// bind. This method generates an ephemeral message id (negative number).
		/// 
		/// We bind using the message ID because a different thread may unlock
		/// the semaphore than the one that set it.  It is cleared when the
		/// response to the bind is processed, or when the bind operation times out.
		/// 
		/// Returns when the semaphore is acquired
		/// 
		/// </summary>
		/// <returns> the ephemeral message id that identifies semaphore's owner
		/// </returns>
		/* package */
		internal int acquireWriteSemaphore()
		{
			return acquireWriteSemaphore(0);
		}
		
		/// <summary> Acquire a simple counting semaphore that synchronizes state affecting
		/// bind. The semaphore is held by setting a value in writeSemaphoreOwner.
		/// 
		/// We bind using the message ID because a different thread may unlock
		/// the semaphore than the one that set it.  It is cleared when the
		/// response to the bind is processed, or when the bind operation times out.
		/// Returns when the semaphore is acquired.
		/// 
		/// </summary>
		/// <param name="msgId">a value that identifies the owner of this semaphore. A
		/// value of zero means assign a unique semaphore value.
		/// 
		/// </param>
		/// <returns> the semaphore value used to acquire the lock
		/// </returns>
		/* package */
		internal int acquireWriteSemaphore(int msgId)
		{
			int id = msgId;
			lock (writeSemaphore)
			{
				if (id == 0)
				{
					ephemeralId = ((ephemeralId == System.Int32.MinValue)?(ephemeralId = - 1):--ephemeralId);
					id = ephemeralId;
				}
				while (true)
				{
					if (writeSemaphoreOwner == 0)
					{
						// we have acquired the semahpore
						writeSemaphoreOwner = id;
						break;
					}
					else
					{
						if (writeSemaphoreOwner == id)
						{
							// we already own the semahpore
							break;
						}
						try
						{
							// Keep trying for the lock
							System.Threading.Monitor.Wait(writeSemaphore);
							continue;
						}
						catch (System.Threading.ThreadInterruptedException ex)
						{
							// Keep trying for the lock
							continue;
						}
					}
				}
				writeSemaphoreCount++;
			}
			return id;
		}
		
		/// <summary> Release a simple counting semaphore that synchronizes state affecting
		/// bind.  Frees the semaphore when number of acquires and frees for this
		/// thread match.
		/// 
		/// </summary>
		/// <param name="msgId">a value that identifies the owner of this semaphore
		/// </param>
		/* package */
		internal void  freeWriteSemaphore(int msgId)
		{
			lock (writeSemaphore)
			{
				if (writeSemaphoreOwner == 0)
				{
					throw new System.SystemException("Connection.freeWriteSemaphore(" + msgId + "): semaphore not owned by any thread");
				}
				else if (writeSemaphoreOwner != msgId)
				{
					throw new System.SystemException("Connection.freeWriteSemaphore(" + msgId + "): thread does not own the semaphore, owned by " + writeSemaphoreOwner);
				}
				// if all instances of this semaphore for this thread are released,
				// wake up all threads waiting.
				if (--writeSemaphoreCount == 0)
				{
					writeSemaphoreOwner = 0;
					System.Threading.Monitor.Pulse(writeSemaphore);
				}
			}
			return ;
		}
		
		/*
		* Wait until the reader thread ID matches the specified parameter.
		* Null = wait for the reader to terminate
		* Non Null = wait for the reader to start
		* Returns when the ID matches, i.e. reader stopped, or reader started.
		*
		* @param the thread id to match
		*/
		private void  waitForReader(Thread thread)
		{
			// wait for previous reader thread to terminate
			System.Threading.Thread rInst;
			System.Threading.Thread tInst;
			if(reader!=null)
			{
				rInst=reader;
			}
			else
			{
				rInst=null;
			}

			if(thread!=null)
			{
				tInst=thread;
			}
			else
			{
				tInst=null;
			}
//			while (reader != thread)
			while (!Object.Equals(rInst,tInst))
			{
				// Don't initialize connection while previous reader thread still
				// active.
				try
				{
					/*
					* The reader thread may start and immediately terminate.
					* To prevent the waitForReader from waiting forever
					* for the dead to rise, we leave traces of the deceased.
					* If the thread is already gone, we throw an exception.
					*/
					if (thread == deadReader)
					{
						if (thread == null)
						/* then we wanted a shutdown */
							return ;
						System.IO.IOException lex = deadReaderException;
						deadReaderException = null;
						deadReader = null;
						// Reader thread terminated
						throw new LdapException(ExceptionMessages.CONNECTION_READER, LdapException.CONNECT_ERROR, null, lex);
					}
					lock (this)
					{
						System.Threading.Monitor.Wait(this, TimeSpan.FromMilliseconds(5));
					}
				}
				catch (System.Threading.ThreadInterruptedException ex)
				{
					;
				}
				if(reader!=null)
				{
					rInst=reader;
				}
				else
				{
					rInst=null;
				}

				if(thread!=null)
				{
					tInst=thread;
				}
				else
				{
					tInst=null;
				}

			}
			deadReaderException = null;
			deadReader = null;
			return ;
		}
		
		/// <summary> Constructs a TCP/IP connection to a server specified in host and port.
		/// 
		/// </summary>
		/// <param name="host">The host to connect to.
		/// 
		/// </param>
		/// <param name="port">The port on the host to connect to.
		/// </param>
		/* package */
		internal void  connect(System.String host, int port)
		{
			connect(host, port, 0);
			return ;
		}


/****************************************************************************/
 public  bool ServerCertificateValidation(
                        Syscert.X509Certificate certificate,
                        int[]                   certificateErrors)
                {
			if (null != OnCertificateValidation)
			{
				return OnCertificateValidation(certificate, certificateErrors);
			}

			return DefaultCertificateValidationHandler(certificate, certificateErrors);
		}
	
		public bool DefaultCertificateValidationHandler(
			Syscert.X509Certificate certificate,
			int[]                   certificateErrors)
		{
			bool retFlag=false;

                        if (certificateErrors != null &&
                                certificateErrors.Length > 0)
                        {
				if( certificateErrors.Length==1 && certificateErrors[0] == -2146762481)
				{
						retFlag = true;
				}
				else
                                {
                                	Console.WriteLine("Detected errors in the Server Certificate:");
                                                                                                
       		                         for (int i = 0; i < certificateErrors.Length; i++)
               		                 {
						handshakeProblemsEncountered.Add((CertificateProblem)((uint)certificateErrors[i]));
                       		                 Console.WriteLine(certificateErrors[i]);
                                	 }
					retFlag = false;
				}
                        }
			else
			{
				retFlag = true;
			}

 
                        // Skip the server cert errors.
                        return retFlag;
                }


/***********************************************************************/	
		/// <summary> Constructs a TCP/IP connection to a server specified in host and port.
		/// Starts the reader thread.
		/// 
		/// </summary>
		/// <param name="host">The host to connect to.
		/// 
		/// </param>
		/// <param name="port">The port on the host to connect to.
		/// 
		/// </param>
		/// <param name="semaphoreId">The write semaphore ID to use for the connect
		/// </param>
		private void  connect(System.String host, int port, int semaphoreId)
		{
			/* Synchronized so all variables are in a consistant state and
			* so that another thread isn't doing a connect, disconnect, or clone
			* at the same time.
			*/
			// Wait for active reader to terminate
			waitForReader(null);
			
			// Clear the server shutdown notification flag.  This should already
			// be false unless of course we are reusing the same Connection object
			// after a server shutdown notification
			unsolSvrShutDnNotification = false;
			
			int semId = acquireWriteSemaphore(semaphoreId);
			
			// Make socket connection to specified host and port
			if (port == 0)
			{
				port = 389;//LdapConnection.DEFAULT_PORT;
			}
			
			try
			{
				if ((in_Renamed == null) || (out_Renamed == null))
				{
#if !TARGET_JVM
					if(Ssl)
					{
						this.host = host;
						this.port = port;
						this.sock = 	new Socket ( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
						IPAddress hostadd = Dns.Resolve(host).AddressList[0];
						IPEndPoint ephost = new IPEndPoint(hostadd,port);
						sock.Connect(ephost);
						NetworkStream nstream = new NetworkStream(sock,true);
						// Load Mono.Security.dll
						Assembly a;
						try
						{
							a = Assembly.LoadWithPartialName("Mono.Security");
						}
						catch(System.IO.FileNotFoundException)
						{
							throw new LdapException(ExceptionMessages.SSL_PROVIDER_MISSING, LdapException.SSL_PROVIDER_NOT_FOUND, null);
						}
						Type tSslClientStream = a.GetType("Mono.Security.Protocol.Tls.SslClientStream");
						BindingFlags flags = (BindingFlags.NonPublic  | BindingFlags.Public |
							BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);

						object[] consArgs = new object[4];
						consArgs[0] = nstream;
						consArgs[1] = host;
						consArgs[2] = false;
						Type tSecurityProtocolType = a.GetType("Mono.Security.Protocol.Tls.SecurityProtocolType");
						Enum objSPType = (Enum)(Activator.CreateInstance(tSecurityProtocolType));
						int nSsl3Val = (int) Enum.Parse(tSecurityProtocolType, "Ssl3");
						int nTlsVal = (int) Enum.Parse(tSecurityProtocolType, "Tls");
						consArgs[3] = Enum.ToObject(tSecurityProtocolType, nSsl3Val | nTlsVal);

						object objSslClientStream = 
							Activator.CreateInstance(tSslClientStream, consArgs);

						// Register ServerCertValidationDelegate handler
						PropertyInfo pi = tSslClientStream.GetProperty("ServerCertValidationDelegate");
						pi.SetValue(objSslClientStream, 
							Delegate.CreateDelegate(pi.PropertyType, this, "ServerCertificateValidation"),
							null);
						
						// Get the in and out streams
						in_Renamed = (System.IO.Stream) objSslClientStream;
						out_Renamed = (System.IO.Stream) objSslClientStream;
						/*
						SslClientStream sslstream = new SslClientStream(
											nstream,
											host,
											false,
											Mono.Security.Protocol.Tls.SecurityProtocolType.Ssl3|Mono.Security.Protocol.Tls.SecurityProtocolType.Tls);
						sslstream.ServerCertValidationDelegate += new CertificateValidationCallback(ServerCertificateValidation);*/
						//						byte[] buffer = new byte[0];
						//						sslstream.Read(buffer, 0, buffer.Length);
						//						sslstream.doHandshake();												
						/*
						in_Renamed = (System.IO.Stream) sslstream;
						out_Renamed = (System.IO.Stream) sslstream;*/
					}
					else{
#endif
						socket = new System.Net.Sockets.TcpClient(host, port);				
						in_Renamed = (System.IO.Stream) socket.GetStream();
						out_Renamed = (System.IO.Stream) socket.GetStream();
#if !TARGET_JVM
					}
#endif
				}
				else
				{
					Console.WriteLine( "connect input/out Stream specified");

				}
			}
			catch (System.IO.IOException ioe)
			{
				// Unable to connect to server host:port
//				freeWriteSemaphore(semId);
				throw new LdapException(ExceptionMessages.CONNECTION_ERROR, new System.Object[]{host, port}, LdapException.CONNECT_ERROR, null, ioe);
			}
			// Set host and port
			this.host = host;
			this.port = port;
			// start the reader thread
			this.startReader();
			freeWriteSemaphore(semId);
			clientActive = true; // Client is up
			return ;
		}
		
		/// <summary>  Increments the count of cloned connections</summary>
		/* package */
		internal void  incrCloneCount()
		{
			lock (this)
			{
				cloneCount++;
				return ;
			}
		}
		
		/// <summary> Destroys a clone of <code>LdapConnection</code>.
		/// 
		/// This method first determines if only one <code>LdapConnection</code>
		/// object is associated with this connection, i.e. if no clone exists.
		/// 
		/// If no clone exists, the socket is closed, and the current
		/// <code>Connection</code> object is returned.
		/// 
		/// If multiple <code>LdapConnection</code> objects are associated
		/// with this connection, i.e. clones exist, a {@link #copy} of the
		/// this object is made, but is not connected to any host. This
		/// disassociates that clone from the original connection.  The new
		/// <code>Connection</code> object is returned.
		/// 
		/// Only one destroyClone instance is allowed to run at any one time.
		/// 
		/// If the connection is closed, any threads waiting for operations
		/// on that connection will wake with an LdapException indicating
		/// the connection is closed.
		/// 
		/// </summary>
		/// <param name="apiCall"><code>true</code> indicates the application is closing the
		/// connection or or creating a new one by calling either the
		/// <code>connect</code> or <code>disconnect</code> methods
		/// of <code>LdapConnection</code>.  <code>false</code>
		/// indicates that <code>LdapConnection</code> is being finalized.
		/// 
		/// </param>
		/// <returns> a Connection object or null if finalizing.
		/// </returns>
		/* package */
		internal Connection destroyClone(bool apiCall)
		{
			lock (this)
			{
				Connection conn = this;

				if (cloneCount > 0)
				{
					cloneCount--;
					// This is a clone, set a new connection object.
					if (apiCall)
					{
						conn = (Connection) this.copy();
					}
					else
					{
						conn = null;
					}
				}
				else
				{
					if (in_Renamed != null)
					{
						// Not a clone and connected
						/*
						* Either the application has called disconnect or connect
						* resulting in the current connection being closed. If the
						* application has any queues waiting on messages, we
						* need wake these up so the application does not hang.
						* The boolean flag indicates whether the close came
						* from an API call or from the object being finalized.
						*/
						InterThreadException notify = new InterThreadException((apiCall?ExceptionMessages.CONNECTION_CLOSED:ExceptionMessages.CONNECTION_FINALIZED), null, LdapException.CONNECT_ERROR, null, null);
						// Destroy old connection
						shutdown("destroy clone", 0, notify);
					}
				}
				return conn;
			}
		}
		
		/// <summary> sets the default socket factory
		/// 
		/// </summary>
		/// <param name="factory">the default factory to set
		/// </param>
		/* package */
		/// <summary> gets the socket factory used for this connection
		/// 
		/// </summary>
		/// <returns> the default factory for this connection
		/// </returns>
		/* package */
		
		/// <summary> clears the writeSemaphore id used for active bind operation</summary>
		/* package */
		internal void  clearBindSemId()
		{
			bindSemaphoreId = 0;
			return ;
		}
		
		/// <summary> Writes an LdapMessage to the Ldap server over a socket.
		/// 
		/// </summary>
		/// <param name="info">the Message containing the message to write.
		/// </param>
		/* package */
		internal void  writeMessage(Message info)
		{
			messages.Add(info);
			// For bind requests, if not connected, attempt to reconnect
			if (info.BindRequest && (Connected == false) && ((System.Object) host != null))
			{
				connect(host, port, info.MessageID);
			}
			LdapMessage msg = info.Request;
			writeMessage(msg);
			return ;
		}
		
		
		/// <summary> Writes an LdapMessage to the Ldap server over a socket.
		/// 
		/// </summary>
		/// <param name="msg">the message to write.
		/// </param>
		/* package */
		internal void  writeMessage(LdapMessage msg)
		{
			int id;
			// Get the correct semaphore id for bind operations
			if (bindSemaphoreId == 0)
			{
				// Semaphore id for normal operations
				id = msg.MessageID;
			}
			else
			{
				// Semaphore id for sasl bind operations
				id = bindSemaphoreId;
			}
			System.IO.Stream myOut = out_Renamed;
			
			acquireWriteSemaphore(id);
			try
			{
				if (myOut == null)
				{
					throw new System.IO.IOException("Output stream not initialized");
				}
				if (!(myOut.CanWrite))
				{
					return;
				}
				sbyte[] ber = msg.Asn1Object.getEncoding(encoder);
				myOut.Write(SupportClass.ToByteArray(ber), 0, ber.Length);
				myOut.Flush();
			}
			catch (System.IO.IOException ioe)
			{
				if ((msg.Type == LdapMessage.BIND_REQUEST) &&
					(ssl))
				{
					string strMsg = "Following problem(s) occurred while establishing SSL based Connection : ";
					if (handshakeProblemsEncountered.Count > 0)
					{
						strMsg += GetProblemMessage((CertificateProblem)handshakeProblemsEncountered[0]); 
						for (int nProbIndex = 1; nProbIndex < handshakeProblemsEncountered.Count; nProbIndex++)
						{
							strMsg += ", " + GetProblemMessage((CertificateProblem)handshakeProblemsEncountered[nProbIndex]);
						} 
					}
					else
					{
						strMsg += "Unknown Certificate Problem";
					}
					throw new LdapException(strMsg, new System.Object[]{host, port}, LdapException.SSL_HANDSHAKE_FAILED, null, ioe);
				}				
				/*
				* IOException could be due to a server shutdown notification which
				* caused our Connection to quit.  If so we send back a slightly
				* different error message.  We could have checked this a little
				* earlier in the method but that would be an expensive check each
				* time we send out a message.  Since this shutdown request is
				* going to be an infrequent occurence we check for it only when
				* we get an IOException.  shutdown() will do the cleanup.
				*/
				if (clientActive)
				{
					// We beliefe the connection was alive
					if (unsolSvrShutDnNotification)
					{
						// got server shutdown
						throw new LdapException(ExceptionMessages.SERVER_SHUTDOWN_REQ, new System.Object[]{host, port}, LdapException.CONNECT_ERROR, null, ioe);
					}
					
					// Other I/O Exceptions on host:port are reported as is
					throw new LdapException(ExceptionMessages.IO_EXCEPTION, new System.Object[]{host, port}, LdapException.CONNECT_ERROR, null, ioe);
				}
			}
			finally
			{
				freeWriteSemaphore(id);
				handshakeProblemsEncountered.Clear();
			}
			return ;
		}
		
		/// <summary> Returns the message agent for this msg ID</summary>
		/* package */
		internal MessageAgent getMessageAgent(int msgId)
		{
			Message info = messages.findMessageById(msgId);
			return info.MessageAgent;
		}
		
		/// <summary> Removes a Message class from the Connection's list
		/// 
		/// </summary>
		/// <param name="info">the Message class to remove from the list
		/// </param>
		/* package */
		internal void  removeMessage(Message info)
		{
			bool done = SupportClass.VectorRemoveElement(messages, info);
			return ;
		}
		
		/// <summary> Cleans up resources associated with this connection.</summary>
		~Connection()
		{
			shutdown("Finalize", 0, null);
			return ;
		}
		/// <summary> Cleans up resources associated with this connection.
		/// This method may be called by finalize() for the connection, or it may
		/// be called by LdapConnection.disconnect().
		/// Should not have a writeSemaphore lock in place, as deadlock can occur
		/// while abandoning connections.
		/// </summary>
		private void  shutdown(System.String reason, int semaphoreId, InterThreadException notifyUser)
		{
			Message info = null;
			if (!clientActive)
			{
				return ;
			}
			clientActive = false;
			while (true)
			{
				// remove messages from connection list and send abandon
				try
				{
					System.Object temp_object;
					temp_object = messages[0];
					messages.RemoveAt(0);
					info = (Message) temp_object;
				}
				catch (ArgumentOutOfRangeException ex)
				{
					// No more messages
					break;
				}
				info.Abandon(null, notifyUser); // also notifies the application
			}
			
			int semId = acquireWriteSemaphore(semaphoreId);
			// Now send unbind if socket not closed
			if ((bindProperties != null) && (out_Renamed != null) && (out_Renamed.CanWrite) && (!bindProperties.Anonymous))
			{
				try
				{
					LdapMessage msg = new LdapUnbindRequest(null);
					sbyte[] ber = msg.Asn1Object.getEncoding(encoder);
					out_Renamed.Write(SupportClass.ToByteArray(ber), 0, ber.Length);
					out_Renamed.Flush();

				}
				catch (System.Exception ex)
				{
					; // don't worry about error
				}
			}
			bindProperties = null;
			
			if (socket != null)
			{
#if !TARGET_JVM
				// Just before closing the sockets, abort the reader thread
				reader.Abort();
#endif
				// Close the socket
				try
				{
					if(Ssl)
					{
						sock.Shutdown(SocketShutdown.Both);
						sock.Close();
					}
					else
					{
						if(in_Renamed != null)
							in_Renamed.Close();						
						socket.Close();
					}
				}
				catch (System.IO.IOException ie)
				{
					// ignore problem closing socket
				}
				socket = null;
				sock = null;
				in_Renamed=null;
				out_Renamed=null;
			}
			freeWriteSemaphore(semId);
			return ;
		}
		
		/// <summary> This tests to see if there are any outstanding messages.  If no messages
		/// are in the queue it returns true.  Each message will be tested to
		/// verify that it is complete.
		/// <I>The writeSemaphore must be set for this method to be reliable!</I>
		/// 
		/// </summary>
		/// <returns> true if no outstanding messages
		/// </returns>
		/* package */
		internal bool areMessagesComplete()
		{
			System.Object[] messages = this.messages.ObjectArray;
			int length = messages.Length;
			
			// Check if SASL bind in progress
			if (bindSemaphoreId != 0)
			{
				return false;
			}
			
			// Check if any messages queued
			if (length == 0)
			{
				return true;
			}
			
			for (int i = 0; i < length; i++)
			{
				if (((Message) messages[i]).Complete == false)
					return false;
			}
			return true;
		}
		
		/// <summary> The reader thread will stop when a reply is read with an ID equal
		/// to the messageID passed in to this method.  This is used by
		/// LdapConnection.StartTLS.
		/// </summary>
		/* package */
		internal void  stopReaderOnReply(int messageID)
		{
			
			this.stopReaderMessageID = messageID;
			return ;
		}
		
		/// <summary>startReader
		/// startReader should be called when socket and io streams have been
		/// set or changed.  In particular after client.Connection.startTLS()
		/// It assumes the reader thread is not running.
		/// </summary>
		/* package */
		internal void  startReader()
		{
			// Start Reader Thread
			Thread r = new Thread(new ThreadStart(new ReaderThread(this).Run));
			r.IsBackground = true; // If the last thread running, allow exit.
			r.Start();
			waitForReader(r);
			return ;
		}
		
		/// <summary> Indicates if the conenction is using TLS protection
		///
		/// Return true if using TLS protection
		/// </summary>
		internal bool TLS
		{
			get
			{
				return (this.nonTLSBackup != null);
			}
		}
		
		/// <summary> StartsTLS, in this package, assumes the caller has:
		/// 1) Acquired the writeSemaphore
		/// 2) Stopped the reader thread
		/// 3) checked that no messages are outstanding on this connection.
		/// 
		/// After calling this method upper layers should start the reader
		/// by calling startReader()
		/// 
		/// In the client.Connection, StartTLS assumes Ldap.LdapConnection will
		/// stop and start the reader thread.  Connection.StopTLS will stop
		/// and start the reader thread.
		/// </summary>
		/* package */
		internal void  startTLS()
		{
#if !TARGET_JVM			
			try
			{
				waitForReader(null);
				this.nonTLSBackup = this.socket;
/*				this.sock = 	new Socket ( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
				IPAddress hostadd = Dns.Resolve(host).AddressList[0];
				IPEndPoint ephost = new IPEndPoint(hostadd,port);
				sock.Connect(ephost);
*/
//				NetworkStream nstream = new NetworkStream(this.socket,true);
				// Load Mono.Security.dll
				Assembly a = Assembly.LoadFrom("Mono.Security.dll");
				Type tSslClientStream = a.GetType("Mono.Security.Protocol.Tls.SslClientStream");
				BindingFlags flags = (BindingFlags.NonPublic  | BindingFlags.Public |
					BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);

				object[] consArgs = new object[4];
				consArgs[0] = socket.GetStream();
				consArgs[1] = host;
				consArgs[2] = false;
				Type tSecurityProtocolType = a.GetType("Mono.Security.Protocol.Tls.SecurityProtocolType");
				Enum objSPType = (Enum)(Activator.CreateInstance(tSecurityProtocolType));
				int nSsl3Val = (int) Enum.Parse(tSecurityProtocolType, "Ssl3");
				int nTlsVal = (int) Enum.Parse(tSecurityProtocolType, "Tls");
				consArgs[3] = Enum.ToObject(tSecurityProtocolType, nSsl3Val | nTlsVal);

				object objSslClientStream = 
					Activator.CreateInstance(tSslClientStream, consArgs);

				// Register ServerCertValidationDelegate handler
				EventInfo ei = tSslClientStream.GetEvent("ServerCertValidationDelegate");
				ei.AddEventHandler(objSslClientStream, 
					Delegate.CreateDelegate(ei.EventHandlerType, this, "ServerCertificateValidation"));
						
				// Get the in and out streams
				in_Renamed = (System.IO.Stream) objSslClientStream;
				out_Renamed = (System.IO.Stream) objSslClientStream;

				/*
				SslClientStream sslstream = new SslClientStream(
									socket.GetStream(),
									nstream,
									host,
									false,
									Mono.Security.Protocol.Tls.SecurityProtocolType.Ssl3| Mono.Security.Protocol.Tls.SecurityProtocolType.Tls);
				sslstream.ServerCertValidationDelegate = new CertificateValidationCallback(ServerCertificateValidation);
				this.in_Renamed = (System.IO.Stream) sslstream;
				this.out_Renamed = (System.IO.Stream) sslstream;*/
			}
			catch (System.IO.IOException ioe)
			{
				this.nonTLSBackup = null;
				throw new LdapException("Could not negotiate a secure connection", LdapException.CONNECT_ERROR, null, ioe);
			}
			catch (System.Exception uhe)
			{
				this.nonTLSBackup = null;
				throw new LdapException("The host is unknown", LdapException.CONNECT_ERROR, null, uhe);
			}
			return ;
#endif
		}
		
		/*
		* Stops TLS.
		*
		* StopTLS, in this package, assumes the caller has:
		*  1) blocked writing (acquireWriteSemaphore).
		*  2) checked that no messages are outstanding.
		*
		*  StopTLS Needs to do the following:
		*  1) close the current socket
		*      - This stops the reader thread
		*      - set STOP_READING flag on stopReaderMessageID so that
		*        the reader knows that the IOException is planned.
		*  2) replace the current socket with nonTLSBackup,
		*  3) and set nonTLSBackup to null;
		*  4) reset input and outputstreams
		*  5) start the reader thread by calling startReader
		*
		*  Note: Sun's JSSE doesn't allow the nonTLSBackup socket to be
		* used any more, even though autoclose was false: you get an IOException.
		* IBM's JSSE hangs when you close the JSSE socket.
		*/
		/* package */
		internal void  stopTLS()
		{
			try
			{
				this.stopReaderMessageID = Connection.STOP_READING;
				this.out_Renamed.Close();
				this.in_Renamed.Close();
//				this.sock.Shutdown(SocketShutdown.Both);
//				this.sock.Close();
				waitForReader(null);
				this.socket = this.nonTLSBackup;
				this.in_Renamed = (System.IO.Stream) this.socket.GetStream();
				this.out_Renamed = (System.IO.Stream) this.socket.GetStream();
				// Allow the new reader to start
				this.stopReaderMessageID = Connection.CONTINUE_READING;
			}
			catch (System.IO.IOException ioe)
			{
				throw new LdapException(ExceptionMessages.STOPTLS_ERROR, LdapException.CONNECT_ERROR, null, ioe);
			}
			finally
			{
				this.nonTLSBackup = null;
				startReader();
			}
			return ;
		}
///TLS not supported in first release		

		internal Stream InputStream
		{
			get { return in_Renamed; }
		}

		internal Stream OutputStream
		{
			get { return out_Renamed; }
		}

		internal void ReplaceStreams(Stream newIn, Stream newOut)
		{
			// wait for reader to stop, see LdapConnection.Bind
			waitForReader(null);
			in_Renamed = newIn;
			out_Renamed = newOut;
			startReader();
		}

		public class ReaderThread
		{
			private void  InitBlock(Connection enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private Connection enclosingInstance;
			public Connection Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			public ReaderThread(Connection enclosingInstance)
			{
				InitBlock(enclosingInstance);
				return ;
			}
			
			/// <summary> This thread decodes and processes RfcLdapMessage's from the server.
			/// 
			/// Note: This thread needs a graceful shutdown implementation.
			/// </summary>
			public virtual void  Run()
			{
				
				System.String reason = "reader: thread stopping";
				InterThreadException notify = null;
				Message info = null;
				System.IO.IOException ioex = null;
				this.enclosingInstance.reader = System.Threading.Thread.CurrentThread;				
//				Enclosing_Instance.reader = SupportClass.ThreadClass.Current();
//				Console.WriteLine("Inside run:" + this.enclosingInstance.reader.Name);
				try
				{
					for (; ; )
					{
						// -------------------------------------------------------
						// Decode an RfcLdapMessage directly from the socket.
						// -------------------------------------------------------
						Asn1Identifier asn1ID;
						System.IO.Stream myIn;
						/* get current value of in, keep value consistant
						* though the loop, i.e. even during shutdown
						*/
						myIn = this.enclosingInstance.in_Renamed;
						if (myIn == null)
						{
							break;
						}
						asn1ID = new Asn1Identifier(myIn);
						int tag = asn1ID.Tag;
						if (asn1ID.Tag != Asn1Sequence.TAG)
						{
							continue; // loop looking for an RfcLdapMessage identifier
						}
						
						// Turn the message into an RfcMessage class
						Asn1Length asn1Len = new Asn1Length(myIn);
						
						RfcLdapMessage msg = new RfcLdapMessage(this.enclosingInstance.decoder, myIn, asn1Len.Length);
						
						// ------------------------------------------------------------
						// Process the decoded RfcLdapMessage.
						// ------------------------------------------------------------
						int msgId = msg.MessageID;
						
						// Find the message which requested this response.
						// It is possible to receive a response for a request which
						// has been abandoned. If abandoned, throw it away
						try
						{
							info = this.enclosingInstance.messages.findMessageById(msgId);
							info.putReply(msg); // queue & wake up waiting thread
						}
						catch (System.FieldAccessException ex)
						{
							
							/*
							* We get the NoSuchFieldException when we could not find
							* a matching message id.  First check to see if this is
							* an unsolicited notification (msgID == 0). If it is not
							* we throw it away. If it is we call any unsolicited
							* listeners that might have been registered to listen for these
							* messages.
							*/
							
							
							/* Note the location of this code.  We could have required
							* that message ID 0 be just like other message ID's but
							* since message ID 0 has to be treated specially we have
							* a separate check for message ID 0.  Also note that
							* this test is after the regular message list has been
							* checked for.  We could have always checked the list
							* of messages after checking if this is an unsolicited
							* notification but that would have inefficient as
							* message ID 0 is a rare event (as of this time).
							*/
							if (msgId == 0)
							{
								
								
								// Notify any listeners that might have been registered
								this.enclosingInstance.notifyAllUnsolicitedListeners(msg);
								
								/*
								* Was this a server shutdown unsolicited notification.
								* IF so we quit. Actually calling the return will
								* first transfer control to the finally clause which
								* will do the necessary clean up.
								*/
								if (this.enclosingInstance.unsolSvrShutDnNotification)
								{
									notify = new InterThreadException(ExceptionMessages.SERVER_SHUTDOWN_REQ, new System.Object[]{this.enclosingInstance.host, this.enclosingInstance.port}, LdapException.CONNECT_ERROR, null, null);
									
									return ;
								}
							}
							else
							{
								
							}
						}
						if ((this.enclosingInstance.stopReaderMessageID == msgId) || (this.enclosingInstance.stopReaderMessageID == Novell.Directory.Ldap.Connection.STOP_READING))
						{
							// Stop the reader Thread.
							return ;
						}
					}
				}
				catch(ThreadAbortException tae)
				{
					// Abort has been called on reader
					// before closing sockets, from shutdown
					return;
				}
#if TARGET_JVM
				catch (ObjectDisposedException)
				{
					// we do not support Thread.Abort under java
					// so we close the stream and the working thread
					// catches ObjectDisposedException exception
					return;
				}
#endif
				catch (System.IO.IOException ioe)
				{
					
					ioex = ioe;
					if ((this.enclosingInstance.stopReaderMessageID != Novell.Directory.Ldap.Connection.STOP_READING) && this.enclosingInstance.clientActive)
					{
						// Connection lost waiting for results from host:port
						notify = new InterThreadException(ExceptionMessages.CONNECTION_WAIT, new System.Object[]{this.enclosingInstance.host, this.enclosingInstance.port}, LdapException.CONNECT_ERROR, ioe, info);
					}
					// The connection is no good, don't use it any more
					this.enclosingInstance.in_Renamed = null;
					this.enclosingInstance.out_Renamed = null;
				}
				finally
				{
					/*
					* There can be four states that the reader can be in at this point:
					*  1) We are starting TLS and will be restarting the reader
					*     after we have negotiated TLS.
					*      - Indicated by whether stopReaderMessageID does not
					*        equal CONTINUE_READING.
					*      - Don't call Shutdown.
					*  2) We are stoping TLS and will be restarting after TLS is
					*     stopped.
					*      - Indicated by an IOException AND stopReaderMessageID equals
					*        STOP_READING - in which case notify will be null.
					*      - Don't call Shutdown
					*  3) We receive a Server Shutdown notification.
					*      - Indicated by messageID equal to 0.
					*      - call Shutdown.
					*  4) Another error occured
					*      - Indicated by an IOException AND notify is not NULL
					*      - call Shutdown.
					*/
					if ((!this.enclosingInstance.clientActive) || (notify != null))
					{
						//#3 & 4
						this.enclosingInstance.shutdown(reason, 0, notify);
					}
					else
					{
						this.enclosingInstance.stopReaderMessageID = Novell.Directory.Ldap.Connection.CONTINUE_READING;
					}
				}
				this.enclosingInstance.deadReaderException = ioex;
				this.enclosingInstance.deadReader = this.enclosingInstance.reader;
				this.enclosingInstance.reader = null;
				return ;
			}
		} // End class ReaderThread
		
		/// <summary>Add the specific object to the list of listeners that want to be
		/// notified when an unsolicited notification is received.
		/// </summary>
		/* package */
		internal void  AddUnsolicitedNotificationListener(LdapUnsolicitedNotificationListener listener)
		{
			unsolicitedListeners.Add(listener);
			return ;
		}
		
		/// <summary>Remove the specific object from current list of listeners</summary>
		/* package */
		internal void  RemoveUnsolicitedNotificationListener(LdapUnsolicitedNotificationListener listener)
		{
			SupportClass.VectorRemoveElement(unsolicitedListeners, listener);
			return ;
		}
		
		/// <summary>Inner class defined so that we can spawn off each unsolicited
		/// listener as a seperate thread.  We did not want to call the
		/// unsolicited listener method directly as this would have tied up our
		/// deamon listener thread in the applications unsolicited listener method.
		/// Since we do not know what the application unsolicited listener
		/// might be doing and how long it will take to process the uncoslicited
		/// notification.  We use this class to spawn off the unsolicited
		/// notification as a separate thread
		/// </summary>
		private class UnsolicitedListenerThread:SupportClass.ThreadClass
		{
			private void  InitBlock(Connection enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private Connection enclosingInstance;
			public Connection Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			private LdapUnsolicitedNotificationListener listenerObj;
			private LdapExtendedResponse unsolicitedMsg;
			
			/* package */
			internal UnsolicitedListenerThread(Connection enclosingInstance, LdapUnsolicitedNotificationListener l, LdapExtendedResponse m)
			{
				InitBlock(enclosingInstance);
				this.listenerObj = l;
				this.unsolicitedMsg = m;
				return ;
			}
			
			public override void  Run()
			{
				listenerObj.messageReceived(unsolicitedMsg);
				return ;
			}
		}
		
		private void  notifyAllUnsolicitedListeners(RfcLdapMessage message)
		{
			
			
			// MISSING:  If this is a shutdown notification from the server
			// set a flag in the Connection class so that we can throw an
			// appropriate LdapException to the application
			LdapMessage extendedLdapMessage = new LdapExtendedResponse(message);
			System.String notificationOID = ((LdapExtendedResponse) extendedLdapMessage).ID;
			if (notificationOID.Equals(LdapConnection.SERVER_SHUTDOWN_OID))
			{
				
				
				unsolSvrShutDnNotification = true;
			}
			
			int numOfListeners = unsolicitedListeners.Count;
			
			// Cycle through all the listeners
			for (int i = 0; i < numOfListeners; i++)
			{
				
				// Get next listener
				LdapUnsolicitedNotificationListener listener = (LdapUnsolicitedNotificationListener) unsolicitedListeners[i];
				
				
				// Create a new ExtendedResponse each time as we do not want each listener
				// to have its own copy of the message
				LdapExtendedResponse tempLdapMessage = new LdapExtendedResponse(message);
				
				// Spawn a new thread for each listener to go process the message
				// The reason we create a new thread rather than just call the
				// the messageReceived method directly is beacuse we do not know
				// what kind of processing the notification listener class will
				// do.  We do not want our deamon thread to block waiting for
				// the notification listener method to return.
				UnsolicitedListenerThread u = new UnsolicitedListenerThread(this, listener, tempLdapMessage);
				u.Start();
			}
			
			
			return ;
		}
		static Connection()
		{
			nameLock = new System.Object();
			sdk = new System.Text.StringBuilder("2.1.4").ToString();
			protocol = 3;
		}
	}
}