2 // Mono.Data.Tds.Protocol.Tds70.cs
5 // Tim Coleman (tim@timcoleman.com)
6 // Diego Caravana (diego@toth.it)
7 // Sebastien Pouliot (sebastien@ximian.com)
8 // Daniel Morgan (danielmorgan@verizon.net)
10 // Copyright (C) 2002 Tim Coleman
11 // Portions (C) 2003 Motus Technologies Inc. (http://www.motus.com)
12 // Portions (C) 2003 Daniel Morgan
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using Mono
.Security
.Protocol
.Ntlm
;
40 namespace Mono
.Data
.Tds
.Protocol
{
41 public class Tds70
: Tds
45 public readonly static TdsVersion Version
= TdsVersion
.tds70
;
51 public Tds70 (string server
, int port
)
52 : this (server
, port
, 512, 15)
56 public Tds70 (string server
, int port
, int packetSize
, int timeout
)
57 : base (server
, port
, packetSize
, timeout
, Version
)
61 #endregion // Constructors
65 private string BuildExec (string sql
)
67 string esql
= sql
.Replace ("'", "''"); // escape single quote
68 if (Parameters
!= null && Parameters
.Count
> 0)
69 return BuildProcedureCall (String
.Format ("sp_executesql N'{0}', N'{1}', ", esql
, BuildPreparedParameters ()));
71 return BuildProcedureCall (String
.Format ("sp_executesql N'{0}'", esql
));
74 private string BuildParameters ()
76 if (Parameters
== null || Parameters
.Count
== 0)
79 StringBuilder result
= new StringBuilder ();
80 foreach (TdsMetaParameter p
in Parameters
) {
81 if (p
.Direction
!= TdsParameterDirection
.ReturnValue
) {
82 if (result
.Length
> 0)
84 if (p
.Direction
== TdsParameterDirection
.InputOutput
)
85 result
.Append (String
.Format("{0}={0} output", p
.ParameterName
));
87 result
.Append (FormatParameter (p
));
90 return result
.ToString ();
93 private string BuildPreparedParameters ()
95 StringBuilder parms
= new StringBuilder ();
96 foreach (TdsMetaParameter p
in Parameters
) {
99 parms
.Append (p
.Prepare ());
100 if (p
.Direction
== TdsParameterDirection
.Output
)
101 parms
.Append (" output");
103 return parms
.ToString ();
106 private string BuildPreparedQuery (string id
)
108 return BuildProcedureCall (String
.Format ("sp_execute {0},", id
));
111 private string BuildProcedureCall (string procedure
)
113 string exec
= String
.Empty
;
115 StringBuilder declare
= new StringBuilder ();
116 StringBuilder
select = new StringBuilder ();
117 StringBuilder
set = new StringBuilder ();
120 if (Parameters
!= null) {
121 foreach (TdsMetaParameter p
in Parameters
) {
122 if (p
.Direction
!= TdsParameterDirection
.Input
) {
125 select.Append ("select ");
127 select.Append (", ");
128 select.Append (p
.ParameterName
);
130 declare
.Append (String
.Format ("declare {0}\n", p
.Prepare ()));
132 if (p
.Direction
!= TdsParameterDirection
.ReturnValue
) {
133 if( p
.Direction
== TdsParameterDirection
.InputOutput
)
134 set.Append (String
.Format ("set {0}\n", FormatParameter(p
)));
136 set.Append (String
.Format ("set {0}=NULL\n", p
.ParameterName
));
142 if (p
.Direction
== TdsParameterDirection
.ReturnValue
) {
143 exec
= p
.ParameterName
+ "=";
147 if (count
> 0 || exec
.Length
> 0)
148 exec
= "exec " + exec
;
150 return String
.Format ("{0}{1}{2}{3} {4}\n{5}", declare
.ToString (), set.ToString (), exec
, procedure
, BuildParameters (), select.ToString ());
153 public override bool Connect (TdsConnectionParameters connectionParameters
)
156 throw new InvalidOperationException ("The connection is already open.");
158 connectionParms
= connectionParameters
;
160 SetLanguage (connectionParameters
.Language
);
161 SetCharset ("utf-8");
163 byte[] empty
= new byte[0];
167 byte[] domainMagic
= { 6, 0x7d, 0x0f, 0xfd, 0xff, 0x0, 0x0, 0x0,
168 0x0, 0xe0, 0x83, 0x0, 0x0,
169 0x68, 0x01, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00 };
170 byte[] sqlserverMagic
= { 6, 0x83, 0xf2, 0xf8,
172 0x0, 0xe0, 0x03, 0x0,
173 0x0, 0x88, 0xff, 0xff, 0xff, 0x36,
177 if (connectionParameters
.DomainLogin
== true)
180 magic
= sqlserverMagic
;
182 string username
= connectionParameters
.User
;
184 string domain
= Environment
.UserDomainName
;
185 domain
= connectionParameters
.DefaultDomain
= Environment
.UserDomainName
;
188 if ((idx
= username
.IndexOf (@"\")) > -1) {
189 domain
= username
.Substring (0, idx
);
190 username
= username
.Substring (idx
+ 1);
192 connectionParameters
.DefaultDomain
= domain
;
193 connectionParameters
.User
= username
;
196 short partialPacketSize
= (short) (86 + (
197 connectionParameters
.Hostname
.Length
+
198 connectionParameters
.ApplicationName
.Length
+
200 connectionParameters
.LibraryName
.Length
+
202 connectionParameters
.Database
.Length
) * 2);
204 if(connectionParameters
.DomainLogin
== true) {
205 authLen
= ((short) (32 + (connectionParameters
.Hostname
.Length
+
207 partialPacketSize
+= authLen
;
210 partialPacketSize
+= ((short) ((username
.Length
+ connectionParameters
.Password
.Length
) * 2));
212 short totalPacketSize
= (short) partialPacketSize
;
214 Comm
.StartPacket (TdsPacketType
.Logon70
);
216 Comm
.Append (totalPacketSize
);
217 Comm
.Append (empty
, 5, pad
);
219 Comm
.Append ((byte) 0x70); // TDS Version 7
220 Comm
.Append (empty
, 3, pad
);
221 Comm
.Append (empty
, 4, pad
);
227 Comm
.Append (curPos
);
228 Comm
.Append ((short) connectionParameters
.Hostname
.Length
);
229 curPos
+= (short) (connectionParameters
.Hostname
.Length
* 2);
231 if(connectionParameters
.DomainLogin
.Equals(true))
233 Comm
.Append((short)0);
234 Comm
.Append((short)0);
235 Comm
.Append((short)0);
236 Comm
.Append((short)0);
241 Comm
.Append (curPos
);
242 Comm
.Append ((short) username
.Length
);
243 curPos
+= ((short) (username
.Length
* 2));
246 Comm
.Append (curPos
);
247 Comm
.Append ((short) connectionParameters
.Password
.Length
);
248 curPos
+= (short) (connectionParameters
.Password
.Length
* 2);
252 Comm
.Append (curPos
);
253 Comm
.Append ((short) connectionParameters
.ApplicationName
.Length
);
254 curPos
+= (short) (connectionParameters
.ApplicationName
.Length
* 2);
257 Comm
.Append (curPos
);
258 Comm
.Append ((short) DataSource
.Length
);
259 curPos
+= (short) (DataSource
.Length
* 2);
262 Comm
.Append ((short) 0);
263 Comm
.Append ((short) 0);
266 Comm
.Append (curPos
);
267 Comm
.Append ((short) connectionParameters
.LibraryName
.Length
);
268 curPos
+= (short) (connectionParameters
.LibraryName
.Length
* 2);
271 Comm
.Append (curPos
);
272 Comm
.Append ((short) Language
.Length
);
273 curPos
+= (short) (Language
.Length
* 2);
276 Comm
.Append (curPos
);
277 Comm
.Append ((short) connectionParameters
.Database
.Length
);
278 curPos
+= (short) (connectionParameters
.Database
.Length
* 2);
281 Comm
.Append((byte) 0);
282 Comm
.Append((byte) 0);
283 Comm
.Append((byte) 0);
284 Comm
.Append((byte) 0);
285 Comm
.Append((byte) 0);
286 Comm
.Append((byte) 0);
288 // Authentication Stuff
289 Comm
.Append ((short) curPos
);
290 if (connectionParameters
.DomainLogin
== true)
292 Comm
.Append ((short) authLen
);
293 curPos
+= (short) authLen
;
296 Comm
.Append ((short) 0);
299 Comm
.Append (curPos
);
300 Comm
.Append ((short) 0);
302 // Connection Parameters
303 Comm
.Append (connectionParameters
.Hostname
);
304 if (connectionParameters
.DomainLogin
== false)
306 // SQL Server Authentication
307 Comm
.Append (connectionParameters
.User
);
308 string scrambledPwd
= EncryptPassword (connectionParameters
.Password
);
309 Comm
.Append (scrambledPwd
);
311 Comm
.Append (connectionParameters
.ApplicationName
);
312 Comm
.Append (DataSource
);
313 Comm
.Append (connectionParameters
.LibraryName
);
314 Comm
.Append (Language
);
315 Comm
.Append (connectionParameters
.Database
);
317 if (connectionParameters
.DomainLogin
)
319 // the rest of the packet is NTLMSSP authentication
320 Type1Message msg
= new Type1Message ();
322 msg
.Host
= connectionParameters
.Hostname
;
323 msg
.Flags
= NtlmFlags
.NegotiateUnicode
|
324 NtlmFlags
.NegotiateNtlm
|
325 NtlmFlags
.NegotiateDomainSupplied
|
326 NtlmFlags
.NegotiateWorkstationSupplied
|
327 NtlmFlags
.NegotiateAlwaysSign
; // 0xb201
328 Comm
.Append (msg
.GetBytes ());
338 private static string EncryptPassword (string pass
)
340 int xormask
= 0x5a5a;
341 int len
= pass
.Length
;
342 char[] chars
= new char[len
];
344 for (int i
= 0; i
< len
; ++i
) {
345 int c
= ((int) (pass
[i
])) ^ xormask
;
346 int m1
= (c
>> 4) & 0x0f0f;
347 int m2
= (c
<< 4) & 0xf0f0;
348 chars
[i
] = (char) (m1
| m2
);
351 return new String (chars
);
354 public override bool Reset ()
358 ExecProc ("exec sp_reset_connection");
363 Console
.WriteLine ("Error reseting");
368 public override void ExecPrepared (string commandText
, TdsMetaParameterCollection parameters
, int timeout
, bool wantResults
)
370 Parameters
= parameters
;
371 ExecuteQuery (BuildPreparedQuery (commandText
), timeout
, wantResults
);
374 public override void ExecProc (string commandText
, TdsMetaParameterCollection parameters
, int timeout
, bool wantResults
)
376 Parameters
= parameters
;
377 ExecuteQuery (BuildProcedureCall (commandText
), timeout
, wantResults
);
380 public override void Execute (string commandText
, TdsMetaParameterCollection parameters
, int timeout
, bool wantResults
)
382 Parameters
= parameters
;
383 string sql
= commandText
;
384 if (wantResults
|| (Parameters
!= null && Parameters
.Count
> 0))
385 sql
= BuildExec (commandText
);
386 ExecuteQuery (sql
, timeout
, wantResults
);
389 private bool IsBlobType (TdsColumnType columnType
)
391 return (columnType
== TdsColumnType
.Text
|| columnType
== TdsColumnType
.Image
|| columnType
== TdsColumnType
.NText
);
394 private bool IsLargeType (TdsColumnType columnType
)
396 return (columnType
== TdsColumnType
.NChar
|| (byte) columnType
> 128);
399 private string FormatParameter (TdsMetaParameter parameter
)
401 if (parameter
.Direction
== TdsParameterDirection
.Output
)
402 return String
.Format ("{0}={0} output", parameter
.ParameterName
);
404 if (parameter
.Value
== null || parameter
.Value
== DBNull
.Value
)
405 return parameter
.ParameterName
+ "=NULL";
408 switch (parameter
.TypeName
) {
418 object paramValue
= parameter
.Value
;
419 Type paramType
= paramValue
.GetType ();
420 if (paramType
.IsEnum
)
421 paramValue
= Convert
.ChangeType (paramValue
,
422 Type
.GetTypeCode (paramType
));
423 value = paramValue
.ToString ();
427 value = String
.Format ("N'{0}'", parameter
.Value
.ToString ().Replace ("'", "''"));
429 case "uniqueidentifier":
430 value = String
.Format ("0x{0}", ((Guid
) parameter
.Value
).ToString ("N"));
433 if (parameter
.Value
.GetType () == typeof (bool))
434 value = (((bool) parameter
.Value
) ? "0x1" : "0x0");
436 value = parameter
.Value
.ToString ();
442 value = String
.Format ("0x{0}", BitConverter
.ToString ((byte[]) parameter
.Value
).Replace ("-", "").ToLower ());
445 value = String
.Format ("'{0}'", parameter
.Value
.ToString ().Replace ("'", "''"));
449 return parameter
.ParameterName
+ "=" + value;
452 public override string Prepare (string commandText
, TdsMetaParameterCollection parameters
)
454 Parameters
= parameters
;
456 TdsMetaParameterCollection parms
= new TdsMetaParameterCollection ();
457 TdsMetaParameter parm
= new TdsMetaParameter ("@P1", "int", null);
458 parm
.Direction
= TdsParameterDirection
.Output
;
461 parms
.Add (new TdsMetaParameter ("@P2", "nvarchar", BuildPreparedParameters ()));
462 parms
.Add (new TdsMetaParameter ("@P3", "nvarchar", commandText
));
464 ExecProc ("sp_prepare", parms
, 0, true);
466 if (ColumnValues
[0] == null || ColumnValues
[0] == DBNull
.Value
)
467 throw new TdsInternalException ();
468 return ColumnValues
[0].ToString ();
471 protected override TdsDataColumnCollection
ProcessColumnInfo ()
473 TdsDataColumnCollection result
= new TdsDataColumnCollection ();
474 int numColumns
= Comm
.GetTdsShort ();
476 for (int i
= 0; i
< numColumns
; i
+= 1) {
477 byte[] flagData
= new byte[4];
478 for (int j
= 0; j
< 4; j
+= 1)
479 flagData
[j
] = Comm
.GetByte ();
481 bool nullable
= (flagData
[2] & 0x01) > 0;
482 bool caseSensitive
= (flagData
[2] & 0x02) > 0;
483 bool writable
= (flagData
[2] & 0x0c) > 0;
484 bool autoIncrement
= (flagData
[2] & 0x10) > 0;
485 bool isIdentity
= (flagData
[2] & 0x10) > 0;
487 TdsColumnType columnType
= (TdsColumnType
) (Comm
.GetByte () & 0xff);
488 if ((byte) columnType
== 0xef)
489 columnType
= TdsColumnType
.NChar
;
491 byte xColumnType
= 0;
492 if (IsLargeType (columnType
)) {
493 xColumnType
= (byte) columnType
;
494 if (columnType
!= TdsColumnType
.NChar
)
499 string tableName
= null;
501 if (IsBlobType (columnType
)) {
502 columnSize
= Comm
.GetTdsInt ();
503 tableName
= Comm
.GetString (Comm
.GetTdsShort ());
506 else if (IsFixedSizeColumn (columnType
))
507 columnSize
= LookupBufferSize (columnType
);
508 else if (IsLargeType ((TdsColumnType
) xColumnType
))
509 columnSize
= Comm
.GetTdsShort ();
511 columnSize
= Comm
.GetByte () & 0xff;
516 switch (columnType
) {
517 case TdsColumnType
.NText
:
518 case TdsColumnType
.NChar
:
519 case TdsColumnType
.NVarChar
:
522 case TdsColumnType
.Decimal
:
523 case TdsColumnType
.Numeric
:
524 precision
= Comm
.GetByte ();
525 scale
= Comm
.GetByte ();
529 string columnName
= Comm
.GetString (Comm
.GetByte ());
531 int index
= result
.Add (new TdsDataColumn ());
532 result
[index
]["AllowDBNull"] = nullable
;
533 result
[index
]["ColumnName"] = columnName
;
534 result
[index
]["ColumnSize"] = columnSize
;
535 result
[index
]["ColumnType"] = columnType
;
536 result
[index
]["IsIdentity"] = isIdentity
;
537 result
[index
]["IsReadOnly"] = !writable
;
538 result
[index
]["NumericPrecision"] = precision
;
539 result
[index
]["NumericScale"] = scale
;
540 result
[index
]["BaseTableName"] = tableName
;
546 public override void Unprepare (string statementId
)
548 TdsMetaParameterCollection parms
= new TdsMetaParameterCollection ();
549 parms
.Add (new TdsMetaParameter ("@P1", "int", Int32
.Parse (statementId
)));
550 ExecProc ("sp_unprepare", parms
, 0, false);
553 #endregion // Methods