Add unit test for AggregateException.GetBaseException that works on .net but is broke...
[mono.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / TdsComm.cs
1 //
2 // Mono.Data.Tds.Protocol.TdsComm.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //   Gonzalo Paniagua Javier (gonzalo@novell.com)
7 //
8 // Copyright (C) 2002 Tim Coleman
9 // Copyright (c) 2009 Novell, Inc.
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.IO;
35 using System.Net;
36 using System.Net.Sockets;
37 using System.Text;
38 using System.Threading;
39
40 namespace Mono.Data.Tds.Protocol {
41         internal sealed class TdsComm
42         {
43                 #region Fields
44
45                 NetworkStream stream;
46                 int packetSize;
47                 TdsPacketType packetType = TdsPacketType.None;
48                 bool connReset;
49                 Encoding encoder;
50
51                 string dataSource;
52                 int commandTimeout;
53
54                 byte[] outBuffer;
55                 int outBufferLength;
56                 int nextOutBufferIndex = 0;
57                 bool lsb;
58
59                 byte[] inBuffer;
60                 int inBufferLength;
61                 int inBufferIndex = 0;
62
63                 static int headerLength = 8;
64
65                 byte[] tmpBuf = new byte[8];
66                 byte[] resBuffer = new byte[256];
67
68                 int packetsSent;
69                 int packetsReceived = 0;
70
71                 Socket socket;
72                 TdsVersion tdsVersion;
73
74                 #endregion // Fields
75                 
76                 #region Constructors
77
78                 public TdsComm (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion)
79                 {
80                         this.packetSize = packetSize;
81                         this.tdsVersion = tdsVersion;
82                         this.dataSource = dataSource;
83
84                         outBuffer = new byte[packetSize];
85                         inBuffer = new byte[packetSize];
86
87                         outBufferLength = packetSize;
88                         inBufferLength = packetSize;
89
90                         lsb = true;
91                         
92                         IPEndPoint endPoint;
93                         bool have_exception = false;
94                         
95                         try {
96 #if NET_2_0
97                                 IPAddress ip;
98                                 if(IPAddress.TryParse(this.dataSource, out ip)) {
99                                         endPoint = new IPEndPoint(ip, port);
100                                 } else {
101                                         IPHostEntry hostEntry = Dns.GetHostEntry (this.dataSource);
102                                         endPoint = new IPEndPoint(hostEntry.AddressList [0], port);
103                                 }
104 #else
105                                 IPHostEntry hostEntry = Dns.Resolve (this.dataSource);
106                                 endPoint = new IPEndPoint (hostEntry.AddressList [0], port);
107 #endif
108                         } catch (SocketException e) {
109                                 throw new TdsInternalException ("Server does not exist or connection refused.", e);
110                         }
111
112                         try {
113                                 socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
114                                 IAsyncResult ares = socket.BeginConnect (endPoint, null, null);
115                                 int timeout_ms = timeout * 1000;
116                                 if (timeout > 0 && !ares.IsCompleted && !ares.AsyncWaitHandle.WaitOne (timeout_ms, false))
117                                         throw Tds.CreateTimeoutException (dataSource, "Open()");
118                                 socket.EndConnect (ares);
119                                 try {
120                                         // MS sets these socket option
121                                         socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
122                                 } catch (SocketException) {
123                                         // Some platform may throw an exception, so
124                                         // eat all socket exception, yeaowww! 
125                                 }
126
127                                 try {
128 #if NET_2_0
129                                         socket.NoDelay = true;
130 #endif
131                                         socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendTimeout, timeout_ms);
132                                         socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, timeout_ms);
133                                 } catch {
134                                         // Ignore exceptions here for systems that do not support these options.
135                                 }
136                                 // Let the stream own the socket and take the pleasure of closing it
137                                 stream = new NetworkStream (socket, true);
138                         } catch (SocketException e) {
139                                 have_exception = true;
140                                 throw new TdsInternalException ("Server does not exist or connection refused.", e);
141                         } catch (Exception) {
142                                 have_exception = true;
143                                 throw;
144                         } finally {
145                                 if (have_exception && socket != null) {
146                                         try {
147                                                 Socket s = socket;
148                                                 socket = null;
149                                                 s.Close ();
150                                         } catch {}
151                                 }
152                         }
153                         if (!socket.Connected)
154                                 throw new TdsInternalException ("Server does not exist or connection refused.", null);
155                         packetsSent = 1;
156                 }
157                 
158                 #endregion // Constructors
159                 
160                 #region Properties
161
162                 public int CommandTimeout {
163                         get { return commandTimeout; }
164                         set { commandTimeout = value; }
165                 }
166
167                 internal Encoding Encoder {
168                         get { return encoder; }
169                         set { encoder = value; }
170                 }
171                 
172                 public int PacketSize {
173                         get { return packetSize; }
174                         set { packetSize = value; }
175                 }
176                 
177                 public bool TdsByteOrder {
178                         get { return !lsb; }
179                         set { lsb = !value; }
180                 }
181                 #endregion // Properties
182                 
183                 #region Methods
184
185                 public byte[] Swap(byte[] toswap) {
186                         byte[] ret = new byte[toswap.Length];
187                         for(int i = 0; i < toswap.Length; i++)
188                                 ret [toswap.Length - i - 1] = toswap[i];
189
190                         return ret;
191                 }
192
193                 public void SendIfFull ()
194                 {
195                         if (nextOutBufferIndex == outBufferLength) {
196                                 SendPhysicalPacket (false);
197                                 nextOutBufferIndex = headerLength;
198                         }
199                 }
200
201                 public void SendIfFull (int reserve) 
202                 {
203                         if (nextOutBufferIndex+reserve > outBufferLength) {
204                                 SendPhysicalPacket (false);
205                                 nextOutBufferIndex = headerLength;
206                         }
207                 }
208
209                 public void Append (object o)
210                 {
211                         if (o == null || o == DBNull.Value) {
212                                 Append ((byte)0);
213                                 return ;
214                         }
215
216                         switch (Type.GetTypeCode (o.GetType ())) {
217                         case TypeCode.Byte :
218                                 Append ((byte) o);
219                                 return;
220                         case TypeCode.Boolean:
221                                 if ((bool)o == true)
222                                         Append ((byte)1);
223                                 else
224                                         Append ((byte)0);
225                                 return;
226                         case TypeCode.Object :
227                                 if (o is byte[])
228                                         Append ((byte[]) o);
229                                 return;
230                         case TypeCode.Int16 :
231                                 Append ((short) o);
232                                 return;
233                         case TypeCode.Int32 :
234                                 Append ((int) o);
235                                 return;
236                         case TypeCode.String :
237                                 Append ((string) o);
238                                 return;
239                         case TypeCode.Double :
240                                 Append ((double) o);
241                                 return;
242                         case TypeCode.Single :
243                                 Append ((float) o);
244                                 return;
245                         case TypeCode.Int64 :
246                                 Append ((long) o);
247                                 return;
248                         case TypeCode.Decimal:
249                                 Append ((decimal) o, 17);
250                                 return;
251                         case TypeCode.DateTime:
252                                 Append ((DateTime) o, 8);
253                                 return;
254                         }
255                         throw new InvalidOperationException (String.Format ("Object Type :{0} , not being appended", o.GetType ()));
256                 }
257
258                 public void Append (byte b)
259                 {
260                         SendIfFull ();
261                         Store (nextOutBufferIndex, b);
262                         nextOutBufferIndex++;
263                 }       
264
265                 public void Append (DateTime t, int bytes)
266                 {
267                         DateTime epoch = new DateTime (1900,1,1);
268                         
269                         TimeSpan span = t - epoch; //new TimeSpan (t.Ticks - epoch.Ticks);
270                         int days, hours, minutes, secs;
271                         long msecs;
272                         int val = 0;    
273
274                         days = span.Days;
275                         hours = span.Hours;
276                         minutes = span.Minutes;
277                         secs = span.Seconds;
278                         msecs = span.Milliseconds;
279                         
280                         if (epoch > t) {
281                                 // If t.Hour/Min/Sec/MSec is > 0, days points to the next day and hence, 
282                                 // we move it back by a day - otherwise, no change
283                                 days = (t.Hour > 0 || t.Minute > 0 || t.Second > 0 || t.Millisecond > 0) ? days-1: days;
284                                 hours = t.Hour;
285                                 minutes = t.Minute;
286                                 secs = t.Second;
287                                 msecs = t.Millisecond;
288                         }
289
290                         SendIfFull (bytes);
291                         if (bytes == 8) {
292                                 long ms = (hours * 3600 + minutes * 60 + secs)*1000L + (long)msecs;
293                                 val = (int) ((ms*300)/1000);
294                                 AppendInternal ((int) days);
295                                 AppendInternal ((int) val);
296                         } else if (bytes ==4) {
297                                 val = span.Hours * 60 + span.Minutes;
298                                 AppendInternal ((short) days);
299                                 AppendInternal ((short) val);
300                         } else {
301                                 throw new Exception ("Invalid No of bytes");
302                         }
303                 }
304
305                 public void Append (byte[] b)
306                 {
307                         Append (b, b.Length, (byte) 0);
308                 }
309
310                 
311                 public void Append (byte[] b, int len, byte pad)
312                 {
313                         int bufBytesToCopy = System.Math.Min (b.Length, len);
314                         int padBytesToCopy = len - bufBytesToCopy;
315                         int bufPos = 0;
316
317                         /* copy out of our input buffer in the largest chunks possible *
318                          * at a time. limited only by the buffer size for our outgoing *
319                          * packets.                                                    */
320
321                         while (bufBytesToCopy > 0)
322                         {
323                                 SendIfFull ();
324
325                                 int availBytes = outBufferLength - nextOutBufferIndex;
326                                 int bufSize = System.Math.Min (availBytes, bufBytesToCopy);
327
328                                 Buffer.BlockCopy (b, bufPos, outBuffer, nextOutBufferIndex, bufSize);
329
330                                 nextOutBufferIndex += bufSize;
331                                 bufBytesToCopy -= bufSize;
332                                 bufPos += bufSize;
333                         }
334
335                         while (padBytesToCopy > 0)
336                         {
337                                 SendIfFull ();
338
339                                 int availBytes = outBufferLength - nextOutBufferIndex;
340                                 int bufSize = System.Math.Min (availBytes, padBytesToCopy);
341
342                                 for (int i = 0; i < bufSize; i++)
343                                         outBuffer [nextOutBufferIndex++] = pad;
344
345                                 padBytesToCopy -= bufSize;
346                         }
347                 }
348
349                 private void AppendInternal (short s)
350                 {
351                         if (!lsb) {
352                                 outBuffer[nextOutBufferIndex++] = (byte) (((byte) (s >> 8)) & 0xff);
353                                 outBuffer[nextOutBufferIndex++] = (byte) ((byte) (s & 0xff));
354                         } else {
355                                 outBuffer[nextOutBufferIndex++] = (byte) ((byte) (s & 0xff));
356                                 outBuffer[nextOutBufferIndex++] = (byte) (((byte) (s >> 8)) & 0xff);
357                         }
358                 }
359
360                 public void Append (short s)
361                 {
362                         SendIfFull (sizeof (short));
363                         AppendInternal (s);
364                 }
365
366                 public void Append (ushort s)
367                 {
368                         SendIfFull (sizeof (short));
369                         AppendInternal ((short) s);
370                 }
371
372                 private void AppendInternal (int i)
373                 {
374                         if (!lsb) {
375                                 AppendInternal ((short) (((short) (i >> 16)) & 0xffff));
376                                 AppendInternal ((short) ((short) (i & 0xffff)));
377                         } else {
378                                 AppendInternal ((short) ((short) (i & 0xffff)));
379                                 AppendInternal ((short) (((short) (i >> 16)) & 0xffff));
380                         }                               
381                 }
382
383                 public void Append (int i)
384                 {
385                         SendIfFull (sizeof (int));
386                         AppendInternal (i);
387                 }
388
389                 public void Append (string s)
390                 {
391                         if (tdsVersion < TdsVersion.tds70) { 
392                                 Append (encoder.GetBytes (s));
393                         } else {
394                                 for (int i = 0; i < s.Length; i++) {
395                                         SendIfFull (sizeof(short));
396                                         AppendInternal ((short)s[i]);
397                                 }
398                         }
399                 }       
400
401                 // Appends with padding
402                 public byte[] Append (string s, int len, byte pad)
403                 {
404                         if (s == null)
405                                 return new byte[0];
406
407                         byte[] result = encoder.GetBytes (s);
408                         Append (result, len, pad);
409                         return result;
410                 }
411
412                 public void Append (double value)
413                 {
414                         if (!lsb)
415                                 Append (Swap (BitConverter.GetBytes (value)), sizeof(double), (byte)0);
416                         else
417                                 Append (BitConverter.GetBytes (value), sizeof(double), (byte)0);
418                 }
419
420                 public void Append (float value)
421                 {
422                         if (!lsb)
423                                 Append (Swap (BitConverter.GetBytes (value)), sizeof(float), (byte)0);
424                         else
425                                 Append (BitConverter.GetBytes (value), sizeof(float), (byte)0);
426                 }
427
428                 public void Append (long l)
429                 {
430                         SendIfFull (sizeof (long));
431                         if (!lsb) {
432                                 AppendInternal ((int) (((int) (l >> 32)) & 0xffffffff));
433                                 AppendInternal ((int) ((int) (l & 0xffffffff)));
434                         } else {
435                                 AppendInternal ((int) ((int) (l & 0xffffffff)));
436                                 AppendInternal ((int) (((int) (l >> 32)) & 0xffffffff));
437                         }                               
438                 }
439
440                 public void Append (decimal d, int bytes)
441                 {
442                         int[] arr = Decimal.GetBits (d);
443                         byte sign =  (d > 0 ? (byte)1 : (byte)0);
444                         SendIfFull (bytes);
445                         Append (sign) ;
446                         AppendInternal (arr[0]);
447                         AppendInternal (arr[1]);
448                         AppendInternal (arr[2]);
449                         AppendInternal ((int)0);
450                 }
451
452                 public void Close ()
453                 {
454                         if (stream == null)
455                                 return;
456
457                         connReset = false;
458                         socket = null;
459                         try {
460                                 stream.Close ();
461                         } catch {
462                         }
463                         stream = null;
464                 }
465
466                 public bool IsConnected () 
467                 {
468                         return socket != null && socket.Connected && !(socket.Poll (0, SelectMode.SelectRead) && socket.Available == 0);
469                 }
470                 
471                 public byte GetByte ()
472                 {
473                         byte result;
474
475                         if (inBufferIndex >= inBufferLength) {
476                                 // out of data, read another physical packet.
477                                 GetPhysicalPacket ();
478                         }
479                         result = inBuffer[inBufferIndex++];
480                         return result;
481                 }
482
483                 public byte[] GetBytes (int len, bool exclusiveBuffer)
484                 {
485                         byte[] result = null;
486                         int i;
487
488                         // Do not keep an internal result buffer larger than 16k.
489                         // This would unnecessarily use up memory.
490                         if (exclusiveBuffer || len > 16384)
491                                 result = new byte[len];
492                         else
493                         {
494                                 if (resBuffer.Length < len)
495                                         resBuffer = new byte[len];
496                                 result = resBuffer;
497                         }
498
499                         for (i = 0; i<len; )
500                         {
501                                 if (inBufferIndex >= inBufferLength)
502                                         GetPhysicalPacket ();
503
504                                 int avail = inBufferLength - inBufferIndex;
505                                 avail = avail>len-i ? len-i : avail;
506
507                                 Buffer.BlockCopy (inBuffer, inBufferIndex, result, i, avail);
508                                 i += avail;
509                                 inBufferIndex += avail;
510                         }
511
512                         return result;
513                 }
514
515                 public string GetString (int len, Encoding enc)
516                 {
517                         if (tdsVersion >= TdsVersion.tds70) 
518                                 return GetString (len, true, null);
519                         else
520                                 return GetString (len, false, null);
521                 }
522                 
523                 public string GetString (int len)
524                 {
525                         if (tdsVersion >= TdsVersion.tds70) 
526                                 return GetString (len, true);
527                         else
528                                 return GetString (len, false);
529                 }
530
531                 public string GetString (int len, bool wide, Encoding enc)
532                 {
533                         if (wide) {
534                                 char[] chars = new char[len];
535                                 for (int i = 0; i < len; ++i) {
536                                         int lo = ((byte) GetByte ()) & 0xFF;
537                                         int hi = ((byte) GetByte ()) & 0xFF;
538                                         chars[i] = (char) (lo | ( hi << 8));
539                                 }
540                                 return new String (chars);
541                         }
542                         else {
543                                 byte[] result = new byte[len];
544                                 Array.Copy (GetBytes (len, false), result, len);
545                                 // Use the passed encoder, if available
546                                 if (enc != null)
547                                         return (enc.GetString (result));
548                                 else
549                                         return (encoder.GetString (result));
550                         }
551                 }
552                 
553                 public string GetString (int len, bool wide)
554                 {
555                         return GetString (len, wide, null);
556                 }
557
558                 public int GetNetShort ()
559                 {
560                         byte[] tmp = new byte[2];
561                         tmp[0] = GetByte ();
562                         tmp[1] = GetByte ();
563                         return Ntohs (tmp, 0);
564                 }
565
566                 public short GetTdsShort ()
567                 {
568                         byte[] input = new byte[2];
569
570                         for (int i = 0; i < 2; i += 1)
571                                 input[i] = GetByte ();
572                         if(!BitConverter.IsLittleEndian)
573                                 return (BitConverter.ToInt16 (Swap (input), 0));
574                         else
575                                 return (BitConverter.ToInt16 (input, 0));
576                 }
577
578
579                 public int GetTdsInt ()
580                 {
581                         byte[] input = new byte[4];
582                         for (int i = 0; i < 4; i += 1) {
583                                 input[i] = GetByte ();
584                         }
585                         if(!BitConverter.IsLittleEndian)
586                                 return (BitConverter.ToInt32 (Swap (input), 0));
587                         else
588                                 return (BitConverter.ToInt32 (input, 0));
589                 }
590
591                 public long GetTdsInt64 ()
592                 {
593                         byte[] input = new byte[8];
594                         for (int i = 0; i < 8; i += 1)
595                                 input[i] = GetByte ();
596                         if(!BitConverter.IsLittleEndian)
597                                 return (BitConverter.ToInt64 (Swap (input), 0));
598                         else
599                                 return (BitConverter.ToInt64 (input, 0));
600                 }
601
602                 private void GetPhysicalPacket ()
603                 {
604                         int dataLength = GetPhysicalPacketHeader ();
605                         GetPhysicalPacketData (dataLength);
606                 }
607
608                 int Read (byte [] buffer, int offset, int count)
609                 {
610                         try {
611                                 return stream.Read (buffer, offset, count);
612                         } catch {
613                                 socket = null;
614                                 stream.Close ();
615                                 throw;
616                         }
617                 }
618
619                 private int GetPhysicalPacketHeader ()
620                 {
621                         int nread = 0;
622
623                         int n;
624                         // read the header
625                         while (nread < 8) {
626                                 n = Read (tmpBuf, nread, 8 - nread);
627                                 if (n <= 0) {
628                                         socket = null;
629                                         stream.Close ();
630                                         throw new IOException (n == 0 ? "Connection lost" : "Connection error");
631                                 }
632                                 nread += n;
633                         }
634
635                         TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
636                         if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply) 
637                         {
638                                 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
639                         }
640
641                         // figure out how many bytes are remaining in this packet.
642                         int len = Ntohs (tmpBuf, 2) - 8;
643                         if (len >= inBuffer.Length) 
644                                 inBuffer = new byte[len];
645
646                         if (len < 0) {
647                                 throw new Exception (String.Format ("Confused by a length of {0}", len));
648                         }
649                         
650                         return len;
651
652                 }
653                 
654                 private void GetPhysicalPacketData (int length)
655                 {
656                         // now get the data
657                         int nread = 0;
658                         int n;
659
660                         while (nread < length) {
661                                 n = Read (inBuffer, nread, length - nread);
662                                 if (n <= 0) {
663                                         socket = null;
664                                         stream.Close ();
665                                         throw new IOException (n == 0 ? "Connection lost" : "Connection error");
666                                 }
667                                 nread += n;
668                         }
669
670                         packetsReceived++;
671
672                         // adjust the bookkeeping info about the incoming buffer
673                         inBufferLength = length;
674                         inBufferIndex = 0;
675                 }
676                 
677
678                 private static int Ntohs (byte[] buf, int offset)
679                 {
680                         int lo = ((int) buf[offset + 1] & 0xff);
681                         int hi = (((int) buf[offset] & 0xff ) << 8);
682
683                         return hi | lo;
684                         // return an int since we really want an _unsigned_
685                 }               
686
687                 public byte Peek ()
688                 {
689                         // If out of data, read another physical packet.
690                         if (inBufferIndex >= inBufferLength)
691                                 GetPhysicalPacket ();
692
693                         return inBuffer[inBufferIndex];
694                 }
695
696                 public bool Poll (int seconds, SelectMode selectMode)
697                 {
698                         return Poll (socket, seconds, selectMode);
699                 }
700
701                 private bool Poll (Socket s, int seconds, SelectMode selectMode)
702                 {
703                         long uSeconds = seconds * 1000000;
704                         bool bState = false;
705
706                         while (uSeconds > (long) Int32.MaxValue) {
707                                 bState = s.Poll (Int32.MaxValue, selectMode);
708                                 if (bState) 
709                                         return true;
710                                 uSeconds -= Int32.MaxValue;
711                         }
712                         return s.Poll ((int) uSeconds, selectMode);
713                 }
714
715                 internal void ResizeOutBuf (int newSize)
716                 {
717                         if (newSize != outBufferLength) {
718                                 byte[] newBuf = new byte [newSize];
719                                 Buffer.BlockCopy (outBuffer, 0, newBuf, 0, newSize);
720                                 outBufferLength = newSize;
721                                 outBuffer = newBuf;
722                         }
723                 }
724
725                 public bool ResetConnection {
726                         get { return connReset; }
727                         set { connReset = value; }
728                 }
729
730                 public void SendPacket ()
731                 {
732                         // Reset connection flag is only valid for SQLBatch/RPC/DTC messages
733                         if (packetType != TdsPacketType.Query && packetType != TdsPacketType.RPC)
734                                 connReset = false;
735                         
736                         SendPhysicalPacket (true);
737                         nextOutBufferIndex = 0;
738                         packetType = TdsPacketType.None;
739                         // Reset connection-reset flag to false - as any exception would anyway close 
740                         // the whole connection
741                         connReset = false;
742                         packetsSent = 1;
743                 }
744                 
745                 private void SendPhysicalPacket (bool isLastSegment)
746                 {
747                         if (nextOutBufferIndex > headerLength || packetType == TdsPacketType.Cancel) {
748                                 byte status =  (byte) ((isLastSegment ? 0x01 : 0x00) | (connReset ? 0x08 : 0x00)); 
749                                 // packet type
750                                 Store (0, (byte) packetType);
751                                 Store (1, status);
752                                 Store (2, (short) nextOutBufferIndex );
753                                 Store (4, (byte) 0);
754                                 Store (5, (byte) 0);
755                                 if (tdsVersion >= TdsVersion.tds70)
756                                         Store (6, (byte) packetsSent);
757                                 else    
758                                         Store (6, (byte) 0);
759                                 Store (7, (byte) 0);
760
761                                 stream.Write (outBuffer, 0, nextOutBufferIndex);
762                                 stream.Flush ();
763                                 packetsSent++;
764                         }
765                 }
766                 
767                 public void Skip (long i)
768                 {
769                         for ( ; i > 0; i--)
770                                 GetByte ();
771                 }
772
773                 public void StartPacket (TdsPacketType type)
774                 {
775                         if (type != TdsPacketType.Cancel && inBufferIndex != inBufferLength)
776                                 inBufferIndex = inBufferLength;
777
778                         packetType = type;
779                         nextOutBufferIndex = headerLength;
780                 }
781
782                 private void Store (int index, byte value)
783                 {
784                         outBuffer[index] = value;
785                 }               
786
787                 private void Store (int index, short value)
788                 {
789                         outBuffer[index] = (byte) (((byte) (value >> 8)) & 0xff);
790                         outBuffer[index + 1] = (byte) (((byte) (value >> 0)) & 0xff);
791                 }
792
793                 #endregion // Methods
794 #if NET_2_0
795                 #region Async Methods
796
797                 public IAsyncResult BeginReadPacket (AsyncCallback callback, object stateObject)
798                 {
799                         TdsAsyncResult ar = new TdsAsyncResult (callback, stateObject);
800
801                         stream.BeginRead (tmpBuf, 0, 8, new AsyncCallback(OnReadPacketCallback), ar);
802                         return ar;
803                 }
804                 
805                 /// <returns>Packet size in bytes</returns>
806                 public int EndReadPacket (IAsyncResult ar)
807                 {
808                         if (!ar.IsCompleted)
809                                 ar.AsyncWaitHandle.WaitOne ();
810                         return (int) ((TdsAsyncResult) ar).ReturnValue;
811                 }
812                 
813
814                 public void OnReadPacketCallback (IAsyncResult socketAsyncResult)
815                 {
816                         TdsAsyncResult ar = (TdsAsyncResult) socketAsyncResult.AsyncState;
817                         int nread = stream.EndRead (socketAsyncResult);
818                         int n;
819                         
820                         while (nread < 8) {
821                                 n = Read (tmpBuf, nread, 8 - nread);
822                                 if (n <= 0) {
823                                         socket = null;
824                                         stream.Close ();
825                                         throw new IOException (n == 0 ? "Connection lost" : "Connection error");
826                                 }
827                                 nread += n;
828                         }
829
830                         TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
831                         if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply) 
832                         {
833                                 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
834                         }
835
836                         // figure out how many bytes are remaining in this packet.
837                         int len = Ntohs (tmpBuf, 2) - 8;
838
839                         if (len >= inBuffer.Length) 
840                                 inBuffer = new byte[len];
841
842                         if (len < 0) {
843                                 throw new Exception (String.Format ("Confused by a length of {0}", len));
844                         }
845
846                         GetPhysicalPacketData (len);
847                         int value = len + 8;
848                         ar.ReturnValue = ((object)value); // packet size
849                         ar.MarkComplete ();
850                 }
851                 
852                 #endregion // Async Methods
853 #endif // NET_2_0
854
855         }
856
857 }