**** Merged from MCS ****
[mono-project.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / TdsComm.cs
blobdd15580154101d9186cf3d23e24b821c474c6ff7
1 //
2 // Mono.Data.Tds.Protocol.TdsComm.cs
3 //
4 // Author:
5 // Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) 2002 Tim Coleman
8 //
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:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
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.
31 using System;
32 using System.Net;
33 using System.Net.Sockets;
34 using System.Text;
35 using System.Threading;
37 namespace Mono.Data.Tds.Protocol {
38 internal sealed class TdsComm
40 #region Fields
42 NetworkStream stream;
43 int packetSize;
44 TdsPacketType packetType = TdsPacketType.None;
45 Encoding encoder;
47 string dataSource;
48 int commandTimeout;
49 int connectionTimeout;
51 byte[] outBuffer;
52 int outBufferLength;
53 int nextOutBufferIndex = 0;
55 byte[] inBuffer;
56 int inBufferLength;
57 int inBufferIndex = 0;
59 static int headerLength = 8;
61 byte[] tmpBuf = new byte[8];
62 byte[] resBuffer = new byte[256];
64 int packetsSent = 0;
65 int packetsReceived = 0;
67 Socket socket;
68 TdsVersion tdsVersion;
70 ManualResetEvent connected = new ManualResetEvent (false);
72 #endregion // Fields
74 #region Constructors
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;
90 socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
91 IPHostEntry hostEntry = Dns.Resolve (dataSource);
92 IPEndPoint endPoint;
93 endPoint = new IPEndPoint (hostEntry.AddressList [0], port);
95 // This replaces the code below for now
96 socket.Connect (endPoint);
99 FIXME: Asynchronous socket connection doesn't work right on linux, so comment
100 this out for now. This *does* do the right thing on windows
102 connected.Reset ();
103 IAsyncResult asyncResult = socket.BeginConnect (endPoint, new AsyncCallback (ConnectCallback), socket);
105 if (timeout > 0 && !connected.WaitOne (new TimeSpan (0, 0, timeout), true))
106 throw Tds.CreateTimeoutException (dataSource, "Open()");
107 else if (timeout > 0 && !connected.WaitOne ())
108 throw Tds.CreateTimeoutException (dataSource, "Open()");
111 stream = new NetworkStream (socket);
114 #endregion // Constructors
116 #region Properties
118 public int CommandTimeout {
119 get { return commandTimeout; }
120 set { commandTimeout = value; }
123 internal Encoding Encoder {
124 set { encoder = value; }
127 public int PacketSize {
128 get { return packetSize; }
129 set { packetSize = value; }
132 #endregion // Properties
134 #region Methods
136 public byte[] Swap(byte[] toswap) {
137 byte[] ret = new byte[toswap.Length];
138 for(int i = 0; i < toswap.Length; i++)
139 ret [toswap.Length - i - 1] = toswap[i];
141 return ret;
143 public void Append (object o)
145 switch (o.GetType ().ToString ()) {
146 case "System.Byte":
147 Append ((byte) o);
148 return;
149 case "System.Byte[]":
150 Append ((byte[]) o);
151 return;
152 case "System.Int16":
153 Append ((short) o);
154 return;
155 case "System.Int32":
156 Append ((int) o);
157 return;
158 case "System.String":
159 Append ((string) o);
160 return;
161 case "System.Double":
162 Append ((double) o);
163 return;
164 case "System.Int64":
165 Append ((long) o);
166 return;
170 public void Append (byte b)
172 if (nextOutBufferIndex == outBufferLength) {
173 SendPhysicalPacket (false);
174 nextOutBufferIndex = headerLength;
176 Store (nextOutBufferIndex, b);
177 nextOutBufferIndex++;
180 public void Append (byte[] b)
182 Append (b, b.Length, (byte) 0);
185 public void Append (byte[] b, int len, byte pad)
187 int i = 0;
188 for ( ; i < b.Length && i < len; i++)
189 Append (b[i]);
191 for ( ; i < len; i++)
192 Append (pad);
195 public void Append (short s)
197 if(!BitConverter.IsLittleEndian)
198 Append (Swap (BitConverter.GetBytes(s)));
199 else
200 Append (BitConverter.GetBytes (s));
203 public void Append (int i)
205 if(!BitConverter.IsLittleEndian)
206 Append (Swap (BitConverter.GetBytes(i)));
207 else
208 Append (BitConverter.GetBytes (i));
211 public void Append (string s)
213 if (tdsVersion < TdsVersion.tds70)
214 Append (encoder.GetBytes (s));
215 else
216 foreach (char c in s)
217 if(!BitConverter.IsLittleEndian)
218 Append (Swap (BitConverter.GetBytes (c)));
219 else
220 Append (BitConverter.GetBytes (c));
223 // Appends with padding
224 public byte[] Append (string s, int len, byte pad)
226 if (s == null)
227 return new byte[0];
229 byte[] result = encoder.GetBytes (s);
230 Append (result, len, pad);
231 return result;
234 public void Append (double value)
236 Append (BitConverter.DoubleToInt64Bits (value));
239 public void Append (long l)
241 if (tdsVersion < TdsVersion.tds70) {
242 Append ((byte) (((byte) (l >> 56)) & 0xff));
243 Append ((byte) (((byte) (l >> 48)) & 0xff));
244 Append ((byte) (((byte) (l >> 40)) & 0xff));
245 Append ((byte) (((byte) (l >> 32)) & 0xff));
246 Append ((byte) (((byte) (l >> 24)) & 0xff));
247 Append ((byte) (((byte) (l >> 16)) & 0xff));
248 Append ((byte) (((byte) (l >> 8)) & 0xff));
249 Append ((byte) (((byte) (l >> 0)) & 0xff));
251 else
252 if (!BitConverter.IsLittleEndian)
253 Append (Swap (BitConverter.GetBytes (l)));
254 else
255 Append (BitConverter.GetBytes (l));
258 public void Close ()
260 stream.Close ();
263 private void ConnectCallback (IAsyncResult ar)
265 Socket s = (Socket) ar.AsyncState;
266 if (Poll (s, connectionTimeout, SelectMode.SelectWrite)) {
267 socket.EndConnect (ar);
268 connected.Set ();
272 public byte GetByte ()
274 byte result;
276 if (inBufferIndex >= inBufferLength) {
277 // out of data, read another physical packet.
278 GetPhysicalPacket ();
281 result = inBuffer[inBufferIndex++];
282 return result;
285 public byte[] GetBytes (int len, bool exclusiveBuffer)
287 byte[] result = null;
288 int i;
290 // Do not keep an internal result buffer larger than 16k.
291 // This would unnecessarily use up memory.
292 if (exclusiveBuffer || len > 16384)
293 result = new byte[len];
294 else
296 if (resBuffer.Length < len)
297 resBuffer = new byte[len];
298 result = resBuffer;
301 for (i = 0; i<len; )
303 if (inBufferIndex >= inBufferLength)
304 GetPhysicalPacket ();
306 int avail = inBufferLength - inBufferIndex;
307 avail = avail>len-i ? len-i : avail;
309 System.Array.Copy (inBuffer, inBufferIndex, result, i, avail);
310 i += avail;
311 inBufferIndex += avail;
314 return result;
317 public string GetString (int len)
319 if (tdsVersion == TdsVersion.tds70)
320 return GetString (len, true);
321 else
322 return GetString (len, false);
325 public string GetString (int len, bool wide)
327 if (wide) {
328 char[] chars = new char[len];
329 for (int i = 0; i < len; ++i) {
330 int lo = ((byte) GetByte ()) & 0xFF;
331 int hi = ((byte) GetByte ()) & 0xFF;
332 chars[i] = (char) (lo | ( hi << 8));
334 return new String (chars);
336 else {
337 byte[] result = new byte[len];
338 Array.Copy (GetBytes (len, false), result, len);
339 return (encoder.GetString (result));
343 public int GetNetShort ()
345 byte[] tmp = new byte[2];
346 tmp[0] = GetByte ();
347 tmp[1] = GetByte ();
348 return Ntohs (tmp, 0);
351 public short GetTdsShort ()
353 byte[] input = new byte[2];
355 for (int i = 0; i < 2; i += 1)
356 input[i] = GetByte ();
357 if(!BitConverter.IsLittleEndian)
358 return (BitConverter.ToInt16 (Swap (input), 0));
359 else
360 return (BitConverter.ToInt16 (input, 0));
364 public int GetTdsInt ()
366 byte[] input = new byte[4];
367 for (int i = 0; i < 4; i += 1)
368 input[i] = GetByte ();
369 if(!BitConverter.IsLittleEndian)
370 return (BitConverter.ToInt32 (Swap (input), 0));
371 else
372 return (BitConverter.ToInt32 (input, 0));
375 public long GetTdsInt64 ()
377 byte[] input = new byte[8];
378 for (int i = 0; i < 8; i += 1)
379 input[i] = GetByte ();
380 if(!BitConverter.IsLittleEndian)
381 return (BitConverter.ToInt64 (Swap (input), 0));
382 else
383 return (BitConverter.ToInt64 (input, 0));
386 private void GetPhysicalPacket ()
388 int nread = 0;
390 // read the header
391 while (nread < 8)
392 nread += stream.Read (tmpBuf, nread, 8 - nread);
394 TdsPacketType packetType = (TdsPacketType) tmpBuf[0];
395 if (packetType != TdsPacketType.Logon && packetType != TdsPacketType.Query && packetType != TdsPacketType.Reply)
397 throw new Exception (String.Format ("Unknown packet type {0}", tmpBuf[0]));
400 // figure out how many bytes are remaining in this packet.
401 int len = Ntohs (tmpBuf, 2) - 8;
403 if (len >= inBuffer.Length)
404 inBuffer = new byte[len];
406 if (len < 0) {
407 throw new Exception (String.Format ("Confused by a length of {0}", len));
410 // now get the data
411 nread = 0;
412 while (nread < len) {
413 nread += stream.Read (inBuffer, nread, len - nread);
416 packetsReceived++;
418 // adjust the bookkeeping info about the incoming buffer
419 inBufferLength = len;
420 inBufferIndex = 0;
423 private static int Ntohs (byte[] buf, int offset)
425 int lo = ((int) buf[offset + 1] & 0xff);
426 int hi = (((int) buf[offset] & 0xff ) << 8);
428 return hi | lo;
429 // return an int since we really want an _unsigned_
432 public byte Peek ()
434 // If out of data, read another physical packet.
435 if (inBufferIndex >= inBufferLength)
436 GetPhysicalPacket ();
438 return inBuffer[inBufferIndex];
441 public bool Poll (int seconds, SelectMode selectMode)
443 return Poll (socket, seconds, selectMode);
446 private bool Poll (Socket s, int seconds, SelectMode selectMode)
448 long uSeconds = seconds * 1000000;
449 bool bState = false;
451 while (uSeconds > (long) Int32.MaxValue) {
452 bState = s.Poll (Int32.MaxValue, selectMode);
453 if (bState)
454 return true;
455 uSeconds -= Int32.MaxValue;
457 return s.Poll ((int) uSeconds, selectMode);
460 internal void ResizeOutBuf (int newSize)
462 if (newSize > outBufferLength) {
463 byte[] newBuf = new byte [newSize];
464 Array.Copy (outBuffer, 0, newBuf, 0, outBufferLength);
465 outBufferLength = newSize;
466 outBuffer = newBuf;
470 public void SendPacket ()
472 SendPhysicalPacket (true);
473 nextOutBufferIndex = 0;
474 packetType = TdsPacketType.None;
477 private void SendPhysicalPacket (bool isLastSegment)
479 if (nextOutBufferIndex > headerLength || packetType == TdsPacketType.Cancel) {
480 // packet type
481 Store (0, (byte) packetType);
482 Store (1, (byte) (isLastSegment ? 1 : 0));
483 Store (2, (short) nextOutBufferIndex );
484 Store (4, (byte) 0);
485 Store (5, (byte) 0);
486 Store (6, (byte) (tdsVersion == TdsVersion.tds70 ? 0x1 : 0x0));
487 Store (7, (byte) 0);
489 stream.Write (outBuffer, 0, nextOutBufferIndex);
490 stream.Flush ();
491 packetsSent++;
495 public void Skip (int i)
497 for ( ; i > 0; i--)
498 GetByte ();
501 public void StartPacket (TdsPacketType type)
503 if (type != TdsPacketType.Cancel && inBufferIndex != inBufferLength)
504 inBufferIndex = inBufferLength;
506 packetType = type;
507 nextOutBufferIndex = headerLength;
510 private void Store (int index, byte value)
512 outBuffer[index] = value;
515 private void Store (int index, short value)
517 outBuffer[index] = (byte) (((byte) (value >> 8)) & 0xff);
518 outBuffer[index + 1] = (byte) (((byte) (value >> 0)) & 0xff);
521 #endregion // Methods