Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Brian Bruns
3 : * Copyright (C) 2010 Frediano Ziglio
4 : *
5 : * This library is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU Library General Public
7 : * License as published by the Free Software Foundation; either
8 : * version 2 of the License, or (at your option) any later version.
9 : *
10 : * This library is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : * Library General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU Library General Public
16 : * License along with this library; if not, write to the
17 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 : * Boston, MA 02111-1307, USA.
19 : */
20 :
21 : #include <config.h>
22 :
23 : #if HAVE_STRING_H
24 : #include <string.h>
25 : #endif /* HAVE_STRING_H */
26 :
27 : #include <assert.h>
28 :
29 : #include <freetds/tds.h>
30 : #include <freetds/server.h>
31 : #include <freetds/utils/string.h>
32 : #include <freetds/data.h>
33 : #include <freetds/bytes.h>
34 :
35 : void
36 2136 : tds_env_change(TDSSOCKET * tds, int type, const char *oldvalue, const char *newvalue)
37 : {
38 : TDS_SMALLINT totsize;
39 :
40 : /* If oldvalue is NULL, treat it like "" */
41 2136 : if (oldvalue == NULL)
42 712 : oldvalue = "";
43 :
44 : /*
45 : * NOTE: I don't know why each type of environment value has a different
46 : * format. According to the TDS5 specifications, they should all use
47 : * the same format. The code for the TDS_ENV_CHARSET case *should*
48 : * work for all environment values. -- Steve Kirkendall
49 : */
50 :
51 2136 : switch (type) {
52 2136 : case TDS_ENV_DATABASE:
53 : case TDS_ENV_LANG:
54 : case TDS_ENV_PACKSIZE:
55 : case TDS_ENV_CHARSET:
56 2136 : tds_put_byte(tds, TDS_ENVCHANGE_TOKEN);
57 : /* totsize = type + newlen + newvalue + oldlen + oldvalue */
58 : /* FIXME ucs2 */
59 2136 : totsize = (IS_TDS7_PLUS(tds->conn) ? 2 : 1) * (strlen(oldvalue) + strlen(newvalue)) + 3;
60 2136 : tds_put_smallint(tds, totsize);
61 2136 : tds_put_byte(tds, type);
62 2136 : tds_put_byte(tds, strlen(newvalue));
63 : /* FIXME this assume singlebyte -> ucs2 for mssql */
64 2136 : tds_put_string(tds, newvalue, strlen(newvalue));
65 2136 : tds_put_byte(tds, strlen(oldvalue));
66 : /* FIXME this assume singlebyte -> ucs2 for mssql */
67 2136 : tds_put_string(tds, oldvalue, strlen(oldvalue));
68 2136 : break;
69 0 : case TDS_ENV_LCID:
70 : case TDS_ENV_SQLCOLLATION:
71 : #if 1
72 0 : tds_put_byte(tds, TDS_ENVCHANGE_TOKEN);
73 : /* totsize = type + len + oldvalue + len + newvalue */
74 0 : totsize = 3 + strlen(newvalue) + strlen(oldvalue);
75 0 : tds_put_smallint(tds, totsize);
76 0 : tds_put_byte(tds, type);
77 0 : tds_put_byte(tds, strlen(newvalue));
78 0 : tds_put_n(tds, newvalue, strlen(newvalue));
79 0 : tds_put_byte(tds, strlen(oldvalue));
80 0 : tds_put_n(tds, oldvalue, strlen(oldvalue));
81 0 : break;
82 : #endif
83 0 : default:
84 0 : tdsdump_log(TDS_DBG_WARN, "tds_env_change() ignoring unsupported environment code #%d", type);
85 : }
86 2136 : }
87 :
88 : void
89 0 : tds_send_eed(TDSSOCKET * tds, int msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line)
90 : {
91 : int totsize;
92 :
93 0 : tds_put_byte(tds, TDS_EED_TOKEN);
94 0 : totsize = 7 + strlen(procname) + 5 + strlen(msgtext) + 2 + strlen(srvname) + 3;
95 0 : tds_put_smallint(tds, totsize);
96 0 : tds_put_smallint(tds, msgno);
97 0 : tds_put_smallint(tds, 0); /* unknown */
98 0 : tds_put_byte(tds, msgstate);
99 0 : tds_put_byte(tds, severity);
100 0 : tds_put_byte(tds, strlen(procname));
101 0 : tds_put_n(tds, procname, strlen(procname));
102 0 : tds_put_byte(tds, 0); /* unknown */
103 0 : tds_put_byte(tds, 1); /* unknown */
104 0 : tds_put_byte(tds, 0); /* unknown */
105 0 : tds_put_smallint(tds, strlen(msgtext) + 1);
106 0 : tds_put_n(tds, msgtext, strlen(msgtext));
107 0 : tds_put_byte(tds, severity);
108 0 : tds_put_byte(tds, strlen(srvname));
109 0 : tds_put_n(tds, srvname, strlen(srvname));
110 0 : tds_put_byte(tds, 0); /* unknown */
111 0 : tds_put_byte(tds, 1); /* unknown */
112 0 : tds_put_byte(tds, 0); /* unknown */
113 0 : }
114 :
115 : void
116 1424 : tds_send_msg(TDSSOCKET * tds, int msgno, int msgstate, int severity,
117 : const char *msgtext, const char *srvname, const char *procname, int line)
118 : {
119 : int msgsz;
120 : size_t len;
121 :
122 1424 : tds_put_byte(tds, TDS_INFO_TOKEN);
123 1424 : if (!procname)
124 1424 : procname = "";
125 1424 : len = strlen(procname);
126 1424 : msgsz = 4 /* msg no */
127 : + 1 /* msg state */
128 : + 1 /* severity */
129 : /* FIXME ucs2 */
130 2848 : + 4 + (IS_TDS7_PLUS(tds->conn) ? 2 : 1) * (strlen(msgtext) + strlen(srvname) + len)
131 2848 : + (IS_TDS72_PLUS(tds->conn) ? 4 : 2); /* line number */
132 1424 : tds_put_smallint(tds, msgsz);
133 1424 : tds_put_int(tds, msgno);
134 1424 : tds_put_byte(tds, msgstate);
135 1424 : tds_put_byte(tds, severity);
136 1424 : tds_put_smallint(tds, strlen(msgtext));
137 : /* FIXME ucs2 */
138 1424 : tds_put_string(tds, msgtext, strlen(msgtext));
139 1424 : tds_put_byte(tds, strlen(srvname));
140 : /* FIXME ucs2 */
141 1424 : tds_put_string(tds, srvname, strlen(srvname));
142 1424 : if (len) {
143 0 : tds_put_byte(tds, len);
144 : /* FIXME ucs2 */
145 0 : tds_put_string(tds, procname, len);
146 : } else {
147 1424 : tds_put_byte(tds, 0);
148 : }
149 1424 : if (IS_TDS72_PLUS(tds->conn))
150 0 : tds_put_int(tds, line);
151 : else
152 1424 : tds_put_smallint(tds, line);
153 1424 : }
154 :
155 : void
156 0 : tds_send_err(TDSSOCKET * tds, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
157 : {
158 0 : tds_put_byte(tds, TDS_ERROR_TOKEN);
159 0 : }
160 :
161 : void
162 712 : tds_send_login_ack(TDSSOCKET * tds, const char *progname)
163 : {
164 : TDS_UINT ui, version;
165 :
166 712 : tds_put_byte(tds, TDS_LOGINACK_TOKEN);
167 712 : tds_put_smallint(tds, 10 + (IS_TDS7_PLUS(tds->conn)? 2 : 1) * strlen(progname)); /* length of message */
168 712 : if (IS_TDS50(tds->conn)) {
169 0 : tds_put_byte(tds, 5);
170 0 : version = 0x05000000u;
171 : } else {
172 712 : tds_put_byte(tds, 1);
173 : /* see src/tds/token.c */
174 712 : if (IS_TDS73_PLUS(tds->conn)) {
175 : version = 0x730B0003u;
176 712 : } else if (IS_TDS72_PLUS(tds->conn)) {
177 : version = 0x72090002u;
178 712 : } else if (IS_TDS71_PLUS(tds->conn)) {
179 712 : version = tds->conn->tds71rev1 ? 0x07010000u : 0x71000001u;
180 : } else {
181 0 : version = (TDS_MAJOR(tds->conn) << 24) | (TDS_MINOR(tds->conn) << 16);
182 : }
183 : }
184 712 : TDS_PUT_A4BE(&ui, version);
185 712 : tds_put_n(tds, &ui, 4);
186 :
187 712 : tds_put_byte(tds, strlen(progname));
188 : /* FIXME ucs2 */
189 712 : tds_put_string(tds, progname, strlen(progname));
190 :
191 : /* server version, always big endian */
192 712 : TDS_PUT_A4BE(&ui, tds->conn->product_version & 0x7fffffffu);
193 712 : tds_put_n(tds, &ui, 4);
194 712 : }
195 :
196 : void
197 0 : tds_send_capabilities_token(TDSSOCKET * tds)
198 : {
199 0 : tds_put_byte(tds, TDS_CAPABILITY_TOKEN);
200 0 : tds_put_smallint(tds, 18);
201 0 : tds_put_byte(tds, 1);
202 0 : tds_put_byte(tds, 7);
203 0 : tds_put_byte(tds, 7);
204 0 : tds_put_byte(tds, 97);
205 0 : tds_put_byte(tds, 65);
206 0 : tds_put_byte(tds, 207);
207 0 : tds_put_byte(tds, 255);
208 0 : tds_put_byte(tds, 255);
209 0 : tds_put_byte(tds, 230);
210 0 : tds_put_byte(tds, 2);
211 0 : tds_put_byte(tds, 7);
212 0 : tds_put_byte(tds, 0);
213 0 : tds_put_byte(tds, 0);
214 0 : tds_put_byte(tds, 2);
215 0 : tds_put_byte(tds, 0);
216 0 : tds_put_byte(tds, 0);
217 0 : tds_put_byte(tds, 0);
218 0 : tds_put_byte(tds, 0);
219 0 : }
220 :
221 : /**
222 : * Send a "done" token, marking the end of a table, stored procedure, or query.
223 : * \param tds Where the token will be written to.
224 : * \param token The appropriate type of "done" token for this context:
225 : * TDS_DONE_TOKEN outside a stored procedure,
226 : * TDS_DONEINPROC_TOKEN inside a stored procedure, or
227 : * TDS_DONEPROC_TOKEN at the end of a stored procedure.
228 : * \param flags Bitwise-OR of flags in the "enum tds_end" data type.
229 : * TDS_DONE_FINAL for the last statement in a query,
230 : * TDS_DONE_MORE_RESULTS if not the last statement,
231 : * TDS_DONE_ERROR if the statement had an error,
232 : * TDS_DONE_INXACT if a transaction is pending,
233 : * TDS_DONE_PROC inside a stored procedure,
234 : * TDS_DONE_COUNT if a table was sent (and rows counted)
235 : * TDS_DONE_CANCELLED if the query was canceled, and
236 : * TDS_DONE_EVENT if the token marks an event.
237 : * \param numrows Number of rows, if flags has TDS_DONE_COUNT.
238 : */
239 : void
240 712 : tds_send_done(TDSSOCKET * tds, int token, TDS_SMALLINT flags, TDS_INT numrows)
241 : {
242 712 : tds_put_byte(tds, token);
243 712 : tds_put_smallint(tds, flags);
244 712 : tds_put_smallint(tds, 2); /* are these two bytes the transaction status? */
245 712 : if (IS_TDS72_PLUS(tds->conn))
246 0 : tds_put_int8(tds, numrows);
247 : else
248 712 : tds_put_int(tds, numrows);
249 712 : }
250 :
251 : void
252 712 : tds_send_done_token(TDSSOCKET * tds, TDS_SMALLINT flags, TDS_INT numrows)
253 : {
254 712 : tds_send_done(tds, TDS_DONE_TOKEN, flags, numrows);
255 712 : }
256 :
257 : void
258 0 : tds_send_control_token(TDSSOCKET * tds, TDS_SMALLINT numcols)
259 : {
260 : int i;
261 :
262 0 : tds_put_byte(tds, TDS_CONTROL_FEATUREEXTACK_TOKEN);
263 0 : tds_put_smallint(tds, numcols);
264 0 : for (i = 0; i < numcols; i++) {
265 0 : tds_put_byte(tds, 0);
266 : }
267 0 : }
268 : void
269 0 : tds_send_col_name(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
270 : {
271 0 : int col, hdrsize = 0;
272 : TDSCOLUMN *curcol;
273 :
274 0 : tds_put_byte(tds, TDS_COLNAME_TOKEN);
275 0 : for (col = 0; col < resinfo->num_cols; col++) {
276 0 : curcol = resinfo->columns[col];
277 0 : hdrsize += tds_dstr_len(&curcol->column_name) + 1;
278 : }
279 :
280 0 : tds_put_smallint(tds, hdrsize);
281 0 : for (col = 0; col < resinfo->num_cols; col++) {
282 0 : curcol = resinfo->columns[col];
283 0 : tds_put_byte(tds, tds_dstr_len(&curcol->column_name));
284 : /* exclude the null */
285 0 : tds_put_n(tds, tds_dstr_cstr(&curcol->column_name), tds_dstr_len(&curcol->column_name));
286 : }
287 0 : }
288 : void
289 0 : tds_send_col_info(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
290 : {
291 0 : int col, hdrsize = 0;
292 : TDSCOLUMN *curcol;
293 :
294 0 : tds_put_byte(tds, TDS_COLFMT_TOKEN);
295 :
296 0 : for (col = 0; col < resinfo->num_cols; col++) {
297 0 : curcol = resinfo->columns[col];
298 0 : hdrsize += 5;
299 0 : if (!is_fixed_type(curcol->column_type)) {
300 0 : hdrsize++;
301 : }
302 : }
303 0 : tds_put_smallint(tds, hdrsize);
304 :
305 0 : for (col = 0; col < resinfo->num_cols; col++) {
306 0 : curcol = resinfo->columns[col];
307 0 : tds_put_n(tds, "\0\0\0\0", 4);
308 0 : tds_put_byte(tds, curcol->column_type);
309 0 : if (!is_fixed_type(curcol->column_type)) {
310 0 : tds_put_byte(tds, curcol->column_size);
311 : }
312 : }
313 0 : }
314 :
315 : void
316 0 : tds_send_result(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
317 : {
318 : TDSCOLUMN *curcol;
319 : int i, totlen;
320 : size_t len;
321 :
322 0 : tds_put_byte(tds, TDS_RESULT_TOKEN);
323 0 : totlen = 2;
324 0 : for (i = 0; i < resinfo->num_cols; i++) {
325 0 : curcol = resinfo->columns[i];
326 0 : len = tds_dstr_len(&curcol->column_name);
327 0 : totlen += 8;
328 0 : totlen += len;
329 0 : curcol = resinfo->columns[i];
330 0 : if (!is_fixed_type(curcol->column_type)) {
331 0 : totlen++;
332 : }
333 : }
334 0 : tds_put_smallint(tds, totlen);
335 0 : tds_put_smallint(tds, resinfo->num_cols);
336 0 : for (i = 0; i < resinfo->num_cols; i++) {
337 0 : curcol = resinfo->columns[i];
338 0 : len = tds_dstr_len(&curcol->column_name);
339 0 : tds_put_byte(tds, tds_dstr_len(&curcol->column_name));
340 0 : tds_put_n(tds, tds_dstr_cstr(&curcol->column_name), len);
341 0 : tds_put_byte(tds, '0');
342 0 : tds_put_int(tds, curcol->column_usertype);
343 0 : tds_put_byte(tds, curcol->column_type);
344 0 : if (!is_fixed_type(curcol->column_type)) {
345 0 : tds_put_byte(tds, curcol->column_size);
346 : }
347 0 : tds_put_byte(tds, 0);
348 : }
349 0 : }
350 :
351 : void
352 0 : tds7_send_result(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
353 : {
354 : int i, j;
355 : TDSCOLUMN *curcol;
356 :
357 : /* TDS7+ uses TDS7_RESULT_TOKEN to send column names and info */
358 0 : tds_put_byte(tds, TDS7_RESULT_TOKEN);
359 :
360 : /* send the number of columns */
361 0 : tds_put_smallint(tds, resinfo->num_cols);
362 :
363 : /* send info about each column */
364 0 : for (i = 0; i < resinfo->num_cols; i++) {
365 :
366 : /* usertype, flags, and type */
367 0 : curcol = resinfo->columns[i];
368 0 : tds_put_smallint(tds, curcol->column_usertype);
369 0 : tds_put_smallint(tds, curcol->column_flags);
370 0 : tds_put_byte(tds, curcol->column_type); /* smallint? */
371 :
372 : /* bytes in "size" field varies */
373 0 : if (is_blob_type(curcol->column_type)) {
374 0 : tds_put_int(tds, curcol->column_size);
375 0 : } else if (curcol->column_type>=128) { /*is_large_type*/
376 0 : tds_put_smallint(tds, curcol->column_size);
377 : } else {
378 0 : tds_put_tinyint(tds, curcol->column_size);
379 : }
380 :
381 : /* some types have extra info */
382 0 : if (is_numeric_type(curcol->column_type)) {
383 0 : tds_put_tinyint(tds, curcol->column_prec);
384 0 : tds_put_tinyint(tds, curcol->column_scale);
385 0 : } else if (is_blob_type(curcol->column_type)) {
386 0 : size_t len = tds_dstr_len(&curcol->table_name);
387 0 : const char *name = tds_dstr_cstr(&curcol->table_name);
388 :
389 0 : tds_put_smallint(tds, 2 * len);
390 0 : for (j = 0; name[j] != '\0'; j++){
391 0 : tds_put_byte(tds, name[j]);
392 0 : tds_put_byte(tds, 0);
393 : }
394 : }
395 :
396 : /* finally the name, in UCS16 format */
397 0 : tds_put_byte(tds, tds_dstr_len(&curcol->column_name));
398 0 : for (j = 0; j < tds_dstr_len(&curcol->column_name); j++) {
399 0 : tds_put_byte(tds, tds_dstr_cstr(&curcol->column_name)[j]);
400 0 : tds_put_byte(tds, 0);
401 : }
402 : }
403 0 : }
404 :
405 : /**
406 : * Send any tokens that mark the start of a table. This automatically chooses
407 : * the right tokens for this client's version of the TDS protocol. In other
408 : * words, it is a wrapper around tds_send_col_name(), tds_send_col_info(),
409 : * tds_send_result(), and tds7_send_result().
410 : * \param tds The socket to which the tokens will be written. Also,
411 : * it contains the TDS protocol version number.
412 : * \param resinfo Describes the table to be send, especially the number
413 : * of columns and the names & data types of each column.
414 : */
415 0 : void tds_send_table_header(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
416 : {
417 0 : switch (TDS_MAJOR(tds->conn)) {
418 0 : case 4:
419 : /*
420 : * TDS4 uses TDS_COLNAME_TOKEN to send column names, and
421 : * TDS_COLFMT_TOKEN to send column info. The number of columns
422 : * is implied by the number of column names.
423 : */
424 0 : tds_send_col_name(tds, resinfo);
425 0 : tds_send_col_info(tds, resinfo);
426 0 : break;
427 :
428 0 : case 5:
429 : /* TDS5 uses a TDS_RESULT_TOKEN to send all column information */
430 0 : tds_send_result(tds, resinfo);
431 0 : break;
432 :
433 0 : case 7:
434 : /*
435 : * TDS7+ uses a TDS7_RESULT_TOKEN to send all column
436 : * information.
437 : */
438 0 : tds7_send_result(tds, resinfo);
439 0 : break;
440 : }
441 0 : }
442 :
443 : void
444 0 : tds_send_row(TDSSOCKET * tds, TDSRESULTINFO * resinfo)
445 : {
446 : TDSCOLUMN *curcol;
447 : int colsize, i;
448 :
449 0 : tds_put_byte(tds, TDS_ROW_TOKEN);
450 0 : for (i = 0; i < resinfo->num_cols; i++) {
451 0 : curcol = resinfo->columns[i];
452 0 : if (!is_fixed_type(curcol->column_type)) {
453 : /* FIX ME -- I have no way of knowing the actual length of non character variable data (eg nullable int) */
454 0 : colsize = strlen((char *) curcol->column_data);
455 0 : tds_put_byte(tds, colsize);
456 0 : tds_put_n(tds, curcol->column_data, colsize);
457 : } else {
458 0 : tds_put_n(tds, curcol->column_data, tds_get_size_by_type(curcol->column_type));
459 : }
460 : }
461 0 : }
462 :
463 : void
464 0 : tds71_send_prelogin(TDSSOCKET * tds)
465 : {
466 : static const unsigned char prelogin[] = {
467 : 0x00, 0x00, 0x15, 0x00, 0x06, 0x01, 0x00, 0x1b,
468 : 0x00, 0x01, 0x02, 0x00, 0x1c, 0x00, 0x01, 0x03,
469 : 0x00, 0x1d, 0x00, 0x00, 0xff, 0x08, 0x00, 0x01,
470 : 0x55, 0x00, 0x00, 0x02, 0x00
471 : };
472 :
473 0 : tds_put_n(tds, prelogin, sizeof(prelogin));
474 0 : }
475 :
|