Merge pull request #1304 from slluis/mac-proxy-autoconfig
[mono.git] / mcs / class / Novell.Directory.Ldap / Novell.Directory.Ldap / Connection.cs
1 /******************************************************************************
2 * The MIT License
3 * Copyright (c) 2003 Novell Inc.  www.novell.com
4
5 * Permission is hereby granted, free of charge, to any person obtaining  a copy
6 * of this software and associated documentation files (the Software), to deal
7 * in the Software without restriction, including  without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
9 * copies of the Software, and to  permit persons to whom the Software is 
10 * furnished to do so, subject to the following conditions:
11
12 * The above copyright notice and this permission notice shall be included in 
13 * all copies or substantial portions of the Software.
14
15 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *******************************************************************************/
23 //
24 // Novell.Directory.Ldap.Connection.cs
25 //
26 // Author:
27 //   Sunil Kumar (Sunilk@novell.com)
28 //
29 // (C) 2003 Novell, Inc (http://www.novell.com)
30 //
31
32 using System;
33 using System.Threading;
34 using Novell.Directory.Ldap.Asn1;
35 using Novell.Directory.Ldap.Rfc2251;
36 using Novell.Directory.Ldap.Utilclass;
37 using Mono.Security.Protocol.Tls;
38 using Mono.Security.X509.Extensions;
39 using Syscert = System.Security.Cryptography.X509Certificates;
40 using System.Security.Cryptography;
41 using System.Net;
42 using System.Net.Sockets;
43 using System.Collections;
44 using System.IO;
45 using System.Text;
46 using Mono.Security.X509;
47 using System.Text.RegularExpressions;
48 using System.Globalization;
49 using System.Reflection;
50
51 namespace Novell.Directory.Ldap
52 {
53         public delegate bool CertificateValidationCallback(
54                 Syscert.X509Certificate certificate,
55                 int[] certificateErrors);
56         
57         /// <summary> The class that creates a connection to the Ldap server. After the
58         /// connection is made, a thread is created that reads data from the
59         /// connection.
60         /// 
61         /// The application's thread sends a request to the MessageAgent class, which
62         /// creates a Message class.  The Message class calls the writeMessage method
63         /// of this class to send the request to the server. The application thread
64         /// will then query the MessageAgent class for a response.
65         /// 
66         /// The reader thread multiplexes response messages received from the
67         /// server to the appropriate Message class. Each Message class
68         /// has its own message queue.
69         /// 
70         /// Unsolicited messages are process separately, and if the application
71         /// has registered a handler, a separate thread is created for that
72         /// application's handler to process the message.
73         /// 
74         /// Note: the reader thread must not be a "selfish" thread, since some
75         /// operating systems do not time slice.
76         /// 
77         /// </summary>
78         /*package*/
79         sealed class Connection
80         {
81                 public event CertificateValidationCallback OnCertificateValidation;
82                 public  enum    CertificateProblem  : long
83                 {
84                         CertEXPIRED                   = 0x800B0101,
85                         CertVALIDITYPERIODNESTING     = 0x800B0102,
86                         CertROLE                      = 0x800B0103,
87                         CertPATHLENCONST              = 0x800B0104,
88                         CertCRITICAL                  = 0x800B0105,
89                         CertPURPOSE                   = 0x800B0106,
90                         CertISSUERCHAINING            = 0x800B0107,
91                         CertMALFORMED                 = 0x800B0108,
92                         CertUNTRUSTEDROOT             = 0x800B0109,
93                         CertCHAINING                  = 0x800B010A,
94                         CertREVOKED                   = 0x800B010C,
95                         CertUNTRUSTEDTESTROOT         = 0x800B010D,
96                         CertREVOCATION_FAILURE        = 0x800B010E,
97                         CertCN_NO_MATCH               = 0x800B010F,
98                         CertWRONG_USAGE               = 0x800B0110,
99                         CertUNTRUSTEDCA               = 0x800B0112
100                 }
101                 private static String GetProblemMessage(CertificateProblem Problem)
102                 {
103                         String ProblemMessage = "";
104                         String ProblemCodeName = CertificateProblem.GetName(typeof(CertificateProblem), Problem);
105                         if(ProblemCodeName != null)
106                                 ProblemMessage = ProblemMessage + ProblemCodeName;
107                         else
108                                 ProblemMessage = "Unknown Certificate Problem";
109                         return ProblemMessage;
110                 }
111  
112                 private ArrayList handshakeProblemsEncountered = new ArrayList();
113                 private void  InitBlock()
114                 {
115                         writeSemaphore = new System.Object();
116                         encoder = new LBEREncoder();
117                         decoder = new LBERDecoder();
118                         stopReaderMessageID = CONTINUE_READING;
119                         messages = new MessageVector(5, 5);
120                         unsolicitedListeners = new System.Collections.ArrayList(3);
121                 }
122                 /// <summary>  Indicates whether clones exist for LdapConnection
123                 /// 
124                 /// </summary>
125                 /// <returns> true if clones exist, false otherwise.
126                 /// </returns>
127                 internal bool Cloned
128                 {
129                         /* package */
130                         
131                         get
132                         {
133                                 return (cloneCount > 0);
134                         }
135                         
136                 }
137         
138
139
140                 internal bool Ssl
141                 {
142                         get
143                         {
144                                 return ssl;
145                         }
146                         set
147                         {
148                                 ssl=value;
149                         }
150                 }
151                 /// <summary> gets the host used for this connection</summary>
152                 internal System.String Host
153                 {
154                         /* package */
155                         
156                         get
157                         {
158                                 return host;
159                         }
160                         
161                 }
162                 /// <summary> gets the port used for this connection</summary>
163                 internal int Port
164                 {
165                         /* package */
166                         
167                         get
168                         {
169                                 return port;
170                         }
171                         
172                 }
173                 /// <summary> gets the writeSemaphore id used for active bind operation</summary>
174                 /// <summary> sets the writeSemaphore id used for active bind operation</summary>
175                 internal int BindSemId
176                 {
177                         /* package */
178                         
179                         get
180                         {
181                                 return bindSemaphoreId;
182                         }
183                         
184                         /* package */
185                         
186                         set
187                         {
188                                 bindSemaphoreId = value;
189                                 return ;
190                         }
191                         
192                 }
193                 /// <summary> checks if the writeSemaphore id used for active bind operation is clear</summary>
194                 internal bool BindSemIdClear
195                 {
196                         /* package */
197                         
198                         get
199                         {
200                                 if (bindSemaphoreId == 0)
201                                 {
202                                         return true;
203                                 }
204                                 return false;
205                         }
206                         
207                 }
208                 /// <summary> Return whether the application is bound to this connection.
209                 /// Note: an anonymous bind returns false - not bound
210                 /// </summary>
211                 internal bool Bound
212                 {
213                         /* package */
214                         
215                         get
216                         {
217                                 if (bindProperties != null)
218                                 {
219                                         // Bound if not anonymous
220                                         return (!bindProperties.Anonymous);
221                                 }
222                                 return false;
223                         }
224                         
225                 }
226                 /// <summary> Return whether a connection has been made</summary>
227                 internal bool Connected
228                 {
229                         /* package */
230                         
231                         get
232                         {
233                                 return (in_Renamed != null);
234                         }
235                         
236                 }
237                 /// <summary> 
238                 /// Sets the authentication credentials in the object
239                 /// and set flag indicating successful bind.
240                 /// 
241                 /// 
242                 /// 
243                 /// </summary>
244                 /// <returns>  The BindProperties object for this connection.
245                 /// </returns>
246                 /// <summary> 
247                 /// Sets the authentication credentials in the object
248                 /// and set flag indicating successful bind.
249                 /// 
250                 /// 
251                 /// 
252                 /// </summary>
253                 /// <param name="bindProps">  The BindProperties object to set.
254                 /// </param>
255                 internal BindProperties BindProperties
256                 {
257                         /* package */
258                         
259                         get
260                         {
261                                 return bindProperties;
262                         }
263                         
264                         /* package */
265                         
266                         set
267                         {
268                                 bindProperties = value;
269                                 return ;
270                         }
271                         
272                 }
273                 /// <summary> Gets the current referral active on this connection if created to
274                 /// follow referrals.
275                 /// 
276                 /// </summary>
277                 /// <returns> the active referral url
278                 /// </returns>
279                 /// <summary> Sets the current referral active on this connection if created to
280                 /// follow referrals.
281                 /// </summary>
282                 internal ReferralInfo ActiveReferral
283                 {
284
285                         
286                         get
287                         {
288                                 return activeReferral;
289                         }
290                         
291
292                         
293                         set
294                         {
295                                 activeReferral = value;
296                                 return ;
297                         }
298                         
299                 }
300                 
301                 /// <summary> Returns the name of this Connection, used for debug only
302                 /// 
303                 /// </summary>
304                 /// <returns> the name of this connection
305                 /// </returns>
306                 internal System.String ConnectionName
307                 {
308                         /*package*/
309                         
310                         get
311                         {
312                                 return name;
313                         }
314                         
315                 }
316                 
317                 private System.Object writeSemaphore;
318                 private int writeSemaphoreOwner = 0;
319                 private int writeSemaphoreCount = 0;
320                 
321                 // We need a message number for disconnect to grab the semaphore,
322                 // but may not have one, so we invent a unique one.
323                 private int ephemeralId = - 1;
324                 private BindProperties bindProperties = null;
325                 private int bindSemaphoreId = 0; // 0 is never used by to lock a semaphore
326                 
327                 private Thread reader = null; // New thread that reads data from the server.
328                 private Thread deadReader = null; // Identity of last reader thread
329                 private System.IO.IOException deadReaderException = null; // Last exception of reader
330                 
331                 private LBEREncoder encoder;
332                 private LBERDecoder decoder;
333                 
334                 /*
335                 * socket is the current socket being used.
336                 * nonTLSBackup is the backup socket if startTLS is called.
337                 * if nonTLSBackup is null then startTLS has not been called,
338                 * or stopTLS has been called to end TLS protection
339                 */
340                 private System.Net.Sockets.Socket sock = null;
341                 private System.Net.Sockets.TcpClient socket = null;
342                 private System.Net.Sockets.TcpClient nonTLSBackup = null;
343                 
344                 private System.IO.Stream in_Renamed = null;
345                 private System.IO.Stream out_Renamed = null;
346                 // When set to true the client connection is up and running
347                 private bool clientActive = true;
348                 
349                 private bool ssl = false;
350                 
351                 // Indicates we have received a server shutdown unsolicited notification
352                 private bool unsolSvrShutDnNotification = false;
353                 
354                 //  Ldap message IDs are all positive numbers so we can use negative
355                 //  numbers as flags.  This are flags assigned to stopReaderMessageID
356                 //  to tell the reader what state we are in.
357                 private const int CONTINUE_READING = - 99;
358                 private const int STOP_READING = - 98;
359                 
360                 //  Stops the reader thread when a Message with the passed-in ID is read.
361                 //  This parameter is set by stopReaderOnReply and stopTLS
362                 private int stopReaderMessageID;
363                 
364                 
365                 // Place to save message information classes
366                 private MessageVector messages;
367                 
368                 // Connection created to follow referral
369                 private ReferralInfo activeReferral = null;
370                 
371                 // Place to save unsolicited message listeners
372                 private System.Collections.ArrayList unsolicitedListeners;
373                 
374                 // The LdapSocketFactory to be used as the default to create new connections
375 //              private static LdapSocketFactory socketFactory = null;
376                 // The LdapSocketFactory used for this connection
377 //              private LdapSocketFactory mySocketFactory;
378                 private System.String host = null;
379                 private int port = 0;
380                 // Number of clones in addition to original LdapConnection using this
381                 // connection.
382                 private int cloneCount = 0;
383                 // Connection number & name used only for debug
384                 private System.String name = "";
385                 private static System.Object nameLock; // protect connNum
386                 private static int connNum = 0;
387                 
388                 // These attributes can be retreived using the getProperty
389                 // method in LdapConnection.  Future releases might require
390                 // these to be local variables that can be modified using
391                 // the setProperty method.
392                 /* package */
393                 internal static System.String sdk;
394                 /* package */
395                 internal static System.Int32 protocol;
396                 /* package */
397                 internal static System.String security = "simple";
398                 
399                 /// <summary> Create a new Connection object
400                 /// 
401                 /// </summary>
402                 /// <param name="factory">specifies the factory to use to produce SSL sockets.
403                 /// </param>
404                 /* package */
405 //              internal Connection(LdapSocketFactory factory)
406                 internal Connection()
407                 {
408                         InitBlock();
409                         return ;
410                 }
411                 
412                 /// <summary> Copy this Connection object.
413                 /// 
414                 /// This is not a true clone, but creates a new object encapsulating
415                 /// part of the connection information from the original object.
416                 /// The new object will have the same default socket factory,
417                 /// designated socket factory, host, port, and protocol version
418                 /// as the original object.
419                 /// The new object is NOT be connected to the host.
420                 /// 
421                 /// </summary>
422                 /// <returns> a shallow copy of this object
423                 /// </returns>
424                 /* package */
425                 internal System.Object copy()
426                 {
427                         Connection c = new Connection();
428                         c.host = this.host;
429                         c.port = this.port;
430                         Novell.Directory.Ldap.Connection.protocol = Connection.protocol;
431                         return c;
432                 }
433                 
434                 /// <summary> Acquire a simple counting semaphore that synchronizes state affecting
435                 /// bind. This method generates an ephemeral message id (negative number).
436                 /// 
437                 /// We bind using the message ID because a different thread may unlock
438                 /// the semaphore than the one that set it.  It is cleared when the
439                 /// response to the bind is processed, or when the bind operation times out.
440                 /// 
441                 /// Returns when the semaphore is acquired
442                 /// 
443                 /// </summary>
444                 /// <returns> the ephemeral message id that identifies semaphore's owner
445                 /// </returns>
446                 /* package */
447                 internal int acquireWriteSemaphore()
448                 {
449                         return acquireWriteSemaphore(0);
450                 }
451                 
452                 /// <summary> Acquire a simple counting semaphore that synchronizes state affecting
453                 /// bind. The semaphore is held by setting a value in writeSemaphoreOwner.
454                 /// 
455                 /// We bind using the message ID because a different thread may unlock
456                 /// the semaphore than the one that set it.  It is cleared when the
457                 /// response to the bind is processed, or when the bind operation times out.
458                 /// Returns when the semaphore is acquired.
459                 /// 
460                 /// </summary>
461                 /// <param name="msgId">a value that identifies the owner of this semaphore. A
462                 /// value of zero means assign a unique semaphore value.
463                 /// 
464                 /// </param>
465                 /// <returns> the semaphore value used to acquire the lock
466                 /// </returns>
467                 /* package */
468                 internal int acquireWriteSemaphore(int msgId)
469                 {
470                         int id = msgId;
471                         lock (writeSemaphore)
472                         {
473                                 if (id == 0)
474                                 {
475                                         ephemeralId = ((ephemeralId == System.Int32.MinValue)?(ephemeralId = - 1):--ephemeralId);
476                                         id = ephemeralId;
477                                 }
478                                 while (true)
479                                 {
480                                         if (writeSemaphoreOwner == 0)
481                                         {
482                                                 // we have acquired the semahpore
483                                                 writeSemaphoreOwner = id;
484                                                 break;
485                                         }
486                                         else
487                                         {
488                                                 if (writeSemaphoreOwner == id)
489                                                 {
490                                                         // we already own the semahpore
491                                                         break;
492                                                 }
493                                                 try
494                                                 {
495                                                         // Keep trying for the lock
496                                                         System.Threading.Monitor.Wait(writeSemaphore);
497                                                         continue;
498                                                 }
499                                                 catch (System.Threading.ThreadInterruptedException ex)
500                                                 {
501                                                         // Keep trying for the lock
502                                                         continue;
503                                                 }
504                                         }
505                                 }
506                                 writeSemaphoreCount++;
507                         }
508                         return id;
509                 }
510                 
511                 /// <summary> Release a simple counting semaphore that synchronizes state affecting
512                 /// bind.  Frees the semaphore when number of acquires and frees for this
513                 /// thread match.
514                 /// 
515                 /// </summary>
516                 /// <param name="msgId">a value that identifies the owner of this semaphore
517                 /// </param>
518                 /* package */
519                 internal void  freeWriteSemaphore(int msgId)
520                 {
521                         lock (writeSemaphore)
522                         {
523                                 if (writeSemaphoreOwner == 0)
524                                 {
525                                         throw new System.SystemException("Connection.freeWriteSemaphore(" + msgId + "): semaphore not owned by any thread");
526                                 }
527                                 else if (writeSemaphoreOwner != msgId)
528                                 {
529                                         throw new System.SystemException("Connection.freeWriteSemaphore(" + msgId + "): thread does not own the semaphore, owned by " + writeSemaphoreOwner);
530                                 }
531                                 // if all instances of this semaphore for this thread are released,
532                                 // wake up all threads waiting.
533                                 if (--writeSemaphoreCount == 0)
534                                 {
535                                         writeSemaphoreOwner = 0;
536                                         System.Threading.Monitor.Pulse(writeSemaphore);
537                                 }
538                         }
539                         return ;
540                 }
541                 
542                 /*
543                 * Wait until the reader thread ID matches the specified parameter.
544                 * Null = wait for the reader to terminate
545                 * Non Null = wait for the reader to start
546                 * Returns when the ID matches, i.e. reader stopped, or reader started.
547                 *
548                 * @param the thread id to match
549                 */
550                 private void  waitForReader(Thread thread)
551                 {
552                         // wait for previous reader thread to terminate
553                         System.Threading.Thread rInst;
554                         System.Threading.Thread tInst;
555                         if(reader!=null)
556                         {
557                                 rInst=reader;
558                         }
559                         else
560                         {
561                                 rInst=null;
562                         }
563
564                         if(thread!=null)
565                         {
566                                 tInst=thread;
567                         }
568                         else
569                         {
570                                 tInst=null;
571                         }
572 //                      while (reader != thread)
573                         while (!Object.Equals(rInst,tInst))
574                         {
575                                 // Don't initialize connection while previous reader thread still
576                                 // active.
577                                 try
578                                 {
579                                         /*
580                                         * The reader thread may start and immediately terminate.
581                                         * To prevent the waitForReader from waiting forever
582                                         * for the dead to rise, we leave traces of the deceased.
583                                         * If the thread is already gone, we throw an exception.
584                                         */
585                                         if (thread == deadReader)
586                                         {
587                                                 if (thread == null)
588                                                 /* then we wanted a shutdown */
589                                                         return ;
590                                                 System.IO.IOException lex = deadReaderException;
591                                                 deadReaderException = null;
592                                                 deadReader = null;
593                                                 // Reader thread terminated
594                                                 throw new LdapException(ExceptionMessages.CONNECTION_READER, LdapException.CONNECT_ERROR, null, lex);
595                                         }
596                                         lock (this)
597                                         {
598                                                 System.Threading.Monitor.Wait(this, TimeSpan.FromMilliseconds(5));
599                                         }
600                                 }
601                                 catch (System.Threading.ThreadInterruptedException ex)
602                                 {
603                                         ;
604                                 }
605                                 if(reader!=null)
606                                 {
607                                         rInst=reader;
608                                 }
609                                 else
610                                 {
611                                         rInst=null;
612                                 }
613
614                                 if(thread!=null)
615                                 {
616                                         tInst=thread;
617                                 }
618                                 else
619                                 {
620                                         tInst=null;
621                                 }
622
623                         }
624                         deadReaderException = null;
625                         deadReader = null;
626                         return ;
627                 }
628                 
629                 /// <summary> Constructs a TCP/IP connection to a server specified in host and port.
630                 /// 
631                 /// </summary>
632                 /// <param name="host">The host to connect to.
633                 /// 
634                 /// </param>
635                 /// <param name="port">The port on the host to connect to.
636                 /// </param>
637                 /* package */
638                 internal void  connect(System.String host, int port)
639                 {
640                         connect(host, port, 0);
641                         return ;
642                 }
643
644
645 /****************************************************************************/
646  public  bool ServerCertificateValidation(
647                         Syscert.X509Certificate certificate,
648                         int[]                   certificateErrors)
649                 {
650                         if (null != OnCertificateValidation)
651                         {
652                                 return OnCertificateValidation(certificate, certificateErrors);
653                         }
654
655                         return DefaultCertificateValidationHandler(certificate, certificateErrors);
656                 }
657         
658                 public bool DefaultCertificateValidationHandler(
659                         Syscert.X509Certificate certificate,
660                         int[]                   certificateErrors)
661                 {
662                         bool retFlag=false;
663
664                         if (certificateErrors != null &&
665                                 certificateErrors.Length > 0)
666                         {
667                                 if( certificateErrors.Length==1 && certificateErrors[0] == -2146762481)
668                                 {
669                                                 retFlag = true;
670                                 }
671                                 else
672                                 {
673                                         Console.WriteLine("Detected errors in the Server Certificate:");
674                                                                                                 
675                                          for (int i = 0; i < certificateErrors.Length; i++)
676                                          {
677                                                 handshakeProblemsEncountered.Add((CertificateProblem)((uint)certificateErrors[i]));
678                                                  Console.WriteLine(certificateErrors[i]);
679                                          }
680                                         retFlag = false;
681                                 }
682                         }
683                         else
684                         {
685                                 retFlag = true;
686                         }
687
688  
689                         // Skip the server cert errors.
690                         return retFlag;
691                 }
692
693
694 /***********************************************************************/       
695                 /// <summary> Constructs a TCP/IP connection to a server specified in host and port.
696                 /// Starts the reader thread.
697                 /// 
698                 /// </summary>
699                 /// <param name="host">The host to connect to.
700                 /// 
701                 /// </param>
702                 /// <param name="port">The port on the host to connect to.
703                 /// 
704                 /// </param>
705                 /// <param name="semaphoreId">The write semaphore ID to use for the connect
706                 /// </param>
707                 private void  connect(System.String host, int port, int semaphoreId)
708                 {
709                         /* Synchronized so all variables are in a consistant state and
710                         * so that another thread isn't doing a connect, disconnect, or clone
711                         * at the same time.
712                         */
713                         // Wait for active reader to terminate
714                         waitForReader(null);
715                         
716                         // Clear the server shutdown notification flag.  This should already
717                         // be false unless of course we are reusing the same Connection object
718                         // after a server shutdown notification
719                         unsolSvrShutDnNotification = false;
720                         
721                         int semId = acquireWriteSemaphore(semaphoreId);
722                         try {
723                         
724                                 // Make socket connection to specified host and port
725                                 if (port == 0)
726                                 {
727                                         port = 389;//LdapConnection.DEFAULT_PORT;
728                                 }
729                         
730                                 try
731                                 {
732                                         if ((in_Renamed == null) || (out_Renamed == null))
733                                         {
734                                                 if(Ssl)
735                                                 {
736                                                         this.host = host;
737                                                         this.port = port;
738                                                         this.sock =     new Socket ( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
739                                                         IPAddress hostadd = Dns.Resolve(host).AddressList[0];
740                                                         IPEndPoint ephost = new IPEndPoint(hostadd,port);
741                                                         sock.Connect(ephost);
742                                                         NetworkStream nstream = new NetworkStream(sock,true);
743                                                         // Load Mono.Security.dll
744                                                         Assembly a;
745                                                         try
746                                                         {
747                                                                 a = Assembly.LoadWithPartialName("Mono.Security");
748                                                         }
749                                                         catch(System.IO.FileNotFoundException)
750                                                         {
751                                                                 throw new LdapException(ExceptionMessages.SSL_PROVIDER_MISSING, LdapException.SSL_PROVIDER_NOT_FOUND, null);
752                                                         }
753                                                         Type tSslClientStream = a.GetType("Mono.Security.Protocol.Tls.SslClientStream");
754                                                         BindingFlags flags = (BindingFlags.NonPublic  | BindingFlags.Public |
755                                                                 BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
756         
757                                                         object[] consArgs = new object[4];
758                                                         consArgs[0] = nstream;
759                                                         consArgs[1] = host;
760                                                         consArgs[2] = false;
761                                                         Type tSecurityProtocolType = a.GetType("Mono.Security.Protocol.Tls.SecurityProtocolType");
762                                                         Enum objSPType = (Enum)(Activator.CreateInstance(tSecurityProtocolType));
763                                                         int nSsl3Val = (int) Enum.Parse(tSecurityProtocolType, "Ssl3");
764                                                         int nTlsVal = (int) Enum.Parse(tSecurityProtocolType, "Tls");
765                                                         consArgs[3] = Enum.ToObject(tSecurityProtocolType, nSsl3Val | nTlsVal);
766         
767                                                         object objSslClientStream = 
768                                                                 Activator.CreateInstance(tSslClientStream, consArgs);
769         
770                                                         // Register ServerCertValidationDelegate handler
771                                                         PropertyInfo pi = tSslClientStream.GetProperty("ServerCertValidationDelegate");
772                                                         pi.SetValue(objSslClientStream, 
773                                                                 Delegate.CreateDelegate(pi.PropertyType, this, "ServerCertificateValidation"),
774                                                                 null);
775                                                         
776                                                         // Get the in and out streams
777                                                         in_Renamed = (System.IO.Stream) objSslClientStream;
778                                                         out_Renamed = (System.IO.Stream) objSslClientStream;
779                                                         /*
780                                                         SslClientStream sslstream = new SslClientStream(
781                                                                                                 nstream,
782                                                                                                 host,
783                                                                                                 false,
784                                                                                                 Mono.Security.Protocol.Tls.SecurityProtocolType.Ssl3|Mono.Security.Protocol.Tls.SecurityProtocolType.Tls);
785                                                         sslstream.ServerCertValidationDelegate += new CertificateValidationCallback(ServerCertificateValidation);*/
786                                                         //                                              byte[] buffer = new byte[0];
787                                                         //                                              sslstream.Read(buffer, 0, buffer.Length);
788                                                         //                                              sslstream.doHandshake();                                                                                                
789                                                         /*
790                                                         in_Renamed = (System.IO.Stream) sslstream;
791                                                         out_Renamed = (System.IO.Stream) sslstream;*/
792                                                 }
793                                                 else{
794                                                         socket = new System.Net.Sockets.TcpClient(host, port);                          
795                                                         in_Renamed = (System.IO.Stream) socket.GetStream();
796                                                         out_Renamed = (System.IO.Stream) socket.GetStream();
797                                                 }
798                                         }
799                                         else
800                                         {
801                                                 Console.WriteLine( "connect input/out Stream specified");
802         
803                                         }
804                                 }
805                         catch (System.Net.Sockets.SocketException se)
806                         {
807                                 // Unable to connect to server host:port
808                                 sock = null;
809                                 socket = null;
810                                 throw new LdapException(ExceptionMessages.CONNECTION_ERROR, new System.Object[] { host, port }, LdapException.CONNECT_ERROR, null, se);
811                         }
812                                 catch (System.IO.IOException ioe)
813                                 {
814                                         // Unable to connect to server host:port
815                                 sock = null;
816                                 socket = null;
817                                         throw new LdapException(ExceptionMessages.CONNECTION_ERROR, new System.Object[]{host, port}, LdapException.CONNECT_ERROR, null, ioe);
818                                 }
819                                 // Set host and port
820                                 this.host = host;
821                                 this.port = port;
822                                 // start the reader thread
823                                 this.startReader();
824                                 clientActive = true; // Client is up
825                         } finally {
826                                 freeWriteSemaphore(semId);
827                                 
828                         }
829                         return;
830                 }
831                         
832                 /// <summary>  Increments the count of cloned connections</summary>
833                 /* package */
834                 internal void  incrCloneCount()
835                 {
836                         lock (this)
837                         {
838                                 cloneCount++;
839                                 return ;
840                         }
841                 }
842                 
843                 /// <summary> Destroys a clone of <code>LdapConnection</code>.
844                 /// 
845                 /// This method first determines if only one <code>LdapConnection</code>
846                 /// object is associated with this connection, i.e. if no clone exists.
847                 /// 
848                 /// If no clone exists, the socket is closed, and the current
849                 /// <code>Connection</code> object is returned.
850                 /// 
851                 /// If multiple <code>LdapConnection</code> objects are associated
852                 /// with this connection, i.e. clones exist, a {@link #copy} of the
853                 /// this object is made, but is not connected to any host. This
854                 /// disassociates that clone from the original connection.  The new
855                 /// <code>Connection</code> object is returned.
856                 /// 
857                 /// Only one destroyClone instance is allowed to run at any one time.
858                 /// 
859                 /// If the connection is closed, any threads waiting for operations
860                 /// on that connection will wake with an LdapException indicating
861                 /// the connection is closed.
862                 /// 
863                 /// </summary>
864                 /// <param name="apiCall"><code>true</code> indicates the application is closing the
865                 /// connection or or creating a new one by calling either the
866                 /// <code>connect</code> or <code>disconnect</code> methods
867                 /// of <code>LdapConnection</code>.  <code>false</code>
868                 /// indicates that <code>LdapConnection</code> is being finalized.
869                 /// 
870                 /// </param>
871                 /// <returns> a Connection object or null if finalizing.
872                 /// </returns>
873                 /* package */
874                 internal Connection destroyClone(bool apiCall)
875                 {
876                         lock (this)
877                         {
878                                 Connection conn = this;
879
880                                 if (cloneCount > 0)
881                                 {
882                                         cloneCount--;
883                                         // This is a clone, set a new connection object.
884                                         if (apiCall)
885                                         {
886                                                 conn = (Connection) this.copy();
887                                         }
888                                         else
889                                         {
890                                                 conn = null;
891                                         }
892                                 }
893                                 else
894                                 {
895                                         if (in_Renamed != null)
896                                         {
897                                                 // Not a clone and connected
898                                                 /*
899                                                 * Either the application has called disconnect or connect
900                                                 * resulting in the current connection being closed. If the
901                                                 * application has any queues waiting on messages, we
902                                                 * need wake these up so the application does not hang.
903                                                 * The boolean flag indicates whether the close came
904                                                 * from an API call or from the object being finalized.
905                                                 */
906                                                 InterThreadException notify = new InterThreadException((apiCall?ExceptionMessages.CONNECTION_CLOSED:ExceptionMessages.CONNECTION_FINALIZED), null, LdapException.CONNECT_ERROR, null, null);
907                                                 // Destroy old connection
908                                                 shutdown("destroy clone", 0, notify);
909                                         }
910                                 }
911                                 return conn;
912                         }
913                 }
914                 
915                 /// <summary> sets the default socket factory
916                 /// 
917                 /// </summary>
918                 /// <param name="factory">the default factory to set
919                 /// </param>
920                 /* package */
921                 /// <summary> gets the socket factory used for this connection
922                 /// 
923                 /// </summary>
924                 /// <returns> the default factory for this connection
925                 /// </returns>
926                 /* package */
927                 
928                 /// <summary> clears the writeSemaphore id used for active bind operation</summary>
929                 /* package */
930                 internal void  clearBindSemId()
931                 {
932                         bindSemaphoreId = 0;
933                         return ;
934                 }
935                 
936                 /// <summary> Writes an LdapMessage to the Ldap server over a socket.
937                 /// 
938                 /// </summary>
939                 /// <param name="info">the Message containing the message to write.
940                 /// </param>
941                 /* package */
942                 internal void  writeMessage(Message info)
943                 {
944                         ExceptionMessages em = new ExceptionMessages();
945                         System.Object [][]contents = em.getContents();
946                         messages.Add(info);
947                         // For bind requests, if not connected, attempt to reconnect
948                         if (info.BindRequest && (Connected == false) && ((System.Object) host != null))
949                         {
950                                 connect(host, port, info.MessageID);
951                         }
952                         if(Connected == true)
953                         {
954                         LdapMessage msg = info.Request;
955                         writeMessage(msg);
956                         return ;
957                         }
958                         else
959                         {
960                                 int errorcount=0;
961                                 for(errorcount=0;errorcount<contents.Length;errorcount++)
962                                         if(contents[errorcount][0]=="CONNECTION_CLOSED")
963                                                 break;
964                                 throw new LdapException(ExceptionMessages.CONNECTION_CLOSED, new System.Object[]{host, port}, LdapException.CONNECT_ERROR, (String)contents[errorcount][1]);
965                         }
966                 }
967                 
968                 
969                 /// <summary> Writes an LdapMessage to the Ldap server over a socket.
970                 /// 
971                 /// </summary>
972                 /// <param name="msg">the message to write.
973                 /// </param>
974                 /* package */
975                 internal void  writeMessage(LdapMessage msg)
976                 {
977                         int id;
978                         // Get the correct semaphore id for bind operations
979                         if (bindSemaphoreId == 0)
980                         {
981                                 // Semaphore id for normal operations
982                                 id = msg.MessageID;
983                         }
984                         else
985                         {
986                                 // Semaphore id for sasl bind operations
987                                 id = bindSemaphoreId;
988                         }
989                         System.IO.Stream myOut = out_Renamed;
990                         
991                         acquireWriteSemaphore(id);
992                         try
993                         {
994                                 if (myOut == null)
995                                 {
996                                         throw new System.IO.IOException("Output stream not initialized");
997                                 }
998                                 if (!(myOut.CanWrite))
999                                 {
1000                                         return;
1001                                 }
1002                                 sbyte[] ber = msg.Asn1Object.getEncoding(encoder);
1003                                 myOut.Write(SupportClass.ToByteArray(ber), 0, ber.Length);
1004                                 myOut.Flush();
1005                         }
1006                         catch (System.IO.IOException ioe)
1007                         {
1008                                 if ((msg.Type == LdapMessage.BIND_REQUEST) &&
1009                                         (ssl))
1010                                 {
1011                                         string strMsg = "Following problem(s) occurred while establishing SSL based Connection : ";
1012                                         if (handshakeProblemsEncountered.Count > 0)
1013                                         {
1014                                                 strMsg += GetProblemMessage((CertificateProblem)handshakeProblemsEncountered[0]); 
1015                                                 for (int nProbIndex = 1; nProbIndex < handshakeProblemsEncountered.Count; nProbIndex++)
1016                                                 {
1017                                                         strMsg += ", " + GetProblemMessage((CertificateProblem)handshakeProblemsEncountered[nProbIndex]);
1018                                                 } 
1019                                         }
1020                                         else
1021                                         {
1022                                                 strMsg += "Unknown Certificate Problem";
1023                                         }
1024                                         throw new LdapException(strMsg, new System.Object[]{host, port}, LdapException.SSL_HANDSHAKE_FAILED, null, ioe);
1025                                 }                               
1026                                 /*
1027                                 * IOException could be due to a server shutdown notification which
1028                                 * caused our Connection to quit.  If so we send back a slightly
1029                                 * different error message.  We could have checked this a little
1030                                 * earlier in the method but that would be an expensive check each
1031                                 * time we send out a message.  Since this shutdown request is
1032                                 * going to be an infrequent occurence we check for it only when
1033                                 * we get an IOException.  shutdown() will do the cleanup.
1034                                 */
1035                                 if (clientActive)
1036                                 {
1037                                         // We beliefe the connection was alive
1038                                         if (unsolSvrShutDnNotification)
1039                                         {
1040                                                 // got server shutdown
1041                                                 throw new LdapException(ExceptionMessages.SERVER_SHUTDOWN_REQ, new System.Object[]{host, port}, LdapException.CONNECT_ERROR, null, ioe);
1042                                         }
1043                                         
1044                                         // Other I/O Exceptions on host:port are reported as is
1045                                         throw new LdapException(ExceptionMessages.IO_EXCEPTION, new System.Object[]{host, port}, LdapException.CONNECT_ERROR, null, ioe);
1046                                 }
1047                         }
1048                         finally
1049                         {
1050                                 freeWriteSemaphore(id);
1051                                 handshakeProblemsEncountered.Clear();
1052                         }
1053                         return ;
1054                 }
1055                 
1056                 /// <summary> Returns the message agent for this msg ID</summary>
1057                 /* package */
1058                 internal MessageAgent getMessageAgent(int msgId)
1059                 {
1060                         Message info = messages.findMessageById(msgId);
1061                         return info.MessageAgent;
1062                 }
1063                 
1064                 /// <summary> Removes a Message class from the Connection's list
1065                 /// 
1066                 /// </summary>
1067                 /// <param name="info">the Message class to remove from the list
1068                 /// </param>
1069                 /* package */
1070                 internal void  removeMessage(Message info)
1071                 {
1072                         bool done = SupportClass.VectorRemoveElement(messages, info);
1073                         return ;
1074                 }
1075                 
1076                 /// <summary> Cleans up resources associated with this connection.</summary>
1077                 ~Connection()
1078                 {
1079                         shutdown("Finalize", 0, null);
1080                         return ;
1081                 }
1082                 /// <summary> Cleans up resources associated with this connection.
1083                 /// This method may be called by finalize() for the connection, or it may
1084                 /// be called by LdapConnection.disconnect().
1085                 /// Should not have a writeSemaphore lock in place, as deadlock can occur
1086                 /// while abandoning connections.
1087                 /// </summary>
1088                 private void  shutdown(System.String reason, int semaphoreId, InterThreadException notifyUser)
1089                 {
1090                         Message info = null;
1091                         if (!clientActive)
1092                         {
1093                                 return ;
1094                         }
1095                         clientActive = false;
1096                         while (true)
1097                         {
1098                                 // remove messages from connection list and send abandon
1099                                 try
1100                                 {
1101                                         System.Object temp_object;
1102                                         temp_object = messages[0];
1103                                         messages.RemoveAt(0);
1104                                         info = (Message) temp_object;
1105                                 }
1106                                 catch (ArgumentOutOfRangeException ex)
1107                                 {
1108                                         // No more messages
1109                                         break;
1110                                 }
1111                                 info.Abandon(null, notifyUser); // also notifies the application
1112                         }
1113                         
1114                         int semId = acquireWriteSemaphore(semaphoreId);
1115                         // Now send unbind if socket not closed
1116                         if ((bindProperties != null) && (out_Renamed != null) && (out_Renamed.CanWrite) && (!bindProperties.Anonymous))
1117                         {
1118                                 try
1119                                 {
1120                                         LdapMessage msg = new LdapUnbindRequest(null);
1121                                         sbyte[] ber = msg.Asn1Object.getEncoding(encoder);
1122                                         out_Renamed.Write(SupportClass.ToByteArray(ber), 0, ber.Length);
1123                                         out_Renamed.Flush();
1124                                         out_Renamed.Close();
1125                                 }
1126                                 catch (System.Exception ex)
1127                                 {
1128                                         ; // don't worry about error
1129                                 }
1130                         }
1131                         bindProperties = null;
1132                         
1133                         if (socket != null || sock != null)
1134                         {
1135                                 // Just before closing the sockets, abort the reader thread
1136                                 if ((reader != null) && (reason != "reader: thread stopping")) 
1137                                 reader.Abort();
1138                                 // Close the socket
1139                                 try
1140                                 {
1141                                         if(Ssl)
1142                                         {
1143                                                 try {
1144                                                         sock.Shutdown(SocketShutdown.Both);
1145                                                 } catch {}
1146                                                 sock.Close();
1147                                         }
1148                                         else
1149                                         {
1150                                                 if(in_Renamed != null)
1151                                                         in_Renamed.Close();                                             
1152                                                 socket.Close();
1153                                         }
1154                                 }
1155                                 catch (Exception)
1156                                 {
1157                                         // ignore problem closing socket
1158                                 }
1159                                 socket = null;
1160                                 sock = null;
1161                                 in_Renamed=null;
1162                                 out_Renamed=null;
1163                         }
1164                         freeWriteSemaphore(semId);
1165                         return ;
1166                 }
1167                 
1168                 /// <summary> This tests to see if there are any outstanding messages.  If no messages
1169                 /// are in the queue it returns true.  Each message will be tested to
1170                 /// verify that it is complete.
1171                 /// <I>The writeSemaphore must be set for this method to be reliable!</I>
1172                 /// 
1173                 /// </summary>
1174                 /// <returns> true if no outstanding messages
1175                 /// </returns>
1176                 /* package */
1177                 internal bool areMessagesComplete()
1178                 {
1179                         System.Object[] messages = this.messages.ObjectArray;
1180                         int length = messages.Length;
1181                         
1182                         // Check if SASL bind in progress
1183                         if (bindSemaphoreId != 0)
1184                         {
1185                                 return false;
1186                         }
1187                         
1188                         // Check if any messages queued
1189                         if (length == 0)
1190                         {
1191                                 return true;
1192                         }
1193                         
1194                         for (int i = 0; i < length; i++)
1195                         {
1196                                 if (((Message) messages[i]).Complete == false)
1197                                         return false;
1198                         }
1199                         return true;
1200                 }
1201                 
1202                 /// <summary> The reader thread will stop when a reply is read with an ID equal
1203                 /// to the messageID passed in to this method.  This is used by
1204                 /// LdapConnection.StartTLS.
1205                 /// </summary>
1206                 /* package */
1207                 internal void  stopReaderOnReply(int messageID)
1208                 {
1209                         
1210                         this.stopReaderMessageID = messageID;
1211                         return ;
1212                 }
1213                 
1214                 /// <summary>startReader
1215                 /// startReader should be called when socket and io streams have been
1216                 /// set or changed.  In particular after client.Connection.startTLS()
1217                 /// It assumes the reader thread is not running.
1218                 /// </summary>
1219                 /* package */
1220                 internal void  startReader()
1221                 {
1222                         // Start Reader Thread
1223                         Thread r = new Thread(new ThreadStart(new ReaderThread(this).Run));
1224                         r.IsBackground = true; // If the last thread running, allow exit.
1225                         r.Start();
1226                         waitForReader(r);
1227                         return ;
1228                 }
1229                 
1230                 /// <summary> Indicates if the conenction is using TLS protection
1231                 ///
1232                 /// Return true if using TLS protection
1233                 /// </summary>
1234                 internal bool TLS
1235                 {
1236                         get
1237                         {
1238                                 return (this.nonTLSBackup != null);
1239                         }
1240                 }
1241                 
1242                 /// <summary> StartsTLS, in this package, assumes the caller has:
1243                 /// 1) Acquired the writeSemaphore
1244                 /// 2) Stopped the reader thread
1245                 /// 3) checked that no messages are outstanding on this connection.
1246                 /// 
1247                 /// After calling this method upper layers should start the reader
1248                 /// by calling startReader()
1249                 /// 
1250                 /// In the client.Connection, StartTLS assumes Ldap.LdapConnection will
1251                 /// stop and start the reader thread.  Connection.StopTLS will stop
1252                 /// and start the reader thread.
1253                 /// </summary>
1254                 /* package */
1255                 internal void  startTLS()
1256                 {
1257                         try
1258                         {
1259                                 waitForReader(null);
1260                                 this.nonTLSBackup = this.socket;
1261 /*                              this.sock =     new Socket ( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
1262                                 IPAddress hostadd = Dns.Resolve(host).AddressList[0];
1263                                 IPEndPoint ephost = new IPEndPoint(hostadd,port);
1264                                 sock.Connect(ephost);
1265 */
1266 //                              NetworkStream nstream = new NetworkStream(this.socket,true);
1267                                 // Load Mono.Security.dll
1268                                 Assembly a = null;
1269                                 try
1270                                 {
1271                                         a = Assembly.LoadFrom("Mono.Security.dll");
1272                                 }
1273                                 catch(System.IO.FileNotFoundException)
1274                                 {
1275                                         throw new LdapException(ExceptionMessages.SSL_PROVIDER_MISSING, LdapException.SSL_PROVIDER_NOT_FOUND, null);                                                    
1276                                 }
1277                                 Type tSslClientStream = a.GetType("Mono.Security.Protocol.Tls.SslClientStream");
1278                                 BindingFlags flags = (BindingFlags.NonPublic  | BindingFlags.Public |
1279                                         BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
1280
1281                                 object[] consArgs = new object[4];
1282                                 consArgs[0] = socket.GetStream();
1283                                 consArgs[1] = host;
1284                                 consArgs[2] = false;
1285                                 Type tSecurityProtocolType = a.GetType("Mono.Security.Protocol.Tls.SecurityProtocolType");
1286                                 Enum objSPType = (Enum)(Activator.CreateInstance(tSecurityProtocolType));
1287                                 int nSsl3Val = (int) Enum.Parse(tSecurityProtocolType, "Ssl3");
1288                                 int nTlsVal = (int) Enum.Parse(tSecurityProtocolType, "Tls");
1289                                 consArgs[3] = Enum.ToObject(tSecurityProtocolType, nSsl3Val | nTlsVal);
1290
1291                                 object objSslClientStream = 
1292                                         Activator.CreateInstance(tSslClientStream, consArgs);
1293
1294                                 // Register ServerCertValidationDelegate handler
1295                                 EventInfo ei = tSslClientStream.GetEvent("ServerCertValidationDelegate");
1296                                 ei.AddEventHandler(objSslClientStream, 
1297                                         Delegate.CreateDelegate(ei.EventHandlerType, this, "ServerCertificateValidation"));
1298                                                 
1299                                 // Get the in and out streams
1300                                 in_Renamed = (System.IO.Stream) objSslClientStream;
1301                                 out_Renamed = (System.IO.Stream) objSslClientStream;
1302
1303                                 /*
1304                                 SslClientStream sslstream = new SslClientStream(
1305                                                                         socket.GetStream(),
1306                                                                         nstream,
1307                                                                         host,
1308                                                                         false,
1309                                                                         Mono.Security.Protocol.Tls.SecurityProtocolType.Ssl3| Mono.Security.Protocol.Tls.SecurityProtocolType.Tls);
1310                                 sslstream.ServerCertValidationDelegate = new CertificateValidationCallback(ServerCertificateValidation);
1311                                 this.in_Renamed = (System.IO.Stream) sslstream;
1312                                 this.out_Renamed = (System.IO.Stream) sslstream;*/
1313                         }
1314                         catch (System.IO.IOException ioe)
1315                         {
1316                                 this.nonTLSBackup = null;
1317                                 throw new LdapException("Could not negotiate a secure connection", LdapException.CONNECT_ERROR, null, ioe);
1318                         }
1319                         catch (System.Exception uhe)
1320                         {
1321                                 this.nonTLSBackup = null;
1322                                 throw new LdapException("The host is unknown", LdapException.CONNECT_ERROR, null, uhe);
1323                         }
1324                         return ;
1325                 }
1326                 
1327                 /*
1328                 * Stops TLS.
1329                 *
1330                 * StopTLS, in this package, assumes the caller has:
1331                 *  1) blocked writing (acquireWriteSemaphore).
1332                 *  2) checked that no messages are outstanding.
1333                 *
1334                 *  StopTLS Needs to do the following:
1335                 *  1) close the current socket
1336                 *      - This stops the reader thread
1337                 *      - set STOP_READING flag on stopReaderMessageID so that
1338                 *        the reader knows that the IOException is planned.
1339                 *  2) replace the current socket with nonTLSBackup,
1340                 *  3) and set nonTLSBackup to null;
1341                 *  4) reset input and outputstreams
1342                 *  5) start the reader thread by calling startReader
1343                 *
1344                 *  Note: Sun's JSSE doesn't allow the nonTLSBackup socket to be
1345                 * used any more, even though autoclose was false: you get an IOException.
1346                 * IBM's JSSE hangs when you close the JSSE socket.
1347                 */
1348                 /* package */
1349                 internal void  stopTLS()
1350                 {
1351                         try
1352                         {
1353                                 this.stopReaderMessageID = Connection.STOP_READING;
1354                                 this.out_Renamed.Close();
1355                                 this.in_Renamed.Close();
1356 //                              this.sock.Shutdown(SocketShutdown.Both);
1357 //                              this.sock.Close();
1358                                 waitForReader(null);
1359                                 this.socket = this.nonTLSBackup;
1360                                 this.in_Renamed = (System.IO.Stream) this.socket.GetStream();
1361                                 this.out_Renamed = (System.IO.Stream) this.socket.GetStream();
1362                                 // Allow the new reader to start
1363                                 this.stopReaderMessageID = Connection.CONTINUE_READING;
1364                         }
1365                         catch (System.IO.IOException ioe)
1366                         {
1367                                 throw new LdapException(ExceptionMessages.STOPTLS_ERROR, LdapException.CONNECT_ERROR, null, ioe);
1368                         }
1369                         finally
1370                         {
1371                                 this.nonTLSBackup = null;
1372                                 startReader();
1373                         }
1374                         return ;
1375                 }
1376 ///TLS not supported in first release           
1377
1378                 internal Stream InputStream
1379                 {
1380                         get { return in_Renamed; }
1381                 }
1382
1383                 internal Stream OutputStream
1384                 {
1385                         get { return out_Renamed; }
1386                 }
1387
1388                 internal void ReplaceStreams(Stream newIn, Stream newOut)
1389                 {
1390                         // wait for reader to stop, see LdapConnection.Bind
1391                         waitForReader(null);
1392                         in_Renamed = newIn;
1393                         out_Renamed = newOut;
1394                         startReader();
1395                 }
1396
1397                 public class ReaderThread
1398                 {
1399                         private void  InitBlock(Connection enclosingInstance)
1400                         {
1401                                 this.enclosingInstance = enclosingInstance;
1402                         }
1403                         private Connection enclosingInstance;
1404                         public Connection Enclosing_Instance
1405                         {
1406                                 get
1407                                 {
1408                                         return enclosingInstance;
1409                                 }
1410                                 
1411                         }
1412                         public ReaderThread(Connection enclosingInstance)
1413                         {
1414                                 InitBlock(enclosingInstance);
1415                                 return ;
1416                         }
1417                         
1418                         /// <summary> This thread decodes and processes RfcLdapMessage's from the server.
1419                         /// 
1420                         /// Note: This thread needs a graceful shutdown implementation.
1421                         /// </summary>
1422                         public virtual void  Run()
1423                         {
1424                                 
1425                                 System.String reason = "reader: thread stopping";
1426                                 InterThreadException notify = null;
1427                                 Message info = null;
1428                                 System.IO.IOException ioex = null;
1429                                 this.enclosingInstance.reader = System.Threading.Thread.CurrentThread;                          
1430 //                              Enclosing_Instance.reader = SupportClass.ThreadClass.Current();
1431 //                              Console.WriteLine("Inside run:" + this.enclosingInstance.reader.Name);
1432                                 try
1433                                 {
1434                                         for (; ; )
1435                                         {
1436                                                 // -------------------------------------------------------
1437                                                 // Decode an RfcLdapMessage directly from the socket.
1438                                                 // -------------------------------------------------------
1439                                                 Asn1Identifier asn1ID;
1440                                                 System.IO.Stream myIn;
1441                                                 /* get current value of in, keep value consistant
1442                                                 * though the loop, i.e. even during shutdown
1443                                                 */
1444                                                 myIn = this.enclosingInstance.in_Renamed;
1445                                                 if (myIn == null)
1446                                                 {
1447                                                         break;
1448                                                 }
1449                                                 asn1ID = new Asn1Identifier(myIn);
1450                                                 int tag = asn1ID.Tag;
1451                                                 if (asn1ID.Tag != Asn1Sequence.TAG)
1452                                                 {
1453                                                         continue; // loop looking for an RfcLdapMessage identifier
1454                                                 }
1455                                                 
1456                                                 // Turn the message into an RfcMessage class
1457                                                 Asn1Length asn1Len = new Asn1Length(myIn);
1458                                                 
1459                                                 RfcLdapMessage msg = new RfcLdapMessage(this.enclosingInstance.decoder, myIn, asn1Len.Length);
1460                                                 
1461                                                 // ------------------------------------------------------------
1462                                                 // Process the decoded RfcLdapMessage.
1463                                                 // ------------------------------------------------------------
1464                                                 int msgId = msg.MessageID;
1465                                                 
1466                                                 // Find the message which requested this response.
1467                                                 // It is possible to receive a response for a request which
1468                                                 // has been abandoned. If abandoned, throw it away
1469                                                 try
1470                                                 {
1471                                                         info = this.enclosingInstance.messages.findMessageById(msgId);
1472                                                         info.putReply(msg); // queue & wake up waiting thread
1473                                                 }
1474                                                 catch (System.FieldAccessException ex)
1475                                                 {
1476                                                         
1477                                                         /*
1478                                                         * We get the NoSuchFieldException when we could not find
1479                                                         * a matching message id.  First check to see if this is
1480                                                         * an unsolicited notification (msgID == 0). If it is not
1481                                                         * we throw it away. If it is we call any unsolicited
1482                                                         * listeners that might have been registered to listen for these
1483                                                         * messages.
1484                                                         */
1485                                                         
1486                                                         
1487                                                         /* Note the location of this code.  We could have required
1488                                                         * that message ID 0 be just like other message ID's but
1489                                                         * since message ID 0 has to be treated specially we have
1490                                                         * a separate check for message ID 0.  Also note that
1491                                                         * this test is after the regular message list has been
1492                                                         * checked for.  We could have always checked the list
1493                                                         * of messages after checking if this is an unsolicited
1494                                                         * notification but that would have inefficient as
1495                                                         * message ID 0 is a rare event (as of this time).
1496                                                         */
1497                                                         if (msgId == 0)
1498                                                         {
1499                                                                 
1500                                                                 
1501                                                                 // Notify any listeners that might have been registered
1502                                                                 this.enclosingInstance.notifyAllUnsolicitedListeners(msg);
1503                                                                 
1504                                                                 /*
1505                                                                 * Was this a server shutdown unsolicited notification.
1506                                                                 * IF so we quit. Actually calling the return will
1507                                                                 * first transfer control to the finally clause which
1508                                                                 * will do the necessary clean up.
1509                                                                 */
1510                                                                 if (this.enclosingInstance.unsolSvrShutDnNotification)
1511                                                                 {
1512                                                                         notify = new InterThreadException(ExceptionMessages.SERVER_SHUTDOWN_REQ, new System.Object[]{this.enclosingInstance.host, this.enclosingInstance.port}, LdapException.CONNECT_ERROR, null, null);
1513                                                                         
1514                                                                         return ;
1515                                                                 }
1516                                                         }
1517                                                         else
1518                                                         {
1519                                                                 
1520                                                         }
1521                                                 }
1522                                                 if ((this.enclosingInstance.stopReaderMessageID == msgId) || (this.enclosingInstance.stopReaderMessageID == Novell.Directory.Ldap.Connection.STOP_READING))
1523                                                 {
1524                                                         // Stop the reader Thread.
1525                                                         return ;
1526                                                 }
1527                                         }
1528                                 }
1529                                 catch(ThreadAbortException tae)
1530                                 {
1531                                         // Abort has been called on reader
1532                                         // before closing sockets, from shutdown
1533                                         return;
1534                                 }
1535                                 catch (System.IO.IOException ioe)
1536                                 {
1537                                         
1538                                         ioex = ioe;
1539                                         if ((this.enclosingInstance.stopReaderMessageID != Novell.Directory.Ldap.Connection.STOP_READING) && this.enclosingInstance.clientActive)
1540                                         {
1541                                                 // Connection lost waiting for results from host:port
1542                                                 notify = new InterThreadException(ExceptionMessages.CONNECTION_WAIT, new System.Object[]{this.enclosingInstance.host, this.enclosingInstance.port}, LdapException.CONNECT_ERROR, ioe, info);
1543                                         }
1544                                         // The connection is no good, don't use it any more
1545                                         this.enclosingInstance.in_Renamed = null;
1546                                         this.enclosingInstance.out_Renamed = null;
1547                                 }
1548                                 finally
1549                                 {
1550                                         /*
1551                                         * There can be four states that the reader can be in at this point:
1552                                         *  1) We are starting TLS and will be restarting the reader
1553                                         *     after we have negotiated TLS.
1554                                         *      - Indicated by whether stopReaderMessageID does not
1555                                         *        equal CONTINUE_READING.
1556                                         *      - Don't call Shutdown.
1557                                         *  2) We are stoping TLS and will be restarting after TLS is
1558                                         *     stopped.
1559                                         *      - Indicated by an IOException AND stopReaderMessageID equals
1560                                         *        STOP_READING - in which case notify will be null.
1561                                         *      - Don't call Shutdown
1562                                         *  3) We receive a Server Shutdown notification.
1563                                         *      - Indicated by messageID equal to 0.
1564                                         *      - call Shutdown.
1565                                         *  4) Another error occured
1566                                         *      - Indicated by an IOException AND notify is not NULL
1567                                         *      - call Shutdown.
1568                                         */
1569                                         if ((!this.enclosingInstance.clientActive) || (notify != null))
1570                                         {
1571                                                 //#3 & 4
1572                                                 this.enclosingInstance.shutdown(reason, 0, notify);
1573                                         }
1574                                         else
1575                                         {
1576                                                 this.enclosingInstance.stopReaderMessageID = Novell.Directory.Ldap.Connection.CONTINUE_READING;
1577                                         }
1578                                 }
1579                                 this.enclosingInstance.deadReaderException = ioex;
1580                                 this.enclosingInstance.deadReader = this.enclosingInstance.reader;
1581                                 this.enclosingInstance.reader = null;
1582                                 return ;
1583                         }
1584                 } // End class ReaderThread
1585                 
1586                 /// <summary>Add the specific object to the list of listeners that want to be
1587                 /// notified when an unsolicited notification is received.
1588                 /// </summary>
1589                 /* package */
1590                 internal void  AddUnsolicitedNotificationListener(LdapUnsolicitedNotificationListener listener)
1591                 {
1592                         unsolicitedListeners.Add(listener);
1593                         return ;
1594                 }
1595                 
1596                 /// <summary>Remove the specific object from current list of listeners</summary>
1597                 /* package */
1598                 internal void  RemoveUnsolicitedNotificationListener(LdapUnsolicitedNotificationListener listener)
1599                 {
1600                         SupportClass.VectorRemoveElement(unsolicitedListeners, listener);
1601                         return ;
1602                 }
1603                 
1604                 /// <summary>Inner class defined so that we can spawn off each unsolicited
1605                 /// listener as a seperate thread.  We did not want to call the
1606                 /// unsolicited listener method directly as this would have tied up our
1607                 /// deamon listener thread in the applications unsolicited listener method.
1608                 /// Since we do not know what the application unsolicited listener
1609                 /// might be doing and how long it will take to process the uncoslicited
1610                 /// notification.  We use this class to spawn off the unsolicited
1611                 /// notification as a separate thread
1612                 /// </summary>
1613                 private class UnsolicitedListenerThread:SupportClass.ThreadClass
1614                 {
1615                         private void  InitBlock(Connection enclosingInstance)
1616                         {
1617                                 this.enclosingInstance = enclosingInstance;
1618                         }
1619                         private Connection enclosingInstance;
1620                         public Connection Enclosing_Instance
1621                         {
1622                                 get
1623                                 {
1624                                         return enclosingInstance;
1625                                 }
1626                                 
1627                         }
1628                         private LdapUnsolicitedNotificationListener listenerObj;
1629                         private LdapExtendedResponse unsolicitedMsg;
1630                         
1631                         /* package */
1632                         internal UnsolicitedListenerThread(Connection enclosingInstance, LdapUnsolicitedNotificationListener l, LdapExtendedResponse m)
1633                         {
1634                                 InitBlock(enclosingInstance);
1635                                 this.listenerObj = l;
1636                                 this.unsolicitedMsg = m;
1637                                 return ;
1638                         }
1639                         
1640                         public override void  Run()
1641                         {
1642                                 listenerObj.messageReceived(unsolicitedMsg);
1643                                 return ;
1644                         }
1645                 }
1646                 
1647                 private void  notifyAllUnsolicitedListeners(RfcLdapMessage message)
1648                 {
1649                         
1650                         
1651                         // MISSING:  If this is a shutdown notification from the server
1652                         // set a flag in the Connection class so that we can throw an
1653                         // appropriate LdapException to the application
1654                         LdapMessage extendedLdapMessage = new LdapExtendedResponse(message);
1655                         System.String notificationOID = ((LdapExtendedResponse) extendedLdapMessage).ID;
1656                         if (notificationOID.Equals(LdapConnection.SERVER_SHUTDOWN_OID))
1657                         {
1658                                 
1659                                 
1660                                 unsolSvrShutDnNotification = true;
1661                         }
1662                         
1663                         int numOfListeners = unsolicitedListeners.Count;
1664                         
1665                         // Cycle through all the listeners
1666                         for (int i = 0; i < numOfListeners; i++)
1667                         {
1668                                 
1669                                 // Get next listener
1670                                 LdapUnsolicitedNotificationListener listener = (LdapUnsolicitedNotificationListener) unsolicitedListeners[i];
1671                                 
1672                                 
1673                                 // Create a new ExtendedResponse each time as we do not want each listener
1674                                 // to have its own copy of the message
1675                                 LdapExtendedResponse tempLdapMessage = new LdapExtendedResponse(message);
1676                                 
1677                                 // Spawn a new thread for each listener to go process the message
1678                                 // The reason we create a new thread rather than just call the
1679                                 // the messageReceived method directly is beacuse we do not know
1680                                 // what kind of processing the notification listener class will
1681                                 // do.  We do not want our deamon thread to block waiting for
1682                                 // the notification listener method to return.
1683                                 UnsolicitedListenerThread u = new UnsolicitedListenerThread(this, listener, tempLdapMessage);
1684                                 u.Start();
1685                         }
1686                         
1687                         
1688                         return ;
1689                 }
1690                 static Connection()
1691                 {
1692                         nameLock = new System.Object();
1693                         sdk = new System.Text.StringBuilder("2.1.8").ToString();
1694                         protocol = 3;
1695                 }
1696         }
1697 }