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
;
90 socket
= new Socket (AddressFamily
.InterNetwork
, SocketType
.Stream
, ProtocolType
.Tcp
);
91 IPHostEntry hostEntry
= Dns
.Resolve (dataSource
);
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
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
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
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
];
143 public void Append (object o
)
145 switch (o
.GetType ().ToString ()) {
149 case "System.Byte[]":
158 case "System.String":
161 case "System.Double":
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
)
188 for ( ; i
< b
.Length
&& i
< len
; i
++)
191 for ( ; i
< len
; i
++)
195 public void Append (short s
)
197 if(!BitConverter
.IsLittleEndian
)
198 Append (Swap (BitConverter
.GetBytes(s
)));
200 Append (BitConverter
.GetBytes (s
));
203 public void Append (int i
)
205 if(!BitConverter
.IsLittleEndian
)
206 Append (Swap (BitConverter
.GetBytes(i
)));
208 Append (BitConverter
.GetBytes (i
));
211 public void Append (string s
)
213 if (tdsVersion
< TdsVersion
.tds70
)
214 Append (encoder
.GetBytes (s
));
216 foreach (char c
in s
)
217 if(!BitConverter
.IsLittleEndian
)
218 Append (Swap (BitConverter
.GetBytes (c
)));
220 Append (BitConverter
.GetBytes (c
));
223 // Appends with padding
224 public byte[] Append (string s
, int len
, byte pad
)
229 byte[] result
= encoder
.GetBytes (s
);
230 Append (result
, len
, pad
);
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));
252 if (!BitConverter
.IsLittleEndian
)
253 Append (Swap (BitConverter
.GetBytes (l
)));
255 Append (BitConverter
.GetBytes (l
));
263 private void ConnectCallback (IAsyncResult ar
)
265 Socket s
= (Socket
) ar
.AsyncState
;
266 if (Poll (s
, connectionTimeout
, SelectMode
.SelectWrite
)) {
267 socket
.EndConnect (ar
);
272 public byte GetByte ()
276 if (inBufferIndex
>= inBufferLength
) {
277 // out of data, read another physical packet.
278 GetPhysicalPacket ();
281 result
= inBuffer
[inBufferIndex
++];
285 public byte[] GetBytes (int len
, bool exclusiveBuffer
)
287 byte[] result
= null;
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
];
296 if (resBuffer
.Length
< len
)
297 resBuffer
= new byte[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
);
311 inBufferIndex
+= avail
;
317 public string GetString (int len
)
319 if (tdsVersion
== TdsVersion
.tds70
)
320 return GetString (len
, true);
322 return GetString (len
, false);
325 public string GetString (int len
, bool 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
);
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];
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));
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));
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));
383 return (BitConverter
.ToInt64 (input
, 0));
386 private void GetPhysicalPacket ()
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
];
407 throw new Exception (String
.Format ("Confused by a length of {0}", len
));
412 while (nread
< len
) {
413 nread
+= stream
.Read (inBuffer
, nread
, len
- nread
);
418 // adjust the bookkeeping info about the incoming buffer
419 inBufferLength
= len
;
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);
429 // return an int since we really want an _unsigned_
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;
451 while (uSeconds
> (long) Int32
.MaxValue
) {
452 bState
= s
.Poll (Int32
.MaxValue
, selectMode
);
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
;
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
) {
481 Store (0, (byte) packetType
);
482 Store (1, (byte) (isLastSegment
? 1 : 0));
483 Store (2, (short) nextOutBufferIndex
);
486 Store (6, (byte) (tdsVersion
== TdsVersion
.tds70
? 0x1 : 0x0));
489 stream
.Write (outBuffer
, 0, nextOutBufferIndex
);
495 public void Skip (int i
)
501 public void StartPacket (TdsPacketType type
)
503 if (type
!= TdsPacketType
.Cancel
&& inBufferIndex
!= inBufferLength
)
504 inBufferIndex
= inBufferLength
;
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