2 // Mono.Data.Tds.Protocol.TdsComm.cs
5 // Tim Coleman (tim@timcoleman.com)
7 // Copyright (C) 2002 Tim Coleman
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Net.Sockets;
35 using System.Threading;
37 namespace Mono.Data.Tds.Protocol {
38 internal sealed class TdsComm
44 TdsPacketType packetType = TdsPacketType.None;
49 int connectionTimeout;
53 int nextOutBufferIndex = 0;
57 int inBufferIndex = 0;
59 static int headerLength = 8;
61 byte[] tmpBuf = new byte[8];
62 byte[] resBuffer = new byte[256];
65 int packetsReceived = 0;
68 TdsVersion tdsVersion;
70 ManualResetEvent connected = new ManualResetEvent (false);
76 [MonoTODO ("Fix when asynchronous socket connect works on Linux.")]
77 public TdsComm (string dataSource, int port, int packetSize, int timeout, TdsVersion tdsVersion)
79 this.packetSize = packetSize;
80 this.tdsVersion = tdsVersion;
81 this.dataSource = dataSource;
82 this.connectionTimeout = timeout;
84 outBuffer = new byte[packetSize];
85 inBuffer = new byte[packetSize];
87 outBufferLength = packetSize;
88 inBufferLength = packetSize;
93 socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
95 IPHostEntry hostEntry = Dns.GetHostEntry (this.dataSource);
97 IPHostEntry hostEntry = Dns.Resolve (this.dataSource);
99 endPoint = new IPEndPoint (hostEntry.AddressList [0], port);
101 // This replaces the code below for now
102 socket.Connect (endPoint);
105 FIXME: Asynchronous socket connection doesn't work right on linux, so comment
106 this out for now. This *does* do the right thing on windows
109 IAsyncResult asyncResult = socket.BeginConnect (endPoint, new AsyncCallback (ConnectCallback), socket);
111 if (timeout > 0 && !connected.WaitOne (new TimeSpan (0, 0, timeout), true))
112 throw Tds.CreateTimeoutException (dataSource, "Open()");
113 else if (timeout > 0 && !connected.WaitOne ())
114 throw Tds.CreateTimeoutException (dataSource, "Open()");
117 stream = new NetworkStream (socket);
118 } catch (SocketException e) {
119 throw new TdsInternalException ("Server does not exist or connection refused.", e);
123 #endregion // Constructors
127 public int CommandTimeout {
128 get { return commandTimeout; }
129 set { commandTimeout = value; }
132 internal Encoding Encoder {
133 get { return encoder; }
134 set { encoder = value; }
137 public int PacketSize {
138 get { return packetSize; }
139 set { packetSize = value; }
142 #endregion // Properties
146 public byte[] Swap(byte[] toswap) {
147 byte[] ret = new byte[toswap.Length];
148 for(int i = 0; i < toswap.Length; i++)
149 ret [toswap.Length - i - 1] = toswap[i];
153 public void Append (object o)
155 if (o == null || o == DBNull.Value) {
159 switch (Type.GetTypeCode (o.GetType ())) {
163 case TypeCode.Boolean:
169 case TypeCode.Object :
173 case TypeCode.Int16 :
176 case TypeCode.Int32 :
179 case TypeCode.String :
182 case TypeCode.Double :
185 case TypeCode.Single :
188 case TypeCode.Int64 :
191 case TypeCode.Decimal:
192 Append ((decimal) o, 17);
194 case TypeCode.DateTime:
195 Append ((DateTime) o, 8);
198 throw new InvalidOperationException (String.Format ("Object Type :{0} , not being appended", o.GetType ()));
201 public void Append (byte b)
203 if (nextOutBufferIndex == outBufferLength) {
204 SendPhysicalPacket (false);
205 nextOutBufferIndex = headerLength;
207 Store (nextOutBufferIndex, b);
208 nextOutBufferIndex++;
211 public void Append (DateTime t, int bytes)
213 DateTime epoch = new DateTime (1900,1,1);
215 TimeSpan span = t - epoch;
216 int days = span.Days ;
220 long ms = (span.Hours * 3600 + span.Minutes * 60 + span.Seconds)*1000L + (long)span.Milliseconds;
221 val = (int) ((ms*300)/1000);
224 } else if (bytes ==4) {
225 val = span.Hours * 60 + span.Minutes;
226 Append ((ushort) days);
227 Append ((short) val);
229 throw new Exception ("Invalid No of bytes");
233 public void Append (byte[] b)
235 Append (b, b.Length, (byte) 0);
238 public void Append (byte[] b, int len, byte pad)
241 for ( ; i < b.Length && i < len; i++)
244 for ( ; i < len; i++)
248 public void Append (short s)
250 if(!BitConverter.IsLittleEndian)
251 Append (Swap (BitConverter.GetBytes(s)));
253 Append (BitConverter.GetBytes (s));
256 public void Append (ushort s)
258 if(!BitConverter.IsLittleEndian)
259 Append (Swap (BitConverter.GetBytes(s)));
261 Append (BitConverter.GetBytes (s));
264 public void Append (int i)
266 if(!BitConverter.IsLittleEndian)
267 Append (Swap (BitConverter.GetBytes(i)));
269 Append (BitConverter.GetBytes (i));
272 public void Append (string s)
274 if (tdsVersion < TdsVersion.tds70)
275 Append (encoder.GetBytes (s));
277 foreach (char c in s)
278 if(!BitConverter.IsLittleEndian)
279 Append (Swap (BitConverter.GetBytes (c)));
281 Append (BitConverter.GetBytes (c));
284 // Appends with padding
285 public byte[] Append (string s, int len, byte pad)
290 byte[] result = encoder.GetBytes (s);
291 Append (result, len, pad);
295 public void Append (double value)
297 if (!BitConverter.IsLittleEndian)
298 Append (Swap (BitConverter.GetBytes (value)));
300 Append (BitConverter.GetBytes (value));
303 public void Append (float value)
305 if (!BitConverter.IsLittleEndian)
306 Append (Swap (BitConverter.GetBytes (value)));
308 Append (BitConverter.GetBytes (value));
311 public void Append (long l)
313 if (!BitConverter.IsLittleEndian)
314 Append (Swap (BitConverter.GetBytes (l)));
316 Append (BitConverter.GetBytes (l));
319 public void Append (decimal d, int bytes)
321 int[] arr = Decimal.GetBits (d);
322 byte sign = (d > 0 ? (byte)1 : (byte)0);
335 private void ConnectCallback (IAsyncResult ar)
337 Socket s = (Socket) ar.AsyncState;
338 if (Poll (s, connectionTimeout, SelectMode.SelectWrite)) {
339 socket.EndConnect (ar);
344 public byte GetByte ()
348 if (inBufferIndex >= inBufferLength) {
349 // out of data, read another physical packet.
350 GetPhysicalPacket ();
352 result = inBuffer[inBufferIndex++];
356 public byte[] GetBytes (int len, bool exclusiveBuffer)
358 byte[] result = null;
361 // Do not keep an internal result buffer larger than 16k.
362 // This would unnecessarily use up memory.
363 if (exclusiveBuffer || len > 16384)
364 result = new byte[len];
367 if (resBuffer.Length < len)
368 resBuffer = new byte[len];
374 if (inBufferIndex >= inBufferLength)
375 GetPhysicalPacket ();
377 int avail = inBufferLength - inBufferIndex;
378 avail = avail>len-i ? len-i : avail;
380 System.Array.Copy (inBuffer, inBufferIndex, result, i, avail);
382 inBufferIndex += avail;
388 public string GetString (int len)
390 if (tdsVersion == TdsVersion.tds70)
391 return GetString (len, true);
393 return GetString (len, false);
396 public string GetString (int len, bool wide)
399 char[] chars = new char[len];
400 for (int i = 0; i < len; ++i) {
401 int lo = ((byte) GetByte ()) & 0xFF;
402 int hi = ((byte) GetByte ()) & 0xFF;
403 chars[i] = (char) (lo | ( hi << 8));
405 return new String (chars);
408 byte[] result = new byte[len];
409 Array.Copy (GetBytes (len, false), result, len);
410 return (encoder.GetString (result));
414 public int GetNetShort ()
416 byte[] tmp = new byte[2];
419 return Ntohs (tmp, 0);
422 public short GetTdsShort ()
424 byte[] input = new byte[2];
426 for (int i = 0; i < 2; i += 1)
427 input[i] = GetByte ();
428 if(!BitConverter.IsLittleEndian)
429 return (BitConverter.ToInt16 (Swap (input), 0));
431 return (BitConverter.ToInt16 (input, 0));
435 public int GetTdsInt ()
437 byte[] input = new byte[4];
438 for (int i = 0; i < 4; i += 1) {
439 input[i] = GetByte ();
441 if(!BitConverter.IsLittleEndian)
442 return (BitConverter.ToInt32 (Swap (input), 0));
444 return (BitConverter.ToInt32 (input, 0));
447 public long GetTdsInt64 ()
449 byte[] input = new byte[8];
450 for (int i = 0; i < 8; i += 1)
451 input[i] = GetByte ();
452 if(!BitConverter.IsLittleEndian)
453 return (BitConverter.ToInt64 (Swap (input), 0));
455 return (BitConverter.ToInt64 (input, 0));
458 private void GetPhysicalPacket ()
460 int dataLength = GetPhysicalPacketHeader ();
461 GetPhysicalPacketData (dataLength);
464 private int GetPhysicalPacketHeader ()
470 nread += stream.Read (tmpBuf, nread, 8 - nread);
472 TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
473 if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
475 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
478 // figure out how many bytes are remaining in this packet.
479 int len = Ntohs (tmpBuf, 2) - 8;
480 if (len >= inBuffer.Length)
481 inBuffer = new byte[len];
484 throw new Exception (String.Format ("Confused by a length of {0}", len));
491 private void GetPhysicalPacketData (int length)
495 while (nread < length) {
496 nread += stream.Read (inBuffer, nread, length - nread);
500 // adjust the bookkeeping info about the incoming buffer
501 inBufferLength = length;
506 private static int Ntohs (byte[] buf, int offset)
508 int lo = ((int) buf[offset + 1] & 0xff);
509 int hi = (((int) buf[offset] & 0xff ) << 8);
512 // return an int since we really want an _unsigned_
517 // If out of data, read another physical packet.
518 if (inBufferIndex >= inBufferLength)
519 GetPhysicalPacket ();
521 return inBuffer[inBufferIndex];
524 public bool Poll (int seconds, SelectMode selectMode)
526 return Poll (socket, seconds, selectMode);
529 private bool Poll (Socket s, int seconds, SelectMode selectMode)
531 long uSeconds = seconds * 1000000;
534 while (uSeconds > (long) Int32.MaxValue) {
535 bState = s.Poll (Int32.MaxValue, selectMode);
538 uSeconds -= Int32.MaxValue;
540 return s.Poll ((int) uSeconds, selectMode);
543 internal void ResizeOutBuf (int newSize)
545 if (newSize != outBufferLength) {
546 byte[] newBuf = new byte [newSize];
547 Array.Copy (outBuffer, 0, newBuf, 0, newSize);
548 outBufferLength = newSize;
553 public void SendPacket ()
555 SendPhysicalPacket (true);
556 nextOutBufferIndex = 0;
557 packetType = TdsPacketType.None;
560 private void SendPhysicalPacket (bool isLastSegment)
562 if (nextOutBufferIndex > headerLength || packetType == TdsPacketType.Cancel) {
564 Store (0, (byte) packetType);
565 Store (1, (byte) (isLastSegment ? 1 : 0));
566 Store (2, (short) nextOutBufferIndex );
569 Store (6, (byte) (tdsVersion == TdsVersion.tds70 ? 0x1 : 0x0));
572 stream.Write (outBuffer, 0, nextOutBufferIndex);
578 public void Skip (long i)
584 public void StartPacket (TdsPacketType type)
586 if (type != TdsPacketType.Cancel && inBufferIndex != inBufferLength)
587 inBufferIndex = inBufferLength;
590 nextOutBufferIndex = headerLength;
593 private void Store (int index, byte value)
595 outBuffer[index] = value;
598 private void Store (int index, short value)
600 outBuffer[index] = (byte) (((byte) (value >> 8)) & 0xff);
601 outBuffer[index + 1] = (byte) (((byte) (value >> 0)) & 0xff);
604 #endregion // Methods
606 #region Async Methods
608 public IAsyncResult BeginReadPacket (AsyncCallback callback, object stateObject)
610 TdsAsyncResult ar = new TdsAsyncResult (callback, stateObject);
612 stream.BeginRead (tmpBuf, 0, 8, new AsyncCallback(OnReadPacketCallback), ar);
616 /// <returns>Packet size in bytes</returns>
617 public int EndReadPacket (IAsyncResult ar)
620 ar.AsyncWaitHandle.WaitOne ();
621 return (int) ((TdsAsyncResult) ar).ReturnValue;
625 public void OnReadPacketCallback (IAsyncResult socketAsyncResult)
627 TdsAsyncResult ar = (TdsAsyncResult) socketAsyncResult.AsyncState;
628 int nread = stream.EndRead (socketAsyncResult);
631 nread += stream.Read (tmpBuf, nread, 8 - nread);
633 TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
634 if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
636 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
639 // figure out how many bytes are remaining in this packet.
640 int len = Ntohs (tmpBuf, 2) - 8;
642 if (len >= inBuffer.Length)
643 inBuffer = new byte[len];
646 throw new Exception (String.Format ("Confused by a length of {0}", len));
649 GetPhysicalPacketData (len);
651 ar.ReturnValue = ((object)value); // packet size
655 #endregion // Async Methods