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);
94 IPHostEntry hostEntry = Dns.Resolve (dataSource);
95 endPoint = new IPEndPoint (hostEntry.AddressList [0], port);
97 // This replaces the code below for now
98 socket.Connect (endPoint);
101 FIXME: Asynchronous socket connection doesn't work right on linux, so comment
102 this out for now. This *does* do the right thing on windows
105 IAsyncResult asyncResult = socket.BeginConnect (endPoint, new AsyncCallback (ConnectCallback), socket);
107 if (timeout > 0 && !connected.WaitOne (new TimeSpan (0, 0, timeout), true))
108 throw Tds.CreateTimeoutException (dataSource, "Open()");
109 else if (timeout > 0 && !connected.WaitOne ())
110 throw Tds.CreateTimeoutException (dataSource, "Open()");
113 stream = new NetworkStream (socket);
114 } catch (SocketException e) {
115 throw new TdsInternalException ("Server does not exist or connection refused.", e);
119 #endregion // Constructors
123 public int CommandTimeout {
124 get { return commandTimeout; }
125 set { commandTimeout = value; }
128 internal Encoding Encoder {
129 get { return encoder; }
130 set { encoder = value; }
133 public int PacketSize {
134 get { return packetSize; }
135 set { packetSize = value; }
138 #endregion // Properties
142 public byte[] Swap(byte[] toswap) {
143 byte[] ret = new byte[toswap.Length];
144 for(int i = 0; i < toswap.Length; i++)
145 ret [toswap.Length - i - 1] = toswap[i];
149 public void Append (object o)
151 switch (o.GetType ().ToString ()) {
155 case "System.Byte[]":
164 case "System.String":
167 case "System.Double":
176 public void Append (byte b)
178 if (nextOutBufferIndex == outBufferLength) {
179 SendPhysicalPacket (false);
180 nextOutBufferIndex = headerLength;
182 Store (nextOutBufferIndex, b);
183 nextOutBufferIndex++;
186 public void Append (byte[] b)
188 Append (b, b.Length, (byte) 0);
191 public void Append (byte[] b, int len, byte pad)
194 for ( ; i < b.Length && i < len; i++)
197 for ( ; i < len; i++)
201 public void Append (short s)
203 if(!BitConverter.IsLittleEndian)
204 Append (Swap (BitConverter.GetBytes(s)));
206 Append (BitConverter.GetBytes (s));
209 public void Append (int i)
211 if(!BitConverter.IsLittleEndian)
212 Append (Swap (BitConverter.GetBytes(i)));
214 Append (BitConverter.GetBytes (i));
217 public void Append (string s)
219 if (tdsVersion < TdsVersion.tds70)
220 Append (encoder.GetBytes (s));
222 foreach (char c in s)
223 if(!BitConverter.IsLittleEndian)
224 Append (Swap (BitConverter.GetBytes (c)));
226 Append (BitConverter.GetBytes (c));
229 // Appends with padding
230 public byte[] Append (string s, int len, byte pad)
235 byte[] result = encoder.GetBytes (s);
236 Append (result, len, pad);
240 public void Append (double value)
242 Append (BitConverter.DoubleToInt64Bits (value));
245 public void Append (long l)
247 if (tdsVersion < TdsVersion.tds70) {
248 Append ((byte) (((byte) (l >> 56)) & 0xff));
249 Append ((byte) (((byte) (l >> 48)) & 0xff));
250 Append ((byte) (((byte) (l >> 40)) & 0xff));
251 Append ((byte) (((byte) (l >> 32)) & 0xff));
252 Append ((byte) (((byte) (l >> 24)) & 0xff));
253 Append ((byte) (((byte) (l >> 16)) & 0xff));
254 Append ((byte) (((byte) (l >> 8)) & 0xff));
255 Append ((byte) (((byte) (l >> 0)) & 0xff));
258 if (!BitConverter.IsLittleEndian)
259 Append (Swap (BitConverter.GetBytes (l)));
261 Append (BitConverter.GetBytes (l));
269 private void ConnectCallback (IAsyncResult ar)
271 Socket s = (Socket) ar.AsyncState;
272 if (Poll (s, connectionTimeout, SelectMode.SelectWrite)) {
273 socket.EndConnect (ar);
278 public byte GetByte ()
282 if (inBufferIndex >= inBufferLength) {
283 // out of data, read another physical packet.
284 GetPhysicalPacket ();
287 result = inBuffer[inBufferIndex++];
291 public byte[] GetBytes (int len, bool exclusiveBuffer)
293 byte[] result = null;
296 // Do not keep an internal result buffer larger than 16k.
297 // This would unnecessarily use up memory.
298 if (exclusiveBuffer || len > 16384)
299 result = new byte[len];
302 if (resBuffer.Length < len)
303 resBuffer = new byte[len];
309 if (inBufferIndex >= inBufferLength)
310 GetPhysicalPacket ();
312 int avail = inBufferLength - inBufferIndex;
313 avail = avail>len-i ? len-i : avail;
315 System.Array.Copy (inBuffer, inBufferIndex, result, i, avail);
317 inBufferIndex += avail;
323 public string GetString (int len)
325 if (tdsVersion == TdsVersion.tds70)
326 return GetString (len, true);
328 return GetString (len, false);
331 public string GetString (int len, bool wide)
334 char[] chars = new char[len];
335 for (int i = 0; i < len; ++i) {
336 int lo = ((byte) GetByte ()) & 0xFF;
337 int hi = ((byte) GetByte ()) & 0xFF;
338 chars[i] = (char) (lo | ( hi << 8));
340 return new String (chars);
343 byte[] result = new byte[len];
344 Array.Copy (GetBytes (len, false), result, len);
345 return (encoder.GetString (result));
349 public int GetNetShort ()
351 byte[] tmp = new byte[2];
354 return Ntohs (tmp, 0);
357 public short GetTdsShort ()
359 byte[] input = new byte[2];
361 for (int i = 0; i < 2; i += 1)
362 input[i] = GetByte ();
363 if(!BitConverter.IsLittleEndian)
364 return (BitConverter.ToInt16 (Swap (input), 0));
366 return (BitConverter.ToInt16 (input, 0));
370 public int GetTdsInt ()
372 byte[] input = new byte[4];
373 for (int i = 0; i < 4; i += 1)
374 input[i] = GetByte ();
375 if(!BitConverter.IsLittleEndian)
376 return (BitConverter.ToInt32 (Swap (input), 0));
378 return (BitConverter.ToInt32 (input, 0));
381 public long GetTdsInt64 ()
383 byte[] input = new byte[8];
384 for (int i = 0; i < 8; i += 1)
385 input[i] = GetByte ();
386 if(!BitConverter.IsLittleEndian)
387 return (BitConverter.ToInt64 (Swap (input), 0));
389 return (BitConverter.ToInt64 (input, 0));
392 private void GetPhysicalPacket ()
394 int dataLength = GetPhysicalPacketHeader ();
395 GetPhysicalPacketData (dataLength);
398 private int GetPhysicalPacketHeader ()
404 nread += stream.Read (tmpBuf, nread, 8 - nread);
406 TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
407 if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
409 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
412 // figure out how many bytes are remaining in this packet.
413 int len = Ntohs (tmpBuf, 2) - 8;
415 if (len >= inBuffer.Length)
416 inBuffer = new byte[len];
419 throw new Exception (String.Format ("Confused by a length of {0}", len));
426 private void GetPhysicalPacketData (int length)
430 while (nread < length) {
431 nread += stream.Read (inBuffer, nread, length - nread);
436 // adjust the bookkeeping info about the incoming buffer
437 inBufferLength = length;
442 private static int Ntohs (byte[] buf, int offset)
444 int lo = ((int) buf[offset + 1] & 0xff);
445 int hi = (((int) buf[offset] & 0xff ) << 8);
448 // return an int since we really want an _unsigned_
453 // If out of data, read another physical packet.
454 if (inBufferIndex >= inBufferLength)
455 GetPhysicalPacket ();
457 return inBuffer[inBufferIndex];
460 public bool Poll (int seconds, SelectMode selectMode)
462 return Poll (socket, seconds, selectMode);
465 private bool Poll (Socket s, int seconds, SelectMode selectMode)
467 long uSeconds = seconds * 1000000;
470 while (uSeconds > (long) Int32.MaxValue) {
471 bState = s.Poll (Int32.MaxValue, selectMode);
474 uSeconds -= Int32.MaxValue;
476 return s.Poll ((int) uSeconds, selectMode);
479 internal void ResizeOutBuf (int newSize)
481 if (newSize > outBufferLength) {
482 byte[] newBuf = new byte [newSize];
483 Array.Copy (outBuffer, 0, newBuf, 0, outBufferLength);
484 outBufferLength = newSize;
489 public void SendPacket ()
491 SendPhysicalPacket (true);
492 nextOutBufferIndex = 0;
493 packetType = TdsPacketType.None;
496 private void SendPhysicalPacket (bool isLastSegment)
498 if (nextOutBufferIndex > headerLength || packetType == TdsPacketType.Cancel) {
500 Store (0, (byte) packetType);
501 Store (1, (byte) (isLastSegment ? 1 : 0));
502 Store (2, (short) nextOutBufferIndex );
505 Store (6, (byte) (tdsVersion == TdsVersion.tds70 ? 0x1 : 0x0));
508 stream.Write (outBuffer, 0, nextOutBufferIndex);
514 public void Skip (int i)
520 public void StartPacket (TdsPacketType type)
522 if (type != TdsPacketType.Cancel && inBufferIndex != inBufferLength)
523 inBufferIndex = inBufferLength;
526 nextOutBufferIndex = headerLength;
529 private void Store (int index, byte value)
531 outBuffer[index] = value;
534 private void Store (int index, short value)
536 outBuffer[index] = (byte) (((byte) (value >> 8)) & 0xff);
537 outBuffer[index + 1] = (byte) (((byte) (value >> 0)) & 0xff);
540 #endregion // Methods
542 #region Async Methods
544 public IAsyncResult BeginReadPacket (AsyncCallback callback, object stateObject)
546 TdsAsyncResult ar = new TdsAsyncResult (callback, stateObject);
548 stream.BeginRead (tmpBuf, 0, 8, new AsyncCallback(OnReadPacketCallback), ar);
552 /// <returns>Packet size in bytes</returns>
553 public int EndReadPacket (IAsyncResult ar)
556 ar.AsyncWaitHandle.WaitOne ();
557 return (int) ((TdsAsyncResult) ar).ReturnValue;
561 public void OnReadPacketCallback (IAsyncResult socketAsyncResult)
563 TdsAsyncResult ar = (TdsAsyncResult) socketAsyncResult.AsyncState;
564 int nread = stream.EndRead (socketAsyncResult);
567 nread += stream.Read (tmpBuf, nread, 8 - nread);
569 TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
570 if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
572 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
575 // figure out how many bytes are remaining in this packet.
576 int len = Ntohs (tmpBuf, 2) - 8;
578 if (len >= inBuffer.Length)
579 inBuffer = new byte[len];
582 throw new Exception (String.Format ("Confused by a length of {0}", len));
585 GetPhysicalPacketData (len);
587 ar.ReturnValue = ((object)value); // packet size
591 #endregion // Async Methods